summaryrefslogtreecommitdiff
path: root/plugins/gme/game-music-emu-0.6pre
diff options
context:
space:
mode:
authorGravatar waker <wakeroid@gmail.com>2012-05-19 13:51:12 +0200
committerGravatar waker <wakeroid@gmail.com>2012-05-19 13:51:12 +0200
commit2c5210741763c398d8965db59027299985013331 (patch)
tree3b1ed5fddb58632e9363e2d8363ac627691d0805 /plugins/gme/game-music-emu-0.6pre
parent2db95026b4d64a3b6d2f37b88824245a221ca5b7 (diff)
switched to gme0.6pre from foo_gep to fix GBS crash
Diffstat (limited to 'plugins/gme/game-music-emu-0.6pre')
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.cpp406
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.h104
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp190
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.h81
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ay_Cpu.cpp59
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.cpp306
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.h58
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.cpp509
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.h198
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl.h135
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl2.h282
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.cpp124
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.h79
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Data_Reader.cpp315
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Data_Reader.h151
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Downsampler.cpp74
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Downsampler.h25
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.cpp200
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.h58
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.cpp640
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.h149
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.cpp123
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.h101
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.cpp407
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.h193
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.cpp51
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.h82
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu_run.h1183
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.cpp712
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.h188
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.cpp208
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.h106
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gbs_Cpu.cpp134
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.cpp143
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.h61
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gme_File.cpp183
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gme_File.h152
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.cpp86
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.h92
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.cpp405
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.h86
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.cpp361
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.h87
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp309
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.h94
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.cpp408
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.h119
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.cpp123
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.h139
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu_run.h1342
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.cpp163
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.h40
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.cpp214
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.h97
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Kss_Cpu.cpp35
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.cpp470
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.h77
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp124
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.h111
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/M3u_Playlist.cpp476
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/M3u_Playlist.h87
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.cpp290
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.h219
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.cpp235
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.h241
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.cpp392
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.h181
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.cpp62
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.h131
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu_run.h1121
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.cpp280
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.h139
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.cpp121
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.h131
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Mmc5_Apu.h70
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Namco_Apu.cpp152
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Namco_Apu.h102
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.cpp578
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.h147
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.cpp216
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.h95
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp206
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.h80
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.cpp302
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.h68
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsf_Cpu.cpp116
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.cpp314
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.h51
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.cpp327
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.h189
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsfe_Emu.cpp321
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Nsfe_Emu.h72
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.cpp270
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.h63
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Resampler.cpp79
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Resampler.h110
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.cpp99
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.h94
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.cpp81
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.h53
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.cpp339
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.h103
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp192
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.h91
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Cpu.cpp96
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.cpp385
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.h51
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.cpp108
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.h44
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sgc_Cpu.cpp36
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.cpp136
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.h43
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.cpp225
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.h114
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.cpp371
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.h128
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.cpp80
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.h47
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.cpp378
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.h291
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.cpp562
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.h1225
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.cpp1387
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.h315
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.cpp393
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.h85
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Spc_Filter.h53
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.cpp293
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.h105
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Upsampler.cpp73
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Upsampler.h25
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.cpp441
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.h160
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.cpp360
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.h66
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.cpp70
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.h37
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.cpp2510
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.h38
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.cpp82
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.h122
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu_run.h1695
-rwxr-xr-xplugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.cpp555
-rwxr-xr-xplugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.h268
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/blargg_common.cpp58
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/blargg_common.h224
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/blargg_config.h55
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/blargg_endian.h189
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.cpp117
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.h84
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/blargg_source.h135
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/divfix.h18
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/emu2413.cpp742
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/emu2413.h143
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/fmopl.cpp2617
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/fmopl.h116
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/gme.cpp431
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/gme.h298
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/i_fmpac.h38
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/i_fmunit.h38
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/i_vrc7.h38
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/kmsnddev.h31
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/nestypes.h39
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_deltat.c281
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_deltat.h23
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.c88
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.h43
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_opl.c1244
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_opl.h26
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.c136
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.h38
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/ym2413.c2106
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/ym2413.h61
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.cpp655
-rw-r--r--plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.h98
175 files changed, 45901 insertions, 0 deletions
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.cpp
new file mode 100644
index 00000000..81ff27c7
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.cpp
@@ -0,0 +1,406 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Ay_Apu.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// Emulation inaccuracies:
+// * Noise isn't run when not in use
+// * Changes to envelope and noise periods are delayed until next reload
+// * Super-sonic tone should attenuate output to about 60%, not 50%
+
+// Tones above this frequency are treated as disabled tone at half volume.
+// Power of two is more efficient (avoids division).
+int const inaudible_freq = 16384;
+
+int const period_factor = 16;
+
+static byte const amp_table [16] =
+{
+#define ENTRY( n ) byte (n * Ay_Apu::amp_range + 0.5)
+ // With channels tied together and 1K resistor to ground (as datasheet recommends),
+ // output nearly matches logarithmic curve as claimed. Approx. 1.5 dB per step.
+ ENTRY(0.000000),ENTRY(0.007813),ENTRY(0.011049),ENTRY(0.015625),
+ ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500),
+ ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000),
+ ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000),
+
+ /*
+ // Measured from an AY-3-8910A chip with date code 8611.
+
+ // Direct voltages without any load (very linear)
+ ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785),
+ ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032),
+ ENTRY(0.360215),ENTRY(0.494624),ENTRY(0.594624),ENTRY(0.672043),
+ ENTRY(0.766129),ENTRY(0.841935),ENTRY(0.926882),ENTRY(1.000000),
+ // With only some load
+ ENTRY(0.000000),ENTRY(0.011940),ENTRY(0.017413),ENTRY(0.024876),
+ ENTRY(0.036318),ENTRY(0.054229),ENTRY(0.072637),ENTRY(0.122388),
+ ENTRY(0.174129),ENTRY(0.239303),ENTRY(0.323881),ENTRY(0.410945),
+ ENTRY(0.527363),ENTRY(0.651741),ENTRY(0.832338),ENTRY(1.000000),
+ */
+#undef ENTRY
+};
+
+static byte const modes [8] =
+{
+#define MODE( a0,a1, b0,b1, c0,c1 ) \
+ (a0 | a1<<1 | b0<<2 | b1<<3 | c0<<4 | c1<<5)
+ MODE( 1,0, 1,0, 1,0 ),
+ MODE( 1,0, 0,0, 0,0 ),
+ MODE( 1,0, 0,1, 1,0 ),
+ MODE( 1,0, 1,1, 1,1 ),
+ MODE( 0,1, 0,1, 0,1 ),
+ MODE( 0,1, 1,1, 1,1 ),
+ MODE( 0,1, 1,0, 0,1 ),
+ MODE( 0,1, 0,0, 0,0 ),
+};
+
+void Ay_Apu::set_output( Blip_Buffer* b )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ set_output( i, b );
+}
+
+Ay_Apu::Ay_Apu()
+{
+ // build full table of the upper 8 envelope waveforms
+ for ( int m = 8; m--; )
+ {
+ byte* out = env_modes [m];
+ int flags = modes [m];
+ for ( int x = 3; --x >= 0; )
+ {
+ int amp = flags & 1;
+ int end = flags >> 1 & 1;
+ int step = end - amp;
+ amp *= 15;
+ for ( int y = 16; --y >= 0; )
+ {
+ *out++ = amp_table [amp];
+ amp += step;
+ }
+ flags >>= 2;
+ }
+ }
+
+ set_output( NULL );
+ volume( 1.0 );
+ reset();
+}
+
+void Ay_Apu::reset()
+{
+ addr_ = 0;
+ last_time = 0;
+ noise_delay = 0;
+ noise_lfsr = 1;
+
+ for ( osc_t* osc = &oscs [osc_count]; osc != oscs; )
+ {
+ osc--;
+ osc->period = period_factor;
+ osc->delay = 0;
+ osc->last_amp = 0;
+ osc->phase = 0;
+ }
+
+ for ( int i = sizeof regs; --i >= 0; )
+ regs [i] = 0;
+ regs [7] = 0xFF;
+ write_data_( 13, 0 );
+}
+
+int Ay_Apu::read()
+{
+ static byte const masks [reg_count] = {
+ 0xFF, 0x0F, 0xFF, 0x0F, 0xFF, 0x0F, 0x1F, 0x3F,
+ 0x1F, 0x1F, 0x1F, 0xFF, 0xFF, 0x0F, 0x00, 0x00
+ };
+ return regs [addr_] & masks [addr_];
+}
+
+void Ay_Apu::write_data_( int addr, int data )
+{
+ assert( (unsigned) addr < reg_count );
+
+ if ( (unsigned) addr >= 14 )
+ dprintf( "Wrote to I/O port %02X\n", (int) addr );
+
+ // envelope mode
+ if ( addr == 13 )
+ {
+ if ( !(data & 8) ) // convert modes 0-7 to proper equivalents
+ data = (data & 4) ? 15 : 9;
+ env_wave = env_modes [data - 7];
+ env_pos = -48;
+ env_delay = 0; // will get set to envelope period in run_until()
+ }
+ regs [addr] = data;
+
+ // handle period changes accurately
+ int i = addr >> 1;
+ if ( i < osc_count )
+ {
+ blip_time_t period = (regs [i * 2 + 1] & 0x0F) * (0x100 * period_factor) +
+ regs [i * 2] * period_factor;
+ if ( !period )
+ period = period_factor;
+
+ // adjust time of next timer expiration based on change in period
+ osc_t& osc = oscs [i];
+ if ( (osc.delay += period - osc.period) < 0 )
+ osc.delay = 0;
+ osc.period = period;
+ }
+
+ // TODO: same as above for envelope timer, and it also has a divide by two after it
+}
+
+int const noise_off = 0x08;
+int const tone_off = 0x01;
+
+void Ay_Apu::run_until( blip_time_t final_end_time )
+{
+ require( final_end_time >= last_time );
+
+ // noise period and initial values
+ blip_time_t const noise_period_factor = period_factor * 2; // verified
+ blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor;
+ if ( !noise_period )
+ noise_period = noise_period_factor;
+ blip_time_t const old_noise_delay = noise_delay;
+ unsigned const old_noise_lfsr = noise_lfsr;
+
+ // envelope period
+ blip_time_t const env_period_factor = period_factor * 2; // verified
+ blip_time_t env_period = (regs [12] * 0x100 + regs [11]) * env_period_factor;
+ if ( !env_period )
+ env_period = env_period_factor; // same as period 1 on my AY chip
+ if ( !env_delay )
+ env_delay = env_period;
+
+ // run each osc separately
+ for ( int index = 0; index < osc_count; index++ )
+ {
+ osc_t* const osc = &oscs [index];
+ int osc_mode = regs [7] >> index;
+
+ // output
+ Blip_Buffer* const osc_output = osc->output;
+ if ( !osc_output )
+ continue;
+ osc_output->set_modified();
+
+ // period
+ int half_vol = 0;
+ blip_time_t inaudible_period = (unsigned) (osc_output->clock_rate() +
+ inaudible_freq) / (unsigned) (inaudible_freq * 2);
+ if ( osc->period <= inaudible_period && !(osc_mode & tone_off) )
+ {
+ half_vol = 1; // Actually around 60%, but 50% is close enough
+ osc_mode |= tone_off;
+ }
+
+ // envelope
+ blip_time_t start_time = last_time;
+ blip_time_t end_time = final_end_time;
+ int const vol_mode = regs [0x08 + index];
+ int 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
+ {
+ int count = (final_end_time - time + period - 1) / period;
+ time += count * period;
+ osc->phase ^= count & 1;
+ }
+
+ // noise time
+ blip_time_t ntime = final_end_time;
+ unsigned noise_lfsr = 1;
+ if ( !(osc_mode & noise_off) )
+ {
+ ntime = start_time + old_noise_delay;
+ noise_lfsr = old_noise_lfsr;
+ //if ( (regs [6] & 0x1F) == 0 )
+ // dprintf( "Used noise period 0\n" );
+ }
+
+ // The following efficiently handles several cases (least demanding first):
+ // * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC
+ // * Just tone or just noise, envelope disabled
+ // * Envelope controlling tone and/or noise
+ // * Tone and noise disabled, envelope enabled with high frequency
+ // * Tone and noise together
+ // * Tone and noise together with envelope
+
+ // This loop only runs one iteration if envelope is disabled. If envelope
+ // is being used as a waveform (tone and noise disabled), this loop will
+ // still be reasonably efficient since the bulk of it will be skipped.
+ while ( 1 )
+ {
+ // current amplitude
+ int amp = 0;
+ if ( (osc_mode | osc->phase) & 1 & (osc_mode >> 3 | noise_lfsr) )
+ amp = volume;
+ {
+ int delta = amp - osc->last_amp;
+ if ( delta )
+ {
+ osc->last_amp = amp;
+ synth_.offset( start_time, delta, osc_output );
+ }
+ }
+
+ // Run wave and noise interleved with each catching up to the other.
+ // If one or both are disabled, their "current time" will be past end time,
+ // so there will be no significant performance hit.
+ if ( ntime < end_time || time < end_time )
+ {
+ // Since amplitude was updated above, delta will always be +/- volume,
+ // so we can avoid using last_amp every time to calculate the delta.
+ int delta = amp * 2 - volume;
+ int delta_non_zero = delta != 0;
+ int phase = osc->phase | (osc_mode & tone_off); assert( tone_off == 0x01 );
+ do
+ {
+ // run noise
+ blip_time_t end = end_time;
+ if ( end_time > time ) end = time;
+ if ( phase & delta_non_zero )
+ {
+ while ( ntime <= end ) // must advance *past* time to avoid hang
+ {
+ int changed = noise_lfsr + 1;
+ noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1);
+ if ( changed & 2 )
+ {
+ delta = -delta;
+ synth_.offset( ntime, delta, osc_output );
+ }
+ ntime += noise_period;
+ }
+ }
+ else
+ {
+ // 20 or more noise periods on average for some music
+ int remain = end - ntime;
+ int count = remain / noise_period;
+ if ( remain >= 0 )
+ ntime += noise_period + count * noise_period;
+ }
+
+ // run tone
+ end = end_time;
+ if ( end_time > ntime ) end = ntime;
+ if ( noise_lfsr & delta_non_zero )
+ {
+ while ( time < end )
+ {
+ delta = -delta;
+ synth_.offset( time, delta, osc_output );
+ time += period;
+
+ // alternate (less-efficient) implementation
+ //phase ^= 1;
+ }
+ phase = unsigned (-delta) >> (CHAR_BIT * sizeof (unsigned) - 1);
+ check( phase == (delta > 0) );
+ }
+ else
+ {
+ // loop usually runs less than once
+ //SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period );
+
+ while ( time < end )
+ {
+ time += period;
+ phase ^= 1;
+ }
+ }
+ }
+ while ( time < end_time || ntime < end_time );
+
+ osc->last_amp = (delta + volume) >> 1;
+ if ( !(osc_mode & tone_off) )
+ osc->phase = phase;
+ }
+
+ if ( end_time >= final_end_time )
+ break; // breaks first time when envelope is disabled
+
+ // next envelope step
+ if ( ++osc_env_pos >= 0 )
+ osc_env_pos -= 32;
+ volume = env_wave [osc_env_pos] >> half_vol;
+
+ start_time = end_time;
+ end_time += env_period;
+ if ( end_time > final_end_time )
+ end_time = final_end_time;
+ }
+ osc->delay = time - final_end_time;
+
+ if ( !(osc_mode & noise_off) )
+ {
+ noise_delay = ntime - final_end_time;
+ this->noise_lfsr = noise_lfsr;
+ }
+ }
+
+ // TODO: optimized saw wave envelope?
+
+ // maintain envelope phase
+ blip_time_t remain = final_end_time - last_time - env_delay;
+ if ( remain >= 0 )
+ {
+ int count = (remain + env_period) / env_period;
+ env_pos += count;
+ if ( env_pos >= 0 )
+ env_pos = (env_pos & 31) - 32;
+ remain -= count * env_period;
+ assert( -remain <= env_period );
+ }
+ env_delay = -remain;
+ assert( env_delay > 0 );
+ assert( env_pos < 0 );
+
+ last_time = final_end_time;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.h
new file mode 100644
index 00000000..bafdea47
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Apu.h
@@ -0,0 +1,104 @@
+// AY-3-8910 sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef AY_APU_H
+#define AY_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Ay_Apu {
+public:
+// Basics
+
+ // Sets buffer to generate sound into, or 0 to mute.
+ void set_output( Blip_Buffer* );
+
+ // Writes to address register
+ void write_addr( int data ) { addr_ = data & 0x0F; }
+
+ // Emulates to time t, then writes to current data register
+ void write_data( blip_time_t t, int data ) { run_until( t ); write_data_( addr_, data ); }
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Reads from current data register
+ int read();
+
+ // Resets sound chip
+ void reset();
+
+ // Number of registers
+ enum { reg_count = 16 };
+
+ // Same as set_output(), but for a particular channel
+ enum { osc_count = 3 };
+ void set_output( int chan, Blip_Buffer* );
+
+ // Sets overall volume, where 1.0 is normal
+ void volume( double v ) { synth_.volume( 0.7/osc_count/amp_range * v ); }
+
+ // Sets treble equalization
+ void treble_eq( blip_eq_t const& eq ) { synth_.treble_eq( eq ); }
+
+private:
+ // noncopyable
+ Ay_Apu( const Ay_Apu& );
+ Ay_Apu& operator = ( const Ay_Apu& );
+
+// Implementation
+public:
+ Ay_Apu();
+ BLARGG_DISABLE_NOTHROW
+ typedef BOOST::uint8_t byte;
+
+private:
+ struct osc_t
+ {
+ blip_time_t period;
+ blip_time_t delay;
+ short last_amp;
+ short phase;
+ Blip_Buffer* output;
+ } oscs [osc_count];
+
+ blip_time_t last_time;
+ byte addr_;
+ byte regs [reg_count];
+
+ blip_time_t noise_delay;
+ unsigned noise_lfsr;
+
+ blip_time_t env_delay;
+ byte const* env_wave;
+ int env_pos;
+ byte env_modes [8] [48]; // values already passed through volume table
+
+ void write_data_( int addr, int data );
+ void run_until( blip_time_t );
+
+public:
+ enum { amp_range = 255 };
+ Blip_Synth_Norm synth_; // used by Ay_Core for beeper sound
+};
+
+inline void Ay_Apu::set_output( int i, Blip_Buffer* out )
+{
+ assert( (unsigned) i < osc_count );
+ oscs [i].output = out;
+}
+
+inline void Ay_Apu::end_frame( blip_time_t time )
+{
+ if ( time > last_time )
+ run_until( time );
+
+ last_time -= time;
+ assert( last_time >= 0 );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp
new file mode 100644
index 00000000..4c56f56a
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.cpp
@@ -0,0 +1,190 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Ay_Core.h"
+
+/* Copyright (C) 2006-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+inline void Ay_Core::disable_beeper()
+{
+ beeper_mask = 0;
+ last_beeper = 0;
+}
+
+Ay_Core::Ay_Core()
+{
+ beeper_output = NULL;
+ disable_beeper();
+}
+
+Ay_Core::~Ay_Core() { }
+
+void Ay_Core::set_beeper_output( Blip_Buffer* b )
+{
+ beeper_output = b;
+ if ( b && !cpc_mode )
+ beeper_mask = 0x10;
+ else
+ disable_beeper();
+}
+
+void Ay_Core::start_track( registers_t const& r, addr_t play )
+{
+ play_addr = play;
+
+ memset( mem_.padding1, 0xFF, sizeof mem_.padding1 );
+
+ int const mirrored = 0x80; // this much is mirrored after end of memory
+ memset( mem_.ram + mem_size + mirrored, 0xFF, sizeof mem_.ram - mem_size - mirrored );
+ memcpy( mem_.ram + mem_size, mem_.ram, mirrored ); // some code wraps around (ugh)
+
+ cpu.reset( mem_.padding1, mem_.padding1 );
+ cpu.map_mem( 0, mem_size, mem_.ram, mem_.ram );
+ cpu.r = r;
+
+ beeper_delta = (int) (apu_.amp_range * 0.8);
+ last_beeper = 0;
+ next_play = play_period;
+ spectrum_mode = false;
+ cpc_mode = false;
+ cpc_latch = 0;
+ set_beeper_output( beeper_output );
+ apu_.reset();
+
+ // a few tunes rely on channels having tone enabled at the beginning
+ apu_.write_addr( 7 );
+ apu_.write_data( 0, 0x38 );
+
+}
+
+// Emulation
+
+void Ay_Core::cpu_out_( time_t time, addr_t addr, int data )
+{
+ // Spectrum
+ if ( !cpc_mode )
+ {
+ switch ( addr & 0xFEFF )
+ {
+ case 0xFEFD:
+ spectrum_mode = true;
+ apu_.write_addr( data );
+ return;
+
+ case 0xBEFD:
+ spectrum_mode = true;
+ apu_.write_data( time, data );
+ return;
+ }
+ }
+
+ // CPC
+ if ( !spectrum_mode )
+ {
+ switch ( addr >> 8 )
+ {
+ case 0xF6:
+ switch ( data & 0xC0 )
+ {
+ case 0xC0:
+ apu_.write_addr( cpc_latch );
+ goto enable_cpc;
+
+ case 0x80:
+ apu_.write_data( time, cpc_latch );
+ goto enable_cpc;
+ }
+ break;
+
+ case 0xF4:
+ cpc_latch = data;
+ goto enable_cpc;
+ }
+ }
+
+ dprintf( "Unmapped OUT: $%04X <- $%02X\n", addr, data );
+ return;
+
+enable_cpc:
+ if ( !cpc_mode )
+ {
+ cpc_mode = true;
+ disable_beeper();
+ set_cpc_callback.f( set_cpc_callback.data );
+ }
+}
+
+int Ay_Core::cpu_in( addr_t addr )
+{
+ // keyboard read and other things
+ if ( (addr & 0xFF) == 0xFE )
+ return 0xFF; // other values break some beeper tunes
+
+ dprintf( "Unmapped IN : $%04X\n", addr );
+ return 0xFF;
+}
+
+void Ay_Core::end_frame( time_t* end )
+{
+ cpu.set_time( 0 );
+
+ // Since detection of CPC mode will halve clock rate during the frame
+ // and thus generate up to twice as much sound, we must generate half
+ // as much until mode is known.
+ if ( !(spectrum_mode | cpc_mode) )
+ *end /= 2;
+
+ while ( cpu.time() < *end )
+ {
+ run_cpu( min( *end, next_play ) );
+
+ if ( cpu.time() >= next_play )
+ {
+ // next frame
+ next_play += play_period;
+
+ if ( cpu.r.iff1 )
+ {
+ // interrupt enabled
+
+ if ( mem_.ram [cpu.r.pc] == 0x76 )
+ cpu.r.pc++; // advance past HALT instruction
+
+ cpu.r.iff1 = 0;
+ cpu.r.iff2 = 0;
+
+ mem_.ram [--cpu.r.sp] = byte (cpu.r.pc >> 8);
+ mem_.ram [--cpu.r.sp] = byte (cpu.r.pc);
+
+ // fixed interrupt
+ cpu.r.pc = 0x38;
+ cpu.adjust_time( 12 );
+
+ if ( cpu.r.im == 2 )
+ {
+ // vectored interrupt
+ addr_t addr = cpu.r.i * 0x100 + 0xFF;
+ cpu.r.pc = mem_.ram [(addr + 1) & 0xFFFF] * 0x100 + mem_.ram [addr];
+ cpu.adjust_time( 6 );
+ }
+ }
+ }
+ }
+
+ // End time frame
+ *end = cpu.time();
+ next_play -= *end;
+ check( next_play >= 0 );
+ cpu.adjust_time( -*end );
+ apu_.end_frame( *end );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.h
new file mode 100644
index 00000000..30230f66
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Core.h
@@ -0,0 +1,81 @@
+// Sinclair Spectrum AY music emulator core
+
+// Game_Music_Emu 0.6-pre
+#ifndef AY_CORE_H
+#define AY_CORE_H
+
+#include "Z80_Cpu.h"
+#include "Ay_Apu.h"
+
+class Ay_Core {
+public:
+
+ // Clock count
+ typedef int time_t;
+
+ // Sound chip access, to assign it to Blip_Buffer etc.
+ Ay_Apu& apu() { return apu_; }
+
+ // Sets beeper sound buffer, or NULL to mute it. Volume and treble EQ of
+ // beeper are set by APU.
+ void set_beeper_output( Blip_Buffer* );
+
+ // Sets time between calls to play routine. Can be changed while playing.
+ void set_play_period( time_t p ) { play_period = p; }
+
+ // 64K memory to load code and data into before starting track. Caller
+ // must parse the AY file.
+ BOOST::uint8_t* mem() { return mem_.ram; }
+ enum { mem_size = 0x10000 };
+ enum { ram_addr = 0x4000 }; // where official RAM starts
+
+ // Starts track using specified register values, and sets play routine that
+ // is called periodically
+ typedef Z80_Cpu::registers_t registers_t;
+ typedef int addr_t;
+ void start_track( registers_t const&, addr_t play );
+
+ // Ends time frame of at most *end clocks and sets *end to number of clocks
+ // emulated. Until Spectrum/CPC mode is determined, *end is HALVED.
+ void end_frame( time_t* end );
+
+ // Called when CPC hardware is first accessed. AY file format doesn't specify
+ // which sound hardware is used, so it must be determined during playback
+ // based on which sound port is first used.
+ blargg_callback<void (*)( void* )> set_cpc_callback;
+
+// Implementation
+public:
+ Ay_Core();
+ ~Ay_Core();
+
+private:
+ Blip_Buffer* beeper_output;
+ int beeper_delta;
+ int last_beeper;
+ int beeper_mask;
+
+ addr_t play_addr;
+ time_t play_period;
+ time_t next_play;
+
+ int cpc_latch;
+ bool spectrum_mode;
+ bool cpc_mode;
+
+ // large items
+ Z80_Cpu cpu;
+ struct {
+ BOOST::uint8_t padding1 [0x100];
+ BOOST::uint8_t ram [mem_size + 0x100];
+ } mem_;
+ Ay_Apu apu_;
+
+ int cpu_in( addr_t );
+ void cpu_out( time_t, addr_t, int data );
+ void cpu_out_( time_t, addr_t, int data );
+ bool run_cpu( time_t end );
+ void disable_beeper();
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Cpu.cpp
new file mode 100644
index 00000000..f0b66b62
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Cpu.cpp
@@ -0,0 +1,59 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Ay_Core.h"
+
+#include "blargg_endian.h"
+//#include "z80_cpu_log.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void Ay_Core::cpu_out( time_t time, addr_t addr, int data )
+{
+ if ( (addr & 0xFF) == 0xFE )
+ {
+ check( !cpc_mode );
+ spectrum_mode = !cpc_mode;
+
+ // beeper_mask and last_beeper are 0 if (cpc_mode || !beeper_output)
+ if ( (data &= beeper_mask) != last_beeper )
+ {
+ last_beeper = data;
+ int delta = -beeper_delta;
+ beeper_delta = delta;
+ Blip_Buffer* bb = beeper_output;
+ bb->set_modified();
+ apu_.synth_.offset( time, delta, bb );
+ }
+ }
+ else
+ {
+ cpu_out_( time, addr, data );
+ }
+}
+
+#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data )
+#define IN_PORT( addr ) cpu_in( addr )
+#define FLAT_MEM mem
+#define CPU cpu
+
+#define CPU_BEGIN \
+bool Ay_Core::run_cpu( time_t end_time ) \
+{\
+ cpu.set_end_time( end_time );\
+ byte* const mem = mem_.ram; // cache
+
+ #include "Z80_Cpu_run.h"
+
+ return warning;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.cpp
new file mode 100644
index 00000000..fafbc4ce
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.cpp
@@ -0,0 +1,306 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Ay_Emu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// TODO: probably don't need detailed errors as to why file is corrupt
+
+int const spectrum_clock = 3546900; // 128K Spectrum
+int const spectrum_period = 70908;
+
+//int const spectrum_clock = 3500000; // 48K Spectrum
+//int const spectrum_period = 69888;
+
+int const cpc_clock = 2000000;
+
+Ay_Emu::Ay_Emu()
+{
+ core.set_cpc_callback( enable_cpc_, this );
+ set_type( gme_ay_type );
+ set_silence_lookahead( 6 );
+}
+
+Ay_Emu::~Ay_Emu() { }
+
+// Track info
+
+// Given pointer to 2-byte offset of data, returns pointer to data, or NULL if
+// offset is 0 or there is less than min_size bytes of data available.
+static byte const* get_data( Ay_Emu::file_t const& file, byte const ptr [], int min_size )
+{
+ int offset = (BOOST::int16_t) get_be16( ptr );
+ int pos = ptr - (byte const*) file.header;
+ int size = file.end - (byte const*) file.header;
+ assert( (unsigned) pos <= (unsigned) size - 2 );
+ int limit = size - min_size;
+ if ( limit < 0 || !offset || (unsigned) (pos + offset) > (unsigned) limit )
+ return NULL;
+ return ptr + offset;
+}
+
+static blargg_err_t parse_header( byte const in [], int size, Ay_Emu::file_t* out )
+{
+ typedef Ay_Emu::header_t header_t;
+ if ( size < header_t::size )
+ return blargg_err_file_type;
+
+ out->header = (header_t const*) in;
+ out->end = in + size;
+ header_t const& h = *(header_t const*) in;
+ if ( memcmp( h.tag, "ZXAYEMUL", 8 ) )
+ return blargg_err_file_type;
+
+ out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 );
+ if ( !out->tracks )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "missing track data" );
+
+ return blargg_ok;
+}
+
+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 ) * (1000 / 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 blargg_ok;
+}
+
+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 [], int size )
+ {
+ RETURN_ERR( parse_header( begin, size, &file ) );
+ set_track_count( file.header->max_track + 1 );
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int track ) const
+ {
+ copy_ay_fields( file, out, track );
+ return blargg_ok;
+ }
+};
+
+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 [], int size )
+{
+ assert( offsetof (header_t,track_info [2]) == header_t::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" );
+
+ int const osc_count = Ay_Apu::osc_count + 1; // +1 for beeper
+
+ set_voice_count( osc_count );
+ core.apu().volume( gain() );
+
+ 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+1
+ };
+ set_voice_types( types );
+
+ return setup_buffer( spectrum_clock );
+}
+
+void Ay_Emu::update_eq( blip_eq_t const& eq )
+{
+ core.apu().treble_eq( eq );
+}
+
+void Ay_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer*, Blip_Buffer* )
+{
+ if ( i >= Ay_Apu::osc_count )
+ core.set_beeper_output( center );
+ else
+ core.apu().set_output( i, center );
+}
+
+void Ay_Emu::set_tempo_( double t )
+{
+ int p = spectrum_period;
+ if ( clock_rate() != spectrum_clock )
+ p = clock_rate() / 50;
+
+ core.set_play_period( blip_time_t (p / t) );
+}
+
+blargg_err_t Ay_Emu::start_track_( int track )
+{
+ RETURN_ERR( Classic_Emu::start_track_( track ) );
+
+ byte* const mem = core.mem();
+
+ memset( mem + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET
+ memset( mem + 0x0100, 0xFF, 0x4000 - 0x100 );
+ memset( mem + core.ram_addr, 0x00, core.mem_size - core.ram_addr );
+
+ // locate data blocks
+ byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 );
+ if ( !data )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" );
+
+ byte const* const more_data = get_data( file, data + 10, 6 );
+ if ( !more_data )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" );
+
+ byte const* blocks = get_data( file, data + 12, 8 );
+ if ( !blocks )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "file data missing" );
+
+ // initial addresses
+ unsigned addr = get_be16( blocks );
+ if ( !addr )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "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 > core.mem_size )
+ {
+ set_warning( "Bad data block size" );
+ len = core.mem_size - addr;
+ }
+ check( len );
+ byte const* in = get_data( file, blocks, 0 ); blocks += 2;
+ if ( len > (unsigned) (file.end - in) )
+ {
+ set_warning( "File data missing" );
+ len = file.end - in;
+ }
+ //dprintf( "addr: $%04X, len: $%04X\n", addr, len );
+ if ( addr < core.ram_addr && addr >= 0x400 ) // several tracks use low data
+ dprintf( "Block addr in ROM\n" );
+ memcpy( mem + addr, in, len );
+
+ if ( file.end - blocks < 8 )
+ {
+ set_warning( "File data missing" );
+ 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, passive, sizeof passive );
+ int const play_addr = get_be16( more_data + 4 );
+ if ( play_addr )
+ {
+ memcpy( mem, active, sizeof active );
+ mem [ 9] = play_addr;
+ mem [10] = play_addr >> 8;
+ }
+ mem [2] = init;
+ mem [3] = init >> 8;
+
+ mem [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET)
+
+ // start at spectrum speed
+ change_clock_rate( spectrum_clock );
+ set_tempo( tempo() );
+
+ Ay_Core::registers_t r = { };
+ 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;
+
+ core.start_track( r, play_addr );
+
+ return blargg_ok;
+}
+
+blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int )
+{
+ core.end_frame( &duration );
+ return blargg_ok;
+}
+
+inline void Ay_Emu::enable_cpc()
+{
+ change_clock_rate( cpc_clock );
+ set_tempo( tempo() );
+}
+
+void Ay_Emu::enable_cpc_( void* data )
+{
+ STATIC_CAST(Ay_Emu*,data)->enable_cpc();
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.h
new file mode 100644
index 00000000..440353b6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ay_Emu.h
@@ -0,0 +1,58 @@
+// Sinclair Spectrum AY music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef AY_EMU_H
+#define AY_EMU_H
+
+#include "Classic_Emu.h"
+#include "Ay_Core.h"
+
+class Ay_Emu : public Classic_Emu {
+public:
+ // AY file header
+ struct header_t
+ {
+ enum { size = 0x14 };
+
+ byte tag [8];
+ byte vers;
+ byte player;
+ byte unused [2];
+ byte author [2];
+ byte comment [2];
+ byte max_track;
+ byte first_track;
+ byte track_info [2];
+ };
+
+ static gme_type_t static_type() { return gme_ay_type; }
+
+// Implementation
+public:
+ Ay_Emu();
+ ~Ay_Emu();
+
+ struct file_t {
+ header_t const* header;
+ byte const* tracks;
+ byte const* end; // end of file data
+ };
+
+protected:
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_mem_( byte const [], int );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+
+private:
+ file_t file;
+ Ay_Core core;
+
+ void enable_cpc();
+ static void enable_cpc_( void* data );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.cpp
new file mode 100644
index 00000000..97166297
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.cpp
@@ -0,0 +1,509 @@
+// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
+
+#include "Blip_Buffer.h"
+
+#include <math.h>
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+//// Blip_Buffer
+
+Blip_Buffer::Blip_Buffer()
+{
+ factor_ = UINT_MAX/2 + 1;
+ buffer_ = NULL;
+ buffer_center_ = NULL;
+ buffer_size_ = 0;
+ sample_rate_ = 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
+ int i = -0x7FFFFFFE;
+ assert( (i >> 1) == -0x3FFFFFFF );
+
+ // casting truncates and sign-extends
+ i = 0x18000;
+ assert( (BOOST::int16_t) i == -0x8000 );
+ #endif
+
+ clear();
+}
+
+Blip_Buffer::~Blip_Buffer()
+{
+ free( buffer_ );
+}
+
+void Blip_Buffer::clear()
+{
+ bool const entire_buffer = true;
+
+ offset_ = 0;
+ reader_accum_ = 0;
+ modified_ = false;
+
+ if ( buffer_ )
+ {
+ int count = (entire_buffer ? buffer_size_ : samples_avail());
+ memset( buffer_, 0, (count + blip_buffer_extra_) * sizeof (delta_t) );
+ }
+}
+
+blargg_err_t Blip_Buffer::set_sample_rate( int new_rate, int msec )
+{
+ // Limit to maximum size that resampled time can represent
+ int max_size = (((blip_resampled_time_t) -1) >> BLIP_BUFFER_ACCURACY) -
+ blip_buffer_extra_ - 64; // TODO: -64 isn't needed
+ int new_size = (new_rate * (msec + 1) + 999) / 1000;
+ if ( new_size > max_size )
+ new_size = max_size;
+
+ // Resize buffer
+ if ( buffer_size_ != new_size )
+ {
+ //dprintf( "%d \n", (new_size + blip_buffer_extra_) * sizeof *buffer_ );
+ void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ );
+ CHECK_ALLOC( p );
+ buffer_ = (delta_t*) p;
+ buffer_center_ = buffer_ + BLIP_MAX_QUALITY/2;
+ buffer_size_ = new_size;
+ }
+
+ // Update sample_rate and things that depend on it
+ sample_rate_ = new_rate;
+ length_ = new_size * 1000 / new_rate - 1;
+ if ( clock_rate_ )
+ clock_rate( clock_rate_ );
+ bass_freq( bass_freq_ );
+
+ clear();
+
+ return blargg_ok;
+}
+
+blip_resampled_time_t Blip_Buffer::clock_rate_factor( int rate ) const
+{
+ double ratio = (double) sample_rate_ / rate;
+ int factor = (int) floor( ratio * (1 << 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 && sample_rate_ )
+ {
+ shift = 13;
+ int f = (freq << 16) / sample_rate_;
+ while ( (f >>= 1) != 0 && --shift ) { }
+ }
+ bass_shift_ = shift;
+}
+
+void Blip_Buffer::end_frame( blip_time_t t )
+{
+ offset_ += t * factor_;
+ assert( samples_avail() <= (int) buffer_size_ ); // fails if time is past end of buffer
+}
+
+int Blip_Buffer::count_samples( blip_time_t t ) const
+{
+ blip_resampled_time_t last_sample = resampled_time( t ) >> BLIP_BUFFER_ACCURACY;
+ blip_resampled_time_t first_sample = offset_ >> BLIP_BUFFER_ACCURACY;
+ return (int) (last_sample - first_sample);
+}
+
+blip_time_t Blip_Buffer::count_clocks( int count ) const
+{
+ 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( int count )
+{
+ if ( count )
+ {
+ remove_silence( count );
+
+ // copy remaining samples to beginning and clear old samples
+ int remain = samples_avail() + blip_buffer_extra_;
+ memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
+ memset( buffer_ + remain, 0, count * sizeof *buffer_ );
+ }
+}
+
+int Blip_Buffer::read_samples( blip_sample_t out_ [], int max_samples, bool stereo )
+{
+ int count = samples_avail();
+ if ( count > max_samples )
+ count = max_samples;
+
+ if ( count )
+ {
+ int const bass = highpass_shift();
+ delta_t const* reader = read_pos() + count;
+ int reader_sum = integrator();
+
+ blip_sample_t* BLARGG_RESTRICT out = out_ + count;
+ if ( stereo )
+ out += count;
+ int offset = -count;
+
+ if ( !stereo )
+ {
+ do
+ {
+ int s = reader_sum >> delta_bits;
+
+ reader_sum -= reader_sum >> bass;
+ reader_sum += reader [offset];
+
+ BLIP_CLAMP( s, s );
+ out [offset] = (blip_sample_t) s;
+ }
+ while ( ++offset );
+ }
+ else
+ {
+ do
+ {
+ int s = reader_sum >> delta_bits;
+
+ reader_sum -= reader_sum >> bass;
+ reader_sum += reader [offset];
+
+ BLIP_CLAMP( s, s );
+ out [offset * 2] = (blip_sample_t) s;
+ }
+ while ( ++offset );
+ }
+
+ set_integrator( reader_sum );
+
+ remove_samples( count );
+ }
+ return count;
+}
+
+void Blip_Buffer::mix_samples( blip_sample_t const in [], int count )
+{
+ delta_t* out = buffer_center_ + (offset_ >> BLIP_BUFFER_ACCURACY);
+
+ int const sample_shift = blip_sample_bits - 16;
+ int prev = 0;
+ while ( --count >= 0 )
+ {
+ int s = *in++ << sample_shift;
+ *out += s - prev;
+ prev = s;
+ ++out;
+ }
+ *out -= prev;
+}
+
+void Blip_Buffer::save_state( blip_buffer_state_t* out )
+{
+ assert( samples_avail() == 0 );
+ out->offset_ = offset_;
+ out->reader_accum_ = reader_accum_;
+ memcpy( out->buf, &buffer_ [offset_ >> BLIP_BUFFER_ACCURACY], sizeof out->buf );
+}
+
+void Blip_Buffer::load_state( blip_buffer_state_t const& in )
+{
+ clear();
+
+ offset_ = in.offset_;
+ reader_accum_ = in.reader_accum_;
+ memcpy( buffer_, in.buf, sizeof in.buf );
+}
+
+
+//// Blip_Synth_
+
+Blip_Synth_Fast_::Blip_Synth_Fast_()
+{
+ buf = NULL;
+ last_amp = 0;
+ delta_factor = 0;
+}
+
+void Blip_Synth_Fast_::volume_unit( double new_unit )
+{
+ delta_factor = int (new_unit * (1 << blip_sample_bits) + 0.5);
+}
+
+#if BLIP_BUFFER_FAST
+
+void blip_eq_t::generate( float* out, int count ) const { }
+
+#else
+
+Blip_Synth_::Blip_Synth_( short p [], int w ) :
+ phases( p ),
+ width( w )
+{
+ volume_unit_ = 0.0;
+ kernel_unit = 0;
+ buf = NULL;
+ last_amp = 0;
+ delta_factor = 0;
+}
+
+#undef PI
+#define PI 3.1415926535897932384626433832795029
+
+// Generates right half of sinc kernel (including center point) with cutoff at
+// sample rate / 2 / oversample. Frequency response at cutoff frequency is
+// treble dB (-6=0.5,-12=0.25). Mid controls frequency that rolloff begins at,
+// cut * sample rate / 2.
+static void gen_sinc( float out [], int out_size, double oversample,
+ double treble, double mid )
+{
+ if ( mid > 0.9999 ) mid = 0.9999;
+ if ( treble < -300.0 ) treble = -300.0;
+ if ( treble > 5.0 ) treble = 5.0;
+
+ double const maxh = 4096.0;
+ double rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - mid) );
+ double const pow_a_n = pow( rolloff, maxh - maxh * mid );
+ double const to_angle = PI / maxh / oversample;
+ for ( int i = 1; i < out_size; i++ )
+ {
+ double angle = i * to_angle;
+ double c = rolloff * cos( angle * maxh - angle ) -
+ cos( angle * maxh );
+ double cos_nc_angle = cos( angle * maxh * mid );
+ double cos_nc1_angle = cos( angle * maxh * mid - 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
+ }
+
+ // Approximate center by looking at two points to right. Much simpler
+ // and more reliable than trying to calculate it properly.
+ out [0] = out [1] + 0.5 * (out [1] - out [2]);
+}
+
+// Gain is 1-2800 for beta of 0-10, instead of 1.0 as it should be, but
+// this is corrected by normalization in treble_eq().
+static void kaiser_window( float io [], int count, float beta )
+{
+ int const accuracy = 10;
+
+ float const beta2 = beta * beta;
+ float const step = (float) 0.5 / count;
+ float pos = (float) 0.5;
+ for ( float* const end = io + count; io < end; ++io )
+ {
+ float x = (pos - pos*pos) * beta2;
+ float u = x;
+ float k = 1;
+ float n = 2;
+
+ // Keep refining until adjustment becomes small
+ do
+ {
+ u *= x / (n * n);
+ n += 1;
+ k += u;
+ }
+ while ( k <= u * (1 << accuracy) );
+
+ pos += step;
+ *io *= k;
+ }
+}
+
+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 cutoff_adj = blip_res * 2.25 / count + 0.85;
+ if ( cutoff_adj < 1.02 )
+ cutoff_adj = 1.02;
+ double half_rate = sample_rate * 0.5;
+ if ( cutoff_freq )
+ cutoff_adj = half_rate / cutoff_freq;
+ double cutoff = rolloff_freq * cutoff_adj / half_rate;
+
+ gen_sinc( out, count, oversample * cutoff_adj, treble, cutoff );
+
+ kaiser_window( out, count, kaiser );
+}
+
+void Blip_Synth_::treble_eq( blip_eq_t const& eq )
+{
+ // Generate right half of kernel
+ int const half_size = blip_eq_t::calc_count( width );
+ float fimpulse [blip_res / 2 * (BLIP_MAX_QUALITY - 1) + 1];
+ eq.generate( fimpulse, half_size );
+
+ int i;
+
+ // Find rescale factor. Summing from small to large (right to left)
+ // reduces error.
+ double total = 0.0;
+ for ( i = half_size; --i > 0; )
+ total += fimpulse [i];
+ total = total * 2.0 + fimpulse [0];
+
+ //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 / total;
+ kernel_unit = (int) base_unit;
+
+ // Integrate, first difference, rescale, convert to int
+ double sum = 0;
+ double next = 0;
+ int const size = impulses_size();
+ for ( i = 0; i < size; i++ )
+ {
+ int j = (half_size - 1) - i;
+
+ if ( i >= blip_res )
+ sum += fimpulse [j + blip_res];
+
+ // goes slightly past center, so it needs a little mirroring
+ next += fimpulse [j < 0 ? -j : j];
+
+ // calculate unintereleved index
+ int x = (~i & (blip_res - 1)) * (width >> 1) + (i >> BLIP_PHASE_BITS);
+ assert( (unsigned) x < (unsigned) size );
+
+ // flooring separately virtually eliminates error
+ phases [x] = (short) (int)
+ (floor( sum * rescale + 0.5 ) - floor( next * rescale + 0.5 ));
+ //phases [x] = (short) (int)
+ // floor( sum * rescale - next * rescale + 0.5 );
+ }
+
+ adjust_impulse();
+
+ // volume might require rescaling
+ double vol = volume_unit_;
+ if ( vol )
+ {
+ volume_unit_ = 0.0;
+ volume_unit( vol );
+ }
+}
+
+void Blip_Synth_::adjust_impulse()
+{
+ int const size = impulses_size();
+ int const half_width = width / 2;
+
+ // Sum each phase as would be done when synthesizing, and correct
+ // any that don't add up to exactly kernel_half.
+ for ( int phase = blip_res / 2; --phase >= 0; )
+ {
+ int const fwd = phase * half_width;
+ int const rev = size - half_width - fwd;
+
+ int error = kernel_unit;
+ for ( int i = half_width; --i >= 0; )
+ {
+ error += phases [fwd + i];
+ error += phases [rev + i];
+ }
+ phases [fwd + half_width - 1] -= (short) error;
+
+ // Error shouldn't occur now with improved calculation
+ //if ( error ) printf( "error: %ld\n", error );
+ }
+
+ #if 0
+ for ( int i = 0; i < blip_res; i++, printf( "\n" ) )
+ for ( int j = 0; j < width / 2; j++ )
+ printf( "%5d,", (int) -phases [j + width/2 * i] );
+ #endif
+}
+
+void Blip_Synth_::rescale_kernel( int shift )
+{
+ // Keep values positive to avoid round-towards-zero of sign-preserving
+ // right shift for negative values.
+ int const keep_positive = 0x8000 + (1 << (shift - 1));
+
+ int const half_width = width / 2;
+ for ( int phase = blip_res; --phase >= 0; )
+ {
+ int const fwd = phase * half_width;
+
+ // Integrate, rescale, then differentiate again.
+ // If differences are rescaled directly, more error results.
+ int sum = keep_positive;
+ for ( int i = 0; i < half_width; i++ )
+ {
+ int prev = sum;
+ sum += phases [fwd + i];
+ phases [fwd + i] = (sum >> shift) - (prev >> shift);
+ }
+ }
+
+ adjust_impulse();
+}
+
+void Blip_Synth_::volume_unit( double new_unit )
+{
+ if ( volume_unit_ != new_unit )
+ {
+ // use default eq if it hasn't been set yet
+ if ( !kernel_unit )
+ treble_eq( -8.0 );
+
+ // Factor that kernel must be multiplied by
+ volume_unit_ = new_unit;
+ double factor = new_unit * (1 << blip_sample_bits) / kernel_unit;
+
+ if ( factor > 0.0 )
+ {
+ // If factor is low, reduce amplitude of kernel itself
+ int shift = 0;
+ while ( factor < 2.0 )
+ {
+ shift++;
+ factor *= 2.0;
+ }
+
+ if ( shift )
+ {
+ kernel_unit >>= shift;
+ assert( kernel_unit > 0 ); // fails if volume unit is too low
+
+ rescale_kernel( shift );
+ }
+ }
+
+ delta_factor = -(int) floor( factor + 0.5 );
+ //printf( "delta_factor: %d, kernel_unit: %d\n", delta_factor, kernel_unit );
+ }
+}
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.h b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.h
new file mode 100644
index 00000000..5208c816
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer.h
@@ -0,0 +1,198 @@
+// Band-limited sound synthesis buffer
+
+// Blip_Buffer 0.4.0
+#ifndef BLIP_BUFFER_H
+#define BLIP_BUFFER_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer_impl.h"
+
+typedef int blip_time_t; // Source clocks in current time frame
+typedef BOOST::int16_t blip_sample_t; // 16-bit signed output sample
+int const blip_default_length = 1000 / 4; // Default Blip_Buffer length (1/4 second)
+
+
+//// Sample buffer for band-limited synthesis
+
+class Blip_Buffer : public Blip_Buffer_ {
+public:
+
+ // Sets output sample rate and resizes and clears sample buffer
+ blargg_err_t set_sample_rate( int samples_per_sec, int msec_length = blip_default_length );
+
+ // Sets number of source time units per second
+ void clock_rate( int clocks_per_sec );
+
+ // Clears buffer and removes all samples
+ void clear();
+
+ // Use Blip_Synth to add waveform to buffer
+
+ // Resamples to time t, then subtracts t from current time. Appends result of resampling
+ // to buffer for reading.
+ void end_frame( blip_time_t t );
+
+ // Number of samples available for reading with read_samples()
+ int samples_avail() const;
+
+ // Reads at most n samples to out [0 to n-1] and returns number actually read. If stereo
+ // is true, writes to out [0], out [2], out [4] etc. instead.
+ int read_samples( blip_sample_t out [], int n, bool stereo = false );
+
+// More features
+
+ // Sets flag that tells some Multi_Buffer types that sound was added to buffer,
+ // so they know that it needs to be mixed in. Only needs to be called once
+ // per time frame that sound was added. Not needed if not using Multi_Buffer.
+ void set_modified() { modified_ = true; }
+
+ // Sets high-pass filter frequency, from 0 to 20000 Hz, where higher values reduce bass more
+ void bass_freq( int frequency );
+
+ int length() const; // Length of buffer in milliseconds
+ int sample_rate() const; // Current output sample rate
+ int clock_rate() const; // Number of source time units per second
+ int output_latency() const; // Number of samples delay from offset() to read_samples()
+
+// Low-level features
+
+ // Removes the first n samples
+ void remove_samples( int n );
+
+ // Returns number of clocks needed until n samples will be available.
+ // If buffer cannot even hold n samples, returns number of clocks
+ // until buffer becomes full.
+ blip_time_t count_clocks( int n ) const;
+
+ // Number of samples that should be mixed before calling end_frame( t )
+ int count_samples( blip_time_t t ) const;
+
+ // Mixes n samples into buffer
+ void mix_samples( const blip_sample_t in [], int n );
+
+// Resampled time (sorry, poor documentation right now)
+
+ // Resampled time is fixed-point, in terms of output samples.
+
+ // Converts clock count to resampled time
+ blip_resampled_time_t resampled_duration( int t ) const { return t * factor_; }
+
+ // Converts clock time since beginning of current time frame to resampled time
+ blip_resampled_time_t resampled_time( blip_time_t t ) const { return t * factor_ + offset_; }
+
+ // Returns factor that converts clock rate to resampled time
+ blip_resampled_time_t clock_rate_factor( int clock_rate ) const;
+
+// State save/load
+
+ // Saves state, including high-pass filter and tails of last deltas.
+ // All samples must have been read from buffer before calling this
+ // (that is, samples_avail() must return 0).
+ void save_state( blip_buffer_state_t* out );
+
+ // Loads state. State must have been saved from Blip_Buffer with same
+ // settings during same run of program; states can NOT be stored on disk.
+ // Clears buffer before loading state.
+ void load_state( const blip_buffer_state_t& in );
+
+private:
+ // noncopyable
+ Blip_Buffer( const Blip_Buffer& );
+ Blip_Buffer& operator = ( const Blip_Buffer& );
+
+// Implementation
+public:
+ BLARGG_DISABLE_NOTHROW
+ Blip_Buffer();
+ ~Blip_Buffer();
+ void remove_silence( int n );
+};
+
+
+//// Adds amplitude changes to Blip_Buffer
+
+template<int quality,int range> class Blip_Synth;
+
+typedef Blip_Synth<8, 1> Blip_Synth_Fast; // faster, but less equalizer control
+typedef Blip_Synth<12,1> Blip_Synth_Norm; // good for most things
+typedef Blip_Synth<16,1> Blip_Synth_Good; // sharper filter cutoff
+
+template<int quality,int range>
+class Blip_Synth {
+public:
+
+ // Sets volume of amplitude delta unit
+ void volume( double v ) { impl.volume_unit( 1.0 / range * v ); }
+
+ // Configures low-pass filter
+ void treble_eq( const blip_eq_t& eq ) { impl.treble_eq( eq ); }
+
+ // Gets/sets default Blip_Buffer
+ Blip_Buffer* output() const { return impl.buf; }
+ void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; }
+
+ // Extends waveform to time t at current amplitude, then changes its amplitude to a
+ // Using this requires a separate Blip_Synth for each waveform.
+ void update( blip_time_t t, int a );
+
+// Low-level interface
+
+ // If no Blip_Buffer* is specified, uses one set by output() above
+
+ // Adds amplitude transition at time t. Delta can be positive or negative.
+ // The actual change in amplitude is delta * volume.
+ void offset( blip_time_t t, int delta, Blip_Buffer* ) const;
+ void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); }
+
+ // 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( buf->to_fixed( t ), delta, buf ); }
+ void offset_inline( blip_time_t t, int delta ) const { offset_resampled( impl.buf->to_fixed( t ), delta, impl.buf ); }
+
+ // Works directly in terms of fractional output samples. Use resampled time functions in Blip_Buffer
+ // to convert clock counts to resampled time.
+ void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
+
+// Implementation
+public:
+ BLARGG_DISABLE_NOTHROW
+
+private:
+#if BLIP_BUFFER_FAST
+ Blip_Synth_Fast_ impl;
+ typedef char coeff_t;
+#else
+ Blip_Synth_ impl;
+ typedef short coeff_t;
+ // Left halves of first difference of step response for each possible phase
+ coeff_t phases [quality / 2 * blip_res];
+public:
+ Blip_Synth() : impl( phases, quality ) { }
+#endif
+};
+
+
+//// Low-pass equalization parameters
+
+class blip_eq_t {
+ double treble, kaiser;
+ int rolloff_freq, sample_rate, cutoff_freq;
+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, int rolloff_freq, int sample_rate, int cutoff_freq = 0,
+ double kaiser = 5.2 );
+
+ // Generate center point and right half of impulse response
+ virtual void generate( float out [], int count ) const;
+ virtual ~blip_eq_t() { }
+
+ enum { oversample = blip_res };
+ static int calc_count( int quality ) { return (quality - 1) * (oversample / 2) + 1; }
+};
+
+#include "Blip_Buffer_impl2.h"
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl.h b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl.h
new file mode 100644
index 00000000..56af3783
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl.h
@@ -0,0 +1,135 @@
+// Internal stuff here to keep public header uncluttered
+
+// Blip_Buffer 0.4.0
+#ifndef BLIP_BUFFER_IMPL_H
+#define BLIP_BUFFER_IMPL_H
+
+typedef unsigned blip_resampled_time_t;
+
+#ifndef BLIP_MAX_QUALITY
+ #define BLIP_MAX_QUALITY 32
+#endif
+
+#ifndef BLIP_BUFFER_ACCURACY
+ #define BLIP_BUFFER_ACCURACY 16
+#endif
+
+#ifndef BLIP_PHASE_BITS
+ #define BLIP_PHASE_BITS 6
+#endif
+
+class blip_eq_t;
+class Blip_Buffer;
+
+#if BLIP_BUFFER_FAST
+ // linear interpolation needs 8 bits
+ #undef BLIP_PHASE_BITS
+ #define BLIP_PHASE_BITS 8
+
+ #undef BLIP_MAX_QUALITY
+ #define BLIP_MAX_QUALITY 2
+#endif
+
+int const blip_res = 1 << BLIP_PHASE_BITS;
+int const blip_buffer_extra_ = BLIP_MAX_QUALITY + 2;
+
+class Blip_Buffer_ {
+public:
+// Writer
+
+ typedef int clocks_t;
+
+ // Properties of fixed-point sample position
+ typedef unsigned fixed_t; // unsigned for more range, optimized shifts
+ enum { fixed_bits = BLIP_BUFFER_ACCURACY }; // bits in fraction
+ enum { fixed_unit = 1 << fixed_bits }; // 1.0 samples
+
+ // Converts clock count to fixed-point sample position
+ fixed_t to_fixed( clocks_t t ) const { return t * factor_ + offset_; }
+
+ // Deltas in buffer are fixed-point with this many fraction bits.
+ // Less than 16 for extra range.
+ enum { delta_bits = 14 };
+
+ // Pointer to first committed delta sample
+ typedef int delta_t;
+
+ // Pointer to delta corresponding to fixed-point sample position
+ delta_t* delta_at( fixed_t );
+
+// Reader
+
+ delta_t* read_pos() { return buffer_; }
+
+ void clear_modified() { modified_ = false; }
+ int highpass_shift() const { return bass_shift_; }
+ int integrator() const { return reader_accum_; }
+ void set_integrator( int n ) { reader_accum_ = n; }
+
+public: //friend class Tracked_Blip_Buffer; private:
+ bool modified() const { return modified_; }
+ void remove_silence( int count );
+
+private:
+ unsigned factor_;
+ fixed_t offset_;
+ delta_t* buffer_center_;
+ int buffer_size_;
+ int reader_accum_;
+ int bass_shift_;
+ delta_t* buffer_;
+ int sample_rate_;
+ int clock_rate_;
+ int bass_freq_;
+ int length_;
+ bool modified_;
+
+ friend class Blip_Buffer;
+};
+
+class Blip_Synth_Fast_ {
+public:
+ int delta_factor;
+ int last_amp;
+ Blip_Buffer* buf;
+
+ void volume_unit( double );
+ void treble_eq( blip_eq_t const& ) { }
+ Blip_Synth_Fast_();
+};
+
+class Blip_Synth_ {
+public:
+ int delta_factor;
+ int last_amp;
+ Blip_Buffer* buf;
+
+ void volume_unit( double );
+ void treble_eq( blip_eq_t const& );
+ Blip_Synth_( short phases [], int width );
+private:
+ double volume_unit_;
+ short* const phases;
+ int const width;
+ int kernel_unit;
+
+ void adjust_impulse();
+ void rescale_kernel( int shift );
+ int impulses_size() const { return blip_res / 2 * width; }
+};
+
+class blip_buffer_state_t
+{
+ blip_resampled_time_t offset_;
+ int reader_accum_;
+ int buf [blip_buffer_extra_];
+ friend class Blip_Buffer;
+};
+
+inline Blip_Buffer_::delta_t* Blip_Buffer_::delta_at( fixed_t f )
+{
+ assert( (f >> fixed_bits) < (unsigned) buffer_size_ );
+ return buffer_center_ + (f >> fixed_bits);
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl2.h b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl2.h
new file mode 100644
index 00000000..5d2b04b0
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Blip_Buffer_impl2.h
@@ -0,0 +1,282 @@
+// Internal stuff here to keep public header uncluttered
+
+// Blip_Buffer 0.4.0
+#ifndef BLIP_BUFFER_IMPL2_H
+#define BLIP_BUFFER_IMPL2_H
+
+//// Compatibility
+
+BLARGG_DEPRECATED( int const blip_low_quality = 8; )
+BLARGG_DEPRECATED( int const blip_med_quality = 8; )
+BLARGG_DEPRECATED( int const blip_good_quality = 12; )
+BLARGG_DEPRECATED( int const blip_high_quality = 16; )
+
+BLARGG_DEPRECATED( int const blip_sample_max = 32767; )
+
+// Number of bits in raw sample that covers normal output range. Less than 32 bits to give
+// extra amplitude range. That is,
+// +1 << (blip_sample_bits-1) = +1.0
+// -1 << (blip_sample_bits-1) = -1.0
+int const blip_sample_bits = 30;
+
+//// BLIP_READER_
+
+//// Optimized reading from Blip_Buffer, for use in custom sample buffer or mixer
+
+// Begins reading from buffer. Name should be unique to the current {} block.
+#define BLIP_READER_BEGIN( name, blip_buffer ) \
+ const Blip_Buffer::delta_t* BLARGG_RESTRICT name##_reader_buf = (blip_buffer).read_pos();\
+ int name##_reader_accum = (blip_buffer).integrator()
+
+// Gets value to pass to BLIP_READER_NEXT()
+#define BLIP_READER_BASS( blip_buffer ) (blip_buffer).highpass_shift()
+
+// Constant value to use instead of BLIP_READER_BASS(), for slightly more optimal
+// code at the cost of having no bass_freq() functionality
+int const blip_reader_default_bass = 9;
+
+// Current sample as 16-bit signed value
+#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)
+
+// Advances to next sample
+#define BLIP_READER_NEXT( name, bass ) \
+ (void) (name##_reader_accum += *name##_reader_buf++ - (name##_reader_accum >> (bass)))
+
+// Ends 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).set_integrator( name##_reader_accum ))
+
+#define BLIP_READER_ADJ_( name, offset ) (name##_reader_buf += offset)
+
+int const blip_reader_idx_factor = sizeof (Blip_Buffer::delta_t);
+
+#define BLIP_READER_NEXT_IDX_( name, bass, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> (bass);\
+ name##_reader_accum += name##_reader_buf [(idx)];\
+}
+
+#define BLIP_READER_NEXT_RAW_IDX_( name, bass, idx ) {\
+ name##_reader_accum -= name##_reader_accum >> (bass);\
+ name##_reader_accum +=\
+ *(Blip_Buffer::delta_t const*) ((char const*) name##_reader_buf + (idx));\
+}
+
+//// BLIP_CLAMP
+
+#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
+ defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
+ #define BLIP_X86 1
+ #define BLIP_CLAMP_( in ) in < -0x8000 || 0x7FFF < in
+#else
+ #define BLIP_CLAMP_( in ) (blip_sample_t) in != in
+#endif
+
+// Clamp sample to blip_sample_t range
+#define BLIP_CLAMP( sample, out )\
+ { if ( BLIP_CLAMP_( (sample) ) ) (out) = ((sample) >> 31) ^ 0x7FFF; }
+
+
+//// Blip_Synth
+
+// (in >> sh & mask) * mul
+#define BLIP_SH_AND_MUL( in, sh, mask, mul ) \
+((int) (in) / ((1U << (sh)) / (mul)) & (unsigned) ((mask) * (mul)))
+
+// (T*) ptr + (off >> sh)
+#define BLIP_PTR_OFF_SH( T, ptr, off, sh ) \
+ ((T*) (BLIP_SH_AND_MUL( off, sh, -1, sizeof (T) ) + (char*) (ptr)))
+
+template<int quality,int range>
+inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t time,
+ int delta, Blip_Buffer* blip_buf ) const
+{
+#if BLIP_BUFFER_FAST
+ int const half_width = 1;
+#else
+ int const half_width = quality / 2;
+#endif
+
+ Blip_Buffer::delta_t* BLARGG_RESTRICT buf = blip_buf->delta_at( time );
+
+ delta *= impl.delta_factor;
+
+ int const phase_shift = BLIP_BUFFER_ACCURACY - BLIP_PHASE_BITS;
+ int const phase = (half_width & (half_width - 1)) ?
+ (int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) ) * half_width :
+ (int) BLIP_SH_AND_MUL( time, phase_shift, blip_res - 1, sizeof (coeff_t) * half_width );
+
+#if BLIP_BUFFER_FAST
+ int 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.
+ int right = (delta >> BLIP_PHASE_BITS) * phase;
+ #if BLIP_BUFFER_NOINTERP
+ // TODO: remove? (just a hack to see how it sounds)
+ right = 0;
+ #endif
+ left -= right;
+ right += buf [1];
+
+ buf [0] = left;
+ buf [1] = right;
+#else
+
+ int const fwd = -quality / 2;
+ int const rev = fwd + quality - 2;
+
+ coeff_t const* BLARGG_RESTRICT imp = (coeff_t const*) ((char const*) phases + phase);
+ int const phase2 = phase + phase - (blip_res - 1) * half_width * sizeof (coeff_t);
+
+ #define BLIP_MID_IMP imp = (coeff_t const*) ((char const*) imp - phase2);
+
+ #if BLIP_MAX_QUALITY > 16
+ // General version for any quality
+ if ( quality != 8 && quality != 12 && quality != 16 )
+ {
+ buf += fwd;
+
+ // left half
+ for ( int n = half_width / 2; --n >= 0; )
+ {
+ buf [0] += imp [0] * delta;
+ buf [1] += imp [1] * delta;
+ imp += 2;
+ buf += 2;
+ }
+
+ // mirrored right half
+ BLIP_MID_IMP
+ for ( int n = half_width / 2; --n >= 0; )
+ {
+ buf [0] += imp [-1] * delta;
+ buf [1] += *(imp -= 2) * delta;
+ buf += 2;
+ }
+
+ return;
+ }
+ #endif
+
+ // Unrolled versions for qualities 8, 12, and 16
+
+ #if BLIP_X86
+ // This gives better code for x86
+ #define BLIP_ADD( out, in ) \
+ buf [out] += imp [in] * delta
+
+ #define BLIP_FWD( i ) {\
+ BLIP_ADD( fwd + i, i );\
+ BLIP_ADD( fwd + 1 + i, i + 1 );\
+ }
+
+ #define BLIP_REV( r ) {\
+ BLIP_ADD( rev - r, r + 1 );\
+ BLIP_ADD( rev + 1 - r, r );\
+ }
+
+ BLIP_FWD( 0 )
+ BLIP_FWD( 2 )
+ if ( quality > 8 ) BLIP_FWD( 4 )
+ if ( quality > 12 ) BLIP_FWD( 6 )
+ BLIP_MID_IMP
+ if ( quality > 12 ) BLIP_REV( 6 )
+ if ( quality > 8 ) BLIP_REV( 4 )
+ BLIP_REV( 2 )
+ BLIP_REV( 0 )
+
+ #else
+ // Help RISC processors and simplistic compilers by reading ahead of writes
+ #define BLIP_FWD( i ) {\
+ int t0 = i0 * delta + buf [fwd + i];\
+ int t1 = imp [i + 1] * delta + buf [fwd + 1 + i];\
+ i0 = imp [i + 2];\
+ buf [fwd + i] = t0;\
+ buf [fwd + 1 + i] = t1;\
+ }
+
+ #define BLIP_REV( r ) {\
+ int t0 = i0 * delta + buf [rev - r];\
+ int t1 = imp [r] * delta + buf [rev + 1 - r];\
+ i0 = imp [r - 1];\
+ buf [rev - r] = t0;\
+ buf [rev + 1 - r] = t1;\
+ }
+
+ int i0 = *imp;
+ BLIP_FWD( 0 )
+ if ( quality > 8 ) BLIP_FWD( 2 )
+ if ( quality > 12 ) BLIP_FWD( 4 )
+ {
+ int const mid = half_width - 1;
+ int t0 = i0 * delta + buf [fwd + mid - 1];
+ int t1 = imp [mid] * delta + buf [fwd + mid ];
+ BLIP_MID_IMP
+ i0 = imp [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 )
+
+ int t0 = i0 * delta + buf [rev ];
+ int t1 = *imp * delta + buf [rev + 1];
+ buf [rev ] = t0;
+ buf [rev + 1] = t1;
+ #endif
+
+#endif
+}
+
+template<int quality,int range>
+#if BLIP_BUFFER_FAST
+ inline
+#endif
+void Blip_Synth<quality,range>::offset( blip_time_t t, int delta, Blip_Buffer* buf ) const
+{
+ offset_resampled( buf->to_fixed( t ), delta, buf );
+}
+
+template<int quality,int range>
+#if BLIP_BUFFER_FAST
+ inline
+#endif
+void Blip_Synth<quality,range>::update( blip_time_t t, int amp )
+{
+ int delta = amp - impl.last_amp;
+ impl.last_amp = amp;
+ offset_resampled( impl.buf->to_fixed( t ), delta, impl.buf );
+}
+
+
+//// blip_eq_t
+
+inline blip_eq_t::blip_eq_t( double t ) :
+ treble( t ), kaiser( 5.2 ), rolloff_freq( 0 ), sample_rate( 44100 ), cutoff_freq( 0 ) { }
+inline blip_eq_t::blip_eq_t( double t, int rf, int sr, int cf, double k ) :
+ treble( t ), kaiser( k ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }
+
+
+//// Blip_Buffer
+
+inline int Blip_Buffer::length() const { return length_; }
+inline int Blip_Buffer::samples_avail() const { return (int) (offset_ >> BLIP_BUFFER_ACCURACY); }
+inline int Blip_Buffer::sample_rate() const { return sample_rate_; }
+inline int Blip_Buffer::output_latency() const { return BLIP_MAX_QUALITY / 2; }
+inline int Blip_Buffer::clock_rate() const { return clock_rate_; }
+inline void Blip_Buffer::clock_rate( int cps ) { factor_ = clock_rate_factor( clock_rate_ = cps ); }
+
+inline void Blip_Buffer::remove_silence( int count )
+{
+ // fails if you try to remove more samples than available
+ assert( count <= samples_avail() );
+ offset_ -= (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.cpp
new file mode 100644
index 00000000..229bb67d
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.cpp
@@ -0,0 +1,124 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Classic_Emu.h"
+
+#include "Multi_Buffer.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Classic_Emu::Classic_Emu()
+{
+ buf = NULL;
+ stereo_buffer = NULL;
+ voice_types = NULL;
+
+ // avoid inconsistency in our duplicated constants
+ assert( (int) wave_type == (int) Multi_Buffer::wave_type );
+ assert( (int) noise_type == (int) Multi_Buffer::noise_type );
+ assert( (int) mixed_type == (int) Multi_Buffer::mixed_type );
+}
+
+Classic_Emu::~Classic_Emu()
+{
+ delete stereo_buffer;
+ delete effects_buffer_;
+ effects_buffer_ = NULL;
+}
+
+void Classic_Emu::set_equalizer_( equalizer_t const& eq )
+{
+ Music_Emu::set_equalizer_( eq );
+ update_eq( eq.treble );
+ if ( buf )
+ buf->bass_freq( (int) equalizer().bass );
+}
+
+blargg_err_t Classic_Emu::set_sample_rate_( int rate )
+{
+ if ( !buf )
+ {
+ if ( !stereo_buffer )
+ CHECK_ALLOC( stereo_buffer = BLARGG_NEW Stereo_Buffer );
+ buf = stereo_buffer;
+ }
+ return buf->set_sample_rate( rate, 1000 / 20 );
+}
+
+void Classic_Emu::mute_voices_( int mask )
+{
+ Music_Emu::mute_voices_( mask );
+ for ( int i = voice_count(); i--; )
+ {
+ if ( mask & (1 << i) )
+ {
+ set_voice( i, NULL, NULL, NULL );
+ }
+ else
+ {
+ Multi_Buffer::channel_t ch = buf->channel( i );
+ assert( (ch.center && ch.left && ch.right) ||
+ (!ch.center && !ch.left && !ch.right) ); // all or nothing
+ set_voice( i, ch.center, ch.left, ch.right );
+ }
+ }
+}
+
+void Classic_Emu::change_clock_rate( int rate )
+{
+ clock_rate_ = rate;
+ buf->clock_rate( rate );
+}
+
+blargg_err_t Classic_Emu::setup_buffer( int rate )
+{
+ change_clock_rate( rate );
+ RETURN_ERR( buf->set_channel_count( voice_count(), voice_types ) );
+ set_equalizer( equalizer() );
+ buf_changed_count = buf->channels_changed_count();
+ return blargg_ok;
+}
+
+blargg_err_t Classic_Emu::start_track_( int track )
+{
+ RETURN_ERR( Music_Emu::start_track_( track ) );
+ buf->clear();
+ return blargg_ok;
+}
+
+blargg_err_t Classic_Emu::play_( int count, sample_t out [] )
+{
+ // read from buffer, then refill buffer and repeat if necessary
+ int remain = count;
+ while ( remain )
+ {
+ buf->disable_immediate_removal();
+ remain -= buf->read_samples( &out [count - remain], remain );
+ if ( remain )
+ {
+ if ( buf_changed_count != buf->channels_changed_count() )
+ {
+ buf_changed_count = buf->channels_changed_count();
+ remute_voices();
+ }
+
+ // TODO: use more accurate length calculation
+ int msec = buf->length();
+ blip_time_t clocks_emulated = msec * clock_rate_ / 1000 - 100;
+ RETURN_ERR( run_clocks( clocks_emulated, msec ) );
+ assert( clocks_emulated );
+ buf->end_frame( clocks_emulated );
+ }
+ }
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.h
new file mode 100644
index 00000000..b5760e75
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Classic_Emu.h
@@ -0,0 +1,79 @@
+// Common aspects of emulators which use Blip_Buffer for sound output
+
+// Game_Music_Emu 0.6-pre
+#ifndef CLASSIC_EMU_H
+#define CLASSIC_EMU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+#include "Music_Emu.h"
+
+class Classic_Emu : public Music_Emu {
+protected:
+// Derived interface
+
+ // Advertises type of sound on each voice, so Effects_Buffer can better choose
+ // what effect to apply (pan, echo, surround). Constant can have value added so
+ // that voices of the same type can be spread around the stereo sound space.
+ enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
+ void set_voice_types( int const types [] ) { voice_types = types; }
+
+ // Sets up Blip_Buffers after loading file
+ blargg_err_t setup_buffer( int clock_rate );
+
+ // Clock rate of Blip_buffers
+ int clock_rate() const { return clock_rate_; }
+
+ // Changes clock rate of Blip_Buffers (experimental)
+ void change_clock_rate( int );
+
+// Overrides should do the indicated task
+
+ // Set Blip_Buffer(s) voice outputs to, or mute voice if pointer is NULL
+ virtual void set_voice( int index, Blip_Buffer* center,
+ Blip_Buffer* left, Blip_Buffer* right ) BLARGG_PURE( ; )
+
+ // Update equalization
+ virtual void update_eq( blip_eq_t const& ) BLARGG_PURE( ; )
+
+ // Start track
+ virtual blargg_err_t start_track_( int track ) BLARGG_PURE( ; )
+
+ // Run for at most msec or time_io clocks, then set time_io to number of clocks
+ // actually run for. After returning, Blip_Buffers have time frame of time_io clocks
+ // ended.
+ virtual blargg_err_t run_clocks( blip_time_t& time_io, int msec ) BLARGG_PURE( ; )
+
+// Internal
+public:
+ Classic_Emu();
+ ~Classic_Emu();
+ virtual void set_buffer( Multi_Buffer* );
+
+protected:
+ virtual blargg_err_t set_sample_rate_( int sample_rate );
+ virtual void mute_voices_( int );
+ virtual void set_equalizer_( equalizer_t const& );
+ virtual blargg_err_t play_( int, sample_t [] );
+
+private:
+ Multi_Buffer* buf;
+ Multi_Buffer* stereo_buffer; // NULL if using custom buffer
+ int clock_rate_;
+ unsigned buf_changed_count;
+ int const* voice_types;
+};
+
+inline void Classic_Emu::set_buffer( Multi_Buffer* new_buf )
+{
+ assert( !buf && new_buf );
+ buf = new_buf;
+}
+
+inline void Classic_Emu::set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* ) { }
+
+inline void Classic_Emu::update_eq( blip_eq_t const& ) { }
+
+inline blargg_err_t Classic_Emu::run_clocks( blip_time_t&, int ) { return blargg_ok; }
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Data_Reader.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Data_Reader.cpp
new file mode 100644
index 00000000..5bbfbf55
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/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 <assert.h>
+#include <string.h>
+#include <stdio.h>
+
+/* 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.6pre/gme/Data_Reader.h b/plugins/gme/game-music-emu-0.6pre/gme/Data_Reader.h
new file mode 100644
index 00000000..acf571f6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/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, int 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.6pre/gme/Downsampler.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Downsampler.cpp
new file mode 100644
index 00000000..aeb50fc5
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Downsampler.cpp
@@ -0,0 +1,74 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Downsampler.h"
+
+/* Copyright (C) 2004-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const shift = 14;
+int const unit = 1 << shift;
+
+void Downsampler::clear_()
+{
+ pos = 0;
+ Resampler::clear_();
+}
+
+Downsampler::Downsampler()
+{
+ clear();
+}
+
+blargg_err_t Downsampler::set_rate_( double new_factor )
+{
+ step = (int) (new_factor * unit + 0.5);
+ return Resampler::set_rate_( 1.0 / unit * step );
+}
+
+Resampler::sample_t const* Downsampler::resample_( sample_t** out_,
+ sample_t const* out_end, sample_t const in [], int in_size )
+{
+ in_size -= write_offset;
+ if ( in_size > 0 )
+ {
+ sample_t* BLARGG_RESTRICT out = *out_;
+ sample_t const* const in_end = in + in_size;
+
+ int const step = this->step;
+ int pos = this->pos;
+
+ // TODO: IIR filter, then linear resample
+ // TODO: detect skipped sample, allowing merging of IIR and resample?
+
+ do
+ {
+ #define INTERP( i, out )\
+ out = (in [0 + i] * (unit - pos) + ((in [2 + i] + in [4 + i] + in [6 + i]) << shift) +\
+ in [8 + i] * pos) >> (shift + 2);
+
+ int out_0;
+ INTERP( 0, out_0 )
+ INTERP( 1, out [0] = out_0; out [1] )
+ out += stereo;
+
+ pos += step;
+ in += ((unsigned) pos >> shift) * stereo;
+ pos &= unit - 1;
+ }
+ while ( in < in_end && out < out_end );
+
+ this->pos = pos;
+ *out_ = out;
+ }
+ return in;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Downsampler.h b/plugins/gme/game-music-emu-0.6pre/gme/Downsampler.h
new file mode 100644
index 00000000..8ce85e27
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Downsampler.h
@@ -0,0 +1,25 @@
+// Linear downsampler with pre-low-pass
+
+// Game_Music_Emu 0.6-pre
+#ifndef DOWNSAMPLER_H
+#define DOWNSAMPLER_H
+
+#include "Resampler.h"
+
+class Downsampler : public Resampler {
+public:
+ Downsampler();
+
+protected:
+ virtual blargg_err_t set_rate_( double );
+ virtual void clear_();
+ virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int );
+
+private:
+ enum { stereo = 2 };
+ enum { write_offset = 8 * stereo };
+ int pos;
+ int step;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.cpp
new file mode 100644
index 00000000..152fbbd3
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.cpp
@@ -0,0 +1,200 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Dual_Resampler.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// TODO: fix this. hack since resampler holds back some output.
+int const resampler_extra = 34;
+
+int const stereo = 2;
+
+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_ERR( resampler.resize_buffer( resampler_size ) );
+ resampler.clear();
+ return blargg_ok;
+}
+
+void Dual_Resampler::resize( int pairs )
+{
+ int new_sample_buf_size = pairs * 2;
+ //new_sample_buf_size = new_sample_buf_size / 4 * 4; // TODO: needed only for 3:2 downsampler
+ if ( sample_buf_size != new_sample_buf_size )
+ {
+ if ( (unsigned) new_sample_buf_size > sample_buf.size() )
+ {
+ check( false );
+ return;
+ }
+ sample_buf_size = new_sample_buf_size;
+ oversamples_per_frame = int (pairs * resampler.rate()) * 2 + 2;
+ clear();
+ }
+}
+
+void Dual_Resampler::clear()
+{
+ buf_pos = sample_buf_size;
+ resampler.clear();
+}
+
+
+void Dual_Resampler::play_frame_( Stereo_Buffer& stereo_buf, dsample_t out [] )
+{
+ int pair_count = sample_buf_size >> 1;
+ blip_time_t blip_time = stereo_buf.center()->count_clocks( pair_count );
+ int sample_count = oversamples_per_frame - resampler.written() + resampler_extra;
+
+ int new_count = set_callback.f( set_callback.data, blip_time, sample_count, resampler.buffer() );
+ assert( new_count < resampler_size );
+
+ stereo_buf.end_frame( blip_time );
+ assert( stereo_buf.samples_avail() == pair_count * 2 );
+
+ resampler.write( new_count );
+
+ int count = resampler.read( sample_buf.begin(), sample_buf_size );
+ assert( count == sample_buf_size );
+
+ mix_samples( stereo_buf, out );
+ stereo_buf.left()->remove_samples( pair_count );
+ stereo_buf.right()->remove_samples( pair_count );
+ stereo_buf.center()->remove_samples( pair_count );
+}
+
+void Dual_Resampler::dual_play( int count, dsample_t out [], Stereo_Buffer& stereo_buf )
+{
+ // empty extra buffer
+ int 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 >= sample_buf_size )
+ {
+ play_frame_( stereo_buf, out );
+ out += sample_buf_size;
+ count -= sample_buf_size;
+ }
+
+ // extra
+ if ( count )
+ {
+ play_frame_( stereo_buf, sample_buf.begin() );
+ buf_pos = count;
+ memcpy( out, sample_buf.begin(), count * sizeof *out );
+ out += count;
+ }
+}
+
+void Dual_Resampler::mix_samples( Stereo_Buffer& stereo_buf, dsample_t out_ [] )
+{
+ // lol hax
+ if ( ((Tracked_Blip_Buffer*)stereo_buf.left())->non_silent() | ((Tracked_Blip_Buffer*)stereo_buf.right())->non_silent() )
+ mix_stereo( stereo_buf, out_ );
+ else
+ mix_mono( stereo_buf, out_ );
+}
+
+void Dual_Resampler::mix_mono( Stereo_Buffer& stereo_buf, dsample_t out_ [] )
+{
+ int const bass = BLIP_READER_BASS( *stereo_buf.center() );
+ BLIP_READER_BEGIN( sn, *stereo_buf.center() );
+
+ int count = sample_buf_size >> 1;
+ BLIP_READER_ADJ_( sn, count );
+
+ typedef dsample_t stereo_dsample_t [2];
+ stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
+ stereo_dsample_t const* BLARGG_RESTRICT in =
+ (stereo_dsample_t const*) sample_buf.begin() + count;
+ int offset = -count;
+ int const gain = gain_;
+ do
+ {
+ int s = BLIP_READER_READ_RAW( sn ) >> (blip_sample_bits - 16);
+ BLIP_READER_NEXT_IDX_( sn, bass, offset );
+
+ int l = (in [offset] [0] * gain >> gain_bits) + s;
+ int r = (in [offset] [1] * gain >> gain_bits) + s;
+
+ BLIP_CLAMP( l, l );
+ out [offset] [0] = (blip_sample_t) l;
+
+ BLIP_CLAMP( r, r );
+ out [offset] [1] = (blip_sample_t) r;
+ }
+ while ( ++offset );
+
+ BLIP_READER_END( sn, *stereo_buf.center() );
+}
+
+void Dual_Resampler::mix_stereo( Stereo_Buffer& stereo_buf, dsample_t out_ [] )
+{
+ int const bass = BLIP_READER_BASS( *stereo_buf.center() );
+ BLIP_READER_BEGIN( snc, *stereo_buf.center() );
+ BLIP_READER_BEGIN( snl, *stereo_buf.left() );
+ BLIP_READER_BEGIN( snr, *stereo_buf.right() );
+
+ int count = sample_buf_size >> 1;
+ BLIP_READER_ADJ_( snc, count );
+ BLIP_READER_ADJ_( snl, count );
+ BLIP_READER_ADJ_( snr, count );
+
+ typedef dsample_t stereo_dsample_t [2];
+ stereo_dsample_t* BLARGG_RESTRICT out = (stereo_dsample_t*) out_ + count;
+ stereo_dsample_t const* BLARGG_RESTRICT in =
+ (stereo_dsample_t const*) sample_buf.begin() + count;
+ int offset = -count;
+ int const gain = gain_;
+ do
+ {
+ int sc = BLIP_READER_READ_RAW( snc ) >> (blip_sample_bits - 16);
+ int sl = BLIP_READER_READ_RAW( snl ) >> (blip_sample_bits - 16);
+ int sr = BLIP_READER_READ_RAW( snr ) >> (blip_sample_bits - 16);
+ BLIP_READER_NEXT_IDX_( snc, bass, offset );
+ BLIP_READER_NEXT_IDX_( snl, bass, offset );
+ BLIP_READER_NEXT_IDX_( snr, bass, offset );
+
+ int l = (in [offset] [0] * gain >> gain_bits) + sl + sc;
+ int r = (in [offset] [1] * gain >> gain_bits) + sr + sc;
+
+ BLIP_CLAMP( l, l );
+ out [offset] [0] = (blip_sample_t) l;
+
+ BLIP_CLAMP( r, r );
+ out [offset] [1] = (blip_sample_t) r;
+ }
+ while ( ++offset );
+
+ BLIP_READER_END( snc, *stereo_buf.center() );
+ BLIP_READER_END( snl, *stereo_buf.left() );
+ BLIP_READER_END( snr, *stereo_buf.right() );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.h b/plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.h
new file mode 100644
index 00000000..6d77fd59
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Dual_Resampler.h
@@ -0,0 +1,58 @@
+// Combination of Fir_Resampler and Stereo_Buffer mixing. Used by Sega FM emulators.
+
+// Game_Music_Emu 0.6-pre
+#ifndef DUAL_RESAMPLER_H
+#define DUAL_RESAMPLER_H
+
+#include "Multi_Buffer.h"
+
+#if GME_VGM_FAST_RESAMPLER
+ #include "Downsampler.h"
+ typedef Downsampler Dual_Resampler_Downsampler;
+#else
+ #include "Fir_Resampler.h"
+ typedef Fir_Resampler_Norm Dual_Resampler_Downsampler;
+#endif
+
+class Dual_Resampler {
+public:
+ typedef short dsample_t;
+
+ blargg_err_t setup( double oversample, double rolloff, double gain );
+ double rate() const { return resampler.rate(); }
+ blargg_err_t reset( int max_pairs );
+ void resize( int pairs_per_frame );
+ void clear();
+
+ void dual_play( int count, dsample_t out [], Stereo_Buffer& );
+
+ blargg_callback<int (*)( void*, blip_time_t, int, dsample_t* )> set_callback;
+
+// Implementation
+public:
+ Dual_Resampler();
+ ~Dual_Resampler();
+
+private:
+ enum { gain_bits = 14 };
+ blargg_vector<dsample_t> sample_buf;
+ int sample_buf_size;
+ int oversamples_per_frame;
+ int buf_pos;
+ int resampler_size;
+ int gain_;
+
+ Dual_Resampler_Downsampler resampler;
+ void mix_samples( Stereo_Buffer&, dsample_t []);
+ void mix_mono( Stereo_Buffer&, dsample_t []);
+ void mix_stereo( Stereo_Buffer&, dsample_t []);
+ void play_frame_( Stereo_Buffer&, dsample_t []);
+};
+
+inline blargg_err_t Dual_Resampler::setup( double oversample, double rolloff, double gain )
+{
+ gain_ = (int) ((1 << gain_bits) * gain);
+ return resampler.set_rate( oversample );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.cpp
new file mode 100644
index 00000000..2a490561
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.cpp
@@ -0,0 +1,640 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Effects_Buffer.h"
+
+/* Copyright (C) 2006-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+int const fixed_shift = 12;
+#define TO_FIXED( f ) fixed_t ((f) * ((fixed_t) 1 << fixed_shift))
+#define FROM_FIXED( f ) (f >> fixed_shift)
+
+int const max_read = 2560; // determines minimum delay
+
+Effects_Buffer::Effects_Buffer( int max_bufs, int echo_size_ ) : Multi_Buffer( stereo )
+{
+ echo_size = max( max_read * (int) stereo, echo_size_ & ~1 );
+ clock_rate_ = 0;
+ bass_freq_ = 90;
+ bufs = NULL;
+ bufs_size = 0;
+ bufs_max = max( max_bufs, (int) extra_chans );
+ no_echo = true;
+ no_effects = true;
+
+ // defaults
+ config_.enabled = false;
+ config_.delay [0] = 120;
+ config_.delay [1] = 122;
+ config_.feedback = 0.2f;
+ config_.treble = 0.4f;
+
+ static float const sep = 0.8f;
+ config_.side_chans [0].pan = -sep;
+ config_.side_chans [1].pan = +sep;
+ config_.side_chans [0].vol = 1.0f;
+ config_.side_chans [1].vol = 1.0f;
+
+ memset( &s, 0, sizeof s );
+ clear();
+}
+
+Effects_Buffer::~Effects_Buffer()
+{
+ delete_bufs();
+}
+
+// avoid using new []
+blargg_err_t Effects_Buffer::new_bufs( int size )
+{
+ bufs = (buf_t*) malloc( size * sizeof *bufs );
+ CHECK_ALLOC( bufs );
+ for ( int i = 0; i < size; i++ )
+ new (bufs + i) buf_t;
+ bufs_size = size;
+ return blargg_ok;
+}
+
+void Effects_Buffer::delete_bufs()
+{
+ if ( bufs )
+ {
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].~buf_t();
+ free( bufs );
+ bufs = NULL;
+ }
+ bufs_size = 0;
+}
+
+blargg_err_t Effects_Buffer::set_sample_rate( int rate, int msec )
+{
+ // extra to allow farther past-the-end pointers
+ mixer.samples_read = 0;
+ RETURN_ERR( echo.resize( echo_size + stereo ) );
+ return Multi_Buffer::set_sample_rate( rate, msec );
+}
+
+void Effects_Buffer::clock_rate( int rate )
+{
+ clock_rate_ = rate;
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clock_rate( clock_rate_ );
+}
+
+void Effects_Buffer::bass_freq( int freq )
+{
+ bass_freq_ = freq;
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].bass_freq( bass_freq_ );
+}
+
+blargg_err_t Effects_Buffer::set_channel_count( int count, int const types [] )
+{
+ RETURN_ERR( Multi_Buffer::set_channel_count( count, types ) );
+
+ delete_bufs();
+
+ mixer.samples_read = 0;
+
+ RETURN_ERR( chans.resize( count + extra_chans ) );
+
+ RETURN_ERR( new_bufs( min( bufs_max, count + extra_chans ) ) );
+
+ for ( int i = bufs_size; --i >= 0; )
+ RETURN_ERR( bufs [i].set_sample_rate( sample_rate(), length() ) );
+
+ for ( int i = chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.cfg.vol = 1.0f;
+ ch.cfg.pan = 0.0f;
+ ch.cfg.surround = false;
+ ch.cfg.echo = false;
+ }
+ // side channels with echo
+ chans [2].cfg.echo = true;
+ chans [3].cfg.echo = true;
+
+ clock_rate( clock_rate_ );
+ bass_freq( bass_freq_ );
+ apply_config();
+ clear();
+
+ return blargg_ok;
+}
+
+void Effects_Buffer::clear_echo()
+{
+ if ( echo.size() )
+ memset( echo.begin(), 0, echo.size() * sizeof echo [0] );
+}
+
+void Effects_Buffer::clear()
+{
+ echo_pos = 0;
+ s.low_pass [0] = 0;
+ s.low_pass [1] = 0;
+ mixer.samples_read = 0;
+
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clear();
+ clear_echo();
+}
+
+Effects_Buffer::channel_t Effects_Buffer::channel( int i )
+{
+ i += extra_chans;
+ require( extra_chans <= i && i < (int) chans.size() );
+ return chans [i].channel;
+}
+
+
+// Configuration
+
+// 3 wave positions with/without surround, 2 multi (one with same config as wave)
+int const simple_bufs = 3 * 2 + 2 - 1;
+
+Simple_Effects_Buffer::Simple_Effects_Buffer() :
+ Effects_Buffer( extra_chans + simple_bufs, 18 * 1024 )
+{
+ config_.echo = 0.20f;
+ config_.stereo = 0.20f;
+ config_.surround = true;
+ config_.enabled = false;
+}
+
+void Simple_Effects_Buffer::apply_config()
+{
+ Effects_Buffer::config_t& c = Effects_Buffer::config();
+
+ c.enabled = config_.enabled;
+ if ( c.enabled )
+ {
+ c.delay [0] = 120;
+ c.delay [1] = 122;
+ c.feedback = config_.echo * 0.7f;
+ c.treble = 0.6f - 0.3f * config_.echo;
+
+ float sep = config_.stereo + 0.80f;
+ if ( sep > 1.0f )
+ sep = 1.0f;
+
+ c.side_chans [0].pan = -sep;
+ c.side_chans [1].pan = +sep;
+
+ for ( int i = channel_count(); --i >= 0; )
+ {
+ chan_config_t& ch = Effects_Buffer::chan_config( i );
+
+ ch.pan = 0.0f;
+ ch.surround = config_.surround;
+ ch.echo = false;
+
+ int const type = (channel_types() ? channel_types() [i] : 0);
+ if ( !(type & noise_type) )
+ {
+ int index = (type & type_index_mask) % 6 - 3;
+ if ( index < 0 )
+ {
+ index += 3;
+ ch.surround = false;
+ ch.echo = true;
+ }
+ if ( index >= 1 )
+ {
+ ch.pan = config_.stereo;
+ if ( index == 1 )
+ ch.pan = -ch.pan;
+ }
+ }
+ else if ( type & 1 )
+ {
+ ch.surround = false;
+ }
+ }
+ }
+
+ Effects_Buffer::apply_config();
+}
+
+int Effects_Buffer::min_delay() const
+{
+ require( sample_rate() );
+ return max_read * 1000 / sample_rate();
+}
+
+int Effects_Buffer::max_delay() const
+{
+ require( sample_rate() );
+ return (echo_size / stereo - max_read) * 1000 / sample_rate();
+}
+
+void Effects_Buffer::apply_config()
+{
+ int i;
+
+ if ( !bufs_size )
+ return;
+
+ s.treble = TO_FIXED( config_.treble );
+
+ bool echo_dirty = false;
+
+ fixed_t old_feedback = s.feedback;
+ s.feedback = TO_FIXED( config_.feedback );
+ if ( !old_feedback && s.feedback )
+ echo_dirty = true;
+
+ // delays
+ for ( i = stereo; --i >= 0; )
+ {
+ int delay = config_.delay [i] * sample_rate() / 1000 * stereo;
+ delay = max( delay, (int) (max_read * stereo) );
+ delay = min( delay, (int) (echo_size - max_read * stereo) );
+ if ( s.delay [i] != delay )
+ {
+ s.delay [i] = delay;
+ echo_dirty = true;
+ }
+ }
+
+ // side channels
+ for ( i = 2; --i >= 0; )
+ {
+ chans [i+2].cfg.vol = chans [i].cfg.vol = config_.side_chans [i].vol * 0.5f;
+ chans [i+2].cfg.pan = chans [i].cfg.pan = config_.side_chans [i].pan;
+ }
+
+ // convert volumes
+ for ( i = chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.vol [0] = TO_FIXED( ch.cfg.vol - ch.cfg.vol * ch.cfg.pan );
+ ch.vol [1] = TO_FIXED( ch.cfg.vol + ch.cfg.vol * ch.cfg.pan );
+ if ( ch.cfg.surround )
+ ch.vol [0] = -ch.vol [0];
+ }
+
+ assign_buffers();
+
+ // set side channels
+ for ( i = chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.channel.left = chans [ch.cfg.echo*2 ].channel.center;
+ ch.channel.right = chans [ch.cfg.echo*2+1].channel.center;
+ }
+
+ bool old_echo = !no_echo && !no_effects;
+
+ // determine whether effects and echo are needed at all
+ no_effects = true;
+ no_echo = true;
+ for ( i = chans.size(); --i >= extra_chans; )
+ {
+ chan_t& ch = chans [i];
+ if ( ch.cfg.echo && s.feedback )
+ no_echo = false;
+
+ if ( ch.vol [0] != TO_FIXED( 1 ) || ch.vol [1] != TO_FIXED( 1 ) )
+ no_effects = false;
+ }
+ if ( !no_echo )
+ no_effects = false;
+
+ if ( chans [0].vol [0] != TO_FIXED( 1 ) ||
+ chans [0].vol [1] != TO_FIXED( 0 ) ||
+ chans [1].vol [0] != TO_FIXED( 0 ) ||
+ chans [1].vol [1] != TO_FIXED( 1 ) )
+ no_effects = false;
+
+ if ( !config_.enabled )
+ no_effects = true;
+
+ if ( no_effects )
+ {
+ for ( i = chans.size(); --i >= 0; )
+ {
+ chan_t& ch = chans [i];
+ ch.channel.center = &bufs [2];
+ ch.channel.left = &bufs [0];
+ ch.channel.right = &bufs [1];
+ }
+ }
+
+ mixer.bufs [0] = &bufs [0];
+ mixer.bufs [1] = &bufs [1];
+ mixer.bufs [2] = &bufs [2];
+
+ if ( echo_dirty || (!old_echo && (!no_echo && !no_effects)) )
+ clear_echo();
+
+ channels_changed();
+}
+
+void Effects_Buffer::assign_buffers()
+{
+ // assign channels to buffers
+ int buf_count = 0;
+ for ( int i = 0; i < (int) chans.size(); i++ )
+ {
+ // put second two side channels at end to give priority to main channels
+ // in case closest matching is necessary
+ int x = i;
+ if ( i > 1 )
+ x += 2;
+ if ( x >= (int) chans.size() )
+ x -= (chans.size() - 2);
+ chan_t& ch = chans [x];
+
+ int b = 0;
+ for ( ; b < buf_count; b++ )
+ {
+ if ( ch.vol [0] == bufs [b].vol [0] &&
+ ch.vol [1] == bufs [b].vol [1] &&
+ (ch.cfg.echo == bufs [b].echo || !s.feedback) )
+ break;
+ }
+
+ if ( b >= buf_count )
+ {
+ if ( buf_count < bufs_max )
+ {
+ bufs [b].vol [0] = ch.vol [0];
+ bufs [b].vol [1] = ch.vol [1];
+ bufs [b].echo = ch.cfg.echo;
+ buf_count++;
+ }
+ else
+ {
+ // TODO: this is a mess, needs refinement
+ dprintf( "Effects_Buffer ran out of buffers; using closest match\n" );
+ b = 0;
+ fixed_t best_dist = TO_FIXED( 8 );
+ for ( int h = buf_count; --h >= 0; )
+ {
+ #define CALC_LEVELS( vols, sum, diff, surround ) \
+ fixed_t sum, diff;\
+ bool surround = false;\
+ {\
+ fixed_t vol_0 = vols [0];\
+ if ( vol_0 < 0 ) vol_0 = -vol_0, surround = true;\
+ fixed_t vol_1 = vols [1];\
+ if ( vol_1 < 0 ) vol_1 = -vol_1, surround = true;\
+ sum = vol_0 + vol_1;\
+ diff = vol_0 - vol_1;\
+ }
+ CALC_LEVELS( ch.vol, ch_sum, ch_diff, ch_surround );
+ CALC_LEVELS( bufs [h].vol, buf_sum, buf_diff, buf_surround );
+
+ fixed_t dist = abs( ch_sum - buf_sum ) + abs( ch_diff - buf_diff );
+
+ if ( ch_surround != buf_surround )
+ dist += TO_FIXED( 1 ) / 2;
+
+ if ( s.feedback && ch.cfg.echo != bufs [h].echo )
+ dist += TO_FIXED( 1 ) / 2;
+
+ if ( best_dist > dist )
+ {
+ best_dist = dist;
+ b = h;
+ }
+ }
+ }
+ }
+
+ //dprintf( "ch %d->buf %d\n", x, b );
+ ch.channel.center = &bufs [b];
+ }
+}
+
+
+// Mixing
+
+void Effects_Buffer::end_frame( blip_time_t time )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].end_frame( time );
+}
+
+int Effects_Buffer::read_samples( blip_sample_t out [], int out_size )
+{
+ out_size = min( out_size, samples_avail() );
+
+ int pair_count = int (out_size >> 1);
+ require( pair_count * stereo == out_size ); // must read an even number of samples
+ if ( pair_count )
+ {
+ if ( no_effects )
+ {
+ mixer.read_pairs( out, pair_count );
+ }
+ else
+ {
+ int pairs_remain = pair_count;
+ do
+ {
+ // mix at most max_read pairs at a time
+ int count = max_read;
+ if ( count > pairs_remain )
+ count = pairs_remain;
+
+ if ( no_echo )
+ {
+ // optimization: clear echo here to keep mix_effects() a leaf function
+ echo_pos = 0;
+ memset( echo.begin(), 0, count * stereo * sizeof echo [0] );
+ }
+ mix_effects( out, count );
+
+ int new_echo_pos = echo_pos + count * stereo;
+ if ( new_echo_pos >= echo_size )
+ new_echo_pos -= echo_size;
+ echo_pos = new_echo_pos;
+ assert( echo_pos < echo_size );
+
+ out += count * stereo;
+ mixer.samples_read += count;
+ pairs_remain -= count;
+ }
+ while ( pairs_remain );
+ }
+
+ if ( samples_avail() <= 0 || immediate_removal() )
+ {
+ for ( int i = bufs_size; --i >= 0; )
+ {
+ buf_t& b = bufs [i];
+ // TODO: might miss non-silence settling since it checks END of last read
+ if ( b.non_silent() )
+ b.remove_samples( mixer.samples_read );
+ else
+ b.remove_silence( mixer.samples_read );
+ }
+ mixer.samples_read = 0;
+ }
+ }
+ return out_size;
+}
+
+void Effects_Buffer::mix_effects( blip_sample_t out_ [], int pair_count )
+{
+ typedef fixed_t stereo_fixed_t [stereo];
+
+ // add channels with echo, do echo, add channels without echo, then convert to 16-bit and output
+ int echo_phase = 1;
+ do
+ {
+ // mix any modified buffers
+ {
+ buf_t* buf = bufs;
+ int bufs_remain = bufs_size;
+ do
+ {
+ if ( buf->non_silent() && buf->echo == echo_phase )
+ {
+ stereo_fixed_t* BLARGG_RESTRICT out = (stereo_fixed_t*) &echo [echo_pos];
+ int const bass = BLIP_READER_BASS( *buf );
+ BLIP_READER_BEGIN( in, *buf );
+ BLIP_READER_ADJ_( in, mixer.samples_read );
+ fixed_t const vol_0 = buf->vol [0];
+ fixed_t const vol_1 = buf->vol [1];
+
+ int count = (unsigned) (echo_size - echo_pos) / stereo;
+ int remain = pair_count;
+ if ( count > remain )
+ count = remain;
+ do
+ {
+ remain -= count;
+ BLIP_READER_ADJ_( in, count );
+
+ out += count;
+ int offset = -count;
+ do
+ {
+ fixed_t s = BLIP_READER_READ( in );
+ BLIP_READER_NEXT_IDX_( in, bass, offset );
+
+ out [offset] [0] += s * vol_0;
+ out [offset] [1] += s * vol_1;
+ }
+ while ( ++offset );
+
+ out = (stereo_fixed_t*) echo.begin();
+ count = remain;
+ }
+ while ( remain );
+
+ BLIP_READER_END( in, *buf );
+ }
+ buf++;
+ }
+ while ( --bufs_remain );
+ }
+
+ // add echo
+ if ( echo_phase && !no_echo )
+ {
+ fixed_t const feedback = s.feedback;
+ fixed_t const treble = s.treble;
+
+ int i = 1;
+ do
+ {
+ fixed_t low_pass = s.low_pass [i];
+
+ fixed_t* echo_end = &echo [echo_size + i];
+ fixed_t const* BLARGG_RESTRICT in_pos = &echo [echo_pos + i];
+ int out_offset = echo_pos + i + s.delay [i];
+ if ( out_offset >= echo_size )
+ out_offset -= echo_size;
+ assert( out_offset < echo_size );
+ fixed_t* BLARGG_RESTRICT out_pos = &echo [out_offset];
+
+ // break into up to three chunks to avoid having to handle wrap-around
+ // in middle of core loop
+ int remain = pair_count;
+ do
+ {
+ fixed_t const* pos = in_pos;
+ if ( pos < out_pos )
+ pos = out_pos;
+ int count = (unsigned) ((char*) echo_end - (char const*) pos) /
+ (unsigned) (stereo * sizeof (fixed_t));
+ if ( count > remain )
+ count = remain;
+ remain -= count;
+
+ in_pos += count * stereo;
+ out_pos += count * stereo;
+ int offset = -count;
+ do
+ {
+ low_pass += FROM_FIXED( in_pos [offset * stereo] - low_pass ) * treble;
+ out_pos [offset * stereo] = FROM_FIXED( low_pass ) * feedback;
+ }
+ while ( ++offset );
+
+ if ( in_pos >= echo_end ) in_pos -= echo_size;
+ if ( out_pos >= echo_end ) out_pos -= echo_size;
+ }
+ while ( remain );
+
+ s.low_pass [i] = low_pass;
+ }
+ while ( --i >= 0 );
+ }
+ }
+ while ( --echo_phase >= 0 );
+
+ // clamp to 16 bits
+ {
+ stereo_fixed_t const* BLARGG_RESTRICT in = (stereo_fixed_t*) &echo [echo_pos];
+ typedef blip_sample_t stereo_blip_sample_t [stereo];
+ stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_;
+ int count = (unsigned) (echo_size - echo_pos) / (unsigned) stereo;
+ int remain = pair_count;
+ if ( count > remain )
+ count = remain;
+ do
+ {
+ remain -= count;
+ in += count;
+ out += count;
+ int offset = -count;
+ do
+ {
+ fixed_t in_0 = FROM_FIXED( in [offset] [0] );
+ fixed_t in_1 = FROM_FIXED( in [offset] [1] );
+
+ BLIP_CLAMP( in_0, in_0 );
+ out [offset] [0] = (blip_sample_t) in_0;
+
+ BLIP_CLAMP( in_1, in_1 );
+ out [offset] [1] = (blip_sample_t) in_1;
+ }
+ while ( ++offset );
+
+ in = (stereo_fixed_t*) echo.begin();
+ count = remain;
+ }
+ while ( remain );
+ }
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.h b/plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.h
new file mode 100644
index 00000000..348c74c7
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Effects_Buffer.h
@@ -0,0 +1,149 @@
+// Multi-channel effects buffer with echo and individual panning for each channel
+
+// Game_Music_Emu 0.6-pre
+#ifndef EFFECTS_BUFFER_H
+#define EFFECTS_BUFFER_H
+
+#include "Multi_Buffer.h"
+
+// See Simple_Effects_Buffer (below) for a simpler interface
+
+class Effects_Buffer : public Multi_Buffer {
+public:
+ // To reduce memory usage, fewer buffers can be used (with a best-fit
+ // approach if there are too few), and maximum echo delay can be reduced
+ Effects_Buffer( int max_bufs = 32, int echo_size = 24 * 1024 );
+
+ struct pan_vol_t
+ {
+ float vol; // 0.0 = silent, 0.5 = half volume, 1.0 = normal
+ float pan; // -1.0 = left, 0.0 = center, +1.0 = right
+ };
+
+ // Global configuration
+ struct config_t
+ {
+ bool enabled; // false = disable all effects
+
+ // Current sound is echoed at adjustable left/right delay,
+ // with reduced treble and volume (feedback).
+ float treble; // 1.0 = full treble, 0.1 = very little, 0.0 = silent
+ int delay [2]; // left, right delays (msec)
+ float feedback; // 0.0 = no echo, 0.5 = each echo half previous, 1.0 = cacophony
+ pan_vol_t side_chans [2]; // left and right side channel volume and pan
+ };
+ config_t& config() { return config_; }
+
+ // Limits of delay (msec)
+ int min_delay() const;
+ int max_delay() const;
+
+ // Per-channel configuration. Two or more channels with matching parameters are
+ // optimized to internally use the same buffer.
+ struct chan_config_t : pan_vol_t
+ {
+ // (inherited from pan_vol_t)
+ //float vol; // these only affect center channel
+ //float pan;
+ bool surround; // if true, negates left volume to put sound in back
+ bool echo; // false = channel doesn't have any echo
+ };
+ chan_config_t& chan_config( int i ) { return chans [i + extra_chans].cfg; }
+
+ // Applies any changes made to config() and chan_config()
+ virtual void apply_config();
+
+// Implementation
+public:
+ ~Effects_Buffer();
+ blargg_err_t set_sample_rate( int samples_per_sec, int msec = blip_default_length );
+ blargg_err_t set_channel_count( int, int const* = NULL );
+ void clock_rate( int );
+ void bass_freq( int );
+ void clear();
+ channel_t channel( int );
+ void end_frame( blip_time_t );
+ int read_samples( blip_sample_t [], int );
+ int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
+ enum { stereo = 2 };
+ typedef int fixed_t;
+
+protected:
+ enum { extra_chans = stereo * stereo };
+
+private:
+ config_t config_;
+ int clock_rate_;
+ int bass_freq_;
+
+ int echo_size;
+
+ struct chan_t
+ {
+ fixed_t vol [stereo];
+ chan_config_t cfg;
+ channel_t channel;
+ };
+ blargg_vector<chan_t> chans;
+
+ struct buf_t : Tracked_Blip_Buffer
+ {
+ // nasty: Blip_Buffer has something called fixed_t
+ Effects_Buffer::fixed_t vol [stereo];
+ bool echo;
+
+ void* operator new ( size_t, void* p ) { return p; }
+ void operator delete ( void* ) { }
+
+ ~buf_t() { }
+ };
+ buf_t* bufs;
+ int bufs_size;
+ int bufs_max; // bufs_size <= bufs_max, to limit memory usage
+ Stereo_Mixer mixer;
+
+ struct {
+ int delay [stereo];
+ fixed_t treble;
+ fixed_t feedback;
+ fixed_t low_pass [stereo];
+ } s;
+
+ blargg_vector<fixed_t> echo;
+ int echo_pos;
+
+ bool no_effects;
+ bool no_echo;
+
+ void assign_buffers();
+ void clear_echo();
+ void mix_effects( blip_sample_t out [], int pair_count );
+ blargg_err_t new_bufs( int size );
+ void delete_bufs();
+};
+
+// Simpler interface and lower memory usage
+class Simple_Effects_Buffer : public Effects_Buffer {
+public:
+ struct config_t
+ {
+ bool enabled; // false = disable all effects
+
+ float echo; // 0.0 = none, 1.0 = lots
+ float stereo; // 0.0 = channels in center, 1.0 = channels on left/right
+ bool surround; // true = put some channels in back
+ };
+ config_t& config() { return config_; }
+
+ // Applies any changes made to config()
+ void apply_config();
+
+// Implementation
+public:
+ Simple_Effects_Buffer();
+private:
+ config_t config_;
+ void chan_config(); // hide
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.cpp
new file mode 100644
index 00000000..b46ad922
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.cpp
@@ -0,0 +1,123 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Fir_Resampler.h"
+
+#include <math.h>
+
+/* Copyright (C) 2004-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#undef PI
+#define PI 3.1415926535897932384626433832795029
+
+static void gen_sinc( double rolloff, int width, double offset, double spacing, double scale,
+ int count, short* out )
+{
+ double const maxh = 256;
+ double const step = PI / maxh * spacing;
+ double const to_w = maxh * 2 / width;
+ double const pow_a_n = pow( rolloff, maxh );
+ scale /= maxh * 2;
+
+ double angle = (count / 2 - 1 + offset) * -step;
+ while ( count-- )
+ {
+ *out++ = 0;
+ double w = angle * to_w;
+ if ( fabs( w ) < PI )
+ {
+ double rolloff_cos_a = rolloff * cos( angle );
+ double num = 1 - rolloff_cos_a -
+ pow_a_n * cos( maxh * angle ) +
+ pow_a_n * rolloff * cos( (maxh - 1) * angle );
+ double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
+ double sinc = scale * num / den - scale;
+
+ out [-1] = (short) (cos( w ) * sinc + sinc);
+ }
+ angle += step;
+ }
+}
+
+Fir_Resampler_::Fir_Resampler_( int width, sample_t impulses_ [] ) :
+ width_( width ),
+ impulses( impulses_ )
+{
+ imp = NULL;
+}
+
+void Fir_Resampler_::clear_()
+{
+ imp = impulses;
+ Resampler::clear_();
+}
+
+blargg_err_t Fir_Resampler_::set_rate_( double new_factor )
+{
+ double const rolloff = 0.999;
+ double const gain = 1.0;
+
+ // determine number of sub-phases that yield lowest error
+ double ratio_ = 0.0;
+ int res = -1;
+ {
+ double least_error = 2;
+ double pos = 0;
+ for ( int r = 1; r <= max_res; r++ )
+ {
+ pos += new_factor;
+ double nearest = floor( pos + 0.5 );
+ double error = fabs( pos - nearest );
+ if ( error < least_error )
+ {
+ res = r;
+ ratio_ = nearest / res;
+ least_error = error;
+ }
+ }
+ }
+ RETURN_ERR( Resampler::set_rate_( ratio_ ) );
+
+ // how much of input is used for each output sample
+ int const step = stereo * (int) floor( ratio_ );
+ double fraction = fmod( ratio_, 1.0 );
+
+ double const filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
+ double pos = 0.0;
+ //int input_per_cycle = 0;
+ sample_t* out = impulses;
+ for ( int n = res; --n >= 0; )
+ {
+ gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter,
+ double (0x7FFF * gain * filter), (int) width_, out );
+ out += width_;
+
+ int cur_step = step;
+ pos += fraction;
+ if ( pos >= 0.9999999 )
+ {
+ pos -= 1.0;
+ cur_step += stereo;
+ }
+
+ *out++ = (cur_step - width_ * 2 + 4) * sizeof (sample_t);
+ *out++ = 4 * sizeof (sample_t);
+ //input_per_cycle += cur_step;
+ }
+ // last offset moves back to beginning of impulses
+ out [-1] -= (char*) out - (char*) impulses;
+
+ imp = impulses;
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.h b/plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.h
new file mode 100644
index 00000000..daabf146
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Fir_Resampler.h
@@ -0,0 +1,101 @@
+// Finite impulse response (FIR) resampler with adjustable FIR size
+
+// Game_Music_Emu 0.6-pre
+#ifndef FIR_RESAMPLER_H
+#define FIR_RESAMPLER_H
+
+#include "Resampler.h"
+
+template<int width>
+class Fir_Resampler;
+
+// Use one of these typedefs
+typedef Fir_Resampler< 8> Fir_Resampler_Fast;
+typedef Fir_Resampler<16> Fir_Resampler_Norm;
+typedef Fir_Resampler<24> Fir_Resampler_Good;
+
+// Implementation
+class Fir_Resampler_ : public Resampler {
+protected:
+ virtual blargg_err_t set_rate_( double );
+ virtual void clear_();
+
+protected:
+ enum { stereo = 2 };
+ enum { max_res = 32 }; // TODO: eliminate and keep impulses on freestore?
+ sample_t const* imp;
+ int const width_;
+ sample_t* impulses;
+
+ Fir_Resampler_( int width, sample_t [] );
+};
+
+// Width is number of points in FIR. More points give better quality and
+// rolloff effectiveness, and take longer to calculate.
+template<int width>
+class Fir_Resampler : public Fir_Resampler_ {
+ enum { min_width = (width < 4 ? 4 : width) };
+ enum { adj_width = min_width / 4 * 4 + 2 };
+ enum { write_offset = adj_width * stereo };
+ short impulses [max_res * (adj_width + 2)];
+public:
+ Fir_Resampler() : Fir_Resampler_( adj_width, impulses ) { }
+
+protected:
+ virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int );
+};
+
+template<int width>
+Resampler::sample_t const* Fir_Resampler<width>::resample_( sample_t** out_,
+ sample_t const* out_end, sample_t const in [], int in_size )
+{
+ in_size -= write_offset;
+ if ( in_size > 0 )
+ {
+ sample_t* BLARGG_RESTRICT out = *out_;
+ sample_t const* const in_end = in + in_size;
+ sample_t const* imp = this->imp;
+
+ do
+ {
+ // accumulate in extended precision
+ int pt = imp [0];
+ int l = pt * in [0];
+ int r = pt * in [1];
+ if ( out >= out_end )
+ break;
+ for ( int n = (adj_width - 2) / 2; n; --n )
+ {
+ pt = imp [1];
+ l += pt * in [2];
+ r += pt * in [3];
+
+ // pre-increment more efficient on some RISC processors
+ imp += 2;
+ pt = imp [0];
+ r += pt * in [5];
+ in += 4;
+ l += pt * in [0];
+ }
+ pt = imp [1];
+ l += pt * in [2];
+ r += pt * in [3];
+
+ // these two "samples" after the end of the impulse give the
+ // proper offsets to the next input sample and next impulse
+ in = (sample_t const*) ((char const*) in + imp [2]); // some negative value
+ imp = (sample_t const*) ((char const*) imp + imp [3]); // small positive or large negative
+
+ out [0] = sample_t (l >> 15);
+ out [1] = sample_t (r >> 15);
+ out += 2;
+ }
+ while ( in < in_end );
+
+ this->imp = imp;
+ *out_ = out;
+ }
+ return in;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.cpp
new file mode 100644
index 00000000..af5954f1
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.cpp
@@ -0,0 +1,407 @@
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
+
+#include "Gb_Apu.h"
+
+//#include "gb_apu_logger.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const vol_reg = 0xFF24;
+int const stereo_reg = 0xFF25;
+int const status_reg = 0xFF26;
+int const wave_ram = 0xFF30;
+
+int const power_mask = 0x80;
+
+void Gb_Apu::treble_eq( blip_eq_t const& eq )
+{
+ norm_synth.treble_eq( eq );
+ fast_synth.treble_eq( eq );
+}
+
+inline int Gb_Apu::calc_output( int osc ) const
+{
+ int bits = regs [stereo_reg - io_addr] >> osc;
+ return (bits >> 3 & 2) | (bits & 1);
+}
+
+void Gb_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
+
+ if ( !center || !left || !right )
+ {
+ left = center;
+ right = center;
+ }
+
+ Gb_Osc& o = *oscs [i];
+ o.outputs [1] = right;
+ o.outputs [2] = left;
+ o.outputs [3] = center;
+ o.output = o.outputs [calc_output( i )];
+}
+
+void Gb_Apu::synth_volume( int iv )
+{
+ double v = volume_ * 0.60 / osc_count / 15 /*steps*/ / 8 /*master vol range*/ * iv;
+ norm_synth.volume( v );
+ fast_synth.volume( v );
+}
+
+void Gb_Apu::apply_volume()
+{
+ // TODO: Doesn't handle differing left and right volumes (panning).
+ // Not worth the complexity.
+ int data = regs [vol_reg - io_addr];
+ int left = data >> 4 & 7;
+ int right = data & 7;
+ //if ( data & 0x88 ) dprintf( "Vin: %02X\n", data & 0x88 );
+ //if ( left != right ) dprintf( "l: %d r: %d\n", left, right );
+ synth_volume( max( left, right ) + 1 );
+}
+
+void Gb_Apu::volume( double v )
+{
+ if ( volume_ != v )
+ {
+ volume_ = v;
+ apply_volume();
+ }
+}
+
+void Gb_Apu::reset_regs()
+{
+ for ( int i = 0; i < 0x20; i++ )
+ regs [i] = 0;
+
+ square1.reset();
+ square2.reset();
+ wave .reset();
+ noise .reset();
+
+ apply_volume();
+}
+
+void Gb_Apu::reset_lengths()
+{
+ square1.length_ctr = 64;
+ square2.length_ctr = 64;
+ wave .length_ctr = 256;
+ noise .length_ctr = 64;
+}
+
+void Gb_Apu::reduce_clicks( bool reduce )
+{
+ reduce_clicks_ = reduce;
+
+ // Click reduction makes DAC off generate same output as volume 0
+ int dac_off_amp = 0;
+ if ( reduce && wave.mode != mode_agb ) // AGB already eliminates clicks
+ dac_off_amp = -Gb_Osc::dac_bias;
+
+ for ( int i = 0; i < osc_count; i++ )
+ oscs [i]->dac_off_amp = dac_off_amp;
+
+ // AGB always eliminates clicks on wave channel using same method
+ if ( wave.mode == mode_agb )
+ wave.dac_off_amp = -Gb_Osc::dac_bias;
+}
+
+void Gb_Apu::reset( mode_t mode, bool agb_wave )
+{
+ // Hardware mode
+ if ( agb_wave )
+ mode = mode_agb; // using AGB wave features implies AGB hardware
+ wave.agb_mask = agb_wave ? 0xFF : 0;
+ for ( int i = 0; i < osc_count; i++ )
+ oscs [i]->mode = mode;
+ reduce_clicks( reduce_clicks_ );
+
+ // Reset state
+ frame_time = 0;
+ last_time = 0;
+ frame_phase = 0;
+
+ reset_regs();
+ reset_lengths();
+
+ // Load initial wave RAM
+ static byte const initial_wave [2] [16] = {
+ {0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C,0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA},
+ {0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF,0x00,0xFF},
+ };
+ for ( int b = 2; --b >= 0; )
+ {
+ // Init both banks (does nothing if not in AGB mode)
+ // TODO: verify that this works
+ write_register( 0, 0xFF1A, b * 0x40 );
+ for ( unsigned i = 0; i < sizeof initial_wave [0]; i++ )
+ write_register( 0, i + wave_ram, initial_wave [(mode != mode_dmg)] [i] );
+ }
+}
+
+void Gb_Apu::set_tempo( double t )
+{
+ frame_period = 4194304 / 512; // 512 Hz
+ if ( t != 1.0 )
+ frame_period = t ? blip_time_t (frame_period / t) : blip_time_t(0);
+}
+
+Gb_Apu::Gb_Apu()
+{
+ wave.wave_ram = &regs [wave_ram - io_addr];
+
+ oscs [0] = &square1;
+ oscs [1] = &square2;
+ oscs [2] = &wave;
+ oscs [3] = &noise;
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Gb_Osc& o = *oscs [i];
+ o.regs = &regs [i * 5];
+ o.output = NULL;
+ o.outputs [0] = NULL;
+ o.outputs [1] = NULL;
+ o.outputs [2] = NULL;
+ o.outputs [3] = NULL;
+ o.norm_synth = &norm_synth;
+ o.fast_synth = &fast_synth;
+ }
+
+ reduce_clicks_ = false;
+ set_tempo( 1.0 );
+ volume_ = 1.0;
+ reset();
+}
+
+void Gb_Apu::run_until_( blip_time_t end_time )
+{
+ if ( !frame_period )
+ frame_time += end_time - last_time;
+
+ while ( true )
+ {
+ // run oscillators
+ blip_time_t time = end_time;
+ if ( time > frame_time )
+ time = frame_time;
+
+ square1.run( last_time, time );
+ square2.run( last_time, time );
+ wave .run( last_time, time );
+ noise .run( last_time, time );
+ last_time = time;
+
+ if ( time == end_time )
+ break;
+
+ // run frame sequencer
+ assert( frame_period );
+ frame_time += frame_period * Gb_Osc::clk_mul;
+ switch ( frame_phase++ )
+ {
+ case 2:
+ case 6:
+ // 128 Hz
+ square1.clock_sweep();
+ case 0:
+ case 4:
+ // 256 Hz
+ square1.clock_length();
+ square2.clock_length();
+ wave .clock_length();
+ noise .clock_length();
+ break;
+
+ case 7:
+ // 64 Hz
+ frame_phase = 0;
+ square1.clock_envelope();
+ square2.clock_envelope();
+ noise .clock_envelope();
+ }
+ }
+}
+
+inline void Gb_Apu::run_until( blip_time_t time )
+{
+ require( time >= last_time ); // end_time must not be before previous time
+ if ( time > last_time )
+ run_until_( time );
+}
+
+void Gb_Apu::end_frame( blip_time_t end_time )
+{
+ #ifdef LOG_FRAME
+ LOG_FRAME( end_time );
+ #endif
+
+ if ( end_time > last_time )
+ run_until( end_time );
+
+ frame_time -= end_time;
+ assert( frame_time >= 0 );
+
+ last_time -= end_time;
+ assert( last_time >= 0 );
+}
+
+void Gb_Apu::silence_osc( Gb_Osc& o )
+{
+ int delta = -o.last_amp;
+ if ( reduce_clicks_ )
+ delta += o.dac_off_amp;
+
+ if ( delta )
+ {
+ o.last_amp = o.dac_off_amp;
+ if ( o.output )
+ {
+ o.output->set_modified();
+ fast_synth.offset( last_time, delta, o.output );
+ }
+ }
+}
+
+void Gb_Apu::apply_stereo()
+{
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Gb_Osc& o = *oscs [i];
+ Blip_Buffer* out = o.outputs [calc_output( i )];
+ if ( o.output != out )
+ {
+ silence_osc( o );
+ o.output = out;
+ }
+ }
+}
+
+void Gb_Apu::write_register( blip_time_t time, int addr, int data )
+{
+ require( (unsigned) data < 0x100 );
+
+ int reg = addr - io_addr;
+ if ( (unsigned) reg >= io_size )
+ {
+ require( false );
+ return;
+ }
+
+ #ifdef LOG_WRITE
+ LOG_WRITE( time, addr, data );
+ #endif
+
+ if ( addr < status_reg && !(regs [status_reg - io_addr] & power_mask) )
+ {
+ // Power is off
+
+ // length counters can only be written in DMG mode
+ if ( wave.mode != mode_dmg || (reg != 1 && reg != 5+1 && reg != 10+1 && reg != 15+1) )
+ return;
+
+ if ( reg < 10 )
+ data &= 0x3F; // clear square duty
+ }
+
+ run_until( time );
+
+ if ( addr >= wave_ram )
+ {
+ wave.write( addr, data );
+ }
+ else
+ {
+ int old_data = regs [reg];
+ regs [reg] = data;
+
+ if ( addr < vol_reg )
+ {
+ // Oscillator
+ write_osc( reg, old_data, data );
+ }
+ else if ( addr == vol_reg && data != old_data )
+ {
+ // Master volume
+ for ( int i = osc_count; --i >= 0; )
+ silence_osc( *oscs [i] );
+
+ apply_volume();
+ }
+ else if ( addr == stereo_reg )
+ {
+ // Stereo panning
+ apply_stereo();
+ }
+ else if ( addr == status_reg && (data ^ old_data) & power_mask )
+ {
+ // Power control
+ frame_phase = 0;
+ for ( int i = osc_count; --i >= 0; )
+ silence_osc( *oscs [i] );
+
+ reset_regs();
+ if ( wave.mode != mode_dmg )
+ reset_lengths();
+
+ regs [status_reg - io_addr] = data;
+ }
+ }
+}
+
+int Gb_Apu::read_register( blip_time_t time, int addr )
+{
+ if ( addr >= status_reg )
+ run_until( time );
+
+ int reg = addr - io_addr;
+ if ( (unsigned) reg >= io_size )
+ {
+ require( false );
+ return 0;
+ }
+
+ if ( addr >= wave_ram )
+ return wave.read( addr );
+
+ // Value read back has some bits always set
+ static byte const masks [] = {
+ 0x80,0x3F,0x00,0xFF,0xBF,
+ 0xFF,0x3F,0x00,0xFF,0xBF,
+ 0x7F,0xFF,0x9F,0xFF,0xBF,
+ 0xFF,0xFF,0x00,0x00,0xBF,
+ 0x00,0x00,0x70,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF
+ };
+ int mask = masks [reg];
+ if ( wave.agb_mask && (reg == 10 || reg == 12) )
+ mask = 0x1F; // extra implemented bits in wave regs on AGB
+ int data = regs [reg] | mask;
+
+ // Status register
+ if ( addr == status_reg )
+ {
+ data &= 0xF0;
+ data |= (int) square1.enabled << 0;
+ data |= (int) square2.enabled << 1;
+ data |= (int) wave .enabled << 2;
+ data |= (int) noise .enabled << 3;
+ }
+
+ return data;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.h
new file mode 100644
index 00000000..f7ffb037
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Apu.h
@@ -0,0 +1,193 @@
+// Nintendo Game Boy sound hardware emulator with save state support
+
+// Gb_Snd_Emu 0.1.4
+#ifndef GB_APU_H
+#define GB_APU_H
+
+#include "Gb_Oscs.h"
+
+struct gb_apu_state_t;
+
+class Gb_Apu {
+public:
+// Basics
+
+ // Sets buffer(s) to generate sound into, or NULL to mute. If only center is not NULL,
+ // output is mono.
+ void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Emulates to time t, then writes data to addr
+ void write_register( blip_time_t t, int addr, int data );
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Clock rate sound hardware runs at
+ enum { clock_rate = 4194304 * GB_APU_OVERCLOCK };
+
+ // Registers are at io_addr to io_addr+io_size-1
+ enum { io_addr = 0xFF10 };
+ enum { io_size = 0x30 };
+
+ // Emulates to time t, then reads from addr
+ int read_register( blip_time_t t, int addr );
+
+ // Resets hardware to state after power, BEFORE boot ROM runs. Mode selects
+ // sound hardware. If agb_wave is true, enables AGB's extra wave features.
+ enum mode_t {
+ mode_dmg, // Game Boy monochrome
+ mode_cgb, // Game Boy Color
+ mode_agb // Game Boy Advance
+ };
+ void reset( mode_t mode = mode_cgb, bool agb_wave = false );
+
+ // Same as set_output(), but for a particular channel
+ // 0: Square 1, 1: Square 2, 2: Wave, 3: Noise
+ enum { osc_count = 4 }; // 0 <= chan < osc_count
+ void set_output( int chan, Blip_Buffer* center,
+ Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Sets overall volume, where 1.0 is normal
+ void volume( double );
+
+ // Sets treble equalization
+ void treble_eq( blip_eq_t const& );
+
+ // Treble and bass values for various hardware.
+ enum {
+ speaker_treble = -47, // speaker on system
+ speaker_bass = 2000,
+ dmg_treble = 0, // headphones on each system
+ dmg_bass = 30,
+ cgb_treble = 0,
+ cgb_bass = 300, // CGB has much less bass
+ agb_treble = 0,
+ agb_bass = 30
+ };
+
+ // If true, reduces clicking by disabling DAC biasing. Note that this reduces
+ // emulation accuracy, since the clicks are authentic.
+ void reduce_clicks( bool reduce = true );
+
+ // Sets frame sequencer rate, where 1.0 is normal. Meant for adjusting the
+ // tempo in a music player.
+ void set_tempo( double );
+
+ // Saves full emulation state to state_out. Data format is portable and
+ // includes some extra space to avoid expansion in case more state needs
+ // to be stored in the future.
+ void save_state( gb_apu_state_t* state_out );
+
+ // Loads state. You should call reset() BEFORE this.
+ blargg_err_t load_state( gb_apu_state_t const& in );
+
+private:
+ // noncopyable
+ Gb_Apu( const Gb_Apu& );
+ Gb_Apu& operator = ( const Gb_Apu& );
+
+// Implementation
+public:
+ Gb_Apu();
+
+ // Use set_output() in place of these
+ BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); )
+ BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ); )
+ BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } )
+ BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } )
+
+ BLARGG_DEPRECATED_TEXT( enum { start_addr = 0xFF10 }; )
+ BLARGG_DEPRECATED_TEXT( enum { end_addr = 0xFF3F }; )
+ BLARGG_DEPRECATED_TEXT( enum { register_count = end_addr - start_addr + 1 }; )
+
+private:
+ Gb_Osc* oscs [osc_count];
+ blip_time_t last_time; // time sound emulator has been run to
+ blip_time_t frame_period; // clocks between each frame sequencer step
+ double volume_;
+ bool reduce_clicks_;
+
+ Gb_Sweep_Square square1;
+ Gb_Square square2;
+ Gb_Wave wave;
+ Gb_Noise noise;
+ blip_time_t frame_time; // time of next frame sequencer action
+ int frame_phase; // phase of next frame sequencer step
+ enum { regs_size = io_size + 0x10 };
+ BOOST::uint8_t regs [regs_size];// last values written to registers
+
+ // large objects after everything else
+ Blip_Synth_Norm norm_synth;
+ Blip_Synth_Fast fast_synth;
+
+ void reset_lengths();
+ void reset_regs();
+ int calc_output( int osc ) const;
+ void apply_stereo();
+ void apply_volume();
+ void synth_volume( int );
+ void run_until_( blip_time_t );
+ void run_until( blip_time_t );
+ void silence_osc( Gb_Osc& );
+ void write_osc( int reg, int old_data, int data );
+ const char* save_load( gb_apu_state_t*, bool save );
+ void save_load2( gb_apu_state_t*, bool save );
+ friend class Gb_Apu2;
+};
+
+// Format of save state. Should be stable across versions of the library,
+// with earlier versions properly opening later save states. Includes some
+// room for expansion so the state size shouldn't increase.
+struct gb_apu_state_t
+{
+#if GB_APU_CUSTOM_STATE
+ // Values stored as plain int so your code can read/write them easily.
+ // Structure can NOT be written to disk, since format is not portable.
+ typedef int val_t;
+#else
+ // Values written in portable little-endian format, allowing structure
+ // to be written directly to disk.
+ typedef unsigned char val_t [4];
+#endif
+
+ enum { format0 = 0x50414247 }; // 'GBAP'
+
+ val_t format; // format of all following data
+ val_t version; // later versions just add fields to end
+
+ unsigned char regs [0x40];
+ val_t frame_time;
+ val_t frame_phase;
+
+ val_t sweep_freq;
+ val_t sweep_delay;
+ val_t sweep_enabled;
+ val_t sweep_neg;
+ val_t noise_divider;
+ val_t wave_buf;
+
+ val_t delay [4];
+ val_t length_ctr [4];
+ val_t phase [4];
+ val_t enabled [4];
+
+ val_t env_delay [3];
+ val_t env_volume [3];
+ val_t env_enabled [3];
+
+ val_t unused [13]; // for future expansion
+};
+
+inline void Gb_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ for ( int i = osc_count; --i >= 0; )
+ set_output( i, c, l, r );
+}
+
+BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c ) { set_output( c, c, c ); } )
+BLARGG_DEPRECATED_TEXT( inline void Gb_Apu::output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } )
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.cpp
new file mode 100644
index 00000000..4a97ffb5
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.cpp
@@ -0,0 +1,51 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Gb_Cpu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+inline void Gb_Cpu::set_code_page( int i, void* p )
+{
+ byte* p2 = STATIC_CAST(byte*,p) - GB_CPU_OFFSET( i * page_size );
+ cpu_state_.code_map [i] = p2;
+ cpu_state->code_map [i] = p2;
+}
+
+void Gb_Cpu::reset( void* unmapped )
+{
+ check( cpu_state == &cpu_state_ );
+ cpu_state = &cpu_state_;
+
+ cpu_state_.time = 0;
+
+ for ( int i = 0; i < page_count + 1; ++i )
+ set_code_page( i, unmapped );
+
+ memset( &r, 0, sizeof r );
+
+ blargg_verify_byte_order();
+}
+
+void Gb_Cpu::map_code( addr_t start, int size, void* data )
+{
+ // address range must begin and end on page boundaries
+ require( start % page_size == 0 );
+ require( size % page_size == 0 );
+ require( start + size <= mem_size );
+
+ for ( int offset = 0; offset < size; offset += page_size )
+ set_code_page( (start + offset) >> page_bits, STATIC_CAST(char*,data) + offset );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.h b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.h
new file mode 100644
index 00000000..1aeccdfd
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu.h
@@ -0,0 +1,82 @@
+// Nintendo Game Boy CPU emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef GB_CPU_H
+#define GB_CPU_H
+
+#include "blargg_common.h"
+
+class Gb_Cpu {
+public:
+ typedef int addr_t;
+ typedef BOOST::uint8_t byte;
+
+ enum { mem_size = 0x10000 };
+
+ // Clears registers and map all pages to unmapped
+ void reset( void* unmapped = NULL );
+
+ // Maps code memory (memory accessed via the program counter). Start and size
+ // must be multiple of page_size.
+ enum { page_bits = 13 };
+ enum { page_size = 1 << page_bits };
+ void map_code( addr_t start, int size, void* code );
+
+ // Accesses emulated memory as CPU does
+ byte* get_code( addr_t );
+
+ // Game Boy Z-80 registers. NOT kept updated during emulation.
+ struct core_regs_t {
+ BOOST::uint16_t bc, de, hl, fa;
+ };
+
+ struct registers_t : core_regs_t {
+ int pc; // more than 16 bits to allow overflow detection
+ BOOST::uint16_t sp;
+ };
+ registers_t r;
+
+ // Base address for RST vectors, to simplify GBS player (normally 0)
+ addr_t rst_base;
+
+ // Current time.
+ int time() const { return cpu_state->time; }
+
+ // Changes time. Must not be called during emulation.
+ // Should be negative, because emulation stops once it becomes >= 0.
+ void set_time( int t ) { cpu_state->time = t; }
+
+ // Emulator reads this many bytes past end of a page
+ enum { cpu_padding = 8 };
+
+
+// Implementation
+public:
+ Gb_Cpu() : rst_base( 0 ) { cpu_state = &cpu_state_; }
+ enum { page_count = mem_size >> page_bits };
+
+ struct cpu_state_t {
+ byte* code_map [page_count + 1];
+ int time;
+ };
+ cpu_state_t* cpu_state; // points to state_ or a local copy within run()
+ cpu_state_t cpu_state_;
+
+private:
+ void set_code_page( int, void* );
+};
+
+#define GB_CPU_PAGE( addr ) ((unsigned) (addr) >> Gb_Cpu::page_bits)
+
+#if BLARGG_NONPORTABLE
+ #define GB_CPU_OFFSET( addr ) (addr)
+#else
+ #define GB_CPU_OFFSET( addr ) ((addr) & (Gb_Cpu::page_size - 1))
+#endif
+
+inline BOOST::uint8_t* Gb_Cpu::get_code( addr_t addr )
+{
+ return cpu_state_.code_map [GB_CPU_PAGE( addr )] + GB_CPU_OFFSET( addr );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu_run.h b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu_run.h
new file mode 100644
index 00000000..85f190e2
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Cpu_run.h
@@ -0,0 +1,1183 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // time functions can be used
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+
+ // Access of 0xFF00 + offset
+ // 0 <= offset <= 0xFF
+ int READ_IO( int offset );
+ void WRITE_IO( int offset, int data );
+
+ // Often-used instructions use this instead of READ_MEM
+ void READ_FAST( addr_t, int& out );
+
+// The following can be used within macros:
+
+ // Current time
+ cpu_time_t TIME();
+#endif
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// Common instructions:
+//
+// 365880 FA LD A,(nn)
+// 355863 20 JR NZ
+// 313655 21 LD HL,nn
+// 274580 28 JR Z
+// 252878 FE CP n
+// 230541 7E LD A,(HL)
+// 226209 2A LD A,(HL+)
+// 217467 CD CALL
+// 212034 C9 RET
+// 208376 CB CB prefix
+//
+// 27486 CB 7E BIT 7,(HL)
+// 15925 CB 76 BIT 6,(HL)
+// 13035 CB 19 RR C
+// 11557 CB 7F BIT 7,A
+// 10898 CB 37 SWAP A
+// 10208 CB 66 BIT 4,(HL)
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+#define TIME() s.time
+
+#define CODE_PAGE( addr ) s.code_map [GB_CPU_PAGE( addr )]
+#define READ_CODE( addr ) (CODE_PAGE( addr ) [GB_CPU_OFFSET( addr )])
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const z80 = 0x80; // cz
+int const n40 = 0x40; // ph
+int const h20 = 0x20; // ph
+int const c10 = 0x10; // cz
+
+#define SET_FLAGS( in )\
+{\
+ cz = ((in) << 4 & 0x100) + (~(in) >> 7 & 1);\
+ ph = (~(in) << 2 & 0x100) + ((in) >> 1 & 0x10);\
+}
+
+// random bits in cz to catch misuse of them
+#define SET_FLAGS_DEBUG( in )\
+{\
+ cz = ((in) << 4 & 0x100) | (rand() & ~0x1FF) | ((in) & 0x80 ? 0 : (rand() & 0xFF) | 1);\
+ ph = (~(in) << 2 & 0x100) | (((in) >> 1 & 0x10) ^ BYTE( cz ));\
+}
+
+#define GET_FLAGS( out )\
+{\
+ out = (cz >> 4 & c10);\
+ out += ~ph >> 2 & n40;\
+ out += (ph ^ cz) << 1 & h20;\
+ if ( !BYTE( cz ) )\
+ out += z80;\
+}
+
+#define CC_NZ() ( BYTE( cz ))
+#define CC_Z() (!BYTE( cz ))
+#define CC_NC() (!(cz & 0x100))
+#define CC_C() ( cz & 0x100 )
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+{
+ Gb_Cpu::cpu_state_t s;
+ CPU.cpu_state = &s;
+ memcpy( &s, &CPU.cpu_state_, sizeof s );
+
+ union {
+ struct {
+ #if BLARGG_BIG_ENDIAN
+ byte b, c, d, e, h, l, flags, a;
+ #else
+ byte c, b, e, d, l, h, a, flags;
+ #endif
+ } rg; // individual registers
+ Gb_Cpu::core_regs_t rp; // pairs
+
+ byte r8_ [8]; // indexed registers (use R8 macro due to endian dependence)
+ BOOST::uint16_t r16 [4]; // indexed pairs
+ };
+ BLARGG_STATIC_ASSERT( sizeof rg == 8 && sizeof rp == 8 );
+
+ #if BLARGG_BIG_ENDIAN
+ #define R8( n ) (r8_ [n])
+ #elif BLARGG_LITTLE_ENDIAN
+ #define R8( n ) (r8_ [(n) ^ 1])
+ #else
+ // Be sure "blargg_endian.h" has been #included in the file that #includes this
+ #error "Byte order of CPU must be known"
+ #endif
+
+ rp = CPU.r;
+ int pc = CPU.r.pc;
+ int sp = CPU.r.sp;
+ int ph;
+ int cz;
+ SET_FLAGS( rg.flags );
+
+ int time = s.time;
+
+loop:
+
+ check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around
+ check( (unsigned) sp < 0x10000 );
+
+ byte const* instr = CODE_PAGE( pc );
+ int op;
+
+ if ( GB_CPU_OFFSET(~0) == ~0 )
+ {
+ op = instr [pc];
+ pc++;
+ instr += pc;
+ }
+ else
+ {
+ instr += GB_CPU_OFFSET( pc );
+ op = *instr++;
+ pc++;
+ }
+
+#define GET_ADDR() GET_LE16( instr )
+
+ static byte const instr_times [256*2] = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 4,12, 8, 8, 4, 4, 8, 4,20, 8, 8, 8, 4, 4, 8, 4,// 0
+ 4,12, 8, 8, 4, 4, 8, 4,12, 8, 8, 8, 4, 4, 8, 4,// 1
+ 8,12, 8, 8, 4, 4, 8, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 2
+ 8,12, 8, 8,12,12,12, 4, 8, 8, 8, 8, 4, 4, 8, 4,// 3
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 4
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 5
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 6
+ 8, 8, 8, 8, 8, 8, 0, 8, 4, 4, 4, 4, 4, 4, 8, 4,// 7
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 8
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// 9
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// A
+ 4, 4, 4, 4, 4, 4, 8, 4, 4, 4, 4, 4, 4, 4, 8, 4,// B
+ 8,12,16,16,12,16, 8,16, 8,16,16, 0,12,24, 8,16,// C
+ 8,12,16, 0,12,16, 8,16, 8,16,16, 0,12, 0, 8,16,// D
+ 12,12, 8, 0, 0,16, 8,16,16, 4,16, 0, 0, 0, 8,16,// E
+ 12,12, 8, 4, 0,16, 8,16,12, 8,16, 4, 0, 0, 8,16,// F
+
+ // CB prefixed
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 0
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 1
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 2
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 3
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 4
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 5
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 6
+ 8, 8, 8, 8, 8, 8,12, 8, 8, 8, 8, 8, 8, 8,12, 8,// 7
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 8
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// 9
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// A
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// B
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// C
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// D
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// E
+ 8, 8, 8, 8, 8, 8,16, 8, 8, 8, 8, 8, 8, 8,16, 8,// F
+ };
+
+ if ( time >= 0 )
+ goto stop;
+
+ time += instr_times [op];
+
+ int data;
+ data = *instr;
+ s.time = time;
+
+ #ifdef CPU_INSTR_HOOK
+ { CPU_INSTR_HOOK( (pc-1), (instr-1), rg.a, rp.bc, rp.de, rp.hl, sp ); }
+ #endif
+
+ switch ( op )
+ {
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH_( cond, clocks )\
+{\
+ pc++;\
+ if ( !(cond) )\
+ goto loop;\
+ pc = WORD( pc + SBYTE( data ) );\
+ time += clocks;\
+ goto loop;\
+}
+
+#define BRANCH( cond ) BRANCH_( cond, 4 )
+
+// Most Common
+
+ case 0x20: // JR NZ
+ BRANCH( CC_NZ() )
+
+ case 0x21: // LD HL,IMM (common)
+ rp.hl = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0x28: // JR Z
+ BRANCH( CC_Z() )
+
+ case 0xF2: // LD A,(0xFF00+C)
+ READ_IO( rg.c, rg.a );
+ goto loop;
+
+ case 0xF0: // LD A,(0xFF00+imm)
+ pc++;
+ READ_IO( data, rg.a );
+ goto loop;
+
+ {
+ int temp;
+ case 0x0A: // LD A,(BC)
+ temp = rp.bc;
+ goto ld_a_ind_comm;
+
+ case 0x3A: // LD A,(HL-)
+ temp = rp.hl;
+ rp.hl = temp - 1;
+ goto ld_a_ind_comm;
+
+ case 0x1A: // LD A,(DE)
+ temp = rp.de;
+ goto ld_a_ind_comm;
+
+ case 0x2A: // LD A,(HL+) (common)
+ temp = rp.hl;
+ rp.hl = temp + 1;
+ goto ld_a_ind_comm;
+
+ case 0xFA: // LD A,IND16 (common)
+ temp = GET_ADDR();
+ pc += 2;
+ ld_a_ind_comm:
+ READ_FAST( temp, rg.a );
+ goto loop;
+ }
+
+ {
+ int temp;
+ case 0xBE: // CP (HL)
+ temp = READ_MEM( rp.hl );
+ goto cmp_comm;
+
+ case 0xB8: // CP B
+ case 0xB9: // CP C
+ case 0xBA: // CP D
+ case 0xBB: // CP E
+ case 0xBC: // CP H
+ case 0xBD: // CP L
+ case 0xBF: // CP A
+ temp = R8( op & 7 );
+ cmp_comm:
+ ph = rg.a ^ temp; // N=1 H=*
+ cz = rg.a - temp; // C=* Z=*
+ goto loop;
+ }
+
+ case 0xFE: // CP IMM
+ pc++;
+ ph = rg.a ^ data; // N=1 H=*
+ cz = rg.a - data; // C=* Z=*
+ goto loop;
+
+ case 0x46: // LD B,(HL)
+ case 0x4E: // LD C,(HL)
+ case 0x56: // LD D,(HL)
+ case 0x5E: // LD E,(HL)
+ case 0x66: // LD H,(HL)
+ case 0x6E: // LD L,(HL)
+ case 0x7E:{// LD A,(HL)
+ int addr = rp.hl;
+ READ_FAST( addr, R8( op >> 3 & 7 ) );
+ goto loop;
+ }
+
+ case 0xC4: // CNZ (next-most-common)
+ pc += 2;
+ if ( CC_Z() )
+ goto loop;
+ call:
+ time += 12;
+ pc -= 2;
+ case 0xCD: // CALL (most-common)
+ data = pc + 2;
+ pc = GET_ADDR();
+ push: {
+ int addr = WORD( sp - 1 );
+ WRITE_MEM( addr, (data >> 8) );
+ sp = WORD( sp - 2 );
+ WRITE_MEM( sp, data );
+ goto loop;
+ }
+
+ case 0xC8: // RET Z (next-most-common)
+ if ( CC_NZ() )
+ goto loop;
+ ret:
+ time += 12;
+ case 0xD9: // RETI
+ case 0xC9:{// RET (most common)
+ pc = READ_MEM( sp );
+ int addr = sp + 1;
+ sp = WORD( sp + 2 );
+ pc += 0x100 * READ_MEM( addr );
+ goto loop;
+ }
+
+ case 0x00: // NOP
+ case 0x40: // LD B,B
+ case 0x49: // LD C,C
+ case 0x52: // LD D,D
+ case 0x5B: // LD E,E
+ case 0x64: // LD H,H
+ case 0x6D: // LD L,L
+ case 0x7F: // LD A,A
+ goto loop;
+
+// CB Instructions
+
+ case 0xCB:
+ time += (instr_times + 256) [data];
+ pc++;
+ // now data is the opcode
+ switch ( data ) {
+
+ case 0x46: // BIT b,(HL)
+ case 0x4E:
+ case 0x56:
+ case 0x5E:
+ case 0x66:
+ case 0x6E:
+ case 0x76:
+ case 0x7E: {
+ int addr = rp.hl;
+ READ_FAST( addr, op );
+ goto bit_comm;
+ }
+
+ case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r
+ case 0x44: case 0x45: case 0x47: case 0x48:
+ case 0x49: case 0x4A: case 0x4B: case 0x4C:
+ case 0x4D: case 0x4F: case 0x50: case 0x51:
+ case 0x52: case 0x53: case 0x54: case 0x55:
+ case 0x57: case 0x58: case 0x59: case 0x5A:
+ case 0x5B: case 0x5C: case 0x5D: case 0x5F:
+ case 0x60: case 0x61: case 0x62: case 0x63:
+ case 0x64: case 0x65: case 0x67: case 0x68:
+ case 0x69: case 0x6A: case 0x6B: case 0x6C:
+ case 0x6D: case 0x6F: case 0x70: case 0x71:
+ case 0x72: case 0x73: case 0x74: case 0x75:
+ case 0x77: case 0x78: case 0x79: case 0x7A:
+ case 0x7B: case 0x7C: case 0x7D: case 0x7F:
+ op = R8( data & 7 );
+ bit_comm:
+ ph = op >> (data >> 3 & 7) & 1;
+ cz = (cz & 0x100) + ph;
+ ph ^= 0x110; // N=0 H=1
+ goto loop;
+
+ case 0x86: // RES b,(HL)
+ case 0x8E:
+ case 0x96:
+ case 0x9E:
+ case 0xA6:
+ case 0xAE:
+ case 0xB6:
+ case 0xBE: {
+ int temp = READ_MEM( rp.hl );
+ temp &= ~(1 << (data >> 3 & 7));
+ WRITE_MEM( rp.hl, temp );
+ goto loop;
+ }
+
+ case 0xC6: // SET b,(HL)
+ case 0xCE:
+ case 0xD6:
+ case 0xDE:
+ case 0xE6:
+ case 0xEE:
+ case 0xF6:
+ case 0xFE: {
+ int temp = READ_MEM( rp.hl );
+ temp |= 1 << (data >> 3 & 7);
+ WRITE_MEM( rp.hl, temp );
+ goto loop;
+ }
+
+ case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r
+ case 0xC4: case 0xC5: case 0xC7: case 0xC8:
+ case 0xC9: case 0xCA: case 0xCB: case 0xCC:
+ case 0xCD: case 0xCF: case 0xD0: case 0xD1:
+ case 0xD2: case 0xD3: case 0xD4: case 0xD5:
+ case 0xD7: case 0xD8: case 0xD9: case 0xDA:
+ case 0xDB: case 0xDC: case 0xDD: case 0xDF:
+ case 0xE0: case 0xE1: case 0xE2: case 0xE3:
+ case 0xE4: case 0xE5: case 0xE7: case 0xE8:
+ case 0xE9: case 0xEA: case 0xEB: case 0xEC:
+ case 0xED: case 0xEF: case 0xF0: case 0xF1:
+ case 0xF2: case 0xF3: case 0xF4: case 0xF5:
+ case 0xF7: case 0xF8: case 0xF9: case 0xFA:
+ case 0xFB: case 0xFC: case 0xFD: case 0xFF:
+ R8( data & 7 ) |= 1 << (data >> 3 & 7);
+ goto loop;
+
+ case 0x80: case 0x81: case 0x82: case 0x83: // RES b,r
+ case 0x84: case 0x85: case 0x87: case 0x88:
+ case 0x89: case 0x8A: case 0x8B: case 0x8C:
+ case 0x8D: case 0x8F: case 0x90: case 0x91:
+ case 0x92: case 0x93: case 0x94: case 0x95:
+ case 0x97: case 0x98: case 0x99: case 0x9A:
+ case 0x9B: case 0x9C: case 0x9D: case 0x9F:
+ case 0xA0: case 0xA1: case 0xA2: case 0xA3:
+ case 0xA4: case 0xA5: case 0xA7: case 0xA8:
+ case 0xA9: case 0xAA: case 0xAB: case 0xAC:
+ case 0xAD: case 0xAF: case 0xB0: case 0xB1:
+ case 0xB2: case 0xB3: case 0xB4: case 0xB5:
+ case 0xB7: case 0xB8: case 0xB9: case 0xBA:
+ case 0xBB: case 0xBC: case 0xBD: case 0xBF:
+ R8( data & 7 ) &= ~(1 << (data >> 3 & 7));
+ goto loop;
+
+ case 0x36: // SWAP (HL)
+ op = READ_MEM( rp.hl );
+ goto swap_comm;
+
+ case 0x30: // SWAP B
+ case 0x31: // SWAP C
+ case 0x32: // SWAP D
+ case 0x33: // SWAP E
+ case 0x34: // SWAP H
+ case 0x35: // SWAP L
+ case 0x37: // SWAP A
+ op = R8( data & 7 );
+ swap_comm:
+ op = (op >> 4) + (op << 4);
+ cz = BYTE( op );
+ ph = cz + 0x100;
+ if ( data == 0x36 )
+ goto write_hl_op_ff;
+ R8( data & 7 ) = op;
+ goto loop;
+
+// Shift/Rotate
+
+ case 0x26: // SLA (HL)
+ cz = 0;
+ case 0x16: // RL (HL)
+ cz = (cz >> 8 & 1) + (READ_MEM( rp.hl ) << 1);
+ goto rl_hl_common;
+
+ case 0x06: // RLC (HL)
+ cz = READ_MEM( rp.hl );
+ cz = (cz << 1) + (cz >> 7 & 1);
+ rl_hl_common:
+ // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ WRITE_MEM( rp.hl, cz );
+ goto loop;
+
+ case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA r
+ cz = 0;
+ case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL r
+ cz = (cz >> 8 & 1) + (R8( data & 7 ) << 1);
+ goto rl_common;
+
+ case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC r
+ cz = R8( data & 7 );
+ cz = (cz << 1) + (cz >> 7 & 1);
+ rl_common:
+ // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ R8( data & 7 ) = cz;
+ goto loop;
+
+ case 0x0E: // RRC (HL)
+ cz = READ_MEM( rp.hl );
+ cz += cz << 8 & 0x100;
+ goto rr_hl_common;
+
+ case 0x2E: // SRA (HL)
+ cz = READ_MEM( rp.hl );
+ cz += cz << 1 & 0x100;
+ goto rr_hl_common;
+
+ case 0x3E: // SRL (HL)
+ cz = 0;
+ case 0x1E: // RR (HL)
+ cz = (cz & 0x100) + READ_MEM( rp.hl );
+ rr_hl_common:
+ cz = (cz << 8) + (cz >> 1); // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ WRITE_MEM( rp.hl, cz );
+ goto loop;
+
+ case 0x08: case 0x09: case 0x0A: case 0x0B: case 0x0C: case 0x0D: case 0x0F: // RRC r
+ cz = R8( data & 7 );
+ cz += cz << 8 & 0x100;
+ goto rr_common;
+
+ case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA r
+ cz = R8( data & 7 );
+ cz += cz << 1 & 0x100;
+ goto rr_common;
+
+ case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL r
+ cz = 0;
+ case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR r
+ cz = (cz & 0x100) + R8( data & 7 );
+ rr_common:
+ cz = (cz << 8) + (cz >> 1); // Z=* C=*
+ ph = cz | 0x100; // N=0 H=0
+ R8( data & 7 ) = cz;
+ goto loop;
+
+ } // CB op
+ assert( false ); // unhandled CB op
+
+ case 0x07: // RLCA
+ cz = rg.a >> 7;
+ goto rlc_common;
+ case 0x17: // RLA
+ cz = cz >> 8 & 1;
+ rlc_common:
+ cz += rg.a << 1;
+ ph = cz | 0x100;
+ rg.a = BYTE( cz );
+ cz |= 1;
+ goto loop;
+
+ case 0x0F: // RRCA
+ ph = rg.a << 8;
+ goto rrc_common;
+ case 0x1F: // RRA
+ ph = cz;
+ rrc_common:
+ cz = (rg.a << 8) + 1; // Z=0 C=*
+ rg.a = ((ph & 0x100) + rg.a) >> 1;
+ ph = 0x100; // N=0 H=0
+ goto loop;
+
+// Load
+
+ case 0x70: // LD (HL),B
+ case 0x71: // LD (HL),C
+ case 0x72: // LD (HL),D
+ case 0x73: // LD (HL),E
+ case 0x74: // LD (HL),H
+ case 0x75: // LD (HL),L
+ case 0x77: // LD (HL),A
+ op = R8( op & 7 );
+ write_hl_op_ff:
+ WRITE_MEM( rp.hl, op );
+ goto loop;
+
+ case 0x41: case 0x42: case 0x43: case 0x44: case 0x45: case 0x47: // LD r,r
+ case 0x48: case 0x4A: case 0x4B: case 0x4C: case 0x4D: case 0x4F:
+ case 0x50: case 0x51: case 0x53: case 0x54: case 0x55: case 0x57:
+ case 0x58: case 0x59: case 0x5A: case 0x5C: case 0x5D: case 0x5F:
+ case 0x60: case 0x61: case 0x62: case 0x63: case 0x65: case 0x67:
+ case 0x68: case 0x69: case 0x6A: case 0x6B: case 0x6C: case 0x6F:
+ case 0x78: case 0x79: case 0x7A: case 0x7B: case 0x7C: case 0x7D:
+ R8( op >> 3 & 7 ) = R8( op & 7 );
+ goto loop;
+
+ case 0x08: // LD IND16,SP
+ data = GET_ADDR();
+ pc += 2;
+ WRITE_MEM( data, sp );
+ data++;
+ WRITE_MEM( data, (sp >> 8) );
+ goto loop;
+
+ case 0xF9: // LD SP,HL
+ sp = rp.hl;
+ goto loop;
+
+ case 0x31: // LD SP,IMM
+ sp = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0x01: // LD BC,IMM
+ case 0x11: // LD DE,IMM
+ r16 [(unsigned) op >> 4] = GET_ADDR();
+ pc += 2;
+ goto loop;
+
+ case 0xE2: // LD (0xFF00+C),A
+ WRITE_IO( rg.c, rg.a );
+ goto loop;
+
+ case 0xE0: // LD (0xFF00+imm),A
+ pc++;
+ WRITE_IO( data, rg.a );
+ goto loop;
+
+ {
+ int temp;
+ case 0x32: // LD (HL-),A
+ temp = rp.hl;
+ rp.hl = temp - 1;
+ goto write_data_rg_a;
+
+ case 0x02: // LD (BC),A
+ temp = rp.bc;
+ goto write_data_rg_a;
+
+ case 0x12: // LD (DE),A
+ temp = rp.de;
+ goto write_data_rg_a;
+
+ case 0x22: // LD (HL+),A
+ temp = rp.hl;
+ rp.hl = temp + 1;
+ goto write_data_rg_a;
+
+ case 0xEA: // LD IND16,A (common)
+ temp = GET_ADDR();
+ pc += 2;
+ write_data_rg_a:
+ WRITE_MEM( temp, rg.a );
+ goto loop;
+ }
+
+ case 0x06: // LD B,IMM
+ rg.b = data;
+ pc++;
+ goto loop;
+
+ case 0x0E: // LD C,IMM
+ rg.c = data;
+ pc++;
+ goto loop;
+
+ case 0x16: // LD D,IMM
+ rg.d = data;
+ pc++;
+ goto loop;
+
+ case 0x1E: // LD E,IMM
+ rg.e = data;
+ pc++;
+ goto loop;
+
+ case 0x26: // LD H,IMM
+ rg.h = data;
+ pc++;
+ goto loop;
+
+ case 0x2E: // LD L,IMM
+ rg.l = data;
+ pc++;
+ goto loop;
+
+ case 0x36: // LD (HL),IMM
+ WRITE_MEM( rp.hl, data );
+ pc++;
+ goto loop;
+
+ case 0x3E: // LD A,IMM
+ rg.a = data;
+ pc++;
+ goto loop;
+
+// Increment/decrement
+
+ case 0x03: // INC BC
+ case 0x13: // INC DE
+ case 0x23: // INC HL
+ r16 [(unsigned) op >> 4]++;
+ goto loop;
+
+ case 0x33: // INC SP
+ sp = WORD( sp + 1 );
+ goto loop;
+
+ case 0x0B: // DEC BC
+ case 0x1B: // DEC DE
+ case 0x2B: // DEC HL
+ r16 [(unsigned) op >> 4]--;
+ goto loop;
+
+ case 0x3B: // DEC SP
+ sp = WORD( sp - 1 );
+ goto loop;
+
+ case 0x34: // INC (HL)
+ op = rp.hl;
+ data = READ_MEM( op );
+ data++;
+ WRITE_MEM( op, data );
+ goto inc_comm;
+
+ case 0x04: // INC B
+ case 0x0C: // INC C (common)
+ case 0x14: // INC D
+ case 0x1C: // INC E
+ case 0x24: // INC H
+ case 0x2C: // INC L
+ case 0x3C: // INC A
+ op = op >> 3 & 7;
+ data = R8( op ) + 1;
+ R8( op ) = data;
+ inc_comm:
+ ph = data - 0x101; // N=0 H=*
+ cz = (cz & 0x100) + BYTE( data ); // C=- Z=*
+ goto loop;
+
+ case 0x35: // DEC (HL)
+ op = rp.hl;
+ data = READ_MEM( op );
+ data--;
+ WRITE_MEM( op, data );
+ goto dec_comm;
+
+ case 0x05: // DEC B
+ case 0x0D: // DEC C
+ case 0x15: // DEC D
+ case 0x1D: // DEC E
+ case 0x25: // DEC H
+ case 0x2D: // DEC L
+ case 0x3D: // DEC A
+ op = op >> 3 & 7;
+ data = R8( op ) - 1;
+ R8( op ) = data;
+ dec_comm:
+ ph = data + 1; // N=1 H=*
+ cz = (cz & 0x100) + BYTE( data ); // C=- Z=*
+ goto loop;
+
+// Add 16-bit
+
+ case 0xF8: // LD HL,SP+n
+ case 0xE8:{// ADD SP,n
+ pc++;
+ int t = WORD( sp + SBYTE( data ) );
+ cz = ((BYTE( sp ) + data) & 0x100) + 1; // Z=0 C=*
+ ph = (sp ^ data ^ t) | 0x100; // N=0 H=*
+ if ( op == 0xF8 )
+ {
+ rp.hl = t;
+ goto loop;
+ }
+ sp = t;
+ goto loop;
+ }
+
+ case 0x39: // ADD HL,SP
+ data = sp;
+ goto add_hl_comm;
+
+ case 0x09: // ADD HL,BC
+ case 0x19: // ADD HL,DE
+ case 0x29: // ADD HL,HL
+ data = r16 [(unsigned) op >> 4];
+ add_hl_comm:
+ ph = rp.hl ^ data;
+ data += rp.hl;
+ rp.hl = WORD( data );
+ ph ^= data;
+ cz = BYTE( cz ) + (data >> 8 & 0x100); // C=* Z=-
+ ph = ((ph >> 8) ^ cz) | 0x100; // N=0 H=*
+ goto loop;
+
+ case 0x86: // ADD (HL)
+ data = READ_MEM( rp.hl );
+ goto add_comm;
+
+ case 0x80: // ADD B
+ case 0x81: // ADD C
+ case 0x82: // ADD D
+ case 0x83: // ADD E
+ case 0x84: // ADD H
+ case 0x85: // ADD L
+ case 0x87: // ADD A
+ data = R8( op & 7 );
+ goto add_comm;
+
+ case 0xC6: // ADD IMM
+ pc++;
+ add_comm:
+ ph = (rg.a ^ data) | 0x100; // N=1 H=*
+ cz = rg.a + data; // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+// Add/Subtract
+
+ case 0x8E: // ADC (HL)
+ data = READ_MEM( rp.hl );
+ goto adc_comm;
+
+ case 0x88: // ADC B
+ case 0x89: // ADC C
+ case 0x8A: // ADC D
+ case 0x8B: // ADC E
+ case 0x8C: // ADC H
+ case 0x8D: // ADC L
+ case 0x8F: // ADC A
+ data = R8( op & 7 );
+ goto adc_comm;
+
+ case 0xCE: // ADC IMM
+ pc++;
+ adc_comm:
+ ph = (rg.a ^ data) | 0x100; // N=1 H=*
+ cz = rg.a + data + (cz >> 8 & 1); // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+ case 0x96: // SUB (HL)
+ data = READ_MEM( rp.hl );
+ goto sub_comm;
+
+ case 0x90: // SUB B
+ case 0x91: // SUB C
+ case 0x92: // SUB D
+ case 0x93: // SUB E
+ case 0x94: // SUB H
+ case 0x95: // SUB L
+ case 0x97: // SUB A
+ data = R8( op & 7 );
+ goto sub_comm;
+
+ case 0xD6: // SUB IMM
+ pc++;
+ sub_comm:
+ ph = rg.a ^ data; // N=1 H=*
+ cz = rg.a - data; // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+ case 0x9E: // SBC (HL)
+ data = READ_MEM( rp.hl );
+ goto sbc_comm;
+
+ case 0x98: // SBC B
+ case 0x99: // SBC C
+ case 0x9A: // SBC D
+ case 0x9B: // SBC E
+ case 0x9C: // SBC H
+ case 0x9D: // SBC L
+ case 0x9F: // SBC A
+ data = R8( op & 7 );
+ goto sbc_comm;
+
+ case 0xDE: // SBC IMM
+ pc++;
+ sbc_comm:
+ ph = rg.a ^ data; // N=1 H=*
+ cz = rg.a - data - (cz >> 8 & 1); // C=* Z=*
+ rg.a = cz;
+ goto loop;
+
+// Logical
+
+ case 0xA0: // AND B
+ case 0xA1: // AND C
+ case 0xA2: // AND D
+ case 0xA3: // AND E
+ case 0xA4: // AND H
+ case 0xA5: // AND L
+ data = R8( op & 7 );
+ goto and_comm;
+
+ case 0xA6: // AND (HL)
+ data = READ_MEM( rp.hl );
+ goto and_comm;
+ case 0xE6: // AND IMM
+ pc++;
+ and_comm:
+ cz = rg.a & data; // C=0 Z=*
+ ph = ~cz; // N=0 H=1
+ rg.a = cz;
+ goto loop;
+
+ case 0xA7: // AND A
+ cz = rg.a; // C=0 Z=*
+ ph = ~rg.a; // N=0 H=1
+ goto loop;
+
+ case 0xB0: // OR B
+ case 0xB1: // OR C
+ case 0xB2: // OR D
+ case 0xB3: // OR E
+ case 0xB4: // OR H
+ case 0xB5: // OR L
+ data = R8( op & 7 );
+ goto or_comm;
+
+ case 0xB6: // OR (HL)
+ data = READ_MEM( rp.hl );
+ goto or_comm;
+ case 0xF6: // OR IMM
+ pc++;
+ or_comm:
+ cz = rg.a | data; // C=0 Z=*
+ ph = cz | 0x100; // N=0 H=0
+ rg.a = cz;
+ goto loop;
+
+ case 0xB7: // OR A
+ cz = rg.a; // C=0 Z=*
+ ph = rg.a + 0x100; // N=0 H=0
+ goto loop;
+
+ case 0xA8: // XOR B
+ case 0xA9: // XOR C
+ case 0xAA: // XOR D
+ case 0xAB: // XOR E
+ case 0xAC: // XOR H
+ case 0xAD: // XOR L
+ data = R8( op & 7 );
+ goto xor_comm;
+
+ case 0xAE: // XOR (HL)
+ data = READ_MEM( rp.hl );
+ pc--;
+ case 0xEE: // XOR IMM
+ pc++;
+ xor_comm:
+ cz = rg.a ^ data; // C=0 Z=*
+ ph = cz + 0x100; // N=0 H=0
+ rg.a = cz;
+ goto loop;
+
+ case 0xAF: // XOR A
+ rg.a = 0;
+ cz = 0; // C=0 Z=*
+ ph = 0x100; // N=0 H=0
+ goto loop;
+
+// Stack
+
+ case 0xF1: // POP AF
+ case 0xC1: // POP BC
+ case 0xD1: // POP DE
+ case 0xE1: // POP HL (common)
+ data = READ_MEM( sp );
+ r16 [op >> 4 & 3] = data + 0x100 * READ_MEM( (sp + 1) );
+ sp = WORD( sp + 2 );
+ if ( op != 0xF1 )
+ goto loop;
+
+ SET_FLAGS( rg.a );
+ rg.a = rg.flags;
+ goto loop;
+
+ case 0xC5: // PUSH BC
+ data = rp.bc;
+ goto push;
+
+ case 0xD5: // PUSH DE
+ data = rp.de;
+ goto push;
+
+ case 0xE5: // PUSH HL
+ data = rp.hl;
+ goto push;
+
+ case 0xF5: // PUSH AF
+ GET_FLAGS( data );
+ data += rg.a << 8;
+ goto push;
+
+// Flow control
+
+ case 0xFF: case 0xC7: case 0xCF: case 0xD7: // RST
+ case 0xDF: case 0xE7: case 0xEF: case 0xF7:
+ data = pc;
+ pc = (op & 0x38) + CPU.rst_base;
+ goto push;
+
+ case 0xCC: // CALL Z
+ pc += 2;
+ if ( CC_Z() )
+ goto call;
+ goto loop;
+
+ case 0xD4: // CALL NC
+ pc += 2;
+ if ( CC_NC() )
+ goto call;
+ goto loop;
+
+ case 0xDC: // CALL C
+ pc += 2;
+ if ( CC_C() )
+ goto call;
+ goto loop;
+
+ case 0xC0: // RET NZ
+ if ( CC_NZ() )
+ goto ret;
+ goto loop;
+
+ case 0xD0: // RET NC
+ if ( CC_NC() )
+ goto ret;
+ goto loop;
+
+ case 0xD8: // RET C
+ if ( CC_C() )
+ goto ret;
+ goto loop;
+
+ case 0x18: // JR
+ BRANCH_( true, 0 )
+
+ case 0x30: // JR NC
+ BRANCH( CC_NC() )
+
+ case 0x38: // JR C
+ BRANCH( CC_C() )
+
+ case 0xE9: // LD PC,HL
+ pc = rp.hl;
+ goto loop;
+
+ case 0xC3: // JP (next-most-common)
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0xC2: // JP NZ
+ pc += 2;
+ if ( CC_NZ() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+ case 0xCA: // JP Z (most common)
+ pc += 2;
+ if ( CC_Z() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+ jp_taken:
+ pc -= 2;
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0xD2: // JP NC
+ pc += 2;
+ if ( CC_NC() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+ case 0xDA: // JP C
+ pc += 2;
+ if ( CC_C() )
+ goto jp_taken;
+ time -= 4;
+ goto loop;
+
+// Flags
+
+ case 0x2F: // CPL
+ rg.a = ~rg.a;
+ ph = BYTE( ~cz ); // N=1 H=1
+ goto loop;
+
+ case 0x3F: // CCF
+ ph = cz | 0x100; // N=0 H=0
+ cz ^= 0x100; // C=* Z=-
+ goto loop;
+
+ case 0x37: // SCF
+ ph = cz | 0x100; // N=0 H=0
+ cz |= 0x100; // C=1 Z=-
+ goto loop;
+
+ case 0xF3: // DI
+ goto loop;
+
+ case 0xFB: // EI
+ goto loop;
+
+ case 0x27:{// DAA
+ unsigned a = rg.a;
+ int h = ph ^ cz;
+ if ( ph & 0x100 )
+ {
+ if ( (h & 0x10) || (a & 0x0F) > 9 )
+ a += 6;
+
+ if ( (cz & 0x100) || a > 0x9F )
+ a += 0x60;
+ }
+ else
+ {
+ if ( h & 0x10 )
+ a = (a - 6) & 0xFF;
+
+ if ( cz & 0x100 )
+ a -= 0x60;
+ }
+ cz = (cz & 0x100) | a; // C=- Z=*
+ rg.a = a;
+ ph = (ph & 0x100) + BYTE( a ); // N=- H=0
+ goto loop;
+ }
+
+// Special
+
+ case 0x76: // HALT
+ case 0x10: // STOP
+ case 0xD3: case 0xDB: case 0xDD: // Illegal
+ case 0xE3: case 0xE4: case 0xEB: case 0xEC: case 0xED: // (all freeze CPU)
+ case 0xF4: case 0xFC: case 0xFD:
+ goto stop;
+ }
+
+ // If this fails then an opcode isn't handled above
+ assert( false );
+
+stop:
+ pc--;
+
+ // copy state back
+ CPU.cpu_state_.time = time;
+ CPU.r.pc = pc;
+ CPU.r.sp = sp;
+ {
+ int t;
+ GET_FLAGS( t );
+ rg.flags = t;
+ }
+ CPU.cpu_state = &CPU.cpu_state_;
+ STATIC_CAST(Gb_Cpu::core_regs_t&,CPU.r) = rp;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.cpp
new file mode 100644
index 00000000..b3a8a6ed
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.cpp
@@ -0,0 +1,712 @@
+// Gb_Snd_Emu 0.1.4. http://www.slack.net/~ant/
+
+#include "Gb_Apu.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+bool const cgb_02 = false; // enables bug in early CGB units that causes problems in some games
+bool const cgb_05 = false; // enables CGB-05 zombie behavior
+
+int const trigger_mask = 0x80;
+int const length_enabled = 0x40;
+
+void Gb_Osc::reset()
+{
+ output = NULL;
+ last_amp = 0;
+ delay = 0;
+ phase = 0;
+ enabled = false;
+}
+
+inline void Gb_Osc::update_amp( blip_time_t time, int new_amp )
+{
+ output->set_modified();
+ int delta = new_amp - last_amp;
+ if ( delta )
+ {
+ last_amp = new_amp;
+ fast_synth->offset( time, delta, output );
+ }
+}
+
+// Units
+
+void Gb_Osc::clock_length()
+{
+ if ( (regs [4] & length_enabled) && length_ctr )
+ {
+ if ( --length_ctr <= 0 )
+ enabled = false;
+ }
+}
+
+inline int Gb_Env::reload_env_timer()
+{
+ int raw = regs [2] & 7;
+ env_delay = (raw ? raw : 8);
+ return raw;
+}
+
+void Gb_Env::clock_envelope()
+{
+ if ( env_enabled && --env_delay <= 0 && reload_env_timer() )
+ {
+ int v = volume + (regs [2] & 0x08 ? +1 : -1);
+ if ( 0 <= v && v <= 15 )
+ volume = v;
+ else
+ env_enabled = false;
+ }
+}
+
+inline void Gb_Sweep_Square::reload_sweep_timer()
+{
+ sweep_delay = (regs [0] & period_mask) >> 4;
+ if ( !sweep_delay )
+ sweep_delay = 8;
+}
+
+void Gb_Sweep_Square::calc_sweep( bool update )
+{
+ int const shift = regs [0] & shift_mask;
+ int const delta = sweep_freq >> shift;
+ sweep_neg = (regs [0] & 0x08) != 0;
+ int const freq = sweep_freq + (sweep_neg ? -delta : delta);
+
+ if ( freq > 0x7FF )
+ {
+ enabled = false;
+ }
+ else if ( shift && update )
+ {
+ sweep_freq = freq;
+
+ regs [3] = freq & 0xFF;
+ regs [4] = (regs [4] & ~0x07) | (freq >> 8 & 0x07);
+ }
+}
+
+void Gb_Sweep_Square::clock_sweep()
+{
+ if ( --sweep_delay <= 0 )
+ {
+ reload_sweep_timer();
+ if ( sweep_enabled && (regs [0] & period_mask) )
+ {
+ calc_sweep( true );
+ calc_sweep( false );
+ }
+ }
+}
+
+int Gb_Wave::access( int addr ) const
+{
+ if ( enabled )
+ {
+ addr = phase & (bank_size - 1);
+ if ( mode == Gb_Apu::mode_dmg )
+ {
+ addr++;
+ if ( delay > clk_mul )
+ return -1; // can only access within narrow time window while playing
+ }
+ addr >>= 1;
+ }
+ return addr & 0x0F;
+}
+
+// write_register
+
+int Gb_Osc::write_trig( int frame_phase, int max_len, int old_data )
+{
+ int data = regs [4];
+
+ if ( (frame_phase & 1) && !(old_data & length_enabled) && length_ctr )
+ {
+ if ( (data & length_enabled) || cgb_02 )
+ length_ctr--;
+ }
+
+ if ( data & trigger_mask )
+ {
+ enabled = true;
+ if ( !length_ctr )
+ {
+ length_ctr = max_len;
+ if ( (frame_phase & 1) && (data & length_enabled) )
+ length_ctr--;
+ }
+ }
+
+ if ( !length_ctr )
+ enabled = false;
+
+ return data & trigger_mask;
+}
+
+inline void Gb_Env::zombie_volume( int old, int data )
+{
+ int v = volume;
+ if ( mode == Gb_Apu::mode_agb || cgb_05 )
+ {
+ // CGB-05 behavior, very close to AGB behavior as well
+ if ( (old ^ data) & 8 )
+ {
+ if ( !(old & 8) )
+ {
+ v++;
+ if ( old & 7 )
+ v++;
+ }
+
+ v = 16 - v;
+ }
+ else if ( (old & 0x0F) == 8 )
+ {
+ v++;
+ }
+ }
+ else
+ {
+ // CGB-04&02 behavior, very close to MGB behavior as well
+ if ( !(old & 7) && env_enabled )
+ v++;
+ else if ( !(old & 8) )
+ v += 2;
+
+ if ( (old ^ data) & 8 )
+ v = 16 - v;
+ }
+ volume = v & 0x0F;
+}
+
+bool Gb_Env::write_register( int frame_phase, int reg, int old, int data )
+{
+ int const max_len = 64;
+
+ switch ( reg )
+ {
+ case 1:
+ length_ctr = max_len - (data & (max_len - 1));
+ break;
+
+ case 2:
+ if ( !dac_enabled() )
+ enabled = false;
+
+ zombie_volume( old, data );
+
+ if ( (data & 7) && env_delay == 8 )
+ {
+ env_delay = 1;
+ clock_envelope(); // TODO: really happens at next length clock
+ }
+ break;
+
+ case 4:
+ if ( write_trig( frame_phase, max_len, old ) )
+ {
+ volume = regs [2] >> 4;
+ reload_env_timer();
+ env_enabled = true;
+ if ( frame_phase == 7 )
+ env_delay++;
+ if ( !dac_enabled() )
+ enabled = false;
+ return true;
+ }
+ }
+ return false;
+}
+
+bool Gb_Square::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ bool result = Gb_Env::write_register( frame_phase, reg, old_data, data );
+ if ( result )
+ delay = (delay & (4 * clk_mul - 1)) + period();
+ return result;
+}
+
+inline void Gb_Noise::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ if ( Gb_Env::write_register( frame_phase, reg, old_data, data ) )
+ {
+ phase = 0x7FFF;
+ delay += 8 * clk_mul;
+ }
+}
+
+inline void Gb_Sweep_Square::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ if ( reg == 0 && sweep_enabled && sweep_neg && !(data & 0x08) )
+ enabled = false; // sweep negate disabled after used
+
+ if ( Gb_Square::write_register( frame_phase, reg, old_data, data ) )
+ {
+ sweep_freq = frequency();
+ sweep_neg = false;
+ reload_sweep_timer();
+ sweep_enabled = (regs [0] & (period_mask | shift_mask)) != 0;
+ if ( regs [0] & shift_mask )
+ calc_sweep( false );
+ }
+}
+
+void Gb_Wave::corrupt_wave()
+{
+ int pos = ((phase + 1) & (bank_size - 1)) >> 1;
+ if ( pos < 4 )
+ wave_ram [0] = wave_ram [pos];
+ else
+ for ( int i = 4; --i >= 0; )
+ wave_ram [i] = wave_ram [(pos & ~3) + i];
+}
+
+inline void Gb_Wave::write_register( int frame_phase, int reg, int old_data, int data )
+{
+ int const max_len = 256;
+
+ switch ( reg )
+ {
+ case 0:
+ if ( !dac_enabled() )
+ enabled = false;
+ break;
+
+ case 1:
+ length_ctr = max_len - data;
+ break;
+
+ case 4:
+ bool was_enabled = enabled;
+ if ( write_trig( frame_phase, max_len, old_data ) )
+ {
+ if ( !dac_enabled() )
+ enabled = false;
+ else if ( mode == Gb_Apu::mode_dmg && was_enabled &&
+ (unsigned) (delay - 2 * clk_mul) < 2 * clk_mul )
+ corrupt_wave();
+
+ phase = 0;
+ delay = period() + 6 * clk_mul;
+ }
+ }
+}
+
+void Gb_Apu::write_osc( int reg, int old_data, int data )
+{
+ int index = (reg * 3 + 3) >> 4; // avoids divide
+ assert( index == reg / 5 );
+ reg -= index * 5;
+ switch ( index )
+ {
+ case 0: square1.write_register( frame_phase, reg, old_data, data ); break;
+ case 1: square2.write_register( frame_phase, reg, old_data, data ); break;
+ case 2: wave .write_register( frame_phase, reg, old_data, data ); break;
+ case 3: noise .write_register( frame_phase, reg, old_data, data ); break;
+ }
+}
+
+// Synthesis
+
+void Gb_Square::run( blip_time_t time, blip_time_t end_time )
+{
+ // Calc duty and phase
+ static byte const duty_offsets [4] = { 1, 1, 3, 7 };
+ static byte const duties [4] = { 1, 2, 4, 6 };
+ int const duty_code = regs [1] >> 6;
+ int duty_offset = duty_offsets [duty_code];
+ int duty = duties [duty_code];
+ if ( mode == Gb_Apu::mode_agb )
+ {
+ // AGB uses inverted duty
+ duty_offset -= duty;
+ duty = 8 - duty;
+ }
+ int ph = (this->phase + duty_offset) & 7;
+
+ // Determine what will be generated
+ int vol = 0;
+ Blip_Buffer* const out = this->output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( dac_enabled() )
+ {
+ if ( enabled )
+ vol = this->volume;
+
+ amp = -dac_bias;
+ if ( mode == Gb_Apu::mode_agb )
+ amp = -(vol >> 1);
+
+ // Play inaudible frequencies as constant amplitude
+ if ( frequency() >= 0x7FA && delay < 32 * clk_mul )
+ {
+ amp += (vol * duty) >> 3;
+ vol = 0;
+ }
+
+ if ( ph < duty )
+ {
+ amp += vol;
+ vol = -vol;
+ }
+ }
+ update_amp( time, amp );
+ }
+
+ // Generate wave
+ time += delay;
+ if ( time < end_time )
+ {
+ int const per = this->period();
+ if ( !vol )
+ {
+ #if GB_APU_FAST
+ time = end_time;
+ #else
+ // Maintain phase when not playing
+ int count = (end_time - time + per - 1) / per;
+ ph += count; // will be masked below
+ time += (blip_time_t) count * per;
+ #endif
+ }
+ else
+ {
+ // Output amplitude transitions
+ int delta = vol;
+ do
+ {
+ ph = (ph + 1) & 7;
+ if ( ph == 0 || ph == duty )
+ {
+ norm_synth->offset_inline( time, delta, out );
+ delta = -delta;
+ }
+ time += per;
+ }
+ while ( time < end_time );
+
+ if ( delta != vol )
+ last_amp -= delta;
+ }
+ this->phase = (ph - duty_offset) & 7;
+ }
+ delay = time - end_time;
+}
+
+#if !GB_APU_FAST
+// Quickly runs LFSR for a large number of clocks. For use when noise is generating
+// no sound.
+static unsigned run_lfsr( unsigned s, unsigned mask, int count )
+{
+ bool const optimized = true; // set to false to use only unoptimized loop in middle
+
+ // optimization used in several places:
+ // ((s & (1 << b)) << n) ^ ((s & (1 << b)) << (n + 1)) = (s & (1 << b)) * (3 << n)
+
+ if ( mask == 0x4000 && optimized )
+ {
+ if ( count >= 32767 )
+ count %= 32767;
+
+ // Convert from Fibonacci to Galois configuration,
+ // shifted left 1 bit
+ s ^= (s & 1) * 0x8000;
+
+ // Each iteration is equivalent to clocking LFSR 255 times
+ while ( (count -= 255) > 0 )
+ s ^= ((s & 0xE) << 12) ^ ((s & 0xE) << 11) ^ (s >> 3);
+ count += 255;
+
+ // Each iteration is equivalent to clocking LFSR 15 times
+ // (interesting similarity to single clocking below)
+ while ( (count -= 15) > 0 )
+ s ^= ((s & 2) * (3 << 13)) ^ (s >> 1);
+ count += 15;
+
+ // Remaining singles
+ while ( --count >= 0 )
+ s = ((s & 2) * (3 << 13)) ^ (s >> 1);
+
+ // Convert back to Fibonacci configuration
+ s &= 0x7FFF;
+ }
+ else if ( count < 8 || !optimized )
+ {
+ // won't fully replace upper 8 bits, so have to do the unoptimized way
+ while ( --count >= 0 )
+ s = (s >> 1 | mask) ^ (mask & -((s - 1) & 2));
+ }
+ else
+ {
+ if ( count > 127 )
+ {
+ count %= 127;
+ if ( !count )
+ count = 127; // must run at least once
+ }
+
+ // Need to keep one extra bit of history
+ s = s << 1 & 0xFF;
+
+ // Convert from Fibonacci to Galois configuration,
+ // shifted left 2 bits
+ s ^= (s & 2) * 0x80;
+
+ // Each iteration is equivalent to clocking LFSR 7 times
+ // (interesting similarity to single clocking below)
+ while ( (count -= 7) > 0 )
+ s ^= ((s & 4) * (3 << 5)) ^ (s >> 1);
+ count += 7;
+
+ // Remaining singles
+ while ( --count >= 0 )
+ s = ((s & 4) * (3 << 5)) ^ (s >> 1);
+
+ // Convert back to Fibonacci configuration and
+ // repeat last 8 bits above significant 7
+ s = (s << 7 & 0x7F80) | (s >> 1 & 0x7F);
+ }
+
+ return s;
+}
+#endif
+
+void Gb_Noise::run( blip_time_t time, blip_time_t end_time )
+{
+ // Determine what will be generated
+ int vol = 0;
+ Blip_Buffer* const out = this->output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( dac_enabled() )
+ {
+ if ( enabled )
+ vol = this->volume;
+
+ amp = -dac_bias;
+ if ( mode == Gb_Apu::mode_agb )
+ amp = -(vol >> 1);
+
+ if ( !(phase & 1) )
+ {
+ amp += vol;
+ vol = -vol;
+ }
+ }
+
+ // AGB negates final output
+ if ( mode == Gb_Apu::mode_agb )
+ {
+ vol = -vol;
+ amp = -amp;
+ }
+
+ update_amp( time, amp );
+ }
+
+ // Run timer and calculate time of next LFSR clock
+ static byte const period1s [8] = { 1, 2, 4, 6, 8, 10, 12, 14 };
+ int const period1 = period1s [regs [3] & 7] * clk_mul;
+
+ #if GB_APU_FAST
+ time += delay;
+ #else
+ {
+ int extra = (end_time - time) - delay;
+ int const per2 = this->period2();
+ time += delay + ((divider ^ (per2 >> 1)) & (per2 - 1)) * period1;
+
+ int count = (extra < 0 ? 0 : (extra + period1 - 1) / period1);
+ divider = (divider - count) & period2_mask;
+ delay = count * period1 - extra;
+ }
+ #endif
+
+ // Generate wave
+ if ( time < end_time )
+ {
+ unsigned const mask = this->lfsr_mask();
+ unsigned bits = this->phase;
+
+ int per = period2( period1 * 8 );
+ #if GB_APU_FAST
+ // Noise can be THE biggest time hog; adjust as necessary
+ int const min_period = 24;
+ if ( per < min_period )
+ per = min_period;
+ #endif
+ if ( period2_index() >= 0xE )
+ {
+ time = end_time;
+ }
+ else if ( !vol )
+ {
+ #if GB_APU_FAST
+ time = end_time;
+ #else
+ // Maintain phase when not playing
+ int count = (end_time - time + per - 1) / per;
+ time += (blip_time_t) count * per;
+ bits = run_lfsr( bits, ~mask, count );
+ #endif
+ }
+ else
+ {
+ Blip_Synth_Fast const* const synth = fast_synth; // cache
+
+ // Output amplitude transitions
+ int delta = -vol;
+ do
+ {
+ unsigned changed = bits + 1;
+ bits = bits >> 1 & mask;
+ if ( changed & 2 )
+ {
+ bits |= ~mask;
+ delta = -delta;
+ synth->offset_inline( time, delta, out );
+ }
+ time += per;
+ }
+ while ( time < end_time );
+
+ if ( delta == vol )
+ last_amp += delta;
+ }
+ this->phase = bits;
+ }
+
+ #if GB_APU_FAST
+ delay = time - end_time;
+ #endif
+}
+
+void Gb_Wave::run( blip_time_t time, blip_time_t end_time )
+{
+ // Calc volume
+#if GB_APU_NO_AGB
+ static byte const shifts [4] = { 4+4, 0+4, 1+4, 2+4 };
+ int const volume_idx = regs [2] >> 5 & 3;
+ int const volume_shift = shifts [volume_idx];
+ int const volume_mul = 1;
+#else
+ static byte const volumes [8] = { 0, 4, 2, 1, 3, 3, 3, 3 };
+ int const volume_shift = 2 + 4;
+ int const volume_idx = regs [2] >> 5 & (agb_mask | 3); // 2 bits on DMG/CGB, 3 on AGB
+ int const volume_mul = volumes [volume_idx];
+#endif
+
+ // Determine what will be generated
+ int playing = false;
+ Blip_Buffer* const out = this->output;
+ if ( out )
+ {
+ int amp = dac_off_amp;
+ if ( dac_enabled() )
+ {
+ // Play inaudible frequencies as constant amplitude
+ amp = 8 << 4; // really depends on average of all samples in wave
+
+ // if delay is larger, constant amplitude won't start yet
+ if ( frequency() <= 0x7FB || delay > 15 * clk_mul )
+ {
+ if ( volume_mul && volume_shift != 4+4 )
+ playing = (int) enabled;
+
+ amp = (sample_buf << (phase << 2 & 4) & 0xF0) * playing;
+ }
+
+ amp = ((amp * volume_mul) >> volume_shift) - dac_bias;
+ }
+ update_amp( time, amp );
+ }
+
+ // Generate wave
+ time += delay;
+ if ( time < end_time )
+ {
+ byte const* wave = this->wave_ram;
+
+ // wave size and bank
+ #if GB_APU_NO_AGB
+ int const wave_mask = 0x1F;
+ int const swap_banks = 0;
+ #else
+ int const size20_mask = 0x20;
+ int const flags = regs [0] & agb_mask;
+ int const wave_mask = (flags & size20_mask) | 0x1F;
+ int swap_banks = 0;
+ if ( flags & bank40_mask )
+ {
+ swap_banks = flags & size20_mask;
+ wave += bank_size/2 - (swap_banks >> 1);
+ }
+ #endif
+
+ int ph = this->phase ^ swap_banks;
+ ph = (ph + 1) & wave_mask; // pre-advance
+
+ int const per = this->period();
+ if ( !playing )
+ {
+ #if GB_APU_FAST
+ time = end_time;
+ #else
+ // Maintain phase when not playing
+ int count = (end_time - time + per - 1) / per;
+ ph += count; // will be masked below
+ time += (blip_time_t) count * per;
+ #endif
+ }
+ else
+ {
+ Blip_Synth_Fast const* const synth = fast_synth; // cache
+
+ // Output amplitude transitions
+ int lamp = this->last_amp + dac_bias;
+ do
+ {
+ // Extract nibble
+ int nibble = wave [ph >> 1] << (ph << 2 & 4) & 0xF0;
+ ph = (ph + 1) & wave_mask;
+
+ // Scale by volume
+ int amp = (nibble * volume_mul) >> volume_shift;
+
+ int delta = amp - lamp;
+ if ( delta )
+ {
+ lamp = amp;
+ synth->offset_inline( time, delta, out );
+ }
+ time += per;
+ }
+ while ( time < end_time );
+ this->last_amp = lamp - dac_bias;
+ }
+ ph = (ph - 1) & wave_mask; // undo pre-advance and mask position
+
+ // Keep track of last byte read
+ if ( enabled )
+ sample_buf = wave [ph >> 1];
+
+ this->phase = ph ^ swap_banks; // undo swapped banks
+ }
+ delay = time - end_time;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.h b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.h
new file mode 100644
index 00000000..100b4f2d
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gb_Oscs.h
@@ -0,0 +1,188 @@
+// Private oscillators used by Gb_Apu
+
+// Gb_Snd_Emu 0.1.4
+#ifndef GB_OSCS_H
+#define GB_OSCS_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+#ifndef GB_APU_OVERCLOCK
+ #define GB_APU_OVERCLOCK 1
+#endif
+
+#if GB_APU_OVERCLOCK & (GB_APU_OVERCLOCK - 1)
+ #error "GB_APU_OVERCLOCK must be a power of 2"
+#endif
+
+class Gb_Osc {
+protected:
+
+ // 11-bit frequency in NRx3 and NRx4
+ int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
+
+ void update_amp( blip_time_t, int new_amp );
+ int write_trig( int frame_phase, int max_len, int old_data );
+public:
+
+ enum { clk_mul = GB_APU_OVERCLOCK };
+ enum { dac_bias = 7 };
+
+ Blip_Buffer* outputs [4];// NULL, right, left, center
+ Blip_Buffer* output; // where to output sound
+ BOOST::uint8_t* regs; // osc's 5 registers
+ int mode; // mode_dmg, mode_cgb, mode_agb
+ int dac_off_amp;// amplitude when DAC is off
+ int last_amp; // current amplitude in Blip_Buffer
+ Blip_Synth_Norm const* norm_synth;
+ Blip_Synth_Fast const* fast_synth;
+
+ int delay; // clocks until frequency timer expires
+ int length_ctr; // length counter
+ unsigned phase; // waveform phase (or equivalent)
+ bool enabled; // internal enabled flag
+
+ void clock_length();
+ void reset();
+};
+
+class Gb_Env : public Gb_Osc {
+public:
+ int env_delay;
+ int volume;
+ bool env_enabled;
+
+ void clock_envelope();
+ bool write_register( int frame_phase, int reg, int old_data, int data );
+
+ void reset()
+ {
+ env_delay = 0;
+ volume = 0;
+ Gb_Osc::reset();
+ }
+protected:
+ // Non-zero if DAC is enabled
+ int dac_enabled() const { return regs [2] & 0xF8; }
+private:
+ void zombie_volume( int old, int data );
+ int reload_env_timer();
+};
+
+class Gb_Square : public Gb_Env {
+public:
+ bool write_register( int frame_phase, int reg, int old_data, int data );
+ void run( blip_time_t, blip_time_t );
+
+ void reset()
+ {
+ Gb_Env::reset();
+ delay = 0x40000000; // TODO: something less hacky (never clocked until first trigger)
+ }
+private:
+ // Frequency timer period
+ int period() const { return (2048 - frequency()) * (4 * clk_mul); }
+};
+
+class Gb_Sweep_Square : public Gb_Square {
+public:
+ int sweep_freq;
+ int sweep_delay;
+ bool sweep_enabled;
+ bool sweep_neg;
+
+ void clock_sweep();
+ void write_register( int frame_phase, int reg, int old_data, int data );
+
+ void reset()
+ {
+ sweep_freq = 0;
+ sweep_delay = 0;
+ sweep_enabled = false;
+ sweep_neg = false;
+ Gb_Square::reset();
+ }
+private:
+ enum { period_mask = 0x70 };
+ enum { shift_mask = 0x07 };
+
+ void calc_sweep( bool update );
+ void reload_sweep_timer();
+};
+
+class Gb_Noise : public Gb_Env {
+public:
+
+ int divider; // noise has more complex frequency divider setup
+
+ void run( blip_time_t, blip_time_t );
+ void write_register( int frame_phase, int reg, int old_data, int data );
+
+ void reset()
+ {
+ divider = 0;
+ Gb_Env::reset();
+ delay = 4 * clk_mul; // TODO: remove?
+ }
+private:
+ enum { period2_mask = 0x1FFFF };
+
+ int period2_index() const { return regs [3] >> 4; }
+ int period2( int base = 8 ) const { return base << period2_index(); }
+ unsigned lfsr_mask() const { return (regs [3] & 0x08) ? ~0x4040 : ~0x4000; }
+};
+
+class Gb_Wave : public Gb_Osc {
+public:
+ int sample_buf; // last wave RAM byte read (hardware has this as well)
+
+ void write_register( int frame_phase, int reg, int old_data, int data );
+ void run( blip_time_t, blip_time_t );
+
+ // Reads/writes wave RAM
+ int read( int addr ) const;
+ void write( int addr, int data );
+
+ void reset()
+ {
+ sample_buf = 0;
+ Gb_Osc::reset();
+ }
+
+private:
+ enum { bank40_mask = 0x40 };
+ enum { bank_size = 32 };
+
+ int agb_mask; // 0xFF if AGB features enabled, 0 otherwise
+ BOOST::uint8_t* wave_ram; // 32 bytes (64 nybbles), stored in APU
+
+ friend class Gb_Apu;
+
+ // Frequency timer period
+ int period() const { return (2048 - frequency()) * (2 * clk_mul); }
+
+ // Non-zero if DAC is enabled
+ int dac_enabled() const { return regs [0] & 0x80; }
+
+ void corrupt_wave();
+
+ BOOST::uint8_t* wave_bank() const { return &wave_ram [(~regs [0] & bank40_mask) >> 2 & agb_mask]; }
+
+ // Wave index that would be accessed, or -1 if no access would occur
+ int access( int addr ) const;
+};
+
+inline int Gb_Wave::read( int addr ) const
+{
+ int index = access( addr );
+ return (index < 0 ? 0xFF : wave_bank() [index]);
+}
+
+inline void Gb_Wave::write( int addr, int data )
+{
+ int index = access( addr );
+ if ( index >= 0 )
+ wave_bank() [index] = data;;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.cpp
new file mode 100644
index 00000000..1c366d2a
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.cpp
@@ -0,0 +1,208 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Gbs_Core.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const tempo_unit = 16;
+int const idle_addr = 0xF00D;
+int const bank_size = 0x4000;
+
+Gbs_Core::Gbs_Core() : rom( bank_size )
+{
+ tempo = tempo_unit;
+ assert( offsetof (header_t,copyright [32]) == header_t::size );
+}
+
+Gbs_Core::~Gbs_Core() { }
+
+void Gbs_Core::unload()
+{
+ header_.timer_mode = 0; // set_tempo() reads this
+ rom.clear();
+ Gme_Loader::unload();
+}
+
+bool Gbs_Core::header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "GBS", 3 );
+}
+
+blargg_err_t Gbs_Core::load_( Data_Reader& in )
+{
+ RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ if ( header_.vers != 1 )
+ set_warning( "Unknown file version" );
+
+ if ( header_.timer_mode & 0x78 )
+ set_warning( "Invalid timer mode" );
+
+ addr_t load_addr = get_le16( header_.load_addr );
+ if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
+ load_addr < 0x400 )
+ set_warning( "Invalid load/init/play address" );
+
+ cpu.rst_base = load_addr;
+ rom.set_addr( load_addr );
+
+ return blargg_ok;
+}
+
+void Gbs_Core::set_bank( int n )
+{
+ addr_t addr = rom.mask_addr( n * bank_size );
+ if ( addr == 0 && rom.size() > bank_size )
+ addr = bank_size; // MBC1&2 behavior, bank 0 acts like bank 1
+ cpu.map_code( bank_size, bank_size, rom.at_addr( addr ) );
+}
+
+void Gbs_Core::update_timer()
+{
+ play_period_ = 70224 / tempo_unit; // 59.73 Hz
+
+ if ( header_.timer_mode & 0x04 )
+ {
+ // Using custom rate
+ static byte const rates [4] = { 6, 0, 2, 4 };
+ // TODO: emulate double speed CPU mode rather than halving timer rate
+ int double_speed = header_.timer_mode >> 7;
+ int shift = rates [ram [hi_page + 7] & 3] - double_speed;
+ play_period_ = (256 - ram [hi_page + 6]) << shift;
+ }
+
+ play_period_ *= tempo;
+}
+
+void Gbs_Core::set_tempo( double t )
+{
+ tempo = (int) (tempo_unit / t + 0.5);
+ apu_.set_tempo( t );
+ update_timer();
+}
+
+// Jumps to routine, given pointer to address in file header. Pushes idle_addr
+// as return address, NOT old PC.
+void Gbs_Core::jsr_then_stop( byte const addr [] )
+{
+ check( cpu.r.sp == get_le16( header_.stack_ptr ) );
+ cpu.r.pc = get_le16( addr );
+ write_mem( --cpu.r.sp, idle_addr >> 8 );
+ write_mem( --cpu.r.sp, idle_addr );
+}
+
+blargg_err_t Gbs_Core::start_track( int track, Gb_Apu::mode_t mode )
+{
+ // Reset APU to state expected by most rips
+ static byte const sound_data [] = {
+ 0x80, 0xBF, 0x00, 0x00, 0xB8, // square 1 DAC disabled
+ 0x00, 0x3F, 0x00, 0x00, 0xB8, // square 2 DAC disabled
+ 0x7F, 0xFF, 0x9F, 0x00, 0xB8, // wave DAC disabled
+ 0x00, 0xFF, 0x00, 0x00, 0xB8, // noise DAC disabled
+ 0x77, 0xFF, 0x80, // max volume, all chans in center, power on
+ };
+ apu_.reset( mode );
+ apu_.write_register( 0, 0xFF26, 0x80 ); // power on
+ for ( int i = 0; i < (int) sizeof sound_data; i++ )
+ apu_.write_register( 0, i + apu_.io_addr, sound_data [i] );
+ apu_.end_frame( 1 ); // necessary to get click out of the way
+
+ // Init memory and I/O registers
+ memset( ram, 0, 0x4000 );
+ memset( ram + 0x4000, 0xFF, 0x1F80 );
+ memset( ram + 0x5F80, 0, sizeof ram - 0x5F80 );
+ ram [hi_page] = 0; // joypad reads back as 0
+ ram [idle_addr - ram_addr] = 0xED; // illegal instruction
+ ram [hi_page + 6] = header_.timer_modulo;
+ ram [hi_page + 7] = header_.timer_mode;
+
+ // Map memory
+ cpu.reset( rom.unmapped() );
+ cpu.map_code( ram_addr, 0x10000 - ram_addr, ram );
+ cpu.map_code( 0, bank_size, rom.at_addr( 0 ) );
+ set_bank( rom.size() > bank_size );
+
+ // CPU registers, timing
+ update_timer();
+ next_play = play_period_;
+ cpu.r.fa = track;
+ cpu.r.sp = get_le16( header_.stack_ptr );
+ jsr_then_stop( header_.init_addr );
+
+ return blargg_ok;
+}
+
+blargg_err_t Gbs_Core::run_until( int end )
+{
+ end_time = end;
+ cpu.set_time( cpu.time() - end );
+ while ( true )
+ {
+ run_cpu();
+ if ( cpu.time() >= 0 )
+ break;
+
+ if ( cpu.r.pc == idle_addr )
+ {
+ if ( next_play > end_time )
+ {
+ cpu.set_time( 0 );
+ break;
+ }
+
+ if ( cpu.time() < next_play - end_time )
+ cpu.set_time( next_play - end_time );
+ next_play += play_period_;
+ jsr_then_stop( header_.play_addr );
+ }
+ else if ( cpu.r.pc > 0xFFFF )
+ {
+ dprintf( "PC wrapped around\n" );
+ cpu.r.pc &= 0xFFFF;
+ }
+ else
+ {
+ set_warning( "Emulation error (illegal/unsupported instruction)" );
+ dprintf( "Bad opcode $%02X at $%04X\n",
+ (int) *cpu.get_code( cpu.r.pc ), (int) cpu.r.pc );
+ cpu.r.pc = (cpu.r.pc + 1) & 0xFFFF;
+ cpu.set_time( cpu.time() + 6 );
+ }
+ }
+
+ return blargg_ok;
+}
+
+blargg_err_t Gbs_Core::end_frame( int end )
+{
+ RETURN_ERR( run_until( end ) );
+
+ next_play -= end;
+ if ( next_play < 0 ) // happens when play routine takes too long
+ {
+ #if !GBS_IGNORE_STARVED_PLAY
+ check( false );
+ #endif
+ next_play = 0;
+ }
+
+ apu_.end_frame( end );
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.h
new file mode 100644
index 00000000..f5a3ca69
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Core.h
@@ -0,0 +1,106 @@
+// Nintendo Game Boy GBS music file emulator core
+
+// Game_Music_Emu 0.6-pre
+#ifndef GBS_CORE_H
+#define GBS_CORE_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Gb_Cpu.h"
+#include "Gb_Apu.h"
+
+class Gbs_Core : public Gme_Loader {
+public:
+
+ // GBS file header
+ struct header_t
+ {
+ enum { size = 112 };
+
+ char tag [ 3];
+ byte vers;
+ byte track_count;
+ byte first_track;
+ byte load_addr [ 2];
+ byte init_addr [ 2];
+ byte play_addr [ 2];
+ byte stack_ptr [ 2];
+ byte timer_modulo;
+ byte timer_mode;
+ char game [32]; // strings can be 32 chars, NOT terminated
+ char author [32];
+ char copyright [32];
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ // Sound chip
+ Gb_Apu& apu() { return apu_; }
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ void set_tempo( double );
+
+ // Starts track, where 0 is the first. Uses specified APU mode.
+ blargg_err_t start_track( int, Gb_Apu::mode_t = Gb_Apu::mode_cgb );
+
+ // Ends time frame at time t
+ typedef int time_t; // clock count
+ blargg_err_t end_frame( time_t t );
+
+ // Clocks between calls to play routine
+ time_t play_period() const { return play_period_; }
+
+protected:
+ typedef int addr_t;
+
+ // Current time
+ time_t time() const { return cpu.time() + end_time; }
+
+ // Runs emulator to time t
+ blargg_err_t run_until( time_t t );
+
+ // Runs CPU until time becomes >= 0
+ void run_cpu();
+
+ // Reads/writes memory and I/O
+ int read_mem( addr_t );
+ void write_mem( addr_t, int );
+
+// Implementation
+public:
+ Gbs_Core();
+ ~Gbs_Core();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+
+private:
+ enum { ram_addr = 0xA000 };
+ enum { io_base = 0xFF00 };
+ enum { hi_page = io_base - ram_addr };
+
+ Rom_Data rom;
+ int tempo;
+ time_t end_time;
+ time_t play_period_;
+ time_t next_play;
+ header_t header_;
+ Gb_Cpu cpu;
+ Gb_Apu apu_;
+ byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding];
+
+ void update_timer();
+ void jsr_then_stop( byte const [] );
+ void set_bank( int n );
+ void write_io_inline( int offset, int data, int base );
+ void write_io_( int offset, int data );
+ int read_io( int offset );
+ void write_io( int offset, int data );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Cpu.cpp
new file mode 100644
index 00000000..b683eee2
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Cpu.cpp
@@ -0,0 +1,134 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Gbs_Core.h"
+
+#include "blargg_endian.h"
+
+//#include "gb_cpu_log.h"
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifndef LOG_MEM
+ #define LOG_MEM( addr, str, data ) data
+#endif
+
+int Gbs_Core::read_mem( addr_t addr )
+{
+ int result = *cpu.get_code( addr );
+ if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )
+ result = apu_.read_register( time(), addr );
+
+#ifndef NDEBUG
+ else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
+ dprintf( "Unmapped read $%04X\n", (unsigned) addr );
+ else if ( unsigned (addr - 0xFF01) < 0xFF80 - 0xFF01 && addr != 0xFF70 && addr != 0xFF05 )
+ dprintf( "Unmapped read $%04X\n", (unsigned) addr );
+#endif
+
+ return LOG_MEM( addr, ">", result );
+}
+
+inline void Gbs_Core::write_io_inline( int offset, int data, int base )
+{
+ if ( (unsigned) (offset - (apu_.io_addr - base)) < apu_.io_size )
+ apu_.write_register( time(), offset + base, data & 0xFF );
+ else if ( (unsigned) (offset - (0xFF06 - base)) < 2 )
+ update_timer();
+ else if ( offset == io_base - base )
+ ram [base - ram_addr + offset] = 0; // keep joypad return value 0
+ else
+ ram [base - ram_addr + offset] = 0xFF;
+
+ //if ( offset == 0xFFFF - base )
+ // dprintf( "Wrote interrupt mask\n" );
+}
+
+void Gbs_Core::write_mem( addr_t addr, int data )
+{
+ (void) LOG_MEM( addr, "<", data );
+
+ int offset = addr - ram_addr;
+ if ( (unsigned) offset < 0x10000 - ram_addr )
+ {
+ ram [offset] = data;
+
+ offset -= 0xE000 - ram_addr;
+ if ( (unsigned) offset < 0x1F80 )
+ write_io_inline( offset, data, 0xE000 );
+ }
+ else if ( (unsigned) (offset - (0x2000 - ram_addr)) < 0x2000 )
+ {
+ set_bank( data & 0xFF );
+ }
+#ifndef NDEBUG
+ else if ( unsigned (addr - 0x8000) < 0x2000 || unsigned (addr - 0xE000) < 0x1F00 )
+ {
+ dprintf( "Unmapped write $%04X\n", (unsigned) addr );
+ }
+#endif
+}
+
+void Gbs_Core::write_io_( int offset, int data )
+{
+ write_io_inline( offset, data, io_base );
+}
+
+inline void Gbs_Core::write_io( int offset, int data )
+{
+ (void) LOG_MEM( offset + io_base, "<", data );
+
+ ram [io_base - ram_addr + offset] = data;
+ if ( (unsigned) offset < 0x80 )
+ write_io_( offset, data );
+}
+
+int Gbs_Core::read_io( int offset )
+{
+ int const io_base = 0xFF00;
+ int result = ram [io_base - ram_addr + offset];
+
+ if ( (unsigned) (offset - (apu_.io_addr - io_base)) < apu_.io_size )
+ {
+ result = apu_.read_register( time(), offset + io_base );
+ (void) LOG_MEM( offset + io_base, ">", result );
+ }
+ else
+ {
+ check( result == read_mem( offset + io_base ) );
+ }
+ return result;
+}
+
+#define READ_FAST( addr, out ) \
+{\
+ out = READ_CODE( addr );\
+ if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )\
+ out = LOG_MEM( addr, ">", apu_.read_register( TIME() + end_time, addr ) );\
+ else\
+ check( out == read_mem( addr ) );\
+}
+
+#define READ_MEM( addr ) read_mem( addr )
+#define WRITE_MEM( addr, data ) write_mem( addr, data )
+
+#define WRITE_IO( addr, data ) write_io( addr, data )
+#define READ_IO( addr, out ) out = read_io( addr )
+
+#define CPU cpu
+
+#define CPU_BEGIN \
+void Gbs_Core::run_cpu()\
+{
+ #include "Gb_Cpu_run.h"
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.cpp
new file mode 100644
index 00000000..81fa42f2
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.cpp
@@ -0,0 +1,143 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Gbs_Emu.h"
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Gbs_Emu::equalizer_t const Gbs_Emu::handheld_eq = { -47.0, 2000 };
+Gbs_Emu::equalizer_t const Gbs_Emu::cgb_eq = { 0.0, 300 };
+Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = { 0.0, 30 }; // DMG
+
+Gbs_Emu::Gbs_Emu()
+{
+ sound_hardware = sound_gbs;
+ enable_clicking( false );
+ set_type( gme_gbs_type );
+ set_silence_lookahead( 6 );
+ set_max_initial_silence( 21 );
+ set_gain( 1.2 );
+
+ // kind of midway between headphones and speaker
+ static equalizer_t const eq = { -1.0, 120 };
+ set_equalizer( eq );
+}
+
+Gbs_Emu::~Gbs_Emu() { }
+
+void Gbs_Emu::unload()
+{
+ core_.unload();
+ Music_Emu::unload();
+}
+
+// Track info
+
+static void copy_gbs_fields( Gbs_Emu::header_t const& h, track_info_t* out )
+{
+ GME_COPY_FIELD( h, out, game );
+ GME_COPY_FIELD( h, out, author );
+ GME_COPY_FIELD( h, out, copyright );
+}
+
+blargg_err_t Gbs_Emu::track_info_( track_info_t* out, int ) const
+{
+ copy_gbs_fields( header(), out );
+ return blargg_ok;
+}
+
+struct Gbs_File : Gme_Info_
+{
+ Gbs_Emu::header_t h;
+
+ Gbs_File() { set_type( gme_gbs_type ); }
+
+ blargg_err_t load_( Data_Reader& in )
+ {
+ blargg_err_t err = in.read( &h, h.size );
+ if ( err )
+ return (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err);
+
+ set_track_count( h.track_count );
+ if ( !h.valid_tag() )
+ return blargg_err_file_type;
+
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ copy_gbs_fields( h, out );
+ return blargg_ok;
+ }
+};
+
+static Music_Emu* new_gbs_emu () { return BLARGG_NEW Gbs_Emu ; }
+static Music_Emu* new_gbs_file() { return BLARGG_NEW Gbs_File; }
+
+gme_type_t_ const gme_gbs_type [1] = {{ "Game Boy", 0, &new_gbs_emu, &new_gbs_file, "GBS", 1 }};
+
+// Setup
+
+blargg_err_t Gbs_Emu::load_( Data_Reader& in )
+{
+ RETURN_ERR( core_.load( in ) );
+ set_warning( core_.warning() );
+ set_track_count( header().track_count );
+ set_voice_count( Gb_Apu::osc_count );
+ core_.apu().volume( gain() );
+
+ static const char* const names [Gb_Apu::osc_count] = {
+ "Square 1", "Square 2", "Wave", "Noise"
+ };
+ set_voice_names( names );
+
+ static int const types [Gb_Apu::osc_count] = {
+ wave_type+1, wave_type+2, wave_type+3, mixed_type+1
+ };
+ set_voice_types( types );
+
+ return setup_buffer( 4194304 );
+}
+
+void Gbs_Emu::update_eq( blip_eq_t const& eq )
+{
+ core_.apu().treble_eq( eq );
+}
+
+void Gbs_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ core_.apu().set_output( i, c, l, r );
+}
+
+void Gbs_Emu::set_tempo_( double t )
+{
+ core_.set_tempo( t );
+}
+
+blargg_err_t Gbs_Emu::start_track_( int track )
+{
+ sound_t mode = sound_hardware;
+ if ( mode == sound_gbs )
+ mode = (header().timer_mode & 0x80) ? sound_cgb : sound_dmg;
+
+ RETURN_ERR( core_.start_track( track, (Gb_Apu::mode_t) mode ) );
+
+ // clear buffer AFTER track is started, eliminating initial click
+ return Classic_Emu::start_track_( track );
+}
+
+blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
+{
+ return core_.end_frame( duration );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.h
new file mode 100644
index 00000000..96fd56c9
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gbs_Emu.h
@@ -0,0 +1,61 @@
+// Nintendo Game Boy GBS music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef GBS_EMU_H
+#define GBS_EMU_H
+
+#include "Classic_Emu.h"
+#include "Gbs_Core.h"
+
+class Gbs_Emu : public Classic_Emu {
+public:
+ // Equalizer profiles for Game Boy speaker and headphones
+ static equalizer_t const handheld_eq;
+ static equalizer_t const headphones_eq;
+ static equalizer_t const cgb_eq; // Game Boy Color headphones have less bass
+
+ // GBS file header (see Gbs_Core.h)
+ typedef Gbs_Core::header_t header_t;
+
+ // Header for currently loaded file
+ header_t const& header() const { return core_.header(); }
+
+ // Selects which sound hardware to use. AGB hardware is cleaner than the
+ // others. Doesn't take effect until next start_track().
+ enum sound_t {
+ sound_dmg = Gb_Apu::mode_dmg, // Game Boy monochrome
+ sound_cgb = Gb_Apu::mode_cgb, // Game Boy Color
+ sound_agb = Gb_Apu::mode_agb, // Game Boy Advance
+ sound_gbs // Use DMG/CGB based on GBS (default)
+ };
+ void set_sound( sound_t s ) { sound_hardware = s; }
+
+ // If true, makes APU more accurate, which results in more clicking.
+ void enable_clicking( bool enable = true ) { core_.apu().reduce_clicks( !enable ); }
+
+ static gme_type_t static_type() { return gme_gbs_type; }
+
+ Gbs_Core& core() { return core_; }
+
+// Internal
+public:
+ Gbs_Emu();
+ ~Gbs_Emu();
+
+protected:
+ // Overrides
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+ virtual void unload();
+
+private:
+ sound_t sound_hardware;
+ Gbs_Core core_;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gme_File.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gme_File.cpp
new file mode 100644
index 00000000..5930dcbf
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gme_File.cpp
@@ -0,0 +1,183 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Gme_File.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void Gme_File::unload()
+{
+ clear_playlist(); // BEFORE clearing track count
+ track_count_ = 0;
+ raw_track_count_ = 0;
+ Gme_Loader::unload();
+}
+
+Gme_File::Gme_File()
+{
+ type_ = NULL;
+ user_data_ = NULL;
+ user_cleanup_ = NULL;
+ Gme_File::unload(); // clears fields
+}
+
+Gme_File::~Gme_File()
+{
+ if ( user_cleanup_ )
+ user_cleanup_( user_data_ );
+}
+
+blargg_err_t Gme_File::post_load()
+{
+ if ( !track_count() )
+ set_track_count( type()->track_count );
+ return Gme_Loader::post_load();
+}
+
+void Gme_File::clear_playlist()
+{
+ playlist.clear();
+ clear_playlist_();
+ track_count_ = raw_track_count_;
+}
+
+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 BLARGG_ERR( BLARGG_ERR_CALLER, "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;
+ // TODO: really needs to be removed?
+ //if ( !(type_->flags_ & 0x02) )
+ // *track_io -= e.decimal_track;
+ }
+ if ( *track_io >= raw_track_count_ )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "invalid track in m3u playlist" );
+ }
+ else
+ {
+ check( !playlist.size() );
+ }
+ return blargg_ok;
+}
+
+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->fade_length = -1;
+ out->play_length = -1;
+ out->repeat_count = -1;
+ out->song [0] = 0;
+ out->game [0] = 0;
+ out->author [0] = 0;
+ out->composer [0] = 0;
+ out->engineer [0] = 0;
+ out->sequencer [0] = 0;
+ out->tagger [0] = 0;
+ out->copyright [0] = 0;
+ out->date [0] = 0;
+ out->comment [0] = 0;
+ out->dumper [0] = 0;
+ out->system [0] = 0;
+ out->disc [0] = 0;
+ out->track [0] = 0;
+ out->ost [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.artist );
+ copy_field_( out->engineer , i.engineer );
+ copy_field_( out->composer , i.composer );
+ copy_field_( out->sequencer, i.sequencer );
+ copy_field_( out->copyright, i.copyright );
+ copy_field_( out->dumper , i.ripping );
+ copy_field_( out->tagger , i.tagging );
+ copy_field_( out->date , i.date );
+
+ M3u_Playlist::entry_t const& e = playlist [track];
+ if ( e.length >= 0 ) out->length = e.length;
+ if ( e.intro >= 0 ) out->intro_length = e.intro;
+ if ( e.loop >= 0 ) out->loop_length = e.loop;
+ if ( e.fade >= 0 ) out->fade_length = e.fade;
+ if ( e.repeat >= 0 ) out->repeat_count = e.repeat;
+ copy_field_( out->song, e.name );
+ }
+
+ // play_length
+ out->play_length = out->length;
+ if ( out->play_length <= 0 )
+ {
+ out->play_length = out->intro_length + 2 * out->loop_length; // intro + 2 loops
+ if ( out->play_length <= 0 )
+ out->play_length = 150 * 1000; // 2.5 minutes
+ }
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gme_File.h b/plugins/gme/game-music-emu-0.6pre/gme/Gme_File.h
new file mode 100644
index 00000000..9ed83f54
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gme_File.h
@@ -0,0 +1,152 @@
+// Common interface for track information
+
+// Game_Music_Emu 0.6-pre
+#ifndef GME_FILE_H
+#define GME_FILE_H
+
+#include "gme.h"
+#include "Gme_Loader.h"
+#include "M3u_Playlist.h"
+
+struct track_info_t
+{
+ int track_count;
+
+ /* times in milliseconds; -1 if unknown */
+ int length; /* total length, if file specifies it */
+ int intro_length; /* length of song up to looping section */
+ int loop_length; /* length of looping section */
+ int fade_length;
+ int repeat_count;
+
+ /* Length if available, otherwise intro_length+loop_length*2 if available,
+ otherwise a default of 150000 (2.5 minutes). */
+ int play_length;
+
+ /* empty string if not available */
+ char system [256];
+ char game [256];
+ char song [256];
+ char author [256];
+ char composer [256];
+ char engineer [256];
+ char sequencer [256];
+ char tagger [256];
+ char copyright [256];
+ char date [256];
+ char comment [256];
+ char dumper [256];
+ char disc [256];
+ char track [256];
+ char ost [256];
+};
+enum { gme_max_field = 255 };
+
+class Gme_File : public Gme_Loader {
+public:
+ // Type of emulator. For example if this returns gme_nsfe_type, this object
+ // is an NSFE emulator, and you can downcast to an Nsfe_Emu* if necessary.
+ gme_type_t type() const;
+
+ // Loads 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();
+
+ // Number of tracks or 0 if no file has been loaded
+ int track_count() const;
+
+ // Gets 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
+
+ // Sets/gets 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_; }
+
+ // Registers 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:
+ Gme_File();
+ ~Gme_File();
+
+protected:
+ // Services
+ void set_type( gme_type_t t ) { type_ = t; }
+ void set_track_count( int n ) { track_count_ = raw_track_count_ = n; }
+
+ // Must be overridden
+ virtual blargg_err_t track_info_( track_info_t* out, int track ) const BLARGG_PURE( ; )
+
+ // Optionally overridden
+ virtual void clear_playlist_() { }
+
+protected: // Gme_Loader overrides
+ virtual void unload();
+ virtual blargg_err_t post_load();
+
+protected:
+ blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu
+private:
+ gme_type_t type_;
+ void* user_data_;
+ gme_user_cleanup_t user_cleanup_;
+ int track_count_;
+ int raw_track_count_;
+ M3u_Playlist playlist;
+ char playlist_warning [64];
+
+ blargg_err_t load_m3u_( blargg_err_t );
+
+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 );
+};
+
+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 (C++ only) */
+ Music_Emu* (*new_info)();/* Create new info reader for this type (C++ only) */
+
+ /* internal */
+ const char* extension_;
+ int flags_;
+};
+
+/* Emulator type constants for each supported file type */
+extern const gme_type_t_
+ gme_ay_type [1],
+ gme_gbs_type [1],
+ gme_gym_type [1],
+ gme_hes_type [1],
+ gme_kss_type [1],
+ gme_nsf_type [1],
+ gme_nsfe_type [1],
+ gme_sap_type [1],
+ gme_sgc_type [1],
+ gme_spc_type [1],
+ gme_vgm_type [1],
+ gme_vgz_type [1];
+
+#define GME_COPY_FIELD( in, out, name ) \
+ { Gme_File::copy_field_( out->name, in.name, sizeof in.name ); }
+
+inline gme_type_t Gme_File::type() const { return type_; }
+
+inline int Gme_File::track_count() const { return track_count_; }
+
+inline blargg_err_t Gme_File::track_info_( track_info_t*, int ) const { return blargg_ok; }
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.cpp
new file mode 100644
index 00000000..186d84b7
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.cpp
@@ -0,0 +1,86 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Gme_Loader.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void Gme_Loader::unload()
+{
+ file_begin_ = NULL;
+ file_end_ = NULL;
+ file_data.clear();
+}
+
+Gme_Loader::Gme_Loader()
+{
+ warning_ = NULL;
+ Gme_Loader::unload();
+ blargg_verify_byte_order(); // used by most emulator types, so save them the trouble
+}
+
+Gme_Loader::~Gme_Loader() { }
+
+blargg_err_t Gme_Loader::load_mem_( byte const data [], int size )
+{
+ require( data != file_data.begin() ); // load_mem_() or load_() must be overridden
+ Mem_File_Reader in( data, size );
+ return load_( in );
+}
+
+inline blargg_err_t Gme_Loader::load_mem_wrapper( byte const data [], int size )
+{
+ file_begin_ = data;
+ file_end_ = data + size;
+ return load_mem_( data, size );
+}
+
+blargg_err_t Gme_Loader::load_( Data_Reader& in )
+{
+ RETURN_ERR( file_data.resize( in.remain() ) );
+ RETURN_ERR( in.read( file_data.begin(), file_data.size() ) );
+ return load_mem_wrapper( file_data.begin(), file_data.size() );
+}
+
+blargg_err_t Gme_Loader::post_load_( blargg_err_t err )
+{
+ if ( err )
+ {
+ unload();
+ return err;
+ }
+
+ return post_load();
+}
+
+blargg_err_t Gme_Loader::load_mem( void const* in, long size )
+{
+ pre_load();
+ return post_load_( load_mem_wrapper( (byte const*) in, (int) size ) );
+}
+
+blargg_err_t Gme_Loader::load( Data_Reader& in )
+{
+ pre_load();
+ return post_load_( load_( in ) );
+}
+
+blargg_err_t Gme_Loader::load_file( const char path [] )
+{
+ pre_load();
+ GME_FILE_READER in;
+ RETURN_ERR( in.open( path ) );
+ return post_load_( load_( in ) );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.h b/plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.h
new file mode 100644
index 00000000..d92d97a0
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gme_Loader.h
@@ -0,0 +1,92 @@
+// Common interface for loading file data from various sources
+
+// Game_Music_Emu 0.6-pre
+#ifndef GME_LOADER_H
+#define GME_LOADER_H
+
+#include "blargg_common.h"
+#include "Data_Reader.h"
+
+class Gme_Loader {
+public:
+
+ // Each loads game music data from a file and returns an error if
+ // file is wrong type or is seriously corrupt. Minor problems are
+ // reported using warning().
+
+ // Loads from file on disk
+ blargg_err_t load_file( const char path [] );
+
+ // Loads from custom data source (see Data_Reader.h)
+ blargg_err_t load( Data_Reader& );
+
+ // Loads from file already read into memory. Object might keep pointer to
+ // data; if it does, you MUST NOT free it until you're done with the file.
+ blargg_err_t load_mem( void const* data, long size );
+
+ // Most recent warning string, or NULL if none. Clears current warning after
+ // returning.
+ const char* warning();
+
+ // Unloads file from memory
+ virtual void unload();
+
+ virtual ~Gme_Loader();
+
+protected:
+ typedef BOOST::uint8_t byte;
+
+ // File data in memory, or 0 if data was loaded with load_()
+ byte const* file_begin() const { return file_begin_; }
+ byte const* file_end() const { return file_end_; }
+ int file_size() const { return (int) (file_end_ - file_begin_); }
+
+ // Sets warning string
+ void set_warning( const char s [] ) { warning_ = s; }
+
+ // At least one must be overridden
+ virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_()
+ virtual blargg_err_t load_mem_( byte const data [], int size ); // use data in memory
+
+ // Optionally overridden
+ virtual void pre_load() { unload(); } // called before load_()/load_mem_()
+ virtual blargg_err_t post_load() { return blargg_ok; } // called after load_()/load_mem_() succeeds
+
+private:
+ // noncopyable
+ Gme_Loader( const Gme_Loader& );
+ Gme_Loader& operator = ( const Gme_Loader& );
+
+// Implementation
+public:
+ Gme_Loader();
+ BLARGG_DISABLE_NOTHROW
+
+ blargg_vector<byte> file_data; // used only when loading from file to load_mem_()
+ byte const* file_begin_;
+ byte const* file_end_;
+ const char* warning_;
+
+ blargg_err_t load_mem_wrapper( byte const [], int );
+ blargg_err_t post_load_( blargg_err_t err );
+};
+
+// Files are read with GME_FILE_READER. Default supports gzip if zlib is available.
+#ifndef GME_FILE_READER
+ #ifdef HAVE_ZLIB_H
+ #define GME_FILE_READER Gzip_File_Reader
+ #else
+ #define GME_FILE_READER Std_File_Reader
+ #endif
+#elif defined (GME_FILE_READER_INCLUDE)
+ #include GME_FILE_READER_INCLUDE
+#endif
+
+inline const char* Gme_Loader::warning()
+{
+ const char* s = warning_;
+ warning_ = NULL;
+ return s;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.cpp
new file mode 100644
index 00000000..ce27877a
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.cpp
@@ -0,0 +1,405 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Gym_Emu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+double const min_tempo = 0.25;
+double const oversample = 5 / 3.0;
+double const fm_gain = 3.0;
+
+int const base_clock = 53700300;
+int const clock_rate = base_clock / 15;
+
+Gym_Emu::Gym_Emu()
+{
+ resampler.set_callback( play_frame_, this );
+ pos = NULL;
+ disable_oversampling_ = false;
+ set_type( gme_gym_type );
+ set_silence_lookahead( 1 ); // tracks should already be trimmed
+ pcm_buf = stereo_buf.center();
+}
+
+Gym_Emu::~Gym_Emu() { }
+
+// Track info
+
+static void get_gym_info( Gym_Emu::header_t const& h, int length, track_info_t* out )
+{
+ if ( 0 != memcmp( h.tag, "GYMX", 4 ) )
+ return;
+
+ length = length * 50 / 3; // 1000 / 60
+ int 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 blank
+ 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 );
+}
+
+static int gym_track_length( byte const p [], byte const* end )
+{
+ int 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;
+}
+
+blargg_err_t Gym_Emu::track_info_( track_info_t* out, int ) const
+{
+ get_gym_info( header_, gym_track_length( log_begin(), file_end() ), out );
+ return blargg_ok;
+}
+
+static blargg_err_t check_header( byte const in [], int size, int* data_offset = NULL )
+{
+ if ( size < 4 )
+ return blargg_err_file_type;
+
+ if ( memcmp( in, "GYMX", 4 ) == 0 )
+ {
+ if ( size < Gym_Emu::header_t::size + 1 )
+ return blargg_err_file_type;
+
+ if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 )
+ return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "packed GYM file" );
+
+ if ( data_offset )
+ *data_offset = Gym_Emu::header_t::size;
+ }
+ else if ( *in > 3 )
+ {
+ return blargg_err_file_type;
+ }
+
+ return blargg_ok;
+}
+
+struct Gym_File : Gme_Info_
+{
+ int data_offset;
+
+ Gym_File() { set_type( gme_gym_type ); }
+
+ blargg_err_t load_mem_( byte const in [], int size )
+ {
+ data_offset = 0;
+ return check_header( in, size, &data_offset );
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ int length = gym_track_length( &file_begin() [data_offset], file_end() );
+ get_gym_info( *(Gym_Emu::header_t const*) file_begin(), length, out );
+ return blargg_ok;
+ }
+};
+
+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_( int sample_rate )
+{
+ blip_eq_t eq( -32, 8000, sample_rate );
+ apu.treble_eq( eq );
+ pcm_synth.treble_eq( eq );
+
+ apu.volume( 0.135 * fm_gain * gain() );
+
+ double factor = oversample;
+ if ( disable_oversampling_ )
+ factor = (double) base_clock / 7 / 144 / sample_rate;
+ RETURN_ERR( resampler.setup( factor, 0.990, fm_gain * gain() ) );
+ factor = resampler.rate();
+ double fm_rate = sample_rate * factor;
+
+ RETURN_ERR( stereo_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) );
+ stereo_buf.clock_rate( clock_rate );
+
+ RETURN_ERR( fm.set_rate( fm_rate, base_clock / 7.0 ) );
+ RETURN_ERR( resampler.reset( (int) (1.0 / 60 / min_tempo * sample_rate) ) );
+
+ return blargg_ok;
+}
+
+void Gym_Emu::set_tempo_( double t )
+{
+ if ( t < min_tempo )
+ {
+ set_tempo( min_tempo );
+ return;
+ }
+
+ if ( stereo_buf.sample_rate() )
+ {
+ double denom = tempo() * 60;
+ clocks_per_frame = (int) (clock_rate / denom);
+ resampler.resize( (int) (sample_rate() / denom) );
+ }
+}
+
+void Gym_Emu::mute_voices_( int mask )
+{
+ Music_Emu::mute_voices_( mask );
+ fm.mute_voices( mask );
+ apu.set_output( (mask & 0x80) ? 0 : stereo_buf.center() );
+ pcm_synth.volume( (mask & 0x40) ? 0.0 : 0.125 / 256 * fm_gain * gain() );
+}
+
+blargg_err_t Gym_Emu::load_mem_( byte const in [], int size )
+{
+ assert( offsetof (header_t,packed [4]) == header_t::size );
+ log_offset = 0;
+ RETURN_ERR( check_header( in, size, &log_offset ) );
+
+ loop_begin = NULL;
+
+ static const char* const names [] = {
+ "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
+ };
+ set_voice_names( names );
+
+ set_voice_count( 8 );
+
+ if ( log_offset )
+ header_ = *(header_t const*) in;
+ else
+ memset( &header_, 0, sizeof header_ );
+
+ return blargg_ok;
+}
+
+// Emulation
+
+blargg_err_t Gym_Emu::start_track_( int track )
+{
+ RETURN_ERR( Music_Emu::start_track_( track ) );
+
+ pos = log_begin();
+ loop_remain = get_le32( header_.loop_start );
+
+ prev_pcm_count = 0;
+ pcm_enabled = 0;
+ pcm_amp = -1;
+
+ fm.reset();
+ apu.reset();
+ stereo_buf.clear();
+ resampler.clear();
+ pcm_buf = stereo_buf.center();
+ return blargg_ok;
+}
+
+void Gym_Emu::run_pcm( byte const pcm_in [], int pcm_count )
+{
+ // Guess beginning and end of sample and adjust rate and buffer position accordingly.
+
+ // count dac samples in next frame
+ int next_pcm_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_pcm_count++;
+ }
+
+ // detect beginning and end of sample
+ int rate_count = pcm_count;
+ int start = 0;
+ if ( !prev_pcm_count && next_pcm_count && pcm_count < next_pcm_count )
+ {
+ rate_count = next_pcm_count;
+ start = next_pcm_count - pcm_count;
+ }
+ else if ( prev_pcm_count && !next_pcm_count && pcm_count < prev_pcm_count )
+ {
+ rate_count = prev_pcm_count;
+ }
+
+ // Evenly space samples within buffer section being used
+ blip_resampled_time_t period = pcm_buf->resampled_duration( clocks_per_frame ) / rate_count;
+
+ blip_resampled_time_t time = pcm_buf->resampled_time( 0 ) + period * start + (unsigned) period / 2;
+
+ int pcm_amp = this->pcm_amp;
+ if ( pcm_amp < 0 )
+ pcm_amp = pcm_in [0];
+
+ for ( int i = 0; i < pcm_count; i++ )
+ {
+ int delta = pcm_in [i] - pcm_amp;
+ pcm_amp += delta;
+ pcm_synth.offset_resampled( time, delta, pcm_buf );
+ time += period;
+ }
+ this->pcm_amp = pcm_amp;
+ pcm_buf->set_modified();
+}
+
+void Gym_Emu::parse_frame()
+{
+ byte pcm [1024]; // all PCM writes for frame
+ int pcm_size = 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 )
+ {
+ pcm [pcm_size] = data2;
+ if ( pcm_size < (int) sizeof pcm - 1 )
+ pcm_size += pcm_enabled;
+ }
+ else
+ {
+ if ( data == 0x2B )
+ pcm_enabled = data2 >> 7 & 1;
+
+ fm.write0( data, data2 );
+ }
+ }
+ else if ( cmd == 2 )
+ {
+ int data2 = *pos++;
+ if ( data == 0xB6 )
+ {
+ Blip_Buffer * pcm_buf = NULL;
+ switch ( data2 >> 6 )
+ {
+ case 0: pcm_buf = NULL; break;
+ case 1: pcm_buf = stereo_buf.right(); break;
+ case 2: pcm_buf = stereo_buf.left(); break;
+ case 3: pcm_buf = stereo_buf.center(); break;
+ }
+ /*if ( this->pcm_buf != pcm_buf )
+ {
+ if ( this->pcm_buf ) pcm_synth.offset_inline( 0, -pcm_amp, this->pcm_buf );
+ if ( pcm_buf ) pcm_synth.offset_inline( 0, pcm_amp, pcm_buf );
+ }*/
+ this->pcm_buf = pcm_buf;
+ }
+ fm.write1( data, data2 );
+ }
+ 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
+ }
+ }
+
+ if ( pos >= file_end() )
+ {
+ // Reached end
+ check( pos == file_end() );
+
+ if ( loop_begin )
+ pos = loop_begin;
+ else
+ set_track_ended();
+ }
+ this->pos = pos;
+
+ // PCM
+ if ( pcm_buf && pcm_size )
+ run_pcm( pcm, pcm_size );
+ prev_pcm_count = pcm_size;
+}
+
+inline 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;
+}
+
+int Gym_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] )
+{
+ return STATIC_CAST(Gym_Emu*,p)->play_frame( a, b, c );
+}
+
+blargg_err_t Gym_Emu::play_( int count, sample_t out [] )
+{
+ resampler.dual_play( count, out, stereo_buf );
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.h
new file mode 100644
index 00000000..e8df596b
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Gym_Emu.h
@@ -0,0 +1,86 @@
+// Sega Genesis/Mega Drive GYM music file emulator
+// Performs PCM timing recovery to improve sample quality.
+
+// Game_Music_Emu 0.6-pre
+#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 {
+public:
+
+ // GYM file header (optional; many files have NO header at all)
+ struct header_t
+ {
+ enum { size = 428 };
+
+ 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; }
+
+ // Disables running FM chips at higher than normal rate. Will result in slightly
+ // more aliasing of high notes.
+ void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; }
+
+// Implementation
+public:
+ Gym_Emu();
+ ~Gym_Emu();
+
+protected:
+ virtual blargg_err_t load_mem_( byte const [], int );
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t set_sample_rate_( int sample_rate );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t play_( int count, sample_t [] );
+ virtual void mute_voices_( int );
+ virtual void set_tempo_( double );
+
+private:
+ // Log
+ byte const* pos; // current position
+ byte const* loop_begin;
+ int log_offset; // size of header (0 or header_t::size)
+ int loop_remain; // frames remaining until loop_begin has been located
+ int clocks_per_frame;
+
+ bool disable_oversampling_;
+
+ // PCM
+ int pcm_amp;
+ int prev_pcm_count; // for detecting beginning/end of group of samples
+ int pcm_enabled;
+
+ // large objects
+ Dual_Resampler resampler;
+ Stereo_Buffer stereo_buf;
+ Blip_Buffer * pcm_buf;
+ Ym2612_Emu fm;
+ Sms_Apu apu;
+ Blip_Synth_Fast pcm_synth;
+ header_t header_;
+
+ byte const* log_begin() const { return file_begin() + log_offset; }
+ void parse_frame();
+ void run_pcm( byte const in [], int count );
+ int play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] );
+ static int play_frame_( void*, blip_time_t, int, sample_t [] );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.cpp
new file mode 100644
index 00000000..99afdf87
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.cpp
@@ -0,0 +1,361 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Hes_Apu.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+bool const center_waves = true; // reduces asymmetry and clamping when starting notes
+
+Hes_Apu::Hes_Apu()
+{
+ for ( Osc* osc = &oscs [osc_count]; osc != oscs; )
+ {
+ osc--;
+ osc->output [0] = NULL;
+ osc->output [1] = NULL;
+ osc->outputs [0] = NULL;
+ osc->outputs [1] = NULL;
+ osc->outputs [2] = NULL;
+ }
+
+ reset();
+}
+
+void Hes_Apu::reset()
+{
+ latch = 0;
+ balance = 0xFF;
+
+ for ( Osc* osc = &oscs [osc_count]; osc != oscs; )
+ {
+ osc--;
+ memset( osc, 0, offsetof (Osc,output) );
+ osc->lfsr = 0;
+ osc->control = 0x40;
+ osc->balance = 0xFF;
+ }
+
+ // Only last two oscs support noise
+ oscs [osc_count - 2].lfsr = 0x200C3; // equivalent to 1 in Fibonacci LFSR
+ oscs [osc_count - 1].lfsr = 0x200C3;
+}
+
+void Hes_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
+
+ if ( !center || !left || !right )
+ {
+ left = center;
+ right = center;
+ }
+
+ Osc& o = oscs [i];
+ o.outputs [0] = center;
+ o.outputs [1] = left;
+ o.outputs [2] = right;
+ balance_changed( o );
+}
+
+void Hes_Apu::run_osc( Blip_Synth_Fast& syn, Osc& o, blip_time_t end_time )
+{
+ int vol0 = o.volume [0];
+ int vol1 = o.volume [1];
+ int dac = o.dac;
+
+ Blip_Buffer* out0 = o.output [0]; // cache often-used values
+ Blip_Buffer* out1 = o.output [1];
+ if ( !(o.control & 0x80) )
+ out0 = NULL;
+
+ if ( out0 )
+ {
+ // Update amplitudes
+ if ( out1 )
+ {
+ int delta = dac * vol1 - o.last_amp [1];
+ if ( delta )
+ {
+ syn.offset( o.last_time, delta, out1 );
+ out1->set_modified();
+ }
+ }
+ int delta = dac * vol0 - o.last_amp [0];
+ if ( delta )
+ {
+ syn.offset( o.last_time, delta, out0 );
+ out0->set_modified();
+ }
+
+ // Don't generate if silent
+ if ( !(vol0 | vol1) )
+ out0 = NULL;
+ }
+
+ // Generate noise
+ int noise = 0;
+ if ( o.lfsr )
+ {
+ noise = o.noise & 0x80;
+
+ blip_time_t time = o.last_time + o.noise_delay;
+ if ( time < end_time )
+ {
+ int period = (~o.noise & 0x1F) * 128;
+ if ( !period )
+ period = 64;
+
+ if ( noise && out0 )
+ {
+ unsigned lfsr = o.lfsr;
+ do
+ {
+ int new_dac = -(lfsr & 1);
+ lfsr = (lfsr >> 1) ^ (0x30061 & new_dac);
+
+ int delta = (new_dac &= 0x1F) - dac;
+ if ( delta )
+ {
+ dac = new_dac;
+ syn.offset( time, delta * vol0, out0 );
+ if ( out1 )
+ syn.offset( time, delta * vol1, out1 );
+ }
+ time += period;
+ }
+ while ( time < end_time );
+
+ if ( !lfsr )
+ {
+ lfsr = 1;
+ check( false );
+ }
+ o.lfsr = lfsr;
+
+ out0->set_modified();
+ if ( out1 )
+ out1->set_modified();
+ }
+ else
+ {
+ // Maintain phase when silent
+ int count = (end_time - time + period - 1) / period;
+ time += count * period;
+
+ // not worth it
+ //while ( count-- )
+ // o.lfsr = (o.lfsr >> 1) ^ (0x30061 * (o.lfsr & 1));
+ }
+ }
+ o.noise_delay = time - end_time;
+ }
+
+ // Generate wave
+ blip_time_t time = o.last_time + o.delay;
+ if ( time < end_time )
+ {
+ int phase = (o.phase + 1) & 0x1F; // pre-advance for optimal inner loop
+ int period = o.period * 2;
+
+ if ( period >= 14 && out0 && !((o.control & 0x40) | noise) )
+ {
+ do
+ {
+ int new_dac = o.wave [phase];
+ phase = (phase + 1) & 0x1F;
+ int delta = new_dac - dac;
+ if ( delta )
+ {
+ dac = new_dac;
+ syn.offset( time, delta * vol0, out0 );
+ if ( out1 )
+ syn.offset( time, delta * vol1, out1 );
+ }
+ time += period;
+ }
+ while ( time < end_time );
+ out0->set_modified();
+ if ( out1 )
+ out1->set_modified();
+ }
+ else
+ {
+ // Maintain phase when silent
+ int count = end_time - time;
+ if ( !period )
+ period = 1;
+ count = (count + period - 1) / period;
+
+ phase += count; // phase will be masked below
+ time += count * period;
+ }
+
+ // TODO: Find whether phase increments even when both volumes are zero.
+ // CAN'T simply check for out0 being non-NULL, since it could be NULL
+ // if channel is muted in player, but still has non-zero volume.
+ // City Hunter breaks when this check is removed.
+ if ( !(o.control & 0x40) && (vol0 | vol1) )
+ o.phase = (phase - 1) & 0x1F; // undo pre-advance
+ }
+ o.delay = time - end_time;
+ check( o.delay >= 0 );
+
+ o.last_time = end_time;
+ o.dac = dac;
+ o.last_amp [0] = dac * vol0;
+ o.last_amp [1] = dac * vol1;
+}
+
+void Hes_Apu::balance_changed( Osc& osc )
+{
+ static short const log_table [32] = { // ~1.5 db per step
+ #define ENTRY( factor ) short (factor * amp_range / 31.0 + 0.5)
+ ENTRY( 0.000000 ),ENTRY( 0.005524 ),ENTRY( 0.006570 ),ENTRY( 0.007813 ),
+ ENTRY( 0.009291 ),ENTRY( 0.011049 ),ENTRY( 0.013139 ),ENTRY( 0.015625 ),
+ ENTRY( 0.018581 ),ENTRY( 0.022097 ),ENTRY( 0.026278 ),ENTRY( 0.031250 ),
+ ENTRY( 0.037163 ),ENTRY( 0.044194 ),ENTRY( 0.052556 ),ENTRY( 0.062500 ),
+ ENTRY( 0.074325 ),ENTRY( 0.088388 ),ENTRY( 0.105112 ),ENTRY( 0.125000 ),
+ ENTRY( 0.148651 ),ENTRY( 0.176777 ),ENTRY( 0.210224 ),ENTRY( 0.250000 ),
+ ENTRY( 0.297302 ),ENTRY( 0.353553 ),ENTRY( 0.420448 ),ENTRY( 0.500000 ),
+ ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ),
+ #undef ENTRY
+ };
+
+ int vol = (osc.control & 0x1F) - 0x1E * 2;
+
+ int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E);
+ if ( left < 0 ) left = 0;
+
+ int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E);
+ if ( right < 0 ) right = 0;
+
+ // optimizing for the common case of being centered also allows easy
+ // panning using Effects_Buffer
+
+ // Separate balance into center volume and additional on either left or right
+ osc.output [0] = osc.outputs [0]; // center
+ osc.output [1] = osc.outputs [2]; // right
+ int base = log_table [left ];
+ int side = log_table [right] - base;
+ if ( side < 0 )
+ {
+ base += side;
+ side = -side;
+ osc.output [1] = osc.outputs [1]; // left
+ }
+
+ // Optimize when output is far left, center, or far right
+ if ( !base || osc.output [0] == osc.output [1] )
+ {
+ base += side;
+ side = 0;
+ osc.output [0] = osc.output [1];
+ osc.output [1] = NULL;
+ osc.last_amp [1] = 0;
+ }
+
+ if ( center_waves )
+ {
+ // TODO: this can leave a non-zero level in a buffer (minor)
+ osc.last_amp [0] += (base - osc.volume [0]) * 16;
+ osc.last_amp [1] += (side - osc.volume [1]) * 16;
+ }
+
+ osc.volume [0] = base;
+ osc.volume [1] = side;
+}
+
+void Hes_Apu::write_data( blip_time_t time, int addr, int data )
+{
+ if ( addr == 0x800 )
+ {
+ latch = data & 7;
+ }
+ else if ( addr == 0x801 )
+ {
+ if ( balance != data )
+ {
+ balance = data;
+
+ for ( Osc* osc = &oscs [osc_count]; osc != oscs; )
+ {
+ osc--;
+ run_osc( synth, *osc, time );
+ balance_changed( *oscs );
+ }
+ }
+ }
+ else if ( latch < osc_count )
+ {
+ Osc& osc = oscs [latch];
+ run_osc( synth, osc, time );
+ switch ( addr )
+ {
+ case 0x802:
+ osc.period = (osc.period & 0xF00) | data;
+ break;
+
+ case 0x803:
+ osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8);
+ break;
+
+ case 0x804:
+ if ( osc.control & 0x40 & ~data )
+ osc.phase = 0;
+ osc.control = data;
+ balance_changed( osc );
+ break;
+
+ case 0x805:
+ osc.balance = data;
+ balance_changed( osc );
+ break;
+
+ case 0x806:
+ data &= 0x1F;
+ if ( !(osc.control & 0x40) )
+ {
+ osc.wave [osc.phase] = data;
+ osc.phase = (osc.phase + 1) & 0x1F;
+ }
+ else if ( osc.control & 0x80 )
+ {
+ osc.dac = data;
+ }
+ break;
+
+ case 0x807:
+ osc.noise = data;
+ break;
+
+ case 0x809:
+ if ( !(data & 0x80) && (data & 0x03) != 0 )
+ dprintf( "HES LFO not supported\n" );
+ }
+ }
+}
+
+void Hes_Apu::end_frame( blip_time_t end_time )
+{
+ for ( Osc* osc = &oscs [osc_count]; osc != oscs; )
+ {
+ osc--;
+ if ( end_time > osc->last_time )
+ run_osc( synth, *osc, end_time );
+ osc->last_time -= end_time;
+ check( osc->last_time >= 0 );
+ }
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.h
new file mode 100644
index 00000000..6375993c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu.h
@@ -0,0 +1,87 @@
+// Turbo Grafx 16 (PC Engine) PSG sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef HES_APU_H
+#define HES_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Hes_Apu {
+public:
+// Basics
+
+ // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0,
+ // output is mono.
+ void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Emulates to time t, then writes data to addr
+ void write_data( blip_time_t t, int addr, int data );
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Resets sound chip
+ void reset();
+
+ // Same as set_output(), but for a particular channel
+ enum { osc_count = 6 }; // 0 <= chan < osc_count
+ void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Sets treble equalization
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+ // Sets overall volume, where 1.0 is normal
+ void volume( double v ) { synth.volume( 1.8 / osc_count / amp_range * v ); }
+
+ // Registers are at io_addr to io_addr+io_size-1
+ enum { io_addr = 0x0800 };
+ enum { io_size = 10 };
+
+// Implementation
+public:
+ Hes_Apu();
+ typedef BOOST::uint8_t byte;
+
+private:
+ enum { amp_range = 0x8000 };
+ struct Osc
+ {
+ byte wave [32];
+ int delay;
+ int period;
+ int phase;
+
+ int noise_delay;
+ byte noise;
+ unsigned lfsr;
+
+ byte control;
+ byte balance;
+ byte dac;
+ short volume [2];
+ int last_amp [2];
+
+ blip_time_t last_time;
+ Blip_Buffer* output [2];
+ Blip_Buffer* outputs [3];
+ };
+ Osc oscs [osc_count];
+ int latch;
+ int balance;
+ Blip_Synth_Fast synth;
+
+ void balance_changed( Osc& );
+ static void run_osc( Blip_Synth_Fast&, Osc&, blip_time_t );
+};
+
+inline void Hes_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ for ( int i = osc_count; --i >= 0; )
+ set_output( i, c, l, r );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp
new file mode 100644
index 00000000..14eb6910
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.cpp
@@ -0,0 +1,309 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Hes_Apu_Adpcm.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Hes_Apu_Adpcm::Hes_Apu_Adpcm()
+{
+ output = NULL;
+
+ memset( &state, 0, sizeof( state ) );
+
+ reset();
+}
+
+void Hes_Apu_Adpcm::reset()
+{
+ last_time = 0;
+ next_timer = 0;
+ last_amp = 0;
+
+ memset( &state.pcmbuf, 0, sizeof(state.pcmbuf) );
+ memset( &state.port, 0, sizeof(state.port) );
+
+ state.ad_sample = 0;
+ state.ad_ref_index = 0;
+
+ state.addr = 0;
+ state.freq = 0;
+ state.writeptr = 0;
+ state.readptr = 0;
+ state.playflag = 0;
+ state.repeatflag = 0;
+ state.length = 0;
+ state.volume = 0xFF;
+ state.fadetimer = 0;
+ state.fadecount = 0;
+}
+
+void Hes_Apu_Adpcm::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
+
+ if ( !center || !left || !right )
+ {
+ left = center;
+ right = center;
+ }
+
+ output = center;
+}
+
+void Hes_Apu_Adpcm::run_until( blip_time_t end_time )
+{
+ int volume = state.volume;
+ int fadetimer = state.fadetimer;
+ int fadecount = state.fadecount;
+ int last_time = this->last_time;
+ double next_timer = this->next_timer;
+ int last_amp = this->last_amp;
+
+ Blip_Buffer* output = this->output; // cache often-used values
+
+ while ( state.playflag && last_time < end_time )
+ {
+ while ( last_time >= next_timer )
+ {
+ if ( fadetimer )
+ {
+ if ( fadecount > 0 )
+ {
+ fadecount--;
+ volume = 0xFF * fadecount / fadetimer;
+ }
+ else if ( fadecount < 0 )
+ {
+ fadecount++;
+ volume = 0xFF - ( 0xFF * fadecount / fadetimer );
+ }
+ }
+ next_timer += 7159.091;
+ }
+ int amp;
+ if ( state.ad_low_nibble )
+ {
+ amp = adpcm_decode( state.pcmbuf[ state.playptr ] & 0x0F );
+ state.ad_low_nibble = false;
+ state.playptr++;
+ state.playedsamplecount++;
+ if ( state.playedsamplecount == state.playlength )
+ {
+ state.playflag = 0;
+ }
+ }
+ else
+ {
+ amp = adpcm_decode( state.pcmbuf[ state.playptr ] >> 4 );
+ state.ad_low_nibble = true;
+ }
+ amp = amp * volume / 0xFF;
+ int delta = amp - last_amp;
+ if ( output && delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( last_time, delta, output );
+ }
+ last_time += state.freq;
+ }
+
+ if ( !state.playflag )
+ {
+ while ( next_timer <= end_time ) next_timer += 7159.091;
+ last_time = end_time;
+ }
+
+ this->last_time = last_time;
+ this->next_timer = next_timer;
+ this->last_amp = last_amp;
+ state.volume = volume;
+ state.fadetimer = fadetimer;
+ state.fadecount = fadecount;
+}
+
+void Hes_Apu_Adpcm::write_data( blip_time_t time, int addr, int data )
+{
+ if ( time > last_time ) run_until( time );
+
+ data &= 0xFF;
+ state.port[ addr & 15 ] = data;
+ switch ( addr & 15 )
+ {
+ case 8:
+ state.addr &= 0xFF00;
+ state.addr |= data;
+ break;
+ case 9:
+ state.addr &= 0xFF;
+ state.addr |= data << 8;
+ break;
+ case 10:
+ state.pcmbuf[ state.writeptr++ ] = data;
+ state.playlength ++;
+ break;
+ case 11:
+ dprintf("ADPCM DMA 0x%02X", data);
+ break;
+ case 13:
+ if ( data & 0x80 )
+ {
+ state.addr = 0;
+ state.freq = 0;
+ state.writeptr = 0;
+ state.readptr = 0;
+ state.playflag = 0;
+ state.repeatflag = 0;
+ state.length = 0;
+ state.volume = 0xFF;
+ }
+ if ( ( data & 3 ) == 3 )
+ {
+ state.writeptr = state.addr;
+ }
+ if ( data & 8 )
+ {
+ state.readptr = state.addr ? state.addr - 1 : state.addr;
+ }
+ if ( data & 0x10 )
+ {
+ state.length = state.addr;
+ }
+ state.repeatflag = data & 0x20;
+ state.playflag = data & 0x40;
+ if ( state.playflag )
+ {
+ state.playptr = state.readptr;
+ state.playlength = state.length + 1;
+ state.playedsamplecount = 0;
+ state.ad_sample = 0;
+ state.ad_low_nibble = false;
+ }
+ break;
+ case 14:
+ state.freq = 7159091 / ( 32000 / ( 16 - ( data & 15 ) ) );
+ break;
+ case 15:
+ switch ( data & 15 )
+ {
+ case 0:
+ case 8:
+ case 12:
+ state.fadetimer = -100;
+ state.fadecount = state.fadetimer;
+ break;
+ case 10:
+ state.fadetimer = 5000;
+ state.fadecount = state.fadetimer;
+ break;
+ case 14:
+ state.fadetimer = 1500;
+ state.fadecount = state.fadetimer;
+ break;
+ }
+ break;
+ }
+}
+
+int Hes_Apu_Adpcm::read_data( blip_time_t time, int addr )
+{
+ if ( time > last_time ) run_until( time );
+
+ switch ( addr & 15 )
+ {
+ case 10:
+ return state.pcmbuf [state.readptr++];
+ case 11:
+ return state.port [11] & ~1;
+ case 12:
+ if (!state.playflag)
+ {
+ state.port [12] |= 1;
+ state.port [12] &= ~8;
+ }
+ else
+ {
+ state.port [12] &= ~1;
+ state.port [12] |= 8;
+ }
+ return state.port [12];
+ case 13:
+ return state.port [13];
+ }
+
+ return 0xFF;
+}
+
+void Hes_Apu_Adpcm::end_frame( blip_time_t end_time )
+{
+ run_until( end_time );
+ last_time -= end_time;
+ next_timer -= (double)end_time;
+ check( last_time >= 0 );
+ if ( output )
+ output->set_modified();
+}
+
+static short stepsize[49] = {
+ 16, 17, 19, 21, 23, 25, 28,
+ 31, 34, 37, 41, 45, 50, 55,
+ 60, 66, 73, 80, 88, 97, 107,
+ 118, 130, 143, 157, 173, 190, 209,
+ 230, 253, 279, 307, 337, 371, 408,
+ 449, 494, 544, 598, 658, 724, 796,
+ 876, 963,1060,1166,1282,1411,1552
+};
+
+int Hes_Apu_Adpcm::adpcm_decode( int code )
+{
+ int step = stepsize[state.ad_ref_index];
+ int delta;
+ int c = code & 7;
+#if 1
+ delta = 0;
+ if ( c & 4 ) delta += step;
+ step >>= 1;
+ if ( c & 2 ) delta += step;
+ step >>= 1;
+ if ( c & 1 ) delta += step;
+ step >>= 1;
+ delta += step;
+#else
+ delta = ( ( c + c + 1 ) * step ) / 8; // maybe faster, but introduces rounding
+#endif
+ if ( c != code )
+ {
+ state.ad_sample -= delta;
+ if ( state.ad_sample < -2048 )
+ state.ad_sample = -2048;
+ }
+ else
+ {
+ state.ad_sample += delta;
+ if ( state.ad_sample > 2047 )
+ state.ad_sample = 2047;
+ }
+
+ static int const steps [8] = {
+ -1, -1, -1, -1, 2, 4, 6, 8
+ };
+ state.ad_ref_index += steps [c];
+ if ( state.ad_ref_index < 0 )
+ state.ad_ref_index = 0;
+ else if ( state.ad_ref_index > 48 )
+ state.ad_ref_index = 48;
+
+ return state.ad_sample;
+} \ No newline at end of file
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.h b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.h
new file mode 100644
index 00000000..a75b369a
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Apu_Adpcm.h
@@ -0,0 +1,94 @@
+// Turbo Grafx 16 (PC Engine) ADPCM sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef HES_APU_ADPCM_H
+#define HES_APU_ADPCM_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Hes_Apu_Adpcm {
+public:
+// Basics
+
+ // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0,
+ // output is mono.
+ void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Emulates to time t, then writes data to addr
+ void write_data( blip_time_t t, int addr, int data );
+
+ // Emulates to time t, then reads from addr
+ int read_data( blip_time_t t, int addr );
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Resets sound chip
+ void reset();
+
+ // Same as set_output(), but for a particular channel
+ enum { osc_count = 1 }; // 0 <= chan < osc_count
+ void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Sets treble equalization
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+ // Sets overall volume, where 1.0 is normal
+ void volume( double v ) { synth.volume( 0.6 / osc_count / amp_range * v ); }
+
+ // Registers are at io_addr to io_addr+io_size-1
+ enum { io_addr = 0x1800 };
+ enum { io_size = 0x400 };
+
+// Implementation
+public:
+ Hes_Apu_Adpcm();
+ typedef BOOST::uint8_t byte;
+
+private:
+ enum { amp_range = 2048 };
+
+ struct State
+ {
+ byte pcmbuf [0x10000];
+ byte port [0x10];
+ int ad_sample;
+ int ad_ref_index;
+ bool ad_low_nibble;
+ int freq;
+ unsigned short addr;
+ unsigned short writeptr;
+ unsigned short readptr;
+ unsigned short playptr;
+ byte playflag;
+ byte repeatflag;
+ int length;
+ int playlength;
+ int playedsamplecount;
+ int volume;
+ int fadetimer;
+ int fadecount;
+ };
+ State state;
+ Blip_Synth_Fast synth;
+
+ Blip_Buffer* output;
+ blip_time_t last_time;
+ double next_timer;
+ int last_amp;
+
+ void run_until( blip_time_t );
+
+ int adpcm_decode( int );
+};
+
+inline void Hes_Apu_Adpcm::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ set_output( 0, c, l, r );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.cpp
new file mode 100644
index 00000000..2640ad1f
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.cpp
@@ -0,0 +1,408 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Hes_Core.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const timer_mask = 0x04;
+int const vdp_mask = 0x02;
+int const i_flag_mask = 0x04;
+int const unmapped = 0xFF;
+
+int const period_60hz = 262 * 455; // scanlines * clocks per scanline
+
+Hes_Core::Hes_Core() : rom( Hes_Cpu::page_size )
+{
+ timer.raw_load = 0;
+}
+
+Hes_Core::~Hes_Core() { }
+
+void Hes_Core::unload()
+{
+ rom.clear();
+ Gme_Loader::unload();
+}
+
+bool Hes_Core::header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "HESM", 4 );
+}
+
+blargg_err_t Hes_Core::load_( Data_Reader& in )
+{
+ assert( offsetof (header_t,unused [4]) == header_t::size );
+ RETURN_ERR( rom.load( in, header_t::size, &header_, unmapped ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ if ( header_.vers != 0 )
+ set_warning( "Unknown file version" );
+
+ if ( memcmp( header_.data_tag, "DATA", 4 ) )
+ set_warning( "Data header missing" );
+
+ if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
+ set_warning( "Unknown header data" );
+
+ // File spec supports multiple blocks, but I haven't found any, and
+ // many files have bad sizes in the only block, so it's simpler to
+ // just try to load the damn data as best as possible.
+
+ int addr = get_le32( header_.addr );
+ int size = get_le32( header_.data_size );
+ int const rom_max = 0x100000;
+ if ( (unsigned) addr >= (unsigned) rom_max )
+ {
+ set_warning( "Invalid address" );
+ addr &= rom_max - 1;
+ }
+ if ( (unsigned) (addr + size) > (unsigned) rom_max )
+ set_warning( "Invalid size" );
+
+ if ( size != rom.file_size() )
+ {
+ if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) )
+ set_warning( "Multiple DATA not supported" );
+ else if ( size < rom.file_size() )
+ set_warning( "Extra file data" );
+ else
+ set_warning( "Missing file data" );
+ }
+
+ rom.set_addr( addr );
+
+ return blargg_ok;
+}
+
+void Hes_Core::recalc_timer_load()
+{
+ timer.load = timer.raw_load * timer_base + 1;
+}
+
+void Hes_Core::set_tempo( double t )
+{
+ play_period = (time_t) (period_60hz / t);
+ timer_base = (int) (1024 / t);
+ recalc_timer_load();
+}
+
+blargg_err_t Hes_Core::start_track( int track )
+{
+ memset( ram, 0, sizeof ram ); // some HES music relies on zero fill
+ memset( sgx, 0, sizeof sgx );
+
+ apu_.reset();
+ adpcm_.reset();
+ cpu.reset();
+
+ for ( int i = 0; i < (int) sizeof header_.banks; i++ )
+ set_mmr( i, header_.banks [i] );
+ set_mmr( cpu.page_count, 0xFF ); // unmapped beyond end of address space
+
+ irq.disables = timer_mask | vdp_mask;
+ irq.timer = cpu.future_time;
+ irq.vdp = cpu.future_time;
+
+ timer.enabled = false;
+ timer.raw_load = 0x80;
+ timer.count = timer.load;
+ timer.fired = false;
+ timer.last_time = 0;
+
+ vdp.latch = 0;
+ vdp.control = 0;
+ vdp.next_vbl = 0;
+
+ ram [0x1FF] = (idle_addr - 1) >> 8;
+ ram [0x1FE] = (idle_addr - 1) & 0xFF;
+ cpu.r.sp = 0xFD;
+ cpu.r.pc = get_le16( header_.init_addr );
+ cpu.r.a = track;
+
+ recalc_timer_load();
+
+ return blargg_ok;
+}
+
+// Hardware
+
+void Hes_Core::run_until( time_t present )
+{
+ while ( vdp.next_vbl < present )
+ vdp.next_vbl += play_period;
+
+ time_t elapsed = present - timer.last_time;
+ if ( elapsed > 0 )
+ {
+ if ( timer.enabled )
+ {
+ timer.count -= elapsed;
+ if ( timer.count <= 0 )
+ timer.count += timer.load;
+ }
+ timer.last_time = present;
+ }
+}
+
+void Hes_Core::write_vdp( int addr, int data )
+{
+ switch ( addr )
+ {
+ case 0:
+ vdp.latch = data & 0x1F;
+ break;
+
+ case 2:
+ if ( vdp.latch == 5 )
+ {
+ if ( data & 0x04 )
+ set_warning( "Scanline interrupt unsupported" );
+ run_until( cpu.time() );
+ vdp.control = data;
+ irq_changed();
+ }
+ else
+ {
+ dprintf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
+ }
+ break;
+
+ case 3:
+ dprintf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data );
+ break;
+ }
+}
+
+void Hes_Core::write_mem_( addr_t addr, int data )
+{
+ time_t time = cpu.time();
+ if ( (unsigned) (addr - apu_.io_addr) < apu_.io_size )
+ {
+ // Avoid going way past end when a long block xfer is writing to I/O space.
+ // Not a problem for other registers below because they don't write to
+ // Blip_Buffer.
+ time_t t = min( time, cpu.end_time() + 8 );
+ apu_.write_data( t, addr, data );
+ return;
+ }
+ if ( (unsigned) (addr - adpcm_.io_addr) < adpcm_.io_size )
+ {
+ time_t t = min( time, cpu.end_time() + 6 );
+ adpcm_.write_data( t, addr, data );
+ return;
+ }
+
+ switch ( addr )
+ {
+ case 0x0000:
+ case 0x0002:
+ case 0x0003:
+ write_vdp( addr, data );
+ return;
+
+ case 0x0C00: {
+ run_until( time );
+ timer.raw_load = (data & 0x7F) + 1;
+ recalc_timer_load();
+ timer.count = timer.load;
+ break;
+ }
+
+ case 0x0C01:
+ data &= 1;
+ if ( timer.enabled == data )
+ return;
+ run_until( time );
+ timer.enabled = data;
+ if ( data )
+ timer.count = timer.load;
+ break;
+
+ case 0x1402:
+ run_until( time );
+ irq.disables = data;
+ if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
+ dprintf( "Int mask: $%02X\n", data );
+ break;
+
+ case 0x1403:
+ run_until( time );
+ if ( timer.enabled )
+ timer.count = timer.load;
+ timer.fired = false;
+ break;
+
+#ifndef NDEBUG
+ case 0x1000: // I/O port
+ case 0x0402: // palette
+ case 0x0403:
+ case 0x0404:
+ case 0x0405:
+ return;
+
+ default:
+ dprintf( "unmapped write $%04X <- $%02X\n", addr, data );
+ return;
+#endif
+ }
+
+ irq_changed();
+}
+
+int Hes_Core::read_mem_( addr_t addr )
+{
+ time_t time = cpu.time();
+ addr &= cpu.page_size - 1;
+ switch ( addr )
+ {
+ case 0x0000:
+ if ( irq.vdp > time )
+ return 0;
+ irq.vdp = cpu.future_time;
+ run_until( time );
+ irq_changed();
+ return 0x20;
+
+ case 0x0002:
+ case 0x0003:
+ dprintf( "VDP read not supported: %d\n", addr );
+ return 0;
+
+ case 0x0C01:
+ //return timer.enabled; // TODO: remove?
+ case 0x0C00:
+ run_until( time );
+ dprintf( "Timer count read\n" );
+ return (unsigned) (timer.count - 1) / timer_base;
+
+ case 0x1402:
+ return irq.disables;
+
+ case 0x1403:
+ {
+ int status = 0;
+ if ( irq.timer <= time ) status |= timer_mask;
+ if ( irq.vdp <= time ) status |= vdp_mask;
+ return status;
+ }
+
+ case 0x180A:
+ case 0x180B:
+ case 0x180C:
+ case 0x180D:
+ return adpcm_.read_data( time, addr );
+
+ #ifndef NDEBUG
+ case 0x1000: // I/O port
+ //case 0x180C: // CD-ROM
+ //case 0x180D:
+ break;
+
+ default:
+ dprintf( "unmapped read $%04X\n", addr );
+ #endif
+ }
+
+ return unmapped;
+}
+
+void Hes_Core::irq_changed()
+{
+ time_t present = cpu.time();
+
+ if ( irq.timer > present )
+ {
+ irq.timer = cpu.future_time;
+ if ( timer.enabled && !timer.fired )
+ irq.timer = present + timer.count;
+ }
+
+ if ( irq.vdp > present )
+ {
+ irq.vdp = cpu.future_time;
+ if ( vdp.control & 0x08 )
+ irq.vdp = vdp.next_vbl;
+ }
+
+ time_t time = cpu.future_time;
+ if ( !(irq.disables & timer_mask) ) time = irq.timer;
+ if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp );
+
+ cpu.set_irq_time( time );
+}
+
+int Hes_Core::cpu_done()
+{
+ check( cpu.time() >= cpu.end_time() ||
+ (!(cpu.r.flags & i_flag_mask) && cpu.time() >= cpu.irq_time()) );
+
+ if ( !(cpu.r.flags & i_flag_mask) )
+ {
+ time_t present = cpu.time();
+
+ if ( irq.timer <= present && !(irq.disables & timer_mask) )
+ {
+ timer.fired = true;
+ irq.timer = cpu.future_time;
+ irq_changed(); // overkill, but not worth writing custom code
+ return 0x0A;
+ }
+
+ if ( irq.vdp <= present && !(irq.disables & vdp_mask) )
+ {
+ // work around for bugs with music not acknowledging VDP
+ //run_until( present );
+ //irq.vdp = cpu.future_time;
+ //irq_changed();
+ return 0x08;
+ }
+ }
+ return -1;
+}
+
+static void adjust_time( Hes_Core::time_t& time, Hes_Core::time_t delta )
+{
+ if ( time < Hes_Cpu::future_time )
+ {
+ time -= delta;
+ if ( time < 0 )
+ time = 0;
+ }
+}
+
+blargg_err_t Hes_Core::end_frame( time_t duration )
+{
+ if ( run_cpu( duration ) )
+ set_warning( "Emulation error (illegal instruction)" );
+
+ check( cpu.time() >= duration );
+ //check( time() - duration < 20 ); // Txx instruction could cause going way over
+
+ run_until( duration );
+
+ // end time frame
+ timer.last_time -= duration;
+ vdp.next_vbl -= duration;
+ cpu.end_frame( duration );
+ ::adjust_time( irq.timer, duration );
+ ::adjust_time( irq.vdp, duration );
+ apu_.end_frame( duration );
+ adpcm_.end_frame( duration );
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.h
new file mode 100644
index 00000000..0f5cbf8c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Core.h
@@ -0,0 +1,119 @@
+// TurboGrafx-16/PC Engine HES music file emulator core
+
+// Game_Music_Emu 0.6-pre
+#ifndef HES_CORE_H
+#define HES_CORE_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Hes_Apu.h"
+#include "Hes_Apu_Adpcm.h"
+#include "Hes_Cpu.h"
+
+class Hes_Core : public Gme_Loader {
+public:
+
+ // HES file header
+ enum { info_offset = 0x20 };
+ struct header_t
+ {
+ enum { size = 0x20 };
+
+ byte tag [4];
+ byte vers;
+ byte first_track;
+ byte init_addr [2];
+ byte banks [8];
+ byte data_tag [4];
+ byte data_size [4];
+ byte addr [4];
+ byte unused [4];
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ // Pointer to ROM data, for getting track information from
+ byte const* data() const { return rom.begin(); }
+
+ // Adjusts rate play routine is called at, where 1.0 is normal.
+ // Can be changed while track is playing.
+ void set_tempo( double );
+
+ // Sound chip
+ Hes_Apu& apu() { return apu_; }
+
+ Hes_Apu_Adpcm& adpcm() { return adpcm_; }
+
+ // Starts track
+ blargg_err_t start_track( int );
+
+ // Ends time frame at time t
+ typedef int time_t;
+ blargg_err_t end_frame( time_t );
+
+// Implementation
+public:
+ Hes_Core();
+ ~Hes_Core();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+
+private:
+ enum { idle_addr = 0x1FFF };
+
+ typedef int addr_t;
+ Hes_Cpu cpu;
+ Rom_Data rom;
+ header_t header_;
+ time_t play_period;
+ int timer_base;
+
+ struct {
+ time_t last_time;
+ int count;
+ int load;
+ int raw_load;
+ byte enabled;
+ byte fired;
+ } timer;
+
+ struct {
+ time_t next_vbl;
+ byte latch;
+ byte control;
+ } vdp;
+
+ struct {
+ time_t timer;
+ time_t vdp;
+ byte disables;
+ } irq;
+
+ void recalc_timer_load();
+
+ // large items
+ byte* write_pages [Hes_Cpu::page_count + 1]; // 0 if unmapped or I/O space
+ Hes_Apu apu_;
+ Hes_Apu_Adpcm adpcm_;
+ byte ram [Hes_Cpu::page_size];
+ byte sgx [3 * Hes_Cpu::page_size + Hes_Cpu::cpu_padding];
+
+ void irq_changed();
+ void run_until( time_t );
+ bool run_cpu( time_t end );
+ int read_mem_( addr_t );
+ int read_mem( addr_t );
+ void write_mem_( addr_t, int data );
+ void write_mem( addr_t, int );
+ void write_vdp( int addr, int data );
+ void set_mmr( int reg, int bank );
+ int cpu_done();
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.cpp
new file mode 100644
index 00000000..6c1f7d73
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.cpp
@@ -0,0 +1,123 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Hes_Cpu.h"
+
+#include "blargg_endian.h"
+#include "Hes_Core.h"
+
+//#include "hes_cpu_log.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define PAGE HES_CPU_PAGE
+
+int Hes_Core::read_mem( addr_t addr )
+{
+ check( addr < 0x10000 );
+ int result = *cpu.get_code( addr );
+ if ( cpu.mmr [PAGE( addr )] == 0xFF )
+ result = read_mem_( addr );
+ return result;
+}
+
+void Hes_Core::write_mem( addr_t addr, int data )
+{
+ check( addr < 0x10000 );
+ byte* out = write_pages [PAGE( addr )];
+ if ( out )
+ out [addr & (cpu.page_size - 1)] = data;
+ else if ( cpu.mmr [PAGE( addr )] == 0xFF )
+ write_mem_( addr, data );
+}
+
+void Hes_Core::set_mmr( int page, int bank )
+{
+ write_pages [page] = 0;
+ byte* data = rom.at_addr( bank * cpu.page_size );
+ if ( bank >= 0x80 )
+ {
+ data = 0;
+ switch ( bank )
+ {
+ case 0xF8:
+ data = ram;
+ break;
+
+ case 0xF9:
+ case 0xFA:
+ case 0xFB:
+ data = &sgx [(bank - 0xF9) * cpu.page_size];
+ break;
+
+ default:
+ if ( bank != 0xFF )
+ dprintf( "Unmapped bank $%02X\n", bank );
+ data = rom.unmapped();
+ goto end;
+ }
+
+ write_pages [page] = data;
+ }
+end:
+ cpu.set_mmr( page, bank, data );
+}
+
+#define READ_FAST( addr, out ) \
+{\
+ out = READ_CODE( addr );\
+ if ( CPU.mmr [PAGE( addr )] == 0xFF )\
+ {\
+ FLUSH_TIME();\
+ out = read_mem_( addr );\
+ CACHE_TIME();\
+ }\
+}
+
+#define WRITE_FAST( addr, data ) \
+{\
+ int page = PAGE( addr );\
+ byte* out = write_pages [page];\
+ addr &= CPU.page_size - 1;\
+ if ( out )\
+ {\
+ out [addr] = data;\
+ }\
+ else if ( CPU.mmr [page] == 0xFF )\
+ {\
+ FLUSH_TIME();\
+ write_mem_( addr, data );\
+ CACHE_TIME();\
+ }\
+}
+
+#define READ_LOW( addr ) (ram [addr])
+#define WRITE_LOW( addr, data ) (ram [addr] = data)
+#define READ_MEM( addr ) read_mem( addr )
+#define WRITE_MEM( addr, data ) write_mem( addr, data )
+#define WRITE_VDP( addr, data ) write_vdp( addr, data )
+#define CPU_DONE( result_out ) { FLUSH_TIME(); result_out = cpu_done(); CACHE_TIME(); }
+#define SET_MMR( reg, bank ) set_mmr( reg, bank )
+
+#define CPU cpu
+#define IDLE_ADDR idle_addr
+
+#define CPU_BEGIN \
+bool Hes_Core::run_cpu( time_t end_time )\
+{\
+ cpu.set_end_time( end_time );
+
+ #include "Hes_Cpu_run.h"
+
+ return illegal_encountered;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.h b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.h
new file mode 100644
index 00000000..b71cbc64
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu.h
@@ -0,0 +1,139 @@
+// PC Engine CPU emulator for use with HES music files
+
+// Game_Music_Emu 0.6-pre
+#ifndef HES_CPU_H
+#define HES_CPU_H
+
+#include "blargg_common.h"
+
+class Hes_Cpu {
+public:
+ typedef BOOST::uint8_t byte;
+ typedef int time_t;
+ typedef int addr_t;
+ enum { future_time = INT_MAX/2 + 1 };
+
+ void reset();
+
+ enum { page_bits = 13 };
+ enum { page_size = 1 << page_bits };
+ enum { page_count = 0x10000 / page_size };
+ void set_mmr( int reg, int bank, void const* code );
+
+ byte const* get_code( addr_t );
+
+ // NOT kept updated during emulation.
+ struct registers_t {
+ BOOST::uint16_t pc;
+ byte a;
+ byte x;
+ byte y;
+ byte flags;
+ byte sp;
+ };
+ registers_t r;
+
+ // page mapping registers
+ byte mmr [page_count + 1];
+
+ // Time of beginning of next instruction to be executed
+ time_t time() const { return cpu_state->time + cpu_state->base; }
+ void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; }
+ void adjust_time( int delta ) { cpu_state->time += delta; }
+
+ // Clocks past end (negative if before)
+ int time_past_end() const { return cpu_state->time; }
+
+ // Time of next IRQ
+ time_t irq_time() const { return irq_time_; }
+ void set_irq_time( time_t );
+
+ // Emulation stops once time >= end_time
+ time_t end_time() const { return end_time_; }
+ void set_end_time( time_t );
+
+ // Subtracts t from all times
+ void end_frame( time_t t );
+
+ // Can read this many bytes past end of a page
+ enum { cpu_padding = 8 };
+
+private:
+ // noncopyable
+ Hes_Cpu( const Hes_Cpu& );
+ Hes_Cpu& operator = ( const Hes_Cpu& );
+
+
+// Implementation
+public:
+ Hes_Cpu() { cpu_state = &cpu_state_; }
+ enum { irq_inhibit_mask = 0x04 };
+
+ struct cpu_state_t {
+ byte const* code_map [page_count + 1];
+ time_t base;
+ int time;
+ };
+ cpu_state_t* cpu_state; // points to cpu_state_ or a local copy
+ cpu_state_t cpu_state_;
+ time_t irq_time_;
+ time_t end_time_;
+
+private:
+ void set_code_page( int, void const* );
+ inline void update_end_time( time_t end, time_t irq );
+};
+
+#define HES_CPU_PAGE( addr ) ((unsigned) (addr) >> Hes_Cpu::page_bits)
+
+#if BLARGG_NONPORTABLE
+ #define HES_CPU_OFFSET( addr ) (addr)
+#else
+ #define HES_CPU_OFFSET( addr ) ((addr) & (Hes_Cpu::page_size - 1))
+#endif
+
+inline BOOST::uint8_t const* Hes_Cpu::get_code( addr_t addr )
+{
+ return cpu_state_.code_map [HES_CPU_PAGE( addr )] + HES_CPU_OFFSET( addr );
+}
+
+inline void Hes_Cpu::update_end_time( time_t end, time_t irq )
+{
+ if ( end > irq && !(r.flags & irq_inhibit_mask) )
+ end = irq;
+
+ cpu_state->time += cpu_state->base - end;
+ cpu_state->base = end;
+}
+
+inline void Hes_Cpu::set_irq_time( time_t t )
+{
+ irq_time_ = t;
+ update_end_time( end_time_, t );
+}
+
+inline void Hes_Cpu::set_end_time( time_t t )
+{
+ end_time_ = t;
+ update_end_time( t, irq_time_ );
+}
+
+inline void Hes_Cpu::end_frame( time_t t )
+{
+ assert( cpu_state == &cpu_state_ );
+ cpu_state_.base -= t;
+ if ( irq_time_ < future_time ) irq_time_ -= t;
+ if ( end_time_ < future_time ) end_time_ -= t;
+}
+
+inline void Hes_Cpu::set_mmr( int reg, int bank, void const* code )
+{
+ assert( (unsigned) reg <= page_count ); // allow page past end to be set
+ assert( (unsigned) bank < 0x100 );
+ mmr [reg] = bank;
+ byte const* p = STATIC_CAST(byte const*,code) - HES_CPU_OFFSET( reg << page_bits );
+ cpu_state->code_map [reg] = p;
+ cpu_state_.code_map [reg] = p;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu_run.h b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu_run.h
new file mode 100644
index 00000000..2374d217
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Cpu_run.h
@@ -0,0 +1,1342 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Except where noted, time() and related functions will NOT work
+correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
+CACHE_TIME() allow the time changing functions to work.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // time functions can be used
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+
+ // 0 <= addr <= 0x1FF
+ int READ_LOW( addr_t );
+ void WRITE_LOW( addr_t, int data );
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // Used by common instructions.
+ int READ_FAST( addr_t, int& out );
+ void WRITE_FAST( addr_t, int data );
+
+ // 0 <= addr <= 2
+ // ST0, ST1, ST2 instructions
+ void WRITE_VDP( int addr, int data );
+
+// The following can be used within macros:
+
+ // Current time
+ time_t TIME();
+
+ // Allows use of time functions
+ void FLUSH_TIME();
+
+ // Must be used before end of macro if FLUSH_TIME() was used earlier
+ void CACHE_TIME();
+
+// Configuration (optional; commented behavior if defined)
+
+ // Expanded just before beginning of code, to help debugger
+ #define CPU_BEGIN void my_run_cpu() {
+#endif
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// TODO: support T flag, including clearing it at appropriate times?
+
+// all zero-page should really use whatever is at page 1, but that would
+// reduce efficiency quite a bit
+int const ram_addr = 0x2000;
+
+void Hes_Cpu::reset()
+{
+ check( cpu_state == &cpu_state_ );
+ cpu_state = &cpu_state_;
+
+ cpu_state_.time = 0;
+ cpu_state_.base = 0;
+ irq_time_ = future_time;
+ end_time_ = future_time;
+
+ r.flags = 0x04;
+ r.sp = 0;
+ r.pc = 0;
+ r.a = 0;
+ r.x = 0;
+ r.y = 0;
+
+ // Be sure "blargg_endian.h" has been #included
+ blargg_verify_byte_order();
+}
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+// Time
+#define TIME() (s_time + s.base)
+#define FLUSH_TIME() {s.time = s_time;}
+#define CACHE_TIME() {s_time = s.time;}
+
+// Memory
+#define READ_STACK READ_LOW
+#define WRITE_STACK WRITE_LOW
+
+#define CODE_PAGE( addr ) s.code_map [HES_CPU_PAGE( addr )]
+#define CODE_OFFSET( addr ) HES_CPU_OFFSET( addr )
+#define READ_CODE( addr ) CODE_PAGE( addr ) [CODE_OFFSET( addr )]
+
+// Stack
+#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
+#define GET_SP() ((sp - 1) & 0xFF)
+#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100)
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const n80 = 0x80; // nz
+int const v40 = 0x40; // flags
+//int const t20 = 0x20;
+int const b10 = 0x10;
+int const d08 = 0x08; // flags
+int const i04 = 0x04; // flags
+int const z02 = 0x02; // nz
+int const c01 = 0x01; // c
+
+#define IS_NEG (nz & 0x8080)
+
+#define GET_FLAGS( out ) \
+{\
+ out = flags & (v40 | d08 | i04);\
+ out += ((nz >> 8) | nz) & n80;\
+ out += c >> 8 & c01;\
+ if ( !BYTE( nz ) )\
+ out += z02;\
+}
+
+#define SET_FLAGS( in ) \
+{\
+ flags = in & (v40 | d08 | i04);\
+ c = nz = in << 8;\
+ nz += ~in & z02;\
+}
+
+bool illegal_encountered = false;
+{
+ Hes_Cpu::cpu_state_t s = CPU.cpu_state_;
+ CPU.cpu_state = &s;
+ // even on x86, using s.time in place of s_time was slower
+ int s_time = s.time;
+
+ // registers
+ int pc = CPU.r.pc;
+ int a = CPU.r.a;
+ int x = CPU.r.x;
+ int y = CPU.r.y;
+ int sp;
+ SET_SP( CPU.r.sp );
+
+ // Flags
+ int flags;
+ int c; // carry set if (c & 0x100) != 0
+ int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
+ {
+ int temp = CPU.r.flags;
+ SET_FLAGS( temp );
+ }
+
+loop:
+
+ #ifndef NDEBUG
+ {
+ time_t correct = CPU.end_time_;
+ if ( !(flags & i04) && correct > CPU.irq_time_ )
+ correct = CPU.irq_time_;
+ check( s.base == correct );
+ /*
+ static int count;
+ if ( count == 1844 ) Debugger();
+ if ( s.base != correct ) dprintf( "%ld\n", count );
+ count++;
+ */
+ }
+ #endif
+
+ // Check all values
+ check( (unsigned) sp - 0x100 < 0x100 );
+ check( (unsigned) pc < 0x10000 + 0x100 ); // +0x100 so emulator can catch wrap-around
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ // Read instruction
+ byte const* instr = CODE_PAGE( pc );
+ int opcode;
+
+ if ( CODE_OFFSET(~0) == ~0 )
+ {
+ opcode = instr [pc];
+ pc++;
+ instr += pc;
+ }
+ else
+ {
+ instr += CODE_OFFSET( pc );
+ opcode = *instr++;
+ pc++;
+ }
+
+ // TODO: each reference lists slightly different timing values, ugh
+ static byte const clock_table [256] =
+ {// 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 1,7,3, 4,6,4,6,7,3,2,2,2,7,5,7,4,// 0
+ 2,7,7, 4,6,4,6,7,2,5,2,2,7,5,7,4,// 1
+ 7,7,3, 4,4,4,6,7,4,2,2,2,5,5,7,4,// 2
+ 2,7,7, 2,4,4,6,7,2,5,2,2,5,5,7,4,// 3
+ 7,7,3, 4,8,4,6,7,3,2,2,2,4,5,7,4,// 4
+ 2,7,7, 5,2,4,6,7,2,5,3,2,2,5,7,4,// 5
+ 7,7,2, 2,4,4,6,7,4,2,2,2,7,5,7,4,// 6
+ 2,7,7,17,4,4,6,7,2,5,4,2,7,5,7,4,// 7
+ 4,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// 8
+ 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// 9
+ 2,7,2, 7,4,4,4,7,2,2,2,2,5,5,5,4,// A
+ 2,7,7, 8,4,4,4,7,2,5,2,2,5,5,5,4,// B
+ 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// C
+ 2,7,7,17,2,4,6,7,2,5,3,2,2,5,7,4,// D
+ 2,7,2,17,4,4,6,7,2,2,2,2,5,5,7,4,// E
+ 2,7,7,17,2,4,6,7,2,5,4,2,2,5,7,4 // F
+ }; // 0x00 was 8
+
+ // Update time
+ if ( s_time >= 0 )
+ goto out_of_time;
+
+ #ifdef HES_CPU_LOG_H
+ log_cpu( "new", pc - 1, opcode, instr [0], instr [1], instr [2],
+ instr [3], instr [4], instr [5], a, x, y );
+ //log_opcode( opcode );
+ #endif
+
+ s_time += clock_table [opcode];
+
+ int data;
+ data = *instr;
+
+ switch ( opcode )
+ {
+// Macros
+
+#define GET_MSB() (instr [1])
+#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB());
+#define GET_ADDR() GET_LE16( instr )
+
+// TODO: is the penalty really always added? the original 6502 was much better
+//#define PAGE_PENALTY( lsb ) (void) (s_time += (lsb) >> 8)
+#define PAGE_PENALTY( lsb )
+
+// Branch
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH_( cond, adj )\
+{\
+ pc++;\
+ if ( !(cond) ) goto loop;\
+ pc = (BOOST::uint16_t) (pc + SBYTE( data ));\
+ s_time += adj;\
+ goto loop;\
+}
+
+#define BRANCH( cond ) BRANCH_( cond, 2 )
+
+ case 0xF0: // BEQ
+ BRANCH( !BYTE( nz ) );
+
+ case 0xD0: // BNE
+ BRANCH( BYTE( nz ) );
+
+ case 0x10: // BPL
+ BRANCH( !IS_NEG );
+
+ case 0x90: // BCC
+ BRANCH( !(c & 0x100) )
+
+ case 0x30: // BMI
+ BRANCH( IS_NEG )
+
+ case 0x50: // BVC
+ BRANCH( !(flags & v40) )
+
+ case 0x70: // BVS
+ BRANCH( flags & v40 )
+
+ case 0xB0: // BCS
+ BRANCH( c & 0x100 )
+
+ case 0x80: // BRA
+ branch_taken:
+ BRANCH_( true, 0 );
+
+ case 0xFF:
+ #ifdef IDLE_ADDR
+ if ( pc == IDLE_ADDR + 1 )
+ goto idle_done;
+ #endif
+
+ pc = (BOOST::uint16_t) pc;
+
+ case 0x0F: // BBRn
+ case 0x1F:
+ case 0x2F:
+ case 0x3F:
+ case 0x4F:
+ case 0x5F:
+ case 0x6F:
+ case 0x7F:
+ case 0x8F: // BBSn
+ case 0x9F:
+ case 0xAF:
+ case 0xBF:
+ case 0xCF:
+ case 0xDF:
+ case 0xEF: {
+ // Make two copies of bits, one negated
+ int t = 0x101 * READ_LOW( data );
+ t ^= 0xFF;
+ pc++;
+ data = GET_MSB();
+ BRANCH( t & (1 << (opcode >> 4)) )
+ }
+
+ case 0x4C: // JMP abs
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0x7C: // JMP (ind+X)
+ data += x;
+ case 0x6C:{// JMP (ind)
+ data += 0x100 * GET_MSB();
+ pc = GET_LE16( &READ_CODE( data ) );
+ goto loop;
+ }
+
+// Subroutine
+
+ case 0x44: // BSR
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, pc );
+ goto branch_taken;
+
+ case 0x20: { // JSR
+ int temp = pc + 1;
+ pc = GET_ADDR();
+ WRITE_STACK( SP( -1 ), temp >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, temp );
+ goto loop;
+ }
+
+ case 0x60: // RTS
+ pc = 1 + READ_STACK( sp );
+ pc += 0x100 * READ_STACK( SP( 1 ) );
+ sp = SP( 2 );
+ goto loop;
+
+ case 0x00: // BRK
+ goto handle_brk;
+
+// Common
+
+ case 0xBD:{// LDA abs,X
+ PAGE_PENALTY( data + x );
+ int addr = GET_ADDR() + x;
+ pc += 2;
+ READ_FAST( addr, nz );
+ a = nz;
+ goto loop;
+ }
+
+ case 0x9D:{// STA abs,X
+ int addr = GET_ADDR() + x;
+ pc += 2;
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+
+ case 0x95: // STA zp,x
+ data = BYTE( data + x );
+ case 0x85: // STA zp
+ pc++;
+ WRITE_LOW( data, a );
+ goto loop;
+
+ case 0xAE:{// LDX abs
+ int addr = GET_ADDR();
+ pc += 2;
+ READ_FAST( addr, nz );
+ x = nz;
+ goto loop;
+ }
+
+ case 0xA5: // LDA zp
+ a = nz = READ_LOW( data );
+ pc++;
+ goto loop;
+
+// Load/store
+
+ {
+ int addr;
+ case 0x91: // STA (ind),Y
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data ) + y;
+ pc++;
+ goto sta_ptr;
+
+ case 0x81: // STA (ind,X)
+ data = BYTE( data + x );
+ case 0x92: // STA (ind)
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data );
+ pc++;
+ goto sta_ptr;
+
+ case 0x99: // STA abs,Y
+ data += y;
+ case 0x8D: // STA abs
+ addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ sta_ptr:
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+
+ {
+ int addr;
+ case 0xA1: // LDA (ind,X)
+ data = BYTE( data + x );
+ case 0xB2: // LDA (ind)
+ addr = 0x100 * READ_LOW( BYTE( data + 1 ) );
+ addr += READ_LOW( data );
+ pc++;
+ goto a_nz_read_addr;
+
+ case 0xB1:// LDA (ind),Y
+ addr = READ_LOW( data ) + y;
+ PAGE_PENALTY( addr );
+ addr += 0x100 * READ_LOW( BYTE( data + 1 ) );
+ pc++;
+ goto a_nz_read_addr;
+
+ case 0xB9: // LDA abs,Y
+ data += y;
+ PAGE_PENALTY( data );
+ case 0xAD: // LDA abs
+ addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ a_nz_read_addr:
+ READ_FAST( addr, nz );
+ a = nz;
+ goto loop;
+ }
+
+ case 0xBE:{// LDX abs,y
+ PAGE_PENALTY( data + y );
+ int addr = GET_ADDR() + y;
+ pc += 2;
+ FLUSH_TIME();
+ x = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xB5: // LDA zp,x
+ a = nz = READ_LOW( BYTE( data + x ) );
+ pc++;
+ goto loop;
+
+ case 0xA9: // LDA #imm
+ pc++;
+ a = data;
+ nz = data;
+ goto loop;
+
+// Bit operations
+
+ case 0x3C: // BIT abs,x
+ data += x;
+ case 0x2C:{// BIT abs
+ int addr;
+ ADD_PAGE( addr );
+ FLUSH_TIME();
+ nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto bit_common;
+ }
+ case 0x34: // BIT zp,x
+ data = BYTE( data + x );
+ case 0x24: // BIT zp
+ data = READ_LOW( data );
+ case 0x89: // BIT imm
+ nz = data;
+ bit_common:
+ pc++;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( nz & a )
+ goto loop; // Z should be clear, and nz must be non-zero if nz & a is
+ nz <<= 8; // set Z flag without affecting N flag
+ goto loop;
+
+ {
+ int addr;
+
+ case 0xB3: // TST abs,x
+ addr = GET_MSB() + x;
+ goto tst_abs;
+
+ case 0x93: // TST abs
+ addr = GET_MSB();
+ tst_abs:
+ addr += 0x100 * instr [2];
+ pc++;
+ FLUSH_TIME();
+ nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto tst_common;
+ }
+
+ case 0xA3: // TST zp,x
+ nz = READ_LOW( BYTE( GET_MSB() + x ) );
+ goto tst_common;
+
+ case 0x83: // TST zp
+ nz = READ_LOW( GET_MSB() );
+ tst_common:
+ pc += 2;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( nz & data )
+ goto loop; // Z should be clear, and nz must be non-zero if nz & data is
+ nz <<= 8; // set Z flag without affecting N flag
+ goto loop;
+
+ {
+ int addr;
+ case 0x0C: // TSB abs
+ case 0x1C: // TRB abs
+ addr = GET_ADDR();
+ pc++;
+ goto txb_addr;
+
+ // TODO: everyone lists different behaviors for the flags flags, ugh
+ case 0x04: // TSB zp
+ case 0x14: // TRB zp
+ addr = data + ram_addr;
+ txb_addr:
+ FLUSH_TIME();
+ nz = a | READ_MEM( addr );
+ if ( opcode & 0x10 )
+ nz ^= a; // bits from a will already be set, so this clears them
+ flags = (flags & ~v40) + (nz & v40);
+ pc++;
+ WRITE_MEM( addr, nz );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0x07: // RMBn
+ case 0x17:
+ case 0x27:
+ case 0x37:
+ case 0x47:
+ case 0x57:
+ case 0x67:
+ case 0x77:
+ pc++;
+ READ_LOW( data ) &= ~(1 << (opcode >> 4));
+ goto loop;
+
+ case 0x87: // SMBn
+ case 0x97:
+ case 0xA7:
+ case 0xB7:
+ case 0xC7:
+ case 0xD7:
+ case 0xE7:
+ case 0xF7:
+ pc++;
+ READ_LOW( data ) |= 1 << ((opcode >> 4) - 8);
+ goto loop;
+
+// Load/store
+
+ case 0x9E: // STZ abs,x
+ data += x;
+ case 0x9C: // STZ abs
+ ADD_PAGE( data );
+ pc++;
+ FLUSH_TIME();
+ WRITE_MEM( data, 0 );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x74: // STZ zp,x
+ data = BYTE( data + x );
+ case 0x64: // STZ zp
+ pc++;
+ WRITE_LOW( data, 0 );
+ goto loop;
+
+ case 0x94: // STY zp,x
+ data = BYTE( data + x );
+ case 0x84: // STY zp
+ pc++;
+ WRITE_LOW( data, y );
+ goto loop;
+
+ case 0x96: // STX zp,y
+ data = BYTE( data + y );
+ case 0x86: // STX zp
+ pc++;
+ WRITE_LOW( data, x );
+ goto loop;
+
+ case 0xB6: // LDX zp,y
+ data = BYTE( data + y );
+ case 0xA6: // LDX zp
+ data = READ_LOW( data );
+ case 0xA2: // LDX #imm
+ pc++;
+ x = data;
+ nz = data;
+ goto loop;
+
+ case 0xB4: // LDY zp,x
+ data = BYTE( data + x );
+ case 0xA4: // LDY zp
+ data = READ_LOW( data );
+ case 0xA0: // LDY #imm
+ pc++;
+ y = data;
+ nz = data;
+ goto loop;
+
+ case 0xBC: // LDY abs,X
+ data += x;
+ PAGE_PENALTY( data );
+ case 0xAC:{// LDY abs
+ int addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ FLUSH_TIME();
+ y = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ {
+ int temp;
+ case 0x8C: // STY abs
+ temp = y;
+ if ( 0 )
+ case 0x8E: // STX abs
+ temp = x;
+ int addr = GET_ADDR();
+ pc += 2;
+ FLUSH_TIME();
+ WRITE_MEM( addr, temp );
+ CACHE_TIME();
+ goto loop;
+ }
+
+// Compare
+
+ case 0xEC:{// CPX abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpx_data;
+ }
+
+ case 0xE4: // CPX zp
+ data = READ_LOW( data );
+ case 0xE0: // CPX #imm
+ cpx_data:
+ nz = x - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+ case 0xCC:{// CPY abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpy_data;
+ }
+
+ case 0xC4: // CPY zp
+ data = READ_LOW( data );
+ case 0xC0: // CPY #imm
+ cpy_data:
+ nz = y - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+// Logical
+
+#define ARITH_ADDR_MODES( op )\
+ case op - 0x04: /* (ind,x) */\
+ data = BYTE( data + x );\
+ case op + 0x0D: /* (ind) */\
+ data = 0x100 * READ_LOW( BYTE( data + 1 ) ) + READ_LOW( data );\
+ goto ptr##op;\
+ case op + 0x0C:{/* (ind),y */\
+ int temp = READ_LOW( data ) + y;\
+ PAGE_PENALTY( temp );\
+ data = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\
+ goto ptr##op;\
+ }\
+ case op + 0x10: /* zp,X */\
+ data = BYTE( data + x );\
+ case op + 0x00: /* zp */\
+ data = READ_LOW( data );\
+ goto imm##op;\
+ case op + 0x14: /* abs,Y */\
+ data += y;\
+ goto ind##op;\
+ case op + 0x18: /* abs,X */\
+ data += x;\
+ ind##op:\
+ PAGE_PENALTY( data );\
+ case op + 0x08: /* abs */\
+ ADD_PAGE( data );\
+ ptr##op:\
+ FLUSH_TIME();\
+ data = READ_MEM( data );\
+ CACHE_TIME();\
+ case op + 0x04: /* imm */\
+ imm##op:
+
+ ARITH_ADDR_MODES( 0xC5 ) // CMP
+ nz = a - data;
+ pc++;
+ c = ~nz;
+ nz = BYTE( nz );
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x25 ) // AND
+ nz = (a &= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x45 ) // EOR
+ nz = (a ^= data);
+ pc++;
+ goto loop;
+
+ ARITH_ADDR_MODES( 0x05 ) // ORA
+ nz = (a |= data);
+ pc++;
+ goto loop;
+
+// Add/subtract
+
+ ARITH_ADDR_MODES( 0xE5 ) // SBC
+ data ^= 0xFF;
+ goto adc_imm;
+
+ ARITH_ADDR_MODES( 0x65 ) // ADC
+ adc_imm: {
+ if ( flags & d08 )
+ dprintf( "Decimal mode not supported\n" );
+ int carry = c >> 8 & 1;
+ int ov = (a ^ 0x80) + carry + SBYTE( data );
+ flags = (flags & ~v40) + (ov >> 2 & v40);
+ c = nz = a + data + carry;
+ pc++;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+// Shift/rotate
+
+ case 0x4A: // LSR A
+ c = 0;
+ case 0x6A: // ROR A
+ nz = c >> 1 & 0x80;
+ c = a << 8;
+ nz += a >> 1;
+ a = nz;
+ goto loop;
+
+ case 0x0A: // ASL A
+ nz = a << 1;
+ c = nz;
+ a = BYTE( nz );
+ goto loop;
+
+ case 0x2A: { // ROL A
+ nz = a << 1;
+ int temp = c >> 8 & 1;
+ c = nz;
+ nz += temp;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+ case 0x5E: // LSR abs,X
+ data += x;
+ case 0x4E: // LSR abs
+ c = 0;
+ case 0x6E: // ROR abs
+ ror_abs: {
+ ADD_PAGE( data );
+ FLUSH_TIME();
+ int temp = READ_MEM( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto rotate_common;
+ }
+
+ case 0x3E: // ROL abs,X
+ data += x;
+ goto rol_abs;
+
+ case 0x1E: // ASL abs,X
+ data += x;
+ case 0x0E: // ASL abs
+ c = 0;
+ case 0x2E: // ROL abs
+ rol_abs:
+ ADD_PAGE( data );
+ nz = c >> 8 & 1;
+ FLUSH_TIME();
+ nz += (c = READ_MEM( data ) << 1);
+ rotate_common:
+ pc++;
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x7E: // ROR abs,X
+ data += x;
+ goto ror_abs;
+
+ case 0x76: // ROR zp,x
+ data = BYTE( data + x );
+ goto ror_zp;
+
+ case 0x56: // LSR zp,x
+ data = BYTE( data + x );
+ case 0x46: // LSR zp
+ c = 0;
+ case 0x66: // ROR zp
+ ror_zp: {
+ int temp = READ_LOW( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto write_nz_zp;
+ }
+
+ case 0x36: // ROL zp,x
+ data = BYTE( data + x );
+ goto rol_zp;
+
+ case 0x16: // ASL zp,x
+ data = BYTE( data + x );
+ case 0x06: // ASL zp
+ c = 0;
+ case 0x26: // ROL zp
+ rol_zp:
+ nz = c >> 8 & 1;
+ nz += (c = READ_LOW( data ) << 1);
+ goto write_nz_zp;
+
+// Increment/decrement
+
+#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;
+
+ case 0x1A: // INA
+ INC_DEC( a, +1 )
+
+ case 0xE8: // INX
+ INC_DEC( x, +1 )
+
+ case 0xC8: // INY
+ INC_DEC( y, +1 )
+
+ case 0x3A: // DEA
+ INC_DEC( a, -1 )
+
+ case 0xCA: // DEX
+ INC_DEC( x, -1 )
+
+ case 0x88: // DEY
+ INC_DEC( y, -1 )
+
+ case 0xF6: // INC zp,x
+ data = BYTE( data + x );
+ case 0xE6: // INC zp
+ nz = 1;
+ goto add_nz_zp;
+
+ case 0xD6: // DEC zp,x
+ data = BYTE( data + x );
+ case 0xC6: // DEC zp
+ nz = -1;
+ add_nz_zp:
+ nz += READ_LOW( data );
+ write_nz_zp:
+ pc++;
+ WRITE_LOW( data, nz );
+ goto loop;
+
+ case 0xFE: // INC abs,x
+ data = x + GET_ADDR();
+ goto inc_ptr;
+
+ case 0xEE: // INC abs
+ data = GET_ADDR();
+ inc_ptr:
+ nz = 1;
+ goto inc_common;
+
+ case 0xDE: // DEC abs,x
+ data = x + GET_ADDR();
+ goto dec_ptr;
+
+ case 0xCE: // DEC abs
+ data = GET_ADDR();
+ dec_ptr:
+ nz = -1;
+ inc_common:
+ FLUSH_TIME();
+ pc += 2;
+ nz += READ_MEM( data );
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+// Transfer
+
+ case 0xA8: // TAY
+ y = nz = a;
+ goto loop;
+
+ case 0x98: // TYA
+ a = nz = y;
+ goto loop;
+
+ case 0xAA: // TAX
+ x = nz = a;
+ goto loop;
+
+ case 0x8A: // TXA
+ a = nz = x;
+ goto loop;
+
+ case 0x9A: // TXS
+ SET_SP( x ); // verified (no flag change)
+ goto loop;
+
+ case 0xBA: // TSX
+ x = nz = GET_SP();
+ goto loop;
+
+ #define SWAP_REGS( r1, r2 ) {\
+ int t = r1;\
+ r1 = r2;\
+ r2 = t;\
+ goto loop;\
+ }
+
+ case 0x02: // SXY
+ SWAP_REGS( x, y );
+
+ case 0x22: // SAX
+ SWAP_REGS( a, x );
+
+ case 0x42: // SAY
+ SWAP_REGS( a, y );
+
+ case 0x62: // CLA
+ a = 0;
+ goto loop;
+
+ case 0x82: // CLX
+ x = 0;
+ goto loop;
+
+ case 0xC2: // CLY
+ y = 0;
+ goto loop;
+
+// Stack
+
+ case 0x48: // PHA
+ sp = SP( -1 );
+ WRITE_STACK( sp, a );
+ goto loop;
+
+ case 0x68: // PLA
+ a = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0xDA: // PHX
+ sp = SP( -1 );
+ WRITE_STACK( sp, x );
+ goto loop;
+
+ case 0x5A: // PHY
+ sp = SP( -1 );
+ WRITE_STACK( sp, y );
+ goto loop;
+
+ case 0x40:{// RTI
+ pc = READ_STACK( SP( 1 ) );
+ pc += READ_STACK( SP( 2 ) ) * 0x100;
+ int temp = READ_STACK( sp );
+ sp = SP( 3 );
+ data = flags;
+ SET_FLAGS( temp );
+ CPU.r.flags = flags; // update externally-visible I flag
+ if ( (data ^ flags) & i04 )
+ {
+ time_t new_time = CPU.end_time_;
+ if ( !(flags & i04) && new_time > CPU.irq_time_ )
+ new_time = CPU.irq_time_;
+ int delta = s.base - new_time;
+ s.base = new_time;
+ s_time += delta;
+ }
+ goto loop;
+ }
+
+ case 0xFA: // PLX
+ x = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x7A: // PLY
+ y = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x28:{// PLP
+ int temp = READ_STACK( sp );
+ sp = SP( 1 );
+ int changed = flags ^ temp;
+ SET_FLAGS( temp );
+ if ( !(changed & i04) )
+ goto loop; // I flag didn't change
+ if ( flags & i04 )
+ goto handle_sei;
+ goto handle_cli;
+ }
+
+ case 0x08:{// PHP
+ int temp;
+ GET_FLAGS( temp );
+ sp = SP( -1 );
+ WRITE_STACK( sp, temp | b10 );
+ goto loop;
+ }
+
+// Flags
+
+ case 0x38: // SEC
+ c = 0x100;
+ goto loop;
+
+ case 0x18: // CLC
+ c = 0;
+ goto loop;
+
+ case 0xB8: // CLV
+ flags &= ~v40;
+ goto loop;
+
+ case 0xD8: // CLD
+ flags &= ~d08;
+ goto loop;
+
+ case 0xF8: // SED
+ flags |= d08;
+ goto loop;
+
+ case 0x58: // CLI
+ if ( !(flags & i04) )
+ goto loop;
+ flags &= ~i04;
+ handle_cli: {
+ //dprintf( "CLI at %d\n", TIME );
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.irq_time_;
+ if ( delta <= 0 )
+ {
+ if ( TIME() < CPU.irq_time_ )
+ goto loop;
+ goto delayed_cli;
+ }
+ s.base = CPU.irq_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ if ( delta >= s_time + 1 )
+ {
+ // delayed irq until after next instruction
+ s.base += s_time + 1;
+ s_time = -1;
+ CPU.irq_time_ = s.base; // TODO: remove, as only to satisfy debug check in loop
+ goto loop;
+ }
+
+ // TODO: implement
+ delayed_cli:
+ dprintf( "Delayed CLI not supported\n" );
+ goto loop;
+ }
+
+ case 0x78: // SEI
+ if ( flags & i04 )
+ goto loop;
+ flags |= i04;
+ handle_sei: {
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.end_time_;
+ s.base = CPU.end_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ dprintf( "Delayed SEI not supported\n" );
+ goto loop;
+ }
+
+// Special
+
+ case 0x53:{// TAM
+ int bits = data; // avoid using data across function call
+ pc++;
+ for ( int i = 0; i < 8; i++ )
+ if ( bits & (1 << i) )
+ SET_MMR( i, a );
+ goto loop;
+ }
+
+ case 0x43:{// TMA
+ pc++;
+ byte const* in = CPU.mmr;
+ do
+ {
+ if ( data & 1 )
+ a = *in;
+ in++;
+ }
+ while ( (data >>= 1) != 0 );
+ goto loop;
+ }
+
+ case 0x03: // ST0
+ case 0x13: // ST1
+ case 0x23:{// ST2
+ int addr = opcode >> 4;
+ if ( addr )
+ addr++;
+ pc++;
+ FLUSH_TIME();
+ WRITE_VDP( addr, data );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xEA: // NOP
+ goto loop;
+
+ case 0x54: // CSL
+ dprintf( "CSL not supported\n" );
+ illegal_encountered = true;
+ goto loop;
+
+ case 0xD4: // CSH
+ goto loop;
+
+ case 0xF4: { // SET
+ //int operand = GET_MSB();
+ dprintf( "SET not handled\n" );
+ //switch ( data )
+ //{
+ //}
+ illegal_encountered = true;
+ goto loop;
+ }
+
+// Block transfer
+
+ {
+ int in_alt;
+ int in_inc;
+ int out_alt;
+ int out_inc;
+
+ case 0xE3: // TIA
+ in_alt = 0;
+ goto bxfer_alt;
+
+ case 0xF3: // TAI
+ in_alt = 1;
+ bxfer_alt:
+ in_inc = in_alt ^ 1;
+ out_alt = in_inc;
+ out_inc = in_alt;
+ goto bxfer;
+
+ case 0xD3: // TIN
+ in_inc = 1;
+ out_inc = 0;
+ goto bxfer_no_alt;
+
+ case 0xC3: // TDD
+ in_inc = -1;
+ out_inc = -1;
+ goto bxfer_no_alt;
+
+ case 0x73: // TII
+ in_inc = 1;
+ out_inc = 1;
+ bxfer_no_alt:
+ in_alt = 0;
+ out_alt = 0;
+ bxfer:
+ int in = GET_LE16( instr + 0 );
+ int out = GET_LE16( instr + 2 );
+ int count = GET_LE16( instr + 4 );
+ if ( !count )
+ count = 0x10000;
+ pc += 6;
+ WRITE_STACK( SP( -1 ), y );
+ WRITE_STACK( SP( -2 ), a );
+ WRITE_STACK( SP( -3 ), x );
+ FLUSH_TIME();
+ do
+ {
+ // TODO: reads from $0800-$1400 in I/O page should do I/O
+ int t = READ_MEM( in );
+ in = WORD( in + in_inc );
+ s.time += 6;
+ if ( in_alt )
+ in_inc = -in_inc;
+ WRITE_MEM( out, t );
+ out = WORD( out + out_inc );
+ if ( out_alt )
+ out_inc = -out_inc;
+ }
+ while ( --count );
+ CACHE_TIME();
+ goto loop;
+ }
+
+// Illegal
+
+ default:
+ check( (unsigned) opcode <= 0xFF );
+ dprintf( "Illegal opcode $%02X at $%04X\n", (int) opcode, (int) pc - 1 );
+ illegal_encountered = true;
+ goto loop;
+ }
+ assert( false ); // catch missing 'goto loop' or accidental 'break'
+
+ int result_;
+handle_brk:
+ pc++;
+ result_ = 6;
+
+interrupt:
+ {
+ s_time += 7;
+
+ // Save PC and read vector
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ WRITE_STACK( SP( -2 ), pc );
+ pc = GET_LE16( &READ_CODE( 0xFFF0 ) + result_ );
+
+ // Save flags
+ int temp;
+ GET_FLAGS( temp );
+ if ( result_ == 6 )
+ temp |= b10; // BRK sets B bit
+ sp = SP( -3 );
+ WRITE_STACK( sp, temp );
+
+ // Update I flag in externally-visible flags
+ flags &= ~d08;
+ CPU.r.flags = (flags |= i04);
+
+ // Update time
+ int delta = s.base - CPU.end_time_;
+ if ( delta >= 0 )
+ goto loop;
+ s_time += delta;
+ s.base = CPU.end_time_;
+ goto loop;
+ }
+
+idle_done:
+ s_time = 0;
+
+out_of_time:
+ pc--;
+
+ // Optional action that triggers interrupt or changes irq/end time
+ #ifdef CPU_DONE
+ {
+ CPU_DONE( result_ );
+ if ( result_ >= 0 )
+ goto interrupt;
+ if ( s_time < 0 )
+ goto loop;
+ }
+ #endif
+
+ // Flush cached state
+ CPU.r.pc = pc;
+ CPU.r.sp = GET_SP();
+ CPU.r.a = a;
+ CPU.r.x = x;
+ CPU.r.y = y;
+
+ int temp;
+ GET_FLAGS( temp );
+ CPU.r.flags = temp;
+
+ CPU.cpu_state_.base = s.base;
+ CPU.cpu_state_.time = s_time;
+ CPU.cpu_state = &CPU.cpu_state_;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.cpp
new file mode 100644
index 00000000..0253fce3
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.cpp
@@ -0,0 +1,163 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Hes_Emu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Hes_Emu::Hes_Emu()
+{
+ set_type( gme_hes_type );
+ set_silence_lookahead( 6 );
+ set_gain( 1.11 );
+}
+
+Hes_Emu::~Hes_Emu() { }
+
+void Hes_Emu::unload()
+{
+ core.unload();
+ Music_Emu::unload();
+}
+
+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 < len && in [i]; i++ )
+ if ( (unsigned) (in [i] - ' ') >= 0xFF - ' ' ) // 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( core.data() + core.info_offset, out );
+ return blargg_ok;
+}
+
+struct Hes_File : Gme_Info_
+{
+ enum { fields_offset = Hes_Core::header_t::size + Hes_Core::info_offset };
+
+ union header_t {
+ Hes_Core::header_t header;
+ byte data [fields_offset + 0x30 * 3];
+ } h;
+
+ Hes_File()
+ {
+ set_type( gme_hes_type );
+ }
+
+ blargg_err_t load_( Data_Reader& in )
+ {
+ blargg_err_t err = in.read( &h, sizeof h );
+ if ( err )
+ return (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err);
+
+ if ( !h.header.valid_tag() )
+ return blargg_err_file_type;
+
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ copy_hes_fields( h.data + fields_offset, out );
+ return blargg_ok;
+ }
+};
+
+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 }};
+
+blargg_err_t Hes_Emu::load_( Data_Reader& in )
+{
+ RETURN_ERR( core.load( in ) );
+
+ static const char* const names [Hes_Apu::osc_count + Hes_Apu_Adpcm::osc_count] = {
+ "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2", "ADPCM"
+ };
+ set_voice_names( names );
+
+ static int const types [Hes_Apu::osc_count + Hes_Apu_Adpcm::osc_count] = {
+ wave_type+0, wave_type+1, wave_type+2, wave_type+3, mixed_type+0, mixed_type+1, mixed_type+2
+ };
+ set_voice_types( types );
+
+ set_voice_count( core.apu().osc_count + core.adpcm().osc_count );
+ core.apu().volume( gain() );
+ core.adpcm().volume( gain() );
+
+ return setup_buffer( 7159091 );
+}
+
+void Hes_Emu::update_eq( blip_eq_t const& eq )
+{
+ core.apu().treble_eq( eq );
+}
+
+void Hes_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ if ( i < core.apu().osc_count )
+ core.apu().set_output( i, c, l, r );
+ else if ( i == core.apu().osc_count )
+ core.adpcm().set_output( 0, c, l, r );
+}
+
+void Hes_Emu::set_tempo_( double t )
+{
+ core.set_tempo( t );
+}
+
+blargg_err_t Hes_Emu::start_track_( int track )
+{
+ RETURN_ERR( Classic_Emu::start_track_( track ) );
+ return core.start_track( track );
+}
+
+blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int )
+{
+ return core.end_frame( duration_ );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.h
new file mode 100644
index 00000000..6d7293b6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Hes_Emu.h
@@ -0,0 +1,40 @@
+// TurboGrafx-16/PC Engine HES music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef HES_EMU_H
+#define HES_EMU_H
+
+#include "Classic_Emu.h"
+#include "Hes_Core.h"
+
+class Hes_Emu : public Classic_Emu {
+public:
+
+ static gme_type_t static_type() { return gme_hes_type; }
+
+ // HES file header (see Hes_Core.h)
+ typedef Hes_Core::header_t header_t;
+
+ // Header for currently loaded file
+ header_t const& header() const { return core.header(); }
+
+// Implementation
+public:
+ Hes_Emu();
+ ~Hes_Emu();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+
+private:
+ Hes_Core core;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.cpp
new file mode 100644
index 00000000..01396016
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.cpp
@@ -0,0 +1,214 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Kss_Core.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Kss_Core::Kss_Core() : rom( Kss_Cpu::page_size )
+{
+ memset( unmapped_read, 0xFF, sizeof unmapped_read );
+}
+
+Kss_Core::~Kss_Core() { }
+
+void Kss_Core::unload()
+{
+ rom.clear();
+}
+
+static blargg_err_t check_kss_header( void const* header )
+{
+ if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) )
+ return blargg_err_file_type;
+ return blargg_ok;
+}
+
+blargg_err_t Kss_Core::load_( Data_Reader& in )
+{
+ memset( &header_, 0, sizeof header_ );
+ assert( offsetof (header_t,msx_audio_vol) == header_t::size - 1 );
+ RETURN_ERR( rom.load( in, header_t::base_size, &header_, 0 ) );
+
+ RETURN_ERR( check_kss_header( header_.tag ) );
+
+ header_.last_track [0] = 255;
+ if ( header_.tag [3] == 'C' )
+ {
+ if ( header_.extra_header )
+ {
+ header_.extra_header = 0;
+ set_warning( "Unknown data in header" );
+ }
+ if ( header_.device_flags & ~0x0F )
+ {
+ header_.device_flags &= 0x0F;
+ set_warning( "Unknown data in header" );
+ }
+ }
+ else if ( header_.extra_header )
+ {
+ if ( header_.extra_header != header_.ext_size )
+ {
+ header_.extra_header = 0;
+ set_warning( "Invalid extra_header_size" );
+ }
+ else
+ {
+ memcpy( header_.data_size, rom.begin(), header_.ext_size );
+ }
+ }
+
+ #ifndef NDEBUG
+ {
+ int ram_mode = header_.device_flags & 0x84; // MSX
+ if ( header_.device_flags & 0x02 ) // SMS
+ ram_mode = (header_.device_flags & 0x88);
+
+ if ( ram_mode )
+ dprintf( "RAM not supported\n" ); // TODO: support
+ }
+ #endif
+
+ return blargg_ok;
+}
+
+void Kss_Core::jsr( byte const (&addr) [2] )
+{
+ ram [--cpu.r.sp] = idle_addr >> 8;
+ ram [--cpu.r.sp] = idle_addr & 0xFF;
+ cpu.r.pc = get_le16( addr );
+}
+
+blargg_err_t Kss_Core::start_track( int track )
+{
+ memset( ram, 0xC9, 0x4000 );
+ memset( ram + 0x4000, 0, sizeof ram - 0x4000 );
+
+ // copy driver code to lo RAM
+ static byte const bios [] = {
+ 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG
+ 0xD3, 0xA0, 0xDB, 0xA2, 0xC9 // $0009: RDPSG
+ };
+ static byte const vectors [] = {
+ 0xC3, 0x01, 0x00, // $0093: WRTPSG vector
+ 0xC3, 0x09, 0x00, // $0096: RDPSG vector
+ };
+ memcpy( ram + 0x01, bios, sizeof bios );
+ memcpy( ram + 0x93, vectors, sizeof vectors );
+
+ // copy non-banked data into RAM
+ int load_addr = get_le16( header_.load_addr );
+ int orig_load_size = get_le16( header_.load_size );
+ int load_size = min( orig_load_size, rom.file_size() );
+ load_size = min( load_size, (int) mem_size - load_addr );
+ if ( load_size != orig_load_size )
+ set_warning( "Excessive data size" );
+ memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size );
+
+ rom.set_addr( -load_size - header_.extra_header );
+
+ // check available bank data
+ int const bank_size = this->bank_size();
+ int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size;
+ bank_count = header_.bank_mode & 0x7F;
+ if ( bank_count > max_banks )
+ {
+ bank_count = max_banks;
+ set_warning( "Bank data missing" );
+ }
+ //dprintf( "load_size : $%X\n", load_size );
+ //dprintf( "bank_size : $%X\n", bank_size );
+ //dprintf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F );
+
+ ram [idle_addr] = 0xFF;
+ cpu.reset( unmapped_write, unmapped_read );
+ cpu.map_mem( 0, mem_size, ram, ram );
+
+ cpu.r.sp = 0xF380;
+ cpu.r.b.a = track;
+ cpu.r.b.h = 0;
+ next_play = play_period;
+ gain_updated = false;
+ jsr( header_.init_addr );
+
+ return blargg_ok;
+}
+
+void Kss_Core::set_bank( int logical, int physical )
+{
+ int const bank_size = this->bank_size();
+
+ int addr = 0x8000;
+ if ( logical && bank_size == 8 * 1024 )
+ addr = 0xA000;
+
+ physical -= header_.first_bank;
+ if ( (unsigned) physical >= (unsigned) bank_count )
+ {
+ byte* data = ram + addr;
+ cpu.map_mem( addr, bank_size, data, data );
+ }
+ else
+ {
+ int phys = physical * bank_size;
+ for ( int offset = 0; offset < bank_size; offset += cpu.page_size )
+ cpu.map_mem( addr + offset, cpu.page_size,
+ unmapped_write, rom.at_addr( phys + offset ) );
+ }
+}
+
+void Kss_Core::cpu_out( time_t, addr_t addr, int data )
+{
+ dprintf( "OUT $%04X,$%02X\n", addr, data );
+}
+
+int Kss_Core::cpu_in( time_t, addr_t addr )
+{
+ dprintf( "IN $%04X\n", addr );
+ return 0xFF;
+}
+
+blargg_err_t Kss_Core::end_frame( time_t end )
+{
+ while ( cpu.time() < end )
+ {
+ time_t next = min( end, next_play );
+ run_cpu( next );
+ if ( cpu.r.pc == idle_addr )
+ cpu.set_time( next );
+
+ if ( cpu.time() >= next_play )
+ {
+ next_play += play_period;
+ if ( cpu.r.pc == idle_addr )
+ {
+ if ( !gain_updated )
+ {
+ gain_updated = true;
+ update_gain();
+ }
+
+ jsr( header_.play_addr );
+ }
+ }
+ }
+
+ next_play -= end;
+ check( next_play >= 0 );
+ cpu.adjust_time( -end );
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.h
new file mode 100644
index 00000000..3c9d8d9c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Core.h
@@ -0,0 +1,97 @@
+// MSX computer KSS music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef KSS_CORE_H
+#define KSS_CORE_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Z80_Cpu.h"
+
+class Kss_Core : public Gme_Loader {
+public:
+ // KSS file header
+ struct header_t
+ {
+ enum { size = 0x20 };
+ enum { base_size = 0x10 };
+ enum { ext_size = size - base_size };
+
+ byte tag [4];
+ byte load_addr [2];
+ byte load_size [2];
+ byte init_addr [2];
+ byte play_addr [2];
+ byte first_bank;
+ byte bank_mode;
+ byte extra_header;
+ byte device_flags;
+
+ // KSSX extended data, if extra_header==0x10
+ byte data_size [4];
+ byte unused [4];
+ byte first_track [2];
+ byte last_track [2]; // if no extended data, we set this to 0xFF
+ byte psg_vol;
+ byte scc_vol;
+ byte msx_music_vol;
+ byte msx_audio_vol;
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ typedef int time_t;
+ void set_play_period( time_t p ) { play_period = p; }
+
+ blargg_err_t start_track( int );
+
+ blargg_err_t end_frame( time_t );
+
+protected:
+ typedef Z80_Cpu Kss_Cpu;
+ Kss_Cpu cpu;
+
+ void set_bank( int logical, int physical );
+
+ typedef int addr_t;
+ virtual void cpu_write( addr_t, int ) = 0;
+ virtual int cpu_in( time_t, addr_t );
+ virtual void cpu_out( time_t, addr_t, int );
+
+ // Called after one frame of emulation
+ virtual void update_gain() = 0;
+
+// Implementation
+public:
+ Kss_Core();
+ virtual ~Kss_Core();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual void unload();
+
+private:
+ enum { idle_addr = 0xFFFF };
+
+ Rom_Data rom;
+ header_t header_;
+ bool gain_updated;
+ int bank_count;
+ time_t play_period;
+ time_t next_play;
+
+ // large items
+ enum { mem_size = 0x10000 };
+ byte ram [mem_size + Kss_Cpu::cpu_padding];
+ byte unmapped_read [0x100]; // TODO: why isn't this page_size?
+ // because CPU can't read beyond this in last page? or because it will spill into unmapped_write?
+
+ byte unmapped_write [Kss_Cpu::page_size];
+
+ int bank_size() const { return (16 * 1024) >> (header_.bank_mode >> 7 & 1); }
+ bool run_cpu( time_t end );
+ void jsr( byte const (&addr) [2] );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Cpu.cpp
new file mode 100644
index 00000000..ef9f1040
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Cpu.cpp
@@ -0,0 +1,35 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Kss_Core.h"
+
+#include "blargg_endian.h"
+//#include "z80_cpu_log.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data )
+#define IN_PORT( addr ) cpu_in( TIME(), addr )
+#define WRITE_MEM( addr, data ) {FLUSH_TIME(); cpu_write( addr, data );}
+#define IDLE_ADDR idle_addr
+#define CPU cpu
+
+#define CPU_BEGIN \
+bool Kss_Core::run_cpu( time_t end_time )\
+{\
+ cpu.set_end_time( end_time );
+
+ #include "Z80_Cpu_run.h"
+
+ return warning;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.cpp
new file mode 100644
index 00000000..9eac0756
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.cpp
@@ -0,0 +1,470 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Kss_Emu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define IF_PTR( ptr ) if ( ptr ) (ptr)
+
+int const clock_rate = 3579545;
+
+#define FOR_EACH_APU( macro )\
+{\
+ macro( sms.psg );\
+ macro( sms.fm );\
+ macro( msx.psg );\
+ macro( msx.scc );\
+ macro( msx.music );\
+ macro( msx.audio );\
+}
+
+Kss_Emu::Kss_Emu() :
+ core( this )
+{
+ #define ACTION( apu ) { core.apu = NULL; }
+ FOR_EACH_APU( ACTION );
+ #undef ACTION
+
+ set_type( gme_kss_type );
+}
+
+Kss_Emu::~Kss_Emu()
+{
+ unload();
+}
+
+inline void Kss_Emu::Core::unload()
+{
+ #define ACTION( ptr ) { delete (ptr); (ptr) = 0; }
+ FOR_EACH_APU( ACTION );
+ #undef ACTION
+}
+
+void Kss_Emu::unload()
+{
+ core.unload();
+ Classic_Emu::unload();
+}
+
+// Track info
+
+static void copy_kss_fields( Kss_Core::header_t const& h, track_info_t* out )
+{
+ const char* system = "MSX";
+
+ if ( h.device_flags & 0x02 )
+ {
+ system = "Sega Master System";
+ if ( h.device_flags & 0x04 )
+ system = "Game Gear";
+
+ if ( h.device_flags & 0x01 )
+ system = "Sega Mark III";
+ }
+ else
+ {
+ if ( h.device_flags & 0x09 )
+ system = "MSX + FM Sound";
+ }
+ Gme_File::copy_field_( out->system, system );
+}
+
+blargg_err_t Kss_Emu::track_info_( track_info_t* out, int ) const
+{
+ copy_kss_fields( header(), out );
+// TODO: remove
+//if ( msx.music ) strcpy( out->system, "msxmusic" );
+//if ( msx.audio ) strcpy( out->system, "msxaudio" );
+//if ( sms.fm ) strcpy( out->system, "fmunit" );
+ return blargg_ok;
+}
+
+static blargg_err_t check_kss_header( void const* header )
+{
+ if ( memcmp( header, "KSCC", 4 ) && memcmp( header, "KSSX", 4 ) )
+ return blargg_err_file_type;
+
+ return blargg_ok;
+}
+
+struct Kss_File : Gme_Info_
+{
+ Kss_Emu::header_t header_;
+
+ Kss_File() { set_type( gme_kss_type ); }
+
+ blargg_err_t load_( Data_Reader& in )
+ {
+ memset( &header_, 0, sizeof header_ );
+ blargg_err_t err = in.read( &header_, header_.size );
+ if ( err )
+ return (err == blargg_err_file_eof ? blargg_err_file_type : err);
+
+ if ( header_.tag [3] == 'X' && header_.extra_header == 0x10 )
+ set_track_count( get_le16( header_.last_track ) + 1 );
+
+ return check_kss_header( &header_ );
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ copy_kss_fields( header_, out );
+ return blargg_ok;
+ }
+};
+
+static Music_Emu* new_kss_emu () { return BLARGG_NEW Kss_Emu ; }
+static Music_Emu* new_kss_file() { return BLARGG_NEW Kss_File; }
+
+gme_type_t_ const gme_kss_type [1] = {{
+ "MSX",
+ 256,
+ &new_kss_emu,
+ &new_kss_file,
+ "KSS",
+ 0x03
+}};
+
+// Setup
+
+void Kss_Emu::Core::update_gain_()
+{
+ double g = emu.gain();
+ if ( msx.music || msx.audio || sms.fm )
+ {
+ g *= 0.3;
+ }
+ else
+ {
+ g *= 1.2;
+ if ( scc_accessed )
+ g *= 1.4;
+ }
+
+ #define ACTION( apu ) IF_PTR( apu )->volume( g )
+ FOR_EACH_APU( ACTION );
+ #undef ACTION
+}
+
+static blargg_err_t new_opl_apu( Opl_Apu::type_t type, Opl_Apu** out )
+{
+ check( !*out );
+ CHECK_ALLOC( *out = BLARGG_NEW( Opl_Apu ) );
+ blip_time_t const period = 72;
+ int const rate = clock_rate / period;
+ return (*out)->init( rate * period, rate, period, type );
+}
+
+blargg_err_t Kss_Emu::load_( Data_Reader& in )
+{
+ RETURN_ERR( core.load( in ) );
+ set_warning( core.warning() );
+
+ set_track_count( get_le16( header().last_track ) + 1 );
+
+ core.scc_enabled = false;
+ if ( header().device_flags & 0x02 ) // Sega Master System
+ {
+ int const osc_count = Sms_Apu::osc_count + Opl_Apu::osc_count;
+ static const char* const names [osc_count] = {
+ "Square 1", "Square 2", "Square 3", "Noise", "FM"
+ };
+ set_voice_names( names );
+
+ static int const types [osc_count] = {
+ wave_type+1, wave_type+3, wave_type+2, mixed_type+1, wave_type+0
+ };
+ set_voice_types( types );
+
+ // sms.psg
+ set_voice_count( Sms_Apu::osc_count );
+ check( !core.sms.psg );
+ CHECK_ALLOC( core.sms.psg = BLARGG_NEW Sms_Apu );
+
+ // sms.fm
+ if ( header().device_flags & 0x01 )
+ {
+ set_voice_count( osc_count );
+ RETURN_ERR( new_opl_apu( Opl_Apu::type_smsfmunit, &core.sms.fm ) );
+ }
+
+ }
+ else // MSX
+ {
+ int const osc_count = Ay_Apu::osc_count + Opl_Apu::osc_count;
+ static const char* const names [osc_count] = {
+ "Square 1", "Square 2", "Square 3", "FM"
+ };
+ set_voice_names( names );
+
+ static int const types [osc_count] = {
+ wave_type+1, wave_type+3, wave_type+2, wave_type+0
+ };
+ set_voice_types( types );
+
+ // msx.psg
+ set_voice_count( Ay_Apu::osc_count );
+ check( !core.msx.psg );
+ CHECK_ALLOC( core.msx.psg = BLARGG_NEW Ay_Apu );
+
+ if ( header().device_flags & 0x10 )
+ set_warning( "MSX stereo not supported" );
+
+ // msx.music
+ if ( header().device_flags & 0x01 )
+ {
+ set_voice_count( osc_count );
+ RETURN_ERR( new_opl_apu( Opl_Apu::type_msxmusic, &core.msx.music ) );
+ }
+
+ // msx.audio
+ if ( header().device_flags & 0x08 )
+ {
+ set_voice_count( osc_count );
+ RETURN_ERR( new_opl_apu( Opl_Apu::type_msxaudio, &core.msx.audio ) );
+ }
+
+ if ( !(header().device_flags & 0x80) )
+ {
+ if ( !(header().device_flags & 0x84) )
+ core.scc_enabled = core.scc_enabled_true;
+
+ // msx.scc
+ check( !core.msx.scc );
+ CHECK_ALLOC( core.msx.scc = BLARGG_NEW Scc_Apu );
+
+ int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count;
+ static const char* const names [osc_count] = {
+ "Square 1", "Square 2", "Square 3",
+ "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5"
+ };
+ set_voice_names( names );
+
+ static int const types [osc_count] = {
+ wave_type+1, wave_type+3, wave_type+2,
+ wave_type+0, wave_type+4, wave_type+5, wave_type+6, wave_type+7,
+ };
+ set_voice_types( types );
+
+ set_voice_count( osc_count );
+ }
+ }
+
+ set_silence_lookahead( 6 );
+ if ( core.sms.fm || core.msx.music || core.msx.audio )
+ {
+ if ( !Opl_Apu::supported() )
+ set_warning( "FM sound not supported" );
+ else
+ set_silence_lookahead( 3 ); // Opl_Apu is really slow
+ }
+
+ return setup_buffer( ::clock_rate );
+}
+
+void Kss_Emu::update_eq( blip_eq_t const& eq )
+{
+ #define ACTION( apu ) IF_PTR( core.apu )->treble_eq( eq )
+ FOR_EACH_APU( ACTION );
+ #undef ACTION
+}
+
+void Kss_Emu::set_voice( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+ if ( core.sms.psg ) // Sega Master System
+ {
+ i -= core.sms.psg->osc_count;
+ if ( i < 0 )
+ {
+ core.sms.psg->set_output( i + core.sms.psg->osc_count, center, left, right );
+ return;
+ }
+
+ if ( core.sms.fm && i < core.sms.fm->osc_count )
+ core.sms.fm->set_output( i, center, NULL, NULL );
+ }
+ else if ( core.msx.psg ) // MSX
+ {
+ i -= core.msx.psg->osc_count;
+ if ( i < 0 )
+ {
+ core.msx.psg->set_output( i + core.msx.psg->osc_count, center );
+ return;
+ }
+
+ if ( core.msx.scc && i < core.msx.scc->osc_count ) core.msx.scc ->set_output( i, center );
+ if ( core.msx.music && i < core.msx.music->osc_count ) core.msx.music->set_output( i, center, NULL, NULL );
+ if ( core.msx.audio && i < core.msx.audio->osc_count ) core.msx.audio->set_output( i, center, NULL, NULL );
+ }
+}
+
+void Kss_Emu::set_tempo_( double t )
+{
+ int period = (header().device_flags & 0x40 ? ::clock_rate / 50 : ::clock_rate / 60);
+ core.set_play_period( (Kss_Core::time_t) (period / t) );
+}
+
+blargg_err_t Kss_Emu::start_track_( int track )
+{
+ RETURN_ERR( Classic_Emu::start_track_( track ) );
+
+ #define ACTION( apu ) IF_PTR( core.apu )->reset()
+ FOR_EACH_APU( ACTION );
+ #undef ACTION
+
+ core.scc_accessed = false;
+ core.update_gain_();
+
+ return core.start_track( track );
+}
+
+void Kss_Emu::Core::cpu_write_( addr_t addr, int data )
+{
+ // TODO: SCC+ support
+
+ data &= 0xFF;
+ switch ( addr )
+ {
+ case 0x9000:
+ set_bank( 0, data );
+ return;
+
+ case 0xB000:
+ set_bank( 1, data );
+ return;
+
+ case 0xBFFE: // selects between mapping areas (we just always enable both)
+ if ( data == 0 || data == 0x20 )
+ return;
+ }
+
+ int scc_addr = (addr & 0xDFFF) - 0x9800;
+ if ( (unsigned) scc_addr < 0xB0 && msx.scc )
+ {
+ scc_accessed = true;
+ //if ( (unsigned) (scc_addr - 0x90) < 0x10 )
+ // scc_addr -= 0x10; // 0x90-0x9F mirrors to 0x80-0x8F
+ if ( scc_addr < Scc_Apu::reg_count )
+ msx.scc->write( cpu.time(), addr, data );
+ return;
+ }
+
+ dprintf( "LD ($%04X),$%02X\n", addr, data );
+}
+
+void Kss_Emu::Core::cpu_write( addr_t addr, int data )
+{
+ *cpu.write( addr ) = data;
+ if ( (addr & scc_enabled) == 0x8000 )
+ cpu_write_( addr, data );
+}
+
+void Kss_Emu::Core::cpu_out( time_t time, addr_t addr, int data )
+{
+ data &= 0xFF;
+ switch ( addr & 0xFF )
+ {
+ case 0xA0:
+ if ( msx.psg )
+ msx.psg->write_addr( data );
+ return;
+
+ case 0xA1:
+ if ( msx.psg )
+ msx.psg->write_data( time, data );
+ return;
+
+ case 0x06:
+ if ( sms.psg && (header().device_flags & 0x04) )
+ {
+ sms.psg->write_ggstereo( time, data );
+ return;
+ }
+ break;
+
+ case 0x7E:
+ case 0x7F:
+ if ( sms.psg )
+ {
+ sms.psg->write_data( time, data );
+ return;
+ }
+ break;
+
+ #define OPL_WRITE_HANDLER( base, opl )\
+ case base : if ( opl ) { opl->write_addr( data ); return; } break;\
+ case base+1: if ( opl ) { opl->write_data( time, data ); return; } break;
+
+ OPL_WRITE_HANDLER( 0x7C, msx.music )
+ OPL_WRITE_HANDLER( 0xC0, msx.audio )
+ OPL_WRITE_HANDLER( 0xF0, sms.fm )
+
+ case 0xFE:
+ set_bank( 0, data );
+ return;
+
+ #ifndef NDEBUG
+ case 0xA8: // PPI
+ return;
+ #endif
+ }
+
+ Kss_Core::cpu_out( time, addr, data );
+}
+
+int Kss_Emu::Core::cpu_in( time_t time, addr_t addr )
+{
+ switch ( addr & 0xFF )
+ {
+ case 0xC0:
+ case 0xC1:
+ if ( msx.audio )
+ return msx.audio->read( time, addr & 1 );
+ break;
+
+ case 0xA2:
+ if ( msx.psg )
+ return msx.psg->read();
+ break;
+
+ #ifndef NDEBUG
+ case 0xA8: // PPI
+ return 0;
+ #endif
+ }
+
+ return Kss_Core::cpu_in( time, addr );
+}
+
+void Kss_Emu::Core::update_gain()
+{
+ if ( scc_accessed )
+ {
+ dprintf( "SCC accessed\n" );
+ update_gain_();
+ }
+}
+
+blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
+{
+ RETURN_ERR( core.end_frame( duration ) );
+
+ #define ACTION( apu ) IF_PTR( core.apu )->end_frame( duration )
+ FOR_EACH_APU( ACTION );
+ #undef ACTION
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.h
new file mode 100644
index 00000000..78f251b2
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Emu.h
@@ -0,0 +1,77 @@
+// MSX computer KSS music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef KSS_EMU_H
+#define KSS_EMU_H
+
+#include "Classic_Emu.h"
+#include "Kss_Core.h"
+#include "Kss_Scc_Apu.h"
+#include "Sms_Apu.h"
+#include "Ay_Apu.h"
+#include "Opl_Apu.h"
+
+class Kss_Emu : public Classic_Emu {
+public:
+ // KSS file header (see Kss_Core.h)
+ typedef Kss_Core::header_t header_t;
+
+ // Header for currently loaded file
+ header_t const& header() const { return core.header(); }
+
+ static gme_type_t static_type() { return gme_kss_type; }
+
+// Implementation
+public:
+ Kss_Emu();
+ ~Kss_Emu();
+
+protected:
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+ virtual void unload();
+
+private:
+ struct Core;
+ friend struct Core;
+ struct Core : Kss_Core {
+ Kss_Emu& emu;
+
+ // detection of tunes that use SCC so they can be made louder
+ bool scc_accessed;
+
+ enum { scc_enabled_true = 0xC000 };
+ unsigned scc_enabled; // 0 or 0xC000
+ int ay_latch;
+
+ struct {
+ Sms_Apu* psg;
+ Opl_Apu* fm;
+ } sms;
+
+ struct {
+ Ay_Apu* psg;
+ Scc_Apu* scc;
+ Opl_Apu* music;
+ Opl_Apu* audio;
+ } msx;
+
+ Core( Kss_Emu* e ) : emu( *e ) { }
+
+ virtual void cpu_write( addr_t, int );
+ virtual int cpu_in( time_t, addr_t );
+ virtual void cpu_out( time_t, addr_t, int );
+ virtual void update_gain();
+
+ void cpu_write_( addr_t addr, int data );
+ void update_gain_();
+ void unload();
+ } core;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp
new file mode 100644
index 00000000..18625894
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.cpp
@@ -0,0 +1,124 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Kss_Scc_Apu.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// Tones above this frequency are treated as disabled tone at half volume.
+// Power of two is more efficient (avoids division).
+int const inaudible_freq = 16384;
+
+int const wave_size = 0x20;
+
+void Scc_Apu::set_output( Blip_Buffer* buf )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ set_output( i, buf );
+}
+
+void Scc_Apu::volume( double v )
+{
+ synth.volume( 0.43 / osc_count / amp_range * v );
+}
+
+void Scc_Apu::reset()
+{
+ last_time = 0;
+
+ for ( int i = osc_count; --i >= 0; )
+ memset( &oscs [i], 0, offsetof (osc_t,output) );
+
+ memset( regs, 0, sizeof regs );
+}
+
+Scc_Apu::Scc_Apu()
+{
+ set_output( NULL );
+ volume( 1.0 );
+ reset();
+}
+
+void Scc_Apu::run_until( blip_time_t end_time )
+{
+ for ( int index = 0; index < osc_count; index++ )
+ {
+ osc_t& osc = oscs [index];
+
+ Blip_Buffer* const output = osc.output;
+ if ( !output )
+ continue;
+
+ blip_time_t period = (regs [0xA0 + index * 2 + 1] & 0x0F) * 0x100 +
+ regs [0xA0 + index * 2] + 1;
+ int volume = 0;
+ if ( regs [0xAF] & (1 << index) )
+ {
+ blip_time_t inaudible_period = (unsigned) (output->clock_rate() +
+ inaudible_freq * 32) / (unsigned) (inaudible_freq * 16);
+ if ( period > inaudible_period )
+ volume = (regs [0xAA + index] & 0x0F) * (amp_range / 256 / 15);
+ }
+
+ BOOST::int8_t const* wave = (BOOST::int8_t*) regs + index * wave_size;
+ /*if ( index == osc_count - 1 )
+ wave -= wave_size; // last two oscs share same wave RAM*/
+
+ {
+ int delta = wave [osc.phase] * volume - osc.last_amp;
+ if ( delta )
+ {
+ osc.last_amp += delta;
+ output->set_modified();
+ synth.offset( last_time, delta, output );
+ }
+ }
+
+ blip_time_t time = last_time + osc.delay;
+ if ( time < end_time )
+ {
+ int phase = osc.phase;
+ if ( !volume )
+ {
+ // maintain phase
+ int count = (end_time - time + period - 1) / period;
+ phase += count; // will be masked below
+ time += count * period;
+ }
+ else
+ {
+ int last_wave = wave [phase];
+ phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop
+ do
+ {
+ int delta = wave [phase] - last_wave;
+ phase = (phase + 1) & (wave_size - 1);
+ if ( delta )
+ {
+ last_wave += delta;
+ synth.offset_inline( time, delta * volume, output );
+ }
+ time += period;
+ }
+ while ( time < end_time );
+
+ osc.last_amp = last_wave * volume;
+ output->set_modified();
+ phase--; // undo pre-advance
+ }
+ osc.phase = phase & (wave_size - 1);
+ }
+ osc.delay = time - end_time;
+ }
+ last_time = end_time;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.h
new file mode 100644
index 00000000..ee9234a4
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Kss_Scc_Apu.h
@@ -0,0 +1,111 @@
+// Konami SCC sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef KSS_SCC_APU_H
+#define KSS_SCC_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Scc_Apu {
+public:
+// Basics
+
+ // Sets buffer to generate sound into, or 0 to mute.
+ void set_output( Blip_Buffer* );
+
+ // Emulates to time t, then writes data to reg
+ enum { reg_count = 0xB0 }; // 0 <= reg < reg_count
+ void write( blip_time_t t, int reg, int data );
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Resets sound chip
+ void reset();
+
+ // Same as set_output(), but for a particular channel
+ enum { osc_count = 5 };
+ void set_output( int chan, Blip_Buffer* );
+
+ // Set overall volume, where 1.0 is normal
+ void volume( double );
+
+ // Set treble equalization
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+private:
+ // noncopyable
+ Scc_Apu( const Scc_Apu& );
+ Scc_Apu& operator = ( const Scc_Apu& );
+
+
+// Implementation
+public:
+ Scc_Apu();
+ BLARGG_DISABLE_NOTHROW
+
+private:
+ enum { amp_range = 0x8000 };
+ struct osc_t
+ {
+ int delay;
+ int phase;
+ int last_amp;
+ Blip_Buffer* output;
+ };
+ osc_t oscs [osc_count];
+ blip_time_t last_time;
+ unsigned char regs [reg_count];
+ Blip_Synth_Fast synth;
+
+ void run_until( blip_time_t );
+};
+
+inline void Scc_Apu::set_output( int index, Blip_Buffer* b )
+{
+ assert( (unsigned) index < osc_count );
+ oscs [index].output = b;
+}
+
+inline void Scc_Apu::write( blip_time_t time, int addr, int data )
+{
+ //assert( (unsigned) addr < reg_count );
+ assert( ( addr >= 0x9800 && addr <= 0x988F ) || ( addr >= 0xB800 && addr <= 0xB8AF ) );
+ run_until( time );
+
+ addr -= 0x9800;
+ if ( ( unsigned ) addr < 0x90 )
+ {
+ if ( ( unsigned ) addr < 0x60 )
+ regs [addr] = data;
+ else if ( ( unsigned ) addr < 0x80 )
+ {
+ regs [addr] = regs[addr + 0x20] = data;
+ }
+ else if ( ( unsigned ) addr < 0x90 )
+ {
+ regs [addr + 0x20] = data;
+ }
+ }
+ else
+ {
+ addr -= 0xB800 - 0x9800;
+ if ( ( unsigned ) addr < 0xB0 )
+ regs [addr] = data;
+ }
+}
+
+inline void Scc_Apu::end_frame( blip_time_t end_time )
+{
+ if ( end_time > last_time )
+ run_until( end_time );
+
+ last_time -= end_time;
+ assert( last_time >= 0 );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/M3u_Playlist.cpp b/plugins/gme/game-music-emu-0.6pre/gme/M3u_Playlist.cpp
new file mode 100644
index 00000000..1f1a6522
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/M3u_Playlist.cpp
@@ -0,0 +1,476 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "M3u_Playlist.h"
+#include "Music_Emu.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"
+
+// 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 )
+{
+ if ( !err )
+ {
+ require( raw_track_count_ ); // file must be loaded first
+ 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 ( unsigned (*in - 1) <= ' ' - 1 )
+ 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;
+ while ( *in == ':' )
+ {
+ n = -1;
+ in = parse_int_( in + 1, &n );
+ if ( n >= 0 )
+ *out = *out * 60 + n;
+ }
+ *out *= 1000;
+ if ( *in == '.' )
+ {
+ n = -1;
+ in = parse_int_( in + 1, &n );
+ if ( n >= 0 )
+ *out = *out + 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 = entry.length - entry.loop;
+ 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, char *& last_comment_value, bool first )
+{
+ in = skip_white( in + 1 );
+ const char* field = in;
+ if ( *field != '@' )
+ 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 if ( !strcmp( "Game" , field ) ) info.title = text;
+ else if ( !strcmp( "Artist" , field ) ) info.artist = text;
+ else if ( !strcmp( "Copyright", field ) ) info.copyright = text;
+ else
+ text = 0;
+ if ( text )
+ return;
+ *in = ':';
+ }
+ }
+ else if ( *field == '@' )
+ {
+ ++field;
+ in = (char*)field;
+ while ( *in && *in > ' ' )
+ in++;
+ const char* text = skip_white( in );
+ if ( *text )
+ {
+ char saved = *in;
+ *in = 0;
+ if ( !strcmp( "TITLE" , field ) ) info.title = text;
+ else if ( !strcmp( "ARTIST", field ) ) info.artist = text;
+ else if ( !strcmp( "DATE", field ) ) info.date = text;
+ else if ( !strcmp( "COMPOSER", field ) ) info.composer = text;
+ else if ( !strcmp( "SEQUENCER", field ) ) info.sequencer = text;
+ else if ( !strcmp( "ENGINEER", field ) ) info.engineer = text;
+ else if ( !strcmp( "RIPPER", field ) ) info.ripping = text;
+ else if ( !strcmp( "TAGGER", field ) ) info.tagging = text;
+ else
+ text = 0;
+ if ( text )
+ {
+ last_comment_value = (char*)text;
+ return;
+ }
+ *in = saved;
+ }
+ }
+ else if ( last_comment_value )
+ {
+ size_t len = strlen( last_comment_value );
+ last_comment_value[ len ] = ',';
+ last_comment_value[ len + 1 ] = ' ';
+ size_t field_len = strlen( field );
+ memmove( last_comment_value + len + 2, field, field_len );
+ last_comment_value[ len + 2 + field_len ] = 0;
+ return;
+ }
+
+ if ( first )
+ info.title = field;
+}
+
+blargg_err_t M3u_Playlist::parse_()
+{
+ info_.title = "";
+ info_.artist = "";
+ info_.date = "";
+ info_.composer = "";
+ info_.sequencer = "";
+ info_.engineer = "";
+ info_.ripping = "";
+ info_.tagging = "";
+ info_.copyright = "";
+
+ 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();
+ char* last_comment_value = 0;
+ while ( in < data.end() )
+ {
+ // find end of line and terminate it
+ line++;
+ char* begin = in;
+ while ( *in != CR && *in != LF )
+ {
+ if ( !*in )
+ return blargg_err_file_type;
+ 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_, last_comment_value, 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;
+ }
+ else last_comment_value = 0;
+ }
+ if ( count <= 0 )
+ return blargg_err_file_type;
+
+ // Treat first comment as title only if another field is also specified
+ if ( !(info_.artist [0] | info_.composer [0] | info_.date [0] | info_.engineer [0] | info_.ripping [0] | info_.sequencer [0] | info_.tagging [0] | info_.copyright[0]) )
+ info_.title = "";
+
+ return entries.resize( count );
+}
+
+blargg_err_t M3u_Playlist::parse()
+{
+ blargg_err_t err = parse_();
+ if ( err )
+ 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.6pre/gme/M3u_Playlist.h b/plugins/gme/game-music-emu-0.6pre/gme/M3u_Playlist.h
new file mode 100644
index 00000000..5fe62ec3
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/M3u_Playlist.h
@@ -0,0 +1,87 @@
+// M3U playlist file parser, with support for subtrack information
+
+// Game_Music_Emu 0.6-pre
+#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_; }
+
+ // All string pointers point to valid string, or "" if not available
+ struct info_t
+ {
+ const char* title;
+ const char* artist;
+ const char* date;
+ const char* composer;
+ const char* sequencer;
+ const char* engineer;
+ const char* ripping;
+ const char* tagging;
+ const char* copyright;
+ };
+ 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 is "TYPE", otherwise ""
+ const char* name;
+ bool decimal_track; // true if track was specified in decimal
+ // integers are -1 if not present
+ int track;
+ int length; // milliseconds
+ 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<entry_t> entries;
+ blargg_vector<char> data;
+ int first_error_;
+ info_t info_;
+
+ blargg_err_t parse();
+ blargg_err_t parse_();
+ void clear_();
+};
+
+inline void M3u_Playlist::clear_()
+{
+ info_.title = "";
+ info_.artist = "";
+ info_.date = "";
+ info_.composer = "";
+ info_.sequencer = "";
+ info_.engineer = "";
+ info_.ripping = "";
+ info_.tagging = "";
+ info_.copyright = "";
+ entries.clear();
+ data.clear();
+}
+
+inline void M3u_Playlist::clear()
+{
+ first_error_ = 0;
+ clear_();
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.cpp
new file mode 100644
index 00000000..c23b633e
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.cpp
@@ -0,0 +1,290 @@
+// Blip_Buffer 0.4.0. http://www.slack.net/~ant/
+
+#include "Multi_Buffer.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Multi_Buffer::Multi_Buffer( int spf ) : samples_per_frame_( spf )
+{
+ length_ = 0;
+ sample_rate_ = 0;
+ channels_changed_count_ = 1;
+ channel_types_ = NULL;
+ channel_count_ = 0;
+ immediate_removal_ = true;
+}
+
+Multi_Buffer::channel_t Multi_Buffer::channel( int /*index*/ )
+{
+ channel_t ch;
+ ch.center = ch.left = ch.right = NULL;
+ return ch;
+}
+
+// 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 = NULL;
+ chan.center = NULL;
+ chan.right = NULL;
+}
+
+// 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( int rate, int msec )
+{
+ RETURN_ERR( buf.set_sample_rate( rate, msec ) );
+ return Multi_Buffer::set_sample_rate( buf.sample_rate(), buf.length() );
+}
+
+
+// Tracked_Blip_Buffer
+
+int const blip_buffer_extra = 32; // TODO: explain why this value
+
+Tracked_Blip_Buffer::Tracked_Blip_Buffer()
+{
+ last_non_silence = 0;
+}
+
+void Tracked_Blip_Buffer::clear()
+{
+ last_non_silence = 0;
+ Blip_Buffer::clear();
+}
+
+void Tracked_Blip_Buffer::end_frame( blip_time_t t )
+{
+ Blip_Buffer::end_frame( t );
+ if ( modified() )
+ {
+ clear_modified();
+ last_non_silence = samples_avail() + blip_buffer_extra;
+ }
+}
+
+unsigned Tracked_Blip_Buffer::non_silent() const
+{
+ return last_non_silence | unsettled();
+}
+
+inline void Tracked_Blip_Buffer::remove_( int n )
+{
+ if ( (last_non_silence -= n) < 0 )
+ last_non_silence = 0;
+}
+
+void Tracked_Blip_Buffer::remove_silence( int n )
+{
+ remove_( n );
+ Blip_Buffer::remove_silence( n );
+}
+
+void Tracked_Blip_Buffer::remove_samples( int n )
+{
+ remove_( n );
+ Blip_Buffer::remove_samples( n );
+}
+
+void Tracked_Blip_Buffer::remove_all_samples()
+{
+ int avail = samples_avail();
+ if ( !non_silent() )
+ remove_silence( avail );
+ else
+ remove_samples( avail );
+}
+
+int Tracked_Blip_Buffer::read_samples( blip_sample_t out [], int count )
+{
+ count = Blip_Buffer::read_samples( out, count );
+ remove_( count );
+ return count;
+}
+
+// Stereo_Buffer
+
+int const stereo = 2;
+
+Stereo_Buffer::Stereo_Buffer() : Multi_Buffer( 2 )
+{
+ chan.center = mixer.bufs [2] = &bufs [2];
+ chan.left = mixer.bufs [0] = &bufs [0];
+ chan.right = mixer.bufs [1] = &bufs [1];
+ mixer.samples_read = 0;
+}
+
+Stereo_Buffer::~Stereo_Buffer() { }
+
+blargg_err_t Stereo_Buffer::set_sample_rate( int rate, int msec )
+{
+ mixer.samples_read = 0;
+ for ( int i = bufs_size; --i >= 0; )
+ 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( int rate )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clock_rate( rate );
+}
+
+void Stereo_Buffer::bass_freq( int bass )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].bass_freq( bass );
+}
+
+void Stereo_Buffer::clear()
+{
+ mixer.samples_read = 0;
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].clear();
+}
+
+void Stereo_Buffer::end_frame( blip_time_t time )
+{
+ for ( int i = bufs_size; --i >= 0; )
+ bufs [i].end_frame( time );
+}
+
+int Stereo_Buffer::read_samples( blip_sample_t out [], int out_size )
+{
+ require( (out_size & 1) == 0 ); // must read an even number of samples
+ out_size = min( out_size, samples_avail() );
+
+ int pair_count = int (out_size >> 1);
+ if ( pair_count )
+ {
+ mixer.read_pairs( out, pair_count );
+
+ if ( samples_avail() <= 0 || immediate_removal() )
+ {
+ for ( int i = bufs_size; --i >= 0; )
+ {
+ buf_t& b = bufs [i];
+ // TODO: might miss non-silence settling since it checks END of last read
+ if ( !b.non_silent() )
+ b.remove_silence( mixer.samples_read );
+ else
+ b.remove_samples( mixer.samples_read );
+ }
+ mixer.samples_read = 0;
+ }
+ }
+ return out_size;
+}
+
+
+// Stereo_Mixer
+
+// mixers use a single index value to improve performance on register-challenged processors
+// offset goes from negative to zero
+
+void Stereo_Mixer::read_pairs( blip_sample_t out [], int count )
+{
+ // TODO: if caller never marks buffers as modified, uses mono
+ // except that buffer isn't cleared, so caller can encounter
+ // subtle problems and not realize the cause.
+ samples_read += count;
+ if ( bufs [0]->non_silent() | bufs [1]->non_silent() )
+ mix_stereo( out, count );
+ else
+ mix_mono( out, count );
+}
+
+void Stereo_Mixer::mix_mono( blip_sample_t out_ [], int count )
+{
+ int const bass = bufs [2]->highpass_shift();
+ Blip_Buffer::delta_t const* center = bufs [2]->read_pos() + samples_read;
+ int center_sum = bufs [2]->integrator();
+
+ typedef blip_sample_t stereo_blip_sample_t [stereo];
+ stereo_blip_sample_t* BLARGG_RESTRICT out = (stereo_blip_sample_t*) out_ + count;
+ int offset = -count;
+ do
+ {
+ int s = center_sum >> bufs [2]->delta_bits;
+
+ center_sum -= center_sum >> bass;
+ center_sum += center [offset];
+
+ BLIP_CLAMP( s, s );
+
+ out [offset] [0] = (blip_sample_t) s;
+ out [offset] [1] = (blip_sample_t) s;
+ }
+ while ( ++offset );
+
+ bufs [2]->set_integrator( center_sum );
+}
+
+void Stereo_Mixer::mix_stereo( blip_sample_t out_ [], int count )
+{
+ blip_sample_t* BLARGG_RESTRICT out = out_ + count * stereo;
+
+ // do left + center and right + center separately to reduce register load
+ Tracked_Blip_Buffer* const* buf = &bufs [2];
+ while ( true ) // loop runs twice
+ {
+ --buf;
+ --out;
+
+ int const bass = bufs [2]->highpass_shift();
+ Blip_Buffer::delta_t const* side = (*buf)->read_pos() + samples_read;
+ Blip_Buffer::delta_t const* center = bufs [2]->read_pos() + samples_read;
+
+ int side_sum = (*buf)->integrator();
+ int center_sum = bufs [2]->integrator();
+
+ int offset = -count;
+ do
+ {
+ int s = (center_sum + side_sum) >> Blip_Buffer::delta_bits;
+
+ side_sum -= side_sum >> bass;
+ center_sum -= center_sum >> bass;
+
+ side_sum += side [offset];
+ center_sum += center [offset];
+
+ BLIP_CLAMP( s, s );
+
+ ++offset; // before write since out is decremented to slightly before end
+ out [offset * stereo] = (blip_sample_t) s;
+ }
+ while ( offset );
+
+ (*buf)->set_integrator( side_sum );
+
+ if ( buf != bufs )
+ continue;
+
+ // only end center once
+ bufs [2]->set_integrator( center_sum );
+ break;
+ }
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.h b/plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.h
new file mode 100644
index 00000000..57a41ba5
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Multi_Buffer.h
@@ -0,0 +1,219 @@
+// Multi-channel sound buffer interface, and basic mono and stereo buffers
+
+// Blip_Buffer 0.4.0
+#ifndef MULTI_BUFFER_H
+#define MULTI_BUFFER_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+// Interface to one or more Blip_Buffers mapped to one or more channels
+// consisting of left, center, and right buffers.
+class Multi_Buffer {
+public:
+
+ // 1=mono, 2=stereo
+ Multi_Buffer( int samples_per_frame );
+ virtual ~Multi_Buffer() { }
+
+ // Sets the number of channels available and optionally their types
+ // (type information used by Effects_Buffer)
+ enum { type_index_mask = 0xFF };
+ enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
+ virtual blargg_err_t set_channel_count( int, int const types [] = NULL );
+ int channel_count() const { return channel_count_; }
+
+ // Gets indexed channel, from 0 to channel_count()-1
+ struct channel_t {
+ Blip_Buffer* center;
+ Blip_Buffer* left;
+ Blip_Buffer* right;
+ };
+ virtual channel_t channel( int index ) BLARGG_PURE( ; )
+
+ // Number of samples per output frame (1 = mono, 2 = stereo)
+ int samples_per_frame() const;
+
+ // Count of changes to channel configuration. Incremented whenever
+ // a change is made to any of the Blip_Buffers for any channel.
+ unsigned channels_changed_count() { return channels_changed_count_; }
+
+ // See Blip_Buffer.h
+ virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length ) BLARGG_PURE( ; )
+ int sample_rate() const;
+ int length() const;
+ virtual void clock_rate( int ) BLARGG_PURE( ; )
+ virtual void bass_freq( int ) BLARGG_PURE( ; )
+ virtual void clear() BLARGG_PURE( ; )
+ virtual void end_frame( blip_time_t ) BLARGG_PURE( ; )
+ virtual int read_samples( blip_sample_t [], int ) BLARGG_PURE( ; )
+ virtual int samples_avail() const BLARGG_PURE( ; )
+
+private:
+ // noncopyable
+ Multi_Buffer( const Multi_Buffer& );
+ Multi_Buffer& operator = ( const Multi_Buffer& );
+
+// Implementation
+public:
+ BLARGG_DISABLE_NOTHROW
+ void disable_immediate_removal() { immediate_removal_ = false; }
+
+protected:
+ bool immediate_removal() const { return immediate_removal_; }
+ int const* channel_types() const { return channel_types_; }
+ void channels_changed() { channels_changed_count_++; }
+
+private:
+ unsigned channels_changed_count_;
+ int sample_rate_;
+ int length_;
+ int channel_count_;
+ int const samples_per_frame_;
+ int const* channel_types_;
+ bool immediate_removal_;
+};
+
+
+// Uses a single buffer and outputs mono samples.
+class Mono_Buffer : public Multi_Buffer {
+public:
+ // Buffer used for all channels
+ Blip_Buffer* center() { return &buf; }
+
+// Implementation
+public:
+ Mono_Buffer();
+ ~Mono_Buffer();
+ virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length );
+ virtual void clock_rate( int rate ) { buf.clock_rate( rate ); }
+ virtual void bass_freq( int freq ) { buf.bass_freq( freq ); }
+ virtual void clear() { buf.clear(); }
+ virtual int samples_avail() const { return buf.samples_avail(); }
+ virtual int read_samples( blip_sample_t p [], int s ) { return buf.read_samples( p, s ); }
+ virtual channel_t channel( int ) { return chan; }
+ virtual void end_frame( blip_time_t t ) { buf.end_frame( t ); }
+
+private:
+ Blip_Buffer buf;
+ channel_t chan;
+};
+
+ class Tracked_Blip_Buffer : public Blip_Buffer {
+ public:
+ // Non-zero if buffer still has non-silent samples in it. Requires that you call
+ // set_modified() appropriately.
+ unsigned non_silent() const;
+
+ // remove_samples( samples_avail() )
+ void remove_all_samples();
+
+ // Implementation
+ public:
+ BLARGG_DISABLE_NOTHROW
+ int read_samples( blip_sample_t [], int );
+ void remove_silence( int );
+ void remove_samples( int );
+ Tracked_Blip_Buffer();
+ void clear();
+ void end_frame( blip_time_t );
+
+ private:
+ int last_non_silence;
+
+ delta_t unsettled() const { return integrator() >> delta_bits; }
+ void remove_( int );
+ };
+
+ class Stereo_Mixer {
+ public:
+ Tracked_Blip_Buffer* bufs [3];
+ int samples_read;
+
+ Stereo_Mixer() : samples_read( 0 ) { }
+ void read_pairs( blip_sample_t out [], int count );
+
+ private:
+ void mix_mono ( blip_sample_t out [], int pair_count );
+ void mix_stereo( blip_sample_t out [], int pair_count );
+ };
+
+
+// Uses three buffers (one for center) and outputs stereo sample pairs.
+class Stereo_Buffer : public Multi_Buffer {
+public:
+
+ // Buffers used for all channels
+ Blip_Buffer* center() { return &bufs [2]; }
+ Blip_Buffer* left() { return &bufs [0]; }
+ Blip_Buffer* right() { return &bufs [1]; }
+
+// Implementation
+public:
+ Stereo_Buffer();
+ ~Stereo_Buffer();
+ virtual blargg_err_t set_sample_rate( int, int msec = blip_default_length );
+ virtual void clock_rate( int );
+ virtual void bass_freq( int );
+ virtual void clear();
+ virtual channel_t channel( int ) { return chan; }
+ virtual void end_frame( blip_time_t );
+ virtual int samples_avail() const { return (bufs [0].samples_avail() - mixer.samples_read) * 2; }
+ virtual int read_samples( blip_sample_t [], int );
+
+private:
+ enum { bufs_size = 3 };
+ typedef Tracked_Blip_Buffer buf_t;
+ buf_t bufs [bufs_size];
+ Stereo_Mixer mixer;
+ channel_t chan;
+ int samples_avail_;
+};
+
+
+// Silent_Buffer generates no samples, useful where no sound is wanted
+class Silent_Buffer : public Multi_Buffer {
+ channel_t chan;
+public:
+ Silent_Buffer();
+ virtual blargg_err_t set_sample_rate( int rate, int msec = blip_default_length );
+ virtual void clock_rate( int ) { }
+ virtual void bass_freq( int ) { }
+ virtual void clear() { }
+ virtual channel_t channel( int ) { return chan; }
+ virtual void end_frame( blip_time_t ) { }
+ virtual int samples_avail() const { return 0; }
+ virtual int read_samples( blip_sample_t [], int ) { return 0; }
+};
+
+
+inline blargg_err_t Multi_Buffer::set_sample_rate( int rate, int msec )
+{
+ sample_rate_ = rate;
+ length_ = msec;
+ return blargg_ok;
+}
+
+inline int Multi_Buffer::samples_per_frame() const { return samples_per_frame_; }
+inline int Multi_Buffer::sample_rate() const { return sample_rate_; }
+inline int Multi_Buffer::length() const { return length_; }
+inline void Multi_Buffer::clock_rate( int ) { }
+inline void Multi_Buffer::bass_freq( int ) { }
+inline void Multi_Buffer::clear() { }
+inline void Multi_Buffer::end_frame( blip_time_t ) { }
+inline int Multi_Buffer::read_samples( blip_sample_t [], int ) { return 0; }
+inline int Multi_Buffer::samples_avail() const { return 0; }
+
+inline blargg_err_t Multi_Buffer::set_channel_count( int n, int const types [] )
+{
+ channel_count_ = n;
+ channel_types_ = types;
+ return blargg_ok;
+}
+
+inline blargg_err_t Silent_Buffer::set_sample_rate( int rate, int msec )
+{
+ return Multi_Buffer::set_sample_rate( rate, msec );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.cpp
new file mode 100644
index 00000000..ed760afd
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.cpp
@@ -0,0 +1,235 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Music_Emu.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const stereo = 2; // number of channels for stereo
+
+Music_Emu::equalizer_t const Music_Emu::tv_eq = { -8.0, 180 };
+
+void Music_Emu::clear_track_vars()
+{
+ current_track_ = -1;
+ warning(); // clear warning
+ track_filter.stop();
+}
+
+void Music_Emu::unload()
+{
+ voice_count_ = 0;
+ clear_track_vars();
+ Gme_File::unload();
+}
+
+Music_Emu::gme_t()
+{
+ effects_buffer_ = NULL;
+ sample_rate_ = 0;
+ mute_mask_ = 0;
+ tempo_ = 1.0;
+ gain_ = 1.0;
+
+ // defaults
+ tfilter = track_filter.setup();
+ set_max_initial_silence( 15 );
+ set_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(); // clears fields
+}
+
+Music_Emu::~gme_t()
+{
+ assert( !effects_buffer_ );
+}
+
+blargg_err_t Music_Emu::set_sample_rate( int rate )
+{
+ require( !sample_rate() ); // sample rate can't be changed once set
+ RETURN_ERR( set_sample_rate_( rate ) );
+ RETURN_ERR( track_filter.init( this ) );
+ sample_rate_ = rate;
+ tfilter.max_silence = 6 * stereo * sample_rate();
+ return blargg_ok;
+}
+
+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 )
+{
+ // TODO: why is GCC generating memcpy call here?
+ // Without the 'if', valgrind flags it.
+ if ( &eq != &equalizer_ )
+ 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 );
+}
+
+const char* Music_Emu::voice_name( int i ) const
+{
+ if ( (unsigned) i < (unsigned) voice_count_ )
+ return voice_names_ [i];
+
+ //check( false ); // TODO: enable?
+ return "";
+}
+
+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 );
+}
+
+blargg_err_t Music_Emu::post_load()
+{
+ set_tempo( tempo_ );
+ remute_voices();
+ return Gme_File::post_load();
+}
+
+// Tell/Seek
+
+int Music_Emu::msec_to_samples( int msec ) const
+{
+ int sec = msec / 1000;
+ msec -= sec * 1000;
+ return (sec * sample_rate() + msec * sample_rate() / 1000) * stereo;
+}
+
+int Music_Emu::tell() const
+{
+ int rate = sample_rate() * stereo;
+ int sec = track_filter.sample_count() / rate;
+ return sec * 1000 + (track_filter.sample_count() - sec * rate) * 1000 / rate;
+}
+
+blargg_err_t Music_Emu::seek( int msec )
+{
+ int time = msec_to_samples( msec );
+ if ( time < track_filter.sample_count() )
+ RETURN_ERR( start_track( current_track_ ) );
+ return skip( time - track_filter.sample_count() );
+}
+
+blargg_err_t Music_Emu::skip( int count )
+{
+ require( current_track() >= 0 ); // start_track() must have been called already
+ return track_filter.skip( count );
+}
+
+blargg_err_t Music_Emu::skip_( int count )
+{
+ // for long skip, mute sound
+ const int threshold = 32768;
+ if ( count > threshold )
+ {
+ int saved_mute = mute_mask_;
+ mute_voices( ~0 );
+
+ int n = count - threshold/2;
+ n &= ~(2048-1); // round to multiple of 2048
+ count -= n;
+ RETURN_ERR( track_filter.skip_( n ) );
+
+ mute_voices( saved_mute );
+ }
+
+ return track_filter.skip_( count );
+}
+
+// Playback
+
+blargg_err_t Music_Emu::start_track( int track )
+{
+ clear_track_vars();
+
+ int remapped = track;
+ RETURN_ERR( remap_track_( &remapped ) );
+ current_track_ = track;
+ blargg_err_t err = start_track_( remapped );
+ if ( err )
+ {
+ current_track_ = -1;
+ return err;
+ }
+
+ // convert filter times to samples
+ Track_Filter::setup_t s = tfilter;
+ s.max_initial *= sample_rate() * stereo;
+ #if GME_DISABLE_SILENCE_LOOKAHEAD
+ s.lookahead = 1;
+ #endif
+ track_filter.setup( s );
+
+ return track_filter.start_track();
+}
+
+void Music_Emu::set_fade( int start_msec, int length_msec )
+{
+ track_filter.set_fade( msec_to_samples( start_msec ),
+ length_msec * sample_rate() / (1000 / stereo) );
+}
+
+blargg_err_t Music_Emu::play( int out_count, sample_t out [] )
+{
+ require( current_track() >= 0 );
+ require( out_count % stereo == 0 );
+
+ return track_filter.play( out_count, out );
+}
+
+// Gme_Info_
+
+blargg_err_t Gme_Info_::set_sample_rate_( int ) { return blargg_ok; }
+void Gme_Info_::pre_load() { Gme_File::pre_load(); } // skip Music_Emu
+blargg_err_t Gme_Info_::post_load() { return 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 BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); }
+blargg_err_t Gme_Info_::play_( int, sample_t [] ) { return BLARGG_ERR( BLARGG_ERR_CALLER, "can't play file opened for info only" ); }
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.h
new file mode 100644
index 00000000..ccdc2288
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Music_Emu.h
@@ -0,0 +1,241 @@
+// Common interface to game music file emulators
+
+// Game_Music_Emu 0.6-pre
+#ifndef MUSIC_EMU_H
+#define MUSIC_EMU_H
+
+#include "Gme_File.h"
+#include "Track_Filter.h"
+class Multi_Buffer;
+
+struct gme_t : public Gme_File, private Track_Filter::callbacks_t {
+public:
+ // Sets output sample rate. Must be called only once before loading file.
+ blargg_err_t set_sample_rate( int sample_rate );
+
+ // Sample rate sound is generated at
+ int sample_rate() const;
+
+// File loading
+
+ // See Gme_Loader.h
+
+// Basic playback
+
+ // Starts a track, where 0 is the first track. Also clears warning string.
+ blargg_err_t start_track( int );
+
+ // Generates '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( int count, sample_t* buf );
+
+// Track information
+
+ // See Gme_File.h
+
+ // Index of current track or -1 if one hasn't been started
+ int current_track() const;
+
+ // Info for currently playing track
+ using Gme_File::track_info;
+ blargg_err_t track_info( track_info_t* out ) const;
+
+// Track status/control
+
+ // Number of milliseconds played since beginning of track (1000 per second)
+ int tell() const;
+
+ // Seeks to new time in track. Seeking backwards or far forward can take a while.
+ blargg_err_t seek( int msec );
+
+ // Skips n samples
+ blargg_err_t skip( int n );
+
+ // True if a track has reached its end
+ bool track_ended() const;
+
+ // Sets start time and length of track fade out. Once fade ends track_ended() returns
+ // true. Fade time must be set after track has been started, and can be changed
+ // at any time.
+ void set_fade( int start_msec, int length_msec = 8000 );
+
+ // Disables automatic end-of-track detection and skipping of silence at beginning
+ void ignore_silence( bool disable = true );
+
+// Voices
+
+ // Number of voices used by currently loaded file
+ int voice_count() const;
+
+ // Name of voice i, from 0 to voice_count()-1
+ const char* voice_name( int i ) const;
+
+ // Mutes/unmutes voice i, where voice 0 is first voice
+ void mute_voice( int index, bool mute = true );
+
+ // Sets 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 );
+
+// Sound customization
+
+ // Adjusts 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 );
+
+ // Changes overall output amplitude, where 1.0 results in minimal clamping.
+ // Must be called before set_sample_rate().
+ void set_gain( double );
+
+ // Requests 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( class 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;
+
+ // Sets frequency equalizer parameters
+ void set_equalizer( equalizer_t const& );
+
+ // Equalizer preset for a TV speaker
+ static equalizer_t const tv_eq;
+
+// Derived interface
+protected:
+ // Cause any further generated samples to be silence, instead of calling play_()
+ void set_track_ended() { track_filter.set_track_ended(); }
+
+ // If more than secs of silence are encountered, track is ended
+ void set_max_initial_silence( int secs ) { tfilter.max_initial = secs; }
+
+ // Sets rate emulator is run at when scanning ahead for silence. 1=100%, 2=200% etc.
+ void set_silence_lookahead( int rate ) { tfilter.lookahead = rate; }
+
+ // Sets number of voices
+ void set_voice_count( int n ) { voice_count_ = n; }
+
+ // Sets names of voices
+ void set_voice_names( const char* const names [] );
+
+ // Current gain
+ double gain() const { return gain_; }
+
+ // Current tempo
+ double tempo() const { return tempo_; }
+
+ // Re-applies muting mask using mute_voices_()
+ void remute_voices();
+
+// Overrides should do the indicated task
+
+ // Set sample rate as close as possible to sample_rate, then call
+ // Music_Emu::set_sample_rate_() with the actual rate used.
+ virtual blargg_err_t set_sample_rate_( int sample_rate ) BLARGG_PURE( ; )
+
+ // Set equalizer parameters
+ virtual void set_equalizer_( equalizer_t const& ) { }
+
+ // Mute voices based on mask
+ virtual void mute_voices_( int mask ) BLARGG_PURE( ; )
+
+ // Set tempo to t, which is constrained to the range 0.02 to 4.0.
+ virtual void set_tempo_( double t ) BLARGG_PURE( ; )
+
+ // Start track t, where 0 is the first track
+ virtual blargg_err_t start_track_( int t ) BLARGG_PURE( ; ) // tempo is set before this
+
+ // Generate count samples into *out. Count will always be even.
+ virtual blargg_err_t play_( int count, sample_t out [] ) BLARGG_PURE( ; )
+
+ // Skip count samples. Count will always be even.
+ virtual blargg_err_t skip_( int count );
+
+
+// Implementation
+public:
+ gme_t();
+ ~gme_t();
+ BLARGG_DEPRECATED( const char** voice_names() const { return CONST_CAST(const char**,voice_names_); } )
+
+protected:
+ virtual void unload();
+ virtual void pre_load();
+ virtual blargg_err_t post_load();
+
+private:
+ Track_Filter::setup_t tfilter;
+ Track_Filter track_filter;
+ equalizer_t equalizer_;
+ const char* const* voice_names_;
+ int voice_count_;
+ int mute_mask_;
+ double tempo_;
+ double gain_;
+ int sample_rate_;
+ int current_track_;
+
+ void clear_track_vars();
+ int msec_to_samples( int msec ) const;
+
+ friend Music_Emu* gme_new_emu( gme_type_t, int );
+ friend void gme_effects( Music_Emu const*, gme_effects_t* );
+ friend void gme_set_effects( Music_Emu*, gme_effects_t const* );
+ friend void gme_set_stereo_depth( Music_Emu*, double );
+ friend const char** gme_voice_names ( Music_Emu const* );
+
+protected:
+ Multi_Buffer* effects_buffer_;
+};
+
+// base class for info-only derivations
+struct Gme_Info_ : Music_Emu
+{
+ virtual blargg_err_t set_sample_rate_( int 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_( int count, sample_t out [] );
+ virtual void pre_load();
+ virtual blargg_err_t post_load();
+};
+
+inline blargg_err_t Music_Emu::track_info( track_info_t* out ) const
+{
+ return track_info( out, current_track_ );
+}
+
+inline int Music_Emu::sample_rate() const { return sample_rate_; }
+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_filter.track_ended(); }
+inline const Music_Emu::equalizer_t& Music_Emu::equalizer() const { return equalizer_; }
+
+inline void Music_Emu::ignore_silence( bool b ) { track_filter.ignore_silence( b ); }
+inline void Music_Emu::set_tempo_( double t ) { tempo_ = t; }
+inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); }
+
+inline void Music_Emu::set_voice_names( const char* const p [] ) { voice_names_ = p; }
+
+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;
+}
+
+inline blargg_err_t Music_Emu::start_track_( int ) { return blargg_ok; }
+
+inline blargg_err_t Music_Emu::set_sample_rate_( int ) { return blargg_ok; }
+
+inline blargg_err_t Music_Emu::play_( int, sample_t [] ) { return blargg_ok; }
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.cpp
new file mode 100644
index 00000000..675007a6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.cpp
@@ -0,0 +1,392 @@
+// Nes_Snd_Emu 0.2.0-pre. http://www.slack.net/~ant/
+
+#include "Nes_Apu.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const amp_range = 15;
+
+Nes_Apu::Nes_Apu() :
+ square1( &square_synth ),
+ square2( &square_synth )
+{
+ tempo_ = 1.0;
+ dmc.apu = this;
+
+ oscs [0] = &square1;
+ oscs [1] = &square2;
+ oscs [2] = &triangle;
+ oscs [3] = &noise;
+ oscs [4] = &dmc;
+
+ set_output( NULL );
+ dmc.nonlinear = false;
+ volume( 1.0 );
+ reset( false );
+}
+
+void Nes_Apu::treble_eq( const blip_eq_t& eq )
+{
+ square_synth .treble_eq( eq );
+ triangle.synth.treble_eq( eq );
+ noise .synth.treble_eq( eq );
+ dmc .synth.treble_eq( eq );
+}
+
+void Nes_Apu::enable_nonlinear_( double sq, double tnd )
+{
+ dmc.nonlinear = true;
+ square_synth.volume( sq );
+
+ triangle.synth.volume( tnd * 2.752 );
+ noise .synth.volume( tnd * 1.849 );
+ dmc .synth.volume( tnd );
+
+ square1 .last_amp = 0;
+ square2 .last_amp = 0;
+ triangle.last_amp = 0;
+ noise .last_amp = 0;
+ dmc .last_amp = 0;
+}
+
+void Nes_Apu::volume( double v )
+{
+ if ( !dmc.nonlinear )
+ {
+ v *= 1.0 / 1.11; // TODO: merge into values below
+ square_synth .volume( 0.125 / amp_range * v ); // was 0.1128 1.108
+ triangle.synth.volume( 0.150 / amp_range * v ); // was 0.12765 1.175
+ noise .synth.volume( 0.095 / amp_range * v ); // was 0.0741 1.282
+ dmc .synth.volume( 0.450 / 2048 * v ); // was 0.42545 1.058
+ }
+}
+
+void Nes_Apu::set_output( Blip_Buffer* buffer )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ set_output( i, buffer );
+}
+
+void Nes_Apu::set_tempo( double t )
+{
+ tempo_ = t;
+ frame_period = (dmc.pal_mode ? 8314 : 7458);
+ if ( t != 1.0 )
+ frame_period = (int) (frame_period / t) & ~1; // must be even
+}
+
+void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac )
+{
+ dmc.pal_mode = pal_mode;
+ set_tempo( tempo_ );
+
+ square1.reset();
+ square2.reset();
+ triangle.reset();
+ noise.reset();
+ dmc.reset();
+
+ last_time = 0;
+ last_dmc_time = 0;
+ osc_enables = 0;
+ irq_flag = false;
+ earliest_irq_ = no_irq;
+ frame_delay = 1;
+ write_register( 0, 0x4017, 0x00 );
+ write_register( 0, 0x4015, 0x00 );
+
+ for ( int addr = io_addr; addr <= 0x4013; addr++ )
+ write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
+
+ dmc.dac = initial_dmc_dac;
+ if ( !dmc.nonlinear )
+ triangle.last_amp = 15;
+ if ( !dmc.nonlinear ) // TODO: remove?
+ dmc.last_amp = initial_dmc_dac; // prevent output transition
+}
+
+void Nes_Apu::irq_changed()
+{
+ blip_time_t new_irq = dmc.next_irq;
+ if ( dmc.irq_flag | irq_flag ) {
+ new_irq = 0;
+ }
+ else if ( new_irq > next_irq ) {
+ new_irq = next_irq;
+ }
+
+ if ( new_irq != earliest_irq_ ) {
+ earliest_irq_ = new_irq;
+ if ( irq_notifier.f )
+ irq_notifier.f( irq_notifier.data );
+ }
+}
+
+// frames
+
+void Nes_Apu::run_until( blip_time_t end_time )
+{
+ require( end_time >= last_dmc_time );
+ if ( end_time > next_dmc_read_time() )
+ {
+ blip_time_t start = last_dmc_time;
+ last_dmc_time = end_time;
+ dmc.run( start, end_time );
+ }
+}
+
+void Nes_Apu::run_until_( blip_time_t end_time )
+{
+ require( end_time >= last_time );
+
+ if ( end_time == last_time )
+ return;
+
+ if ( last_dmc_time < end_time )
+ {
+ blip_time_t start = last_dmc_time;
+ last_dmc_time = end_time;
+ dmc.run( start, end_time );
+ }
+
+ while ( true )
+ {
+ // earlier of next frame time or end time
+ blip_time_t time = last_time + frame_delay;
+ if ( time > end_time )
+ time = end_time;
+ frame_delay -= time - last_time;
+
+ // run oscs to present
+ square1.run( last_time, time );
+ square2.run( last_time, time );
+ triangle.run( last_time, time );
+ noise.run( last_time, time );
+ last_time = time;
+
+ if ( time == end_time )
+ break; // no more frames to run
+
+ // take frame-specific actions
+ frame_delay = frame_period;
+ switch ( frame++ )
+ {
+ case 0:
+ if ( !(frame_mode & 0xC0) ) {
+ next_irq = time + frame_period * 4 + 2;
+ irq_flag = true;
+ }
+ // fall through
+ case 2:
+ // clock length and sweep on frames 0 and 2
+ square1.clock_length( 0x20 );
+ square2.clock_length( 0x20 );
+ noise.clock_length( 0x20 );
+ triangle.clock_length( 0x80 ); // different bit for halt flag on triangle
+
+ square1.clock_sweep( -1 );
+ square2.clock_sweep( 0 );
+
+ // frame 2 is slightly shorter in mode 1
+ if ( dmc.pal_mode && frame == 3 )
+ frame_delay -= 2;
+ break;
+
+ case 1:
+ // frame 1 is slightly shorter in mode 0
+ if ( !dmc.pal_mode )
+ frame_delay -= 2;
+ break;
+
+ case 3:
+ frame = 0;
+
+ // frame 3 is almost twice as long in mode 1
+ if ( frame_mode & 0x80 )
+ frame_delay += frame_period - (dmc.pal_mode ? 2 : 6);
+ break;
+ }
+
+ // clock envelopes and linear counter every frame
+ triangle.clock_linear_counter();
+ square1.clock_envelope();
+ square2.clock_envelope();
+ noise.clock_envelope();
+ }
+}
+
+template<class T>
+inline void zero_apu_osc( T* osc, blip_time_t time )
+{
+ Blip_Buffer* output = osc->output;
+ int last_amp = osc->last_amp;
+ osc->last_amp = 0;
+ if ( output && last_amp )
+ osc->synth.offset( time, -last_amp, output );
+}
+
+void Nes_Apu::end_frame( blip_time_t end_time )
+{
+ if ( end_time > last_time )
+ run_until_( end_time );
+
+ if ( dmc.nonlinear )
+ {
+ zero_apu_osc( &square1, last_time );
+ zero_apu_osc( &square2, last_time );
+ zero_apu_osc( &triangle, last_time );
+ zero_apu_osc( &noise, last_time );
+ zero_apu_osc( &dmc, last_time );
+ }
+
+ // make times relative to new frame
+ last_time -= end_time;
+ require( last_time >= 0 );
+
+ last_dmc_time -= end_time;
+ require( last_dmc_time >= 0 );
+
+ if ( next_irq != no_irq ) {
+ next_irq -= end_time;
+ check( next_irq >= 0 );
+ }
+ if ( dmc.next_irq != no_irq ) {
+ dmc.next_irq -= end_time;
+ check( dmc.next_irq >= 0 );
+ }
+ if ( earliest_irq_ != no_irq ) {
+ earliest_irq_ -= end_time;
+ if ( earliest_irq_ < 0 )
+ earliest_irq_ = 0;
+ }
+}
+
+// registers
+
+static const unsigned char length_table [0x20] = {
+ 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
+ 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
+ 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
+ 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
+};
+
+void Nes_Apu::write_register( blip_time_t time, int addr, int data )
+{
+ require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
+ require( (unsigned) data <= 0xFF );
+
+ // Ignore addresses outside range
+ if ( unsigned (addr - io_addr) >= io_size )
+ return;
+
+ run_until_( time );
+
+ if ( addr < 0x4014 )
+ {
+ // Write to channel
+ int osc_index = (addr - io_addr) >> 2;
+ Nes_Osc* osc = oscs [osc_index];
+
+ int reg = addr & 3;
+ osc->regs [reg] = data;
+ osc->reg_written [reg] = true;
+
+ if ( osc_index == 4 )
+ {
+ // handle DMC specially
+ dmc.write_register( reg, data );
+ }
+ else if ( reg == 3 )
+ {
+ // load length counter
+ if ( (osc_enables >> osc_index) & 1 )
+ osc->length_counter = length_table [(data >> 3) & 0x1F];
+
+ // reset square phase
+ if ( osc_index < 2 )
+ ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1;
+ }
+ }
+ else if ( addr == 0x4015 )
+ {
+ // Channel enables
+ for ( int i = osc_count; i--; )
+ if ( !((data >> i) & 1) )
+ oscs [i]->length_counter = 0;
+
+ bool recalc_irq = dmc.irq_flag;
+ dmc.irq_flag = false;
+
+ int old_enables = osc_enables;
+ osc_enables = data;
+ if ( !(data & 0x10) ) {
+ dmc.next_irq = no_irq;
+ recalc_irq = true;
+ }
+ else if ( !(old_enables & 0x10) ) {
+ dmc.start(); // dmc just enabled
+ }
+
+ if ( recalc_irq )
+ irq_changed();
+ }
+ else if ( addr == 0x4017 )
+ {
+ // Frame mode
+ frame_mode = data;
+
+ bool irq_enabled = !(data & 0x40);
+ irq_flag &= irq_enabled;
+ next_irq = no_irq;
+
+ // mode 1
+ frame_delay = (frame_delay & 1);
+ frame = 0;
+
+ if ( !(data & 0x80) )
+ {
+ // mode 0
+ frame = 1;
+ frame_delay += frame_period;
+ if ( irq_enabled )
+ next_irq = time + frame_delay + frame_period * 3 + 1;
+ }
+
+ irq_changed();
+ }
+}
+
+int Nes_Apu::read_status( blip_time_t time )
+{
+ run_until_( time - 1 );
+
+ int result = (dmc.irq_flag << 7) | (irq_flag << 6);
+
+ for ( int i = 0; i < osc_count; i++ )
+ if ( oscs [i]->length_counter )
+ result |= 1 << i;
+
+ run_until_( time );
+
+ if ( irq_flag )
+ {
+ result |= 0x40;
+ irq_flag = false;
+ irq_changed();
+ }
+
+ //dprintf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result );
+
+ return result;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.h
new file mode 100644
index 00000000..1004338a
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Apu.h
@@ -0,0 +1,181 @@
+// NES 2A03 APU sound chip emulator
+
+// Nes_Snd_Emu 0.2.0-pre
+#ifndef NES_APU_H
+#define NES_APU_H
+
+#include "blargg_common.h"
+#include "Nes_Oscs.h"
+
+struct apu_state_t;
+class Nes_Buffer;
+
+class Nes_Apu {
+public:
+// Basics
+
+ typedef int nes_time_t; // NES CPU clock cycle count
+
+ // Sets memory reader callback used by DMC oscillator to fetch samples.
+ // When callback is invoked, 'user_data' is passed unchanged as the
+ // first parameter.
+ //void dmc_reader( int (*callback)( void* user_data, int addr ), void* user_data = NULL );
+
+ // Sets buffer to generate sound into, or 0 to mute output (reduces
+ // emulation accuracy).
+ void set_output( Blip_Buffer* );
+
+ // All time values are the number of CPU clock cycles relative to the
+ // beginning of the current time frame. Before resetting the CPU clock
+ // count, call end_frame( last_cpu_time ).
+
+ // Writes to register (0x4000-0x4013, and 0x4015 and 0x4017)
+ enum { io_addr = 0x4000 };
+ enum { io_size = 0x18 };
+ void write_register( nes_time_t, int addr, int data );
+
+ // Reads from status register (0x4015)
+ enum { status_addr = 0x4015 };
+ int read_status( nes_time_t );
+
+ // Runs all oscillators up to specified time, ends current time frame, then
+ // starts a new time frame at time 0. Time frames have no effect on emulation
+ // and each can be whatever length is convenient.
+ void end_frame( nes_time_t );
+
+// Optional
+
+ // Resets internal frame counter, registers, and all oscillators.
+ // Uses PAL timing if pal_timing is true, otherwise use NTSC timing.
+ // Sets the DMC oscillator's initial DAC value to initial_dmc_dac without
+ // any audible click.
+ void reset( bool pal_mode = false, int initial_dmc_dac = 0 );
+
+ // Same as set_output(), but for a particular channel
+ // 0: Square 1, 1: Square 2, 2: Triangle, 3: Noise, 4: DMC
+ enum { osc_count = 5 };
+ void set_output( int chan, Blip_Buffer* buf );
+
+ // Adjusts frame period
+ void set_tempo( double );
+
+ // Saves/loads exact emulation state
+ void save_state( apu_state_t* out ) const;
+ void load_state( apu_state_t const& );
+
+ // Sets overall volume (default is 1.0)
+ void volume( double );
+
+ // Sets treble equalization (see notes.txt)
+ void treble_eq( const blip_eq_t& );
+
+ // Sets IRQ time callback that is invoked when the time of earliest IRQ
+ // may have changed, or NULL to disable. When callback is invoked,
+ // 'user_data' is passed unchanged as the first parameter.
+ //void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL );
+
+ // Gets time that APU-generated IRQ will occur if no further register reads
+ // or writes occur. If IRQ is already pending, returns irq_waiting. If no
+ // IRQ will occur, returns no_irq.
+ enum { no_irq = INT_MAX/2 + 1 };
+ enum { irq_waiting = 0 };
+ nes_time_t earliest_irq( nes_time_t ) const;
+
+ // Counts number of DMC reads that would occur if 'run_until( t )' were executed.
+ // If last_read is not NULL, set *last_read to the earliest time that
+ // 'count_dmc_reads( time )' would result in the same result.
+ int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const;
+
+ // Time when next DMC memory read will occur
+ nes_time_t next_dmc_read_time() const;
+
+ // Runs DMC until specified time, so that any DMC memory reads can be
+ // accounted for (i.e. inserting CPU wait states).
+ void run_until( nes_time_t );
+
+
+// Implementation
+public:
+ Nes_Apu();
+ BLARGG_DISABLE_NOTHROW
+ // Use set_output() in place of these
+ BLARGG_DEPRECATED( void output ( Blip_Buffer* c ); )
+ BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ); )
+
+ BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4000 }; )
+ BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4017 }; )
+
+ blargg_callback<int (*)( void* user_data, int addr )> dmc_reader;
+ blargg_callback<void (*)( void* user_data )> irq_notifier;
+
+ void enable_nonlinear_( double sq, double tnd );
+ static float tnd_total_() { return 196.015f; }
+
+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;
+ Nes_Square::Synth square_synth; // shared by squares
+
+ void irq_changed();
+ void state_restored();
+ void run_until_( nes_time_t );
+
+ // TODO: remove
+ friend class Nes_Core;
+};
+
+inline void Nes_Apu::set_output( int osc, Blip_Buffer* buf )
+{
+ assert( (unsigned) osc < osc_count );
+ oscs [osc]->output = buf;
+}
+
+inline Nes_Apu::nes_time_t Nes_Apu::earliest_irq( nes_time_t ) const
+{
+ return earliest_irq_;
+}
+
+inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) const
+{
+ return dmc.count_reads( time, last_read );
+}
+
+inline Nes_Apu::nes_time_t Nes_Dmc::next_read_time() const
+{
+ if ( length_counter == 0 )
+ return Nes_Apu::no_irq; // not reading
+
+ return apu->last_dmc_time + delay + (bits_remain - 1) * period;
+}
+
+inline Nes_Apu::nes_time_t Nes_Apu::next_dmc_read_time() const { return dmc.next_read_time(); }
+
+BLARGG_DEPRECATED( typedef int nes_time_t; ) // use your own typedef
+BLARGG_DEPRECATED( typedef unsigned nes_addr_t; ) // use your own typedef
+
+BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::output ( Blip_Buffer* c ) { set_output( c ); } )
+BLARGG_DEPRECATED_TEXT( inline void Nes_Apu::osc_output( int i, Blip_Buffer* c ) { set_output( i, c ); } )
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.cpp
new file mode 100644
index 00000000..5cfb3db3
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.cpp
@@ -0,0 +1,62 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nes_Cpu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+inline void Nes_Cpu::set_code_page( int i, void const* p )
+{
+ byte const* p2 = STATIC_CAST(byte const*,p) - NES_CPU_OFFSET( i * page_size );
+ cpu_state->code_map [i] = p2;
+ cpu_state_.code_map [i] = p2;
+}
+
+void Nes_Cpu::map_code( addr_t start, int size, void const* data, int mirror_size )
+{
+ // address range must begin and end on page boundaries
+ require( start % page_size == 0 );
+ require( size % page_size == 0 );
+ require( start + size <= 0x10000 );
+ require( mirror_size % page_size == 0 );
+
+ for ( int offset = 0; offset < size; offset += page_size )
+ set_code_page( NES_CPU_PAGE( start + offset ),
+ STATIC_CAST(char const*,data) + (offset & ((unsigned) mirror_size - 1)) );
+}
+
+void Nes_Cpu::reset( void const* unmapped_page )
+{
+ check( cpu_state == &cpu_state_ );
+ cpu_state = &cpu_state_;
+
+ r.flags = irq_inhibit_mask;
+ r.sp = 0xFF;
+ r.pc = 0;
+ r.a = 0;
+ r.x = 0;
+ r.y = 0;
+
+ cpu_state_.time = 0;
+ cpu_state_.base = 0;
+ irq_time_ = future_time;
+ end_time_ = future_time;
+ error_count_ = 0;
+
+ set_code_page( page_count, unmapped_page );
+ map_code( 0, 0x10000, unmapped_page, page_size );
+
+ blargg_verify_byte_order();
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.h
new file mode 100644
index 00000000..41b5163c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu.h
@@ -0,0 +1,131 @@
+// NES CPU emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef NES_CPU_H
+#define NES_CPU_H
+
+#include "blargg_common.h"
+
+class Nes_Cpu {
+public:
+ typedef BOOST::uint8_t byte;
+ typedef int time_t;
+ typedef int addr_t;
+ enum { future_time = INT_MAX/2 + 1 };
+
+ // Clears registers and maps all pages to unmapped_page
+ void reset( void const* unmapped_page = NULL );
+
+ // Maps code memory (memory accessed via the program counter). Start and size
+ // must be multiple of page_size. If mirror_size is non-zero, the first
+ // mirror_size bytes are repeated over the range. mirror_size must be a
+ // multiple of page_size.
+ enum { page_bits = 11 };
+ enum { page_size = 1 << page_bits };
+ void map_code( addr_t start, int size, void const* code, int mirror_size = 0 );
+
+ // Accesses emulated memory as CPU does
+ byte const* get_code( addr_t ) const;
+
+ // NES 6502 registers. NOT kept updated during emulation.
+ struct registers_t {
+ BOOST::uint16_t pc;
+ byte a;
+ byte x;
+ byte y;
+ byte flags;
+ byte sp;
+ };
+ registers_t r;
+
+ // Time of beginning of next instruction to be executed
+ time_t time() const { return cpu_state->time + cpu_state->base; }
+ void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; }
+ void adjust_time( int delta ) { cpu_state->time += delta; }
+
+ // Clocks past end (negative if before)
+ int time_past_end() const { return cpu_state->time; }
+
+ // Time of next IRQ
+ time_t irq_time() const { return irq_time_; }
+ void set_irq_time( time_t );
+
+ // Emulation stops once time >= end_time
+ time_t end_time() const { return end_time_; }
+ void set_end_time( time_t );
+
+ // Number of unimplemented instructions encountered and skipped
+ void clear_error_count() { error_count_ = 0; }
+ unsigned error_count() const { return error_count_; }
+ void count_error() { error_count_++; }
+
+ // Unmapped page should be filled with this
+ enum { halt_opcode = 0x22 };
+
+ enum { irq_inhibit_mask = 0x04 };
+
+ // Can read this many bytes past end of a page
+ enum { cpu_padding = 8 };
+
+private:
+ // noncopyable
+ Nes_Cpu( const Nes_Cpu& );
+ Nes_Cpu& operator = ( const Nes_Cpu& );
+
+
+// Implementation
+public:
+ Nes_Cpu() { cpu_state = &cpu_state_; }
+ enum { page_count = 0x10000 >> page_bits };
+
+ struct cpu_state_t {
+ byte const* code_map [page_count + 1];
+ time_t base;
+ int time;
+ };
+ cpu_state_t* cpu_state; // points to cpu_state_ or a local copy
+ cpu_state_t cpu_state_;
+ time_t irq_time_;
+ time_t end_time_;
+ unsigned error_count_;
+
+private:
+ void set_code_page( int, void const* );
+ inline void update_end_time( time_t end, time_t irq );
+};
+
+#define NES_CPU_PAGE( addr ) ((unsigned) (addr) >> Nes_Cpu::page_bits)
+
+#if BLARGG_NONPORTABLE
+ #define NES_CPU_OFFSET( addr ) (addr)
+#else
+ #define NES_CPU_OFFSET( addr ) ((addr) & (Nes_Cpu::page_size - 1))
+#endif
+
+inline BOOST::uint8_t const* Nes_Cpu::get_code( addr_t addr ) const
+{
+ return cpu_state_.code_map [NES_CPU_PAGE( addr )] + NES_CPU_OFFSET( addr );
+}
+
+inline void Nes_Cpu::update_end_time( time_t end, time_t irq )
+{
+ if ( end > irq && !(r.flags & irq_inhibit_mask) )
+ end = irq;
+
+ cpu_state->time += cpu_state->base - end;
+ cpu_state->base = end;
+}
+
+inline void Nes_Cpu::set_irq_time( time_t t )
+{
+ irq_time_ = t;
+ update_end_time( end_time_, t );
+}
+
+inline void Nes_Cpu::set_end_time( time_t t )
+{
+ end_time_ = t;
+ update_end_time( t, irq_time_ );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu_run.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu_run.h
new file mode 100644
index 00000000..0973b185
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Cpu_run.h
@@ -0,0 +1,1121 @@
+// NES 6502 CPU emulator run function
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Except where noted, time() and related functions will NOT work
+correctly inside a macro. TIME() is always correct, and FLUSH_TIME() and
+CACHE_TIME() allow the time changing functions to work.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + page_size
+ // time functions can be used
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+ // 0 <= READ_MEM() <= 0xFF
+
+ // 0 <= addr <= 0x1FF
+ int READ_LOW( addr_t );
+ void WRITE_LOW( addr_t, int data );
+ // 0 <= READ_LOW() <= 0xFF
+
+ // Often-used instructions attempt these before using a normal memory access.
+ // Optional; defaults to READ_MEM() and WRITE_MEM()
+ bool CAN_READ_FAST( addr_t ); // if true, uses result of READ_FAST
+ void READ_FAST( addr_t, int& out ); // ALWAYS called BEFORE CAN_READ_FAST
+ bool CAN_WRITE_FAST( addr_t ); // if true, uses WRITE_FAST instead of WRITE_MEM
+ void WRITE_FAST( addr_t, int data );
+
+ // Used by instructions most often used to access the NES PPU (LDA abs and BIT abs).
+ // Optional; defaults to READ_MEM.
+ void READ_PPU( addr_t, int& out );
+ // 0 <= out <= 0xFF
+
+// The following can be used within macros:
+
+ // Current time
+ time_t TIME();
+
+ // Allows use of time functions
+ void FLUSH_TIME();
+
+ // Must be used before end of macro if FLUSH_TIME() was used earlier
+ void CACHE_TIME();
+
+// Configuration (optional; commented behavior if defined)
+
+ // Emulates dummy reads for indexed instructions
+ #define NES_CPU_DUMMY_READS 1
+
+ // Optimizes as if map_code( 0, 0x10000 + cpu_padding, FLAT_MEM ) is always in effect
+ #define FLAT_MEM my_mem_array
+
+ // Expanded just before beginning of code, to help debugger
+ #define CPU_BEGIN void my_run_cpu() {
+
+#endif
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+// Allows MWCW debugger to step through code properly
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+// Time
+#define TIME() (s_time + s.base)
+#define FLUSH_TIME() {s.time = s_time - time_offset;}
+#define CACHE_TIME() {s_time = s.time + time_offset;}
+
+// Defaults
+#ifndef CAN_WRITE_FAST
+ #define CAN_WRITE_FAST( addr ) 0
+ #define WRITE_FAST( addr, data )
+#endif
+
+#ifndef CAN_READ_FAST
+ #define CAN_READ_FAST( addr ) 0
+ #define READ_FAST( addr, out )
+#endif
+
+#ifndef READ_PPU
+ #define READ_PPU( addr, out )\
+ {\
+ FLUSH_TIME();\
+ out = READ_MEM( addr );\
+ CACHE_TIME();\
+ }
+#endif
+
+#define READ_STACK READ_LOW
+#define WRITE_STACK WRITE_LOW
+
+// Dummy reads
+#if NES_CPU_DUMMY_READS
+ // TODO: optimize time handling
+ #define DUMMY_READ( addr, idx ) \
+ if ( (addr & 0xFF) < idx )\
+ {\
+ int const time_offset = 1;\
+ FLUSH_TIME();\
+ READ_MEM( (addr - 0x100) );\
+ CACHE_TIME();\
+ }
+#else
+ #define DUMMY_READ( addr, idx )
+#endif
+
+// Code
+#ifdef FLAT_MEM
+ #define CODE_PAGE( addr ) (FLAT_MEM)
+ #define CODE_OFFSET( addr ) (addr)
+#else
+ #define CODE_PAGE( addr ) (s.code_map [NES_CPU_PAGE( addr )])
+ #define CODE_OFFSET( addr ) NES_CPU_OFFSET( addr )
+#endif
+#define READ_CODE( addr ) (CODE_PAGE( addr ) [CODE_OFFSET( addr )])
+
+// Stack
+#define SET_SP( v ) (sp = ((v) + 1) | 0x100)
+#define GET_SP() ((sp - 1) & 0xFF)
+#define SP( o ) ((sp + (o - (o>0)*0x100)) | 0x100)
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const n80 = 0x80; // nz
+int const v40 = 0x40; // flags
+int const r20 = 0x20;
+int const b10 = 0x10;
+int const d08 = 0x08; // flags
+int const i04 = 0x04; // flags
+int const z02 = 0x02; // nz
+int const c01 = 0x01; // c
+
+#define IS_NEG (nz & 0x8080)
+
+#define GET_FLAGS( out ) \
+{\
+ out = flags & (v40 | d08 | i04);\
+ out += ((nz >> 8) | nz) & n80;\
+ out += c >> 8 & c01;\
+ if ( !BYTE( nz ) )\
+ out += z02;\
+}
+
+#define SET_FLAGS( in ) \
+{\
+ flags = in & (v40 | d08 | i04);\
+ c = nz = in << 8;\
+ nz += ~in & z02;\
+}
+
+{
+ int const time_offset = 0;
+
+ // Local state
+ Nes_Cpu::cpu_state_t s;
+ #ifdef FLAT_MEM
+ s.base = CPU.cpu_state_.base;
+ #else
+ s = CPU.cpu_state_;
+ #endif
+ CPU.cpu_state = &s;
+ int s_time = CPU.cpu_state_.time; // helps even on x86
+
+ // Registers
+ int pc = CPU.r.pc;
+ int a = CPU.r.a;
+ int x = CPU.r.x;
+ int y = CPU.r.y;
+ int sp;
+ SET_SP( CPU.r.sp );
+
+ // Flags
+ int flags;
+ int c; // carry set if (c & 0x100) != 0
+ int nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
+ {
+ int temp = CPU.r.flags;
+ SET_FLAGS( temp );
+ }
+
+loop:
+
+ // Check all values
+ check( (unsigned) sp - 0x100 < 0x100 );
+ check( (unsigned) pc < 0x10000 );
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ // Read instruction
+ byte const* instr = CODE_PAGE( pc );
+ int opcode;
+
+ if ( CODE_OFFSET(~0) == ~0 )
+ {
+ opcode = instr [pc];
+ pc++;
+ instr += pc;
+ }
+ else
+ {
+ instr += CODE_OFFSET( pc );
+ opcode = *instr++;
+ pc++;
+ }
+
+ // local to function in case it helps optimizer
+ static byte 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
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7,// 1
+ 6,6,0,8,3,3,5,5,4,2,2,2,4,4,6,6,// 2
+ 2,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
+ 2,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
+ 2,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
+ 2,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
+ 2,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
+ 2,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
+ 2,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
+ }; // 0x00 was 7 and 0x22 was 2
+
+ // Update time
+ if ( s_time >= 0 )
+ goto out_of_time;
+
+ #ifdef CPU_INSTR_HOOK
+ { CPU_INSTR_HOOK( (pc-1), (&instr [-1]), a, x, y, GET_SP(), TIME() ); }
+ #endif
+
+ s_time += clock_table [opcode];
+
+ int data;
+ data = *instr;
+
+ switch ( opcode )
+ {
+
+// Macros
+
+#define GET_MSB() (instr [1])
+#define ADD_PAGE( out ) (pc++, out = data + 0x100 * GET_MSB())
+#define GET_ADDR() GET_LE16( instr )
+
+#define PAGE_PENALTY( lsb ) s_time += (lsb) >> 8;
+
+#define INC_DEC( reg, n ) reg = BYTE( nz = reg + n ); goto loop;
+
+#define IND_Y( cross, out ) {\
+ int temp = READ_LOW( data ) + y;\
+ out = temp + 0x100 * READ_LOW( BYTE( data + 1 ) );\
+ cross( temp );\
+ }
+
+#define IND_X( out ) {\
+ int temp = data + x;\
+ out = 0x100 * READ_LOW( BYTE( temp + 1 ) ) + READ_LOW( BYTE( temp ) );\
+ }
+
+#define ARITH_ADDR_MODES( op )\
+case op - 0x04: /* (ind,x) */\
+ IND_X( data )\
+ goto ptr##op;\
+case op + 0x0C: /* (ind),y */\
+ IND_Y( PAGE_PENALTY, data )\
+ goto ptr##op;\
+case op + 0x10: /* zp,X */\
+ data = BYTE( data + x );\
+case op + 0x00: /* zp */\
+ data = READ_LOW( data );\
+ goto imm##op;\
+case op + 0x14: /* abs,Y */\
+ data += y;\
+ goto ind##op;\
+case op + 0x18: /* abs,X */\
+ data += x;\
+ind##op:\
+ PAGE_PENALTY( data );\
+case op + 0x08: /* abs */\
+ ADD_PAGE( data );\
+ptr##op:\
+ FLUSH_TIME();\
+ data = READ_MEM( data );\
+ CACHE_TIME();\
+case op + 0x04: /* imm */\
+imm##op:
+
+// TODO: more efficient way to handle negative branch that wraps PC around
+#define BRANCH( cond )\
+{\
+ ++pc;\
+ if ( !(cond) ) goto loop;\
+ s_time++;\
+ int offset = SBYTE( data );\
+ s_time += (BYTE(pc) + offset) >> 8 & 1;\
+ pc = WORD( pc + offset );\
+ goto loop;\
+}
+
+// Often-Used
+
+ case 0xB5: // LDA zp,x
+ a = nz = READ_LOW( BYTE( data + x ) );
+ pc++;
+ goto loop;
+
+ case 0xA5: // LDA zp
+ a = nz = READ_LOW( data );
+ pc++;
+ goto loop;
+
+ case 0xD0: // BNE
+ BRANCH( BYTE( nz ) );
+
+ case 0x20: { // JSR
+ int temp = pc + 1;
+ pc = GET_ADDR();
+ WRITE_STACK( SP( -1 ), temp >> 8 );
+ sp = SP( -2 );
+ WRITE_STACK( sp, temp );
+ goto loop;
+ }
+
+ case 0x4C: // JMP abs
+ pc = GET_ADDR();
+ goto loop;
+
+ case 0xE8: // INX
+ INC_DEC( 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( !BYTE( nz ) );
+
+ case 0x95: // STA zp,x
+ data = BYTE( data + x );
+ case 0x85: // STA zp
+ pc++;
+ WRITE_LOW( data, a );
+ goto loop;
+
+ case 0xC8: // INY
+ INC_DEC( y, 1 )
+
+ case 0xA8: // TAY
+ y = a;
+ nz = a;
+ goto loop;
+
+ case 0x98: // TYA
+ a = y;
+ nz = y;
+ goto loop;
+
+ case 0xAD:{// LDA abs
+ int addr = GET_ADDR();
+ pc += 2;
+ READ_PPU( addr, a = nz );
+ goto loop;
+ }
+
+ case 0x60: // RTS
+ pc = 1 + READ_STACK( sp );
+ pc += 0x100 * READ_STACK( SP( 1 ) );
+ sp = SP( 2 );
+ goto loop;
+
+ {
+ int addr;
+
+ case 0x8D: // STA abs
+ addr = GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+ sta_ptr:
+ FLUSH_TIME();
+ WRITE_MEM( addr, a );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x99: // STA abs,Y
+ addr = y + GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+ goto sta_abs_x;
+
+ case 0x9D: // STA abs,X (slightly more common than STA abs)
+ addr = x + GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, a );
+ goto loop;
+ }
+ DUMMY_READ( addr, x );
+ sta_abs_x:
+ FLUSH_TIME();
+ WRITE_MEM( addr, a );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x91: // STA (ind),Y
+ #define NO_PAGE_PENALTY( lsb )
+ IND_Y( NO_PAGE_PENALTY, addr )
+ pc++;
+ DUMMY_READ( addr, y );
+ 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
+ {
+ int 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;
+ PAGE_PENALTY( addr );
+ addr += 0x100 * READ_LOW( BYTE( data + 1 ) );
+ pc++;
+ READ_FAST( addr, a = nz );
+ if ( CAN_READ_FAST( addr ) )
+ goto loop;
+ DUMMY_READ( addr, y );
+ goto a_nz_read_addr;
+
+ case 0xB9: // LDA abs,Y
+ PAGE_PENALTY( data + y );
+ addr = GET_ADDR() + y;
+ pc += 2;
+ READ_FAST( addr, a = nz );
+ if ( CAN_READ_FAST( addr ) )
+ goto loop;
+ goto a_nz_read_addr;
+
+ case 0xBD: // LDA abs,X
+ PAGE_PENALTY( data + x );
+ addr = GET_ADDR() + x;
+ pc += 2;
+ READ_FAST( addr, a = nz );
+ if ( CAN_READ_FAST( addr ) )
+ goto loop;
+ DUMMY_READ( addr, x );
+ a_nz_read_addr:
+ FLUSH_TIME();
+ a = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+
+ }
+
+// Branch
+
+ case 0x50: // BVC
+ BRANCH( !(flags & v40) )
+
+ case 0x70: // BVS
+ BRANCH( flags & v40 )
+
+ case 0xB0: // BCS
+ BRANCH( c & 0x100 )
+
+ case 0x90: // BCC
+ BRANCH( !(c & 0x100) )
+
+// Load/store
+
+ case 0x94: // STY zp,x
+ data = BYTE( data + x );
+ case 0x84: // STY zp
+ pc++;
+ WRITE_LOW( data, y );
+ goto loop;
+
+ case 0x96: // STX zp,y
+ data = BYTE( data + y );
+ case 0x86: // STX zp
+ pc++;
+ WRITE_LOW( data, x );
+ goto loop;
+
+ case 0xB6: // LDX zp,y
+ data = BYTE( data + y );
+ case 0xA6: // LDX zp
+ data = READ_LOW( data );
+ case 0xA2: // LDX #imm
+ pc++;
+ x = data;
+ nz = data;
+ goto loop;
+
+ case 0xB4: // LDY zp,x
+ data = BYTE( data + x );
+ case 0xA4: // LDY zp
+ data = READ_LOW( data );
+ case 0xA0: // LDY #imm
+ pc++;
+ y = data;
+ nz = data;
+ goto loop;
+
+ case 0xBC: // LDY abs,X
+ data += x;
+ PAGE_PENALTY( data );
+ case 0xAC:{// LDY abs
+ int addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ FLUSH_TIME();
+ y = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ case 0xBE: // LDX abs,y
+ data += y;
+ PAGE_PENALTY( data );
+ case 0xAE:{// LDX abs
+ int addr = data + 0x100 * GET_MSB();
+ pc += 2;
+ FLUSH_TIME();
+ x = nz = READ_MEM( addr );
+ CACHE_TIME();
+ goto loop;
+ }
+
+ {
+ int temp;
+ case 0x8C: // STY abs
+ temp = y;
+ goto store_abs;
+
+ case 0x8E: // STX abs
+ temp = x;
+ store_abs:
+ int addr = GET_ADDR();
+ pc += 2;
+ if ( CAN_WRITE_FAST( addr ) )
+ {
+ WRITE_FAST( addr, temp );
+ goto loop;
+ }
+ FLUSH_TIME();
+ WRITE_MEM( addr, temp );
+ CACHE_TIME();
+ goto loop;
+ }
+
+// Compare
+
+ case 0xEC:{// CPX abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpx_data;
+ }
+
+ case 0xE4: // CPX zp
+ data = READ_LOW( data );
+ case 0xE0: // CPX #imm
+ cpx_data:
+ nz = x - data;
+ pc++;
+ c = ~nz;
+ nz &= 0xFF;
+ goto loop;
+
+ case 0xCC:{// CPY abs
+ int addr = GET_ADDR();
+ pc++;
+ FLUSH_TIME();
+ data = READ_MEM( addr );
+ CACHE_TIME();
+ goto cpy_data;
+ }
+
+ case 0xC4: // CPY zp
+ data = READ_LOW( data );
+ case 0xC0: // CPY #imm
+ cpy_data:
+ nz = y - data;
+ pc++;
+ c = ~nz;
+ nz &= 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
+ int addr = GET_ADDR();
+ pc += 2;
+ READ_PPU( addr, nz );
+ flags = (flags & ~v40) + (nz & v40);
+ 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++;
+ flags = (flags & ~v40) + (nz & v40);
+ if ( a & nz )
+ 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;
+
+// Add/subtract
+
+ ARITH_ADDR_MODES( 0xE5 ) // SBC
+ case 0xEB: // unofficial equivalent
+ data ^= 0xFF;
+ goto adc_imm;
+
+ ARITH_ADDR_MODES( 0x65 ) // ADC
+ adc_imm: {
+ int carry = c >> 8 & 1;
+ int ov = (a ^ 0x80) + carry + SBYTE( data );
+ flags = (flags & ~v40) + (ov >> 2 & v40);
+ c = nz = a + data + carry;
+ pc++;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+// Shift/rotate
+
+ case 0x4A: // LSR A
+ c = 0;
+ case 0x6A: // ROR A
+ nz = c >> 1 & 0x80;
+ c = a << 8;
+ nz += a >> 1;
+ a = nz;
+ goto loop;
+
+ case 0x0A: // ASL A
+ nz = a << 1;
+ c = nz;
+ a = BYTE( nz );
+ goto loop;
+
+ case 0x2A: { // ROL A
+ nz = a << 1;
+ int temp = c >> 8 & 1;
+ c = nz;
+ nz += temp;
+ a = BYTE( nz );
+ goto loop;
+ }
+
+ case 0x5E: // LSR abs,X
+ data += x;
+ case 0x4E: // LSR abs
+ c = 0;
+ case 0x6E: // ROR abs
+ ror_abs: {
+ ADD_PAGE( data );
+ FLUSH_TIME();
+ int temp = READ_MEM( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto rotate_common;
+ }
+
+ case 0x3E: // ROL abs,X
+ data += x;
+ goto rol_abs;
+
+ case 0x1E: // ASL abs,X
+ data += x;
+ case 0x0E: // ASL abs
+ c = 0;
+ case 0x2E: // ROL abs
+ rol_abs:
+ ADD_PAGE( data );
+ nz = c >> 8 & 1;
+ FLUSH_TIME();
+ nz += (c = READ_MEM( data ) << 1);
+ rotate_common:
+ pc++;
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+ case 0x7E: // ROR abs,X
+ data += x;
+ goto ror_abs;
+
+ case 0x76: // ROR zp,x
+ data = BYTE( data + x );
+ goto ror_zp;
+
+ case 0x56: // LSR zp,x
+ data = BYTE( data + x );
+ case 0x46: // LSR zp
+ c = 0;
+ case 0x66: // ROR zp
+ ror_zp: {
+ int temp = READ_LOW( data );
+ nz = (c >> 1 & 0x80) + (temp >> 1);
+ c = temp << 8;
+ goto write_nz_zp;
+ }
+
+ case 0x36: // ROL zp,x
+ data = BYTE( data + x );
+ goto rol_zp;
+
+ case 0x16: // ASL zp,x
+ data = BYTE( data + x );
+ case 0x06: // ASL zp
+ c = 0;
+ case 0x26: // ROL zp
+ rol_zp:
+ nz = c >> 8 & 1;
+ nz += (c = READ_LOW( data ) << 1);
+ goto write_nz_zp;
+
+// Increment/decrement
+
+ case 0xCA: // DEX
+ INC_DEC( x, -1 )
+
+ case 0x88: // DEY
+ INC_DEC( y, -1 )
+
+ case 0xF6: // INC zp,x
+ data = BYTE( data + x );
+ case 0xE6: // INC zp
+ nz = 1;
+ goto add_nz_zp;
+
+ case 0xD6: // DEC zp,x
+ data = BYTE( data + x );
+ case 0xC6: // DEC zp
+ nz = -1;
+ add_nz_zp:
+ nz += READ_LOW( data );
+ write_nz_zp:
+ pc++;
+ WRITE_LOW( data, nz );
+ goto loop;
+
+ case 0xFE: // INC abs,x
+ data = x + GET_ADDR();
+ goto inc_ptr;
+
+ case 0xEE: // INC abs
+ data = GET_ADDR();
+ inc_ptr:
+ nz = 1;
+ goto inc_common;
+
+ case 0xDE: // DEC abs,x
+ data = x + GET_ADDR();
+ goto dec_ptr;
+
+ case 0xCE: // DEC abs
+ data = GET_ADDR();
+ dec_ptr:
+ nz = -1;
+ inc_common:
+ FLUSH_TIME();
+ pc += 2;
+ nz += READ_MEM( data );
+ WRITE_MEM( data, BYTE( nz ) );
+ CACHE_TIME();
+ goto loop;
+
+// Transfer
+
+ case 0xAA: // TAX
+ x = nz = a;
+ goto loop;
+
+ case 0x8A: // TXA
+ a = nz = x;
+ goto loop;
+
+ case 0x9A: // TXS
+ SET_SP( x ); // verified (no flag change)
+ goto loop;
+
+ case 0xBA: // TSX
+ x = nz = GET_SP();
+ goto loop;
+
+// Stack
+
+ case 0x48: // PHA
+ sp = SP( -1 );
+ WRITE_STACK( sp, a );
+ goto loop;
+
+ case 0x68: // PLA
+ a = nz = READ_STACK( sp );
+ sp = SP( 1 );
+ goto loop;
+
+ case 0x40:{// RTI
+ pc = READ_STACK( SP( 1 ) );
+ pc += READ_STACK( SP( 2 ) ) * 0x100;
+ int temp = READ_STACK( sp );
+ sp = SP( 3 );
+ data = flags;
+ SET_FLAGS( temp );
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.irq_time_;
+ if ( delta <= 0 ) goto loop; // end_time < irq_time
+ if ( flags & i04 ) goto loop;
+ s_time += delta;
+ s.base = CPU.irq_time_;
+ goto loop;
+ }
+
+ case 0x28:{// PLP
+ int temp = READ_STACK( sp );
+ sp = SP( 1 );
+ int changed = flags ^ temp;
+ SET_FLAGS( temp );
+ if ( !(changed & i04) )
+ goto loop; // I flag didn't change
+ if ( flags & i04 )
+ goto handle_sei;
+ goto handle_cli;
+ }
+
+ case 0x08:{// PHP
+ int temp;
+ GET_FLAGS( temp );
+ sp = SP( -1 );
+ WRITE_STACK( sp, temp | (b10 | r20) );
+ goto loop;
+ }
+
+ case 0x6C:{// JMP (ind)
+ data = GET_ADDR();
+ byte const* page = CODE_PAGE( data );
+ pc = page [CODE_OFFSET( data )];
+ data = (data & 0xFF00) + ((data + 1) & 0xFF);
+ pc += page [CODE_OFFSET( data )] * 0x100;
+ goto loop;
+ }
+
+ case 0x00: // BRK
+ goto handle_brk;
+
+// Flags
+
+ case 0x38: // SEC
+ c = 0x100;
+ goto loop;
+
+ case 0x18: // CLC
+ c = 0;
+ goto loop;
+
+ case 0xB8: // CLV
+ flags &= ~v40;
+ goto loop;
+
+ case 0xD8: // CLD
+ flags &= ~d08;
+ goto loop;
+
+ case 0xF8: // SED
+ flags |= d08;
+ goto loop;
+
+ case 0x58: // CLI
+ if ( !(flags & i04) )
+ goto loop;
+ flags &= ~i04;
+ handle_cli: {
+ //dprintf( "CLI at %d\n", TIME );
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.irq_time_;
+ if ( delta <= 0 )
+ {
+ if ( TIME() < CPU.irq_time_ )
+ goto loop;
+ goto delayed_cli;
+ }
+ s.base = CPU.irq_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ if ( delta >= s_time + 1 )
+ {
+ // delayed irq until after next instruction
+ s.base += s_time + 1;
+ s_time = -1;
+ goto loop;
+ }
+
+ // TODO: implement
+ delayed_cli:
+ dprintf( "Delayed CLI not emulated\n" );
+ goto loop;
+ }
+
+ case 0x78: // SEI
+ if ( flags & i04 )
+ goto loop;
+ flags |= i04;
+ handle_sei: {
+ CPU.r.flags = flags; // update externally-visible I flag
+ int delta = s.base - CPU.end_time_;
+ s.base = CPU.end_time_;
+ s_time += delta;
+ if ( s_time < 0 )
+ goto loop;
+
+ dprintf( "Delayed SEI not emulated\n" );
+ goto loop;
+ }
+
+// Unofficial
+
+ // SKW - skip word
+ case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
+ PAGE_PENALTY( 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 Nes_Cpu::halt_opcode: // HLT - halt processor
+ if ( pc-- > 0x10000 )
+ {
+ // handle wrap-around (assumes caller has put page of HLT at 0x10000)
+ pc = WORD( pc );
+ goto loop;
+ }
+ case 0x02: case 0x12: case 0x32: case 0x42: case 0x52:
+ case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
+ goto stop;
+
+// Unimplemented
+
+ case 0xFF: // force 256-entry jump table for optimization purposes
+ c |= 1; // compiler doesn't know that this won't affect anything
+ default:
+ check( (unsigned) opcode < 0x100 );
+
+ #ifdef UNIMPL_INSTR
+ UNIMPL_INSTR();
+ #endif
+
+ // At least skip over proper number of bytes instruction uses
+ static unsigned char const illop_lens [8] = {
+ 0x40, 0x40, 0x40, 0x80, 0x40, 0x40, 0x80, 0xA0
+ };
+ int opcode = instr [-1];
+ int len = illop_lens [opcode >> 2 & 7] >> (opcode << 1 & 6) & 3;
+ if ( opcode == 0x9C )
+ len = 2;
+ pc += len;
+ CPU.error_count_++;
+
+ // Account for extra clock
+ if ( (opcode >> 4) == 0x0B )
+ {
+ if ( opcode == 0xB3 )
+ data = READ_LOW( data );
+ if ( opcode != 0xB7 )
+ PAGE_PENALTY( data + y );
+ }
+ goto loop;
+ }
+ assert( false ); // catch missing 'goto loop' or accidental 'break'
+
+ int result_;
+handle_brk:
+ pc++;
+ result_ = b10 | 4;
+
+#ifdef CPU_DONE
+interrupt:
+#endif
+ {
+ s_time += 7;
+
+ // Save PC and read vector
+ WRITE_STACK( SP( -1 ), pc >> 8 );
+ WRITE_STACK( SP( -2 ), pc );
+ pc = GET_LE16( &READ_CODE( 0xFFFA ) + (result_ & 4) );
+
+ // Save flags
+ int temp;
+ GET_FLAGS( temp );
+ temp |= r20 + (result_ & b10); // B flag set for BRK
+ sp = SP( -3 );
+ WRITE_STACK( sp, temp );
+
+ // Update I flag in externally-visible flags
+ CPU.r.flags = (flags |= i04);
+
+ // Update time
+ int delta = s.base - CPU.end_time_;
+ if ( delta >= 0 )
+ goto loop;
+ s_time += delta;
+ s.base = CPU.end_time_;
+ goto loop;
+ }
+
+out_of_time:
+ pc--;
+
+ // Optional action that triggers interrupt or changes irq/end time
+ #ifdef CPU_DONE
+ {
+ CPU_DONE( result_ );
+ if ( result_ >= 0 )
+ goto interrupt;
+ if ( s_time < 0 )
+ goto loop;
+ }
+ #endif
+stop:
+
+ // Flush cached state
+ CPU.r.pc = pc;
+ CPU.r.sp = GET_SP();
+ CPU.r.a = a;
+ CPU.r.x = x;
+ CPU.r.y = y;
+
+ int temp;
+ GET_FLAGS( temp );
+ CPU.r.flags = temp;
+
+ CPU.cpu_state_.base = s.base;
+ CPU.cpu_state_.time = s_time;
+ CPU.cpu_state = &CPU.cpu_state_;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.cpp
new file mode 100644
index 00000000..21602196
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.cpp
@@ -0,0 +1,280 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nes_Fds_Apu.h"
+
+/* Copyright (C) 2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const fract_range = 65536;
+
+void Nes_Fds_Apu::reset()
+{
+ memset( regs_, 0, sizeof regs_ );
+ memset( mod_wave, 0, sizeof mod_wave );
+
+ last_time = 0;
+ env_delay = 0;
+ sweep_delay = 0;
+ wave_pos = 0;
+ last_amp = 0;
+ wave_fract = fract_range;
+ mod_fract = fract_range;
+ mod_pos = 0;
+ mod_write_pos = 0;
+
+ static byte const initial_regs [0x0B] = {
+ 0x80, // disable envelope
+ 0, 0, 0xC0, // disable wave and lfo
+ 0x80, // disable sweep
+ 0, 0, 0x80, // disable modulation
+ 0, 0, 0xFF // LFO period // TODO: use 0xE8 as FDS ROM does?
+ };
+ for ( int i = 0; i < (int) sizeof initial_regs; i++ )
+ {
+ // two writes to set both gain and period for envelope registers
+ write_( io_addr + wave_size + i, 0 );
+ write_( io_addr + wave_size + i, initial_regs [i] );
+ }
+}
+
+void Nes_Fds_Apu::write_( unsigned addr, int data )
+{
+ unsigned reg = addr - io_addr;
+ if ( reg < io_size )
+ {
+ if ( reg < wave_size )
+ {
+ if ( regs (0x4089) & 0x80 )
+ regs_ [reg] = data & wave_sample_max;
+ }
+ else
+ {
+ regs_ [reg] = data;
+ switch ( addr )
+ {
+ case 0x4080:
+ if ( data & 0x80 )
+ env_gain = data & 0x3F;
+ else
+ env_speed = (data & 0x3F) + 1;
+ break;
+
+ case 0x4084:
+ if ( data & 0x80 )
+ sweep_gain = data & 0x3F;
+ else
+ sweep_speed = (data & 0x3F) + 1;
+ break;
+
+ case 0x4085:
+ mod_pos = mod_write_pos;
+ regs (0x4085) = data & 0x7F;
+ break;
+
+ case 0x4088:
+ if ( regs (0x4087) & 0x80 )
+ {
+ int pos = mod_write_pos;
+ data &= 0x07;
+ mod_wave [pos ] = data;
+ mod_wave [pos + 1] = data;
+ mod_write_pos = (pos + 2) & (wave_size - 1);
+ mod_pos = (mod_pos + 2) & (wave_size - 1);
+ }
+ break;
+ }
+ }
+ }
+}
+
+void Nes_Fds_Apu::set_tempo( double t )
+{
+ lfo_tempo = lfo_base_tempo;
+ if ( t != 1.0 )
+ {
+ lfo_tempo = int ((double) lfo_base_tempo / t + 0.5);
+ if ( lfo_tempo <= 0 )
+ lfo_tempo = 1;
+ }
+}
+
+void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
+{
+ int const wave_freq = (regs (0x4083) & 0x0F) * 0x100 + regs (0x4082);
+ Blip_Buffer* const output_ = this->output_;
+ if ( wave_freq && output_ && !((regs (0x4089) | regs (0x4083)) & 0x80) )
+ {
+ output_->set_modified();
+
+ // master_volume
+ #define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100
+ static unsigned char const master_volumes [4] = {
+ MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 )
+ };
+ int const master_volume = master_volumes [regs (0x4089) & 0x03];
+
+ // lfo_period
+ blip_time_t lfo_period = regs (0x408A) * lfo_tempo;
+ if ( regs (0x4083) & 0x40 )
+ lfo_period = 0;
+
+ // sweep setup
+ blip_time_t sweep_time = last_time + sweep_delay;
+ blip_time_t const sweep_period = lfo_period * sweep_speed;
+ if ( !sweep_period || regs (0x4084) & 0x80 )
+ sweep_time = final_end_time;
+
+ // envelope setup
+ blip_time_t env_time = last_time + env_delay;
+ blip_time_t const env_period = lfo_period * env_speed;
+ if ( !env_period || regs (0x4080) & 0x80 )
+ env_time = final_end_time;
+
+ // modulation
+ int mod_freq = 0;
+ if ( !(regs (0x4087) & 0x80) )
+ mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086);
+
+ blip_time_t end_time = last_time;
+ do
+ {
+ // sweep
+ if ( sweep_time <= end_time )
+ {
+ sweep_time += sweep_period;
+ int mode = regs (0x4084) >> 5 & 2;
+ int new_sweep_gain = sweep_gain + mode - 1;
+ if ( (unsigned) new_sweep_gain <= (unsigned) 0x80 >> mode )
+ sweep_gain = new_sweep_gain;
+ else
+ regs (0x4084) |= 0x80; // optimization only
+ }
+
+ // envelope
+ if ( env_time <= end_time )
+ {
+ env_time += env_period;
+ int mode = regs (0x4080) >> 5 & 2;
+ int new_env_gain = env_gain + mode - 1;
+ if ( (unsigned) new_env_gain <= (unsigned) 0x80 >> mode )
+ env_gain = new_env_gain;
+ else
+ regs (0x4080) |= 0x80; // optimization only
+ }
+
+ // new end_time
+ blip_time_t const start_time = end_time;
+ end_time = final_end_time;
+ if ( end_time > env_time ) end_time = env_time;
+ if ( end_time > sweep_time ) end_time = sweep_time;
+
+ // frequency modulation
+ int freq = wave_freq;
+ if ( mod_freq )
+ {
+ // time of next modulation clock
+ blip_time_t mod_time = start_time + (mod_fract + mod_freq - 1) / mod_freq;
+ if ( end_time > mod_time )
+ end_time = mod_time;
+
+ // run modulator up to next clock and save old sweep_bias
+ int sweep_bias = regs (0x4085);
+ mod_fract -= (end_time - start_time) * mod_freq;
+ if ( mod_fract <= 0 )
+ {
+ mod_fract += fract_range;
+ check( (unsigned) mod_fract <= fract_range );
+
+ static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 };
+ int mod = mod_wave [mod_pos];
+ mod_pos = (mod_pos + 1) & (wave_size - 1);
+ int new_sweep_bias = (sweep_bias + mod_table [mod]) & 0x7F;
+ if ( mod == 4 )
+ new_sweep_bias = 0;
+ regs (0x4085) = new_sweep_bias;
+ }
+
+ // apply frequency modulation
+ sweep_bias = (sweep_bias ^ 0x40) - 0x40;
+ int factor = sweep_bias * sweep_gain;
+ int extra = factor & 0x0F;
+ factor >>= 4;
+ if ( extra )
+ {
+ factor--;
+ if ( sweep_bias >= 0 )
+ factor += 3;
+ }
+ if ( factor > 193 ) factor -= 258;
+ if ( factor < -64 ) factor += 256;
+ freq += (freq * factor) >> 6;
+ if ( freq <= 0 )
+ continue;
+ }
+
+ // wave
+ int wave_fract = this->wave_fract;
+ blip_time_t delay = (wave_fract + freq - 1) / freq;
+ blip_time_t time = start_time + delay;
+
+ if ( time <= end_time )
+ {
+ // at least one wave clock within start_time...end_time
+
+ blip_time_t const min_delay = fract_range / freq;
+ int wave_pos = this->wave_pos;
+
+ int volume = env_gain;
+ if ( volume > vol_max )
+ volume = vol_max;
+ volume *= master_volume;
+
+ int const min_fract = min_delay * freq;
+
+ do
+ {
+ // clock wave
+ int amp = regs_ [wave_pos] * volume;
+ wave_pos = (wave_pos + 1) & (wave_size - 1);
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output_ );
+ }
+
+ wave_fract += fract_range - delay * freq;
+ check( unsigned (fract_range - wave_fract) < freq );
+
+ // delay until next clock
+ delay = min_delay;
+ if ( wave_fract > min_fract )
+ delay++;
+ check( delay && delay == (wave_fract + freq - 1) / freq );
+
+ time += delay;
+ }
+ while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong
+
+ this->wave_pos = wave_pos;
+ }
+ this->wave_fract = wave_fract - (end_time - (time - delay)) * freq;
+ check( this->wave_fract > 0 );
+ }
+ while ( end_time < final_end_time );
+
+ env_delay = env_time - final_end_time; check( env_delay >= 0 );
+ sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 );
+ }
+ last_time = final_end_time;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.h
new file mode 100644
index 00000000..0d4775c5
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fds_Apu.h
@@ -0,0 +1,139 @@
+// NES FDS sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef NES_FDS_APU_H
+#define NES_FDS_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Nes_Fds_Apu {
+public:
+ // setup
+ void set_tempo( double );
+ enum { osc_count = 1 };
+ void set_output( Blip_Buffer* buf );
+ void volume( double );
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+ // emulation
+ void reset();
+ enum { io_addr = 0x4040 };
+ enum { io_size = 0x53 };
+ void write( blip_time_t time, unsigned addr, int data );
+ int read( blip_time_t time, unsigned addr );
+ void end_frame( blip_time_t );
+
+public:
+ Nes_Fds_Apu();
+ void write_( unsigned addr, int data );
+ BLARGG_DISABLE_NOTHROW
+
+ void set_output( int index, Blip_Buffer* center,
+ Blip_Buffer* left_ignored = NULL, Blip_Buffer* right_ignored = NULL );
+ BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x4040 }; )
+ BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x4092 }; )
+ BLARGG_DEPRECATED_TEXT( enum { reg_count = end_addr - start_addr + 1 }; )
+ void osc_output( int, Blip_Buffer* );
+private:
+ enum { wave_size = 0x40 };
+ enum { master_vol_max = 10 };
+ enum { vol_max = 0x20 };
+ enum { wave_sample_max = 0x3F };
+
+ unsigned char regs_ [io_size];// last written value to registers
+
+ enum { lfo_base_tempo = 8 };
+ int lfo_tempo; // normally 8; adjusted by set_tempo()
+
+ int env_delay;
+ int env_speed;
+ int env_gain;
+
+ int sweep_delay;
+ int sweep_speed;
+ int sweep_gain;
+
+ int wave_pos;
+ int last_amp;
+ blip_time_t wave_fract;
+
+ int mod_fract;
+ int mod_pos;
+ int mod_write_pos;
+ unsigned char mod_wave [wave_size];
+
+ // synthesis
+ blip_time_t last_time;
+ Blip_Buffer* output_;
+ Blip_Synth_Fast synth;
+
+ // allow access to registers by absolute address (i.e. 0x4080)
+ unsigned char& regs( unsigned addr ) { return regs_ [addr - io_addr]; }
+
+ void run_until( blip_time_t );
+};
+
+inline void Nes_Fds_Apu::volume( double v )
+{
+ synth.volume( 0.14 / master_vol_max / vol_max / wave_sample_max * v );
+}
+
+inline void Nes_Fds_Apu::set_output( Blip_Buffer* b )
+{
+ output_ = b;
+}
+
+inline void Nes_Fds_Apu::set_output( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
+{
+ assert( (unsigned) i < osc_count );
+ output_ = buf;
+}
+
+inline void Nes_Fds_Apu::end_frame( blip_time_t end_time )
+{
+ if ( end_time > last_time )
+ run_until( end_time );
+ last_time -= end_time;
+ assert( last_time >= 0 );
+}
+
+inline void Nes_Fds_Apu::write( blip_time_t time, unsigned addr, int data )
+{
+ run_until( time );
+ write_( addr, data );
+}
+
+inline int Nes_Fds_Apu::read( blip_time_t time, unsigned addr )
+{
+ run_until( time );
+
+ int result = 0xFF;
+ switch ( addr )
+ {
+ case 0x4090:
+ result = env_gain;
+ break;
+
+ case 0x4092:
+ result = sweep_gain;
+ break;
+
+ default:
+ unsigned i = addr - io_addr;
+ if ( i < wave_size )
+ result = regs_ [i];
+ }
+
+ return result | 0x40;
+}
+
+inline Nes_Fds_Apu::Nes_Fds_Apu()
+{
+ lfo_tempo = lfo_base_tempo;
+ set_output( NULL );
+ volume( 1.0 );
+ reset();
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.cpp
new file mode 100644
index 00000000..27418b55
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.cpp
@@ -0,0 +1,121 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nes_Fme7_Apu.h"
+
+/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void Nes_Fme7_Apu::reset()
+{
+ last_time = 0;
+
+ for ( int i = 0; i < osc_count; i++ )
+ oscs [i].last_amp = 0;
+
+ fme7_apu_state_t* state = this;
+ memset( state, 0, sizeof *state );
+}
+
+unsigned char const Nes_Fme7_Apu::amp_table [16] =
+{
+ #define ENTRY( n ) (unsigned char) (n * amp_range + 0.5)
+ ENTRY(0.0000), ENTRY(0.0078), ENTRY(0.0110), ENTRY(0.0156),
+ ENTRY(0.0221), ENTRY(0.0312), ENTRY(0.0441), ENTRY(0.0624),
+ ENTRY(0.0883), ENTRY(0.1249), ENTRY(0.1766), ENTRY(0.2498),
+ ENTRY(0.3534), ENTRY(0.4998), ENTRY(0.7070), ENTRY(1.0000)
+ #undef ENTRY
+};
+
+void Nes_Fme7_Apu::run_until( blip_time_t end_time )
+{
+ require( end_time >= last_time );
+
+ for ( int index = 0; index < osc_count; index++ )
+ {
+ int mode = regs [7] >> index;
+ int vol_mode = regs [010 + index];
+ int volume = amp_table [vol_mode & 0x0F];
+
+ Blip_Buffer* const osc_output = oscs [index].output;
+ if ( !osc_output )
+ continue;
+
+ // check for unsupported mode
+ #ifndef NDEBUG
+ if ( (mode & 011) <= 001 && vol_mode & 0x1F )
+ dprintf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n",
+ mode, vol_mode & 0x1F );
+ #endif
+
+ if ( (mode & 001) | (vol_mode & 0x10) )
+ volume = 0; // noise and envelope aren't supported
+
+ // period
+ int const period_factor = 16;
+ unsigned period = (regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor +
+ regs [index * 2] * period_factor;
+ if ( period < 50 ) // around 22 kHz
+ {
+ volume = 0;
+ if ( !period ) // on my AY-3-8910A, period doesn't have extra one added
+ period = period_factor;
+ }
+
+ // current amplitude
+ int amp = volume;
+ if ( !phases [index] )
+ amp = 0;
+
+ {
+ int delta = amp - oscs [index].last_amp;
+ if ( delta )
+ {
+ oscs [index].last_amp = amp;
+ osc_output->set_modified();
+ synth.offset( last_time, delta, osc_output );
+ }
+ }
+
+ blip_time_t time = last_time + delays [index];
+ if ( time < end_time )
+ {
+ int delta = amp * 2 - volume;
+ osc_output->set_modified();
+ if ( volume )
+ {
+ do
+ {
+ delta = -delta;
+ synth.offset_inline( time, delta, osc_output );
+ time += period;
+ }
+ while ( time < end_time );
+
+ oscs [index].last_amp = (delta + volume) >> 1;
+ phases [index] = (delta > 0);
+ }
+ else
+ {
+ // maintain phase when silent
+ int count = (end_time - time + period - 1) / period;
+ phases [index] ^= count & 1;
+ time += count * period;
+ }
+ }
+
+ delays [index] = time - end_time;
+ }
+
+ last_time = end_time;
+}
+
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.h
new file mode 100644
index 00000000..6fee7c81
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Fme7_Apu.h
@@ -0,0 +1,131 @@
+// Sunsoft FME-7 sound emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef NES_FME7_APU_H
+#define NES_FME7_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+struct fme7_apu_state_t
+{
+ enum { reg_count = 14 };
+ BOOST::uint8_t regs [reg_count];
+ BOOST::uint8_t phases [3]; // 0 or 1
+ BOOST::uint8_t latch;
+ BOOST::uint16_t delays [3]; // a, b, c
+};
+
+class Nes_Fme7_Apu : private fme7_apu_state_t {
+public:
+ // See Nes_Apu.h for reference
+ void reset();
+ void volume( double );
+ void treble_eq( blip_eq_t const& );
+ void set_output( Blip_Buffer* );
+ enum { osc_count = 3 };
+ void set_output( int index, Blip_Buffer* );
+ void end_frame( blip_time_t );
+ void save_state( fme7_apu_state_t* ) const;
+ void load_state( fme7_apu_state_t const& );
+
+ // Mask and addresses of registers
+ enum { addr_mask = 0xE000 };
+ enum { data_addr = 0xE000 };
+ enum { latch_addr = 0xC000 };
+
+ // (addr & addr_mask) == latch_addr
+ void write_latch( int );
+
+ // (addr & addr_mask) == data_addr
+ void write_data( blip_time_t, int data );
+
+public:
+ Nes_Fme7_Apu();
+ BLARGG_DISABLE_NOTHROW
+private:
+ // noncopyable
+ Nes_Fme7_Apu( const Nes_Fme7_Apu& );
+ Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& );
+
+ static unsigned char const amp_table [16];
+
+ struct {
+ Blip_Buffer* output;
+ int last_amp;
+ } oscs [osc_count];
+ blip_time_t last_time;
+
+ enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff
+ Blip_Synth_Norm synth;
+
+ void run_until( blip_time_t );
+};
+
+inline void Nes_Fme7_Apu::volume( double v )
+{
+ synth.volume( 0.38 / amp_range * v ); // to do: fine-tune
+}
+
+inline void Nes_Fme7_Apu::treble_eq( blip_eq_t const& eq )
+{
+ synth.treble_eq( eq );
+}
+
+inline void Nes_Fme7_Apu::set_output( int i, Blip_Buffer* buf )
+{
+ assert( (unsigned) i < osc_count );
+ oscs [i].output = buf;
+}
+
+inline void Nes_Fme7_Apu::set_output( Blip_Buffer* buf )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ set_output( i, buf );
+}
+
+inline Nes_Fme7_Apu::Nes_Fme7_Apu()
+{
+ set_output( NULL );
+ volume( 1.0 );
+ reset();
+}
+
+inline void Nes_Fme7_Apu::write_latch( int data ) { latch = data; }
+
+inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data )
+{
+ if ( (unsigned) latch >= reg_count )
+ {
+ #ifdef dprintf
+ dprintf( "FME7 write to %02X (past end of sound registers)\n", (int) latch );
+ #endif
+ return;
+ }
+
+ run_until( time );
+ regs [latch] = data;
+}
+
+inline void Nes_Fme7_Apu::end_frame( blip_time_t time )
+{
+ if ( time > last_time )
+ run_until( time );
+
+ assert( last_time >= time );
+ last_time -= time;
+}
+
+inline void Nes_Fme7_Apu::save_state( fme7_apu_state_t* out ) const
+{
+ *out = *this;
+}
+
+inline void Nes_Fme7_Apu::load_state( fme7_apu_state_t const& in )
+{
+ reset();
+ fme7_apu_state_t* state = this;
+ *state = in;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Mmc5_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Mmc5_Apu.h
new file mode 100644
index 00000000..f55b8d82
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Mmc5_Apu.h
@@ -0,0 +1,70 @@
+// NES MMC5 sound chip emulator
+
+// Nes_Snd_Emu 0.2.0-pre
+#ifndef NES_MMC5_APU_H
+#define NES_MMC5_APU_H
+
+#include "blargg_common.h"
+#include "Nes_Apu.h"
+
+class Nes_Mmc5_Apu : public Nes_Apu {
+public:
+ enum { regs_addr = 0x5000 };
+ enum { regs_size = 0x16 };
+
+ enum { osc_count = 3 };
+ void write_register( blip_time_t, unsigned addr, int data );
+ void set_output( Blip_Buffer* );
+ void set_output( int index, Blip_Buffer* );
+
+ enum { exram_size = 1024 };
+ unsigned char exram [exram_size];
+
+ BLARGG_DEPRECATED_TEXT( enum { start_addr = 0x5000 }; )
+ BLARGG_DEPRECATED_TEXT( enum { end_addr = 0x5015 }; )
+};
+
+inline void Nes_Mmc5_Apu::set_output( int i, Blip_Buffer* b )
+{
+ // in: square 1, square 2, PCM
+ // out: square 1, square 2, skipped, skipped, PCM
+ if ( i > 1 )
+ i += 2;
+ Nes_Apu::set_output( i, b );
+}
+
+inline void Nes_Mmc5_Apu::set_output( Blip_Buffer* b )
+{
+ set_output( 0, b );
+ set_output( 1, b );
+ set_output( 2, b );
+}
+
+inline void Nes_Mmc5_Apu::write_register( blip_time_t time, unsigned addr, int data )
+{
+ switch ( addr )
+ {
+ case 0x5015: // channel enables
+ data &= 0x03; // enable the square waves only
+ // fall through
+ case 0x5000: // Square 1
+ case 0x5002:
+ case 0x5003:
+ case 0x5004: // Square 2
+ case 0x5006:
+ case 0x5007:
+ case 0x5011: // DAC
+ Nes_Apu::write_register( time, addr - 0x1000, data );
+ break;
+
+ case 0x5010: // some things write to this for some reason
+ break;
+
+#ifdef BLARGG_DEBUG_H
+ default:
+ dprintf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data );
+#endif
+ }
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Namco_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Namco_Apu.cpp
new file mode 100644
index 00000000..b2baa2d9
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Namco_Apu.cpp
@@ -0,0 +1,152 @@
+// Nes_Snd_Emu 0.2.0-pre. 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()
+{
+ set_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::set_output( Blip_Buffer* buf )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ set_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], &reg [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;
+
+ 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 = &reg [i * 8 + 0x40];
+ if ( !(osc_reg [4] & 0xE0) )
+ continue;
+
+ int volume = osc_reg [7] & 15;
+ if ( !volume )
+ continue;
+
+ int freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100 + osc_reg [0];
+ if ( freq < 64 * active_oscs )
+ continue; // prevent low frequencies from excessively delaying freq changes
+
+ int const master_clock_divider = 12; // NES time derived via divider of master clock
+ int const n106_divider = 45; // N106 then divides master clock by this
+ int const max_freq = 0x3FFFF;
+ int const lowest_freq_period = (max_freq + 1) * n106_divider / master_clock_divider;
+ // divide by 8 to avoid overflow
+ blip_resampled_time_t period =
+ output->resampled_duration( lowest_freq_period / 8 ) / freq * 8 * 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;
+
+ output->set_modified();
+
+ 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.6pre/gme/Nes_Namco_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Namco_Apu.h
new file mode 100644
index 00000000..00e8dcbc
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Namco_Apu.h
@@ -0,0 +1,102 @@
+// Namco 106 sound chip emulator
+
+// Nes_Snd_Emu 0.2.0-pre
+#ifndef NES_NAMCO_APU_H
+#define NES_NAMCO_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+struct namco_state_t;
+
+class Nes_Namco_Apu {
+public:
+ // See Nes_Apu.h for reference.
+ void volume( double );
+ void treble_eq( const blip_eq_t& );
+ void set_output( Blip_Buffer* );
+ enum { osc_count = 8 };
+ void set_output( int index, Blip_Buffer* );
+ void reset();
+ void end_frame( blip_time_t );
+
+ // Read/write data register is at 0x4800
+ enum { data_reg_addr = 0x4800 };
+ void write_data( blip_time_t, int );
+ int read_data();
+
+ // Write-only address register is at 0xF800
+ enum { addr_reg_addr = 0xF800 };
+ void write_addr( int );
+
+ // to do: implement save/restore
+ void save_state( namco_state_t* out ) const;
+ void load_state( namco_state_t const& );
+
+public:
+ Nes_Namco_Apu();
+ BLARGG_DISABLE_NOTHROW
+private:
+ // noncopyable
+ Nes_Namco_Apu( const Nes_Namco_Apu& );
+ Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& );
+
+ struct Namco_Osc {
+ int delay;
+ Blip_Buffer* output;
+ short last_amp;
+ short wave_pos;
+ };
+
+ Namco_Osc oscs [osc_count];
+
+ blip_time_t last_time;
+ int addr_reg;
+
+ enum { reg_count = 0x80 };
+ BOOST::uint8_t reg [reg_count];
+ Blip_Synth_Norm synth;
+
+ BOOST::uint8_t& access();
+ void run_until( blip_time_t );
+};
+/*
+struct namco_state_t
+{
+ BOOST::uint8_t regs [0x80];
+ BOOST::uint8_t addr;
+ BOOST::uint8_t unused;
+ BOOST::uint8_t positions [8];
+ BOOST::uint32_t delays [8];
+};
+*/
+
+inline BOOST::uint8_t& Nes_Namco_Apu::access()
+{
+ int addr = addr_reg & 0x7F;
+ if ( addr_reg & 0x80 )
+ addr_reg = (addr + 1) | 0x80;
+ return reg [addr];
+}
+
+inline void Nes_Namco_Apu::volume( double v ) { synth.volume( 0.10 / osc_count / 15 * v ); }
+
+inline void Nes_Namco_Apu::treble_eq( const blip_eq_t& eq ) { synth.treble_eq( eq ); }
+
+inline void Nes_Namco_Apu::write_addr( int v ) { addr_reg = v; }
+
+inline int Nes_Namco_Apu::read_data() { return access(); }
+
+inline void Nes_Namco_Apu::set_output( int i, Blip_Buffer* buf )
+{
+ assert( (unsigned) i < osc_count );
+ oscs [i].output = buf;
+}
+
+inline void Nes_Namco_Apu::write_data( blip_time_t time, int data )
+{
+ run_until( time );
+ access() = data;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.cpp
new file mode 100644
index 00000000..906955fd
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.cpp
@@ -0,0 +1,578 @@
+// Nes_Snd_Emu 0.2.0-pre. http://www.slack.net/~ant/
+
+#include "Nes_Apu.h"
+
+/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// Nes_Osc
+
+void Nes_Osc::clock_length( int halt_mask )
+{
+ if ( length_counter && !(regs [0] & halt_mask) )
+ length_counter--;
+}
+
+void Nes_Envelope::clock_envelope()
+{
+ int period = regs [0] & 15;
+ if ( reg_written [3] )
+ {
+ reg_written [3] = false;
+ env_delay = period;
+ envelope = 15;
+ }
+ else if ( --env_delay < 0 )
+ {
+ env_delay = period;
+ if ( envelope | (regs [0] & 0x20) )
+ envelope = (envelope - 1) & 15;
+ }
+}
+
+int Nes_Envelope::volume() const
+{
+ return length_counter == 0 ? 0 : (regs [0] & 0x10) ? (regs [0] & 15) : envelope;
+}
+
+// Nes_Square
+
+void Nes_Square::clock_sweep( int negative_adjust )
+{
+ int sweep = regs [1];
+
+ if ( --sweep_delay < 0 )
+ {
+ reg_written [1] = true;
+
+ int period = this->period();
+ int shift = sweep & shift_mask;
+ if ( shift && (sweep & 0x80) && period >= 8 )
+ {
+ int offset = period >> shift;
+
+ if ( sweep & negate_flag )
+ offset = negative_adjust - offset;
+
+ if ( period + offset < 0x800 )
+ {
+ period += offset;
+ // rewrite period
+ regs [2] = period & 0xFF;
+ regs [3] = (regs [3] & ~7) | ((period >> 8) & 7);
+ }
+ }
+ }
+
+ if ( reg_written [1] )
+ {
+ reg_written [1] = false;
+ sweep_delay = (sweep >> 4) & 7;
+ }
+}
+
+// TODO: clean up
+inline Nes_Square::nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_time,
+ nes_time_t timer_period )
+{
+ nes_time_t remain = end_time - time;
+ if ( remain > 0 )
+ {
+ int count = (remain + timer_period - 1) / timer_period;
+ phase = (phase + count) & (phase_range - 1);
+ time += count * timer_period;
+ }
+ return time;
+}
+
+void Nes_Square::run( nes_time_t time, nes_time_t end_time )
+{
+ const int period = this->period();
+ const int timer_period = (period + 1) * 2;
+
+ if ( !output )
+ {
+ delay = maintain_phase( time + delay, end_time, timer_period ) - end_time;
+ return;
+ }
+
+ int offset = period >> (regs [1] & shift_mask);
+ if ( regs [1] & negate_flag )
+ offset = 0;
+
+ const int volume = this->volume();
+ if ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
+ {
+ if ( last_amp )
+ {
+ output->set_modified();
+ synth.offset( time, -last_amp, output );
+ last_amp = 0;
+ }
+
+ time += delay;
+ time = maintain_phase( time, end_time, timer_period );
+ }
+ else
+ {
+ // handle duty select
+ int duty_select = (regs [0] >> 6) & 3;
+ int duty = 1 << duty_select; // 1, 2, 4, 2
+ int amp = 0;
+ if ( duty_select == 3 )
+ {
+ duty = 2; // negated 25%
+ amp = volume;
+ }
+ if ( phase < duty )
+ amp ^= volume;
+
+ output->set_modified();
+ {
+ int delta = update_amp( amp );
+ if ( delta )
+ synth.offset( time, delta, output );
+ }
+
+ time += delay;
+ if ( time < end_time )
+ {
+ Blip_Buffer* const output = this->output;
+ const Synth& synth = this->synth;
+ int delta = amp * 2 - volume;
+ int phase = this->phase;
+
+ do
+ {
+ phase = (phase + 1) & (phase_range - 1);
+ if ( phase == 0 || phase == duty )
+ {
+ delta = -delta;
+ synth.offset_inline( time, delta, output );
+ }
+ time += timer_period;
+ }
+ while ( time < end_time );
+
+ last_amp = (delta + volume) >> 1;
+ this->phase = phase;
+ }
+ }
+
+ delay = time - end_time;
+}
+
+// Nes_Triangle
+
+void Nes_Triangle::clock_linear_counter()
+{
+ if ( reg_written [3] )
+ linear_counter = regs [0] & 0x7F;
+ else if ( linear_counter )
+ linear_counter--;
+
+ if ( !(regs [0] & 0x80) )
+ reg_written [3] = false;
+}
+
+inline int Nes_Triangle::calc_amp() const
+{
+ int amp = phase_range - phase;
+ if ( amp < 0 )
+ amp = phase - (phase_range + 1);
+ return amp;
+}
+
+// TODO: clean up
+inline Nes_Square::nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_time,
+ nes_time_t timer_period )
+{
+ nes_time_t remain = end_time - time;
+ if ( remain > 0 )
+ {
+ int count = (remain + timer_period - 1) / timer_period;
+ phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1);
+ phase++;
+ time += count * timer_period;
+ }
+ return time;
+}
+
+void Nes_Triangle::run( nes_time_t time, nes_time_t end_time )
+{
+ const int timer_period = period() + 1;
+ if ( !output )
+ {
+ time += delay;
+ delay = 0;
+ if ( length_counter && linear_counter && timer_period >= 3 )
+ delay = maintain_phase( time, end_time, timer_period ) - end_time;
+ return;
+ }
+
+ // to do: track phase when period < 3
+ // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
+
+ int delta = update_amp( calc_amp() );
+ if ( delta )
+ {
+ output->set_modified();
+ synth.offset( time, delta, output );
+ }
+
+ time += delay;
+ if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 )
+ {
+ time = end_time;
+ }
+ else if ( time < end_time )
+ {
+ Blip_Buffer* const output = this->output;
+
+ int phase = this->phase;
+ int volume = 1;
+ if ( phase > phase_range )
+ {
+ phase -= phase_range;
+ volume = -volume;
+ }
+ output->set_modified();
+
+ do
+ {
+ if ( --phase == 0 )
+ {
+ phase = phase_range;
+ volume = -volume;
+ }
+ else
+ {
+ synth.offset_inline( time, volume, output );
+ }
+
+ time += timer_period;
+ }
+ while ( time < end_time );
+
+ if ( volume < 0 )
+ phase += phase_range;
+ this->phase = phase;
+ last_amp = calc_amp();
+ }
+ delay = time - end_time;
+}
+
+// Nes_Dmc
+
+void Nes_Dmc::reset()
+{
+ address = 0;
+ dac = 0;
+ buf = 0;
+ bits_remain = 1;
+ bits = 0;
+ buf_full = false;
+ silence = true;
+ next_irq = Nes_Apu::no_irq;
+ irq_flag = false;
+ irq_enabled = false;
+
+ Nes_Osc::reset();
+ period = 0x1AC;
+}
+
+void Nes_Dmc::recalc_irq()
+{
+ nes_time_t irq = Nes_Apu::no_irq;
+ if ( irq_enabled && length_counter )
+ irq = apu->last_dmc_time + delay +
+ ((length_counter - 1) * 8 + bits_remain - 1) * nes_time_t (period) + 1;
+ if ( irq != next_irq )
+ {
+ next_irq = irq;
+ apu->irq_changed();
+ }
+}
+
+int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const
+{
+ if ( last_read )
+ *last_read = time;
+
+ if ( length_counter == 0 )
+ return 0; // not reading
+
+ nes_time_t first_read = next_read_time();
+ nes_time_t avail = time - first_read;
+ if ( avail <= 0 )
+ return 0;
+
+ int count = (avail - 1) / (period * 8) + 1;
+ if ( !(regs [0] & loop_flag) && count > length_counter )
+ count = length_counter;
+
+ if ( last_read )
+ {
+ *last_read = first_read + (count - 1) * (period * 8) + 1;
+ check( *last_read <= time );
+ check( count == count_reads( *last_read, NULL ) );
+ check( count - 1 == count_reads( *last_read - 1, NULL ) );
+ }
+
+ return count;
+}
+
+static short const dmc_period_table [2] [16] = {
+ {428, 380, 340, 320, 286, 254, 226, 214, // NTSC
+ 190, 160, 142, 128, 106, 84, 72, 54},
+
+ {398, 354, 316, 298, 276, 236, 210, 198, // PAL
+ 176, 148, 132, 118, 98, 78, 66, 50}
+};
+
+inline void Nes_Dmc::reload_sample()
+{
+ address = 0x4000 + regs [2] * 0x40;
+ length_counter = regs [3] * 0x10 + 1;
+}
+
+static int const dmc_table [128] =
+{
+ 0, 24, 48, 71, 94, 118, 141, 163, 186, 209, 231, 253, 275, 297, 319, 340,
+ 361, 383, 404, 425, 445, 466, 486, 507, 527, 547, 567, 587, 606, 626, 645, 664,
+ 683, 702, 721, 740, 758, 777, 795, 813, 832, 850, 867, 885, 903, 920, 938, 955,
+ 972, 989,1006,1023,1040,1056,1073,1089,1105,1122,1138,1154,1170,1185,1201,1217,
+1232,1248,1263,1278,1293,1308,1323,1338,1353,1368,1382,1397,1411,1425,1440,1454,
+1468,1482,1496,1510,1523,1537,1551,1564,1578,1591,1604,1618,1631,1644,1657,1670,
+1683,1695,1708,1721,1733,1746,1758,1771,1783,1795,1807,1819,1831,1843,1855,1867,
+1879,1890,1902,1914,1925,1937,1948,1959,1971,1982,1993,2004,2015,2026,2037,2048,
+};
+
+inline int Nes_Dmc::update_amp_nonlinear( int in )
+{
+ if ( !nonlinear )
+ in = dmc_table [in];
+ int delta = in - last_amp;
+ last_amp = in;
+ return delta;
+}
+
+void Nes_Dmc::write_register( int addr, int data )
+{
+ if ( addr == 0 )
+ {
+ period = dmc_period_table [pal_mode] [data & 15];
+ irq_enabled = (data & 0xC0) == 0x80; // enabled only if loop disabled
+ irq_flag &= irq_enabled;
+ recalc_irq();
+ }
+ else if ( addr == 1 )
+ {
+ dac = data & 0x7F;
+ }
+}
+
+void Nes_Dmc::start()
+{
+ reload_sample();
+ fill_buffer();
+ recalc_irq();
+}
+
+void Nes_Dmc::fill_buffer()
+{
+ if ( !buf_full && length_counter )
+ {
+ require( apu->dmc_reader.f ); // dmc_reader must be set
+ buf = apu->dmc_reader.f( apu->dmc_reader.data, 0x8000u + address );
+ address = (address + 1) & 0x7FFF;
+ buf_full = true;
+ if ( --length_counter == 0 )
+ {
+ if ( regs [0] & loop_flag )
+ {
+ reload_sample();
+ }
+ else
+ {
+ apu->osc_enables &= ~0x10;
+ irq_flag = irq_enabled;
+ next_irq = Nes_Apu::no_irq;
+ apu->irq_changed();
+ }
+ }
+ }
+}
+
+void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
+{
+ int delta = update_amp_nonlinear( dac );
+ if ( !output )
+ {
+ silence = true;
+ }
+ else if ( delta )
+ {
+ output->set_modified();
+ synth.offset( time, delta, output );
+ }
+
+ time += delay;
+ if ( time < end_time )
+ {
+ int bits_remain = this->bits_remain;
+ if ( silence && !buf_full )
+ {
+ int count = (end_time - time + period - 1) / period;
+ bits_remain = (bits_remain - 1 + 8 - (count % 8)) % 8 + 1;
+ time += count * period;
+ }
+ else
+ {
+ Blip_Buffer* const output = this->output;
+ const int period = this->period;
+ int bits = this->bits;
+ int dac = this->dac;
+ if ( output )
+ output->set_modified();
+
+ do
+ {
+ if ( !silence )
+ {
+ int step = (bits & 1) * 4 - 2;
+ bits >>= 1;
+ if ( unsigned (dac + step) <= 0x7F )
+ {
+ dac += step;
+ synth.offset_inline( time, update_amp_nonlinear( dac ), output );
+ }
+ }
+
+ time += period;
+
+ if ( --bits_remain == 0 )
+ {
+ bits_remain = 8;
+ if ( !buf_full )
+ {
+ silence = true;
+ }
+ else
+ {
+ silence = false;
+ bits = buf;
+ buf_full = false;
+ if ( !output )
+ silence = true;
+ fill_buffer();
+ }
+ }
+ }
+ while ( time < end_time );
+
+ this->dac = dac;
+ this->bits = bits;
+ }
+ this->bits_remain = bits_remain;
+ }
+ delay = time - end_time;
+}
+
+// Nes_Noise
+
+static short const noise_period_table [16] = {
+ 0x004, 0x008, 0x010, 0x020, 0x040, 0x060, 0x080, 0x0A0,
+ 0x0CA, 0x0FE, 0x17C, 0x1FC, 0x2FA, 0x3F8, 0x7F2, 0xFE4
+};
+
+void Nes_Noise::run( nes_time_t time, nes_time_t end_time )
+{
+ int period = noise_period_table [regs [2] & 15];
+
+ if ( !output )
+ {
+ // TODO: clean up
+ time += delay;
+ delay = time + (end_time - time + period - 1) / period * period - end_time;
+ return;
+ }
+
+
+ const int volume = this->volume();
+ int amp = (noise & 1) ? volume : 0;
+ {
+ int delta = update_amp( amp );
+ if ( delta )
+ {
+ output->set_modified();
+ synth.offset( time, delta, output );
+ }
+ }
+
+ time += delay;
+ if ( time < end_time )
+ {
+ const int mode_flag = 0x80;
+
+ if ( !volume )
+ {
+ // round to next multiple of period
+ time += (end_time - time + period - 1) / period * period;
+
+ // approximate noise cycling while muted, by shuffling up noise register
+ // to do: precise muted noise cycling?
+ if ( !(regs [2] & mode_flag) )
+ {
+ int feedback = (noise << 13) ^ (noise << 14);
+ noise = (feedback & 0x4000) | (noise >> 1);
+ }
+ }
+ else
+ {
+ Blip_Buffer* const output = this->output;
+
+ // using resampled time avoids conversion in synth.offset()
+ blip_resampled_time_t rperiod = output->resampled_duration( period );
+ blip_resampled_time_t rtime = output->resampled_time( time );
+
+ int noise = this->noise;
+ int delta = amp * 2 - volume;
+ const int tap = (regs [2] & mode_flag ? 8 : 13);
+ output->set_modified();
+
+ do
+ {
+ int feedback = (noise << tap) ^ (noise << 14);
+ time += period;
+
+ if ( (noise + 1) & 2 )
+ {
+ // bits 0 and 1 of noise differ
+ delta = -delta;
+ synth.offset_resampled( rtime, delta, output );
+ }
+
+ rtime += rperiod;
+ noise = (feedback & 0x4000) | (noise >> 1);
+ }
+ while ( time < end_time );
+
+ last_amp = (delta + volume) >> 1;
+ this->noise = noise;
+ }
+ }
+
+ delay = time - end_time;
+}
+
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.h
new file mode 100644
index 00000000..f46cec8c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Oscs.h
@@ -0,0 +1,147 @@
+// Private oscillators used by Nes_Apu
+
+// Nes_Snd_Emu 0.2.0-pre
+#ifndef NES_OSCS_H
+#define NES_OSCS_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Nes_Apu;
+
+struct Nes_Osc
+{
+ typedef int nes_time_t;
+
+ unsigned char regs [4];
+ bool reg_written [4];
+ Blip_Buffer* output;
+ int length_counter;// length counter (0 if unused by oscillator)
+ int delay; // delay until next (potential) transition
+ int last_amp; // last amplitude oscillator was outputting
+
+ void clock_length( int halt_mask );
+ int period() const {
+ return (regs [3] & 7) * 0x100 + (regs [2] & 0xFF);
+ }
+ void reset() {
+ delay = 0;
+ last_amp = 0;
+ }
+ int update_amp( int amp ) {
+ int delta = amp - last_amp;
+ last_amp = amp;
+ return delta;
+ }
+};
+
+struct Nes_Envelope : Nes_Osc
+{
+ int envelope;
+ int env_delay;
+
+ void clock_envelope();
+ int volume() const;
+ void reset() {
+ envelope = 0;
+ env_delay = 0;
+ Nes_Osc::reset();
+ }
+};
+
+// Nes_Square
+struct Nes_Square : Nes_Envelope
+{
+ enum { negate_flag = 0x08 };
+ enum { shift_mask = 0x07 };
+ enum { phase_range = 8 };
+ int phase;
+ int sweep_delay;
+
+ typedef Blip_Synth_Norm Synth;
+ Synth const& synth; // shared between squares
+
+ Nes_Square( Synth const* s ) : synth( *s ) { }
+
+ void clock_sweep( int adjust );
+ void run( nes_time_t, nes_time_t );
+ void reset() {
+ sweep_delay = 0;
+ Nes_Envelope::reset();
+ }
+ nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time,
+ nes_time_t timer_period );
+};
+
+// Nes_Triangle
+struct Nes_Triangle : Nes_Osc
+{
+ enum { phase_range = 16 };
+ int phase;
+ int linear_counter;
+ Blip_Synth_Fast synth;
+
+ int calc_amp() const;
+ void run( nes_time_t, nes_time_t );
+ void clock_linear_counter();
+ void reset() {
+ linear_counter = 0;
+ phase = 1;
+ Nes_Osc::reset();
+ }
+ nes_time_t maintain_phase( nes_time_t time, nes_time_t end_time,
+ nes_time_t timer_period );
+};
+
+// Nes_Noise
+struct Nes_Noise : Nes_Envelope
+{
+ int noise;
+ Blip_Synth_Fast synth;
+
+ void run( nes_time_t, nes_time_t );
+ void reset() {
+ noise = 1 << 14;
+ Nes_Envelope::reset();
+ }
+};
+
+// Nes_Dmc
+struct Nes_Dmc : Nes_Osc
+{
+ int address; // address of next byte to read
+ int period;
+ //int length_counter; // bytes remaining to play (already defined in Nes_Osc)
+ int buf;
+ int bits_remain;
+ int bits;
+ bool buf_full;
+ bool silence;
+
+ enum { loop_flag = 0x40 };
+
+ int dac;
+
+ nes_time_t next_irq;
+ bool irq_enabled;
+ bool irq_flag;
+ bool pal_mode;
+ bool nonlinear;
+
+ Nes_Apu* apu;
+
+ Blip_Synth_Fast synth;
+
+ int update_amp_nonlinear( int dac_in );
+ void start();
+ void write_register( int, int );
+ void run( nes_time_t, nes_time_t );
+ void recalc_irq();
+ void fill_buffer();
+ void reload_sample();
+ void reset();
+ int count_reads( nes_time_t, nes_time_t* ) const;
+ nes_time_t next_read_time() const;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.cpp
new file mode 100644
index 00000000..0799225c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.cpp
@@ -0,0 +1,216 @@
+// Nes_Snd_Emu 0.2.0-pre. http://www.slack.net/~ant/
+
+#include "Nes_Vrc6_Apu.h"
+
+/* Copyright (C) 2003-2006 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void Nes_Vrc6_Apu::set_output( Blip_Buffer* buf )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ set_output( i, buf );
+}
+
+void Nes_Vrc6_Apu::reset()
+{
+ last_time = 0;
+ for ( int i = 0; i < osc_count; i++ )
+ {
+ Vrc6_Osc& osc = oscs [i];
+ for ( int j = 0; j < reg_count; j++ )
+ osc.regs [j] = 0;
+ osc.delay = 0;
+ osc.last_amp = 0;
+ osc.phase = 1;
+ osc.amp = 0;
+ }
+}
+
+Nes_Vrc6_Apu::Nes_Vrc6_Apu()
+{
+ set_output( NULL );
+ volume( 1.0 );
+ reset();
+}
+
+void Nes_Vrc6_Apu::run_until( blip_time_t time )
+{
+ require( time >= last_time );
+ run_square( oscs [0], time );
+ run_square( oscs [1], time );
+ run_saw( time );
+ last_time = time;
+}
+
+void Nes_Vrc6_Apu::write_osc( blip_time_t time, int osc_index, int reg, int data )
+{
+ require( (unsigned) osc_index < osc_count );
+ require( (unsigned) reg < reg_count );
+
+ run_until( time );
+ oscs [osc_index].regs [reg] = data;
+}
+
+void Nes_Vrc6_Apu::end_frame( blip_time_t time )
+{
+ if ( time > last_time )
+ run_until( time );
+
+ assert( last_time >= time );
+ last_time -= time;
+}
+
+void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const
+{
+ assert( sizeof (vrc6_apu_state_t) == 20 );
+ out->saw_amp = oscs [2].amp;
+ for ( int i = 0; i < osc_count; i++ )
+ {
+ Vrc6_Osc const& osc = oscs [i];
+ for ( int r = 0; r < reg_count; r++ )
+ out->regs [i] [r] = osc.regs [r];
+
+ out->delays [i] = osc.delay;
+ out->phases [i] = osc.phase;
+ }
+}
+
+void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in )
+{
+ reset();
+ oscs [2].amp = in.saw_amp;
+ for ( int i = 0; i < osc_count; i++ )
+ {
+ Vrc6_Osc& osc = oscs [i];
+ for ( int r = 0; r < reg_count; r++ )
+ osc.regs [r] = in.regs [i] [r];
+
+ osc.delay = in.delays [i];
+ osc.phase = in.phases [i];
+ }
+ if ( !oscs [2].phase )
+ oscs [2].phase = 1;
+}
+
+void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time )
+{
+ Blip_Buffer* output = osc.output;
+ if ( !output )
+ return;
+
+ int volume = osc.regs [0] & 15;
+ if ( !(osc.regs [2] & 0x80) )
+ volume = 0;
+
+ int gate = osc.regs [0] & 0x80;
+ int duty = ((osc.regs [0] >> 4) & 7) + 1;
+ int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp;
+ blip_time_t time = last_time;
+ if ( delta )
+ {
+ osc.last_amp += delta;
+ output->set_modified();
+ square_synth.offset( time, delta, output );
+ }
+
+ time += osc.delay;
+ osc.delay = 0;
+ int period = osc.period();
+ if ( volume && !gate && period > 4 )
+ {
+ if ( time < end_time )
+ {
+ int phase = osc.phase;
+ output->set_modified();
+
+ do
+ {
+ phase++;
+ if ( phase == 16 )
+ {
+ phase = 0;
+ osc.last_amp = volume;
+ square_synth.offset( time, volume, output );
+ }
+ if ( phase == duty )
+ {
+ osc.last_amp = 0;
+ square_synth.offset( time, -volume, output );
+ }
+ time += period;
+ }
+ while ( time < end_time );
+
+ osc.phase = phase;
+ }
+ osc.delay = time - end_time;
+ }
+}
+
+void Nes_Vrc6_Apu::run_saw( blip_time_t end_time )
+{
+ Vrc6_Osc& osc = oscs [2];
+ Blip_Buffer* output = osc.output;
+ if ( !output )
+ return;
+ output->set_modified();
+
+ int amp = osc.amp;
+ int amp_step = osc.regs [0] & 0x3F;
+ blip_time_t time = last_time;
+ int last_amp = osc.last_amp;
+ if ( !(osc.regs [2] & 0x80) || !(amp_step | amp) )
+ {
+ osc.delay = 0;
+ int delta = (amp >> 3) - last_amp;
+ last_amp = amp >> 3;
+ saw_synth.offset( time, delta, output );
+ }
+ else
+ {
+ time += osc.delay;
+ if ( time < end_time )
+ {
+ int period = osc.period() * 2;
+ int phase = osc.phase;
+
+ do
+ {
+ if ( --phase == 0 )
+ {
+ phase = 7;
+ amp = 0;
+ }
+
+ int delta = (amp >> 3) - last_amp;
+ if ( delta )
+ {
+ last_amp = amp >> 3;
+ saw_synth.offset( time, delta, output );
+ }
+
+ time += period;
+ amp = (amp + amp_step) & 0xFF;
+ }
+ while ( time < end_time );
+
+ osc.phase = phase;
+ osc.amp = amp;
+ }
+
+ osc.delay = time - end_time;
+ }
+
+ osc.last_amp = last_amp;
+}
+
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.h
new file mode 100644
index 00000000..123364d6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc6_Apu.h
@@ -0,0 +1,95 @@
+// Konami VRC6 sound chip emulator
+
+// Nes_Snd_Emu 0.2.0-pre
+#ifndef NES_VRC6_APU_H
+#define NES_VRC6_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+struct vrc6_apu_state_t;
+
+class Nes_Vrc6_Apu {
+public:
+ // See Nes_Apu.h for reference
+ void reset();
+ void volume( double );
+ void treble_eq( blip_eq_t const& );
+ void set_output( Blip_Buffer* );
+ enum { osc_count = 3 };
+ void set_output( int index, Blip_Buffer* );
+ void end_frame( blip_time_t );
+ void save_state( vrc6_apu_state_t* ) const;
+ void load_state( vrc6_apu_state_t const& );
+
+ // Oscillator 0 write-only registers are at $9000-$9002
+ // Oscillator 1 write-only registers are at $A000-$A002
+ // Oscillator 2 write-only registers are at $B000-$B002
+ enum { reg_count = 3 };
+ enum { base_addr = 0x9000 };
+ enum { addr_step = 0x1000 };
+ void write_osc( blip_time_t, int osc, int reg, int data );
+
+public:
+ Nes_Vrc6_Apu();
+ BLARGG_DISABLE_NOTHROW
+private:
+ // noncopyable
+ Nes_Vrc6_Apu( const Nes_Vrc6_Apu& );
+ Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& );
+
+ struct Vrc6_Osc
+ {
+ BOOST::uint8_t regs [3];
+ Blip_Buffer* output;
+ int delay;
+ int last_amp;
+ int phase;
+ int amp; // only used by saw
+
+ int period() const
+ {
+ return (regs [2] & 0x0F) * 0x100 + regs [1] + 1;
+ }
+ };
+
+ Vrc6_Osc oscs [osc_count];
+ blip_time_t last_time;
+
+ Blip_Synth_Fast saw_synth;
+ Blip_Synth_Norm square_synth;
+
+ void run_until( blip_time_t );
+ void run_square( Vrc6_Osc& osc, blip_time_t );
+ void run_saw( blip_time_t );
+};
+
+struct vrc6_apu_state_t
+{
+ BOOST::uint8_t regs [3] [3];
+ BOOST::uint8_t saw_amp;
+ BOOST::uint16_t delays [3];
+ BOOST::uint8_t phases [3];
+ BOOST::uint8_t unused;
+};
+
+inline void Nes_Vrc6_Apu::set_output( int i, Blip_Buffer* buf )
+{
+ assert( (unsigned) i < osc_count );
+ oscs [i].output = buf;
+}
+
+inline void Nes_Vrc6_Apu::volume( double v )
+{
+ double const factor = 0.0967 * 2;
+ saw_synth.volume( factor / 31 * v );
+ square_synth.volume( factor * 0.5 / 15 * v );
+}
+
+inline void Nes_Vrc6_Apu::treble_eq( blip_eq_t const& eq )
+{
+ saw_synth.treble_eq( eq );
+ square_synth.treble_eq( eq );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp
new file mode 100644
index 00000000..033061d6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.cpp
@@ -0,0 +1,206 @@
+#include "Nes_Vrc7_Apu.h"
+
+#include "ym2413.h"
+#include <string.h>
+
+#include "blargg_source.h"
+
+int const period = 36; // NES CPU clocks per FM clock
+
+Nes_Vrc7_Apu::Nes_Vrc7_Apu()
+{
+ opll = 0;
+}
+
+blargg_err_t Nes_Vrc7_Apu::init()
+{
+ CHECK_ALLOC( opll = ym2413_init( 3579545, 3579545 / 72, 1 ) );
+
+ set_output( 0 );
+ volume( 1.0 );
+ reset();
+ return 0;
+}
+
+Nes_Vrc7_Apu::~Nes_Vrc7_Apu()
+{
+ if ( opll )
+ ym2413_shutdown( opll );
+}
+
+void Nes_Vrc7_Apu::set_output( Blip_Buffer* buf )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ oscs [i].output = buf;
+ output_changed();
+}
+
+void Nes_Vrc7_Apu::output_changed()
+{
+ mono.output = oscs [0].output;
+ for ( int i = osc_count; --i; )
+ {
+ if ( mono.output != oscs [i].output )
+ {
+ mono.output = 0;
+ break;
+ }
+ }
+
+ if ( mono.output )
+ {
+ for ( int i = osc_count; --i; )
+ {
+ mono.last_amp += oscs [i].last_amp;
+ oscs [i].last_amp = 0;
+ }
+ }
+}
+
+void Nes_Vrc7_Apu::reset()
+{
+ addr = 0;
+ next_time = 0;
+ mono.last_amp = 0;
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Vrc7_Osc& osc = oscs [i];
+ osc.last_amp = 0;
+ for ( int j = 0; j < 3; ++j )
+ osc.regs [j] = 0;
+ }
+
+ ym2413_reset_chip( opll );
+}
+
+void Nes_Vrc7_Apu::write_reg( int data )
+{
+ addr = data;
+}
+
+void Nes_Vrc7_Apu::write_data( blip_time_t time, int data )
+{
+ int type = (addr >> 4) - 1;
+ int chan = addr & 15;
+ if ( (unsigned) type < 3 && chan < osc_count )
+ oscs [chan].regs [type] = data;
+
+ if ( time > next_time )
+ run_until( time );
+ ym2413_write( opll, 0, addr );
+ ym2413_write( opll, 1, data );
+}
+
+void Nes_Vrc7_Apu::end_frame( blip_time_t time )
+{
+ if ( time > next_time )
+ run_until( time );
+
+ next_time -= time;
+ assert( next_time >= 0 );
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Blip_Buffer* output = oscs [i].output;
+ if ( output )
+ output->set_modified();
+ }
+}
+
+void Nes_Vrc7_Apu::save_snapshot( vrc7_snapshot_t* out ) const
+{
+ out->latch = addr;
+ out->delay = next_time;
+ for ( int i = osc_count; --i >= 0; )
+ {
+ for ( int j = 0; j < 3; ++j )
+ out->regs [i] [j] = oscs [i].regs [j];
+ }
+ memcpy( out->inst, ym2413_get_inst0( opll ), 8 );
+}
+
+void Nes_Vrc7_Apu::load_snapshot( vrc7_snapshot_t const& in )
+{
+ assert( offsetof (vrc7_snapshot_t,delay) == 28 - 1 );
+
+ reset();
+ next_time = in.delay;
+ write_reg( in.latch );
+ int i;
+ for ( i = 0; i < osc_count; ++i )
+ {
+ for ( int j = 0; j < 3; ++j )
+ oscs [i].regs [j] = in.regs [i] [j];
+ }
+
+ for ( i = 0; i < 8; ++i )
+ {
+ ym2413_write( opll, 0, i );
+ ym2413_write( opll, 1, in.inst [i] );
+ }
+
+ for ( i = 0; i < 3; ++i )
+ {
+ for ( int j = 0; j < 6; ++j )
+ {
+ ym2413_write( opll, 0, 0x10 + i * 0x10 + j );
+ ym2413_write( opll, 1, oscs [j].regs [i] );
+ }
+ }
+}
+
+void Nes_Vrc7_Apu::run_until( blip_time_t end_time )
+{
+ require( end_time > next_time );
+
+ blip_time_t time = next_time;
+ void* opll = this->opll; // cache
+ Blip_Buffer* const mono_output = mono.output;
+ if ( mono_output )
+ {
+ // optimal case
+ do
+ {
+ ym2413_advance_lfo( opll );
+ int amp = 0;
+ for ( int i = 0; i < osc_count; i++ )
+ amp += ym2413_calcch( opll, i );
+ ym2413_advance( opll );
+ int delta = amp - mono.last_amp;
+ if ( delta )
+ {
+ mono.last_amp = amp;
+ synth.offset_inline( time, delta, mono_output );
+ }
+ time += period;
+ }
+ while ( time < end_time );
+ }
+ else
+ {
+ mono.last_amp = 0;
+ do
+ {
+ ym2413_advance_lfo( opll );
+ for ( int i = 0; i < osc_count; ++i )
+ {
+ Vrc7_Osc& osc = oscs [i];
+ if ( osc.output )
+ {
+ int amp = ym2413_calcch( opll, i );
+ int delta = amp - osc.last_amp;
+ if ( delta )
+ {
+ osc.last_amp = amp;
+ synth.offset( time, delta, osc.output );
+ }
+ }
+ }
+ ym2413_advance( opll );
+ time += period;
+ }
+ while ( time < end_time );
+ }
+ next_time = time;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.h
new file mode 100644
index 00000000..60ea1631
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nes_Vrc7_Apu.h
@@ -0,0 +1,80 @@
+// Konami VRC7 sound chip emulator
+
+#ifndef NES_VRC7_APU_H
+#define NES_VRC7_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+struct vrc7_snapshot_t;
+
+class Nes_Vrc7_Apu {
+public:
+ blargg_err_t init();
+
+ // See Nes_Apu.h for reference
+ void reset();
+ void volume( double );
+ void treble_eq( blip_eq_t const& );
+ void set_output( Blip_Buffer* );
+ enum { osc_count = 6 };
+ void set_output( int index, Blip_Buffer* );
+ void end_frame( blip_time_t );
+ void save_snapshot( vrc7_snapshot_t* ) const;
+ void load_snapshot( vrc7_snapshot_t const& );
+
+ void write_reg( int reg );
+ void write_data( blip_time_t, int data );
+
+public:
+ Nes_Vrc7_Apu();
+ ~Nes_Vrc7_Apu();
+ BLARGG_DISABLE_NOTHROW
+private:
+ // noncopyable
+ Nes_Vrc7_Apu( const Nes_Vrc7_Apu& );
+ Nes_Vrc7_Apu& operator = ( const Nes_Vrc7_Apu& );
+
+ struct Vrc7_Osc
+ {
+ BOOST::uint8_t regs [3];
+ Blip_Buffer* output;
+ int last_amp;
+ };
+
+ Vrc7_Osc oscs [osc_count];
+ void* opll;
+ int addr;
+ blip_time_t next_time;
+ struct {
+ Blip_Buffer* output;
+ int last_amp;
+ } mono;
+
+ Blip_Synth_Fast synth;
+
+ void run_until( blip_time_t );
+ void output_changed();
+};
+
+struct vrc7_snapshot_t
+{
+ BOOST::uint8_t latch;
+ BOOST::uint8_t inst [8];
+ BOOST::uint8_t regs [6] [3];
+ BOOST::uint8_t delay;
+};
+
+inline void Nes_Vrc7_Apu::set_output( int i, Blip_Buffer* buf )
+{
+ assert( (unsigned) i < osc_count );
+ oscs [i].output = buf;
+ output_changed();
+}
+
+// DB2LIN_AMP_BITS == 11, * 2
+inline void Nes_Vrc7_Apu::volume( double v ) { synth.volume( 1.0 / 3 / 4096 * v ); }
+
+inline void Nes_Vrc7_Apu::treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.cpp
new file mode 100644
index 00000000..f0822250
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.cpp
@@ -0,0 +1,302 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nsf_Core.h"
+
+#include "blargg_endian.h"
+
+#if !NSF_EMU_APU_ONLY
+ #include "Nes_Namco_Apu.h"
+ #include "Nes_Vrc6_Apu.h"
+ #include "Nes_Fme7_Apu.h"
+ #include "Nes_Fds_Apu.h"
+ #include "Nes_Mmc5_Apu.h"
+ #include "Nes_Vrc7_Apu.h"
+#endif
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Nsf_Core::Nsf_Core()
+{
+ fds = NULL;
+ fme7 = NULL;
+ mmc5 = NULL;
+ namco = NULL;
+ vrc6 = NULL;
+ vrc7 = NULL;
+}
+
+Nsf_Core::~Nsf_Core()
+{
+ unload();
+}
+
+void Nsf_Core::unload()
+{
+#if !NSF_EMU_APU_ONLY
+ delete fds;
+ fds = NULL;
+
+ delete fme7;
+ fme7 = NULL;
+
+ delete namco;
+ namco = NULL;
+
+ delete mmc5;
+ mmc5 = NULL;
+
+ delete vrc6;
+ vrc6 = NULL;
+
+ delete vrc7;
+ vrc7 = NULL;
+#endif
+
+ Nsf_Impl::unload();
+}
+
+void Nsf_Core::set_tempo( double t )
+{
+ set_play_period( (int) (header().play_period() / t) );
+ nes_apu()->set_tempo( t );
+#if !NSF_EMU_APU_ONLY
+ if ( fds )
+ fds->set_tempo( t );
+#endif
+}
+
+blargg_err_t Nsf_Core::post_load()
+{
+ int chip_flags = header().chip_flags;
+
+ #if !NSF_EMU_APU_ONLY
+ if ( chip_flags & header_t::fds_mask )
+ CHECK_ALLOC( fds = BLARGG_NEW Nes_Fds_Apu );
+
+ if ( chip_flags & header_t::fme7_mask )
+ CHECK_ALLOC( fme7 = BLARGG_NEW Nes_Fme7_Apu );
+
+ if ( chip_flags & header_t::mmc5_mask )
+ CHECK_ALLOC( mmc5 = BLARGG_NEW Nes_Mmc5_Apu );
+
+ if ( chip_flags & header_t::namco_mask )
+ CHECK_ALLOC( namco = BLARGG_NEW Nes_Namco_Apu );
+
+ if ( chip_flags & header_t::vrc6_mask )
+ CHECK_ALLOC( vrc6 = BLARGG_NEW Nes_Vrc6_Apu );
+
+ if ( chip_flags & header_t::vrc7_mask )
+ {
+ #if NSF_EMU_NO_VRC7
+ chip_flags = ~chips_mask; // give warning rather than error
+ #else
+ CHECK_ALLOC( vrc7 = BLARGG_NEW Nes_Vrc7_Apu );
+ RETURN_ERR( vrc7->init() );
+ #endif
+ }
+ #endif
+
+ set_tempo( 1.0 );
+
+ if ( chip_flags & ~chips_mask )
+ set_warning( "Uses unsupported audio expansion hardware" );
+
+ return Nsf_Impl::post_load();
+}
+
+int Nsf_Core::cpu_read( addr_t addr )
+{
+ #if !NSF_EMU_APU_ONLY
+ {
+ if ( addr == Nes_Namco_Apu::data_reg_addr && namco )
+ return namco->read_data();
+
+ if ( (unsigned) (addr - Nes_Fds_Apu::io_addr) < Nes_Fds_Apu::io_size && fds )
+ return fds->read( time(), addr );
+
+ int i = addr - 0x5C00;
+ if ( (unsigned) i < mmc5->exram_size && mmc5 )
+ return mmc5->exram [i];
+
+ int m = addr - 0x5205;
+ if ( (unsigned) m < 2 && mmc5 )
+ return (mmc5_mul [0] * mmc5_mul [1]) >> (m * 8) & 0xFF;
+ }
+ #endif
+
+ return Nsf_Impl::cpu_read( addr );
+}
+
+int Nsf_Core::unmapped_read( addr_t addr )
+{
+ switch ( addr )
+ {
+ case 0x2002:
+ case 0x4016:
+ case 0x4017:
+ return addr >> 8;
+ }
+
+ return Nsf_Impl::unmapped_read( addr );
+}
+
+void Nsf_Core::cpu_write( addr_t addr, int data )
+{
+ #if !NSF_EMU_APU_ONLY
+ {
+ if ( (unsigned) (addr - fds->io_addr) < fds->io_size && fds )
+ {
+ fds->write( time(), addr, data );
+ return;
+ }
+
+ if ( namco )
+ {
+ if ( addr == namco->addr_reg_addr )
+ {
+ namco->write_addr( data );
+ return;
+ }
+
+ if ( addr == namco->data_reg_addr )
+ {
+ namco->write_data( time(), data );
+ return;
+ }
+ }
+
+ if ( vrc6 )
+ {
+ int reg = addr & (vrc6->addr_step - 1);
+ int osc = (unsigned) (addr - vrc6->base_addr) / vrc6->addr_step;
+ if ( (unsigned) osc < vrc6->osc_count && (unsigned) reg < vrc6->reg_count )
+ {
+ vrc6->write_osc( time(), osc, reg, data );
+ return;
+ }
+ }
+
+ if ( addr >= fme7->latch_addr && fme7 )
+ {
+ switch ( addr & fme7->addr_mask )
+ {
+ case Nes_Fme7_Apu::latch_addr:
+ fme7->write_latch( data );
+ return;
+
+ case Nes_Fme7_Apu::data_addr:
+ fme7->write_data( time(), data );
+ return;
+ }
+ }
+
+ if ( mmc5 )
+ {
+ if ( (unsigned) (addr - mmc5->regs_addr) < mmc5->regs_size )
+ {
+ mmc5->write_register( time(), addr, data );
+ return;
+ }
+
+ int m = addr - 0x5205;
+ if ( (unsigned) m < 2 )
+ {
+ mmc5_mul [m] = data;
+ return;
+ }
+
+ int i = addr - 0x5C00;
+ if ( (unsigned) i < mmc5->exram_size )
+ {
+ mmc5->exram [i] = data;
+ return;
+ }
+ }
+
+ if ( vrc7 )
+ {
+ if ( addr == 0x9010 )
+ {
+ vrc7->write_reg( data );
+ return;
+ }
+
+ if ( (unsigned) (addr - 0x9028) <= 0x08 )
+ {
+ vrc7->write_data( time(), data );
+ return;
+ }
+ }
+ }
+ #endif
+
+ return Nsf_Impl::cpu_write( addr, data );
+}
+
+void Nsf_Core::unmapped_write( addr_t addr, int data )
+{
+ switch ( addr )
+ {
+ case 0x8000: // some write to $8000 and $8001 repeatedly
+ case 0x8001:
+ case 0x4800: // probably namco sound mistakenly turned on in MCK
+ case 0xF800:
+ case 0xFFF8: // memory mapper?
+ return;
+ }
+
+ if ( mmc5 && addr == 0x5115 ) return;
+
+ // FDS memory
+ if ( fds && (unsigned) (addr - 0x8000) < 0x6000 ) return;
+
+ Nsf_Impl::unmapped_write( addr, data );
+}
+
+blargg_err_t Nsf_Core::start_track( int track )
+{
+ #if !NSF_EMU_APU_ONLY
+ if ( mmc5 )
+ {
+ mmc5_mul [0] = 0;
+ mmc5_mul [1] = 0;
+ memset( mmc5->exram, 0, mmc5->exram_size );
+ }
+ #endif
+
+ #if !NSF_EMU_APU_ONLY
+ if ( fds ) fds ->reset();
+ if ( fme7 ) fme7 ->reset();
+ if ( mmc5 ) mmc5 ->reset();
+ if ( namco ) namco->reset();
+ if ( vrc6 ) vrc6 ->reset();
+ if ( vrc7 ) vrc7 ->reset();
+ #endif
+
+ return Nsf_Impl::start_track( track );
+}
+
+void Nsf_Core::end_frame( time_t end )
+{
+ Nsf_Impl::end_frame( end );
+
+ #if !NSF_EMU_APU_ONLY
+ if ( fds ) fds ->end_frame( end );
+ if ( fme7 ) fme7 ->end_frame( end );
+ if ( mmc5 ) mmc5 ->end_frame( end );
+ if ( namco ) namco->end_frame( end );
+ if ( vrc6 ) vrc6 ->end_frame( end );
+ if ( vrc7 ) vrc7 ->end_frame( end );
+ #endif
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.h
new file mode 100644
index 00000000..957efda7
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Core.h
@@ -0,0 +1,68 @@
+// Loads NSF file and emulates CPU and sound chips
+
+// Game_Music_Emu 0.6-pre
+#ifndef NSF_CORE_H
+#define NSF_CORE_H
+
+#include "Nsf_Impl.h"
+
+class Nes_Namco_Apu;
+class Nes_Vrc6_Apu;
+class Nes_Fme7_Apu;
+class Nes_Mmc5_Apu;
+class Nes_Vrc7_Apu;
+class Nes_Fds_Apu;
+
+class Nsf_Core : public Nsf_Impl {
+public:
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ // Loading a file resets tempo to 1.0.
+ void set_tempo( double );
+
+ // Pointer to sound chip, or NULL if not used by current file.
+ // Must be assigned to a Blip_Buffer to get any sound.
+ Nes_Fds_Apu * fds_apu () { return fds; }
+ Nes_Fme7_Apu * fme7_apu () { return fme7; }
+ Nes_Mmc5_Apu * mmc5_apu () { return mmc5; }
+ Nes_Namco_Apu* namco_apu() { return namco; }
+ Nes_Vrc6_Apu * vrc6_apu () { return vrc6; }
+ Nes_Vrc7_Apu * vrc7_apu () { return vrc7; }
+
+ // Mask for which chips are supported
+ #if NSF_EMU_APU_ONLY
+ enum { chips_mask = 0 };
+ #else
+ enum { chips_mask = header_t::all_mask };
+ #endif
+
+protected:
+ virtual int unmapped_read( addr_t );
+ virtual void unmapped_write( addr_t, int data );
+
+
+// Implementation
+public:
+ Nsf_Core();
+ ~Nsf_Core();
+ virtual void unload();
+ virtual blargg_err_t start_track( int );
+ virtual void end_frame( time_t );
+
+protected:
+ virtual blargg_err_t post_load();
+ virtual int cpu_read( addr_t );
+ virtual void cpu_write( addr_t, int );
+
+private:
+ byte mmc5_mul [2];
+
+ Nes_Fds_Apu* fds;
+ Nes_Fme7_Apu* fme7;
+ Nes_Mmc5_Apu* mmc5;
+ Nes_Namco_Apu* namco;
+ Nes_Vrc6_Apu* vrc6;
+ Nes_Vrc7_Apu* vrc7;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Cpu.cpp
new file mode 100644
index 00000000..296c2f1c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Cpu.cpp
@@ -0,0 +1,116 @@
+// Normal CPU for NSF emulator
+
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nsf_Impl.h"
+
+#include "blargg_endian.h"
+
+#ifdef BLARGG_DEBUG_H
+ //#define CPU_LOG_START 1000000
+ //#include "nes_cpu_log.h"
+ #undef LOG_MEM
+#endif
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifndef LOG_MEM
+ #define LOG_MEM( addr, str, data ) data
+#endif
+
+int Nsf_Impl::read_mem( addr_t addr )
+{
+ int result = low_ram [addr & (low_ram_size-1)]; // also handles wrap-around
+ if ( addr & 0xE000 )
+ {
+ result = *cpu.get_code( addr );
+ if ( addr < sram_addr )
+ {
+ if ( addr == apu.status_addr )
+ result = apu.read_status( time() );
+ else
+ result = cpu_read( addr );
+ }
+ }
+ return LOG_MEM( addr, ">", result );
+}
+
+void Nsf_Impl::write_mem( addr_t addr, int data )
+{
+ (void) LOG_MEM( addr, "<", data );
+
+ int offset = addr - sram_addr;
+ if ( (unsigned) offset < sram_size )
+ {
+ sram() [offset] = data;
+ }
+ else
+ {
+ // after sram because CPU handles most low_ram accesses internally already
+ int temp = addr & (low_ram_size-1); // also handles wrap-around
+ if ( !(addr & 0xE000) )
+ {
+ low_ram [temp] = data;
+ }
+ else
+ {
+ int bank = addr - banks_addr;
+ if ( (unsigned) bank < bank_count )
+ {
+ write_bank( bank, data );
+ }
+ else if ( (unsigned) (addr - apu.io_addr) < apu.io_size )
+ {
+ apu.write_register( time(), addr, data );
+ }
+ else
+ {
+ #if !NSF_EMU_APU_ONLY
+ // 0x8000-0xDFFF is writable
+ int i = addr - 0x8000;
+ if ( (unsigned) i < fdsram_size && fds_enabled() )
+ fdsram() [i] = data;
+ else
+ #endif
+ cpu_write( addr, data );
+ }
+ }
+ }
+}
+
+#define READ_LOW( addr ) (LOG_MEM( addr, ">", low_ram [addr] ))
+#define WRITE_LOW( addr, data ) (LOG_MEM( addr, "<", low_ram [addr] = data ))
+
+#define CAN_WRITE_FAST( addr ) (addr < low_ram_size)
+#define WRITE_FAST WRITE_LOW
+
+// addr < 0x2000 || addr >= 0x8000
+#define CAN_READ_FAST( addr ) ((addr ^ 0x8000) < 0xA000)
+#define READ_FAST( addr, out ) (LOG_MEM( addr, ">", out = READ_CODE( addr ) ))
+
+#define READ_MEM( addr ) read_mem( addr )
+#define WRITE_MEM( addr, data ) write_mem( addr, data )
+
+#define CPU cpu
+
+#define CPU_BEGIN \
+bool Nsf_Impl::run_cpu_until( time_t end )\
+{\
+ cpu.set_end_time( end );\
+ if ( *cpu.get_code( cpu.r.pc ) != cpu.halt_opcode )\
+ {
+ #include "Nes_Cpu_run.h"
+ }
+ return cpu.time_past_end() < 0;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.cpp
new file mode 100644
index 00000000..0c854438
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.cpp
@@ -0,0 +1,314 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nsf_Emu.h"
+
+#if !NSF_EMU_APU_ONLY
+ #include "Nes_Namco_Apu.h"
+ #include "Nes_Vrc6_Apu.h"
+ #include "Nes_Fme7_Apu.h"
+ #include "Nes_Fds_Apu.h"
+ #include "Nes_Mmc5_Apu.h"
+ #include "Nes_Vrc7_Apu.h"
+#endif
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Nsf_Emu::equalizer_t const Nsf_Emu::nes_eq = { -1.0, 80 };
+Nsf_Emu::equalizer_t const Nsf_Emu::famicom_eq = { -15.0, 80 };
+
+Nsf_Emu::Nsf_Emu()
+{
+ set_type( gme_nsf_type );
+ set_silence_lookahead( 6 );
+ set_gain( 1.4 );
+ set_equalizer( nes_eq );
+}
+
+Nsf_Emu::~Nsf_Emu()
+{
+ unload();
+}
+
+void Nsf_Emu::unload()
+{
+ core_.unload();
+ 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 )
+ Music_Emu::copy_field_( out->system, "Famicom" );
+}
+
+blargg_err_t Nsf_Emu::track_info_( track_info_t* out, int ) const
+{
+ copy_nsf_fields( header(), out );
+ return blargg_ok;
+}
+
+static blargg_err_t check_nsf_header( Nsf_Emu::header_t const& h )
+{
+ if ( !h.valid_tag() )
+ return blargg_err_file_type;
+ return blargg_ok;
+}
+
+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, h.size );
+ if ( err )
+ return (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err);
+
+ if ( h.vers != 1 )
+ set_warning( "Unknown file version" );
+
+ int unsupported_chips = ~Nsf_Core::chips_mask;
+ #if NSF_EMU_NO_VRC7
+ unsupported_chips |= Nsf_Emu::header_t::vrc7_mask;
+ #endif
+ if ( h.chip_flags & unsupported_chips )
+ 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 blargg_ok;
+ }
+};
+
+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 )
+{
+ core_.set_tempo( t );
+}
+
+void Nsf_Emu::append_voices( const char* const names [], int const types [], int count )
+{
+ assert( voice_count_ + count < max_voices );
+ for ( int i = 0; i < count; i++ )
+ {
+ voice_names_ [voice_count_ + i] = names [i];
+ voice_types_ [voice_count_ + i] = types [i];
+ }
+ voice_count_ += count;
+ set_voice_count( voice_count_ );
+ set_voice_types( voice_types_ );
+}
+
+blargg_err_t Nsf_Emu::init_sound()
+{
+ voice_count_ = 0;
+ set_voice_names( voice_names_ );
+
+ {
+ int const count = Nes_Apu::osc_count;
+ static const char* const names [Nes_Apu::osc_count] = {
+ "Square 1", "Square 2", "Triangle", "Noise", "DMC"
+ };
+ static int const types [count] = {
+ wave_type+1, wave_type+2, mixed_type+1, noise_type+0, mixed_type+1
+ };
+ append_voices( names, types, count );
+ }
+
+ // Make adjusted_gain * 0.75 = 1.0 so usual APU and one sound chip uses 1.0
+ double adjusted_gain = 1.0 / 0.75 * gain();
+
+#if !NSF_EMU_APU_ONLY
+ // TODO: order of chips here must match that in set_voice()
+
+ if ( core_.vrc6_apu() )
+ {
+ int const count = Nes_Vrc6_Apu::osc_count;
+ static const char* const names [count] = {
+ "Square 3", "Square 4", "Saw Wave"
+ };
+ static int const types [count] = {
+ wave_type+3, wave_type+4, wave_type+5,
+ };
+ append_voices( names, types, count );
+ adjusted_gain *= 0.75;
+ }
+
+ if ( core_.fme7_apu() )
+ {
+ int const count = Nes_Fme7_Apu::osc_count;
+ static const char* const names [count] = {
+ "Square 3", "Square 4", "Square 5"
+ };
+ static int const types [count] = {
+ wave_type+3, wave_type+4, wave_type+5,
+ };
+ append_voices( names, types, count );
+ adjusted_gain *= 0.75;
+ }
+
+ if ( core_.mmc5_apu() )
+ {
+ int const count = Nes_Mmc5_Apu::osc_count;
+ static const char* const names [count] = {
+ "Square 3", "Square 4", "PCM"
+ };
+ static int const types [count] = {
+ wave_type+3, wave_type+4, mixed_type+2
+ };
+ append_voices( names, types, count );
+ adjusted_gain *= 0.75;
+ }
+
+ if ( core_.fds_apu() )
+ {
+ int const count = Nes_Fds_Apu::osc_count;
+ static const char* const names [count] = {
+ "FM"
+ };
+ static int const types [count] = {
+ wave_type+0
+ };
+ append_voices( names, types, count );
+ adjusted_gain *= 0.75;
+ }
+
+ if ( core_.namco_apu() )
+ {
+ int const count = Nes_Namco_Apu::osc_count;
+ static const char* const names [count] = {
+ "Wave 1", "Wave 2", "Wave 3", "Wave 4",
+ "Wave 5", "Wave 6", "Wave 7", "Wave 8"
+ };
+ static int const types [count] = {
+ wave_type+3, wave_type+4, wave_type+5, wave_type+ 6,
+ wave_type+7, wave_type+8, wave_type+9, wave_type+10,
+ };
+ append_voices( names, types, count );
+ adjusted_gain *= 0.75;
+ }
+
+ if ( core_.vrc7_apu() )
+ {
+ int const count = Nes_Vrc7_Apu::osc_count;
+ static const char* const names [count] = {
+ "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6"
+ };
+ static int const types [count] = {
+ wave_type+3, wave_type+4, wave_type+5, wave_type+6,
+ wave_type+7, wave_type+8
+ };
+ append_voices( names, types, count );
+ adjusted_gain *= 0.75;
+ }
+
+ if ( core_.vrc7_apu() ) core_.vrc7_apu() ->volume( adjusted_gain );
+ if ( core_.namco_apu() ) core_.namco_apu()->volume( adjusted_gain );
+ if ( core_.vrc6_apu() ) core_.vrc6_apu() ->volume( adjusted_gain );
+ if ( core_.fme7_apu() ) core_.fme7_apu() ->volume( adjusted_gain );
+ if ( core_.mmc5_apu() ) core_.mmc5_apu() ->volume( adjusted_gain );
+ if ( core_.fds_apu() ) core_.fds_apu() ->volume( adjusted_gain );
+#endif
+
+ if ( adjusted_gain > gain() )
+ adjusted_gain = gain(); // only occurs if no other sound chips
+
+ core_.nes_apu()->volume( adjusted_gain );
+
+ return blargg_ok;
+}
+
+blargg_err_t Nsf_Emu::load_( Data_Reader& in )
+{
+ RETURN_ERR( core_.load( in ) );
+ set_track_count( header().track_count );
+ RETURN_ERR( check_nsf_header( header() ) );
+ set_warning( core_.warning() );
+ RETURN_ERR( init_sound() );
+ set_tempo( tempo() );
+ return setup_buffer( (int) (header().clock_rate() + 0.5) );
+}
+
+void Nsf_Emu::update_eq( blip_eq_t const& eq )
+{
+ core_.nes_apu()->treble_eq( eq );
+
+ #if !NSF_EMU_APU_ONLY
+ {
+ if ( core_.namco_apu() ) core_.namco_apu()->treble_eq( eq );
+ if ( core_.vrc6_apu() ) core_.vrc6_apu() ->treble_eq( eq );
+ if ( core_.fme7_apu() ) core_.fme7_apu() ->treble_eq( eq );
+ if ( core_.mmc5_apu() ) core_.mmc5_apu() ->treble_eq( eq );
+ if ( core_.fds_apu() ) core_.fds_apu() ->treble_eq( eq );
+ if ( core_.vrc7_apu() ) core_.vrc7_apu() ->treble_eq( eq );
+ }
+ #endif
+}
+
+void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
+{
+ #define HANDLE_CHIP( chip ) \
+ if ( chip && (i -= chip->osc_count) < 0 )\
+ {\
+ chip->set_output( i + chip->osc_count, buf );\
+ return;\
+ }\
+
+ HANDLE_CHIP( core_.nes_apu() );
+
+ #if !NSF_EMU_APU_ONLY
+ {
+ // TODO: order of chips here must match that in init_sound()
+ HANDLE_CHIP( core_.vrc6_apu() );
+ HANDLE_CHIP( core_.fme7_apu() );
+ HANDLE_CHIP( core_.mmc5_apu() );
+ HANDLE_CHIP( core_.fds_apu() );
+ HANDLE_CHIP( core_.namco_apu() );
+ HANDLE_CHIP( core_.vrc7_apu() );
+ }
+ #endif
+}
+
+blargg_err_t Nsf_Emu::start_track_( int track )
+{
+ RETURN_ERR( Classic_Emu::start_track_( track ) );
+ return core_.start_track( track );
+}
+
+blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int )
+{
+ core_.end_frame( duration );
+ const char* w = core_.warning();
+ if ( w )
+ set_warning( w );
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.h
new file mode 100644
index 00000000..0e7e654c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Emu.h
@@ -0,0 +1,51 @@
+// Nintendo NES/Famicom NSF music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef NSF_EMU_H
+#define NSF_EMU_H
+
+#include "Classic_Emu.h"
+#include "Nsf_Core.h"
+
+class Nsf_Emu : public Classic_Emu {
+public:
+ // Equalizer profiles for US NES and Japanese Famicom
+ static equalizer_t const nes_eq;
+ static equalizer_t const famicom_eq;
+
+ // NSF file header (see Nsf_Impl.h)
+ typedef Nsf_Core::header_t header_t;
+
+ // Header for currently loaded file
+ header_t const& header() const { return core_.header(); }
+
+ static gme_type_t static_type() { return gme_nsf_type; }
+
+ Nsf_Core& core() { return core_; }
+
+public:
+ Nsf_Emu();
+ ~Nsf_Emu();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+
+private:
+ enum { max_voices = 32 };
+ const char* voice_names_ [32];
+ int voice_types_ [32];
+ int voice_count_;
+ Nsf_Core core_;
+
+ blargg_err_t init_sound();
+ void append_voices( const char* const names [], int const types [], int count );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.cpp
new file mode 100644
index 00000000..53b3a416
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.cpp
@@ -0,0 +1,327 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nsf_Impl.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// number of frames until play interrupts init
+int const initial_play_delay = 7; // KikiKaikai needed this to work
+int const bank_size = 0x1000;
+int const rom_addr = 0x8000;
+
+int Nsf_Impl::read_code( addr_t addr ) const
+{
+ return *cpu.get_code( addr );
+}
+
+int Nsf_Impl::pcm_read( void* self, int addr )
+{
+ return STATIC_CAST(Nsf_Impl*,self)->read_code( addr );
+}
+
+Nsf_Impl::Nsf_Impl() : rom( bank_size )
+{
+ apu.dmc_reader( pcm_read, this );
+ assert( offsetof (header_t,unused [4]) == header_t::size );
+}
+
+void Nsf_Impl::unload()
+{
+ rom.clear();
+ high_ram.clear();
+ Gme_Loader::unload();
+}
+
+Nsf_Impl::~Nsf_Impl() { unload(); }
+
+bool nsf_header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "NESM\x1A", 5 );
+}
+
+double nsf_header_t::clock_rate() const
+{
+ return pal_only() ? 1662607.125 : 1789772.727272727;
+}
+
+int nsf_header_t::play_period() const
+{
+ // NTSC
+ int clocks = 29780;
+ int value = 0x411A;
+ byte const* rate_ptr = ntsc_speed;
+
+ // PAL
+ if ( pal_only() )
+ {
+ clocks = 33247;
+ value = 0x4E20;
+ rate_ptr = pal_speed;
+ }
+
+ // Default rate
+ int rate = get_le16( rate_ptr );
+ if ( rate == 0 )
+ rate = value;
+
+ // Custom rate
+ if ( rate != value )
+ clocks = (int) (rate * clock_rate() * (1.0/1000000.0));
+
+ return clocks;
+}
+
+// Gets address, given pointer to it in file header. If zero, returns rom_addr.
+Nsf_Impl::addr_t Nsf_Impl::get_addr( byte const in [] )
+{
+ addr_t addr = get_le16( in );
+ if ( addr == 0 )
+ addr = rom_addr;
+ return addr;
+}
+
+blargg_err_t Nsf_Impl::load_( Data_Reader& in )
+{
+ // pad ROM data with 0
+ RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ RETURN_ERR( high_ram.resize( (fds_enabled() ? fdsram_offset + fdsram_size : fdsram_offset) ) );
+
+ addr_t load_addr = get_addr( header_.load_addr );
+ if ( load_addr < (fds_enabled() ? sram_addr : rom_addr) )
+ set_warning( "Load address is too low" );
+
+ rom.set_addr( load_addr % bank_size );
+
+ if ( header_.vers != 1 )
+ set_warning( "Unknown file version" );
+
+ set_play_period( header_.play_period() );
+
+ return blargg_ok;
+}
+
+void Nsf_Impl::write_bank( int bank, int data )
+{
+ // Find bank in ROM
+ int offset = rom.mask_addr( data * bank_size );
+ if ( offset >= rom.size() )
+ special_event( "invalid bank" );
+ void const* rom_data = rom.at_addr( offset );
+
+ #if !NSF_EMU_APU_ONLY
+ if ( bank < bank_count - fds_banks && fds_enabled() )
+ {
+ // TODO: FDS bank switching is kind of hacky, might need to
+ // treat ROM as RAM so changes won't get lost when switching.
+ byte* out = sram();
+ if ( bank >= fds_banks )
+ {
+ out = fdsram();
+ bank -= fds_banks;
+ }
+ memcpy( &out [bank * bank_size], rom_data, bank_size );
+ return;
+ }
+ #endif
+
+ if ( bank >= fds_banks )
+ cpu.map_code( (bank + 6) * bank_size, bank_size, rom_data );
+}
+
+void Nsf_Impl::map_memory()
+{
+ // Map standard things
+ cpu.reset( unmapped_code() );
+ cpu.map_code( 0, 0x2000, low_ram, low_ram_size ); // mirrored four times
+ cpu.map_code( sram_addr, sram_size, sram() );
+
+ // Determine initial banks
+ byte banks [bank_count];
+ static byte const zero_banks [sizeof header_.banks] = { 0 };
+ if ( memcmp( header_.banks, zero_banks, sizeof zero_banks ) )
+ {
+ banks [0] = header_.banks [6];
+ banks [1] = header_.banks [7];
+ memcpy( banks + fds_banks, header_.banks, sizeof header_.banks );
+ }
+ else
+ {
+ // No initial banks, so assign them based on load_addr
+ int first_bank = (get_addr( header_.load_addr ) - sram_addr) / bank_size;
+ unsigned total_banks = rom.size() / bank_size;
+ for ( int i = bank_count; --i >= 0; )
+ {
+ int bank = i - first_bank;
+ if ( (unsigned) bank >= total_banks )
+ bank = 0;
+ banks [i] = bank;
+ }
+ }
+
+ // Map banks
+ for ( int i = (fds_enabled() ? 0 : fds_banks); i < bank_count; ++i )
+ write_bank( i, banks [i] );
+
+ // Map FDS RAM
+ if ( fds_enabled() )
+ cpu.map_code( rom_addr, fdsram_size, fdsram() );
+}
+
+inline void Nsf_Impl::push_byte( int b )
+{
+ low_ram [0x100 + cpu.r.sp--] = b;
+}
+
+// Jumps to routine, given pointer to address in file header. Pushes idle_addr
+// as return address, NOT old PC.
+void Nsf_Impl::jsr_then_stop( byte const addr [] )
+{
+ cpu.r.pc = get_addr( addr );
+ push_byte( (idle_addr - 1) >> 8 );
+ push_byte( (idle_addr - 1) );
+}
+
+blargg_err_t Nsf_Impl::start_track( int track )
+{
+ int speed_flags = 0;
+ #if NSF_EMU_EXTRA_FLAGS
+ speed_flags = header().speed_flags;
+ #endif
+
+ apu.reset( header().pal_only(), (speed_flags & 0x20) ? 0x3F : 0 );
+ apu.write_register( 0, 0x4015, 0x0F );
+ apu.write_register( 0, 0x4017, (speed_flags & 0x10) ? 0x80 : 0 );
+
+ // Clear memory
+ memset( unmapped_code(), Nes_Cpu::halt_opcode, unmapped_size );
+ memset( low_ram, 0, low_ram_size );
+ memset( sram(), 0, sram_size );
+
+ map_memory();
+
+ // Arrange time of first call to play routine
+ play_extra = 0;
+ next_play = play_period;
+
+ play_delay = initial_play_delay;
+ saved_state.pc = idle_addr;
+
+ // Setup for call to init routine
+ cpu.r.a = track;
+ cpu.r.x = header_.pal_only();
+ cpu.r.sp = 0xFF;
+ jsr_then_stop( header_.init_addr );
+ if ( cpu.r.pc < get_addr( header_.load_addr ) )
+ set_warning( "Init address < load address" );
+
+ return blargg_ok;
+}
+
+void Nsf_Impl::unmapped_write( addr_t addr, int data )
+{
+ dprintf( "Unmapped write $%04X <- %02X\n", (int) addr, data );
+}
+
+int Nsf_Impl::unmapped_read( addr_t addr )
+{
+ dprintf( "Unmapped read $%04X\n", (int) addr );
+ return addr >> 8;
+}
+
+void Nsf_Impl::special_event( const char str [] )
+{
+ dprintf( "%s\n", str );
+}
+
+void Nsf_Impl::run_once( time_t end )
+{
+ // Emulate until next play call if possible
+ if ( run_cpu_until( min( next_play, end ) ) )
+ {
+ // Halt instruction encountered
+
+ if ( cpu.r.pc != idle_addr )
+ {
+ special_event( "illegal instruction" );
+ cpu.count_error();
+ cpu.set_time( cpu.end_time() );
+ return;
+ }
+
+ // Init/play routine returned
+ play_delay = 1; // play can now be called regularly
+
+ if ( saved_state.pc == idle_addr )
+ {
+ // nothing to run
+ time_t t = cpu.end_time();
+ if ( cpu.time() < t )
+ cpu.set_time( t );
+ }
+ else
+ {
+ // continue init routine that was interrupted by play routine
+ cpu.r = saved_state;
+ saved_state.pc = idle_addr;
+ }
+ }
+
+ if ( time() >= next_play )
+ {
+ // Calculate time of next call to play routine
+ play_extra ^= 1; // extra clock every other call
+ next_play += play_period + play_extra;
+
+ // Call routine if ready
+ if ( play_delay && !--play_delay )
+ {
+ // Save state if init routine is still running
+ if ( cpu.r.pc != idle_addr )
+ {
+ check( saved_state.pc == idle_addr );
+ saved_state = cpu.r;
+ special_event( "play called during init" );
+ }
+
+ jsr_then_stop( header_.play_addr );
+ }
+ }
+}
+
+void Nsf_Impl::run_until( time_t end )
+{
+ while ( time() < end )
+ run_once( end );
+}
+
+void Nsf_Impl::end_frame( time_t end )
+{
+ if ( time() < end )
+ run_until( end );
+ cpu.adjust_time( -end );
+
+ // Localize to new time frame
+ next_play -= end;
+ check( next_play >= 0 );
+ if ( next_play < 0 )
+ next_play = 0;
+
+ apu.end_frame( end );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.h b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.h
new file mode 100644
index 00000000..d7c5ad4f
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsf_Impl.h
@@ -0,0 +1,189 @@
+// Loads NSF file and emulates CPU and RAM, no sound chips
+
+// Game_Music_Emu 0.6-pre
+#ifndef NSF_IMPL_H
+#define NSF_IMPL_H
+
+#include "Gme_Loader.h"
+#include "Nes_Cpu.h"
+#include "Rom_Data.h"
+#include "Nes_Apu.h"
+
+// NSF file header
+struct nsf_header_t
+{
+ typedef unsigned char byte;
+ enum { size = 0x80 };
+
+ char tag [ 5];
+ byte vers;
+ byte track_count;
+ byte first_track;
+ byte load_addr [ 2];
+ byte init_addr [ 2];
+ byte play_addr [ 2];
+ char game [32]; // NOT null-terminated if 32 chars in length
+ char author [32];
+ char copyright [32];
+ byte ntsc_speed [ 2];
+ byte banks [ 8];
+ byte pal_speed [ 2];
+ byte speed_flags;
+ byte chip_flags;
+ byte unused [ 4];
+
+ // Sound chip masks
+ enum {
+ vrc6_mask = 1 << 0,
+ vrc7_mask = 1 << 1,
+ fds_mask = 1 << 2,
+ mmc5_mask = 1 << 3,
+ namco_mask = 1 << 4,
+ fme7_mask = 1 << 5,
+ all_mask = (1 << 6) - 1
+ };
+
+ // True if header has proper NSF file signature
+ bool valid_tag() const;
+
+ // True if file supports only PAL speed
+ bool pal_only() const { return (speed_flags & 3) == 1; }
+
+ // Clocks per second
+ double clock_rate() const;
+
+ // Clocks between calls to play routine
+ int play_period() const;
+};
+
+/* Loads NSF file into memory, then emulates CPU, RAM, and ROM.
+Non-memory accesses are routed through cpu_read() and cpu_write(). */
+class Nsf_Impl : public Gme_Loader {
+public:
+
+ // Sound chip
+ Nes_Apu* nes_apu() { return &apu; }
+
+ // Starts track, where 0 is the first
+ virtual blargg_err_t start_track( int );
+
+ // Emulates to at least time t, then begins new time frame at
+ // time t. Might emulate a few clocks extra, so after returning,
+ // time() may not be zero.
+ typedef int time_t; // clock count
+ virtual void end_frame( time_t n );
+
+// Finer control
+
+ // Header for currently loaded file
+ typedef nsf_header_t header_t;
+ header_t const& header() const { return header_; }
+
+ // Sets clocks between calls to play routine to p + 1/2 clock
+ void set_play_period( int p ) { play_period = p; }
+
+ // Time play routine will next be called
+ time_t play_time() const { return next_play; }
+
+ // Emulates to at least time t. Might emulate a few clocks extra.
+ virtual void run_until( time_t t );
+
+ // Time emulated to
+ time_t time() const { return cpu.time(); }
+
+protected:
+// Nsf_Core use
+
+ typedef int addr_t;
+
+ // Called for unmapped accesses. Default just prints info if debugging.
+ virtual void unmapped_write( addr_t, int data );
+ virtual int unmapped_read( addr_t );
+
+ // Override in derived class
+ // Bank writes and RAM at 0-$7FF and $6000-$7FFF are handled internally
+ virtual int cpu_read( addr_t a ) { return unmapped_read( a ); }
+ virtual void cpu_write( addr_t a, int data ){ unmapped_write( a, data ); }
+
+ // Reads byte as CPU would when executing code. Only works for RAM/ROM,
+ // NOT I/O like sound chips.
+ int read_code( addr_t addr ) const;
+
+// Debugger services
+
+ enum { mem_size = 0x10000 };
+
+ // CPU sits here when waiting for next call to play routine
+ enum { idle_addr = 0x5FF6 };
+
+ Nes_Cpu cpu;
+
+ // Runs CPU to at least time t and returns false, or returns true
+ // if it encounters illegal instruction (halt).
+ virtual bool run_cpu_until( time_t t );
+
+ // CPU calls through to these to access memory (except instructions)
+ int read_mem( addr_t );
+ void write_mem( addr_t, int );
+
+ // Address of play routine
+ addr_t play_addr() const { return get_addr( header_.play_addr ); }
+
+ // Same as run_until, except emulation stops for any event (routine returned,
+ // play routine called, illegal instruction).
+ void run_once( time_t );
+
+ // Make a note of event
+ virtual void special_event( const char str [] );
+
+
+// Implementation
+public:
+ Nsf_Impl();
+ ~Nsf_Impl();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual void unload();
+
+private:
+ enum { low_ram_size = 0x800 };
+ enum { fdsram_size = 0x6000 };
+ enum { sram_size = 0x2000 };
+ enum { unmapped_size= Nes_Cpu::page_size + 8 };
+ enum { fds_banks = 2 };
+ enum { bank_count = fds_banks + 8 };
+ enum { banks_addr = idle_addr };
+ enum { sram_addr = 0x6000 };
+
+ blargg_vector<byte> high_ram;
+ Rom_Data rom;
+
+ // Play routine timing
+ time_t next_play;
+ time_t play_period;
+ int play_extra;
+ int play_delay;
+ Nes_Cpu::registers_t saved_state; // of interrupted init routine
+
+ // Large objects after others
+ header_t header_;
+ Nes_Apu apu;
+ byte low_ram [low_ram_size];
+
+ // Larger RAM areas allocated separately
+ enum { fdsram_offset = sram_size + unmapped_size };
+ byte* sram() { return high_ram.begin(); }
+ byte* unmapped_code() { return &high_ram [sram_size]; }
+ byte* fdsram() { return &high_ram [fdsram_offset]; }
+ int fds_enabled() const { return header_.chip_flags & header_t::fds_mask; }
+
+ void map_memory();
+ void write_bank( int index, int data );
+ void jsr_then_stop( byte const addr [] );
+ void push_byte( int );
+ static addr_t get_addr( byte const [] );
+ static int pcm_read( void*, int );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Nsfe_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Nsfe_Emu.cpp
new file mode 100644
index 00000000..0aee0c67
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsfe_Emu.cpp
@@ -0,0 +1,321 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Nsfe_Emu.h"
+
+#include "blargg_endian.h"
+#include <ctype.h>
+
+/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+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, int size, blargg_vector<char>& chars,
+ blargg_vector<const char*>& 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, Nsfe_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 (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err);
+ if ( memcmp( signature, "NSFE", 4 ) )
+ return blargg_err_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 ) );
+ int size = get_le32( block_header [0] );
+ int 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 blargg_err_file_corrupt;
+
+ nsfe_info_t finfo;
+ finfo.track_count = 1;
+ finfo.first_track = 0;
+
+ RETURN_ERR( in.read( &finfo, min( size, (int) 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 blargg_err_file_corrupt;
+ RETURN_ERR( in.read( info.banks, size ) );
+ break;
+
+ case BLARGG_4CHAR('h','t','u','a'): {
+ blargg_vector<char> chars;
+ blargg_vector<const char*> 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, header.size, &sub );
+ RETURN_ERR( nsf_emu->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 blargg_ok;
+}
+
+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() )
+ {
+ int 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 blargg_ok;
+}
+
+Nsfe_Emu::Nsfe_Emu()
+{
+ set_type( gme_nsfe_type );
+}
+
+Nsfe_Emu::~Nsfe_Emu() { }
+
+void Nsfe_Emu::unload()
+{
+ info.unload();
+ 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 blargg_ok;
+ }
+
+ 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 )
+{
+ RETURN_ERR( info.load( in, this ) );
+ disable_playlist_( false );
+ return blargg_ok;
+}
+
+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_( true );
+ 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.6pre/gme/Nsfe_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Nsfe_Emu.h
new file mode 100644
index 00000000..3edf3bd1
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Nsfe_Emu.h
@@ -0,0 +1,72 @@
+// Nintendo NES/Famicom NSFE music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef NSFE_EMU_H
+#define NSFE_EMU_H
+
+#include "blargg_common.h"
+#include "Nsf_Emu.h"
+class Nsfe_Emu;
+
+// Allows reading info from NSFE file without creating emulator
+class Nsfe_Info {
+public:
+ blargg_err_t load( Data_Reader&, Nsfe_Emu* );
+
+ struct info_t : Nsf_Emu::header_t
+ {
+ char game [256];
+ char author [256];
+ char copyright [256];
+ char dumper [256];
+ } info;
+
+ void disable_playlist( bool = true );
+
+ blargg_err_t track_info_( track_info_t* out, int track ) const;
+
+ int remap_track( int i ) const;
+
+ void unload();
+
+// Implementation
+public:
+ Nsfe_Info();
+ ~Nsfe_Info();
+ BLARGG_DISABLE_NOTHROW
+private:
+ blargg_vector<char> track_name_data;
+ blargg_vector<const char*> track_names;
+ blargg_vector<unsigned char> playlist;
+ blargg_vector<char [4]> track_times;
+ int actual_track_count_;
+ bool playlist_disabled;
+};
+
+class Nsfe_Emu : public Nsf_Emu {
+public:
+ static gme_type_t static_type() { return gme_nsfe_type; }
+
+ struct header_t { char tag [4]; };
+
+
+// Implementation
+public:
+ Nsfe_Emu();
+ ~Nsfe_Emu();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t start_track_( int );
+ virtual void clear_playlist_();
+
+private:
+ Nsfe_Info info;
+
+ void disable_playlist_( bool b );
+ friend class Nsfe_Info;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.cpp
new file mode 100644
index 00000000..33f5bd46
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.cpp
@@ -0,0 +1,270 @@
+#include "Opl_Apu.h"
+
+#include "blargg_source.h"
+
+#include "ym2413.h"
+#include "fmopl.h"
+
+Opl_Apu::Opl_Apu() { opl = 0; opl_memory = 0; }
+
+blargg_err_t Opl_Apu::init( long clock, long rate, blip_time_t period, type_t type )
+{
+ type_ = type;
+ clock_ = clock;
+ rate_ = rate;
+ period_ = period;
+ set_output( 0, 0 );
+ volume( 1.0 );
+ switch (type)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ opl = ym2413_init( clock, rate, 0 );
+ break;
+
+ case type_vrc7:
+ opl = ym2413_init( clock, rate, 1 );
+ break;
+
+ case type_opl:
+ opl = ym3526_init( clock, rate );
+ break;
+
+ case type_msxaudio:
+ //logfile = fopen("c:\\temp\\msxaudio.log", "wb");
+ opl = y8950_init( clock, rate );
+ opl_memory = malloc( 32768 );
+ y8950_set_delta_t_memory( opl, opl_memory, 32768 );
+ break;
+
+ case type_opl2:
+ opl = ym3812_init( clock, rate );
+ break;
+ }
+ reset();
+ return 0;
+}
+
+Opl_Apu::~Opl_Apu()
+{
+ if (opl)
+ {
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ ym2413_shutdown( opl );
+ break;
+
+ case type_opl:
+ ym3526_shutdown( opl );
+ break;
+
+ case type_msxaudio:
+ y8950_shutdown( opl );
+ free( opl_memory );
+ //fclose( logfile );
+ break;
+
+ case type_opl2:
+ ym3812_shutdown( opl );
+ break;
+ }
+ }
+}
+
+void Opl_Apu::reset()
+{
+ addr = 0;
+ next_time = 0;
+ last_amp = 0;
+
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ ym2413_reset_chip( opl );
+ break;
+
+ case type_opl:
+ ym3526_reset_chip( opl );
+ break;
+
+ case type_msxaudio:
+ y8950_reset_chip( opl );
+ break;
+
+ case type_opl2:
+ ym3812_reset_chip( opl );
+ break;
+ }
+}
+
+void Opl_Apu::write_data( blip_time_t time, int data )
+{
+ run_until( time );
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ ym2413_write( opl, 0, addr );
+ ym2413_write( opl, 1, data );
+ break;
+
+ case type_opl:
+ ym3526_write( opl, 0, addr );
+ ym3526_write( opl, 1, data );
+ break;
+
+ case type_msxaudio:
+ /*if ( addr >= 7 && addr <= 7 + 11 )
+ {
+ unsigned char temp [2] = { addr - 7, data };
+ fwrite( &temp, 1, 2, logfile );
+ }*/
+ y8950_write( opl, 0, addr );
+ y8950_write( opl, 1, data );
+ break;
+
+ case type_opl2:
+ ym3812_write( opl, 0, addr );
+ ym3812_write( opl, 1, data );
+ break;
+ }
+}
+
+int Opl_Apu::read( blip_time_t time, int port )
+{
+ run_until( time );
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ return ym2413_read( opl, port );
+
+ case type_opl:
+ return ym3526_read( opl, port );
+
+ case type_msxaudio:
+ {
+ int ret = y8950_read( opl, port );
+ /*unsigned char temp [2] = { port + 0x80, ret };
+ fwrite( &temp, 1, 2, logfile );*/
+ return ret;
+ }
+
+ case type_opl2:
+ return ym3812_read( opl, port );
+ }
+
+ return 0;
+}
+
+void Opl_Apu::end_frame( blip_time_t time )
+{
+ run_until( time );
+ next_time -= time;
+
+ if ( output_ )
+ output_->set_modified();
+}
+
+void Opl_Apu::run_until( blip_time_t end_time )
+{
+ if ( end_time > next_time )
+ {
+ blip_time_t time_delta = end_time - next_time;
+ blip_time_t time = next_time;
+ unsigned count = time_delta / period_ + 1;
+ switch (type_)
+ {
+ case type_opll:
+ case type_msxmusic:
+ case type_smsfmunit:
+ case type_vrc7:
+ {
+ SAMP bufMO[ 1024 ];
+ SAMP bufRO[ 1024 ];
+ SAMP * buffers[2] = { bufMO, bufRO };
+
+ while ( count > 0 )
+ {
+ unsigned todo = count;
+ if ( todo > 1024 ) todo = 1024;
+ ym2413_update_one( opl, buffers, todo );
+
+ if ( output_ )
+ {
+ int last_amp = this->last_amp;
+ for ( unsigned i = 0; i < todo; i++ )
+ {
+ int amp = bufMO [i] + bufRO [i];
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output_ );
+ }
+ time += period_;
+ }
+ this->last_amp = last_amp;
+ }
+ else time += period_ * todo;
+
+ count -= todo;
+ }
+ }
+ break;
+
+ case type_opl:
+ case type_msxaudio:
+ case type_opl2:
+ {
+ OPLSAMPLE buffer[ 1024 ];
+
+ while ( count > 0 )
+ {
+ unsigned todo = count;
+ if ( todo > 1024 ) todo = 1024;
+ switch (type_)
+ {
+ case type_opl: ym3526_update_one( opl, buffer, todo ); break;
+ case type_msxaudio: y8950_update_one( opl, buffer, todo ); break;
+ case type_opl2: ym3812_update_one( opl, buffer, todo ); break;
+ }
+
+ if ( output_ )
+ {
+ int last_amp = this->last_amp;
+ for ( unsigned i = 0; i < todo; i++ )
+ {
+ int amp = buffer [i];
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output_ );
+ }
+ time += period_;
+ }
+ this->last_amp = last_amp;
+ }
+ else time += period_ * todo;
+
+ count -= todo;
+ }
+ }
+ break;
+ }
+ next_time = time;
+ }
+} \ No newline at end of file
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.h
new file mode 100644
index 00000000..86d73d74
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Opl_Apu.h
@@ -0,0 +1,63 @@
+#ifndef OPL_APU_H
+#define OPL_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+#include <stdio.h>
+
+class Opl_Apu {
+public:
+ Opl_Apu();
+ ~Opl_Apu();
+
+ enum type_t { type_opll = 0x10, type_msxmusic = 0x11, type_smsfmunit = 0x12,
+ type_vrc7 = 0x13, type_opl = 0x20, type_msxaudio = 0x21, type_opl2 = 0x22 };
+ blargg_err_t init( long clock, long rate, blip_time_t period, type_t );
+
+ void reset();
+ void volume( double v ) { synth.volume( 1.0 / (4096 * 6) * v ); }
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+ enum { osc_count = 1 };
+ void osc_output( int index, Blip_Buffer* );
+ void set_output( int i, Blip_Buffer* buf, Blip_Buffer* = NULL, Blip_Buffer* = NULL ) { osc_output( 0, buf ); }
+ void end_frame( blip_time_t );
+
+ void write_addr( int data ) { addr = data; }
+ void write_data( blip_time_t, int data );
+
+ int read( blip_time_t, int port );
+
+ static bool supported() { return true; }
+
+private:
+ // noncopyable
+ Opl_Apu( const Opl_Apu& );
+ Opl_Apu& operator = ( const Opl_Apu& );
+
+ Blip_Buffer* output_;
+ type_t type_;
+ void* opl;
+ void* opl_memory;
+ //FILE* logfile;
+ unsigned char regs[ 0x100 ];
+ blip_time_t next_time;
+ int last_amp;
+ int addr;
+
+ long clock_;
+ long rate_;
+ blip_time_t period_;
+
+ Blip_Synth_Fast synth;
+
+ void run_until( blip_time_t );
+};
+
+inline void Opl_Apu::osc_output( int i, Blip_Buffer* buf )
+{
+ assert( (unsigned) i < osc_count );
+ output_ = buf;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Resampler.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Resampler.cpp
new file mode 100644
index 00000000..96bb73e9
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Resampler.cpp
@@ -0,0 +1,79 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Resampler.h"
+
+/* Copyright (C) 2004-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Resampler::Resampler()
+{
+ write_pos = 0;
+ rate_ = 0;
+}
+
+Resampler::~Resampler() { }
+
+void Resampler::clear()
+{
+ write_pos = 0;
+ clear_();
+}
+
+inline int Resampler::resample_wrapper( sample_t out [], int* out_size,
+ sample_t const in [], int in_size )
+{
+ assert( rate() );
+
+ sample_t* out_ = out;
+ int result = resample_( &out_, out + *out_size, in, in_size ) - in;
+ assert( out_ <= out + *out_size );
+ assert( result <= in_size );
+
+ *out_size = out_ - out;
+ return result;
+}
+
+int Resampler::resample( sample_t out [], int out_size, sample_t const in [], int* in_size )
+{
+ *in_size = resample_wrapper( out, &out_size, in, *in_size );
+ return out_size;
+}
+
+
+//// Buffering
+
+blargg_err_t Resampler::resize_buffer( int new_size )
+{
+ RETURN_ERR( buf.resize( new_size ) );
+ clear();
+ return blargg_ok;
+}
+
+int Resampler::skip_input( int count )
+{
+ write_pos -= count;
+ if ( write_pos < 0 ) // occurs when downsampling
+ {
+ count += write_pos;
+ write_pos = 0;
+ }
+ memmove( buf.begin(), &buf [count], write_pos * sizeof buf [0] );
+ return count;
+}
+
+int Resampler::read( sample_t out [], int out_size )
+{
+ if ( out_size )
+ skip_input( resample_wrapper( out, &out_size, buf.begin(), write_pos ) );
+ return out_size;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Resampler.h b/plugins/gme/game-music-emu-0.6pre/gme/Resampler.h
new file mode 100644
index 00000000..c8f96f72
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Resampler.h
@@ -0,0 +1,110 @@
+// Common interface for resamplers
+
+// Game_Music_Emu 0.6-pre
+#ifndef RESAMPLER_H
+#define RESAMPLER_H
+
+#include "blargg_common.h"
+
+class Resampler {
+public:
+
+ virtual ~Resampler();
+
+ // Sets input/output resampling ratio
+ blargg_err_t set_rate( double );
+
+ // Current input/output ratio
+ double rate() const { return rate_; }
+
+ // Samples are 16-bit signed
+ typedef short sample_t;
+
+// One of two different buffering schemes can be used, as decided by the caller:
+
+// External buffering (caller provides input buffer)
+
+ // Resamples in to at most n out samples and returns number of samples actually
+ // written. Sets *in_size to number of input samples that aren't needed anymore
+ // and should be removed from input.
+ int resample( sample_t out [], int n, sample_t const in [], int* in_size );
+
+// Internal buffering (resampler manages buffer)
+
+ // Resizes input buffer to n samples, then clears it
+ blargg_err_t resize_buffer( int n );
+
+ // Clears input buffer
+ void clear();
+
+ // Writes at most n samples to input buffer and returns number actually written.
+ // Result will be less than n if there isn't enough free space in buffer.
+ int write( sample_t const in [], int n );
+
+ // Number of input samples in buffer
+ int written() const { return write_pos; }
+
+ // Removes first n input samples from buffer, fewer if there aren't that many.
+ // Returns number of samples actually removed.
+ int skip_input( int n );
+
+ // Resamples input to at most n output samples. Returns number of samples
+ // actually written to out. Result will be less than n if there aren't
+ // enough input samples in buffer.
+ int read( sample_t out [], int n );
+
+// Direct writing to input buffer, instead of using write( in, n ) above
+
+ // Pointer to place to write input samples
+ sample_t* buffer() { return &buf [write_pos]; }
+
+ // Number of samples that can be written to buffer()
+ int buffer_free() const { return buf.size() - write_pos; }
+
+ // Notifies resampler that n input samples have been written to buffer().
+ // N must not be greater than buffer_free().
+ void write( int n );
+
+// Derived interface
+protected:
+ virtual blargg_err_t set_rate_( double rate ) BLARGG_PURE( ; )
+
+ virtual void clear_() { }
+
+ // Resample as many available in samples as will fit within out_size and
+ // return pointer past last input sample read and set *out just past
+ // the last output sample.
+ virtual sample_t const* resample_( sample_t** out, sample_t const* out_end,
+ sample_t const in [], int in_size ) BLARGG_PURE( { return in; } )
+
+// Implementation
+public:
+ Resampler();
+
+private:
+ blargg_vector<sample_t> buf;
+ int write_pos;
+ double rate_;
+
+ int resample_wrapper( sample_t out [], int* out_size,
+ sample_t const in [], int in_size );
+};
+
+inline void Resampler::write( int count )
+{
+ write_pos += count;
+ assert( (unsigned) write_pos <= buf.size() );
+}
+
+inline blargg_err_t Resampler::set_rate_( double r )
+{
+ rate_ = r;
+ return blargg_ok;
+}
+
+inline blargg_err_t Resampler::set_rate( double r )
+{
+ return set_rate_( r );
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.cpp
new file mode 100644
index 00000000..55d53d33
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.cpp
@@ -0,0 +1,99 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Rom_Data.h"
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void Rom_Data::clear()
+{
+ file_size_ = 0;
+ rom_addr = 0;
+ mask = 0;
+ rom.clear();
+}
+
+Rom_Data::Rom_Data( int page_size ) :
+ pad_size( page_size + pad_extra )
+{
+ // page_size should be power of 2
+ check( (page_size & (page_size - 1)) == 0 );
+
+ clear();
+}
+
+Rom_Data::~Rom_Data()
+{ }
+
+// Reads file into array, placing file_offset bytes of padding before the beginning, and pad_size after the end
+blargg_err_t Rom_Data::load_( Data_Reader& in, int header_size, int file_offset )
+{
+ clear();
+ file_size_ = in.remain();
+ if ( file_size_ <= header_size ) // <= because there must be data after header
+ return blargg_err_file_type;
+
+ RETURN_ERR( rom.resize( file_offset + file_size_ + pad_size ) );
+
+ return in.read( rom.begin() + file_offset, file_size_ );
+}
+
+blargg_err_t Rom_Data::load( Data_Reader& in, int header_size,
+ void* header_out, int fill )
+{
+ int file_offset = pad_size - header_size;
+ blargg_err_t err = load_( in, header_size, file_offset );
+ if ( err )
+ {
+ 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 blargg_ok;
+}
+
+void Rom_Data::set_addr( int addr )
+{
+ int const page_size = pad_size - pad_extra;
+
+ // Minimum size that contains all bytes and is a multiple of page_size
+ int const size = (addr + file_size_ + page_size - 1) / page_size * page_size;
+
+ // Find lowest power of 2 that is >= size
+ int power2 = 1;
+ while ( power2 < size )
+ power2 *= 2;
+
+ mask = power2 - 1;
+
+ // Address of first byte of ROM (possibly negative)
+ rom_addr = addr - page_size - pad_extra;
+
+ if ( rom.resize( size - rom_addr + pad_extra ) ) { } // OK if shrink fails
+}
+
+byte* Rom_Data::at_addr( int addr )
+{
+ int offset = mask_addr( addr ) - rom_addr;
+
+ if ( (unsigned) offset > (unsigned) (rom.size() - pad_size) )
+ offset = 0; // unmapped
+
+ return &rom [offset];
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.h b/plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.h
new file mode 100644
index 00000000..e1c2ef32
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Rom_Data.h
@@ -0,0 +1,94 @@
+// Manages ROM data loaded from file in an efficient manner
+
+// Game_Music_Emu 0.6-pre
+#ifndef ROM_DATA_H
+#define ROM_DATA_H
+
+#include "blargg_common.h"
+#include "Data_Reader.h"
+
+/* Loads a ROM file into memory and allows access to it in page-sized chunks.
+
+* ROM file consists of header followed by ROM data. Instead of storing the entire
+ROM contents, the file only stores the occupied portion, with the bytes before and
+after that cleared to some value. The size and format of the header is up to the
+caller, as is the starting address of the ROM data following it. File loading is
+performed with a single read, rather than two or more that might otherwise be
+required.
+
+* Once ROM data is loaded and its address specified, a pointer to any "page" can
+be obtained. ROM data is mirrored using smallest power of 2 that contains it.
+Addresses not aligned to pages can also be used, but this might cause unexpected
+results.
+
+Example with file data of size 0x0C put at address 0x0F, with page size of 8:
+
+---------------0123456789AB--------------------0123456789AB---------...
+^ ^ ^ ^ ^ ^ ^ ^ ^
+0 0x08 0x10 0x18 0x20 0x28 0x30 0x38 0x40
+
+at_addr(0x00) = pointer to 8 bytes of fill.
+at_addr(0x08) = pointer to 7 bytes of fill, followed by first byte of file.
+at_addr(0x10) = pointer to next 8 bytes of file.
+at_addr(0x18) = pointer to last 3 bytes of file, followed by 5 bytes of fill.
+at_addr(0x20) = pointer to 8 bytes of fill.
+at_addr(0x28) = pointer to 7 bytes of fill, followed by first byte of file.
+etc. */
+
+class Rom_Data {
+ enum { pad_extra = 8 };
+public:
+ typedef unsigned char byte;
+
+ // Page_size should be a power of 2
+ Rom_Data( int page_size );
+
+ // Loads file into memory, then copies header to *header_out and fills
+ // unmapped bank and file data padding with fill. Returns blargg_err_file_type
+ // if in.remain() <= header_size.
+ blargg_err_t load( Data_Reader& in, int header_size, void* header_out, int fill );
+
+ // Below, "file data" refers to data AFTER the header
+
+ // Size of file data
+ int file_size() const { return file_size_; }
+
+ // Pointer to beginning of file data
+ byte * begin() { return rom.begin() + pad_size; }
+ byte const* begin() const { return rom.begin() + pad_size; }
+
+ // Pointer to unmapped page cleared with fill value
+ byte* unmapped() { return rom.begin(); }
+
+ // Sets address that file data will start at. Must be set before using following
+ // functions, and cannot be set more than once.
+ void set_addr( int addr );
+
+ // Address of first empty page (file size + addr rounded up to multiple of page_size)
+ int size() const { return rom.size() - pad_extra + rom_addr; }
+
+ // Masks address to nearest power of two greater than size()
+ int mask_addr( int addr ) const { return addr & mask; }
+
+ // Pointer to page beginning at addr, or unmapped() if outside data.
+ // Mirrored using mask_addr().
+ byte* at_addr( int addr );
+
+ // Frees memory
+ void clear();
+
+// Implementation
+public:
+ ~Rom_Data();
+
+protected:
+ blargg_vector<byte> rom;
+ int mask;
+ int rom_addr;
+ int const pad_size;
+ int file_size_;
+
+ blargg_err_t load_( Data_Reader& in, int header_size, int file_offset );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.cpp b/plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.cpp
new file mode 100644
index 00000000..5d7f9421
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.cpp
@@ -0,0 +1,81 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "SPC_Filter.h"
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void SPC_Filter::clear() { memset( ch, 0, sizeof ch ); }
+
+SPC_Filter::SPC_Filter()
+{
+ enabled = true;
+ gain = gain_unit;
+ bass = bass_norm;
+ clear();
+}
+
+void SPC_Filter::run( short io [], int count )
+{
+ require( (count & 1) == 0 ); // must be even
+
+ int const gain = this->gain;
+ if ( enabled )
+ {
+ int const bass = this->bass;
+ chan_t* c = &ch [2];
+ do
+ {
+ // cache in registers
+ int sum = (--c)->sum;
+ int pp1 = c->pp1;
+ int p1 = c->p1;
+
+ for ( int i = 0; i < count; i += 2 )
+ {
+ // Low-pass filter (two point FIR with coeffs 0.25, 0.75)
+ int f = io [i] + p1;
+ p1 = io [i] * 3;
+
+ // High-pass filter ("leaky integrator")
+ int delta = f - pp1;
+ pp1 = f;
+ int s = sum >> (gain_bits + 2);
+ sum += (delta * gain) - (sum >> bass);
+
+ // Clamp to 16 bits
+ if ( (short) s != s )
+ s = (s >> 31) ^ 0x7FFF;
+
+ io [i] = (short) s;
+ }
+
+ c->p1 = p1;
+ c->pp1 = pp1;
+ c->sum = sum;
+ ++io;
+ }
+ while ( c != ch );
+ }
+ else if ( gain != gain_unit )
+ {
+ short* const end = io + count;
+ while ( io < end )
+ {
+ int s = (*io * gain) >> gain_bits;
+ if ( (short) s != s )
+ s = (s >> 31) ^ 0x7FFF;
+ *io++ = (short) s;
+ }
+ }
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.h b/plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.h
new file mode 100644
index 00000000..0068817b
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/SPC_Filter.h
@@ -0,0 +1,53 @@
+// Simple low-pass and high-pass filter to better match sound output of a SNES
+
+// snes_spc 0.9.0
+#ifndef SPC_FILTER_H
+#define SPC_FILTER_H
+
+#include "blargg_common.h"
+
+struct SPC_Filter {
+public:
+
+ // Filters count samples of stereo sound in place. Count must be a multiple of 2.
+ typedef short sample_t;
+ void run( sample_t io [], int count );
+
+// Optional features
+
+ // Clears filter to silence
+ void clear();
+
+ // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit
+ // are fine, since output is clamped to 16-bit sample range.
+ enum { gain_unit = 0x100 };
+ void set_gain( int gain );
+
+ // Enables/disables filtering (when disabled, gain is still applied)
+ void enable( bool b );
+
+ // Sets amount of bass (logarithmic scale)
+ enum { bass_none = 0 };
+ enum { bass_norm = 8 }; // normal amount
+ enum { bass_max = 31 };
+ void set_bass( int bass );
+
+public:
+ SPC_Filter();
+ BLARGG_DISABLE_NOTHROW
+private:
+ enum { gain_bits = 8 };
+ int gain;
+ int bass;
+ bool enabled;
+ struct chan_t { int p1, pp1, sum; };
+ chan_t ch [2];
+};
+
+inline void SPC_Filter::enable( bool b ) { enabled = b; }
+
+inline void SPC_Filter::set_gain( int g ) { gain = g; }
+
+inline void SPC_Filter::set_bass( int b ) { bass = b; }
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.cpp
new file mode 100644
index 00000000..23633dc4
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.cpp
@@ -0,0 +1,339 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sap_Apu.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const max_frequency = 12000; // pure waves above this frequency are silenced
+
+static void gen_poly( unsigned mask, int count, byte out [] )
+{
+ unsigned n = 1;
+ do
+ {
+ int bits = 0;
+ int b = 0;
+ do
+ {
+ // implemented using "Galios configuration"
+ bits |= (n & 1) << b;
+ n = (n >> 1) ^ (mask * (n & 1));
+ }
+ while ( b++ < 7 );
+ *out++ = bits;
+ }
+ while ( --count );
+}
+
+// poly5
+int const poly5_len = (1 << 5) - 1;
+unsigned const poly5_mask = (1U << poly5_len) - 1;
+unsigned const poly5 = 0x167C6EA1;
+
+inline unsigned run_poly5( unsigned in, int shift )
+{
+ return (in << shift & poly5_mask) | (in >> (poly5_len - shift));
+}
+
+#define POLY_MASK( width, tap1, tap2 ) \
+ ((1U << (width - 1 - tap1)) | (1U << (width - 1 - tap2)))
+
+Sap_Apu_Impl::Sap_Apu_Impl()
+{
+ gen_poly( POLY_MASK( 4, 1, 0 ), sizeof poly4, poly4 );
+ gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 );
+ gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 );
+
+ if ( 0 ) // comment out to recauculate poly5 constant
+ {
+ byte poly5 [4];
+ gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 );
+ unsigned n = poly5 [3] * 0x1000000 + poly5 [2] * 0x10000 +
+ poly5 [1] * 0x100 + poly5 [0];
+ unsigned rev = n & 1;
+ for ( int i = 1; i < poly5_len; i++ )
+ rev |= (n >> i & 1) << (poly5_len - i);
+ dprintf( "poly5: 0x%08lX\n", rev );
+ }
+}
+
+void Sap_Apu::set_output( Blip_Buffer* b )
+{
+ for ( int i = 0; i < osc_count; ++i )
+ set_output( i, b );
+}
+
+Sap_Apu::Sap_Apu()
+{
+ impl = NULL;
+ set_output( NULL );
+}
+
+void Sap_Apu::reset( Sap_Apu_Impl* new_impl )
+{
+ impl = new_impl;
+ last_time = 0;
+ poly5_pos = 0;
+ poly4_pos = 0;
+ polym_pos = 0;
+ control = 0;
+
+ for ( int i = 0; i < osc_count; i++ )
+ memset( &oscs [i], 0, offsetof (osc_t,output) );
+}
+
+inline void Sap_Apu::calc_periods()
+{
+ // 15/64 kHz clock
+ int divider = 28;
+ if ( this->control & 1 )
+ divider = 114;
+
+ for ( int i = 0; i < osc_count; i++ )
+ {
+ osc_t* const osc = &oscs [i];
+
+ int const osc_reload = osc->regs [0]; // cache
+ int period = (osc_reload + 1) * divider;
+ static byte const fast_bits [osc_count] = { 1 << 6, 1 << 4, 1 << 5, 1 << 3 };
+ if ( this->control & fast_bits [i] )
+ {
+ period = osc_reload + 4;
+ if ( i & 1 )
+ {
+ period = osc_reload * 0x100 + osc [-1].regs [0] + 7;
+ if ( !(this->control & fast_bits [i - 1]) )
+ period = (period - 6) * divider;
+
+ if ( (osc [-1].regs [1] & 0x1F) > 0x10 )
+ dprintf( "Use of slave channel in 16-bit mode not supported\n" );
+ }
+ }
+ osc->period = period;
+ }
+}
+
+void Sap_Apu::run_until( blip_time_t end_time )
+{
+ calc_periods();
+ Sap_Apu_Impl* const impl = this->impl; // cache
+
+ // 17/9-bit poly selection
+ byte const* polym = impl->poly17;
+ int polym_len = poly17_len;
+ if ( this->control & 0x80 )
+ {
+ polym_len = poly9_len;
+ polym = impl->poly9;
+ }
+ polym_pos %= polym_len;
+
+ for ( int i = 0; i < osc_count; i++ )
+ {
+ osc_t* const osc = &oscs [i];
+ blip_time_t time = last_time + osc->delay;
+ blip_time_t const period = osc->period;
+
+ // output
+ Blip_Buffer* output = osc->output;
+ if ( output )
+ {
+ int const osc_control = osc->regs [1]; // cache
+ int volume = (osc_control & 0x0F) * 2;
+ if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency
+ ((osc_control & 0xA0) == 0xA0 && period < 1789773 / 2 / max_frequency) )
+ {
+ if ( !(osc_control & 0x10) )
+ volume >>= 1; // inaudible frequency = half volume
+
+ int delta = volume - osc->last_amp;
+ if ( delta )
+ {
+ osc->last_amp = volume;
+ output->set_modified();
+ impl->synth.offset( last_time, delta, output );
+ }
+
+ // TODO: doesn't maintain high pass flip-flop (very minor issue)
+ }
+ else
+ {
+ // high pass
+ static byte const hipass_bits [osc_count] = { 1 << 2, 1 << 1, 0, 0 };
+ blip_time_t period2 = 0; // unused if no high pass
+ blip_time_t time2 = end_time;
+ if ( this->control & hipass_bits [i] )
+ {
+ period2 = osc [2].period;
+ time2 = last_time + osc [2].delay;
+ if ( osc->invert )
+ {
+ // trick inner wave loop into inverting output
+ osc->last_amp -= volume;
+ volume = -volume;
+ }
+ }
+
+ if ( time < end_time || time2 < end_time )
+ {
+ // poly source
+ static byte const poly1 [] = { 0x55, 0x55 }; // square wave
+ byte const* poly = poly1;
+ int poly_len = 8 * sizeof poly1; // can be just 2 bits, but this is faster
+ int poly_pos = osc->phase & 1;
+ int poly_inc = 1;
+ if ( !(osc_control & 0x20) )
+ {
+ poly = polym;
+ poly_len = polym_len;
+ poly_pos = polym_pos;
+ if ( osc_control & 0x40 )
+ {
+ poly = impl->poly4;
+ poly_len = poly4_len;
+ poly_pos = poly4_pos;
+ }
+ poly_inc = period % poly_len;
+ poly_pos = (poly_pos + osc->delay) % poly_len;
+ }
+ poly_inc -= poly_len; // allows more optimized inner loop below
+
+ // square/poly5 wave
+ unsigned wave = poly5;
+ check( poly5 & 1 ); // low bit is set for pure wave
+ int poly5_inc = 0;
+ if ( !(osc_control & 0x80) )
+ {
+ wave = run_poly5( wave, (osc->delay + poly5_pos) % poly5_len );
+ poly5_inc = period % poly5_len;
+ }
+
+ output->set_modified();
+
+ // Run wave and high pass interleved with each catching up to the other.
+ // Disabled high pass has no performance effect since inner wave loop
+ // makes no compromise for high pass, and only runs once in that case.
+ int osc_last_amp = osc->last_amp;
+ do
+ {
+ // run high pass
+ if ( time2 < time )
+ {
+ int delta = -osc_last_amp;
+ if ( volume < 0 )
+ delta += volume;
+ if ( delta )
+ {
+ osc_last_amp += delta - volume;
+ volume = -volume;
+ impl->synth.offset( time2, delta, output );
+ }
+ }
+ while ( time2 <= time ) // must advance *past* time to avoid hang
+ time2 += period2;
+
+ // run wave
+ blip_time_t end = end_time;
+ if ( end > time2 )
+ end = time2;
+ while ( time < end )
+ {
+ if ( wave & 1 )
+ {
+ int amp = volume * (poly [poly_pos >> 3] >> (poly_pos & 7) & 1);
+ if ( (poly_pos += poly_inc) < 0 )
+ poly_pos += poly_len;
+ int delta = amp - osc_last_amp;
+ if ( delta )
+ {
+ osc_last_amp = amp;
+ impl->synth.offset( time, delta, output );
+ }
+ }
+ wave = run_poly5( wave, poly5_inc );
+ time += period;
+ }
+ }
+ while ( time < end_time || time2 < end_time );
+
+ osc->phase = poly_pos;
+ osc->last_amp = osc_last_amp;
+ }
+
+ osc->invert = 0;
+ if ( volume < 0 )
+ {
+ // undo inversion trickery
+ osc->last_amp -= volume;
+ osc->invert = 1;
+ }
+ }
+ }
+
+ // maintain divider
+ blip_time_t remain = end_time - time;
+ if ( remain > 0 )
+ {
+ int count = (remain + period - 1) / period;
+ osc->phase ^= count;
+ time += count * period;
+ }
+ osc->delay = time - end_time;
+ }
+
+ // advance polies
+ blip_time_t duration = end_time - last_time;
+ last_time = end_time;
+ poly4_pos = (poly4_pos + duration) % poly4_len;
+ poly5_pos = (poly5_pos + duration) % poly5_len;
+ polym_pos += duration; // will get %'d on next call
+}
+
+void Sap_Apu::write_data( blip_time_t time, int addr, int data )
+{
+ run_until( time );
+ int i = (addr - 0xD200) >> 1;
+ if ( (unsigned) i < osc_count )
+ {
+ oscs [i].regs [addr & 1] = data;
+ }
+ else if ( addr == 0xD208 )
+ {
+ control = data;
+ }
+ else if ( addr == 0xD209 )
+ {
+ oscs [0].delay = 0;
+ oscs [1].delay = 0;
+ oscs [2].delay = 0;
+ oscs [3].delay = 0;
+ }
+ /*
+ // TODO: are polynomials reset in this case?
+ else if ( addr == 0xD20F )
+ {
+ if ( (data & 3) == 0 )
+ polym_pos = 0;
+ }
+ */
+}
+
+void Sap_Apu::end_frame( blip_time_t end_time )
+{
+ if ( end_time > last_time )
+ run_until( end_time );
+
+ last_time -= end_time;
+ assert( last_time >= 0 );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.h
new file mode 100644
index 00000000..f7099373
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Apu.h
@@ -0,0 +1,103 @@
+// Atari POKEY sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef SAP_APU_H
+#define SAP_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+class Sap_Apu_Impl;
+
+class Sap_Apu {
+public:
+// Basics
+
+ // Sets buffer to generate sound into, or 0 to mute
+ void set_output( Blip_Buffer* );
+
+ // Emulates to time t, then writes data to addr
+ void write_data( blip_time_t t, int addr, int data );
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Same as set_output(), but for a particular channel
+ enum { osc_count = 4 };
+ void set_output( int index, Blip_Buffer* );
+
+ // Resets sound chip and sets Sap_Apu_Impl
+ void reset( Sap_Apu_Impl* impl );
+
+ // Registers are at io_addr to io_addr+io_size-1
+ enum { io_addr = 0xD200 };
+ enum { io_size = 0x0A };
+
+private:
+ // noncopyable
+ Sap_Apu( const Sap_Apu& );
+ Sap_Apu& operator = ( const Sap_Apu& );
+
+// Implementation
+public:
+ Sap_Apu();
+
+private:
+ struct osc_t
+ {
+ unsigned char regs [2];
+ unsigned char phase;
+ unsigned char invert;
+ int last_amp;
+ blip_time_t delay;
+ blip_time_t period; // always recalculated before use; here for convenience
+ Blip_Buffer* output;
+ };
+ osc_t oscs [osc_count];
+ Sap_Apu_Impl* impl;
+ blip_time_t last_time;
+ int poly5_pos;
+ int poly4_pos;
+ int polym_pos;
+ int control;
+
+ void calc_periods();
+ void run_until( blip_time_t );
+
+ enum { poly4_len = (1 << 4) - 1 };
+ enum { poly9_len = (1 << 9) - 1 };
+ enum { poly17_len = (1 << 17) - 1 };
+ friend class Sap_Apu_Impl;
+};
+
+// Common tables and Blip_Synth that can be shared among multiple Sap_Apu objects
+class Sap_Apu_Impl {
+public:
+ // Set treble with synth.treble_eq()
+ Blip_Synth_Norm synth;
+
+ // Sets overall volume, where 1.0is normal
+ void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); }
+
+
+// Implementation
+public:
+ Sap_Apu_Impl();
+
+private:
+ BOOST::uint8_t poly4 [Sap_Apu::poly4_len /8 + 1];
+ BOOST::uint8_t poly9 [Sap_Apu::poly9_len /8 + 1];
+ BOOST::uint8_t poly17 [Sap_Apu::poly17_len/8 + 1];
+ friend class Sap_Apu;
+};
+
+inline void Sap_Apu::set_output( int i, Blip_Buffer* b )
+{
+ assert( (unsigned) i < osc_count );
+ oscs [i].output = b;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp
new file mode 100644
index 00000000..fa132458
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.cpp
@@ -0,0 +1,192 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sap_Core.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const idle_addr = 0xD2D2;
+
+Sap_Core::Sap_Core()
+{
+ set_tempo( 1 );
+}
+
+void Sap_Core::push( int b )
+{
+ mem.ram [0x100 + cpu.r.sp--] = (byte) b;
+}
+
+void Sap_Core::jsr_then_stop( addr_t addr )
+{
+ cpu.r.pc = addr;
+
+ // Some rips pop three bytes off stack before RTS.
+ push( (idle_addr - 1) >> 8 );
+ push( idle_addr - 1 );
+
+ // 3 bytes so that RTI or RTS will jump to idle_addr.
+ // RTI will use the first two bytes as the address, 0xD2D2.
+ // RTS will use the last two bytes, 0xD2D1, which it internally increments.
+ push( (idle_addr - 1) >> 8 );
+ push( (idle_addr - 1) >> 8 );
+ push( idle_addr - 1 );
+}
+
+// Runs routine and allows it up to one second to return
+void Sap_Core::run_routine( addr_t addr )
+{
+ jsr_then_stop( addr );
+ run_cpu( lines_per_frame * base_scanline_period * 60 );
+ check( cpu.r.pc == idle_addr );
+ check( cpu.r.sp >= 0xFF - 6 );
+}
+
+inline void Sap_Core::call_init( int track )
+{
+ cpu.r.a = track;
+
+ switch ( info.type )
+ {
+ case 'B':
+ run_routine( info.init_addr );
+ break;
+
+ case 'C':
+ cpu.r.a = 0x70;
+ cpu.r.x = info.music_addr&0xFF;
+ cpu.r.y = info.music_addr >> 8;
+ run_routine( info.play_addr + 3 );
+ cpu.r.a = 0;
+ cpu.r.x = track;
+ run_routine( info.play_addr + 3 );
+ break;
+
+ case 'D':
+ check( info.fastplay == lines_per_frame );
+ jsr_then_stop( info.init_addr );
+ break;
+ }
+}
+
+void Sap_Core::setup_ram()
+{
+ memset( &mem, 0, sizeof mem );
+
+ ram() [idle_addr] = cpu.halt_opcode;
+
+ addr_t const irq_addr = idle_addr - 1;
+ ram() [irq_addr] = cpu.halt_opcode;
+ ram() [0xFFFE] = (byte) irq_addr;
+ ram() [0xFFFF] = irq_addr >> 8;
+}
+
+blargg_err_t Sap_Core::start_track( int track, info_t const& new_info )
+{
+ info = new_info;
+
+ check( ram() [idle_addr] == cpu.halt_opcode );
+
+ apu_ .reset( &apu_impl_ );
+ apu2_.reset( &apu_impl_ );
+
+ cpu.reset( ram() );
+
+ frame_start = 0;
+ next_play = play_period() * 4;
+ saved_state.pc = idle_addr;
+
+ time_mask = 0; // disables sound during init
+ call_init( track );
+ time_mask = ~0;
+
+ return blargg_ok;
+}
+
+blargg_err_t Sap_Core::run_until( time_t end )
+{
+ while ( cpu.time() < end )
+ {
+ time_t next = min( next_play, end );
+ if ( (run_cpu( next ) && cpu.r.pc != idle_addr) || cpu.error_count() )
+ // TODO: better error
+ return BLARGG_ERR( BLARGG_ERR_GENERIC, "Emulation error (illegal instruction)" );
+
+ if ( cpu.r.pc == idle_addr )
+ {
+ if ( saved_state.pc == idle_addr )
+ {
+ // no code to run until next play call
+ cpu.set_time( next );
+ }
+ else
+ {
+ // play had interrupted non-returning init, so restore registers
+ // init routine was running
+ check( cpu.r.sp == saved_state.sp - 3 );
+ cpu.r = saved_state;
+ saved_state.pc = idle_addr;
+ }
+ }
+
+ if ( cpu.time() >= next_play )
+ {
+ next_play += play_period();
+
+ if ( cpu.r.pc == idle_addr || info.type == 'D' )
+ {
+ // Save state if init routine is still running
+ if ( cpu.r.pc != idle_addr )
+ {
+ check( info.type == 'D' );
+ check( saved_state.pc == idle_addr );
+ saved_state = cpu.r;
+ }
+
+ addr_t addr = info.play_addr;
+ if ( info.type == 'C' )
+ addr += 6;
+ jsr_then_stop( addr );
+ }
+ else
+ {
+ dprintf( "init/play hadn't returned before next play call\n" );
+ }
+ }
+ }
+ return blargg_ok;
+}
+
+blargg_err_t Sap_Core::end_frame( time_t end )
+{
+ RETURN_ERR( run_until( end ) );
+
+ cpu.adjust_time( -end );
+
+ time_t frame_time = lines_per_frame * scanline_period;
+ while ( frame_start < end )
+ frame_start += frame_time;
+ frame_start -= end + frame_time;
+
+ if ( (next_play -= end) < 0 )
+ {
+ next_play = 0;
+ check( false );
+ }
+
+ apu_.end_frame( end );
+ if ( info.stereo )
+ apu2_.end_frame( end );
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.h
new file mode 100644
index 00000000..89ce0a95
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Core.h
@@ -0,0 +1,91 @@
+// Atari XL/XE SAP core CPU and RAM emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef SAP_CORE_H
+#define SAP_CORE_H
+
+#include "Sap_Apu.h"
+#include "Nes_Cpu.h"
+
+class Sap_Core {
+public:
+
+ // Sound chips and common state
+ Sap_Apu& apu() { return apu_; }
+ Sap_Apu& apu2() { return apu2_; }
+ Sap_Apu_Impl& apu_impl() { return apu_impl_; }
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ void set_tempo( double );
+
+ // Clears RAM and sets up default vectors, etc.
+ void setup_ram();
+
+ // 64K RAM to load file data blocks into
+ BOOST::uint8_t* ram() { return mem.ram; }
+
+ // Calls init routine and configures playback. RAM must have been
+ // set up already.
+ struct info_t {
+ int init_addr;
+ int play_addr;
+ int music_addr;
+ int type;
+ int fastplay;
+ bool stereo;
+ };
+ blargg_err_t start_track( int track, info_t const& );
+
+ // Ends time frame at time t, then begins new at time 0
+ typedef Nes_Cpu::time_t time_t; // Clock count
+ blargg_err_t end_frame( time_t t );
+
+
+// Implementation
+public:
+ Sap_Core();
+
+private:
+ enum { base_scanline_period = 114 };
+ enum { lines_per_frame = 312 };
+ typedef Nes_Cpu::addr_t addr_t;
+
+ time_t scanline_period;
+ time_t next_play;
+ time_t time_mask;
+ time_t frame_start;
+ Nes_Cpu cpu;
+ Nes_Cpu::registers_t saved_state;
+ info_t info;
+ Sap_Apu apu_;
+ Sap_Apu apu2_;
+
+ // large items
+ struct {
+ BOOST::uint8_t padding1 [ 0x100];
+ BOOST::uint8_t ram [0x10000];
+ BOOST::uint8_t padding2 [ 0x100];
+ } mem; // TODO: put on freestore
+ Sap_Apu_Impl apu_impl_;
+
+ void push( int b );
+ void jsr_then_stop( addr_t );
+ void run_routine( addr_t );
+ void call_init( int track );
+ bool run_cpu( time_t end );
+ int play_addr();
+ int read_d40b();
+ int read_mem( addr_t );
+ void write_D2xx( int d2xx, int data );
+
+ time_t time() const { return cpu.time() & time_mask; }
+ blargg_err_t run_until( time_t t );
+ time_t play_period() const { return info.fastplay * scanline_period; }
+};
+
+inline void Sap_Core::set_tempo( double t )
+{
+ scanline_period = (int) (base_scanline_period / t + 0.5);
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Cpu.cpp
new file mode 100644
index 00000000..2f1e5dcc
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Cpu.cpp
@@ -0,0 +1,96 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sap_Core.h"
+
+#include "blargg_endian.h"
+
+//#define CPU_LOG_MAX 100000
+//#include "nes_cpu_log.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// functions defined in same file as CPU emulator to help compiler's optimizer
+
+int Sap_Core::read_d40b()
+{
+ //dprintf( "D40B read\n" );
+ check( cpu.time() >= frame_start );
+ return ((unsigned) (cpu.time() - frame_start) / scanline_period % lines_per_frame) / 2;
+}
+
+void Sap_Core::write_D2xx( int d2xx, int data )
+{
+ addr_t const base = 0xD200;
+
+ if ( d2xx < apu_.io_size )
+ {
+ apu_.write_data( time(), d2xx + base, data );
+ return;
+ }
+
+ if ( (unsigned) (d2xx - 0x10) < apu2_.io_size && info.stereo )
+ {
+ apu2_.write_data( time(), d2xx + (base - 0x10), data );
+ return;
+ }
+
+ if ( d2xx == 0xD40A - base )
+ {
+ dprintf( "D40A write\n" );
+ time_t t = cpu.time();
+ time_t into_line = (t - frame_start) % scanline_period;
+ cpu.set_end_time( t - into_line + scanline_period );
+ return;
+ }
+
+ if ( (d2xx & ~0x0010) != 0x0F || data != 0x03 )
+ dprintf( "Unmapped write $%04X <- $%02X\n", d2xx + base, data );
+}
+
+inline int Sap_Core::read_mem( addr_t addr )
+{
+ int result = mem.ram [addr];
+ if ( addr == 0xD40B )
+ result = read_d40b();
+ else if ( (addr & 0xF900) == 0xD000 )
+ dprintf( "Unmapped read $%04X\n", addr );
+ return result;
+}
+
+
+#define READ_LOW( addr ) (ram [addr])
+#define WRITE_LOW( addr, data ) (ram [addr] = data)
+
+#define READ_MEM( addr ) read_mem( addr )
+#define WRITE_MEM( addr, data ) \
+{\
+ ram [addr] = data;\
+ int d2xx = addr - 0xD200;\
+ if ( (unsigned) d2xx < 0x100 )\
+ write_D2xx( d2xx, data );\
+}
+
+#define CPU cpu
+#define FLAT_MEM ram
+
+#define CPU_BEGIN \
+bool Sap_Core::run_cpu( time_t end )\
+{\
+ CPU.set_end_time( end );\
+ byte* const ram = this->mem.ram; /* cache */
+
+ #include "Nes_Cpu_run.h"
+
+ return cpu.time_past_end() < 0;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.cpp
new file mode 100644
index 00000000..ba2a164c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.cpp
@@ -0,0 +1,385 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sap_Emu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+Sap_Emu::Sap_Emu()
+{
+ set_type( gme_sap_type );
+ set_silence_lookahead( 6 );
+}
+
+Sap_Emu::~Sap_Emu() { }
+
+// Track info
+
+// Returns 16 or greater if not hex. Handles uppercase and lowercase.
+// Thoroughly tested and rejects ALL non-hex characters.
+inline int from_hex_char( int h )
+{
+ h -= 0x30;
+ if ( (unsigned) h > 9 )
+ h = ((h - 0x11) & 0xDF) + 10;
+ return h;
+}
+
+static int from_hex( byte const in [] )
+{
+ int 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 parse_int( byte const* io [], byte const* end )
+{
+ byte const* in = *io;
+ int n = 0;
+ while ( in < end )
+ {
+ int dig = *in - '0';
+ if ( (unsigned) dig > 9 )
+ break;
+ ++in;
+ n = n * 10 + dig;
+ }
+ if ( in == *io )
+ n = -1; // no numeric characters
+ *io = in;
+ return n;
+}
+
+static int from_dec( byte const in [], byte const* end )
+{
+ int n = parse_int( &in, end );
+ if ( in < end )
+ n = -1;
+ 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 int parse_time( byte const in [], byte const* end )
+{
+ int minutes = parse_int( &in, end );
+ if ( minutes < 0 || *in != ':' )
+ return 0;
+
+ ++in;
+ int seconds = parse_int( &in, end );
+ if ( seconds < 0 )
+ return 0;
+
+ int time = minutes * 60000 + seconds * 1000;
+ if ( *in == '.' )
+ {
+ byte const* start = ++in;
+ int msec = parse_int( &in, end );
+ if ( msec >= 0 )
+ {
+ // allow 1-3 digits
+ for ( int n = in - start; n < 3; n++ )
+ msec *= 10;
+ time += msec;
+ }
+ }
+
+ while ( in < end && *in <= ' ' )
+ ++in;
+
+ if ( end - in >= 4 && !memcmp( in, "LOOP", 4 ) )
+ time = -time;
+
+ return time;
+}
+
+static blargg_err_t parse_info( byte const in [], int size, Sap_Emu::info_t* out )
+{
+ out->track_count = 1;
+ out->author [0] = 0;
+ out->name [0] = 0;
+ out->copyright [0] = 0;
+
+ for ( int i = 0; i < Sap_Emu::max_tracks; i++ )
+ out->track_times [i] = 0;
+
+ if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) )
+ return blargg_err_file_type;
+
+ int time_count = 0;
+ 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( "TIME", tag, tag_len ) && time_count < Sap_Emu::max_tracks )
+ {
+ out->track_times [time_count++] = parse_time( in, line_end );
+ }
+ else if ( !strncmp( "INIT", tag, tag_len ) )
+ {
+ out->init_addr = from_hex( in );
+ if ( (unsigned) out->init_addr >= 0x10000 )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "init address" );
+ }
+ else if ( !strncmp( "PLAYER", tag, tag_len ) )
+ {
+ out->play_addr = from_hex( in );
+ if ( (unsigned) out->play_addr >= 0x10000 )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "play address" );
+ }
+ else if ( !strncmp( "MUSIC", tag, tag_len ) )
+ {
+ out->music_addr = from_hex( in );
+ if ( (unsigned) out->music_addr >= 0x10000 )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "music address" );
+ }
+ else if ( !strncmp( "SONGS", tag, tag_len ) )
+ {
+ out->track_count = from_dec( in, line_end );
+ if ( out->track_count <= 0 )
+ return BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "track count" );
+ }
+ else if ( !strncmp( "TYPE", tag, tag_len ) )
+ {
+ switch ( out->type = *in )
+ {
+ case 'S':
+ out->type = 'C';
+ case 'B':
+ case 'C':
+ case 'D':
+ break;
+
+ default:
+ return BLARGG_ERR( BLARGG_ERR_FILE_FEATURE, "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 BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "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 BLARGG_ERR( BLARGG_ERR_FILE_CORRUPT, "ROM data missing" );
+ out->rom_data = in + 2;
+
+ return blargg_ok;
+}
+
+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 track ) const
+{
+ copy_sap_fields( info_, out );
+
+ if ( track < max_tracks )
+ {
+ int time = info_.track_times [track];
+ if ( time )
+ {
+ if ( time > 0 )
+ {
+ out->loop_length = 0;
+ }
+ else
+ {
+ time = -time;
+ out->loop_length = time;
+ }
+ out->length = time;
+ }
+ }
+ return blargg_ok;
+}
+
+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 [], int size )
+ {
+ RETURN_ERR( parse_info( begin, size, &info ) );
+ set_track_count( info.track_count );
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ copy_sap_fields( info, out );
+ return blargg_ok;
+ }
+};
+
+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 [], int size )
+{
+ file_end = in + size;
+
+ info_.warning = NULL;
+ 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 );
+ core.apu_impl().volume( gain() );
+
+ 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 );
+
+ return setup_buffer( 1773447 );
+}
+
+void Sap_Emu::update_eq( blip_eq_t const& eq )
+{
+ core.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 )
+ core.apu2().set_output( i2, right );
+ else
+ core.apu().set_output( i, (info_.stereo ? left : center) );
+}
+
+// Emulation
+
+void Sap_Emu::set_tempo_( double t )
+{
+ core.set_tempo( t );
+}
+
+blargg_err_t Sap_Emu::start_track_( int track )
+{
+ RETURN_ERR( Classic_Emu::start_track_( track ) );
+
+ core.setup_ram();
+
+ // Copy file data to RAM
+ byte const* in = info_.rom_data;
+ while ( file_end - in >= 5 )
+ {
+ int start = get_le16( in );
+ int end = get_le16( in + 2 );
+ //dprintf( "Block $%04X-$%04X\n", start, end );
+ in += 4;
+ int len = end - start + 1;
+ if ( (unsigned) len > (unsigned) (file_end - in) )
+ {
+ set_warning( "Invalid file data block" );
+ break;
+ }
+
+ memcpy( core.ram() + start, in, len );
+ in += len;
+ if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
+ in += 2;
+ }
+
+ return core.start_track( track, info_ );
+}
+
+blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
+{
+ return core.end_frame( duration );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.h
new file mode 100644
index 00000000..198c27a1
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sap_Emu.h
@@ -0,0 +1,51 @@
+// Atari XL/XE SAP music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef SAP_EMU_H
+#define SAP_EMU_H
+
+#include "Classic_Emu.h"
+#include "Sap_Apu.h"
+#include "Sap_Core.h"
+
+class Sap_Emu : public Classic_Emu {
+public:
+ enum { max_tracks = 32 }; // TODO: no fixed limit
+
+ // SAP file info (see Sap_Core.h for more)
+ struct info_t : Sap_Core::info_t {
+ byte const* rom_data;
+ const char* warning;
+ int track_count;
+ int track_times [max_tracks];
+ char author [256];
+ char name [256];
+ char copyright [ 32];
+ };
+
+ // Info for currently loaded file
+ info_t const& info() const { return info_; }
+
+ static gme_type_t static_type() { return gme_sap_type; }
+
+// Implementation
+public:
+ Sap_Emu();
+ ~Sap_Emu();
+
+protected:
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_mem_( byte const [], int );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+
+private:
+ info_t info_;
+ byte const* file_end;
+ Sap_Core core;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.cpp
new file mode 100644
index 00000000..ff24b576
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.cpp
@@ -0,0 +1,108 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sgc_Core.h"
+
+/* Copyright (C) 2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void Sgc_Core::set_tempo( double t )
+{
+ set_play_period( clock_rate() / (header().rate ? 50 : 60) / t );
+}
+
+blargg_err_t Sgc_Core::load_( Data_Reader& dr )
+{
+ RETURN_ERR( Sgc_Impl::load_( dr ) );
+
+ if ( sega_mapping() && fm_apu_.supported() )
+ RETURN_ERR( fm_apu_.init( clock_rate(), clock_rate() / 72 ) );
+
+ set_tempo( 1.0 );
+ return blargg_ok;
+}
+
+blargg_err_t Sgc_Core::start_track( int t )
+{
+ if ( sega_mapping() )
+ {
+ apu_.reset();
+ fm_apu_.reset();
+ fm_accessed = false;
+ }
+ else
+ {
+ apu_.reset( 0x0003, 15 );
+ }
+
+ return Sgc_Impl::start_track( t );
+}
+
+blargg_err_t Sgc_Core::end_frame( time_t t )
+{
+ RETURN_ERR( Sgc_Impl::end_frame( t ) );
+ apu_.end_frame( t );
+ if ( sega_mapping() && fm_accessed )
+ {
+ if ( fm_apu_.supported() )
+ fm_apu_.end_frame( t );
+ else
+ set_warning( "FM sound not supported" );
+ }
+
+ return blargg_ok;
+}
+
+Sgc_Core::Sgc_Core()
+{ }
+
+Sgc_Core::~Sgc_Core()
+{ }
+
+void Sgc_Core::cpu_out( time_t time, addr_t addr, int data )
+{
+ int port = addr & 0xFF;
+
+ if ( sega_mapping() )
+ {
+ switch ( port )
+ {
+ case 0x06:
+ apu_.write_ggstereo( time, data );
+ return;
+
+ case 0x7E:
+ case 0x7F:
+ apu_.write_data( time, data ); dprintf( "$7E<-%02X\n", data );
+ return;
+
+ case 0xF0:
+ fm_accessed = true;
+ if ( fm_apu_.supported() )
+ fm_apu_.write_addr( data );//, dprintf( "$F0<-%02X\n", data );
+ return;
+
+ case 0xF1:
+ fm_accessed = true;
+ if ( fm_apu_.supported() )
+ fm_apu_.write_data( time, data );//, dprintf( "$F1<-%02X\n", data );
+ return;
+ }
+ }
+ else if ( port >= 0xE0 )
+ {
+ apu_.write_data( time, data );
+ return;
+ }
+
+ Sgc_Impl::cpu_out( time, addr, data );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.h
new file mode 100644
index 00000000..ef59194c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Core.h
@@ -0,0 +1,44 @@
+// Sega/Game Gear/Coleco SGC music file emulator core
+
+// Game_Music_Emu 0.6-pre
+#ifndef SGC_CORE_H
+#define SGC_CORE_H
+
+#include "Sgc_Impl.h"
+#include "Sms_Fm_Apu.h"
+#include "Sms_Apu.h"
+
+class Sgc_Core : public Sgc_Impl {
+public:
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ // Resets to 1.0 when loading file.
+ void set_tempo( double );
+
+ // Starts track, where 0 is the first.
+ blargg_err_t start_track( int );
+
+ // Ends time frame at time t
+ blargg_err_t end_frame( time_t t );
+
+ // SN76489 sound chip
+ Sms_Apu& apu() { return apu_; }
+ Sms_Fm_Apu& fm_apu() { return fm_apu_; }
+
+protected:
+ // Overrides
+ virtual void cpu_out( time_t, addr_t, int data );
+ virtual blargg_err_t load_( Data_Reader& );
+
+// Implementation
+public:
+ Sgc_Core();
+ ~Sgc_Core();
+
+private:
+ bool fm_accessed;
+ Sms_Apu apu_;
+ Sms_Fm_Apu fm_apu_;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Cpu.cpp
new file mode 100644
index 00000000..4f7d5816
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Cpu.cpp
@@ -0,0 +1,36 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sgc_Impl.h"
+
+#include "blargg_endian.h"
+//#include "z80_cpu_log.h"
+
+/* Copyright (C) 2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define OUT_PORT( addr, data ) cpu_out( TIME(), addr, data )
+#define IN_PORT( addr ) cpu_in( addr )
+#define WRITE_MEM( addr, data ) cpu_write( addr, data )
+#define IDLE_ADDR idle_addr
+#define CPU cpu
+#define RST_BASE vectors_addr
+
+#define CPU_BEGIN \
+bool Sgc_Impl::run_cpu( time_t end_time )\
+{\
+ cpu.set_end_time( end_time );
+
+ #include "Z80_Cpu_run.h"
+
+ return warning;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.cpp
new file mode 100644
index 00000000..0b7ff4d3
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.cpp
@@ -0,0 +1,136 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sgc_Emu.h"
+
+/* Copyright (C) 2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const osc_count = Sms_Apu::osc_count + Sms_Fm_Apu::osc_count;
+
+Sgc_Emu::Sgc_Emu()
+{
+ set_type( gme_sgc_type );
+ set_silence_lookahead( 6 );
+ set_gain( 1.2 );
+}
+
+Sgc_Emu::~Sgc_Emu() { }
+
+void Sgc_Emu::unload()
+{
+ core_.unload();
+ Music_Emu::unload();
+}
+
+// Track info
+
+static void copy_sgc_fields( Sgc_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 Sgc_Emu::track_info_( track_info_t* out, int ) const
+{
+ copy_sgc_fields( header(), out );
+ return blargg_ok;
+}
+
+struct Sgc_File : Gme_Info_
+{
+ Sgc_Emu::header_t h;
+
+ Sgc_File() { set_type( gme_sgc_type ); }
+
+ blargg_err_t load_( Data_Reader& in )
+ {
+ blargg_err_t err = in.read( &h, h.size );
+ if ( err )
+ return (blargg_is_err_type( err, blargg_err_file_eof ) ? blargg_err_file_type : err);
+
+ set_track_count( h.song_count );
+ if ( !h.valid_tag() )
+ return blargg_err_file_type;
+
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ copy_sgc_fields( h, out );
+ return blargg_ok;
+ }
+};
+
+static Music_Emu* new_sgc_emu () { return BLARGG_NEW Sgc_Emu ; }
+static Music_Emu* new_sgc_file() { return BLARGG_NEW Sgc_File; }
+
+gme_type_t_ const gme_sgc_type [1] = {{ "Z80 PSG", 0, &new_sgc_emu, &new_sgc_file, "SGC", 1 }};
+
+// Setup
+
+blargg_err_t Sgc_Emu::load_( Data_Reader& in )
+{
+ RETURN_ERR( core_.load( in ) );
+ set_warning( core_.warning() );
+ set_track_count( header().song_count );
+ set_voice_count( core_.sega_mapping() ? osc_count : core_.apu().osc_count );
+
+ core_.apu ().volume( gain() );
+ core_.fm_apu().volume( gain() );
+
+ static const char* const names [osc_count + 1] = {
+ "Square 1", "Square 2", "Square 3", "Noise", "FM"
+ };
+ set_voice_names( names );
+
+ static int const types [osc_count + 1] = {
+ wave_type+1, wave_type+2, wave_type+3, mixed_type+1, mixed_type+2
+ };
+ set_voice_types( types );
+
+ return setup_buffer( core_.clock_rate() );
+}
+
+void Sgc_Emu::update_eq( blip_eq_t const& eq )
+{
+ core_.apu ().treble_eq( eq );
+ core_.fm_apu().treble_eq( eq );
+}
+
+void Sgc_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ if ( i < core_.apu().osc_count )
+ core_.apu().set_output( i, c, l, r );
+ else
+ core_.fm_apu().set_output( c, l, r );
+}
+
+void Sgc_Emu::set_tempo_( double t )
+{
+ core_.set_tempo( t );
+}
+
+blargg_err_t Sgc_Emu::start_track_( int track )
+{
+ RETURN_ERR( core_.start_track( track ) );
+ return Classic_Emu::start_track_( track );
+}
+
+blargg_err_t Sgc_Emu::run_clocks( blip_time_t& duration, int )
+{
+ RETURN_ERR( core_.end_frame( duration ) );
+ set_warning( core_.warning() );
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.h
new file mode 100644
index 00000000..6fed10ad
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Emu.h
@@ -0,0 +1,43 @@
+// Sega/Game Gear/Coleco SGC music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef SGC_EMU_H
+#define SGC_EMU_H
+
+#include "Classic_Emu.h"
+#include "Sgc_Core.h"
+
+class Sgc_Emu : public Classic_Emu {
+public:
+ // SGC file header (see Sgc_Impl.h)
+ typedef Sgc_Core::header_t header_t;
+
+ // Header for currently loaded file
+ header_t const& header() const { return core_.header(); }
+
+ // Sets 0x2000-byte Coleco BIOS. Necessary to play Coleco tracks.
+ static void set_coleco_bios( void const* p ){ Sgc_Core::set_coleco_bios( p ); }
+
+ static gme_type_t static_type() { return gme_sgc_type; }
+
+// Internal
+public:
+ Sgc_Emu();
+ ~Sgc_Emu();
+
+protected:
+ // Classic_Emu overrides
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t load_( Data_Reader& );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+ virtual void unload();
+
+private:
+ Sgc_Core core_;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.cpp
new file mode 100644
index 00000000..01c8ae4f
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.cpp
@@ -0,0 +1,225 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Sgc_Impl.h"
+
+/* Copyright (C) 2006-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+void const* Sgc_Impl::coleco_bios;
+
+Sgc_Impl::Sgc_Impl() :
+ rom( bank_size )
+{
+ assert( offsetof (header_t,copyright [32]) == header_t::size );
+}
+
+Sgc_Impl::~Sgc_Impl()
+{ }
+
+bool Sgc_Impl::header_t::valid_tag() const
+{
+ return 0 == memcmp( tag, "SGC\x1A", 4 );
+}
+
+blargg_err_t Sgc_Impl::load_( Data_Reader& in )
+{
+ RETURN_ERR( rom.load( in, header_.size, &header_, 0 ) );
+
+ if ( !header_.valid_tag() )
+ return blargg_err_file_type;
+
+ if ( header_.vers != 1 )
+ set_warning( "Unknown file version" );
+
+ if ( header_.system > 2 )
+ set_warning( "Unknown system" );
+
+ addr_t load_addr = get_le16( header_.load_addr );
+ if ( load_addr < 0x400 )
+ set_warning( "Invalid load address" );
+
+ rom.set_addr( load_addr );
+ play_period = clock_rate() / 60;
+
+ if ( sega_mapping() )
+ {
+ RETURN_ERR( ram.resize( 0x2000 + Sgc_Cpu::page_padding ) );
+ RETURN_ERR( ram2.resize( bank_size + Sgc_Cpu::page_padding ) );
+ }
+ else
+ {
+ RETURN_ERR( ram.resize( 0x400 + Sgc_Cpu::page_padding ) );
+ }
+
+ RETURN_ERR( vectors.resize( Sgc_Cpu::page_size + Sgc_Cpu::page_padding ) );
+
+ // TODO: doesn't need to be larger than page size, if we do mapping calls right
+ RETURN_ERR( unmapped_write.resize( bank_size ) );
+
+ return blargg_ok;
+}
+
+void Sgc_Impl::unload()
+{
+ rom.clear();
+ vectors.clear();
+ ram.clear();
+ ram2.clear();
+ unmapped_write.clear();
+ Gme_Loader::unload();
+}
+
+blargg_err_t Sgc_Impl::start_track( int track )
+{
+ memset( ram .begin(), 0, ram .size() );
+ memset( ram2.begin(), 0, ram2.size() );
+ memset( vectors.begin(), 0xFF, vectors.size() );
+ cpu.reset( unmapped_write.begin(), rom.unmapped() );
+
+ if ( sega_mapping() )
+ {
+ vectors_addr = 0x10000 - Sgc_Cpu::page_size;
+ idle_addr = vectors_addr;
+ for ( int i = 1; i < 8; ++i )
+ {
+ vectors [i*8 + 0] = 0xC3; // JP addr
+ vectors [i*8 + 1] = header_.rst_addrs [i*2 + 0];
+ vectors [i*8 + 2] = header_.rst_addrs [i*2 + 1];
+ }
+
+ cpu.map_mem( 0xC000, 0x2000, ram.begin() );
+ cpu.map_mem( vectors_addr, cpu.page_size, unmapped_write.begin(), vectors.begin() );
+
+ bank2 = NULL;
+ for ( int i = 0; i < 4; ++i )
+ cpu_write( 0xFFFC + i, header_.mapping [i] );
+ }
+ else
+ {
+ if ( !coleco_bios )
+ return BLARGG_ERR( BLARGG_ERR_CALLER, "Coleco BIOS not set" );
+
+ vectors_addr = 0;
+ cpu.map_mem( 0, 0x2000, unmapped_write.begin(), coleco_bios );
+ for ( int i = 0; i < 8; ++i )
+ cpu.map_mem( 0x6000 + i*0x400, 0x400, ram.begin() );
+
+ idle_addr = 0x2000;
+ cpu.map_mem( 0x2000, cpu.page_size, unmapped_write.begin(), vectors.begin() );
+
+ for ( int i = 0; i < 0x8000 / bank_size; ++i )
+ {
+ int addr = 0x8000 + i*bank_size;
+ cpu.map_mem( addr, bank_size, unmapped_write.begin(), rom.at_addr( addr ) );
+ }
+ }
+
+ cpu.r.sp = get_le16( header_.stack_ptr );
+ cpu.r.b.a = track;
+ next_play = play_period;
+
+ jsr( header_.init_addr );
+
+ return blargg_ok;
+}
+
+// Emulation
+
+void Sgc_Impl::jsr( byte const (&addr) [2] )
+{
+ *cpu.write( --cpu.r.sp ) = idle_addr >> 8;
+ *cpu.write( --cpu.r.sp ) = idle_addr & 0xFF;
+ cpu.r.pc = get_le16( addr );
+}
+
+void Sgc_Impl::set_bank( int bank, void const* data )
+{
+ //dprintf( "map bank %d to %p\n", bank, (byte*) data - rom.at_addr( 0 ) );
+ cpu.map_mem( bank * bank_size, bank_size, unmapped_write.begin(), data );
+}
+
+void Sgc_Impl::cpu_write( addr_t addr, int data )
+{
+ if ( (addr ^ 0xFFFC) > 3 || !sega_mapping() )
+ {
+ *cpu.write( addr ) = data;
+ return;
+ }
+
+ switch ( addr )
+ {
+ case 0xFFFC:
+ cpu.map_mem( 2 * bank_size, bank_size, ram2.begin() );
+ if ( data & 0x08 )
+ break;
+
+ bank2 = ram2.begin();
+ // FALL THROUGH
+
+ case 0xFFFF: {
+ bool rom_mapped = (cpu.read( 2 * bank_size ) == bank2);
+ bank2 = rom.at_addr( data * bank_size );
+ if ( rom_mapped )
+ set_bank( 2, bank2 );
+ break;
+ }
+
+ case 0xFFFD:
+ set_bank( 0, rom.at_addr( data * bank_size ) );
+ break;
+
+ case 0xFFFE:
+ set_bank( 1, rom.at_addr( data * bank_size ) );
+ break;
+ }
+}
+
+int Sgc_Impl::cpu_in( addr_t addr )
+{
+ dprintf( "in %02X\n", addr );
+ return 0;
+}
+
+void Sgc_Impl::cpu_out( time_t, addr_t addr, int )
+{
+ dprintf( "out %02X\n", addr & 0xFF );
+}
+
+blargg_err_t Sgc_Impl::end_frame( time_t end )
+{
+ while ( cpu.time() < end )
+ {
+ time_t next = min( end, next_play );
+ if ( run_cpu( next ) )
+ {
+ set_warning( "Unsupported CPU instruction" );
+ cpu.set_time( next );
+ }
+
+ if ( cpu.r.pc == idle_addr )
+ cpu.set_time( next );
+
+ if ( cpu.time() >= next_play )
+ {
+ next_play += play_period;
+ if ( cpu.r.pc == idle_addr )
+ jsr( header_.play_addr );
+ }
+ }
+
+ next_play -= end;
+ check( next_play >= 0 );
+ cpu.adjust_time( -end );
+
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.h b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.h
new file mode 100644
index 00000000..21b7e3f4
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sgc_Impl.h
@@ -0,0 +1,114 @@
+// Sega/Game Gear/Coleco SGC music file emulator implementation internals
+
+// Game_Music_Emu 0.6-pre
+#ifndef SGC_IMPL_H
+#define SGC_IMPL_H
+
+#include "Gme_Loader.h"
+#include "Rom_Data.h"
+#include "Z80_Cpu.h"
+
+class Sgc_Impl : public Gme_Loader {
+public:
+
+ // SGC file header
+ struct header_t
+ {
+ enum { size = 0xA0 };
+
+ char tag [4]; // "SGC\x1A"
+ byte vers; // 0x01
+ byte rate; // 0=NTSC 1=PAL
+ byte reserved1 [2];
+ byte load_addr [2];
+ byte init_addr [2];
+ byte play_addr [2];
+ byte stack_ptr [2];
+ byte reserved2 [2];
+ byte rst_addrs [7*2];
+ byte mapping [4]; // Used by Sega only
+ byte first_song; // Song to start playing first
+ byte song_count;
+ byte first_effect;
+ byte last_effect;
+ byte system; // 0=Master System 1=Game Gear 2=Colecovision
+ byte reserved3 [23];
+ char game [32]; // strings can be 32 chars, NOT terminated
+ char author [32];
+ char copyright [32];
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+
+ int effect_count() const { return last_effect ? last_effect - first_effect + 1 : 0; }
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return header_; }
+
+ int clock_rate() const { return header_.rate ? 3546893 : 3579545; }
+
+ // 0x2000 bytes
+ static void set_coleco_bios( void const* p ) { coleco_bios = p; }
+
+ // Clocks between calls to play routine
+ typedef int time_t;
+ void set_play_period( time_t p ) { play_period = p; }
+
+ // 0 = first track
+ blargg_err_t start_track( int );
+
+ // Runs for t clocks
+ blargg_err_t end_frame( time_t t );
+
+ // True if Master System or Game Gear
+ bool sega_mapping() const;
+
+protected:
+ typedef Z80_Cpu Sgc_Cpu;
+ Sgc_Cpu cpu;
+
+ typedef int addr_t;
+ virtual void cpu_out( time_t, addr_t, int data ) BLARGG_PURE( ; )
+
+// Implementation
+public:
+ Sgc_Impl();
+ ~Sgc_Impl();
+ virtual void unload();
+
+protected:
+ virtual blargg_err_t load_( Data_Reader& );
+
+private:
+ enum { bank_size = 0x4000 };
+
+ Rom_Data rom;
+ time_t play_period;
+ time_t next_play;
+ void const* bank2; // ROM selected for bank 2, in case RAM is currently hiding it
+ addr_t vectors_addr; // RST vectors start here
+ addr_t idle_addr; // return address for init/play routines
+ static void const* coleco_bios;
+
+ // large items
+ header_t header_;
+ blargg_vector<byte> vectors;
+ blargg_vector<byte> ram;
+ blargg_vector<byte> ram2;
+ blargg_vector<byte> unmapped_write;
+
+ bool run_cpu( time_t end );
+ void jsr( byte const (&addr) [2] );
+ void cpu_write( addr_t, int data );
+ int cpu_in( addr_t );
+
+ void set_bank( int bank, void const* data );
+};
+
+inline bool Sgc_Impl::sega_mapping() const
+{
+ return header_.system <= 1;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.cpp
new file mode 100644
index 00000000..5ad62e43
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.cpp
@@ -0,0 +1,371 @@
+// Sms_Snd_Emu 0.1.1. http://www.slack.net/~ant/
+
+#include "Sms_Apu.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const noise_osc = 3;
+
+void Sms_Apu::volume( double vol )
+{
+ vol *= 0.85 / osc_count / 64;
+ norm_synth.volume( vol );
+ fast_synth.volume( vol );
+}
+
+void Sms_Apu::treble_eq( blip_eq_t const& eq )
+{
+ norm_synth.treble_eq( eq );
+ fast_synth.treble_eq( eq );
+}
+
+inline int Sms_Apu::calc_output( int i ) const
+{
+ int flags = ggstereo >> i;
+ return (flags >> 3 & 2) | (flags & 1);
+}
+
+void Sms_Apu::set_output( int i, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right )
+{
+ // Must be silent (all NULL), mono (left and right NULL), or stereo (none NULL)
+ require( !center || (center && !left && !right) || (center && left && right) );
+ require( (unsigned) i < osc_count ); // fails if you pass invalid osc index
+
+ if ( center )
+ {
+ unsigned const divisor = 16384 * 16 * 2;
+ min_tone_period = ((unsigned) center->clock_rate() + divisor/2) / divisor;
+ }
+
+ if ( !center || !left || !right )
+ {
+ left = center;
+ right = center;
+ }
+
+ Osc& o = oscs [i];
+ o.outputs [0] = NULL;
+ o.outputs [1] = right;
+ o.outputs [2] = left;
+ o.outputs [3] = center;
+ o.output = o.outputs [calc_output( i )];
+}
+
+void Sms_Apu::set_output( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ for ( int i = osc_count; --i >= 0; )
+ set_output( i, c, l, r );
+}
+
+static inline unsigned fibonacci_to_galois_lfsr( unsigned fibonacci, int width )
+{
+ unsigned galois = 0;
+ while ( --width >= 0 )
+ {
+ galois = (galois << 1) | (fibonacci & 1);
+ fibonacci >>= 1;
+ }
+ return galois;
+}
+
+void Sms_Apu::reset( unsigned feedback, int noise_width )
+{
+ last_time = 0;
+ latch = 0;
+ ggstereo = 0;
+
+ // Calculate noise feedback values
+ if ( !feedback || !noise_width )
+ {
+ feedback = 0x0009;
+ noise_width = 16;
+ }
+ looped_feedback = 1 << (noise_width - 1);
+ noise_feedback = fibonacci_to_galois_lfsr( feedback, noise_width );
+
+ // Reset oscs
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Osc& o = oscs [i];
+ o.output = NULL;
+ o.last_amp = 0;
+ o.delay = 0;
+ o.phase = 0;
+ o.period = 0;
+ o.volume = 15; // silent
+ }
+
+ oscs [noise_osc].phase = 0x8000;
+ write_ggstereo( 0, 0xFF );
+}
+
+Sms_Apu::Sms_Apu()
+{
+ min_tone_period = 7;
+
+ // Clear outputs to NULL FIRST
+ ggstereo = 0;
+ set_output( NULL );
+
+ volume( 1.0 );
+ reset();
+}
+
+void Sms_Apu::run_until( blip_time_t end_time )
+{
+ require( end_time >= last_time );
+ if ( end_time <= last_time )
+ return;
+
+ // Synthesize each oscillator
+ for ( int idx = osc_count; --idx >= 0; )
+ {
+ Osc& osc = oscs [idx];
+ int vol = 0;
+ int amp = 0;
+
+ // Determine what will be generated
+ Blip_Buffer* const out = osc.output;
+ if ( out )
+ {
+ // volumes [i] ~= 64 * pow( 1.26, 15 - i ) / pow( 1.26, 15 )
+ static unsigned char const volumes [16] = {
+ 64, 50, 40, 32, 25, 20, 16, 13, 10, 8, 6, 5, 4, 3, 2, 0
+ };
+
+ vol = volumes [osc.volume];
+ amp = (osc.phase & 1) * vol;
+
+ // Square freq above 16 kHz yields constant amplitude at half volume
+ if ( idx != noise_osc && osc.period < min_tone_period )
+ {
+ amp = vol >> 1;
+ vol = 0;
+ }
+
+ // Update amplitude
+ int delta = amp - osc.last_amp;
+ if ( delta )
+ {
+ osc.last_amp = amp;
+ norm_synth.offset( last_time, delta, out );
+ out->set_modified();
+ }
+ }
+
+ // Generate wave
+ blip_time_t time = last_time + osc.delay;
+ if ( time < end_time )
+ {
+ // Calculate actual period
+ int period = osc.period;
+ if ( idx == noise_osc )
+ {
+ period = 0x20 << (period & 3);
+ if ( period == 0x100 )
+ period = oscs [2].period * 2;
+ }
+ period *= 0x10;
+ if ( !period )
+ period = 0x10;
+
+ // Maintain phase when silent
+ int phase = osc.phase;
+ if ( !vol )
+ {
+ int count = (end_time - time + period - 1) / period;
+ time += count * period;
+ if ( idx != noise_osc ) // TODO: maintain noise LFSR phase?
+ phase ^= count & 1;
+ }
+ else
+ {
+ int delta = amp * 2 - vol;
+
+ if ( idx != noise_osc )
+ {
+ // Square
+ do
+ {
+ delta = -delta;
+ norm_synth.offset( time, delta, out );
+ time += period;
+ }
+ while ( time < end_time );
+ phase = (delta >= 0);
+ }
+ else
+ {
+ // Noise
+ unsigned const feedback = (osc.period & 4 ? noise_feedback : looped_feedback);
+ do
+ {
+ unsigned changed = phase + 1;
+ phase = ((phase & 1) * feedback) ^ (phase >> 1);
+ if ( changed & 2 ) // true if bits 0 and 1 differ
+ {
+ delta = -delta;
+ fast_synth.offset_inline( time, delta, out );
+ }
+ time += period;
+ }
+ while ( time < end_time );
+ check( phase );
+ }
+ osc.last_amp = (phase & 1) * vol;
+ out->set_modified();
+ }
+ osc.phase = phase;
+ }
+ osc.delay = time - end_time;
+ }
+ last_time = end_time;
+}
+
+void Sms_Apu::write_ggstereo( blip_time_t time, int data )
+{
+ require( (unsigned) data <= 0xFF );
+
+ run_until( time );
+ ggstereo = data;
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Osc& osc = oscs [i];
+
+ Blip_Buffer* old = osc.output;
+ osc.output = osc.outputs [calc_output( i )];
+ if ( osc.output != old )
+ {
+ int delta = -osc.last_amp;
+ if ( delta )
+ {
+ osc.last_amp = 0;
+ if ( old )
+ {
+ old->set_modified();
+ fast_synth.offset( last_time, delta, old );
+ }
+ }
+ }
+ }
+}
+
+void Sms_Apu::write_data( blip_time_t time, int data )
+{
+ require( (unsigned) data <= 0xFF );
+
+ run_until( time );
+
+ if ( data & 0x80 )
+ latch = data;
+
+ // We want the raw values written so our save state format can be
+ // as close to hardware as possible and unspecific to any emulator.
+ int idx = latch >> 5 & 3;
+ Osc& osc = oscs [idx];
+ if ( latch & 0x10 )
+ {
+ osc.volume = data & 0x0F;
+ }
+ else
+ {
+ if ( idx == noise_osc )
+ osc.phase = 0x8000; // reset noise LFSR
+
+ // Replace high 6 bits/low 4 bits of register with data
+ int lo = osc.period;
+ int hi = data << 4;
+ if ( idx == noise_osc || (data & 0x80) )
+ {
+ hi = lo;
+ lo = data;
+ }
+ osc.period = (hi & 0x3F0) | (lo & 0x00F);
+ }
+}
+
+void Sms_Apu::end_frame( blip_time_t end_time )
+{
+ if ( end_time > last_time )
+ run_until( end_time );
+
+ last_time -= end_time;
+ assert( last_time >= 0 );
+}
+
+#if SMS_APU_CUSTOM_STATE
+ #define REFLECT( x, y ) (save ? (io->y) = (x) : (x) = (io->y) )
+#else
+ #define REFLECT( x, y ) (save ? set_val( io->y, x ) : (void) ((x) = get_val( io->y )))
+
+ static unsigned get_val( byte const p [] )
+ {
+ return p [3] * 0x1000000 + p [2] * 0x10000 + p [1] * 0x100 + p [0];
+ }
+
+ static void set_val( byte p [], unsigned n )
+ {
+ p [0] = (byte) (n );
+ p [1] = (byte) (n >> 8);
+ p [2] = (byte) (n >> 16);
+ p [3] = (byte) (n >> 24);
+ }
+#endif
+
+inline const char* Sms_Apu::save_load( sms_apu_state_t* io, bool save )
+{
+ #if !SMS_APU_CUSTOM_STATE
+ assert( sizeof (sms_apu_state_t) == 128 );
+ #endif
+
+ // Format of data, where later format is incompatible with earlier
+ int format = io->format0;
+ REFLECT( format, format );
+ if ( format != io->format0 )
+ return "Unsupported sound save state format";
+
+ // Version of data, where later versions just add fields to the end
+ int version = 0;
+ REFLECT( version, version );
+
+ REFLECT( latch, latch );
+ REFLECT( ggstereo, ggstereo );
+
+ for ( int i = osc_count; --i >= 0; )
+ {
+ Osc& osc = oscs [i];
+ REFLECT( osc.period, periods [i] );
+ REFLECT( osc.volume, volumes [i] );
+ REFLECT( osc.delay, delays [i] );
+ REFLECT( osc.phase, phases [i] );
+ }
+
+ return 0;
+}
+
+void Sms_Apu::save_state( sms_apu_state_t* out )
+{
+ save_load( out, true );
+ #if !SMS_APU_CUSTOM_STATE
+ memset( out->unused, 0, sizeof out->unused );
+ #endif
+}
+
+blargg_err_t Sms_Apu::load_state( sms_apu_state_t const& in )
+{
+ RETURN_ERR( save_load( CONST_CAST(sms_apu_state_t*,&in), false ) );
+ write_ggstereo( 0, ggstereo );
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.h
new file mode 100644
index 00000000..1da4da30
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Apu.h
@@ -0,0 +1,128 @@
+// Sega Master System SN76489 PSG sound chip emulator
+
+// Sms_Snd_Emu 0.1.2
+#ifndef SMS_APU_H
+#define SMS_APU_H
+
+#include "blargg_common.h"
+#include "Blip_Buffer.h"
+
+struct sms_apu_state_t;
+
+class Sms_Apu {
+public:
+// Basics
+
+ // Sets buffer(s) to generate sound into, or 0 to mute. If only center is not 0,
+ // output is mono.
+ void set_output( Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Emulates to time t, then writes data to Game Gear left/right assignment byte
+ void write_ggstereo( blip_time_t t, int data );
+
+ // Emulates to time t, then writes data
+ void write_data( blip_time_t t, int data );
+
+ // Emulates to time t, then subtracts t from the current time.
+ // OK if previous write call had time slightly after t.
+ void end_frame( blip_time_t t );
+
+// More features
+
+ // Resets sound chip and sets noise feedback bits and width
+ void reset( unsigned noise_feedback = 0, int noise_width = 0 );
+
+ // Same as set_output(), but for a particular channel
+ // 0: Square 1, 1: Square 2, 2: Square 3, 3: Noise
+ enum { osc_count = 4 }; // 0 <= chan < osc_count
+ void set_output( int chan, Blip_Buffer* center, Blip_Buffer* left = NULL, Blip_Buffer* right = NULL );
+
+ // Sets overall volume, where 1.0 is normal
+ void volume( double );
+
+ // Sets treble equalization
+ void treble_eq( blip_eq_t const& );
+
+ // Saves full emulation state to state_out. Data format is portable and
+ // includes some extra space to avoid expansion in case more state needs
+ // to be stored in the future.
+ void save_state( sms_apu_state_t* state_out );
+
+ // Loads state. You should call reset() BEFORE this.
+ blargg_err_t load_state( sms_apu_state_t const& in );
+
+private:
+ // noncopyable
+ Sms_Apu( const Sms_Apu& );
+ Sms_Apu& operator = ( const Sms_Apu& );
+
+// Implementation
+public:
+ Sms_Apu();
+ ~Sms_Apu() { }
+ BLARGG_DISABLE_NOTHROW
+
+ // Use set_output() instead
+ BLARGG_DEPRECATED( void output ( Blip_Buffer* c ) { set_output( c, c, c ); } )
+ BLARGG_DEPRECATED( void output ( Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( c, l, r ); } )
+ BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c ) { set_output( i, c, c, c ); } )
+ BLARGG_DEPRECATED( void osc_output( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r ) { set_output( i, c, l, r ); } )
+
+private:
+ struct Osc
+ {
+ Blip_Buffer* outputs [4]; // NULL, right, left, center
+ Blip_Buffer* output;
+ int last_amp;
+
+ int volume;
+ int period;
+ int delay;
+ unsigned phase;
+ };
+
+ Osc oscs [osc_count];
+ int ggstereo;
+ int latch;
+
+ blip_time_t last_time;
+ int min_tone_period;
+ unsigned noise_feedback;
+ unsigned looped_feedback;
+ Blip_Synth_Fast fast_synth;
+ Blip_Synth_Norm norm_synth;
+
+ int calc_output( int i ) const;
+ void run_until( blip_time_t );
+ const char* save_load( sms_apu_state_t*, bool save );
+ friend class Sms_Apu_Tester;
+};
+
+struct sms_apu_state_t
+{
+ // If SMS_APU_CUSTOM_STATE is 1, values are stored as normal integers,
+ // so your code can then save and load them however it likes. Otherwise,
+ // they are 4-byte arrays in little-endian format, making entire
+ // structure suitable for direct storage on disk.
+
+#if SMS_APU_CUSTOM_STATE
+ typedef int val_t;
+#else
+ typedef unsigned char val_t [4];
+#endif
+
+ enum { format0 = 0x50414D53 };
+
+ val_t format;
+ val_t version;
+ val_t latch;
+ val_t ggstereo;
+ val_t periods [4];
+ val_t volumes [4];
+ val_t delays [4];
+ val_t phases [4];
+
+ val_t unused [12]; // for future expansion
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.cpp
new file mode 100644
index 00000000..55ad8beb
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.cpp
@@ -0,0 +1,80 @@
+#include "Sms_Fm_Apu.h"
+
+#include "blargg_source.h"
+
+Sms_Fm_Apu::Sms_Fm_Apu()
+{ }
+
+Sms_Fm_Apu::~Sms_Fm_Apu()
+{ }
+
+blargg_err_t Sms_Fm_Apu::init( double clock_rate, double sample_rate )
+{
+ period_ = clock_rate / sample_rate + 0.5;
+ CHECK_ALLOC( !apu.set_rate( sample_rate, clock_rate ) );
+
+ set_output( 0 );
+ volume( 1.0 );
+ reset();
+ return blargg_ok;
+}
+
+void Sms_Fm_Apu::reset()
+{
+ addr = 0;
+ next_time = 0;
+ last_amp = 0;
+
+ apu.reset();
+}
+
+void Sms_Fm_Apu::write_data( blip_time_t time, int data )
+{
+ if ( time > next_time )
+ run_until( time );
+
+ apu.write( addr, data );
+}
+
+void Sms_Fm_Apu::run_until( blip_time_t end_time )
+{
+ assert( end_time > next_time );
+
+ Blip_Buffer* const output = this->output_;
+ if ( !output )
+ {
+ next_time = end_time;
+ return;
+ }
+
+ blip_time_t time = next_time;
+ do
+ {
+ Ym2413_Emu::sample_t samples [2];
+ apu.run( 1, samples );
+ int amp = (samples [0] + samples [1]) >> 1;
+
+ int delta = amp - last_amp;
+ if ( delta )
+ {
+ last_amp = amp;
+ synth.offset_inline( time, delta, output );
+ }
+ time += period_;
+ }
+ while ( time < end_time );
+
+ next_time = time;
+}
+
+void Sms_Fm_Apu::end_frame( blip_time_t time )
+{
+ if ( time > next_time )
+ run_until( time );
+
+ next_time -= time;
+ assert( next_time >= 0 );
+
+ if ( output_ )
+ output_->set_modified();
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.h b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.h
new file mode 100644
index 00000000..5fc2ea1e
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Sms_Fm_Apu.h
@@ -0,0 +1,47 @@
+#ifndef SMS_FM_APU_H
+#define SMS_FM_APU_H
+
+#include "Blip_Buffer.h"
+#include "Ym2413_Emu.h"
+
+class Sms_Fm_Apu {
+public:
+ static bool supported() { return Ym2413_Emu::supported(); }
+ blargg_err_t init( double clock_rate, double sample_rate );
+
+ void set_output( Blip_Buffer* b, Blip_Buffer* = NULL, Blip_Buffer* = NULL ) { output_ = b; }
+ void volume( double v ) { synth.volume( 0.4 / 4096 * v ); }
+ void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
+
+ void reset();
+
+ void write_addr( int data ) { addr = data; }
+ void write_data( blip_time_t, int data );
+
+ void end_frame( blip_time_t t );
+
+// Implementation
+public:
+ Sms_Fm_Apu();
+ ~Sms_Fm_Apu();
+ BLARGG_DISABLE_NOTHROW
+ enum { osc_count = 1 };
+ void set_output( int i, Blip_Buffer* b, Blip_Buffer* = NULL, Blip_Buffer* = NULL ) { output_ = b; }
+
+private:
+ Blip_Buffer* output_;
+ blip_time_t next_time;
+ int last_amp;
+ int addr;
+
+ int clock_;
+ int rate_;
+ blip_time_t period_;
+
+ Blip_Synth_Norm synth;
+ Ym2413_Emu apu;
+
+ void run_until( blip_time_t );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.cpp
new file mode 100644
index 00000000..caa2fef4
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.cpp
@@ -0,0 +1,378 @@
+// SPC emulation support: init, sample buffering, reset, SPC loading
+
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "Snes_Spc.h"
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define RAM (m.ram.ram)
+#define REGS (m.smp_regs [0])
+#define REGS_IN (m.smp_regs [1])
+
+// (n ? n : 256)
+#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
+
+
+//// Init
+
+blargg_err_t Snes_Spc::init()
+{
+ memset( &m, 0, sizeof m );
+ dsp.init( RAM );
+
+ m.tempo = tempo_unit;
+
+ // Most SPC music doesn't need ROM, and almost all the rest only rely
+ // on these two bytes
+ m.rom [0x3E] = 0xFF;
+ m.rom [0x3F] = 0xC0;
+
+ static unsigned char const cycle_table [128] =
+ {// 01 23 45 67 89 AB CD EF
+ 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0
+ 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1
+ 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2
+ 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3
+ 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4
+ 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5
+ 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6
+ 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7
+ 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8
+ 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9
+ 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A
+ 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B
+ 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C
+ 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D
+ 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E
+ 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F
+ };
+
+ // unpack cycle table
+ for ( int i = 0; i < 128; i++ )
+ {
+ int n = cycle_table [i];
+ m.cycle_table [i * 2 + 0] = n >> 4;
+ m.cycle_table [i * 2 + 1] = n & 0x0F;
+ }
+
+ #if SPC_LESS_ACCURATE
+ memcpy( reg_times, reg_times_, sizeof reg_times );
+ #endif
+
+ reset();
+ return blargg_ok;
+}
+
+void Snes_Spc::init_rom( uint8_t const in [rom_size] )
+{
+ memcpy( m.rom, in, sizeof m.rom );
+}
+
+void Snes_Spc::set_tempo( int t )
+{
+ m.tempo = t;
+ int const timer2_shift = 4; // 64 kHz
+ int const other_shift = 3; // 8 kHz
+
+ #if SPC_DISABLE_TEMPO
+ m.timers [2].prescaler = timer2_shift;
+ m.timers [1].prescaler = timer2_shift + other_shift;
+ m.timers [0].prescaler = timer2_shift + other_shift;
+ #else
+ if ( !t )
+ t = 1;
+ int const timer2_rate = 1 << timer2_shift;
+ int rate = (timer2_rate * tempo_unit + (t >> 1)) / t;
+ if ( rate < timer2_rate / 4 )
+ rate = timer2_rate / 4; // max 4x tempo
+ m.timers [2].prescaler = rate;
+ m.timers [1].prescaler = rate << other_shift;
+ m.timers [0].prescaler = rate << other_shift;
+ #endif
+}
+
+// Timer registers have been loaded. Applies these to the timers. Does not
+// reset timer prescalers or dividers.
+void Snes_Spc::timers_loaded()
+{
+ int i;
+ for ( i = 0; i < timer_count; i++ )
+ {
+ Timer* t = &m.timers [i];
+ t->period = IF_0_THEN_256( REGS [r_t0target + i] );
+ t->enabled = REGS [r_control] >> i & 1;
+ t->counter = REGS_IN [r_t0out + i] & 0x0F;
+ }
+
+ set_tempo( m.tempo );
+}
+
+// Loads registers from unified 16-byte format
+void Snes_Spc::load_regs( uint8_t const in [reg_count] )
+{
+ memcpy( REGS, in, reg_count );
+ memcpy( REGS_IN, REGS, reg_count );
+
+ // These always read back as 0
+ REGS_IN [r_test ] = 0;
+ REGS_IN [r_control ] = 0;
+ REGS_IN [r_t0target] = 0;
+ REGS_IN [r_t1target] = 0;
+ REGS_IN [r_t2target] = 0;
+}
+
+// RAM was just loaded from SPC, with $F0-$FF containing SMP registers
+// and timer counts. Copies these to proper registers.
+void Snes_Spc::ram_loaded()
+{
+ m.rom_enabled = 0;
+ load_regs( &RAM [0xF0] );
+
+ // Put STOP instruction around memory to catch PC underflow/overflow
+ memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 );
+ memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 );
+}
+
+// Registers were just loaded. Applies these new values.
+void Snes_Spc::regs_loaded()
+{
+ enable_rom( REGS [r_control] & 0x80 );
+ timers_loaded();
+}
+
+void Snes_Spc::reset_time_regs()
+{
+ m.cpu_error = NULL;
+ m.echo_accessed = 0;
+ m.spc_time = 0;
+ m.dsp_time = 0;
+ #if SPC_LESS_ACCURATE
+ m.dsp_time = clocks_per_sample + 1;
+ #endif
+
+ for ( int i = 0; i < timer_count; i++ )
+ {
+ Timer* t = &m.timers [i];
+ t->next_time = 1;
+ t->divider = 0;
+ }
+
+ regs_loaded();
+
+ m.extra_clocks = 0;
+ reset_buf();
+}
+
+void Snes_Spc::reset_common( int timer_counter_init )
+{
+ int i;
+ for ( i = 0; i < timer_count; i++ )
+ REGS_IN [r_t0out + i] = timer_counter_init;
+
+ // Run IPL ROM
+ memset( &m.cpu_regs, 0, sizeof m.cpu_regs );
+ m.cpu_regs.pc = rom_addr;
+
+ REGS [r_test ] = 0x0A;
+ REGS [r_control] = 0xB0; // ROM enabled, clear ports
+ for ( i = 0; i < port_count; i++ )
+ REGS_IN [r_cpuio0 + i] = 0;
+
+ reset_time_regs();
+}
+
+void Snes_Spc::soft_reset()
+{
+ reset_common( 0 );
+ dsp.soft_reset();
+}
+
+void Snes_Spc::reset()
+{
+ memset( RAM, 0xFF, 0x10000 );
+ ram_loaded();
+ reset_common( 0x0F );
+ dsp.reset();
+}
+
+char const Snes_Spc::signature [signature_size + 1] =
+ "SNES-SPC700 Sound File Data v0.30\x1A\x1A";
+
+blargg_err_t Snes_Spc::load_spc( void const* data, long size )
+{
+ spc_file_t const* const spc = (spc_file_t const*) data;
+
+ // be sure compiler didn't insert any padding into fle_t
+ assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 );
+
+ // Check signature and file size
+ if ( size < signature_size || memcmp( spc, signature, 27 ) )
+ return "Not an SPC file";
+
+ if ( size < spc_min_file_size )
+ return "Corrupt SPC file";
+
+ // CPU registers
+ m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl;
+ m.cpu_regs.a = spc->a;
+ m.cpu_regs.x = spc->x;
+ m.cpu_regs.y = spc->y;
+ m.cpu_regs.psw = spc->psw;
+ m.cpu_regs.sp = spc->sp;
+
+ // RAM and registers
+ memcpy( RAM, spc->ram, 0x10000 );
+ ram_loaded();
+
+ // DSP registers
+ dsp.load( spc->dsp );
+
+ reset_time_regs();
+
+ return blargg_ok;
+}
+
+void Snes_Spc::clear_echo()
+{
+ if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) )
+ {
+ int addr = 0x100 * dsp.read( Spc_Dsp::r_esa );
+ int end = addr + 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F);
+ if ( end > 0x10000 )
+ end = 0x10000;
+ memset( &RAM [addr], 0xFF, end - addr );
+ }
+}
+
+
+//// Sample output
+
+void Snes_Spc::reset_buf()
+{
+ // Start with half extra buffer of silence
+ sample_t* out = m.extra_buf;
+ while ( out < &m.extra_buf [extra_size / 2] )
+ *out++ = 0;
+
+ m.extra_pos = out;
+ m.buf_begin = NULL;
+
+ dsp.set_output( NULL, 0 );
+}
+
+void Snes_Spc::set_output( sample_t out [], int size )
+{
+ require( (size & 1) == 0 ); // size must be even
+
+ m.extra_clocks &= clocks_per_sample - 1;
+ if ( out )
+ {
+ sample_t const* out_end = out + size;
+ m.buf_begin = out;
+ m.buf_end = out_end;
+
+ // Copy extra to output
+ sample_t const* in = m.extra_buf;
+ while ( in < m.extra_pos && out < out_end )
+ *out++ = *in++;
+
+ // Handle output being full already
+ if ( out >= out_end )
+ {
+ // Have DSP write to remaining extra space
+ out = dsp.extra();
+ out_end = &dsp.extra() [extra_size];
+
+ // Copy any remaining extra samples as if DSP wrote them
+ while ( in < m.extra_pos )
+ *out++ = *in++;
+ assert( out <= out_end );
+ }
+
+ dsp.set_output( out, out_end - out );
+ }
+ else
+ {
+ reset_buf();
+ }
+}
+
+void Snes_Spc::save_extra()
+{
+ // Get end pointers
+ sample_t const* main_end = m.buf_end; // end of data written to buf
+ sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra()
+ if ( m.buf_begin <= dsp_end && dsp_end <= main_end )
+ {
+ main_end = dsp_end;
+ dsp_end = dsp.extra(); // nothing in DSP's extra
+ }
+
+ // Copy any extra samples at these ends into extra_buf
+ sample_t* out = m.extra_buf;
+ sample_t const* in;
+ for ( in = m.buf_begin + sample_count(); in < main_end; in++ )
+ *out++ = *in;
+ for ( in = dsp.extra(); in < dsp_end ; in++ )
+ *out++ = *in;
+
+ m.extra_pos = out;
+ assert( out <= &m.extra_buf [extra_size] );
+}
+
+blargg_err_t Snes_Spc::play( int count, sample_t out [] )
+{
+ require( (count & 1) == 0 ); // must be even
+ if ( count )
+ {
+ set_output( out, count );
+ end_frame( count * (clocks_per_sample / 2) );
+ }
+
+ const char* err = m.cpu_error;
+ m.cpu_error = NULL;
+ return err;
+}
+
+blargg_err_t Snes_Spc::skip( int count )
+{
+ #if SPC_LESS_ACCURATE
+ if ( count > 2 * sample_rate * 2 )
+ {
+ set_output( NULL, 0 );
+
+ // Skip a multiple of 4 samples
+ time_t end = count;
+ count = (count & 3) + 1 * sample_rate * 2;
+ end = (end - count) * (clocks_per_sample / 2);
+
+ m.skipped_kon = 0;
+ m.skipped_koff = 0;
+
+ // Preserve DSP and timer synchronization
+ // TODO: verify that this really preserves it
+ int old_dsp_time = m.dsp_time + m.spc_time;
+ m.dsp_time = end - m.spc_time + skipping_time;
+ end_frame( end );
+ m.dsp_time = m.dsp_time - skipping_time + old_dsp_time;
+
+ dsp.write( Spc_Dsp::r_koff, m.skipped_koff & ~m.skipped_kon );
+ dsp.write( Spc_Dsp::r_kon , m.skipped_kon );
+ clear_echo();
+ }
+ #endif
+
+ return play( count, NULL );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.h b/plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.h
new file mode 100644
index 00000000..0882354a
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Snes_Spc.h
@@ -0,0 +1,291 @@
+// SNES SPC-700 APU emulator
+
+// snes_spc 0.9.0
+#ifndef SNES_SPC_H
+#define SNES_SPC_H
+
+#include "Spc_Dsp.h"
+#include "blargg_endian.h"
+
+struct Snes_Spc {
+public:
+ typedef BOOST::uint8_t uint8_t;
+
+ // Must be called once before using
+ blargg_err_t init();
+
+ // Sample pairs generated per second
+ enum { sample_rate = 32000 };
+
+// Emulator use
+
+ // Sets IPL ROM data. Library does not include ROM data. Most SPC music files
+ // don't need ROM, but a full emulator must provide this.
+ enum { rom_size = 0x40 };
+ void init_rom( uint8_t const rom [rom_size] );
+
+ // Sets destination for output samples
+ typedef short sample_t;
+ void set_output( sample_t* out, int out_size );
+
+ // Number of samples written to output since last set
+ int sample_count() const;
+
+ // Resets SPC to power-on state. This resets your output buffer, so you must
+ // call set_output() after this.
+ void reset();
+
+ // Emulates pressing reset switch on SNES. This resets your output buffer, so
+ // you must call set_output() after this.
+ void soft_reset();
+
+ // 1024000 SPC clocks per second, sample pair every 32 clocks
+ typedef int time_t;
+ enum { clock_rate = 1024000 };
+ enum { clocks_per_sample = 32 };
+
+ // Emulated port read/write at specified time
+ enum { port_count = 4 };
+ int read_port ( time_t, int port );
+ void write_port( time_t, int port, int data );
+
+ // Runs SPC to end_time and starts a new time frame at 0
+ void end_frame( time_t end_time );
+
+// Sound control
+
+ // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
+ // Reduces emulation accuracy.
+ enum { voice_count = 8 };
+ void mute_voices( int mask );
+
+ // If true, prevents channels and global volumes from being phase-negated.
+ void disable_surround( bool disable = true );
+
+ // If true, enables cubic interpolation
+ void interpolation_level( int level = 0 );
+
+ // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.
+ enum { tempo_unit = 0x100 };
+ void set_tempo( int );
+
+// SPC music files
+
+ // Loads SPC data into emulator
+ enum { spc_min_file_size = 0x10180 };
+ enum { spc_file_size = 0x10200 };
+ blargg_err_t load_spc( void const* in, long size );
+
+ // Clears echo region. Useful after loading an SPC as many have garbage in echo.
+ void clear_echo();
+
+ // Plays for count samples and write samples to out. Discards samples if out
+ // is NULL. Count must be a multiple of 2 since output is stereo.
+ blargg_err_t play( int count, sample_t out [] );
+
+ // Skips count samples. Several times faster than play() when using fast DSP.
+ blargg_err_t skip( int count );
+
+// State save/load (only available with accurate DSP)
+
+#if !SPC_NO_COPY_STATE_FUNCS
+ // Saves/loads state
+ enum { state_size = 67 * 1024 }; // maximum space needed when saving
+ typedef Spc_Dsp::copy_func_t copy_func_t;
+ void copy_state( unsigned char** io, copy_func_t );
+
+ // Writes minimal header to spc_out
+ static void init_header( void* spc_out );
+
+ // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out.
+ // Does not set up SPC header; use init_header() for that.
+ void save_spc( void* spc_out );
+
+ // Returns true if new key-on events occurred since last check. Useful for
+ // trimming silence while saving an SPC.
+ bool check_kon();
+#endif
+
+public:
+ // TODO: document
+ struct regs_t
+ {
+ int pc;
+ int a;
+ int x;
+ int y;
+ int psw;
+ int sp;
+ };
+ regs_t& smp_regs() { return m.cpu_regs; }
+
+ uint8_t* smp_ram() { return m.ram.ram; }
+
+ void run_until( time_t t ) { run_until_( t ); }
+public:
+ BLARGG_DISABLE_NOTHROW
+
+ typedef BOOST::uint16_t uint16_t;
+
+ // Time relative to m_spc_time. Speeds up code a bit by eliminating need to
+ // constantly add m_spc_time to time from CPU. CPU uses time that ends at
+ // 0 to eliminate reloading end time every instruction. It pays off.
+ typedef int rel_time_t;
+
+ struct Timer
+ {
+ rel_time_t next_time; // time of next event
+ int prescaler;
+ int period;
+ int divider;
+ int enabled;
+ int counter;
+ };
+ enum { reg_count = 0x10 };
+ enum { timer_count = 3 };
+ enum { extra_size = Spc_Dsp::extra_size };
+
+ enum { signature_size = 35 };
+
+private:
+ Spc_Dsp dsp;
+
+ #if SPC_LESS_ACCURATE
+ static signed char const reg_times_ [256];
+ signed char reg_times [256];
+ #endif
+
+ struct state_t
+ {
+ Timer timers [timer_count];
+
+ uint8_t smp_regs [2] [reg_count];
+
+ regs_t cpu_regs;
+
+ rel_time_t dsp_time;
+ time_t spc_time;
+ bool echo_accessed;
+
+ int tempo;
+ int skipped_kon;
+ int skipped_koff;
+ const char* cpu_error;
+
+ int extra_clocks;
+ sample_t* buf_begin;
+ sample_t const* buf_end;
+ sample_t* extra_pos;
+ sample_t extra_buf [extra_size];
+
+ int rom_enabled;
+ uint8_t rom [rom_size];
+ uint8_t hi_ram [rom_size];
+
+ unsigned char cycle_table [256];
+
+ struct
+ {
+ // padding to neutralize address overflow
+ union {
+ uint8_t padding1 [0x100];
+ uint16_t align; // makes compiler align data for 16-bit access
+ } padding1 [1];
+ uint8_t ram [0x10000];
+ uint8_t padding2 [0x100];
+ } ram;
+ };
+ state_t m;
+
+ enum { rom_addr = 0xFFC0 };
+
+ enum { skipping_time = 127 };
+
+ // Value that padding should be filled with
+ enum { cpu_pad_fill = 0xFF };
+
+ enum {
+ r_test = 0x0, r_control = 0x1,
+ r_dspaddr = 0x2, r_dspdata = 0x3,
+ r_cpuio0 = 0x4, r_cpuio1 = 0x5,
+ r_cpuio2 = 0x6, r_cpuio3 = 0x7,
+ r_f8 = 0x8, r_f9 = 0x9,
+ r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,
+ r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF
+ };
+
+ void timers_loaded();
+ void enable_rom( int enable );
+ void reset_buf();
+ void save_extra();
+ void load_regs( uint8_t const in [reg_count] );
+ void ram_loaded();
+ void regs_loaded();
+ void reset_time_regs();
+ void reset_common( int timer_counter_init );
+
+ Timer* run_timer_ ( Timer* t, rel_time_t );
+ Timer* run_timer ( Timer* t, rel_time_t );
+ int dsp_read ( rel_time_t );
+ void dsp_write ( int data, rel_time_t );
+ void cpu_write_smp_reg_( int data, rel_time_t, int addr );
+ void cpu_write_smp_reg ( int data, rel_time_t, int addr );
+ void cpu_write_high ( int data, int i, rel_time_t );
+ void cpu_write ( int data, int addr, rel_time_t );
+ int cpu_read_smp_reg ( int i, rel_time_t );
+ int cpu_read ( int addr, rel_time_t );
+ unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t );
+
+ bool check_echo_access ( int addr );
+ uint8_t* run_until_( time_t end_time );
+
+ struct spc_file_t
+ {
+ char signature [signature_size];
+ uint8_t has_id666;
+ uint8_t version;
+ uint8_t pcl, pch;
+ uint8_t a;
+ uint8_t x;
+ uint8_t y;
+ uint8_t psw;
+ uint8_t sp;
+ char text [212];
+ uint8_t ram [0x10000];
+ uint8_t dsp [128];
+ uint8_t unused [0x40];
+ uint8_t ipl_rom [0x40];
+ };
+
+ static char const signature [signature_size + 1];
+
+ void save_regs( uint8_t out [reg_count] );
+};
+
+#include <assert.h>
+
+inline int Snes_Spc::sample_count() const { return (m.extra_clocks >> 5) * 2; }
+
+inline int Snes_Spc::read_port( time_t t, int port )
+{
+ assert( (unsigned) port < port_count );
+ return run_until_( t ) [port];
+}
+
+inline void Snes_Spc::write_port( time_t t, int port, int data )
+{
+ assert( (unsigned) port < port_count );
+ run_until_( t ) [0x10 + port] = data;
+}
+
+inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); }
+
+inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
+
+inline void Snes_Spc::interpolation_level( int level ) { dsp.interpolation_level( level ); }
+
+#if !SPC_NO_COPY_STATE_FUNCS
+inline bool Snes_Spc::check_kon() { return dsp.check_kon(); }
+#endif
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.cpp
new file mode 100644
index 00000000..02e8a354
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.cpp
@@ -0,0 +1,562 @@
+// Core SPC emulation: CPU, timers, SMP registers, memory
+
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "Snes_Spc.h"
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#define RAM (m.ram.ram)
+#define REGS (m.smp_regs [0])
+#define REGS_IN (m.smp_regs [1])
+
+// (n ? n : 256)
+#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1)
+
+// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which
+// do crazy echo buffer accesses.
+#ifndef SPC_MORE_ACCURACY
+ #define SPC_MORE_ACCURACY 0
+#endif
+
+
+//// Timers
+
+#if SPC_DISABLE_TEMPO
+ #define TIMER_DIV( t, n ) ((n) >> t->prescaler)
+ #define TIMER_MUL( t, n ) ((n) << t->prescaler)
+#else
+ #define TIMER_DIV( t, n ) ((n) / t->prescaler)
+ #define TIMER_MUL( t, n ) ((n) * t->prescaler)
+#endif
+
+Snes_Spc::Timer* Snes_Spc::run_timer_( Timer* t, rel_time_t time )
+{
+ int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
+ t->next_time += TIMER_MUL( t, elapsed );
+
+ if ( t->enabled )
+ {
+ int remain = IF_0_THEN_256( t->period - t->divider );
+ int divider = t->divider + elapsed;
+ int over = elapsed - remain;
+ if ( over >= 0 )
+ {
+ int n = over / t->period;
+ t->counter = (t->counter + 1 + n) & 0x0F;
+ divider = over - n * t->period;
+ }
+ t->divider = (uint8_t) divider;
+ }
+ return t;
+}
+
+inline Snes_Spc::Timer* Snes_Spc::run_timer( Timer* t, rel_time_t time )
+{
+ if ( time >= t->next_time )
+ t = run_timer_( t, time );
+ return t;
+}
+
+
+//// ROM
+
+void Snes_Spc::enable_rom( int enable )
+{
+ if ( m.rom_enabled != enable )
+ {
+ m.rom_enabled = enable;
+ if ( enable )
+ memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram );
+ memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size );
+ // TODO: ROM can still get overwritten when DSP writes to echo buffer
+ }
+}
+
+
+//// DSP
+
+#if SPC_LESS_ACCURATE
+ int const max_reg_time = 29;
+
+ /* Fast DSP only runs every 32nd clock. By adjusting the end time based
+ on which register is being accessed, in most cases the register access
+ is emulated at the precise time. */
+ signed char const Snes_Spc::reg_times_ [256] =
+ {
+ -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22,
+ 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23,
+ 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23,
+ 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24,
+ 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24,
+ 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24,
+ 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25,
+ 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
+
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
+ };
+
+ #define RUN_DSP( time, offset ) \
+ int count = (time) - (offset) - m.dsp_time;\
+ if ( count >= 0 )\
+ {\
+ int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\
+ m.dsp_time += clock_count;\
+ dsp.run( clock_count );\
+ }
+#else
+ #define RUN_DSP( time, offset ) \
+ {\
+ int count = (time) - m.dsp_time;\
+ if ( !SPC_MORE_ACCURACY || count )\
+ {\
+ assert( count > 0 );\
+ m.dsp_time = (time);\
+ dsp.run( count );\
+ }\
+ }
+#endif
+
+int Snes_Spc::dsp_read( rel_time_t time )
+{
+ RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
+
+ int result = dsp.read( REGS [r_dspaddr] & 0x7F );
+
+ #ifdef SPC_DSP_READ_HOOK
+ SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
+ #endif
+
+ return result;
+}
+
+inline void Snes_Spc::dsp_write( int data, rel_time_t time )
+{
+ RUN_DSP( time, reg_times [REGS [r_dspaddr]] )
+ #if SPC_LESS_ACCURATE
+ else if ( m.dsp_time == skipping_time )
+ {
+ int r = REGS [r_dspaddr];
+ if ( r == Spc_Dsp::r_kon )
+ m.skipped_kon |= data & ~dsp.read( Spc_Dsp::r_koff );
+
+ if ( r == Spc_Dsp::r_koff )
+ {
+ m.skipped_koff |= data;
+ m.skipped_kon &= ~data;
+ }
+ }
+ #endif
+
+ #ifdef SPC_DSP_WRITE_HOOK
+ SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
+ #endif
+
+ if ( REGS [r_dspaddr] <= 0x7F )
+ dsp.write( REGS [r_dspaddr], data );
+ else if ( !SPC_MORE_ACCURACY )
+ dprintf( "SPC wrote to DSP register > $7F\n" );
+}
+
+
+//// Memory access extras
+
+#if SPC_MORE_ACCURACY
+ #define MEM_ACCESS( time, addr ) \
+ {\
+ if ( time >= m.dsp_time )\
+ {\
+ RUN_DSP( time, max_reg_time );\
+ }\
+ }
+#elif !defined (NDEBUG)
+ // 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.
+
+ bool Snes_Spc::check_echo_access( int addr )
+ {
+ if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) )
+ {
+ int start = 0x100 * dsp.read( Spc_Dsp::r_esa );
+ int size = 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F);
+ int end = start + (size ? size : 4);
+ if ( start <= addr && addr < end )
+ {
+ if ( !m.echo_accessed )
+ {
+ m.echo_accessed = 1;
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+
+ #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
+#else
+ #define MEM_ACCESS( time, addr )
+#endif
+
+
+//// CPU write
+
+#if SPC_MORE_ACCURACY
+static unsigned char const glitch_probs [3] [256] =
+{
+ 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
+ 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
+ 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
+ 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
+ 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
+ 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
+ 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
+ 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
+ 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
+ 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
+ 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
+ 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
+ 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
+ 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
+ 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02,
+ 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01,
+
+ 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
+ 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
+ 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
+ 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
+ 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
+ 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
+ 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
+ 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
+ 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
+ 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
+ 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
+ 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
+ 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05,
+ 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01,
+ 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
+ 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01,
+
+ 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
+ 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
+ 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
+ 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
+ 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
+ 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
+ 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
+ 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
+ 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
+ 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
+ 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04,
+ 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02,
+ 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06,
+ 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03,
+ 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02,
+ 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03,
+};
+#endif
+
+// Read/write handlers are divided into multiple functions to keep rarely-used
+// functionality separate so often-used functionality can be optimized better
+// by compiler.
+
+// If write isn't preceded by read, data has this added to it
+int const no_read_before_write = 0x2000;
+
+void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, int addr )
+{
+ switch ( addr )
+ {
+ case r_t0target:
+ case r_t1target:
+ case r_t2target: {
+ Timer* t = &m.timers [addr - r_t0target];
+ int period = IF_0_THEN_256( data );
+ if ( t->period != period )
+ {
+ t = run_timer( t, time );
+ #if SPC_MORE_ACCURACY
+ // Insane behavior when target is written just after counter is
+ // clocked and counter matches new period and new period isn't 1, 2, 4, or 8
+ if ( t->divider == (period & 0xFF) &&
+ t->next_time == time + TIMER_MUL( t, 1 ) &&
+ ((period - 1) | ~0x0F) & period )
+ {
+ //dprintf( "SPC pathological timer target write\n" );
+
+ // If the period is 3, 5, or 9, there's a probability this behavior won't occur,
+ // based on the previous period
+ int prob = 0xFF;
+ int old_period = t->period & 0xFF;
+ if ( period == 3 ) prob = glitch_probs [0] [old_period];
+ if ( period == 5 ) prob = glitch_probs [1] [old_period];
+ if ( period == 9 ) prob = glitch_probs [2] [old_period];
+
+ // The glitch suppresses incrementing of one of the counter bits, based on
+ // the lowest set bit in the new period
+ int b = 1;
+ while ( !(period & b) )
+ b <<= 1;
+
+ if ( (rand() >> 4 & 0xFF) <= prob )
+ t->divider = (t->divider - b) & 0xFF;
+ }
+ #endif
+ t->period = period;
+ }
+ break;
+ }
+
+ case r_t0out:
+ case r_t1out:
+ case r_t2out:
+ if ( !SPC_MORE_ACCURACY )
+ dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
+
+ if ( data < no_read_before_write / 2 )
+ run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
+ break;
+
+ // Registers that act like RAM
+ case 0x8:
+ case 0x9:
+ REGS_IN [addr] = (uint8_t) data;
+ break;
+
+ case r_test:
+ if ( (uint8_t) data != 0x0A )
+ dprintf( "SPC wrote to test register\n" );
+ break;
+
+ case r_control:
+ // port clears
+ if ( data & 0x10 )
+ {
+ REGS_IN [r_cpuio0] = 0;
+ REGS_IN [r_cpuio1] = 0;
+ }
+ if ( data & 0x20 )
+ {
+ REGS_IN [r_cpuio2] = 0;
+ REGS_IN [r_cpuio3] = 0;
+ }
+
+ // timers
+ {
+ for ( int i = 0; i < timer_count; i++ )
+ {
+ Timer* t = &m.timers [i];
+ int enabled = data >> i & 1;
+ if ( t->enabled != enabled )
+ {
+ t = run_timer( t, time );
+ t->enabled = enabled;
+ if ( enabled )
+ {
+ t->divider = 0;
+ t->counter = 0;
+ }
+ }
+ }
+ }
+ enable_rom( data & 0x80 );
+ break;
+ }
+}
+
+void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, int addr )
+{
+ if ( addr == r_dspdata ) // 99%
+ dsp_write( data, time );
+ else
+ cpu_write_smp_reg_( data, time, addr );
+}
+
+void Snes_Spc::cpu_write_high( int data, int i, rel_time_t time )
+{
+ if ( i < rom_size )
+ {
+ m.hi_ram [i] = (uint8_t) data;
+ if ( m.rom_enabled )
+ RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM
+ }
+ else
+ {
+ assert( RAM [i + rom_addr] == (uint8_t) data );
+ RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding
+ cpu_write( data, i + rom_addr - 0x10000, time );
+ }
+}
+
+int const bits_in_int = CHAR_BIT * sizeof (int);
+
+void Snes_Spc::cpu_write( int data, int addr, rel_time_t time )
+{
+ MEM_ACCESS( time, addr )
+
+ // RAM
+ RAM [addr] = (uint8_t) data;
+ int reg = addr - 0xF0;
+ if ( reg >= 0 ) // 64%
+ {
+ // $F0-$FF
+ if ( reg < reg_count ) // 87%
+ {
+ REGS [reg] = (uint8_t) data;
+
+ // Ports
+ #ifdef SPC_PORT_WRITE_HOOK
+ if ( (unsigned) (reg - r_cpuio0) < port_count )
+ SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
+ (uint8_t) data, &REGS [r_cpuio0] );
+ #endif
+
+ // Registers other than $F2 and $F4-$F7
+ //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 )
+ // TODO: this is a bit on the fragile side
+ if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36%
+ cpu_write_smp_reg( data, time, reg );
+ }
+ // High mem/address wrap-around
+ else
+ {
+ reg -= rom_addr - 0xF0;
+ if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around
+ cpu_write_high( data, reg, time );
+ }
+ }
+}
+
+
+//// CPU read
+
+inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time )
+{
+ int result = REGS_IN [reg];
+ reg -= r_dspaddr;
+ // DSP addr and data
+ if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3
+ {
+ result = REGS [r_dspaddr];
+ if ( (unsigned) reg == 1 )
+ result = dsp_read( time ); // 0xF3
+ }
+ return result;
+}
+
+int Snes_Spc::cpu_read( int addr, rel_time_t time )
+{
+ MEM_ACCESS( time, addr )
+
+ // RAM
+ int result = RAM [addr];
+ int reg = addr - 0xF0;
+ if ( reg >= 0 ) // 40%
+ {
+ reg -= 0x10;
+ if ( (unsigned) reg >= 0xFF00 ) // 21%
+ {
+ reg += 0x10 - r_t0out;
+
+ // Timers
+ if ( (unsigned) reg < timer_count ) // 90%
+ {
+ Timer* t = &m.timers [reg];
+ if ( time >= t->next_time )
+ t = run_timer_( t, time );
+ result = t->counter;
+ t->counter = 0;
+ }
+ // Other registers
+ else if ( reg < 0 ) // 10%
+ {
+ result = cpu_read_smp_reg( reg + r_t0out, time );
+ }
+ else // 1%
+ {
+ assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 );
+ result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time );
+ }
+ }
+ }
+
+ return result;
+}
+
+
+//// Run
+
+// Prefix and suffix for CPU emulator function
+#define SPC_CPU_RUN_FUNC \
+BOOST::uint8_t* Snes_Spc::run_until_( time_t end_time )\
+{\
+ rel_time_t rel_time = m.spc_time - end_time;\
+ assert( rel_time <= 0 );\
+ m.spc_time = end_time;\
+ m.dsp_time += rel_time;\
+ m.timers [0].next_time += rel_time;\
+ m.timers [1].next_time += rel_time;\
+ m.timers [2].next_time += rel_time;
+
+#define SPC_CPU_RUN_FUNC_END \
+ m.spc_time += rel_time;\
+ m.dsp_time -= rel_time;\
+ m.timers [0].next_time -= rel_time;\
+ m.timers [1].next_time -= rel_time;\
+ m.timers [2].next_time -= rel_time;\
+ assert( m.spc_time <= end_time );\
+ return &REGS [r_cpuio0];\
+}
+
+int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
+
+void Snes_Spc::end_frame( time_t end_time )
+{
+ // Catch CPU up to as close to end as possible. If final instruction
+ // would exceed end, does NOT execute it and leaves m.spc_time < end.
+ if ( end_time > m.spc_time )
+ run_until_( end_time );
+
+ m.spc_time -= end_time;
+ m.extra_clocks += end_time;
+
+ // Greatest number of clocks early that emulation can stop early due to
+ // not being able to execute current instruction without going over
+ // allowed time.
+ assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 );
+
+ // Catch timers up to CPU
+ for ( int i = 0; i < timer_count; i++ )
+ run_timer( &m.timers [i], 0 );
+
+ // Catch DSP up to CPU
+ if ( m.dsp_time < 0 )
+ {
+ RUN_DSP( 0, max_reg_time );
+ }
+
+ // Save any extra samples beyond what should be generated
+ if ( m.buf_begin )
+ save_extra();
+}
+
+// Inclusion here allows static memory access functions and better optimization
+#include "Spc_Cpu.h"
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.h b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.h
new file mode 100644
index 00000000..706f7d33
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Cpu.h
@@ -0,0 +1,1225 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+/* Copyright (C) 2004-2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+//// Memory access
+
+#if SPC_MORE_ACCURACY
+ #define SUSPICIOUS_OPCODE( name ) ((void) 0)
+#else
+ #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" )
+#endif
+
+#define CPU_READ( time, offset, addr )\
+ cpu_read( addr, time + offset )
+
+#define CPU_WRITE( time, offset, addr, data )\
+ cpu_write( data, addr, time + offset )
+
+#if SPC_MORE_ACCURACY
+ #define CPU_READ_TIMER( time, offset, addr, out )\
+ { out = CPU_READ( time, offset, addr ); }
+
+#else
+ // timers are by far the most common thing read from dp
+ #define CPU_READ_TIMER( time, offset, addr_, out )\
+ {\
+ rel_time_t adj_time = time + offset;\
+ int dp_addr = addr_;\
+ int ti = dp_addr - (r_t0out + 0xF0);\
+ if ( (unsigned) ti < timer_count )\
+ {\
+ Timer* t = &m.timers [ti];\
+ if ( adj_time >= t->next_time )\
+ t = run_timer_( t, adj_time );\
+ out = t->counter;\
+ t->counter = 0;\
+ }\
+ else\
+ {\
+ out = ram [dp_addr];\
+ int i = dp_addr - 0xF0;\
+ if ( (unsigned) i < 0x10 )\
+ out = cpu_read_smp_reg( i, adj_time );\
+ }\
+ }
+#endif
+
+#define TIME_ADJ( n ) (n)
+
+#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out )
+#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) )
+#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) )
+
+#define DP_ADDR( addr ) (dp + (addr))
+
+#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out )
+#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) )
+#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data )
+
+#define READ_PROG16( addr ) GET_LE16( ram + (addr) )
+
+#define SET_PC( n ) (pc = ram + (n))
+#define GET_PC() (pc - ram)
+#define READ_PC( pc ) (*(pc))
+#define READ_PC16( pc ) GET_LE16( pc )
+
+// TODO: remove non-wrapping versions?
+#define SPC_NO_SP_WRAPAROUND 0
+
+#define SET_SP( v ) (sp = ram + 0x101 + (v))
+#define GET_SP() (sp - 0x101 - ram)
+
+#if SPC_NO_SP_WRAPAROUND
+#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v ))
+#define PUSH( v ) (void) (*--sp = (uint8_t) (v))
+#define POP( out ) (void) ((out) = *sp++)
+
+#else
+#define PUSH16( data )\
+{\
+ int addr = (sp -= 2) - ram;\
+ if ( addr > 0x100 )\
+ {\
+ SET_LE16( sp, data );\
+ }\
+ else\
+ {\
+ ram [(uint8_t) addr + 0x100] = (uint8_t) data;\
+ sp [1] = (uint8_t) (data >> 8);\
+ sp += 0x100;\
+ }\
+}
+
+#define PUSH( data )\
+{\
+ *--sp = (uint8_t) (data);\
+ if ( sp - ram == 0x100 )\
+ sp += 0x100;\
+}
+
+#define POP( out )\
+{\
+ out = *sp++;\
+ if ( sp - ram == 0x201 )\
+ {\
+ out = sp [-0x101];\
+ sp -= 0x100;\
+ }\
+}
+
+#endif
+
+#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel )
+
+unsigned Snes_Spc::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time )
+{
+ unsigned addr = READ_PC16( pc );
+ unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13);
+ return t << 8 & 0x100;
+}
+
+//// Status flag handling
+
+// Flags with hex value for clarity when used as mask.
+// Stored in indicated variable during emulation.
+int const n80 = 0x80; // nz
+int const v40 = 0x40; // psw
+int const p20 = 0x20; // dp
+int const b10 = 0x10; // psw
+int const h08 = 0x08; // psw
+int const i04 = 0x04; // psw
+int const z02 = 0x02; // nz
+int const c01 = 0x01; // c
+
+int const nz_neg_mask = 0x880; // either bit set indicates N flag set
+
+#define GET_PSW( out )\
+{\
+ out = psw & ~(n80 | p20 | z02 | c01);\
+ out |= c >> 8 & c01;\
+ out |= dp >> 3 & p20;\
+ out |= ((nz >> 4) | nz) & n80;\
+ if ( !(uint8_t) nz ) out |= z02;\
+}
+
+#define SET_PSW( in )\
+{\
+ psw = in;\
+ c = in << 8;\
+ dp = in << 3 & 0x100;\
+ nz = (in << 4 & 0x800) | (~in & z02);\
+}
+
+SPC_CPU_RUN_FUNC
+{
+ uint8_t* const ram = RAM;
+ int a = m.cpu_regs.a;
+ int x = m.cpu_regs.x;
+ int y = m.cpu_regs.y;
+ uint8_t const* pc;
+ uint8_t* sp;
+ int psw;
+ int c;
+ int nz;
+ int dp;
+
+ SET_PC( m.cpu_regs.pc );
+ SET_SP( m.cpu_regs.sp );
+ SET_PSW( m.cpu_regs.psw );
+
+ goto loop;
+
+
+ // Main loop
+
+cbranch_taken_loop:
+ pc += *(BOOST::int8_t const*) pc;
+inc_pc_loop:
+ pc++;
+loop:
+{
+ unsigned opcode;
+ unsigned data;
+
+ check( (unsigned) a < 0x100 );
+ check( (unsigned) x < 0x100 );
+ check( (unsigned) y < 0x100 );
+
+ opcode = *pc;
+ if ( (rel_time += m.cycle_table [opcode]) > 0 )
+ goto out_of_time;
+
+ #ifdef SPC_CPU_OPCODE_HOOK
+ SPC_CPU_OPCODE_HOOK( GET_PC(), opcode );
+ #endif
+
+ #ifdef CPU_INSTR_HOOK
+ CPU_INSTR_HOOK( GET_PC(), pc, a, x, y, GET_SP(), rel_time );
+ #endif
+
+ /*
+ //SUB_CASE_COUNTER( 1 );
+ #define PROFILE_TIMER_LOOP( op, addr, len )\
+ if ( opcode == op )\
+ {\
+ int cond = (unsigned) ((addr) - 0xFD) < 3 &&\
+ pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\
+ SUB_CASE_COUNTER( op && cond );\
+ }
+
+ PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 );
+ PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 );
+ PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 );
+ */
+
+ // TODO: if PC is at end of memory, this will get wrong operand (very obscure)
+ data = *++pc;
+ switch ( opcode )
+ {
+
+// Common instructions
+
+#define BRANCH( cond )\
+{\
+ pc++;\
+ pc += (BOOST::int8_t) data;\
+ if ( cond )\
+ goto loop;\
+ pc -= (BOOST::int8_t) data;\
+ rel_time -= 2;\
+ goto loop;\
+}
+
+ case 0xF0: // BEQ
+ BRANCH( !(uint8_t) nz ) // 89% taken
+
+ case 0xD0: // BNE
+ BRANCH( (uint8_t) nz )
+
+ case 0x3F:{// CALL
+ int old_addr = GET_PC() + 2;
+ SET_PC( READ_PC16( pc ) );
+ PUSH16( old_addr );
+ goto loop;
+ }
+
+ case 0x6F:// RET
+ #if SPC_NO_SP_WRAPAROUND
+ {
+ SET_PC( GET_LE16( sp ) );
+ sp += 2;
+ }
+ #else
+ {
+ int addr = sp - ram;
+ SET_PC( GET_LE16( sp ) );
+ sp += 2;
+ if ( addr < 0x1FF )
+ goto loop;
+
+ SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] );
+ sp -= 0x100;
+ }
+ #endif
+ goto loop;
+
+ case 0xE4: // MOV a,dp
+ ++pc;
+ // 80% from timer
+ READ_DP_TIMER( 0, data, a = nz );
+ goto loop;
+
+ case 0xFA:{// MOV dp,dp
+ int temp;
+ READ_DP_TIMER( -2, data, temp );
+ data = temp + no_read_before_write ;
+ }
+ // fall through
+ case 0x8F:{// MOV dp,#imm
+ int temp = READ_PC( pc + 1 );
+ pc += 2;
+
+ #if !SPC_MORE_ACCURACY
+ {
+ int i = dp + temp;
+ ram [i] = (uint8_t) data;
+ i -= 0xF0;
+ if ( (unsigned) i < 0x10 ) // 76%
+ {
+ REGS [i] = (uint8_t) data;
+
+ // Registers other than $F2 and $F4-$F7
+ //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 )
+ if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12%
+ cpu_write_smp_reg( data, rel_time, i );
+ }
+ }
+ #else
+ WRITE_DP( 0, temp, data );
+ #endif
+ goto loop;
+ }
+
+ case 0xC4: // MOV dp,a
+ ++pc;
+ #if !SPC_MORE_ACCURACY
+ {
+ int i = dp + data;
+ ram [i] = (uint8_t) a;
+ i -= 0xF0;
+ if ( (unsigned) i < 0x10 ) // 39%
+ {
+ unsigned sel = i - 2;
+ REGS [i] = (uint8_t) a;
+
+ if ( sel == 1 ) // 51% $F3
+ dsp_write( a, rel_time );
+ else if ( sel > 1 ) // 1% not $F2 or $F3
+ cpu_write_smp_reg_( a, rel_time, i );
+ }
+ }
+ #else
+ WRITE_DP( 0, data, a );
+ #endif
+ 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:\
+ data += 0x100 * READ_PC( ++pc );\
+ goto end_##op;\
+ CASE( op + 0x0C ) /* dp+X */\
+ data = (uint8_t) (data + x);
+
+#define ADDR_MODES_NO_DP( op )\
+ ADDR_MODES_( op )\
+ data += dp;\
+ end_##op:
+
+#define ADDR_MODES( op )\
+ ADDR_MODES_( op )\
+ CASE( op - 0x04 ) /* dp */\
+ data += dp;\
+ end_##op:
+
+// 1. 8-bit Data Transmission Commands. Group I
+
+ ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr
+ a = nz = READ( 0, data );
+ goto inc_pc_loop;
+
+ case 0xBF:{// MOV A,(X)+
+ int temp = x + dp;
+ x = (uint8_t) (x + 1);
+ a = nz = READ( -1, temp );
+ goto loop;
+ }
+
+ 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
+ READ_DP_TIMER( 0, data, x = nz );
+ goto inc_pc_loop;
+
+ case 0xE9: // MOV X,abs
+ data = READ_PC16( pc );
+ ++pc;
+ data = READ( 0, 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
+ // 70% from timer
+ pc++;
+ READ_DP_TIMER( 0, data, y = nz );
+ goto loop;
+
+ case 0xEC:{// MOV Y,abs
+ int temp = READ_PC16( pc );
+ pc += 2;
+ READ_TIMER( 0, temp, y = nz );
+ //y = nz = READ( 0, temp );
+ goto loop;
+ }
+
+ case 0x8D: // MOV Y,imm
+ y = data;
+ nz = data;
+ goto inc_pc_loop;
+
+// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2
+
+ ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A
+ WRITE( 0, 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( 0, READ_PC16( pc ), temp );
+ pc += 2;
+ goto loop;
+ }
+
+ case 0xD9: // MOV dp+Y,X
+ data = (uint8_t) (data + y);
+ case 0xD8: // MOV dp,X
+ WRITE( 0, data + dp, x );
+ goto inc_pc_loop;
+
+ case 0xDB: // MOV dp+X,Y
+ data = (uint8_t) (data + x);
+ case 0xCB: // MOV dp,Y
+ WRITE( 0, data + dp, y );
+ 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( 0, x, a + no_read_before_write );
+ x++;
+ goto loop;
+
+// 5. 8-BIT LOGIC OPERATION COMMANDS
+
+#define LOGICAL_OP( op, func )\
+ ADDR_MODES( op ) /* addr */\
+ data = READ( 0, data );\
+ case op: /* imm */\
+ nz = a func##= data;\
+ goto inc_pc_loop;\
+ { unsigned addr;\
+ case op + 0x11: /* X,Y */\
+ data = READ_DP( -2, y );\
+ addr = x + dp;\
+ goto addr_##op;\
+ case op + 0x01: /* dp,dp */\
+ data = READ_DP( -3, data );\
+ case op + 0x10:{/*dp,imm*/\
+ uint8_t const* addr2 = pc + 1;\
+ pc += 2;\
+ addr = READ_PC( addr2 ) + dp;\
+ }\
+ addr_##op:\
+ nz = data func READ( -1, addr );\
+ WRITE( 0, addr, nz );\
+ goto 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( 0, data );
+ case 0x68: // CMP imm
+ nz = a - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto inc_pc_loop;
+
+ case 0x79: // CMP (X),(Y)
+ data = READ_DP( -2, y );
+ nz = READ_DP( -1, x ) - data;
+ c = ~nz;
+ nz &= 0xFF;
+ goto loop;
+
+ case 0x69: // CMP dp,dp
+ data = READ_DP( -3, data );
+ case 0x78: // CMP dp,imm
+ nz = READ_DP( -1, READ_PC( ++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_PC16( pc );
+ pc++;
+ cmp_x_addr:
+ data = READ( 0, 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_PC16( pc );
+ pc++;
+ cmp_y_addr:
+ data = READ( 0, 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( -2, y );
+ addr = x + dp;
+ goto adc_addr;
+ case 0xA9: // SBC dp,dp
+ case 0x89: // ADC dp,dp
+ data = READ_DP( -3, data );
+ case 0xB8: // SBC dp,imm
+ case 0x98: // ADC dp,imm
+ addr = READ_PC( ++pc ) + dp;
+ adc_addr:
+ nz = READ( -1, 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( 0, data );
+ case 0xA8: // SBC imm
+ case 0x88: // ADC imm
+ addr = -1; // A
+ nz = a;
+ adc_data: {
+ int flags;
+ if ( opcode >= 0xA0 ) // SBC
+ data ^= 0xFF;
+
+ flags = data ^ nz;
+ nz += data + (c >> 8 & 1);
+ flags ^= nz;
+
+ psw = (psw & ~(v40 | h08)) |
+ (flags >> 1 & h08) |
+ ((flags + 0x80) >> 2 & v40);
+ c = nz;
+ if ( addr < 0 )
+ {
+ a = (uint8_t) nz;
+ goto inc_pc_loop;
+ }
+ WRITE( 0, addr, /*(uint8_t)*/ nz );
+ goto inc_pc_loop;
+ }
+
+ }
+
+// 6. ADDITION & SUBTRACTION COMMANDS
+
+#define INC_DEC_REG( reg, op )\
+ nz = reg op;\
+ 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_PC16( pc );
+ pc++;
+ inc_abs:
+ nz = (opcode >> 4 & 2) - 1;
+ nz += READ( -1, data );
+ WRITE( 0, 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_PC16( pc );
+ pc++;
+ rol_mem:
+ nz = c >> 8 & 1;
+ nz |= (c = READ( -1, data ) << 1);
+ WRITE( 0, 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_PC16( pc );
+ pc++;
+ ror_mem: {
+ int temp = READ( -1, data );
+ nz = (c >> 1 & 0x80) | (temp >> 1);
+ c = temp << 8;
+ WRITE( 0, 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( -2, data );
+ nz = (a & 0x7F) | (a >> 1);
+ y = READ_DP( 0, (uint8_t) (data + 1) );
+ nz |= y;
+ goto inc_pc_loop;
+
+ case 0xDA: // MOVW dp,YA
+ WRITE_DP( -1, data, a );
+ WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write );
+ goto inc_pc_loop;
+
+// 9. 16-BIT OPERATION COMMANDS
+
+ case 0x3A: // INCW dp
+ case 0x1A:{// DECW dp
+ int temp;
+ // low byte
+ data += dp;
+ temp = READ( -3, data );
+ temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW
+ nz = ((temp >> 1) | temp) & 0x7F;
+ WRITE( -2, data, /*(uint8_t)*/ temp );
+
+ // high byte
+ data = (uint8_t) (data + 1) + dp;
+ temp = (uint8_t) ((temp >> 8) + READ( -1, data ));
+ nz |= temp;
+ WRITE( 0, data, temp );
+
+ goto inc_pc_loop;
+ }
+
+ case 0x7A: // ADDW YA,dp
+ case 0x9A:{// SUBW YA,dp
+ int lo = READ_DP( -2, data );
+ int hi = READ_DP( 0, (uint8_t) (data + 1) );
+ int result;
+ int flags;
+
+ if ( opcode == 0x9A ) // SUBW
+ {
+ lo = (lo ^ 0xFF) + 1;
+ hi ^= 0xFF;
+ }
+
+ lo += a;
+ result = y + hi + (lo >> 8);
+ flags = hi ^ y ^ result;
+
+ psw = (psw & ~(v40 | h08)) |
+ (flags >> 1 & h08) |
+ ((flags + 0x80) >> 2 & v40);
+ c = result;
+ a = (uint8_t) lo;
+ result = (uint8_t) result;
+ y = result;
+ nz = (((lo >> 1) | lo) & 0x7F) | result;
+
+ goto inc_pc_loop;
+ }
+
+ case 0x5A: { // CMPW YA,dp
+ int temp = a - READ_DP( -1, data );
+ nz = ((temp >> 1) | temp) & 0x7F;
+ temp = y + (temp >> 8);
+ temp -= READ_DP( 0, (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
+ {
+ unsigned ya = y * 0x100 + a;
+
+ psw &= ~(h08 | v40);
+
+ if ( y >= x )
+ psw |= v40;
+
+ if ( (y & 15) >= (x & 15) )
+ psw |= h08;
+
+ 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
+
+ case 0xDF: // DAA
+ SUSPICIOUS_OPCODE( "DAA" );
+ if ( a > 0x99 || c & 0x100 )
+ {
+ a += 0x60;
+ c = 0x100;
+ }
+
+ if ( (a & 0x0F) > 9 || psw & h08 )
+ a += 0x06;
+
+ nz = a;
+ a = (uint8_t) a;
+ goto loop;
+
+ case 0xBE: // DAS
+ SUSPICIOUS_OPCODE( "DAS" );
+ if ( a > 0x99 || !(c & 0x100) )
+ {
+ a -= 0x60;
+ c = 0;
+ }
+
+ if ( (a & 0x0F) > 9 || !(psw & h08) )
+ a -= 0x06;
+
+ nz = a;
+ a = (uint8_t) a;
+ goto loop;
+
+// 12. BRANCHING COMMANDS
+
+ case 0x2F: // BRA rel
+ pc += (BOOST::int8_t) data;
+ goto inc_pc_loop;
+
+ case 0x30: // BMI
+ BRANCH( (nz & nz_neg_mask) )
+
+ case 0x10: // BPL
+ BRANCH( !(nz & nz_neg_mask) )
+
+ case 0xB0: // BCS
+ BRANCH( c & 0x100 )
+
+ case 0x90: // BCC
+ BRANCH( !(c & 0x100) )
+
+ case 0x70: // BVS
+ BRANCH( psw & v40 )
+
+ case 0x50: // BVC
+ BRANCH( !(psw & v40) )
+
+ #define CBRANCH( cond )\
+ {\
+ pc++;\
+ if ( cond )\
+ goto cbranch_taken_loop;\
+ rel_time -= 2;\
+ goto inc_pc_loop;\
+ }
+
+ case 0x03: // BBS dp.bit,rel
+ case 0x23:
+ case 0x43:
+ case 0x63:
+ case 0x83:
+ case 0xA3:
+ case 0xC3:
+ case 0xE3:
+ CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 )
+
+ case 0x13: // BBC dp.bit,rel
+ case 0x33:
+ case 0x53:
+ case 0x73:
+ case 0x93:
+ case 0xB3:
+ case 0xD3:
+ case 0xF3:
+ CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) )
+
+ case 0xDE: // CBNE dp+X,rel
+ data = (uint8_t) (data + x);
+ // fall through
+ case 0x2E:{// CBNE dp,rel
+ int temp;
+ // 61% from timer
+ READ_DP_TIMER( -4, data, temp );
+ CBRANCH( temp != a )
+ }
+
+ case 0x6E: { // DBNZ dp,rel
+ unsigned temp = READ_DP( -4, data ) - 1;
+ WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write );
+ CBRANCH( temp )
+ }
+
+ case 0xFE: // DBNZ Y,rel
+ y = (uint8_t) (y - 1);
+ BRANCH( y )
+
+ case 0x1F: // JMP [abs+X]
+ SET_PC( READ_PC16( pc ) + x );
+ // fall through
+ case 0x5F: // JMP abs
+ SET_PC( READ_PC16( pc ) );
+ goto loop;
+
+// 13. SUB-ROUTINE CALL RETURN COMMANDS
+
+ case 0x0F:{// BRK
+ int temp;
+ int ret_addr = GET_PC();
+ SUSPICIOUS_OPCODE( "BRK" );
+ SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified
+ PUSH16( ret_addr );
+ GET_PSW( temp );
+ psw = (psw | b10) & ~i04;
+ PUSH( temp );
+ goto loop;
+ }
+
+ case 0x4F:{// PCALL offset
+ int ret_addr = GET_PC() + 1;
+ SET_PC( 0xFF00 | data );
+ PUSH16( ret_addr );
+ 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: {
+ int ret_addr = GET_PC();
+ SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) );
+ PUSH16( ret_addr );
+ goto loop;
+ }
+
+// 14. STACK OPERATION COMMANDS
+
+ {
+ int temp;
+ case 0x7F: // RET1
+ temp = *sp;
+ SET_PC( GET_LE16( sp + 1 ) );
+ sp += 3;
+ goto set_psw;
+ case 0x8E: // POP PSW
+ POP( temp );
+ set_psw:
+ SET_PSW( temp );
+ goto loop;
+ }
+
+ case 0x0D: { // PUSH PSW
+ int temp;
+ GET_PSW( 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
+ POP( a );
+ goto loop;
+
+ case 0xCE: // POP X
+ POP( x );
+ goto loop;
+
+ case 0xEE: // POP Y
+ POP( y );
+ 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: {
+ int bit = 1 << (opcode >> 5);
+ int mask = ~bit;
+ if ( opcode & 0x10 )
+ bit = 0;
+ data += dp;
+ WRITE( 0, data, (READ( -1, data ) & mask) | bit );
+ goto inc_pc_loop;
+ }
+
+ case 0x0E: // TSET1 abs
+ case 0x4E: // TCLR1 abs
+ data = READ_PC16( pc );
+ pc += 2;
+ {
+ unsigned temp = READ( -2, data );
+ nz = (uint8_t) (a - temp);
+ temp &= ~a;
+ if ( opcode == 0x0E )
+ temp |= a;
+ WRITE( 0, data, temp );
+ }
+ goto loop;
+
+ case 0x4A: // AND1 C,mem.bit
+ c &= MEM_BIT( 0 );
+ pc += 2;
+ goto loop;
+
+ case 0x6A: // AND1 C,/mem.bit
+ c &= ~MEM_BIT( 0 );
+ pc += 2;
+ goto loop;
+
+ case 0x0A: // OR1 C,mem.bit
+ c |= MEM_BIT( -1 );
+ pc += 2;
+ goto loop;
+
+ case 0x2A: // OR1 C,/mem.bit
+ c |= ~MEM_BIT( -1 );
+ pc += 2;
+ goto loop;
+
+ case 0x8A: // EOR1 C,mem.bit
+ c ^= MEM_BIT( -1 );
+ pc += 2;
+ goto loop;
+
+ case 0xEA: // NOT1 mem.bit
+ data = READ_PC16( pc );
+ pc += 2;
+ {
+ unsigned temp = READ( -1, data & 0x1FFF );
+ temp ^= 1 << (data >> 13);
+ WRITE( 0, data & 0x1FFF, temp );
+ }
+ goto loop;
+
+ case 0xCA: // MOV1 mem.bit,C
+ data = READ_PC16( pc );
+ pc += 2;
+ {
+ unsigned temp = READ( -2, data & 0x1FFF );
+ unsigned bit = data >> 13;
+ temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit);
+ WRITE( 0, data & 0x1FFF, temp + no_read_before_write );
+ }
+ goto loop;
+
+ case 0xAA: // MOV1 C,mem.bit
+ c = MEM_BIT( 0 );
+ pc += 2;
+ goto loop;
+
+// 16. PROGRAM PSW 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
+ psw &= ~(v40 | h08);
+ goto loop;
+
+ case 0x20: // CLRP
+ dp = 0;
+ goto loop;
+
+ case 0x40: // SETP
+ dp = 0x100;
+ goto loop;
+
+ case 0xA0: // EI
+ SUSPICIOUS_OPCODE( "EI" );
+ psw |= i04;
+ goto loop;
+
+ case 0xC0: // DI
+ SUSPICIOUS_OPCODE( "DI" );
+ psw &= ~i04;
+ goto loop;
+
+// 17. OTHER COMMANDS
+
+ case 0x00: // NOP
+ goto loop;
+
+ case 0xFF:{// STOP
+ // handle PC wrap-around
+ unsigned addr = GET_PC() - 1;
+ if ( addr >= 0x10000 )
+ {
+ addr &= 0xFFFF;
+ SET_PC( addr );
+ dprintf( "SPC: PC wrapped around\n" );
+ goto loop;
+ }
+ }
+ // fall through
+ case 0xEF: // SLEEP
+ SUSPICIOUS_OPCODE( "STOP/SLEEP" );
+ --pc;
+ rel_time = 0;
+ m.cpu_error = "SPC emulation error";
+ goto stop;
+ } // switch
+
+ assert( 0 ); // catch any unhandled instructions
+}
+out_of_time:
+ rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode
+stop:
+
+ // Uncache registers
+ if ( GET_PC() >= 0x10000 )
+ dprintf( "SPC: PC wrapped around\n" );
+ m.cpu_regs.pc = (uint16_t) GET_PC();
+ m.cpu_regs.sp = ( uint8_t) GET_SP();
+ m.cpu_regs.a = ( uint8_t) a;
+ m.cpu_regs.x = ( uint8_t) x;
+ m.cpu_regs.y = ( uint8_t) y;
+ {
+ int temp;
+ GET_PSW( temp );
+ m.cpu_regs.psw = (uint8_t) temp;
+ }
+}
+SPC_CPU_RUN_FUNC_END
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.cpp
new file mode 100644
index 00000000..f423e454
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.cpp
@@ -0,0 +1,1387 @@
+// snes_spc 0.9.0. http://www.slack.net/~ant/
+
+#include "Spc_Dsp.h"
+
+#include "blargg_endian.h"
+#include <string.h>
+
+/* Copyright (C) 2007 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifdef BLARGG_ENABLE_OPTIMIZER
+ #include BLARGG_ENABLE_OPTIMIZER
+#endif
+
+#if INT_MAX < 0x7FFFFFFF
+ #error "Requires that int type have at least 32 bits"
+#endif
+
+// TODO: add to blargg_endian.h
+#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr ))
+#define GET_LE16A( addr ) GET_LE16( addr )
+#define SET_LE16A( addr, data ) SET_LE16( addr, data )
+
+static BOOST::uint8_t const initial_regs [Spc_Dsp::register_count] =
+{
+ 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80,
+ 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF,
+ 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A,
+ 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF,
+ 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67,
+ 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF,
+ 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F,
+ 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF
+};
+
+// if ( io < -32768 ) io = -32768;
+// if ( io > 32767 ) io = 32767;
+#define CLAMP16( io )\
+{\
+ if ( (int16_t) io != io )\
+ io = (io >> 31) ^ 0x7FFF;\
+}
+
+// Access global DSP register
+#define REG(n) m.regs [r_##n]
+
+// Access voice DSP register
+#define VREG(r,n) r [v_##n]
+
+#define WRITE_SAMPLES( l, r, out ) \
+{\
+ out [0] = l;\
+ out [1] = r;\
+ out += 2;\
+ if ( out >= m.out_end )\
+ {\
+ check( out == m.out_end );\
+ check( m.out_end != &m.extra [extra_size] || \
+ (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\
+ out = m.extra;\
+ m.out_end = &m.extra [extra_size];\
+ }\
+}\
+
+void Spc_Dsp::set_output( sample_t* out, int size )
+{
+ require( (size & 1) == 0 ); // must be even
+ if ( !out )
+ {
+ out = m.extra;
+ size = extra_size;
+ }
+ m.out_begin = out;
+ m.out = out;
+ m.out_end = out + size;
+}
+
+// Volume registers and efb are signed! Easy to forget int8_t cast.
+// Prefixes are to avoid accidental use of locals with same names.
+
+// Gaussian interpolation
+
+static short const gauss [512] =
+{
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2,
+ 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5,
+ 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10,
+ 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17,
+ 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27,
+ 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40,
+ 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56,
+ 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77,
+ 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102,
+ 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132,
+ 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168,
+ 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210,
+ 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257,
+ 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311,
+ 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370,
+ 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434,
+ 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504,
+ 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577,
+ 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654,
+ 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732,
+ 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811,
+ 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889,
+ 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965,
+ 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036,
+1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102,
+1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160,
+1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210,
+1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251,
+1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280,
+1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298,
+1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305,
+};
+
+static short const cubic [514] =
+{
+ 0, -4, -8, -12, -16, -20, -23, -27, -30, -34, -37, -41, -44, -47, -50, -53,
+ -56, -59, -62, -65, -68, -71, -73, -76, -78, -81, -84, -87, -89, -91, -93, -95,
+ -98,-100,-102,-104,-106,-109,-110,-112,-113,-116,-117,-119,-121,-122,-123,-125,
+-126,-128,-129,-131,-132,-134,-134,-136,-136,-138,-138,-140,-141,-141,-142,-143,
+-144,-144,-145,-146,-147,-148,-147,-148,-148,-149,-149,-150,-150,-150,-150,-151,
+-151,-151,-151,-151,-152,-152,-151,-152,-151,-152,-151,-151,-151,-151,-150,-150,
+-150,-149,-149,-149,-149,-148,-147,-147,-146,-146,-145,-145,-144,-144,-143,-142,
+-141,-141,-140,-139,-139,-138,-137,-136,-135,-135,-133,-133,-132,-131,-130,-129,
+-128,-127,-126,-125,-124,-123,-121,-121,-119,-118,-117,-116,-115,-114,-112,-111,
+-110,-109,-107,-106,-105,-104,-102,-102,-100, -99, -97, -97, -95, -94, -92, -91,
+ -90, -88, -87, -86, -85, -84, -82, -81, -79, -78, -76, -76, -74, -73, -71, -70,
+ -68, -67, -66, -65, -63, -62, -60, -60, -58, -57, -55, -55, -53, -52, -50, -49,
+ -48, -46, -45, -44, -43, -42, -40, -39, -38, -37, -36, -35, -34, -32, -31, -30,
+ -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -19, -17, -16, -15, -14,
+ -14, -13, -12, -11, -11, -10, -9, -9, -8, -8, -7, -7, -6, -5, -4, -4,
+ -3, -3, -3, -2, -2, -2, -1, -1, 0, -1, 0, -1, 0, 0, 0, 0,
+ 0,
+2048,2048,2048,2048,2047,2047,2046,2045,2043,2042,2041,2039,2037,2035,2033,2031,
+2028,2026,2024,2021,2018,2015,2012,2009,2005,2002,1999,1995,1991,1987,1982,1978,
+1974,1969,1965,1960,1955,1951,1946,1940,1934,1929,1924,1918,1912,1906,1900,1895,
+1888,1882,1875,1869,1862,1856,1849,1842,1835,1828,1821,1814,1806,1799,1791,1783,
+1776,1768,1760,1753,1744,1737,1728,1720,1711,1703,1695,1686,1677,1668,1659,1651,
+1641,1633,1623,1614,1605,1596,1587,1577,1567,1559,1549,1539,1529,1520,1510,1499,
+1490,1480,1470,1460,1450,1440,1430,1420,1408,1398,1389,1378,1367,1357,1346,1336,
+1325,1315,1304,1293,1282,1272,1261,1250,1239,1229,1218,1207,1196,1185,1174,1163,
+1152,1141,1130,1119,1108,1097,1086,1075,1063,1052,1042,1030,1019,1008, 997, 986,
+ 974, 964, 952, 941, 930, 919, 908, 897, 886, 875, 864, 853, 842, 831, 820, 809,
+ 798, 787, 776, 765, 754, 744, 733, 722, 711, 700, 690, 679, 668, 658, 647, 637,
+ 626, 616, 605, 595, 584, 574, 564, 554, 543, 534, 524, 514, 503, 494, 483, 473,
+ 464, 454, 444, 435, 425, 416, 407, 397, 387, 378, 370, 360, 351, 342, 333, 325,
+ 315, 307, 298, 290, 281, 273, 265, 256, 248, 241, 233, 225, 216, 209, 201, 193,
+ 186, 178, 171, 164, 157, 150, 143, 137, 129, 123, 117, 110, 103, 97, 91, 85,
+ 79, 74, 68, 62, 56, 51, 46, 41, 35, 31, 27, 22, 17, 13, 8, 4,
+ 0
+};
+
+static short const sinc [2048] =
+{
+ 39, -315, 666, 15642, 666, -315, 39, -38,
+ 38, -302, 613, 15642, 718, -328, 41, -38,
+ 36, -288, 561, 15641, 772, -342, 42, -38,
+ 35, -275, 510, 15639, 826, -355, 44, -38,
+ 33, -263, 459, 15636, 880, -369, 46, -38,
+ 32, -250, 408, 15632, 935, -383, 47, -38,
+ 31, -237, 358, 15628, 990, -396, 49, -38,
+ 29, -224, 309, 15622, 1046, -410, 51, -38,
+ 28, -212, 259, 15616, 1103, -425, 53, -38,
+ 27, -200, 211, 15609, 1159, -439, 54, -38,
+ 25, -188, 163, 15601, 1216, -453, 56, -38,
+ 24, -175, 115, 15593, 1274, -467, 58, -38,
+ 23, -164, 68, 15583, 1332, -482, 60, -38,
+ 22, -152, 22, 15573, 1391, -496, 62, -37,
+ 21, -140, -24, 15562, 1450, -511, 64, -37,
+ 19, -128, -70, 15550, 1509, -526, 66, -37,
+ 18, -117, -115, 15538, 1569, -540, 68, -37,
+ 17, -106, -159, 15524, 1629, -555, 70, -37,
+ 16, -94, -203, 15510, 1690, -570, 72, -36,
+ 15, -83, -247, 15495, 1751, -585, 74, -36,
+ 14, -72, -289, 15479, 1813, -600, 76, -36,
+ 13, -62, -332, 15462, 1875, -616, 79, -36,
+ 12, -51, -374, 15445, 1937, -631, 81, -35,
+ 11, -40, -415, 15426, 2000, -646, 83, -35,
+ 11, -30, -456, 15407, 2063, -662, 85, -35,
+ 10, -20, -496, 15387, 2127, -677, 88, -34,
+ 9, -9, -536, 15366, 2191, -693, 90, -34,
+ 8, 1, -576, 15345, 2256, -708, 92, -34,
+ 7, 10, -614, 15323, 2321, -724, 95, -33,
+ 7, 20, -653, 15300, 2386, -740, 97, -33,
+ 6, 30, -690, 15276, 2451, -755, 99, -33,
+ 5, 39, -728, 15251, 2517, -771, 102, -32,
+ 5, 49, -764, 15226, 2584, -787, 104, -32,
+ 4, 58, -801, 15200, 2651, -803, 107, -32,
+ 3, 67, -836, 15173, 2718, -819, 109, -31,
+ 3, 76, -871, 15145, 2785, -835, 112, -31,
+ 2, 85, -906, 15117, 2853, -851, 115, -30,
+ 2, 93, -940, 15087, 2921, -867, 117, -30,
+ 1, 102, -974, 15057, 2990, -883, 120, -29,
+ 1, 110, -1007, 15027, 3059, -899, 122, -29,
+ 0, 118, -1039, 14995, 3128, -915, 125, -29,
+ 0, 127, -1071, 14963, 3198, -931, 128, -28,
+ -1, 135, -1103, 14930, 3268, -948, 131, -28,
+ -1, 142, -1134, 14896, 3338, -964, 133, -27,
+ -1, 150, -1164, 14862, 3409, -980, 136, -27,
+ -2, 158, -1194, 14827, 3480, -996, 139, -26,
+ -2, 165, -1224, 14791, 3551, -1013, 142, -26,
+ -3, 172, -1253, 14754, 3622, -1029, 144, -25,
+ -3, 179, -1281, 14717, 3694, -1045, 147, -25,
+ -3, 187, -1309, 14679, 3766, -1062, 150, -24,
+ -3, 193, -1337, 14640, 3839, -1078, 153, -24,
+ -4, 200, -1363, 14601, 3912, -1094, 156, -23,
+ -4, 207, -1390, 14561, 3985, -1110, 159, -23,
+ -4, 213, -1416, 14520, 4058, -1127, 162, -22,
+ -4, 220, -1441, 14479, 4131, -1143, 165, -22,
+ -4, 226, -1466, 14437, 4205, -1159, 168, -22,
+ -5, 232, -1490, 14394, 4279, -1175, 171, -21,
+ -5, 238, -1514, 14350, 4354, -1192, 174, -21,
+ -5, 244, -1537, 14306, 4428, -1208, 177, -20,
+ -5, 249, -1560, 14261, 4503, -1224, 180, -20,
+ -5, 255, -1583, 14216, 4578, -1240, 183, -19,
+ -5, 260, -1604, 14169, 4653, -1256, 186, -19,
+ -5, 265, -1626, 14123, 4729, -1272, 189, -18,
+ -5, 271, -1647, 14075, 4805, -1288, 192, -18,
+ -5, 276, -1667, 14027, 4881, -1304, 195, -17,
+ -6, 280, -1687, 13978, 4957, -1320, 198, -17,
+ -6, 285, -1706, 13929, 5033, -1336, 201, -16,
+ -6, 290, -1725, 13879, 5110, -1352, 204, -16,
+ -6, 294, -1744, 13829, 5186, -1368, 207, -15,
+ -6, 299, -1762, 13777, 5263, -1383, 210, -15,
+ -6, 303, -1779, 13726, 5340, -1399, 213, -14,
+ -6, 307, -1796, 13673, 5418, -1414, 216, -14,
+ -6, 311, -1813, 13620, 5495, -1430, 219, -13,
+ -5, 315, -1829, 13567, 5573, -1445, 222, -13,
+ -5, 319, -1844, 13512, 5651, -1461, 225, -13,
+ -5, 322, -1859, 13458, 5728, -1476, 229, -12,
+ -5, 326, -1874, 13402, 5806, -1491, 232, -12,
+ -5, 329, -1888, 13347, 5885, -1506, 235, -11,
+ -5, 332, -1902, 13290, 5963, -1521, 238, -11,
+ -5, 335, -1915, 13233, 6041, -1536, 241, -10,
+ -5, 338, -1928, 13176, 6120, -1551, 244, -10,
+ -5, 341, -1940, 13118, 6199, -1566, 247, -10,
+ -5, 344, -1952, 13059, 6277, -1580, 250, -9,
+ -5, 347, -1964, 13000, 6356, -1595, 253, -9,
+ -5, 349, -1975, 12940, 6435, -1609, 256, -8,
+ -4, 352, -1986, 12880, 6514, -1623, 259, -8,
+ -4, 354, -1996, 12819, 6594, -1637, 262, -8,
+ -4, 356, -2005, 12758, 6673, -1651, 265, -7,
+ -4, 358, -2015, 12696, 6752, -1665, 268, -7,
+ -4, 360, -2024, 12634, 6831, -1679, 271, -7,
+ -4, 362, -2032, 12572, 6911, -1693, 274, -6,
+ -4, 364, -2040, 12509, 6990, -1706, 277, -6,
+ -4, 366, -2048, 12445, 7070, -1719, 280, -6,
+ -3, 367, -2055, 12381, 7149, -1732, 283, -5,
+ -3, 369, -2062, 12316, 7229, -1745, 286, -5,
+ -3, 370, -2068, 12251, 7308, -1758, 289, -5,
+ -3, 371, -2074, 12186, 7388, -1771, 291, -4,
+ -3, 372, -2079, 12120, 7467, -1784, 294, -4,
+ -3, 373, -2084, 12054, 7547, -1796, 297, -4,
+ -3, 374, -2089, 11987, 7626, -1808, 300, -4,
+ -2, 375, -2094, 11920, 7706, -1820, 303, -3,
+ -2, 376, -2098, 11852, 7785, -1832, 305, -3,
+ -2, 376, -2101, 11785, 7865, -1844, 308, -3,
+ -2, 377, -2104, 11716, 7944, -1855, 311, -3,
+ -2, 377, -2107, 11647, 8024, -1866, 313, -2,
+ -2, 378, -2110, 11578, 8103, -1877, 316, -2,
+ -2, 378, -2112, 11509, 8182, -1888, 318, -2,
+ -1, 378, -2113, 11439, 8262, -1899, 321, -2,
+ -1, 378, -2115, 11369, 8341, -1909, 323, -2,
+ -1, 378, -2116, 11298, 8420, -1920, 326, -2,
+ -1, 378, -2116, 11227, 8499, -1930, 328, -1,
+ -1, 378, -2116, 11156, 8578, -1940, 331, -1,
+ -1, 378, -2116, 11084, 8656, -1949, 333, -1,
+ -1, 377, -2116, 11012, 8735, -1959, 335, -1,
+ -1, 377, -2115, 10940, 8814, -1968, 337, -1,
+ -1, 377, -2114, 10867, 8892, -1977, 340, -1,
+ -1, 376, -2112, 10795, 8971, -1985, 342, -1,
+ 0, 375, -2111, 10721, 9049, -1994, 344, -1,
+ 0, 375, -2108, 10648, 9127, -2002, 346, 0,
+ 0, 374, -2106, 10574, 9205, -2010, 348, 0,
+ 0, 373, -2103, 10500, 9283, -2018, 350, 0,
+ 0, 372, -2100, 10426, 9360, -2025, 352, 0,
+ 0, 371, -2097, 10351, 9438, -2032, 354, 0,
+ 0, 370, -2093, 10276, 9515, -2039, 355, 0,
+ 0, 369, -2089, 10201, 9592, -2046, 357, 0,
+ 0, 367, -2084, 10126, 9669, -2052, 359, 0,
+ 0, 366, -2080, 10050, 9745, -2058, 360, 0,
+ 0, 365, -2075, 9974, 9822, -2064, 362, 0,
+ 0, 363, -2070, 9898, 9898, -2070, 363, 0,
+ 0, 362, -2064, 9822, 9974, -2075, 365, 0,
+ 0, 360, -2058, 9745, 10050, -2080, 366, 0,
+ 0, 359, -2052, 9669, 10126, -2084, 367, 0,
+ 0, 357, -2046, 9592, 10201, -2089, 369, 0,
+ 0, 355, -2039, 9515, 10276, -2093, 370, 0,
+ 0, 354, -2032, 9438, 10351, -2097, 371, 0,
+ 0, 352, -2025, 9360, 10426, -2100, 372, 0,
+ 0, 350, -2018, 9283, 10500, -2103, 373, 0,
+ 0, 348, -2010, 9205, 10574, -2106, 374, 0,
+ 0, 346, -2002, 9127, 10648, -2108, 375, 0,
+ -1, 344, -1994, 9049, 10721, -2111, 375, 0,
+ -1, 342, -1985, 8971, 10795, -2112, 376, -1,
+ -1, 340, -1977, 8892, 10867, -2114, 377, -1,
+ -1, 337, -1968, 8814, 10940, -2115, 377, -1,
+ -1, 335, -1959, 8735, 11012, -2116, 377, -1,
+ -1, 333, -1949, 8656, 11084, -2116, 378, -1,
+ -1, 331, -1940, 8578, 11156, -2116, 378, -1,
+ -1, 328, -1930, 8499, 11227, -2116, 378, -1,
+ -2, 326, -1920, 8420, 11298, -2116, 378, -1,
+ -2, 323, -1909, 8341, 11369, -2115, 378, -1,
+ -2, 321, -1899, 8262, 11439, -2113, 378, -1,
+ -2, 318, -1888, 8182, 11509, -2112, 378, -2,
+ -2, 316, -1877, 8103, 11578, -2110, 378, -2,
+ -2, 313, -1866, 8024, 11647, -2107, 377, -2,
+ -3, 311, -1855, 7944, 11716, -2104, 377, -2,
+ -3, 308, -1844, 7865, 11785, -2101, 376, -2,
+ -3, 305, -1832, 7785, 11852, -2098, 376, -2,
+ -3, 303, -1820, 7706, 11920, -2094, 375, -2,
+ -4, 300, -1808, 7626, 11987, -2089, 374, -3,
+ -4, 297, -1796, 7547, 12054, -2084, 373, -3,
+ -4, 294, -1784, 7467, 12120, -2079, 372, -3,
+ -4, 291, -1771, 7388, 12186, -2074, 371, -3,
+ -5, 289, -1758, 7308, 12251, -2068, 370, -3,
+ -5, 286, -1745, 7229, 12316, -2062, 369, -3,
+ -5, 283, -1732, 7149, 12381, -2055, 367, -3,
+ -6, 280, -1719, 7070, 12445, -2048, 366, -4,
+ -6, 277, -1706, 6990, 12509, -2040, 364, -4,
+ -6, 274, -1693, 6911, 12572, -2032, 362, -4,
+ -7, 271, -1679, 6831, 12634, -2024, 360, -4,
+ -7, 268, -1665, 6752, 12696, -2015, 358, -4,
+ -7, 265, -1651, 6673, 12758, -2005, 356, -4,
+ -8, 262, -1637, 6594, 12819, -1996, 354, -4,
+ -8, 259, -1623, 6514, 12880, -1986, 352, -4,
+ -8, 256, -1609, 6435, 12940, -1975, 349, -5,
+ -9, 253, -1595, 6356, 13000, -1964, 347, -5,
+ -9, 250, -1580, 6277, 13059, -1952, 344, -5,
+ -10, 247, -1566, 6199, 13118, -1940, 341, -5,
+ -10, 244, -1551, 6120, 13176, -1928, 338, -5,
+ -10, 241, -1536, 6041, 13233, -1915, 335, -5,
+ -11, 238, -1521, 5963, 13290, -1902, 332, -5,
+ -11, 235, -1506, 5885, 13347, -1888, 329, -5,
+ -12, 232, -1491, 5806, 13402, -1874, 326, -5,
+ -12, 229, -1476, 5728, 13458, -1859, 322, -5,
+ -13, 225, -1461, 5651, 13512, -1844, 319, -5,
+ -13, 222, -1445, 5573, 13567, -1829, 315, -5,
+ -13, 219, -1430, 5495, 13620, -1813, 311, -6,
+ -14, 216, -1414, 5418, 13673, -1796, 307, -6,
+ -14, 213, -1399, 5340, 13726, -1779, 303, -6,
+ -15, 210, -1383, 5263, 13777, -1762, 299, -6,
+ -15, 207, -1368, 5186, 13829, -1744, 294, -6,
+ -16, 204, -1352, 5110, 13879, -1725, 290, -6,
+ -16, 201, -1336, 5033, 13929, -1706, 285, -6,
+ -17, 198, -1320, 4957, 13978, -1687, 280, -6,
+ -17, 195, -1304, 4881, 14027, -1667, 276, -5,
+ -18, 192, -1288, 4805, 14075, -1647, 271, -5,
+ -18, 189, -1272, 4729, 14123, -1626, 265, -5,
+ -19, 186, -1256, 4653, 14169, -1604, 260, -5,
+ -19, 183, -1240, 4578, 14216, -1583, 255, -5,
+ -20, 180, -1224, 4503, 14261, -1560, 249, -5,
+ -20, 177, -1208, 4428, 14306, -1537, 244, -5,
+ -21, 174, -1192, 4354, 14350, -1514, 238, -5,
+ -21, 171, -1175, 4279, 14394, -1490, 232, -5,
+ -22, 168, -1159, 4205, 14437, -1466, 226, -4,
+ -22, 165, -1143, 4131, 14479, -1441, 220, -4,
+ -22, 162, -1127, 4058, 14520, -1416, 213, -4,
+ -23, 159, -1110, 3985, 14561, -1390, 207, -4,
+ -23, 156, -1094, 3912, 14601, -1363, 200, -4,
+ -24, 153, -1078, 3839, 14640, -1337, 193, -3,
+ -24, 150, -1062, 3766, 14679, -1309, 187, -3,
+ -25, 147, -1045, 3694, 14717, -1281, 179, -3,
+ -25, 144, -1029, 3622, 14754, -1253, 172, -3,
+ -26, 142, -1013, 3551, 14791, -1224, 165, -2,
+ -26, 139, -996, 3480, 14827, -1194, 158, -2,
+ -27, 136, -980, 3409, 14862, -1164, 150, -1,
+ -27, 133, -964, 3338, 14896, -1134, 142, -1,
+ -28, 131, -948, 3268, 14930, -1103, 135, -1,
+ -28, 128, -931, 3198, 14963, -1071, 127, 0,
+ -29, 125, -915, 3128, 14995, -1039, 118, 0,
+ -29, 122, -899, 3059, 15027, -1007, 110, 1,
+ -29, 120, -883, 2990, 15057, -974, 102, 1,
+ -30, 117, -867, 2921, 15087, -940, 93, 2,
+ -30, 115, -851, 2853, 15117, -906, 85, 2,
+ -31, 112, -835, 2785, 15145, -871, 76, 3,
+ -31, 109, -819, 2718, 15173, -836, 67, 3,
+ -32, 107, -803, 2651, 15200, -801, 58, 4,
+ -32, 104, -787, 2584, 15226, -764, 49, 5,
+ -32, 102, -771, 2517, 15251, -728, 39, 5,
+ -33, 99, -755, 2451, 15276, -690, 30, 6,
+ -33, 97, -740, 2386, 15300, -653, 20, 7,
+ -33, 95, -724, 2321, 15323, -614, 10, 7,
+ -34, 92, -708, 2256, 15345, -576, 1, 8,
+ -34, 90, -693, 2191, 15366, -536, -9, 9,
+ -34, 88, -677, 2127, 15387, -496, -20, 10,
+ -35, 85, -662, 2063, 15407, -456, -30, 11,
+ -35, 83, -646, 2000, 15426, -415, -40, 11,
+ -35, 81, -631, 1937, 15445, -374, -51, 12,
+ -36, 79, -616, 1875, 15462, -332, -62, 13,
+ -36, 76, -600, 1813, 15479, -289, -72, 14,
+ -36, 74, -585, 1751, 15495, -247, -83, 15,
+ -36, 72, -570, 1690, 15510, -203, -94, 16,
+ -37, 70, -555, 1629, 15524, -159, -106, 17,
+ -37, 68, -540, 1569, 15538, -115, -117, 18,
+ -37, 66, -526, 1509, 15550, -70, -128, 19,
+ -37, 64, -511, 1450, 15562, -24, -140, 21,
+ -37, 62, -496, 1391, 15573, 22, -152, 22,
+ -38, 60, -482, 1332, 15583, 68, -164, 23,
+ -38, 58, -467, 1274, 15593, 115, -175, 24,
+ -38, 56, -453, 1216, 15601, 163, -188, 25,
+ -38, 54, -439, 1159, 15609, 211, -200, 27,
+ -38, 53, -425, 1103, 15616, 259, -212, 28,
+ -38, 51, -410, 1046, 15622, 309, -224, 29,
+ -38, 49, -396, 990, 15628, 358, -237, 31,
+ -38, 47, -383, 935, 15632, 408, -250, 32,
+ -38, 46, -369, 880, 15636, 459, -263, 33,
+ -38, 44, -355, 826, 15639, 510, -275, 35,
+ -38, 42, -342, 772, 15641, 561, -288, 36,
+ -38, 41, -328, 718, 15642, 613, -302, 38,
+};
+
+inline int Spc_Dsp::interpolate( voice_t const* v )
+{
+ // Make pointers into gaussian based on fractional position between samples
+ int offset = v->interp_pos >> 4 & 0xFF;
+ short const* fwd = gauss + 255 - offset;
+ short const* rev = gauss + offset; // mirror left half of gaussian
+
+ int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];
+ int out;
+ out = (fwd [ 0] * in [0]) >> 11;
+ out += (fwd [256] * in [1]) >> 11;
+ out += (rev [256] * in [2]) >> 11;
+ out = (int16_t) out;
+ out += (rev [ 0] * in [3]) >> 11;
+
+ CLAMP16( out );
+ out &= ~1;
+ return out;
+}
+
+inline int Spc_Dsp::interpolate_cubic( voice_t const* v )
+{
+ // Make pointers into cubic based on fractional position between samples
+ int offset = v->interp_pos >> 4 & 0xFF;
+ short const* fwd = cubic + offset;
+ short const* rev = cubic + 256 - offset; // mirror left half of cubic
+
+ int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];
+ int out;
+ out = fwd [ 0] * in [0];
+ out += fwd [257] * in [1];
+ out += rev [257] * in [2];
+ out += rev [ 0] * in [3];
+ out >>= 11;
+
+ CLAMP16( out );
+ out &= ~1;
+ return out;
+}
+
+inline int Spc_Dsp::interpolate_sinc( voice_t const* v )
+{
+ // Make pointers into cubic based on fractional position between samples
+ int offset = v->interp_pos & 0xFF0;
+ short const* filt = (short const*) (((char const*)sinc) + offset);
+
+ int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos];
+ int out;
+ out = filt [0] * in [0];
+ out += filt [1] * in [1];
+ out += filt [2] * in [2];
+ out += filt [3] * in [3];
+ out += filt [4] * in [4];
+ out += filt [5] * in [5];
+ out += filt [6] * in [6];
+ out += filt [7] * in [7];
+ out >>= 14;
+
+ CLAMP16( out );
+ out &= ~1;
+ return out;
+}
+
+//// Counters
+
+int const simple_counter_range = 2048 * 5 * 3; // 30720
+
+static unsigned const counter_rates [32] =
+{
+ simple_counter_range + 1, // never fires
+ 2048, 1536,
+ 1280, 1024, 768,
+ 640, 512, 384,
+ 320, 256, 192,
+ 160, 128, 96,
+ 80, 64, 48,
+ 40, 32, 24,
+ 20, 16, 12,
+ 10, 8, 6,
+ 5, 4, 3,
+ 2,
+ 1
+};
+
+static unsigned const counter_offsets [32] =
+{
+ 1, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 536, 0, 1040,
+ 0,
+ 0
+};
+
+inline void Spc_Dsp::init_counter()
+{
+ m.counter = 0;
+}
+
+inline void Spc_Dsp::run_counters()
+{
+ if ( --m.counter < 0 )
+ m.counter = simple_counter_range - 1;
+}
+
+inline unsigned Spc_Dsp::read_counter( int rate )
+{
+ return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate];
+}
+
+
+//// Envelope
+
+inline void Spc_Dsp::run_envelope( voice_t* const v )
+{
+ int env = v->env;
+ if ( v->env_mode == env_release ) // 60%
+ {
+ if ( (env -= 0x8) < 0 )
+ env = 0;
+ v->env = env;
+ }
+ else
+ {
+ int rate;
+ int env_data = VREG(v->regs,adsr1);
+ if ( m.t_adsr0 & 0x80 ) // 99% ADSR
+ {
+ if ( v->env_mode >= env_decay ) // 99%
+ {
+ env--;
+ env -= env >> 8;
+ rate = env_data & 0x1F;
+ if ( v->env_mode == env_decay ) // 1%
+ rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10;
+ }
+ else // env_attack
+ {
+ rate = (m.t_adsr0 & 0x0F) * 2 + 1;
+ env += rate < 31 ? 0x20 : 0x400;
+ }
+ }
+ else // GAIN
+ {
+ int mode;
+ env_data = VREG(v->regs,gain);
+ mode = env_data >> 5;
+ if ( mode < 4 ) // direct
+ {
+ env = env_data * 0x10;
+ rate = 31;
+ }
+ else
+ {
+ rate = env_data & 0x1F;
+ if ( mode == 4 ) // 4: linear decrease
+ {
+ env -= 0x20;
+ }
+ else if ( mode < 6 ) // 5: exponential decrease
+ {
+ env--;
+ env -= env >> 8;
+ }
+ else // 6,7: linear increase
+ {
+ env += 0x20;
+ if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 )
+ env += 0x8 - 0x20; // 7: two-slope linear increase
+ }
+ }
+ }
+
+ // Sustain level
+ if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )
+ v->env_mode = env_sustain;
+
+ v->hidden_env = env;
+
+ // unsigned cast because linear decrease going negative also triggers this
+ if ( (unsigned) env > 0x7FF )
+ {
+ env = (env < 0 ? 0 : 0x7FF);
+ if ( v->env_mode == env_attack )
+ v->env_mode = env_decay;
+ }
+
+ if ( !read_counter( rate ) )
+ v->env = env; // nothing else is controlled by the counter
+ }
+}
+
+
+//// BRR Decoding
+
+inline void Spc_Dsp::decode_brr( voice_t* v )
+{
+ // Arrange the four input nybbles in 0xABCD order for easy decoding
+ int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];
+
+ int const header = m.t_brr_header;
+
+ // Write to next four samples in circular buffer
+ int* pos = &v->buf [v->buf_pos];
+ int* end;
+ if ( (v->buf_pos += 4) >= brr_buf_size )
+ v->buf_pos = 0;
+
+ // Decode four samples
+ for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
+ {
+ // Extract nybble and sign-extend
+ int s = (int16_t) nybbles >> 12;
+
+ // Shift sample based on header
+ int const shift = header >> 4;
+ s = (s << shift) >> 1;
+ if ( shift >= 0xD ) // handle invalid range
+ s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0)
+
+ // Apply IIR filter (8 is the most commonly used)
+ int const filter = header & 0x0C;
+ int const p1 = pos [brr_buf_size - 1];
+ int const p2 = pos [brr_buf_size - 2] >> 1;
+ if ( filter >= 8 )
+ {
+ s += p1;
+ s -= p2;
+ if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875
+ {
+ s += p2 >> 4;
+ s += (p1 * -3) >> 6;
+ }
+ else // s += p1 * 0.8984375 - p2 * 0.40625
+ {
+ s += (p1 * -13) >> 7;
+ s += (p2 * 3) >> 4;
+ }
+ }
+ else if ( filter ) // s += p1 * 0.46875
+ {
+ s += p1 >> 1;
+ s += (-p1) >> 5;
+ }
+
+ // Adjust and write sample
+ CLAMP16( s );
+ s = (int16_t) (s * 2);
+ pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around
+ }
+}
+
+
+//// Misc
+
+#define MISC_CLOCK( n ) inline void Spc_Dsp::misc_##n()
+
+MISC_CLOCK( 27 )
+{
+ m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON
+}
+MISC_CLOCK( 28 )
+{
+ m.t_non = REG(non);
+ m.t_eon = REG(eon);
+ m.t_dir = REG(dir);
+}
+MISC_CLOCK( 29 )
+{
+ if ( (m.every_other_sample ^= 1) != 0 )
+ m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read
+}
+MISC_CLOCK( 30 )
+{
+ if ( m.every_other_sample )
+ {
+ m.kon = m.new_kon;
+ m.t_koff = REG(koff) | m.mute_mask;
+ }
+
+ run_counters();
+
+ // Noise
+ if ( !read_counter( REG(flg) & 0x1F ) )
+ {
+ int feedback = (m.noise << 13) ^ (m.noise << 14);
+ m.noise = (feedback & 0x4000) ^ (m.noise >> 1);
+ }
+}
+
+
+//// Voices
+
+#define VOICE_CLOCK( n ) void Spc_Dsp::voice_##n( voice_t* const v )
+
+inline VOICE_CLOCK( V1 )
+{
+ m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4;
+ m.t_srcn = VREG(v->regs,srcn);
+}
+inline VOICE_CLOCK( V2 )
+{
+ // Read sample pointer (ignored if not needed)
+ uint8_t const* entry = &m.ram [m.t_dir_addr];
+ if ( !v->kon_delay )
+ entry += 2;
+ m.t_brr_next_addr = GET_LE16A( entry );
+
+ m.t_adsr0 = VREG(v->regs,adsr0);
+
+ // Read pitch, spread over two clocks
+ m.t_pitch = VREG(v->regs,pitchl);
+}
+inline VOICE_CLOCK( V3a )
+{
+ m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8;
+}
+inline VOICE_CLOCK( V3b )
+{
+ // Read BRR header and byte
+ m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF];
+ m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking
+}
+VOICE_CLOCK( V3c )
+{
+ // Pitch modulation using previous voice's output
+ if ( m.t_pmon & v->vbit )
+ m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10;
+
+ if ( v->kon_delay )
+ {
+ // Get ready to start BRR decoding on next sample
+ if ( v->kon_delay == 5 )
+ {
+ v->brr_addr = m.t_brr_next_addr;
+ v->brr_offset = 1;
+ v->buf_pos = 0;
+ m.t_brr_header = 0; // header is ignored on this sample
+ m.kon_check = true;
+ }
+
+ // Envelope is never run during KON
+ v->env = 0;
+ v->hidden_env = 0;
+
+ // Disable BRR decoding until last three samples
+ v->interp_pos = 0;
+ if ( --v->kon_delay & 3 )
+ v->interp_pos = 0x4000;
+
+ // Pitch is never added during KON
+ m.t_pitch = 0;
+ }
+
+ // Gaussian interpolation
+ {
+ int output;
+
+ switch ( m.interpolation_level )
+ {
+ case 0:
+ default:
+ output = interpolate( v );
+ break;
+
+ case 1:
+ output = interpolate_cubic( v );
+ break;
+
+ case 2:
+ output = interpolate_sinc( v );
+ break;
+ }
+
+ // Noise
+ if ( m.t_non & v->vbit )
+ output = (int16_t) (m.noise * 2);
+
+ // Apply envelope
+ m.t_output = (output * v->env) >> 11 & ~1;
+ v->t_envx_out = (uint8_t) (v->env >> 4);
+ }
+
+ // Immediate silence due to end of sample or soft reset
+ if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 )
+ {
+ v->env_mode = env_release;
+ v->env = 0;
+ }
+
+ if ( m.every_other_sample )
+ {
+ // KOFF
+ if ( m.t_koff & v->vbit )
+ v->env_mode = env_release;
+
+ // KON
+ if ( m.kon & v->vbit )
+ {
+ v->kon_delay = 5;
+ v->env_mode = env_attack;
+ }
+ }
+
+ // Run envelope for next sample
+ if ( !v->kon_delay )
+ run_envelope( v );
+}
+inline void Spc_Dsp::voice_output( voice_t const* v, int ch )
+{
+ // Check surround removal
+ int vol = (int8_t) VREG(v->regs,voll + ch);
+ int voln = (int8_t) VREG(v->regs,voll + ch ^ 1);
+ if ( vol * voln < m.surround_threshold )
+ vol ^= vol >> 7;
+
+ // Apply left/right volume
+ int amp = (m.t_output * vol) >> 7;
+
+ // Add to output total
+ m.t_main_out [ch] += amp;
+ CLAMP16( m.t_main_out [ch] );
+
+ // Optionally add to echo total
+ if ( m.t_eon & v->vbit )
+ {
+ m.t_echo_out [ch] += amp;
+ CLAMP16( m.t_echo_out [ch] );
+ }
+}
+VOICE_CLOCK( V4 )
+{
+ // Decode BRR
+ m.t_looped = 0;
+ if ( v->interp_pos >= 0x4000 )
+ {
+ decode_brr( v );
+
+ if ( (v->brr_offset += 2) >= brr_block_size )
+ {
+ // Start decoding next BRR block
+ assert( v->brr_offset == brr_block_size );
+ v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF;
+ if ( m.t_brr_header & 1 )
+ {
+ v->brr_addr = m.t_brr_next_addr;
+ m.t_looped = v->vbit;
+ }
+ v->brr_offset = 1;
+ }
+ }
+
+ // Apply pitch
+ v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch;
+
+ // Keep from getting too far ahead (when using pitch modulation)
+ if ( v->interp_pos > 0x7FFF )
+ v->interp_pos = 0x7FFF;
+
+ // Output left
+ voice_output( v, 0 );
+}
+inline VOICE_CLOCK( V5 )
+{
+ // Output right
+ voice_output( v, 1 );
+
+ // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier
+ int endx_buf = REG(endx) | m.t_looped;
+
+ // Clear bit in ENDX if KON just began
+ if ( v->kon_delay == 5 )
+ endx_buf &= ~v->vbit;
+ m.endx_buf = (uint8_t) endx_buf;
+}
+inline VOICE_CLOCK( V6 )
+{
+ (void) v; // avoid compiler warning about unused v
+ m.outx_buf = (uint8_t) (m.t_output >> 8);
+}
+inline VOICE_CLOCK( V7 )
+{
+ // Update ENDX
+ REG(endx) = m.endx_buf;
+
+ m.envx_buf = v->t_envx_out;
+}
+inline VOICE_CLOCK( V8 )
+{
+ // Update OUTX
+ VREG(v->regs,outx) = m.outx_buf;
+}
+inline VOICE_CLOCK( V9 )
+{
+ // Update ENVX
+ VREG(v->regs,envx) = m.envx_buf;
+}
+
+// Most voices do all these in one clock, so make a handy composite
+inline VOICE_CLOCK( V3 )
+{
+ voice_V3a( v );
+ voice_V3b( v );
+ voice_V3c( v );
+}
+
+// Common combinations of voice steps on different voices. This greatly reduces
+// code size and allows everything to be inlined in these functions.
+VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); }
+VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); }
+VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); }
+
+
+//// Echo
+
+// Current echo buffer pointer for left/right channel
+#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2])
+
+// Sample in echo history buffer, where 0 is the oldest
+#define ECHO_FIR( i ) (m.echo_hist_pos [i])
+
+// Calculate FIR point for left/right channel
+#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6)
+
+#define ECHO_CLOCK( n ) inline void Spc_Dsp::echo_##n()
+
+inline void Spc_Dsp::echo_read( int ch )
+{
+ int s = GET_LE16SA( ECHO_PTR( ch ) );
+ // second copy simplifies wrap-around handling
+ ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1;
+}
+
+ECHO_CLOCK( 22 )
+{
+ // History
+ if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] )
+ m.echo_hist_pos = m.echo_hist;
+
+ m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF;
+ echo_read( 0 );
+
+ // FIR (using l and r temporaries below helps compiler optimize)
+ int l = CALC_FIR( 0, 0 );
+ int r = CALC_FIR( 0, 1 );
+
+ m.t_echo_in [0] = l;
+ m.t_echo_in [1] = r;
+}
+ECHO_CLOCK( 23 )
+{
+ int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 );
+ int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 );
+
+ m.t_echo_in [0] += l;
+ m.t_echo_in [1] += r;
+
+ echo_read( 1 );
+}
+ECHO_CLOCK( 24 )
+{
+ int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 );
+ int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 );
+
+ m.t_echo_in [0] += l;
+ m.t_echo_in [1] += r;
+}
+ECHO_CLOCK( 25 )
+{
+ int l = m.t_echo_in [0] + CALC_FIR( 6, 0 );
+ int r = m.t_echo_in [1] + CALC_FIR( 6, 1 );
+
+ l = (int16_t) l;
+ r = (int16_t) r;
+
+ l += (int16_t) CALC_FIR( 7, 0 );
+ r += (int16_t) CALC_FIR( 7, 1 );
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ m.t_echo_in [0] = l & ~1;
+ m.t_echo_in [1] = r & ~1;
+}
+inline int Spc_Dsp::echo_output( int ch )
+{
+ // Check surround removal
+ int vol = (int8_t) REG(mvoll + ch * 0x10);
+ int voln = (int8_t) REG(mvoll + ch * 0x10 ^ 0x10);
+ if ( vol * voln < m.surround_threshold )
+ vol ^= vol >> 7;
+
+ int out = (int16_t) ((m.t_main_out [ch] * vol) >> 7) +
+ (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7);
+ CLAMP16( out );
+ return out;
+}
+ECHO_CLOCK( 26 )
+{
+ // Left output volumes
+ // (save sample for next clock so we can output both together)
+ m.t_main_out [0] = echo_output( 0 );
+
+ // Echo feedback
+ int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7);
+ int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7);
+
+ CLAMP16( l );
+ CLAMP16( r );
+
+ m.t_echo_out [0] = l & ~1;
+ m.t_echo_out [1] = r & ~1;
+}
+ECHO_CLOCK( 27 )
+{
+ // Output
+ int l = m.t_main_out [0];
+ int r = echo_output( 1 );
+ m.t_main_out [0] = 0;
+ m.t_main_out [1] = 0;
+
+ // TODO: global muting isn't this simple (turns DAC on and off
+ // or something, causing small ~37-sample pulse when first muted)
+ if ( REG(flg) & 0x40 )
+ {
+ l = 0;
+ r = 0;
+ }
+
+ // Output sample to DAC
+ #ifdef SPC_DSP_OUT_HOOK
+ SPC_DSP_OUT_HOOK( l, r );
+ #else
+ sample_t* out = m.out;
+ WRITE_SAMPLES( l, r, out );
+ m.out = out;
+ #endif
+}
+ECHO_CLOCK( 28 )
+{
+ m.t_echo_enabled = REG(flg);
+}
+inline void Spc_Dsp::echo_write( int ch )
+{
+ if ( !(m.t_echo_enabled & 0x20) )
+ SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] );
+ m.t_echo_out [ch] = 0;
+}
+ECHO_CLOCK( 29 )
+{
+ m.t_esa = REG(esa);
+
+ if ( !m.echo_offset )
+ m.echo_length = (REG(edl) & 0x0F) * 0x800;
+
+ m.echo_offset += 4;
+ if ( m.echo_offset >= m.echo_length )
+ m.echo_offset = 0;
+
+ // Write left echo
+ echo_write( 0 );
+
+ m.t_echo_enabled = REG(flg);
+}
+ECHO_CLOCK( 30 )
+{
+ // Write right echo
+ echo_write( 1 );
+}
+
+
+//// Timing
+
+// Execute clock for a particular voice
+#define V( clock, voice ) voice_##clock( &m.voices [voice] );
+
+/* The most common sequence of clocks uses composite operations
+for efficiency. For example, the following are equivalent to the
+individual steps on the right:
+
+V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5)
+V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4)
+V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */
+
+// Voice 0 1 2 3 4 5 6 7
+#define GEN_DSP_TIMING \
+PHASE( 0) V(V5,0)V(V2,1)\
+PHASE( 1) V(V6,0)V(V3,1)\
+PHASE( 2) V(V7_V4_V1,0)\
+PHASE( 3) V(V8_V5_V2,0)\
+PHASE( 4) V(V9_V6_V3,0)\
+PHASE( 5) V(V7_V4_V1,1)\
+PHASE( 6) V(V8_V5_V2,1)\
+PHASE( 7) V(V9_V6_V3,1)\
+PHASE( 8) V(V7_V4_V1,2)\
+PHASE( 9) V(V8_V5_V2,2)\
+PHASE(10) V(V9_V6_V3,2)\
+PHASE(11) V(V7_V4_V1,3)\
+PHASE(12) V(V8_V5_V2,3)\
+PHASE(13) V(V9_V6_V3,3)\
+PHASE(14) V(V7_V4_V1,4)\
+PHASE(15) V(V8_V5_V2,4)\
+PHASE(16) V(V9_V6_V3,4)\
+PHASE(17) V(V1,0) V(V7,5)V(V4,6)\
+PHASE(18) V(V8_V5_V2,5)\
+PHASE(19) V(V9_V6_V3,5)\
+PHASE(20) V(V1,1) V(V7,6)V(V4,7)\
+PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\
+PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\
+PHASE(23) V(V7,7) echo_23();\
+PHASE(24) V(V8,7) echo_24();\
+PHASE(25) V(V3b,0) V(V9,7) echo_25();\
+PHASE(26) echo_26();\
+PHASE(27) misc_27(); echo_27();\
+PHASE(28) misc_28(); echo_28();\
+PHASE(29) misc_29(); echo_29();\
+PHASE(30) misc_30();V(V3c,0) echo_30();\
+PHASE(31) V(V4,0) V(V1,2)\
+
+#if !SPC_DSP_CUSTOM_RUN
+
+void Spc_Dsp::run( int clocks_remain )
+{
+ require( clocks_remain > 0 );
+
+ int const phase = m.phase;
+ m.phase = (phase + clocks_remain) & 31;
+ switch ( phase )
+ {
+ loop:
+
+ #define PHASE( n ) if ( n && !--clocks_remain ) break; case n:
+ GEN_DSP_TIMING
+ #undef PHASE
+
+ if ( --clocks_remain )
+ goto loop;
+ }
+}
+
+#endif
+
+
+//// Setup
+
+void Spc_Dsp::init( void* ram_64k )
+{
+ m.ram = (uint8_t*) ram_64k;
+ mute_voices( 0 );
+ disable_surround( false );
+ interpolation_level( 0 );
+ set_output( 0, 0 );
+ reset();
+
+ #ifndef NDEBUG
+ // be sure this sign-extends
+ assert( (int16_t) 0x8000 == -0x8000 );
+
+ // be sure right shift preserves sign
+ assert( (-1 >> 1) == -1 );
+
+ // check clamp macro
+ int i;
+ i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );
+ i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );
+
+ blargg_verify_byte_order();
+ #endif
+}
+
+void Spc_Dsp::soft_reset_common()
+{
+ require( m.ram ); // init() must have been called already
+
+ m.noise = 0x4000;
+ m.echo_hist_pos = m.echo_hist;
+ m.every_other_sample = 1;
+ m.echo_offset = 0;
+ m.phase = 0;
+
+ init_counter();
+}
+
+void Spc_Dsp::soft_reset()
+{
+ REG(flg) = 0xE0;
+ soft_reset_common();
+}
+
+void Spc_Dsp::load( uint8_t const regs [register_count] )
+{
+ memcpy( m.regs, regs, sizeof m.regs );
+ memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );
+
+ // Internal state
+ for ( int i = voice_count; --i >= 0; )
+ {
+ voice_t* v = &m.voices [i];
+ v->brr_offset = 1;
+ v->vbit = 1 << i;
+ v->regs = &m.regs [i * 0x10];
+ }
+ m.new_kon = REG(kon);
+ m.t_dir = REG(dir);
+ m.t_esa = REG(esa);
+
+ soft_reset_common();
+}
+
+void Spc_Dsp::reset() { load( initial_regs ); }
+
+
+//// State save/load
+
+#if !SPC_NO_COPY_STATE_FUNCS
+
+void SPC_State_Copier::copy( void* state, size_t size )
+{
+ func( buf, state, size );
+}
+
+int SPC_State_Copier::copy_int( int state, int size )
+{
+ BOOST::uint8_t s [2];
+ SET_LE16( s, state );
+ func( buf, &s, size );
+ return GET_LE16( s );
+}
+
+void SPC_State_Copier::skip( int count )
+{
+ if ( count > 0 )
+ {
+ char temp [64];
+ memset( temp, 0, sizeof temp );
+ do
+ {
+ int n = sizeof temp;
+ if ( n > count )
+ n = count;
+ count -= n;
+ func( buf, temp, n );
+ }
+ while ( count );
+ }
+}
+
+void SPC_State_Copier::extra()
+{
+ int n = 0;
+ SPC_State_Copier& copier = *this;
+ SPC_COPY( uint8_t, n );
+ skip( n );
+}
+
+void Spc_Dsp::copy_state( unsigned char** io, copy_func_t copy )
+{
+ SPC_State_Copier copier( io, copy );
+
+ // DSP registers
+ copier.copy( m.regs, register_count );
+
+ // Internal state
+
+ // Voices
+ int i;
+ for ( i = 0; i < voice_count; i++ )
+ {
+ voice_t* v = &m.voices [i];
+
+ // BRR buffer
+ int i;
+ for ( i = 0; i < brr_buf_size; i++ )
+ {
+ int s = v->buf [i];
+ SPC_COPY( int16_t, s );
+ v->buf [i] = v->buf [i + brr_buf_size] = s;
+ }
+
+ SPC_COPY( uint16_t, v->interp_pos );
+ SPC_COPY( uint16_t, v->brr_addr );
+ SPC_COPY( uint16_t, v->env );
+ SPC_COPY( int16_t, v->hidden_env );
+ SPC_COPY( uint8_t, v->buf_pos );
+ SPC_COPY( uint8_t, v->brr_offset );
+ SPC_COPY( uint8_t, v->kon_delay );
+ {
+ int m = v->env_mode;
+ SPC_COPY( uint8_t, m );
+ v->env_mode = (enum env_mode_t) m;
+ }
+ SPC_COPY( uint8_t, v->t_envx_out );
+
+ copier.extra();
+ }
+
+ // Echo history
+ for ( i = 0; i < echo_hist_size; i++ )
+ {
+ int j;
+ for ( j = 0; j < 2; j++ )
+ {
+ int s = m.echo_hist_pos [i] [j];
+ SPC_COPY( int16_t, s );
+ m.echo_hist [i] [j] = s; // write back at offset 0
+ }
+ }
+ m.echo_hist_pos = m.echo_hist;
+ memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] );
+
+ // Misc
+ SPC_COPY( uint8_t, m.every_other_sample );
+ SPC_COPY( uint8_t, m.kon );
+
+ SPC_COPY( uint16_t, m.noise );
+ SPC_COPY( uint16_t, m.counter );
+ SPC_COPY( uint16_t, m.echo_offset );
+ SPC_COPY( uint16_t, m.echo_length );
+ SPC_COPY( uint8_t, m.phase );
+
+ SPC_COPY( uint8_t, m.new_kon );
+ SPC_COPY( uint8_t, m.endx_buf );
+ SPC_COPY( uint8_t, m.envx_buf );
+ SPC_COPY( uint8_t, m.outx_buf );
+
+ SPC_COPY( uint8_t, m.t_pmon );
+ SPC_COPY( uint8_t, m.t_non );
+ SPC_COPY( uint8_t, m.t_eon );
+ SPC_COPY( uint8_t, m.t_dir );
+ SPC_COPY( uint8_t, m.t_koff );
+
+ SPC_COPY( uint16_t, m.t_brr_next_addr );
+ SPC_COPY( uint8_t, m.t_adsr0 );
+ SPC_COPY( uint8_t, m.t_brr_header );
+ SPC_COPY( uint8_t, m.t_brr_byte );
+ SPC_COPY( uint8_t, m.t_srcn );
+ SPC_COPY( uint8_t, m.t_esa );
+ SPC_COPY( uint8_t, m.t_echo_enabled );
+
+ SPC_COPY( int16_t, m.t_main_out [0] );
+ SPC_COPY( int16_t, m.t_main_out [1] );
+ SPC_COPY( int16_t, m.t_echo_out [0] );
+ SPC_COPY( int16_t, m.t_echo_out [1] );
+ SPC_COPY( int16_t, m.t_echo_in [0] );
+ SPC_COPY( int16_t, m.t_echo_in [1] );
+
+ SPC_COPY( uint16_t, m.t_dir_addr );
+ SPC_COPY( uint16_t, m.t_pitch );
+ SPC_COPY( int16_t, m.t_output );
+ SPC_COPY( uint16_t, m.t_echo_ptr );
+ SPC_COPY( uint8_t, m.t_looped );
+
+ copier.extra();
+}
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.h b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.h
new file mode 100644
index 00000000..bd9b620c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Dsp.h
@@ -0,0 +1,315 @@
+// Highly accurate SNES SPC-700 DSP emulator
+
+// snes_spc 0.9.0
+#ifndef SPC_DSP_H
+#define SPC_DSP_H
+
+#include "blargg_common.h"
+
+extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
+
+class Spc_Dsp {
+public:
+ typedef BOOST::uint8_t uint8_t;
+
+// Setup
+
+ // Initializes DSP and has it use the 64K RAM provided
+ void init( void* ram_64k );
+
+ // Sets destination for output samples. If out is NULL or out_size is 0,
+ // doesn't generate any.
+ typedef short sample_t;
+ void set_output( sample_t* out, int out_size );
+
+ // Number of samples written to output since it was last set, always
+ // a multiple of 2. Undefined if more samples were generated than
+ // output buffer could hold.
+ int sample_count() const;
+
+// Emulation
+
+ // Resets DSP to power-on state
+ void reset();
+
+ // Emulates pressing reset switch on SNES
+ void soft_reset();
+
+ // Reads/writes DSP registers. For accuracy, you must first call run()
+ // to catch the DSP up to present.
+ int read ( int addr ) const;
+ void write( int addr, int data );
+
+ // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
+ // a pair of samples is be generated.
+ void run( int clock_count );
+
+// Sound control
+
+ // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
+ // Reduces emulation accuracy.
+ enum { voice_count = 8 };
+ void mute_voices( int mask );
+
+// State
+
+ // Resets DSP and uses supplied values to initialize registers
+ enum { register_count = 128 };
+ void load( uint8_t const regs [register_count] );
+
+ // Saves/loads exact emulator state
+ enum { state_size = 640 }; // maximum space needed when saving
+ typedef dsp_copy_func_t copy_func_t;
+ void copy_state( unsigned char** io, copy_func_t );
+
+ // Returns non-zero if new key-on events occurred since last call
+ bool check_kon();
+
+// DSP register addresses
+
+ // Global registers
+ enum {
+ r_mvoll = 0x0C, r_mvolr = 0x1C,
+ r_evoll = 0x2C, r_evolr = 0x3C,
+ r_kon = 0x4C, r_koff = 0x5C,
+ r_flg = 0x6C, r_endx = 0x7C,
+ r_efb = 0x0D, r_pmon = 0x2D,
+ r_non = 0x3D, r_eon = 0x4D,
+ r_dir = 0x5D, r_esa = 0x6D,
+ r_edl = 0x7D,
+ r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
+ };
+
+ // Voice registers
+ enum {
+ v_voll = 0x00, v_volr = 0x01,
+ v_pitchl = 0x02, v_pitchh = 0x03,
+ v_srcn = 0x04, v_adsr0 = 0x05,
+ v_adsr1 = 0x06, v_gain = 0x07,
+ v_envx = 0x08, v_outx = 0x09
+ };
+
+public:
+ enum { extra_size = 16 };
+ sample_t* extra() { return m.extra; }
+ sample_t const* out_pos() const { return m.out; }
+ void disable_surround( bool disable = true );
+ void interpolation_level( int level = 0 ) { m.interpolation_level = level; }
+public:
+ BLARGG_DISABLE_NOTHROW
+
+ typedef BOOST::int8_t int8_t;
+ typedef BOOST::int16_t int16_t;
+
+ enum { echo_hist_size = 8 };
+
+ enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
+ enum { brr_buf_size = 12 };
+ struct voice_t
+ {
+ int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
+ int buf_pos; // place in buffer where next samples will be decoded
+ int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
+ int brr_addr; // address of current BRR block
+ int brr_offset; // current decoding offset in BRR block
+ uint8_t* regs; // pointer to voice's DSP registers
+ int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
+ int kon_delay; // KON delay/current setup phase
+ env_mode_t env_mode;
+ int env; // current envelope level
+ int hidden_env; // used by GAIN mode 7, very obscure quirk
+ uint8_t t_envx_out;
+ };
+
+private:
+ enum { brr_block_size = 9 };
+
+ struct state_t
+ {
+ uint8_t regs [register_count];
+
+ // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
+ int echo_hist [echo_hist_size * 2] [2];
+ int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
+
+ int every_other_sample; // toggles every sample
+ int kon; // KON value when last checked
+ int noise;
+ int counter;
+ int echo_offset; // offset from ESA in echo buffer
+ int echo_length; // number of bytes that echo_offset will stop at
+ int phase; // next clock cycle to run (0-31)
+ bool kon_check; // set when a new KON occurs
+
+ // Hidden registers also written to when main register is written to
+ int new_kon;
+ uint8_t endx_buf;
+ uint8_t envx_buf;
+ uint8_t outx_buf;
+
+ // Temporary state between clocks
+
+ // read once per sample
+ int t_pmon;
+ int t_non;
+ int t_eon;
+ int t_dir;
+ int t_koff;
+
+ // read a few clocks ahead then used
+ int t_brr_next_addr;
+ int t_adsr0;
+ int t_brr_header;
+ int t_brr_byte;
+ int t_srcn;
+ int t_esa;
+ int t_echo_enabled;
+
+ // internal state that is recalculated every sample
+ int t_dir_addr;
+ int t_pitch;
+ int t_output;
+ int t_looped;
+ int t_echo_ptr;
+
+ // left/right sums
+ int t_main_out [2];
+ int t_echo_out [2];
+ int t_echo_in [2];
+
+ voice_t voices [voice_count];
+
+ // non-emulation state
+ uint8_t* ram; // 64K shared RAM between DSP and SMP
+ int mute_mask;
+ int surround_threshold;
+ int interpolation_level;
+ sample_t* out;
+ sample_t* out_end;
+ sample_t* out_begin;
+ sample_t extra [extra_size];
+ };
+ state_t m;
+
+ void init_counter();
+ void run_counters();
+ unsigned read_counter( int rate );
+
+ int interpolate( voice_t const* v );
+ int interpolate_cubic( voice_t const* v );
+ int interpolate_sinc( voice_t const* v );
+ void run_envelope( voice_t* const v );
+ void decode_brr( voice_t* v );
+
+ void misc_27();
+ void misc_28();
+ void misc_29();
+ void misc_30();
+
+ void voice_output( voice_t const* v, int ch );
+ void voice_V1( voice_t* const );
+ void voice_V2( voice_t* const );
+ void voice_V3( voice_t* const );
+ void voice_V3a( voice_t* const );
+ void voice_V3b( voice_t* const );
+ void voice_V3c( voice_t* const );
+ void voice_V4( voice_t* const );
+ void voice_V5( voice_t* const );
+ void voice_V6( voice_t* const );
+ void voice_V7( voice_t* const );
+ void voice_V8( voice_t* const );
+ void voice_V9( voice_t* const );
+ void voice_V7_V4_V1( voice_t* const );
+ void voice_V8_V5_V2( voice_t* const );
+ void voice_V9_V6_V3( voice_t* const );
+
+ void echo_read( int ch );
+ int echo_output( int ch );
+ void echo_write( int ch );
+ void echo_22();
+ void echo_23();
+ void echo_24();
+ void echo_25();
+ void echo_26();
+ void echo_27();
+ void echo_28();
+ void echo_29();
+ void echo_30();
+
+ void soft_reset_common();
+};
+
+#include <assert.h>
+
+inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; }
+
+inline int Spc_Dsp::read( int addr ) const
+{
+ assert( (unsigned) addr < register_count );
+ return m.regs [addr];
+}
+
+inline void Spc_Dsp::write( int addr, int data )
+{
+ assert( (unsigned) addr < register_count );
+
+ m.regs [addr] = (uint8_t) data;
+ switch ( addr & 0x0F )
+ {
+ case v_envx:
+ m.envx_buf = (uint8_t) data;
+ break;
+
+ case v_outx:
+ m.outx_buf = (uint8_t) data;
+ break;
+
+ case 0x0C:
+ if ( addr == r_kon )
+ m.new_kon = (uint8_t) data;
+
+ if ( addr == r_endx ) // always cleared, regardless of data written
+ {
+ m.endx_buf = 0;
+ m.regs [r_endx] = 0;
+ }
+ break;
+ }
+}
+
+inline void Spc_Dsp::mute_voices( int mask ) { m.mute_mask = mask; }
+
+inline void Spc_Dsp::disable_surround( bool disable )
+{
+ m.surround_threshold = disable ? 0 : -0x4000;
+}
+
+inline bool Spc_Dsp::check_kon()
+{
+ bool old = m.kon_check;
+ m.kon_check = 0;
+ return old;
+}
+
+#if !SPC_NO_COPY_STATE_FUNCS
+
+class SPC_State_Copier {
+ Spc_Dsp::copy_func_t func;
+ unsigned char** buf;
+public:
+ SPC_State_Copier( unsigned char** p, Spc_Dsp::copy_func_t f ) { func = f; buf = p; }
+ void copy( void* state, size_t size );
+ int copy_int( int state, int size );
+ void skip( int count );
+ void extra();
+};
+
+#define SPC_COPY( type, state )\
+{\
+ state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
+ assert( (BOOST::type) state == state );\
+}
+
+#endif
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.cpp
new file mode 100644
index 00000000..51cb10bc
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.cpp
@@ -0,0 +1,393 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Spc_Emu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2004-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// TODO: support Spc_Filter's bass
+
+Spc_Emu::Spc_Emu()
+{
+ set_type( gme_spc_type );
+ set_gain( 1.4 );
+}
+
+Spc_Emu::~Spc_Emu() { }
+
+// Track info
+
+int const trailer_offset = 0x10200;
+
+inline byte const* Spc_Emu::trailer_() const { return &file_begin() [min( file_size(), trailer_offset )]; }
+
+inline int Spc_Emu::trailer_size_() const { return max( 0, file_size() - trailer_offset ); }
+
+static void get_spc_xid6( byte const begin [], int size, track_info_t* out )
+{
+ // header
+ byte const* end = begin + size;
+ if ( size < 8 || memcmp( begin, "xid6", 4 ) )
+ {
+ check( false );
+ return;
+ }
+ int info_size = get_le32( begin + 4 );
+ byte const* in = begin + 8;
+ if ( end - in > info_size )
+ {
+ dprintf( "SPC: Extra data after xid6\n" );
+ end = in + info_size;
+ }
+
+ int year = 0;
+ char copyright [256 + 5];
+ int copyright_len = 0;
+ int const year_len = 5;
+ int disc = 0, track = 0;
+
+ 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 )
+ {
+ dprintf( "SPC: xid6 goes past end" );
+ break; // block goes past end of data
+ }
+
+ // handle specific block types
+ char* field = NULL;
+ 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 0x10: field = out->ost; break;
+ case 0x11: disc = data; break;
+ case 0x12: track = data; 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 )
+ {
+ int loop = out->length - out->intro_length;
+ if ( loop >= 2000 )
+ out->loop_length = loop;
+ }
+ }
+ break;
+ */
+
+ case 0x33:
+ check( len == 4 );
+ if ( len >= 4 )
+ {
+ out->fade_length = get_le32( in ) / 64;
+ }
+ break;
+
+ case 0x13:
+ copyright_len = min( len, (int) sizeof copyright - year_len );
+ memcpy( &copyright [year_len], in, copyright_len );
+ break;
+
+ default:
+ if ( id < 0x01 || (id > 0x07 && id < 0x10) ||
+ (id > 0x14 && id < 0x30) || id > 0x36 )
+ dprintf( "SPC: Unknown 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: xid6 info tag wasn't properly padded to align\n" );
+ break;
+ }
+ }
+ }
+
+ char* p = &copyright [year_len];
+ if ( year )
+ {
+ *--p = ' ';
+ // avoid using bloated printf
+ 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 );
+
+ if ( disc > 0 && disc <= 9 )
+ {
+ out->disc [0] = disc + '0';
+ out->disc [1] = 0;
+ }
+
+ if ( track > 255 && track < ( ( 100 << 8 ) - 1 ) )
+ {
+ char* p = &copyright [3];
+ *p = 0;
+ if ( track & 255 ) *--p = char (track & 255);
+ track >>= 8;
+ for ( int n = 2; n-- && track; )
+ {
+ *--p = char (track % 10 + '0');
+ track /= 10;
+ }
+ memcpy( out->track, p, &copyright [4] - p );
+ }
+
+ check( in == end );
+}
+
+static void get_spc_info( Spc_Emu::header_t const& h, byte const xid6 [], int xid6_size,
+ track_info_t* out )
+{
+ // decode length (can be in text or binary format, sometimes ambiguous ugh)
+ int len_secs = 0;
+ int i;
+ for ( 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;
+
+ long fade_msec = 0;
+ for ( i = 0; i < 4; i++ )
+ {
+ unsigned n = h.fade_msec [i] - '0';
+ if ( n > 9 )
+ {
+ if ( i == 1 && (h.author [0] || !h.author [1]) )
+ fade_msec = -1;
+ break;
+ }
+ fade_msec *= 10;
+ fade_msec += n;
+ }
+ if ( i == 4 && unsigned( h.author [0] - '0' ) <= 9 )
+ fade_msec = fade_msec * 10 + h.author [0] - '0';
+ if ( fade_msec < 0 || fade_msec > 0x7FFF )
+ fade_msec = get_le32( h.fade_msec );
+ if ( fade_msec < 0x7FFF )
+ out->fade_length = fade_msec;
+
+ 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 blargg_ok;
+}
+
+static blargg_err_t check_spc_header( void const* header )
+{
+ if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) )
+ return blargg_err_file_type;
+ return blargg_ok;
+}
+
+struct Spc_File : Gme_Info_
+{
+ Spc_Emu::header_t header;
+ blargg_vector<byte> xid6;
+
+ Spc_File() { set_type( gme_spc_type ); }
+
+ blargg_err_t load_( Data_Reader& in )
+ {
+ int file_size = in.remain();
+ if ( file_size < Snes_Spc::spc_min_file_size )
+ return blargg_err_file_type;
+ RETURN_ERR( in.read( &header, header.size ) );
+ RETURN_ERR( check_spc_header( header.tag ) );
+ int const xid6_offset = 0x10200;
+ int xid6_size = file_size - xid6_offset;
+ if ( xid6_size > 0 )
+ {
+ RETURN_ERR( xid6.resize( xid6_size ) );
+ RETURN_ERR( in.skip( xid6_offset - header.size ) );
+ RETURN_ERR( in.read( xid6.begin(), xid6.size() ) );
+ }
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ get_spc_info( header, xid6.begin(), xid6.size(), out );
+ return blargg_ok;
+ }
+};
+
+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_( int sample_rate )
+{
+ RETURN_ERR( apu.init() );
+ if ( sample_rate != native_sample_rate )
+ {
+ RETURN_ERR( resampler.resize_buffer( native_sample_rate / 20 * 2 ) );
+ RETURN_ERR( resampler.set_rate( (double) native_sample_rate / sample_rate ) ); // 0.9965 rolloff
+ }
+ return blargg_ok;
+}
+
+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 [], int size )
+{
+ assert( offsetof (header_t,unused2 [46]) == header_t::size );
+ set_voice_count( Spc_Dsp::voice_count );
+ if ( size < Snes_Spc::spc_min_file_size )
+ return blargg_err_file_type;
+
+ static const char* const names [Spc_Dsp::voice_count] = {
+ "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8"
+ };
+ set_voice_names( names );
+
+ return check_spc_header( in );
+}
+
+// Emulation
+
+void Spc_Emu::set_tempo_( double t )
+{
+ apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) );
+}
+
+blargg_err_t Spc_Emu::start_track_( int track )
+{
+ RETURN_ERR( Music_Emu::start_track_( track ) );
+ resampler.clear();
+ filter.clear();
+ RETURN_ERR( apu.load_spc( file_begin(), file_size() ) );
+ filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) );
+ apu.clear_echo();
+ return blargg_ok;
+}
+
+blargg_err_t Spc_Emu::play_and_filter( int count, sample_t out [] )
+{
+ RETURN_ERR( apu.play( count, out ) );
+ filter.run( out, count );
+ return blargg_ok;
+}
+
+blargg_err_t Spc_Emu::skip_( int count )
+{
+ if ( sample_rate() != native_sample_rate )
+ {
+ count = (int) (count * resampler.rate()) & ~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 ) );
+ filter.clear();
+ }
+
+ // 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_( int count, sample_t out [] )
+{
+ if ( sample_rate() == native_sample_rate )
+ return play_and_filter( count, out );
+
+ int remain = count;
+ while ( remain > 0 )
+ {
+ remain -= resampler.read( &out [count - remain], remain );
+ if ( remain > 0 )
+ {
+ int n = resampler.buffer_free();
+ RETURN_ERR( play_and_filter( n, resampler.buffer() ) );
+ resampler.write( n );
+ }
+ }
+ check( remain == 0 );
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.h
new file mode 100644
index 00000000..00901424
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Emu.h
@@ -0,0 +1,85 @@
+// Super Nintendo SPC music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef SPC_EMU_H
+#define SPC_EMU_H
+
+#include "Music_Emu.h"
+#include "Snes_Spc.h"
+#include "Spc_Filter.h"
+
+#if GME_SPC_FAST_RESAMPLER
+ #include "Upsampler.h"
+ typedef Upsampler Spc_Emu_Resampler;
+#else
+ #include "Fir_Resampler.h"
+ typedef Fir_Resampler<24> Spc_Emu_Resampler;
+#endif
+
+class Spc_Emu : public Music_Emu {
+public:
+ // The Super Nintendo hardware samples at 32kHz. Other sample rates are
+ // handled by resampling the 32kHz output; emulation accuracy is not affected.
+ enum { native_sample_rate = 32000 };
+
+ // Disables annoying pseudo-surround effect some music uses
+ void disable_surround( bool disable = true ) { apu.disable_surround( disable ); }
+
+ // Enables gaussian, cubic or sinc interpolation
+ void interpolation_level( int level = 0 ) { apu.interpolation_level( level ); }
+
+ // SPC file header
+ struct header_t
+ {
+ enum { size = 0x100 };
+
+ char tag [35];
+ byte format;
+ byte version;
+ byte pc [ 2];
+ byte a, x, y, psw, sp;
+ byte unused [ 2];
+ char song [32];
+ char game [32];
+ char dumper [16];
+ char comment [32];
+ byte date [11];
+ byte len_secs [ 3];
+ byte fade_msec [ 4];
+ char author [32]; // sometimes first char should be skipped (see official SPC spec)
+ byte mute_mask;
+ byte emulator;
+ byte unused2 [46];
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return *(header_t const*) file_begin(); }
+
+ static gme_type_t static_type() { return gme_spc_type; }
+
+// Implementation
+public:
+ Spc_Emu();
+ ~Spc_Emu();
+
+protected:
+ virtual blargg_err_t load_mem_( byte const [], int );
+ virtual blargg_err_t track_info_( track_info_t*, int track ) const;
+ virtual blargg_err_t set_sample_rate_( int );
+ virtual blargg_err_t start_track_( int );
+ virtual blargg_err_t play_( int, sample_t [] );
+ virtual blargg_err_t skip_( int );
+ virtual void mute_voices_( int );
+ virtual void set_tempo_( double );
+
+private:
+ Spc_Emu_Resampler resampler;
+ SPC_Filter filter;
+ Snes_Spc apu;
+
+ byte const* trailer_() const;
+ int trailer_size_() const;
+ blargg_err_t play_and_filter( int count, sample_t out [] );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Spc_Filter.h b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Filter.h
new file mode 100644
index 00000000..9de56b18
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Spc_Filter.h
@@ -0,0 +1,53 @@
+// Simple low-pass and high-pass filter to better match sound output of a SNES
+
+// Game_Music_Emu 0.5.5
+#ifndef SPC_FILTER_H
+#define SPC_FILTER_H
+
+#include "blargg_common.h"
+
+struct SPC_Filter {
+public:
+
+ // Filters count samples of stereo sound in place. Count must be a multiple of 2.
+ typedef short sample_t;
+ void run( sample_t* io, int count );
+
+// Optional features
+
+ // Clears filter to silence
+ void clear();
+
+ // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit
+ // are fine, since output is clamped to 16-bit sample range.
+ enum { gain_unit = 0x100 };
+ void set_gain( int gain );
+
+ // Enables/disables filtering (when disabled, gain is still applied)
+ void enable( bool b );
+
+ // Sets amount of bass (logarithmic scale)
+ enum { bass_none = 0 };
+ enum { bass_norm = 8 }; // normal amount
+ enum { bass_max = 31 };
+ void set_bass( int bass );
+
+public:
+ SPC_Filter();
+ BLARGG_DISABLE_NOTHROW
+private:
+ enum { gain_bits = 8 };
+ int gain;
+ int bass;
+ bool enabled;
+ struct chan_t { int p1, pp1, sum; };
+ chan_t ch [2];
+};
+
+inline void SPC_Filter::enable( bool b ) { enabled = b; }
+
+inline void SPC_Filter::set_gain( int g ) { gain = g; }
+
+inline void SPC_Filter::set_bass( int b ) { bass = b; }
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.cpp
new file mode 100644
index 00000000..dc54ea81
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.cpp
@@ -0,0 +1,293 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Track_Filter.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const fade_block_size = 512;
+int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
+int const silence_threshold = 8;
+
+blargg_err_t Track_Filter::init( callbacks_t* c )
+{
+ callbacks = c;
+ return buf.resize( buf_size );
+}
+
+void Track_Filter::clear_time_vars()
+{
+ emu_time = buf_remain;
+ out_time = 0;
+ silence_time = 0;
+ silence_count = 0;
+}
+
+void Track_Filter::stop()
+{
+ emu_track_ended_ = true;
+ track_ended_ = true;
+ fade_start = indefinite_count;
+ fade_step = 1;
+ buf_remain = 0;
+ emu_error = NULL;
+ clear_time_vars();
+}
+
+Track_Filter::Track_Filter() : setup_()
+{
+ callbacks = NULL;
+ setup_.max_silence = indefinite_count;
+ silence_ignored_ = false;
+ stop();
+}
+
+Track_Filter::~Track_Filter() { }
+
+blargg_err_t Track_Filter::start_track()
+{
+ emu_error = NULL;
+ stop();
+
+ emu_track_ended_ = false;
+ track_ended_ = false;
+
+ if ( !silence_ignored_ )
+ {
+ // play until non-silence or end of track
+ while ( emu_time < setup_.max_initial )
+ {
+ fill_buf();
+ if ( buf_remain | emu_track_ended_ )
+ break;
+ }
+ }
+
+ clear_time_vars();
+ return emu_error;
+}
+
+void Track_Filter::end_track_if_error( blargg_err_t err )
+{
+ if ( err )
+ {
+ emu_error = err;
+ emu_track_ended_ = true;
+ }
+}
+
+blargg_err_t Track_Filter::skip( int count )
+{
+ emu_error = NULL;
+ out_time += count;
+
+ // remove from silence and buf first
+ {
+ int 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;
+ silence_time = emu_time; // would otherwise be invalid
+ end_track_if_error( callbacks->skip_( count ) );
+ }
+
+ if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended
+ track_ended_ |= emu_track_ended_;
+
+ return emu_error;
+}
+
+blargg_err_t Track_Filter::skip_( int count )
+{
+ while ( count && !emu_track_ended_ )
+ {
+ int n = buf_size;
+ if ( n > count )
+ n = count;
+ count -= n;
+ RETURN_ERR( callbacks->play_( n, buf.begin() ) );
+ }
+ return blargg_ok;
+}
+
+// Fading
+
+void Track_Filter::set_fade( int start, int length )
+{
+ fade_start = start;
+ fade_step = length / (fade_block_size * fade_shift);
+ if ( fade_step < 1 )
+ fade_step = 1;
+}
+
+bool Track_Filter::is_fading() const
+{
+ return out_time >= fade_start && fade_start != indefinite_count;
+}
+
+// unit / pow( 2.0, (double) x / step )
+static int int_log( int x, int step, int unit )
+{
+ int shift = x / step;
+ int fraction = (x - shift * step) * unit / step;
+ return ((unit - fraction) + (fraction >> 1)) >> shift;
+}
+
+void Track_Filter::handle_fade( sample_t out [], int out_count )
+{
+ 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 Track_Filter::emu_play( sample_t out [], int count )
+{
+ emu_time += count;
+ if ( !emu_track_ended_ )
+ end_track_if_error( callbacks->play_( count, out ) );
+ else
+ memset( out, 0, count * sizeof *out );
+}
+
+// number of consecutive silent samples at end
+static int count_silence( Track_Filter::sample_t begin [], int size )
+{
+ Track_Filter::sample_t first = *begin;
+ *begin = silence_threshold * 2; // sentinel
+ Track_Filter::sample_t* p = begin + size;
+ while ( (unsigned) (*--p + silence_threshold) <= (unsigned) silence_threshold * 2 ) { }
+ *begin = first;
+ return size - (p - begin);
+}
+
+// fill internal buffer and check it for silence
+void Track_Filter::fill_buf()
+{
+ assert( !buf_remain );
+ if ( !emu_track_ended_ )
+ {
+ emu_play( buf.begin(), buf_size );
+ int 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 Track_Filter::play( int out_count, sample_t out [] )
+{
+ emu_error = NULL;
+ if ( track_ended_ )
+ {
+ memset( out, 0, out_count * sizeof *out );
+ }
+ else
+ {
+ 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 / 44100), "*" );
+
+ // use any remaining silence samples
+ int pos = 0;
+ if ( silence_count )
+ {
+ if ( !silence_ignored_ )
+ {
+ // during a run of silence, run emulator at >=2x speed so it gets ahead
+ int ahead_time = setup_.lookahead * (out_time + out_count - silence_time) +
+ silence_time;
+ while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) )
+ fill_buf();
+
+ // end track if sufficient silence has been found
+ if ( emu_time - silence_time > setup_.max_silence )
+ {
+ track_ended_ = emu_track_ended_ = true;
+ silence_count = out_count;
+ buf_remain = 0;
+ }
+ }
+
+ // fill from remaining silence
+ pos = min( silence_count, out_count );
+ memset( out, 0, pos * sizeof *out );
+ silence_count -= pos;
+ }
+
+ // use any remaining samples from buffer
+ if ( buf_remain )
+ {
+ int n = min( buf_remain, (int) (out_count - pos) );
+ memcpy( out + pos, buf.begin() + (buf_size - buf_remain), n * sizeof *out );
+ buf_remain -= n;
+ pos += n;
+ }
+
+ // generate remaining samples normally
+ int remain = out_count - pos;
+ if ( remain )
+ {
+ emu_play( out + pos, remain );
+ track_ended_ |= emu_track_ended_;
+
+ if ( silence_ignored_ && !is_fading() )
+ {
+ // if left unupdated, ahead_time could become too large
+ silence_time = emu_time;
+ }
+ else
+ {
+ // check end for a new run of silence
+ int 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 ( is_fading() )
+ handle_fade( out, out_count );
+ }
+ out_time += out_count;
+ return emu_error;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.h b/plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.h
new file mode 100644
index 00000000..2743543f
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Track_Filter.h
@@ -0,0 +1,105 @@
+// Removes silence from beginning of track, fades end of track. Also looks ahead
+// for excessive silence, and if found, ends track.
+
+// Game_Music_Emu 0.6-pre
+#ifndef TRACK_FILTER_H
+#define TRACK_FILTER_H
+
+#include "blargg_common.h"
+
+class Track_Filter {
+public:
+ typedef int sample_count_t;
+ typedef short sample_t;
+
+ enum { indefinite_count = INT_MAX/2 + 1 };
+
+ struct callbacks_t {
+ // Samples may be stereo or mono
+ virtual blargg_err_t play_( int count, sample_t* out ) BLARGG_PURE( { return blargg_ok; } )
+ virtual blargg_err_t skip_( int count ) BLARGG_PURE( { return blargg_ok; } )
+ virtual ~callbacks_t() { } // avoids silly "non-virtual dtor" warning
+ };
+
+ // Initializes filter. Must be done once before using object.
+ blargg_err_t init( callbacks_t* );
+
+ struct setup_t {
+ sample_count_t max_initial; // maximum silence to strip from beginning of track
+ sample_count_t max_silence; // maximum silence in middle of track without it ending
+ int lookahead; // internal speed when looking ahead for silence (2=200% etc.)
+ };
+
+ // Gets/sets setup
+ setup_t const& setup() const { return setup_; }
+ void setup( setup_t const& s ) { setup_ = s; }
+
+ // Disables automatic end-of-track detection and skipping of silence at beginning
+ void ignore_silence( bool disable = true ) { silence_ignored_ = disable; }
+
+ // Clears state and skips initial silence in track
+ blargg_err_t start_track();
+
+ // Sets time that fade starts, and how long until track ends.
+ void set_fade( sample_count_t start, sample_count_t length );
+
+ // Generates n samples into buf
+ blargg_err_t play( int n, sample_t buf [] );
+
+ // Skips n samples
+ blargg_err_t skip( int n );
+
+ // Number of samples played/skipped since start_track()
+ int sample_count() const { return out_time; }
+
+ // True if track ended. Causes are end of source samples, end of fade,
+ // or excessive silence.
+ bool track_ended() const { return track_ended_; }
+
+ // Clears state
+ void stop();
+
+// For use by callbacks
+
+ // Sets internal "track ended" flag and stops generation of further source samples
+ void set_track_ended() { emu_track_ended_ = true; }
+
+ // For use by skip_() callback
+ blargg_err_t skip_( int count );
+
+// Implementation
+public:
+ Track_Filter();
+ ~Track_Filter();
+
+private:
+ callbacks_t* callbacks;
+ setup_t setup_;
+ const char* emu_error;
+ bool silence_ignored_;
+
+ // Timing
+ int out_time; // number of samples played since start of track
+ int emu_time; // number of samples emulator has generated since start of track
+ int emu_track_ended_; // emulator has reached end of track
+ volatile int track_ended_;
+ void clear_time_vars();
+ void end_track_if_error( blargg_err_t );
+
+ // Fading
+ int fade_start;
+ int fade_step;
+ bool is_fading() const;
+ void handle_fade( sample_t out [], int count );
+
+ // Silence detection
+ int silence_time; // absolute number of samples where most recent silence began
+ int silence_count; // number of samples of silence to play before using buf
+ int buf_remain; // number of samples left in silence buffer
+ enum { buf_size = 2048 };
+ blargg_vector<sample_t> buf;
+ void fill_buf();
+ void emu_play( sample_t out [], int count );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Upsampler.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Upsampler.cpp
new file mode 100644
index 00000000..8272b978
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Upsampler.cpp
@@ -0,0 +1,73 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Upsampler.h"
+
+/* Copyright (C) 2004-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const shift = 15;
+int const unit = 1 << shift;
+
+void Upsampler::clear_()
+{
+ pos = 0;
+ Resampler::clear_();
+}
+
+Upsampler::Upsampler()
+{
+ clear();
+}
+
+blargg_err_t Upsampler::set_rate_( double new_factor )
+{
+ step = (int) (new_factor * unit + 0.5);
+ return Resampler::set_rate_( 1.0 / unit * step );
+}
+
+Resampler::sample_t const* Upsampler::resample_( sample_t** out_, sample_t const* out_end,
+ sample_t const in [], int in_size )
+{
+ in_size -= write_offset;
+ if ( in_size > 0 )
+ {
+ sample_t* BLARGG_RESTRICT out = *out_;
+ sample_t const* const in_end = in + in_size;
+
+ int const step = this->step;
+ int pos = this->pos;
+
+ do
+ {
+ #define INTERP( i, out )\
+ {\
+ int t = in [0 + i] * (unit - pos) + in [stereo + i] * pos;\
+ out = t >> shift;\
+ }
+
+ int out_0;
+ INTERP( 0, out_0 )
+ INTERP( 1, out [0] = out_0; out [1] )
+ out += stereo;
+
+ pos += step;
+ in += ((unsigned) pos >> shift) * stereo;
+ pos &= unit - 1;
+ }
+ while ( in < in_end && out < out_end );
+
+ this->pos = pos;
+ *out_ = out;
+ }
+ return in;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Upsampler.h b/plugins/gme/game-music-emu-0.6pre/gme/Upsampler.h
new file mode 100644
index 00000000..1bdec661
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Upsampler.h
@@ -0,0 +1,25 @@
+// Increases sampling rate using linear interpolation
+
+// Game_Music_Emu 0.6-pre
+#ifndef UPSAMPLER_H
+#define UPSAMPLER_H
+
+#include "Resampler.h"
+
+class Upsampler : public Resampler {
+public:
+ Upsampler();
+
+protected:
+ virtual blargg_err_t set_rate_( double );
+ virtual void clear_();
+ virtual sample_t const* resample_( sample_t**, sample_t const*, sample_t const [], int );
+
+protected:
+ enum { stereo = 2 };
+ enum { write_offset = 2 * stereo };
+ int pos;
+ int step;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.cpp
new file mode 100644
index 00000000..333cd2dc
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.cpp
@@ -0,0 +1,441 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Vgm_Core.h"
+
+#include "blargg_endian.h"
+#include <math.h>
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+int const stereo = 2;
+int const fm_time_bits = 12;
+int const blip_time_bits = 12;
+
+enum {
+ cmd_gg_stereo = 0x4F,
+ cmd_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,
+ ym2612_dac_pan_port = 0xB6
+};
+
+inline int command_len( int command )
+{
+ static byte const lens [0x10] = {
+ // 0 1 2 3 4 5 6 7 8 9 A B C D E F
+ 1,1,1,2,2,3,1,1,1,1,3,3,4,4,5,5
+ };
+ int len = lens [command >> 4];
+ check( len != 1 );
+ return len;
+}
+
+int Vgm_Core::run_ym2413( int time )
+{
+ return ym2413.run_until( time );
+}
+
+int Vgm_Core::run_ym2612( int time )
+{
+ return ym2612.run_until( time );
+}
+
+Vgm_Core::Vgm_Core() { blip_buf = stereo_buf.center(); }
+
+void Vgm_Core::set_tempo( double t )
+{
+ if ( file_begin() )
+ {
+ vgm_rate = (int) (44100 * t + 0.5);
+ blip_time_factor = (int) ((double)
+ (1 << blip_time_bits) / vgm_rate * stereo_buf.center()->clock_rate() + 0.5);
+ //dprintf( "blip_time_factor: %ld\n", blip_time_factor );
+ //dprintf( "vgm_rate: %ld\n", vgm_rate );
+ // TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only)
+ //blip_time_factor = (int) floor( double (1 << blip_time_bits) * psg_rate_ / 44100 / t + 0.5 );
+ //vgm_rate = (int) floor( double (1 << blip_time_bits) * psg_rate_ / blip_time_factor + 0.5 );
+
+ fm_time_factor = 2 + (int) (fm_rate * (1 << fm_time_bits) / vgm_rate + 0.5);
+ }
+}
+
+bool Vgm_Core::header_t::valid_tag() const
+{
+ return !memcmp( tag, "Vgm ", 4 );
+}
+
+blargg_err_t Vgm_Core::load_mem_( byte const data [], int size )
+{
+ assert( offsetof (header_t,unused2 [8]) == header_t::size );
+
+ if ( size <= header_t::size )
+ return blargg_err_file_type;
+
+ header_t const& h = *(header_t const*) data;
+
+ if ( !h.valid_tag() )
+ return blargg_err_file_type;
+
+ check( get_le32( h.version ) <= 0x150 );
+
+ // Get loop
+ loop_begin = file_end();
+ if ( get_le32( h.loop_offset ) )
+ loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
+
+ // PSG rate
+ int psg_rate = get_le32( h.psg_rate );
+ if ( !psg_rate )
+ psg_rate = 3579545;
+ stereo_buf.clock_rate( psg_rate );
+
+ // Disable FM
+ fm_rate = 0;
+ ym2612.enable( false );
+ ym2413.enable( false );
+
+ set_tempo( 1 );
+
+ return blargg_ok;
+}
+
+// Update pre-1.10 header FM rates by scanning commands
+void Vgm_Core::update_fm_rates( int* ym2413_rate, int* ym2612_rate ) const
+{
+ byte const* p = file_begin() + 0x40;
+ while ( p < file_end() )
+ {
+ switch ( *p )
+ {
+ case cmd_end:
+ return;
+
+ case cmd_psg:
+ case cmd_byte_delay:
+ p += 2;
+ break;
+
+ case cmd_delay:
+ p += 3;
+ break;
+
+ case cmd_data_block:
+ p += 7 + get_le32( p + 3 );
+ break;
+
+ case cmd_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 );
+ }
+ }
+}
+
+blargg_err_t Vgm_Core::init_fm( double* rate )
+{
+ int ym2612_rate = get_le32( header().ym2612_rate );
+ int ym2413_rate = get_le32( header().ym2413_rate );
+ if ( ym2413_rate && get_le32( header().version ) < 0x110 )
+ update_fm_rates( &ym2413_rate, &ym2612_rate );
+
+ if ( ym2612_rate )
+ {
+ if ( !*rate )
+ *rate = ym2612_rate / 144.0;
+ RETURN_ERR( ym2612.set_rate( *rate, ym2612_rate ) );
+ ym2612.enable();
+ }
+ else if ( ym2413_rate )
+ {
+ if ( !*rate )
+ *rate = ym2413_rate / 72.0;
+ int result = ym2413.set_rate( *rate, ym2413_rate );
+ if ( result == 2 )
+ return "YM2413 FM sound not supported";
+ CHECK_ALLOC( !result );
+ ym2413.enable();
+ }
+
+ fm_rate = *rate;
+
+ return blargg_ok;
+}
+
+void Vgm_Core::start_track()
+{
+ psg.reset( get_le16( header().noise_feedback ), header().noise_width );
+
+ blip_buf = stereo_buf.center();
+
+ dac_disabled = -1;
+ pos = file_begin() + header_t::size;
+ pcm_data = pos;
+ pcm_pos = pos;
+ dac_amp = -1;
+ vgm_time = 0;
+ if ( get_le32( header().version ) >= 0x150 )
+ {
+ int 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();
+
+ stereo_buf.clear();
+ }
+
+ fm_time_offset = 0;
+}
+
+inline Vgm_Core::fm_time_t Vgm_Core::to_fm_time( vgm_time_t t ) const
+{
+ return (t * fm_time_factor + fm_time_offset) >> fm_time_bits;
+}
+
+inline blip_time_t Vgm_Core::to_psg_time( vgm_time_t t ) const
+{
+ return (t * blip_time_factor) >> blip_time_bits;
+}
+
+void Vgm_Core::write_pcm( vgm_time_t vgm_time, int amp )
+{
+ if ( blip_buf )
+ {
+ check( amp >= 0 );
+ blip_time_t blip_time = to_psg_time( vgm_time );
+ int old = dac_amp;
+ int delta = amp - old;
+ dac_amp = amp;
+ blip_buf->set_modified();
+ if ( old >= 0 ) // first write is ignored, to avoid click
+ pcm.offset_inline( blip_time, delta, blip_buf );
+ else
+ dac_amp |= dac_disabled;
+ }
+}
+
+blip_time_t Vgm_Core::run( vgm_time_t end_time )
+{
+ vgm_time_t vgm_time = this->vgm_time;
+ vgm_time_t vgm_loop_time = ~0;
+ byte const* pos = this->pos;
+ if ( pos > file_end() )
+ set_warning( "Stream lacked end event" );
+
+ while ( vgm_time < end_time && pos < file_end() )
+ {
+ // TODO: be sure there are enough bytes left in stream for particular command
+ // so we don't read past end
+ switch ( *pos++ )
+ {
+ case cmd_end:
+ if ( vgm_loop_time == ~0 ) vgm_loop_time = vgm_time;
+ else if ( vgm_loop_time == vgm_time ) loop_begin = file_end(); // XXX some files may loop forever on a region without any delay commands
+ pos = loop_begin; // if not looped, loop_begin == file_end()
+ 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_psg_time( vgm_time ), *pos++ );
+ break;
+
+ case cmd_psg:
+ psg.write_data( to_psg_time( vgm_time ), *pos++ );
+ break;
+
+ case cmd_delay:
+ vgm_time += pos [1] * 0x100 + pos [0];
+ pos += 2;
+ break;
+
+ case cmd_byte_delay:
+ vgm_time += *pos++;
+ break;
+
+ case cmd_ym2413:
+ if ( run_ym2413( 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 ( run_ym2612( 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 ( run_ym2612( to_fm_time( vgm_time ) ) )
+ {
+ if ( pos [0] == ym2612_dac_pan_port )
+ {
+ Blip_Buffer * blip_buf = NULL;
+ switch ( pos [1] >> 6 )
+ {
+ case 0: blip_buf = NULL; break;
+ case 1: blip_buf = stereo_buf.right(); break;
+ case 2: blip_buf = stereo_buf.left(); break;
+ case 3: blip_buf = stereo_buf.center(); break;
+ }
+ /*if ( this->blip_buf != blip_buf )
+ {
+ blip_time_t blip_time = to_psg_time( vgm_time );
+ if ( this->blip_buf ) pcm.offset_inline( blip_time, -dac_amp, this->blip_buf );
+ if ( blip_buf ) pcm.offset_inline( blip_time, dac_amp, blip_buf );
+ }*/
+ this->blip_buf = blip_buf;
+ }
+ ym2612.write1( pos [0], pos [1] );
+ }
+ pos += 2;
+ break;
+
+ case cmd_data_block: {
+ check( *pos == cmd_end );
+ int type = pos [1];
+ int 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] * 0x1000000 + pos [2] * 0x10000 +
+ pos [1] * 0x100 + 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_psg_time( end_time );
+}
+
+blip_time_t Vgm_Core::run_psg( int msec )
+{
+ blip_time_t t = run( msec * vgm_rate / 1000 );
+ psg.end_frame( t );
+ return t;
+}
+
+int Vgm_Core::play_frame( blip_time_t blip_time, int sample_count, blip_sample_t out [] )
+{
+ // to do: timing is working mostly by luck
+ int min_pairs = (unsigned) sample_count / 2;
+ int vgm_time = (min_pairs << fm_time_bits) / fm_time_factor - 1;
+ assert( to_fm_time( vgm_time ) <= min_pairs );
+ int pairs;
+ while ( (pairs = to_fm_time( vgm_time )) < min_pairs )
+ vgm_time++;
+ //dprintf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs );
+
+ if ( ym2612.enabled() )
+ {
+ ym2612.begin_frame( out );
+ memset( out, 0, pairs * stereo * sizeof *out );
+ }
+ else if ( ym2413.enabled() )
+ {
+ ym2413.begin_frame( out );
+ }
+
+ run( vgm_time );
+ run_ym2612( pairs );
+ run_ym2413( pairs );
+
+ fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - (pairs << fm_time_bits);
+
+ psg.end_frame( blip_time );
+
+ return pairs * stereo;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.h b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.h
new file mode 100644
index 00000000..7f83610c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Core.h
@@ -0,0 +1,160 @@
+// Sega VGM music file emulator core
+
+// Game_Music_Emu 0.6-pre
+#ifndef VGM_CORE_H
+#define VGM_CORE_H
+
+#include "Gme_Loader.h"
+#include "Ym2612_Emu.h"
+#include "Ym2413_Emu.h"
+#include "Sms_Apu.h"
+#include "Multi_Buffer.h"
+
+ template<class Emu>
+ class Ym_Emu : public Emu {
+ int last_time;
+ short* out;
+ enum { disabled_time = -1 };
+ public:
+ Ym_Emu() { last_time = disabled_time; out = NULL; }
+ void enable( bool b = true ) { last_time = b ? 0 : disabled_time; }
+ bool enabled() const { return last_time != disabled_time; }
+ void begin_frame( short* buf ) { out = buf; last_time = 0; }
+
+ int run_until( int time )
+ {
+ int count = time - last_time;
+ if ( count > 0 )
+ {
+ if ( last_time < 0 )
+ return false;
+ last_time = time;
+ short* p = out;
+ out += count * Emu::out_chan_count;
+ Emu::run( count, p );
+ }
+ return true;
+ }
+ };
+
+class Vgm_Core : public Gme_Loader {
+public:
+
+ // VGM file header
+ struct header_t
+ {
+ enum { size = 0x40 };
+
+ 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];
+
+ // True if header has valid file signature
+ bool valid_tag() const;
+ };
+
+ // Header for currently loaded file
+ header_t const& header() const { return *(header_t const*) file_begin(); }
+
+ // Raw file data, for parsing GD3 tags
+ byte const* file_begin() const { return Gme_Loader::file_begin(); }
+ byte const* file_end () const { return Gme_Loader::file_end(); }
+
+ // If file uses FM, initializes FM sound emulator using *sample_rate. If
+ // *sample_rate is zero, sets *sample_rate to the proper accurate rate and
+ // uses that. The output of the FM sound emulator is resampled to the
+ // final sampling rate.
+ blargg_err_t init_fm( double* sample_rate );
+
+ // True if any FM chips are used by file. Always false until init_fm()
+ // is called.
+ bool uses_fm() const { return ym2612.enabled() || ym2413.enabled(); }
+
+ // Adjusts music tempo, where 1.0 is normal. Can be changed while playing.
+ // Loading a file resets tempo to 1.0.
+ void set_tempo( double );
+
+ // Starts track
+ void start_track();
+
+ // Runs PSG-only VGM for msec and returns number of clocks it ran for
+ blip_time_t run_psg( int msec );
+
+ // Plays FM for at most count samples into *out, and returns number of
+ // samples actually generated (always even). Also runs PSG for blip_time.
+ int play_frame( blip_time_t blip_time, int count, blip_sample_t out [] );
+
+ // True if all of file data has been played
+ bool track_ended() const { return pos >= file_end(); }
+
+ // PCM sound is always generated here
+ Stereo_Buffer stereo_buf;
+ Blip_Buffer * blip_buf;
+
+ // PSG sound chip, for assigning to Blip_Buffer, and setting volume and EQ
+ Sms_Apu psg;
+
+ // PCM synth, for setting volume and EQ
+ Blip_Synth_Fast pcm;
+
+ // FM sound chips
+ Ym_Emu<Ym2612_Emu> ym2612;
+ Ym_Emu<Ym2413_Emu> ym2413;
+
+// Implementation
+public:
+ Vgm_Core();
+
+protected:
+ virtual blargg_err_t load_mem_( byte const [], int );
+
+private:
+ // blip_time_t // PSG clocks
+ typedef int vgm_time_t; // 44100 per second, REGARDLESS of sample rate
+ typedef int fm_time_t; // FM sample count
+
+ int vgm_rate; // rate of log, 44100 normally, adjusted by tempo
+ double fm_rate; // FM samples per second
+
+ // VGM to FM time
+ int fm_time_factor;
+ int fm_time_offset;
+ fm_time_t to_fm_time( vgm_time_t ) const;
+
+ // VGM to PSG time
+ int blip_time_factor;
+ blip_time_t to_psg_time( vgm_time_t ) const;
+
+ // Current time and position in log
+ vgm_time_t vgm_time;
+ byte const* pos;
+ byte const* loop_begin;
+
+ // PCM
+ byte const* pcm_data; // location of PCM data in log
+ byte const* pcm_pos; // current position in PCM data
+ int dac_amp;
+ int dac_disabled; // -1 if disabled
+ void write_pcm( vgm_time_t, int amp );
+
+ blip_time_t run( vgm_time_t );
+ int run_ym2413( int time );
+ int run_ym2612( int time );
+ void update_fm_rates( int* ym2413_rate, int* ym2612_rate ) const;
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.cpp
new file mode 100644
index 00000000..ea8fb558
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.cpp
@@ -0,0 +1,360 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Vgm_Emu.h"
+
+#include "blargg_endian.h"
+
+/* Copyright (C) 2003-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// FM emulators are internally quieter to avoid 16-bit overflow
+double const fm_gain = 3.0;
+double const rolloff = 0.990;
+double const oversample_factor = 1.5;
+
+Vgm_Emu::Vgm_Emu()
+{
+ resampler.set_callback( play_frame_, this );
+ disable_oversampling_ = false;
+ set_type( gme_vgm_type );
+ set_max_initial_silence( 1 );
+ set_silence_lookahead( 1 ); // tracks should already be trimmed
+
+ static equalizer_t const eq = { -14.0, 80 };
+ set_equalizer( eq );
+}
+
+Vgm_Emu::~Vgm_Emu() { }
+
+void Vgm_Emu::unload()
+{
+ core.unload();
+ Classic_Emu::unload();
+}
+
+// Track info
+
+static byte const* skip_gd3_str( byte const in [], byte const* end )
+{
+ while ( end - in >= 2 )
+ {
+ in += 2;
+ if ( !(in [-2] | in [-1]) )
+ break;
+ }
+ return in;
+}
+
+static byte const* get_gd3_str( byte const* in, byte const* end, char field [] )
+{
+ byte const* mid = skip_gd3_str( in, end );
+ int len = (mid - in) / 2 - 1;
+ if ( len > 0 )
+ {
+ len = min( len, (int) Gme_File::max_field_ );
+ field [len] = 0;
+ for ( int i = 0; i < len; i++ )
+ field [i] = (in [i * 2 + 1] ? '?' : in [i * 2]); // TODO: convert to utf-8
+ }
+ return mid;
+}
+
+static byte const* get_gd3_pair( byte const* in, byte const* end, char field [] )
+{
+ return skip_gd3_str( get_gd3_str( in, end, field ), end );
+}
+
+static void parse_gd3( byte const in [], byte const* end, track_info_t* out )
+{
+ in = get_gd3_pair( in, end, out->song );
+ in = get_gd3_pair( in, end, out->game );
+ in = get_gd3_pair( in, end, out->system );
+ in = get_gd3_pair( in, end, out->author );
+ in = get_gd3_str ( in, end, out->copyright );
+ in = get_gd3_pair( in, end, out->dumper );
+ in = get_gd3_str ( in, end, out->comment );
+}
+
+int const gd3_header_size = 12;
+
+static int check_gd3_header( byte const h [], int remain )
+{
+ if ( remain < gd3_header_size ) return 0;
+ if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
+ if ( get_le32( h + 4 ) >= 0x200 ) return 0;
+
+ int gd3_size = get_le32( h + 8 );
+ if ( gd3_size > remain - gd3_header_size ) return 0;
+
+ return gd3_size;
+}
+
+static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
+{
+ int length = get_le32( h.track_duration ) * 10 / 441; // 1000 / 44100
+ if ( length > 0 )
+ {
+ int loop = get_le32( h.loop_duration );
+ if ( loop > 0 && get_le32( h.loop_offset ) )
+ {
+ out->loop_length = loop * 10 / 441;
+ out->intro_length = length - out->loop_length;
+ check( out->loop_length <= length );
+ // TODO: Also set out->length? We now have play_length for suggested play time.
+ }
+ else
+ {
+ out->length = length;
+ out->intro_length = length;
+ out->loop_length = 0;
+ }
+ }
+}
+
+blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const
+{
+ get_vgm_length( header(), out );
+
+ int gd3_offset = get_le32( header().gd3_offset ) - 0x2C;
+ if ( gd3_offset < 0 )
+ return blargg_ok;
+
+ byte const* gd3 = core.file_begin() + header_t::size + gd3_offset;
+ int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 );
+ if ( gd3_size )
+ {
+ byte const* gd3_data = gd3 + gd3_header_size;
+ parse_gd3( gd3_data, gd3_data + gd3_size, out );
+ }
+
+ return blargg_ok;
+}
+
+blargg_err_t Vgm_Emu::gd3_data( const unsigned char ** data, int * size )
+{
+ *data = 0;
+ *size = 0;
+
+ int gd3_offset = get_le32( header().gd3_offset ) - 0x2C;
+ if ( gd3_offset < 0 )
+ return blargg_ok;
+
+ byte const* gd3 = core.file_begin() + header_t::size + gd3_offset;
+ int gd3_size = check_gd3_header( gd3, core.file_end() - gd3 );
+ if ( gd3_size )
+ {
+ *data = gd3;
+ *size = gd3_size + gd3_header_size;
+ }
+
+ return blargg_ok;
+}
+
+struct Vgm_File : Gme_Info_
+{
+ Vgm_Emu::header_t h;
+ blargg_vector<byte> gd3;
+
+ Vgm_File() { set_type( gme_vgm_type ); }
+
+ blargg_err_t load_( Data_Reader& in )
+ {
+ int file_size = in.remain();
+ if ( file_size <= h.size )
+ return blargg_err_file_type;
+
+ RETURN_ERR( in.read( &h, h.size ) );
+ if ( !h.valid_tag() )
+ return blargg_err_file_type;
+
+ int gd3_offset = get_le32( h.gd3_offset ) - 0x2C;
+ int remain = file_size - h.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 ) );
+ int gd3_size = check_gd3_header( gd3_h, remain );
+ if ( gd3_size )
+ {
+ RETURN_ERR( gd3.resize( gd3_size ) );
+ RETURN_ERR( in.read( gd3.begin(), gd3.size() ) );
+ }
+ }
+ return blargg_ok;
+ }
+
+ blargg_err_t track_info_( track_info_t* out, int ) const
+ {
+ get_vgm_length( h, out );
+ if ( gd3.size() )
+ parse_gd3( gd3.begin(), gd3.end(), out );
+ return blargg_ok;
+ }
+};
+
+static Music_Emu* new_vgm_emu () { return BLARGG_NEW Vgm_Emu ; }
+static Music_Emu* new_vgm_file() { return BLARGG_NEW Vgm_File; }
+
+gme_type_t_ const gme_vgm_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGM", 1 }};
+
+gme_type_t_ const gme_vgz_type [1] = {{ "Sega SMS/Genesis", 1, &new_vgm_emu, &new_vgm_file, "VGZ", 1 }};
+
+// Setup
+
+void Vgm_Emu::set_tempo_( double t )
+{
+ core.set_tempo( t );
+}
+
+blargg_err_t Vgm_Emu::set_sample_rate_( int sample_rate )
+{
+ RETURN_ERR( core.stereo_buf.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 )
+{
+ core.psg.treble_eq( eq );
+ core.pcm.treble_eq( eq );
+}
+
+void Vgm_Emu::set_voice( int i, Blip_Buffer* c, Blip_Buffer* l, Blip_Buffer* r )
+{
+ if ( i < core.psg.osc_count )
+ core.psg.set_output( i, c, l, r );
+}
+
+void Vgm_Emu::mute_voices_( int mask )
+{
+ Classic_Emu::mute_voices_( mask );
+
+ // TODO: what was this for?
+ //core.pcm.output( &core.blip_buf );
+
+ // TODO: silence PCM if FM isn't used?
+ if ( core.uses_fm() )
+ {
+ core.psg.set_output( ( mask & 0x80 ) ? 0 : core.stereo_buf.center() );
+ if ( core.ym2612.enabled() )
+ {
+ core.pcm.volume( (mask & 0x40) ? 0.0 : 0.1115 / 256 * fm_gain * gain() );
+ core.ym2612.mute_voices( mask );
+ }
+
+ if ( core.ym2413.enabled() )
+ {
+ int m = mask & 0x3F;
+ if ( mask & 0x20 )
+ m |= 0x01E0; // channels 5-8
+ if ( mask & 0x40 )
+ m |= 0x3E00;
+ core.ym2413.mute_voices( m );
+ }
+ }
+}
+
+blargg_err_t Vgm_Emu::load_mem_( byte const data [], int size )
+{
+ RETURN_ERR( core.load_mem( data, size ) );
+
+ set_voice_count( core.psg.osc_count );
+
+ double fm_rate = 0.0;
+ if ( !disable_oversampling_ )
+ fm_rate = sample_rate() * oversample_factor;
+ RETURN_ERR( core.init_fm( &fm_rate ) );
+
+ if ( core.uses_fm() )
+ {
+ set_voice_count( 8 );
+ RETURN_ERR( resampler.setup( fm_rate / sample_rate(), rolloff, fm_gain * gain() ) );
+ RETURN_ERR( resampler.reset( core.stereo_buf.length() * sample_rate() / 1000 ) );
+ core.psg.volume( 0.135 * fm_gain * gain() );
+ }
+ else
+ {
+ core.psg.volume( gain() );
+ }
+
+ static const char* const fm_names [] = {
+ "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
+ };
+ static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
+ set_voice_names( core.uses_fm() ? fm_names : psg_names );
+
+ static int const types [8] = {
+ wave_type+1, wave_type+2, wave_type+3, noise_type+1,
+ 0, 0, 0, 0
+ };
+ set_voice_types( types );
+
+ return Classic_Emu::setup_buffer( core.stereo_buf.center()->clock_rate() );
+}
+
+// Emulation
+
+blargg_err_t Vgm_Emu::start_track_( int track )
+{
+ RETURN_ERR( Classic_Emu::start_track_( track ) );
+
+ core.start_track();
+
+ if ( core.uses_fm() )
+ resampler.clear();
+
+ return blargg_ok;
+}
+
+inline void Vgm_Emu::check_end()
+{
+ if ( core.track_ended() )
+ set_track_ended();
+}
+
+inline void Vgm_Emu::check_warning()
+{
+ const char* w = core.warning();
+ if ( w )
+ set_warning( w );
+}
+
+blargg_err_t Vgm_Emu::run_clocks( blip_time_t& time_io, int msec )
+{
+ check_end();
+ time_io = core.run_psg( msec );
+ check_warning();
+ return blargg_ok;
+}
+
+inline int Vgm_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] )
+{
+ check_end();
+ int result = core.play_frame( blip_time, sample_count, buf );
+ check_warning();
+ return result;
+}
+
+int Vgm_Emu::play_frame_( void* p, blip_time_t a, int b, sample_t c [] )
+{
+ return STATIC_CAST(Vgm_Emu*,p)->play_frame( a, b, c );
+}
+
+blargg_err_t Vgm_Emu::play_( int count, sample_t out [] )
+{
+ if ( !core.uses_fm() )
+ return Classic_Emu::play_( count, out );
+
+ resampler.dual_play( count, out, core.stereo_buf );
+ return blargg_ok;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.h
new file mode 100644
index 00000000..67163d20
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Vgm_Emu.h
@@ -0,0 +1,66 @@
+// Sega VGM music file emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef VGM_EMU_H
+#define VGM_EMU_H
+
+#include "Classic_Emu.h"
+#include "Dual_Resampler.h"
+#include "Vgm_Core.h"
+
+/* Emulates VGM music using SN76489/SN76496 PSG, and YM2612 and YM2413 FM sound chips.
+Supports custom sound buffer and frequency equalization when VGM uses just the PSG. FM
+sound chips can be run at their proper rates, or slightly higher to reduce aliasing on
+high notes. A YM2413 is supported but not provided separately from the library. */
+class Vgm_Emu : public Classic_Emu {
+public:
+
+ // True if custom buffer and custom equalization are supported
+ // TODO: move into Music_Emu and rename to something like supports_custom_buffer()
+ bool is_classic_emu() const { return !core.uses_fm(); }
+
+ // Disables running FM chips at higher than normal rate. Will result in slightly
+ // more aliasing of high notes.
+ void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; }
+
+ // VGM file header (see Vgm_Core.h)
+ typedef Vgm_Core::header_t header_t;
+
+ // Header for currently loaded file
+ header_t const& header() const { return core.header(); }
+
+ // Gd3 tag for currently loaded file
+ blargg_err_t gd3_data( const unsigned char ** data, int * size );
+
+ static gme_type_t static_type() { return gme_vgm_type; }
+
+// Implementation
+public:
+ Vgm_Emu();
+ ~Vgm_Emu();
+
+protected:
+ blargg_err_t track_info_( track_info_t*, int track ) const;
+ blargg_err_t load_mem_( byte const [], int );
+ blargg_err_t set_sample_rate_( int sample_rate );
+ blargg_err_t start_track_( int );
+ blargg_err_t play_( int count, sample_t []);
+ blargg_err_t run_clocks( blip_time_t&, int );
+ virtual void set_tempo_( double );
+ virtual void mute_voices_( int mask );
+ virtual void set_voice( int, Blip_Buffer*, Blip_Buffer*, Blip_Buffer* );
+ virtual void update_eq( blip_eq_t const& );
+ virtual void unload();
+
+private:
+ bool disable_oversampling_;
+ Dual_Resampler resampler;
+ Vgm_Core core;
+
+ void check_end();
+ void check_warning();
+ int play_frame( blip_time_t blip_time, int sample_count, sample_t buf [] );
+ static int play_frame_( void*, blip_time_t, int, sample_t [] );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.cpp
new file mode 100644
index 00000000..99fb407c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.cpp
@@ -0,0 +1,70 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+#include "Ym2413_Emu.h"
+#include "ym2413.h"
+
+Ym2413_Emu::Ym2413_Emu() { opll = 0; }
+
+Ym2413_Emu::~Ym2413_Emu()
+{
+ if ( opll ) ym2413_shutdown( opll );
+}
+
+int Ym2413_Emu::set_rate( double sample_rate, double clock_rate )
+{
+ if ( opll )
+ {
+ ym2413_shutdown( opll );
+ opll = 0;
+ }
+
+ opll = ym2413_init( clock_rate, sample_rate, 0 );
+ if ( !opll )
+ return 1;
+
+ reset();
+ return 0;
+}
+
+void Ym2413_Emu::reset()
+{
+ ym2413_reset_chip( opll );
+ ym2413_set_mask( opll, 0 );
+}
+
+void Ym2413_Emu::write( int addr, int data )
+{
+ ym2413_write( opll, 0, addr );
+ ym2413_write( opll, 1, data );
+}
+
+void Ym2413_Emu::mute_voices( int mask )
+{
+ ym2413_set_mask( opll, mask );
+}
+
+void Ym2413_Emu::run( int pair_count, sample_t* out )
+{
+ SAMP bufMO[ 1024 ];
+ SAMP bufRO[ 1024 ];
+ SAMP * buffers[2] = { bufMO, bufRO };
+
+ while (pair_count > 0)
+ {
+ int todo = pair_count;
+ if (todo > 1024) todo = 1024;
+ ym2413_update_one( opll, buffers, todo );
+
+ for (int i = 0; i < todo; i++)
+ {
+ int output = bufMO [i];
+ output += bufRO [i];
+ if ( (short)output != output ) output = 0x7FFF ^ ( output >> 31 );
+ out [0] = output;
+ out [1] = output;
+ out += 2;
+ }
+
+ pair_count -= todo;
+ }
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.h
new file mode 100644
index 00000000..5b82af5e
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ym2413_Emu.h
@@ -0,0 +1,37 @@
+// YM2413 FM sound chip emulator interface
+
+// Game_Music_Emu 0.6-pre
+#ifndef YM2413_EMU_H
+#define YM2413_EMU_H
+
+struct OPLL;
+
+class Ym2413_Emu {
+ void* opll;
+public:
+ Ym2413_Emu();
+ ~Ym2413_Emu();
+
+ static bool supported() { return true; }
+
+ // Sets output sample rate and chip clock rates, in Hz. Returns non-zero
+ // if error.
+ int set_rate( double sample_rate, double clock_rate );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 14 };
+ void mute_voices( int mask );
+
+ // Writes data to addr
+ void write( int addr, int data );
+
+ // Runs and writes pair_count*2 samples to output
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.cpp
new file mode 100644
index 00000000..a670d8cd
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.cpp
@@ -0,0 +1,2510 @@
+// Game_Music_Emu 0.5.5. http://www.slack.net/~ant/
+
+// File: fm.c -- software implementation of Yamaha FM sound generator
+// Copyright (C) 2001, 2002, 2003 Jarek Burczynski (bujar at mame dot net)
+// Copyright (C) 1998 Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+// Version 1.4 (final beta)
+
+#include "Ym2612_Emu.h"
+typedef Ym2612_Impl YM2612;
+
+#include "blargg_errors.h"
+
+// fm.h
+YM2612* YM2612Init( void* user_data, int index, long baseclock, long rate );
+void YM2612Shutdown( YM2612* );
+void YM2612ResetChip( YM2612* );
+void YM2612UpdateOne( YM2612*, short* out, int pair_count );
+int YM2612Write( YM2612*, unsigned int a, unsigned int v );
+unsigned char YM2612Read( YM2612*, int a );
+int YM2612TimerOver( YM2612*, int c );
+void YM2612Postload( YM2612* );
+void YM2612Mute( YM2612*, int mask );
+
+#include <stdlib.h>
+#include <limits.h>
+#include <math.h>
+
+/* Copyright (C) 1997-2005, Nicola Salmoria and the MAME team. All rights
+reserved. Redistribution and use of this code or any derivative works are
+permitted provided that the following conditions are met:
+- Redistributions may not be sold, nor may they be used in a commercial
+product or activity.
+- Redistributions that are modified from the original source must include the
+complete source code, including the source code for all components used by a
+binary built from the modified sources. However, as a special exception, the
+source code distributed need not include anything that is normally distributed
+(in either source or binary form) with the major components (compiler, kernel,
+and so on) of the operating system on which the executable runs, unless that
+component itself accompanies the executable.
+- Redistributions must reproduce the above copyright notice, this list of
+conditions and the following disclaimer in the documentation and/or other
+materials provided with the distribution.
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */
+
+#define BUILD_YM2612 1
+#define FM_BUSY_FLAG_SUPPORT 0
+#define YM2612UpdateReq( chip ) ((void) 0)
+
+typedef unsigned char UINT8;
+typedef unsigned short UINT16;
+
+#if ULONG_MAX == 0xFFFFFFFF
+ typedef long INT32;
+ typedef unsigned long UINT32;
+#elif UINT_MAX == 0xFFFFFFFF
+ typedef int INT32;
+ typedef unsigned int UINT32;
+#else
+ #error "No suitable 32-bit type available"
+#endif
+
+#define INLINE inline
+
+#define logerror
+#define state_save_register_UINT8(mod, ins, name, val, size)
+#define state_save_register_UINT16(mod, ins, name, val, size)
+#define state_save_register_UINT32(mod, ins, name, val, size)
+#define state_save_register_INT8(mod, ins, name, val, size)
+#define state_save_register_INT16(mod, ins, name, val, size)
+#define state_save_register_INT32(mod, ins, name, val, size)
+#define state_save_register_int(mod, ins, name, val)
+#define state_save_register_func_postload(a) a();
+
+#ifndef PI
+#define PI 3.14159265358979323846
+#endif
+
+/* shared function building option */
+#define BUILD_OPN (BUILD_YM2203||BUILD_YM2608||BUILD_YM2610||BUILD_YM2610B||BUILD_YM2612)
+#define BUILD_OPN_PRESCALER (BUILD_YM2203||BUILD_YM2608)
+
+/* globals */
+#define TYPE_SSG 0x01 /* SSG support */
+#define TYPE_LFOPAN 0x02 /* OPN type LFO and PAN */
+#define TYPE_6CH 0x04 /* FM 6CH / 3CH */
+#define TYPE_DAC 0x08 /* YM2612's DAC device */
+#define TYPE_ADPCM 0x10 /* two ADPCM units */
+#define TYPE_2610 0x20 /* bogus flag to differentiate 2608 from 2610 */
+
+#define TYPE_YM2203 (TYPE_SSG)
+#define TYPE_YM2608 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM)
+#define TYPE_YM2610 (TYPE_SSG |TYPE_LFOPAN |TYPE_6CH |TYPE_ADPCM |TYPE_2610)
+#define TYPE_YM2612 (TYPE_DAC |TYPE_LFOPAN |TYPE_6CH)
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (envelope generator timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX (ENV_LEN-1) /* 1023 */
+#define MIN_ATT_INDEX (0) /* 0 */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+/* TL_TAB_LEN is calculated as:
+* 13 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (13*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>3)
+
+/* sin waveform table in 'decibel' scale */
+static unsigned int sin_tab[SIN_LEN];
+
+/* sustain level table (3dB per step) */
+/* bit0, bit1, bit2, bit3, bit4, bit5, bit6 */
+/* 1, 2, 4, 8, 16, 32, 64 (value)*/
+/* 0.75, 1.5, 3, 6, 12, 24, 48 (dB)*/
+
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (UINT32) ( db * (4.0/ENV_STEP) )
+static const UINT32 sl_table[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const UINT8 eg_inc[19*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..11 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..11 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..11 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..11 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 12 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 12 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 12 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 12 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 13 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 13 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 13 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 13 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rate 14 0 (increment by 4) */
+/*13 */ 4,4, 4,8, 4,4, 4,8, /* rate 14 1 */
+/*14 */ 4,8, 4,8, 4,8, 4,8, /* rate 14 2 */
+/*15 */ 4,8, 8,8, 4,8, 8,8, /* rate 14 3 */
+
+/*16 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 8) */
+/*17 */ 16,16,16,16,16,16,16,16, /* rates 15 2, 15 3 for attack */
+/*18 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+static const UINT8 eg_rate_select[32+64+32]={ /* Envelope Generator rates (32 + 64 rates + 32 RKS) from tests on YM2612 */
+/* 32 infinite time rates */
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+O(18),O(18),O(18),O(18),O(18),O(18),O(18),O(18),
+
+/* rates 00-11 */
+O( 18),O( 18),O( 0),O( 0),
+O( 0),O( 0),O( 2),O( 2), // Nemesis's tests
+
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 12 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 13 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 14 */
+O(12),O(13),O(14),O(15),
+
+/* rate 15 */
+O(16),O(16),O(16),O(16),
+
+/* 32 dummy rates (same as 15 3) */
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16),
+O(16),O(16),O(16),O(16),O(16),O(16),O(16),O(16)
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15*/
+/*shift 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0, 0 */
+/*mask 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const UINT8 eg_rate_shift[32+64+32]={ /* Envelope Generator counter shifts (32 + 64 rates + 32 RKS) */
+/* 32 infinite time rates */
+/* O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0), */
+
+/* fixed (should be the same as rate 0, even if it makes no difference since increment value is 0 for these rates) */
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+O(11),O(11),O(11),O(11),O(11),O(11),O(11),O(11),
+
+/* rates 00-11 */
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 12 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 32 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0)
+
+};
+#undef O
+
+static const UINT8 dt_tab[4 * 32]={
+/* this is YM2151 and YM2612 phase increment data (in 10.10 fixed point format)*/
+/* 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
+};
+
+
+/* OPN key frequency number -> key code follow table */
+/* fnum higher 4bit -> keycode lower 2bit */
+static const UINT8 opn_fktable[16] = {0,0,0,0,0,0,0,1,2,3,3,3,3,3,3,3};
+
+
+/* 8 LFO speed parameters */
+/* each value represents number of samples that one LFO level will last for */
+static const UINT32 lfo_samples_per_step[8] = {108, 77, 71, 67, 62, 44, 8, 5};
+
+
+
+/*There are 4 different LFO AM depths available, they are:
+ 0 dB, 1.4 dB, 5.9 dB, 11.8 dB
+ Here is how it is generated (in EG steps):
+
+ 11.8 dB = 0, 2, 4, 6, 8, 10,12,14,16...126,126,124,122,120,118,....4,2,0
+ 5.9 dB = 0, 1, 2, 3, 4, 5, 6, 7, 8....63, 63, 62, 61, 60, 59,.....2,1,0
+ 1.4 dB = 0, 0, 0, 0, 1, 1, 1, 1, 2,...15, 15, 15, 15, 14, 14,.....0,0,0
+
+ (1.4 dB is loosing precision as you can see)
+
+ It's implemented as generator from 0..126 with step 2 then a shift
+ right N times, where N is:
+ 8 for 0 dB
+ 3 for 1.4 dB
+ 1 for 5.9 dB
+ 0 for 11.8 dB
+*/
+static const UINT8 lfo_ams_depth_shift[4] = {8, 3, 1, 0};
+
+
+
+/*There are 8 different LFO PM depths available, they are:
+ 0, 3.4, 6.7, 10, 14, 20, 40, 80 (cents)
+
+ Modulation level at each depth depends on F-NUMBER bits: 4,5,6,7,8,9,10
+ (bits 8,9,10 = FNUM MSB from OCT/FNUM register)
+
+ Here we store only first quarter (positive one) of full waveform.
+ Full table (lfo_pm_table) containing all 128 waveforms is build
+ at run (init) time.
+
+ One value in table below represents 4 (four) basic LFO steps
+ (1 PM step = 4 AM steps).
+
+ For example:
+ at LFO SPEED=0 (which is 108 samples per basic LFO step)
+ one value from "lfo_pm_output" table lasts for 432 consecutive
+ samples (4*108=432) and one full LFO waveform cycle lasts for 13824
+ samples (32*432=13824; 32 because we store only a quarter of whole
+ waveform in the table below)
+*/
+static const UINT8 lfo_pm_output[7*8][8]={ /* 7 bits meaningful (of F-NUMBER), 8 LFO output levels per one depth (out of 32), 8 LFO depths */
+/* FNUM BIT 4: 000 0001xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 7 */ {0, 0, 0, 0, 1, 1, 1, 1},
+
+/* FNUM BIT 5: 000 0010xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 5 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 6 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 7 */ {0, 0, 1, 1, 2, 2, 2, 3},
+
+/* FNUM BIT 6: 000 0100xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 3 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 4 */ {0, 0, 0, 0, 0, 0, 0, 1},
+/* DEPTH 5 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 6 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 7 */ {0, 0, 2, 3, 4, 4, 5, 6},
+
+/* FNUM BIT 7: 000 1000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 2 */ {0, 0, 0, 0, 0, 0, 1, 1},
+/* DEPTH 3 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 4 */ {0, 0, 0, 1, 1, 1, 1, 2},
+/* DEPTH 5 */ {0, 0, 1, 1, 2, 2, 2, 3},
+/* DEPTH 6 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 7 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+
+/* FNUM BIT 8: 001 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 1, 1, 1, 1},
+/* DEPTH 2 */ {0, 0, 0, 1, 1, 1, 2, 2},
+/* DEPTH 3 */ {0, 0, 1, 1, 2, 2, 3, 3},
+/* DEPTH 4 */ {0, 0, 1, 2, 2, 2, 3, 4},
+/* DEPTH 5 */ {0, 0, 2, 3, 4, 4, 5, 6},
+/* DEPTH 6 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 7 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+
+/* FNUM BIT 9: 010 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 2, 2, 2, 2},
+/* DEPTH 2 */ {0, 0, 0, 2, 2, 2, 4, 4},
+/* DEPTH 3 */ {0, 0, 2, 2, 4, 4, 6, 6},
+/* DEPTH 4 */ {0, 0, 2, 4, 4, 4, 6, 8},
+/* DEPTH 5 */ {0, 0, 4, 6, 8, 8, 0xa, 0xc},
+/* DEPTH 6 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 7 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+
+/* FNUM BIT10: 100 0000xxxx */
+/* DEPTH 0 */ {0, 0, 0, 0, 0, 0, 0, 0},
+/* DEPTH 1 */ {0, 0, 0, 0, 4, 4, 4, 4},
+/* DEPTH 2 */ {0, 0, 0, 4, 4, 4, 8, 8},
+/* DEPTH 3 */ {0, 0, 4, 4, 8, 8, 0xc, 0xc},
+/* DEPTH 4 */ {0, 0, 4, 8, 8, 8, 0xc,0x10},
+/* DEPTH 5 */ {0, 0, 8, 0xc,0x10,0x10,0x14,0x18},
+/* DEPTH 6 */ {0, 0,0x10,0x18,0x20,0x20,0x28,0x30},
+/* DEPTH 7 */ {0, 0,0x20,0x30,0x40,0x40,0x50,0x60},
+
+};
+
+/* all 128 LFO PM waveforms */
+static INT32 lfo_pm_table[128*8*32]; /* 128 combinations of 7 bits meaningful (of F-NUMBER), 8 LFO depths, 32 LFO output levels per one depth */
+
+
+
+
+
+/* register number to channel number , slot offset */
+#define OPN_CHAN(N) (N&3)
+#define OPN_SLOT(N) ((N>>2)&3)
+
+/* slot number */
+#define SLOT1 0
+#define SLOT2 2
+#define SLOT3 1
+#define SLOT4 3
+
+/* bit0 = Right enable , bit1 = Left enable */
+#define OUTD_RIGHT 1
+#define OUTD_LEFT 2
+#define OUTD_CENTER 3
+
+
+/* save output as raw 16-bit sample */
+/* #define SAVE_SAMPLE */
+
+#ifdef SAVE_SAMPLE
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+
+/* struct describing a single operator (SLOT) */
+typedef struct
+{
+ INT32 *DT; /* detune :dt_tab[DT] */
+ UINT8 KSR; /* key scale rate :3-KSR */
+ UINT32 ar; /* attack rate */
+ UINT32 d1r; /* decay rate */
+ UINT32 d2r; /* sustain rate */
+ UINT32 rr; /* release rate */
+ UINT8 ksr; /* key scale rate :kcode>>(3-KSR) */
+ UINT32 mul; /* multiple :ML_TABLE[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* phase counter */
+ INT32 Incr; /* phase step */
+
+ /* Envelope Generator */
+ UINT8 state; /* phase type */
+ UINT32 tl; /* total level: TL << 3 */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level:sl_table[SL] */
+ UINT32 vol_out; /* current output from EG circuit (without AM from LFO) */
+
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_d1r; /* (decay state) */
+ UINT8 eg_sel_d1r; /* (decay state) */
+ UINT8 eg_sh_d2r; /* (sustain state) */
+ UINT8 eg_sel_d2r; /* (sustain state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+
+ UINT8 ssg; /* SSG-EG waveform */
+ UINT8 ssgn; /* SSG-EG negated output */
+
+ UINT8 key; /* 0=last key was KEY OFF, 1=KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* AM enable flag */
+
+} FM_SLOT;
+
+typedef struct
+{
+ FM_SLOT SLOT[4]; /* four SLOTs (operators) */
+
+ UINT8 ALGO; /* algorithm */
+ UINT8 FB; /* feedback shift */
+ INT32 op1_out[2]; /* op1 output for feedback */
+
+ INT32 *connect1; /* SLOT1 output pointer */
+ INT32 *connect3; /* SLOT3 output pointer */
+ INT32 *connect2; /* SLOT2 output pointer */
+ INT32 *connect4; /* SLOT4 output pointer */
+
+ INT32 *mem_connect;/* where to put the delayed sample (MEM) */
+ INT32 mem_value; /* delayed sample (MEM) value */
+
+ INT32 pms; /* channel PMS */
+ UINT8 ams; /* channel AMS */
+
+ UINT32 fc; /* fnum,blk:adjusted to sample rate */
+ UINT8 kcode; /* key code: */
+ UINT32 block_fnum; /* current blk/fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+} FM_CH;
+
+
+typedef struct
+{
+ void * param; /* this chip parameter */
+ float clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+#if FM_BUSY_FLAG_SUPPORT
+ double BusyExpire; /* ExpireTime of Busy clear */
+#endif
+ UINT16 address; /* address register */
+ UINT8 irq; /* interrupt level */
+ UINT8 irqmask; /* irq mask */
+ UINT8 status; /* status flag */
+ UINT32 mode; /* mode CSM / 3SLOT */
+ UINT8 prescaler_sel;/* prescaler selector */
+ UINT8 fn_h; /* freq latch */
+ INT32 TimerBase; /* Timer base time */
+ INT32 TA; /* timer a value */
+ INT32 TAL; /* timer a base */
+ INT32 TAC; /* timer a counter */
+ INT32 TB; /* timer b value */
+ INT32 TBL; /* timer b base */
+ INT32 TBC; /* timer b counter */
+ /* Extention Timer and IRQ handler */
+ //const struct ssg_callbacks *SSG;
+ /* local time tables */
+ INT32 dt_tab[8][32];/* DeTune table */
+} FM_ST;
+
+
+
+/***********************************************************/
+/* OPN unit */
+/***********************************************************/
+
+/* OPN 3slot struct */
+typedef struct
+{
+ UINT32 fc[3]; /* fnum3,blk3: calculated */
+ UINT8 fn_h; /* freq3 latch */
+ UINT8 kcode[3]; /* key code */
+ UINT32 block_fnum[3]; /* current fnum value for this slot (can be different betweeen slots of one channel in 3slot mode) */
+ UINT8 key_csm;
+} FM_3SLOT;
+
+/* OPN/A/B common state */
+typedef struct
+{
+ FM_ST ST; /* general state */
+ FM_3SLOT SL3; /* 3 slot mode state */
+ FM_CH *P_CH; /* pointer of CH */
+ unsigned char pan_regs [6]; /* last pan register write (high two bits) */
+ unsigned char pan_mutes [6]; /* external channel *disable* mask to apply to pan registers */
+ unsigned int pan[6*2]; /* fm channels output masks (0xffffffff = enable) */
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/144/3 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow;/* envelope generator timer overlfows every 3 samples (on real chip) */
+
+ /* LFO */
+ UINT32 lfo_cnt;
+ UINT32 lfo_timer;
+ UINT32 lfo_timer_add;
+ UINT32 lfo_timer_overflow;
+ UINT32 LFO_AM; /* runtime LFO calculations helper */
+ INT32 LFO_PM; /* runtime LFO calculations helper */
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+
+ UINT32 fn_table[4096]; /* fnumber->increment counter */
+
+ UINT32 fn_max;
+
+ INT32 m2,c1,c2; /* Phase Modulation input for operators 2,3,4 */
+ INT32 mem; /* one sample delay memory */
+
+ INT32 out_fm[8]; /* outputs of working channels */
+} FM_OPN;
+
+
+/* limitter */
+#define Limit(val) { \
+ if ( (short)(val) != (val) ) val = 0x7FFF ^ ( ( val ) >> 31 ); \
+}
+
+
+/* status set and IRQ handling */
+INLINE void FM_STATUS_SET(FM_ST *ST,int flag)
+{
+ /* set status flag */
+ ST->status |= flag;
+ if ( !(ST->irq) && (ST->status & ST->irqmask) )
+ {
+ ST->irq = 1;
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void FM_STATUS_RESET(FM_ST *ST,int flag)
+{
+ /* reset status flag */
+ ST->status &=~flag;
+ if ( (ST->irq) && !(ST->status & ST->irqmask) )
+ {
+ ST->irq = 0;
+ }
+}
+
+/* IRQ mask set */
+INLINE void FM_IRQMASK_SET(FM_ST *ST,int flag)
+{
+ ST->irqmask = flag;
+ /* IRQ handling check */
+ FM_STATUS_SET(ST,0);
+ FM_STATUS_RESET(ST,0);
+}
+
+/* Timer A Overflow */
+INLINE void TimerAOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x04) FM_STATUS_SET(ST,0x01);
+ /* clear or reload the counter */
+ ST->TAC = (1024-ST->TA);
+}
+/* Timer B Overflow */
+INLINE void TimerBOver(FM_ST *ST)
+{
+ /* set status (if enabled) */
+ if(ST->mode & 0x08) FM_STATUS_SET(ST,0x02);
+ /* clear or reload the counter */
+ ST->TBC = ( 256-ST->TB)<<4;
+}
+
+
+#if FM_BUSY_FLAG_SUPPORT
+INLINE UINT8 FM_STATUS_FLAG(FM_ST *ST)
+{
+ if( ST->BusyExpire )
+ {
+ if( (ST->BusyExpire - FM_GET_TIME_NOW()) > 0)
+ return ST->status | 0x80; /* with busy */
+ /* expire */
+ ST->BusyExpire = 0;
+ }
+ return ST->status;
+}
+INLINE void FM_BUSY_SET(FM_ST *ST,int busyclock )
+{
+ ST->BusyExpire = FM_GET_TIME_NOW() + (ST->TimerBase * busyclock);
+}
+#define FM_BUSY_CLEAR(ST) ((ST)->BusyExpire = 0)
+#else
+#define FM_STATUS_FLAG(ST) ((ST)->status)
+#define FM_BUSY_SET(ST,bclock) {}
+#define FM_BUSY_CLEAR(ST) {}
+#endif
+
+
+
+
+INLINE void FM_KEYON(FM_OPN *OPN, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( !SLOT->key && !OPN->SL3.key_csm)
+ {
+ /* restart Phase Generator */
+ SLOT->phase = 0;
+
+ /* reset SSG-EG inversion flag */
+ SLOT->ssgn = 0;
+
+ if( (SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* force attenuation level to 0 */
+ SLOT->volume = MIN_ATT_INDEX;
+
+ /* directly switch to Decay (or Sustain) */
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ SLOT->key = 1;
+}
+
+INLINE void FM_KEYOFF(FM_OPN *OPN, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if( SLOT->key && !OPN->SL3.key_csm)
+ {
+ if (SLOT->state>EG_REL)
+ {
+ SLOT->state = EG_REL; /* phase -> Release */
+
+ if ( SLOT->ssg & 0x8 )
+ {
+ /* convert EG attenuation level */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->volume = (0x200 - SLOT->volume);
+
+ /* force EG attenuation level */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ }
+
+ SLOT->key = 0;
+}
+
+INLINE void FM_KEYON_CSM(FM_OPN *OPN, FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+
+ if( !SLOT->key && !OPN->SL3.key_csm)
+ {
+ /* restart Phase Generator */
+ SLOT->phase = 0;
+
+ /* reset SSG-EG inversion flag */
+ SLOT->ssgn = 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* force attenuation level to 0 */
+ SLOT->volume = MIN_ATT_INDEX;
+
+ /* directly switch to Decay (or Sustain) */
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+}
+
+INLINE void FM_KEYOFF_CSM(FM_CH *CH , int s )
+{
+ FM_SLOT *SLOT = &CH->SLOT[s];
+ if (!SLOT->key)
+ {
+ if (SLOT->state>EG_REL)
+ {
+ SLOT->state = EG_REL; /* phase -> Release */
+
+ /* SSG-EG specific update */
+ if (SLOT->ssg&0x08)
+ {
+ /* convert EG attenuation level */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->volume = (0x200 - SLOT->volume);
+
+ /* force EG attenuation level */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ }
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(FM_OPN *OPN, FM_CH *CH)
+{
+ /* all key ON (verified by Nemesis on real hardware) */
+ FM_KEYON_CSM(OPN,CH,SLOT1);
+ FM_KEYON_CSM(OPN,CH,SLOT2);
+ FM_KEYON_CSM(OPN,CH,SLOT3);
+ FM_KEYON_CSM(OPN,CH,SLOT4);
+ OPN->SL3.key_csm = 1;
+}
+
+INLINE void INTERNAL_TIMER_A(FM_OPN * OPN)
+{
+ if (OPN->ST.mode & 0x01)
+ {
+ if ((OPN->ST.TAC -= OPN->ST.TimerBase) <= 0)
+ {
+ /* set status (if enabled) */
+ if (OPN->ST.mode & 0x04) OPN->ST.status |= 0x01;
+
+ /* reload the counter */
+ if (OPN->ST.TAL) OPN->ST.TAC += OPN->ST.TAL;
+ else OPN->ST.TAC = OPN->ST.TAL;
+
+ /* CSM mode auto key on */
+ if ((OPN->ST.mode & 0xC0) == 0x80) CSMKeyControll(OPN, &OPN->P_CH[2]);
+ }
+ }
+}
+
+INLINE void INTERNAL_TIMER_B(FM_ST * ST, int step)
+{
+ if (ST->mode & 0x02)
+ {
+ if ((ST->TBC -= (ST->TimerBase * step)) <= 0)
+ {
+ /* set status (if enabled) */
+ if (ST->mode & 0x08) ST->status |= 0x02;
+
+ /* reload the counter */
+ if (ST->TBL) ST->TBC += ST->TBL;
+ else ST->TBC = ST->TBL;
+ }
+ }
+}
+
+/* OPN Mode Register Write */
+INLINE void set_timers( FM_OPN *OPN, int v )
+{
+ /* 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 ((OPN->ST.mode ^ v) & 0xC0)
+ {
+ /* phase increment need to be recalculated */
+ OPN->P_CH[2].SLOT[SLOT1].Incr=-1;
+
+ /* CSM mode disabled and CSM key ON active*/
+ if (((v & 0xC0) != 0x80) && OPN->SL3.key_csm)
+ {
+ /* CSM Mode Key OFF (verified by Nemesis on real hardware) */
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT1);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT2);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT3);
+ FM_KEYOFF_CSM(&OPN->P_CH[2],SLOT4);
+ OPN->SL3.key_csm = 0;
+ }
+ }
+
+ /* reload Timers */
+ if ((v&1) & !(OPN->ST.mode&1)) OPN->ST.TAC = OPN->ST.TAL;
+ if ((v&2) & !(OPN->ST.mode&2)) OPN->ST.TBC = OPN->ST.TBL;
+
+ /* reset Timers flags */
+ OPN->ST.status &= (~v >> 4);
+
+ OPN->ST.mode = v;
+}
+
+
+/* set algorithm connection */
+static void setup_connection( FM_OPN *OPN, FM_CH *CH, int ch )
+{
+ INT32 *carrier = &OPN->out_fm[ch];
+
+ INT32 **om1 = &CH->connect1;
+ INT32 **om2 = &CH->connect3;
+ INT32 **oc1 = &CH->connect2;
+
+ INT32 **memc = &CH->mem_connect;
+
+ switch( CH->ALGO ){
+ case 0:
+ /* M1---C1---MEM---M2---C2---OUT */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 1:
+ /* M1------+-MEM---M2---C2---OUT */
+ /* C1-+ */
+ *om1 = &OPN->mem;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 2:
+ /* M1-----------------+-C2---OUT */
+ /* C1---MEM---M2-+ */
+ *om1 = &OPN->c2;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->m2;
+ break;
+ case 3:
+ /* M1---C1---MEM------+-C2---OUT */
+ /* M2-+ */
+ *om1 = &OPN->c1;
+ *oc1 = &OPN->mem;
+ *om2 = &OPN->c2;
+ *memc= &OPN->c2;
+ break;
+ case 4:
+ /* M1---C1-+-OUT */
+ /* M2---C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = &OPN->c2;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 5:
+ /* +----C1----+ */
+ /* M1-+-MEM---M2-+-OUT */
+ /* +----C2----+ */
+ *om1 = 0; /* special mark */
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->m2;
+ break;
+ case 6:
+ /* M1---C1-+ */
+ /* M2-+-OUT */
+ /* C2-+ */
+ /* MEM: not used */
+ *om1 = &OPN->c1;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ case 7:
+ /* M1-+ */
+ /* C1-+-OUT */
+ /* M2-+ */
+ /* C2-+ */
+ /* MEM: not used*/
+ *om1 = carrier;
+ *oc1 = carrier;
+ *om2 = carrier;
+ *memc= &OPN->mem; /* store it anywhere where it will not be used */
+ break;
+ }
+
+ CH->connect4 = carrier;
+}
+
+/* set detune & multiple */
+INLINE void set_det_mul(FM_ST *ST,FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ SLOT->mul = (v&0x0f)? (v&0x0f)*2 : 1;
+ SLOT->DT = ST->dt_tab[(v>>4)&7];
+ CH->SLOT[SLOT1].Incr=-1;
+}
+
+/* set total level */
+INLINE void set_tl(FM_CH *CH,FM_SLOT *SLOT , int v)
+{
+ SLOT->tl = (v&0x7f)<<(ENV_BITS-7); /* 7bit TL */
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+}
+
+/* set attack rate & key scale */
+INLINE void set_ar_ksr(FM_CH *CH,FM_SLOT *SLOT,int v)
+{
+ UINT8 old_KSR = SLOT->KSR;
+
+ SLOT->ar = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->KSR = 3-(v>>6);
+ if (SLOT->KSR != old_KSR)
+ {
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+
+ /* Even if it seems unnecessary, in some odd case, KSR and KC are both modified */
+ /* and could result in SLOT->kc remaining unchanged. */
+ /* In such case, AR values would not be recalculated despite SLOT->ar has changed */
+ /* This fixes the introduction music of Batman & Robin (Eke-Eke) */
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 18*RATE_STEPS; /* verified by Nemesis on real hardware */
+ }
+}
+
+/* set decay rate */
+INLINE void set_dr(FM_SLOT *SLOT,int v)
+{
+ SLOT->d1r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr];
+}
+
+/* set sustain rate */
+INLINE void set_sr(FM_SLOT *SLOT,int v)
+{
+ SLOT->d2r = (v&0x1f) ? 32 + ((v&0x1f)<<1) : 0;
+
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr];
+}
+
+/* set release rate */
+INLINE void set_sl_rr(FM_SLOT *SLOT,int v)
+{
+ SLOT->sl = sl_table[ v>>4 ];
+
+ /* check EG state changes */
+ if ((SLOT->state == EG_DEC) && (SLOT->volume >= (INT32)(SLOT->sl)))
+ SLOT->state = EG_SUS;
+
+ SLOT->rr = 34 + ((v&0x0f)<<2);
+
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
+}
+
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPN *OPN)
+{
+ if (OPN->lfo_timer_overflow) /* LFO enabled ? */
+ {
+ /* increment LFO timer */
+ OPN->lfo_timer += OPN->lfo_timer_add;
+
+ /* when LFO is enabled, one level will last for 108, 77, 71, 67, 62, 44, 8 or 5 samples */
+ while (OPN->lfo_timer >= OPN->lfo_timer_overflow)
+ {
+ OPN->lfo_timer -= OPN->lfo_timer_overflow;
+
+ /* There are 128 LFO steps */
+ OPN->lfo_cnt = ( OPN->lfo_cnt + 1 ) & 127;
+
+ /* triangle */
+ /* AM: 0 to 126 step +2, 126 to 0 step -2 */
+ if (OPN->lfo_cnt<64)
+ OPN->LFO_AM = OPN->lfo_cnt * 2;
+ else
+ OPN->LFO_AM = 126 - ((OPN->lfo_cnt&63) * 2);
+
+ /* PM works with 4 times slower clock */
+ OPN->LFO_PM = OPN->lfo_cnt >> 2;
+ }
+ }
+}
+
+/* changed from INLINE to static here to work around gcc 4.2.1 codegen bug */
+static void advance_eg_channel(FM_OPN *OPN, FM_SLOT *SLOT)
+{
+ unsigned int i = 4; /* four operators per channel */
+
+ do
+ {
+ switch(SLOT->state)
+ {
+ case EG_ATT: /* attack phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_ar)-1)))
+ {
+ /* update attenuation level */
+ SLOT->volume += (~SLOT->volume * (eg_inc[SLOT->eg_sel_ar + ((OPN->eg_cnt>>SLOT->eg_sh_ar)&7)]))>>4;
+
+ /* check phase transition*/
+ if (SLOT->volume <= MIN_ATT_INDEX)
+ {
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC; /* special case where SL=0 */
+ }
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04))) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_d1r)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_d1r + ((OPN->eg_cnt>>SLOT->eg_sh_d1r)&7)];
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ /* check phase transition*/
+ if (SLOT->volume >= (INT32)(SLOT->sl))
+ SLOT->state = EG_SUS;
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_d2r)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ {
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04)) /* SSG-EG Output Inversion */
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_d2r + ((OPN->eg_cnt>>SLOT->eg_sh_d2r)&7)];
+
+ /* check phase transition*/
+ if ( SLOT->volume >= MAX_ATT_INDEX )
+ SLOT->volume = MAX_ATT_INDEX;
+ /* do not change SLOT->state (verified on real chip) */
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if (!(OPN->eg_cnt & ((1<<SLOT->eg_sh_rr)-1)))
+ {
+ /* SSG EG type */
+ if (SLOT->ssg&0x08)
+ {
+ /* update attenuation level */
+ if (SLOT->volume < 0x200)
+ SLOT->volume += 4 * eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+ /* check phase transition */
+ if (SLOT->volume >= 0x200)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+ else
+ {
+ /* update attenuation level */
+ SLOT->volume += eg_inc[SLOT->eg_sel_rr + ((OPN->eg_cnt>>SLOT->eg_sh_rr)&7)];
+
+ /* check phase transition*/
+ if (SLOT->volume >= MAX_ATT_INDEX)
+ {
+ SLOT->volume = MAX_ATT_INDEX;
+ SLOT->state = EG_OFF;
+ }
+ }
+
+ /* recalculate EG output */
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+
+ }
+ break;
+ }
+
+ SLOT++;
+ i--;
+ } while (i);
+}
+
+/* SSG-EG update process */
+/* The behavior is based upon Nemesis tests on real hardware */
+/* This is actually executed before each samples */
+static void update_ssg_eg_channel(FM_SLOT *SLOT)
+{
+ unsigned int i = 4; /* four operators per channel */
+
+ do
+ {
+ /* detect SSG-EG transition */
+ /* this is not required during release phase as the attenuation has been forced to MAX and output invert flag is not used */
+ /* if an Attack Phase is programmed, inversion can occur on each sample */
+ if ((SLOT->ssg & 0x08) && (SLOT->volume >= 0x200) && (SLOT->state > EG_REL))
+ {
+ if (SLOT->ssg & 0x01) /* bit 0 = hold SSG-EG */
+ {
+ /* set inversion flag */
+ if (SLOT->ssg & 0x02)
+ SLOT->ssgn = 4;
+
+ /* force attenuation level during decay phases */
+ if ((SLOT->state != EG_ATT) && !(SLOT->ssgn ^ (SLOT->ssg & 0x04)))
+ SLOT->volume = MAX_ATT_INDEX;
+ }
+ else /* loop SSG-EG */
+ {
+ /* toggle output inversion flag or reset Phase Generator */
+ if (SLOT->ssg & 0x02)
+ SLOT->ssgn ^= 4;
+ else
+ SLOT->phase = 0;
+
+ /* same as Key ON */
+ if (SLOT->state != EG_ATT)
+ {
+ if ((SLOT->ar + SLOT->ksr) < 94 /*32+62*/)
+ {
+ SLOT->state = (SLOT->volume <= MIN_ATT_INDEX) ? ((SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC) : EG_ATT;
+ }
+ else
+ {
+ /* Attack Rate is maximal: directly switch to Decay or Substain */
+ SLOT->volume = MIN_ATT_INDEX;
+ SLOT->state = (SLOT->sl == MIN_ATT_INDEX) ? EG_SUS : EG_DEC;
+ }
+ }
+ }
+
+ /* recalculate EG output */
+ if (SLOT->ssgn ^ (SLOT->ssg&0x04))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+ }
+
+ /* next slot */
+ SLOT++;
+ i--;
+ } while (i);
+}
+
+
+INLINE void update_phase_lfo_slot(FM_OPN *OPN, FM_SLOT *SLOT, INT32 pms, UINT32 block_fnum)
+{
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + pms + OPN->LFO_PM ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ UINT8 blk = (block_fnum&0x7000) >> 12;
+ UINT32 fn = block_fnum & 0xfff;
+
+ /* recalculate keyscale code */
+ int kc = (blk<<2) | opn_fktable[fn >> 8];
+
+ /* recalculate (frequency) phase increment counter */
+ int fc = (OPN->fn_table[fn]>>(7-blk)) + SLOT->DT[kc];
+
+ /* (frequency) phase overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* update phase */
+ SLOT->phase += (fc * SLOT->mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ SLOT->phase += SLOT->Incr;
+ }
+}
+
+INLINE void update_phase_lfo_channel(FM_OPN *OPN, FM_CH *CH)
+{
+ UINT32 block_fnum = CH->block_fnum;
+
+ UINT32 fnum_lfo = ((block_fnum & 0x7f0) >> 4) * 32 * 8;
+ INT32 lfo_fn_table_index_offset = lfo_pm_table[ fnum_lfo + CH->pms + OPN->LFO_PM ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum = block_fnum*2 + lfo_fn_table_index_offset;
+
+ UINT8 blk = (block_fnum&0x7000) >> 12;
+ UINT32 fn = block_fnum & 0xfff;
+
+ /* recalculate keyscale code */
+ int kc = (blk<<2) | opn_fktable[fn >> 8];
+
+ /* recalculate (frequency) phase increment counter */
+ int fc = (OPN->fn_table[fn]>>(7-blk));
+
+ /* (frequency) phase overflow (credits to Nemesis) */
+ int finc = fc + CH->SLOT[SLOT1].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT1].phase += (finc*CH->SLOT[SLOT1].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT2].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT2].phase += (finc*CH->SLOT[SLOT2].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT3].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT3].phase += (finc*CH->SLOT[SLOT3].mul) >> 1;
+
+ finc = fc + CH->SLOT[SLOT4].DT[kc];
+ if (finc < 0) finc += OPN->fn_max;
+ CH->SLOT[SLOT4].phase += (finc*CH->SLOT[SLOT4].mul) >> 1;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+/* update phase increment and envelope generator */
+INLINE void refresh_fc_eg_slot(FM_OPN *OPN, FM_SLOT *SLOT , int fc , int kc )
+{
+ int ksr = kc >> SLOT->KSR;
+
+ fc += SLOT->DT[kc];
+
+ /* detects frequency overflow (credits to Nemesis) */
+ if (fc < 0) fc += OPN->fn_max;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = (fc * SLOT->mul) >> 1;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 32+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ /* verified by Nemesis on real hardware (Attack phase is blocked) */
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 18*RATE_STEPS;
+ }
+
+ SLOT->eg_sh_d1r = eg_rate_shift [SLOT->d1r + SLOT->ksr];
+ SLOT->eg_sel_d1r= eg_rate_select[SLOT->d1r + SLOT->ksr];
+
+ SLOT->eg_sh_d2r = eg_rate_shift [SLOT->d2r + SLOT->ksr];
+ SLOT->eg_sel_d2r= eg_rate_select[SLOT->d2r + SLOT->ksr];
+
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr];
+ }
+}
+
+/* update phase increment counters */
+INLINE void refresh_fc_eg_chan(FM_OPN *OPN, FM_CH *CH )
+{
+ if( CH->SLOT[SLOT1].Incr==-1){
+ int fc = CH->fc;
+ int kc = CH->kcode;
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT1] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT2] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT3] , fc , kc );
+ refresh_fc_eg_slot(OPN, &CH->SLOT[SLOT4] , fc , kc );
+ }
+}
+
+#define volume_calc(OP) ((OP)->vol_out + (AM & (OP)->AMmask))
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + (pm<<15))) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm)
+{
+ UINT32 p;
+
+ p = (env<<3) + sin_tab[ ( ((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE void chan_calc(FM_OPN *OPN, FM_CH *CH)
+{
+ UINT32 AM = OPN->LFO_AM >> CH->ams;
+
+ OPN->m2 = OPN->c1 = OPN->c2 = OPN->mem = 0;
+
+ *CH->mem_connect = CH->mem_value; /* restore delayed sample (MEM) value to m2 or c2 */
+
+ unsigned int eg_out = volume_calc(&CH->SLOT[SLOT1]);
+ {
+ INT32 out = CH->op1_out[0] + CH->op1_out[1];
+ CH->op1_out[0] = CH->op1_out[1];
+
+ if( !CH->connect1 ){
+ /* algorithm 5 */
+ OPN->mem = OPN->c1 = OPN->c2 = CH->op1_out[0];
+ }else{
+ /* other algorithms */
+ *CH->connect1 += CH->op1_out[0];
+ }
+
+ CH->op1_out[1] = 0;
+ if( eg_out < ENV_QUIET ) /* SLOT 1 */
+ {
+ if (!CH->FB)
+ out=0;
+
+ CH->op1_out[1] = op_calc1(CH->SLOT[SLOT1].phase, eg_out, (out<<CH->FB) );
+ }
+ }
+
+ eg_out = volume_calc(&CH->SLOT[SLOT3]);
+ if( eg_out < ENV_QUIET ) /* SLOT 3 */
+ *CH->connect3 += op_calc(CH->SLOT[SLOT3].phase, eg_out, OPN->m2);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT2]);
+ if( eg_out < ENV_QUIET ) /* SLOT 2 */
+ *CH->connect2 += op_calc(CH->SLOT[SLOT2].phase, eg_out, OPN->c1);
+
+ eg_out = volume_calc(&CH->SLOT[SLOT4]);
+ if( eg_out < ENV_QUIET ) /* SLOT 4 */
+ *CH->connect4 += op_calc(CH->SLOT[SLOT4].phase, eg_out, OPN->c2);
+
+
+ /* store current MEM */
+ CH->mem_value = OPN->mem;
+
+ /* update phase counters AFTER output calculations */
+ if(CH->pms)
+ {
+ /* add support for 3 slot mode */
+ if ((OPN->ST.mode & 0xC0) && (CH == &OPN->P_CH[2]))
+ {
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT1], CH->pms, OPN->SL3.block_fnum[1]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT2], CH->pms, OPN->SL3.block_fnum[2]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT3], CH->pms, OPN->SL3.block_fnum[0]);
+ update_phase_lfo_slot(OPN, &CH->SLOT[SLOT4], CH->pms, CH->block_fnum);
+ }
+ else update_phase_lfo_channel(OPN, CH);
+ }
+ else /* no LFO phase modulation */
+ {
+ CH->SLOT[SLOT1].phase += CH->SLOT[SLOT1].Incr;
+ CH->SLOT[SLOT2].phase += CH->SLOT[SLOT2].Incr;
+ CH->SLOT[SLOT3].phase += CH->SLOT[SLOT3].Incr;
+ CH->SLOT[SLOT4].phase += CH->SLOT[SLOT4].Incr;
+ }
+}
+
+/* initialize time tables */
+static void init_timetables( FM_OPN *OPN )
+{
+ int i,d;
+ double rate;
+
+ /* DeTune table */
+ for (d = 0;d <= 3;d++)
+ {
+ for (i = 0;i <= 31;i++)
+ {
+ rate = ((double)dt_tab[d*32 + i]) * OPN->ST.freqbase * (1<<(FREQ_SH-10)); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ OPN->ST.dt_tab[d][i] = (INT32) rate;
+ OPN->ST.dt_tab[d+4][i] = -OPN->ST.dt_tab[d][i];
+ }
+ }
+
+ /* there are 2048 FNUMs that can be generated using FNUM/BLK registers
+ but LFO works with one more bit of a precision so we really need 4096 elements */
+ /* calculate fnumber -> increment counter table */
+ for(i = 0; i < 4096; i++)
+ {
+ /* freq table for octave 7 */
+ /* OPN phase increment counter = 20bit */
+ /* the correct formula is : F-Number = (144 * fnote * 2^20 / M) / 2^(B-1) */
+ /* where sample clock is M/144 */
+ /* this means the increment value for one clock sample is FNUM * 2^(B-1) = FNUM * 64 for octave 7 */
+ /* we also need to handle the ratio between the chip frequency and the emulated frequency (can be 1.0) */
+ OPN->fn_table[i] = (UINT32)( (double)i * 32 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+ }
+
+ /* maximal frequency is required for Phase overflow calculation, register size is 17 bits (Nemesis) */
+ OPN->fn_max = (UINT32)( (double)0x20000 * OPN->ST.freqbase * (1<<(FREQ_SH-10)) );
+}
+
+
+static void reset_channels( FM_CH *CH , int num )
+{
+ int c,s;
+
+ for( c = 0 ; c < num ; c++ )
+ {
+ CH[c].mem_value = 0;
+ CH[c].op1_out[0] = 0;
+ CH[c].op1_out[1] = 0;
+ for(s = 0 ; s < 4 ; s++ )
+ {
+ CH[c].SLOT[s].Incr = -1;
+ CH[c].SLOT[s].key = 0;
+ CH[c].SLOT[s].phase = 0;
+ CH[c].SLOT[s].ssgn = 0;
+ CH[c].SLOT[s].state = EG_OFF;
+ CH[c].SLOT[s].volume = MAX_ATT_INDEX;
+ CH[c].SLOT[s].vol_out = MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* initialize generic tables */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 2; /* 13 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<13; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x);
+ for (i=0; i<13; i++)
+ logerror(", [%02i] %4x", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ]);
+ logerror("\n");
+ }
+ #endif
+ }
+ /*logerror("FM.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+ /*logerror("FM.C: sin [%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[i],tl_tab[sin_tab[i]]);*/
+ }
+
+ /*logerror("FM.C: ENV_QUIET= %08x\n",ENV_QUIET );*/
+
+
+ /* build LFO PM modulation table */
+ for(i = 0; i < 8; i++) /* 8 PM depths */
+ {
+ UINT8 fnum;
+ for (fnum=0; fnum<128; fnum++) /* 7 bits meaningful of F-NUMBER */
+ {
+ UINT8 value;
+ UINT8 step;
+ UINT32 offset_depth = i;
+ UINT32 offset_fnum_bit;
+ UINT32 bit_tmp;
+
+ for (step=0; step<8; step++)
+ {
+ value = 0;
+ for (bit_tmp=0; bit_tmp<7; bit_tmp++) /* 7 bits */
+ {
+ if (fnum & (1<<bit_tmp)) /* only if bit "bit_tmp" is set */
+ {
+ offset_fnum_bit = bit_tmp * 8;
+ value += lfo_pm_output[offset_fnum_bit + offset_depth][step];
+ }
+ }
+ lfo_pm_table[(fnum*32*8) + (i*32) + step + 0] = value;
+ lfo_pm_table[(fnum*32*8) + (i*32) +(step^7)+ 8] = value;
+ lfo_pm_table[(fnum*32*8) + (i*32) + step +16] = -value;
+ lfo_pm_table[(fnum*32*8) + (i*32) +(step^7)+24] = -value;
+ }
+#if 0
+ logerror("LFO depth=%1x FNUM=%04x (<<4=%4x): ", i, fnum, fnum<<4);
+ for (step=0; step<16; step++) /* dump only positive part of waveforms */
+ logerror("%02x ", lfo_pm_table[(fnum*32*8) + (i*32) + step] );
+ logerror("\n");
+#endif
+
+ }
+ }
+
+
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+
+}
+
+
+
+static void FMCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+ return;
+}
+
+
+#ifdef _STATE_H
+/* FM channel save , internal state only */
+static void FMsave_state_channel(const char *name,int num,FM_CH *CH,int num_ch)
+{
+ int slot , ch;
+ char state_name[20];
+ const char slot_array[4] = { 1 , 3 , 2 , 4 };
+
+ for(ch=0;ch<num_ch;ch++,CH++)
+ {
+ /* channel */
+ sprintf(state_name,"%s.CH%d",name,ch);
+ state_save_register_INT32(state_name, num, "feedback" , CH->op1_out , 2);
+ state_save_register_UINT32(state_name, num, "phasestep" , &CH->fc , 1);
+ /* slots */
+ for(slot=0;slot<4;slot++)
+ {
+ FM_SLOT *SLOT = &CH->SLOT[slot];
+
+ sprintf(state_name,"%s.CH%d.SLOT%d",name,ch,slot_array[slot]);
+ state_save_register_UINT32(state_name, num, "phasecount" , &SLOT->phase, 1);
+ state_save_register_UINT8 (state_name, num, "state" , &SLOT->state, 1);
+ state_save_register_INT32 (state_name, num, "volume" , &SLOT->volume, 1);
+ }
+ }
+}
+
+static void FMsave_state_st(const char *state_name,int num,FM_ST *ST)
+{
+#if FM_BUSY_FLAG_SUPPORT
+ state_save_register_double(state_name, num, "BusyExpire", &ST->BusyExpire , 1);
+#endif
+ state_save_register_UINT8 (state_name, num, "address" , &ST->address , 1);
+ state_save_register_UINT8 (state_name, num, "IRQ" , &ST->irq , 1);
+ state_save_register_UINT8 (state_name, num, "IRQ MASK" , &ST->irqmask , 1);
+ state_save_register_UINT8 (state_name, num, "status" , &ST->status , 1);
+ state_save_register_UINT32(state_name, num, "mode" , &ST->mode , 1);
+ state_save_register_UINT8 (state_name, num, "prescaler" , &ST->prescaler_sel , 1);
+ state_save_register_UINT8 (state_name, num, "freq latch", &ST->fn_h , 1);
+ state_save_register_int (state_name, num, "TIMER A" , &ST->TA );
+ state_save_register_int (state_name, num, "TIMER Acnt", &ST->TAC );
+ state_save_register_UINT8 (state_name, num, "TIMER B" , &ST->TB , 1);
+ state_save_register_int (state_name, num, "TIMER Bcnt", &ST->TBC );
+}
+#endif /* _STATE_H */
+
+#if BUILD_OPN
+
+
+
+/* prescaler set (and make time tables) */
+static void OPNSetPres(FM_OPN *OPN , int pres , int TimerPres, int SSGpres)
+{
+ /* frequency base */
+ OPN->ST.freqbase = (OPN->ST.rate) ? ((double)OPN->ST.clock / OPN->ST.rate) / pres : 0;
+ if ( fabs( OPN->ST.freqbase - 1.0 ) < 0.0001 )
+ OPN->ST.freqbase = 1.0;
+
+ OPN->eg_timer_add = (UINT32)((1<<EG_SH) * OPN->ST.freqbase);
+ OPN->eg_timer_overflow = ( 3 ) * (1<<EG_SH);
+
+ /* LFO timer increment (every samples) */
+ OPN->lfo_timer_add = (UINT32)((1<<LFO_SH) * OPN->ST.freqbase);
+
+ /* Timer base time (every samples) */
+ OPN->ST.TimerBase = (int) ((1 << TIMER_SH) * OPN->ST.freqbase);
+
+ /* make time tables */
+ init_timetables( OPN );
+}
+
+
+
+/* write a OPN mode register 0x20-0x2f */
+static void OPNWriteMode(FM_OPN *OPN, int r, int v)
+{
+ UINT8 c;
+ FM_CH *CH;
+
+ switch(r){
+ case 0x21: /* Test */
+ break;
+ case 0x22: /* LFO FREQ (YM2608/YM2610/YM2610B/YM2612) */
+ if (v&8) /* LFO enabled ? */
+ {
+ if (!OPN->lfo_timer_overflow)
+ {
+ /* restart LFO */
+ OPN->lfo_cnt = 0;
+ OPN->lfo_timer = 0;
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+ }
+
+ OPN->lfo_timer_overflow = lfo_samples_per_step[v&7] << LFO_SH;
+ }
+ else
+ {
+ OPN->lfo_timer_overflow = 0;
+ }
+ break;
+ case 0x24: /* timer A High 8*/
+ OPN->ST.TA = (OPN->ST.TA & 0x03)|(((int)v)<<2);
+ OPN->ST.TAL = (1024 - OPN->ST.TA) << TIMER_SH;
+ break;
+ case 0x25: /* timer A Low 2*/
+ OPN->ST.TA = (OPN->ST.TA & 0x3fc)|(v&3);
+ OPN->ST.TAL = (1024 - OPN->ST.TA) << TIMER_SH;
+ break;
+ case 0x26: /* timer B */
+ OPN->ST.TB = v;
+ OPN->ST.TBL = (256 - OPN->ST.TB) << (TIMER_SH + 4);
+ break;
+ case 0x27: /* mode, timer control */
+ set_timers( OPN, v );
+ break;
+ case 0x28: /* key on / off */
+ c = v & 0x03;
+ if( c == 3 ) break;
+ if( (v&0x04) ) c+=3;
+ CH = OPN->P_CH;
+ CH = &CH[c];
+ if(v&0x10) FM_KEYON(OPN,CH,SLOT1); else FM_KEYOFF(OPN,CH,SLOT1);
+ if(v&0x20) FM_KEYON(OPN,CH,SLOT2); else FM_KEYOFF(OPN,CH,SLOT2);
+ if(v&0x40) FM_KEYON(OPN,CH,SLOT3); else FM_KEYOFF(OPN,CH,SLOT3);
+ if(v&0x80) FM_KEYON(OPN,CH,SLOT4); else FM_KEYOFF(OPN,CH,SLOT4);
+ break;
+ }
+}
+
+INLINE void OPNUpdatePan( FM_OPN *OPN, int c )
+{
+ int v = OPN->pan_regs [c] & ~OPN->pan_mutes [c];
+ OPN->pan[ c*2 ] = (v & 0x80) ? ~0 : 0;
+ OPN->pan[ c*2+1 ] = (v & 0x40) ? ~0 : 0;
+}
+
+/* write a OPN register (0x30-0xff) */
+static void OPNWriteReg(FM_OPN *OPN, int r, int v)
+{
+ FM_CH *CH;
+ FM_SLOT *SLOT;
+
+ UINT8 c = OPN_CHAN(r);
+
+ if (c == 3) return; /* 0xX3,0xX7,0xXB,0xXF */
+
+ if (r >= 0x100) c+=3;
+
+ CH = OPN->P_CH;
+ CH = &CH[c];
+
+ SLOT = &(CH->SLOT[OPN_SLOT(r)]);
+
+ switch( r & 0xf0 ) {
+ case 0x30: /* DET , MUL */
+ set_det_mul(&OPN->ST,CH,SLOT,v);
+ break;
+
+ case 0x40: /* TL */
+ set_tl(CH,SLOT,v);
+ break;
+
+ case 0x50: /* KS, AR */
+ set_ar_ksr(CH,SLOT,v);
+ break;
+
+ case 0x60: /* bit7 = AM ENABLE, DR */
+ set_dr(SLOT,v);
+
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ break;
+
+ case 0x70: /* SR */
+ set_sr(SLOT,v);
+ break;
+
+ case 0x80: /* SL, RR */
+ set_sl_rr(SLOT,v);
+ break;
+
+ case 0x90: /* SSG-EG */
+ SLOT->ssg = v&0x0f;
+
+ /* recalculate EG output */
+ if ((SLOT->ssg&0x08) && (SLOT->ssgn ^ (SLOT->ssg&0x04)) && (SLOT->state > EG_REL))
+ SLOT->vol_out = ((UINT32)(0x200 - SLOT->volume) & MAX_ATT_INDEX) + SLOT->tl;
+ else
+ SLOT->vol_out = (UINT32)SLOT->volume + SLOT->tl;
+
+ /* SSG-EG envelope shapes :
+
+ E AtAlH
+ 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
+
+
+ The shapes are generated using Attack, Decay and Sustain phases.
+
+ Each single character in the diagrams above represents this whole
+ sequence:
+
+ - when KEY-ON = 1, normal Attack phase is generated (*without* any
+ difference when compared to normal mode),
+
+ - later, when envelope level reaches minimum level (max volume),
+ the EG switches to Decay phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - later when envelope level passes the SL level,
+ the EG swithes to Sustain phase (which works with bigger steps
+ when compared to normal mode - see below),
+
+ - finally when envelope level reaches maximum level (min volume),
+ the EG switches to Attack phase again (depends on actual waveform).
+
+ Important is that when switch to Attack phase occurs, the phase counter
+ of that operator will be zeroed-out (as in normal KEY-ON) but not always.
+ (I havent found the rule for that - perhaps only when the output level is low)
+
+ The difference (when compared to normal Envelope Generator mode) is
+ that the resolution in Decay and Sustain phases is 4 times lower;
+ this results in only 256 steps instead of normal 1024.
+ In other words:
+ when SSG-EG is disabled, the step inside of the EG is one,
+ when SSG-EG is enabled, the step is four (in Decay and Sustain phases).
+
+ Times between the level changes are the same in both modes.
+
+
+ Important:
+ Decay 1 Level (so called SL) is compared to actual SSG-EG output, so
+ it is the same in both SSG and no-SSG modes, with this exception:
+
+ when the SSG-EG is enabled and is generating raising levels
+ (when the EG output is inverted) the SL will be found at wrong level !!!
+ For example, when SL=02:
+ 0 -6 = -6dB in non-inverted EG output
+ 96-6 = -90dB in inverted EG output
+ Which means that EG compares its level to SL as usual, and that the
+ output is simply inverted afterall.
+
+
+ The Yamaha's manuals say that AR should be set to 0x1f (max speed).
+ That is not necessary, but then EG will be generating Attack phase.
+
+ */
+
+
+ break;
+
+ case 0xa0:
+ switch( OPN_SLOT(r) ){
+ case 0: /* 0xa0-0xa2 : FNUM1 */
+ {
+ UINT32 fn = (((UINT32)( (OPN->ST.fn_h)&7))<<8) + v;
+ UINT8 blk = OPN->ST.fn_h>>3;
+ /* keyscale code */
+ CH->kcode = (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ CH->fc = OPN->fn_table[fn*2]>>(7-blk);
+
+ /* store fnum in clear form for LFO PM calculations */
+ CH->block_fnum = (blk<<11) | fn;
+
+ CH->SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 1: /* 0xa4-0xa6 : FNUM2,BLK */
+ OPN->ST.fn_h = v&0x3f;
+ break;
+ case 2: /* 0xa8-0xaa : 3CH FNUM1 */
+ if(r < 0x100)
+ {
+ UINT32 fn = (((UINT32)(OPN->SL3.fn_h&7))<<8) + v;
+ UINT8 blk = OPN->SL3.fn_h>>3;
+ /* keyscale code */
+ OPN->SL3.kcode[c]= (blk<<2) | opn_fktable[fn >> 7];
+ /* phase increment counter */
+ OPN->SL3.fc[c] = OPN->fn_table[fn*2]>>(7-blk);
+ OPN->SL3.block_fnum[c] = (blk<<11) | fn;
+ (OPN->P_CH)[2].SLOT[SLOT1].Incr=-1;
+ }
+ break;
+ case 3: /* 0xac-0xae : 3CH FNUM2,BLK */
+ if(r < 0x100)
+ OPN->SL3.fn_h = v&0x3f;
+ break;
+ }
+ break;
+
+ case 0xb0:
+ switch( OPN_SLOT(r) ){
+ case 0: /* 0xb0-0xb2 : FB,ALGO */
+ {
+ int feedback = (v>>3)&7;
+ CH->ALGO = v&7;
+ CH->FB = feedback ? feedback+6 : 0;
+ setup_connection( OPN, CH, c );
+ }
+ break;
+ case 1: /* 0xb4-0xb6 : L , R , AMS , PMS (YM2612/YM2610B/YM2610/YM2608) */
+ {
+ /* b0-2 PMS */
+ CH->pms = (v & 7) * 32; /* CH->pms = PM depth * 32 (index in lfo_pm_table) */
+
+ /* b4-5 AMS */
+ CH->ams = lfo_ams_depth_shift[(v>>4) & 0x03];
+
+ /* PAN : b7 = L, b6 = R */
+ OPN->pan_regs [c] = v & 0xc0;
+ OPNUpdatePan( OPN, c );
+
+ }
+ break;
+ }
+ break;
+ }
+}
+
+#endif /* BUILD_OPN */
+
+#if BUILD_YM2612
+/*******************************************************************************/
+/* YM2612 local section */
+/*******************************************************************************/
+/* here's the virtual YM2612 */
+struct Ym2612_Impl
+{
+#ifdef _STATE_H
+ UINT8 REGS[512]; /* registers */
+#endif
+ FM_OPN OPN; /* OPN state */
+ FM_CH CH[6]; /* channel state */
+
+ /* dac output (YM2612) */
+ int dacen;
+ INT32 dacout;
+};
+
+//static int dacen;
+
+/* Generate samples for one of the YM2612s */
+void YM2612UpdateOne(YM2612 *F2612, short *buffer, int length)
+{
+ FM_OPN *OPN = &F2612->OPN;
+ int i;
+ INT32 dacout = F2612->dacout;
+ FM_CH *cch[6];
+ int dacen;
+
+ cch[0] = &F2612->CH[0];
+ cch[1] = &F2612->CH[1];
+ cch[2] = &F2612->CH[2];
+ cch[3] = &F2612->CH[3];
+ cch[4] = &F2612->CH[4];
+ cch[5] = &F2612->CH[5];
+ /* DAC mode */
+ dacen = F2612->dacen;
+
+ /* refresh PG and EG */
+ refresh_fc_eg_chan( OPN, cch[0] );
+ refresh_fc_eg_chan( OPN, cch[1] );
+ if( (OPN->ST.mode & 0xc0) )
+ {
+ /* 3SLOT MODE */
+ if( cch[2]->SLOT[SLOT1].Incr==-1)
+ {
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT1] , OPN->SL3.fc[1] , OPN->SL3.kcode[1] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT2] , OPN->SL3.fc[2] , OPN->SL3.kcode[2] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT3] , OPN->SL3.fc[0] , OPN->SL3.kcode[0] );
+ refresh_fc_eg_slot(OPN, &cch[2]->SLOT[SLOT4] , cch[2]->fc , cch[2]->kcode );
+ }
+ }else refresh_fc_eg_chan( OPN, cch[2] );
+ refresh_fc_eg_chan( OPN, cch[3] );
+ refresh_fc_eg_chan( OPN, cch[4] );
+ refresh_fc_eg_chan( OPN, cch[5] );
+
+ /* buffering */
+ for(i=0; i < length ; i++)
+ {
+ /* clear outputs */
+ OPN->out_fm[0] = 0;
+ OPN->out_fm[1] = 0;
+ OPN->out_fm[2] = 0;
+ OPN->out_fm[3] = 0;
+ OPN->out_fm[4] = 0;
+ OPN->out_fm[5] = 0;
+
+ /* update SSG-EG output */
+ update_ssg_eg_channel(&cch[0]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[1]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[2]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[3]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[4]->SLOT[SLOT1]);
+ update_ssg_eg_channel(&cch[5]->SLOT[SLOT1]);
+
+ /* calculate FM */
+ chan_calc(OPN, cch[0] );
+ chan_calc(OPN, cch[1] );
+ chan_calc(OPN, cch[2] );
+ chan_calc(OPN, cch[3] );
+ chan_calc(OPN, cch[4] );
+ if( dacen )
+ /* *cch[5]->connect4 += dacout */;
+ else
+ chan_calc(OPN, cch[5] );
+
+ /* advance LFO */
+ advance_lfo(OPN);
+
+ /* advance envelope generator */
+ OPN->eg_timer += OPN->eg_timer_add;
+ while (OPN->eg_timer >= OPN->eg_timer_overflow)
+ {
+ OPN->eg_timer -= OPN->eg_timer_overflow;
+ OPN->eg_cnt++;
+
+ advance_eg_channel(OPN, &cch[0]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[1]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[2]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[3]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[4]->SLOT[SLOT1]);
+ advance_eg_channel(OPN, &cch[5]->SLOT[SLOT1]);
+ }
+
+ {
+ int lt,rt;
+
+ if (OPN->out_fm[0] > 8191) OPN->out_fm[0] = 8191;
+ else if (OPN->out_fm[0] < -8192) OPN->out_fm[0] = -8192;
+ if (OPN->out_fm[1] > 8191) OPN->out_fm[1] = 8191;
+ else if (OPN->out_fm[1] < -8192) OPN->out_fm[1] = -8192;
+ if (OPN->out_fm[2] > 8191) OPN->out_fm[2] = 8191;
+ else if (OPN->out_fm[2] < -8192) OPN->out_fm[2] = -8192;
+ if (OPN->out_fm[3] > 8191) OPN->out_fm[3] = 8191;
+ else if (OPN->out_fm[3] < -8192) OPN->out_fm[3] = -8192;
+ if (OPN->out_fm[4] > 8191) OPN->out_fm[4] = 8191;
+ else if (OPN->out_fm[4] < -8192) OPN->out_fm[4] = -8192;
+ if (OPN->out_fm[5] > 8191) OPN->out_fm[5] = 8191;
+ else if (OPN->out_fm[5] < -8192) OPN->out_fm[5] = -8192;
+
+ lt = ((OPN->out_fm[0]>>0) & OPN->pan[0]);
+ rt = ((OPN->out_fm[0]>>0) & OPN->pan[1]);
+ lt += ((OPN->out_fm[1]>>0) & OPN->pan[2]);
+ rt += ((OPN->out_fm[1]>>0) & OPN->pan[3]);
+ lt += ((OPN->out_fm[2]>>0) & OPN->pan[4]);
+ rt += ((OPN->out_fm[2]>>0) & OPN->pan[5]);
+ lt += ((OPN->out_fm[3]>>0) & OPN->pan[6]);
+ rt += ((OPN->out_fm[3]>>0) & OPN->pan[7]);
+ lt += ((OPN->out_fm[4]>>0) & OPN->pan[8]);
+ rt += ((OPN->out_fm[4]>>0) & OPN->pan[9]);
+ lt += ((OPN->out_fm[5]>>0) & OPN->pan[10]);
+ rt += ((OPN->out_fm[5]>>0) & OPN->pan[11]);
+
+ lt >>= 1;
+ rt >>= 1;
+
+ Limit( lt );
+ Limit( rt );
+
+ #ifdef SAVE_SAMPLE
+ SAVE_ALL_CHANNELS
+ #endif
+
+ /* buffering */
+ buffer[i*2] = lt;
+ buffer[i*2+1] = rt;
+ }
+
+ /* CSM mode: if CSM Key ON has occured, CSM Key OFF need to be sent */
+ /* only if Timer A does not overflow again (i.e CSM Key ON not set again) */
+ OPN->SL3.key_csm <<= 1;
+
+ /* timer A control */
+ INTERNAL_TIMER_A( OPN );
+
+ /* CSM Mode Key ON still disabled */
+ /* CSM Mode Key OFF (verified by Nemesis on real hardware) */
+ FM_KEYOFF_CSM(cch[2],SLOT1);
+ FM_KEYOFF_CSM(cch[2],SLOT2);
+ FM_KEYOFF_CSM(cch[2],SLOT3);
+ FM_KEYOFF_CSM(cch[2],SLOT4);
+ OPN->SL3.key_csm = 0;
+ }
+ INTERNAL_TIMER_B(&OPN->ST,length);
+
+}
+
+#ifdef _STATE_H
+void YM2612Postload(void *chip)
+{
+ if (chip)
+ {
+ YM2612 *F2612 = (YM2612 *)chip;
+ int r;
+
+ /* DAC data & port */
+ F2612->dacout = ((int)F2612->REGS[0x2a] - 0x80) << 0; /* level unknown */
+ F2612->dacen = F2612->REGS[0x2d] & 0x80;
+ /* OPN registers */
+ /* DT / MULTI , TL , KS / AR , AMON / DR , SR , SL / RR , SSG-EG */
+ for(r=0x30;r<0x9e;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]);
+ OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]);
+ }
+ /* FB / CONNECT , L / R / AMS / PMS */
+ for(r=0xb0;r<0xb6;r++)
+ if((r&3) != 3)
+ {
+ OPNWriteReg(&F2612->OPN,r,F2612->REGS[r]);
+ OPNWriteReg(&F2612->OPN,r|0x100,F2612->REGS[r|0x100]);
+ }
+ /* channels */
+ /*FM_channel_postload(F2612->CH,6);*/
+ OPN->cur_chip = NULL;
+ }
+}
+
+static void YM2612_save_state(YM2612 *F2612, int index)
+{
+ const char statename[] = "YM2612";
+
+ state_save_register_UINT8 (statename, index, "regs" , F2612->REGS , 512);
+ FMsave_state_st(statename,index,&F2612->OPN.ST);
+ FMsave_state_channel(statename,index,F2612->CH,6);
+ /* 3slots */
+ state_save_register_UINT32 (statename, index, "slot3fc" , F2612->OPN.SL3.fc , 3);
+ state_save_register_UINT8 (statename, index, "slot3fh" , &F2612->OPN.SL3.fn_h, 1);
+ state_save_register_UINT8 (statename, index, "slot3kc" , F2612->OPN.SL3.kcode, 3);
+ /* address register1 */
+ state_save_register_UINT8 (statename, index, "addr_A1" , &F2612->addr_A1, 1);
+}
+#endif /* _STATE_H */
+
+/* initialize YM2612 emulator(s) */
+YM2612 * YM2612Init(void *param, int index, long clock, long rate)
+{
+ YM2612 *F2612;
+
+ /* allocate extend state space */
+ if( (F2612 = (YM2612 *)calloc(1, sizeof(YM2612)))==NULL)
+ return NULL;
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ free( F2612 );
+ return NULL;
+ }
+
+ F2612->OPN.ST.param = param;
+ F2612->OPN.P_CH = F2612->CH;
+ F2612->OPN.ST.clock = clock;
+ F2612->OPN.ST.rate = rate;
+ /* F2612->OPN.ST.irq = 0; */
+ /* F2612->OPN.ST.status = 0; */
+ /* Extend handler */
+ OPNSetPres(&F2612->OPN, 6*24, 6*24, 0);
+ YM2612ResetChip( F2612 );
+
+#ifdef _STATE_H
+ YM2612_save_state(F2612, index);
+#endif
+ return F2612;
+}
+
+void YM2612Mute(YM2612* F2612, int mask)
+{
+ int c;
+ for ( c = 0; c < 6; c++ )
+ {
+ F2612->OPN.pan_mutes [c] = -(mask >> c & 1);
+ OPNUpdatePan( &F2612->OPN, c );
+ }
+}
+
+/* shut down emulator */
+void YM2612Shutdown(YM2612 *F2612)
+{
+ FMCloseTable();
+ free(F2612);
+}
+
+/* reset one of chip */
+void YM2612ResetChip(YM2612 *F2612)
+{
+ int i;
+ FM_OPN *OPN = &F2612->OPN;
+
+ OPN->eg_timer = 0;
+ OPN->eg_cnt = 0;
+
+ OPN->lfo_timer = 0;
+ OPN->lfo_cnt = 0;
+ OPN->LFO_AM = 0;
+ OPN->LFO_PM = 0;
+
+ OPN->ST.TAC = 0;
+ OPN->ST.TBC = 0;
+
+ OPN->SL3.key_csm = 0;
+
+ OPNWriteMode(OPN,0x27,0x30);
+ OPNWriteMode(OPN,0x26,0x00);
+ OPNWriteMode(OPN,0x25,0x00);
+ OPNWriteMode(OPN,0x24,0x00);
+ OPNWriteMode(OPN,0x22,0x00);
+
+ reset_channels( &F2612->CH[0] , 6 );
+
+ for(i = 0xb6 ; i >= 0xb4 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0xc0);
+ OPNWriteReg(OPN,i|0x100,0xc0);
+ }
+ for(i = 0xb2 ; i >= 0x30 ; i-- )
+ {
+ OPNWriteReg(OPN,i ,0);
+ OPNWriteReg(OPN,i|0x100,0);
+ }
+}
+
+/* YM2612 write */
+/* n = number */
+/* a = address */
+/* v = value */
+int YM2612Write(YM2612 *F2612, unsigned int a, unsigned int v)
+{
+ v &= 0xff; /* adjust to 8 bit bus */
+
+ switch( a )
+ {
+ case 0: /* address port 0 */
+ F2612->OPN.ST.address = v;
+ break;
+
+ case 2: /* address port 1 */
+ F2612->OPN.ST.address = v | 0x100;
+ break;
+
+ default:
+ {
+ int addr = F2612->OPN.ST.address; /* verified by Nemesis on real YM2612 */
+#ifdef _STATE_H
+ F2612->REGS[addr] = v;
+#endif
+ switch( addr & 0x1f0 )
+ {
+ case 0x20: /* 0x20-0x2f Mode */
+ switch( addr )
+ {
+ case 0x2a: /* DAC data (YM2612) */
+ YM2612UpdateReq(F2612->OPN.ST.param);
+ F2612->dacout = ((int)v - 0x80) << 8; /* level unknown */
+ break;
+ case 0x2b: /* DAC Sel (YM2612) */
+ /* b7 = dac enable */
+ F2612->dacen = v & 0x80;
+ break;
+ default: /* OPN section */
+ YM2612UpdateReq(F2612->OPN.ST.param);
+ /* write register */
+ OPNWriteMode(&(F2612->OPN),addr,v);
+ }
+ break;
+ default: /* 0x30-0xff OPN section */
+ YM2612UpdateReq(F2612->OPN.ST.param);
+ /* write register */
+ OPNWriteReg(&(F2612->OPN),addr,v);
+ }
+ break;
+ }
+ }
+ return F2612->OPN.ST.irq;
+}
+
+UINT8 YM2612Read(YM2612 *F2612,int a)
+{
+ switch( a&3){
+ case 0: /* status 0 */
+ return FM_STATUS_FLAG(&F2612->OPN.ST);
+ case 1:
+ case 2:
+ case 3:
+ logerror("YM2612 #%p:A=%d read unmapped area\n",F2612->OPN.ST.param,a);
+ return FM_STATUS_FLAG(&F2612->OPN.ST);
+ }
+ return 0;
+}
+
+int YM2612TimerOver(YM2612 *F2612,int c)
+{
+ if( c )
+ { /* Timer B */
+ TimerBOver( &(F2612->OPN.ST) );
+ }
+ else
+ { /* Timer A */
+ YM2612UpdateReq(F2612->OPN.ST.param);
+ /* timer update */
+ TimerAOver( &(F2612->OPN.ST) );
+ /* CSM mode key,TL controll */
+ if ((F2612->OPN.ST.mode & 0xc0) == 0x80)
+ { /* CSM mode total level latch and auto key on */
+ CSMKeyControll( &F2612->OPN, &(F2612->CH[2]) );
+ }
+ }
+ return F2612->OPN.ST.irq;
+}
+
+#endif /* BUILD_YM2612 */
+
+// Ym2612_Emu
+
+Ym2612_Emu::~Ym2612_Emu()
+{
+ if ( impl )
+ YM2612Shutdown( impl );
+}
+
+const char* Ym2612_Emu::set_rate( double sample_rate, double clock_rate )
+{
+ if ( impl )
+ {
+ YM2612Shutdown( impl );
+ impl = 0;
+ }
+
+ if ( !clock_rate )
+ clock_rate = sample_rate * 144.;
+
+ impl = YM2612Init( 0, 0, (long) (clock_rate + 0.5), (long) (sample_rate + 0.5) );
+ if ( !impl )
+ return blargg_err_memory;
+
+ return 0;
+}
+
+void Ym2612_Emu::reset()
+{
+ YM2612ResetChip( impl );
+}
+
+void Ym2612_Emu::write0( int addr, int data )
+{
+ YM2612Write( impl, 0, addr );
+ YM2612Write( impl, 1, data );
+}
+
+void Ym2612_Emu::write1( int addr, int data )
+{
+ YM2612Write( impl, 2, addr );
+ YM2612Write( impl, 3, data );
+}
+
+void Ym2612_Emu::mute_voices( int mask )
+{
+ YM2612Mute( impl, mask );
+}
+
+void Ym2612_Emu::run( int pair_count, sample_t* out )
+{
+ YM2612UpdateOne( impl, out, pair_count );
+}
+
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.h b/plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.h
new file mode 100644
index 00000000..74933337
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Ym2612_Emu.h
@@ -0,0 +1,38 @@
+// YM2612 FM sound chip emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef YM2612_EMU_H
+#define YM2612_EMU_H
+
+struct Ym2612_Impl;
+
+class Ym2612_Emu {
+ Ym2612_Impl* impl;
+public:
+ Ym2612_Emu() { impl = 0; }
+ ~Ym2612_Emu();
+
+ // Sets sample rate and chip clock rate, in Hz. Returns non-zero
+ // if error. If clock_rate=0, uses sample_rate*144
+ const char* set_rate( double sample_rate, double clock_rate = 0 );
+
+ // Resets to power-up state
+ void reset();
+
+ // Mutes voice n if bit n (1 << n) of mask is set
+ enum { channel_count = 6 };
+ void mute_voices( int mask );
+
+ // Writes addr to register 0 then data to register 1
+ void write0( int addr, int data );
+
+ // Writes addr to register 2 then data to register 3
+ void write1( int addr, int data );
+
+ // Runs and adds pair_count*2 samples into current output buffer contents
+ typedef short sample_t;
+ enum { out_chan_count = 2 }; // stereo
+ void run( int pair_count, sample_t* out );
+};
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.cpp b/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.cpp
new file mode 100644
index 00000000..cfc71b7c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.cpp
@@ -0,0 +1,82 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Z80_Cpu.h"
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// flags, named with hex value for clarity
+int const S80 = 0x80;
+int const Z40 = 0x40;
+int const F20 = 0x20;
+int const H10 = 0x10;
+int const F08 = 0x08;
+int const V04 = 0x04;
+int const P04 = 0x04;
+int const N02 = 0x02;
+int const C01 = 0x01;
+
+Z80_Cpu::Z80_Cpu()
+{
+ cpu_state = &cpu_state_;
+
+ for ( int i = 0x100; --i >= 0; )
+ {
+ int even = 1;
+ for ( int p = i; p; p >>= 1 )
+ even ^= p;
+ int n = (i & (S80 | F20 | F08)) | ((even & 1) * P04);
+ szpc [i] = n;
+ szpc [i + 0x100] = n | C01;
+ }
+ szpc [0x000] |= Z40;
+ szpc [0x100] |= Z40;
+}
+
+inline void Z80_Cpu::set_page( int i, void* write, void const* read )
+{
+ int offset = Z80_CPU_OFFSET( i * page_size );
+ byte * write2 = STATIC_CAST(byte *,write) - offset;
+ byte const* read2 = STATIC_CAST(byte const*,read ) - offset;
+ cpu_state_.write [i] = write2;
+ cpu_state_.read [i] = read2;
+ cpu_state->write [i] = write2;
+ cpu_state->read [i] = read2;
+}
+
+void Z80_Cpu::reset( void* unmapped_write, void const* unmapped_read )
+{
+ check( cpu_state == &cpu_state_ );
+ cpu_state = &cpu_state_;
+ cpu_state_.time = 0;
+ cpu_state_.base = 0;
+ end_time_ = 0;
+
+ for ( int i = 0; i < page_count + 1; i++ )
+ set_page( i, unmapped_write, unmapped_read );
+
+ memset( &r, 0, sizeof r );
+}
+
+void Z80_Cpu::map_mem( addr_t start, int size, void* write, void const* read )
+{
+ // address range must begin and end on page boundaries
+ require( start % page_size == 0 );
+ require( size % page_size == 0 );
+ require( start + size <= 0x10000 );
+
+ for ( int offset = 0; offset < size; offset += page_size )
+ set_page( (start + offset) >> page_bits,
+ STATIC_CAST(char *,write) + offset,
+ STATIC_CAST(char const*,read ) + offset );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.h b/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.h
new file mode 100644
index 00000000..743cbb65
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu.h
@@ -0,0 +1,122 @@
+// Z80 CPU emulator
+
+// Game_Music_Emu 0.6-pre
+#ifndef Z80_CPU_H
+#define Z80_CPU_H
+
+#include "blargg_endian.h"
+
+class Z80_Cpu {
+public:
+ typedef int time_t;
+ typedef int addr_t;
+ typedef BOOST::uint8_t byte;
+
+ // Clears registers and maps all pages to unmapped
+ void reset( void* unmapped_write, void const* unmapped_read );
+
+ // TODO: split mapping out of CPU
+
+ // Maps memory. Start and size must be multiple of page_size.
+ enum { page_bits = 10 };
+ enum { page_size = 1 << page_bits };
+ void map_mem( addr_t addr, int size, void* write, void const* read );
+ void map_mem( addr_t addr, int size, void* read_write );
+
+ // Maps address to pointer to that byte
+ byte * write( addr_t addr );
+ byte const* read( addr_t addr );
+
+ // Time of beginning of next instruction
+ time_t time() const { return cpu_state->time + cpu_state->base; }
+
+ // Alter current time
+ void set_time( time_t t ) { cpu_state->time = t - cpu_state->base; }
+ void adjust_time( int delta ) { cpu_state->time += delta; }
+
+ #if BLARGG_BIG_ENDIAN
+ struct regs_t { byte b,c, d,e, h,l, flags,a; };
+ #else
+ struct regs_t { byte c,b, e,d, l,h, a,flags; };
+ #endif
+ BLARGG_STATIC_ASSERT( sizeof (regs_t) == 8 );
+
+ struct pairs_t { BOOST::uint16_t bc, de, hl, fa; };
+
+ // Registers are not updated until run() returns
+ struct registers_t {
+ BOOST::uint16_t pc;
+ BOOST::uint16_t sp;
+ BOOST::uint16_t ix;
+ BOOST::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;
+ byte iff1;
+ byte iff2;
+ byte r;
+ byte i;
+ byte im;
+ };
+ //registers_t r; (below for efficiency)
+
+ // can read this far past end of memory
+ enum { cpu_padding = 0x100 };
+
+ // Can read this many bytes past end of a page
+ enum { page_padding = 4 };
+
+ void set_end_time( time_t t );
+public:
+ Z80_Cpu();
+
+ enum { page_count = 0x10000 / page_size };
+ byte szpc [0x200];
+ time_t end_time_;
+ struct cpu_state_t {
+ byte const* read [page_count + 1];
+ byte * write [page_count + 1];
+ time_t base;
+ time_t time;
+ };
+ cpu_state_t* cpu_state; // points to cpu_state_ or a local copy within run()
+ cpu_state_t cpu_state_;
+ void set_page( int i, void* write, void const* read );
+public:
+ registers_t r;
+};
+
+#if BLARGG_NONPORTABLE
+ #define Z80_CPU_OFFSET( addr ) (addr)
+#else
+ #define Z80_CPU_OFFSET( addr ) ((addr) & (Z80_Cpu::page_size - 1))
+#endif
+
+inline Z80_Cpu::byte* Z80_Cpu::write( addr_t addr )
+{
+ return cpu_state->write [(unsigned) addr >> page_bits] + Z80_CPU_OFFSET( addr );
+}
+
+inline Z80_Cpu::byte const* Z80_Cpu::read( addr_t addr )
+{
+ return cpu_state->read [(unsigned) addr >> page_bits] + Z80_CPU_OFFSET( addr );
+}
+
+inline void Z80_Cpu::map_mem( addr_t addr, int size, void* p )
+{
+ map_mem( addr, size, p, p );
+}
+
+inline void Z80_Cpu::set_end_time( time_t t )
+{
+ time_t delta = cpu_state->base - t;
+ cpu_state->base = t;
+ cpu_state->time += delta;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu_run.h b/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu_run.h
new file mode 100644
index 00000000..0a8cd97d
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/Z80_Cpu_run.h
@@ -0,0 +1,1695 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+// Last validated with zexall 2009.12.05.
+// Doesn't implement the R register or immediate interrupt after EI.
+// Address wrap-around isn't completely correct, but is prevented from crashing emulator.
+// 16-bit memory accesses are made directly to mapped memory, instead of using macro.
+
+#if 0
+/* Define these macros in the source file before #including this file.
+- Parameters might be expressions, so they are best evaluated only once,
+though they NEVER have side-effects, so multiple evaluation is OK.
+- Output parameters might be a multiple-assignment expression like "a=x",
+so they must NOT be parenthesized.
+- Except where noted, time() and related functions will NOT work
+correctly inside a macro. TIME() is always correct, and between FLUSH_TIME() and
+CACHE_TIME() the normal time changing functions can be used.
+- Macros "returning" void may use a {} statement block. */
+
+ // 0 <= addr <= 0xFFFF + 0x100
+ // Optional; default uses whatever was set with map_mem()
+ int READ_MEM( addr_t );
+ void WRITE_MEM( addr_t, int data );
+
+ // 0 <= port <= 0xFFFF (apparently upper 8 bits are output by hardware)
+ void OUT_PORT( int port, int data );
+ int IN_PORT int port );
+
+ // Reference to Z80_Cpu object used for emulation
+ #define CPU cpu
+
+// The following can be used within macros:
+
+ // Current time
+ time_t TIME();
+
+ // Allows use of time functions
+ void FLUSH_TIME();
+
+ // Must be used before end of macro if FLUSH_TIME() was used earlier
+ void CACHE_TIME();
+
+// Configuration (optional; commented behavior if defined)
+
+ // Optimizes as if map_mem( 0, 0x10000, FLAT_MEM, FLAT_MEM ) is always in effect
+ #define FLAT_MEM my_mem_array
+
+ // If RST 7 ($FF) is encountered and PC = IDLE_ADDR, stops execution
+ #define IDLE_ADDR 0x1234
+
+ // Expanded just before beginning of code, to help debugger
+ #define CPU_BEGIN void my_run_cpu() {
+
+#endif
+
+/* Copyright (C) 2006-2008 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#ifdef CPU_BEGIN
+ CPU_BEGIN
+#endif
+
+#define R CPU.r
+
+// 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 ) CPU.szpc [n]
+#define SZ28PC( n ) CPU.szpc [n]
+#define SZ28C( n ) (CPU.szpc [n] & ~P04)
+#define SZ28( n ) SZ28C( n )
+
+#define SET_R( n ) (void) (R.r = n)
+#define GET_R() (R.r)
+
+// Time
+#define TIME() (s_time + s.base)
+#define FLUSH_TIME() {s.time = s_time;}
+#define CACHE_TIME() {s_time = s.time;}
+
+// Memory
+#define RW_MEM( addr, rw ) RW_PAGE( addr, rw ) [RW_OFFSET( addr )]
+#define READ_CODE( addr ) RW_MEM( addr, read )
+
+#ifdef FLAT_MEM
+ #define RW_PAGE( addr, rw ) FLAT_MEM
+ #define RW_OFFSET( addr ) (addr)
+ #define INSTR( off, addr ) READ_CODE( addr )
+#else
+ #define RW_PAGE( addr, rw ) s.rw [(unsigned) (addr) >> Z80_Cpu::page_bits]
+ #define RW_OFFSET( addr ) Z80_CPU_OFFSET( addr )
+ #define INSTR( off, addr ) instr [off]
+#endif
+
+#ifndef READ_MEM
+ #define READ_MEM( addr ) RW_MEM( addr, read )
+#endif
+
+#ifndef WRITE_MEM
+ #define WRITE_MEM( addr, data ) (RW_MEM( addr, write ) = data)
+#endif
+
+#define READ_WORD( addr ) GET_LE16( &RW_MEM( addr, read ) )
+#define WRITE_WORD( addr, data ) SET_LE16( &RW_MEM( addr, write ), data )
+
+// Truncation
+#define BYTE( n ) ((BOOST::uint8_t ) (n)) /* (unsigned) n & 0xFF */
+#define SBYTE( n ) ((BOOST::int8_t ) (n)) /* (BYTE( n ) ^ 0x80) - 0x80 */
+#define WORD( n ) ((BOOST::uint16_t) (n)) /* (unsigned) n & 0xFFFF */
+
+// Misc
+#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
+
+#if BLARGG_BIG_ENDIAN
+ #define R8( n, offset ) ((r.r8_ - offset) [n])
+#elif BLARGG_LITTLE_ENDIAN
+ #define R8( n, offset ) ((r.r8_ - offset) [(n) ^ 1])
+#else
+ #error "Byte order of CPU must be known"
+#endif
+
+#define R16( n, shift, offset ) (r.r16_ [((unsigned) (n) >> shift) - (offset >> shift)])
+
+#define EX( x, y ) \
+ {\
+ int temp = x;\
+ x = y;\
+ y = temp;\
+ }
+
+#define EXX( name ) \
+ EX( R.alt.name, r.name )
+
+bool warning = false;
+{
+ Z80_Cpu::cpu_state_t s;
+ #ifdef FLAT_MEM
+ s.base = CPU.cpu_state_.base;
+ #else
+ s = CPU.cpu_state_;
+ #endif
+ CPU.cpu_state = &s;
+
+
+ union r_t {
+ Z80_Cpu::regs_t b;
+ Z80_Cpu::pairs_t w;
+ byte r8_ [8]; // indexed
+ BOOST::uint16_t r16_ [4];
+ } r;
+ r.b = R.b;
+
+ Z80_Cpu::time_t s_time = CPU.cpu_state_.time;
+ int pc = R.pc;
+ int sp = R.sp;
+ int ix = R.ix; // TODO: keep in memory for direct access?
+ int iy = R.iy;
+ int flags = R.b.flags;
+
+ //goto loop; // confuses optimizer
+ s_time += 7;
+ pc -= 2;
+
+call_not_taken:
+ s_time -= 7;
+jp_not_taken:
+ pc += 2;
+loop:
+
+ check( (unsigned) pc < 0x10000 + 1 ); // +1 so emulator can catch wrap-around
+ check( (unsigned) sp < 0x10000 );
+ check( (unsigned) flags < 0x100 );
+ check( (unsigned) ix < 0x10000 );
+ check( (unsigned) iy < 0x10000 );
+
+ byte const* instr = RW_PAGE( pc, read );
+
+ int opcode;
+
+ if ( RW_OFFSET( ~0 ) == ~0 )
+ {
+ opcode = instr [RW_OFFSET( pc )];
+ pc++;
+ instr += RW_OFFSET( pc );
+ }
+ else
+ {
+ instr += RW_OFFSET( pc );
+ opcode = *instr++;
+ pc++;
+ }
+
+ static byte const clock_table [256 * 2] = {
+ // 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
+ 8,10, 7, 6, 4, 4, 7, 4,12,11, 7, 6, 4, 4, 7, 4, // 1
+ 7,10,16, 6, 4, 4, 7, 4, 7,11,16, 6, 4, 4, 7, 4, // 2
+ 7,10,13, 6,11,11,10, 4, 7,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
+
+ // high four bits are $ED time - 8, low four bits are $DD/$FD time - 8
+ //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,
+ };
+
+ if ( s_time >= 0 )
+ goto out_of_time;
+ s_time += clock_table [opcode];
+
+ #ifdef Z80_CPU_LOG_H
+ //log_opcode( opcode, READ_CODE( pc ) );
+ z80_cpu_log( "log.txt", pc - 1, opcode, READ_CODE( pc ),
+ READ_CODE( pc + 1 ), READ_CODE( pc + 2 ) );
+ z80_log_regs( r.b.a, r.w.bc, r.w.de, r.w.hl, sp, ix, iy );
+ #endif
+
+#define GET_ADDR() GET_LE16( &INSTR( 0, pc ) )
+
+ int data;
+ data = INSTR( 0, pc );
+
+ switch ( opcode )
+ {
+// Common
+
+ case 0x00: // NOP
+ CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc.
+ goto loop;
+
+ case 0x08:{// EX AF,AF'
+ EXX( b.a );
+ EX( R.alt.b.flags, flags );
+ goto loop;
+ }
+
+ case 0xD3: // OUT (imm),A
+ pc++;
+ OUT_PORT( (data + r.b.a * 0x100), r.b.a );
+ goto loop;
+
+ case 0x2E: // LD L,imm
+ pc++;
+ r.b.l = data;
+ goto loop;
+
+ case 0x3E: // LD A,imm
+ pc++;
+ r.b.a = data;
+ goto loop;
+
+ case 0x3A:{// LD A,(addr)
+ int addr = GET_ADDR();
+ pc += 2;
+ r.b.a = READ_MEM( 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, clocks ) {\
+ pc++;\
+ if ( !(cond) )\
+ goto loop;\
+ int offset = SBYTE( data );\
+ pc = WORD( pc + offset );\
+ s_time += clocks;\
+ goto loop;\
+}
+
+#define JR( cond ) JR_( cond, 5 )
+
+ 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,0) // JR disp
+
+ case 0x10:{// DJNZ disp
+ int temp = r.b.b - 1;
+ r.b.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 = r.w.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 = WORD( 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:
+ int addr = pc + 2;
+ pc = GET_ADDR();
+ sp = WORD( sp - 2 );
+ WRITE_WORD( sp, addr );
+ goto loop;
+ }
+
+ case 0xFF: // RST
+ #ifdef IDLE_ADDR
+ if ( pc == IDLE_ADDR + 1 )
+ goto hit_idle_addr;
+ #else
+ if ( pc > 0x10000 )
+ {
+ pc = WORD( pc - 1 );
+ s_time -= 11;
+ goto loop;
+ }
+ #endif
+ CASE7( C7, CF, D7, DF, E7, EF, F7 ):
+ data = pc;
+ pc = opcode & 0x38;
+ #ifdef RST_BASE
+ pc += RST_BASE;
+ #endif
+ goto push_data;
+
+// PUSH/POP
+ case 0xF5: // PUSH AF
+ data = r.b.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 = WORD( sp - 2 );
+ WRITE_WORD( sp, data );
+ goto loop;
+
+ case 0xF1: // POP AF
+ flags = READ_MEM( sp );
+ r.b.a = READ_MEM( (sp + 1) );
+ sp = WORD( sp + 2 );
+ goto loop;
+
+ case 0xC1: // POP BC
+ case 0xD1: // POP DE
+ case 0xE1: // POP HL
+ R16( opcode, 4, 0xC1 ) = READ_WORD( sp );
+ sp = WORD( 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_MEM( r.w.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 ^= r.b.a;
+ flags = opcode >> 3 & N02; // bit 4 is set in subtract opcodes
+ if ( flags )
+ result = -result;
+ result += r.b.a;
+ data ^= result;
+ flags +=(data & H10) +
+ ((data + 0x80) >> 6 & V04) +
+ SZ28C( result & 0x1FF );
+ r.b.a = result;
+ goto loop;
+ }
+
+// CP
+ case 0xBE: // CP (HL)
+ data = READ_MEM( r.w.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 = r.b.a - data;
+ flags = N02 + (data & (F20 | F08)) + (result >> 8 & C01);
+ data ^= r.b.a;
+ flags +=(((result ^ r.b.a) & data) >> 5 & V04) +
+ (((data & H10) ^ result) & (S80 | H10));
+ if ( BYTE( result ) )
+ goto loop;
+ flags += Z40;
+ goto loop;
+ }
+
+// ADD HL,r.w
+
+ 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: {
+ int sum = r.w.hl + data;
+ data ^= r.w.hl;
+ r.w.hl = sum;
+ flags = (flags & (S80 | Z40 | V04)) +
+ (sum >> 16) +
+ (sum >> 8 & (F20 | F08)) +
+ ((data ^ sum) >> 8 & H10);
+ goto loop;
+ }
+
+ case 0x27:{// DAA
+ int a = r.b.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)) +
+ ((r.b.a ^ a) & H10) +
+ SZ28P( BYTE( a ) );
+ r.b.a = a;
+ goto loop;
+ }
+
+// INC/DEC
+ case 0x34: // INC (HL)
+ data = READ_MEM( r.w.hl ) + 1;
+ WRITE_MEM( r.w.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( BYTE( data ) );
+ if ( data != 0x80 )
+ goto loop;
+ flags += V04;
+ goto loop;
+
+ case 0x35: // DEC (HL)
+ data = READ_MEM( r.w.hl ) - 1;
+ WRITE_MEM( r.w.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( BYTE( 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 = WORD( 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 = WORD( sp - 1 );
+ goto loop;
+
+// AND
+ case 0xA6: // AND (HL)
+ data = READ_MEM( r.w.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:
+ r.b.a &= data;
+ flags = SZ28P( r.b.a ) + H10;
+ goto loop;
+
+// OR
+ case 0xB6: // OR (HL)
+ data = READ_MEM( r.w.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:
+ r.b.a |= data;
+ flags = SZ28P( r.b.a );
+ goto loop;
+
+// XOR
+ case 0xAE: // XOR (HL)
+ data = READ_MEM( r.w.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:
+ r.b.a ^= data;
+ flags = SZ28P( r.b.a );
+ goto loop;
+
+// LD
+ CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r
+ WRITE_MEM( r.w.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_MEM( r.w.hl, data );
+ goto loop;
+
+ CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL)
+ R8( opcode >> 3, 8 ) = READ_MEM( r.w.hl );
+ goto loop;
+
+ case 0x01: // LD r.w,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)
+ int addr = GET_ADDR();
+ pc += 2;
+ r.w.hl = READ_WORD( addr );
+ goto loop;
+ }
+
+ case 0x32:{// LD (addr),A
+ int addr = GET_ADDR();
+ pc += 2;
+ WRITE_MEM( addr, r.b.a );
+ goto loop;
+ }
+
+ case 0x22:{// LD (addr),HL
+ int addr = GET_ADDR();
+ pc += 2;
+ WRITE_WORD( addr, r.w.hl );
+ goto loop;
+ }
+
+ case 0x02: // LD (BC),A
+ case 0x12: // LD (DE),A
+ WRITE_MEM( R16( opcode, 4, 0x02 ), r.b.a );
+ goto loop;
+
+ case 0x0A: // LD A,(BC)
+ case 0x1A: // LD A,(DE)
+ r.b.a = READ_MEM( R16( opcode, 4, 0x0A ) );
+ goto loop;
+
+ case 0xF9: // LD SP,HL
+ sp = r.w.hl;
+ goto loop;
+
+// Rotate
+
+ case 0x07:{// RLCA
+ int temp = r.b.a;
+ temp = (temp << 1) + (temp >> 7);
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & (F20 | F08 | C01));
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x0F:{// RRCA
+ int temp = r.b.a;
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & C01);
+ temp = (temp << 7) + (temp >> 1);
+ flags += temp & (F20 | F08);
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x17:{// RLA
+ int temp = (r.b.a << 1) + (flags & C01);
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & (F20 | F08)) +
+ (temp >> 8);
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x1F:{// RRA
+ int temp = (flags << 7) + (r.b.a >> 1);
+ flags = (flags & (S80 | Z40 | P04)) +
+ (temp & (F20 | F08)) +
+ (r.b.a & C01);
+ r.b.a = temp;
+ goto loop;
+ }
+
+// Misc
+ case 0x2F:{// CPL
+ int temp = ~r.b.a;
+ flags = (flags & (S80 | Z40 | P04 | C01)) +
+ (temp & (F20 | F08)) +
+ (H10 | N02);
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x3F:{// CCF
+ flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) +
+ (flags << 4 & H10) +
+ (r.b.a & (F20 | F08));
+ goto loop;
+ }
+
+ case 0x37: // SCF
+ flags = (flags & (S80 | Z40 | P04)) | C01 +
+ (r.b.a & (F20 | F08));
+ goto loop;
+
+ case 0xDB: // IN A,(imm)
+ pc++;
+ r.b.a = IN_PORT( (data + r.b.a * 0x100) );
+ goto loop;
+
+ case 0xE3:{// EX (SP),HL
+ int temp = READ_WORD( sp );
+ WRITE_WORD( sp, r.w.hl );
+ r.w.hl = temp;
+ goto loop;
+ }
+
+ case 0xEB: // EX DE,HL
+ EX( r.w.hl, r.w.de );
+ goto loop;
+
+ case 0xD9: // EXX DE,HL
+ EXX( w.bc );
+ EXX( w.de );
+ EXX( w.hl );
+ 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:
+ pc++;
+ switch ( data )
+ {
+
+ // Rotate left
+
+ #define RLC( read, write ) {\
+ int result = read;\
+ result = BYTE( result << 1 ) + (result >> 7);\
+ flags = SZ28P( result ) + (result & C01);\
+ write;\
+ goto loop;\
+ }
+
+ case 0x06: // RLC (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rlc_data_addr:
+ RLC( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r
+ byte& reg = R8( data, 0 );
+ RLC( reg, reg = result )
+ }
+
+ #define RL( read, write ) {\
+ int result = (read << 1) + (flags & C01);\
+ flags = SZ28PC( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x16: // RL (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rl_data_addr:
+ RL( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r
+ byte& reg = R8( data, 0x10 );
+ RL( reg, reg = result )
+ }
+
+ #define SLA( read, low_bit, write ) {\
+ int result = (read << 1) + low_bit;\
+ flags = SZ28PC( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x26: // SLA (HL)
+ s_time += 7;
+ data = r.w.hl;
+ sla_data_addr:
+ SLA( READ_MEM( data ), 0, WRITE_MEM( data, result ) )
+
+ CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r
+ byte& reg = R8( data, 0x20 );
+ SLA( reg, 0, reg = result )
+ }
+
+ case 0x36: // SLL (HL)
+ s_time += 7;
+ data = r.w.hl;
+ sll_data_addr:
+ SLA( READ_MEM( data ), 1, WRITE_MEM( data, result ) )
+
+ CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r
+ byte& reg = R8( data, 0x30 );
+ SLA( reg, 1, reg = result )
+ }
+
+ // Rotate right
+
+ #define RRC( read, write ) {\
+ int result = read;\
+ flags = result & C01;\
+ result = BYTE( result << 7 ) + (result >> 1);\
+ flags += SZ28P( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x0E: // RRC (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rrc_data_addr:
+ RRC( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r
+ byte& reg = R8( data, 0x08 );
+ RRC( reg, reg = result )
+ }
+
+ #define RR( read, write ) {\
+ int result = read;\
+ int temp = result & C01;\
+ result = BYTE( flags << 7 ) + (result >> 1);\
+ flags = SZ28P( result ) + temp;\
+ write;\
+ goto loop;\
+ }
+
+ case 0x1E: // RR (HL)
+ s_time += 7;
+ data = r.w.hl;
+ rr_data_addr:
+ RR( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r
+ byte& reg = R8( data, 0x18 );
+ RR( reg, reg = result )
+ }
+
+ #define SRA( read, write ) {\
+ int result = read;\
+ flags = result & C01;\
+ result = (result & 0x80) + (result >> 1);\
+ flags += SZ28P( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x2E: // SRA (HL)
+ data = r.w.hl;
+ s_time += 7;
+ sra_data_addr:
+ SRA( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r
+ byte& reg = R8( data, 0x28 );
+ SRA( reg, reg = result )
+ }
+
+ #define SRL( read, write ) {\
+ int result = read;\
+ flags = result & C01;\
+ result >>= 1;\
+ flags += SZ28P( result );\
+ write;\
+ goto loop;\
+ }
+
+ case 0x3E: // SRL (HL)
+ s_time += 7;
+ data = r.w.hl;
+ srl_data_addr:
+ SRL( READ_MEM( data ), WRITE_MEM( data, result ) )
+
+ CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r
+ byte& reg = R8( data, 0x38 );
+ SRL( reg, reg = result )
+ }
+
+ // BIT
+ {
+ int temp;
+ CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ): // BIT b,(HL)
+ s_time += 4;
+ temp = READ_MEM( r.w.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:
+ temp = temp & (1 << (data >> 3 & 7));
+ flags += (temp & S80) + H10;
+ flags += (unsigned) --temp >> 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_MEM( r.w.hl );
+ int bit = 1 << (data >> 3 & 7);
+ temp |= bit; // SET
+ if ( !(data & 0x40) )
+ temp ^= bit; // RES
+ WRITE_MEM( r.w.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, pc ) )
+
+//////////////////////////////////////// ED prefix
+ {
+ case 0xED:
+ pc++;
+ s_time += (clock_table + 256) [data] >> 4;
+ switch ( data )
+ {
+ {
+ int 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 );
+ int sum = temp + (flags & C01);
+ flags = ~data >> 2 & N02;
+ if ( flags )
+ sum = -sum;
+ sum += r.w.hl;
+ temp ^= r.w.hl;
+ temp ^= sum;
+ flags +=(sum >> 16 & C01) +
+ (temp >> 8 & H10) +
+ (sum >> 8 & (S80 | F20 | F08)) +
+ ((temp + 0x8000) >> 14 & V04);
+ r.w.hl = sum;
+ if ( WORD( sum ) )
+ goto loop;
+ flags += Z40;
+ goto loop;
+ }
+
+ CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C)
+ int temp = IN_PORT( r.w.bc );
+ R8( data >> 3, 8 ) = temp;
+ flags = (flags & C01) + SZ28P( temp );
+ goto loop;
+ }
+
+ case 0x71: // OUT (C),0
+ r.b.flags = 0;
+ CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r
+ OUT_PORT( r.w.bc, R8( data >> 3, 8 ) );
+ goto loop;
+
+ {
+ int 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 );
+ int addr = GET_ADDR();
+ pc += 2;
+ WRITE_WORD( addr, temp );
+ goto loop;
+ }
+
+ case 0x4B: // LD BC,(ADDR)
+ case 0x5B:{// LD DE,(ADDR)
+ int addr = GET_ADDR();
+ pc += 2;
+ R16( data, 4, 0x4B ) = READ_WORD( addr );
+ goto loop;
+ }
+
+ case 0x7B:{// LD SP,(ADDR)
+ int addr = GET_ADDR();
+ pc += 2;
+ sp = READ_WORD( addr );
+ goto loop;
+ }
+
+ case 0x67:{// RRD
+ int temp = READ_MEM( r.w.hl );
+ WRITE_MEM( r.w.hl, ((r.b.a << 4) + (temp >> 4)) );
+ temp = (r.b.a & 0xF0) + (temp & 0x0F);
+ flags = (flags & C01) + SZ28P( temp );
+ r.b.a = temp;
+ goto loop;
+ }
+
+ case 0x6F:{// RLD
+ int temp = READ_MEM( r.w.hl );
+ WRITE_MEM( r.w.hl, ((temp << 4) + (r.b.a & 0x0F)) );
+ temp = (r.b.a & 0xF0) + (temp >> 4);
+ flags = (flags & C01) + SZ28P( temp );
+ r.b.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 = r.b.a;
+ r.b.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;
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+ int temp = READ_MEM( addr );
+
+ int result = r.b.a - temp;
+ flags = (flags & C01) + N02 +
+ ((((temp ^ r.b.a) & H10) ^ result) & (S80 | H10));
+
+ if ( !BYTE( result ) )
+ flags += Z40;
+ result -= (flags & H10) >> 4;
+ flags += result & F08;
+ flags += result << 4 & F20;
+ if ( !--r.w.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;
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+ int temp = READ_MEM( addr );
+
+ addr = r.w.de;
+ r.w.de = addr + inc;
+ WRITE_MEM( addr, temp );
+
+ temp += r.b.a;
+ flags = (flags & (S80 | Z40 | C01)) +
+ (temp & F08) + (temp << 4 & F20);
+ if ( !--r.w.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;
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+ int temp = READ_MEM( addr );
+
+ int b = --r.b.b;
+ flags = (temp >> 6 & N02) + SZ28( b );
+ if ( b && data >= 0xB0 )
+ {
+ pc -= 2;
+ s_time += 5;
+ }
+
+ OUT_PORT( r.w.bc, temp );
+ goto loop;
+ }
+
+ {
+ int inc;
+ case 0xAA: // IND
+ case 0xBA: // INDR
+ inc = -1;
+ if ( 0 )
+ case 0xA2: // INI
+ case 0xB2: // INIR
+ inc = +1;
+
+ int addr = r.w.hl;
+ r.w.hl = addr + inc;
+
+ int temp = IN_PORT( r.w.bc );
+
+ int b = --r.b.b;
+ flags = (temp >> 6 & N02) + SZ28( b );
+ if ( b && data >= 0xB0 )
+ {
+ pc -= 2;
+ s_time += 5;
+ }
+
+ WRITE_MEM( addr, temp );
+ goto loop;
+ }
+
+ case 0x47: // LD I,A
+ R.i = r.b.a;
+ goto loop;
+
+ case 0x4F: // LD R,A
+ SET_R( r.b.a );
+ dprintf( "LD R,A not supported\n" );
+ warning = true;
+ goto loop;
+
+ case 0x57: // LD A,I
+ r.b.a = R.i;
+ goto ld_ai_common;
+
+ case 0x5F: // LD A,R
+ r.b.a = GET_R();
+ dprintf( "LD A,R not supported\n" );
+ warning = true;
+ ld_ai_common:
+ flags = (flags & C01) + SZ28( r.b.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
+ {
+ int ixy;
+ case 0xDD:
+ ixy = ix;
+ goto ix_prefix;
+ case 0xFD:
+ ixy = iy;
+ ix_prefix:
+ pc++;
+ int data2 = READ_CODE( pc );
+ s_time += (clock_table + 256) [data] & 0x0F;
+ switch ( data )
+ {
+ // TODO: more efficient way of avoid negative address
+ // TODO: avoid using this as argument to READ_MEM() since it is evaluated twice
+ #define IXY_DISP( ixy, disp ) WORD( (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_MEM( IXY_DISP( ixy, SBYTE( 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 = BYTE( ixy );
+ goto adc_data;
+
+ {
+ int 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: {
+ int sum = ixy + temp;
+ temp ^= ixy;
+ ixy = WORD( 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_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto and_data;
+
+ case 0xA4: // AND HXY
+ data = ixy >> 8;
+ goto and_data;
+
+ case 0xA5: // AND LXY
+ data = BYTE( ixy );
+ goto and_data;
+
+ // OR
+ case 0xB6: // OR (IXY+disp)
+ pc++;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto or_data;
+
+ case 0xB4: // OR HXY
+ data = ixy >> 8;
+ goto or_data;
+
+ case 0xB5: // OR LXY
+ data = BYTE( ixy );
+ goto or_data;
+
+ // XOR
+ case 0xAE: // XOR (IXY+disp)
+ pc++;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto xor_data;
+
+ case 0xAC: // XOR HXY
+ data = ixy >> 8;
+ goto xor_data;
+
+ case 0xAD: // XOR LXY
+ data = BYTE( ixy );
+ goto xor_data;
+
+ // CP
+ case 0xBE: // CP (IXY+disp)
+ pc++;
+ data = READ_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto cp_data;
+
+ case 0xBC: // CP HXY
+ data = ixy >> 8;
+ goto cp_data;
+
+ case 0xBD: // CP LXY
+ data = BYTE( 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_CODE( pc );
+ pc++;
+ WRITE_MEM( IXY_DISP( ixy, SBYTE( 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_MEM( IXY_DISP( ixy, SBYTE( data2 ) ) );
+ goto loop;
+
+ case 0x26: // LD HXY,imm
+ pc++;
+ goto ld_hxy_data;
+
+ case 0x65: // LD HXY,LXY
+ data2 = BYTE( ixy );
+ goto ld_hxy_data;
+
+ CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r
+ data2 = R8( data, 0x60 );
+ ld_hxy_data:
+ ixy = BYTE( 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
+ int 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)
+ int addr = GET_ADDR();
+ ixy = READ_WORD( addr );
+ pc += 2;
+ goto set_ixy;
+ }
+
+ // DD/FD CB prefix
+ case 0xCB: {
+ data = IXY_DISP( ixy, SBYTE( data2 ) );
+ pc++;
+ data2 = READ_CODE( 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)
+ int temp = READ_MEM( data );
+ temp = temp & (1 << (data2 >> 3 & 7));
+ flags = (flags & C01) + H10 + (temp & S80);
+ flags += (unsigned) --temp >> 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_MEM( data );
+ int bit = 1 << (data2 >> 3 & 7);
+ temp |= bit; // SET
+ if ( !(data2 & 0x40) )
+ temp ^= bit; // RES
+ WRITE_MEM( 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 = WORD( ixy + 1 );
+ goto set_ixy;
+
+ case 0x2B: // DEC IXY
+ ixy = WORD( ixy - 1 );
+ goto set_ixy;
+
+ case 0x34: // INC (IXY+disp)
+ ixy = IXY_DISP( ixy, SBYTE( data2 ) );
+ pc++;
+ data = READ_MEM( ixy ) + 1;
+ WRITE_MEM( ixy, data );
+ goto inc_set_flags;
+
+ case 0x35: // DEC (IXY+disp)
+ ixy = IXY_DISP( ixy, SBYTE( data2 ) );
+ pc++;
+ data = READ_MEM( ixy ) - 1;
+ WRITE_MEM( ixy, data );
+ goto dec_set_flags;
+
+ case 0x24: // INC HXY
+ ixy = WORD( ixy + 0x100 );
+ data = ixy >> 8;
+ goto inc_xy_common;
+
+ case 0x2C: // INC LXY
+ data = BYTE( 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 = WORD( ixy - 0x100 );
+ data = ixy >> 8;
+ goto dec_xy_common;
+
+ case 0x2D: // DEC LXY
+ data = BYTE( 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 = WORD( sp + 2 );
+ goto set_ixy;
+ }
+
+ // Misc
+
+ case 0xE9: // JP (IXY)
+ pc = ixy;
+ goto loop;
+
+ case 0xE3:{// EX (SP),IXY
+ int 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 );
+
+#ifdef IDLE_ADDR
+hit_idle_addr:
+ s_time -= 11;
+ goto out_of_time;
+#endif
+halt:
+ s_time &= 3; // increment by multiple of 4
+out_of_time:
+ pc--;
+
+ r.b.flags = flags;
+ R.ix = ix;
+ R.iy = iy;
+ R.sp = sp;
+ R.pc = pc;
+ R.b = r.b;
+
+ CPU.cpu_state_.base = s.base;
+ CPU.cpu_state_.time = s_time;
+ CPU.cpu_state = &CPU.cpu_state_;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.cpp b/plugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.cpp
new file mode 100755
index 00000000..9caaddd7
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.cpp
@@ -0,0 +1,555 @@
+// File_Extractor $vers. http://www.slack.net/~ant/
+
+#include "Data_Reader.h"
+
+#include "blargg_endian.h"
+#include <stdio.h>
+#include <errno.h>
+
+#if BLARGG_UTF8_PATHS
+ #include <windows.h>
+#endif
+
+/* Copyright (C) 2005-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+// Data_Reader
+
+blargg_err_t Data_Reader::read( void* p, int n )
+{
+ assert( n >= 0 );
+
+ if ( n < 0 )
+ return blargg_err_caller;
+
+ if ( n <= 0 )
+ return blargg_ok;
+
+ if ( n > remain() )
+ return blargg_err_file_eof;
+
+ blargg_err_t err = read_v( p, n );
+ if ( !err )
+ remain_ -= n;
+
+ return err;
+}
+
+blargg_err_t Data_Reader::read_avail( void* p, int* n_ )
+{
+ assert( *n_ >= 0 );
+
+ int n = min( *n_, remain() );
+ *n_ = 0;
+
+ if ( n < 0 )
+ return blargg_err_caller;
+
+ if ( n <= 0 )
+ return blargg_ok;
+
+ blargg_err_t err = read_v( p, n );
+ if ( !err )
+ {
+ remain_ -= n;
+ *n_ = n;
+ }
+
+ return err;
+}
+
+blargg_err_t Data_Reader::read_avail( void* p, long* n )
+{
+ int i = STATIC_CAST(int, *n);
+ blargg_err_t err = read_avail( p, &i );
+ *n = i;
+ return err;
+}
+
+blargg_err_t Data_Reader::skip_v( int count )
+{
+ char buf [512];
+ while ( count )
+ {
+ int n = min( count, (int) sizeof buf );
+ count -= n;
+ RETURN_ERR( read_v( buf, n ) );
+ }
+ return blargg_ok;
+}
+
+blargg_err_t Data_Reader::skip( int n )
+{
+ assert( n >= 0 );
+
+ if ( n < 0 )
+ return blargg_err_caller;
+
+ if ( n <= 0 )
+ return blargg_ok;
+
+ if ( n > remain() )
+ return blargg_err_file_eof;
+
+ blargg_err_t err = skip_v( n );
+ if ( !err )
+ remain_ -= n;
+
+ return err;
+}
+
+
+// File_Reader
+
+blargg_err_t File_Reader::seek( int n )
+{
+ assert( n >= 0 );
+
+ if ( n < 0 )
+ return blargg_err_caller;
+
+ if ( n == tell() )
+ return blargg_ok;
+
+ if ( n > size() )
+ return blargg_err_file_eof;
+
+ blargg_err_t err = seek_v( n );
+ if ( !err )
+ set_tell( n );
+
+ return err;
+}
+
+blargg_err_t File_Reader::skip_v( int n )
+{
+ return seek_v( tell() + n );
+}
+
+
+// Subset_Reader
+
+Subset_Reader::Subset_Reader( Data_Reader* dr, int size ) :
+ in( dr )
+{
+ set_remain( min( size, dr->remain() ) );
+}
+
+blargg_err_t Subset_Reader::read_v( void* p, int s )
+{
+ return in->read( p, s );
+}
+
+
+// Remaining_Reader
+
+Remaining_Reader::Remaining_Reader( void const* h, int size, Data_Reader* r ) :
+ in( r )
+{
+ header = h;
+ header_remain = size;
+
+ set_remain( size + r->remain() );
+}
+
+blargg_err_t Remaining_Reader::read_v( void* out, int count )
+{
+ int first = min( count, header_remain );
+ if ( first )
+ {
+ memcpy( out, header, first );
+ header = STATIC_CAST(char const*, header) + first;
+ header_remain -= first;
+ }
+
+ return in->read( STATIC_CAST(char*, out) + first, count - first );
+}
+
+
+// Mem_File_Reader
+
+Mem_File_Reader::Mem_File_Reader( const void* p, long s ) :
+ begin( STATIC_CAST(const char*, p) )
+{
+ set_size( s );
+}
+
+blargg_err_t Mem_File_Reader::read_v( void* p, int s )
+{
+ memcpy( p, begin + tell(), s );
+ return blargg_ok;
+}
+
+blargg_err_t Mem_File_Reader::seek_v( int )
+{
+ return blargg_ok;
+}
+
+
+// Callback_Reader
+
+Callback_Reader::Callback_Reader( callback_t c, long s, void* d ) :
+ callback( c ),
+ user_data( d )
+{
+ set_remain( s );
+}
+
+blargg_err_t Callback_Reader::read_v( void* out, int count )
+{
+ return callback( user_data, out, count );
+}
+
+
+// Callback_File_Reader
+
+Callback_File_Reader::Callback_File_Reader( callback_t c, long s, void* d ) :
+ callback( c ),
+ user_data( d )
+{
+ set_size( s );
+}
+
+blargg_err_t Callback_File_Reader::read_v( void* out, int count )
+{
+ return callback( user_data, out, count, tell() );
+}
+
+blargg_err_t Callback_File_Reader::seek_v( int )
+{
+ return blargg_ok;
+}
+
+
+// BLARGG_UTF8_PATHS
+
+#if BLARGG_UTF8_PATHS
+
+// Thanks to byuu for the idea for BLARGG_UTF8_PATHS and the implementations
+
+BLARGG_NAMESPACE_BEGIN
+
+// Converts wide-character path to UTF-8. Free result with free(). Only supported on Windows.
+char* blargg_to_utf8( const wchar_t* wpath )
+{
+ if ( wpath == NULL )
+ return NULL;
+
+ int needed = WideCharToMultiByte( CP_UTF8, 0, wpath, -1, NULL, 0, NULL, NULL );
+ if ( needed <= 0 )
+ return NULL;
+
+ char* path = (char*) malloc( needed );
+ if ( path == NULL )
+ return NULL;
+
+ int actual = WideCharToMultiByte( CP_UTF8, 0, wpath, -1, path, needed, NULL, NULL );
+ if ( actual == 0 )
+ {
+ free( path );
+ return NULL;
+ }
+
+ assert( actual == needed );
+ return path;
+}
+
+// Converts UTF-8 path to wide-character. Free result with free() Only supported on Windows.
+wchar_t* blargg_to_wide( const char* path )
+{
+ if ( path == NULL )
+ return NULL;
+
+ int needed = MultiByteToWideChar( CP_UTF8, 0, path, -1, NULL, 0 );
+ if ( needed <= 0 )
+ return NULL;
+
+ wchar_t* wpath = (wchar_t*) malloc( needed * sizeof *wpath );
+ if ( wpath == NULL )
+ return NULL;
+
+ int actual = MultiByteToWideChar( CP_UTF8, 0, path, -1, wpath, needed );
+ if ( actual == 0 )
+ {
+ free( wpath );
+ return NULL;
+ }
+
+ assert( actual == needed );
+ return wpath;
+}
+
+BLARGG_NAMESPACE_END
+
+static FILE* blargg_fopen( const char path [], const char mode [] )
+{
+ FILE* file = NULL;
+ wchar_t* wmode = NULL;
+ wchar_t* wpath = NULL;
+
+ wpath = blargg_to_wide( path );
+ if ( wpath )
+ {
+ wmode = blargg_to_wide( mode );
+ if ( wmode )
+ file = _wfopen( wpath, wmode );
+ }
+
+ // Save and restore errno in case free() clears it
+ int saved_errno = errno;
+ free( wmode );
+ free( wpath );
+ errno = saved_errno;
+
+ return file;
+}
+
+#else
+
+static inline FILE* blargg_fopen( const char path [], const char mode [] )
+{
+ return fopen( path, mode );
+}
+
+#endif
+
+
+// Std_File_Reader
+
+Std_File_Reader::Std_File_Reader()
+{
+ file_ = NULL;
+}
+
+Std_File_Reader::~Std_File_Reader()
+{
+ close();
+}
+
+static blargg_err_t blargg_fopen( FILE** out, const char path [] )
+{
+ errno = 0;
+ *out = blargg_fopen( path, "rb" );
+ if ( !*out )
+ {
+ #ifdef ENOENT
+ if ( errno == ENOENT )
+ return blargg_err_file_missing;
+ #endif
+ #ifdef ENOMEM
+ if ( errno == ENOMEM )
+ return blargg_err_memory;
+ #endif
+ return blargg_err_file_read;
+ }
+
+ return blargg_ok;
+}
+
+static blargg_err_t blargg_fsize( FILE* f, long* out )
+{
+ if ( fseek( f, 0, SEEK_END ) )
+ return blargg_err_file_io;
+
+ *out = ftell( f );
+ if ( *out < 0 )
+ return blargg_err_file_io;
+
+ if ( fseek( f, 0, SEEK_SET ) )
+ return blargg_err_file_io;
+
+ return blargg_ok;
+}
+
+blargg_err_t Std_File_Reader::open( const char path [] )
+{
+ close();
+
+ FILE* f;
+ RETURN_ERR( blargg_fopen( &f, path ) );
+
+ long s;
+ blargg_err_t err = blargg_fsize( f, &s );
+ if ( err )
+ {
+ fclose( f );
+ return err;
+ }
+
+ file_ = f;
+ set_size( s );
+
+ return blargg_ok;
+}
+
+void Std_File_Reader::make_unbuffered()
+{
+ if ( setvbuf( STATIC_CAST(FILE*, file_), NULL, _IONBF, 0 ) )
+ check( false ); // shouldn't fail, but OK if it does
+}
+
+blargg_err_t Std_File_Reader::read_v( void* p, int s )
+{
+ if ( (size_t) s != fread( p, 1, s, STATIC_CAST(FILE*, file_) ) )
+ {
+ // Data_Reader's wrapper should prevent EOF
+ check( !feof( STATIC_CAST(FILE*, file_) ) );
+
+ return blargg_err_file_io;
+ }
+
+ return blargg_ok;
+}
+
+blargg_err_t Std_File_Reader::seek_v( int n )
+{
+ if ( fseek( STATIC_CAST(FILE*, file_), n, SEEK_SET ) )
+ {
+ // Data_Reader's wrapper should prevent EOF
+ check( !feof( STATIC_CAST(FILE*, file_) ) );
+
+ return blargg_err_file_io;
+ }
+
+ return blargg_ok;
+}
+
+void Std_File_Reader::close()
+{
+ if ( file_ )
+ {
+ fclose( STATIC_CAST(FILE*, file_) );
+ file_ = NULL;
+ }
+}
+
+
+// Gzip_File_Reader
+
+#ifdef HAVE_ZLIB_H
+
+#include "zlib.h"
+
+static const char* get_gzip_eof( const char path [], long* eof )
+{
+ FILE* file;
+ RETURN_ERR( blargg_fopen( &file, path ) );
+
+ int const h_size = 4;
+ unsigned char h [h_size];
+
+ // read four bytes to ensure that we can seek to -4 later
+ if ( fread( h, 1, h_size, file ) != (size_t) h_size || h[0] != 0x1F || h[1] != 0x8B )
+ {
+ // Not gzipped
+ if ( ferror( file ) )
+ return blargg_err_file_io;
+
+ if ( fseek( file, 0, SEEK_END ) )
+ return blargg_err_file_io;
+
+ *eof = ftell( file );
+ if ( *eof < 0 )
+ return blargg_err_file_io;
+ }
+ else
+ {
+ // Gzipped; get uncompressed size from end
+ if ( fseek( file, -h_size, SEEK_END ) )
+ return blargg_err_file_io;
+
+ if ( fread( h, 1, h_size, file ) != (size_t) h_size )
+ return blargg_err_file_io;
+
+ *eof = get_le32( h );
+ }
+
+ if ( fclose( file ) )
+ check( false );
+
+ return blargg_ok;
+}
+
+Gzip_File_Reader::Gzip_File_Reader()
+{
+ file_ = NULL;
+}
+
+Gzip_File_Reader::~Gzip_File_Reader()
+{
+ close();
+}
+
+blargg_err_t Gzip_File_Reader::open( const char path [] )
+{
+ close();
+
+ long s;
+ RETURN_ERR( get_gzip_eof( path, &s ) );
+
+ file_ = gzopen( path, "rb" );
+ if ( !file_ )
+ return blargg_err_file_read;
+
+ set_size( s );
+ return blargg_ok;
+}
+
+static blargg_err_t convert_gz_error( gzFile file )
+{
+ int err;
+ gzerror( file, &err );
+
+ switch ( err )
+ {
+ case Z_STREAM_ERROR: break;
+ case Z_DATA_ERROR: return blargg_err_file_corrupt;
+ case Z_MEM_ERROR: return blargg_err_memory;
+ case Z_BUF_ERROR: break;
+ }
+ return blargg_err_internal;
+}
+
+blargg_err_t Gzip_File_Reader::read_v( void* p, int s )
+{
+ int result = gzread( file_, p, s );
+ if ( result != s )
+ {
+ if ( result < 0 )
+ return convert_gz_error( file_ );
+
+ return blargg_err_file_corrupt;
+ }
+
+ return blargg_ok;
+}
+
+blargg_err_t Gzip_File_Reader::seek_v( int n )
+{
+ if ( gzseek( file_, n, SEEK_SET ) < 0 )
+ return convert_gz_error( file_ );
+
+ return blargg_ok;
+}
+
+void Gzip_File_Reader::close()
+{
+ if ( file_ )
+ {
+ if ( gzclose( file_ ) )
+ check( false );
+ file_ = NULL;
+ }
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.h b/plugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.h
new file mode 100755
index 00000000..6150add6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/backup/Data_Reader.h
@@ -0,0 +1,268 @@
+// Lightweight interface for reading data from byte stream
+
+// File_Extractor $vers
+#ifndef DATA_READER_H
+#define DATA_READER_H
+
+#include "blargg_common.h"
+
+BLARGG_NAMESPACE_BEGIN
+
+/* Some functions accept a long instead of int for convenience where caller has
+a long due to some other interface, and would otherwise have to get a warning,
+or cast it (and verify that it wasn't outside the range of an int).
+
+To really support huge (>2GB) files, long isn't a solution, since there's no
+guarantee it's more than 32 bits. We'd need to use long long (if available), or
+something compiler-specific, and change all places file sizes or offsets are
+used. */
+
+// Supports reading and finding out how many bytes are remaining
+class Data_Reader {
+public:
+
+ // Reads min(*n,remain()) bytes and sets *n to this number, thus trying to read more
+ // tham remain() bytes doesn't result in error, just *n being set to remain().
+ blargg_err_t read_avail( void* p, int* n );
+ blargg_err_t read_avail( void* p, long* n );
+
+ // Reads exactly n bytes, or returns error if they couldn't ALL be read.
+ // Reading past end of file results in blargg_err_file_eof.
+ blargg_err_t read( void* p, int n );
+
+ // Number of bytes remaining until end of file
+ int remain() const { return remain_; }
+
+ // Reads and discards n bytes. Skipping past end of file results in blargg_err_file_eof.
+ blargg_err_t skip( int n );
+
+ virtual ~Data_Reader() { }
+
+private:
+ // noncopyable
+ Data_Reader( const Data_Reader& );
+ Data_Reader& operator = ( const Data_Reader& );
+
+// Derived interface
+protected:
+ Data_Reader() : remain_( 0 ) { }
+
+ // Sets remain
+ void set_remain( int n ) { assert( n >= 0 ); remain_ = n; }
+
+ // Do same as read(). Guaranteed that 0 < n <= remain(). Value of remain() is updated
+ // AFTER this call succeeds, not before. set_remain() should NOT be called from this.
+ virtual blargg_err_t read_v( void*, int n ) BLARGG_PURE( { (void)n; return blargg_ok; } )
+
+ // Do same as skip(). Guaranteed that 0 < n <= remain(). Default just reads data
+ // and discards it. Value of remain() is updated AFTER this call succeeds, not
+ // before. set_remain() should NOT be called from this.
+ virtual blargg_err_t skip_v( int n );
+
+// Implementation
+public:
+ BLARGG_DISABLE_NOTHROW
+
+private:
+ int remain_;
+};
+
+
+// Supports seeking in addition to Data_Reader operations
+class File_Reader : public Data_Reader {
+public:
+
+ // Size of file
+ int size() const { return size_; }
+
+ // Current position in file
+ int tell() const { return size_ - remain(); }
+
+ // Goes to new position
+ blargg_err_t seek( int );
+
+// Derived interface
+protected:
+ // Sets size and resets position
+ void set_size( int n ) { size_ = n; Data_Reader::set_remain( n ); }
+ void set_size( long n ) { set_size( STATIC_CAST(int, n) ); }
+
+ // Sets reported position
+ void set_tell( int i ) { assert( 0 <= i && i <= size_ ); Data_Reader::set_remain( size_ - i ); }
+
+ // Do same as seek(). Guaranteed that 0 <= n <= size(). Value of tell() is updated
+ // AFTER this call succeeds, not before. set_* functions should NOT be called from this.
+ virtual blargg_err_t seek_v( int n ) BLARGG_PURE( { (void)n; return blargg_ok; } )
+
+// Implementation
+protected:
+ File_Reader() : size_( 0 ) { }
+
+ virtual blargg_err_t skip_v( int );
+
+private:
+ int size_;
+
+ void set_remain(); // avoid accidental use of set_remain
+};
+
+
+// Reads from file on disk
+class Std_File_Reader : public File_Reader {
+public:
+
+ // Opens file
+ blargg_err_t open( const char path [] );
+
+ // Closes file if one was open
+ void close();
+
+ // Switches to unbuffered mode. Useful if buffering is already being
+ // done at a higher level.
+ void make_unbuffered();
+
+// Implementation
+public:
+ Std_File_Reader();
+ virtual ~Std_File_Reader();
+
+protected:
+ virtual blargg_err_t read_v( void*, int );
+ virtual blargg_err_t seek_v( int );
+
+private:
+ void* file_;
+};
+
+
+// Treats range of memory as a file
+class Mem_File_Reader : public File_Reader {
+public:
+
+ Mem_File_Reader( const void* begin, long size );
+
+// Implementation
+protected:
+ virtual blargg_err_t read_v( void*, int );
+ virtual blargg_err_t seek_v( int );
+
+private:
+ const char* const begin;
+};
+
+
+// Allows only count bytes to be read from reader passed
+class Subset_Reader : public Data_Reader {
+public:
+
+ Subset_Reader( Data_Reader*, int count );
+
+// Implementation
+protected:
+ virtual blargg_err_t read_v( void*, int );
+
+private:
+ Data_Reader* const in;
+};
+
+
+// Joins already-read header and remaining data into original file.
+// Meant for cases where you've already read header and don't want
+// to seek and re-read data (for efficiency).
+class Remaining_Reader : public Data_Reader {
+public:
+
+ Remaining_Reader( void const* header, int header_size, Data_Reader* );
+
+// Implementation
+protected:
+ virtual blargg_err_t read_v( void*, int );
+
+private:
+ Data_Reader* const in;
+ void const* header;
+ int header_remain;
+};
+
+
+// Invokes callback function to read data
+extern "C" { // necessary to be usable from C
+ typedef const char* (*callback_reader_func_t)(
+ void* user_data, // Same value passed to constructor
+ void* out, // Buffer to place data into
+ int count // Number of bytes to read
+ );
+}
+class Callback_Reader : public Data_Reader {
+public:
+ typedef callback_reader_func_t callback_t;
+ Callback_Reader( callback_t, long size, void* user_data );
+
+// Implementation
+protected:
+ virtual blargg_err_t read_v( void*, int );
+
+private:
+ callback_t const callback;
+ void* const user_data;
+};
+
+
+// Invokes callback function to read data
+extern "C" { // necessary to be usable from C
+ typedef const char* (*callback_file_reader_func_t)(
+ void* user_data, // Same value passed to constructor
+ void* out, // Buffer to place data into
+ int count, // Number of bytes to read
+ int pos // Position in file to read from
+ );
+}
+class Callback_File_Reader : public File_Reader {
+public:
+ typedef callback_file_reader_func_t callback_t;
+ Callback_File_Reader( callback_t, long size, void* user_data );
+
+// Implementation
+protected:
+ virtual blargg_err_t read_v( void*, int );
+ virtual blargg_err_t seek_v( int );
+
+private:
+ callback_t const callback;
+ void* const user_data;
+};
+
+
+#ifdef HAVE_ZLIB_H
+
+// Reads file compressed with gzip (or uncompressed)
+class Gzip_File_Reader : public File_Reader {
+public:
+
+ // Opens possibly gzipped file
+ blargg_err_t open( const char path [] );
+
+ // Closes file if one was open
+ void close();
+
+// Implementation
+public:
+ Gzip_File_Reader();
+ ~Gzip_File_Reader();
+
+protected:
+ virtual blargg_err_t read_v( void*, int );
+ virtual blargg_err_t seek_v( int );
+
+private:
+ // void* so "zlib.h" doesn't have to be included here
+ void* file_;
+};
+#endif
+
+char* blargg_to_utf8( const wchar_t* );
+wchar_t* blargg_to_wide( const char* );
+
+BLARGG_NAMESPACE_END
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/blargg_common.cpp b/plugins/gme/game-music-emu-0.6pre/gme/blargg_common.cpp
new file mode 100644
index 00000000..91d2c882
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/blargg_common.cpp
@@ -0,0 +1,58 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "blargg_common.h"
+
+/* Copyright (C) 2008-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+BLARGG_NAMESPACE_BEGIN
+
+// defined here to avoid need for blargg_errors.cpp in simple programs
+blargg_err_def_t blargg_err_memory = BLARGG_ERR_MEMORY;
+
+void blargg_vector_::init()
+{
+ begin_ = NULL;
+ size_ = 0;
+}
+
+void blargg_vector_::clear()
+{
+ void* p = begin_;
+ begin_ = NULL;
+ size_ = 0;
+ free( p );
+}
+
+blargg_err_t blargg_vector_::resize_( size_t n, size_t elem_size )
+{
+ if ( n != size_ )
+ {
+ if ( n == 0 )
+ {
+ // Simpler to handle explicitly. Realloc will handle a size of 0,
+ // but then we have to avoid raising an error for a NULL return.
+ clear();
+ }
+ else
+ {
+ void* p = realloc( begin_, n * elem_size );
+ CHECK_ALLOC( p );
+ begin_ = p;
+ size_ = n;
+ }
+ }
+ return blargg_ok;
+}
+
+BLARGG_NAMESPACE_END
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/blargg_common.h b/plugins/gme/game-music-emu-0.6pre/gme/blargg_common.h
new file mode 100644
index 00000000..44e6d39f
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/blargg_common.h
@@ -0,0 +1,224 @@
+// Sets up common environment for Shay Green's libraries.
+// To change configuration options, modify blargg_config.h, not this file.
+
+// Game_Music_Emu 0.6-pre
+#ifndef BLARGG_COMMON_H
+#define BLARGG_COMMON_H
+
+#include <stdlib.h>
+#include <assert.h>
+#include <limits.h>
+
+typedef const char* blargg_err_t; // 0 on success, otherwise error string
+
+// Success; no error
+int const blargg_ok = 0;
+
+// BLARGG_RESTRICT: equivalent to C99's restrict, where supported
+#if __GNUC__ >= 3 || _MSC_VER >= 1100
+ #define BLARGG_RESTRICT __restrict
+#else
+ #define BLARGG_RESTRICT
+#endif
+
+#if __cplusplus >= 199711
+ #define BLARGG_MUTABLE mutable
+#else
+ #define BLARGG_MUTABLE
+#endif
+
+/* BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant).
+I don't just use 'abcd' because that's implementation-dependent. */
+#define BLARGG_4CHAR( a, b, c, d ) \
+ ((a&0xFF)*0x1000000 + (b&0xFF)*0x10000 + (c&0xFF)*0x100 + (d&0xFF))
+
+/* BLARGG_STATIC_ASSERT( expr ): Generates compile error if expr is 0.
+Can be used at file, function, or class scope. */
+#ifdef _MSC_VER
+ // MSVC6 (_MSC_VER < 1300) __LINE__ fails when /Zl is specified
+ #define BLARGG_STATIC_ASSERT( expr ) \
+ void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] )
+#else
+ // Others fail when declaring same function multiple times in class,
+ // so differentiate them by line
+ #define BLARGG_STATIC_ASSERT( expr ) \
+ void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] )
+#endif
+
+/* Pure virtual functions cause a vtable entry to a "called pure virtual"
+error handler, requiring linkage to the C++ runtime library. This macro is
+used in place of the "= 0", and simply expands to its argument. During
+development, it expands to "= 0", allowing detection of missing overrides. */
+#define BLARGG_PURE( def ) def
+
+/* My code depends on ASCII anywhere a character or string constant is
+compared with data read from a file, and anywhere file data is read and
+treated as a string. */
+#if '\n'!=0x0A || ' '!=0x20 || '0'!=0x30 || 'A'!=0x41 || 'a'!=0x61
+ #error "ASCII character set required"
+#endif
+
+/* My code depends on int being at least 32 bits. Almost everything these days
+uses at least 32-bit ints, so it's hard to even find a system with 16-bit ints
+to test with. The issue can't be gotten around by using a suitable blargg_int
+everywhere either, because int is often converted to implicitly when doing
+arithmetic on smaller types. */
+#if UINT_MAX < 0xFFFFFFFF
+ #error "int must be at least 32 bits"
+#endif
+
+// In case compiler doesn't support these properly. Used rarely.
+#define STATIC_CAST(T,expr) static_cast<T> (expr)
+#define CONST_CAST( T,expr) const_cast<T> (expr)
+
+// User configuration can override the above macros if necessary
+#include "blargg_config.h"
+
+#ifdef BLARGG_NAMESPACE
+ #define BLARGG_NAMESPACE_BEGIN namespace BLARGG_NAMESPACE {
+ #define BLARGG_NAMESPACE_END }
+
+ BLARGG_NAMESPACE_BEGIN
+ BLARGG_NAMESPACE_END
+ using namespace BLARGG_NAMESPACE;
+#else
+ #define BLARGG_NAMESPACE_BEGIN
+ #define BLARGG_NAMESPACE_END
+#endif
+
+BLARGG_NAMESPACE_BEGIN
+
+/* BLARGG_DEPRECATED [_TEXT] for any declarations/text to be removed in a
+future version. In GCC, we can let the compiler warn. In other compilers,
+we strip it out unless BLARGG_LEGACY is true. */
+#if BLARGG_LEGACY
+ // Allow old client code to work without warnings
+ #define BLARGG_DEPRECATED_TEXT( text ) text
+ #define BLARGG_DEPRECATED( text ) text
+#elif __GNUC__ >= 4
+ // In GCC, we can mark declarations and let the compiler warn
+ #define BLARGG_DEPRECATED_TEXT( text ) text
+ #define BLARGG_DEPRECATED( text ) __attribute__ ((deprecated)) text
+#else
+ // By default, deprecated items are removed, to avoid use in new code
+ #define BLARGG_DEPRECATED_TEXT( text )
+ #define BLARGG_DEPRECATED( text )
+#endif
+
+/* BOOST::int8_t, BOOST::int32_t, etc.
+I used BOOST since I originally was going to allow use of the boost library
+for prividing the definitions. If I'm defining them, they must be scoped or
+else they could conflict with the standard ones at global scope. Even if
+HAVE_STDINT_H isn't defined, I can't assume the typedefs won't exist at
+global scope already. */
+#if defined (HAVE_STDINT_H) || \
+ UCHAR_MAX != 0xFF || USHRT_MAX != 0xFFFF || UINT_MAX != 0xFFFFFFFF
+ #include <stdint.h>
+ #define BOOST
+#else
+ struct BOOST
+ {
+ typedef signed char int8_t;
+ typedef unsigned char uint8_t;
+ typedef short int16_t;
+ typedef unsigned short uint16_t;
+ typedef int int32_t;
+ typedef unsigned int uint32_t;
+ typedef __int64 int64_t;
+ typedef unsigned __int64 uint64_t;
+ };
+#endif
+
+/* My code is not written with exceptions in mind, so either uses new (nothrow)
+OR overrides operator new in my classes. The former is best since clients
+creating objects will get standard exceptions on failure, but that causes it
+to require the standard C++ library. So, when the client is using the C
+interface, I override operator new to use malloc. */
+
+// BLARGG_DISABLE_NOTHROW is put inside classes
+#ifndef BLARGG_DISABLE_NOTHROW
+ // throw spec mandatory in ISO C++ if NULL can be returned
+ #if __cplusplus >= 199711 || __GNUC__ >= 3 || _MSC_VER >= 1300
+ #define BLARGG_THROWS_NOTHING throw ()
+ #else
+ #define BLARGG_THROWS_NOTHING
+ #endif
+
+ #define BLARGG_DISABLE_NOTHROW \
+ void* operator new ( size_t s ) BLARGG_THROWS_NOTHING { return malloc( s ); }\
+ void operator delete( void* p ) BLARGG_THROWS_NOTHING { free( p ); }
+
+ #define BLARGG_NEW new
+#else
+ // BLARGG_NEW is used in place of new in library code
+ #include <new>
+ #define BLARGG_NEW new (std::nothrow)
+#endif
+
+ class blargg_vector_ {
+ protected:
+ void* begin_;
+ size_t size_;
+ void init();
+ blargg_err_t resize_( size_t n, size_t elem_size );
+ public:
+ size_t size() const { return size_; }
+ void clear();
+ };
+
+// Very lightweight vector for POD types (no constructor/destructor)
+template<class T>
+class blargg_vector : public blargg_vector_ {
+ union T_must_be_pod { T t; }; // fails if T is not POD
+public:
+ blargg_vector() { init(); }
+ ~blargg_vector() { clear(); }
+
+ blargg_err_t resize( size_t n ) { return resize_( n, sizeof (T) ); }
+
+ T* begin() { return static_cast<T*> (begin_); }
+ const T* begin() const { return static_cast<T*> (begin_); }
+
+ T* end() { return static_cast<T*> (begin_) + size_; }
+ const T* end() const { return static_cast<T*> (begin_) + size_; }
+
+ T& operator [] ( size_t n )
+ {
+ assert( n < size_ );
+ return static_cast<T*> (begin_) [n];
+ }
+
+ const T& operator [] ( size_t n ) const
+ {
+ assert( n < size_ );
+ return static_cast<T*> (begin_) [n];
+ }
+};
+
+// Callback function with user data.
+// blargg_callback<T> set_callback; // for user, this acts like...
+// void set_callback( T func, void* user_data = NULL ); // ...this
+// To call function, do set_callback.f( .. set_callback.data ... );
+template<class T>
+struct blargg_callback
+{
+ T f;
+ void* data;
+ blargg_callback() { f = NULL; }
+ void operator () ( T callback, void* user_data = NULL ) { f = callback; data = user_data; }
+};
+
+#ifndef _WIN32
+ // Not supported on any other platforms
+ #undef BLARGG_UTF8_PATHS
+#endif
+
+BLARGG_DEPRECATED( typedef signed int blargg_long; )
+BLARGG_DEPRECATED( typedef unsigned int blargg_ulong; )
+#if BLARGG_LEGACY
+ #define BOOST_STATIC_ASSERT BLARGG_STATIC_ASSERT
+#endif
+
+BLARGG_NAMESPACE_END
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/blargg_config.h b/plugins/gme/game-music-emu-0.6pre/gme/blargg_config.h
new file mode 100644
index 00000000..b3471d4f
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/blargg_config.h
@@ -0,0 +1,55 @@
+// Library configuration. Modify this file as necessary.
+
+// Game_Music_Emu 0.6-pre
+#ifndef BLARGG_CONFIG_H
+#define BLARGG_CONFIG_H
+
+// Uncomment a #define line below to have effect described.
+
+// Allow static linking with this library and one of my other libraries
+// in the same program.
+//#define BLARGG_NAMESPACE blargg_gme
+
+// Use zlib for transparent decompression of gzipped files.
+//#define HAVE_ZLIB_H
+
+// Support only listed music types. Remove a line to disable that type.
+/* #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_sgc_type,\
+ gme_spc_type,\
+ gme_vgm_type,\
+ gme_vgz_type
+*/
+
+// Enable platform-specific optimizations.
+//#define BLARGG_NONPORTABLE 1
+
+// Use faster sample rate convertor for SPC music.
+//#define GME_SPC_FAST_RESAMPLER 1
+
+// Use faster sample rate convertor for VGM and GYM music.
+//#define GME_VGM_FAST_RESAMPLER 1
+
+// Use faster, significantly lower quality sound synthesis for classic emulators.
+//#define BLIP_BUFFER_FAST 1
+
+// Reduce memory usage of gme.h by disabling gme_set_effects_config().
+//#define GME_DISABLE_EFFECTS 1
+
+// Force library to use assume big-endian processor.
+//#define BLARGG_BIG_ENDIAN 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.6pre/gme/blargg_endian.h b/plugins/gme/game-music-emu-0.6pre/gme/blargg_endian.h
new file mode 100644
index 00000000..423f7101
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/blargg_endian.h
@@ -0,0 +1,189 @@
+// CPU Byte Order Utilities
+
+// Game_Music_Emu 0.6-pre
+#ifndef BLARGG_ENDIAN_H
+#define BLARGG_ENDIAN_H
+
+#include "blargg_common.h"
+
+// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16)
+#if defined (__i386__) || defined (__x86_64__) || defined (_M_IX86) || defined (_M_X64)
+ #define BLARGG_CPU_X86 1
+ #define BLARGG_CPU_CISC 1
+#endif
+
+#if defined (__powerpc__) || defined (__ppc__) || defined (__ppc64__) || \
+ defined (__POWERPC__) || defined (__powerc)
+ #define BLARGG_CPU_POWERPC 1
+ #define BLARGG_CPU_RISC 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 <endian.h>
+ #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 (__sparc__) || BLARGG_CPU_POWERPC || \
+ (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321)
+ #define BLARGG_BIG_ENDIAN 1
+#elif !defined (__mips__)
+ // 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
+
+BLARGG_NAMESPACE_BEGIN
+
+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) ((unsigned char const*) p) [1] << 8 |
+ (unsigned) ((unsigned char const*) p) [0];
+}
+
+inline unsigned get_be16( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [0] << 8 |
+ (unsigned) ((unsigned char const*) p) [1];
+}
+
+inline unsigned get_le32( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [3] << 24 |
+ (unsigned) ((unsigned char const*) p) [2] << 16 |
+ (unsigned) ((unsigned char const*) p) [1] << 8 |
+ (unsigned) ((unsigned char const*) p) [0];
+}
+
+inline unsigned get_be32( void const* p )
+{
+ return (unsigned) ((unsigned char const*) p) [0] << 24 |
+ (unsigned) ((unsigned char const*) p) [1] << 16 |
+ (unsigned) ((unsigned char const*) p) [2] << 8 |
+ (unsigned) ((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, unsigned n )
+{
+ ((unsigned char*) p) [0] = (unsigned char) n;
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 16);
+ ((unsigned char*) p) [3] = (unsigned char) (n >> 24);
+}
+
+inline void set_be32( void* p, unsigned n )
+{
+ ((unsigned char*) p) [3] = (unsigned char) n;
+ ((unsigned char*) p) [2] = (unsigned char) (n >> 8);
+ ((unsigned char*) p) [1] = (unsigned char) (n >> 16);
+ ((unsigned char*) p) [0] = (unsigned char) (n >> 24);
+}
+
+#if BLARGG_NONPORTABLE
+ // Optimized implementation if byte order is known
+ #if BLARGG_LITTLE_ENDIAN
+ #define GET_LE16( addr ) (*(BOOST::uint16_t const*) (addr))
+ #define GET_LE32( addr ) (*(BOOST::uint32_t const*) (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 const*) (addr))
+ #define GET_BE32( addr ) (*(BOOST::uint32_t const*) (addr))
+ #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data))
+ #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data))
+
+ #if BLARGG_CPU_POWERPC
+ // PowerPC has special byte-reversed instructions
+ #if defined (__MWERKS__)
+ #define GET_LE16( addr ) (__lhbrx( addr, 0 ))
+ #define GET_LE32( addr ) (__lwbrx( addr, 0 ))
+ #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 ))
+ #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 ))
+ #elif defined (__GNUC__)
+ #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;})
+ #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;})
+ #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );})
+ #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );})
+ #endif
+ #endif
+ #endif
+#endif
+
+#ifndef GET_LE16
+ #define GET_LE16( addr ) get_le16( addr )
+ #define SET_LE16( addr, data ) set_le16( addr, data )
+#endif
+
+#ifndef GET_LE32
+ #define GET_LE32( addr ) get_le32( addr )
+ #define SET_LE32( addr, data ) set_le32( addr, data )
+#endif
+
+#ifndef GET_BE16
+ #define GET_BE16( addr ) get_be16( addr )
+ #define SET_BE16( addr, data ) set_be16( addr, data )
+#endif
+
+#ifndef GET_BE32
+ #define GET_BE32( addr ) get_be32( addr )
+ #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, unsigned 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, unsigned n ) { SET_BE32( p, n ); }
+inline unsigned get_le( BOOST::uint16_t const* p ) { return GET_LE16( p ); }
+inline unsigned get_le( BOOST::uint32_t const* p ) { return GET_LE32( p ); }
+inline unsigned get_be( BOOST::uint16_t const* p ) { return GET_BE16( p ); }
+inline unsigned get_be( BOOST::uint32_t const* p ) { return GET_BE32( p ); }
+
+BLARGG_NAMESPACE_END
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.cpp b/plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.cpp
new file mode 100644
index 00000000..66a5bb93
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.cpp
@@ -0,0 +1,117 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "blargg_errors.h"
+
+/* Copyright (C) 2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+BLARGG_NAMESPACE_BEGIN
+
+blargg_err_def_t blargg_err_generic = BLARGG_ERR_GENERIC;
+// blargg_err_memory is defined in blargg_common.cpp
+blargg_err_def_t blargg_err_caller = BLARGG_ERR_CALLER;
+blargg_err_def_t blargg_err_internal = BLARGG_ERR_INTERNAL;
+blargg_err_def_t blargg_err_limitation = BLARGG_ERR_LIMITATION;
+
+blargg_err_def_t blargg_err_file_missing = BLARGG_ERR_FILE_MISSING;
+blargg_err_def_t blargg_err_file_read = BLARGG_ERR_FILE_READ;
+blargg_err_def_t blargg_err_file_write = BLARGG_ERR_FILE_WRITE;
+blargg_err_def_t blargg_err_file_io = BLARGG_ERR_FILE_IO;
+blargg_err_def_t blargg_err_file_full = BLARGG_ERR_FILE_FULL;
+blargg_err_def_t blargg_err_file_eof = BLARGG_ERR_FILE_EOF;
+
+blargg_err_def_t blargg_err_file_type = BLARGG_ERR_FILE_TYPE;
+blargg_err_def_t blargg_err_file_feature = BLARGG_ERR_FILE_FEATURE;
+blargg_err_def_t blargg_err_file_corrupt = BLARGG_ERR_FILE_CORRUPT;
+
+const char* blargg_err_str( blargg_err_t err )
+{
+ if ( !err )
+ return "";
+
+ if ( *err == BLARGG_ERR_TYPE("")[0] )
+ return err + 1;
+
+ return err;
+}
+
+bool blargg_is_err_type( blargg_err_t err, const char type [] )
+{
+ if ( err )
+ {
+ // True if first strlen(type) characters of err match type
+ char const* p = err;
+ while ( *type && *type == *p )
+ {
+ type++;
+ p++;
+ }
+
+ if ( !*type )
+ return true;
+ }
+
+ return false;
+}
+
+const char* blargg_err_details( blargg_err_t err )
+{
+ const char* p = err;
+ if ( !p )
+ {
+ p = "";
+ }
+ else if ( *p == BLARGG_ERR_TYPE("")[0] )
+ {
+ while ( *p && *p != ';' )
+ p++;
+
+ // Skip ; and space after it
+ if ( *p )
+ {
+ p++;
+
+ check( *p == ' ' );
+ if ( *p )
+ p++;
+ }
+ }
+ return p;
+}
+
+int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const codes [] )
+{
+ if ( !err )
+ return 0;
+
+ while ( codes->str && !blargg_is_err_type( err, codes->str ) )
+ codes++;
+
+ return codes->code;
+}
+
+blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const codes [] )
+{
+ if ( !code )
+ return blargg_ok;
+
+ while ( codes->str && codes->code != code )
+ codes++;
+
+ if ( !codes->str )
+ return blargg_err_generic;
+
+ return codes->str;
+}
+
+BLARGG_NAMESPACE_END
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.h b/plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.h
new file mode 100644
index 00000000..e913bd5a
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/blargg_errors.h
@@ -0,0 +1,84 @@
+// Error strings and conversion functions
+
+// Game_Music_Emu 0.6-pre
+#ifndef BLARGG_ERRORS_H
+#define BLARGG_ERRORS_H
+
+#ifndef BLARGG_COMMON_H
+ #include "blargg_common.h"
+#endif
+
+BLARGG_NAMESPACE_BEGIN
+
+typedef const char blargg_err_def_t [];
+
+// Basic errors
+extern blargg_err_def_t blargg_err_generic;
+extern blargg_err_def_t blargg_err_memory;
+extern blargg_err_def_t blargg_err_caller;
+extern blargg_err_def_t blargg_err_internal;
+extern blargg_err_def_t blargg_err_limitation;
+
+// File low-level
+extern blargg_err_def_t blargg_err_file_missing; // not found
+extern blargg_err_def_t blargg_err_file_read;
+extern blargg_err_def_t blargg_err_file_write;
+extern blargg_err_def_t blargg_err_file_io;
+extern blargg_err_def_t blargg_err_file_full;
+extern blargg_err_def_t blargg_err_file_eof;
+
+// File high-level
+extern blargg_err_def_t blargg_err_file_type; // wrong file type
+extern blargg_err_def_t blargg_err_file_feature;
+extern blargg_err_def_t blargg_err_file_corrupt;
+
+// C string describing error, or "" if err == NULL
+const char* blargg_err_str( blargg_err_t err );
+
+// True iff error is of given type, or false if err == NULL
+bool blargg_is_err_type( blargg_err_t, const char type [] );
+
+// Details of error without describing main cause, or "" if err == NULL
+const char* blargg_err_details( blargg_err_t err );
+
+// Converts error string to integer code using mapping table. Calls blargg_is_err_type()
+// for each str and returns code on first match. Returns 0 if err == NULL.
+struct blargg_err_to_code_t {
+ const char* str;
+ int code;
+};
+int blargg_err_to_code( blargg_err_t err, blargg_err_to_code_t const [] );
+
+// Converts error code back to string. If code == 0, returns NULL. If not in table,
+// returns blargg_err_generic.
+blargg_err_t blargg_code_to_err( int code, blargg_err_to_code_t const [] );
+
+// Generates error string literal with details of cause
+#define BLARGG_ERR( type, str ) (type "; " str)
+
+// Extra space to make it clear when blargg_err_str() isn't called to get
+// printable version of error. At some point, I might prefix error strings
+// with a code, to speed conversion to a code.
+#define BLARGG_ERR_TYPE( str ) " " str
+
+// Error types to pass to BLARGG_ERR macro
+#define BLARGG_ERR_GENERIC BLARGG_ERR_TYPE( "operation failed" )
+#define BLARGG_ERR_MEMORY BLARGG_ERR_TYPE( "out of memory" )
+#define BLARGG_ERR_CALLER BLARGG_ERR_TYPE( "internal usage bug" )
+#define BLARGG_ERR_INTERNAL BLARGG_ERR_TYPE( "internal bug" )
+#define BLARGG_ERR_LIMITATION BLARGG_ERR_TYPE( "exceeded limitation" )
+
+#define BLARGG_ERR_FILE_MISSING BLARGG_ERR_TYPE( "file not found" )
+#define BLARGG_ERR_FILE_READ BLARGG_ERR_TYPE( "couldn't open file" )
+#define BLARGG_ERR_FILE_WRITE BLARGG_ERR_TYPE( "couldn't modify file" )
+#define BLARGG_ERR_FILE_IO BLARGG_ERR_TYPE( "read/write error" )
+#define BLARGG_ERR_FILE_FULL BLARGG_ERR_TYPE( "disk full" )
+#define BLARGG_ERR_FILE_EOF BLARGG_ERR_TYPE( "truncated file" )
+
+#define BLARGG_ERR_FILE_TYPE BLARGG_ERR_TYPE( "wrong file type" )
+#define BLARGG_ERR_FILE_FEATURE BLARGG_ERR_TYPE( "unsupported file feature" )
+#define BLARGG_ERR_FILE_CORRUPT BLARGG_ERR_TYPE( "corrupt file" )
+
+BLARGG_NAMESPACE_END
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/blargg_source.h b/plugins/gme/game-music-emu-0.6pre/gme/blargg_source.h
new file mode 100644
index 00000000..a195b9d7
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/blargg_source.h
@@ -0,0 +1,135 @@
+/* Included at the beginning of library source files, AFTER all other #include
+lines. Sets up helpful macros and services used in my source code. Since this
+is only "active" in my source code, I don't have to worry about polluting the
+global namespace with unprefixed names. */
+
+// Game_Music_Emu 0.6-pre
+#ifndef BLARGG_SOURCE_H
+#define BLARGG_SOURCE_H
+
+#ifndef BLARGG_COMMON_H // optimization only
+ #include "blargg_common.h"
+#endif
+#include "blargg_errors.h"
+
+#include <string.h> /* memcpy(), memset(), memmove() */
+#include <stddef.h> /* offsetof() */
+
+/* The following four macros are for debugging only. Some or all might be
+defined to do nothing, depending on the circumstances. Described is what
+happens when a particular macro is defined to do something. When defined to
+do nothing, the macros do NOT evaluate their argument(s). */
+
+/* If expr is false, prints file and line number, then aborts program. Meant
+for checking internal state and consistency. A failed assertion indicates a bug
+in MY code.
+
+void assert( bool expr ); */
+#include <assert.h>
+
+/* If expr is false, prints file and line number, then aborts program. Meant
+for checking caller-supplied parameters and operations that are outside the
+control of the module. A failed requirement probably indicates a bug in YOUR
+code.
+
+void require( bool expr ); */
+#undef require
+#define require( expr ) assert( expr )
+
+/* Like printf() except output goes to debugging console/file.
+
+void dprintf( const char format [], ... ); */
+#ifdef NDEBUG
+static inline void blargg_dprintf_( const char [], ... ) { }
+#undef dprintf
+#define dprintf (1) ? (void) 0 : blargg_dprintf_
+#else
+#include <stdarg.h>
+#include <stdio.h>
+static inline void blargg_dprintf_( const char * fmt, ... )
+{
+}
+#undef dprintf
+#define dprintf blargg_dprintf_
+#endif
+
+/* If expr is false, prints file and line number to debug console/log, then
+continues execution normally. Meant for flagging potential problems or things
+that should be looked into, but that aren't serious problems.
+
+void check( bool expr ); */
+#undef check
+#define check( expr ) ((void) 0)
+
+/* If expr yields non-NULL error string, returns it from current function,
+otherwise continues normally. */
+#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 NULL, returns out-of-memory error, otherwise continues normally. */
+#undef CHECK_ALLOC
+#define CHECK_ALLOC( ptr ) \
+ do {\
+ if ( !(ptr) )\
+ return blargg_err_memory;\
+ } while ( 0 )
+
+/* The usual min/max functions for built-in types.
+
+template<typename T> T min( T x, T y ) { return x < y ? x : y; }
+template<typename T> T max( T x, T y ) { return x > y ? x : y; } */
+#define BLARGG_DEF_MIN_MAX( type ) \
+ static inline type blargg_min( type x, type y ) { if ( y < x ) x = y; return x; }\
+ static inline type blargg_max( type x, type y ) { if ( x < y ) x = y; return x; }
+
+BLARGG_DEF_MIN_MAX( int )
+BLARGG_DEF_MIN_MAX( unsigned )
+BLARGG_DEF_MIN_MAX( long )
+BLARGG_DEF_MIN_MAX( unsigned long )
+BLARGG_DEF_MIN_MAX( float )
+BLARGG_DEF_MIN_MAX( double )
+
+#undef min
+#define min blargg_min
+
+#undef max
+#define max blargg_max
+
+// typedef unsigned char byte;
+typedef unsigned char blargg_byte;
+#undef byte
+#define byte blargg_byte
+
+#ifndef BLARGG_EXPORT
+ #if defined (_WIN32) && BLARGG_BUILD_DLL
+ #define BLARGG_EXPORT __declspec(dllexport)
+ #elif defined (__GNUC__)
+ // can always set visibility, even when not building DLL
+ #define BLARGG_EXPORT __attribute__ ((visibility ("default")))
+ #else
+ #define BLARGG_EXPORT
+ #endif
+#endif
+
+#if BLARGG_LEGACY
+ #define BLARGG_CHECK_ALLOC CHECK_ALLOC
+ #define BLARGG_RETURN_ERR RETURN_ERR
+#endif
+
+// Called after failed operation when overall operation may still complete OK.
+// Only used by unit testing framework.
+#undef ACK_FAILURE
+#define ACK_FAILURE() ((void)0)
+
+/* BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf etc.
+and check */
+#ifdef BLARGG_SOURCE_BEGIN
+ #include BLARGG_SOURCE_BEGIN
+#endif
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/divfix.h b/plugins/gme/game-music-emu-0.6pre/gme/divfix.h
new file mode 100644
index 00000000..21212df3
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/divfix.h
@@ -0,0 +1,18 @@
+
+static Uint32 DivFix(Uint32 p1, Uint32 p2, Uint32 fix)
+{
+ Uint32 ret;
+ ret = p1 / p2;
+ p1 = p1 % p2;/* p1 = p1 - p2 * ret; */
+ while (fix--)
+ {
+ p1 += p1;
+ ret += ret;
+ if (p1 >= p2)
+ {
+ p1 -= p2;
+ ret++;
+ }
+ }
+ return ret;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/emu2413.cpp b/plugins/gme/game-music-emu-0.6pre/gme/emu2413.cpp
new file mode 100644
index 00000000..8ab07be6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/emu2413.cpp
@@ -0,0 +1,742 @@
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications. To alter this software and redistribute it freely,
+// if the origin of this software is not misrepresented.
+
+// written by Mitsutaka Okazaki 2001
+// Modified by xodnizel to remove code not needed for the VRC7, among other things.
+// Optimized performance and code size - Shay Green
+
+// References:
+// fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development).
+// fmopl.c(fixed) -- (C) 2002 Jarek Burczynski.
+// s_opl.c -- 2001 written by Mamiya (NEZplug development).
+// fmgen.cpp -- 1999,2000 written by cisc.
+// fmpac.ill -- 2000 created by NARUTO.
+// MSX-Datapack
+// YMU757 data sheet
+// YM2143 data sheet
+
+#include <assert.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+#include "emu2413.h"
+
+#undef PI
+#define PI 3.14159265358979323846
+
+static const unsigned char default_inst[15][8] =
+{
+// VRC7 instruments, January 17, 2004 update -Xodnizel
+ {0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17},
+ {0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12},
+ {0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02},
+ {0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27},
+ {0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28},
+ {0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95},
+ {0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07},
+ {0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15},
+ {0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11},
+ {0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12},
+ {0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4},
+ {0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16},
+ {0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51},
+ {0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05},
+ {0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17},
+
+};
+
+#define EG2DB(d) ((d)*(e_int32)(EG_STEP/DB_STEP))
+#define TL2EG(d) ((d)*(e_int32)(TL_STEP/EG_STEP))
+#define SL2EG(d) ((d)*(e_int32)(SL_STEP/EG_STEP))
+
+// Bits for liner value
+#define DB2LIN_AMP_BITS 11
+#define SLOT_AMP_BITS (DB2LIN_AMP_BITS)
+
+// Bits for envelope phase incremental counter
+#define EG_DP_BITS 22
+#define EG_DP_WIDTH (1<<EG_DP_BITS)
+
+// PM table is calcurated by PM_AMP * pow(2,PM_DEPTH*sin(x)/1200)
+#define PM_AMP_BITS 8
+#define PM_AMP (1<<PM_AMP_BITS)
+
+// PM speed(Hz) and depth(cent)
+#define PM_SPEED 6.4
+#define PM_DEPTH 13.75
+
+// AM speed(Hz) and depth(dB)
+#define AM_SPEED 3.7
+//#define AM_DEPTH 4.8
+#define AM_DEPTH 2.4
+
+// Cut the lower b bit(s) off.
+#define HIGHBITS(c,b) ((c)>>(b))
+
+// Expand x which is s bits to d bits.
+#define EXPAND_BITS(x,s,d) ((x)<<((d)-(s)))
+
+#define CAR_OFFSET 1
+#define MOD_CAR( o, x, sel ) (&((o)->slot + (sel)) [(x) << 1])
+#define MOD(o,x) MOD_CAR( o, x, 0 )
+#define CAR(o,x) MOD_CAR( o, x, 1 )
+
+// Definition of envelope mode
+enum { SUSHOLD, SETTLE, ATTACK, DECAY, SUSTINE, RELEASE, FINISH };
+
+inline static void update_eg_dphase_inl ( OPLL* opll, OPLL_SLOT* slot, e_int32 slot_eg_mode )
+{
+ e_uint32 result = 0;
+ int index = slot->patch.RR;
+ switch ( slot_eg_mode )
+ {
+ case ATTACK:
+ result = opll->dphaseARTable[slot->patch.AR][slot->rks];
+ break;
+
+ case DECAY:
+ index = slot->patch.DR;
+ goto common;
+
+ case RELEASE:
+ if (slot->sustine)
+ index = 5;
+ else if (!slot->patch.EG)
+ index = 7;
+ case SUSTINE:
+ common:
+ result = opll->dphaseDRTable [index] [slot->rks];
+ case SUSHOLD:
+ case FINISH:
+ default:
+ break;
+ }
+
+ slot->eg_dphase = result;
+}
+
+// out = in, then verify that value wasn't truncated
+#define ASSIGN( out, in )\
+ ((out = in), assert( out == in ))
+
+static void maketables ( OPLL* opll )
+{
+ e_int32 i;
+
+ // Table for Pitch Modulator
+ for (i = 0; i < PM_PG_WIDTH; i++)
+ ASSIGN( opll->pmtable[i],
+ (e_int32) ((double) PM_AMP * pow (2, (double) PM_DEPTH * sin (2.0 * PI * i / PM_PG_WIDTH) / 1200)) );
+
+ // Table for Amp Modulator
+ for ( i = 0; i < AM_PG_WIDTH; i++)
+ ASSIGN( opll->amtable[i],
+ (e_int32) ((double) AM_DEPTH / 2 / DB_STEP * (1.0 + sin (2.0 * PI * i / PM_PG_WIDTH))) );
+
+ // Table for dB(0 -- (1<<DB_BITS)-1) to Liner(0 -- DB2LIN_AMP_WIDTH)
+ for (i = 0; i < DB_MUTE; i++)
+ ASSIGN( opll->DB2LIN_TABLE[i],
+ (e_int16) ((double) ((1 << DB2LIN_AMP_BITS) - 1) * pow (10, -(double) i * DB_STEP / 20)) );
+
+ // TODO: remove (calloc already ensures zero fill)
+ //for (i = DB_MUTE; i < 2 * DB_MUTE; i++)
+ // opll->DB2LIN_TABLE[i] = 0;
+
+ for (i = 0; i < 2 * DB_MUTE; i++)
+ ASSIGN( opll->DB2LIN_TABLE[i + DB_MUTE + DB_MUTE],
+ (e_int16) (-opll->DB2LIN_TABLE[i]) );
+
+ // Table for AR to LogCurve.
+ {
+ ASSIGN( opll->AR_ADJUST_TABLE[0], (1 << EG_BITS) );
+ for (int i = 1; i < 128; i++)
+ ASSIGN( opll->AR_ADJUST_TABLE[i],
+ (e_uint16) ((double) (1 << EG_BITS) - 1 - (1 << EG_BITS) * log ((double)i) / log (128.)) );
+ }
+
+ {
+ #define dB2(x) ((x)*2)
+ static const double kltable[16] = {
+ dB2 (0.000), dB2 (9.000), dB2 (12.000), dB2 (13.875), dB2 (15.000), dB2 (16.125), dB2 (16.875), dB2 (17.625),
+ dB2 (18.000), dB2 (18.750), dB2 (19.125), dB2 (19.500), dB2 (19.875), dB2 (20.250), dB2 (20.625), dB2 (21.000)
+ };
+
+ for (int fnum = 0; fnum < 16; fnum++)
+ {
+ for (int block = 0; block < 8; block++)
+ {
+ for (int TL = 0; TL < 64; TL++)
+ {
+ e_uint32 eg = TL2EG( TL );
+ ASSIGN( opll->tllTable[fnum][block][TL][0], eg );
+ for (int KL = 1; KL < 4; KL++)
+ {
+ e_int32 tmp = (e_int32) (kltable[fnum] - dB2 (3.000) * (7 - block));
+ e_uint32 n = eg;
+ if ( tmp > 0 )
+ n += (e_uint32) ((tmp >> (3 - KL)) / EG_STEP);
+ ASSIGN( opll->tllTable[fnum][block][TL][KL], n );
+ }
+ }
+ }
+ }
+ }
+
+ {
+ for (int fnum8 = 0; fnum8 < 2; fnum8++)
+ {
+ for (int block = 0; block < 8; block++)
+ {
+ ASSIGN( opll->rksTable[fnum8][block][0], block >> 1 );
+ for (int KR = 1; KR < 2; KR++)
+ ASSIGN( opll->rksTable[fnum8][block][KR], (block << 1) + fnum8 );
+ }
+ }
+ }
+
+ // Sin Table
+ for (i = 0; i < PG_WIDTH / 4; i++)
+ {
+ double d = sin (2.0 * PI / PG_WIDTH * i);
+
+ // Liner(+0.0 - +1.0) to dB((1<<DB_BITS) - 1 -- 0)
+ e_int32 x = DB_MUTE - 1;
+ if ( d )
+ {
+ e_int32 y = -(e_int32) (20.0 / DB_STEP * log10 (d));
+ if ( x > y )
+ x = y;
+ }
+
+ ASSIGN( opll->sintable [0] [i], (e_uint32) x );
+ }
+
+ for (i = 0; i < PG_WIDTH / 4; i++)
+ ASSIGN( opll->sintable [0] [PG_WIDTH / 2 - 1 - i], opll->sintable [0] [i] );
+
+ for (i = 0; i < PG_WIDTH / 2; i++)
+ ASSIGN( opll->sintable [0] [PG_WIDTH / 2 + i], (e_uint32) (DB_MUTE + DB_MUTE + opll->sintable [0] [i]) );
+
+ for (i = 0; i < PG_WIDTH / 2; i++)
+ ASSIGN( opll->sintable [1] [i], opll->sintable [0] [i] );
+
+ for (i = PG_WIDTH / 2; i < PG_WIDTH; i++)
+ ASSIGN( opll->sintable [1] [i], opll->sintable [0] [0] );
+
+ //makeDefaultPatch ();
+
+ // internal refresh
+
+ // Phase increment counter table
+ {
+ static int const mltable[16] = {
+ 1, 1 * 2, 2 * 2, 3 * 2, 4 * 2, 5 * 2, 6 * 2, 7 * 2, 8 * 2,
+ 9 * 2, 10 * 2, 10 * 2, 12 * 2, 12 * 2, 15 * 2, 15 * 2
+ };
+ for (int fnum = 0; fnum < 512; fnum++)
+ for (int block = 0; block < 8; block++)
+ for (int ML = 0; ML < 16; ML++)
+ ASSIGN( opll->dphaseTable[fnum][block][ML],
+ (((fnum * mltable[ML]) << block) >> (20 - DP_BITS)) );
+ }
+
+ // Rate Table for Attack
+ {
+ for (int Rks = 0; Rks < 16; Rks++)
+ {
+ // TODO: remove (calloc already ensures zero fill)
+ //opll->dphaseARTable[ 0][Rks] = 0;
+ //opll->dphaseARTable[15][Rks] = 0;
+ int RL = (Rks & 3) * 3 + 12;
+ for (int AR = 1; AR < 15; AR++)
+ {
+ int RM = AR + (Rks >> 2);
+ if (RM > 15)
+ RM = 15;
+ ASSIGN( opll->dphaseARTable[AR][Rks], RL << (RM + 1) );
+ }
+ }
+ }
+
+ // Rate Table for Decay and Release
+ {
+ for (int Rks = 0; Rks < 16; Rks++)
+ {
+ // TODO: remove (calloc already ensures zero fill)
+ //opll->dphaseDRTable[0][Rks] = 0;
+ int RL = (Rks & 3) + 4;
+ for (int DR = 1; DR < 16; DR++)
+ {
+ int RM = DR + (Rks >> 2);
+ if (RM > 15)
+ RM = 15;
+ ASSIGN( opll->dphaseDRTable[DR][Rks], RL << (RM - 1) );
+ }
+ }
+ }
+}
+
+OPLL* VRC7_new( long clock_rate )
+{
+ OPLL* opll = (OPLL*) calloc( sizeof *opll, 1 );
+ if ( opll )
+ {
+ maketables( opll );
+ ASSIGN( opll->pm_dphase, (e_uint32) (PM_SPEED * PM_DP_WIDTH / (clock_rate / 72)) );
+ ASSIGN( opll->am_dphase, (e_uint32) (AM_SPEED * AM_DP_WIDTH / (clock_rate / 72)) );
+
+ VRC7_reset (opll);
+ }
+ return opll;
+}
+
+void VRC7_delete( OPLL* opll )
+{
+ free (opll);
+}
+
+// Reset whole of OPLL except patch datas.
+void VRC7_reset( OPLL* opll )
+{
+ opll->pm_phase = opll->pm_dphase;
+ opll->am_phase = opll->am_dphase;
+
+ int i;
+ for ( i = 0; i < 12; i++)
+ {
+ OPLL_SLOT* slot = &opll->slot[i];
+ memset( slot, 0, offsetof (OPLL_SLOT,patch) );
+ slot->sintbl = opll->sintable [0];
+ slot->eg_mode = SETTLE;
+ slot->eg_phase = EG_DP_WIDTH;
+ }
+
+ for (i = 0; i < 0x40; i++)
+ VRC7_writeReg (opll, i, 0);
+}
+
+// Force Refresh (When external program changes some parameters).
+/*
+void VRC7_forceRefresh( OPLL* opll )
+{
+ for (e_int32 i = 0; i < 12; i++)
+ UPDATE_ALL( opll, &opll->slot[i], i & 1 );
+}
+*/
+
+// Convert Amp(0 to EG_HEIGHT) to Phase(0 to 2PI).
+#if ( SLOT_AMP_BITS - PG_BITS ) > 0
+#define wave2_2pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS ))
+#else
+#define wave2_2pi(e) ( (e) << ( PG_BITS - SLOT_AMP_BITS ))
+#endif
+
+// Convert Amp(0 to EG_HEIGHT) to Phase(0 to 4PI).
+#if ( SLOT_AMP_BITS - PG_BITS - 1 ) == 0
+#define wave2_4pi(e) (e)
+#elif ( SLOT_AMP_BITS - PG_BITS - 1 ) > 0
+#define wave2_4pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 1 ))
+#else
+#define wave2_4pi(e) ( (e) << ( 1 + PG_BITS - SLOT_AMP_BITS ))
+#endif
+
+// Convert Amp(0 to EG_HEIGHT) to Phase(0 to 8PI).
+#if ( SLOT_AMP_BITS - PG_BITS - 2 ) == 0
+#define wave2_8pi(e) (e)
+#elif ( SLOT_AMP_BITS - PG_BITS - 2 ) > 0
+#define wave2_8pi(e) ( (e) >> ( SLOT_AMP_BITS - PG_BITS - 2 ))
+#else
+#define wave2_8pi(e) ( (e) << ( 2 + PG_BITS - SLOT_AMP_BITS ))
+#endif
+
+void VRC7_run( OPLL* opll )
+{
+ // PM
+ int const opll_lfo_pm = opll->pmtable [HIGHBITS( opll->pm_phase, PM_DP_BITS - PM_PG_BITS )];
+ opll->pm_phase = (opll->pm_phase + opll->pm_dphase) & (PM_DP_WIDTH - 1);
+
+ {
+ int n = 12;
+ OPLL_SLOT* slot = opll->slot;
+ do
+ {
+ // phase
+ int step = slot->dphase;
+ if ( slot->patch.PM ) // 36%
+ step = (step * opll_lfo_pm) >> PM_AMP_BITS;
+
+ e_uint32 slot_phase = (slot->phase + step) & (DP_WIDTH - 1);
+ slot->phase = slot_phase;
+ slot->pgout = HIGHBITS( slot_phase, DP_BASE_BITS );
+
+ slot++;
+ }
+ while ( --n );
+ }
+
+ // AM
+ int const opll_lfo_am = opll->amtable [HIGHBITS( opll->am_phase, AM_DP_BITS - AM_PG_BITS )];
+ opll->am_phase = (opll->am_phase + opll->am_dphase) & (AM_DP_WIDTH - 1);
+
+ int n = 12;
+ OPLL_SLOT* slot = opll->slot;
+ do
+ {
+ // envelope
+ e_uint32 egout = HIGHBITS( slot->eg_phase, EG_DP_BITS - EG_BITS );
+
+ switch ( slot->eg_mode )
+ {
+ case SUSHOLD: // 54%
+ if ( slot->patch.EG ) // 99%
+ break;
+ slot->eg_mode = SUSTINE;
+ update_eg_dphase_inl( opll, slot, SUSTINE );
+ break;
+
+ case DECAY:{// 23%
+ #define S2E(x) (SL2EG((e_int32)(x/SL_STEP))<<(EG_DP_BITS-EG_BITS))
+ static const e_uint32 SL[16] = {
+ S2E (0.0), S2E (3.0), S2E (6.0), S2E (9.0), S2E (12.0), S2E (15.0), S2E (18.0), S2E (21.0),
+ S2E (24.0), S2E (27.0), S2E (30.0), S2E (33.0), S2E (36.0), S2E (39.0), S2E (42.0), S2E (48.0)
+ };
+ slot->eg_phase += slot->eg_dphase;
+ if ( slot->eg_phase >= SL [slot->patch.SL] )
+ {
+ slot->eg_phase = SL [slot->patch.SL];
+ if ( slot->patch.EG )
+ {
+ slot->eg_mode = SUSHOLD;
+ update_eg_dphase_inl( opll, slot, SUSHOLD );
+ }
+ else
+ {
+ slot->eg_mode = SUSTINE;
+ update_eg_dphase_inl( opll, slot, SUSTINE );
+ }
+ }
+ break;
+ }
+
+ case ATTACK: // 3%
+ egout = opll->AR_ADJUST_TABLE[egout];
+ slot->eg_phase += slot->eg_dphase;
+ if((EG_DP_WIDTH & slot->eg_phase)||(slot->patch.AR==15))
+ {
+ egout = 0;
+ slot->eg_phase = 0;
+ slot->eg_mode = DECAY;
+ update_eg_dphase_inl( opll, slot, DECAY );
+ }
+ break;
+
+ case SUSTINE:
+ case RELEASE: // 18%
+ slot->eg_phase += slot->eg_dphase;
+ if ( egout < (1 << EG_BITS) )
+ break;
+ slot->eg_mode = FINISH;
+ case FINISH: // 2%
+ default:
+ egout = (1 << EG_BITS) - 1;
+ break;
+ }
+
+ egout = EG2DB( egout + slot->tll );
+ if ( slot->patch.AM )
+ egout += opll_lfo_am;
+
+ if ( egout > DB_MUTE - 1 )
+ egout = DB_MUTE;
+
+ slot->egout = egout;
+
+ slot++;
+ }
+ while ( --n );
+}
+
+e_uint32 VRC7_calcCh( OPLL* opll, e_uint32 ch )
+{
+ OPLL_SLOT* slot = MOD( opll, ch );
+
+ // modulator
+ e_int32 feedback;
+ {
+ e_int32 fm = wave2_4pi( slot->feedback ) >> slot->patch.FB_shift;
+ int index = (slot->pgout + fm) & (PG_WIDTH - 1);
+ feedback = opll->DB2LIN_TABLE [slot->sintbl [index] + slot->egout];
+ assert( slot->egout < DB_MUTE || feedback == 0 ); // was DB_MUTE - 1 in original
+
+ e_int32 slot_output_1 = slot->output [0];
+
+ if ( slot [CAR_OFFSET].eg_mode == FINISH ) // 3%
+ return 0;
+
+ slot->output [0] = feedback;
+ slot->output [1] = slot_output_1;
+
+ feedback = (feedback + slot_output_1) >> 1;
+ slot->feedback = feedback;
+ }
+ slot += CAR_OFFSET;
+
+ // carrier
+ e_int32 output = opll->DB2LIN_TABLE [
+ slot->sintbl [(slot->pgout + wave2_8pi( feedback )) & (PG_WIDTH-1)] + slot->egout];
+ assert( slot->egout < DB_MUTE || output == 0 ); // was DB_MUTE - 1 in original
+
+ e_int32 slot_output_1 = slot->output [0];
+ slot->output [0] = output;
+ slot->output [1] = slot_output_1;
+
+ return (output + slot_output_1) >> 1;
+}
+
+static void setInstrument( OPLL* opll, unsigned i, unsigned inst )
+{
+ opll->patch_number[i]=inst;
+
+ const e_uint8* src = opll->CustInst;
+ if(inst)
+ src=default_inst[inst-1];
+
+ OPLL_PATCH* modp=&MOD(opll,i)->patch;
+ OPLL_PATCH* carp=&CAR(opll,i)->patch;
+
+ int src_0 = src [0];
+ modp->AM=(src_0>>7)&1;
+ modp->PM=(src_0>>6)&1;
+ modp->EG=(src_0>>5&1);
+ modp->KR=(src_0>>4)&1;
+ modp->ML=(src_0&0xF);
+
+ int src_1 = src [1];
+ carp->AM=(src_1>>7)&1;
+ carp->PM=(src_1>>6)&1;
+ carp->EG=(src_1>>5&1);
+ carp->KR=(src_1>>4)&1;
+ carp->ML=(src_1&0xF);
+
+ int src_2 = src [2];
+ modp->KL=(src_2>>6)&3;
+ modp->TL=(src_2&0x3F);
+
+ int src_3 = src [3];
+ carp->KL = (src_3 >> 6) & 3;
+ carp->WF = (src_3 >> 4) & 1;
+
+ modp->WF = (src_3 >> 3) & 1;
+
+ int FB = (src_3) & 7;
+ modp->FB_shift = (FB ? 7 - FB : 31);
+
+ int src_4 = src [4];
+ modp->AR = (src_4>>4)&0xF;
+ modp->DR = (src_4&0xF);
+
+ int src_5 = src [5];
+ carp->AR = (src_5>>4)&0xF;
+ carp->DR = (src_5&0xF);
+
+ int src_6 = src [6];
+ modp->SL = (src_6>>4)&0xF;
+ modp->RR = (src_6&0xF);
+
+ int src_7 = src [7];
+ carp->SL = (src_7>>4)&0xF;
+ carp->RR = (src_7&0xF);
+}
+
+static void update_eg_dphase( OPLL* opll, OPLL_SLOT* slot )
+{
+ update_eg_dphase_inl( opll, slot, slot->eg_mode );
+}
+
+#define UPDATE_PG(S) (S)->dphase = opll->dphaseTable[(S)->fnum][(S)->block][(S)->patch.ML]
+#define UPDATE_RKS(S) (S)->rks = opll->rksTable[((S)->fnum)>>8][(S)->block][(S)->patch.KR]
+#define UPDATE_WF(S) (S)->sintbl = opll->sintable [(S)->patch.WF]
+#define UPDATE_EG(S) update_eg_dphase( opll, S )
+
+inline static void UPDATE_TLL( OPLL* opll, OPLL_SLOT* S, int type )
+{
+ int index = (type ? (S)->volume : (S)->patch.TL);
+ (S)->tll = opll->tllTable [(S)->fnum >> 5] [(S)->block] [index] [(S)->patch.KL];
+}
+
+static void UPDATE_ALL( OPLL* opll, OPLL_SLOT* S, int type )
+{
+ UPDATE_PG(S);
+ UPDATE_TLL(opll, S, type);
+ UPDATE_RKS(S);
+ UPDATE_WF(S);
+ UPDATE_EG(S); // must be done last
+}
+
+static void UPDATE_ALL_BOTH( OPLL* opll, OPLL_SLOT* slot )
+{
+ UPDATE_ALL( opll, slot, 0 );
+ UPDATE_ALL( opll, slot + CAR_OFFSET, 1 );
+}
+
+inline static void slotOn( OPLL_SLOT* slot )
+{
+ slot->eg_mode = ATTACK;
+ slot->eg_phase = 0;
+ slot->phase = 0;
+}
+
+inline static void setFnumber( OPLL_SLOT* slot, e_int32 fnum )
+{
+ slot [ 0].fnum = fnum;
+ slot [CAR_OFFSET].fnum = fnum;
+}
+
+inline static void setBlock( OPLL_SLOT* slot, e_int32 block )
+{
+ slot [ 0].block = block;
+ slot [CAR_OFFSET].block = block;
+}
+
+void VRC7_writeReg( OPLL* opll, int addr, e_uint32 data )
+{
+ e_int32 i;
+
+ data &= 0xFF;
+ addr &= 0x3F;
+
+ switch ( addr )
+ {
+ case 0x00:
+ case 0x01:
+ opll->CustInst[addr]=data;
+ for (i = 0; i < 6; i++)
+ {
+ if (opll->patch_number[i] == 0)
+ {
+ setInstrument(opll, i, 0);
+ OPLL_SLOT* slot = MOD_CAR( opll, i, addr & 1 );
+ UPDATE_PG ( slot );
+ UPDATE_RKS( slot );
+ UPDATE_EG ( slot );
+ }
+ }
+ break;
+
+ case 0x02:
+ opll->CustInst[2]=data;
+ for (i = 0; i < 6; i++)
+ {
+ if (opll->patch_number[i] == 0)
+ {
+ setInstrument(opll, i, 0);
+ UPDATE_TLL(opll, MOD(opll,i), 0 );
+ }
+ }
+ break;
+
+ case 0x03:
+ opll->CustInst[3]=data;
+ for (i = 0; i < 6; i++)
+ {
+ if (opll->patch_number[i] == 0)
+ {
+ setInstrument(opll, i, 0);
+ UPDATE_WF(MOD(opll,i));
+ UPDATE_WF(CAR(opll,i));
+ }
+ }
+ break;
+
+ case 0x04:
+ case 0x05:
+ case 0x06:
+ case 0x07:
+ opll->CustInst[addr]=data;
+ for ( i = 0; i < 6; i++ )
+ {
+ if ( opll->patch_number [i] == 0 )
+ {
+ setInstrument( opll, i, 0 );
+ UPDATE_EG( MOD_CAR( opll, i, addr & 1 ) );
+ }
+ }
+ break;
+
+ case 0x10:
+ case 0x11:
+ case 0x12:
+ case 0x13:
+ case 0x14:
+ case 0x15: {
+ int ch = addr - 0x10;
+ opll->LowFreq [ch] = data;
+ OPLL_SLOT* slot = MOD( opll, ch );
+
+ setFnumber( slot, data + ((opll->HiFreq[ch] & 1) << 8));
+ UPDATE_ALL_BOTH( opll, slot );
+ break;
+ }
+
+ case 0x20:
+ case 0x21:
+ case 0x22:
+ case 0x23:
+ case 0x24:
+ case 0x25: {
+ int ch = addr - 0x20;
+ opll->HiFreq [ch] = data;
+ OPLL_SLOT* slot = MOD( opll, ch );
+
+ setFnumber( slot, ((data & 1) << 8) + opll->LowFreq[ch]);
+ setBlock( slot, (data >> 1) & 7);
+ slot [CAR_OFFSET].sustine = (data >> 5) & 1;
+ if (data & 0x10)
+ {
+ if ( !slot->slot_on_flag )
+ slotOn( slot );
+
+ if ( !slot [CAR_OFFSET].slot_on_flag )
+ slotOn( slot + CAR_OFFSET );
+ }
+ else if ( slot [CAR_OFFSET].slot_on_flag )
+ {
+ if (slot [CAR_OFFSET].eg_mode == ATTACK)
+ slot [CAR_OFFSET].eg_phase = EXPAND_BITS (opll->AR_ADJUST_TABLE[HIGHBITS (slot [CAR_OFFSET].eg_phase, EG_DP_BITS - EG_BITS)], EG_BITS, EG_DP_BITS);
+ slot [CAR_OFFSET].eg_mode = RELEASE;
+ }
+ UPDATE_ALL_BOTH( opll, slot );
+
+ //inline static void update_key_status (OPLL * opll)
+ for (ch = 0; ch < 6; ch++)
+ {
+ int flag = (opll->HiFreq [ch]) & 0x10;
+ OPLL_SLOT* slot = MOD( opll, ch );
+ slot [ 0].slot_on_flag = flag;
+ slot [CAR_OFFSET].slot_on_flag = flag;
+ }
+ break;
+ }
+
+ case 0x30:
+ case 0x31:
+ case 0x32:
+ case 0x33:
+ case 0x34:
+ case 0x35: {
+ int ch = addr - 0x30;
+ opll->InstVol [ch] = data;
+
+ setInstrument( opll, ch, data >> 4 & 0x0F );
+ OPLL_SLOT* slot = MOD( opll, ch );
+ slot [CAR_OFFSET].volume = (data & 0x0F) << 2;
+ UPDATE_ALL_BOTH( opll, slot );
+ break;
+ }
+
+ default:
+ break;
+ }
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/emu2413.h b/plugins/gme/game-music-emu-0.6pre/gme/emu2413.h
new file mode 100644
index 00000000..f479dbeb
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/emu2413.h
@@ -0,0 +1,143 @@
+#ifndef EMU2413_H
+#define EMU2413_H
+
+typedef unsigned char e_uint8;
+typedef signed short e_int16;
+typedef unsigned short e_uint16;
+typedef signed int e_int32;
+typedef unsigned int e_uint32;
+
+// Size of Sintable ( 8 -- 18 can be used. 9 recommended.)
+#define PG_BITS 9
+#define PG_WIDTH (1<<PG_BITS)
+
+// Phase increment counter
+#define DP_BITS 18
+#define DP_WIDTH (1<<DP_BITS)
+#define DP_BASE_BITS (DP_BITS - PG_BITS)
+
+// Dynamic range (Accuracy of sin table)
+#define DB_BITS 8
+#define DB_STEP (48.0/(1<<DB_BITS))
+#define DB_MUTE (1<<DB_BITS)
+
+// Dynamic range of envelope
+#define EG_STEP 0.375
+#define EG_BITS 7
+#define EG_MUTE (1<<EG_BITS)
+
+// Dynamic range of total level
+#define TL_STEP 0.75
+#define TL_BITS 6
+#define TL_MUTE (1<<TL_BITS)
+
+// Dynamic range of sustine level
+#define SL_STEP 3.0
+#define SL_BITS 4
+#define SL_MUTE (1<<SL_BITS)
+
+// Bits for Pitch and Amp modulator
+#define PM_PG_BITS 8
+#define PM_PG_WIDTH (1<<PM_PG_BITS)
+#define PM_DP_BITS 16
+#define PM_DP_WIDTH (1<<PM_DP_BITS)
+#define AM_PG_BITS 8
+#define AM_PG_WIDTH (1<<AM_PG_BITS)
+#define AM_DP_BITS 16
+#define AM_DP_WIDTH (1<<AM_DP_BITS)
+
+struct OPLL_PATCH
+{
+ e_uint32 TL,FB_shift,EG,ML,AR,DR,SL,RR,KR,KL,AM,PM,WF;
+};
+
+struct OPLL_SLOT
+{
+ // OUTPUT
+ e_int32 feedback;
+ e_int32 output[2]; // Output value of slot
+
+ // for Phase Generator (PG)
+ e_uint16 const* sintbl; // Wavetable
+ e_uint32 phase; // Phase
+ e_uint32 dphase; // Phase increment amount
+ e_uint32 pgout; // output
+
+ // for Envelope Generator (EG)
+ e_int32 fnum; // F-Number
+ e_int32 block; // Block
+ e_int32 volume; // Current volume
+ e_int32 sustine; // Sustine 1 = ON, 0 = OFF
+ e_uint32 tll; // Total Level + Key scale level
+ e_uint32 rks; // Key scale offset (Rks)
+ e_int32 eg_mode; // Current state
+ e_uint32 eg_phase; // Phase
+ e_uint32 eg_dphase; // Phase increment amount
+ e_uint32 egout; // output
+
+ OPLL_PATCH patch;
+ e_uint8 slot_on_flag;
+ e_uint8 filler [7];
+};
+
+#define OPLL_MASK_CH(x) (1<<(x))
+
+struct OPLL
+{
+ OPLL_SLOT slot [6 * 2];
+
+ // Register
+ e_uint8 LowFreq [6];
+ e_uint8 HiFreq [6];
+ e_uint8 InstVol [6];
+
+ // Channel Data
+ e_uint8 patch_number [6];
+
+ e_uint8 CustInst [8];
+
+ // LFO
+ e_uint32 pm_phase;
+ e_uint32 am_phase;
+
+ e_uint32 pm_dphase;
+ e_uint32 am_dphase;
+
+ e_int16 pmtable [PM_PG_WIDTH];
+ e_uint8 amtable [AM_PG_WIDTH];
+
+ e_uint16 sintable [2] [PG_WIDTH]; // [0] = full, [1] = half
+
+ // dB to Liner table
+ e_int16 DB2LIN_TABLE [4 * DB_MUTE];
+
+ // Liner to Log curve conversion table (for Attack rate).
+ e_uint8 AR_ADJUST_TABLE [1 << EG_BITS];
+
+ // Phase incr table for Attack
+ e_uint32 dphaseARTable [16] [16];
+
+ // Phase incr table for Decay and Release
+ e_uint32 dphaseDRTable [16] [16];
+
+ // KSL + TL Table
+ e_uint8 tllTable [16] [8] [1 << TL_BITS] [4];
+ e_uint8 rksTable [2] [8] [2];
+
+ // Phase incr table for PG
+ e_uint32 dphaseTable [512] [8] [16];
+};
+
+OPLL* VRC7_new( long clock_rate );
+void VRC7_delete( OPLL* );
+void VRC7_reset( OPLL* );
+
+void VRC7_writeReg( OPLL*, int addr, e_uint32 data );
+
+// Run for one clock
+void VRC7_run( OPLL* );
+
+// Generate sample for a channel
+e_uint32 VRC7_calcCh( OPLL*, e_uint32 channel );
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/fmopl.cpp b/plugins/gme/game-music-emu-0.6pre/gme/fmopl.cpp
new file mode 100644
index 00000000..9ec1dd46
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/fmopl.cpp
@@ -0,0 +1,2617 @@
+/*
+**
+** File: fmopl.c - software implementation of FM sound generator
+** types OPL and OPL2
+**
+** Copyright Jarek Burczynski (bujar at mame dot net)
+** Copyright Tatsuyuki Satoh , MultiArcadeMachineEmulator development
+**
+** Version 0.72
+**
+
+Revision History:
+
+04-08-2003 Jarek Burczynski:
+ - removed BFRDY hack. BFRDY is busy flag, and it should be 0 only when the chip
+ handles memory read/write or during the adpcm synthesis when the chip
+ requests another byte of ADPCM data.
+
+24-07-2003 Jarek Burczynski:
+ - added a small hack for Y8950 status BFRDY flag (bit 3 should be set after
+ some (unknown) delay). Right now it's always set.
+
+14-06-2003 Jarek Burczynski:
+ - implemented all of the status register flags in Y8950 emulation
+ - renamed y8950_set_delta_t_memory() parameters from _rom_ to _mem_ since
+ they can be either RAM or ROM
+
+08-10-2002 Jarek Burczynski (thanks to Dox for the YM3526 chip)
+ - corrected ym3526_read() to always set bit 2 and bit 1
+ to HIGH state - identical to ym3812_read (verified on real YM3526)
+
+04-28-2002 Jarek Burczynski:
+ - binary exact Envelope Generator (verified on real YM3812);
+ compared to YM2151: the EG clock is equal to internal_clock,
+ rates are 2 times slower and volume resolution is one bit less
+ - modified interface functions (they no longer return pointer -
+ that's internal to the emulator now):
+ - new wrapper functions for OPLCreate: ym3526_init(), ym3812_init() and y8950_init()
+ - corrected 'off by one' error in feedback calculations (when feedback is off)
+ - enabled waveform usage (credit goes to Vlad Romascanu and zazzal22)
+ - speeded up noise generator calculations (Nicola Salmoria)
+
+03-24-2002 Jarek Burczynski (thanks to Dox for the YM3812 chip)
+ Complete rewrite (all verified on real YM3812):
+ - corrected sin_tab and tl_tab data
+ - corrected operator output calculations
+ - corrected waveform_select_enable register;
+ simply: ignore all writes to waveform_select register when
+ waveform_select_enable == 0 and do not change the waveform previously selected.
+ - corrected KSR handling
+ - corrected Envelope Generator: attack shape, Sustain mode and
+ Percussive/Non-percussive modes handling
+ - Envelope Generator rates are two times slower now
+ - LFO amplitude (tremolo) and phase modulation (vibrato)
+ - rhythm sounds phase generation
+ - white noise generator (big thanks to Olivier Galibert for mentioning Berlekamp-Massey algorithm)
+ - corrected key on/off handling (the 'key' signal is ORed from three sources: FM, rhythm and CSM)
+ - funky details (like ignoring output of operator 1 in BD rhythm sound when connect == 1)
+
+12-28-2001 Acho A. Tang
+ - reflected Delta-T EOS status on Y8950 status port.
+ - fixed subscription range of attack/decay tables
+
+
+ To do:
+ add delay before key off in CSM mode (see CSMKeyControll)
+ verify volume of the FM part on the Y8950
+*/
+
+#include <stdlib.h>
+#include <string.h>
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include "fmopl.h"
+#include "ymdeltat.h"
+
+#define INLINE __inline
+#ifndef NULL
+ #define NULL ((void *)0)
+#endif
+#define logerror (void)
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846
+#endif
+
+/* output final shift */
+#if (OPL_SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+#define TIMER_SH 16 /* 16.16 fixed point (timers calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-1))-1) /*511*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+/* save output as raw 16-bit sample */
+
+/*#define SAVE_SAMPLE*/
+
+#ifdef SAVE_SAMPLE
+INLINE signed int acc_calc(signed int value)
+{
+ if (value>=0)
+ {
+ if (value < 0x0200)
+ return (value & ~0);
+ if (value < 0x0400)
+ return (value & ~1);
+ if (value < 0x0800)
+ return (value & ~3);
+ if (value < 0x1000)
+ return (value & ~7);
+ if (value < 0x2000)
+ return (value & ~15);
+ if (value < 0x4000)
+ return (value & ~31);
+ return (value & ~63);
+ }
+ /*else value < 0*/
+ if (value > -0x0200)
+ return (~abs(value) & ~0);
+ if (value > -0x0400)
+ return (~abs(value) & ~1);
+ if (value > -0x0800)
+ return (~abs(value) & ~3);
+ if (value > -0x1000)
+ return (~abs(value) & ~7);
+ if (value > -0x2000)
+ return (~abs(value) & ~15);
+ if (value > -0x4000)
+ return (~abs(value) & ~31);
+ return (~abs(value) & ~63);
+}
+
+
+static FILE *sample[1];
+ #if 1 /*save to MONO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = acc_calc(lt); \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #else /*save to STEREO file */
+ #define SAVE_ALL_CHANNELS \
+ { signed int pom = lt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ pom = rt; \
+ fputc((unsigned short)pom&0xff,sample[0]); \
+ fputc(((unsigned short)pom>>8)&0xff,sample[0]); \
+ }
+ #endif
+#endif
+
+#define LOG_CYM_FILE 0
+//static FILE * cymfile = NULL;
+
+
+
+#define OPL_TYPE_WAVESEL 0x01 /* waveform select */
+#define OPL_TYPE_ADPCM 0x02 /* DELTA-T ADPCM unit */
+#define OPL_TYPE_KEYBOARD 0x04 /* keyboard interface */
+#define OPL_TYPE_IO 0x08 /* I/O port */
+
+/* ---------- Generic interface section ---------- */
+#define OPL_TYPE_YM3526 (0)
+#define OPL_TYPE_YM3812 (OPL_TYPE_WAVESEL)
+#define OPL_TYPE_Y8950 (OPL_TYPE_ADPCM|OPL_TYPE_KEYBOARD|OPL_TYPE_IO)
+
+
+
+typedef struct{
+ UINT32 ar; /* attack rate: AR<<2 */
+ UINT32 dr; /* decay rate: DR<<2 */
+ UINT32 rr; /* release rate:RR<<2 */
+ UINT8 KSR; /* key scale rate */
+ UINT8 ksl; /* keyscale level */
+ UINT8 ksr; /* key scale rate: kcode>>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 Cnt; /* frequency counter */
+ UINT32 Incr; /* frequency counter step */
+ UINT8 FB; /* feedback shift value */
+ INT32 *connect1; /* slot1 output pointer */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+ UINT8 CON; /* connection (algorithm) type */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/non-percussive mode */
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT8 eg_sh_rr; /* (release state) */
+ UINT8 eg_sel_rr; /* (release state) */
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ UINT16 wavetable;
+} OPL_SLOT;
+
+typedef struct{
+ OPL_SLOT SLOT[2];
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. Increment base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+} OPL_CH;
+
+/* OPL state */
+typedef struct fm_opl_f {
+ /* FM channel slots */
+ OPL_CH P_CH[9]; /* OPL/OPL2 chips have 9 channels*/
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ /* LFO */
+ UINT8 lfo_am_depth;
+ UINT8 lfo_pm_depth_range;
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+ UINT8 wavesel; /* waveform select enable flag */
+
+ UINT32 T[2]; /* timer counters */
+ UINT8 st[2]; /* timer enable */
+
+#if BUILD_Y8950
+ /* Delta-T ADPCM unit (Y8950) */
+
+ YM_DELTAT *deltat;
+
+ /* Keyboard and I/O ports interface */
+ UINT8 portDirection;
+ UINT8 portLatch;
+ OPL_PORTHANDLER_R porthandler_r;
+ OPL_PORTHANDLER_W porthandler_w;
+ void * port_param;
+ OPL_PORTHANDLER_R keyboardhandler_r;
+ OPL_PORTHANDLER_W keyboardhandler_w;
+ void * keyboard_param;
+#endif
+
+ /* external event callback handlers */
+ //OPL_TIMERHANDLER timer_handler; /* TIMER handler */
+ void *TimerParam; /* TIMER parameter */
+ OPL_IRQHANDLER IRQHandler; /* IRQ handler */
+ void *IRQParam; /* IRQ parameter */
+ OPL_UPDATEHANDLER UpdateHandler;/* stream update handler */
+ void *UpdateParam; /* stream update parameter */
+
+ UINT8 type; /* chip type */
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+ UINT8 statusmask; /* status mask */
+ UINT8 mode; /* Reg.08 : CSM,notesel,etc. */
+
+ UINT32 clock; /* master clock (Hz) */
+ UINT32 rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+ //attotime TimerBase; /* Timer base time (==sampling time)*/
+
+ OPL_SLOT *SLOT7_1, *SLOT7_2, *SLOT8_1, *SLOT8_2;
+
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+ signed int output[1];
+
+#if BUILD_Y8950
+ INT32 output_deltat[4]; /* for Y8950 DELTA-T, chip is mono, that 4 here is just for safety */
+#endif
+
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+} FM_OPL;
+
+
+
+/* mapping of register number (offset) to slot number used by the emulator */
+static const int slot_array[32]=
+{
+ 0, 2, 4, 1, 3, 5,-1,-1,
+ 6, 8,10, 7, 9,11,-1,-1,
+ 12,14,16,13,15,17,-1,-1,
+ -1,-1,-1,-1,-1,-1,-1,-1
+};
+
+/* key scale level */
+/* table is 3dB/octave , DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/2.0)
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,93 (dB)*/
+#define SC(db) (UINT32) ( db * (2.0/ENV_STEP) )
+static const UINT32 sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(31)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0, 0 */
+/*mask 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+* 12 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (12*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>4)
+
+/* sin waveform table in 'decibel' scale */
+/* four waveforms on OPL2 type chips */
+static unsigned int sin_tab[SIN_LEN * 4];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+ When AM = 1 data is used directly
+ When AM = 0 data is divided by 4 before being used (loosing precision is important)
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM3812) */
+static const INT8 lfo_pm_table[8*8*2] = {
+
+/* FNUM2/FNUM = 00 0xxxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 00 1xxxxxxx (0x0080) */
+0, 0, 0, 0, 0, 0, 0, 0, /*LFO PM depth = 0*/
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 0xxxxxxx (0x0100) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 01 1xxxxxxx (0x0180) */
+1, 0, 0, 0,-1, 0, 0, 0, /*LFO PM depth = 0*/
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 0xxxxxxx (0x0200) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+4, 2, 0,-2,-4,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 10 1xxxxxxx (0x0280) */
+2, 1, 0,-1,-2,-1, 0, 1, /*LFO PM depth = 0*/
+5, 2, 0,-2,-5,-2, 0, 2, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 0xxxxxxx (0x0300) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+6, 3, 0,-3,-6,-3, 0, 3, /*LFO PM depth = 1*/
+
+/* FNUM2/FNUM = 11 1xxxxxxx (0x0380) */
+3, 1, 0,-1,-3,-1, 0, 1, /*LFO PM depth = 0*/
+7, 3, 0,-3,-7,-3, 0, 3 /*LFO PM depth = 1*/
+};
+
+
+/* lock level of common table */
+//static int num_lock = 0;
+
+
+//static void *cur_chip = NULL; /* current chip pointer */
+
+INLINE int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* status set and IRQ handling */
+INLINE void OPL_STATUS_SET(FM_OPL *OPL,int flag)
+{
+ /* set status flag */
+ OPL->status |= flag;
+ if(!(OPL->status & 0x80))
+ {
+ if(OPL->status & OPL->statusmask)
+ { /* IRQ on */
+ OPL->status |= 0x80;
+ /* callback user interrupt handler (IRQ is OFF to ON) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,1);
+ }
+ }
+}
+
+/* status reset and IRQ handling */
+INLINE void OPL_STATUS_RESET(FM_OPL *OPL,int flag)
+{
+ /* reset status flag */
+ OPL->status &=~flag;
+ if((OPL->status & 0x80))
+ {
+ if (!(OPL->status & OPL->statusmask) )
+ {
+ OPL->status &= 0x7f;
+ /* callback user interrupt handler (IRQ is ON to OFF) */
+ if(OPL->IRQHandler) (OPL->IRQHandler)(OPL->IRQParam,0);
+ }
+ }
+}
+
+/* IRQ mask set */
+INLINE void OPL_STATUSMASK_SET(FM_OPL *OPL,int flag)
+{
+ OPL->statusmask = flag;
+ /* IRQ handling check */
+ OPL_STATUS_SET(OPL,0);
+ OPL_STATUS_RESET(OPL,0);
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(FM_OPL *OPL)
+{
+ UINT8 tmp;
+
+ /* LFO */
+ OPL->lfo_am_cnt += OPL->lfo_am_inc;
+ if (OPL->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ OPL->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ tmp = lfo_am_table[ OPL->lfo_am_cnt >> LFO_SH ];
+
+ if (OPL->lfo_am_depth)
+ OPL->LFO_AM = tmp;
+ else
+ OPL->LFO_AM = tmp>>2;
+
+ OPL->lfo_pm_cnt += OPL->lfo_pm_inc;
+ OPL->LFO_PM = ((OPL->lfo_pm_cnt>>LFO_SH) & 7) | OPL->lfo_pm_depth_range;
+}
+
+/* advance to next sample */
+INLINE void advance(FM_OPL *OPL)
+{
+ OPL_CH *CH;
+ OPL_SLOT *op;
+ int i;
+
+ OPL->eg_timer += OPL->eg_timer_add;
+
+ while (OPL->eg_timer >= OPL->eg_timer_overflow)
+ {
+ OPL->eg_timer -= OPL->eg_timer_overflow;
+
+ OPL->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Envelope Generator */
+ switch(op->state)
+ {
+ case EG_ATT: /* attack phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((OPL->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>3;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((OPL->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ if ( !(OPL->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((OPL->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &OPL->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+ unsigned int block_fnum = CH->block_fnum;
+
+ unsigned int fnum_lfo = (block_fnum&0x0380) >> 7;
+
+ signed int lfo_fn_table_index_offset = lfo_pm_table[OPL->LFO_PM + 16*fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->Cnt += (OPL->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->Cnt += op->Incr;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ OPL->noise_p += OPL->noise_f;
+ i = OPL->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ OPL->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ( (OPL->noise_rng) ^ (OPL->noise_rng>>14) ^ (OPL->noise_rng>>15) ^ (OPL->noise_rng>>22) ) & 1;
+ OPL->noise_rng = (j<<22) | (OPL->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (OPL->noise_rng & 1) OPL->noise_rng ^= 0x800302;
+ OPL->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<16))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<4) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + pm )) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (OPL->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+INLINE void OPL_CALC_CH( FM_OPL *OPL, OPL_CH *CH )
+{
+ OPL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+
+ OPL->phase_modulation = 0;
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ *SLOT->connect1 += SLOT->op1_out[0];
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable);
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void OPL_CALC_RH( FM_OPL *OPL, OPL_CH *CH, unsigned int noise )
+{
+ OPL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+ OPL->phase_modulation = 0;
+ /* SLOT 1 */
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ if (!SLOT->CON)
+ OPL->phase_modulation = SLOT->op1_out[0];
+ /* else ignore output of operator 1 */
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->FB)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->Cnt, env, (out<<SLOT->FB), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(SLOT->Cnt, env, OPL->phase_modulation, SLOT->wavetable) * 2;
+
+
+ /* Phase generation is based on: */
+ /* HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases) */
+ /* SD (16) channel 7->slot 1 */
+ /* TOM (14) channel 8->slot 1 */
+ /* TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on: */
+ /* HH channel 7->slot1 */
+ /* SD channel 7->slot2 */
+ /* TOM channel 8->slot1 */
+ /* TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, OPL->SLOT7_1->wavetable) * 2;
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, OPL->SLOT7_2->wavetable) * 2;
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT8_1);
+ if( env < ENV_QUIET )
+ OPL->output[0] += op_calc(OPL->SLOT8_1->Cnt, env, 0, OPL->SLOT8_1->wavetable) * 2;
+
+ /* Top Cymbal (verified on real YM3812) */
+ env = volume_calc(OPL->SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((OPL->SLOT7_1->Cnt>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((OPL->SLOT8_2->Cnt>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e ^ bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ OPL->output[0] += op_calc(phase<<FREQ_SH, env, 0, OPL->SLOT8_2->wavetable) * 2;
+ }
+
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ n <<= 1; /* 12 bits here (as in real chip) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<12; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<12; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("FMOPL.C: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("FMOPL.C: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+ }
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /* waveform 2: __ __ __ __ */
+ /* / \/ \/ \/ \*/
+ /* abs(sin) */
+
+ sin_tab[2*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>1) ];
+
+ /* waveform 3: _ _ _ _ */
+ /* / |_/ |_/ |_/ |_*/
+ /* abs(output only first quarter of the sinus waveform) */
+
+ if (i & (1<<(SIN_BITS-2)) )
+ sin_tab[3*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[3*SIN_LEN+i] = sin_tab[i & (SIN_MASK>>2)];
+
+ /*logerror("FMOPL.C: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin2[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[2*SIN_LEN+i], tl_tab[sin_tab[2*SIN_LEN+i]] );
+ logerror("FMOPL.C: sin3[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[3*SIN_LEN+i], tl_tab[sin_tab[3*SIN_LEN+i]] );*/
+ }
+ /*logerror("FMOPL.C: ENV_QUIET= %08x (dec*8=%i)\n", ENV_QUIET, ENV_QUIET*8 );*/
+
+
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+static void OPLCloseTable( void )
+{
+#ifdef SAVE_SAMPLE
+ fclose(sample[0]);
+#endif
+}
+
+
+
+static void OPL_initalize(FM_OPL *OPL)
+{
+ int i;
+
+ /* frequency base */
+ OPL->freqbase = (OPL->rate) ? ((double)OPL->clock / 72.0) / OPL->rate : 0;
+#if 0
+ OPL->rate = (double)OPL->clock / 72.0;
+ OPL->freqbase = 1.0;
+#endif
+
+ /*logerror("freqbase=%f\n", OPL->freqbase);*/
+
+ /* Timer base time */
+ //OPL->TimerBase = attotime_mul(ATTOTIME_IN_HZ(OPL->clock), 72);
+
+ /* make fnumber -> increment counter table */
+ for( i=0 ; i < 1024 ; i++ )
+ {
+ /* opn phase increment counter = 20bit */
+ OPL->fn_tab[i] = (UINT32)( (double)i * 64 * OPL->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("FMOPL.C: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, OPL->fn_tab[i]>>6, OPL->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("FMOPL.C: sl_tab[%i] = %08x\n",
+ i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("FMOPL.C: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ OPL->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * OPL->freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ OPL->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * OPL->freqbase;
+
+ /*logerror ("OPL->lfo_am_inc = %8x ; OPL->lfo_pm_inc = %8x\n", OPL->lfo_am_inc, OPL->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ OPL->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * OPL->freqbase;
+
+ OPL->eg_timer_add = (1<<EG_SH) * OPL->freqbase;
+ OPL->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("OPLinit eg_timer_add=%8x eg_timer_overflow=%8x\n", OPL->eg_timer_add, OPL->eg_timer_overflow);*/
+
+}
+
+INLINE void FM_KEYON(OPL_SLOT *SLOT, UINT32 key_set)
+{
+ if( !SLOT->key )
+ {
+ /* restart Phase Generator */
+ SLOT->Cnt = 0;
+ /* phase -> Attack */
+ SLOT->state = EG_ATT;
+ }
+ SLOT->key |= key_set;
+}
+
+INLINE void FM_KEYOFF(OPL_SLOT *SLOT, UINT32 key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+INLINE void CALC_FCSLOT(OPL_CH *CH,OPL_SLOT *SLOT)
+{
+ int ksr;
+
+ /* (frequency) phase increment counter */
+ SLOT->Incr = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+ }
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl & tl */
+INLINE void set_ksl_tl(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+ int ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-1-7); /* 7 bits TL (bit 6 = always 0) */
+
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(FM_OPL *OPL,int slot,int v)
+{
+ OPL_CH *CH = &OPL->P_CH[slot/2];
+ OPL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+
+/* write a value v to register r on OPL chip */
+static void OPLWriteReg(FM_OPL *OPL, int r, int v)
+{
+ OPL_CH *CH;
+ int slot;
+ int block_fnum;
+
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+ /*if (LOG_CYM_FILE && (cymfile) && (r!=0) )
+ {
+ fputc( (unsigned char)r, cymfile );
+ fputc( (unsigned char)v, cymfile );
+ }*/
+
+
+ switch(r&0xe0)
+ {
+ case 0x00: /* 00-1f:control */
+ switch(r&0x1f)
+ {
+ case 0x01: /* waveform select enable */
+ if(OPL->type&OPL_TYPE_WAVESEL)
+ {
+ OPL->wavesel = v&0x20;
+ /* do not change the waveform previously selected */
+ }
+ break;
+ case 0x02: /* Timer 1 */
+ OPL->T[0] = (256-v)*4;
+ break;
+ case 0x03: /* Timer 2 */
+ OPL->T[1] = (256-v)*16;
+ break;
+ case 0x04: /* IRQ clear / mask and Timer enable */
+ if(v&0x80)
+ { /* IRQ flag clear */
+ OPL_STATUS_RESET(OPL,0x7f-0x08); /* don't reset BFRDY flag or we will have to call deltat module to set the flag */
+ }
+ else
+ { /* set IRQ mask ,timer enable*/
+ UINT8 st1 = v&1;
+ UINT8 st2 = (v>>1)&1;
+
+ /* IRQRST,T1MSK,t2MSK,EOSMSK,BRMSK,x,ST2,ST1 */
+ OPL_STATUS_RESET(OPL, v & (0x78-0x08) );
+ OPL_STATUSMASK_SET(OPL, (~v) & 0x78 );
+
+ /* timer 2 */
+ /*if(OPL->st[1] != st2)
+ {
+ attotime period = st2 ? attotime_mul(OPL->TimerBase, OPL->T[1]) : attotime_zero;
+ OPL->st[1] = st2;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,1,period);
+ }*/
+ /* timer 1 */
+ /*if(OPL->st[0] != st1)
+ {
+ attotime period = st1 ? attotime_mul(OPL->TimerBase, OPL->T[0]) : attotime_zero;
+ OPL->st[0] = st1;
+ if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,0,period);
+ }*/
+ }
+ break;
+#if BUILD_Y8950
+ case 0x06: /* Key Board OUT */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_w)
+ OPL->keyboardhandler_w(OPL->keyboard_param,v);
+ else
+ logerror("Y8950: write unmapped KEYBOARD port\n");
+ }
+ break;
+ case 0x07: /* DELTA-T control 1 : START,REC,MEMDATA,REPT,SPOFF,x,x,RST */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+#endif
+ case 0x08: /* MODE,DELTA-T control 2 : CSM,NOTESEL,x,x,smpl,da/ad,64k,rom */
+ OPL->mode = v;
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v&0x0f); /* mask 4 LSBs in register 08 for DELTA-T unit */
+#endif
+ break;
+
+#if BUILD_Y8950
+ case 0x09: /* START ADD */
+ case 0x0a:
+ case 0x0b: /* STOP ADD */
+ case 0x0c:
+ case 0x0d: /* PRESCALE */
+ case 0x0e:
+ case 0x0f: /* ADPCM data write */
+ case 0x10: /* DELTA-N */
+ case 0x11: /* DELTA-N */
+ case 0x12: /* ADPCM volume */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ YM_DELTAT_ADPCM_Write(OPL->deltat,r-0x07,v);
+ break;
+
+ case 0x15: /* DAC data high 8 bits (F7,F6...F2) */
+ case 0x16: /* DAC data low 2 bits (F1, F0 in bits 7,6) */
+ case 0x17: /* DAC data shift (S2,S1,S0 in bits 2,1,0) */
+ logerror("FMOPL.C: DAC data register written, but not implemented reg=%02x val=%02x\n",r,v);
+ break;
+
+ case 0x18: /* I/O CTRL (Direction) */
+ if(OPL->type&OPL_TYPE_IO)
+ OPL->portDirection = v&0x0f;
+ break;
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ OPL->portLatch = v;
+ if(OPL->porthandler_w)
+ OPL->porthandler_w(OPL->port_param,v&OPL->portDirection);
+ }
+ break;
+#endif
+ default:
+ logerror("FMOPL.C: write to unknown register: %02x\n",r);
+ break;
+ }
+ break;
+ case 0x20: /* am ON, vib ON, ksr, eg_type, mul */
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_mul(OPL,slot,v);
+ break;
+ case 0x40:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ksl_tl(OPL,slot,v);
+ break;
+ case 0x60:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_ar_dr(OPL,slot,v);
+ break;
+ case 0x80:
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ set_sl_rr(OPL,slot,v);
+ break;
+ case 0xa0:
+ if (r == 0xbd) /* am depth, vibrato depth, r,bd,sd,tom,tc,hh */
+ {
+ OPL->lfo_am_depth = v & 0x80;
+ OPL->lfo_pm_depth_range = (v&0x40) ? 8 : 0;
+
+ OPL->rhythm = v&0x3f;
+
+ if(OPL->rhythm&0x20)
+ {
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT1], 2);
+ FM_KEYON (&OPL->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) FM_KEYON (&OPL->P_CH[7].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT1], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) FM_KEYON (&OPL->P_CH[8].SLOT[SLOT2], 2);
+ else FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ /* BD key off */
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT1],~2);
+ FM_KEYOFF(&OPL->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ FM_KEYOFF(&OPL->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ FM_KEYOFF(&OPL->P_CH[8].SLOT[SLOT2],~2);
+ }
+ return;
+ }
+ /* keyon,block,fnum */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ if(!(r&0x10))
+ { /* a0-a8 */
+ block_fnum = (CH->block_fnum&0x1f00) | v;
+ }
+ else
+ { /* b0-b8 */
+ block_fnum = ((v&0x1f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x20)
+ {
+ FM_KEYON (&CH->SLOT[SLOT1], 1);
+ FM_KEYON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ FM_KEYOFF(&CH->SLOT[SLOT1],~1);
+ FM_KEYOFF(&CH->SLOT[SLOT2],~1);
+ }
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ UINT8 block = block_fnum >> 10;
+
+ CH->block_fnum = block_fnum;
+
+ CH->ksl_base = ksl_tab[block_fnum>>6];
+ CH->fc = OPL->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode */
+ CH->kcode = (CH->block_fnum&0x1c00)>>9;
+
+ /* the info below is actually opposite to what is stated in the Manuals (verifed on real YM3812) */
+ /* if notesel == 0 -> lsb of kcode is bit 10 (MSB) of fnum */
+ /* if notesel == 1 -> lsb of kcode is bit 9 (MSB-1) of fnum */
+ if (OPL->mode&0x40)
+ CH->kcode |= (CH->block_fnum&0x100)>>8; /* notesel == 1 */
+ else
+ CH->kcode |= (CH->block_fnum&0x200)>>9; /* notesel == 0 */
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ break;
+ case 0xc0:
+ /* FB,C */
+ if( (r&0x0f) > 8) return;
+ CH = &OPL->P_CH[r&0x0f];
+ CH->SLOT[SLOT1].FB = (v>>1)&7 ? ((v>>1)&7) + 7 : 0;
+ CH->SLOT[SLOT1].CON = v&1;
+ CH->SLOT[SLOT1].connect1 = CH->SLOT[SLOT1].CON ? &OPL->output[0] : &OPL->phase_modulation;
+ break;
+ case 0xe0: /* waveform select */
+ /* simply ignore write to the waveform select register if selecting not enabled in test register */
+ if(OPL->wavesel)
+ {
+ slot = slot_array[r&0x1f];
+ if(slot < 0) return;
+ CH = &OPL->P_CH[slot/2];
+
+ CH->SLOT[slot&1].wavetable = (v&0x03)*SIN_LEN;
+ }
+ break;
+ }
+}
+
+/*static TIMER_CALLBACK( cymfile_callback )
+{
+ if (cymfile)
+ {
+ fputc( (unsigned char)0, cymfile );
+ }
+}*/
+
+/* lock/unlock for common table */
+#if 0
+static int OPL_LockTable(/*const device_config *device*/)
+{
+ num_lock++;
+ if(num_lock>1) return 0;
+
+ /* first time */
+
+ cur_chip = NULL;
+ /* allocate total level table (128kb space) */
+ if( !init_tables() )
+ {
+ num_lock--;
+ return -1;
+ }
+
+#if 0
+ if (LOG_CYM_FILE)
+ {
+ cymfile = fopen("3812_.cym","wb");
+ if (cymfile)
+ timer_pulse ( device->machine, ATTOTIME_IN_HZ(110), NULL, 0, cymfile_callback); /*110 Hz pulse timer*/
+ else
+ logerror("Could not create file 3812_.cym\n");
+ }
+#endif
+
+ return 0;
+}
+
+static void OPL_UnLockTable(void)
+{
+ if(num_lock) num_lock--;
+ if(num_lock) return;
+
+ /* last time */
+
+ cur_chip = NULL;
+ OPLCloseTable();
+
+ /*if (cymfile)
+ fclose (cymfile);
+ cymfile = NULL;*/
+}
+#endif
+
+static void OPLResetChip(FM_OPL *OPL)
+{
+ int c,s;
+ int i;
+
+ OPL->eg_timer = 0;
+ OPL->eg_cnt = 0;
+
+ OPL->noise_rng = 1; /* noise shift register */
+ OPL->mode = 0; /* normal mode */
+ OPL_STATUS_RESET(OPL,0x7f);
+
+ /* reset with register write */
+ OPLWriteReg(OPL,0x01,0); /* wavesel disable */
+ OPLWriteReg(OPL,0x02,0); /* Timer1 */
+ OPLWriteReg(OPL,0x03,0); /* Timer2 */
+ OPLWriteReg(OPL,0x04,0); /* IRQ mask clear */
+ for(i = 0xff ; i >= 0x20 ; i-- ) OPLWriteReg(OPL,i,0);
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9 ; c++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = 0;
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+#if BUILD_Y8950
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ YM_DELTAT *DELTAT = OPL->deltat;
+
+ DELTAT->freqbase = OPL->freqbase;
+ DELTAT->output_pointer = &OPL->output_deltat[0];
+ DELTAT->portshift = 5;
+ DELTAT->output_range = 1<<23;
+ YM_DELTAT_ADPCM_Reset(DELTAT,0,YM_DELTAT_EMULATION_MODE_NORMAL);
+ }
+#endif
+}
+
+
+#if 0
+static STATE_POSTLOAD( OPL_postload )
+{
+ FM_OPL *OPL = (FM_OPL *)param;
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++ )
+ {
+ OPL_CH *CH = &OPL->P_CH[ch];
+
+ /* Look up key scale level */
+ UINT32 block_fnum = CH->block_fnum;
+ CH->ksl_base = ksl_tab[block_fnum >> 6];
+ CH->fc = OPL->fn_tab[block_fnum & 0x03ff] >> (7 - (block_fnum >> 10));
+
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ /* Calculate key scale rate */
+ SLOT->ksr = CH->kcode >> SLOT->KSR;
+
+ /* Calculate attack, decay and release rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+
+ /* Calculate phase increment */
+ SLOT->Incr = CH->fc * SLOT->mul;
+
+ /* Total level */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base >> SLOT->ksl);
+
+ /* Connect output */
+ SLOT->connect1 = SLOT->CON ? &OPL->output[0] : &OPL->phase_modulation;
+ }
+ }
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ // We really should call the postlod function for the YM_DELTAT, but it's hard without registers
+ // (see the way the YM2610 does it)
+ //YM_DELTAT_postload(OPL->deltat, REGS);
+ }
+#endif
+}
+
+
+static void OPLsave_state_channel(const device_config *device, OPL_CH *CH)
+{
+ int slot, ch;
+
+ for( ch=0 ; ch < 9 ; ch++, CH++ )
+ {
+ /* channel */
+ state_save_register_device_item(device, ch, CH->block_fnum);
+ state_save_register_device_item(device, ch, CH->kcode);
+ /* slots */
+ for( slot=0 ; slot < 2 ; slot++ )
+ {
+ OPL_SLOT *SLOT = &CH->SLOT[slot];
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->ar);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->dr);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->rr);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->KSR);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->ksl);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->mul);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->Cnt);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->FB);
+ state_save_register_device_item_array(device, ch * 2 + slot, SLOT->op1_out);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->CON);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->eg_type);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->state);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->TL);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->volume);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->sl);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->key);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->AMmask);
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->vib);
+
+ state_save_register_device_item(device, ch * 2 + slot, SLOT->wavetable);
+ }
+ }
+}
+
+
+/* Register savestate for a virtual YM3812/YM3526Y8950 */
+
+static void OPL_save_state(FM_OPL *OPL, const device_config *device)
+{
+ OPLsave_state_channel(device, OPL->P_CH);
+
+ state_save_register_device_item(device, 0, OPL->eg_cnt);
+ state_save_register_device_item(device, 0, OPL->eg_timer);
+
+ state_save_register_device_item(device, 0, OPL->rhythm);
+
+ state_save_register_device_item(device, 0, OPL->lfo_am_depth);
+ state_save_register_device_item(device, 0, OPL->lfo_pm_depth_range);
+ state_save_register_device_item(device, 0, OPL->lfo_am_cnt);
+ state_save_register_device_item(device, 0, OPL->lfo_pm_cnt);
+
+ state_save_register_device_item(device, 0, OPL->noise_rng);
+ state_save_register_device_item(device, 0, OPL->noise_p);
+
+ if( OPL->type & OPL_TYPE_WAVESEL )
+ {
+ state_save_register_device_item(device, 0, OPL->wavesel);
+ }
+
+ state_save_register_device_item_array(device, 0, OPL->T);
+ state_save_register_device_item_array(device, 0, OPL->st);
+
+#if BUILD_Y8950
+ if ( (OPL->type & OPL_TYPE_ADPCM) && (OPL->deltat) )
+ {
+ YM_DELTAT_savestate(device, OPL->deltat);
+ }
+
+ if ( OPL->type & OPL_TYPE_IO )
+ {
+ state_save_register_device_item(device, 0, OPL->portDirection);
+ state_save_register_device_item(device, 0, OPL->portLatch);
+ }
+#endif
+
+ state_save_register_device_item(device, 0, OPL->address);
+ state_save_register_device_item(device, 0, OPL->status);
+ state_save_register_device_item(device, 0, OPL->statusmask);
+ state_save_register_device_item(device, 0, OPL->mode);
+
+ state_save_register_postload(device->machine, OPL_postload, OPL);
+}
+#endif
+
+
+/* Create one of virtual YM3812/YM3526/Y8950 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static FM_OPL *OPLCreate(UINT32 clock, UINT32 rate, int type)
+{
+ char *ptr;
+ FM_OPL *OPL;
+ int state_size;
+
+ //if (OPL_LockTable(device) == -1) return NULL;
+ init_tables();
+
+ /* calculate OPL state size */
+ state_size = sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM) state_size+= sizeof(YM_DELTAT);
+#endif
+
+ /* allocate memory block */
+ ptr = (char *)malloc(state_size);
+
+ if (ptr==NULL)
+ return 0;
+
+ /* clear */
+ memset(ptr,0,state_size);
+
+ OPL = (FM_OPL *)ptr;
+
+ ptr += sizeof(FM_OPL);
+
+#if BUILD_Y8950
+ if (type&OPL_TYPE_ADPCM)
+ {
+ OPL->deltat = (YM_DELTAT *)ptr;
+ }
+ ptr += sizeof(YM_DELTAT);
+#endif
+
+ OPL->type = type;
+ OPL->clock = clock;
+ OPL->rate = rate;
+
+ /* init global tables */
+ OPL_initalize(OPL);
+
+ return OPL;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLDestroy(FM_OPL *OPL)
+{
+ //OPL_UnLockTable();
+ free(OPL);
+}
+
+/* Optional handlers */
+
+/*static void OPLSetTimerHandler(FM_OPL *OPL,OPL_TIMERHANDLER timer_handler,void *param)
+{
+ OPL->timer_handler = timer_handler;
+ OPL->TimerParam = param;
+}*/
+static void OPLSetIRQHandler(FM_OPL *OPL,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ OPL->IRQHandler = IRQHandler;
+ OPL->IRQParam = param;
+}
+static void OPLSetUpdateHandler(FM_OPL *OPL,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ OPL->UpdateHandler = UpdateHandler;
+ OPL->UpdateParam = param;
+}
+
+static int OPLWrite(FM_OPL *OPL,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ OPL->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ OPLWriteReg(OPL,OPL->address,v);
+ }
+ return OPL->status>>7;
+}
+
+static unsigned char OPLRead(FM_OPL *OPL,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+
+ #if BUILD_Y8950
+
+ if(OPL->type&OPL_TYPE_ADPCM) /* Y8950 */
+ {
+ return (OPL->status & (OPL->statusmask|0x80)) | (OPL->deltat->PCM_BSY&1);
+ }
+
+ #endif
+
+ /* OPL and OPL2 */
+ return OPL->status & (OPL->statusmask|0x80);
+ }
+
+#if BUILD_Y8950
+ /* data port */
+ switch(OPL->address)
+ {
+ case 0x05: /* KeyBoard IN */
+ if(OPL->type&OPL_TYPE_KEYBOARD)
+ {
+ if(OPL->keyboardhandler_r)
+ return OPL->keyboardhandler_r(OPL->keyboard_param);
+ else
+ logerror("Y8950: read unmapped KEYBOARD port\n");
+ }
+ return 0;
+
+ case 0x0f: /* ADPCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ UINT8 val;
+
+ val = YM_DELTAT_ADPCM_Read(OPL->deltat);
+ /*logerror("Y8950: read ADPCM value read=%02x\n",val);*/
+ return val;
+ }
+ return 0;
+
+ case 0x19: /* I/O DATA */
+ if(OPL->type&OPL_TYPE_IO)
+ {
+ if(OPL->porthandler_r)
+ return OPL->porthandler_r(OPL->port_param);
+ else
+ logerror("Y8950:read unmapped I/O port\n");
+ }
+ return 0;
+ case 0x1a: /* PCM-DATA */
+ if(OPL->type&OPL_TYPE_ADPCM)
+ {
+ logerror("Y8950 A/D convertion is accessed but not implemented !\n");
+ return 0x80; /* 2's complement PCM data - result from A/D convertion */
+ }
+ return 0;
+ }
+#endif
+
+ return 0xff;
+}
+
+/* CSM Key Controll */
+INLINE void CSMKeyControll(OPL_CH *CH)
+{
+ FM_KEYON (&CH->SLOT[SLOT1], 4);
+ FM_KEYON (&CH->SLOT[SLOT2], 4);
+
+ /* The key off should happen exactly one sample later - not implemented correctly yet */
+
+ FM_KEYOFF(&CH->SLOT[SLOT1], ~4);
+ FM_KEYOFF(&CH->SLOT[SLOT2], ~4);
+}
+
+
+static int OPLTimerOver(FM_OPL *OPL,int c)
+{
+ if( c )
+ { /* Timer B */
+ OPL_STATUS_SET(OPL,0x20);
+ }
+ else
+ { /* Timer A */
+ OPL_STATUS_SET(OPL,0x40);
+ /* CSM mode key,TL controll */
+ if( OPL->mode & 0x80 )
+ { /* CSM mode total level latch and auto key on */
+ int ch;
+ if(OPL->UpdateHandler) OPL->UpdateHandler(OPL->UpdateParam,0);
+ for(ch=0; ch<9; ch++)
+ CSMKeyControll( &OPL->P_CH[ch] );
+ }
+ }
+ /* reload timer */
+ //if (OPL->timer_handler) (OPL->timer_handler)(OPL->TimerParam,c,attotime_mul(OPL->TimerBase, OPL->T[c]));
+ return OPL->status>>7;
+}
+
+
+#define MAX_OPL_CHIPS 2
+
+
+#if (BUILD_YM3812)
+
+void * ym3812_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3812 = OPLCreate(clock,rate,OPL_TYPE_YM3812);
+ if (YM3812)
+ {
+ //OPL_save_state(YM3812);
+ ym3812_reset_chip(YM3812);
+ }
+ return YM3812;
+}
+
+void ym3812_shutdown(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+
+ /* emulator shutdown */
+ OPLDestroy(YM3812);
+}
+void ym3812_reset_chip(void *chip)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLResetChip(YM3812);
+}
+
+int ym3812_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLWrite(YM3812, a, v);
+}
+
+unsigned char ym3812_read(void *chip, int a)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ /* YM3812 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3812, a) | 0x06 ;
+}
+int ym3812_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3812, c);
+}
+
+/*void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3812, timer_handler, param);
+}*/
+void ym3812_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3812, IRQHandler, param);
+}
+void ym3812_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3812 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3812, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM3812's
+**
+** 'which' is the virtual YM3812 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ /* rhythm slots */
+ OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1];
+ OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2];
+ OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1];
+ OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2];
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3812 */
+
+
+
+#if (BUILD_YM3526)
+
+void *ym3526_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *YM3526 = OPLCreate(clock,rate,OPL_TYPE_YM3526);
+ if (YM3526)
+ {
+ /*OPL_save_state(YM3526);*/
+ ym3526_reset_chip(YM3526);
+ }
+ return YM3526;
+}
+
+void ym3526_shutdown(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(YM3526);
+}
+void ym3526_reset_chip(void *chip)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLResetChip(YM3526);
+}
+
+int ym3526_write(void *chip, int a, int v)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLWrite(YM3526, a, v);
+}
+
+unsigned char ym3526_read(void *chip, int a)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ /* YM3526 always returns bit2 and bit1 in HIGH state */
+ return OPLRead(YM3526, a) | 0x06 ;
+}
+int ym3526_timer_over(void *chip, int c)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ return OPLTimerOver(YM3526, c);
+}
+
+/*void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetTimerHandler(YM3526, timer_handler, param);
+}*/
+void ym3526_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetIRQHandler(YM3526, IRQHandler, param);
+}
+void ym3526_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *YM3526 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(YM3526, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ OPLSAMPLE *buf = buffer;
+ int i;
+
+ /* rhythm slots */
+ OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1];
+ OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2];
+ OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1];
+ OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2];
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0];
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+#endif /* BUILD_YM3526 */
+
+
+
+
+#if BUILD_Y8950
+
+static void Y8950_deltat_status_set(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_SET(Y8950, changebits);
+}
+static void Y8950_deltat_status_reset(void *chip, UINT8 changebits)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPL_STATUS_RESET(Y8950, changebits);
+}
+
+void *y8950_init(UINT32 clock, UINT32 rate)
+{
+ /* emulator create */
+ FM_OPL *Y8950 = OPLCreate(clock,rate,OPL_TYPE_Y8950);
+ if (Y8950)
+ {
+ Y8950->deltat->status_set_handler = Y8950_deltat_status_set;
+ Y8950->deltat->status_reset_handler = Y8950_deltat_status_reset;
+ Y8950->deltat->status_change_which_chip = Y8950;
+ Y8950->deltat->status_change_EOS_bit = 0x10; /* status flag: set bit4 on End Of Sample */
+ Y8950->deltat->status_change_BRDY_bit = 0x08; /* status flag: set bit3 on BRDY (End Of: ADPCM analysis/synthesis, memory reading/writing) */
+
+ /*Y8950->deltat->write_time = 10.0 / clock;*/ /* a single byte write takes 10 cycles of main clock */
+ /*Y8950->deltat->read_time = 8.0 / clock;*/ /* a single byte read takes 8 cycles of main clock */
+ /* reset */
+ /*OPL_save_state(Y8950);*/
+ y8950_reset_chip(Y8950);
+ }
+
+ return Y8950;
+}
+
+void y8950_shutdown(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ /* emulator shutdown */
+ OPLDestroy(Y8950);
+}
+void y8950_reset_chip(void *chip)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLResetChip(Y8950);
+}
+
+int y8950_write(void *chip, int a, int v)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLWrite(Y8950, a, v);
+}
+
+unsigned char y8950_read(void *chip, int a)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLRead(Y8950, a);
+}
+int y8950_timer_over(void *chip, int c)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ return OPLTimerOver(Y8950, c);
+}
+
+/*void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER timer_handler, void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetTimerHandler(Y8950, timer_handler, param);
+}*/
+void y8950_set_irq_handler(void *chip,OPL_IRQHANDLER IRQHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetIRQHandler(Y8950, IRQHandler, param);
+}
+void y8950_set_update_handler(void *chip,OPL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ FM_OPL *Y8950 = (FM_OPL *)chip;
+ OPLSetUpdateHandler(Y8950, UpdateHandler, param);
+}
+
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size )
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->deltat->memory = (UINT8 *)(deltat_mem_ptr);
+ OPL->deltat->memory_size = deltat_mem_size;
+}
+
+/*
+** Generate samples for one of the Y8950's
+**
+** 'which' is the virtual Y8950 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length)
+{
+ int i;
+ FM_OPL *OPL = (FM_OPL *)chip;
+ UINT8 rhythm = OPL->rhythm&0x20;
+ YM_DELTAT *DELTAT = OPL->deltat;
+ OPLSAMPLE *buf = buffer;
+
+ /* rhythm slots */
+ OPL->SLOT7_1 = &OPL->P_CH[7].SLOT[SLOT1];
+ OPL->SLOT7_2 = &OPL->P_CH[7].SLOT[SLOT2];
+ OPL->SLOT8_1 = &OPL->P_CH[8].SLOT[SLOT1];
+ OPL->SLOT8_2 = &OPL->P_CH[8].SLOT[SLOT2];
+
+ for( i=0; i < length ; i++ )
+ {
+ int lt;
+
+ OPL->output[0] = 0;
+ OPL->output_deltat[0] = 0;
+
+ advance_lfo(OPL);
+
+ /* deltaT ADPCM */
+ if( DELTAT->portstate&0x80 )
+ YM_DELTAT_ADPCM_CALC(DELTAT);
+
+ /* FM part */
+ OPL_CALC_CH(OPL, &OPL->P_CH[0]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[1]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[2]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[3]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[4]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[5]);
+
+ if(!rhythm)
+ {
+ OPL_CALC_CH(OPL, &OPL->P_CH[6]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[7]);
+ OPL_CALC_CH(OPL, &OPL->P_CH[8]);
+ }
+ else /* Rhythm part */
+ {
+ OPL_CALC_RH(OPL, &OPL->P_CH[0], (OPL->noise_rng>>0)&1 );
+ }
+
+ lt = OPL->output[0] + (OPL->output_deltat[0]>>11);
+
+ lt >>= FINAL_SH;
+
+ /* limit check */
+ lt = limit( lt , MAXOUT, MINOUT );
+
+ #ifdef SAVE_SAMPLE
+ if (which==0)
+ {
+ SAVE_ALL_CHANNELS
+ }
+ #endif
+
+ /* store to sound buffer */
+ buf[i] = lt;
+
+ advance(OPL);
+ }
+
+}
+
+void y8950_set_port_handler(void *chip,OPL_PORTHANDLER_W PortHandler_w,OPL_PORTHANDLER_R PortHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->porthandler_w = PortHandler_w;
+ OPL->porthandler_r = PortHandler_r;
+ OPL->port_param = param;
+}
+
+void y8950_set_keyboard_handler(void *chip,OPL_PORTHANDLER_W KeyboardHandler_w,OPL_PORTHANDLER_R KeyboardHandler_r,void * param)
+{
+ FM_OPL *OPL = (FM_OPL *)chip;
+ OPL->keyboardhandler_w = KeyboardHandler_w;
+ OPL->keyboardhandler_r = KeyboardHandler_r;
+ OPL->keyboard_param = param;
+}
+
+#endif
+
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/fmopl.h b/plugins/gme/game-music-emu-0.6pre/gme/fmopl.h
new file mode 100644
index 00000000..868d38be
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/fmopl.h
@@ -0,0 +1,116 @@
+#pragma once
+
+#ifndef __FMOPL_H__
+#define __FMOPL_H__
+
+/* --- select emulation chips --- */
+#define BUILD_YM3812 (1)
+#define BUILD_YM3526 (1)
+#define BUILD_Y8950 (1)
+
+/* select output bits size of output : 8 or 16 */
+#define OPL_SAMPLE_BITS 16
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+
+typedef INT32 stream_sample_t;
+
+#endif /* __OSDCOMM_H__ */
+
+typedef stream_sample_t OPLSAMPLE;
+/*
+#if (OPL_SAMPLE_BITS==16)
+typedef INT16 OPLSAMPLE;
+#endif
+#if (OPL_SAMPLE_BITS==8)
+typedef INT8 OPLSAMPLE;
+#endif
+*/
+
+//typedef void (*OPL_TIMERHANDLER)(void *param,int timer,attotime period);
+typedef void (*OPL_IRQHANDLER)(void *param,int irq);
+typedef void (*OPL_UPDATEHANDLER)(void *param,int min_interval_us);
+typedef void (*OPL_PORTHANDLER_W)(void *param,unsigned char data);
+typedef unsigned char (*OPL_PORTHANDLER_R)(void *param);
+
+
+#if BUILD_YM3812
+
+void *ym3812_init(UINT32 clock, UINT32 rate);
+void ym3812_shutdown(void *chip);
+void ym3812_reset_chip(void *chip);
+int ym3812_write(void *chip, int a, int v);
+unsigned char ym3812_read(void *chip, int a);
+int ym3812_timer_over(void *chip, int c);
+void ym3812_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+//void ym3812_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void ym3812_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3812_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_YM3812 */
+
+
+#if BUILD_YM3526
+
+/*
+** Initialize YM3526 emulator(s).
+**
+** 'num' is the number of virtual YM3526's to allocate
+** 'clock' is the chip clock in Hz
+** 'rate' is sampling rate
+*/
+void *ym3526_init(UINT32 clock, UINT32 rate);
+/* shutdown the YM3526 emulators*/
+void ym3526_shutdown(void *chip);
+void ym3526_reset_chip(void *chip);
+int ym3526_write(void *chip, int a, int v);
+unsigned char ym3526_read(void *chip, int a);
+int ym3526_timer_over(void *chip, int c);
+/*
+** Generate samples for one of the YM3526's
+**
+** 'which' is the virtual YM3526 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym3526_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+//void ym3526_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void ym3526_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void ym3526_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_YM3526 */
+
+
+#if BUILD_Y8950
+
+/* Y8950 port handlers */
+void y8950_set_port_handler(void *chip, OPL_PORTHANDLER_W PortHandler_w, OPL_PORTHANDLER_R PortHandler_r, void *param);
+void y8950_set_keyboard_handler(void *chip, OPL_PORTHANDLER_W KeyboardHandler_w, OPL_PORTHANDLER_R KeyboardHandler_r, void *param);
+void y8950_set_delta_t_memory(void *chip, void * deltat_mem_ptr, int deltat_mem_size );
+
+void * y8950_init(UINT32 clock, UINT32 rate);
+void y8950_shutdown(void *chip);
+void y8950_reset_chip(void *chip);
+int y8950_write(void *chip, int a, int v);
+unsigned char y8950_read (void *chip, int a);
+int y8950_timer_over(void *chip, int c);
+void y8950_update_one(void *chip, OPLSAMPLE *buffer, int length);
+
+//void y8950_set_timer_handler(void *chip, OPL_TIMERHANDLER TimerHandler, void *param);
+void y8950_set_irq_handler(void *chip, OPL_IRQHANDLER IRQHandler, void *param);
+void y8950_set_update_handler(void *chip, OPL_UPDATEHANDLER UpdateHandler, void *param);
+
+#endif /* BUILD_Y8950 */
+
+
+#endif /* __FMOPL_H__ */
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/gme.cpp b/plugins/gme/game-music-emu-0.6pre/gme/gme.cpp
new file mode 100644
index 00000000..1b584961
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/gme.cpp
@@ -0,0 +1,431 @@
+// Game_Music_Emu 0.6-pre. http://www.slack.net/~ant/
+
+#include "Music_Emu.h"
+
+#if !GME_DISABLE_EFFECTS
+#include "Effects_Buffer.h"
+#endif
+#include "blargg_endian.h"
+#include <string.h>
+#include <ctype.h>
+
+/* Copyright (C) 2003-2009 Shay Green. This module is free software; you
+can redistribute it and/or modify it under the terms of the GNU Lesser
+General Public License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version. This
+module is distributed in the hope that it will be useful, but WITHOUT ANY
+WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
+details. You should have received a copy of the GNU Lesser General Public
+License along with this module; if not, write to the Free Software Foundation,
+Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
+
+#include "blargg_source.h"
+
+#ifndef 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_sgc_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','G','C',0x1A): return "SGC";
+ 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 );
+
+ gme_type_t const* types = gme_type_list_;
+ for ( ; *types; types++ )
+ if ( !strcmp( extension, (*types)->extension_ ) )
+ break;
+ return *types;
+}
+
+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 blargg_ok;
+}
+
+gme_err_t gme_open_data( const char *path, void const* data, long size, Music_Emu** out, int sample_rate )
+{
+ require( (data || !size) && out );
+ *out = NULL;
+
+ gme_type_t file_type = 0;
+ if ( size >= 4 )
+ file_type = gme_identify_extension( path );
+ if ( !file_type )
+ return blargg_err_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, int sample_rate )
+{
+ require( path && out );
+ *out = NULL;
+
+ 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 blargg_err_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, int rate )
+{
+ if ( type )
+ {
+ if ( rate == gme_info_only )
+ return type->new_info();
+
+ Music_Emu* gme = type->new_emu();
+ if ( gme )
+ {
+ #if !GME_DISABLE_EFFECTS
+ if ( type->flags_ & 1 )
+ {
+ gme->effects_buffer_ = BLARGG_NEW Simple_Effects_Buffer;
+ if ( gme->effects_buffer_ )
+ gme->set_buffer( gme->effects_buffer_ );
+ }
+
+ if ( !(type->flags_ & 1) || gme->effects_buffer_ )
+ #endif
+ {
+ if ( !gme->set_sample_rate( rate ) )
+ {
+ check( gme->type() == type );
+ return gme;
+ }
+ }
+ delete gme;
+ }
+ }
+ return NULL;
+}
+
+gme_err_t gme_load_file( Music_Emu* gme, const char path [] ) { return gme->load_file( path ); }
+
+gme_err_t gme_load_data( Music_Emu* gme, void const* data, long size )
+{
+ Mem_File_Reader in( data, size );
+ return gme->load( in );
+}
+
+gme_err_t gme_load_custom( Music_Emu* gme, gme_reader_t func, long size, void* data )
+{
+ Callback_Reader in( func, size, data );
+ return gme->load( in );
+}
+
+void gme_delete( Music_Emu* gme ) { delete gme; }
+
+gme_type_t gme_type( Music_Emu const* gme ) { return gme->type(); }
+
+const char* gme_warning( Music_Emu* gme ) { return gme->warning(); }
+
+int gme_track_count( Music_Emu const* gme ) { return gme->track_count(); }
+
+struct gme_info_t_ : gme_info_t
+{
+ track_info_t info;
+
+ BLARGG_DISABLE_NOTHROW
+};
+
+gme_err_t gme_track_info( Music_Emu const* me, gme_info_t** out, int track )
+{
+ *out = NULL;
+
+ gme_info_t_* info = BLARGG_NEW gme_info_t_;
+ CHECK_ALLOC( info );
+
+ gme_err_t err = me->track_info( &info->info, track );
+ if ( err )
+ {
+ gme_free_info( info );
+ return err;
+ }
+
+ #define COPY(name) info->name = info->info.name;
+
+ COPY( length );
+ COPY( intro_length );
+ COPY( loop_length );
+
+ info->i4 = -1;
+ info->i5 = -1;
+ info->i6 = -1;
+ info->i7 = -1;
+ info->i8 = -1;
+ info->i9 = -1;
+ info->i10 = -1;
+ info->i11 = -1;
+ info->i12 = -1;
+ info->i13 = -1;
+ info->i14 = -1;
+ info->i15 = -1;
+
+ info->s7 = "";
+ info->s8 = "";
+ info->s9 = "";
+ info->s10 = "";
+ info->s11 = "";
+ info->s12 = "";
+ info->s13 = "";
+ info->s14 = "";
+ info->s15 = "";
+
+ COPY( system );
+ COPY( game );
+ COPY( song );
+ COPY( author );
+ COPY( copyright );
+ COPY( comment );
+ COPY( dumper );
+
+ #undef COPY
+
+ info->play_length = info->length;
+ if ( info->play_length <= 0 )
+ {
+ info->play_length = info->intro_length + 2 * info->loop_length; // intro + 2 loops
+ if ( info->play_length <= 0 )
+ info->play_length = 150 * 1000; // 2.5 minutes
+ }
+
+ *out = info;
+
+ return blargg_ok;
+}
+
+void gme_free_info( gme_info_t* info )
+{
+ delete STATIC_CAST(gme_info_t_*,info);
+}
+
+void* gme_user_data ( Music_Emu const* gme ) { return gme->user_data(); }
+void gme_set_user_data ( Music_Emu* gme, void* new_user_data ) { gme->set_user_data( new_user_data ); }
+void gme_set_user_cleanup(Music_Emu* gme, gme_user_cleanup_t func ){ gme->set_user_cleanup( func ); }
+
+gme_err_t gme_start_track ( Music_Emu* gme, int index ) { return gme->start_track( index ); }
+gme_err_t gme_play ( Music_Emu* gme, int n, short p [] ) { return gme->play( n, p ); }
+void gme_set_fade ( Music_Emu* gme, int start_msec ) { gme->set_fade( start_msec ); }
+gme_bool gme_track_ended ( Music_Emu const* gme ) { return gme->track_ended(); }
+int gme_tell ( Music_Emu const* gme ) { return gme->tell(); }
+gme_err_t gme_seek ( Music_Emu* gme, int msec ) { return gme->seek( msec ); }
+int gme_voice_count ( Music_Emu const* gme ) { return gme->voice_count(); }
+void gme_ignore_silence ( Music_Emu* gme, gme_bool disable ) { gme->ignore_silence( disable != 0 ); }
+void gme_set_tempo ( Music_Emu* gme, double t ) { gme->set_tempo( t ); }
+void gme_mute_voice ( Music_Emu* gme, int index, gme_bool mute ){ gme->mute_voice( index, mute != 0 ); }
+void gme_mute_voices ( Music_Emu* gme, int mask ) { gme->mute_voices( mask ); }
+void gme_set_equalizer ( Music_Emu* gme, gme_equalizer_t const* eq ) { gme->set_equalizer( *eq ); }
+void gme_equalizer ( Music_Emu const* gme, gme_equalizer_t* o ) { *o = gme->equalizer(); }
+const char* gme_voice_name ( Music_Emu const* gme, int i ) { return gme->voice_name( i ); }
+
+void gme_effects( Music_Emu const* gme, gme_effects_t* out )
+{
+ static gme_effects_t const zero = { 0 };
+ *out = zero;
+
+ #if !GME_DISABLE_EFFECTS
+ {
+ Simple_Effects_Buffer* b = STATIC_CAST(Simple_Effects_Buffer*,gme->effects_buffer_);
+ if ( b )
+ {
+ out->enabled = b->config().enabled;
+ out->echo = b->config().echo;
+ out->stereo = b->config().stereo;
+ out->surround = b->config().surround;
+ }
+ }
+ #endif
+}
+
+void gme_set_effects( Music_Emu* gme, gme_effects_t const* in )
+{
+ #if !GME_DISABLE_EFFECTS
+ {
+ Simple_Effects_Buffer* b = STATIC_CAST(Simple_Effects_Buffer*,gme->effects_buffer_);
+ if ( b )
+ {
+ b->config().enabled = false;
+ if ( in )
+ {
+ b->config().enabled = in->enabled;
+ b->config().echo = in->echo;
+ b->config().stereo = in->stereo;
+ b->config().surround = in->surround;
+ }
+ b->apply_config();
+ }
+ }
+ #endif
+}
+
+void gme_set_stereo_depth( Music_Emu* gme, double depth )
+{
+ #if !GME_DISABLE_EFFECTS
+ {
+ if ( gme->effects_buffer_ )
+ {
+ gme_effects_t cfg;
+ gme_effects( gme, &cfg );
+ cfg.enabled = (depth > 0.0);
+ cfg.echo = depth;
+ cfg.stereo = depth;
+ cfg.surround = true;
+ gme_set_effects( gme, &cfg );
+ }
+ }
+ #endif
+}
+
+#define ENTRY( name ) { blargg_err_##name, gme_err_##name }
+static blargg_err_to_code_t const gme_codes [] =
+{
+ ENTRY( generic ),
+ ENTRY( memory ),
+ ENTRY( caller ),
+ ENTRY( internal ),
+ ENTRY( limitation ),
+
+ ENTRY( file_missing ),
+ ENTRY( file_read ),
+ ENTRY( file_io ),
+ ENTRY( file_eof ),
+
+ ENTRY( file_type ),
+ ENTRY( file_feature ),
+ ENTRY( file_corrupt ),
+
+ { 0, -1 }
+};
+#undef ENTRY
+
+static int err_code( gme_err_t err )
+{
+ return blargg_err_to_code( err, gme_codes );
+}
+
+int gme_err_code( gme_err_t err )
+{
+ int code = err_code( err );
+ return (code >= 0 ? code : gme_err_generic);
+}
+
+gme_err_t gme_code_to_err( int code )
+{
+ return blargg_code_to_err( code, gme_codes );
+}
+
+const char* gme_err_details( gme_err_t err )
+{
+ // If we don't have error code assigned, return entire string
+ return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err ));
+}
+
+const char* gme_err_str( gme_err_t err )
+{
+ return blargg_err_str( err );
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/gme.h b/plugins/gme/game-music-emu-0.6pre/gme/gme.h
new file mode 100644
index 00000000..edd5b5d6
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/gme.h
@@ -0,0 +1,298 @@
+/* Loads and plays video game music files into sample buffer */
+
+/* Game_Music_Emu 0.6-pre */
+#ifndef GME_H
+#define GME_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* Pointer to error, or NULL if function was successful. See Errors below. */
+#ifndef gme_err_t /* (#ifndef allows better testing of library) */
+ typedef const char* gme_err_t;
+#endif
+
+/* First parameter of most functions is gme_t*, or const gme_t* if nothing is
+changed. */
+typedef struct gme_t gme_t;
+
+/* Boolean; false = 0, true = 1 */
+typedef int gme_bool;
+
+
+/******** Basic operations ********/
+
+/* Opens game music file and points *out at it. If error, sets *out to NULL. */
+gme_err_t gme_open_file( const char path [], gme_t** out, int sample_rate );
+
+/* Number of tracks */
+int gme_track_count( const gme_t* );
+
+/* Starts a track, where 0 is the first track. Requires that 0 <= index < gme_track_count(). */
+gme_err_t gme_start_track( gme_t*, int index );
+
+/* Generates 'count' 16-bit signed samples info 'out'. Output is in stereo, so count
+must be even. */
+gme_err_t gme_play( gme_t*, int count, short out [] );
+
+/* Closes file and frees memory. OK to pass NULL. */
+void gme_delete( gme_t* );
+
+
+/******** Track position/length ********/
+
+/* Sets 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( gme_t*, int start_msec );
+
+/* True if a track has reached its end */
+gme_bool gme_track_ended( const gme_t* );
+
+/* Number of milliseconds played since beginning of track (1000 = one second) */
+int gme_tell( const gme_t* );
+
+/* Seeks to new time in track. Seeking backwards or far forward can take a while. */
+gme_err_t gme_seek( gme_t*, int msec );
+
+
+/******** Informational ********/
+
+/* Use in place of sample rate for open/load if you only need to get track
+information from a music file */
+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( gme_t* );
+
+/* Loads m3u playlist file (must be done after loading music) */
+gme_err_t gme_load_m3u( gme_t*, const char path [] );
+
+/* Clears any loaded m3u playlist and any internal playlist that the music format
+supports (NSFE for example). */
+void gme_clear_playlist( gme_t* );
+
+/* Passes back pointer to information for a particular track (length, name, author, etc.).
+Must be freed after use. */
+typedef struct gme_info_t gme_info_t;
+gme_err_t gme_track_info( const gme_t*, gme_info_t** out, int track );
+
+/* Frees track information */
+void gme_free_info( gme_info_t* );
+
+struct gme_info_t
+{
+ /* times in milliseconds; -1 if unknown */
+ int length; /* total length, if file specifies it */
+ int intro_length; /* length of song up to looping section */
+ int loop_length; /* length of looping section */
+
+ /* Length if available, otherwise intro_length+loop_length*2 if available,
+ otherwise a default of 150000 (2.5 minutes). */
+ int play_length;
+
+ int i4,i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */
+
+ /* empty string ("") if not available */
+ const char* system;
+ const char* game;
+ const char* song;
+ const char* author;
+ const char* copyright;
+ const char* comment;
+ const char* dumper;
+
+ const char *s7,*s8,*s9,*s10,*s11,*s12,*s13,*s14,*s15; /* reserved */
+};
+
+
+/******** Advanced playback ********/
+
+/* Disables automatic end-of-track detection and skipping of silence at beginning
+if ignore is true */
+void gme_ignore_silence( gme_t*, gme_bool ignore );
+
+/* Adjusts song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed, etc.
+Track length as returned by track_info() ignores tempo (assumes it's 1.0). */
+void gme_set_tempo( gme_t*, double tempo );
+
+/* Number of voices used by currently loaded file */
+int gme_voice_count( const gme_t* );
+
+/* Name of voice i, from 0 to gme_voice_count() - 1 */
+const char* gme_voice_name( const gme_t*, int i );
+
+/* Mutes/unmutes single voice i, where voice 0 is first voice */
+void gme_mute_voice( gme_t*, int index, gme_bool mute );
+
+/* Sets 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( gme_t*, 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 */
+ double bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */
+
+ double d2,d3,d4,d5,d6,d7,d8,d9; /* reserved */
+} gme_equalizer_t;
+
+/* Gets current frequency equalizer parameters */
+void gme_equalizer( const gme_t*, gme_equalizer_t* out );
+
+/* Changes frequency equalizer parameters */
+void gme_set_equalizer( gme_t*, gme_equalizer_t const* eq );
+
+
+
+/******** Effects processor ********/
+
+/* Adds stereo surround and echo to music that's usually mono or has little
+stereo. Has no effect on GYM, SPC, and Sega Genesis VGM music. */
+
+/* Simplified control using a single value, where 0.0 = off and 1.0 = maximum */
+void gme_set_stereo_depth( gme_t*, double depth );
+
+struct gme_effects_t
+{
+ double echo; /* Amount of echo, where 0.0 = none, 1.0 = lots */
+ double stereo; /* Separation, where 0.0 = mono, 1.0 = hard left and right */
+
+ double d2,d3,d4,d5,d6,d7; /* reserved */
+
+ gme_bool enabled; /* If 0, no effects are added */
+ gme_bool surround;/* If 1, some channels are put in "back", using phase inversion */
+
+ int i1,i3,i4,i5,i6,i7; /* reserved */
+};
+typedef struct gme_effects_t gme_effects_t;
+
+/* Sets effects configuration, or disables effects if NULL */
+void gme_set_effects( gme_t*, gme_effects_t const* );
+
+/* Passes back current effects configuration */
+void gme_effects( const gme_t*, gme_effects_t* out );
+
+
+/******** Game music types ********/
+
+/* Music file type identifier. Can also hold NULL. */
+typedef const struct gme_type_t_* gme_type_t;
+
+/* Type of this emulator */
+gme_type_t gme_type( const gme_t* );
+
+/* 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();
+
+/* Name of game system for this music file type */
+const char* gme_type_system( gme_type_t );
+
+/* True if this music file type supports multiple tracks */
+gme_bool gme_type_multitrack( gme_type_t );
+
+
+/******** Advanced file loading ********/
+
+/* Same as gme_open_file(), but uses file data already in memory. Makes copy of data. */
+gme_err_t gme_open_data( const char *path, void const* data, long size, gme_t** emu_out, int sample_rate );
+
+/* Determines likely game music type based on first four bytes of file. Returns
+string containing proper file suffix ("NSF", "SPC", etc.) or "" if file header
+is not recognized. */
+const char* gme_identify_header( void const* header );
+
+/* Gets corresponding music type for file path or extension passed in. */
+gme_type_t gme_identify_extension( const char path_or_extension [] );
+
+/* Determines 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 );
+
+/* Creates new emulator and sets sample rate. Returns NULL if out of memory. If you only need
+track information, pass gme_info_only for sample_rate. */
+gme_t* gme_new_emu( gme_type_t, int sample_rate );
+
+/* Loads music file into emulator */
+gme_err_t gme_load_file( gme_t*, const char path [] );
+
+/* Loads music file from memory into emulator. Makes a copy of data passed. */
+gme_err_t gme_load_data( gme_t*, void const* data, long size );
+
+/* Loads 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, int count );
+gme_err_t gme_load_custom( gme_t*, gme_reader_t, long file_size, void* your_data );
+
+/* Loads m3u playlist file from memory (must be done after loading music) */
+gme_err_t gme_load_m3u_data( gme_t*, void const* data, long size );
+
+
+/******** User data ********/
+
+/* Sets/gets pointer to data you want to associate with this emulator.
+You can use this for whatever you want. */
+void gme_set_user_data( gme_t*, void* new_user_data );
+void* gme_user_data( const gme_t* );
+
+/* Registers cleanup function to be called when deleting emulator, or NULL to
+clear it. Passes user_data when calling cleanup function. */
+typedef void (*gme_user_cleanup_t)( void* user_data );
+void gme_set_user_cleanup( gme_t*, gme_user_cleanup_t func );
+
+
+/******** Errors ********/
+
+/* Internally, a gme_err_t is a const char* that points to a normal C string.
+This means that other strings can be passed to the following functions. In the
+descriptions below, these other strings are referred to as being not gme_err_t
+strings. */
+
+/* Error string associated with err. Returns "" if err is NULL. Returns err
+unchanged if it isn't a gme_err_t string. */
+const char* gme_err_str( gme_err_t err );
+
+/* Details of error beyond main cause, or "" if none or err is NULL. Returns
+err unchanged if it isn't a gme_err_t string. */
+const char* gme_err_details( gme_err_t err );
+
+/* Numeric code corresponding to err. Returns gme_ok if err is NULL. Returns
+gme_err_generic if err isn't a gme_err_t string. */
+int gme_err_code( gme_err_t err );
+
+enum {
+ gme_ok = 0,/* Successful call. Guaranteed to be zero. */
+ gme_err_generic = 0x01,/* Error of unspecified type */
+ gme_err_memory = 0x02,/* Out of memory */
+ gme_err_caller = 0x03,/* Caller violated requirements of function */
+ gme_err_internal = 0x04,/* Internal problem, corruption, etc. */
+ gme_err_limitation = 0x05,/* Exceeded program limit */
+
+ gme_err_file_missing = 0x20,/* File not found at specified path */
+ gme_err_file_read = 0x21,/* Couldn't open file for reading */
+ gme_err_file_io = 0x23,/* Read/write error */
+ gme_err_file_eof = 0x25,/* Tried to read past end of file */
+
+ gme_err_file_type = 0x30,/* File is of wrong type */
+ gme_err_file_feature = 0x32,/* File requires unsupported feature */
+ gme_err_file_corrupt = 0x33 /* File is corrupt */
+};
+
+/* gme_err_t corresponding to numeric code. Note that this might not recover
+the original gme_err_t before it was converted to a numeric code; in
+particular, gme_err_details(gme_code_to_err(code)) will be "" in most cases. */
+gme_err_t gme_code_to_err( int code );
+
+
+
+/* Deprecated */
+typedef gme_t Music_Emu;
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/i_fmpac.h b/plugins/gme/game-music-emu-0.6pre/gme/i_fmpac.h
new file mode 100644
index 00000000..fecba35d
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/i_fmpac.h
@@ -0,0 +1,38 @@
+ 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x33, 0x21, 0x09, 0x0E, 0x94, 0x90, 0x48, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x13, 0x41, 0x0F, 0x0D, 0xCE, 0xD3, 0x43, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x12, 0x1B, 0x06, 0xFF, 0xD2, 0x00, 0x32,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x20, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x66, 0x21, 0x15, 0x00, 0x93, 0x94, 0x20, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x10, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x20, 0x1F, 0xC0, 0x71, 0x07, 0x47,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x25, 0x31, 0x26, 0x05, 0x64, 0x41, 0x18, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x17, 0x21, 0x28, 0x07, 0xFF, 0x83, 0x02, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x97, 0x81, 0x25, 0x07, 0xCF, 0xC8, 0x02, 0x14,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x21, 0x54, 0x0F, 0x80, 0x7F, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x56, 0x03, 0xD3, 0xB2, 0x43, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x21, 0x0C, 0x03, 0x82, 0xC0, 0x40, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x01, 0x0C, 0x03, 0xD4, 0xD3, 0x40, 0x84,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x22, 0x00, 0x00, 0xA8, 0xF8, 0xF8, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x25, 0x18, 0x00, 0x00, 0xF8, 0xA9, 0xF8, 0x55,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/i_fmunit.h b/plugins/gme/game-music-emu-0.6pre/gme/i_fmunit.h
new file mode 100644
index 00000000..3030810e
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/i_fmunit.h
@@ -0,0 +1,38 @@
+ 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x1E, 0x07, 0xF0, 0x7E, 0x07, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x13, 0x41, 0x0F, 0x1D, 0xCE, 0xD2, 0x43, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x01, 0x99, 0x04, 0xFF, 0xC3, 0x03, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x40, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x61, 0x1D, 0x07, 0x32, 0x81, 0x10, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x2D, 0x16, 0xC0, 0x70, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x21, 0x1B, 0x06, 0x64, 0x65, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x0C, 0x18, 0x85, 0xA0, 0x79, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x87, 0x11, 0xF0, 0xA4, 0x00, 0xF7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x97, 0xE1, 0x28, 0x07, 0xFF, 0xF3, 0x02, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x10, 0x0C, 0x05, 0xF2, 0xC4, 0x40, 0xC8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x56, 0x03, 0xB4, 0xB2, 0x23, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x41, 0x89, 0x03, 0xF1, 0xF4, 0xF0, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x02, 0x00, 0x00, 0xA7, 0xF7, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x1F, 0x00, 0x00, 0xF8, 0xA9, 0x08, 0x05,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/i_vrc7.h b/plugins/gme/game-music-emu-0.6pre/gme/i_vrc7.h
new file mode 100644
index 00000000..56bbfa9d
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/i_vrc7.h
@@ -0,0 +1,38 @@
+ 0x49, 0x4C, 0x4C, 0x32, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x1E, 0x07, 0xF0, 0x7E, 0x07, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x13, 0x41, 0x0F, 0x1D, 0xCE, 0xD2, 0x43, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x03, 0x01, 0x99, 0x04, 0xFF, 0xC3, 0x03, 0x73,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x21, 0x61, 0x1B, 0x07, 0xAF, 0x63, 0x40, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x31, 0x61, 0x1D, 0x07, 0x32, 0x81, 0x10, 0x17,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x2D, 0x16, 0xC0, 0x70, 0x07, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x21, 0x1B, 0x06, 0x64, 0x65, 0x18, 0x18,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x61, 0x0C, 0x18, 0x85, 0xA0, 0x79, 0x07,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x21, 0x87, 0x11, 0xF0, 0xA4, 0x00, 0xF7,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x97, 0xE1, 0x28, 0x07, 0xFF, 0xF3, 0x02, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x10, 0x0C, 0x05, 0xF2, 0xC4, 0x40, 0xC8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x01, 0x01, 0x56, 0x03, 0xB4, 0xB2, 0x23, 0x58,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x61, 0x41, 0x89, 0x03, 0xF1, 0xF4, 0xF0, 0x13,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x04, 0x21, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x28, 0x21, 0x00, 0x00, 0xA8, 0xF8, 0xF8, 0xF8,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x25, 0x18, 0x00, 0x00, 0xF8, 0xA9, 0xF8, 0x55,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/kmsnddev.h b/plugins/gme/game-music-emu-0.6pre/gme/kmsnddev.h
new file mode 100644
index 00000000..ec7552b1
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/kmsnddev.h
@@ -0,0 +1,31 @@
+/* libnezp by Mamiya */
+
+#ifndef KMSNDDEV_H__
+#define KMSNDDEV_H__
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "nestypes.h"
+
+typedef struct KMIF_SOUND_DEVICE {
+ void *ctx;
+ void (*release)(void *ctx);
+ void (*reset)(void *ctx, Uint32 clock, Uint32 freq);
+ int (*synth)(void *ctx);
+ void (*volume)(void *ctx, Int32 v);
+ void (*write)(void *ctx, Uint32 a, Uint32 v);
+ Uint32 (*read)(void *ctx, Uint32 a);
+ void (*setinst)(void *ctx, Uint32 n, void *p, Uint32 l);
+#if 0
+ void (*setrate)(void *ctx, Uint32 clock, Uint32 freq);
+ void (*getinfo)(void *ctx, KMCH_INFO *cip, );
+ void (*volume2)(void *ctx, Uint8 *volp, Uint32 numch);
+ /* 0x00(mute),0x70(x1/2),0x80(x1),0x90(x2) */
+#endif
+} KMIF_SOUND_DEVICE;
+
+#ifdef __cplusplus
+}
+#endif
+#endif /* KMSNDDEV_H__ */
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/nestypes.h b/plugins/gme/game-music-emu-0.6pre/gme/nestypes.h
new file mode 100644
index 00000000..1fd3582b
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/nestypes.h
@@ -0,0 +1,39 @@
+#ifndef NESTYPES_H__
+#define NESTYPES_H__
+
+#if defined(_MSC_VER)
+#define NEVER_REACH __assume(0);
+#define inline __inline
+#elif defined(__BORLANDC__)
+#define __fastcall __msfastcall
+#elif defined(__GNUC__)
+#define __inline __inline__
+#define __fastcall
+#else
+#define __inline
+#define __fastcall
+#endif
+#ifndef NEVER_REACH
+#define NEVER_REACH
+#endif
+
+typedef int Int;
+typedef unsigned int Uint;
+typedef signed int Int32;
+typedef unsigned int Uint32;
+typedef signed short Int16;
+typedef unsigned short Uint16;
+typedef signed char Int8;
+typedef unsigned char Uint8;
+typedef char Char;
+
+#include <stdlib.h>
+
+#define XSLEEP(n) ((void)0)
+#define XMALLOC(s) malloc(s)
+#define XREALLOC(p,s) realloc(p,s)
+#define XFREE(p) free(p)
+#define XMEMCPY(d,s,n) memcpy(d,s,n)
+#define XMEMSET(d,c,n) memset(d,c,n)
+
+#endif /* NESTYPES_H__ */
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_deltat.c b/plugins/gme/game-music-emu-0.6pre/gme/s_deltat.c
new file mode 100644
index 00000000..5bf299a5
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_deltat.c
@@ -0,0 +1,281 @@
+#include "kmsnddev.h"
+#include "divfix.h"
+#include "s_logtbl.h"
+#include "s_deltat.h"
+#include <string.h>
+
+#define CPS_SHIFT 16
+#define PHASE_SHIFT 16 /* 16(fix) */
+
+typedef struct {
+ KMIF_SOUND_DEVICE kmif;
+ KMIF_LOGTABLE *logtbl;
+ struct YMDELTATPCMSOUND_COMMON_TAG {
+ Int32 mastervolume;
+ Int32 step;
+ Int32 output;
+ Uint32 cnt;
+ Uint32 cps;
+ Uint32 phase;
+ Uint32 deltan;
+ Uint32 scale;
+ Uint32 mem;
+ Uint32 play;
+ Uint32 start;
+ Uint32 stop;
+ Int32 level32;
+ Uint8 key;
+ Uint8 level;
+ Uint8 granuality;
+ Uint8 pad4_3;
+ Uint8 regs[0x10];
+ } common;
+ Uint8 *romrambuf;
+ Uint32 romrammask;
+ Uint8 *rambuf;
+ Uint32 rammask;
+ Uint8 *rombuf;
+ Uint32 rommask;
+ Uint8 ymdeltatpcm_type;
+ Uint8 memshift;
+} YMDELTATPCMSOUND;
+
+const static Uint8 table_step[8] =
+{
+ 1, 3, 5, 7, 9, 11, 13, 15,
+};
+const static Uint8 table_scale[16] =
+{
+ 57, 57, 57, 57, 77, 102, 128, 153,
+ 57, 57, 57, 57, 77, 102, 128, 153,
+};
+
+__inline static void writeram(YMDELTATPCMSOUND *sndp, Uint32 v)
+{
+ sndp->rambuf[(sndp->common.mem >> 1) & sndp->rammask] = v;
+ sndp->common.mem += 1 << 1;
+}
+
+__inline static Uint32 readram(YMDELTATPCMSOUND *sndp)
+{
+ Uint32 v;
+ v = sndp->romrambuf[(sndp->common.play >> 1) & sndp->romrammask];
+ if (sndp->common.play & 1)
+ v &= 0x0F;
+ else
+ v >>= 4;
+ sndp->common.play += 1;
+ if (sndp->common.play >= sndp->common.stop)
+ {
+ if (sndp->common.regs[0] & 0x10)
+ {
+ sndp->common.play = sndp->common.start;
+ sndp->common.step = 0;
+ sndp->common.scale = 127;
+ }
+ else
+ {
+ sndp->common.key = 0;
+ }
+ }
+ return v;
+}
+
+__inline static void DelrtatStep(YMDELTATPCMSOUND *sndp, Uint32 data)
+{
+ if (data & 8)
+ sndp->common.step -= (table_step[data & 7] * sndp->common.scale) >> 3;
+ else
+ sndp->common.step += (table_step[data & 7] * sndp->common.scale) >> 3;
+ if (sndp->common.step > ((1 << 15) - 1)) sndp->common.step = ((1 << 15) - 1);
+ if (sndp->common.step < -(1 << 15)) sndp->common.step = -(1 << 15);
+ sndp->common.scale = (sndp->common.scale * table_scale[data]) >> 6;
+ if (sndp->common.scale > 24576) sndp->common.scale = 24576;
+ if (sndp->common.scale < 127) sndp->common.scale = 127;
+}
+
+#if (((-1) >> 1) == -1)
+#define SSR(x, y) (((Int32)x) >> (y))
+#else
+#define SSR(x, y) (((x) >= 0) ? ((x) >> (y)) : (-((-(x) - 1) >> (y)) - 1))
+#endif
+
+static int sndsynth(YMDELTATPCMSOUND *sndp )
+{
+ if (!sndp->common.key)
+ return 0;
+ {
+ Uint32 step;
+ sndp->common.cnt += sndp->common.cps;
+ step = sndp->common.cnt >> CPS_SHIFT;
+ sndp->common.cnt &= (1 << CPS_SHIFT) - 1;
+ sndp->common.phase += step * sndp->common.deltan;
+ step = sndp->common.phase >> PHASE_SHIFT;
+ sndp->common.phase &= (1 << PHASE_SHIFT) - 1;
+ if (step)
+ {
+ do
+ {
+ DelrtatStep(sndp, readram(sndp));
+ } while (--step);
+ sndp->common.output = sndp->common.step * sndp->common.level32;
+ sndp->common.output = SSR(sndp->common.output, 8 + 2);
+ }
+ }
+ return sndp->common.output;
+}
+
+
+
+static void sndwrite(YMDELTATPCMSOUND *sndp, Uint32 a, Uint32 v)
+{
+ sndp->common.regs[a] = v;
+ switch (a)
+ {
+ /* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
+ case 0x00: /* Control Register 1 */
+ if ((v & 0x80) && !sndp->common.key)
+ {
+ sndp->common.key = 1;
+ sndp->common.play = sndp->common.start;
+ sndp->common.step = 0;
+ sndp->common.scale = 127;
+ }
+ if (v & 1) sndp->common.key = 0;
+ break;
+ /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
+ case 0x01: /* Control Register 2 */
+ sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf;
+ sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask;
+ break;
+ case 0x02: /* Start Address L */
+ case 0x03: /* Start Address H */
+ sndp->common.granuality = (v & 2) ? 1 : 4;
+ sndp->common.start = ((sndp->common.regs[3] << 8) + sndp->common.regs[2]) << (sndp->memshift + 1);
+ sndp->common.mem = sndp->common.start;
+ break;
+ case 0x04: /* Stop Address L */
+ case 0x05: /* Stop Address H */
+ sndp->common.stop = ((sndp->common.regs[5] << 8) + sndp->common.regs[4]) << (sndp->memshift + 1);
+ break;
+ case 0x06: /* Prescale L */
+ case 0x07: /* Prescale H */
+ break;
+ case 0x08: /* Data */
+ if ((sndp->common.regs[0] & 0x60) == 0x60) writeram(sndp, v);
+ break;
+ case 0x09: /* Delta-N L */
+ case 0x0A: /* Delta-N H */
+ sndp->common.deltan = (sndp->common.regs[0xA] << 8) + sndp->common.regs[0x9];
+ if (sndp->common.deltan < 0x100) sndp->common.deltan = 0x100;
+ break;
+ case 0x0B: /* Level Control */
+ sndp->common.level = v;
+ sndp->common.level32 = ((Int32)(sndp->common.level * LogToLin(sndp->logtbl, sndp->common.mastervolume, LOG_LIN_BITS - 15))) >> 7;
+ sndp->common.output = sndp->common.step * sndp->common.level32;
+ sndp->common.output = SSR(sndp->common.output, 8 + 2);
+ break;
+ }
+}
+
+static Uint32 sndread(YMDELTATPCMSOUND *sndp, Uint32 a)
+{
+ return 0;
+}
+
+static void sndreset(YMDELTATPCMSOUND *sndp, Uint32 clock, Uint32 freq)
+{
+ XMEMSET(&sndp->common, 0, sizeof(sndp->common));
+ sndp->common.cps = DivFix(clock, 72 * freq, CPS_SHIFT);
+ sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf;
+ sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask;
+ sndp->common.granuality = 4;
+}
+
+static void sndvolume(YMDELTATPCMSOUND *sndp, Int32 volume)
+{
+ volume = (volume << (LOG_BITS - 8)) << 1;
+ sndp->common.mastervolume = volume;
+ sndp->common.level32 = ((Int32)(sndp->common.level * LogToLin(sndp->logtbl, sndp->common.mastervolume, LOG_LIN_BITS - 15))) >> 7;
+ sndp->common.output = sndp->common.step * sndp->common.level32;
+ sndp->common.output = SSR(sndp->common.output, 8 + 2);
+}
+
+static void sndrelease(YMDELTATPCMSOUND *sndp)
+{
+ if (sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx);
+ XFREE(sndp);
+}
+
+static void setinst(YMDELTATPCMSOUND *sndp, Uint32 n, void *p, Uint32 l)
+{
+ if (n) return;
+ if (p)
+ {
+ sndp->rombuf = (Uint8*) p;
+ sndp->rommask = l - 1;
+ sndp->romrambuf = (sndp->common.regs[1] & 1) ? sndp->rombuf : sndp->rambuf;
+ sndp->romrammask = (sndp->common.regs[1] & 1) ? sndp->rommask : sndp->rammask;
+ }
+ else
+ {
+ sndp->rombuf = 0;
+ sndp->rommask = 0;
+ }
+
+}
+
+KMIF_SOUND_DEVICE *YMDELTATPCMSoundAlloc(Uint32 ymdeltatpcm_type)
+{
+ Uint32 ram_size;
+ YMDELTATPCMSOUND *sndp;
+ switch (ymdeltatpcm_type)
+ {
+ case YMDELTATPCM_TYPE_Y8950:
+ ram_size = 32 * 1024;
+ break;
+ case YMDELTATPCM_TYPE_YM2608:
+ ram_size = 256 * 1024;
+ break;
+ default:
+ ram_size = 0;
+ break;
+ }
+ sndp = (YMDELTATPCMSOUND*) XMALLOC(sizeof(YMDELTATPCMSOUND) + ram_size);
+ if (!sndp) return 0;
+ sndp->ymdeltatpcm_type = ymdeltatpcm_type;
+ switch (ymdeltatpcm_type)
+ {
+ case YMDELTATPCM_TYPE_Y8950:
+ sndp->memshift = 2;
+ break;
+ case YMDELTATPCM_TYPE_YM2608:
+ /* OPNA */
+ sndp->memshift = 6;
+ break;
+ case YMDELTATPCM_TYPE_YM2610:
+ sndp->memshift = 9;
+ break;
+ }
+ sndp->kmif.ctx = sndp;
+ sndp->kmif.release = (void (*)( void* )) sndrelease;
+ sndp->kmif.synth = (int (*)( void* )) sndsynth;
+ sndp->kmif.volume = (void (*)( void*, int )) sndvolume;
+ sndp->kmif.reset = (void (*)( void*, Uint32, Uint32 )) sndreset;
+ sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) sndwrite;
+ sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) sndread;
+ sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) setinst;
+ /* RAM */
+ sndp->rambuf = ram_size ? (Uint8 *)(sndp + 1) : 0;
+ sndp->rammask = ram_size ? (ram_size - 1) : 0;
+ /* ROM */
+ sndp->rombuf = 0;
+ sndp->rommask = 0;
+ sndp->logtbl = LogTableAddRef();
+ if (!sndp->logtbl)
+ {
+ sndrelease(sndp);
+ return 0;
+ }
+ return &sndp->kmif;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_deltat.h b/plugins/gme/game-music-emu-0.6pre/gme/s_deltat.h
new file mode 100644
index 00000000..89c9242c
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_deltat.h
@@ -0,0 +1,23 @@
+#ifndef S_DELTAT_H__
+#define S_DELTAT_H__
+
+#include "kmsnddev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ /* MSX-AUDIO */ YMDELTATPCM_TYPE_Y8950,
+ /* OPNA ADPCM */ YMDELTATPCM_TYPE_YM2608,
+ /* OPNB ADPCMB */ YMDELTATPCM_TYPE_YM2610
+};
+
+KMIF_SOUND_DEVICE *YMDELTATPCMSoundAlloc(Uint32 ymdeltatpcm_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_DELTAT_H__ */
+
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.c b/plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.c
new file mode 100644
index 00000000..0f455f2b
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.c
@@ -0,0 +1,88 @@
+#include "nestypes.h"
+#include "s_logtbl.h"
+
+#if STATIC_TABLES
+
+static void LogTableRelease(void *ctx)
+{
+}
+
+static KMIF_LOGTABLE log_static_tables = {
+ &log_static_tables;
+ LogTableRelease,
+#include "s_logt.h"
+};
+
+
+KMIF_LOGTABLE *LogTableAddRef(void)
+{
+ log_static_tables.release = LogTableRelease;
+ return &log_static_tables;
+}
+
+#else
+
+#include <math.h>
+
+static volatile Uint32 log_tables_mutex = 0;
+static Uint32 log_tables_refcount = 0;
+static KMIF_LOGTABLE *log_tables = 0;
+
+static void LogTableRelease(void *ctx)
+{
+ ++log_tables_mutex;
+ while (log_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ log_tables_refcount--;
+ if (!log_tables_refcount)
+ {
+ XFREE(ctx);
+ log_tables = 0;
+ }
+ --log_tables_mutex;
+}
+
+static void LogTableCalc(KMIF_LOGTABLE *kmif_lt)
+{
+ Uint32 i;
+ double a;
+ for (i = 0; i < (1 << LOG_BITS); i++)
+ {
+ a = (1 << LOG_LIN_BITS) / pow(2, i / (double)(1 << LOG_BITS));
+ kmif_lt->logtbl[i] = (Uint32)a;
+ }
+ kmif_lt->lineartbl[0] = LOG_LIN_BITS << LOG_BITS;
+ for (i = 1; i < (1 << LIN_BITS) + 1; i++)
+ {
+ Uint32 ua;
+ a = i << (LOG_LIN_BITS - LIN_BITS);
+ ua = (Uint32)((LOG_LIN_BITS - (log(a) / log(2))) * (1 << LOG_BITS));
+ kmif_lt->lineartbl[i] = ua << 1;
+ }
+}
+
+KMIF_LOGTABLE *LogTableAddRef(void)
+{
+ ++log_tables_mutex;
+ while (log_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ if (!log_tables_refcount)
+ {
+ log_tables = (KMIF_LOGTABLE*) XMALLOC(sizeof(KMIF_LOGTABLE));
+ if (log_tables)
+ {
+ log_tables->ctx = log_tables;
+ log_tables->release = LogTableRelease;
+ LogTableCalc(log_tables);
+ }
+ }
+ if (log_tables) log_tables_refcount++;
+ --log_tables_mutex;
+ return log_tables;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.h b/plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.h
new file mode 100644
index 00000000..3abc79c1
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_logtbl.h
@@ -0,0 +1,43 @@
+#ifndef S_LOGTBL_H__
+#define S_LOGTBL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LOG_BITS 12
+#define LIN_BITS 7
+#define LOG_LIN_BITS 30
+
+typedef struct
+{
+ void *ctx;
+ void (*release)(void *ctx);
+ Uint32 lineartbl[(1 << LIN_BITS) + 1];
+ Uint32 logtbl[1 << LOG_BITS];
+} KMIF_LOGTABLE;
+
+KMIF_LOGTABLE *LogTableAddRef(void);
+
+__inline static Uint32 LinToLog(KMIF_LOGTABLE *kmif_lt, Int32 l)
+{
+ return (l < 0) ? (kmif_lt->lineartbl[-l] + 1) : kmif_lt->lineartbl[l];
+}
+
+__inline static Int32 LogToLin(KMIF_LOGTABLE *kmif_lt, Int32 l, Uint32 sft)
+{
+ Int32 ret;
+ Uint32 ofs;
+ ofs = l + (sft << (LOG_BITS + 1));
+ sft = ofs >> (LOG_BITS + 1);
+ if (sft >= LOG_LIN_BITS) return 0;
+ ofs = (ofs >> 1) & ((1 << LOG_BITS) - 1);
+ ret = kmif_lt->logtbl[ofs] >> sft;
+ return (l & 1) ? -ret : ret;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_LOGTBL_H__ */
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_opl.c b/plugins/gme/game-music-emu-0.6pre/gme/s_opl.c
new file mode 100644
index 00000000..b2da8056
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_opl.c
@@ -0,0 +1,1244 @@
+/*
+ s_opl.c -- YM2413/Y8950/YM3526/YM3812 emulator by Mamiya, 2001.
+
+ References:
+ fmopl.c -- 1999,2000 written by Tatsuyuki Satoh (MAME development).
+ fmopl.c(fixed) -- 2000 modified by mamiya (NEZplug development).
+ fmgen.cpp -- 1999-2001 written by cisc.
+ emu2413.c -- a YM2413 emulator : written by Mitsutaka Okazaki 2001
+ fmpac.ill -- 2000 created by sama.
+ fmpac.ill -- 2000 created by NARUTO.
+ fmpac.ill -- 2001 created by Okazaki.
+ YM2413 application manual
+*/
+
+#include "kmsnddev.h"
+#include "divfix.h"
+#include "s_logtbl.h"
+#include "s_opltbl.h"
+#include "s_opl.h"
+#include "s_deltat.h"
+#include <string.h>
+
+#define PG_SHIFT 10 /* fix */
+#define CPS_SHIFTE 20
+#define CPS_SHIFTP 14
+#define LFO_SHIFT 16
+
+#define OPLL_INST_WORK 0x40
+#define OPLL_INST_WORK2 (OPLL_INST_WORK + 8 * 0x13)
+
+#define AR_BITS 6 /* fix */
+#define AR_SHIFT 14 /* fix */
+#define EG_SHIFT 15 /* fix */
+
+#define AR_PHASEMAX (((1 << AR_BITS) - 1) << AR_SHIFT)
+#define EG_PHASEMAX (127 << EG_SHIFT)
+#define EG_KEYOFF (128 << EG_SHIFT)
+#define LOG_KEYOFF (31 << (LOG_BITS + 1))
+
+#if 0
+ 0-48dB
+ AR_BITS=6
+ AR_SHIFT=14
+ x = FC000h(20.7618(sec) * 3579545 / 72)cycles 63 * 4000h
+ x / 3 * 4 1730.15(ms)
+ x / 3 * 256 27.03(ms)
+
+ EG_BITS=7
+ EG_SHIFT=15
+ x = 3F8000h(83.7064(sec) * 3579545 / 72)cycles 127 * 8000h
+ x / 4 = 20926.6(ms)
+#endif
+
+
+#define TESTING_OPTIMIZE_AME 1
+#define USE_FBBUF 1
+
+typedef struct
+{
+ Uint32 phase;
+ Uint32 spd;
+ Uint32 rng;
+} OPL_PG;
+
+enum {
+ EG_MODE_ATTACK,
+ EG_MODE_DECAY,
+ EG_MODE_SUSTINE,
+ EG_MODE_RELEASE,
+ EG_MODE_NUM,
+ EG_MODE_SUSHOLD,
+ EG_MODE_OFF = EG_MODE_NUM
+};
+
+typedef struct
+{
+ Uint32 phasear;
+ Uint32 phase;
+ Uint32 spd [EG_MODE_NUM];
+ Uint32 dr_phasemax;
+ Uint8 mode;
+ Uint8 pad4_1;
+ Uint8 pad4_2;
+ Uint8 pad4_3;
+} OPL_EG;
+
+enum
+{
+ FLAG_AME = (1 << 0),
+ FLAG_PME = (1 << 1),
+ FLAG_EGT = (1 << 2),
+ FLAG_KSR = (1 << 3)
+};
+
+typedef struct
+{
+ OPL_PG pg;
+ OPL_EG eg;
+ Int32 input;
+#if USE_FBBUF
+ Int32 fbbuf;
+#endif
+#if TESTING_OPTIMIZE_AME
+ Uint32* amin;
+#endif
+ Uint32 tl_ofs;
+ Uint32* sintable;
+
+ Uint8 modcar; /* 1:m 0:c */
+ Uint8 fb;
+ Uint8 lvl;
+ Uint8 nst;
+
+ Uint8 tll;
+ Uint8 key;
+ Uint8 rkey;
+ Uint8 prevkey;
+
+ Uint8 enable;
+ Uint8 __enablehold__;
+ Uint8 flag;
+ Uint8 ksr;
+
+ Uint8 mul;
+ Uint8 ksl;
+ Uint8 ar;
+ Uint8 dr;
+ Uint8 sl;
+ Uint8 rr;
+ Uint8 tl;
+ Uint8 wf;
+} OPL_OP;
+
+typedef struct
+{
+ OPL_OP op [2];
+ Uint8 con;
+ Uint8 freql;
+ Uint8 freqh;
+ Uint8 blk;
+ Uint8 kcode;
+ Uint8 sus;
+ Uint8 ksb;
+ Uint8 pad4_3;
+} OPL_CH;
+
+enum
+{
+ LFO_UNIT_AM,
+ LFO_UNIT_PM,
+ LFO_UNIT_NUM
+};
+
+typedef struct
+{
+ Uint32 output;
+ Uint32 cnt;
+ Uint32 sps; /* step per sample */
+ Uint32 adr;
+ Uint32 adrmask;
+ Uint32* table;
+} OPL_LFO;
+
+typedef struct
+{
+ KMIF_SOUND_DEVICE kmif;
+ KMIF_SOUND_DEVICE* deltatpcm;
+ KMIF_LOGTABLE* logtbl;
+ KMIF_OPLTABLE* opltbl;
+ OPL_CH ch [9];
+ OPL_LFO lfo [LFO_UNIT_NUM];
+ struct OPLSOUND_COMMON_TAG
+ {
+ Uint32 cpsp;
+ Uint32 cnt;
+ Uint32* ar_table;
+ Uint32* tll2logtbl;
+#if TESTING_OPTIMIZE_AME
+ Uint32 amzero;
+#endif
+ Int32 mastervolume;
+ Uint32 sintablemask;
+ Uint32 ratetbla [4];
+ Uint32 ratetbl [4];
+ Uint8 adr;
+ Uint8 wfe;
+ Uint8 rc;
+ Uint8 rmode;
+ Uint8 enable;
+ } common;
+ Uint8 regs [0x100];
+ Uint8 opl_type;
+} OPLSOUND;
+
+static Uint8 romtone [3] [16 * 19] =
+{
+ {
+#include "i_fmpac.h"
+ },
+ {
+#include "i_fmunit.h"
+ },
+ {
+#include "i_vrc7.h"
+ },
+};
+
+static void SetOpOff(OPL_OP* opp )
+{
+ opp->eg.mode = EG_MODE_OFF;
+ opp->eg.phase = EG_KEYOFF;
+ opp->enable = 0;
+}
+
+inline static void EgStep( OPLSOUND* sndp, OPL_OP* opp )
+{
+ switch ( opp->eg.mode )
+ {
+ default:
+ NEVER_REACH
+
+ case EG_MODE_ATTACK:
+ opp->eg.phase = sndp->common.ar_table [opp->eg.phasear >> (AR_SHIFT + AR_BITS - ARTBL_BITS)] >> (ARTBL_SHIFT - EG_SHIFT);
+ opp->eg.phasear += opp->eg.spd [EG_MODE_ATTACK];
+ if ( opp->eg.phasear >= AR_PHASEMAX )
+ {
+ opp->eg.mode = EG_MODE_DECAY;
+ opp->eg.phase = 0;
+ }
+ break;
+
+ case EG_MODE_DECAY:
+ opp->eg.phase += opp->eg.spd [EG_MODE_DECAY];
+ if ( opp->eg.phase >= opp->eg.dr_phasemax )
+ {
+ opp->eg.phase = opp->eg.dr_phasemax;
+ opp->eg.mode = (opp->flag & FLAG_EGT) ? EG_MODE_SUSHOLD : EG_MODE_SUSTINE;
+ }
+ break;
+
+ case EG_MODE_SUSTINE:
+ case EG_MODE_RELEASE:
+ opp->eg.phase += opp->eg.spd [opp->eg.mode];
+ if ( opp->eg.phase >= EG_PHASEMAX )
+ SetOpOff(opp);
+ break;
+
+ case EG_MODE_SUSHOLD:
+ case EG_MODE_OFF:
+ break;
+ }
+}
+
+static void OpStep( OPLSOUND* sndp, OPL_OP* opp )
+{
+ int step;
+ EgStep(sndp, opp);
+ step = opp->pg.spd;
+ if ( opp->flag & FLAG_PME )
+ step = (step * sndp->lfo [LFO_UNIT_PM].output) >> PM_SHIFT;
+ opp->pg.phase += step;
+}
+
+__inline static void OpStepNG( OPLSOUND* sndp, OPL_OP* opp )
+{
+ Uint32 step;
+ EgStep(sndp, opp);
+ opp->pg.phase += opp->pg.spd;
+ step = opp->pg.phase >> opp->nst/*(PG_SHIFT + 5)*/;
+ opp->pg.phase &= (1 << opp->nst/*(PG_SHIFT + 5)*/) - 1;
+ while ( step-- )
+ {
+ opp->pg.rng ^= ((opp->pg.rng & 1) << 16) + ((opp->pg.rng & 1) << 13);
+ opp->pg.rng >>= 1;
+ }
+}
+
+#if -1 >> 1 == -1
+/* RIGHT SHIFT IS SIGNED */
+#define SSR(x, y) ((Int32)(x) >> (y))
+#else
+/* RIGHT SHIFT IS UNSIGNED */
+#define SSR(x, y) (((x) >= 0) ? ((x) >> (y)) : (-((-(x) - 1) >> (y)) - 1))
+#endif
+
+
+inline static void OpSynthMod( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ Int32 output;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+#if TESTING_OPTIMIZE_AME
+ tll += *opp->amin;
+#else
+ if ( opp->flag & FLAG_AME )
+ tll += sndp->lfo [LFO_UNIT_AM].output;
+#endif
+ tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))];
+ output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2)));
+ if ( opp->fb )
+ {
+#if USE_FBBUF
+ Int32 fbtmp;
+ fbtmp = opp->fbbuf + output;
+ opp->fbbuf = output;
+ opp->input = SSR(fbtmp, (9 - opp->fb));
+#else
+ opp->input = SSR(output, (8 - opp->fb));
+#endif
+ }
+ opp [1].input = output;
+ }
+}
+
+inline static Int32 OpSynthCarFb( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+#if TESTING_OPTIMIZE_AME
+ tll +=* opp->amin;
+#else
+ if ( opp->flag & FLAG_AME) tll += sndp->lfo [LFO_UNIT_AM].output;
+#endif
+ tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))];
+ if ( opp->fb )
+ {
+#if USE_FBBUF
+ Int32 output, fbtmp;
+ output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2)));
+ fbtmp = opp->fbbuf + output;
+ opp->fbbuf = output;
+ opp->input = SSR(fbtmp, (9 - opp->fb));
+#else
+ Int32 output;
+ output = LogToLin(sndp->logtbl, tll, -8 + ((LOG_LIN_BITS + 1) - (SINTBL_BITS + 2)));
+ opp->input = SSR(output, (8 - opp->fb));
+#endif
+ }
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+inline static Int32 OpSynthCar( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+#if TESTING_OPTIMIZE_AME
+ tll += *opp->amin;
+#else
+ if ( opp->flag & FLAG_AME )
+ tll += sndp->lfo [LFO_UNIT_AM].output;
+#endif
+ tll += opp->sintable [sndp->common.sintablemask & (opp->input + (opp->pg.phase >> PG_SHIFT))];
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+inline static Int32 OpSynthTom( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStep(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT);
+ tll = (tll >= 128 - 16) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+ tll += opp->sintable [sndp->common.sintablemask & (opp->pg.phase >> PG_SHIFT)];
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+
+static Int32 OpSynthRym( OPLSOUND* sndp, OPL_OP* opp )
+{
+ if ( opp->enable )
+ {
+ Uint32 tll;
+ OpStepNG(sndp, opp);
+ tll = opp->tll + (opp->eg.phase >> EG_SHIFT) + 0x10/* +6dB */;
+ tll = (tll >= (1 << TLLTBL_BITS)) ? LOG_KEYOFF : sndp->common.tll2logtbl [tll];
+ tll += opp->tl_ofs;
+ tll += (opp->pg.rng & 1);
+ return LogToLin(sndp->logtbl, tll + sndp->common.mastervolume, -8 + LOG_LIN_BITS - 19 - opp->lvl);
+ }
+ return 0;
+}
+
+inline static void LfoStep(OPL_LFO* lfop )
+{
+ lfop->cnt += lfop->sps;
+ lfop->adr += lfop->cnt >> LFO_SHIFT;
+ lfop->cnt &= (1 << LFO_SHIFT) - 1;
+ lfop->output = lfop->table [lfop->adr & lfop->adrmask];
+}
+
+static int sndsynth( OPLSOUND* sndp )
+{
+ Int32 accum = 0;
+ if ( sndp->common.enable )
+ {
+ Uint32 i, rch;
+ for ( i = 0; i < LFO_UNIT_NUM; i++ )
+ LfoStep(&sndp->lfo [i]);
+
+ rch = sndp->common.rmode ? 7 : 9;
+ for ( i = 0; i < rch; i++ )
+ {
+ if ( sndp->ch [i].op [0].modcar )
+ OpSynthMod(sndp, &sndp->ch [i].op [0]);
+ else
+ accum += OpSynthCarFb(sndp, &sndp->ch [i].op [0]);
+ accum += OpSynthCar(sndp, &sndp->ch [i].op [1]);
+ }
+ if ( sndp->common.rmode )
+ {
+ accum += OpSynthRym(sndp, &sndp->ch [7].op [0]);
+ accum += OpSynthRym(sndp, &sndp->ch [7].op [1]);
+ accum += OpSynthTom(sndp, &sndp->ch [8].op [0]);
+ accum += OpSynthRym(sndp, &sndp->ch [8].op [1]);
+ }
+ }
+ if ( sndp->deltatpcm )
+ {
+ accum += sndp->deltatpcm->synth(sndp->deltatpcm->ctx);
+ }
+#if 0
+ /* NISE DAC */
+ if ( accum >= 0 )
+ accum = (Int32)(((Uint32) accum) & (((1 << 8) - 1) << (23 - 8)));
+ else
+ accum = -(Int32)(((Uint32)-accum) & (((1 << 8) - 1) << (23 - 8)));
+#endif
+ return accum;
+}
+
+static void sndvolume( OPLSOUND* sndp, Int32 volume )
+{
+ if ( sndp->deltatpcm) sndp->deltatpcm->volume(sndp->deltatpcm->ctx, volume);
+ volume = (volume << (LOG_BITS - 8)) << 1;
+ sndp->common.mastervolume = volume;
+}
+
+const static Uint8 op_table [0x20]=
+{
+ 0, 2, 4, 1, 3, 5,0xFF,0xFF,
+ 6, 8, 10, 7, 9, 11,0xFF,0xFF,
+ 12, 14, 16, 13, 15, 17,0xFF,0xFF,
+ 0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,
+};
+
+const static Uint8 mul_table [0x10]=
+{
+ 1+0, 2+0, 4+0, 2+4, 8+0, 8+2, 8+4,16-2,
+ 16+0,16+2,16+4,16+4,16+8,16+8,32-2,32-2,
+};
+
+#define DB2TLL(x) (x * 2 / 375 )
+const static Uint8 ksl_table [8] [16]=
+{
+ {
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 750), DB2TLL( 1125), DB2TLL( 1500),
+ DB2TLL( 1875), DB2TLL( 2250), DB2TLL( 2625), DB2TLL( 3000),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 0),
+ DB2TLL( 0), DB2TLL( 1125), DB2TLL( 1875), DB2TLL( 2625),
+ DB2TLL( 3000), DB2TLL( 3750), DB2TLL( 4125), DB2TLL( 4500),
+ DB2TLL( 4875), DB2TLL( 5250), DB2TLL( 5625), DB2TLL( 6000),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 0), DB2TLL( 1875),
+ DB2TLL( 3000), DB2TLL( 4125), DB2TLL( 4875), DB2TLL( 5625),
+ DB2TLL( 6000), DB2TLL( 6750), DB2TLL( 7125), DB2TLL( 7500),
+ DB2TLL( 7875), DB2TLL( 8250), DB2TLL( 8625), DB2TLL( 9000),
+ },{
+ DB2TLL( 0), DB2TLL( 0), DB2TLL( 3000), DB2TLL( 4875),
+ DB2TLL( 6000), DB2TLL( 7125), DB2TLL( 7875), DB2TLL( 8625),
+ DB2TLL( 9000), DB2TLL( 9750), DB2TLL(10125), DB2TLL(10500),
+ DB2TLL(10875), DB2TLL(11250), DB2TLL(11625), DB2TLL(12000),
+ },{
+ DB2TLL( 0), DB2TLL( 3000), DB2TLL( 6000), DB2TLL( 7875),
+ DB2TLL( 9000), DB2TLL(10125), DB2TLL(10875), DB2TLL(11625),
+ DB2TLL(12000), DB2TLL(12750), DB2TLL(13125), DB2TLL(13500),
+ DB2TLL(13875), DB2TLL(14250), DB2TLL(14625), DB2TLL(15000),
+ },{
+ DB2TLL( 0), DB2TLL( 6000), DB2TLL( 9000), DB2TLL(10875),
+ DB2TLL(12000), DB2TLL(13125), DB2TLL(13875), DB2TLL(14625),
+ DB2TLL(15000), DB2TLL(15750), DB2TLL(16125), DB2TLL(16500),
+ DB2TLL(16875), DB2TLL(17250), DB2TLL(17625), DB2TLL(18000),
+ },{
+ DB2TLL( 0), DB2TLL( 9000), DB2TLL(12000), DB2TLL(13875),
+ DB2TLL(15000), DB2TLL(16125), DB2TLL(16875), DB2TLL(17625),
+ DB2TLL(18000), DB2TLL(18750), DB2TLL(19125), DB2TLL(19500),
+ DB2TLL(19875), DB2TLL(20250), DB2TLL(20625), DB2TLL(21000),
+ }
+};
+#undef DB2TLL
+
+static Uint32 rateconvAR( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr )
+{
+ if ( !rrr )
+ return 0;
+ rrr = rrr + (ksr >> 2);
+ if ( rrr >= 15)
+ return AR_PHASEMAX;
+ return sndp->common.ratetbla [ksr & 3] >> (CPS_SHIFTE + 1 - rrr);
+}
+
+static Uint32 rateconv( OPLSOUND* sndp, Uint32 rrr, Uint32 ksr )
+{
+ if ( !rrr )
+ return 0;
+ rrr = rrr + (ksr >> 2);
+ if ( rrr > 15 )
+ rrr = 15;
+ return sndp->common.ratetbl [ksr & 3] >> (CPS_SHIFTE + 1 - rrr);
+}
+
+static void OpUpdateWF( OPLSOUND* sndp, OPL_OP* opp )
+{
+ opp->sintable = sndp->opltbl->sin_table [opp->wf & sndp->common.wfe];
+}
+
+static void OpUpdatePG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp )
+{
+ opp->pg.spd = (((chp->freqh << 8) + chp->freql) * opp->mul * sndp->common.cpsp) >> (CPS_SHIFTP - chp->blk);
+}
+
+static void OpUpdateEG( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp )
+{
+ Uint32 sr, rr;
+ opp->ksr = chp->kcode >> ((opp->flag & FLAG_KSR) ? 0 : 2);
+ opp->eg.dr_phasemax = opp->sl << (1 + 2 + EG_SHIFT); /* 3dB->eg */
+ opp->eg.spd [EG_MODE_ATTACK] = rateconvAR(sndp, opp->ar, opp->ksr);
+ opp->eg.spd [EG_MODE_DECAY] = rateconv(sndp, opp->dr, opp->ksr);
+ if ( opp->flag & FLAG_EGT )
+ {
+ if ( opp->eg.mode == EG_MODE_SUSTINE )
+ opp->eg.mode = EG_MODE_SUSHOLD;
+ sr = 0;
+ rr = opp->rr;
+ }
+ else
+ {
+ if ( opp->eg.mode == EG_MODE_SUSHOLD )
+ opp->eg.mode = EG_MODE_SUSTINE;
+ sr = opp->rr;
+ rr = 7;
+ }
+ if ( chp->sus )
+ {
+ rr = 5;
+ }
+ opp->eg.spd [EG_MODE_SUSTINE] = rateconv(sndp, sr, opp->ksr);
+ opp->eg.spd [EG_MODE_RELEASE] = rateconv(sndp, rr, opp->ksr);
+}
+
+static void OpUpdateTLL( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp )
+{
+ opp->tll = (opp->tl + (chp->ksb >> opp->ksl)) << 1;
+}
+
+
+
+static void oplsetopmul( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR);
+#if TESTING_OPTIMIZE_AME
+ if ( v & 0x80 )
+ opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero;
+#else
+ if ( v & 0x80 )
+ opp->flag |= FLAG_AME;
+#endif
+ if ( v & 0x40 ) opp->flag |= FLAG_PME;
+ if ( v & 0x20 ) opp->flag |= FLAG_EGT;
+ if ( v & 0x10 ) opp->flag |= FLAG_KSR;
+ opp->mul = mul_table [v & 0x0F];
+ OpUpdateEG(sndp, chp, opp);
+ OpUpdatePG(sndp, chp, opp);
+}
+
+static void oplsetopkstl( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->ksl = (v >> 6) ? (3 - (v >> 6)) : 15; /* 0 / 1.5 / 3 / 6 db/OCT */
+ opp->tl = v & 0x3F; /* 0.75 db */
+ OpUpdateTLL(sndp, chp, opp);
+}
+
+static void oplsetopardr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->ar = v >> 4;
+ opp->dr = v & 0xF;
+ OpUpdateEG(sndp, chp, opp);
+}
+
+static void oplsetopslrr( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->sl = v >> 4;
+ opp->rr = v & 0xF;
+ OpUpdateEG(sndp, chp, opp);
+}
+
+static void oplsetopwf( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->wf = v & 0x3;
+ OpUpdateWF(sndp, opp);
+}
+
+static void oplsetopkey( OPLSOUND* sndp, OPL_OP* opp )
+{
+ Uint8 nextkey = ((sndp->common.rmode) && opp->rkey) || opp->key;
+ if ( opp->prevkey ^ nextkey )
+ {
+ opp->prevkey = nextkey;
+ if ( nextkey )
+ {
+ sndp->common.enable = 1;
+ opp->eg.mode = EG_MODE_ATTACK;
+ opp->eg.phase = EG_KEYOFF;
+ opp->enable = 1;
+ opp->eg.phasear = 0;
+ }
+ else if ( !opp->modcar && opp->eg.mode != EG_MODE_OFF )
+ opp->eg.mode = EG_MODE_RELEASE;
+ }
+}
+
+static void oplsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ chp->freql = v & 0xFF;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)];
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void oplsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ Uint32 key = v & 0x20;
+ chp->kcode = (v >> 1) & 15;
+ chp->freqh = v & 3;
+ chp->blk = (v >> 2) & 7;
+ chp->op [0].key = chp->op [1].key = key;
+ oplsetopkey(sndp, &chp->op [0]);
+ oplsetopkey(sndp, &chp->op [1]);
+ chp->sus = 0;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 2) + (chp->freql >> 6)];
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ OpUpdateEG(sndp, chp, &chp->op [1]);
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void oplsetchfbcon( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ chp->op [0].fb = (v >> 1) & 7;
+#if USE_FBBUF
+ chp->op [0].fbbuf = 0;
+#endif
+ chp->con = v & 1;
+ chp->op [0].modcar = (chp->con) ? 0 : 1;
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ chp->op [1].input = 0;
+}
+
+static void opllsetopvolume( OPLSOUND* sndp, OPL_CH* chp, OPL_OP* opp, Uint32 v )
+{
+ opp->tl = v;
+ OpUpdateTLL(sndp, chp, opp);
+}
+
+static void opllsetchfreql( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ chp->freql = v & 0xFF;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)];
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void opllsetchfreqh( OPLSOUND* sndp, OPL_CH* chp, Uint32 v )
+{
+ Uint32 key = v & 0x10;
+ chp->kcode = v & 15;
+ chp->freqh = v & 1;
+ chp->blk = (v >> 1) & 7;
+ chp->op [0].key = chp->op [1].key = key;
+ oplsetopkey(sndp, &chp->op [0]);
+ oplsetopkey(sndp, &chp->op [1]);
+ chp->sus = v & 0x20;
+ chp->ksb = ksl_table [chp->blk] [(chp->freqh << 3) + (chp->freql >> 5)];
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ OpUpdateEG(sndp, chp, &chp->op [1]);
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void SetOpTone( OPLSOUND* sndp, OPL_OP* opp, Uint8* tonep )
+{
+ opp->flag &= ~(FLAG_AME | FLAG_PME | FLAG_EGT | FLAG_KSR);
+#if TESTING_OPTIMIZE_AME
+ if ( tonep [0] & 0x80 ) opp->amin = &sndp->lfo [LFO_UNIT_AM].output; else opp->amin = &sndp->common.amzero;
+#else
+ if ( tonep [0] & 0x80 ) opp->flag |= FLAG_AME;
+#endif
+ if ( tonep [0] & 0x40 ) opp->flag |= FLAG_PME;
+ if ( tonep [0] & 0x20 ) opp->flag |= FLAG_EGT;
+ if ( tonep [0] & 0x10 ) opp->flag |= FLAG_KSR;
+ opp->mul = mul_table [tonep [0] & 0x0F] << 1;
+ opp->ksl = (tonep [2] >> 6) ? (3 - (tonep [2] >> 6)) : 15;
+ opp->ar = tonep [4] >> 4;
+ opp->dr = tonep [4] & 0xF;
+ opp->sl = tonep [6] >> 4;
+ opp->rr = tonep [6] & 0xF;
+}
+static void SetChTone( OPLSOUND* sndp, OPL_CH* chp, Uint8* tonep, Uint8* tlofsp )
+{
+ Uint32 op;
+ for ( op = 0; op < 2; op++ )
+ SetOpTone(sndp, &chp->op [op], &tonep [op]);
+ chp->op [0].tl_ofs = (tlofsp [0] ^ 0x80) << (LOG_BITS - 4 + 1);
+ chp->op [1].tl_ofs = (tlofsp [1] ^ 0x80) << (LOG_BITS - 4 + 1);
+ chp->op [0].tl = tonep [2] & 0x3F;
+ chp->op [0].fb = tonep [3] & 0x7;
+ chp->op [0].wf = (tonep [3] >> 3) & 1;
+ chp->op [1].wf = (tonep [3] >> 4) & 1;
+#if USE_FBBUF
+ chp->op [0].fbbuf = 0;
+#endif
+ chp->op [1].input = 0;
+ OpUpdateWF(sndp, &chp->op [0]);
+ OpUpdateWF(sndp, &chp->op [1]);
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ OpUpdateEG(sndp, chp, &chp->op [1]);
+ OpUpdatePG(sndp, chp, &chp->op [0]);
+ OpUpdatePG(sndp, chp, &chp->op [1]);
+ OpUpdateTLL(sndp, chp, &chp->op [0]);
+ OpUpdateTLL(sndp, chp, &chp->op [1]);
+}
+
+static void opllsetchtone( OPLSOUND* sndp, OPL_CH* chp, Uint32 tone )
+{
+ SetChTone(sndp, chp, &sndp->regs [OPLL_INST_WORK + (tone << 3)], &sndp->regs [OPLL_INST_WORK2 + (tone << 1) + 0]);
+}
+
+static void recovercon( OPLSOUND* sndp, OPL_CH* chp )
+{
+ chp->op [0].modcar = (chp->con) ? 0 : 1;
+ chp->op [0].lvl = chp->con ? 1 : 0;
+ chp->op [1].lvl = 1;
+ OpUpdateEG(sndp, chp, &chp->op [0]);
+ chp->op [1].input = 0;
+}
+
+static void initrc_common( OPLSOUND* sndp, Uint32 rmode )
+{
+ if ( rmode )
+ {
+ /* BD */
+ sndp->ch [6].op [0].modcar = 1;
+ sndp->ch [6].op [0].lvl = 0;
+ OpUpdateEG(sndp, &sndp->ch [6], &sndp->ch [6].op [0]);
+ sndp->ch [6].op [1].input = 0;
+ sndp->ch [6].op [1].lvl = 2;
+ /* CYM */
+ sndp->ch [7].op [0].modcar = 0;
+ sndp->ch [7].op [0].lvl = 1;
+ OpUpdateEG(sndp, &sndp->ch [7], &sndp->ch [7].op [0]);
+ /* SD */
+ sndp->ch [7].op [1].input = 0;
+ sndp->ch [7].op [1].lvl = 2;
+ /* TOM */
+ sndp->ch [8].op [0].modcar = 0;
+ sndp->ch [8].op [0].lvl = 2;
+ OpUpdateEG(sndp, &sndp->ch [8], &sndp->ch [8].op [0]);
+ /* HH */
+ sndp->ch [8].op [1].input = 0;
+ sndp->ch [8].op [1].lvl = 1;
+ }
+ else
+ {
+ recovercon(sndp, &sndp->ch [6]);
+ if ( !sndp->ch [6].op [0].key ) SetOpOff(&sndp->ch [6].op [0]);
+ if ( !sndp->ch [6].op [1].key ) SetOpOff(&sndp->ch [6].op [1]);
+ recovercon(sndp, &sndp->ch [7]);
+ if ( !sndp->ch [7].op [0].key ) SetOpOff(&sndp->ch [7].op [0]);
+ if ( !sndp->ch [7].op [1].key ) SetOpOff(&sndp->ch [7].op [1]);
+ recovercon(sndp, &sndp->ch [8]);
+ if ( !sndp->ch [8].op [0].key ) SetOpOff(&sndp->ch [8].op [0]);
+ if ( !sndp->ch [8].op [1].key ) SetOpOff(&sndp->ch [8].op [1]);
+ }
+}
+
+static void oplsetrc( OPLSOUND* sndp, Uint32 rc )
+{
+ sndp->lfo [LFO_UNIT_AM].table = (rc & 0x80) ? sndp->opltbl->am_table1 : sndp->opltbl->am_table2;
+ sndp->lfo [LFO_UNIT_PM].table = (rc & 0x40) ? sndp->opltbl->pm_table1 : sndp->opltbl->pm_table2;
+ if ( (sndp->common.rmode ^ rc) & 0x20 )
+ {
+ if ( rc & 0x20 )
+ {
+#if 0
+ static Uint8 volini [2] = { 0, 0 };
+ static Uint8 bdtone [8] = { 0x04, 0x20, 0x28, 0x00, 0xDF, 0xF8, 0xFF, 0xF8 };
+ SetChTone(sndp, &sndp->ch [6], bdtone, volini);
+ SetChTone(sndp, &sndp->ch [7], &romtone [0] [0x11 << 4], volini);
+ SetChTone(sndp, &sndp->ch [8], &romtone [0] [0x12 << 4], volini);
+#endif
+ sndp->ch [7].op [0].nst = PG_SHIFT + 4;
+ sndp->ch [7].op [1].nst = PG_SHIFT + 6;
+ sndp->ch [8].op [1].nst = PG_SHIFT + 5;
+ }
+ initrc_common(sndp, rc & 0x20);
+ }
+ sndp->common.rmode = rc & 0x20;
+ sndp->common.rc = rc & 0x1F;
+ /* BD */
+ sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10;
+ oplsetopkey(sndp, &sndp->ch [6].op [0]);
+ oplsetopkey(sndp, &sndp->ch [6].op [1]);
+ /* CYM */
+ sndp->ch [7].op [0].rkey = rc & 0x01;
+ oplsetopkey(sndp, &sndp->ch [7].op [0]);
+ /* SD */
+ sndp->ch [7].op [1].rkey = rc & 0x08;
+ oplsetopkey(sndp, &sndp->ch [7].op [1]);
+ /* TOM */
+ sndp->ch [8].op [0].rkey = rc & 0x04;
+ oplsetopkey(sndp, &sndp->ch [8].op [0]);
+ /* HH */
+ sndp->ch [8].op [1].rkey = rc & 0x02;
+ oplsetopkey(sndp, &sndp->ch [8].op [1]);
+}
+
+static void opllsetrc( OPLSOUND* sndp, Uint32 rc )
+{
+ if ( (sndp->common.rmode ^ rc) & 0x20 )
+ {
+ if ( rc & 0x20 )
+ {
+ opllsetchtone(sndp, &sndp->ch [6], 0x10);
+ opllsetchtone(sndp, &sndp->ch [7], 0x11);
+ opllsetchtone(sndp, &sndp->ch [8], 0x12);
+ opllsetopvolume(sndp, &sndp->ch [7], &sndp->ch [7].op [0], (sndp->regs [0x37] & 0xF0) >> 2);
+ opllsetopvolume(sndp, &sndp->ch [8], &sndp->ch [8].op [0], (sndp->regs [0x38] & 0xF0) >> 2);
+ sndp->ch [7].op [0].nst = PG_SHIFT + 5;
+ sndp->ch [7].op [1].nst = PG_SHIFT + 5;
+ sndp->ch [8].op [1].nst = PG_SHIFT + 5;
+ }
+ else
+ {
+ opllsetchtone(sndp, &sndp->ch [6], sndp->regs [0x36]>>4);
+ opllsetchtone(sndp, &sndp->ch [7], sndp->regs [0x37]>>4);
+ opllsetchtone(sndp, &sndp->ch [8], sndp->regs [0x38]>>4);
+ }
+ initrc_common(sndp, rc & 0x20);
+ }
+ sndp->common.rmode = rc & 0x20;
+ sndp->common.rc = rc & 0x1F;
+ /* BD */
+ sndp->ch [6].op [0].rkey = sndp->ch [6].op [1].rkey = rc & 0x10;
+ oplsetopkey(sndp, &sndp->ch [6].op [0]);
+ oplsetopkey(sndp, &sndp->ch [6].op [1]);
+ /* CYM */
+ sndp->ch [7].op [0].rkey = rc & 0x01;
+ oplsetopkey(sndp, &sndp->ch [7].op [0]);
+ /* SD */
+ sndp->ch [7].op [1].rkey = rc & 0x08;
+ oplsetopkey(sndp, &sndp->ch [7].op [1]);
+ /* TOM */
+ sndp->ch [8].op [0].rkey = rc & 0x04;
+ oplsetopkey(sndp, &sndp->ch [8].op [0]);
+ /* HH */
+ sndp->ch [8].op [1].rkey = rc & 0x02;
+ oplsetopkey(sndp, &sndp->ch [8].op [1]);
+}
+
+#define OPLSETOP(func) { \
+ Uint32 op = op_table [a & 0x1F]; \
+ if ( op != 0xFF) func(sndp, &sndp->ch [op >> 1], &sndp->ch [op >> 1].op [op & 1], v); \
+}
+
+__inline static void oplwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ switch ( a >> 5 )
+ {
+ default:
+ NEVER_REACH
+
+ case 0:
+ switch ( a & 0x1F )
+ {
+ case 0x01:
+ if ( sndp->opl_type == OPL_TYPE_OPL2 )
+ {
+ Uint32 i;
+ sndp->common.wfe = (v & 0x20) ? 3 : 0;
+ for ( i = 0; i < 9; i++ )
+ {
+ OpUpdateWF(sndp, &sndp->ch [i].op [0]);
+ OpUpdateWF(sndp, &sndp->ch [i].op [1]);
+ }
+ }
+ break;
+
+ case 0x08:
+ /* CSM mode */
+ case 0x07: case 0x09: case 0x0A: case 0x0B: case 0x0C:
+ case 0x0D: case 0x0E: case 0x0F: case 0x10: case 0x11: case 0x12:
+ if ( sndp->deltatpcm )
+ sndp->deltatpcm->write(sndp->deltatpcm->ctx, a - 0x07, v);
+ break;
+ }
+ break;
+
+ case 1: OPLSETOP(oplsetopmul); break;
+ case 2: OPLSETOP(oplsetopkstl); break;
+ case 3: OPLSETOP(oplsetopardr); break;
+ case 4: OPLSETOP(oplsetopslrr); break;
+ case 7: OPLSETOP(oplsetopwf); break;
+ case 5:
+ if ( (a & 0x1F) == (0xBD & 0x1F) )
+ oplsetrc(sndp, v);
+ else if ( (a & 0x1F) < 9 )
+ oplsetchfreql(sndp, &sndp->ch [a & 0xF], v);
+ else if ( (a & 0xF) < 9 )
+ oplsetchfreqh(sndp, &sndp->ch [a & 0xF], v);
+ break;
+
+ case 6:
+ if ( (a & 0x1F) < 9) oplsetchfbcon(sndp, &sndp->ch [a & 0xF], v);
+ break;
+ }
+}
+
+static void oplwrite( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ if ( a & 1 )
+ {
+ sndp->regs [sndp->common.adr] = v;
+ oplwritereg(sndp, sndp->common.adr, v);
+ }
+ else
+ sndp->common.adr = v;
+}
+
+static Uint32 oplread( OPLSOUND* sndp, Uint32 a )
+{
+ if ( a & 1 )
+ return sndp->regs [sndp->common.adr];
+ else
+ return 0x80;
+}
+
+__inline static void opllwritereg( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ switch ( a >> 3 )
+ {
+ default:
+ NEVER_REACH
+ case 0:
+ sndp->regs [OPLL_INST_WORK + (a & 7)] = v;
+ break;
+
+ case 1:
+ if ( a == 0xE) opllsetrc(sndp, v & 0x3F);
+ break;
+
+ case 2:
+ case 3:
+ a &= 0xF;
+ if ( a < 9) opllsetchfreql(sndp, &sndp->ch [a], v);
+ break;
+
+ case 4:
+ case 5:
+ a &= 0xF;
+ if ( a < 9) opllsetchfreqh(sndp, &sndp->ch [a], v);
+ break;
+
+ case 6:
+ case 7:
+ a &= 0xF;
+ if ( a < 9 )
+ {
+ if ( (sndp->common.rmode) && (a >= 6) )
+ {
+ if ( a != 6) opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [0], (v & 0xF0) >> 2);
+ }
+ else
+ {
+ opllsetchtone(sndp, &sndp->ch [a], (v & 0xF0) >> 4);
+ }
+ opllsetopvolume(sndp, &sndp->ch [a], &sndp->ch [a].op [1], (v & 0xF) << 2);
+ }
+ break;
+ }
+}
+
+static void opllwrite( OPLSOUND* sndp, Uint32 a, Uint32 v )
+{
+ if ( a & 1 )
+ {
+ if ( sndp->common.adr < 0x40 )
+ {
+ sndp->regs [sndp->common.adr] = v;
+ opllwritereg(sndp, sndp->common.adr, v);
+ }
+ }
+ else
+ sndp->common.adr = v;
+}
+
+static Uint32 opllread( OPLSOUND* sndp, Uint32 a )
+{
+ return 0xFF;
+}
+
+static void opreset( OPLSOUND* sndp, OPL_OP* opp )
+{
+ /* XMEMSET(opp, 0, sizeof(OPL_OP)); */
+ SetOpOff(opp);
+ opp->tl_ofs = 0x80 << (LOG_BITS - 4 + 1);
+#if TESTING_OPTIMIZE_AME
+ opp->amin = &sndp->common.amzero;
+#endif
+ opp->pg.rng = 0xFFFF;
+}
+
+static void chreset( OPLSOUND* sndp, OPL_CH* chp, Uint32 clock, Uint32 freq )
+{
+ Uint32 op;
+ XMEMSET(chp, 0, sizeof(OPL_CH));
+ for ( op = 0; op < 2; op++ )
+ {
+ opreset(sndp, &chp->op [op]);
+ }
+ recovercon(sndp, chp);
+}
+
+static void sndreset( OPLSOUND* sndp, Uint32 clock, Uint32 freq )
+{
+ Uint32 i, cpse;
+ XMEMSET(&sndp->common, 0, sizeof(sndp->common));
+ XMEMSET(&sndp->lfo [LFO_UNIT_AM], 0, sizeof(OPL_LFO));
+ sndp->lfo [LFO_UNIT_AM].sps = DivFix(37 * (1 << AMTBL_BITS), freq * 10, LFO_SHIFT);
+ sndp->lfo [LFO_UNIT_AM].adrmask = (1 << AMTBL_BITS) - 1;
+ sndp->lfo [LFO_UNIT_AM].table = sndp->opltbl->am_table1;
+ XMEMSET(&sndp->lfo [LFO_UNIT_PM], 0, sizeof(OPL_LFO));
+ sndp->lfo [LFO_UNIT_PM].sps = DivFix(64 * (1 << PMTBL_BITS), freq * 10, LFO_SHIFT);
+ sndp->lfo [LFO_UNIT_PM].adrmask = (1 << PMTBL_BITS) - 1;
+ sndp->lfo [LFO_UNIT_PM].table = sndp->opltbl->pm_table1;
+ sndp->common.cpsp = DivFix(clock, 72 * freq, CPS_SHIFTP);
+ cpse = DivFix(clock, 72 * freq, CPS_SHIFTE);
+ for ( i = 0; i < 4; i++ )
+ {
+ sndp->common.ratetbl [i] = (i + 4) * cpse;
+ sndp->common.ratetbla [i] = 3 * sndp->common.ratetbl [i];
+ }
+ sndp->common.tll2logtbl = sndp->opltbl->tll2log_table;
+ sndp->common.sintablemask = (1 << SINTBL_BITS) - 1;
+
+ for ( i = 0; i < 9; i++ )
+ chreset(sndp, &sndp->ch [i], clock, freq);
+
+ if ( sndp->deltatpcm )
+ sndp->deltatpcm->reset(sndp->deltatpcm->ctx, clock, freq);
+
+ if ( sndp->opl_type & OPL_TYPE_OPL )
+ {
+ XMEMSET(&sndp->regs, 0, 0x100);
+ sndp->common.ar_table = sndp->opltbl->ar_tablepow;
+ sndp->common.sintablemask -= (1 << (SINTBL_BITS - 11)) - 1;
+ for ( i = 0x0; i < 0x100; i++ )
+ {
+ oplwrite(sndp, 0, i);
+ oplwrite(sndp, 1, 0x00);
+ }
+ for ( i = 0xA0; i < 0xA9; i++ )
+ {
+ oplwrite(sndp, 0, 0xA0 + i);
+ oplwrite(sndp, 1, 0x40);
+ oplwrite(sndp, 0, 0xB0 + i);
+ oplwrite(sndp, 1, 0x0E);
+ }
+ }
+ else
+ {
+ const static Uint8 fmbios_initdata [9] = "\x30\x10\x20\x20\xfb\xb2\xf3\xf3";
+ XMEMSET(&sndp->regs, 0, 0x40);
+ sndp->common.ar_table = sndp->opltbl->ar_tablelog;
+ sndp->common.wfe = 1;
+ sndp->common.sintablemask -= (1 << (SINTBL_BITS - 8)) - 1;
+ for ( i = 0; i < sizeof(fmbios_initdata)-1; i++ )
+ {
+ opllwrite(sndp, 0, i);
+ opllwrite(sndp, 1, fmbios_initdata [i]);
+ }
+ opllwrite(sndp, 0, 0x0E);
+ opllwrite(sndp, 1, 0x00);
+ opllwrite(sndp, 0, 0x0F);
+ opllwrite(sndp, 1, 0x00);
+ for ( i = 0; i < 9; i++ )
+ {
+ opllwrite(sndp, 0, 0x10 + i);
+ opllwrite(sndp, 1, 0x20);
+ opllwrite(sndp, 0, 0x20 + i);
+ opllwrite(sndp, 1, 0x07);
+ opllwrite(sndp, 0, 0x30 + i);
+ opllwrite(sndp, 1, 0xB3);
+ }
+ }
+}
+
+static void oplsetinst( OPLSOUND* sndp, Uint32 n, void* p, Uint32 l )
+{
+ if ( sndp->deltatpcm) sndp->deltatpcm->setinst(sndp->deltatpcm->ctx, n, p, l);
+}
+
+__inline static Uint32 GetDwordLE(Uint8* p )
+{
+ return p [0] | (p [1] << 8) | (p [2] << 16) | (p [3] << 24);
+}
+#define GetDwordLEM(p) (Uint32)((((Uint8* )p) [0] | (((Uint8* )p) [1] << 8) | (((Uint8* )p) [2] << 16) | (((Uint8* )p) [3] << 24)) )
+
+static void opllsetinst( OPLSOUND* sndp, Uint32 n, Uint8* p, Uint32 l )
+{
+ Int32 i, j, sb = 9;
+ if ( n )
+ return;
+ if ( (GetDwordLE(p) & 0xF0FFFFFF) == GetDwordLEM("ILL0") )
+ {
+ if ( 0 < p [4] && p [4] <= SINTBL_BITS) sb = p [4];
+ for ( j = 1; j < 16 + 3; j++ )
+ for ( i = 0; i < 8; i++ )
+ sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [(j << 4) + i];
+ for ( j = 0; j < 16 + 3; j++ )
+ {
+ sndp->regs [OPLL_INST_WORK2 + (j << 1) + 0] = p [(j << 4) + 8];
+ sndp->regs [OPLL_INST_WORK2 + (j << 1) + 1] = p [(j << 4) + 9];
+ }
+ }
+ else
+ {
+ for ( j = 1; j < 16; j++ )
+ for ( i = 0; i < 8; i++ )
+ sndp->regs [OPLL_INST_WORK + (j << 3) + i] = p [((j - 1) << 3) + i];
+ }
+ sndp->common.sintablemask = (1 << SINTBL_BITS) - 1;
+ sndp->common.sintablemask -= (1 << (SINTBL_BITS - sb)) - 1;
+}
+
+static void sndrelease( OPLSOUND* sndp )
+{
+ if ( sndp->logtbl) sndp->logtbl->release(sndp->logtbl->ctx);
+ if ( sndp->opltbl) sndp->opltbl->release(sndp->opltbl->ctx);
+ if ( sndp->deltatpcm) sndp->deltatpcm->release(sndp->deltatpcm->ctx);
+ XFREE(sndp);
+}
+
+KMIF_SOUND_DEVICE* OPLSoundAlloc(Uint32 opl_type )
+{
+ OPLSOUND* sndp;
+ sndp = (OPLSOUND*) XMALLOC(sizeof(OPLSOUND));
+ if ( !sndp) return 0;
+ sndp->opl_type = opl_type;
+ sndp->kmif.ctx = sndp;
+ sndp->kmif.release = (void (*)( void* )) sndrelease;
+ sndp->kmif.volume = (void (*)( void*, int )) sndvolume;
+ sndp->kmif.reset = (void (*)( void*, Uint32, Uint32 )) sndreset;
+ sndp->kmif.synth = (int (*)( void* )) sndsynth;
+ if ( sndp->opl_type == OPL_TYPE_MSXAUDIO )
+ {
+ sndp->deltatpcm = YMDELTATPCMSoundAlloc(YMDELTATPCM_TYPE_Y8950);
+ }
+ else
+ sndp->deltatpcm = 0;
+ if ( sndp->opl_type & OPL_TYPE_OPL )
+ {
+ sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) oplwrite;
+ sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) oplread;
+ sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) oplsetinst;
+ }
+ else
+ {
+ sndp->kmif.write = (void (*)( void*, Uint32, Uint32 )) opllwrite;
+ sndp->kmif.read = (Uint32 (*)( void*, Uint32 )) opllread;
+ sndp->kmif.setinst = (void (*)( void*, Uint32, void*, Uint32 )) opllsetinst;
+ switch ( sndp->opl_type )
+ {
+ case OPL_TYPE_OPLL:
+ case OPL_TYPE_MSXMUSIC:
+ opllsetinst(sndp, 0, romtone [0], 16 * 19);
+ break;
+
+ case OPL_TYPE_SMSFMUNIT:
+ opllsetinst(sndp, 0, romtone [1], 16 * 19);
+ break;
+
+ case OPL_TYPE_VRC7:
+ opllsetinst(sndp, 0, romtone [2], 16 * 19);
+ break;
+ }
+ }
+ sndp->logtbl = LogTableAddRef();
+ sndp->opltbl = OplTableAddRef();
+ if ( !sndp->logtbl || !sndp->opltbl )
+ {
+ sndrelease(sndp);
+ return 0;
+ }
+
+ return &sndp->kmif;
+}
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_opl.h b/plugins/gme/game-music-emu-0.6pre/gme/s_opl.h
new file mode 100644
index 00000000..227c03c3
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_opl.h
@@ -0,0 +1,26 @@
+#ifndef S_OPL_H__
+#define S_OPL_H__
+
+#include "kmsnddev.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+enum {
+ OPL_TYPE_OPLL = 0x10, /* YAMAHA YM2413 */
+ OPL_TYPE_MSXMUSIC = 0x11, /* YAMAHA YM2413 */
+ OPL_TYPE_SMSFMUNIT = 0x12, /* YAMAHA YM2413? */
+ OPL_TYPE_VRC7 = 0x13, /* KONAMI 053982 VRV VII */
+ OPL_TYPE_OPL = 0x20, /* YAMAHA YM3526 */
+ OPL_TYPE_MSXAUDIO = 0x21, /* YAMAHA Y8950 */
+ OPL_TYPE_OPL2 = 0x22 /* YAMAHA YM3812 */
+};
+
+KMIF_SOUND_DEVICE *OPLSoundAlloc(Uint32 opl_type);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_OPL_H__ */
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.c b/plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.c
new file mode 100644
index 00000000..35e54edb
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.c
@@ -0,0 +1,136 @@
+#include "nestypes.h"
+#include "s_logtbl.h"
+#include "s_opltbl.h"
+
+#if STATIC_TABLES
+
+static void OplTableRelease(void *ctx)
+{
+}
+
+static KMIF_OPLTABLE opl_static_tables = {
+ &opl_static_tables;
+ OplTableRelease,
+#include "s_oplt.h"
+};
+
+KMIF_OPLTABLE *OplTableAddRef(void)
+{
+ opl_static_tables.release = OplTableRelease;
+ return &opl_static_tables;
+}
+
+#else
+
+#include <math.h>
+#ifndef M_PI
+#ifdef PI
+#define M_PI PI
+#else
+#define M_PI 3.14159265358979323846
+#endif
+#endif
+
+#define AM_DEPTH1 4.8 /* dB */
+#define AM_DEPTH2 1.0 /* dB */
+#define PM_DEPTH1 14.0 /* cent */
+#define PM_DEPTH2 7.0 /* cent */
+#define LOG_KEYOFF (15 << (LOG_BITS + 1))
+
+#define DB0375_TO_LOG(x) ((Uint32)(0.375 * (1 << (LOG_BITS + x)) / 10))
+
+#define AR_OFF (128 << ARTBL_SHIFT)
+#define AR_MAX (127 << ARTBL_SHIFT)
+
+
+static volatile Uint32 opl_tables_mutex = 0;
+static Uint32 opl_tables_refcount = 0;
+static KMIF_OPLTABLE *opl_tables = 0;
+
+static void OplTableRelease(void *ctx)
+{
+ ++opl_tables_mutex;
+ while (opl_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ opl_tables_refcount--;
+ if (!opl_tables_refcount)
+ {
+ XFREE(ctx);
+ opl_tables = 0;
+ }
+ --opl_tables_mutex;
+}
+
+static void OplTableCalc(KMIF_OPLTABLE *tbl)
+{
+ Uint32 u, u2, i;
+ tbl->sin_table[0][0] = tbl->sin_table[0][1 << (SINTBL_BITS - 1)] = LOG_KEYOFF;
+ for (i = 1 ;i < (1 << (SINTBL_BITS - 1)); i++)
+ {
+ double d;
+ d = (1 << LOG_BITS) * -(log(sin(2.0 * M_PI * ((double)i) / (1 << SINTBL_BITS))) / log(2));
+ if (d > (LOG_KEYOFF >> 1)) d = (LOG_KEYOFF >> 1);
+ tbl->sin_table[0][i] = ((Uint32)d) << 1;
+ tbl->sin_table[0][i + (1 << (SINTBL_BITS - 1))] = (((Uint32)d) << 1) + 1;
+ }
+ for (i = 0 ;i < (1 << SINTBL_BITS); i++)
+ {
+ tbl->sin_table[1][i] = (tbl->sin_table[0][i] & 1) ? tbl->sin_table[0][0] : tbl->sin_table[0][i];
+ tbl->sin_table[2][i] = tbl->sin_table[0][i] & ~1;
+ tbl->sin_table[3][i] = (i & (1 << (SINTBL_BITS - 2))) ? LOG_KEYOFF : tbl->sin_table[2][i];
+ }
+ for (i = 0; i < (1 << TLLTBL_BITS); i++)
+ {
+ tbl->tll2log_table[i] = (i * DB0375_TO_LOG(0)) << 1;
+ }
+ for (i = 0; i < (1 << AMTBL_BITS); i++)
+ {
+ u = (Uint32)((1 + sin(2 * M_PI * ((double)i) / (1 << AMTBL_BITS))) * ((1 << LOG_BITS) * AM_DEPTH1 / 20.0));
+ u2 = (Uint32)((1 + sin(2 * M_PI * ((double)i) / (1 << AMTBL_BITS))) * ((1 << LOG_BITS) * AM_DEPTH2 / 20.0));
+ tbl->am_table1[i] = u << 1;
+ tbl->am_table2[i] = u2 << 1;
+ }
+ for (i = 0; i < (1 << PMTBL_BITS); i++)
+ {
+ u = (Uint32)((1 << PM_SHIFT) * pow(2, sin(2 * M_PI * ((double)i) / (1 << PMTBL_BITS)) * PM_DEPTH1 / 1200.0));
+ u2 = (Uint32)((1 << PM_SHIFT) * pow(2, sin(2 * M_PI * ((double)i) / (1 << PMTBL_BITS)) * PM_DEPTH2 / 1200.0));
+ tbl->pm_table1[i] = u;
+ tbl->pm_table2[i] = u2;
+ }
+
+ for (i = 0; i < (1 << ARTBL_BITS); i++)
+ {
+ u = (Uint32)(((double)AR_MAX) * (1 - log(1 + i) / log(1 << ARTBL_BITS)));
+ tbl->ar_tablelog[i] = u;
+#if 1
+ u = (Uint32)(((double)AR_MAX) * (pow(1 - i / (double)(1 << ARTBL_BITS), 8)));
+ tbl->ar_tablepow[i] = u;
+#endif
+ }
+}
+
+KMIF_OPLTABLE *OplTableAddRef(void)
+{
+ ++opl_tables_mutex;
+ while (opl_tables_mutex != 1)
+ {
+ XSLEEP(0);
+ }
+ if (!opl_tables_refcount)
+ {
+ opl_tables = (KMIF_OPLTABLE*) XMALLOC(sizeof(KMIF_OPLTABLE));
+ if (opl_tables)
+ {
+ opl_tables->ctx = opl_tables;
+ opl_tables->release = OplTableRelease;
+ OplTableCalc(opl_tables);
+ }
+ }
+ if (opl_tables) opl_tables_refcount++;
+ --opl_tables_mutex;
+ return opl_tables;
+}
+
+#endif
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.h b/plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.h
new file mode 100644
index 00000000..7a3f805f
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/s_opltbl.h
@@ -0,0 +1,38 @@
+#ifndef S_OPLTBL_H__
+#define S_OPLTBL_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SINTBL_BITS 11
+#define AMTBL_BITS 8
+#define PMTBL_BITS 8
+#define PM_SHIFT 9
+#define ARTBL_BITS 7
+#define ARTBL_SHIFT 20
+#define TLLTBL_BITS 7
+
+typedef struct
+{
+ void *ctx;
+ void (*release)(void *ctx);
+ Uint32 sin_table[4][1 << SINTBL_BITS];
+ Uint32 tll2log_table[1 << TLLTBL_BITS];
+ Uint32 ar_tablelog[1 << ARTBL_BITS];
+ Uint32 am_table1[1 << AMTBL_BITS];
+ Uint32 pm_table1[1 << PMTBL_BITS];
+#if 1
+ Uint32 ar_tablepow[1 << ARTBL_BITS];
+#endif
+ Uint32 am_table2[1 << AMTBL_BITS];
+ Uint32 pm_table2[1 << PMTBL_BITS];
+} KMIF_OPLTABLE;
+
+KMIF_OPLTABLE *OplTableAddRef(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* S_OPLTBL_H__ */
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/ym2413.c b/plugins/gme/game-music-emu-0.6pre/gme/ym2413.c
new file mode 100644
index 00000000..edb7354b
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/ym2413.c
@@ -0,0 +1,2106 @@
+/*
+**
+** File: ym2413.c - software implementation of YM2413
+** FM sound generator type OPLL
+**
+** Copyright Jarek Burczynski
+**
+** Version 1.0
+**
+
+ Features as listed in LSI-212413A2 data sheet:
+ 1. FM Sound Generator for real sound creation.
+ 2. Two Selectable modes: 9 simultaneous sounds or 6 melody sounds plus 5 rhythm sounds
+ (different tones can be used together in either case).
+ 3. Built-in Instruments data (15 melody tones, 5 rhythm tones, "CAPTAIN and TELETEXT applicalbe tones).
+ 4. Built-in DA Converter.
+ 5. Built-in Quartz Oscillator.
+ 6. Built-in Vibrato Oscillator/AM Oscillator
+ 7. TTL Compatible Input.
+ 8. Si-Gate NMOS LSI
+ 9. A single 5V power source.
+
+to do:
+
+- make sure of the sinus amplitude bits
+
+- make sure of the EG resolution bits (looks like the biggest
+ modulation index generated by the modulator is 123, 124 = no modulation)
+- find proper algorithm for attack phase of EG
+
+- tune up instruments ROM
+
+- support sample replay in test mode (it is NOT as simple as setting bit 0
+ in register 0x0f and using register 0x10 for sample data).
+ Which games use this feature ?
+
+
+*/
+
+#define _USE_MATH_DEFINES
+#include <math.h>
+#include <stdlib.h>
+#include <string.h>
+#include "ym2413.h"
+
+#define INLINE __inline
+#ifndef NULL
+ #define NULL ((void *)0)
+#endif
+#define logerror (void)
+
+#ifndef M_PI
+ #define M_PI 3.14159265358979323846
+#endif
+
+/* output final shift */
+#if (SAMPLE_BITS==16)
+ #define FINAL_SH (0)
+ #define MAXOUT (+32767)
+ #define MINOUT (-32768)
+#else
+ #define FINAL_SH (8)
+ #define MAXOUT (+127)
+ #define MINOUT (-128)
+#endif
+
+
+#define FREQ_SH 16 /* 16.16 fixed point (frequency calculations) */
+#define EG_SH 16 /* 16.16 fixed point (EG timing) */
+#define LFO_SH 24 /* 8.24 fixed point (LFO calculations) */
+
+#define FREQ_MASK ((1<<FREQ_SH)-1)
+
+/* envelope output entries */
+#define ENV_BITS 10
+#define ENV_LEN (1<<ENV_BITS)
+#define ENV_STEP (128.0/ENV_LEN)
+
+#define MAX_ATT_INDEX ((1<<(ENV_BITS-2))-1) /*255*/
+#define MIN_ATT_INDEX (0)
+
+/* sinwave entries */
+#define SIN_BITS 10
+#define SIN_LEN (1<<SIN_BITS)
+#define SIN_MASK (SIN_LEN-1)
+
+#define TL_RES_LEN (256) /* 8 bits addressing (real chip) */
+
+
+
+/* register number to channel number , slot offset */
+#define SLOT1 0
+#define SLOT2 1
+
+/* Envelope Generator phases */
+
+#define EG_DMP 5
+#define EG_ATT 4
+#define EG_DEC 3
+#define EG_SUS 2
+#define EG_REL 1
+#define EG_OFF 0
+
+
+typedef struct{
+ UINT32 ar; /* attack rate: AR<<2 */
+ UINT32 dr; /* decay rate: DR<<2 */
+ UINT32 rr; /* release rate:RR<<2 */
+ UINT8 KSR; /* key scale rate */
+ UINT8 ksl; /* keyscale level */
+ UINT8 ksr; /* key scale rate: kcode>>KSR */
+ UINT8 mul; /* multiple: mul_tab[ML] */
+
+ /* Phase Generator */
+ UINT32 phase; /* frequency counter */
+ UINT32 freq; /* frequency counter step */
+ UINT8 fb_shift; /* feedback shift value */
+ INT32 op1_out[2]; /* slot1 output for feedback */
+
+ /* Envelope Generator */
+ UINT8 eg_type; /* percussive/nonpercussive mode*/
+ UINT8 state; /* phase type */
+ UINT32 TL; /* total level: TL << 2 */
+ INT32 TLL; /* adjusted now TL */
+ INT32 volume; /* envelope counter */
+ UINT32 sl; /* sustain level: sl_tab[SL] */
+
+ UINT8 eg_sh_dp; /* (dump state) */
+ UINT8 eg_sel_dp; /* (dump state) */
+ UINT8 eg_sh_ar; /* (attack state) */
+ UINT8 eg_sel_ar; /* (attack state) */
+ UINT8 eg_sh_dr; /* (decay state) */
+ UINT8 eg_sel_dr; /* (decay state) */
+ UINT8 eg_sh_rr; /* (release state for non-perc.)*/
+ UINT8 eg_sel_rr; /* (release state for non-perc.)*/
+ UINT8 eg_sh_rs; /* (release state for perc.mode)*/
+ UINT8 eg_sel_rs; /* (release state for perc.mode)*/
+
+ UINT32 key; /* 0 = KEY OFF, >0 = KEY ON */
+
+ /* LFO */
+ UINT32 AMmask; /* LFO Amplitude Modulation enable mask */
+ UINT8 vib; /* LFO Phase Modulation enable flag (active high)*/
+
+ /* waveform select */
+ unsigned int wavetable;
+} OPLL_SLOT;
+
+#define OPLL_MASK_CH(x) (1<<(x))
+#define OPLL_MASK_HH (1<<(9))
+#define OPLL_MASK_CYM (1<<(10))
+#define OPLL_MASK_TOM (1<<(11))
+#define OPLL_MASK_SD (1<<(12))
+#define OPLL_MASK_BD (1<<(13))
+#define OPLL_MASK_RHYTHM ( OPLL_MASK_HH | OPLL_MASK_CYM | OPLL_MASK_TOM | OPLL_MASK_SD | OPLL_MASK_BD )
+
+typedef struct{
+ OPLL_SLOT SLOT[2];
+ /* phase generator state */
+ UINT32 block_fnum; /* block+fnum */
+ UINT32 fc; /* Freq. freqement base */
+ UINT32 ksl_base; /* KeyScaleLevel Base step */
+ UINT8 kcode; /* key code (for key scaling) */
+ UINT8 sus; /* sus on/off (release speed in percussive mode)*/
+} OPLL_CH;
+
+/* chip state */
+typedef struct {
+ OPLL_CH P_CH[9]; /* OPLL chips have 9 channels*/
+ UINT8 instvol_r[9]; /* instrument/volume (or volume/volume in percussive mode)*/
+
+ UINT32 eg_cnt; /* global envelope generator counter */
+ UINT32 eg_timer; /* global envelope generator counter works at frequency = chipclock/72 */
+ UINT32 eg_timer_add; /* step of eg_timer */
+ UINT32 eg_timer_overflow; /* envelope generator timer overlfows every 1 sample (on real chip) */
+
+ UINT8 rhythm; /* Rhythm mode */
+
+ /* LFO */
+ UINT32 lfo_am_cnt;
+ UINT32 lfo_am_inc;
+ UINT32 lfo_pm_cnt;
+ UINT32 lfo_pm_inc;
+
+ UINT32 noise_rng; /* 23 bit noise shift register */
+ UINT32 noise_p; /* current noise 'phase' */
+ UINT32 noise_f; /* current noise period */
+
+
+/* instrument settings */
+/*
+ 0-user instrument
+ 1-15 - fixed instruments
+ 16 -bass drum settings
+ 17,18 - other percussion instruments
+*/
+ UINT8 inst_tab[19][8];
+
+ /* external event callback handlers */
+ OPLL_UPDATEHANDLER UpdateHandler; /* stream update handler */
+ void * UpdateParam; /* stream update parameter */
+
+ UINT32 fn_tab[1024]; /* fnumber->increment counter */
+
+ UINT8 address; /* address register */
+ UINT8 status; /* status flag */
+
+ int clock; /* master clock (Hz) */
+ int rate; /* sampling rate (Hz) */
+ double freqbase; /* frequency base */
+
+ /* work table */
+ OPLL_SLOT *SLOT7_1,*SLOT7_2,*SLOT8_1,*SLOT8_2;
+
+ signed int output[2];
+
+ UINT32 LFO_AM;
+ INT32 LFO_PM;
+
+ int chip_type;
+ UINT32 mask;
+} YM2413;
+
+/* key scale level */
+/* table is 3dB/octave, DV converts this into 6dB/octave */
+/* 0.1875 is bit 0 weight of the envelope counter (volume) expressed in the 'decibel' scale */
+#define DV (0.1875/1.0)
+static const UINT32 ksl_tab[8*16]=
+{
+ /* OCT 0 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ /* OCT 1 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 0.750/DV, 1.125/DV, 1.500/DV,
+ 1.875/DV, 2.250/DV, 2.625/DV, 3.000/DV,
+ /* OCT 2 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 0.000/DV,
+ 0.000/DV, 1.125/DV, 1.875/DV, 2.625/DV,
+ 3.000/DV, 3.750/DV, 4.125/DV, 4.500/DV,
+ 4.875/DV, 5.250/DV, 5.625/DV, 6.000/DV,
+ /* OCT 3 */
+ 0.000/DV, 0.000/DV, 0.000/DV, 1.875/DV,
+ 3.000/DV, 4.125/DV, 4.875/DV, 5.625/DV,
+ 6.000/DV, 6.750/DV, 7.125/DV, 7.500/DV,
+ 7.875/DV, 8.250/DV, 8.625/DV, 9.000/DV,
+ /* OCT 4 */
+ 0.000/DV, 0.000/DV, 3.000/DV, 4.875/DV,
+ 6.000/DV, 7.125/DV, 7.875/DV, 8.625/DV,
+ 9.000/DV, 9.750/DV,10.125/DV,10.500/DV,
+ 10.875/DV,11.250/DV,11.625/DV,12.000/DV,
+ /* OCT 5 */
+ 0.000/DV, 3.000/DV, 6.000/DV, 7.875/DV,
+ 9.000/DV,10.125/DV,10.875/DV,11.625/DV,
+ 12.000/DV,12.750/DV,13.125/DV,13.500/DV,
+ 13.875/DV,14.250/DV,14.625/DV,15.000/DV,
+ /* OCT 6 */
+ 0.000/DV, 6.000/DV, 9.000/DV,10.875/DV,
+ 12.000/DV,13.125/DV,13.875/DV,14.625/DV,
+ 15.000/DV,15.750/DV,16.125/DV,16.500/DV,
+ 16.875/DV,17.250/DV,17.625/DV,18.000/DV,
+ /* OCT 7 */
+ 0.000/DV, 9.000/DV,12.000/DV,13.875/DV,
+ 15.000/DV,16.125/DV,16.875/DV,17.625/DV,
+ 18.000/DV,18.750/DV,19.125/DV,19.500/DV,
+ 19.875/DV,20.250/DV,20.625/DV,21.000/DV
+};
+#undef DV
+
+/* sustain level table (3dB per step) */
+/* 0 - 15: 0, 3, 6, 9,12,15,18,21,24,27,30,33,36,39,42,45 (dB)*/
+#define SC(db) (UINT32) ( db * (1.0/ENV_STEP) )
+static const UINT32 sl_tab[16]={
+ SC( 0),SC( 1),SC( 2),SC(3 ),SC(4 ),SC(5 ),SC(6 ),SC( 7),
+ SC( 8),SC( 9),SC(10),SC(11),SC(12),SC(13),SC(14),SC(15)
+};
+#undef SC
+
+
+#define RATE_STEPS (8)
+static const unsigned char eg_inc[15*RATE_STEPS]={
+
+/*cycle:0 1 2 3 4 5 6 7*/
+
+/* 0 */ 0,1, 0,1, 0,1, 0,1, /* rates 00..12 0 (increment by 0 or 1) */
+/* 1 */ 0,1, 0,1, 1,1, 0,1, /* rates 00..12 1 */
+/* 2 */ 0,1, 1,1, 0,1, 1,1, /* rates 00..12 2 */
+/* 3 */ 0,1, 1,1, 1,1, 1,1, /* rates 00..12 3 */
+
+/* 4 */ 1,1, 1,1, 1,1, 1,1, /* rate 13 0 (increment by 1) */
+/* 5 */ 1,1, 1,2, 1,1, 1,2, /* rate 13 1 */
+/* 6 */ 1,2, 1,2, 1,2, 1,2, /* rate 13 2 */
+/* 7 */ 1,2, 2,2, 1,2, 2,2, /* rate 13 3 */
+
+/* 8 */ 2,2, 2,2, 2,2, 2,2, /* rate 14 0 (increment by 2) */
+/* 9 */ 2,2, 2,4, 2,2, 2,4, /* rate 14 1 */
+/*10 */ 2,4, 2,4, 2,4, 2,4, /* rate 14 2 */
+/*11 */ 2,4, 4,4, 2,4, 4,4, /* rate 14 3 */
+
+/*12 */ 4,4, 4,4, 4,4, 4,4, /* rates 15 0, 15 1, 15 2, 15 3 (increment by 4) */
+/*13 */ 8,8, 8,8, 8,8, 8,8, /* rates 15 2, 15 3 for attack */
+/*14 */ 0,0, 0,0, 0,0, 0,0, /* infinity rates for attack and decay(s) */
+};
+
+
+#define O(a) (a*RATE_STEPS)
+
+/*note that there is no O(13) in this table - it's directly in the code */
+static const unsigned char eg_rate_select[16+64+16]={ /* Envelope Generator rates (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+O(14),O(14),O(14),O(14),O(14),O(14),O(14),O(14),
+
+/* rates 00-12 */
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+O( 0),O( 1),O( 2),O( 3),
+
+/* rate 13 */
+O( 4),O( 5),O( 6),O( 7),
+
+/* rate 14 */
+O( 8),O( 9),O(10),O(11),
+
+/* rate 15 */
+O(12),O(12),O(12),O(12),
+
+/* 16 dummy rates (same as 15 3) */
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+O(12),O(12),O(12),O(12),O(12),O(12),O(12),O(12),
+
+};
+#undef O
+
+/*rate 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 */
+/*shift 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0, 0, 0 */
+/*mask 8191, 4095, 2047, 1023, 511, 255, 127, 63, 31, 15, 7, 3, 1, 0, 0, 0 */
+
+#define O(a) (a*1)
+static const unsigned char eg_rate_shift[16+64+16]={ /* Envelope Generator counter shifts (16 + 64 rates + 16 RKS) */
+/* 16 infinite time rates */
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+O(0),O(0),O(0),O(0),O(0),O(0),O(0),O(0),
+
+/* rates 00-12 */
+O(13),O(13),O(13),O(13),
+O(12),O(12),O(12),O(12),
+O(11),O(11),O(11),O(11),
+O(10),O(10),O(10),O(10),
+O( 9),O( 9),O( 9),O( 9),
+O( 8),O( 8),O( 8),O( 8),
+O( 7),O( 7),O( 7),O( 7),
+O( 6),O( 6),O( 6),O( 6),
+O( 5),O( 5),O( 5),O( 5),
+O( 4),O( 4),O( 4),O( 4),
+O( 3),O( 3),O( 3),O( 3),
+O( 2),O( 2),O( 2),O( 2),
+O( 1),O( 1),O( 1),O( 1),
+
+/* rate 13 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 14 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* rate 15 */
+O( 0),O( 0),O( 0),O( 0),
+
+/* 16 dummy rates (same as 15 3) */
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),O( 0),
+
+};
+#undef O
+
+
+/* multiple table */
+#define ML 2
+static const UINT8 mul_tab[16]= {
+/* 1/2, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,10,12,12,15,15 */
+ 0.50*ML, 1.00*ML, 2.00*ML, 3.00*ML, 4.00*ML, 5.00*ML, 6.00*ML, 7.00*ML,
+ 8.00*ML, 9.00*ML,10.00*ML,10.00*ML,12.00*ML,12.00*ML,15.00*ML,15.00*ML
+};
+#undef ML
+
+/* TL_TAB_LEN is calculated as:
+* 11 - sinus amplitude bits (Y axis)
+* 2 - sinus sign bit (Y axis)
+* TL_RES_LEN - sinus resolution (X axis)
+*/
+#define TL_TAB_LEN (11*2*TL_RES_LEN)
+static signed int tl_tab[TL_TAB_LEN];
+
+#define ENV_QUIET (TL_TAB_LEN>>5)
+
+/* sin waveform table in 'decibel' scale */
+/* two waveforms on OPLL type chips */
+static unsigned int sin_tab[SIN_LEN * 2];
+
+
+/* LFO Amplitude Modulation table (verified on real YM3812)
+ 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples
+
+ Length: 210 elements.
+
+ Each of the elements has to be repeated
+ exactly 64 times (on 64 consecutive samples).
+ The whole table takes: 64 * 210 = 13440 samples.
+
+We use data>>1, until we find what it really is on real chip...
+
+*/
+
+#define LFO_AM_TAB_ELEMENTS 210
+
+static const UINT8 lfo_am_table[LFO_AM_TAB_ELEMENTS] = {
+0,0,0,0,0,0,0,
+1,1,1,1,
+2,2,2,2,
+3,3,3,3,
+4,4,4,4,
+5,5,5,5,
+6,6,6,6,
+7,7,7,7,
+8,8,8,8,
+9,9,9,9,
+10,10,10,10,
+11,11,11,11,
+12,12,12,12,
+13,13,13,13,
+14,14,14,14,
+15,15,15,15,
+16,16,16,16,
+17,17,17,17,
+18,18,18,18,
+19,19,19,19,
+20,20,20,20,
+21,21,21,21,
+22,22,22,22,
+23,23,23,23,
+24,24,24,24,
+25,25,25,25,
+26,26,26,
+25,25,25,25,
+24,24,24,24,
+23,23,23,23,
+22,22,22,22,
+21,21,21,21,
+20,20,20,20,
+19,19,19,19,
+18,18,18,18,
+17,17,17,17,
+16,16,16,16,
+15,15,15,15,
+14,14,14,14,
+13,13,13,13,
+12,12,12,12,
+11,11,11,11,
+10,10,10,10,
+9,9,9,9,
+8,8,8,8,
+7,7,7,7,
+6,6,6,6,
+5,5,5,5,
+4,4,4,4,
+3,3,3,3,
+2,2,2,2,
+1,1,1,1
+};
+
+/* LFO Phase Modulation table (verified on real YM2413) */
+static const INT8 lfo_pm_table[8*8] = {
+
+/* FNUM2/FNUM = 0 00xxxxxx (0x0000) */
+0, 0, 0, 0, 0, 0, 0, 0,
+
+/* FNUM2/FNUM = 0 01xxxxxx (0x0040) */
+1, 0, 0, 0,-1, 0, 0, 0,
+
+/* FNUM2/FNUM = 0 10xxxxxx (0x0080) */
+2, 1, 0,-1,-2,-1, 0, 1,
+
+/* FNUM2/FNUM = 0 11xxxxxx (0x00C0) */
+3, 1, 0,-1,-3,-1, 0, 1,
+
+/* FNUM2/FNUM = 1 00xxxxxx (0x0100) */
+4, 2, 0,-2,-4,-2, 0, 2,
+
+/* FNUM2/FNUM = 1 01xxxxxx (0x0140) */
+5, 2, 0,-2,-5,-2, 0, 2,
+
+/* FNUM2/FNUM = 1 10xxxxxx (0x0180) */
+6, 3, 0,-3,-6,-3, 0, 3,
+
+/* FNUM2/FNUM = 1 11xxxxxx (0x01C0) */
+7, 3, 0,-3,-7,-3, 0, 3,
+};
+
+
+
+
+
+
+/* This is not 100% perfect yet but very close */
+/*
+ - multi parameters are 100% correct (instruments and drums)
+ - LFO PM and AM enable are 100% correct
+ - waveform DC and DM select are 100% correct
+*/
+
+static const unsigned char table[19][8] = {
+/* MULT MULT modTL DcDmFb AR/DR AR/DR SL/RR SL/RR */
+/* 0 1 2 3 4 5 6 7 */
+ {0x49, 0x4c, 0x4c, 0x12, 0x00, 0x00, 0x00, 0x00 }, /* 0 */
+
+ {0x61, 0x61, 0x1e, 0x17, 0xf0, 0x78, 0x00, 0x17 }, /* 1 */
+ {0x13, 0x41, 0x1e, 0x0d, 0xd7, 0xf7, 0x13, 0x13 }, /* 2 */
+ {0x13, 0x01, 0x99, 0x04, 0xf2, 0xf4, 0x11, 0x23 }, /* 3 */
+ {0x21, 0x61, 0x1b, 0x07, 0xaf, 0x64, 0x40, 0x27 }, /* 4 */
+
+/* {0x22, 0x21, 0x1e, 0x09, 0xf0, 0x76, 0x08, 0x28 }, //5 */
+ {0x22, 0x21, 0x1e, 0x06, 0xf0, 0x75, 0x08, 0x18 }, /* 5 */
+
+/* {0x31, 0x22, 0x16, 0x09, 0x90, 0x7f, 0x00, 0x08 }, //6 */
+ {0x31, 0x22, 0x16, 0x05, 0x90, 0x71, 0x00, 0x13 }, /* 6 */
+
+ {0x21, 0x61, 0x1d, 0x07, 0x82, 0x80, 0x10, 0x17 }, /* 7 */
+ {0x23, 0x21, 0x2d, 0x16, 0xc0, 0x70, 0x07, 0x07 }, /* 8 */
+ {0x61, 0x61, 0x1b, 0x06, 0x64, 0x65, 0x10, 0x17 }, /* 9 */
+
+/* {0x61, 0x61, 0x0c, 0x08, 0x85, 0xa0, 0x79, 0x07 }, //A */
+ {0x61, 0x61, 0x0c, 0x18, 0x85, 0xf0, 0x70, 0x07 }, /* A */
+
+ {0x23, 0x01, 0x07, 0x11, 0xf0, 0xa4, 0x00, 0x22 }, /* B */
+ {0x97, 0xc1, 0x24, 0x07, 0xff, 0xf8, 0x22, 0x12 }, /* C */
+
+/* {0x61, 0x10, 0x0c, 0x08, 0xf2, 0xc4, 0x40, 0xc8 }, //D */
+ {0x61, 0x10, 0x0c, 0x05, 0xf2, 0xf4, 0x40, 0x44 }, /* D */
+
+ {0x01, 0x01, 0x55, 0x03, 0xf3, 0x92, 0xf3, 0xf3 }, /* E */
+ {0x61, 0x41, 0x89, 0x03, 0xf1, 0xf4, 0xf0, 0x13 }, /* F */
+
+/* drum instruments definitions */
+/* MULTI MULTI modTL xxx AR/DR AR/DR SL/RR SL/RR */
+/* 0 1 2 3 4 5 6 7 */
+ {0x01, 0x01, 0x16, 0x00, 0xfd, 0xf8, 0x2f, 0x6d },/* BD(multi verified, modTL verified, mod env - verified(close), carr. env verifed) */
+ {0x01, 0x01, 0x00, 0x00, 0xd8, 0xd8, 0xf9, 0xf8 },/* HH(multi verified), SD(multi not used) */
+ {0x05, 0x01, 0x00, 0x00, 0xf8, 0xba, 0x49, 0x55 },/* TOM(multi,env verified), TOP CYM(multi verified, env verified) */
+};
+
+static const unsigned char table_vrc7[15][8] =
+{
+/* VRC7 instruments, January 17, 2004 update -Xodnizel */
+ {0x03, 0x21, 0x04, 0x06, 0x8D, 0xF2, 0x42, 0x17},
+ {0x13, 0x41, 0x05, 0x0E, 0x99, 0x96, 0x63, 0x12},
+ {0x31, 0x11, 0x10, 0x0A, 0xF0, 0x9C, 0x32, 0x02},
+ {0x21, 0x61, 0x1D, 0x07, 0x9F, 0x64, 0x20, 0x27},
+ {0x22, 0x21, 0x1E, 0x06, 0xF0, 0x76, 0x08, 0x28},
+ {0x02, 0x01, 0x06, 0x00, 0xF0, 0xF2, 0x03, 0x95},
+ {0x21, 0x61, 0x1C, 0x07, 0x82, 0x81, 0x16, 0x07},
+ {0x23, 0x21, 0x1A, 0x17, 0xEF, 0x82, 0x25, 0x15},
+ {0x25, 0x11, 0x1F, 0x00, 0x86, 0x41, 0x20, 0x11},
+ {0x85, 0x01, 0x1F, 0x0F, 0xE4, 0xA2, 0x11, 0x12},
+ {0x07, 0xC1, 0x2B, 0x45, 0xB4, 0xF1, 0x24, 0xF4},
+ {0x61, 0x23, 0x11, 0x06, 0x96, 0x96, 0x13, 0x16},
+ {0x01, 0x02, 0xD3, 0x05, 0x82, 0xA2, 0x31, 0x51},
+ {0x61, 0x22, 0x0D, 0x02, 0xC3, 0x7F, 0x24, 0x05},
+ {0x21, 0x62, 0x0E, 0x00, 0xA1, 0xA0, 0x44, 0x17},
+
+};
+
+INLINE int limit( int val, int max, int min ) {
+ if ( val > max )
+ val = max;
+ else if ( val < min )
+ val = min;
+
+ return val;
+}
+
+
+/* advance LFO to next sample */
+INLINE void advance_lfo(YM2413 *chip)
+{
+ /* LFO */
+ chip->lfo_am_cnt += chip->lfo_am_inc;
+ if (chip->lfo_am_cnt >= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH) ) /* lfo_am_table is 210 elements long */
+ chip->lfo_am_cnt -= ((UINT32)LFO_AM_TAB_ELEMENTS<<LFO_SH);
+
+ chip->LFO_AM = lfo_am_table[ chip->lfo_am_cnt >> LFO_SH ] >> 1;
+
+ chip->lfo_pm_cnt += chip->lfo_pm_inc;
+ chip->LFO_PM = (chip->lfo_pm_cnt>>LFO_SH) & 7;
+}
+
+/* advance to next sample */
+INLINE void advance(YM2413 *chip)
+{
+ OPLL_CH *CH;
+ OPLL_SLOT *op;
+ unsigned int i;
+
+ /* Envelope Generator */
+ chip->eg_timer += chip->eg_timer_add;
+
+ while (chip->eg_timer >= chip->eg_timer_overflow)
+ {
+ chip->eg_timer -= chip->eg_timer_overflow;
+
+ chip->eg_cnt++;
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+
+ op = &CH->SLOT[i&1];
+
+ switch(op->state)
+ {
+
+ case EG_DMP: /* dump phase */
+ /*dump phase is performed by both operators in each channel*/
+ /*when CARRIER envelope gets down to zero level,
+ ** phases in BOTH opearators are reset (at the same time ?)
+ */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_dp)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dp + ((chip->eg_cnt>>op->eg_sh_dp)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_ATT;
+ /* restart Phase Generator */
+ op->phase = 0;
+ }
+ }
+ break;
+
+ case EG_ATT: /* attack phase */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_ar)-1) ) )
+ {
+ op->volume += (~op->volume *
+ (eg_inc[op->eg_sel_ar + ((chip->eg_cnt>>op->eg_sh_ar)&7)])
+ ) >>2;
+
+ if (op->volume <= MIN_ATT_INDEX)
+ {
+ op->volume = MIN_ATT_INDEX;
+ op->state = EG_DEC;
+ }
+ }
+ break;
+
+ case EG_DEC: /* decay phase */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_dr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_dr + ((chip->eg_cnt>>op->eg_sh_dr)&7)];
+
+ if ( op->volume >= op->sl )
+ op->state = EG_SUS;
+ }
+ break;
+
+ case EG_SUS: /* sustain phase */
+ /* this is important behaviour:
+ one can change percusive/non-percussive modes on the fly and
+ the chip will remain in sustain phase - verified on real YM3812 */
+
+ if(op->eg_type) /* non-percussive mode (sustained tone) */
+ {
+ /* do nothing */
+ }
+ else /* percussive mode */
+ {
+ /* during sustain phase chip adds Release Rate (in percussive mode) */
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+
+ if ( op->volume >= MAX_ATT_INDEX )
+ op->volume = MAX_ATT_INDEX;
+ }
+ /* else do nothing in sustain phase */
+ }
+ break;
+
+ case EG_REL: /* release phase */
+ /* exclude modulators in melody channels from performing anything in this mode*/
+ /* allowed are only carriers in melody mode and rhythm slots in rhythm mode */
+
+ /*This table shows which operators and on what conditions are allowed to perform EG_REL:
+ (a) - always perform EG_REL
+ (n) - never perform EG_REL
+ (r) - perform EG_REL in Rhythm mode ONLY
+ 0: 0 (n), 1 (a)
+ 1: 2 (n), 3 (a)
+ 2: 4 (n), 5 (a)
+ 3: 6 (n), 7 (a)
+ 4: 8 (n), 9 (a)
+ 5: 10(n), 11(a)
+ 6: 12(r), 13(a)
+ 7: 14(r), 15(a)
+ 8: 16(r), 17(a)
+ */
+ if ( (i&1) || ((chip->rhythm&0x20) && (i>=12)) )/* exclude modulators */
+ {
+ if(op->eg_type) /* non-percussive mode (sustained tone) */
+ /*this is correct: use RR when SUS = OFF*/
+ /*and use RS when SUS = ON*/
+ {
+ if (CH->sus)
+ {
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rs)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ else
+ {
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rr)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rr + ((chip->eg_cnt>>op->eg_sh_rr)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ }
+ else /* percussive mode */
+ {
+ if ( !(chip->eg_cnt & ((1<<op->eg_sh_rs)-1) ) )
+ {
+ op->volume += eg_inc[op->eg_sel_rs + ((chip->eg_cnt>>op->eg_sh_rs)&7)];
+ if ( op->volume >= MAX_ATT_INDEX )
+ {
+ op->volume = MAX_ATT_INDEX;
+ op->state = EG_OFF;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+ }
+
+ for (i=0; i<9*2; i++)
+ {
+ CH = &chip->P_CH[i/2];
+ op = &CH->SLOT[i&1];
+
+ /* Phase Generator */
+ if(op->vib)
+ {
+ UINT8 block;
+
+ unsigned int fnum_lfo = 8*((CH->block_fnum&0x01c0) >> 6);
+ unsigned int block_fnum = CH->block_fnum * 2;
+ signed int lfo_fn_table_index_offset = lfo_pm_table[chip->LFO_PM + fnum_lfo ];
+
+ if (lfo_fn_table_index_offset) /* LFO phase modulation active */
+ {
+ block_fnum += lfo_fn_table_index_offset;
+ block = (block_fnum&0x1c00) >> 10;
+ op->phase += (chip->fn_tab[block_fnum&0x03ff] >> (7-block)) * op->mul;
+ }
+ else /* LFO phase modulation = zero */
+ {
+ op->phase += op->freq;
+ }
+ }
+ else /* LFO phase modulation disabled for this operator */
+ {
+ op->phase += op->freq;
+ }
+ }
+
+ /* The Noise Generator of the YM3812 is 23-bit shift register.
+ * Period is equal to 2^23-2 samples.
+ * Register works at sampling frequency of the chip, so output
+ * can change on every sample.
+ *
+ * Output of the register and input to the bit 22 is:
+ * bit0 XOR bit14 XOR bit15 XOR bit22
+ *
+ * Simply use bit 22 as the noise output.
+ */
+
+ chip->noise_p += chip->noise_f;
+ i = chip->noise_p >> FREQ_SH; /* number of events (shifts of the shift register) */
+ chip->noise_p &= FREQ_MASK;
+ while (i)
+ {
+ /*
+ UINT32 j;
+ j = ( (chip->noise_rng) ^ (chip->noise_rng>>14) ^ (chip->noise_rng>>15) ^ (chip->noise_rng>>22) ) & 1;
+ chip->noise_rng = (j<<22) | (chip->noise_rng>>1);
+ */
+
+ /*
+ Instead of doing all the logic operations above, we
+ use a trick here (and use bit 0 as the noise output).
+ The difference is only that the noise bit changes one
+ step ahead. This doesn't matter since we don't know
+ what is real state of the noise_rng after the reset.
+ */
+
+ if (chip->noise_rng & 1) chip->noise_rng ^= 0x800302;
+ chip->noise_rng >>= 1;
+
+ i--;
+ }
+}
+
+
+INLINE signed int op_calc(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+
+ p = (env<<5) + sin_tab[wave_tab + ((((signed int)((phase & ~FREQ_MASK) + (pm<<17))) >> FREQ_SH ) & SIN_MASK) ];
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+INLINE signed int op_calc1(UINT32 phase, unsigned int env, signed int pm, unsigned int wave_tab)
+{
+ UINT32 p;
+ INT32 i;
+
+ i = (phase & ~FREQ_MASK) + pm;
+
+/*logerror("i=%08x (i>>16)&511=%8i phase=%i [pm=%08x] ",i, (i>>16)&511, phase>>FREQ_SH, pm);*/
+
+ p = (env<<5) + sin_tab[ wave_tab + ((i>>FREQ_SH) & SIN_MASK)];
+
+/*logerror("(p&255=%i p>>8=%i) out= %i\n", p&255,p>>8, tl_tab[p&255]>>(p>>8) );*/
+
+ if (p >= TL_TAB_LEN)
+ return 0;
+ return tl_tab[p];
+}
+
+
+#define volume_calc(OP) ((OP)->TLL + ((UINT32)(OP)->volume) + (chip->LFO_AM & (OP)->AMmask))
+
+/* calculate output */
+INLINE void chan_calc( YM2413 *chip, OPLL_CH *CH )
+{
+ OPLL_SLOT *SLOT;
+ unsigned int env;
+ signed int out;
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+
+
+ /* SLOT 1 */
+ SLOT = &CH->SLOT[SLOT1];
+ env = volume_calc(SLOT);
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+ phase_modulation = SLOT->op1_out[0];
+
+ SLOT->op1_out[1] = 0;
+
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->fb_shift)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<<SLOT->fb_shift), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ {
+ signed int outp = op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable);
+ chip->output[0] += outp;
+ /* output[0] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable); */
+ }
+}
+
+/*
+ operators used in the rhythm sounds generation process:
+
+ Envelope Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number TL ARDR SLRR Wave Drum Hat Drum Tom Cymbal
+ 6 / 0 12 50 70 90 f0 +
+ 6 / 1 15 53 73 93 f3 +
+ 7 / 0 13 51 71 91 f1 +
+ 7 / 1 16 54 74 94 f4 +
+ 8 / 0 14 52 72 92 f2 +
+ 8 / 1 17 55 75 95 f5 +
+
+ Phase Generator:
+
+channel operator register number Bass High Snare Tom Top
+/ slot number MULTIPLE Drum Hat Drum Tom Cymbal
+ 6 / 0 12 30 +
+ 6 / 1 15 33 +
+ 7 / 0 13 31 + + +
+ 7 / 1 16 34 ----- n o t u s e d -----
+ 8 / 0 14 32 +
+ 8 / 1 17 35 + +
+
+channel operator register number Bass High Snare Tom Top
+number number BLK/FNUM2 FNUM Drum Hat Drum Tom Cymbal
+ 6 12,15 B6 A6 +
+
+ 7 13,16 B7 A7 + + +
+
+ 8 14,17 B8 A8 + + +
+
+*/
+
+/* calculate rhythm */
+
+INLINE void rhythm_calc( YM2413 *chip, OPLL_CH *CH, unsigned int noise )
+{
+ OPLL_SLOT *SLOT;
+ signed int out;
+ unsigned int env;
+ signed int phase_modulation; /* phase modulation input (SLOT 2) */
+
+
+ /* Bass Drum (verified on real YM3812):
+ - depends on the channel 6 'connect' register:
+ when connect = 0 it works the same as in normal (non-rhythm) mode (op1->op2->out)
+ when connect = 1 _only_ operator 2 is present on output (op2->out), operator 1 is ignored
+ - output sample always is multiplied by 2
+ */
+
+
+ /* SLOT 1 */
+ if ( !(chip->mask & OPLL_MASK_BD) )
+ {
+ SLOT = &CH[6].SLOT[SLOT1];
+ env = volume_calc(SLOT);
+
+ out = SLOT->op1_out[0] + SLOT->op1_out[1];
+ SLOT->op1_out[0] = SLOT->op1_out[1];
+
+ phase_modulation = SLOT->op1_out[0];
+
+ SLOT->op1_out[1] = 0;
+ if( env < ENV_QUIET )
+ {
+ if (!SLOT->fb_shift)
+ out = 0;
+ SLOT->op1_out[1] = op_calc1(SLOT->phase, env, (out<<SLOT->fb_shift), SLOT->wavetable );
+ }
+
+ /* SLOT 2 */
+ SLOT++;
+ env = volume_calc(SLOT);
+ if( env < ENV_QUIET )
+ chip->output[1] += op_calc(SLOT->phase, env, phase_modulation, SLOT->wavetable) * 2;
+ }
+
+
+ /* Phase generation is based on:
+ HH (13) channel 7->slot 1 combined with channel 8->slot 2 (same combination as TOP CYMBAL but different output phases)
+ SD (16) channel 7->slot 1
+ TOM (14) channel 8->slot 1
+ TOP (17) channel 7->slot 1 combined with channel 8->slot 2 (same combination as HIGH HAT but different output phases) */
+
+ /* Envelope generation based on:
+ HH channel 7->slot1
+ SD channel 7->slot2
+ TOM channel 8->slot1
+ TOP channel 8->slot2 */
+
+
+ /* The following formulas can be well optimized.
+ I leave them in direct form for now (in case I've missed something).
+ */
+
+ /* High Hat (verified on real YM3812) */
+ if ( !(chip->mask & OPLL_MASK_HH) )
+ {
+ env = volume_calc(chip->SLOT7_1);
+ if( env < ENV_QUIET )
+ {
+
+ /* high hat phase generation:
+ phase = d0 or 234 (based on frequency only)
+ phase = 34 or 2d0 (based on noise)
+ */
+
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((chip->SLOT7_1->phase>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((chip->SLOT7_1->phase>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((chip->SLOT7_1->phase>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0xd0; */
+ /* when res1 = 1 phase = 0x200 | (0xd0>>2); */
+ UINT32 phase = res1 ? (0x200|(0xd0>>2)) : 0xd0;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((chip->SLOT8_2->phase>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((chip->SLOT8_2->phase>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e | bit5e);
+
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | (0xd0>>2); */
+ if (res2)
+ phase = (0x200|(0xd0>>2));
+
+
+ /* when phase & 0x200 is set and noise=1 then phase = 0x200|0xd0 */
+ /* when phase & 0x200 is set and noise=0 then phase = 0x200|(0xd0>>2), ie no change */
+ if (phase&0x200)
+ {
+ if (noise)
+ phase = 0x200|0xd0;
+ }
+ else
+ /* when phase & 0x200 is clear and noise=1 then phase = 0xd0>>2 */
+ /* when phase & 0x200 is clear and noise=0 then phase = 0xd0, ie no change */
+ {
+ if (noise)
+ phase = 0xd0>>2;
+ }
+
+ chip->output[1] += op_calc(phase<<FREQ_SH, env, 0, chip->SLOT7_1->wavetable) * 2;
+ }
+ }
+
+ /* Snare Drum (verified on real YM3812) */
+ if ( !(chip->mask & OPLL_MASK_SD) )
+ {
+ env = volume_calc(chip->SLOT7_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit8 = ((chip->SLOT7_1->phase>>FREQ_SH)>>8)&1;
+
+ /* when bit8 = 0 phase = 0x100; */
+ /* when bit8 = 1 phase = 0x200; */
+ UINT32 phase = bit8 ? 0x200 : 0x100;
+
+ /* Noise bit XOR'es phase by 0x100 */
+ /* when noisebit = 0 pass the phase from calculation above */
+ /* when noisebit = 1 phase ^= 0x100; */
+ /* in other words: phase ^= (noisebit<<8); */
+ if (noise)
+ phase ^= 0x100;
+
+ chip->output[1] += op_calc(phase<<FREQ_SH, env, 0, chip->SLOT7_2->wavetable) * 2;
+ }
+ }
+
+ /* Tom Tom (verified on real YM3812) */
+ if ( !(chip->mask & OPLL_MASK_TOM) )
+ {
+ env = volume_calc(chip->SLOT8_1);
+ if( env < ENV_QUIET )
+ chip->output[1] += op_calc(chip->SLOT8_1->phase, env, 0, chip->SLOT8_1->wavetable) * 2;
+ }
+
+ /* Top Cymbal (verified on real YM2413) */
+ if ( !(chip->mask & OPLL_MASK_CYM) )
+ {
+ env = volume_calc(chip->SLOT8_2);
+ if( env < ENV_QUIET )
+ {
+ /* base frequency derived from operator 1 in channel 7 */
+ unsigned char bit7 = ((chip->SLOT7_1->phase>>FREQ_SH)>>7)&1;
+ unsigned char bit3 = ((chip->SLOT7_1->phase>>FREQ_SH)>>3)&1;
+ unsigned char bit2 = ((chip->SLOT7_1->phase>>FREQ_SH)>>2)&1;
+
+ unsigned char res1 = (bit2 ^ bit7) | bit3;
+
+ /* when res1 = 0 phase = 0x000 | 0x100; */
+ /* when res1 = 1 phase = 0x200 | 0x100; */
+ UINT32 phase = res1 ? 0x300 : 0x100;
+
+ /* enable gate based on frequency of operator 2 in channel 8 */
+ unsigned char bit5e= ((chip->SLOT8_2->phase>>FREQ_SH)>>5)&1;
+ unsigned char bit3e= ((chip->SLOT8_2->phase>>FREQ_SH)>>3)&1;
+
+ unsigned char res2 = (bit3e | bit5e);
+ /* when res2 = 0 pass the phase from calculation above (res1); */
+ /* when res2 = 1 phase = 0x200 | 0x100; */
+ if (res2)
+ phase = 0x300;
+
+ chip->output[1] += op_calc(phase<<FREQ_SH, env, 0, chip->SLOT8_2->wavetable) * 2;
+ }
+ }
+}
+
+
+/* generic table initialize */
+static int init_tables(void)
+{
+ signed int i,x;
+ signed int n;
+ double o,m;
+
+
+ for (x=0; x<TL_RES_LEN; x++)
+ {
+ m = (1<<16) / pow(2, (x+1) * (ENV_STEP/4.0) / 8.0);
+ m = floor(m);
+
+ /* we never reach (1<<16) here due to the (x+1) */
+ /* result fits within 16 bits at maximum */
+
+ n = (int)m; /* 16 bits here */
+ n >>= 4; /* 12 bits here */
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+ /* 11 bits here (rounded) */
+ tl_tab[ x*2 + 0 ] = n;
+ tl_tab[ x*2 + 1 ] = -tl_tab[ x*2 + 0 ];
+
+ for (i=1; i<11; i++)
+ {
+ tl_tab[ x*2+0 + i*2*TL_RES_LEN ] = tl_tab[ x*2+0 ]>>i;
+ tl_tab[ x*2+1 + i*2*TL_RES_LEN ] = -tl_tab[ x*2+0 + i*2*TL_RES_LEN ];
+ }
+ #if 0
+ logerror("tl %04i", x*2);
+ for (i=0; i<11; i++)
+ logerror(", [%02i] %5i", i*2, tl_tab[ x*2 /*+1*/ + i*2*TL_RES_LEN ] );
+ logerror("\n");
+ #endif
+ }
+ /*logerror("ym2413.c: TL_TAB_LEN = %i elements (%i bytes)\n",TL_TAB_LEN, (int)sizeof(tl_tab));*/
+
+
+ for (i=0; i<SIN_LEN; i++)
+ {
+ /* non-standard sinus */
+ m = sin( ((i*2)+1) * M_PI / SIN_LEN ); /* checked against the real chip */
+
+ /* we never reach zero here due to ((i*2)+1) */
+
+ if (m>0.0)
+ o = 8*log(1.0/m)/log(2.0); /* convert to 'decibels' */
+ else
+ o = 8*log(-1.0/m)/log(2.0); /* convert to 'decibels' */
+
+ o = o / (ENV_STEP/4);
+
+ n = (int)(2.0*o);
+ if (n&1) /* round to nearest */
+ n = (n>>1)+1;
+ else
+ n = n>>1;
+
+ /* waveform 0: standard sinus */
+ sin_tab[ i ] = n*2 + (m>=0.0? 0: 1 );
+
+ /*logerror("ym2413.c: sin [%4i (hex=%03x)]= %4i (tl_tab value=%5i)\n", i, i, sin_tab[i], tl_tab[sin_tab[i]] );*/
+
+
+ /* waveform 1: __ __ */
+ /* / \____/ \____*/
+ /* output only first half of the sinus waveform (positive one) */
+ if (i & (1<<(SIN_BITS-1)) )
+ sin_tab[1*SIN_LEN+i] = TL_TAB_LEN;
+ else
+ sin_tab[1*SIN_LEN+i] = sin_tab[i];
+
+ /*logerror("ym2413.c: sin1[%4i]= %4i (tl_tab value=%5i)\n", i, sin_tab[1*SIN_LEN+i], tl_tab[sin_tab[1*SIN_LEN+i]] );*/
+ }
+#if 0
+ logerror("YM2413.C: ENV_QUIET= %08x (*32=%08x)\n", ENV_QUIET, ENV_QUIET*32 );
+ for (i=0; i<ENV_QUIET; i++)
+ {
+ logerror("tl_tb[%4x(%4i)]=%8x\n", i<<5, i, tl_tab[i<<5] );
+ }
+#endif
+#ifdef SAVE_SAMPLE
+ sample[0]=fopen("sampsum.pcm","wb");
+#endif
+
+ return 1;
+}
+
+static void OPLL_initalize(YM2413 *chip)
+{
+ int i;
+
+ /* frequency base */
+ chip->freqbase = (chip->rate) ? ((double)chip->clock / 72.0) / chip->rate : 0;
+ if ( fabs( chip->freqbase - 1.0 ) < 0.0000001 )
+ chip->freqbase = 1.0;
+#if 0
+ chip->rate = (double)chip->clock / 72.0;
+ chip->freqbase = 1.0;
+ logerror("freqbase=%f\n", chip->freqbase);
+#endif
+
+
+
+ /* make fnumber -> increment counter table */
+ for( i = 0 ; i < 1024; i++ )
+ {
+ /* OPLL (YM2413) phase increment counter = 18bit */
+
+ chip->fn_tab[i] = (UINT32)( (double)i * 64 * chip->freqbase * (1<<(FREQ_SH-10)) ); /* -10 because chip works with 10.10 fixed point, while we use 16.16 */
+#if 0
+ logerror("ym2413.c: fn_tab[%4i] = %08x (dec=%8i)\n",
+ i, chip->fn_tab[i]>>6, chip->fn_tab[i]>>6 );
+#endif
+ }
+
+#if 0
+ for( i=0 ; i < 16 ; i++ )
+ {
+ logerror("ym2413.c: sl_tab[%i] = %08x\n", i, sl_tab[i] );
+ }
+ for( i=0 ; i < 8 ; i++ )
+ {
+ int j;
+ logerror("ym2413.c: ksl_tab[oct=%2i] =",i);
+ for (j=0; j<16; j++)
+ {
+ logerror("%08x ", ksl_tab[i*16+j] );
+ }
+ logerror("\n");
+ }
+#endif
+
+
+ /* Amplitude modulation: 27 output levels (triangle waveform); 1 level takes one of: 192, 256 or 448 samples */
+ /* One entry from LFO_AM_TABLE lasts for 64 samples */
+ chip->lfo_am_inc = (1.0 / 64.0 ) * (1<<LFO_SH) * chip->freqbase;
+
+ /* Vibrato: 8 output levels (triangle waveform); 1 level takes 1024 samples */
+ chip->lfo_pm_inc = (1.0 / 1024.0) * (1<<LFO_SH) * chip->freqbase;
+
+ /*logerror ("chip->lfo_am_inc = %8x ; chip->lfo_pm_inc = %8x\n", chip->lfo_am_inc, chip->lfo_pm_inc);*/
+
+ /* Noise generator: a step takes 1 sample */
+ chip->noise_f = (1.0 / 1.0) * (1<<FREQ_SH) * chip->freqbase;
+ /*logerror("YM2413init noise_f=%8x\n", chip->noise_f);*/
+
+ chip->eg_timer_add = (1<<EG_SH) * chip->freqbase;
+ chip->eg_timer_overflow = ( 1 ) * (1<<EG_SH);
+ /*logerror("YM2413init eg_timer_add=%8x eg_timer_overflow=%8x\n", chip->eg_timer_add, chip->eg_timer_overflow);*/
+}
+
+INLINE void KEY_ON(OPLL_SLOT *SLOT, UINT32 key_set)
+{
+ if( !SLOT->key )
+ {
+ /* do NOT restart Phase Generator (verified on real YM2413)*/
+ /* phase -> Dump */
+ SLOT->state = EG_DMP;
+ }
+ SLOT->key |= key_set;
+}
+
+INLINE void KEY_OFF(OPLL_SLOT *SLOT, UINT32 key_clr)
+{
+ if( SLOT->key )
+ {
+ SLOT->key &= key_clr;
+
+ if( !SLOT->key )
+ {
+ /* phase -> Release */
+ if (SLOT->state>EG_REL)
+ SLOT->state = EG_REL;
+ }
+ }
+}
+
+/* update phase increment counter of operator (also update the EG rates if necessary) */
+INLINE void CALC_FCSLOT(OPLL_CH *CH,OPLL_SLOT *SLOT)
+{
+ int ksr;
+ UINT32 SLOT_rs;
+ UINT32 SLOT_dp;
+
+ /* (frequency) phase increment counter */
+ SLOT->freq = CH->fc * SLOT->mul;
+ ksr = CH->kcode >> SLOT->KSR;
+
+ if( SLOT->ksr != ksr )
+ {
+ SLOT->ksr = ksr;
+
+ /* calculate envelope generator rates */
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+
+ }
+
+ if (CH->sus)
+ SLOT_rs = 16 + (5<<2);
+ else
+ SLOT_rs = 16 + (7<<2);
+
+ SLOT->eg_sh_rs = eg_rate_shift [SLOT_rs + SLOT->ksr ];
+ SLOT->eg_sel_rs = eg_rate_select[SLOT_rs + SLOT->ksr ];
+
+ SLOT_dp = 16 + (13<<2);
+ SLOT->eg_sh_dp = eg_rate_shift [SLOT_dp + SLOT->ksr ];
+ SLOT->eg_sel_dp = eg_rate_select[SLOT_dp + SLOT->ksr ];
+}
+
+/* set multi,am,vib,EG-TYP,KSR,mul */
+INLINE void set_mul(YM2413 *chip,int slot,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[slot/2];
+ OPLL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->mul = mul_tab[v&0x0f];
+ SLOT->KSR = (v&0x10) ? 0 : 2;
+ SLOT->eg_type = (v&0x20);
+ SLOT->vib = (v&0x40);
+ SLOT->AMmask = (v&0x80) ? ~0 : 0;
+ CALC_FCSLOT(CH,SLOT);
+}
+
+/* set ksl, tl */
+INLINE void set_ksl_tl(YM2413 *chip,int chan,int v)
+{
+ int ksl;
+ OPLL_CH *CH = &chip->P_CH[chan];
+/* modulator */
+ OPLL_SLOT *SLOT = &CH->SLOT[SLOT1];
+
+ ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TL = (v&0x3f)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+}
+
+/* set ksl , waveforms, feedback */
+INLINE void set_ksl_wave_fb(YM2413 *chip,int chan,int v)
+{
+ int ksl;
+ OPLL_CH *CH = &chip->P_CH[chan];
+/* modulator */
+ OPLL_SLOT *SLOT = &CH->SLOT[SLOT1];
+ SLOT->wavetable = ((v&0x08)>>3)*SIN_LEN;
+ SLOT->fb_shift = (v&7) ? (v&7) + 8 : 0;
+
+/*carrier*/
+ SLOT = &CH->SLOT[SLOT2];
+ ksl = v>>6; /* 0 / 1.5 / 3.0 / 6.0 dB/OCT */
+
+ SLOT->ksl = ksl ? 3-ksl : 31;
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+
+ SLOT->wavetable = ((v&0x10)>>4)*SIN_LEN;
+}
+
+/* set attack rate & decay rate */
+INLINE void set_ar_dr(YM2413 *chip,int slot,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[slot/2];
+ OPLL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->ar = (v>>4) ? 16 + ((v>>4) <<2) : 0;
+
+ if ((SLOT->ar + SLOT->ksr) < 16+62)
+ {
+ SLOT->eg_sh_ar = eg_rate_shift [SLOT->ar + SLOT->ksr ];
+ SLOT->eg_sel_ar = eg_rate_select[SLOT->ar + SLOT->ksr ];
+ }
+ else
+ {
+ SLOT->eg_sh_ar = 0;
+ SLOT->eg_sel_ar = 13*RATE_STEPS;
+ }
+
+ SLOT->dr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_dr = eg_rate_shift [SLOT->dr + SLOT->ksr ];
+ SLOT->eg_sel_dr = eg_rate_select[SLOT->dr + SLOT->ksr ];
+}
+
+/* set sustain level & release rate */
+INLINE void set_sl_rr(YM2413 *chip,int slot,int v)
+{
+ OPLL_CH *CH = &chip->P_CH[slot/2];
+ OPLL_SLOT *SLOT = &CH->SLOT[slot&1];
+
+ SLOT->sl = sl_tab[ v>>4 ];
+
+ SLOT->rr = (v&0x0f)? 16 + ((v&0x0f)<<2) : 0;
+ SLOT->eg_sh_rr = eg_rate_shift [SLOT->rr + SLOT->ksr ];
+ SLOT->eg_sel_rr = eg_rate_select[SLOT->rr + SLOT->ksr ];
+}
+
+static void load_instrument(YM2413 *chip, UINT32 chan, UINT32 slot, UINT8* inst )
+{
+ set_mul (chip, slot, inst[0]);
+ set_mul (chip, slot+1, inst[1]);
+ set_ksl_tl (chip, chan, inst[2]);
+ set_ksl_wave_fb (chip, chan, inst[3]);
+ set_ar_dr (chip, slot, inst[4]);
+ set_ar_dr (chip, slot+1, inst[5]);
+ set_sl_rr (chip, slot, inst[6]);
+ set_sl_rr (chip, slot+1, inst[7]);
+}
+static void update_instrument_zero(YM2413 *chip, UINT8 r )
+{
+ UINT8* inst = &chip->inst_tab[0][0]; /* point to user instrument */
+ UINT32 chan;
+ UINT32 chan_max;
+
+ chan_max = 9;
+ if (chip->rhythm & 0x20)
+ chan_max=6;
+
+ switch(r)
+ {
+ case 0:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_mul (chip, chan*2, inst[0]);
+ }
+ }
+ break;
+ case 1:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_mul (chip, chan*2+1,inst[1]);
+ }
+ }
+ break;
+ case 2:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ksl_tl (chip, chan, inst[2]);
+ }
+ }
+ break;
+ case 3:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ksl_wave_fb (chip, chan, inst[3]);
+ }
+ }
+ break;
+ case 4:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ar_dr (chip, chan*2, inst[4]);
+ }
+ }
+ break;
+ case 5:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_ar_dr (chip, chan*2+1,inst[5]);
+ }
+ }
+ break;
+ case 6:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_sl_rr (chip, chan*2, inst[6]);
+ }
+ }
+ break;
+ case 7:
+ for (chan=0; chan<chan_max; chan++)
+ {
+ if ((chip->instvol_r[chan]&0xf0)==0)
+ {
+ set_sl_rr (chip, chan*2+1,inst[7]);
+ }
+ }
+ break;
+ }
+}
+
+/* write a value v to register r on chip chip */
+static void OPLLWriteReg(YM2413 *chip, int r, int v)
+{
+ OPLL_CH *CH;
+ OPLL_SLOT *SLOT;
+ UINT8 *inst;
+ int chan;
+ int slot;
+
+ /* adjust bus to 8 bits */
+ r &= 0xff;
+ v &= 0xff;
+
+
+ switch(r&0xf0)
+ {
+ case 0x00: /* 00-0f:control */
+ {
+ switch(r&0x0f)
+ {
+ case 0x00: /* AM/VIB/EGTYP/KSR/MULTI (modulator) */
+ case 0x01: /* AM/VIB/EGTYP/KSR/MULTI (carrier) */
+ case 0x02: /* Key Scale Level, Total Level (modulator) */
+ case 0x03: /* Key Scale Level, carrier waveform, modulator waveform, Feedback */
+ case 0x04: /* Attack, Decay (modulator) */
+ case 0x05: /* Attack, Decay (carrier) */
+ case 0x06: /* Sustain, Release (modulator) */
+ case 0x07: /* Sustain, Release (carrier) */
+ chip->inst_tab[0][r & 0x07] = v;
+ update_instrument_zero(chip,r&7);
+ break;
+
+ case 0x0e: /* x, x, r,bd,sd,tom,tc,hh */
+ {
+ if(v&0x20)
+ {
+ if ((chip->rhythm&0x20)==0)
+ /*rhythm off to on*/
+ {
+ logerror("YM2413: Rhythm mode enable\n");
+
+ /* Load instrument settings for channel seven(chan=6 since we're zero based). (Bass drum) */
+ chan = 6;
+ inst = &chip->inst_tab[16][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ /* Load instrument settings for channel eight. (High hat and snare drum) */
+ chan = 7;
+ inst = &chip->inst_tab[17][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ CH = &chip->P_CH[chan];
+ SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH */
+ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+
+ /* Load instrument settings for channel nine. (Tom-tom and top cymbal) */
+ chan = 8;
+ inst = &chip->inst_tab[18][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ CH = &chip->P_CH[chan];
+ SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is TOM */
+ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ /* BD key on/off */
+ if(v&0x10)
+ {
+ KEY_ON (&chip->P_CH[6].SLOT[SLOT1], 2);
+ KEY_ON (&chip->P_CH[6].SLOT[SLOT2], 2);
+ }
+ else
+ {
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ }
+ /* HH key on/off */
+ if(v&0x01) KEY_ON (&chip->P_CH[7].SLOT[SLOT1], 2);
+ else KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key on/off */
+ if(v&0x08) KEY_ON (&chip->P_CH[7].SLOT[SLOT2], 2);
+ else KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key on/off */
+ if(v&0x04) KEY_ON (&chip->P_CH[8].SLOT[SLOT1], 2);
+ else KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY key on/off */
+ if(v&0x02) KEY_ON (&chip->P_CH[8].SLOT[SLOT2], 2);
+ else KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ else
+ {
+ if ((chip->rhythm&0x20)==1)
+ /*rhythm on to off*/
+ {
+ logerror("YM2413: Rhythm mode disable\n");
+ /* Load instrument settings for channel seven(chan=6 since we're zero based).*/
+ chan = 6;
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ /* Load instrument settings for channel eight.*/
+ chan = 7;
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ /* Load instrument settings for channel nine.*/
+ chan = 8;
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+ }
+ /* BD key off */
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT1],~2);
+ KEY_OFF(&chip->P_CH[6].SLOT[SLOT2],~2);
+ /* HH key off */
+ KEY_OFF(&chip->P_CH[7].SLOT[SLOT1],~2);
+ /* SD key off */
+ KEY_OFF(&chip->P_CH[7].SLOT[SLOT2],~2);
+ /* TOM key off */
+ KEY_OFF(&chip->P_CH[8].SLOT[SLOT1],~2);
+ /* TOP-CY off */
+ KEY_OFF(&chip->P_CH[8].SLOT[SLOT2],~2);
+ }
+ chip->rhythm = v&0x3f;
+ }
+ break;
+ }
+ }
+ break;
+
+ case 0x10:
+ case 0x20:
+ {
+ int block_fnum;
+
+ chan = r&0x0f;
+
+ if (chan >= 9)
+ chan -= 9; /* verified on real YM2413 */
+
+ CH = &chip->P_CH[chan];
+
+ if(r&0x10)
+ { /* 10-18: FNUM 0-7 */
+ block_fnum = (CH->block_fnum&0x0f00) | v;
+ }
+ else
+ { /* 20-28: suson, keyon, block, FNUM 8 */
+ block_fnum = ((v&0x0f)<<8) | (CH->block_fnum&0xff);
+
+ if(v&0x10)
+ {
+ KEY_ON (&CH->SLOT[SLOT1], 1);
+ KEY_ON (&CH->SLOT[SLOT2], 1);
+ }
+ else
+ {
+ KEY_OFF(&CH->SLOT[SLOT1],~1);
+ KEY_OFF(&CH->SLOT[SLOT2],~1);
+ }
+
+
+ if (CH->sus!=(v&0x20))
+ logerror("chan=%i sus=%2x\n",chan,v&0x20);
+
+ CH->sus = v & 0x20;
+ }
+ /* update */
+ if(CH->block_fnum != block_fnum)
+ {
+ UINT8 block;
+
+ CH->block_fnum = block_fnum;
+
+ /* BLK 2,1,0 bits -> bits 3,2,1 of kcode, FNUM MSB -> kcode LSB */
+ CH->kcode = (block_fnum&0x0f00)>>8;
+
+ CH->ksl_base = ksl_tab[block_fnum>>5];
+
+ block_fnum = block_fnum * 2;
+ block = (block_fnum&0x1c00) >> 10;
+ CH->fc = chip->fn_tab[block_fnum&0x03ff] >> (7-block);
+
+ /* refresh Total Level in both SLOTs of this channel */
+ CH->SLOT[SLOT1].TLL = CH->SLOT[SLOT1].TL + (CH->ksl_base>>CH->SLOT[SLOT1].ksl);
+ CH->SLOT[SLOT2].TLL = CH->SLOT[SLOT2].TL + (CH->ksl_base>>CH->SLOT[SLOT2].ksl);
+
+ /* refresh frequency counter in both SLOTs of this channel */
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT1]);
+ CALC_FCSLOT(CH,&CH->SLOT[SLOT2]);
+ }
+ }
+ break;
+
+ case 0x30: /* inst 4 MSBs, VOL 4 LSBs */
+ {
+ UINT8 old_instvol;
+
+ chan = r&0x0f;
+
+ if (chan >= 9)
+ chan -= 9; /* verified on real YM2413 */
+
+ old_instvol = chip->instvol_r[chan];
+ chip->instvol_r[chan] = v; /* store for later use */
+
+ CH = &chip->P_CH[chan];
+ SLOT = &CH->SLOT[SLOT2]; /* carrier */
+ SLOT->TL = ((v&0x0f)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+
+
+ /*check wether we are in rhythm mode and handle instrument/volume register accordingly*/
+ if ((chan>=6) && (chip->rhythm&0x20))
+ {
+ /* we're in rhythm mode*/
+
+ if (chan>=7) /* only for channel 7 and 8 (channel 6 is handled in usual way)*/
+ {
+ SLOT = &CH->SLOT[SLOT1]; /* modulator envelope is HH(chan=7) or TOM(chan=8) */
+ SLOT->TL = ((chip->instvol_r[chan]>>4)<<2)<<(ENV_BITS-2-7); /* 7 bits TL (bit 6 = always 0) */
+ SLOT->TLL = SLOT->TL + (CH->ksl_base>>SLOT->ksl);
+ }
+ }
+ else
+ {
+ if ( (old_instvol&0xf0) == (v&0xf0) )
+ return;
+
+ inst = &chip->inst_tab[chip->instvol_r[chan]>>4][0];
+ slot = chan*2;
+
+ load_instrument(chip, chan, slot, inst);
+
+ #if 0
+ logerror("YM2413: chan#%02i inst=%02i: (r=%2x, v=%2x)\n",chan,v>>4,r,v);
+ logerror(" 0:%2x 1:%2x\n",inst[0],inst[1]); logerror(" 2:%2x 3:%2x\n",inst[2],inst[3]);
+ logerror(" 4:%2x 5:%2x\n",inst[4],inst[5]); logerror(" 6:%2x 7:%2x\n",inst[6],inst[7]);
+ #endif
+ }
+ }
+ break;
+
+ default:
+ break;
+ }
+}
+
+/* lock/unlock for common table */
+static void OPLLResetChip(YM2413 *chip)
+{
+ int c,s;
+ int i;
+
+ chip->eg_timer = 0;
+ chip->eg_cnt = 0;
+
+ chip->noise_rng = 1; /* noise shift register */
+
+ chip->mask = 0;
+
+ /* setup instruments table */
+ if (!chip->chip_type)
+ {
+ for (i=0; i<19; i++)
+ {
+ for (c=0; c<8; c++)
+ {
+ chip->inst_tab[i][c] = table[i][c];
+ }
+ }
+ }
+ else
+ {
+ memset( &chip->inst_tab, 0, sizeof(chip->inst_tab) );
+
+ for (i=0; i<15; i++)
+ {
+ for (c=0; c<8; c++)
+ {
+ chip->inst_tab[i+1][c] = table_vrc7[i][c];
+ }
+ }
+ }
+
+
+ /* reset with register write */
+ OPLLWriteReg(chip,0x0f,0); /*test reg*/
+ for(i = 0x3f ; i >= 0x10 ; i-- ) OPLLWriteReg(chip,i,0x00);
+
+ /* reset operator parameters */
+ for( c = 0 ; c < 9 ; c++ )
+ {
+ OPLL_CH *CH = &chip->P_CH[c];
+ for(s = 0 ; s < 2 ; s++ )
+ {
+ /* wave table */
+ CH->SLOT[s].wavetable = 0;
+ CH->SLOT[s].state = EG_OFF;
+ CH->SLOT[s].volume = MAX_ATT_INDEX;
+ }
+ }
+}
+
+/* Create one of virtual YM2413 */
+/* 'clock' is chip clock in Hz */
+/* 'rate' is sampling rate */
+static YM2413 *OPLLCreate(int clock, int rate, int type)
+{
+ char *ptr;
+ YM2413 *chip;
+ int state_size;
+
+ init_tables();
+
+ /* calculate chip state size */
+ state_size = sizeof(YM2413);
+
+ /* allocate memory block */
+ ptr = (char *)malloc(state_size);
+
+ if (ptr==NULL)
+ return NULL;
+
+ /* clear */
+ memset(ptr,0,state_size);
+
+ chip = (YM2413 *)ptr;
+
+ chip->clock = clock;
+ chip->rate = rate;
+
+ chip->chip_type = type;
+
+ chip->mask = 0;
+
+ /* init global tables */
+ OPLL_initalize(chip);
+
+ /* reset chip */
+ OPLLResetChip(chip);
+ return chip;
+}
+
+/* Destroy one of virtual YM3812 */
+static void OPLLDestroy(YM2413 *chip)
+{
+ free(chip);
+}
+
+/* Option handlers */
+
+static void OPLLSetUpdateHandler(YM2413 *chip,OPLL_UPDATEHANDLER UpdateHandler,void * param)
+{
+ chip->UpdateHandler = UpdateHandler;
+ chip->UpdateParam = param;
+}
+
+/* YM3812 I/O interface */
+static void OPLLWrite(YM2413 *chip,int a,int v)
+{
+ if( !(a&1) )
+ { /* address port */
+ chip->address = v & 0xff;
+ }
+ else
+ { /* data port */
+ if(chip->UpdateHandler) chip->UpdateHandler(chip->UpdateParam,0);
+ OPLLWriteReg(chip,chip->address,v);
+ }
+}
+
+static unsigned char OPLLRead(YM2413 *chip,int a)
+{
+ if( !(a&1) )
+ {
+ /* status port */
+ return chip->status;
+ }
+ return 0xff;
+}
+
+
+
+
+
+void * ym2413_init(int clock, int rate, int type)
+{
+ /* emulator create */
+ return OPLLCreate(clock, rate, type);
+}
+
+void ym2413_shutdown(void *chip)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+
+ /* emulator shutdown */
+ OPLLDestroy(OPLL);
+}
+
+void ym2413_reset_chip(void *chip)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ OPLLResetChip(OPLL);
+}
+
+void ym2413_write(void *chip, int a, int v)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ OPLLWrite(OPLL, a, v);
+}
+
+unsigned char ym2413_read(void *chip, int a)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ return OPLLRead(OPLL, a) & 0x03 ;
+}
+
+void ym2413_set_update_handler(void *chip,OPLL_UPDATEHANDLER UpdateHandler,void *param)
+{
+ YM2413 *OPLL = (YM2413 *)chip;
+ OPLLSetUpdateHandler(OPLL, UpdateHandler, param);
+}
+
+
+/*
+** Generate samples for one of the YM2413's
+**
+** 'which' is the virtual YM2413 number
+** '*buffer' is the output buffer pointer
+** 'length' is the number of samples that should be generated
+*/
+void ym2413_update_one(void *_chip, SAMP **buffers, int length)
+{
+ YM2413 *chip = (YM2413 *)_chip;
+ UINT8 rhythm = chip->rhythm&0x20;
+ SAMP *bufMO = buffers[0];
+ SAMP *bufRO = buffers[1];
+
+ int i,j;
+
+ chip->SLOT7_1 = &chip->P_CH[7].SLOT[SLOT1];
+ chip->SLOT7_2 = &chip->P_CH[7].SLOT[SLOT2];
+ chip->SLOT8_1 = &chip->P_CH[8].SLOT[SLOT1];
+ chip->SLOT8_2 = &chip->P_CH[8].SLOT[SLOT2];
+
+
+ for( i=0; i < length ; i++ )
+ {
+ int mo,ro;
+
+ chip->output[0] = 0;
+ chip->output[1] = 0;
+
+ advance_lfo(chip);
+
+#if 0
+ /* FM part */
+ chan_calc(chip,&chip->P_CH[0]);
+/* SAVE_SEPARATE_CHANNEL(0); */
+ chan_calc(chip,&chip->P_CH[1]);
+ chan_calc(chip,&chip->P_CH[2]);
+ chan_calc(chip,&chip->P_CH[3]);
+ chan_calc(chip,&chip->P_CH[4]);
+ chan_calc(chip,&chip->P_CH[5]);
+#else
+ for ( j=0; j < 6; j++ )
+ {
+ if (!(chip->mask & OPLL_MASK_CH(j))) chan_calc(chip, &chip->P_CH[j]);
+ }
+#endif
+
+ if(!rhythm)
+ {
+#if 0
+ chan_calc(chip,&chip->P_CH[6]);
+ chan_calc(chip,&chip->P_CH[7]);
+ chan_calc(chip,&chip->P_CH[8]);
+#else
+ for ( j=6; j < 9; j++ )
+ {
+ if (!(chip->mask & OPLL_MASK_CH(j))) chan_calc(chip, &chip->P_CH[j]);
+ }
+#endif
+ }
+ else /* Rhythm part */
+ {
+ if ( ( chip->mask & OPLL_MASK_RHYTHM ) != OPLL_MASK_RHYTHM )
+ rhythm_calc(chip,&chip->P_CH[0], (chip->noise_rng>>0)&1 );
+ }
+
+ mo = chip->output[0];
+ ro = chip->output[1];
+
+ mo >>= FINAL_SH;
+ ro >>= FINAL_SH;
+
+ /* limit check */
+ mo = limit( mo , MAXOUT, MINOUT );
+ ro = limit( ro , MAXOUT, MINOUT );
+
+ /* store to sound buffer */
+ bufMO[i] = mo;
+ bufRO[i] = ro;
+
+ advance(chip);
+ }
+
+}
+
+void ym2413_advance_lfo(void *_chip)
+{
+ YM2413 *chip = (YM2413 *)_chip;
+ advance_lfo(chip);
+}
+
+void ym2413_advance(void *_chip)
+{
+ YM2413 *chip = (YM2413 *)_chip;
+ advance(chip);
+}
+
+SAMP ym2413_calcch(void *_chip, int ch)
+{
+ YM2413 *chip = (YM2413 *)_chip;
+
+ int output;
+
+ chip->output[0] = 0;
+ chip->output[1] = 0;
+
+ if (ch >= 0 && ch < 6) chan_calc( chip, &chip->P_CH[ch] );
+ else if (ch >= 6 && ch < 9)
+ {
+ UINT8 rhythm = chip->rhythm&0x20;
+ if (!rhythm) chan_calc( chip, &chip->P_CH[ch] );
+ else if (ch == 6) rhythm_calc(chip,&chip->P_CH[0], (chip->noise_rng>>0)&1 );
+ }
+
+ output = chip->output[0];
+ output += chip->output[1];
+
+ return output;
+}
+
+void * ym2413_get_inst0(void *_chip)
+{
+ YM2413 *chip = (YM2413 *)_chip;
+
+ return &chip->inst_tab;
+}
+
+void ym2413_set_mask(void *_chip, UINT32 mask)
+{
+ YM2413 *chip = (YM2413 *)_chip;
+
+ chip->mask = mask;
+} \ No newline at end of file
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/ym2413.h b/plugins/gme/game-music-emu-0.6pre/gme/ym2413.h
new file mode 100644
index 00000000..679db352
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/ym2413.h
@@ -0,0 +1,61 @@
+#pragma once
+
+#ifndef __YM2413_H__
+#define __YM2413_H__
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* select output bits size of output : 8 or 16 */
+#define SAMPLE_BITS 16
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+
+typedef INT32 stream_sample_t;
+#endif
+
+typedef stream_sample_t SAMP;
+/*
+#if (SAMPLE_BITS==16)
+typedef INT16 SAMP;
+#endif
+#if (SAMPLE_BITS==8)
+typedef INT8 SAMP;
+#endif
+*/
+
+
+
+void *ym2413_init(int clock, int rate, int type);
+void ym2413_shutdown(void *chip);
+void ym2413_reset_chip(void *chip);
+void ym2413_write(void *chip, int a, int v);
+unsigned char ym2413_read(void *chip, int a);
+void ym2413_update_one(void *chip, SAMP **buffers, int length);
+
+void ym2413_advance_lfo(void *chip); /* call this once */
+SAMP ym2413_calcch(void *chip, int ch); /* then call this for each channel */
+void ym2413_advance(void *chip); /* then call this */
+
+void * ym2413_get_inst0(void *chip);
+
+void ym2413_set_mask(void *chip, UINT32 mask);
+
+typedef void (*OPLL_UPDATEHANDLER)(void *param,int min_interval_us);
+
+void ym2413_set_update_handler(void *chip, OPLL_UPDATEHANDLER UpdateHandler, void *param);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*__YM2413_H__*/
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.cpp b/plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.cpp
new file mode 100644
index 00000000..48167205
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.cpp
@@ -0,0 +1,655 @@
+/*
+**
+** File: ymdeltat.c
+**
+** YAMAHA DELTA-T adpcm sound emulation subroutine
+** used by fmopl.c (Y8950) and fm.c (YM2608 and YM2610/B)
+**
+** Base program is YM2610 emulator by Hiromitsu Shioya.
+** Written by Tatsuyuki Satoh
+** Improvements by Jarek Burczynski (bujar at mame dot net)
+**
+**
+** History:
+**
+** 03-08-2003 Jarek Burczynski:
+** - fixed BRDY flag implementation.
+**
+** 24-07-2003 Jarek Burczynski, Frits Hilderink:
+** - fixed delault value for control2 in YM_DELTAT_ADPCM_Reset
+**
+** 22-07-2003 Jarek Burczynski, Frits Hilderink:
+** - fixed external memory support
+**
+** 15-06-2003 Jarek Burczynski:
+** - implemented CPU -> AUDIO ADPCM synthesis (via writes to the ADPCM data reg $08)
+** - implemented support for the Limit address register
+** - supported two bits from the control register 2 ($01): RAM TYPE (x1 bit/x8 bit), ROM/RAM
+** - implemented external memory access (read/write) via the ADPCM data reg reads/writes
+** Thanks go to Frits Hilderink for the example code.
+**
+** 14-06-2003 Jarek Burczynski:
+** - various fixes to enable proper support for status register flags: BSRDY, PCM BSY, ZERO
+** - modified EOS handling
+**
+** 05-04-2003 Jarek Burczynski:
+** - implemented partial support for external/processor memory on sample replay
+**
+** 01-12-2002 Jarek Burczynski:
+** - fixed first missing sound in gigandes thanks to previous fix (interpolator) by ElSemi
+** - renamed/removed some YM_DELTAT struct fields
+**
+** 28-12-2001 Acho A. Tang
+** - added EOS status report on ADPCM playback.
+**
+** 05-08-2001 Jarek Burczynski:
+** - now_step is initialized with 0 at the start of play.
+**
+** 12-06-2001 Jarek Burczynski:
+** - corrected end of sample bug in YM_DELTAT_ADPCM_CALC.
+** Checked on real YM2610 chip - address register is 24 bits wide.
+** Thanks go to Stefan Jokisch (stefan.jokisch@gmx.de) for tracking down the problem.
+**
+** TO DO:
+** Check size of the address register on the other chips....
+**
+** Version 0.72
+**
+** sound chips that have this unit:
+** YM2608 OPNA
+** YM2610/B OPNB
+** Y8950 MSX AUDIO
+**
+*/
+
+#include "ymdeltat.h"
+#define INLINE __inline
+#define logerror (void)
+
+#define YM_DELTAT_DELTA_MAX (24576)
+#define YM_DELTAT_DELTA_MIN (127)
+#define YM_DELTAT_DELTA_DEF (127)
+
+#define YM_DELTAT_DECODE_RANGE 32768
+#define YM_DELTAT_DECODE_MIN (-(YM_DELTAT_DECODE_RANGE))
+#define YM_DELTAT_DECODE_MAX ((YM_DELTAT_DECODE_RANGE)-1)
+
+
+/* Forecast to next Forecast (rate = *8) */
+/* 1/8 , 3/8 , 5/8 , 7/8 , 9/8 , 11/8 , 13/8 , 15/8 */
+static const INT32 ym_deltat_decode_tableB1[16] = {
+ 1, 3, 5, 7, 9, 11, 13, 15,
+ -1, -3, -5, -7, -9, -11, -13, -15,
+};
+/* delta to next delta (rate= *64) */
+/* 0.9 , 0.9 , 0.9 , 0.9 , 1.2 , 1.6 , 2.0 , 2.4 */
+static const INT32 ym_deltat_decode_tableB2[16] = {
+ 57, 57, 57, 57, 77, 102, 128, 153,
+ 57, 57, 57, 57, 77, 102, 128, 153
+};
+
+#if 0
+void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT)
+{
+ logerror("BRDY_callback reached (flag set) !\n");
+
+ /* set BRDY bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+}
+#endif
+
+UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT)
+{
+ UINT8 v = 0;
+
+ /* external memory read */
+ if ( (DELTAT->portstate & 0xe0)==0x20 )
+ {
+ /* two dummy reads */
+ if (DELTAT->memread)
+ {
+ DELTAT->now_addr = DELTAT->start << 1;
+ DELTAT->memread--;
+ return 0;
+ }
+
+
+ if ( DELTAT->now_addr != (DELTAT->end<<1) )
+ {
+ v = DELTAT->memory[DELTAT->now_addr>>1];
+
+ /*logerror("YM Delta-T memory read $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/
+
+ DELTAT->now_addr+=2; /* two nibbles at a time */
+
+ /* reset BRDY bit in status register, which means we are reading the memory now */
+ if(DELTAT->status_reset_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+
+ /* setup a timer that will callback us in 10 master clock cycles for Y8950
+ * in the callback set the BRDY flag to 1 , which means we have another data ready.
+ * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
+ */
+ /* set BRDY bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+ }
+ else
+ {
+ /* set EOS bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_EOS_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);
+ }
+ }
+
+ return v;
+}
+
+
+/* 0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
+static const UINT8 dram_rightshift[4]={3,0,0,0};
+
+/* DELTA-T ADPCM write register */
+void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v)
+{
+ if(r>=0x10) return;
+ DELTAT->reg[r] = v; /* stock data */
+
+ switch( r )
+ {
+ case 0x00:
+/*
+START:
+ Accessing *external* memory is started when START bit (D7) is set to "1", so
+ you must set all conditions needed for recording/playback before starting.
+ If you access *CPU-managed* memory, recording/playback starts after
+ read/write of ADPCM data register $08.
+
+REC:
+ 0 = ADPCM synthesis (playback)
+ 1 = ADPCM analysis (record)
+
+MEMDATA:
+ 0 = processor (*CPU-managed*) memory (means: using register $08)
+ 1 = external memory (using start/end/limit registers to access memory: RAM or ROM)
+
+
+SPOFF:
+ controls output pin that should disable the speaker while ADPCM analysis
+
+RESET and REPEAT only work with external memory.
+
+
+some examples:
+value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
+ C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
+ E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
+ 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
+ a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
+
+ 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
+ 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
+
+*/
+ /* handle emulation mode */
+ if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610)
+ {
+ v |= 0x20; /* YM2610 always uses external memory and doesn't even have memory flag bit. */
+ }
+
+ DELTAT->portstate = v & (0x80|0x40|0x20|0x10|0x01); /* start, rec, memory mode, repeat flag copy, reset(bit0) */
+
+ if( DELTAT->portstate&0x80 )/* START,REC,MEMDATA,REPEAT,SPOFF,--,--,RESET */
+ {
+ /* set PCM BUSY bit */
+ DELTAT->PCM_BSY = 1;
+
+ /* start ADPCM */
+ DELTAT->now_step = 0;
+ DELTAT->acc = 0;
+ DELTAT->prev_acc = 0;
+ DELTAT->adpcml = 0;
+ DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
+ DELTAT->now_data = 0;
+
+ }
+
+ if( DELTAT->portstate&0x20 ) /* do we access external memory? */
+ {
+ DELTAT->now_addr = DELTAT->start << 1;
+ DELTAT->memread = 2; /* two dummy reads needed before accesing external memory via register $08*/
+
+ /* if yes, then let's check if ADPCM memory is mapped and big enough */
+ if(DELTAT->memory == 0)
+ {
+ logerror("YM Delta-T ADPCM rom not mapped\n");
+ DELTAT->portstate = 0x00;
+ DELTAT->PCM_BSY = 0;
+ }
+ else
+ {
+ if( DELTAT->end >= DELTAT->memory_size ) /* Check End in Range */
+ {
+ logerror("YM Delta-T ADPCM end out of range: $%08x\n", DELTAT->end);
+ DELTAT->end = DELTAT->memory_size - 1;
+ }
+ if( DELTAT->start >= DELTAT->memory_size ) /* Check Start in Range */
+ {
+ logerror("YM Delta-T ADPCM start out of range: $%08x\n", DELTAT->start);
+ DELTAT->portstate = 0x00;
+ DELTAT->PCM_BSY = 0;
+ }
+ }
+ }
+ else /* we access CPU memory (ADPCM data register $08) so we only reset now_addr here */
+ {
+ DELTAT->now_addr = 0;
+ }
+
+ if( DELTAT->portstate&0x01 )
+ {
+ DELTAT->portstate = 0x00;
+
+ /* clear PCM BUSY bit (in status register) */
+ DELTAT->PCM_BSY = 0;
+
+ /* set BRDY flag */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+ }
+ break;
+ case 0x01: /* L,R,-,-,SAMPLE,DA/AD,RAMTYPE,ROM */
+ /* handle emulation mode */
+ if(DELTAT->emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610)
+ {
+ v |= 0x01; /* YM2610 always uses ROM as an external memory and doesn't tave ROM/RAM memory flag bit. */
+ }
+
+ DELTAT->pan = &DELTAT->output_pointer[(v>>6)&0x03];
+ if ((DELTAT->control2 & 3) != (v & 3))
+ {
+ /*0-DRAM x1, 1-ROM, 2-DRAM x8, 3-ROM (3 is bad setting - not allowed by the manual) */
+ if (DELTAT->DRAMportshift != dram_rightshift[v&3])
+ {
+ DELTAT->DRAMportshift = dram_rightshift[v&3];
+
+ /* final shift value depends on chip type and memory type selected:
+ 8 for YM2610 (ROM only),
+ 5 for ROM for Y8950 and YM2608,
+ 5 for x8bit DRAMs for Y8950 and YM2608,
+ 2 for x1bit DRAMs for Y8950 and YM2608.
+ */
+
+ /* refresh addresses */
+ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1;
+ DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ }
+ }
+ DELTAT->control2 = v;
+ break;
+ case 0x02: /* Start Address L */
+ case 0x03: /* Start Address H */
+ DELTAT->start = (DELTAT->reg[0x3]*0x0100 | DELTAT->reg[0x2]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ /*logerror("DELTAT start: 02=%2x 03=%2x addr=%8x\n",DELTAT->reg[0x2], DELTAT->reg[0x3],DELTAT->start );*/
+ break;
+ case 0x04: /* Stop Address L */
+ case 0x05: /* Stop Address H */
+ DELTAT->end = (DELTAT->reg[0x5]*0x0100 | DELTAT->reg[0x4]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ DELTAT->end += (1 << (DELTAT->portshift-DELTAT->DRAMportshift) ) - 1;
+ /*logerror("DELTAT end : 04=%2x 05=%2x addr=%8x\n",DELTAT->reg[0x4], DELTAT->reg[0x5],DELTAT->end );*/
+ break;
+ case 0x06: /* Prescale L (ADPCM and Record frq) */
+ case 0x07: /* Prescale H */
+ break;
+ case 0x08: /* ADPCM data */
+
+/*
+some examples:
+value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
+ C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
+ E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
+ 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
+ a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
+
+ 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
+ 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
+
+*/
+
+ /* external memory write */
+ if ( (DELTAT->portstate & 0xe0)==0x60 )
+ {
+ if (DELTAT->memread)
+ {
+ DELTAT->now_addr = DELTAT->start << 1;
+ DELTAT->memread = 0;
+ }
+
+ /*logerror("YM Delta-T memory write $%08x, v=$%02x\n", DELTAT->now_addr >> 1, v);*/
+
+ if ( DELTAT->now_addr != (DELTAT->end<<1) )
+ {
+ DELTAT->memory[DELTAT->now_addr>>1] = v;
+ DELTAT->now_addr+=2; /* two nibbles at a time */
+
+ /* reset BRDY bit in status register, which means we are processing the write */
+ if(DELTAT->status_reset_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+
+ /* setup a timer that will callback us in 10 master clock cycles for Y8950
+ * in the callback set the BRDY flag to 1 , which means we have written the data.
+ * For now, we don't really do this; we simply reset and set the flag in zero time, so that the IRQ will work.
+ */
+ /* set BRDY bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+
+ }
+ else
+ {
+ /* set EOS bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_EOS_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);
+ }
+
+ return;
+ }
+
+ /* ADPCM synthesis from CPU */
+ if ( (DELTAT->portstate & 0xe0)==0x80 )
+ {
+ DELTAT->CPU_data = v;
+
+ /* Reset BRDY bit in status register, which means we are full of data */
+ if(DELTAT->status_reset_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_reset_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+ return;
+ }
+
+ break;
+ case 0x09: /* DELTA-N L (ADPCM Playback Prescaler) */
+ case 0x0a: /* DELTA-N H */
+ DELTAT->delta = (DELTAT->reg[0xa]*0x0100 | DELTAT->reg[0x9]);
+ DELTAT->step = (UINT32)( (double)(DELTAT->delta /* *(1<<(YM_DELTAT_SHIFT-16)) */ ) * (DELTAT->freqbase) );
+ /*logerror("DELTAT deltan:09=%2x 0a=%2x\n",DELTAT->reg[0x9], DELTAT->reg[0xa]);*/
+ break;
+ case 0x0b: /* Output level control (volume, linear) */
+ {
+ INT32 oldvol = DELTAT->volume;
+ DELTAT->volume = (v&0xff) * (DELTAT->output_range/256) / YM_DELTAT_DECODE_RANGE;
+/* v * ((1<<16)>>8) >> 15;
+* thus: v * (1<<8) >> 15;
+* thus: output_range must be (1 << (15+8)) at least
+* v * ((1<<23)>>8) >> 15;
+* v * (1<<15) >> 15;
+*/
+ /*logerror("DELTAT vol = %2x\n",v&0xff);*/
+ if( oldvol != 0 )
+ {
+ DELTAT->adpcml = (int)((double)DELTAT->adpcml / (double)oldvol * (double)DELTAT->volume);
+ }
+ }
+ break;
+ case 0x0c: /* Limit Address L */
+ case 0x0d: /* Limit Address H */
+ DELTAT->limit = (DELTAT->reg[0xd]*0x0100 | DELTAT->reg[0xc]) << (DELTAT->portshift - DELTAT->DRAMportshift);
+ /*logerror("DELTAT limit: 0c=%2x 0d=%2x addr=%8x\n",DELTAT->reg[0xc], DELTAT->reg[0xd],DELTAT->limit );*/
+ break;
+ }
+}
+
+void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode)
+{
+ DELTAT->now_addr = 0;
+ DELTAT->now_step = 0;
+ DELTAT->step = 0;
+ DELTAT->start = 0;
+ DELTAT->end = 0;
+ DELTAT->limit = ~0; /* this way YM2610 and Y8950 (both of which don't have limit address reg) will still work */
+ DELTAT->volume = 0;
+ DELTAT->pan = &DELTAT->output_pointer[pan];
+ DELTAT->acc = 0;
+ DELTAT->prev_acc = 0;
+ DELTAT->adpcmd = 127;
+ DELTAT->adpcml = 0;
+ DELTAT->emulation_mode = (UINT8)emulation_mode;
+ DELTAT->portstate = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x20 : 0;
+ DELTAT->control2 = (emulation_mode == YM_DELTAT_EMULATION_MODE_YM2610) ? 0x01 : 0; /* default setting depends on the emulation mode. MSX demo called "facdemo_4" doesn't setup control2 register at all and still works */
+ DELTAT->DRAMportshift = dram_rightshift[DELTAT->control2 & 3];
+
+ /* The flag mask register disables the BRDY after the reset, however
+ ** as soon as the mask is enabled the flag needs to be set. */
+
+ /* set BRDY bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+}
+
+#if 0
+void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs)
+{
+ int r;
+
+ /* to keep adpcml */
+ DELTAT->volume = 0;
+ /* update */
+ for(r=1;r<16;r++)
+ YM_DELTAT_ADPCM_Write(DELTAT,r,regs[r]);
+ DELTAT->reg[0] = regs[0];
+
+ /* current rom data */
+ if (DELTAT->memory)
+ DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1) );
+
+}
+void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT)
+{
+#ifdef __STATE_H__
+ state_save_register_device_item(device, 0, DELTAT->portstate);
+ state_save_register_device_item(device, 0, DELTAT->now_addr);
+ state_save_register_device_item(device, 0, DELTAT->now_step);
+ state_save_register_device_item(device, 0, DELTAT->acc);
+ state_save_register_device_item(device, 0, DELTAT->prev_acc);
+ state_save_register_device_item(device, 0, DELTAT->adpcmd);
+ state_save_register_device_item(device, 0, DELTAT->adpcml);
+#endif
+}
+#endif
+
+
+#define YM_DELTAT_Limit(val,max,min) \
+{ \
+ if ( val > max ) val = max; \
+ else if ( val < min ) val = min; \
+}
+
+INLINE void YM_DELTAT_synthesis_from_external_memory(YM_DELTAT *DELTAT)
+{
+ UINT32 step;
+ int data;
+
+ DELTAT->now_step += DELTAT->step;
+ if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
+ {
+ step = DELTAT->now_step >> YM_DELTAT_SHIFT;
+ DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
+ do{
+
+ if ( DELTAT->now_addr == (DELTAT->limit<<1) )
+ DELTAT->now_addr = 0;
+
+ if ( DELTAT->now_addr == (DELTAT->end<<1) ) { /* 12-06-2001 JB: corrected comparison. Was > instead of == */
+ if( DELTAT->portstate&0x10 ){
+ /* repeat start */
+ DELTAT->now_addr = DELTAT->start<<1;
+ DELTAT->acc = 0;
+ DELTAT->adpcmd = YM_DELTAT_DELTA_DEF;
+ DELTAT->prev_acc = 0;
+ }else{
+ /* set EOS bit in status register */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_EOS_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_EOS_bit);
+
+ /* clear PCM BUSY bit (reflected in status register) */
+ DELTAT->PCM_BSY = 0;
+
+ DELTAT->portstate = 0;
+ DELTAT->adpcml = 0;
+ DELTAT->prev_acc = 0;
+ return;
+ }
+ }
+
+ if( DELTAT->now_addr&1 ) data = DELTAT->now_data & 0x0f;
+ else
+ {
+ DELTAT->now_data = *(DELTAT->memory + (DELTAT->now_addr>>1));
+ data = DELTAT->now_data >> 4;
+ }
+
+ DELTAT->now_addr++;
+ /* 12-06-2001 JB: */
+ /* YM2610 address register is 24 bits wide.*/
+ /* The "+1" is there because we use 1 bit more for nibble calculations.*/
+ /* WARNING: */
+ /* Side effect: we should take the size of the mapped ROM into account */
+ DELTAT->now_addr &= ( (1<<(24+1))-1);
+
+ /* store accumulator value */
+ DELTAT->prev_acc = DELTAT->acc;
+
+ /* Forecast to next Forecast */
+ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
+ YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
+
+ /* delta to next delta */
+ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
+ YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
+
+ /* ElSemi: Fix interpolator. */
+ /*DELTAT->prev_acc = prev_acc + ((DELTAT->acc - prev_acc) / 2 );*/
+
+ }while(--step);
+
+ }
+
+ /* ElSemi: Fix interpolator. */
+ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
+ DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
+ DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
+
+ /* output for work of output channels (outd[OPNxxxx])*/
+ *(DELTAT->pan) += DELTAT->adpcml;
+}
+
+
+
+INLINE void YM_DELTAT_synthesis_from_CPU_memory(YM_DELTAT *DELTAT)
+{
+ UINT32 step;
+ int data;
+
+ DELTAT->now_step += DELTAT->step;
+ if ( DELTAT->now_step >= (1<<YM_DELTAT_SHIFT) )
+ {
+ step = DELTAT->now_step >> YM_DELTAT_SHIFT;
+ DELTAT->now_step &= (1<<YM_DELTAT_SHIFT)-1;
+ do{
+
+ if( DELTAT->now_addr&1 )
+ {
+ data = DELTAT->now_data & 0x0f;
+
+ DELTAT->now_data = DELTAT->CPU_data;
+
+ /* after we used CPU_data, we set BRDY bit in status register,
+ * which means we are ready to accept another byte of data */
+ if(DELTAT->status_set_handler)
+ if(DELTAT->status_change_BRDY_bit)
+ (DELTAT->status_set_handler)(DELTAT->status_change_which_chip, DELTAT->status_change_BRDY_bit);
+ }
+ else
+ {
+ data = DELTAT->now_data >> 4;
+ }
+
+ DELTAT->now_addr++;
+
+ /* store accumulator value */
+ DELTAT->prev_acc = DELTAT->acc;
+
+ /* Forecast to next Forecast */
+ DELTAT->acc += (ym_deltat_decode_tableB1[data] * DELTAT->adpcmd / 8);
+ YM_DELTAT_Limit(DELTAT->acc,YM_DELTAT_DECODE_MAX, YM_DELTAT_DECODE_MIN);
+
+ /* delta to next delta */
+ DELTAT->adpcmd = (DELTAT->adpcmd * ym_deltat_decode_tableB2[data] ) / 64;
+ YM_DELTAT_Limit(DELTAT->adpcmd,YM_DELTAT_DELTA_MAX, YM_DELTAT_DELTA_MIN );
+
+
+ }while(--step);
+
+ }
+
+ /* ElSemi: Fix interpolator. */
+ DELTAT->adpcml = DELTAT->prev_acc * (int)((1<<YM_DELTAT_SHIFT)-DELTAT->now_step);
+ DELTAT->adpcml += (DELTAT->acc * (int)DELTAT->now_step);
+ DELTAT->adpcml = (DELTAT->adpcml>>YM_DELTAT_SHIFT) * (int)DELTAT->volume;
+
+ /* output for work of output channels (outd[OPNxxxx])*/
+ *(DELTAT->pan) += DELTAT->adpcml;
+}
+
+
+
+/* ADPCM B (Delta-T control type) */
+void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT)
+{
+
+/*
+some examples:
+value: START, REC, MEMDAT, REPEAT, SPOFF, x,x,RESET meaning:
+ 80 1 0 0 0 0 0 0 0 Synthesis (playing) from CPU (from reg $08) to AUDIO,sample rate in DELTA-N register
+ a0 1 0 1 0 0 0 0 0 Synthesis (playing) from EXT.MEMORY to AUDIO, sample rate in DELTA-N register
+ C8 1 1 0 0 1 0 0 0 Analysis (recording) from AUDIO to CPU (to reg $08), sample rate in PRESCALER register
+ E8 1 1 1 0 1 0 0 0 Analysis (recording) from AUDIO to EXT.MEMORY, sample rate in PRESCALER register
+
+ 60 0 1 1 0 0 0 0 0 External memory write via ADPCM data register $08
+ 20 0 0 1 0 0 0 0 0 External memory read via ADPCM data register $08
+
+*/
+
+ if ( (DELTAT->portstate & 0xe0)==0xa0 )
+ {
+ YM_DELTAT_synthesis_from_external_memory(DELTAT);
+ return;
+ }
+
+ if ( (DELTAT->portstate & 0xe0)==0x80 )
+ {
+ /* ADPCM synthesis from CPU-managed memory (from reg $08) */
+ YM_DELTAT_synthesis_from_CPU_memory(DELTAT); /* change output based on data in ADPCM data reg ($08) */
+ return;
+ }
+
+//todo: ADPCM analysis
+// if ( (DELTAT->portstate & 0xe0)==0xc0 )
+// if ( (DELTAT->portstate & 0xe0)==0xe0 )
+
+ return;
+}
+
diff --git a/plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.h b/plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.h
new file mode 100644
index 00000000..33f13344
--- /dev/null
+++ b/plugins/gme/game-music-emu-0.6pre/gme/ymdeltat.h
@@ -0,0 +1,98 @@
+#pragma once
+
+#ifndef __YMDELTAT_H__
+#define __YMDELTAT_H__
+
+/* compiler dependence */
+#ifndef __OSDCOMM_H__
+#define __OSDCOMM_H__
+typedef unsigned char UINT8; /* unsigned 8bit */
+typedef unsigned short UINT16; /* unsigned 16bit */
+typedef unsigned int UINT32; /* unsigned 32bit */
+typedef signed char INT8; /* signed 8bit */
+typedef signed short INT16; /* signed 16bit */
+typedef signed int INT32; /* signed 32bit */
+
+typedef INT32 stream_sample_t;
+
+#endif /* __OSDCOMM_H__ */
+
+#define YM_DELTAT_SHIFT (16)
+
+#define YM_DELTAT_EMULATION_MODE_NORMAL 0
+#define YM_DELTAT_EMULATION_MODE_YM2610 1
+
+
+typedef void (*STATUS_CHANGE_HANDLER)(void *chip, UINT8 status_bits);
+
+
+/* DELTA-T (adpcm type B) struct */
+typedef struct deltat_adpcm_state { /* AT: rearranged and tigntened structure */
+ UINT8 *memory;
+ INT32 *output_pointer;/* pointer of output pointers */
+ INT32 *pan; /* pan : &output_pointer[pan] */
+ double freqbase;
+#if 0
+ double write_time; /* Y8950: 10 cycles of main clock; YM2608: 20 cycles of main clock */
+ double read_time; /* Y8950: 8 cycles of main clock; YM2608: 18 cycles of main clock */
+#endif
+ UINT32 memory_size;
+ int output_range;
+ UINT32 now_addr; /* current address */
+ UINT32 now_step; /* currect step */
+ UINT32 step; /* step */
+ UINT32 start; /* start address */
+ UINT32 limit; /* limit address */
+ UINT32 end; /* end address */
+ UINT32 delta; /* delta scale */
+ INT32 volume; /* current volume */
+ INT32 acc; /* shift Measurement value*/
+ INT32 adpcmd; /* next Forecast */
+ INT32 adpcml; /* current value */
+ INT32 prev_acc; /* leveling value */
+ UINT8 now_data; /* current rom data */
+ UINT8 CPU_data; /* current data from reg 08 */
+ UINT8 portstate; /* port status */
+ UINT8 control2; /* control reg: SAMPLE, DA/AD, RAM TYPE (x8bit / x1bit), ROM/RAM */
+ UINT8 portshift; /* address bits shift-left:
+ ** 8 for YM2610,
+ ** 5 for Y8950 and YM2608 */
+
+ UINT8 DRAMportshift; /* address bits shift-right:
+ ** 0 for ROM and x8bit DRAMs,
+ ** 3 for x1 DRAMs */
+
+ UINT8 memread; /* needed for reading/writing external memory */
+
+ /* handlers and parameters for the status flags support */
+ STATUS_CHANGE_HANDLER status_set_handler;
+ STATUS_CHANGE_HANDLER status_reset_handler;
+
+ /* note that different chips have these flags on different
+ ** bits of the status register
+ */
+ void * status_change_which_chip; /* this chip id */
+ UINT8 status_change_EOS_bit; /* 1 on End Of Sample (record/playback/cycle time of AD/DA converting has passed)*/
+ UINT8 status_change_BRDY_bit; /* 1 after recording 2 datas (2x4bits) or after reading/writing 1 data */
+ UINT8 status_change_ZERO_bit; /* 1 if silence lasts for more than 290 miliseconds on ADPCM recording */
+
+ /* neither Y8950 nor YM2608 can generate IRQ when PCMBSY bit changes, so instead of above,
+ ** the statusflag gets ORed with PCM_BSY (below) (on each read of statusflag of Y8950 and YM2608)
+ */
+ UINT8 PCM_BSY; /* 1 when ADPCM is playing; Y8950/YM2608 only */
+
+ UINT8 reg[16]; /* adpcm registers */
+ UINT8 emulation_mode; /* which chip we're emulating */
+}YM_DELTAT;
+
+/*void YM_DELTAT_BRDY_callback(YM_DELTAT *DELTAT);*/
+
+UINT8 YM_DELTAT_ADPCM_Read(YM_DELTAT *DELTAT);
+void YM_DELTAT_ADPCM_Write(YM_DELTAT *DELTAT,int r,int v);
+void YM_DELTAT_ADPCM_Reset(YM_DELTAT *DELTAT,int pan,int emulation_mode);
+void YM_DELTAT_ADPCM_CALC(YM_DELTAT *DELTAT);
+
+/*void YM_DELTAT_postload(YM_DELTAT *DELTAT,UINT8 *regs);
+void YM_DELTAT_savestate(const device_config *device,YM_DELTAT *DELTAT);*/
+
+#endif /* __YMDELTAT_H__ */