GME: Updated libgme to 0.6.4-13-ga32f34a

Also applied PR !134

Signed-off-by: Christopher Snowhill <kode54@gmail.com>
This commit is contained in:
Christopher Snowhill 2025-08-04 19:59:52 -07:00
parent e0be2497c0
commit 81df9c8282
124 changed files with 4005 additions and 3722 deletions

View file

@ -82,7 +82,6 @@
17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DE0CBED286008D969D /* Sms_Apu.h */; };
17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */; };
17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E70CBED286008D969D /* Spc_Emu.h */; };
8302AF4F2784668C0066143E /* vrc7tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 8302AF4E2784668C0066143E /* vrc7tone.h */; };
83489CBB2783015300BDCEA2 /* gme_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CB12783015200BDCEA2 /* gme_types.h */; };
83489CBC2783015300BDCEA2 /* nes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CB22783015300BDCEA2 /* nes_cpu_io.h */; };
83489CBD2783015300BDCEA2 /* hes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CB32783015300BDCEA2 /* hes_cpu_io.h */; };
@ -100,17 +99,21 @@
83489CD82783C98600BDCEA2 /* Nes_Vrc7_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CD62783C98600BDCEA2 /* Nes_Vrc7_Apu.h */; };
83489CD92783C98600BDCEA2 /* Nes_Vrc7_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83489CD72783C98600BDCEA2 /* Nes_Vrc7_Apu.cpp */; };
83489CE02783CAC100BDCEA2 /* gb_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CDA2783CAC000BDCEA2 /* gb_cpu_io.h */; };
83489CE12783CAC100BDCEA2 /* 2413tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CDB2783CAC000BDCEA2 /* 2413tone.h */; };
83489CE32783CAC100BDCEA2 /* emu2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CDD2783CAC100BDCEA2 /* emu2413.h */; };
83489CE52783CAC100BDCEA2 /* emu2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 83489CDF2783CAC100BDCEA2 /* emu2413.c */; };
83489CEA2783CADC00BDCEA2 /* panning.c in Sources */ = {isa = PBXBuildFile; fileRef = 83489CE72783CADC00BDCEA2 /* panning.c */; };
83489CEB2783CADC00BDCEA2 /* panning.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CE82783CADC00BDCEA2 /* panning.h */; };
83489CED2783D86700BDCEA2 /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CEC2783D86700BDCEA2 /* mamedef.h */; };
83489CEF2783D89300BDCEA2 /* emutypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CEE2783D89300BDCEA2 /* emutypes.h */; };
8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */; };
8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70B17F615FE001A4D7A /* Spc_Filter.h */; };
8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */; };
8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */; };
83BDCDD02E41A269003FC007 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 83BDCDC92E41A269003FC007 /* LICENSE */; };
83BDCDD12E41A269003FC007 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 83BDCDCD2E41A269003FC007 /* README.md */; };
83BDCDD22E41A269003FC007 /* emu2413_NESpatches.txt in Resources */ = {isa = PBXBuildFile; fileRef = 83BDCDC72E41A269003FC007 /* emu2413_NESpatches.txt */; };
83BDCDD32E41A269003FC007 /* panning.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDCB2E41A269003FC007 /* panning.h */; };
83BDCDD42E41A269003FC007 /* 2413tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDC42E41A269003FC007 /* 2413tone.h */; };
83BDCDD52E41A269003FC007 /* emutypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDC82E41A269003FC007 /* emutypes.h */; };
83BDCDD62E41A269003FC007 /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDCA2E41A269003FC007 /* mamedef.h */; };
83BDCDD72E41A269003FC007 /* emu2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDC52E41A269003FC007 /* emu2413.h */; };
83BDCDD82E41A269003FC007 /* vrc7tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDCE2E41A269003FC007 /* vrc7tone.h */; };
83BDCDD92E41A269003FC007 /* emu2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BDCDC62E41A269003FC007 /* emu2413.c */; };
83BDCDDA2E41A269003FC007 /* panning.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BDCDCC2E41A269003FC007 /* panning.c */; };
83FC5D5E181B47FB00B917E5 /* dsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D3B181B47FB00B917E5 /* dsp.cpp */; };
83FC5D5F181B47FB00B917E5 /* dsp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5D3C181B47FB00B917E5 /* dsp.hpp */; };
83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D57181B47FB00B917E5 /* smp.cpp */; };
@ -203,7 +206,6 @@
17C8F1DE0CBED286008D969D /* Sms_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Apu.h; path = gme/Sms_Apu.h; sourceTree = "<group>"; };
17C8F1E60CBED286008D969D /* Spc_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Emu.cpp; path = gme/Spc_Emu.cpp; sourceTree = "<group>"; };
17C8F1E70CBED286008D969D /* Spc_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Emu.h; path = gme/Spc_Emu.h; sourceTree = "<group>"; };
8302AF4E2784668C0066143E /* vrc7tone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vrc7tone.h; sourceTree = "<group>"; };
833F68361CDBCAB200AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
83489CB12783015200BDCEA2 /* gme_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gme_types.h; path = gme/gme_types.h; sourceTree = "<group>"; };
83489CB22783015300BDCEA2 /* nes_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = nes_cpu_io.h; path = gme/nes_cpu_io.h; sourceTree = "<group>"; };
@ -222,14 +224,6 @@
83489CD62783C98600BDCEA2 /* Nes_Vrc7_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Vrc7_Apu.h; path = gme/Nes_Vrc7_Apu.h; sourceTree = SOURCE_ROOT; };
83489CD72783C98600BDCEA2 /* Nes_Vrc7_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Vrc7_Apu.cpp; path = gme/Nes_Vrc7_Apu.cpp; sourceTree = SOURCE_ROOT; };
83489CDA2783CAC000BDCEA2 /* gb_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gb_cpu_io.h; path = gme/gb_cpu_io.h; sourceTree = "<group>"; };
83489CDB2783CAC000BDCEA2 /* 2413tone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2413tone.h; sourceTree = "<group>"; };
83489CDC2783CAC100BDCEA2 /* emu2413_NESpatches.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = emu2413_NESpatches.txt; sourceTree = "<group>"; };
83489CDD2783CAC100BDCEA2 /* emu2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emu2413.h; sourceTree = "<group>"; };
83489CDF2783CAC100BDCEA2 /* emu2413.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emu2413.c; sourceTree = "<group>"; };
83489CE72783CADC00BDCEA2 /* panning.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = panning.c; sourceTree = "<group>"; };
83489CE82783CADC00BDCEA2 /* panning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = panning.h; sourceTree = "<group>"; };
83489CEC2783D86700BDCEA2 /* mamedef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mamedef.h; sourceTree = "<group>"; };
83489CEE2783D89300BDCEA2 /* emutypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emutypes.h; sourceTree = "<group>"; };
835C889022CC1884001B4B3F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = "<group>"; };
8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Filter.cpp; path = gme/Spc_Filter.cpp; sourceTree = "<group>"; };
8370B70B17F615FE001A4D7A /* Spc_Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Spc_Filter.h; path = gme/Spc_Filter.h; sourceTree = "<group>"; };
@ -237,6 +231,17 @@
8370B70D17F615FE001A4D7A /* Spc_Sfm.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Spc_Sfm.h; path = gme/Spc_Sfm.h; sourceTree = "<group>"; };
83747B7F2862D4DB0021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = "<group>"; };
838EE8BD29A8600A00CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
83BDCDC42E41A269003FC007 /* 2413tone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 2413tone.h; sourceTree = "<group>"; };
83BDCDC52E41A269003FC007 /* emu2413.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = emu2413.h; sourceTree = "<group>"; };
83BDCDC62E41A269003FC007 /* emu2413.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = emu2413.c; sourceTree = "<group>"; };
83BDCDC72E41A269003FC007 /* emu2413_NESpatches.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = emu2413_NESpatches.txt; sourceTree = "<group>"; };
83BDCDC82E41A269003FC007 /* emutypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = emutypes.h; sourceTree = "<group>"; };
83BDCDC92E41A269003FC007 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
83BDCDCA2E41A269003FC007 /* mamedef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mamedef.h; sourceTree = "<group>"; };
83BDCDCB2E41A269003FC007 /* panning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = panning.h; sourceTree = "<group>"; };
83BDCDCC2E41A269003FC007 /* panning.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = panning.c; sourceTree = "<group>"; };
83BDCDCD2E41A269003FC007 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
83BDCDCE2E41A269003FC007 /* vrc7tone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vrc7tone.h; sourceTree = "<group>"; };
83F0E6B7287CAB4100D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
83FC5D3B181B47FB00B917E5 /* dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp.cpp; sourceTree = "<group>"; };
83FC5D3C181B47FB00B917E5 /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = "<group>"; };
@ -339,7 +344,7 @@
17C8F1860CBED26C008D969D /* Source */ = {
isa = PBXGroup;
children = (
83489CF02783DC4F00BDCEA2 /* ext */,
83BDCDCF2E41A269003FC007 /* ext */,
83FC5D35181B47FB00B917E5 /* higan */,
17C8F18B0CBED286008D969D /* Ay_Apu.cpp */,
17C8F18C0CBED286008D969D /* Ay_Apu.h */,
@ -447,22 +452,6 @@
name = Frameworks;
sourceTree = "<group>";
};
83489CF02783DC4F00BDCEA2 /* ext */ = {
isa = PBXGroup;
children = (
83489CDB2783CAC000BDCEA2 /* 2413tone.h */,
83489CDC2783CAC100BDCEA2 /* emu2413_NESpatches.txt */,
83489CDF2783CAC100BDCEA2 /* emu2413.c */,
83489CDD2783CAC100BDCEA2 /* emu2413.h */,
83489CEE2783D89300BDCEA2 /* emutypes.h */,
83489CEC2783D86700BDCEA2 /* mamedef.h */,
83489CE72783CADC00BDCEA2 /* panning.c */,
83489CE82783CADC00BDCEA2 /* panning.h */,
8302AF4E2784668C0066143E /* vrc7tone.h */,
);
path = ext;
sourceTree = "<group>";
};
83747B7E2862D4DB0021245F /* Xcode-config */ = {
isa = PBXGroup;
children = (
@ -472,6 +461,25 @@
path = "../../Xcode-config";
sourceTree = "<group>";
};
83BDCDCF2E41A269003FC007 /* ext */ = {
isa = PBXGroup;
children = (
83BDCDC42E41A269003FC007 /* 2413tone.h */,
83BDCDC52E41A269003FC007 /* emu2413.h */,
83BDCDC62E41A269003FC007 /* emu2413.c */,
83BDCDC72E41A269003FC007 /* emu2413_NESpatches.txt */,
83BDCDC82E41A269003FC007 /* emutypes.h */,
83BDCDC92E41A269003FC007 /* LICENSE */,
83BDCDCA2E41A269003FC007 /* mamedef.h */,
83BDCDCB2E41A269003FC007 /* panning.h */,
83BDCDCC2E41A269003FC007 /* panning.c */,
83BDCDCD2E41A269003FC007 /* README.md */,
83BDCDCE2E41A269003FC007 /* vrc7tone.h */,
);
name = ext;
path = gme/ext;
sourceTree = "<group>";
};
83FC5D35181B47FB00B917E5 /* higan */ = {
isa = PBXGroup;
children = (
@ -546,22 +554,25 @@
17C8F1FF0CBED286008D969D /* Blip_Buffer.h in Headers */,
17C8F2010CBED286008D969D /* Classic_Emu.h in Headers */,
17C8F2030CBED286008D969D /* Data_Reader.h in Headers */,
83BDCDD32E41A269003FC007 /* panning.h in Headers */,
83BDCDD42E41A269003FC007 /* 2413tone.h in Headers */,
83BDCDD52E41A269003FC007 /* emutypes.h in Headers */,
83BDCDD62E41A269003FC007 /* mamedef.h in Headers */,
83BDCDD72E41A269003FC007 /* emu2413.h in Headers */,
83BDCDD82E41A269003FC007 /* vrc7tone.h in Headers */,
17C8F2050CBED286008D969D /* Dual_Resampler.h in Headers */,
17C8F2070CBED286008D969D /* Effects_Buffer.h in Headers */,
17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */,
83489CD82783C98600BDCEA2 /* Nes_Vrc7_Apu.h in Headers */,
83489CEB2783CADC00BDCEA2 /* panning.h in Headers */,
83FC5DAE181B8B1900B917E5 /* spc700.hpp in Headers */,
17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */,
83FC5DAB181B8B1900B917E5 /* registers.hpp in Headers */,
17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */,
83489CC22783015300BDCEA2 /* sap_cpu_io.h in Headers */,
8302AF4F2784668C0066143E /* vrc7tone.h in Headers */,
83489CBD2783015300BDCEA2 /* hes_cpu_io.h in Headers */,
17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */,
17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */,
83489CE02783CAC100BDCEA2 /* gb_cpu_io.h in Headers */,
83489CEF2783D89300BDCEA2 /* emutypes.h in Headers */,
83489CD12783BF6D00BDCEA2 /* Nes_Mmc5_Apu.h in Headers */,
83489CC02783015300BDCEA2 /* Ay_Cpu.h in Headers */,
83FC5D9A181B675900B917E5 /* SPC_DSP.h in Headers */,
@ -578,9 +589,7 @@
17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */,
17C8F22B0CBED286008D969D /* Multi_Buffer.h in Headers */,
17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */,
83489CED2783D86700BDCEA2 /* mamedef.h in Headers */,
17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */,
83489CE32783CAC100BDCEA2 /* emu2413.h in Headers */,
17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */,
17C8F2340CBED286008D969D /* Nes_Fme7_Apu.h in Headers */,
17C8F2360CBED286008D969D /* Nes_Namco_Apu.h in Headers */,
@ -595,7 +604,6 @@
8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */,
17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */,
83489CC12783015300BDCEA2 /* Sms_Oscs.h in Headers */,
83489CE12783CAC100BDCEA2 /* 2413tone.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -656,6 +664,9 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
83BDCDD02E41A269003FC007 /* LICENSE in Resources */,
83BDCDD12E41A269003FC007 /* README.md in Resources */,
83BDCDD22E41A269003FC007 /* emu2413_NESpatches.txt in Resources */,
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -672,14 +683,14 @@
17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */,
17C8F1FE0CBED286008D969D /* Blip_Buffer.cpp in Sources */,
17C8F2000CBED286008D969D /* Classic_Emu.cpp in Sources */,
83489CEA2783CADC00BDCEA2 /* panning.c in Sources */,
8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */,
17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */,
83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */,
17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */,
17C8F2060CBED286008D969D /* Effects_Buffer.cpp in Sources */,
17C8F2080CBED286008D969D /* Fir_Resampler.cpp in Sources */,
83489CE52783CAC100BDCEA2 /* emu2413.c in Sources */,
83BDCDD92E41A269003FC007 /* emu2413.c in Sources */,
83BDCDDA2E41A269003FC007 /* panning.c in Sources */,
17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */,
17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */,
17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */,
@ -756,6 +767,7 @@
HAVE_STDINT_H,
"DEBUG=1",
"VGM_YM2612_NUKED=1",
"BLARGG_LITTLE_ENDIAN=1",
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@loader_path/../Frameworks";
@ -789,6 +801,7 @@
HAVE_STDINT_H,
NDEBUG,
"VGM_YM2612_NUKED=1",
"BLARGG_LITTLE_ENDIAN=1",
);
INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@loader_path/../Frameworks";

View file

@ -1,62 +0,0 @@
#ifndef __MAMEDEF_H__
#define __MAMEDEF_H__
// typedefs to use MAME's (U)INTxx types (copied from MAME\src\ods\odscomm.h)
/* 8-bit values */
typedef unsigned char UINT8;
typedef signed char INT8;
/* 16-bit values */
typedef unsigned short UINT16;
typedef signed short INT16;
/* 32-bit values */
#ifndef _WINDOWS_H
typedef unsigned int UINT32;
typedef signed int INT32;
#endif
/* 64-bit values */
#ifndef _WINDOWS_H
#ifdef _MSC_VER
typedef signed __int64 INT64;
typedef unsigned __int64 UINT64;
#else
__extension__ typedef unsigned long long UINT64;
__extension__ typedef signed long long INT64;
#endif
#endif
/* offsets and addresses are 32-bit (for now...) */
typedef UINT32 offs_t;
/* stream_sample_t is used to represent a single sample in a sound stream */
typedef INT32 stream_sample_t;
#ifdef VGM_BIG_ENDIAN
#define BYTE_XOR_BE(x) (x)
#else
#define BYTE_XOR_BE(x) ((x) ^ 0x01)
#endif
#if defined(_MSC_VER)
//#define INLINE static __forceinline
#define INLINE static __inline
#elif defined(__GNUC__)
#define INLINE static __inline__
#else
#define INLINE static inline
#endif
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
#ifdef _DEBUG
#define logerror printf
#else
#define logerror
#endif
typedef void (*SRATE_CALLBACK)(void*, UINT32);
#endif // __MAMEDEF_H__

View file

@ -22,9 +22,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
// Tones above this frequency are treated as disabled tone at half volume.
// Power of two is more efficient (avoids division).
unsigned const inaudible_freq = 16384;
static unsigned const inaudible_freq = 16384;
int const period_factor = 16;
static int const period_factor = 16;
static byte const amp_table [16] =
{
@ -35,10 +35,10 @@ static byte const amp_table [16] =
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),
@ -88,7 +88,7 @@ Ay_Apu::Ay_Apu()
flags >>= 2;
}
}
output( 0 );
volume( 1.0 );
reset();
@ -99,7 +99,7 @@ void Ay_Apu::reset()
last_time = 0;
noise.delay = 0;
noise.lfsr = 1;
osc_t* osc = &oscs [osc_count];
do
{
@ -110,7 +110,7 @@ void Ay_Apu::reset()
osc->phase = 0;
}
while ( osc != oscs );
for ( int i = sizeof regs; --i >= 0; )
regs [i] = 0;
regs [7] = 0xFF;
@ -120,14 +120,14 @@ void Ay_Apu::reset()
void Ay_Apu::write_data_( int addr, int data )
{
assert( (unsigned) addr < reg_count );
if ( (unsigned) addr >= 14 )
{
#ifdef debug_printf
debug_printf( "Wrote to I/O port %02X\n", (int) addr );
#endif
}
// envelope mode
if ( addr == 13 )
{
@ -138,7 +138,7 @@ void Ay_Apu::write_data_( int addr, int data )
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 )
@ -147,32 +147,32 @@ void Ay_Apu::write_data_( int addr, int data )
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;
static int const noise_off = 0x08;
static int const tone_off = 0x01;
void Ay_Apu::run_until( blip_time_t final_end_time )
{
require( final_end_time >= last_time );
// noise period and initial values
blip_time_t const noise_period_factor = period_factor * 2; // verified
blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor;
if ( !noise_period )
noise_period = noise_period_factor;
blip_time_t const old_noise_delay = noise.delay;
blargg_ulong const old_noise_lfsr = noise.lfsr;
uint32_t const old_noise_lfsr = noise.lfsr;
// envelope period
blip_time_t const env_period_factor = period_factor * 2; // verified
blip_time_t env_period = (regs [12] * 0x100L + regs [11]) * env_period_factor;
@ -180,29 +180,29 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
env_period = env_period_factor; // same as period 1 on my AY chip
if ( !env.delay )
env.delay = env_period;
// run each osc separately
for ( int index = 0; index < osc_count; index++ )
{
osc_t* const osc = &oscs [index];
int osc_mode = regs [7] >> index;
// output
Blip_Buffer* const osc_output = osc->output;
if ( !osc_output )
continue;
osc_output->set_modified();
// period
int half_vol = 0;
blip_time_t inaudible_period = (blargg_ulong) (osc_output->clock_rate() +
blip_time_t inaudible_period = (uint32_t) (osc_output->clock_rate() +
inaudible_freq) / (inaudible_freq * 2);
if ( osc->period <= inaudible_period && !(osc_mode & tone_off) )
{
half_vol = 1; // Actually around 60%, but 50% is close enough
osc_mode |= tone_off;
}
// envelope
blip_time_t start_time = last_time;
blip_time_t end_time = final_end_time;
@ -218,7 +218,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
end_time = start_time + env.delay;
if ( end_time >= final_end_time )
end_time = final_end_time;
//if ( !(regs [12] | regs [11]) )
// debug_printf( "Used envelope period 0\n" );
}
@ -231,20 +231,20 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
{
osc_mode = noise_off | tone_off;
}
// tone time
blip_time_t const period = osc->period;
blip_time_t time = start_time + osc->delay;
if ( osc_mode & tone_off ) // maintain tone's phase when off
{
blargg_long count = (final_end_time - time + period - 1) / period;
int32_t count = (final_end_time - time + period - 1) / period;
time += count * period;
osc->phase ^= count & 1;
}
// noise time
blip_time_t ntime = final_end_time;
blargg_ulong noise_lfsr = 1;
uint32_t noise_lfsr = 1;
if ( !(osc_mode & noise_off) )
{
ntime = start_time + old_noise_delay;
@ -252,7 +252,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
//if ( (regs [6] & 0x1F) == 0 )
// debug_printf( "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
@ -260,7 +260,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
// * 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.
@ -278,7 +278,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
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.
@ -299,7 +299,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
while ( ntime <= end ) // must advance *past* time to avoid hang
{
int changed = noise_lfsr + 1;
noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1);
noise_lfsr = (uMinus(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1);
if ( changed & 2 )
{
delta = -delta;
@ -311,12 +311,12 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
else
{
// 20 or more noise periods on average for some music
blargg_long remain = end - ntime;
blargg_long count = remain / noise_period;
int32_t remain = end - ntime;
int32_t count = remain / noise_period;
if ( remain >= 0 )
ntime += noise_period + count * noise_period;
}
// run tone
end = end_time;
if ( end_time > ntime ) end = ntime;
@ -337,7 +337,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
{
// loop usually runs less than once
//SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period );
while ( time < end )
{
time += period;
@ -346,41 +346,41 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
}
}
while ( time < end_time || ntime < end_time );
osc->last_amp = (delta + volume) >> 1;
if ( !(osc_mode & tone_off) )
osc->phase = phase;
}
if ( end_time >= final_end_time )
break; // breaks first time when envelope is disabled
// next envelope step
if ( ++osc_env_pos >= 0 )
osc_env_pos -= 32;
volume = env.wave [osc_env_pos] >> half_vol;
start_time = end_time;
end_time += env_period;
if ( end_time > final_end_time )
end_time = final_end_time;
}
osc->delay = time - final_end_time;
if ( !(osc_mode & noise_off) )
{
noise.delay = ntime - final_end_time;
noise.lfsr = noise_lfsr;
}
}
// TODO: optimized saw wave envelope?
// maintain envelope phase
blip_time_t remain = final_end_time - last_time - env.delay;
if ( remain >= 0 )
{
blargg_long count = (remain + env_period) / env_period;
int32_t count = (remain + env_period) / env_period;
env.pos += count;
if ( env.pos >= 0 )
env.pos = (env.pos & 31) - 32;
@ -390,6 +390,6 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
env.delay = -remain;
assert( env.delay > 0 );
assert( env.pos < 0 );
last_time = final_end_time;
}

View file

@ -11,32 +11,32 @@ class Ay_Apu {
public:
// Set buffer to generate all sound into, or disable sound if NULL
void output( Blip_Buffer* );
// Reset sound chip
void reset();
// Write to register at specified time
enum { reg_count = 16 };
static const unsigned int reg_count = 16;
void write( blip_time_t time, int addr, int data );
// Run sound to specified time, end current time frame, then start a new
// time frame at time 0. Time frames have no effect on emulation and each
// can be whatever length is convenient.
void end_frame( blip_time_t length );
// Additional features
// Set sound output of specific oscillator to buffer, where index is
// 0, 1, or 2. If buffer is NULL, the specified oscillator is muted.
enum { osc_count = 3 };
static const int osc_count = 3;
void osc_output( int index, Blip_Buffer* );
// Set overall volume (default is 1.0)
void volume( double );
// Set treble equalization (see documentation)
void treble_eq( blip_eq_t const& );
public:
Ay_Apu();
typedef unsigned char byte;
@ -51,23 +51,23 @@ private:
} oscs [osc_count];
blip_time_t last_time;
byte regs [reg_count];
struct {
blip_time_t delay;
blargg_ulong lfsr;
uint32_t lfsr;
} noise;
struct {
blip_time_t delay;
byte const* wave;
int pos;
byte modes [8] [48]; // values already passed through volume table
} env;
void run_until( blip_time_t );
void write_data_( int addr, int data );
public:
enum { amp_range = 255 };
static const int amp_range = 255;
Blip_Synth<blip_good_quality,1> synth_;
};
@ -98,7 +98,7 @@ inline void Ay_Apu::end_frame( blip_time_t time )
{
if ( time > last_time )
run_until( time );
assert( last_time >= time );
last_time -= time;
}

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
#include "blargg_endian.h"
typedef blargg_long cpu_time_t;
typedef int32_t cpu_time_t;
// must be defined by caller
void ay_cpu_out( class Ay_Cpu*, cpu_time_t, unsigned addr, int data );
@ -16,27 +16,27 @@ class Ay_Cpu {
public:
// Clear all registers and keep pointer to 64K memory passed in
void reset( void* mem_64k );
// Run until specified time is reached. Returns true if suspicious/unsupported
// instruction was encountered at any point during run.
bool run( cpu_time_t end_time );
// Time of beginning of next instruction
cpu_time_t time() const { return state->time + state->base; }
// Alter current time. Not supported during run() call.
void set_time( cpu_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; }
#if BLARGG_BIG_ENDIAN
struct regs_t { uint8_t b, c, d, e, h, l, flags, a; };
#else
struct regs_t { uint8_t c, b, e, d, l, h, a, flags; };
#endif
static_assert( sizeof (regs_t) == 8, "Invalid register size, padding issue?" );
struct pairs_t { uint16_t bc, de, hl, fa; };
// Registers are not updated until run() returns
struct registers_t {
uint16_t pc;
@ -58,10 +58,10 @@ public:
uint8_t im;
};
//registers_t r; (below for efficiency)
// can read this far past end of memory
enum { cpu_padding = 0x100 };
public:
Ay_Cpu();
private:

View file

@ -20,11 +20,11 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
long const spectrum_clock = 3546900;
long const cpc_clock = 2000000;
static long const spectrum_clock = 3546900;
static long const cpc_clock = 2000000;
unsigned const ram_start = 0x4000;
int const osc_count = Ay_Apu::osc_count + 1;
static unsigned const ram_start = 0x4000;
static int const osc_count = Ay_Apu::osc_count + 1;
using std::min;
using std::max;
@ -33,12 +33,12 @@ Ay_Emu::Ay_Emu()
{
beeper_output = 0;
set_type( gme_ay_type );
static const char* const names [osc_count] = {
"Wave 1", "Wave 2", "Wave 3", "Beeper"
};
set_voice_names( names );
static int const types [osc_count] = {
wave_type | 0, wave_type | 1, wave_type | 2, mixed_type | 0
};
@ -56,7 +56,7 @@ static byte const* get_data( Ay_Emu::file_t const& file, byte const* ptr, int mi
long file_size = file.end - (byte const*) file.header;
assert( (unsigned long) pos <= (unsigned long) file_size - 2 );
int offset = (int16_t) get_be16( ptr );
if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) )
if ( !offset || uint32_t (pos + offset) > uint32_t (file_size - min_size) )
return 0;
return ptr + offset;
}
@ -66,18 +66,18 @@ static blargg_err_t parse_header( byte const* in, long size, Ay_Emu::file_t* out
typedef Ay_Emu::header_t header_t;
out->header = (header_t const*) in;
out->end = in + size;
if ( size < Ay_Emu::header_size )
return gme_wrong_file_type;
header_t const& h = *(header_t const*) in;
if ( memcmp( h.tag, "ZXAYEMUL", 8 ) )
return gme_wrong_file_type;
out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 );
if ( !out->tracks )
return "Missing track data";
return 0;
}
@ -87,7 +87,7 @@ static void copy_ay_fields( Ay_Emu::file_t const& file, track_info_t* out, int t
byte const* track_info = get_data( file, file.tracks + track * 4 + 2, 6 );
if ( track_info )
out->length = get_be16( track_info + 4 ) * (1000L / 50); // frames to msec
Gme_File::copy_field_( out->author, (char const*) get_data( file, file.header->author, 1 ) );
Gme_File::copy_field_( out->comment, (char const*) get_data( file, file.header->comment, 1 ) );
}
@ -101,16 +101,16 @@ blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const
struct Ay_File : Gme_Info_
{
Ay_Emu::file_t file;
Ay_File() { set_type( gme_ay_type ); }
blargg_err_t load_mem_( byte const* begin, long size )
{
RETURN_ERR( parse_header( begin, size, &file ) );
set_track_count( file.header->max_track + 1 );
return 0;
}
blargg_err_t track_info_( track_info_t* out, int track ) const
{
copy_ay_fields( file, out, track );
@ -128,20 +128,20 @@ extern gme_type_t const gme_ay_type = &gme_ay_type_;
blargg_err_t Ay_Emu::load_mem_( byte const* in, long size )
{
assert( offsetof (header_t,track_info [2]) == header_size );
blaarg_static_assert( offsetof (header_t,track_info [2]) == header_size, "AY Header layout incorrect!" );
RETURN_ERR( parse_header( in, size, &file ) );
set_track_count( file.header->max_track + 1 );
if ( file.header->vers > 2 )
set_warning( "Unknown file version" );
set_voice_count( osc_count );
apu.volume( gain() );
return setup_buffer( spectrum_clock );
}
void Ay_Emu::update_eq( blip_eq_t const& eq )
{
apu.treble_eq( eq );
@ -165,23 +165,23 @@ void Ay_Emu::set_tempo_( double t )
blargg_err_t Ay_Emu::start_track_( int track )
{
RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET
memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 );
memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start );
memset( mem.padding1, 0xFF, sizeof mem.padding1 );
memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 );
// locate data blocks
byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 );
if ( !data ) return "File data missing";
byte const* const more_data = get_data( file, data + 10, 6 );
if ( !more_data ) return "File data missing";
byte const* blocks = get_data( file, data + 12, 8 );
if ( !blocks ) return "File data missing";
// initial addresses
cpu::reset( mem.ram );
r.sp = get_be16( more_data );
@ -189,14 +189,14 @@ blargg_err_t Ay_Emu::start_track_( int track )
r.b.flags = r.b.c = r.b.e = r.b.l = data [9];
r.alt.w = r.w;
r.ix = r.iy = r.w.hl;
unsigned addr = get_be16( blocks );
if ( !addr ) return "File data missing";
unsigned init = get_be16( more_data + 2 );
if ( !init )
init = addr;
// copy blocks into memory
do
{
@ -209,7 +209,7 @@ blargg_err_t Ay_Emu::start_track_( int track )
}
check( len );
byte const* in = get_data( file, blocks, 0 ); blocks += 2;
if ( len > blargg_ulong (file.end - in) )
if ( len > uint32_t (file.end - in) )
{
set_warning( "Missing file data" );
len = file.end - in;
@ -218,7 +218,7 @@ blargg_err_t Ay_Emu::start_track_( int track )
if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data
debug_printf( "Block addr in ROM\n" );
memcpy( mem.ram + addr, in, len );
if ( file.end - blocks < 8 )
{
set_warning( "Missing file data" );
@ -226,7 +226,7 @@ blargg_err_t Ay_Emu::start_track_( int track )
}
}
while ( (addr = get_be16( blocks )) != 0 );
// copy and configure driver
static byte const passive [] = {
0xF3, // DI
@ -256,24 +256,24 @@ blargg_err_t Ay_Emu::start_track_( int track )
}
mem.ram [2] = init;
mem.ram [3] = init >> 8;
mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET)
memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh)
beeper_delta = int (apu.amp_range * 0.65);
last_beeper = 0;
apu.reset();
next_play = play_period;
// start at spectrum speed
change_clock_rate( spectrum_clock );
set_tempo( tempo() );
spectrum_mode = false;
cpc_mode = false;
cpc_latch = 0;
return 0;
}
@ -289,14 +289,14 @@ void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data )
spectrum_mode = true;
apu_addr = data & 0x0F;
return;
case 0xBEFD:
spectrum_mode = true;
apu.write( time, apu_addr, data );
return;
}
}
if ( !spectrum_mode )
{
switch ( addr >> 8 )
@ -307,22 +307,22 @@ void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data )
case 0xC0:
apu_addr = cpc_latch & 0x0F;
goto enable_cpc;
case 0x80:
apu.write( time, apu_addr, cpc_latch );
goto enable_cpc;
}
break;
case 0xF4:
cpc_latch = data;
goto enable_cpc;
}
}
debug_printf( "Unmapped OUT: $%04X <- $%02X\n", addr, data );
return;
enable_cpc:
if ( !cpc_mode )
{
@ -335,7 +335,7 @@ enable_cpc:
void ay_cpu_out( Ay_Cpu* cpu, cpu_time_t time, unsigned addr, int data )
{
Ay_Emu& emu = STATIC_CAST(Ay_Emu&,*cpu);
if ( (addr & 0xFF) == 0xFE && !emu.cpc_mode )
{
int delta = emu.beeper_delta;
@ -360,7 +360,7 @@ int ay_cpu_in( Ay_Cpu*, unsigned addr )
// keyboard read and other things
if ( (addr & 0xFF) == 0xFE )
return 0xFF; // other values break some beeper tunes
debug_printf( "Unmapped IN : $%04X\n", addr );
return 0xFF;
}
@ -370,22 +370,22 @@ blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int )
set_time( 0 );
if ( !(spectrum_mode | cpc_mode) )
duration /= 2; // until mode is set, leave room for halved clock rate
while ( time() < duration )
{
cpu::run( min( duration, (blip_time_t) next_play ) );
if ( time() >= next_play )
{
next_play += play_period;
if ( r.iff1 )
{
if ( mem.ram [r.pc] == 0x76 )
r.pc++;
r.iff1 = r.iff2 = 0;
mem.ram [--r.sp] = uint8_t (r.pc >> 8);
mem.ram [--r.sp] = uint8_t (r.pc);
r.pc = 0x38;
@ -403,8 +403,8 @@ blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int )
next_play -= duration;
check( next_play >= 0 );
adjust_time( -duration );
apu.end_frame( duration );
return 0;
}

View file

@ -25,7 +25,7 @@ public:
byte first_track;
byte track_info [2];
};
static gme_type_t static_type() { return gme_ay_type; }
public:
Ay_Emu();
@ -45,7 +45,7 @@ protected:
void update_eq( blip_eq_t const& );
private:
file_t file;
cpu_time_t play_period;
cpu_time_t next_play;
Blip_Buffer* beeper_output;
@ -55,7 +55,7 @@ private:
int cpc_latch;
bool spectrum_mode;
bool cpc_mode;
// large items
struct {
byte padding1 [0x100];

View file

@ -23,7 +23,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include BLARGG_ENABLE_OPTIMIZER
#endif
int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
static int const silent_buf_size = 1; // size used for Silent_Blip_Buffer
Blip_Buffer::Blip_Buffer()
{
@ -37,13 +37,13 @@ Blip_Buffer::Blip_Buffer()
clock_rate_ = 0;
bass_freq_ = 16;
length_ = 0;
// assumptions code makes about implementation-defined features
#ifndef NDEBUG
// right shift of negative value preserves sign
buf_t_ i = -0x7FFFFFFE;
assert( (i >> 1) == -0x3FFFFFFF );
// casting to short truncates to 16 bits and sign-extends
i = 0x18000;
assert( (short) i == -0x8000 );
@ -83,7 +83,7 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec
assert( 0 );
return "Internal (tried to resize Silent_Blip_Buffer)";
}
// start with maximum length that resampled time can represent
long new_size = (UINT_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64;
if ( msec != blip_max_length )
@ -94,7 +94,7 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec
else
assert( 0 ); // fails if requested buffer length exceeds limit
}
if ( buffer_size_ != new_size )
{
void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ );
@ -102,10 +102,10 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec
return "Out of memory";
buffer_ = (buf_t_*) p;
}
buffer_size_ = new_size;
assert( buffer_size_ != silent_buf_size );
// update things based on the sample rate
sample_rate_ = new_rate;
length_ = new_size * 1000 / new_rate - 1;
@ -114,9 +114,9 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec
if ( clock_rate_ )
clock_rate( clock_rate_ );
bass_freq( bass_freq_ );
clear();
return 0; // success
}
@ -144,7 +144,6 @@ void Blip_Buffer::bass_freq( int freq )
void Blip_Buffer::end_frame( blip_time_t t )
{
offset_ += t * factor_;
assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length
}
void Blip_Buffer::remove_silence( long count )
@ -167,7 +166,7 @@ blip_time_t Blip_Buffer::count_clocks( long count ) const
assert( 0 ); // sample rate and clock rates must be set first
return 0;
}
if ( count > buffer_size_ )
count = buffer_size_;
blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY;
@ -179,7 +178,7 @@ void Blip_Buffer::remove_samples( long count )
if ( count )
{
remove_silence( count );
// copy remaining samples to beginning and clear old samples
long remain = samples_avail() + blip_buffer_extra_;
memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ );
@ -221,12 +220,12 @@ static void gen_sinc( float* out, int count, double oversample, double treble, d
{
if ( cutoff >= 0.999 )
cutoff = 0.999;
if ( treble < -300.0 )
treble = -300.0;
if ( treble > 5.0 )
treble = 5.0;
double const maxh = 4096.0;
double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) );
double const pow_a_n = pow( rolloff, maxh - maxh * cutoff );
@ -236,17 +235,17 @@ static void gen_sinc( float* out, int count, double oversample, double treble, d
double angle = ((i - count) * 2 + 1) * to_angle;
double angle_maxh = angle * maxh;
double angle_maxh_mid = angle_maxh * cutoff;
double y = maxh;
// 0 to Fs/2*cutoff, flat
if ( angle_maxh_mid ) // unstable at t=0
y *= sin( angle_maxh_mid ) / angle_maxh_mid;
// Fs/2*cutoff to Fs/2, logarithmic rolloff
double cosa = cos( angle );
double den = 1 + rolloff * (rolloff - cosa - cosa);
// Becomes unstable when rolloff is near 1.0 and t is near 0,
// which is the only time den becomes small
if ( den > 1e-13 )
@ -254,10 +253,10 @@ static void gen_sinc( float* out, int count, double oversample, double treble, d
double num =
(cos( angle_maxh - angle ) * rolloff - cos( angle_maxh )) * pow_a_n -
cos( angle_maxh_mid - angle ) * rolloff + cos( angle_maxh_mid );
y = y * cutoff + num / den;
}
out [i] = (float) y;
}
}
@ -271,9 +270,9 @@ void blip_eq_t::generate( float* out, int count ) const
if ( cutoff_freq )
oversample = half_rate / cutoff_freq;
double cutoff = rolloff_freq * oversample / half_rate;
gen_sinc( out, count, blip_res * oversample, treble, cutoff );
// apply (half of) hamming window
double to_fraction = PI / (count - 1);
for ( int i = count; i--; )
@ -298,7 +297,7 @@ void Blip_Synth_::adjust_impulse()
impulses [size - blip_res + p] += (short) error;
//printf( "error: %ld\n", error );
}
//for ( int i = blip_res; i--; printf( "\n" ) )
// for ( int j = 0; j < width / 2; j++ )
// printf( "%5ld,", impulses [j * blip_res + i + 1] );
@ -307,31 +306,31 @@ void Blip_Synth_::adjust_impulse()
void Blip_Synth_::treble_eq( blip_eq_t const& eq )
{
float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2];
int const half_size = blip_res / 2 * (width - 1);
eq.generate( &fimpulse [blip_res], half_size );
int i;
// need mirror slightly past center for calculation
for ( i = blip_res; i--; )
fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i];
// starts at 0
for ( i = 0; i < blip_res; i++ )
fimpulse [i] = 0.0f;
// find rescale factor
double total = 0.0;
for ( i = 0; i < half_size; i++ )
total += fimpulse [blip_res + i];
//double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB
//double const base_unit = 37888.0; // allows treble to +5 dB
double const base_unit = 32768.0; // necessary for blip_unscaled to work
double rescale = base_unit / 2 / total;
kernel_unit = (long) base_unit;
// integrate, first difference, rescale, convert to int
double sum = 0.0;
double next = 0.0;
@ -343,7 +342,7 @@ void Blip_Synth_::treble_eq( blip_eq_t const& eq )
next += fimpulse [i + blip_res];
}
adjust_impulse();
// volume might require rescaling
double vol = volume_unit_;
if ( vol )
@ -360,26 +359,26 @@ void Blip_Synth_::volume_unit( double new_unit )
// use default eq if it hasn't been set yet
if ( !kernel_unit )
treble_eq( -8.0 );
volume_unit_ = new_unit;
double factor = new_unit * (1L << blip_sample_bits) / kernel_unit;
if ( factor > 0.0 )
{
int shift = 0;
// if unit is really small, might need to attenuate kernel
while ( factor < 2.0 )
{
shift++;
factor *= 2.0;
}
if ( shift )
{
kernel_unit >>= shift;
assert( kernel_unit > 0 ); // fails if volume unit is too low
// keep values positive to avoid round-towards-zero of sign-preserving
// right shift for negative values
long offset = 0x8000 + (1 << (shift - 1));
@ -400,12 +399,12 @@ long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_sampl
long count = samples_avail();
if ( count > max_samples )
count = max_samples;
if ( count )
{
int const bass = BLIP_READER_BASS( *this );
BLIP_READER_BEGIN( reader, *this );
if ( !stereo )
{
for ( blip_long n = count; n; --n )
@ -430,7 +429,7 @@ long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_sampl
}
}
BLIP_READER_END( reader, *this );
remove_samples( count );
}
return count;
@ -443,9 +442,9 @@ void Blip_Buffer::mix_samples( blip_sample_t const* in, long count )
assert( 0 );
return;
}
buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2;
int const sample_shift = blip_sample_bits - 16;
int prev = 0;
while ( count-- )

View file

@ -1,6 +1,7 @@
// Band-limited sound synthesis buffer
// Blip_Buffer 0.4.1
#ifndef BLIP_BUFFER_H
#define BLIP_BUFFER_H
@ -9,7 +10,7 @@
#if INT_MAX < 0x7FFFFFFF
#error "int must be at least 32 bits"
#endif
typedef int blip_long;
typedef unsigned blip_ulong;
@ -23,66 +24,66 @@ enum { blip_sample_max = 32767 };
class Blip_Buffer {
public:
typedef const char* blargg_err_t;
// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults
// to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there
// isn't enough memory, returns error without affecting current buffer setup.
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 );
// Set number of source time units per second
void clock_rate( long );
// End current time frame of specified duration and make its samples available
// (along with any still-unread samples) for reading with read_samples(). Begins
// a new time frame at the end of the current frame.
void end_frame( blip_time_t time );
// Read at most 'max_samples' out of buffer into 'dest', removing them from from
// the buffer. Returns number of samples actually read and removed. If stereo is
// true, increments 'dest' one extra time after writing each sample, to allow
// easy interleving of two channels into a stereo output buffer.
long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 );
// Additional optional features
// Current output sample rate
long sample_rate() const;
// Length of buffer, in milliseconds
int length() const;
// Number of source time units per second
long clock_rate() const;
// Set frequency high-pass filter frequency, where higher values reduce bass more
void bass_freq( int frequency );
// Number of samples delay from synthesis to samples read out
int output_latency() const;
// Remove all available samples and clear buffer to silence. If 'entire_buffer' is
// false, just clears out any samples waiting rather than the entire buffer.
void clear( int entire_buffer = 1 );
// Number of samples available for reading with read_samples()
long samples_avail() const;
// Remove 'count' samples from those waiting to be read
void remove_samples( long count );
// Experimental features
// Count number of clocks needed until 'count' samples will be available.
// If buffer can't even hold 'count' samples, returns number of clocks until
// buffer becomes full.
blip_time_t count_clocks( long count ) const;
// Number of raw samples that can be mixed within frame of specified duration.
long count_samples( blip_time_t duration ) const;
// Mix 'count' samples from 'buf' into buffer.
void mix_samples( blip_sample_t const* buf, long count );
// not documented yet
void set_modified() { modified_ = 1; }
int clear_modified() { int b = modified_; modified_ = 0; return b; }
@ -94,7 +95,7 @@ public:
public:
Blip_Buffer();
~Blip_Buffer();
// Deprecated
typedef blip_resampled_time_t resampled_time_t;
blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); }
@ -120,9 +121,7 @@ private:
friend class Blip_Reader;
};
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "blargg_config.h"
// Number of bits in resample ratio fraction. Higher values give a more accurate ratio
// but reduce maximum buffer size.
@ -147,24 +146,24 @@ private:
int const blip_buffer_extra_ = blip_widest_impulse_ + 2;
int const blip_res = 1 << BLIP_PHASE_BITS;
class blip_eq_t;
class Blip_Synth_Fast_ {
public:
Blip_Buffer* buf;
int last_amp;
int delta_factor;
void volume_unit( double );
Blip_Synth_Fast_();
void treble_eq( blip_eq_t const& ) { }
};
class Blip_Synth_ {
public:
Blip_Buffer* buf;
int last_amp;
int delta_factor;
void volume_unit( double );
Blip_Synth_( short* impulses, int width );
void treble_eq( blip_eq_t const& );
@ -190,14 +189,14 @@ class Blip_Synth {
public:
// Set overall volume of waveform
void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); }
// Configure low-pass filter (see blip_buffer.txt)
void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); }
// Get/set Blip_Buffer used for output
Blip_Buffer* output() const { return impl.buf; }
void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; }
// Update amplitude of waveform at given time. Using this requires a separate
// Blip_Synth for each waveform.
void update( blip_time_t time, int amplitude );
@ -209,10 +208,10 @@ public:
// The actual change in amplitude is delta * (volume / range)
void offset( blip_time_t, int delta, Blip_Buffer* ) const;
void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); }
// Works directly in terms of fractional output samples. Contact author for more info.
void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
// Same as offset(), except code is inlined for higher performance
void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const {
offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
@ -220,7 +219,7 @@ public:
void offset_inline( blip_time_t t, int delta ) const {
offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
}
private:
#if BLIP_BUFFER_FAST
Blip_Synth_Fast_ impl;
@ -233,9 +232,9 @@ public:
#endif
// disable broken defaulted constructors, Blip_Synth_ isn't safe to move/copy
Blip_Synth<quality, range> (const Blip_Synth<quality, range> &) = delete;
Blip_Synth<quality, range> ( Blip_Synth<quality, range> &&) = delete;
Blip_Synth<quality, range>& operator=(const Blip_Synth<quality, range> &) = delete;
Blip_Synth (const Blip_Synth &) = delete;
Blip_Synth ( Blip_Synth &&) = delete;
Blip_Synth& operator=(const Blip_Synth &) = delete;
};
// Low-pass equalization parameters
@ -244,10 +243,10 @@ public:
// Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce
// treble, small positive values (0 to 5.0) increase treble.
blip_eq_t( double treble_db = 0 );
// See blip_buffer.txt
blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 );
private:
double treble;
long rolloff_freq;
@ -268,15 +267,16 @@ public:
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length );
blip_time_t count_clocks( long count ) const;
void mix_samples( blip_sample_t const* buf, long count );
Silent_Blip_Buffer();
};
#if defined (__GNUC__) || _MSC_VER >= 1100
#define BLIP_RESTRICT __restrict
#else
#define BLIP_RESTRICT
#endif
#if (defined(__GNUC__) && (__GNUC__ >= 3)) || \
(defined(_MSC_VER) && (_MSC_VER >= 1100))
#define BLIP_RESTRICT __restrict
#else
#define BLIP_RESTRICT
#endif
// Optimized reading from Blip_Buffer, for use in custom sample output
@ -328,7 +328,7 @@ public:
blip_long read_raw() const { return accum; }
void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); }
void end( Blip_Buffer& b ) { b.reader_accum_ = accum; }
private:
const Blip_Buffer::buf_t_* buf;
blip_long accum;
@ -351,14 +351,14 @@ inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t t
#if BLIP_BUFFER_FAST
blip_long left = buf [0] + delta;
// Kind of crappy, but doing shift after multiply results in overflow.
// Alternate way of delaying multiply by delta_factor results in worse
// sub-sample resolution.
blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
left -= right;
right += buf [1];
buf [0] = left;
buf [1] = right;
#else
@ -366,17 +366,17 @@ inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t t
int const fwd = (blip_widest_impulse_ - quality) / 2;
int const rev = fwd + quality - 2;
int const mid = quality / 2 - 1;
imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase;
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
// straight forward implementation resulted in better code on GCC for x86
#define ADD_IMP( out, in ) \
buf [out] += (blip_long) imp [blip_res * (in)] * delta
#define BLIP_FWD( i ) {\
ADD_IMP( fwd + i, i );\
ADD_IMP( fwd + 1 + i, i + 1 );\
@ -397,14 +397,14 @@ inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t t
if ( quality > 12 ) BLIP_REV( 6 )
if ( quality > 8 ) BLIP_REV( 4 )
BLIP_REV( 2 )
ADD_IMP( rev , 1 );
ADD_IMP( rev + 1, 0 );
#else
// for RISC processors, help compiler by reading ahead of writes
#define BLIP_FWD( i ) {\
blip_long t0 = i0 * delta + buf [fwd + i];\
blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\
@ -419,7 +419,7 @@ inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t t
buf [rev - r] = t0;\
buf [rev + 1 - r] = t1;\
}
blip_long i0 = *imp;
BLIP_FWD( 0 )
if ( quality > 8 ) BLIP_FWD( 2 )
@ -435,13 +435,13 @@ inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t t
if ( quality > 12 ) BLIP_REV( 6 )
if ( quality > 8 ) BLIP_REV( 4 )
BLIP_REV( 2 )
blip_long t0 = i0 * delta + buf [rev ];
blip_long t1 = *imp * delta + buf [rev + 1];
buf [rev ] = t0;
buf [rev + 1] = t1;
#endif
#endif
}
@ -474,7 +474,11 @@ inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) :
treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }
inline int Blip_Buffer::length() const { return length_; }
inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); }
inline long Blip_Buffer::samples_avail() const
{
long samples = (long) (offset_ >> BLIP_BUFFER_ACCURACY);
return samples <= (long) buffer_size_ ? samples : 0;
}
inline long Blip_Buffer::sample_rate() const { return sample_rate_; }
inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; }
inline long Blip_Buffer::clock_rate() const { return clock_rate_; }

View file

@ -1,120 +1,178 @@
if(NOT GME_BUILD_SHARED AND NOT GME_BUILD_STATIC)
message(FATAL_ERROR "Both shared and static builds disabled. Please enable build of shared and/or static build of the GME.")
endif()
if(GME_ZLIB)
find_package(ZLIB QUIET)
endif()
# List of source files required by libgme and any emulators
# This is not 100% accurate (Fir_Resampler for instance) but
# you'll be OK.
set(libgme_SRCS Blip_Buffer.cpp
Blip_Buffer.h
Classic_Emu.cpp
Classic_Emu.h
Data_Reader.cpp
Data_Reader.h
Dual_Resampler.cpp
Dual_Resampler.h
Effects_Buffer.cpp
Effects_Buffer.h
Fir_Resampler.cpp
Fir_Resampler.h
gme.cpp
gme.h
gme_types.h
Gme_File.cpp
Gme_File.h
M3u_Playlist.cpp
M3u_Playlist.h
Multi_Buffer.cpp
Multi_Buffer.h
Music_Emu.cpp
Music_Emu.h
blargg_common.h
blargg_config.h
blargg_endian.h
blargg_source.h
)
find_package(ZLIB QUIET)
# Ay_Apu is very popular around here
if (USE_GME_AY OR USE_GME_KSS)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_AY OR USE_GME_KSS)
list(APPEND libgme_SRCS
Ay_Apu.cpp
Ay_Apu.h
)
endif()
# so is Ym2612_Emu
if (USE_GME_VGM OR USE_GME_GYM)
if(USE_GME_VGM OR USE_GME_GYM)
if(GME_YM2612_EMU STREQUAL "Nuked")
add_definitions(-DVGM_YM2612_NUKED)
set(libgme_SRCS ${libgme_SRCS}
list(APPEND libgme_SRCS
Ym2612_Nuked.cpp
Ym2612_Nuked.h
)
message("VGM/GYM: Nuked OPN2 emulator will be used")
message(STATUS "VGM/GYM: Nuked OPN2 emulator will be used")
elseif(GME_YM2612_EMU STREQUAL "MAME")
add_definitions(-DVGM_YM2612_MAME)
set(libgme_SRCS ${libgme_SRCS}
list(APPEND libgme_SRCS
Ym2612_MAME.cpp
Ym2612_MAME.h
)
message("VGM/GYM: MAME YM2612 emulator will be used")
message(STATUS "VGM/GYM: MAME YM2612 emulator will be used")
else()
add_definitions(-DVGM_YM2612_GENS)
set(libgme_SRCS ${libgme_SRCS}
list(APPEND libgme_SRCS
Ym2612_GENS.cpp
Ym2612_GENS.h
)
message("VGM/GYM: GENS 2.10 emulator will be used")
message(STATUS "VGM/GYM: GENS 2.10 emulator will be used")
endif()
endif()
# But none are as popular as Sms_Apu
if (USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS)
list(APPEND libgme_SRCS
Sms_Apu.cpp
Sms_Apu.h
)
endif()
if (USE_GME_AY)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_AY)
list(APPEND libgme_SRCS
# Ay_Apu.cpp included earlier
Ay_Cpu.cpp
Ay_Cpu.h
Ay_Emu.cpp
Ay_Emu.h
)
endif()
if (USE_GME_GBS)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_GBS)
list(APPEND libgme_SRCS
Gb_Apu.cpp
Gb_Apu.h
Gb_Cpu.cpp
Gb_Cpu.h
Gb_Oscs.cpp
Gb_Oscs.h
Gbs_Emu.cpp
Gbs_Emu.h
gb_cpu_io.h
)
endif()
if (USE_GME_GYM)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_GYM)
list(APPEND libgme_SRCS
# Sms_Apu.cpp included earlier
# Ym2612_Emu.cpp included earlier
Gym_Emu.cpp
Gym_Emu.h
)
endif()
if (USE_GME_HES)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_HES)
list(APPEND libgme_SRCS
Hes_Apu.cpp
Hes_Apu.h
Hes_Cpu.cpp
Hes_Cpu.h
Hes_Emu.cpp
Hes_Emu.h
hes_cpu_io.h
)
endif()
if (USE_GME_KSS)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_KSS)
list(APPEND libgme_SRCS
# Ay_Apu.cpp included earlier
# Sms_Apu.cpp included earlier
Kss_Cpu.cpp
Kss_Cpu.h
Kss_Emu.cpp
Kss_Emu.h
Kss_Scc_Apu.cpp
Kss_Scc_Apu.h
)
endif()
if (USE_GME_NSF OR USE_GME_NSFE)
set(libgme_SRCS ${libgme_SRCS}
if(USE_GME_NSF OR USE_GME_NSFE)
list(APPEND libgme_SRCS
Nes_Apu.cpp
Nes_Apu.h
Nes_Cpu.cpp
Nes_Cpu.h
Nes_Fme7_Apu.cpp
Nes_Fme7_Apu.h
Nes_Namco_Apu.cpp
Nes_Namco_Apu.h
Nes_Oscs.cpp
Nes_Oscs.h
Nes_Vrc6_Apu.cpp
Nes_Vrc6_Apu.h
Nes_Fds_Apu.cpp
Nes_Fds_Apu.h
Nes_Vrc7_Apu.cpp
../ext/emu2413.c
../ext/panning.c
Nes_Vrc7_Apu.h
ext/emu2413.c
ext/emu2413.h
ext/panning.c
ext/panning.h
ext/emutypes.h
ext/2413tone.h
ext/vrc7tone.h
Nsf_Emu.cpp
Nsf_Emu.h
)
endif()
if (USE_GME_NSFE)
set(libgme_SRCS ${libgme_SRCS}
Nsfe_Emu.cpp
Nsfe_Emu.h
)
endif()
@ -123,6 +181,7 @@ if (USE_GME_SAP)
Sap_Apu.cpp
Sap_Cpu.cpp
Sap_Emu.cpp
sap_cpu_io.h
)
endif()
@ -135,12 +194,19 @@ if (USE_GME_SPC)
../higan/dsp/dsp.cpp
../higan/dsp/SPC_DSP.cpp
Snes_Spc.cpp
Snes_Spc.h
Spc_Cpu.cpp
Spc_Cpu.h
Spc_Dsp.cpp
Spc_Dsp.h
Spc_Emu.cpp
Spc_Emu.h
Spc_Filter.cpp
Spc_Filter.h
Bml_Parser.cpp
Bml_Parser.h
Spc_Sfm.cpp
Spc_Sfm.h
)
if (GME_SPC_ISOLATED_ECHO_BUFFER)
add_definitions(-DSPC_ISOLATED_ECHO_BUFFER)
@ -152,88 +218,265 @@ if (USE_GME_VGM)
# Sms_Apu.cpp included earlier
# Ym2612_Emu.cpp included earlier
Vgm_Emu.cpp
Vgm_Emu.h
Vgm_Emu_Impl.cpp
Vgm_Emu_Impl.h
Ym2413_Emu.cpp
Ym2413_Emu.h
)
endif()
# These headers are part of the generic gme interface.
set (EXPORTED_HEADERS gme.h blargg_source.h)
set (EXPORTED_HEADERS gme.h)
# while building a macOS framework, exported headers must be in the source
# list, or the header files aren't copied to the bundle.
if (BUILD_FRAMEWORK)
if(GME_BUILD_FRAMEWORK)
set(libgme_SRCS ${libgme_SRCS} ${EXPORTED_HEADERS})
endif()
# On some platforms we may need to change headers or whatnot based on whether
# we're building the library or merely using the library. The following is
# only defined when building the library to allow us to tell which is which.
add_definitions(-DBLARGG_BUILD_DLL)
# Extract symbols from gme.exports
file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/gme.exports" gme_exports_strings)
set(gme_exports)
foreach(gme_export_string ${gme_exports_strings})
string(STRIP "${gme_export_string}" gme_export_string)
string(SUBSTRING "${gme_export_string}" 0 1 gme_export_first_char)
if(gme_export_string AND NOT gme_export_first_char STREQUAL "#")
list(APPEND gme_exports ${gme_export_string})
endif()
endforeach()
# For the gme_types.h
include_directories(${CMAKE_CURRENT_BINARY_DIR})
# Add library to be compiled.
add_library(gme ${libgme_SRCS})
add_library(gme_deps INTERFACE)
if(ZLIB_FOUND)
message(" ** ZLib library located, compressed file formats will be supported")
target_compile_definitions(gme PRIVATE -DHAVE_ZLIB_H)
target_include_directories(gme PRIVATE ${ZLIB_INCLUDE_DIRS})
target_link_libraries(gme ${ZLIB_LIBRARIES})
# Is not to be installed though
## FIXME: Properly find the C++ library !!!
set(PC_LIBS -lstdc++)
set(PKG_CONFIG_ZLIB -lz) # evaluated in libgme.pc.in
if(GME_ZLIB)
if(ZLIB_FOUND)
message(STATUS "ZLib library located, compressed file formats will be supported")
target_compile_definitions(gme_deps INTERFACE HAVE_ZLIB_H)
target_link_libraries(gme_deps INTERFACE ZLIB::ZLIB)
# Is not to be installed though
list(APPEND PC_LIBS -lz) # for libgme.pc
else()
message(STATUS "** ZLib library not found, disabling support for compressed formats such as VGZ")
endif()
else()
message("ZLib library not found, disabling support for compressed formats such as VGZ")
message(STATUS "Zlib-Compressed formats excluded")
endif()
if(USE_GME_SPC)
if(UNRAR_FOUND)
message(" ** unRAR library located, the RSN file format will be supported")
target_compile_definitions(gme PRIVATE -DRARDLL)
target_include_directories(gme PRIVATE ${UNRAR_INCLUDE_DIRS})
target_link_libraries(gme ${UNRAR_LIBRARIES})
# Is not to be installed though
if(NOT MSVC)
# Link with -no-undefined, if available
if(NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*")
cmake_push_check_state()
set(CMAKE_REQUIRED_FLAGS "-Wl,-no-undefined")
check_cxx_source_compiles("int main(void) { return 0;}" LINKER_SUPPORTS_NO_UNDEFINED)
cmake_pop_check_state()
endif()
set(PKG_CONFIG_UNRAR -lunrar) # evaluated in libgme.pc.in
else()
message("unRAR library not found, disabling support for the RSN file format")
# Link to libm, if necessary
check_cxx_source_compiles("
#include <math.h>
int main(int argc, char** argv) {
return (int) pow(argc, 2.5);
}" LIBM_NOT_NEEDED)
if(NOT LIBM_NOT_NEEDED)
cmake_push_check_state()
set(CMAKE_REQUIRED_LIBRARIES "-lm")
check_cxx_source_compiles("
#include <math.h>
int main(int argc, char** argv) {
return (int) pow(argc, 2.5);
}" HAVE_LIBM)
cmake_pop_check_state()
if(HAVE_LIBM)
list(APPEND PC_LIBS -lm) # for libgme.pc
endif()
endif()
endif()
# The version is the release. The "soversion" is the API version. As long
# as only build fixes are performed (i.e. no backwards-incompatible changes
# to the API), the SOVERSION should be the same even when bumping up VERSION.
# The way gme.h is designed, SOVERSION should very rarely be bumped, if ever.
# Hopefully the API can stay compatible with old versions.
set_target_properties(gme
PROPERTIES VERSION ${GME_VERSION}
SOVERSION 1)
# macOS framework build
if(BUILD_FRAMEWORK)
set_target_properties(gme
PROPERTIES FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER net.mpyne.gme
VERSION ${GME_VERSION}
SOVERSION 0
PUBLIC_HEADER "${EXPORTED_HEADERS}")
# Add a version script to hide the c++ STL
if(APPLE)
set(gme_syms "")
foreach(gme_export ${gme_exports})
set(gme_syms "${gme_syms}_${gme_export}\n")
endforeach()
# Use an intermediate 'gme_generated.sym' file to avoid a relink every time CMake runs
set(temporary_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme_generated.sym")
set(generated_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme.sym")
file(WRITE "${temporary_sym_file}" "${gme_syms}")
execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${temporary_sym_file}" "${generated_sym_file}")
elseif(UNIX)
set(gme_syms "{\n global:\n")
foreach(gme_export ${gme_exports})
set(gme_syms "${gme_syms} ${gme_export};\n")
endforeach()
set(gme_syms "${gme_syms} local: *;\n};\n")
# Use an intermediate 'gme_generated.sym' file to avoid a relink every time CMake runs
set(temporary_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme_generated.sym")
set(generated_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme.sym")
file(WRITE "${temporary_sym_file}" "${gme_syms}")
execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${temporary_sym_file}" "${generated_sym_file}")
cmake_push_check_state()
set(CMAKE_REQUIRED_FLAGS "-Wl,-version-script='${generated_sym_file}'")
check_cxx_source_compiles("int main() { return 0;}" LINKER_SUPPORTS_VERSION_SCRIPT)
cmake_pop_check_state()
endif()
install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX}
RUNTIME DESTINATION bin # DLL platforms
ARCHIVE DESTINATION lib # DLL platforms
FRAMEWORK DESTINATION /Library/Frameworks) # macOS framework
set(INSTALL_TARGETS)
# Shared properties for shared and static builds of libGME
macro(set_gme_props gme_target)
list(APPEND INSTALL_TARGETS ${gme_target})
set_property(TARGET ${gme_target} PROPERTY C_VISIBILITY_PRESET "hidden")
set_property(TARGET ${gme_target} PROPERTY VISIBILITY_INLINES_HIDDEN TRUE)
set_property(TARGET ${gme_target} PROPERTY CXX_VISIBILITY_PRESET "hidden")
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_definitions(${gme_target} PRIVATE LIBGME_VISIBILITY)
endif()
target_compile_definitions(${gme_target} PRIVATE GEN_TYPES_H)
if(WORDS_BIGENDIAN)
target_compile_definitions(${gme_target} PRIVATE BLARGG_BIG_ENDIAN=1)
else()
target_compile_definitions(${gme_target} PRIVATE BLARGG_LITTLE_ENDIAN=1)
endif()
# Try to protect against undefined behavior from signed integer overflow
# This has caused miscompilation of code already and there are other
# potential uses; see https://bitbucket.org/mpyne/game-music-emu/issues/18/
# (https://github.com/libgme/game-music-emu/issues/20 )
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
target_compile_options(${gme_target} PRIVATE -fwrapv)
endif()
target_include_directories(${gme_target}
PRIVATE "$<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>" # For gen_types.h
INTERFACE "$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>"
INTERFACE "$<INSTALL_INTERFACE:${CMAKE_INSTALL_INCLUDEDIR}>"
)
if(NOT MSVC)
# Link with -no-undefined, if available
if(APPLE)
set_property(TARGET ${gme_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-undefined,error")
elseif(NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*" AND LINKER_SUPPORTS_NO_UNDEFINED)
set_property(TARGET ${gme_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-no-undefined")
endif()
endif()
endmacro()
# Building the Shared version of GME
if(GME_BUILD_SHARED)
add_library(gme_shared SHARED ${libgme_SRCS})
set_target_properties(gme_shared PROPERTIES OUTPUT_NAME gme)
if(WIN32)
set_property(TARGET gme_shared PROPERTY DEFINE_SYMBOL BLARGG_BUILD_DLL)
endif()
set_gme_props(gme_shared)
# Add a version script to hide the c++ STL
if(APPLE)
set_property(TARGET gme_shared APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-exported_symbols_list,'${generated_sym_file}'")
set_property(TARGET gme_shared APPEND PROPERTY LINK_DEPENDS "${generated_sym_file}")
elseif(UNIX AND LINKER_SUPPORTS_VERSION_SCRIPT)
set_property(TARGET gme_shared APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-version-script='${generated_sym_file}'")
set_property(TARGET gme_shared APPEND PROPERTY LINK_DEPENDS "${generated_sym_file}")
endif()
target_link_libraries(gme_shared PRIVATE gme_deps)
if(HAVE_LIBM)
target_link_libraries(gme_shared PRIVATE -lm)
endif()
# The version is the release. The "soversion" is the API version. As long
# as only build fixes are performed (i.e. no backwards-incompatible changes
# to the API), the SOVERSION should be the same even when bumping up VERSION.
# The way gme.h is designed, SOVERSION should very rarely be bumped, if ever.
# Hopefully the API can stay compatible with old versions.
set_target_properties(gme_shared PROPERTIES VERSION ${GME_VERSION} SOVERSION 0)
# macOS framework build
if(GME_BUILD_FRAMEWORK)
set_target_properties(gme_shared
PROPERTIES FRAMEWORK TRUE
FRAMEWORK_VERSION A
MACOSX_FRAMEWORK_IDENTIFIER net.mpyne.gme
VERSION ${GME_VERSION}
SOVERSION 0
PUBLIC_HEADER "${EXPORTED_HEADERS}")
endif()
endif()
# Building the Static version of GME
if(GME_BUILD_STATIC)
# Static build.
add_library(gme_static STATIC ${libgme_SRCS})
# Static builds need to find static zlib (and static forms of other needed
# libraries. Ensure CMake looks only for static libs if we're doing a static
# build. See https://stackoverflow.com/a/44738756
if(MSVC)
set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib")
else()
set(CMAKE_FIND_LIBRARY_SUFFIXES ".a")
endif()
if(MSVC AND GME_BUILD_SHARED)
set_target_properties(gme_static PROPERTIES OUTPUT_NAME gme-static)
else()
set_target_properties(gme_static PROPERTIES OUTPUT_NAME gme)
endif()
set_gme_props(gme_static)
target_link_libraries(gme_static PUBLIC gme_deps)
if(HAVE_LIBM)
target_link_libraries(gme_static PUBLIC -lm)
endif()
endif()
if(GME_BUILD_SHARED)
add_library(gme::gme ALIAS gme_shared)
else()
add_library(gme::gme ALIAS gme_static)
endif()
install(TARGETS ${INSTALL_TARGETS}
LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # DLL platforms
ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # DLL platforms
FRAMEWORK DESTINATION /Library/Frameworks) # macOS framework
# Run during cmake phase, so this is available during make
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gme_types.h.in
${CMAKE_CURRENT_BINARY_DIR}/gme_types.h)
configure_file(gme_types.h.in gen_types.h @ONLY)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in
${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY)
set(TMP_PC_LIBS "")
foreach(PC_LIB ${PC_LIBS})
if(NOT "${TMP_PC_LIBS}" STREQUAL "")
set(TMP_PC_LIBS "${TMP_PC_LIBS} ${PC_LIB}")
else()
set(TMP_PC_LIBS "${PC_LIB}")
endif()
endforeach()
set(PC_LIBS "${TMP_PC_LIBS}")
unset(TMP_PC_LIBS)
install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)
configure_file(libgme.pc.in libgme.pc @ONLY)
install(FILES ${EXPORTED_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gme)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig)

View file

@ -23,11 +23,11 @@ Classic_Emu::Classic_Emu()
buf = 0;
stereo_buffer = 0;
voice_types = 0;
// avoid inconsistency in our duplicated constants
assert( (int) wave_type == (int) Multi_Buffer::wave_type );
assert( (int) noise_type == (int) Multi_Buffer::noise_type );
assert( (int) mixed_type == (int) Multi_Buffer::mixed_type );
blaarg_static_assert( (int) wave_type == (int) Multi_Buffer::wave_type, "wave_type inconsistent across two classes using it" );
blaarg_static_assert( (int) noise_type == (int) Multi_Buffer::noise_type, "noise_type inconsistent across two classes using it" );
blaarg_static_assert( (int) mixed_type == (int) Multi_Buffer::mixed_type, "mixed_type inconsistent across two classes using it" );
}
Classic_Emu::~Classic_Emu()
@ -42,7 +42,7 @@ void Classic_Emu::set_equalizer_( equalizer_t const& eq )
if ( buf )
buf->bass_freq( (int) equalizer().bass );
}
blargg_err_t Classic_Emu::set_sample_rate_( long rate )
{
if ( !buf )
@ -115,7 +115,7 @@ blargg_err_t Classic_Emu::play_( long count, sample_t* out )
remute_voices();
}
int msec = buf->length();
blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000;
blip_time_t clocks_emulated = (int32_t) msec * clock_rate_ / 1000;
RETURN_ERR( run_clocks( clocks_emulated, msec ) );
assert( clocks_emulated );
buf->end_frame( clocks_emulated );
@ -130,12 +130,12 @@ blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in,
int header_size, void* header_out, int fill, long pad_size )
{
long file_offset = pad_size - header_size;
rom_addr = 0;
mask = 0;
size_ = 0;
rom.clear();
file_size_ = in.remain();
if ( file_size_ <= header_size ) // <= because there must be data after header
return gme_wrong_file_type;
@ -147,20 +147,20 @@ blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in,
rom.clear();
return err;
}
file_size_ -= header_size;
memcpy( header_out, &rom [file_offset], header_size );
memset( rom.begin() , fill, pad_size );
memset( rom.end() - pad_size, fill, pad_size );
return 0;
}
void Rom_Data_::set_addr_( long addr, int unit )
{
rom_addr = addr - unit - pad_extra;
long rounded = (addr + file_size_ + unit - 1) / unit * unit;
if ( rounded <= 0 )
{
@ -174,7 +174,7 @@ void Rom_Data_::set_addr_( long addr, int unit )
shift++;
mask = (1L << shift) - 1;
}
if ( addr < 0 )
addr = 0;
size_ = rounded;
@ -182,9 +182,9 @@ void Rom_Data_::set_addr_( long addr, int unit )
if ( 0 )
{
debug_printf( "addr: %X\n", addr );
debug_printf( "file_size: %d\n", file_size_ );
debug_printf( "rounded: %d\n", rounded );
debug_printf( "addr: %X\n", (int)addr );
debug_printf( "file_size: %ld\n", file_size_ );
debug_printf( "rounded: %ld\n", rounded );
debug_printf( "mask: $%X\n", mask );
}
}

View file

@ -21,7 +21,7 @@ protected:
blargg_err_t setup_buffer( long clock_rate );
long clock_rate() const { return clock_rate_; }
void change_clock_rate( long ); // experimental
// Overridable
virtual void set_voice( int index, Blip_Buffer* center,
Blip_Buffer* left, Blip_Buffer* right ) = 0;
@ -58,10 +58,10 @@ protected:
enum { pad_extra = 8 };
blargg_vector<byte> rom;
long file_size_;
blargg_long rom_addr;
blargg_long mask;
blargg_long size_; // TODO: eliminate
int32_t rom_addr;
int32_t mask;
int32_t size_; // TODO: eliminate
blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out,
int fill, long pad_size );
void set_addr_( long addr, int unit );
@ -77,39 +77,39 @@ public:
{
return load_rom_data_( in, header_size, header_out, fill, pad_size );
}
// Size of file data read in (excluding header)
long file_size() const { return file_size_; }
// Pointer to beginning of file data
byte* begin() const { return rom.begin() + pad_size; }
// Set address that file data should start at
void set_addr( long addr ) { set_addr_( addr, unit ); }
// Free data
void clear() { rom.clear(); }
// Size of data + start addr, rounded to a multiple of unit
long size() const { return size_; }
// Pointer to unmapped page filled with same value
byte* unmapped() { return rom.begin(); }
// Mask address to nearest power of two greater than size()
blargg_long mask_addr( blargg_long addr ) const
int32_t mask_addr( int32_t addr ) const
{
#ifdef check
check( addr <= mask );
#endif
return addr & mask;
}
// Pointer to page starting at addr. Returns unmapped() if outside data.
byte* at_addr( blargg_long addr )
byte* at_addr( int32_t addr )
{
blargg_ulong offset = mask_addr( addr ) - rom_addr;
if ( offset > blargg_ulong (rom.size() - pad_size) )
uint32_t offset = mask_addr( addr ) - rom_addr;
if ( offset > uint32_t (rom.size() - pad_size) )
offset = 0; // unmapped
return &rom [offset];
}

View file

@ -362,15 +362,17 @@ blargg_err_t Std_File_Reader::open( const char* path )
long Std_File_Reader::size() const
{
if ( !file_ )
return -1L;
#ifdef HAVE_ZLIB_H
if ( file_ )
return size_; // Set for both compressed and uncompressed modes
#endif
return size_; // Set for both compressed and uncompressed modes
#else
long pos = tell();
fseek( (FILE*) file_, 0, SEEK_END );
long result = tell();
fseek( (FILE*) file_, pos, SEEK_SET );
return result;
#endif
}
long Std_File_Reader::read_avail( void* p, long s )
@ -390,52 +392,55 @@ long Std_File_Reader::read_avail( void* p, long s )
blargg_err_t Std_File_Reader::read( void* p, long s )
{
if ( !file_ )
return "NULL FILE pointer";
RETURN_VALIDITY_CHECK( s > 0 && static_cast<unsigned long>(s) <= UINT_MAX );
#ifdef HAVE_ZLIB_H
if ( file_ )
{
const auto &gzfile = reinterpret_cast<gzFile>( file_ );
if ( s == gzread( gzfile, p, static_cast<unsigned>( s ) ) )
return nullptr;
if ( gzeof( gzfile ) )
return eof_error;
return "Couldn't read from GZ file";
}
#endif
const auto &gzfile = reinterpret_cast<gzFile>( file_ );
if ( s == gzread( gzfile, p, static_cast<unsigned>( s ) ) )
return nullptr;
if ( gzeof( gzfile ) )
return eof_error;
return "Couldn't read from GZ file";
#else
const auto &file = reinterpret_cast<FILE*>( file_ );
if ( s == static_cast<long>( fread( p, 1, static_cast<size_t>(s), file ) ) )
return 0;
if ( feof( file ) )
return eof_error;
return "Couldn't read from file";
#endif
}
long Std_File_Reader::tell() const
{
if ( !file_ )
return -1L;
#ifdef HAVE_ZLIB_H
if ( file_ )
return gztell( reinterpret_cast<gzFile>( file_ ) );
#endif
return gztell( reinterpret_cast<gzFile>( file_ ) );
#else
return ftell( reinterpret_cast<FILE*>( file_ ) );
#endif
}
blargg_err_t Std_File_Reader::seek( long n )
{
if ( !file_ )
return "NULL FILE pointer";
#ifdef HAVE_ZLIB_H
if ( file_ )
{
if ( gzseek( reinterpret_cast<gzFile>( file_ ), n, SEEK_SET ) >= 0 )
return nullptr;
if ( n > size_ )
return eof_error;
return "Error seeking in GZ file";
}
#endif
if ( gzseek( reinterpret_cast<gzFile>( file_ ), n, SEEK_SET ) >= 0 )
return nullptr;
if ( n > size_ )
return eof_error;
return "Error seeking in GZ file";
#else
if ( !fseek( reinterpret_cast<FILE*>( file_ ), n, SEEK_SET ) )
return nullptr;
if ( n > size() )
return eof_error;
return "Error seeking in file";
#endif
}
void Std_File_Reader::close()
@ -450,4 +455,3 @@ void Std_File_Reader::close()
file_ = nullptr;
}
}

View file

@ -14,21 +14,21 @@
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
@ -43,13 +43,13 @@ 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 );
};
@ -59,7 +59,7 @@ class Std_File_Reader : public File_Reader {
public:
blargg_err_t open( const char* path );
void close();
public:
Std_File_Reader();
~Std_File_Reader();

View file

@ -58,13 +58,13 @@ void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out )
long pair_count = sample_buf_size >> 1;
blip_time_t blip_time = blip_buf.count_clocks( pair_count );
int sample_count = oversamples_per_frame - resampler.written();
int new_count = play_frame( blip_time, sample_count, resampler.buffer() );
assert( new_count < resampler_size );
blip_buf.end_frame( blip_time );
assert( blip_buf.samples_avail() == pair_count );
resampler.write( new_count );
#ifdef NDEBUG // Avoid warning when asserts are disabled
@ -73,7 +73,7 @@ void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out )
long count = resampler.read( sample_buf.begin(), sample_buf_size );
assert( count == (long) sample_buf_size );
#endif
mix_samples( blip_buf, out );
blip_buf.remove_samples( pair_count );
}
@ -91,7 +91,7 @@ void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_bu
out += remain;
buf_pos += remain;
}
// entire frames
while ( count >= (long) sample_buf_size )
{
@ -99,7 +99,7 @@ void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_bu
out += sample_buf_size;
count -= sample_buf_size;
}
// extra
if ( count )
{
@ -115,25 +115,25 @@ void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out )
Blip_Reader sn;
int bass = sn.begin( blip_buf );
const dsample_t* in = sample_buf.begin();
for ( int n = sample_buf_size >> 1; n--; )
{
int s = sn.read();
blargg_long l = (blargg_long) in [0] * 2 + s;
int32_t l = (int32_t) in [0] * 2 + s;
if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24);
sn.next( bass );
blargg_long r = (blargg_long) in [1] * 2 + s;
int32_t r = (int32_t) in [1] * 2 + s;
if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24);
in += 2;
out [0] = l;
out [1] = r;
out += 2;
}
sn.end( blip_buf );
}

View file

@ -11,26 +11,26 @@ class Dual_Resampler {
public:
Dual_Resampler();
virtual ~Dual_Resampler();
typedef short dsample_t;
double setup( double oversample, double rolloff, double gain );
blargg_err_t reset( int max_pairs );
void resize( int pairs_per_frame );
void clear();
void dual_play( long count, dsample_t* out, Blip_Buffer& );
protected:
virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0;
private:
blargg_vector<dsample_t> sample_buf;
int sample_buf_size;
int oversamples_per_frame;
int buf_pos;
int resampler_size;
Fir_Resampler<12> resampler;
void mix_samples( Blip_Buffer&, dsample_t* );
void play_frame_( Blip_Buffer&, dsample_t* );

View file

@ -22,7 +22,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include BLARGG_ENABLE_OPTIMIZER
#endif
typedef blargg_long fixed_t;
typedef int32_t fixed_t;
using std::min;
using std::max;
@ -30,12 +30,12 @@ using std::max;
#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5)
#define FMUL( x, y ) (((x) * (y)) >> 15)
const unsigned echo_size = 4096;
const unsigned echo_mask = echo_size - 1;
static const unsigned echo_size = 4096;
static const unsigned echo_mask = echo_size - 1;
static_assert( (echo_size & echo_mask) == 0, "echo_size must be a power of 2" );
const unsigned reverb_size = 8192 * 2;
const unsigned reverb_mask = reverb_size - 1;
static const unsigned reverb_size = 8192 * 2;
static const unsigned reverb_mask = reverb_size - 1;
static_assert( (reverb_size & reverb_mask) == 0, "reverb_size must be a power of 2" );
Effects_Buffer::config_t::config_t()
@ -88,34 +88,32 @@ Effects_Buffer::Effects_Buffer( int num_voices, bool center_only )
Effects_Buffer::~Effects_Buffer()
{}
blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec )
blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) noexcept
{
try
for(int i=0; i<max_voices; i++)
{
for(int i=0; i<max_voices; i++)
if ( !echo_buf[i].size() )
{
if ( !echo_buf[i].size() )
{
echo_buf[i].resize( echo_size );
}
if ( !reverb_buf[i].size() )
{
reverb_buf[i].resize( reverb_size );
}
echo_buf[i].resize( echo_size );
}
if ( !reverb_buf[i].size() )
{
reverb_buf[i].resize( reverb_size );
}
if ( !echo_buf[i].size() || !reverb_buf[i].size() )
{
return "Out of memory";
}
}
catch(std::bad_alloc& ba)
{
return "Out of memory";
}
for ( int i = 0; i < buf_count; i++ )
RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
config( config_ );
clear();
return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() );
}
@ -140,7 +138,7 @@ void Effects_Buffer::clear()
{
if ( echo_buf[i].size() )
memset( &echo_buf[i][0], 0, echo_size * sizeof echo_buf[i][0] );
if ( reverb_buf[i].size() )
memset( &reverb_buf[i][0], 0, reverb_size * sizeof reverb_buf[i][0] );
}
@ -161,7 +159,7 @@ inline int pin_range( int n, int max, int min = 0 )
void Effects_Buffer::config( const config_t& cfg )
{
channels_changed();
// clear echo and reverb buffers
// ensure the echo/reverb buffers have already been allocated, so this method can be
// called before set_sample_rate is called
@ -175,49 +173,49 @@ void Effects_Buffer::config( const config_t& cfg )
}
config_ = cfg;
if ( config_.effects_enabled )
{
// convert to internal format
chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 );
chans.pan_1_levels [1] = TO_FIXED( 2 ) - chans.pan_1_levels [0];
chans.pan_2_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 );
chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0];
chans.reverb_level = TO_FIXED( config_.reverb_level );
chans.echo_level = TO_FIXED( config_.echo_level );
int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate());
int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate());
chans.reverb_delay_l = pin_range( reverb_size -
(reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 );
chans.reverb_delay_r = pin_range( reverb_size + 1 -
(reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 );
int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate());
chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset),
echo_size - 1 );
chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset),
echo_size - 1 );
for(int i=0; i<max_voices; i++)
{
chan_types [i*chan_types_count+0].center = &bufs [i*max_buf_count+0];
chan_types [i*chan_types_count+0].left = &bufs [i*max_buf_count+3];
chan_types [i*chan_types_count+0].right = &bufs [i*max_buf_count+4];
chan_types [i*chan_types_count+1].center = &bufs [i*max_buf_count+1];
chan_types [i*chan_types_count+1].left = &bufs [i*max_buf_count+3];
chan_types [i*chan_types_count+1].right = &bufs [i*max_buf_count+4];
chan_types [i*chan_types_count+2].center = &bufs [i*max_buf_count+2];
chan_types [i*chan_types_count+2].left = &bufs [i*max_buf_count+5];
chan_types [i*chan_types_count+2].right = &bufs [i*max_buf_count+6];
}
assert( 2 < chan_types_count );
blaarg_static_assert( chan_types_count >= 3, "Need at least three audio channels for effects processing" );
}
else
{
@ -233,7 +231,7 @@ void Effects_Buffer::config( const config_t& cfg )
}
}
}
if ( buf_count < max_buf_count ) // if center_only
{
for(int i=0; i<max_voices; i++)
@ -263,7 +261,7 @@ Effects_Buffer::channel_t Effects_Buffer::channel( int i, int type )
}
return chan_types [(i%max_voices)*chan_types_count+out];
}
void Effects_Buffer::end_frame( blip_time_t clock_count )
{
int bufs_used = 0;
@ -284,7 +282,7 @@ void Effects_Buffer::end_frame( blip_time_t clock_count )
}
bufs_used = 0;
}
effects_enabled = config_.effects_enabled;
}
@ -307,13 +305,13 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
{
int active_bufs = buf_count_per_voice;
long count = remain;
// optimizing mixing to skip any channels which had nothing added
if ( effect_remain )
{
if ( count > effect_remain )
count = effect_remain;
if ( stereo_remain )
{
mix_enhanced( out, count );
@ -327,25 +325,25 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
else if ( stereo_remain )
{
mix_stereo( out, count );
active_bufs = 3;
active_bufs = 3;
}
else
{
mix_mono( out, count );
active_bufs = 1;
active_bufs = 1;
}
out += count * n_channels;
remain -= count;
stereo_remain -= count;
if ( stereo_remain < 0 )
stereo_remain = 0;
effect_remain -= count;
if ( effect_remain < 0 )
effect_remain = 0;
// skip the output from any buffers that didn't contribute to the sound output
// during this frame (e.g. if we only render mono then only the very first buf
// is 'active')
@ -360,56 +358,52 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
}
}
}
return total_samples * n_channels;
}
void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count )
void Effects_Buffer::mix_mono( blip_sample_t* out_, int32_t count )
{
for(int i=0; i<max_voices; i++)
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( c, bufs [i*max_buf_count+0] );
// unrolled loop
for ( blargg_long n = count >> 1; n; --n )
for ( int32_t n = count >> 1; n; --n )
{
blargg_long cs0 = BLIP_READER_READ( c );
int32_t cs0 = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass );
blargg_long cs1 = BLIP_READER_READ( c );
int32_t cs1 = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass );
if ( (int16_t) cs0 != cs0 )
cs0 = 0x7FFF - (cs0 >> 24);
((uint32_t*) out) [i*2+0] = ((uint16_t) cs0) | (uint16_t(cs0) << 16);
((uint32_t*) out) [i] = ((uint16_t) cs0) | (uint16_t(cs0) << 16);
if ( (int16_t) cs1 != cs1 )
cs1 = 0x7FFF - (cs1 >> 24);
((uint32_t*) out) [i*2+1] = ((uint16_t) cs1) | (uint16_t(cs1) << 16);
((uint32_t*) out) [i+max_voices] = ((uint16_t) cs1) | (uint16_t(cs1) << 16);
out += max_voices*4;
}
if ( count & 1 )
{
int s = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass );
if ( (int16_t) s != s )
s = 0x7FFF - (s >> 24);
out [i*2+0] = s;
out [i*2+1] = s;
if ( (int16_t) s != s )
{
s = 0x7FFF - (s >> 24);
out [i*2+0] = s;
out [i*2+1] = s;
}
}
BLIP_READER_END( c, bufs [i*max_buf_count+0] );
}
}
void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long frames )
void Effects_Buffer::mix_stereo( blip_sample_t* out_, int32_t frames )
{
for(int i=0; i<max_voices; i++)
{
@ -428,27 +422,27 @@ void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long frames )
int right = cs + BLIP_READER_READ( r );
BLIP_READER_NEXT( l, bass );
BLIP_READER_NEXT( r, bass );
if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24);
out [i*2+0] = left;
out [i*2+1] = right;
out += max_voices*2;
}
BLIP_READER_END( r, bufs [i*max_buf_count+2] );
BLIP_READER_END( l, bufs [i*max_buf_count+1] );
BLIP_READER_END( c, bufs [i*max_buf_count+0] );
}
}
void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames )
void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, int32_t frames )
{
for(int i=0; i<max_voices; i++)
{
@ -457,48 +451,48 @@ void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames
BLIP_READER_BEGIN( center, bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] );
blip_sample_t* const reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos[i];
int count = frames;
while ( count-- )
{
int sum1_s = BLIP_READER_READ( sq1 );
int sum2_s = BLIP_READER_READ( sq2 );
BLIP_READER_NEXT( sq1, bass );
BLIP_READER_NEXT( sq2, bass );
int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
FMUL( sum2_s, chans.pan_2_levels [0] ) +
reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
FMUL( sum2_s, chans.pan_2_levels [1] ) +
reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
fixed_t reverb_level = chans.reverb_level;
reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level );
reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level );
reverb_pos = (reverb_pos + 2) & reverb_mask;
int sum3_s = BLIP_READER_READ( center );
BLIP_READER_NEXT( center, bass );
int left = new_reverb_l + sum3_s + FMUL( chans.echo_level,
echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
int right = new_reverb_r + sum3_s + FMUL( chans.echo_level,
echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
echo_buf [echo_pos] = sum3_s;
echo_pos = (echo_pos + 1) & echo_mask;
if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24);
@ -508,14 +502,14 @@ void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames
}
this->reverb_pos[i] = reverb_pos;
this->echo_pos[i] = echo_pos;
BLIP_READER_END( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_END( sq2, bufs [i*max_buf_count+1] );
BLIP_READER_END( center, bufs [i*max_buf_count+2] );
}
}
void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames )
void Effects_Buffer::mix_enhanced( blip_sample_t* out_, int32_t frames )
{
for(int i=0; i<max_voices; i++)
{
@ -528,54 +522,54 @@ void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames )
BLIP_READER_BEGIN( r2, bufs [i*max_buf_count+6] );
BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] );
blip_sample_t* const reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos[i];
int count = frames;
while ( count-- )
{
int sum1_s = BLIP_READER_READ( sq1 );
int sum2_s = BLIP_READER_READ( sq2 );
BLIP_READER_NEXT( sq1, bass );
BLIP_READER_NEXT( sq2, bass );
int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) +
reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) +
reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
BLIP_READER_NEXT( l1, bass );
BLIP_READER_NEXT( r1, bass );
fixed_t reverb_level = chans.reverb_level;
reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level );
reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level );
reverb_pos = (reverb_pos + 2) & reverb_mask;
int sum3_s = BLIP_READER_READ( center );
BLIP_READER_NEXT( center, bass );
int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level,
echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
int right = new_reverb_r + sum3_s + BLIP_READER_READ( r2 ) + FMUL( chans.echo_level,
echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
BLIP_READER_NEXT( l2, bass );
BLIP_READER_NEXT( r2, bass );
echo_buf [echo_pos] = sum3_s;
echo_pos = (echo_pos + 1) & echo_mask;
if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24);
@ -586,7 +580,7 @@ void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames )
}
this->reverb_pos[i] = reverb_pos;
this->echo_pos[i] = echo_pos;
BLIP_READER_END( l1, bufs [i*max_buf_count+3] );
BLIP_READER_END( r1, bufs [i*max_buf_count+4] );
BLIP_READER_END( l2, bufs [i*max_buf_count+5] );

View file

@ -16,7 +16,7 @@ public:
// If center_only is true, only center buffers are created and
// less memory is used.
Effects_Buffer( int nVoices = 1, bool center_only = false );
// Channel Effect Center Pan
// ---------------------------------
// 0,5 reverb pan_1
@ -24,7 +24,7 @@ public:
// 2,7 echo -
// 3 echo -
// 4 echo -
// Channel configuration
struct config_t {
double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right
@ -37,14 +37,14 @@ public:
bool effects_enabled; // if false, use optimized simple mixer
config_t();
};
// Set configuration of buffer
virtual void config( const config_t& );
void set_depth( double );
public:
~Effects_Buffer();
blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length );
blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ) noexcept;
void clock_rate( long );
void bass_freq( int );
void clear();
@ -64,12 +64,12 @@ private:
long effect_remain;
int buf_count;
bool effects_enabled;
std::vector<std::vector<blip_sample_t> > reverb_buf;
std::vector<std::vector<blip_sample_t> > echo_buf;
std::vector<int> reverb_pos;
std::vector<int> echo_pos;
struct {
fixed_t pan_1_levels [2];
fixed_t pan_2_levels [2];
@ -80,11 +80,11 @@ private:
int reverb_delay_r;
fixed_t reverb_level;
} chans;
void mix_mono( blip_sample_t*, blargg_long );
void mix_stereo( blip_sample_t*, blargg_long );
void mix_enhanced( blip_sample_t*, blargg_long );
void mix_mono_enhanced( blip_sample_t*, blargg_long );
void mix_mono( blip_sample_t*, int32_t );
void mix_stereo( blip_sample_t*, int32_t );
void mix_enhanced( blip_sample_t*, int32_t );
void mix_mono_enhanced( blip_sample_t*, int32_t );
};
#endif

View file

@ -31,7 +31,7 @@ static void gen_sinc( double rolloff, int width, double offset, double 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-- )
{
@ -45,7 +45,7 @@ static void gen_sinc( double rolloff, int width, double offset, double spacing,
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;
@ -83,11 +83,11 @@ blargg_err_t Fir_Resampler_::buffer_size( int new_size )
clear();
return 0;
}
double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain )
{
ratio_ = new_factor;
double fstep = 0.0;
{
double least_error = 2;
@ -106,14 +106,14 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai
}
}
}
skip_bits = 0;
step = stereo * (int) floor( fstep );
ratio_ = fstep;
fstep = fmod( fstep, 1.0 );
double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
double pos = 0.0;
input_per_cycle = 0;
@ -122,7 +122,7 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai
gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter,
double (0x7FFF * gain * filter),
(int) width_, impulses + i * width_ );
pos += fstep;
input_per_cycle += step;
if ( pos >= 0.9999999 )
@ -132,16 +132,16 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai
input_per_cycle++;
}
}
clear();
return ratio_;
}
int Fir_Resampler_::input_needed( blargg_long output_count ) const
int Fir_Resampler_::input_needed( int32_t output_count ) const
{
blargg_long input_count = 0;
int32_t input_count = 0;
unsigned long skip = skip_bits >> imp_phase;
int remain = res - imp_phase;
while ( (output_count -= 2) > 0 )
@ -155,20 +155,20 @@ int Fir_Resampler_::input_needed( blargg_long output_count ) const
}
output_count -= 2;
}
long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]);
if ( input_extra < 0 )
input_extra = 0;
return input_extra;
}
int Fir_Resampler_::avail_( blargg_long input_count ) const
int Fir_Resampler_::avail_( int32_t input_count ) const
{
int cycle_count = input_count / input_per_cycle;
int output_count = cycle_count * res * stereo;
input_count -= cycle_count * input_per_cycle;
blargg_ulong skip = skip_bits >> imp_phase;
uint32_t skip = skip_bits >> imp_phase;
int remain = res - imp_phase;
while ( input_count >= 0 )
{
@ -188,12 +188,14 @@ int Fir_Resampler_::skip_input( long count )
{
int remain = write_pos - buf.begin();
int max_count = remain - width_ * stereo;
if ( max_count < 0 )
max_count = 0;
if ( count > max_count )
count = max_count;
remain -= count;
write_pos = &buf [remain];
memmove( buf.begin(), &buf [count], remain * sizeof buf [0] );
return count;
}

View file

@ -9,50 +9,50 @@
class Fir_Resampler_ {
public:
// Use Fir_Resampler<width> (below)
// Set input/output resampling ratio and optionally low-pass rolloff and gain.
// Returns actual ratio used (rounded to internal precision).
double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 );
// Current input/output ratio
double ratio() const { return ratio_; }
// Input
typedef short sample_t;
// Resize and clear input buffer
blargg_err_t buffer_size( int );
// Clear input buffer. At least two output samples will be available after
// two input samples are written.
void clear();
// Number of input samples that can be written
int max_write() const { return buf.end() - write_pos; }
// Pointer to place to write input samples
sample_t* buffer() { return write_pos; }
// Notify resampler that 'count' input samples have been written
void write( long count );
// Number of input samples in buffer
int written() const { return write_pos - &buf [write_offset]; }
// Skip 'count' input samples. Returns number of samples actually skipped.
int skip_input( long count );
// Output
// Number of extra input samples needed until 'count' output samples are available
int input_needed( blargg_long count ) const;
int input_needed( int32_t count ) const;
// Number of output samples available
int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); }
public:
~Fir_Resampler_();
protected:
@ -64,14 +64,14 @@ protected:
int imp_phase;
int const width_;
int const write_offset;
blargg_ulong skip_bits;
uint32_t skip_bits;
int step;
int input_per_cycle;
double ratio_;
sample_t* impulses;
Fir_Resampler_( int width, sample_t* );
int avail_( blargg_long input_count ) const;
int avail_( int32_t input_count ) const;
};
// Width is number of points in FIR. Must be even and 4 or more. More points give
@ -82,10 +82,10 @@ class Fir_Resampler : public Fir_Resampler_ {
short impulses [max_res] [width];
public:
Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { }
// Read at most 'count' samples. Returns number of samples actually read.
typedef short sample_t;
int read( sample_t* out, blargg_long count );
int read( sample_t* out, int32_t count );
};
// End of public interface
@ -97,24 +97,24 @@ inline void Fir_Resampler_::write( long count )
}
template<int width>
int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
int Fir_Resampler<width>::read( sample_t* out_begin, int32_t count )
{
sample_t* out = out_begin;
const sample_t* in = buf.begin();
sample_t* end_pos = write_pos;
blargg_ulong skip = skip_bits >> imp_phase;
uint32_t skip = skip_bits >> imp_phase;
sample_t const* imp = impulses [imp_phase];
int remain = res - imp_phase;
int const step = this->step;
count >>= 1;
// Resampling can add noise so don't actually do it if we've matched sample
// rate
const double ratio1 = ratio() - 1.0;
const bool should_resample =
( ratio1 >= 0 ? ratio1 : -ratio1 ) >= 0.00001;
if ( end_pos - in >= width * stereo )
{
end_pos -= width * stereo;
@ -123,7 +123,7 @@ int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
count--;
if ( count < 0 )
break;
if( !should_resample )
{
out [0] = static_cast<sample_t>( in [0] );
@ -132,11 +132,11 @@ int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
else
{
// accumulate in extended precision
blargg_long l = 0;
blargg_long r = 0;
int32_t l = 0;
int32_t r = 0;
const sample_t* i = in;
for ( int n = width / 2; n; --n )
{
int pt0 = imp [0];
@ -148,38 +148,38 @@ int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
r += pt1 * i [3];
i += 4;
}
remain--;
l >>= 15;
r >>= 15;
in += (skip * stereo) & stereo;
skip >>= 1;
if ( !remain )
{
imp = impulses [0];
skip = skip_bits;
remain = res;
}
out [0] = (sample_t) l;
out [1] = (sample_t) r;
}
in += step;
out += 2;
}
while ( in <= end_pos );
}
imp_phase = res - remain;
int left = write_pos - in;
write_pos = &buf [left];
memmove( buf.begin(), in, left * sizeof *in );
return out - out_begin;
}

View file

@ -18,24 +18,26 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
unsigned const vol_reg = 0xFF24;
unsigned const status_reg = 0xFF26;
static unsigned const vol_reg = 0xFF24;
static unsigned const status_reg = 0xFF26;
using std::min;
using std::max;
Gb_Apu::Gb_Apu()
{
memset(regs, 0, sizeof(regs));
square1.synth = &square_synth;
square2.synth = &square_synth;
wave.synth = &other_synth;
noise.synth = &other_synth;
oscs [0] = &square1;
oscs [1] = &square2;
oscs [2] = &wave;
oscs [3] = &noise;
for ( int i = 0; i < osc_count; i++ )
{
Gb_Osc& osc = *oscs [i];
@ -46,7 +48,7 @@ Gb_Apu::Gb_Apu()
osc.outputs [2] = 0;
osc.outputs [3] = 0;
}
set_tempo( 1.0 );
volume( 1.0 );
reset();
@ -108,21 +110,21 @@ void Gb_Apu::reset()
next_frame_time = 0;
last_time = 0;
frame_count = 0;
square1.reset();
square2.reset();
wave.reset();
noise.reset();
noise.bits = 1;
wave.wave_pos = 0;
// avoid click at beginning
regs [vol_reg - start_addr] = 0x77;
update_volume();
regs [status_reg - start_addr] = 0x01; // force power
write_register( 0, status_reg, 0x00 );
static unsigned char const initial_wave [] = {
0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA
@ -135,13 +137,13 @@ void Gb_Apu::run_until( blip_time_t end_time )
require( end_time >= last_time ); // end_time must not be before previous time
if ( end_time == last_time )
return;
while ( true )
{
blip_time_t time = next_frame_time;
if ( time > end_time )
time = end_time;
// run oscillators
for ( int i = 0; i < osc_count; ++i )
{
@ -163,18 +165,18 @@ void Gb_Apu::run_until( blip_time_t end_time )
}
}
last_time = time;
if ( time == end_time )
break;
next_frame_time += frame_period;
// 256 Hz actions
square1.clock_length();
square2.clock_length();
wave.clock_length();
noise.clock_length();
frame_count = (frame_count + 1) & 3;
if ( frame_count == 0 )
{
@ -183,7 +185,7 @@ void Gb_Apu::run_until( blip_time_t end_time )
square2.clock_envelope();
noise.clock_envelope();
}
if ( frame_count & 1 )
square1.clock_sweep(); // 128 Hz action
}
@ -193,10 +195,10 @@ void Gb_Apu::end_frame( blip_time_t end_time )
{
if ( end_time > last_time )
run_until( end_time );
assert( next_frame_time >= end_time );
next_frame_time -= end_time;
assert( last_time >= end_time );
last_time -= end_time;
}
@ -204,16 +206,16 @@ void Gb_Apu::end_frame( blip_time_t end_time )
void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
{
require( (unsigned) data < 0x100 );
int reg = addr - start_addr;
if ( (unsigned) reg >= register_count )
return;
run_until( time );
int old_reg = regs [reg];
regs [reg] = data;
if ( addr < vol_reg )
{
write_osc( reg / 5, reg, data );
@ -229,22 +231,22 @@ void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
if ( amp && osc.enabled && osc.output )
other_synth.offset( time, -amp, osc.output );
}
if ( wave.outputs [3] )
other_synth.offset( time, 30, wave.outputs [3] );
update_volume();
if ( wave.outputs [3] )
other_synth.offset( time, -30, wave.outputs [3] );
// oscs will update with new amplitude when next run
}
else if ( addr == 0xFF25 || addr == status_reg )
{
int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0;
int flags = regs [0xFF25 - start_addr] & mask;
// left/right assignments
for ( int i = 0; i < osc_count; i++ )
{
@ -262,7 +264,7 @@ void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
other_synth.offset( time, -amp, old_output );
}
}
if ( addr == status_reg && data != old_reg )
{
if ( !(data & 0x80) )
@ -290,11 +292,11 @@ void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
int Gb_Apu::read_register( blip_time_t time, unsigned addr )
{
run_until( time );
int index = addr - start_addr;
require( (unsigned) index < register_count );
int data = regs [index];
if ( addr == status_reg )
{
data = (data & 0x80) | 0x70;
@ -305,6 +307,6 @@ int Gb_Apu::read_register( blip_time_t time, unsigned addr )
data |= 1 << i;
}
}
return data;
}

View file

@ -8,62 +8,62 @@
class Gb_Apu {
public:
// Set overall volume of all oscillators, where 1.0 is full volume
void volume( double );
// Set treble equalization
void treble_eq( const blip_eq_t& );
// Outputs can be assigned to a single buffer for mono output, or to three
// buffers for stereo output (using Stereo_Buffer to do the mixing).
// Assign all oscillator outputs to specified buffer(s). If buffer
// is NULL, silences all oscillators.
void output( Blip_Buffer* mono );
void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Assign single oscillator output to buffer(s). Valid indicies are 0 to 3,
// which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL,
// silences oscillator.
enum { osc_count = 4 };
static const int osc_count = 4;
void osc_output( int index, Blip_Buffer* mono );
void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Reset oscillators and internal state
void reset();
// Reads and writes at addr must satisfy start_addr <= addr <= end_addr
enum { start_addr = 0xFF10 };
enum { end_addr = 0xFF3F };
enum { register_count = end_addr - start_addr + 1 };
static const unsigned int start_addr = 0xFF10;
static const unsigned int end_addr = 0xFF3F;
static const unsigned int register_count = end_addr - start_addr + 1;
// Write 'data' to address at specified time
void write_register( blip_time_t, unsigned addr, int data );
// Read from address at specified time
int read_register( blip_time_t, unsigned addr );
// Run all oscillators up to specified time, end current time frame, then
// start a new frame at time 0.
void end_frame( blip_time_t );
void set_tempo( double );
public:
Gb_Apu();
private:
// noncopyable
Gb_Apu( const Gb_Apu& );
Gb_Apu& operator = ( const Gb_Apu& );
Gb_Osc* oscs [osc_count];
blip_time_t next_frame_time;
blip_time_t last_time;
blip_time_t frame_period;
double volume_unit;
int frame_count;
Gb_Square square1;
Gb_Square square2;
Gb_Wave wave;
@ -71,14 +71,14 @@ private:
uint8_t regs [register_count];
Gb_Square::Synth square_synth; // used by squares
Gb_Wave::Synth other_synth; // used by wave and noise
void update_volume();
void run_until( blip_time_t );
void write_osc( int index, int reg, int data );
};
inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); }
inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); }
inline void Gb_Apu::volume( double vol )

View file

@ -49,22 +49,22 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
inline void Gb_Cpu::set_code_page( int i, uint8_t* p )
{
state->code_map [i] = p - PAGE_OFFSET( i * (blargg_long) page_size );
state->code_map [i] = p - PAGE_OFFSET( i * (int32_t) page_size );
}
void Gb_Cpu::reset( void* unmapped )
{
check( state == &state_ );
state = &state_;
state_.remain = 0;
for ( int i = 0; i < page_count + 1; i++ )
set_code_page( i, (uint8_t*) unmapped );
memset( &r, 0, sizeof r );
//interrupts_enabled = false;
blargg_verify_byte_order();
}
@ -73,7 +73,7 @@ void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data )
// address range must begin and end on page boundaries
require( start % page_size == 0 );
require( size % page_size == 0 );
unsigned first_page = start / page_size;
for ( unsigned i = size / page_size; i--; )
set_code_page( first_page + i, (uint8_t*) data + i * page_size );
@ -84,52 +84,53 @@ void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data )
#define READ_FAST( addr, out ) CPU_READ_FAST( this, (addr), s.remain, out )
#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )])
unsigned const z_flag = 0x80;
unsigned const n_flag = 0x40;
unsigned const h_flag = 0x20;
unsigned const c_flag = 0x10;
bool Gb_Cpu::run( blargg_long cycle_count )
static unsigned const z_flag = 0x80;
static unsigned const n_flag = 0x40;
static unsigned const h_flag = 0x20;
static unsigned const c_flag = 0x10;
bool Gb_Cpu::run( int32_t cycle_count )
{
state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr;
state_.remain = uint32_t (cycle_count + clocks_per_instr) / clocks_per_instr;
state_t s;
this->state = &s;
memcpy( &s, &this->state_, sizeof s );
#if BLARGG_BIG_ENDIAN
#define R8( n ) (r8_ [n])
#define R8( n ) (r8_ [n])
#elif BLARGG_LITTLE_ENDIAN
#define R8( n ) (r8_ [(n) ^ 1])
#define R8( n ) (r8_ [(n) ^ 1])
#else
#error "Byte order of CPU must be known"
#endif
union {
core_regs_t rg; // individual registers
struct {
uint16_t bc, de, hl, unused; // pairs
} rp;
uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence)
uint16_t r16 [4]; // indexed pairs
};
static_assert( sizeof rg == 8 && sizeof rp == 8, "Unhandled word size" );
rg = r;
unsigned pc = r.pc;
unsigned sp = r.sp;
unsigned flags = r.flags;
loop:
check( (unsigned long) pc < 0x10000 );
check( (unsigned long) sp < 0x10000 );
check( (flags & ~0xF0) == 0 );
uint8_t const* instr = s.code_map [pc >> page_shift];
unsigned op;
// TODO: eliminate this special case
#if BLARGG_NONPORTABLE
op = instr [pc];
@ -140,19 +141,19 @@ loop:
op = *instr++;
pc++;
#endif
#define GET_ADDR() GET_LE16( instr )
if ( !--s.remain )
goto stop;
unsigned data;
data = *instr;
#ifdef GB_CPU_LOG_H
gb_cpu_log( "new", pc - 1, op, data, instr [1] );
#endif
switch ( op )
{
@ -170,44 +171,44 @@ loop:
case 0x20: // JR NZ
BRANCH( !(flags & z_flag) )
case 0x21: // LD HL,IMM (common)
rp.hl = GET_ADDR();
pc += 2;
goto loop;
case 0x28: // JR Z
BRANCH( flags & z_flag )
{
unsigned temp;
case 0xF0: // LD A,(0xFF00+imm)
temp = data | 0xFF00;
pc++;
goto ld_a_ind_comm;
case 0xF2: // LD A,(0xFF00+C)
temp = rg.c | 0xFF00;
goto ld_a_ind_comm;
case 0x0A: // LD A,(BC)
temp = rp.bc;
goto ld_a_ind_comm;
case 0x3A: // LD A,(HL-)
temp = rp.hl;
rp.hl = temp - 1;
goto ld_a_ind_comm;
case 0x1A: // LD A,(DE)
temp = rp.de;
goto ld_a_ind_comm;
case 0x2A: // LD A,(HL+) (common)
temp = rp.hl;
rp.hl = temp + 1;
goto ld_a_ind_comm;
case 0xFA: // LD A,IND16 (common)
temp = GET_ADDR();
pc += 2;
@ -215,11 +216,11 @@ loop:
READ_FAST( temp, rg.a );
goto loop;
}
case 0xBE: // CMP (HL)
data = READ( rp.hl );
goto cmp_comm;
case 0xB8: // CMP B
case 0xB9: // CMP C
case 0xBA: // CMP D
@ -228,7 +229,7 @@ loop:
case 0xBD: // CMP L
data = R8( op & 7 );
goto cmp_comm;
case 0xFE: // CMP IMM
pc++;
cmp_comm:
@ -254,7 +255,7 @@ loop:
READ_FAST( addr, R8( (op >> 3) & 7 ) );
goto loop;
}
case 0xC4: // CNZ (next-most-common)
pc += 2;
if ( flags & z_flag )
@ -270,7 +271,7 @@ loop:
sp = (sp - 1) & 0xFFFF;
WRITE( sp, data & 0xFF );
goto loop;
case 0xC8: // RNZ (next-most-common)
if ( !(flags & z_flag) )
goto loop;
@ -281,7 +282,7 @@ loop:
pc += 0x100 * READ( sp + 1 );
sp = (sp + 2) & 0xFFFF;
goto loop;
case 0x00: // NOP
case 0x40: // LD B,B
case 0x49: // LD C,C
@ -291,14 +292,14 @@ loop:
case 0x6D: // LD L,L
case 0x7F: // LD A,A
goto loop;
// CB Instructions
case 0xCB:
pc++;
// now data is the opcode
switch ( data ) {
{
int temp;
case 0x46: // BIT b,(HL)
@ -314,7 +315,7 @@ loop:
READ_FAST( addr, temp );
goto bit_comm;
}
case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r
case 0x44: case 0x45: case 0x47: case 0x48:
case 0x49: case 0x4A: case 0x4B: case 0x4C:
@ -337,7 +338,7 @@ loop:
flags ^= (temp << bit) & z_flag;
goto loop;
}
case 0x86: // RES b,(HL)
case 0x8E:
case 0x96:
@ -362,7 +363,7 @@ loop:
WRITE( rp.hl, temp | bit );
goto loop;
}
case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r
case 0xC4: case 0xC5: case 0xC7: case 0xC8:
case 0xC9: case 0xCA: case 0xCB: case 0xCC:
@ -396,13 +397,13 @@ loop:
case 0xBB: case 0xBC: case 0xBD: case 0xBF:
R8( data & 7 ) &= ~(1 << ((data >> 3) & 7));
goto loop;
{
int temp;
case 0x36: // SWAP (HL)
temp = READ( rp.hl );
goto swap_comm;
case 0x30: // SWAP B
case 0x31: // SWAP C
case 0x32: // SWAP D
@ -416,7 +417,7 @@ loop:
flags = 0;
goto shift_comm;
}
// Shift/Rotate
case 0x06: // RLC (HL)
@ -424,13 +425,13 @@ loop:
case 0x26: // SLA (HL)
op = READ( rp.hl );
goto rl_comm;
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A
case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A
case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A
op = R8( data & 7 );
goto rl_comm;
case 0x3E: // SRL (HL)
data += 0x10; /* fallthrough */ // bump up to 0x4n to avoid preserving sign bit
case 0x1E: // RR (HL)
@ -438,7 +439,7 @@ loop:
case 0x2E: // SRA (HL)
op = READ( rp.hl );
goto rr_comm;
case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A
data += 0x10; /* fallthrough */ // bump up to 0x4n
case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A
@ -446,7 +447,7 @@ loop:
case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A
op = R8( data & 7 );
goto rr_comm;
} // CB op
assert( false ); // unhandled CB op
// fallthrough
@ -463,7 +464,7 @@ loop:
op |= op >> 8;
// SLA doesn't fill lower bit
goto shift_comm;
case 0x0F: // RRCA
case 0x1F: // RRA
data = op;
@ -516,7 +517,7 @@ loop:
data++;
WRITE( data, sp >> 8 );
goto loop;
case 0xF9: // LD SP,HL
sp = rp.hl;
goto loop;
@ -525,20 +526,20 @@ loop:
sp = GET_ADDR();
pc += 2;
goto loop;
case 0x01: // LD BC,IMM
case 0x11: // LD DE,IMM
r16 [op >> 4] = GET_ADDR();
pc += 2;
goto loop;
{
unsigned temp;
case 0xE0: // LD (0xFF00+imm),A
temp = data | 0xFF00;
pc++;
goto write_data_rg_a;
case 0xE2: // LD (0xFF00+C),A
temp = rg.c | 0xFF00;
goto write_data_rg_a;
@ -547,20 +548,20 @@ loop:
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;
@ -568,42 +569,42 @@ loop:
WRITE( temp, rg.a );
goto loop;
}
case 0x06: // LD B,IMM
rg.b = data;
pc++;
goto loop;
case 0x0E: // LD C,IMM
rg.c = data;
pc++;
goto loop;
case 0x16: // LD D,IMM
rg.d = data;
pc++;
goto loop;
case 0x1E: // LD E,IMM
rg.e = data;
pc++;
goto loop;
case 0x26: // LD H,IMM
rg.h = data;
pc++;
goto loop;
case 0x2E: // LD L,IMM
rg.l = data;
pc++;
goto loop;
case 0x36: // LD (HL),IMM
WRITE( rp.hl, data );
pc++;
goto loop;
case 0x3E: // LD A,IMM
rg.a = data;
pc++;
@ -616,7 +617,7 @@ loop:
case 0x23: // INC HL
r16 [op >> 4]++;
goto loop;
case 0x33: // INC SP
sp = (sp + 1) & 0xFFFF;
goto loop;
@ -626,18 +627,18 @@ loop:
case 0x2B: // DEC HL
r16 [op >> 4]--;
goto loop;
case 0x3B: // DEC SP
sp = (sp - 1) & 0xFFFF;
goto loop;
case 0x34: // INC (HL)
op = rp.hl;
data = READ( op );
data++;
WRITE( op, data & 0xFF );
goto inc_comm;
case 0x04: // INC B
case 0x0C: // INC C (common)
case 0x14: // INC D
@ -650,14 +651,14 @@ loop:
inc_comm:
flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag);
goto loop;
case 0x35: // DEC (HL)
op = rp.hl;
data = READ( op );
data--;
WRITE( op, data & 0xFF );
goto dec_comm;
case 0x05: // DEC B
case 0x0D: // DEC C
case 0x15: // DEC D
@ -678,9 +679,9 @@ loop:
// Add 16-bit
{
blargg_ulong temp; // need more than 16 bits for carry
uint32_t temp; // need more than 16 bits for carry
unsigned prev;
case 0xF8: // LD HL,SP+imm
temp = int8_t (data); // sign-extend to 16 bits
pc++;
@ -688,7 +689,7 @@ loop:
temp += sp;
prev = sp;
goto add_16_hl;
case 0xE8: // ADD SP,IMM
temp = int8_t (data); // sign-extend to 16 bits
pc++;
@ -701,7 +702,7 @@ loop:
case 0x39: // ADD HL,SP
temp = sp;
goto add_hl_comm;
case 0x09: // ADD HL,BC
case 0x19: // ADD HL,DE
case 0x29: // ADD HL,HL
@ -717,11 +718,11 @@ loop:
flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag;
goto loop;
}
case 0x86: // ADD (HL)
data = READ( rp.hl );
goto add_comm;
case 0x80: // ADD B
case 0x81: // ADD C
case 0x82: // ADD D
@ -731,7 +732,7 @@ loop:
case 0x87: // ADD A
data = R8( op & 7 );
goto add_comm;
case 0xC6: // ADD IMM
pc++;
add_comm:
@ -750,7 +751,7 @@ loop:
case 0x8E: // ADC (HL)
data = READ( rp.hl );
goto adc_comm;
case 0x88: // ADC B
case 0x89: // ADC C
case 0x8A: // ADC D
@ -760,7 +761,7 @@ loop:
case 0x8F: // ADC A
data = R8( op & 7 );
goto adc_comm;
case 0xCE: // ADC IMM
pc++;
adc_comm:
@ -771,7 +772,7 @@ loop:
case 0x96: // SUB (HL)
data = READ( rp.hl );
goto sub_comm;
case 0x90: // SUB B
case 0x91: // SUB C
case 0x92: // SUB D
@ -781,7 +782,7 @@ loop:
case 0x97: // SUB A
data = R8( op & 7 );
goto sub_comm;
case 0xD6: // SUB IMM
pc++;
sub_comm:
@ -793,7 +794,7 @@ loop:
case 0x9E: // SBC (HL)
data = READ( rp.hl );
goto sbc_comm;
case 0x98: // SBC B
case 0x99: // SBC C
case 0x9A: // SBC D
@ -803,7 +804,7 @@ loop:
case 0x9F: // SBC A
data = R8( op & 7 );
goto sbc_comm;
case 0xDE: // SBC IMM
pc++;
sbc_comm:
@ -821,7 +822,7 @@ loop:
case 0xA5: // AND L
data = R8( op & 7 );
goto and_comm;
case 0xA6: // AND (HL)
data = READ( rp.hl );
pc--; // FALLTHRU
@ -841,7 +842,7 @@ loop:
case 0xB5: // OR L
data = R8( op & 7 );
goto or_comm;
case 0xB6: // OR (HL)
data = READ( rp.hl );
pc--; // FALLTHRU
@ -861,7 +862,7 @@ loop:
case 0xAD: // XOR L
data = R8( op & 7 );
goto xor_comm;
case 0xAE: // XOR (HL)
data = READ( rp.hl );
pc--; // FALLTHRU
@ -873,7 +874,7 @@ loop:
data--;
flags = (data >> 1) & z_flag;
goto loop;
case 0xAF: // XOR A
rg.a = 0;
flags = z_flag;
@ -893,25 +894,25 @@ loop:
flags = rg.a & 0xF0;
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
data = (rg.a << 8) | flags;
goto push;
// Flow control
case 0xFF:
if ( pc == idle_addr + 1 )
goto stop;
@ -921,19 +922,19 @@ loop:
data = pc;
pc = (op & 0x38) + rst_base;
goto push;
case 0xCC: // CZ
pc += 2;
if ( flags & z_flag )
goto call;
goto loop;
case 0xD4: // CNC
pc += 2;
if ( !(flags & c_flag) )
goto call;
goto loop;
case 0xDC: // CC
pc += 2;
if ( flags & c_flag )
@ -943,17 +944,17 @@ loop:
case 0xD9: // RETI
//interrupts_enabled = 1;
goto ret;
case 0xC0: // RZ
if ( !(flags & z_flag) )
goto ret;
goto loop;
case 0xD0: // RNC
if ( !(flags & c_flag) )
goto ret;
goto loop;
case 0xD8: // RC
if ( flags & c_flag )
goto ret;
@ -961,13 +962,13 @@ loop:
case 0x18: // JR
BRANCH( true )
case 0x30: // JR NC
BRANCH( !(flags & c_flag) )
case 0x38: // JR C
BRANCH( flags & c_flag )
case 0xE9: // JP_HL
pc = rp.hl;
goto loop;
@ -975,13 +976,13 @@ loop:
case 0xC3: // JP (next-most-common)
pc = GET_ADDR();
goto loop;
case 0xC2: // JP NZ
pc += 2;
if ( !(flags & z_flag) )
goto jp_taken;
goto loop;
case 0xCA: // JP Z (most common)
pc += 2;
if ( !(flags & z_flag) )
@ -990,13 +991,13 @@ loop:
pc -= 2;
pc = GET_ADDR();
goto loop;
case 0xD2: // JP NC
pc += 2;
if ( !(flags & c_flag) )
goto jp_taken;
goto loop;
case 0xDA: // JP C
pc += 2;
if ( flags & c_flag )
@ -1038,21 +1039,21 @@ loop:
s.remain++;
goto stop;
}
// If this fails then the case above is missing an opcode
assert( false );
stop:
pc--;
// copy state back
STATIC_CAST(core_regs_t&,r) = rg;
r.pc = pc;
r.sp = sp;
r.flags = flags;
this->state = &state_;
memcpy( &this->state_, &s, sizeof this->state_ );
return s.remain > 0;
}

View file

@ -15,17 +15,17 @@ class Gb_Cpu {
public:
// Clear registers and map all pages to unmapped
void reset( void* unmapped = 0 );
// Map code memory (memory accessed via the program counter). Start and size
// must be multiple of page_size.
enum { page_size = 0x2000 };
void map_code( gb_addr_t start, unsigned size, void* code );
uint8_t* get_code( gb_addr_t );
// Push a byte on the stack
void push_byte( int );
// Game Boy Z80 registers. *Not* kept updated during a call to run().
struct core_regs_t {
#if BLARGG_BIG_ENDIAN
@ -34,32 +34,32 @@ public:
uint8_t c, b, e, d, l, h, a, flags;
#endif
};
struct registers_t : core_regs_t {
long pc; // more than 16 bits to allow overflow detection
uint16_t sp;
};
registers_t r;
// Interrupt enable flag set by EI and cleared by DI
//bool interrupts_enabled; // unused
// Base address for RST vectors (normally 0)
gb_addr_t rst_base;
// If CPU executes opcode 0xFF at this address, it treats as illegal instruction
enum { idle_addr = 0xF00D };
// Run CPU for at least 'count' cycles and return false, or return true if
// illegal instruction is encountered.
bool run( blargg_long count );
bool run( int32_t count );
// Number of clock cycles remaining for most recent run() call
blargg_long remain() const { return state->remain * clocks_per_instr; }
int32_t remain() const { return state->remain * clocks_per_instr; }
// Can read this many bytes past end of a page
enum { cpu_padding = 8 };
public:
Gb_Cpu() : rst_base( 0 ) { state = &state_; }
enum { page_shift = 13 };
@ -68,14 +68,14 @@ private:
// noncopyable
Gb_Cpu( const Gb_Cpu& );
Gb_Cpu& operator = ( const Gb_Cpu& );
struct state_t {
uint8_t* code_map [page_count + 1];
blargg_long remain;
int32_t remain;
};
state_t* state; // points to state_ or a local copy within run()
state_t state_;
void set_code_page( int, uint8_t* );
};

View file

@ -54,12 +54,12 @@ bool Gb_Env::write_register( int reg, int data )
case 1:
length = 64 - (regs [1] & 0x3F);
break;
case 2:
if ( !(data >> 4) )
enabled = false;
break;
case 4:
if ( data & trigger )
{
@ -92,12 +92,12 @@ void Gb_Square::clock_sweep()
sweep_delay = sweep_period;
regs [3] = sweep_freq & 0xFF;
regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07);
int offset = sweep_freq >> (regs [0] & shift_mask);
if ( regs [0] & 0x08 )
offset = -offset;
sweep_freq += offset;
if ( sweep_freq < 0 )
{
sweep_freq = 0;
@ -114,13 +114,13 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing )
{
if ( sweep_freq == 2048 )
playing = false;
static unsigned char const table [4] = { 1, 2, 4, 6 };
int const duty = table [regs [1] >> 6];
int amp = volume & playing;
if ( phase >= duty )
amp = -amp;
int frequency = this->frequency();
if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041
{
@ -128,7 +128,7 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing )
amp = volume >> 1;
playing = false;
}
{
int delta = amp - last_amp;
if ( delta )
@ -137,11 +137,11 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing )
synth->offset( time, delta, output );
}
}
time += delay;
if ( !playing )
time = end_time;
if ( time < end_time )
{
int const period = (2048 - frequency) * 4;
@ -159,7 +159,7 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing )
time += period;
}
while ( time < end_time );
this->phase = phase;
last_amp = delta >> 1;
}
@ -174,7 +174,7 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing )
int tap = 13 - (regs [3] & 8);
if ( bits >> tap & 2 )
amp = -amp;
{
int delta = amp - last_amp;
if ( delta )
@ -183,16 +183,16 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing )
synth->offset( time, delta, output );
}
}
time += delay;
if ( !playing )
time = end_time;
if ( time < end_time )
{
static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 };
int period = table [regs [3] & 7] << (regs [3] >> 4);
// keep parallel resampled time to eliminate time conversion in the loop
Blip_Buffer* const output = this->output;
const blip_resampled_time_t resampled_period =
@ -200,7 +200,7 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing )
blip_resampled_time_t resampled_time = output->resampled_time( time );
unsigned bits = this->bits;
int delta = amp * 2;
do
{
unsigned changed = (bits >> tap) + 1;
@ -215,7 +215,7 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing )
resampled_time += resampled_period;
}
while ( time < end_time );
this->bits = bits;
last_amp = delta >> 1;
}
@ -232,15 +232,15 @@ inline void Gb_Wave::write_register( int reg, int data )
if ( !(data & 0x80) )
enabled = false;
break;
case 1:
length = 256 - regs [1];
break;
case 2:
volume = data >> 5 & 3;
break;
case 4:
if ( data & trigger & regs [0] )
{
@ -258,14 +258,14 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing )
int frequency;
{
int amp = (wave [wave_pos] >> volume_shift & playing) * 2;
frequency = this->frequency();
if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045
{
amp = 30 >> volume_shift & playing;
playing = false;
}
int delta = amp - last_amp;
if ( delta )
{
@ -273,17 +273,17 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing )
synth->offset( time, delta, output );
}
}
time += delay;
if ( !playing )
time = end_time;
if ( time < end_time )
{
Blip_Buffer* const output = this->output;
int const period = (2048 - frequency) * 2;
int wave_pos = (this->wave_pos + 1) & (wave_size - 1);
do
{
int amp = (wave [wave_pos] >> volume_shift) * 2;
@ -297,7 +297,7 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing )
time += period;
}
while ( time < end_time );
this->wave_pos = (wave_pos - 1) & (wave_size - 1);
}
delay = time - end_time;
@ -324,11 +324,11 @@ void Gb_Apu::write_osc( int index, int reg, int data )
}
}
break;
case 2:
wave.write_register( reg, data );
break;
case 3:
if ( noise.write_register( reg, data ) )
noise.bits = 0x7FFF;

View file

@ -11,18 +11,18 @@ struct Gb_Osc
{
enum { trigger = 0x80 };
enum { len_enabled_mask = 0x40 };
Blip_Buffer* outputs [4]; // NULL, right, left, center
Blip_Buffer* output;
int output_select;
uint8_t* regs; // osc's 5 registers
int delay;
int last_amp;
int volume;
int length;
int enabled;
void reset();
void clock_length();
int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
@ -31,7 +31,7 @@ struct Gb_Osc
struct Gb_Env : Gb_Osc
{
int env_delay;
void reset();
void clock_envelope();
bool write_register( int, int );
@ -41,13 +41,13 @@ struct Gb_Square : Gb_Env
{
enum { period_mask = 0x70 };
enum { shift_mask = 0x07 };
typedef Blip_Synth<blip_good_quality,1> Synth;
Synth const* synth;
int sweep_delay;
int sweep_freq;
int phase;
void reset();
void clock_sweep();
void run( blip_time_t, blip_time_t, int playing );
@ -58,7 +58,7 @@ struct Gb_Noise : Gb_Env
typedef Blip_Synth<blip_med_quality,1> Synth;
Synth const* synth;
unsigned bits;
void run( blip_time_t, blip_time_t, int playing );
};
@ -69,7 +69,7 @@ struct Gb_Wave : Gb_Osc
int wave_pos;
enum { wave_size = 32 };
uint8_t wave [wave_size];
void write_register( int, int );
void run( blip_time_t, blip_time_t, int playing );
};

View file

@ -26,21 +26,21 @@ Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq =
Gbs_Emu::Gbs_Emu()
{
set_type( gme_gbs_type );
static const char* const names [Gb_Apu::osc_count] = {
"Square 1", "Square 2", "Wave", "Noise"
};
set_voice_names( names );
static int const types [Gb_Apu::osc_count] = {
wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0
};
set_voice_types( types );
set_silence_lookahead( 6 );
set_max_initial_silence( 21 );
set_gain( 1.2 );
set_equalizer( make_equalizer( -1.0, 120 ) );
}
@ -77,19 +77,19 @@ static blargg_err_t check_gbs_header( void const* header )
struct Gbs_File : Gme_Info_
{
Gbs_Emu::header_t h;
Gbs_File() { set_type( gme_gbs_type ); }
blargg_err_t load_( Data_Reader& in )
{
blargg_err_t err = in.read( &h, Gbs_Emu::header_size );
if ( err )
return (err == in.eof_error ? gme_wrong_file_type : err);
set_track_count( h.track_count );
return check_gbs_header( &h );
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
copy_gbs_fields( h, out );
@ -107,27 +107,27 @@ extern gme_type_t const gme_gbs_type = &gme_gbs_type_;
blargg_err_t Gbs_Emu::load_( Data_Reader& in )
{
assert( offsetof (header_t,copyright [32]) == header_size );
blaarg_static_assert( offsetof (header_t,copyright [32]) == header_size, "GBS Header layout incorrect!" );
RETURN_ERR( rom.load( in, header_size, &header_, 0 ) );
set_track_count( header_.track_count );
RETURN_ERR( check_gbs_header( &header_ ) );
if ( header_.vers != 1 )
set_warning( "Unknown file version" );
if ( header_.timer_mode & 0x78 )
set_warning( "Invalid timer mode" );
unsigned load_addr = get_le16( header_.load_addr );
if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
load_addr < 0x400 )
set_warning( "Invalid load/init/play address" );
set_voice_count( Gb_Apu::osc_count );
apu.volume( gain() );
return setup_buffer( 4194304 );
}
@ -153,8 +153,8 @@ void Gbs_Emu::set_bank( int n )
{
n = 1;
}
blargg_long addr = n * (blargg_long) bank_size;
int32_t addr = n * (int32_t) bank_size;
if (addr > rom.size())
{
return;
@ -206,37 +206,37 @@ void Gbs_Emu::set_tempo_( double t )
blargg_err_t Gbs_Emu::start_track_( int track )
{
RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( ram, 0, 0x4000 );
memset( ram + 0x4000, 0xFF, 0x1F00 );
memset( ram + 0x5F00, 0, sizeof ram - 0x5F00 );
ram [hi_page] = 0; // joypad reads back as 0
apu.reset();
for ( int i = 0; i < (int) sizeof sound_data; i++ )
apu.write_register( 0, i + apu.start_addr, sound_data [i] );
unsigned load_addr = get_le16( header_.load_addr );
rom.set_addr( load_addr );
cpu::rst_base = load_addr;
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 );
ram [hi_page + 6] = header_.timer_modulo;
ram [hi_page + 7] = header_.timer_mode;
update_timer();
next_play = play_period;
cpu::r.a = track;
cpu::r.pc = idle_addr;
cpu::r.sp = get_le16( header_.stack_ptr );
cpu_time = 0;
cpu_jsr( get_le16( header_.init_addr ) );
return 0;
}
@ -249,7 +249,7 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
cpu_time = duration;
bool result = cpu::run( count );
cpu_time -= cpu::remain();
if ( result )
{
if ( cpu::r.pc == idle_addr )
@ -259,7 +259,7 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
cpu_time = duration;
break;
}
if ( cpu_time < next_play )
cpu_time = next_play;
next_play += play_period;
@ -282,12 +282,12 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
}
}
}
duration = cpu_time;
next_play -= cpu_time;
if ( next_play < 0 ) // could go negative if routine is taking too long to return
next_play = 0;
apu.end_frame( cpu_time );
return 0;
}

View file

@ -14,7 +14,7 @@ public:
// Equalizer profiles for Game Boy Color speaker and headphones
static equalizer_t const handheld_eq;
static equalizer_t const headphones_eq;
// GBS file header
enum { header_size = 112 };
struct header_t
@ -33,12 +33,12 @@ public:
char author [32];
char copyright [32];
};
// Header for currently loaded file
header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_gbs_type; }
public:
// deprecated
using Music_Emu::load;
@ -62,25 +62,25 @@ private:
enum { bank_size = 0x4000 };
Rom_Data<bank_size> rom;
void set_bank( int );
// timer
blip_time_t cpu_time;
blip_time_t play_period;
blip_time_t next_play;
void update_timer();
header_t header_;
void cpu_jsr( gb_addr_t );
public: private: friend class Gb_Cpu;
blip_time_t clock() const { return cpu_time - cpu::remain(); }
enum { joypad_addr = 0xFF00 };
enum { ram_addr = 0xA000 };
enum { hi_page = 0xFF00 - ram_addr };
byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding];
Gb_Apu apu;
int cpu_read( gb_addr_t );
void cpu_write( gb_addr_t, int );
};

View file

@ -61,6 +61,11 @@ blargg_err_t Gme_File::load_( Data_Reader& in )
{
RETURN_ERR( file_data.resize( in.remain() ) );
RETURN_ERR( in.read( file_data.begin(), file_data.size() ) );
if ( type()->track_count == 1 )
{
RETURN_ERR( tracks.resize( 2 ) );
tracks[0] = 0, tracks[1] = file_data.size();
}
return load_mem_( file_data.begin(), file_data.size() );
}
@ -78,7 +83,7 @@ blargg_err_t Gme_File::post_load( blargg_err_t err )
post_load_();
else
unload();
return err;
}
@ -90,6 +95,22 @@ blargg_err_t Gme_File::load_mem( void const* in, long size )
return post_load( load_mem_( (byte const*) in, size ) );
}
blargg_err_t Gme_File::load_tracks( void const* in, long* sizes, int count )
{
pre_load();
if ( type()->track_count != 1 )
return "File type must have a fixed track count of 1";
set_track_count( count );
RETURN_ERR( tracks.resize( count + 1 ) );
long size = 0;
for ( int i = 0; i < count; size += sizes[i++] )
tracks[i] = size;
tracks[count] = size;
RETURN_ERR( file_data.resize( size ) );
memcpy( file_data.begin(), in, size );
return post_load( load_mem_( file_data.begin(), tracks[1] ) );
}
blargg_err_t Gme_File::load( Data_Reader& in )
{
pre_load();
@ -116,31 +137,31 @@ 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;
@ -155,7 +176,7 @@ blargg_err_t Gme_File::remap_track_( int* track_io ) const
{
if ( (unsigned) *track_io >= (unsigned) track_count() )
return "Invalid track";
if ( (unsigned) *track_io < (unsigned) playlist.size() )
{
M3u_Playlist::entry_t const& e = playlist [*track_io];
@ -186,7 +207,7 @@ blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const
out->play_length = -1;
out->repeat_count = -1;
out->song [0] = 0;
out->game [0] = 0;
out->author [0] = 0;
out->composer [0] = 0;
@ -201,13 +222,13 @@ blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const
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() )
{
@ -221,7 +242,7 @@ blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const
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];
copy_field_( out->song, e.name );
if ( e.length >= 0 ) out->length = e.length;

View file

@ -18,7 +18,7 @@ struct gme_type_t_
int track_count; /* non-zero for formats with a fixed number of tracks */
Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */
Music_Emu* (*new_info)(); /* Create new info reader for this type */
/* internal */
const char* extension_;
int flags_;
@ -27,18 +27,18 @@ struct gme_type_t_
struct track_info_t
{
long track_count;
/* times in milliseconds; -1 if unknown */
long length;
long intro_length;
long loop_length;
long fade_length;
long repeat_count;
/* Length if available, otherwise intro_length+loop_length*2 if available,
* otherwise a default of 150000 (2.5 minutes) */
long play_length;
/* empty string if not available */
char system [256];
char game [256];
@ -61,60 +61,61 @@ enum { gme_max_field = 255 };
struct Gme_File {
public:
// File loading
// Each loads game music data from a file and returns an error if
// file is wrong type or is seriously corrupt. They also set warning
// string for minor problems.
// Load from file on disk
blargg_err_t load_file( const char* path );
// Load from custom data source (see Data_Reader.h)
blargg_err_t load( Data_Reader& );
// Load from file already read into memory. Keeps pointer to data, so you
// must not free it until you're done with the file.
blargg_err_t load_mem( void const* data, long size );
// Load from multiple files already read into memory.
// Should only be used for file types with a fixed track count of 1.
blargg_err_t load_tracks( void const* in, long* sizes, int count );
// Load an m3u playlist. Must be done after loading main music file.
blargg_err_t load_m3u( const char* path );
blargg_err_t load_m3u( Data_Reader& in );
// Clears any loaded m3u playlist and any internal playlist that the music
// format supports (NSFE for example).
void clear_playlist();
// Informational
// Type of emulator. For example if this returns gme_nsfe_type, this object
// is an NSFE emulator, and you can cast to an Nsfe_Emu* if necessary.
gme_type_t type() const;
// Most recent warning string, or NULL if none. Clears current warning after
// returning.
const char* warning();
// Number of tracks or 0 if no file has been loaded
int track_count() const;
// Get information for a track (length, name, author, etc.)
// See gme.h for definition of struct track_info_t.
blargg_err_t track_info( track_info_t* out, int track ) const;
// User data/cleanup
// Set/get pointer to data you want to associate with this emulator.
// You can use this for whatever you want.
void set_user_data( void* p ) { user_data_ = p; }
void* user_data() const { return user_data_; }
// Register cleanup function to be called when deleting emulator, or NULL to
// clear it. Passes user_data to cleanup function.
void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; }
bool is_archive = false;
virtual blargg_err_t load_archive( const char* ) { return gme_wrong_file_type; }
public:
// deprecated
int error_count() const; // use warning()
@ -129,7 +130,10 @@ protected:
void set_warning( const char* s ) { warning_ = s; }
void set_type( gme_type_t t ) { type_ = t; }
blargg_err_t load_remaining_( void const* header, long header_size, Data_Reader& remaining );
const byte* track_pos( int i ) { return &file_data[tracks[i]]; }
long track_size( int i ) { return tracks[i + 1] - tracks[i]; }
// Overridable
virtual void unload(); // called before loading file and if loading fails
virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_()
@ -138,14 +142,14 @@ protected:
virtual void pre_load();
virtual void post_load_();
virtual void clear_playlist_() { }
public:
blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu
private:
// noncopyable
Gme_File( const Gme_File& );
Gme_File& operator = ( const Gme_File& );
gme_type_t type_;
int track_count_;
int raw_track_count_;
@ -155,7 +159,8 @@ private:
M3u_Playlist playlist;
char playlist_warning [64];
blargg_vector<byte> file_data; // only if loaded into memory using default load
blargg_vector<long> tracks; // file start indexes of `file_data`
blargg_err_t load_m3u_( blargg_err_t );
blargg_err_t post_load( blargg_err_t err );
public:

View file

@ -18,19 +18,19 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
double const min_tempo = 0.25;
double const oversample_factor = 5 / 3.0;
double const fm_gain = 3.0;
static double const min_tempo = 0.25;
static double const oversample_factor = 5 / 3.0;
static double const fm_gain = 3.0;
const long base_clock = 53700300;
const long clock_rate = base_clock / 15;
static const long base_clock = 53700300;
static const long clock_rate = base_clock / 15;
Gym_Emu::Gym_Emu()
{
data = 0;
pos = 0;
set_type( gme_gym_type );
static const char* const names [] = {
"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
};
@ -59,20 +59,20 @@ static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t*
out->intro_length = length; // make it clear that track is no longer than length
out->loop_length = 0;
}
// more stupidity where the field should have been left
if ( strcmp( h.song, "Unknown Song" ) )
GME_COPY_FIELD( h, out, song );
if ( strcmp( h.game, "Unknown Game" ) )
GME_COPY_FIELD( h, out, game );
if ( strcmp( h.copyright, "Unknown Publisher" ) )
GME_COPY_FIELD( h, out, copyright );
if ( strcmp( h.dumper, "Unknown Person" ) )
GME_COPY_FIELD( h, out, dumper );
if ( strcmp( h.comment, "Header added by YMAMP" ) )
GME_COPY_FIELD( h, out, comment );
}
@ -94,12 +94,12 @@ static long gym_track_length( byte const* p, byte const* end )
case 0:
time++;
break;
case 1:
case 2:
p += 2;
break;
case 3:
p += 1;
break;
@ -114,15 +114,15 @@ static blargg_err_t check_header( byte const* in, long size, int* data_offset =
{
if ( size < 4 )
return gme_wrong_file_type;
if ( memcmp( in, "GYMX", 4 ) == 0 )
{
if ( size < Gym_Emu::header_size + 1 )
return gme_wrong_file_type;
if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 )
return "Packed GYM file not supported";
if ( data_offset )
*data_offset = Gym_Emu::header_size;
}
@ -130,7 +130,7 @@ static blargg_err_t check_header( byte const* in, long size, int* data_offset =
{
return gme_wrong_file_type;
}
return 0;
}
@ -139,9 +139,9 @@ struct Gym_File : Gme_Info_
byte const* file_begin;
byte const* file_end;
int data_offset;
Gym_File() { set_type( gme_gym_type ); }
blargg_err_t load_mem_( byte const* in, long size )
{
file_begin = in;
@ -149,7 +149,7 @@ struct Gym_File : Gme_Info_
data_offset = 0;
return check_header( in, size, &data_offset );
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
long length = gym_track_length( &file_begin [data_offset], file_end );
@ -175,13 +175,13 @@ blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate )
dac_synth.volume( 0.125 / 256 * fm_gain * gain() );
double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() );
fm_sample_rate = sample_rate * factor;
RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) );
blip_buf.clock_rate( clock_rate );
RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) );
RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) );
return 0;
}
@ -192,7 +192,7 @@ void Gym_Emu::set_tempo_( double t )
set_tempo( min_tempo );
return;
}
if ( blip_buf.sample_rate() )
{
clocks_per_frame = long (clock_rate / 60 / tempo());
@ -210,20 +210,20 @@ void Gym_Emu::mute_voices_( int mask )
blargg_err_t Gym_Emu::load_mem_( byte const* in, long size )
{
assert( offsetof (header_t,packed [4]) == header_size );
blaarg_static_assert( offsetof (header_t,packed [4]) == header_size, "GYM Header layout incorrect!" );
int offset = 0;
RETURN_ERR( check_header( in, size, &offset ) );
set_voice_count( 8 );
data = in + offset;
data_end = in + size;
loop_begin = 0;
if ( offset )
header_ = *(header_t const*) in;
else
memset( &header_, 0, sizeof header_ );
return 0;
}
@ -232,14 +232,14 @@ blargg_err_t Gym_Emu::load_mem_( byte const* in, long size )
blargg_err_t Gym_Emu::start_track_( int track )
{
RETURN_ERR( Music_Emu::start_track_( track ) );
pos = data;
loop_remain = get_le32( header_.loop_start );
prev_dac_count = 0;
dac_enabled = false;
dac_amp = -1;
fm.reset();
apu.reset();
blip_buf.clear();
@ -250,7 +250,7 @@ blargg_err_t Gym_Emu::start_track_( int track )
void Gym_Emu::run_dac( int dac_count )
{
// Guess beginning and end of sample and adjust rate and buffer position accordingly.
// count dac samples in next frame
int next_dac_count = 0;
const byte* p = this->pos;
@ -263,7 +263,7 @@ void Gym_Emu::run_dac( int dac_count )
if ( cmd == 1 && data == 0x2A )
next_dac_count++;
}
// detect beginning and end of sample
int rate_count = dac_count;
int start = 0;
@ -276,17 +276,17 @@ void Gym_Emu::run_dac( int dac_count )
{
rate_count = prev_dac_count;
}
// Evenly space samples within buffer section being used
blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count;
blip_resampled_time_t time = blip_buf.resampled_time( 0 ) +
period * start + (period >> 1);
int dac_amp = this->dac_amp;
if ( dac_amp < 0 )
dac_amp = dac_buf [0];
for ( int i = 0; i < dac_count; i++ )
{
int delta = dac_buf [i] - dac_amp;
@ -301,10 +301,10 @@ void Gym_Emu::parse_frame()
{
int dac_count = 0;
const byte* pos = this->pos;
if ( loop_remain && !--loop_remain )
loop_begin = pos; // find loop on first time through sequence
int cmd;
while ( (cmd = *pos++) != 0 )
{
@ -316,7 +316,7 @@ void Gym_Emu::parse_frame()
{
if ( data == 0x2B )
dac_enabled = (data2 & 0x80) != 0;
fm.write0( data, data2 );
}
else if ( dac_count < (int) sizeof dac_buf )
@ -337,23 +337,23 @@ void Gym_Emu::parse_frame()
{
// to do: many GYM streams are full of errors, and error count should
// reflect cases where music is really having problems
//log_error();
//log_error();
--pos; // put data back
}
}
// loop
if ( pos >= data_end )
{
check( pos == data_end );
if ( loop_begin )
pos = loop_begin;
else
set_track_ended();
}
this->pos = pos;
// dac
if ( dac_count && !dac_muted )
run_dac( dac_count );
@ -364,12 +364,12 @@ 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;
}

View file

@ -26,18 +26,18 @@ public:
byte loop_start [4]; // in 1/60 seconds, 0 if not looped
byte packed [4];
};
// Header for currently loaded file
header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_gym_type; }
public:
// deprecated
using Music_Emu::load;
blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader
{ return load_remaining_( &h, sizeof h, in ); }
enum { gym_rate = 60 };
enum { gym_rate = 60 };
long track_length() const; // use track_info()
public:
@ -58,19 +58,19 @@ private:
const byte* loop_begin;
const byte* pos;
const byte* data_end;
blargg_long loop_remain; // frames remaining until loop beginning has been located
int32_t loop_remain; // frames remaining until loop beginning has been located
header_t header_;
double fm_sample_rate;
blargg_long clocks_per_frame;
int32_t clocks_per_frame;
void parse_frame();
// dac (pcm)
int dac_amp;
int prev_dac_count;
bool dac_enabled;
bool dac_muted;
void run_dac( int );
// sound
Blip_Buffer blip_buf;
Ym2612_Emu fm;

View file

@ -17,7 +17,7 @@ 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
static bool const center_waves = true; // reduces asymmetry and clamping when starting notes
Hes_Apu::Hes_Apu()
{
@ -32,7 +32,7 @@ Hes_Apu::Hes_Apu()
osc->chans [2] = 0;
}
while ( osc != oscs );
reset();
}
@ -40,7 +40,7 @@ void Hes_Apu::reset()
{
latch = 0;
balance = 0xFF;
Hes_Osc* osc = &oscs [osc_count];
do
{
@ -59,7 +59,7 @@ void Hes_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Bli
oscs [index].chans [0] = center;
oscs [index].chans [1] = left;
oscs [index].chans [2] = right;
Hes_Osc* osc = &oscs [osc_count];
do
{
@ -75,7 +75,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
if ( osc_outputs_0 && control & 0x80 )
{
int dac = this->dac;
int const volume_0 = volume [0];
{
int delta = dac * volume_0 - last_amp [0];
@ -83,7 +83,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
synth_.offset( last_time, delta, osc_outputs_0 );
osc_outputs_0->set_modified();
}
Blip_Buffer* const osc_outputs_1 = outputs [1];
int const volume_1 = volume [1];
if ( osc_outputs_1 )
@ -93,7 +93,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
synth_.offset( last_time, delta, osc_outputs_1 );
osc_outputs_1->set_modified();
}
blip_time_t time = last_time + delay;
if ( time < end_time )
{
@ -106,10 +106,10 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
unsigned noise_lfsr = this->noise_lfsr;
do
{
int new_dac = 0x1F & -(noise_lfsr >> 1 & 1);
int new_dac = 0x1F & uMinus(noise_lfsr >> 1 & 1);
// Implemented using "Galios configuration"
// TODO: find correct LFSR algorithm
noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1));
noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & uMinus(noise_lfsr & 1));
//noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1));
int delta = new_dac - dac;
if ( delta )
@ -122,7 +122,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
time += period;
}
while ( time < end_time );
this->noise_lfsr = noise_lfsr;
assert( noise_lfsr );
}
@ -160,9 +160,8 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
//if ( !(volume_0 | volume_1) )
// debug_printf( "Used period 0\n" );
}
// maintain phase when silent
blargg_long count = (end_time - time + period - 1) / period;
int32_t count = (end_time - time + period - 1) / period;
phase += count; // phase will be masked below
time += count * period;
}
@ -173,7 +172,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
if ( time < 0 )
time = 0;
delay = time;
this->dac = dac;
last_amp [0] = dac * volume_0;
last_amp [1] = dac * volume_1;
@ -195,18 +194,18 @@ void Hes_Apu::balance_changed( Hes_Osc& osc )
ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ),
#undef ENTRY
};
int vol = (osc.control & 0x1F) - 0x1E * 2;
int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E);
if ( left < 0 ) left = 0;
int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E);
if ( right < 0 ) right = 0;
left = log_table [left ];
right = log_table [right];
// optimizing for the common case of being centered also allows easy
// panning using Effects_Buffer
osc.outputs [0] = osc.chans [0]; // center
@ -216,13 +215,13 @@ void Hes_Apu::balance_changed( Hes_Osc& osc )
osc.outputs [0] = osc.chans [1]; // left
osc.outputs [1] = osc.chans [2]; // right
}
if ( center_waves )
{
osc.last_amp [0] += (left - osc.volume [0]) * 16;
osc.last_amp [1] += (right - osc.volume [1]) * 16;
}
osc.volume [0] = left;
osc.volume [1] = right;
}
@ -238,7 +237,7 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data )
if ( balance != data )
{
balance = data;
Hes_Osc* osc = &oscs [osc_count];
do
{
@ -258,23 +257,23 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data )
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) )
@ -287,15 +286,15 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data )
osc.dac = data;
}
break;
case 0x807:
if ( &osc >= &oscs [4] )
osc.noise = data;
break;
case 0x809:
if ( !(data & 0x80) && (data & 0x03) != 0 )
debug_printf( "HES LFO not supported\n" );
case 0x807:
if ( &osc >= &oscs [4] )
osc.noise = data;
break;
case 0x809:
if ( !(data & 0x80) && (data & 0x03) != 0 )
debug_printf( "HES LFO not supported\n" );
}
}
}

View file

@ -19,15 +19,15 @@ struct Hes_Osc
unsigned char balance;
unsigned char dac;
blip_time_t last_time;
Blip_Buffer* outputs [2];
Blip_Buffer* chans [3];
unsigned noise_lfsr;
unsigned char control;
enum { amp_range = 0x8000 };
static const unsigned int amp_range = 0x8000;
typedef Blip_Synth<blip_med_quality,1> synth_t;
void run_until( synth_t& synth, blip_time_t );
};
@ -35,18 +35,18 @@ class Hes_Apu {
public:
void treble_eq( blip_eq_t const& );
void volume( double );
enum { osc_count = 6 };
static const int osc_count = 6;
void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
void reset();
enum { start_addr = 0x0800 };
enum { end_addr = 0x0809 };
static const unsigned int start_addr = 0x0800;
static const unsigned int end_addr = 0x0809;
void write_data( blip_time_t, int addr, int data );
void end_frame( blip_time_t );
public:
Hes_Apu();
private:
@ -54,7 +54,7 @@ private:
int latch;
int balance;
Hes_Osc::synth_t synth;
void balance_changed( Hes_Osc& );
void recalc_chans();
};

View file

@ -37,14 +37,16 @@ int const ram_addr = 0x2000;
#endif
// status flags
BLARGG_MAYBE_UNUSED int const st_n = 0x80;
BLARGG_MAYBE_UNUSED int const st_v = 0x40;
BLARGG_MAYBE_UNUSED int const st_t = 0x20;
BLARGG_MAYBE_UNUSED int const st_b = 0x10;
BLARGG_MAYBE_UNUSED int const st_d = 0x08;
BLARGG_MAYBE_UNUSED int const st_i = 0x04;
BLARGG_MAYBE_UNUSED int const st_z = 0x02;
BLARGG_MAYBE_UNUSED int const st_c = 0x01;
enum {
st_n = 0x80,
st_v = 0x40,
st_t = 0x20,
st_b = 0x10,
st_d = 0x08,
st_i = 0x04,
st_z = 0x02,
st_c = 0x01
};
void Hes_Cpu::reset()
{
@ -94,7 +96,7 @@ bool Hes_Cpu::run( hes_time_t end_time )
state_t s = this->state_;
this->state = &s;
// even on x86, using s.time in place of s_time was slower
blargg_long s_time = s.time;
int32_t s_time = s.time;
// registers
uint_fast16_t pc = r.pc;
@ -994,7 +996,7 @@ possibly_out_of_time:
hes_time_t new_time = end_time_;
if ( !(status & st_i) && new_time > irq_time_ )
new_time = irq_time_;
blargg_long delta = s.base - new_time;
int32_t delta = s.base - new_time;
s.base = new_time;
s_time += delta;
}
@ -1062,7 +1064,7 @@ possibly_out_of_time:
status &= ~st_i;
handle_cli: {
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - irq_time_;
int32_t delta = s.base - irq_time_;
if ( delta <= 0 )
{
if ( TIME < irq_time_ )
@ -1093,7 +1095,7 @@ possibly_out_of_time:
status |= st_i;
handle_sei: {
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - end_time_;
int32_t delta = s.base - end_time_;
s.base = end_time_;
s_time += delta;
if ( s_time < 0 )
@ -1260,7 +1262,7 @@ interrupt:
status |= st_i;
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - end_time_;
int32_t delta = s.base - end_time_;
s.base = end_time_;
s_time += delta;
goto loop;

View file

@ -6,23 +6,23 @@
#include "blargg_common.h"
typedef blargg_long hes_time_t; // clock cycle count
typedef int32_t hes_time_t; // clock cycle count
typedef unsigned hes_addr_t; // 16-bit address
enum { future_hes_time = INT_MAX / 2 + 1 };
class Hes_Cpu {
public:
void reset();
enum { page_size = 0x2000 };
enum { page_shift = 13 };
enum { page_count = 8 };
void set_mmr( int reg, int bank );
uint8_t const* get_code( hes_addr_t );
uint8_t ram [page_size];
// not kept updated during a call to run()
struct registers_t {
uint16_t pc;
@ -33,35 +33,35 @@ public:
uint8_t sp;
};
registers_t r;
// page mapping registers
uint8_t mmr [page_count + 1];
// Set end_time and run CPU from current time. Returns true if any illegal
// instructions were encountered.
bool run( hes_time_t end_time );
// Time of beginning of next instruction to be executed
hes_time_t time() const { return state->time + state->base; }
void set_time( hes_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; }
hes_time_t irq_time() const { return irq_time_; }
void set_irq_time( hes_time_t );
hes_time_t end_time() const { return end_time_; }
void set_end_time( hes_time_t );
void end_frame( hes_time_t );
// Attempt to execute instruction here results in CPU advancing time to
// lesser of irq_time() and end_time() (or end_time() if IRQs are
// disabled)
enum { idle_addr = 0x1FFF };
// Can read this many bytes past end of a page
enum { cpu_padding = 8 };
public:
Hes_Cpu() { state = &state_; }
enum { irq_inhibit = 0x04 };
@ -69,17 +69,17 @@ private:
// noncopyable
Hes_Cpu( const Hes_Cpu& );
Hes_Cpu& operator = ( const Hes_Cpu& );
struct state_t {
uint8_t const* code_map [page_count + 1];
hes_time_t base;
blargg_long time;
int32_t time;
};
state_t* state; // points to state_ or a local copy within run()
state_t state_;
hes_time_t irq_time_;
hes_time_t end_time_;
void set_code_page( int, void const* );
inline int update_end_time( hes_time_t end, hes_time_t irq );
};

View file

@ -19,12 +19,12 @@ 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;
static int const timer_mask = 0x04;
static int const vdp_mask = 0x02;
static int const i_flag_mask = 0x04;
static int const unmapped = 0xFF;
long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
static long const period_60hz = 262 * 455L; // scanlines * clocks per scanline
using std::min;
using std::max;
@ -33,12 +33,12 @@ Hes_Emu::Hes_Emu()
{
timer.raw_load = 0;
set_type( gme_hes_type );
static const char* const names [Hes_Apu::osc_count] = {
"Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2"
};
set_voice_names( names );
static int const types [Hes_Apu::osc_count] = {
wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3,
mixed_type | 0, mixed_type | 1
@ -65,19 +65,19 @@ static byte const* copy_field( byte const* in, char* out )
int len = 0x20;
if ( in [0x1F] && !in [0x2F] )
len = 0x30; // fields are sometimes 16 bytes longer (ugh)
// since text fields are where any data could be, detect non-text
// and fields with data after zero byte terminator
int i = 0;
for ( i = 0; i < len && in [i]; i++ )
if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text
return 0; // non-ASCII found
for ( ; i < len; i++ )
if ( in [i] )
return 0; // data after terminator
Gme_File::copy_field_( out, (char const*) in, len );
in += len;
}
@ -114,18 +114,18 @@ struct Hes_File : Gme_Info_
char unused [0x20];
byte fields [0x30 * 3];
} h;
Hes_File() { set_type( gme_hes_type ); }
blargg_err_t load_( Data_Reader& in )
{
assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20 );
blaarg_static_assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20, "HES header layout is incorrect!" );
blargg_err_t err = in.read( &h, sizeof h );
if ( err )
return (err == in.eof_error ? gme_wrong_file_type : err);
return check_hes_header( &h );
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
copy_hes_fields( h.fields, out );
@ -144,24 +144,24 @@ extern gme_type_t const gme_hes_type = &gme_hes_type_;
blargg_err_t Hes_Emu::load_( Data_Reader& in )
{
assert( offsetof (header_t,unused [4]) == header_size );
blaarg_static_assert( offsetof (header_t,unused [4]) == header_size, "HES header layout is incorrect!" );
RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) );
RETURN_ERR( check_hes_header( header_.tag ) );
if ( header_.vers != 0 )
set_warning( "Unknown file version" );
if ( memcmp( header_.data_tag, "DATA", 4 ) )
set_warning( "Data header missing" );
if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
set_warning( "Unknown header data" );
// File spec supports multiple blocks, but I haven't found any, and
// many files have bad sizes in the only block, so it's simpler to
// just try to load the damn data as best as possible.
long addr = get_le32( header_.addr );
long size = get_le32( header_.size );
long const rom_max = 0x100000;
@ -172,7 +172,7 @@ blargg_err_t Hes_Emu::load_( Data_Reader& in )
}
if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
set_warning( "Invalid size" );
if ( size != rom.file_size() )
{
if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) )
@ -182,13 +182,13 @@ blargg_err_t Hes_Emu::load_( Data_Reader& in )
else
set_warning( "Missing file data" );
}
rom.set_addr( addr );
set_voice_count( apu.osc_count );
apu.volume( gain() );
return setup_buffer( 7159091 );
}
@ -219,40 +219,40 @@ void Hes_Emu::set_tempo_( double t )
blargg_err_t Hes_Emu::start_track_( int track )
{
RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( ram, 0, sizeof ram ); // some HES music relies on zero fill
memset( sgx, 0, sizeof sgx );
apu.reset();
cpu::reset();
for ( unsigned i = 0; i < sizeof header_.banks; i++ )
set_mmr( i, header_.banks [i] );
set_mmr( page_count, 0xFF ); // unmapped beyond end of address space
irq.disables = timer_mask | vdp_mask;
irq.timer = future_hes_time;
irq.vdp = future_hes_time;
timer.enabled = false;
timer.raw_load= 0x80;
timer.count = timer.load;
timer.fired = false;
timer.last_time = 0;
vdp.latch = 0;
vdp.control = 0;
vdp.next_vbl = 0;
ram [0x1FF] = (idle_addr - 1) >> 8;
ram [0x1FE] = (idle_addr - 1) & 0xFF;
r.sp = 0xFD;
r.pc = get_le16( header_.init_addr );
r.a = track;
recalc_timer_load();
last_frame_hook = 0;
return 0;
}
@ -265,7 +265,7 @@ void Hes_Emu::cpu_write_vdp( int addr, int data )
case 0:
vdp.latch = data & 0x1F;
break;
case 2:
if ( vdp.latch == 5 )
{
@ -280,7 +280,7 @@ void Hes_Emu::cpu_write_vdp( int addr, int data )
debug_printf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
}
break;
case 3:
debug_printf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data );
break;
@ -297,7 +297,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
apu.write_data( t, addr, data );
return;
}
hes_time_t time = this->time();
switch ( addr )
{
@ -306,7 +306,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
case 0x0003:
cpu_write_vdp( addr, data );
return;
case 0x0C00: {
run_until( time );
timer.raw_load = (data & 0x7F) + 1;
@ -314,7 +314,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
timer.count = timer.load;
break;
}
case 0x0C01:
data &= 1;
if ( timer.enabled == data )
@ -324,21 +324,21 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int 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
debug_printf( "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
@ -346,13 +346,13 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
case 0x0404:
case 0x0405:
return;
default:
debug_printf( "unmapped write $%04X <- $%02X\n", addr, data );
return;
#endif
}
irq_changed();
}
@ -369,22 +369,22 @@ int Hes_Emu::cpu_read_( hes_addr_t addr )
run_until( time );
irq_changed();
return 0x20;
case 0x0002:
case 0x0003:
debug_printf( "VDP read not supported: %d\n", addr );
return 0;
case 0x0C01:
//return timer.enabled; // TODO: remove?
case 0x0C00:
run_until( time );
debug_printf( "Timer count read\n" );
return (unsigned) (timer.count - 1) / timer_base;
case 0x1402:
return irq.disables;
case 0x1403:
{
int status = 0;
@ -392,18 +392,18 @@ int Hes_Emu::cpu_read_( hes_addr_t addr )
if ( irq.vdp <= time ) status |= vdp_mask;
return status;
}
#ifndef NDEBUG
case 0x1000: // I/O port
case 0x180C: // CD-ROM
case 0x180D:
break;
default:
debug_printf( "unmapped read $%04X\n", addr );
#endif
}
return unmapped;
}
@ -415,7 +415,7 @@ void Hes_Emu::run_until( hes_time_t present )
{
while ( vdp.next_vbl < present )
vdp.next_vbl += play_period;
hes_time_t elapsed = present - timer.last_time;
if ( elapsed > 0 )
{
@ -432,25 +432,25 @@ void Hes_Emu::run_until( hes_time_t present )
void Hes_Emu::irq_changed()
{
hes_time_t present = time();
if ( irq.timer > present )
{
irq.timer = future_hes_time;
if ( timer.enabled && !timer.fired )
irq.timer = present + timer.count;
}
if ( irq.vdp > present )
{
irq.vdp = future_hes_time;
if ( vdp.control & 0x08 )
irq.vdp = vdp.next_vbl;
}
hes_time_t time = future_hes_time;
if ( !(irq.disables & timer_mask) ) time = irq.timer;
if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp );
set_irq_time( time );
}
@ -458,11 +458,11 @@ int Hes_Emu::cpu_done()
{
check( time() >= end_time() ||
(!(r.status & i_flag_mask) && time() >= irq_time()) );
if ( !(r.status & i_flag_mask) )
{
hes_time_t present = time();
if ( irq.timer <= present && !(irq.disables & timer_mask) )
{
timer.fired = true;
@ -481,7 +481,7 @@ int Hes_Emu::cpu_done()
#endif
return 0x0A;
}
if ( irq.vdp <= present && !(irq.disables & vdp_mask) )
{
// work around for bugs with music not acknowledging VDP
@ -498,7 +498,7 @@ int Hes_Emu::cpu_done()
return 0;
}
static void adjust_time( blargg_long& time, hes_time_t delta )
static void adjust_time( int32_t& time, hes_time_t delta )
{
if ( time < future_hes_time )
{
@ -511,15 +511,15 @@ static void adjust_time( blargg_long& time, hes_time_t delta )
blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int )
{
blip_time_t const duration = duration_; // cache
if ( cpu::run( duration ) )
set_warning( "Emulation error (illegal instruction)" );
check( time() >= duration );
//check( time() - duration < 20 ); // Txx instruction could cause going way over
run_until( duration );
// end time frame
timer.last_time -= duration;
vdp.next_vbl -= duration;
@ -530,6 +530,6 @@ blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int )
::adjust_time( irq.timer, duration );
::adjust_time( irq.vdp, duration );
apu.end_frame( duration );
return 0;
}

View file

@ -25,10 +25,10 @@ public:
byte addr [4];
byte unused [4];
};
// Header for currently loaded file
header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_hes_type; }
public:
@ -45,7 +45,7 @@ protected:
void unload();
public: private: friend class Hes_Cpu;
byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space
int cpu_read_( hes_addr_t );
int cpu_read( hes_addr_t );
void cpu_write_( hes_addr_t, int data );
@ -59,34 +59,34 @@ private:
hes_time_t play_period;
hes_time_t last_frame_hook;
int timer_base;
struct {
hes_time_t last_time;
blargg_long count;
blargg_long load;
int32_t count;
int32_t load;
int raw_load;
byte enabled;
byte fired;
} timer;
struct {
hes_time_t next_vbl;
byte latch;
byte control;
} vdp;
struct {
hes_time_t timer;
hes_time_t vdp;
byte disables;
} irq;
void recalc_timer_load();
// large items
Hes_Apu apu;
byte sgx [3 * page_size + cpu_padding];
void irq_changed();
void run_until( hes_time_t );
};

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
#include "blargg_endian.h"
typedef blargg_long cpu_time_t;
typedef int32_t cpu_time_t;
// must be defined by caller
void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data );
@ -17,35 +17,35 @@ class Kss_Cpu {
public:
// Clear registers and map all pages to unmapped
void reset( void* unmapped_write, void const* unmapped_read );
// Map memory. Start and size must be multiple of page_size.
enum { page_size = 0x2000 };
void map_mem( unsigned addr, blargg_ulong size, void* write, void const* read );
static const unsigned int page_size = 0x2000;
void map_mem( unsigned addr, uint32_t size, void* write, void const* read );
// Map address to page
uint8_t* write( unsigned addr );
uint8_t const* read( unsigned addr );
// Run until specified time is reached. Returns true if suspicious/unsupported
// instruction was encountered at any point during run.
bool run( cpu_time_t end_time );
// Time of beginning of next instruction
cpu_time_t time() const { return state->time + state->base; }
// Alter current time. Not supported during run() call.
void set_time( cpu_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; }
#if BLARGG_BIG_ENDIAN
struct regs_t { uint8_t b, c, d, e, h, l, flags, a; };
#else
struct regs_t { uint8_t c, b, e, d, l, h, a, flags; };
#endif
static_assert( sizeof (regs_t) == 8, "Invalid registers size, padding issue?" );
struct pairs_t { uint16_t bc, de, hl, fa; };
// Registers are not updated until run() returns
struct registers_t {
uint16_t pc;
@ -67,16 +67,16 @@ public:
uint8_t im;
};
//registers_t r; (below for efficiency)
enum { idle_addr = 0xFFFF };
static const unsigned int idle_addr = 0xFFFF;
// can read this far past end of a page
enum { cpu_padding = 0x100 };
static const unsigned int cpu_padding = 0x100;
public:
Kss_Cpu();
enum { page_shift = 13 };
enum { page_count = 0x10000 >> page_shift };
static const unsigned int page_shift = 13;
static const int page_count = 0x10000 >> page_shift;
private:
uint8_t szpc [0x200];
cpu_time_t end_time_;

View file

@ -19,8 +19,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
long const clock_rate = 3579545;
int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count;
static long const clock_rate = 3579545;
static int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count;
using std::min;
using std::max;
@ -35,13 +35,13 @@ Kss_Emu::Kss_Emu()
"Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5"
};
set_voice_names( names );
static int const types [osc_count] = {
wave_type | 0, wave_type | 1, wave_type | 2,
wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7
};
set_voice_types( types );
memset( unmapped_read, 0xFF, sizeof unmapped_read );
}
@ -84,9 +84,9 @@ static blargg_err_t check_kss_header( void const* header )
struct Kss_File : Gme_Info_
{
Kss_Emu::header_t header_;
Kss_File() { set_type( gme_kss_type ); }
blargg_err_t load_( Data_Reader& in )
{
blargg_err_t err = in.read( &header_, Kss_Emu::header_size );
@ -94,7 +94,7 @@ struct Kss_File : Gme_Info_
return (err == in.eof_error ? gme_wrong_file_type : err);
return check_kss_header( &header_ );
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
copy_kss_fields( header_, out );
@ -125,12 +125,12 @@ void Kss_Emu::update_gain()
blargg_err_t Kss_Emu::load_( Data_Reader& in )
{
memset( &header_, 0, sizeof header_ );
assert( offsetof (header_t,device_flags) == header_size - 1 );
assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 );
blaarg_static_assert( offsetof (header_t,device_flags) == header_size - 1, "KSS Header layout incorrect!" );
blaarg_static_assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1, "KSS Extended Header layout incorrect!" );
RETURN_ERR( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) );
RETURN_ERR( check_kss_header( header_.tag ) );
if ( header_.tag [3] == 'C' )
{
if ( header_.extra_header )
@ -151,19 +151,19 @@ blargg_err_t Kss_Emu::load_( Data_Reader& in )
if ( header_.extra_header > 0x10 )
set_warning( "Unknown data in header" );
}
if ( header_.device_flags & 0x09 )
set_warning( "FM sound not supported" );
scc_enabled = 0xC000;
if ( header_.device_flags & 0x04 )
scc_enabled = 0;
if ( header_.device_flags & 0x02 && !sn )
CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) );
set_voice_count( osc_count );
return setup_buffer( ::clock_rate );
}
@ -201,7 +201,7 @@ blargg_err_t Kss_Emu::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
@ -213,7 +213,7 @@ blargg_err_t Kss_Emu::start_track_( int track )
};
memcpy( ram + 0x01, bios, sizeof bios );
memcpy( ram + 0x93, vectors, sizeof vectors );
// copy non-banked data into RAM
unsigned load_addr = get_le16( header_.load_addr );
long orig_load_size = get_le16( header_.load_size );
@ -222,11 +222,11 @@ blargg_err_t Kss_Emu::start_track_( int track )
if ( load_size != orig_load_size )
set_warning( "Excessive data size" );
memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size );
rom.set_addr( -load_size - header_.extra_header );
// check available bank data
blargg_long const bank_size = this->bank_size();
int32_t 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 )
@ -237,11 +237,11 @@ blargg_err_t Kss_Emu::start_track_( int track )
//debug_printf( "load_size : $%X\n", load_size );
//debug_printf( "bank_size : $%X\n", bank_size );
//debug_printf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F );
ram [idle_addr] = 0xFF;
cpu::reset( unmapped_write, unmapped_read );
cpu::map_mem( 0, mem_size, ram, ram );
ay.reset();
scc.reset();
if ( sn )
@ -256,18 +256,18 @@ blargg_err_t Kss_Emu::start_track_( int track )
gain_updated = false;
update_gain();
ay_latch = 0;
return 0;
}
void Kss_Emu::set_bank( int logical, int physical )
{
unsigned const bank_size = this->bank_size();
unsigned addr = 0x8000;
if ( logical && bank_size == 8 * 1024 )
addr = 0xA000;
physical -= header_.first_bank;
if ( (unsigned) physical >= (unsigned) bank_count )
{
@ -276,7 +276,7 @@ void Kss_Emu::set_bank( int logical, int physical )
}
else
{
long phys = physical * (blargg_long) bank_size;
long phys = physical * (int32_t) bank_size;
for ( unsigned offset = 0; offset < bank_size; offset += page_size )
cpu::map_mem( addr + offset, page_size,
unmapped_write, rom.at_addr( phys + offset ) );
@ -291,12 +291,12 @@ void Kss_Emu::cpu_write( unsigned addr, int data )
case 0x9000:
set_bank( 0, data );
return;
case 0xB000:
set_bank( 1, data );
return;
}
int scc_addr = (addr & 0xDFFF) ^ 0x9800;
if ( scc_addr < scc.reg_count )
{
@ -304,7 +304,7 @@ void Kss_Emu::cpu_write( unsigned addr, int data )
scc.write( time(), scc_addr, data );
return;
}
debug_printf( "LD ($%04X),$%02X\n", addr, data );
}
@ -324,12 +324,12 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data )
case 0xA0:
emu.ay_latch = data & 0x0F;
return;
case 0xA1:
GME_APU_HOOK( &emu, emu.ay_latch, data );
emu.ay.write( time, emu.ay_latch, data );
return;
case 0x06:
if ( emu.sn && (emu.header_.device_flags & 0x04) )
{
@ -337,7 +337,7 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data )
return;
}
break;
case 0x7E:
case 0x7F:
if ( emu.sn )
@ -347,11 +347,11 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data )
return;
}
break;
case 0xFE:
emu.set_bank( 0, data );
return;
#ifndef NDEBUG
case 0xF1: // FM data
if ( data )
@ -361,7 +361,7 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data )
return;
#endif
}
debug_printf( "OUT $%04X,$%02X\n", addr, data );
}
@ -371,7 +371,7 @@ int kss_cpu_in( Kss_Cpu*, cpu_time_t, unsigned addr )
//switch ( addr & 0xFF )
//{
//}
debug_printf( "IN $%04X\n", addr );
return 0;
}
@ -386,7 +386,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
cpu::run( min( duration, next_play ) );
if ( r.pc == idle_addr )
set_time( end );
if ( time() >= next_play )
{
next_play += play_period;
@ -398,7 +398,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
if ( scc_accessed )
update_gain();
}
ram [--r.sp] = idle_addr >> 8;
ram [--r.sp] = idle_addr & 0xFF;
r.pc = get_le16( header_.play_addr );
@ -406,7 +406,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
}
}
}
duration = time();
next_play -= duration;
check( next_play >= 0 );
@ -415,6 +415,6 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
scc.end_frame( duration );
if ( sn )
sn->end_frame( duration );
return 0;
}

View file

@ -27,7 +27,7 @@ public:
byte extra_header;
byte device_flags;
};
enum { ext_header_size = 0x10 };
struct ext_header_t
{
@ -40,12 +40,12 @@ public:
byte msx_music_vol;
byte msx_audio_vol;
};
struct composite_header_t : header_t, ext_header_t { };
// Header for currently loaded file
composite_header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_kss_type; }
public:
Kss_Emu();
@ -62,29 +62,29 @@ protected:
private:
Rom_Data<page_size> rom;
composite_header_t header_;
bool scc_accessed;
bool gain_updated;
void update_gain();
unsigned scc_enabled; // 0 or 0xC000
int bank_count;
void set_bank( int logical, int physical );
blargg_long bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); }
int32_t bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); }
blip_time_t play_period;
blip_time_t next_play;
int ay_latch;
friend void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data );
friend int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr );
void cpu_write( unsigned addr, int data );
friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data );
// large items
enum { mem_size = 0x10000 };
static const unsigned int mem_size = 0x10000;
byte ram [mem_size + cpu_padding];
Ay_Apu ay;
Scc_Apu scc;
Sms_Apu* sn;

View file

@ -17,32 +17,32 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
// Tones above this frequency are treated as disabled tone at half volume.
// Power of two is more efficient (avoids division).
unsigned const inaudible_freq = 16384;
static unsigned const inaudible_freq = 16384;
int const wave_size = 0x20;
static int const wave_size = 0x20;
void Scc_Apu::run_until( blip_time_t end_time )
{
for ( int index = 0; index < osc_count; index++ )
{
osc_t& osc = oscs [index];
Blip_Buffer* const output = osc.output;
if ( !output )
continue;
output->set_modified();
blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 +
regs [0x80 + index * 2] + 1;
int volume = 0;
if ( regs [0x8F] & (1 << index) )
{
blip_time_t inaudible_period = (blargg_ulong) (output->clock_rate() +
blip_time_t inaudible_period = (uint32_t) (output->clock_rate() +
inaudible_freq * 32) / (inaudible_freq * 16);
if ( period > inaudible_period )
volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15);
}
int8_t const* wave = (int8_t*) regs + index * wave_size;
if ( index == osc_count - 1 )
wave -= wave_size; // last two oscs share wave
@ -55,24 +55,24 @@ void Scc_Apu::run_until( blip_time_t end_time )
synth.offset( last_time, delta, output );
}
}
blip_time_t time = last_time + osc.delay;
if ( time < end_time )
{
if ( !volume )
{
// maintain phase
blargg_long count = (end_time - time + period - 1) / period;
int32_t count = (end_time - time + period - 1) / period;
osc.phase = (osc.phase + count) & (wave_size - 1);
time += count * period;
}
else
{
int phase = osc.phase;
int last_wave = wave [phase];
phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop
do
{
int amp = wave [phase];
@ -86,7 +86,7 @@ void Scc_Apu::run_until( blip_time_t end_time )
time += period;
}
while ( time < end_time );
osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance
osc.last_amp = wave [phase] * volume;
}

View file

@ -12,36 +12,36 @@ class Scc_Apu {
public:
// Set buffer to generate all sound into, or disable sound if NULL
void output( Blip_Buffer* );
// Reset sound chip
void reset();
// Write to register at specified time
enum { reg_count = 0x90 };
static const int reg_count = 0x90;
void write( blip_time_t time, int reg, int data );
// Run sound to specified time, end current time frame, then start a new
// time frame at time 0. Time frames have no effect on emulation and each
// can be whatever length is convenient.
void end_frame( blip_time_t length );
// Additional features
// Set sound output of specific oscillator to buffer, where index is
// 0 to 4. If buffer is NULL, the specified oscillator is muted.
enum { osc_count = 5 };
static const int osc_count = 5;
void osc_output( int index, Blip_Buffer* );
// Set overall volume (default is 1.0)
void volume( double );
// Set treble equalization (see documentation)
void treble_eq( blip_eq_t const& );
public:
Scc_Apu();
private:
enum { amp_range = 0x8000 };
static const unsigned int amp_range = 0x8000;
struct osc_t
{
int delay;
@ -53,7 +53,7 @@ private:
blip_time_t last_time;
unsigned char regs [reg_count];
Blip_Synth<blip_med_quality,1> synth;
void run_until( blip_time_t );
};
@ -96,10 +96,10 @@ inline Scc_Apu::Scc_Apu()
inline void Scc_Apu::reset()
{
last_time = 0;
for ( int i = 0; i < osc_count; i++ )
memset( &oscs [i], 0, offsetof (osc_t,output) );
memset( regs, 0, sizeof regs );
}

View file

@ -23,12 +23,12 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
blargg_err_t Gme_File::load_m3u_( blargg_err_t err )
{
require( raw_track_count_ ); // file must be loaded first
if ( !err )
{
if ( playlist.size() )
track_count_ = playlist.size();
int line = playlist.first_error();
if ( line )
{
@ -38,7 +38,7 @@ blargg_err_t Gme_File::load_m3u_( blargg_err_t err )
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 );
@ -60,8 +60,6 @@ gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size )
return me->load_m3u( in );
}
static char* skip_white( char* in )
{
while ( *in == ' ' )
@ -69,7 +67,7 @@ static char* skip_white( char* in )
return in;
}
inline unsigned from_dec( unsigned n ) { return n - '0'; }
static inline unsigned from_dec( unsigned n ) { return n - '0'; }
static char* parse_filename( char* in, M3u_Playlist::entry_t& entry )
{
@ -81,7 +79,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry )
int c = *in;
if ( !c ) break;
in++;
if ( c == ',' ) // commas in filename
{
char* p = skip_white( in );
@ -91,7 +89,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry )
break;
}
}
if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix
{
entry.type = ++in;
@ -104,7 +102,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry )
}
break;
}
if ( c == '\\' ) // \ prefix for special characters
{
c = *in;
@ -122,16 +120,16 @@ static char* next_field( char* in, int* result )
while ( 1 )
{
in = skip_white( in );
if ( !*in )
break;
if ( *in == ',' )
{
in++;
break;
}
*result = 1;
in++;
}
@ -176,7 +174,7 @@ static char* parse_int( char* in, int* out, int* result )
}
// Returns 16 or greater if not hex
inline int from_hex_char( int h )
static inline int from_hex_char( int h )
{
h -= 0x30;
if ( (unsigned) h > 9 )
@ -250,7 +248,7 @@ static char* parse_name( char* in )
int c = *in;
if ( !c ) break;
in++;
if ( c == ',' ) // commas in string
{
char* p = skip_white( in );
@ -260,7 +258,7 @@ static char* parse_name( char* in )
break;
}
}
if ( c == '\\' ) // \ prefix for special characters
{
c = *in;
@ -276,25 +274,25 @@ static char* parse_name( char* 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;
@ -309,7 +307,7 @@ static int parse_line( char* in, M3u_Playlist::entry_t& entry )
if ( entry.loop >= 0 )
{
entry.intro = 0;
if ( *in == '-' ) // trailing '-' means that intro length was specified
if ( *in == '-' ) // trailing '-' means that intro length was specified
{
in++;
entry.intro = entry.loop;
@ -318,15 +316,15 @@ static int parse_line( char* in, M3u_Playlist::entry_t& entry )
}
}
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;
}
@ -337,7 +335,7 @@ static void parse_comment( char* in, M3u_Playlist::info_t& info, char *& last_co
if ( *field != '@' )
while ( *in && *in != ':' )
in++;
if ( *in == ':' )
{
const char* text = skip_white( in + 1 );
@ -395,7 +393,7 @@ static void parse_comment( char* in, M3u_Playlist::info_t& info, char *& last_co
last_comment_value[ len + 2 + field_len ] = 0;
return;
}
if ( first )
info.title = field;
}
@ -411,12 +409,12 @@ blargg_err_t M3u_Playlist::parse_()
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;
@ -437,7 +435,7 @@ blargg_err_t M3u_Playlist::parse_()
if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line
*in++ = 0;
*in++ = 0;
// parse line
if ( *begin == '#' )
{
@ -448,7 +446,7 @@ blargg_err_t M3u_Playlist::parse_()
{
if ( (int) entries.size() <= count )
RETURN_ERR( entries.resize( count * 2 + 64 ) );
if ( !parse_line( begin, entries [count] ) )
count++;
else if ( !first_error_ )
@ -459,10 +457,10 @@ blargg_err_t M3u_Playlist::parse_()
}
if ( count <= 0 )
return "Not an m3u playlist";
if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) )
info_.title = "";
return entries.resize( count );
}

View file

@ -13,11 +13,11 @@ public:
blargg_err_t load( const char* path );
blargg_err_t load( Data_Reader& in );
blargg_err_t load( void const* data, long size );
// Line number of first parse error, 0 if no error. Any lines with parse
// errors are ignored.
int first_error() const { return first_error_; }
struct info_t
{
const char* title;
@ -31,7 +31,7 @@ public:
const char* copyright;
};
info_t const& info() const { return info_; }
struct entry_t
{
const char* file; // filename without stupid ::TYPE suffix
@ -48,15 +48,15 @@ public:
};
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_();
};

View file

@ -107,7 +107,7 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count )
{
require( !(count & 1) ); // count must be even
count = (unsigned) count / 2;
long avail = bufs [0].samples_avail();
if ( count > avail )
count = avail;
@ -136,7 +136,7 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count )
bufs [1].remove_samples( count );
bufs [2].remove_samples( count );
}
// to do: this might miss opportunities for optimization
if ( !bufs [0].samples_avail() )
{
@ -144,89 +144,89 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count )
stereo_added = 0;
}
}
return count * 2;
}
void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count )
void Stereo_Buffer::mix_stereo( blip_sample_t* out_, int32_t count )
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [1] );
BLIP_READER_BEGIN( left, bufs [1] );
BLIP_READER_BEGIN( right, bufs [2] );
BLIP_READER_BEGIN( center, bufs [0] );
for ( ; count; --count )
{
int c = BLIP_READER_READ( center );
blargg_long l = c + BLIP_READER_READ( left );
blargg_long r = c + BLIP_READER_READ( right );
int32_t l = c + BLIP_READER_READ( left );
int32_t r = c + BLIP_READER_READ( right );
if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24);
BLIP_READER_NEXT( center, bass );
if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24);
BLIP_READER_NEXT( left, bass );
BLIP_READER_NEXT( right, bass );
out [0] = l;
out [1] = r;
out += 2;
}
BLIP_READER_END( center, bufs [0] );
BLIP_READER_END( right, bufs [2] );
BLIP_READER_END( left, bufs [1] );
}
void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count )
void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, int32_t count )
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [1] );
BLIP_READER_BEGIN( left, bufs [1] );
BLIP_READER_BEGIN( right, bufs [2] );
for ( ; count; --count )
{
blargg_long l = BLIP_READER_READ( left );
int32_t l = BLIP_READER_READ( left );
if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24);
blargg_long r = BLIP_READER_READ( right );
int32_t r = BLIP_READER_READ( right );
if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24);
BLIP_READER_NEXT( left, bass );
BLIP_READER_NEXT( right, bass );
out [0] = l;
out [1] = r;
out += 2;
}
BLIP_READER_END( right, bufs [2] );
BLIP_READER_END( left, bufs [1] );
}
void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count )
void Stereo_Buffer::mix_mono( blip_sample_t* out_, int32_t count )
{
blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [0] );
BLIP_READER_BEGIN( center, bufs [0] );
for ( ; count; --count )
{
blargg_long s = BLIP_READER_READ( center );
int32_t s = BLIP_READER_READ( center );
if ( (int16_t) s != s )
s = 0x7FFF - (s >> 24);
BLIP_READER_NEXT( center, bass );
out [0] = s;
out [1] = s;
out += 2;
}
BLIP_READER_END( center, bufs [0] );
}

View file

@ -13,10 +13,10 @@ class Multi_Buffer {
public:
Multi_Buffer( int samples_per_frame );
virtual ~Multi_Buffer() { }
// Set the number of channels available
virtual blargg_err_t set_channel_count( int );
// Get indexed channel, from 0 to channel count - 1
struct channel_t {
Blip_Buffer* center;
@ -26,31 +26,31 @@ public:
enum { type_index_mask = 0xFF };
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
virtual channel_t channel( int index, int type ) = 0;
// See Blip_Buffer.h
virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0;
virtual void clock_rate( long ) = 0;
virtual void bass_freq( int ) = 0;
virtual void clear() = 0;
long sample_rate() const;
// Length of buffer, in milliseconds
int length() const;
// See Blip_Buffer.h
virtual void end_frame( blip_time_t ) = 0;
// Number of samples per output frame (1 = mono, 2 = stereo)
int samples_per_frame() const;
// Count of changes to channel configuration. Incremented whenever
// a change is made to any of the Blip_Buffers for any channel.
unsigned channels_changed_count() { return channels_changed_count_; }
// See Blip_Buffer.h
virtual long read_samples( blip_sample_t*, long ) = 0;
virtual long samples_avail() const = 0;
public:
BLARGG_DISABLE_NOTHROW
protected:
@ -59,7 +59,7 @@ private:
// noncopyable
Multi_Buffer( const Multi_Buffer& );
Multi_Buffer& operator = ( const Multi_Buffer& );
unsigned channels_changed_count_;
long sample_rate_;
int length_;
@ -73,7 +73,7 @@ class Mono_Buffer : public Multi_Buffer {
public:
// Buffer used for all channels
Blip_Buffer* center() { return &buf; }
public:
Mono_Buffer();
~Mono_Buffer();
@ -90,12 +90,12 @@ public:
// Uses three buffers (one for center) and outputs stereo sample pairs.
class Stereo_Buffer : public Multi_Buffer {
public:
// Buffers used for all channels
Blip_Buffer* center() { return &bufs [0]; }
Blip_Buffer* left() { return &bufs [1]; }
Blip_Buffer* right() { return &bufs [2]; }
public:
Stereo_Buffer();
~Stereo_Buffer();
@ -105,20 +105,20 @@ public:
void clear();
channel_t channel( int, int ) { return chan; }
void end_frame( blip_time_t );
long samples_avail() const { return bufs [0].samples_avail() * 2; }
long read_samples( blip_sample_t*, long );
private:
enum { buf_count = 3 };
Blip_Buffer bufs [buf_count];
channel_t chan;
int stereo_added;
int was_stereo;
void mix_stereo_no_center( blip_sample_t*, blargg_long );
void mix_stereo( blip_sample_t*, blargg_long );
void mix_mono( blip_sample_t*, blargg_long );
void mix_stereo_no_center( blip_sample_t*, int32_t );
void mix_stereo( blip_sample_t*, int32_t );
void mix_mono( blip_sample_t*, int32_t );
};
// Silent_Buffer generates no samples, useful where no sound is wanted

View file

@ -19,10 +19,10 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const silence_max = 6; // seconds
int const silence_threshold = 0x10;
long const fade_block_size = 512;
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
static int const silence_max = 6; // seconds
static int const silence_threshold = 0x10;
static long const fade_block_size = 512;
static int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
using std::min;
using std::max;
@ -34,6 +34,7 @@ void Music_Emu::clear_track_vars()
{
current_track_ = -1;
out_time = 0;
out_time_scaled = 0;
emu_time = 0;
emu_track_ended_ = true;
track_ended_ = true;
@ -60,14 +61,14 @@ Music_Emu::Music_Emu()
mute_mask_ = 0;
tempo_ = 1.0;
gain_ = 1.0;
// defaults
max_initial_silence = 2;
silence_lookahead = 3;
ignore_silence_ = false;
equalizer_.treble = -1.0;
equalizer_.bass = 60;
emu_autoload_playback_limit_ = true;
static const char* const names [] = {
@ -137,6 +138,11 @@ void Music_Emu::mute_voices( int mask )
mute_voices_( mask );
}
void Music_Emu::disable_echo( bool disable )
{
disable_echo_( disable );
}
void Music_Emu::set_tempo( double t )
{
require( sample_rate() ); // sample rate must be set first
@ -157,15 +163,15 @@ void Music_Emu::post_load_()
blargg_err_t Music_Emu::start_track( int track )
{
clear_track_vars();
int remapped = track;
RETURN_ERR( remap_track_( &remapped ) );
current_track_ = track;
RETURN_ERR( start_track_( remapped ) );
emu_track_ended_ = false;
track_ended_ = false;
if ( !ignore_silence_ )
{
// play until non-silence or end of track
@ -175,11 +181,12 @@ blargg_err_t Music_Emu::start_track( int track )
if ( buf_remain | (int) emu_track_ended_ )
break;
}
emu_time = buf_remain;
out_time = 0;
silence_time = 0;
silence_count = 0;
emu_time = buf_remain;
out_time = 0;
out_time_scaled = 0;
silence_time = 0;
silence_count = 0;
}
return track_ended() ? warning() : 0;
}
@ -205,9 +212,9 @@ void Music_Emu::set_autoload_playback_limit( bool do_autoload_limit )
// Tell/Seek
blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const
int32_t Music_Emu::msec_to_samples( int32_t msec ) const
{
blargg_long sec = msec / 1000;
int32_t sec = msec / 1000;
msec -= sec * 1000;
return (sec * sample_rate() + msec * sample_rate() / 1000) * out_channels();
}
@ -219,11 +226,16 @@ long Music_Emu::tell_samples() const
long Music_Emu::tell() const
{
blargg_long rate = sample_rate() * out_channels();
blargg_long sec = out_time / rate;
int32_t rate = sample_rate() * out_channels();
int32_t sec = out_time / rate;
return sec * 1000 + (out_time - sec * rate) * 1000 / rate;
}
long Music_Emu::tell_scaled() const
{
return out_time_scaled / (sample_rate() / 1000.0);
}
blargg_err_t Music_Emu::seek_samples( long time )
{
if ( time < out_time )
@ -236,31 +248,43 @@ blargg_err_t Music_Emu::seek( long msec )
return seek_samples( msec_to_samples( msec ) );
}
blargg_err_t Music_Emu::seek_scaled( long msec )
{
require( tempo_ > 0 );
int32_t frames = (msec / 1000.0) * sample_rate();
if ( frames < out_time_scaled )
RETURN_ERR( start_track( current_track_ ) );
int samples_to_skip = (frames - out_time_scaled) * out_channels() / tempo_;
samples_to_skip += samples_to_skip % out_channels();
return skip( samples_to_skip );
}
blargg_err_t Music_Emu::skip( long count )
{
require( current_track() >= 0 ); // start_track() must have been called already
out_time += count;
out_time_scaled += count * tempo_ / out_channels();
// remove from silence and buf first
{
long n = min( count, silence_count );
silence_count -= n;
count -= n;
n = min( count, buf_remain );
buf_remain -= n;
count -= n;
}
if ( count && !emu_track_ended_ )
{
emu_time += count;
end_track_if_error( skip_( count ) );
}
if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended
track_ended_ |= emu_track_ended_;
return 0;
}
@ -272,16 +296,16 @@ blargg_err_t Music_Emu::skip_( long count )
{
int saved_mute = mute_mask_;
mute_voices( ~0 );
while ( count > threshold / 2 && !emu_track_ended_ )
{
RETURN_ERR( play_( buf_size, buf.begin() ) );
count -= buf_size;
}
mute_voices( saved_mute );
}
while ( count && !emu_track_ended_ )
{
long n = buf_size;
@ -302,7 +326,7 @@ void Music_Emu::set_fade( long start_msec, long length_msec )
}
// unit / pow( 2.0, (double) x / step )
static int int_log( blargg_long x, int step, int unit )
static int int_log( int32_t x, int step, int unit )
{
int shift = x / step;
int fraction = (x - shift * step) * unit / step;
@ -325,7 +349,7 @@ void Music_Emu::handle_fade( long out_count, sample_t* out )
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 )
{
@ -386,33 +410,33 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out )
{
require( current_track() >= 0 );
require( out_count % out_channels() == 0 );
assert( emu_time >= out_time );
// prints nifty graph of how far ahead we are when searching for silence
//debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
long pos = 0;
if ( silence_count )
{
// during a run of silence, run emulator at >=2x speed so it gets ahead
long ahead_time = silence_lookahead * (out_time + out_count - silence_time) + silence_time;
while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) )
while ( emu_time < ahead_time && !(buf_remain | static_cast<long>(emu_track_ended_)) )
fill_buf();
// fill with silence
pos = min( silence_count, out_count );
memset( out, 0, pos * sizeof *out );
silence_count -= pos;
if ( emu_time - silence_time > silence_max * out_channels() * sample_rate() )
if ( !ignore_silence_ && emu_time - silence_time > silence_max * out_channels() * sample_rate() )
{
track_ended_ = emu_track_ended_ = true;
silence_count = 0;
buf_remain = 0;
}
}
if ( buf_remain )
{
// empty silence buf
@ -421,30 +445,31 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out )
buf_remain -= n;
pos += n;
}
// generate remaining samples normally
long remain = out_count - pos;
if ( remain )
{
emu_play( remain, out + pos );
track_ended_ |= emu_track_ended_;
if ( !ignore_silence_ || out_time > fade_start )
{
// check end for a new run of silence
long silence = count_silence( out + pos, remain );
if ( silence < remain )
silence_time = emu_time - silence;
if ( emu_time - silence_time >= buf_size )
fill_buf(); // cause silence detection on next play()
}
}
if ( fade_start >= 0 && out_time > fade_start )
handle_fade( out_count, out );
}
out_time += out_count;
out_time_scaled += out_count * tempo_ / out_channels();
return 0;
}
@ -459,3 +484,4 @@ void Gme_Info_::mute_voices_( int ) { check( false ); }
void Gme_Info_::set_tempo_( double ) { }
blargg_err_t Gme_Info_::start_track_( int ) { return "Use full emulator for playback"; }
blargg_err_t Gme_Info_::play_( long, sample_t* ) { return "Use full emulator for playback"; }

View file

@ -18,55 +18,61 @@ public:
// default implementation of Music_Emu always returns not supported error (i.e. no multichannel support by default)
// derived emus must override this if they support multichannel rendering
virtual blargg_err_t set_multi_channel( bool is_enabled );
// Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t start_track( int );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track.
typedef short sample_t;
blargg_err_t play( long count, sample_t* buf );
// Informational
// Sample rate sound is generated at
long sample_rate() const;
// Index of current track or -1 if one hasn't been started
int current_track() const;
// Number of voices used by currently loaded file
int voice_count() const;
// Names of voices
const char** voice_names() const;
bool multi_channel() const;
// Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track
long tell() const;
// Number of samples generated since beginning of track
long tell_samples() const;
// Number of milliseconds played since beginning of track (scaled with tempo).
long tell_scaled() const;
// Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t seek( long msec );
// Equivalent to restarting track then skipping n samples
blargg_err_t seek_samples( long n );
// Seek to new time in track (scaled with tempo).
blargg_err_t seek_scaled( long msec );
// Skip n samples
blargg_err_t skip( long n );
// True if a track has reached its end
bool track_ended() const;
// Set start time and length of track fade out. Once fade ends track_ended() returns
// true. Fade time can be changed while track is playing.
void set_fade( long start_msec, long length_msec = 8000 );
// Controls whether or not to automatically load and obey track length
// metadata for supported emulators.
//
@ -76,45 +82,48 @@ public:
// Disable automatic end-of-track detection and skipping of silence at beginning
void ignore_silence( bool disable = true );
// Info for current track
using Gme_File::track_info;
blargg_err_t track_info( track_info_t* out ) const;
// Sound customization
// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed.
// Track length as returned by track_info() assumes a tempo of 1.0.
void set_tempo( double );
// Mute/unmute voice i, where voice 0 is first voice
void mute_voice( int index, bool mute = true );
// Set muting state of all voices at once using a bit mask, where -1 mutes them all,
// 0 unmutes them all, 0x01 mutes just the first voice, etc.
void mute_voices( int mask );
// Disables echo effect at SPC files
void disable_echo( bool disable );
// Change overall output amplitude, where 1.0 results in minimal clamping.
// Must be called before set_sample_rate().
void set_gain( double );
// Request use of custom multichannel buffer. Only supported by "classic" emulators;
// on others this has no effect. Should be called only once *before* set_sample_rate().
virtual void set_buffer( Multi_Buffer* ) { }
// Enables/disables accurate emulation options, if any are supported. Might change
// equalizer settings.
void enable_accuracy( bool enable = true );
// Sound equalization (treble/bass)
// Frequency equalizer parameters (see gme.txt)
// See gme.h for definition of struct gme_equalizer_t.
typedef gme_equalizer_t equalizer_t;
// Current frequency equalizater parameters
equalizer_t const& equalizer() const;
// Set frequency equalizer parameters
void set_equalizer( equalizer_t const& );
@ -125,10 +134,10 @@ public:
0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
return e;
}
// Equalizer settings for TV speaker
static equalizer_t const tv_eq;
public:
Music_Emu();
~Music_Emu();
@ -142,13 +151,14 @@ protected:
double tempo() const { return tempo_; }
void remute_voices();
blargg_err_t set_multi_channel_( bool is_enabled );
virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0;
virtual void set_equalizer_( equalizer_t const& ) { }
virtual void enable_accuracy_( bool /* enable */ ) { }
virtual void mute_voices_( int mask ) = 0;
virtual void set_tempo_( double ) = 0;
virtual blargg_err_t start_track_( int ) = 0; // tempo is set before this
virtual void mute_voices_( int mask );
virtual void disable_echo_( bool /* disable */);
virtual void set_tempo_( double );
virtual blargg_err_t start_track_( int ); // tempo is set before this
virtual blargg_err_t play_( long count, sample_t* out ) = 0;
virtual blargg_err_t skip_( long count );
protected:
@ -170,23 +180,24 @@ private:
int out_channels() const { return this->multi_channel() ? 2*8 : 2; }
long sample_rate_;
blargg_long msec_to_samples( blargg_long msec ) const;
int32_t msec_to_samples( int32_t msec ) const;
// track-specific
int current_track_;
blargg_long out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
int32_t out_time; // number of samples played since start of track
int32_t out_time_scaled; // number of samples played since start of track (scaled with tempo)
int32_t emu_time; // number of samples emulator has generated since start of track
bool emu_track_ended_; // emulator has reached end of track
bool emu_autoload_playback_limit_; // whether to load and obey track length by default
volatile bool track_ended_;
void clear_track_vars();
void end_track_if_error( blargg_err_t );
// fading
blargg_long fade_start;
int32_t fade_start;
int fade_step;
void handle_fade( long count, sample_t* out );
// silence detection
int silence_lookahead; // speed to run emulator when looking ahead for silence
bool ignore_silence_;
@ -197,7 +208,7 @@ private:
blargg_vector<sample_t> buf;
void fill_buf();
void emu_play( long count, sample_t* out );
Multi_Buffer* effects_buffer;
friend Music_Emu* gme_internal_new_emu_( gme_type_t, int, bool );
friend void gme_set_stereo_depth( Music_Emu*, double );
@ -233,7 +244,12 @@ inline void Music_Emu::enable_accuracy( bool b ) { enable_accuracy_( 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::ignore_silence( bool b ) { ignore_silence_ = b; }
inline blargg_err_t Music_Emu::start_track_( int ) { return 0; }
inline blargg_err_t Music_Emu::start_track_( int track )
{
if ( type()->track_count == 1 )
return load_mem_( track_pos( track ), track_size( track ) );
return 0;
}
inline void Music_Emu::set_voice_names( const char* const* names )
{
@ -243,6 +259,8 @@ inline void Music_Emu::set_voice_names( const char* const* names )
inline void Music_Emu::mute_voices_( int ) { }
inline void Music_Emu::disable_echo_( bool ) { }
inline void Music_Emu::set_gain( double g )
{
assert( !sample_rate() ); // you must set gain before setting sample rate

View file

@ -15,7 +15,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const amp_range = 15;
static int const amp_range = 15;
Nes_Apu::Nes_Apu() :
square1( &square_synth ),
@ -25,13 +25,13 @@ Nes_Apu::Nes_Apu() :
dmc.apu = this;
dmc.prg_reader = NULL;
irq_notifier_ = NULL;
oscs [0] = &square1;
oscs [1] = &square2;
oscs [2] = &triangle;
oscs [3] = &noise;
oscs [4] = &dmc;
output( NULL );
volume( 1.0 );
reset( false );
@ -49,12 +49,12 @@ void Nes_Apu::enable_nonlinear( double v )
{
dmc.nonlinear = true;
square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
triangle.synth.volume( 3.0 * tnd );
noise.synth.volume( 2.0 * tnd );
dmc.synth.volume( tnd );
square1 .last_amp = 0;
square2 .last_amp = 0;
triangle.last_amp = 0;
@ -89,13 +89,13 @@ 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;
@ -104,10 +104,10 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac )
frame_delay = 1;
write_register( 0, 0x4017, 0x00 );
write_register( 0, 0x4015, 0x00 );
for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ )
write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
dmc.dac = initial_dmc_dac;
if ( !dmc.nonlinear )
triangle.last_amp = 15;
@ -124,7 +124,7 @@ void Nes_Apu::irq_changed()
else if ( new_irq > next_irq ) {
new_irq = next_irq;
}
if ( new_irq != earliest_irq_ ) {
earliest_irq_ = new_irq;
if ( irq_notifier_ )
@ -148,17 +148,17 @@ void Nes_Apu::run_until( nes_time_t end_time )
void Nes_Apu::run_until_( nes_time_t end_time )
{
require( end_time >= last_time );
if ( end_time == last_time )
return;
if ( last_dmc_time < end_time )
{
nes_time_t start = last_dmc_time;
last_dmc_time = end_time;
dmc.run( start, end_time );
}
while ( true )
{
// earlier of next frame time or end time
@ -166,17 +166,17 @@ void Nes_Apu::run_until_( nes_time_t end_time )
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++ )
@ -193,30 +193,30 @@ void Nes_Apu::run_until_( nes_time_t end_time )
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();
@ -239,7 +239,7 @@ void Nes_Apu::end_frame( nes_time_t end_time )
{
if ( end_time > last_time )
run_until_( end_time );
if ( dmc.nonlinear )
{
zero_apu_osc( &square1, last_time );
@ -248,14 +248,14 @@ void Nes_Apu::end_frame( nes_time_t end_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 );
@ -275,7 +275,7 @@ void Nes_Apu::end_frame( nes_time_t end_time )
static const unsigned char length_table [0x20] = {
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06,
0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E,
0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E
};
@ -284,23 +284,23 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
{
require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
require( (unsigned) data <= 0xFF );
// Ignore addresses outside range
if ( unsigned (addr - start_addr) > end_addr - start_addr )
return;
run_until_( time );
if ( addr < 0x4014 )
{
// Write to channel
int osc_index = (addr - start_addr) >> 2;
Nes_Osc* osc = oscs [osc_index];
int reg = addr & 3;
osc->regs [reg] = data;
osc->reg_written [reg] = true;
if ( osc_index == 4 )
{
// handle DMC specially
@ -311,7 +311,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
// 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;
@ -323,10 +323,10 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
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) ) {
@ -336,7 +336,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
else if ( !(old_enables & 0x10) ) {
dmc.start(); // dmc just enabled
}
if ( recalc_irq )
irq_changed();
}
@ -344,15 +344,15 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
{
// 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
@ -361,7 +361,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
if ( irq_enabled )
next_irq = time + frame_delay + frame_period * 3 + 1;
}
irq_changed();
}
}
@ -369,23 +369,23 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
int Nes_Apu::read_status( nes_time_t time )
{
run_until_( time - 1 );
int result = (dmc.irq_flag << 7) | (irq_flag << 6);
for ( int i = 0; i < osc_count; i++ )
if ( oscs [i]->length_counter )
result |= 1 << i;
run_until_( time );
if ( irq_flag )
{
result |= 0x40;
irq_flag = false;
irq_changed();
}
//debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result );
return result;
}

View file

@ -6,7 +6,7 @@
#include "blargg_common.h"
typedef blargg_long nes_time_t; // CPU clock cycle count
typedef int32_t nes_time_t; // CPU clock cycle count
typedef unsigned nes_addr_t; // 16-bit memory address
#include "Nes_Oscs.h"
@ -18,30 +18,30 @@ class Nes_Apu {
public:
// Set buffer to generate all sound into, or disable sound if NULL
void output( Blip_Buffer* );
// Set memory reader callback used by DMC oscillator to fetch samples.
// When callback is invoked, 'user_data' is passed unchanged as the
// first parameter.
void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL );
// All time values are the number of CPU clock cycles relative to the
// beginning of the current time frame. Before resetting the CPU clock
// count, call end_frame( last_cpu_time ).
// Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
enum { start_addr = 0x4000 };
enum { end_addr = 0x4017 };
static const unsigned int start_addr = 0x4000;
static const unsigned int end_addr = 0x4017;
void write_register( nes_time_t, nes_addr_t, int data );
// Read from status register at 0x4015
enum { status_addr = 0x4015 };
static const unsigned int status_addr = 0x4015;
int read_status( nes_time_t );
// Run all oscillators up to specified time, end current time frame, then
// start a new time frame at time 0. Time frames have no effect on emulation
// and each can be whatever length is convenient.
void end_frame( nes_time_t );
// Additional optional features (can be ignored without any problem)
// Reset internal frame counter, registers, and all oscillators.
@ -49,51 +49,51 @@ public:
// Set the DMC oscillator's initial DAC value to initial_dmc_dac without
// any audible click.
void reset( bool pal_mode = false, int initial_dmc_dac = 0 );
// Adjust frame period
void set_tempo( double );
// Save/load exact emulation state
void save_state( apu_state_t* out ) const;
void load_state( apu_state_t const& );
// Set overall volume (default is 1.0)
void volume( double );
// Set treble equalization (see notes.txt)
void treble_eq( const blip_eq_t& );
// Set sound output of specific oscillator to buffer. If buffer is NULL,
// the specified oscillator is muted and emulation accuracy is reduced.
// The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
// 2) Triangle, 3) Noise, 4) DMC.
enum { osc_count = 5 };
static const int osc_count = 5;
void osc_output( int index, Blip_Buffer* buffer );
// Set IRQ time callback that is invoked when the time of earliest IRQ
// may have changed, or NULL to disable. When callback is invoked,
// 'user_data' is passed unchanged as the first parameter.
void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL );
// Get time that APU-generated IRQ will occur if no further register reads
// or writes occur. If IRQ is already pending, returns irq_waiting. If no
// IRQ will occur, returns no_irq.
enum { no_irq = INT_MAX / 2 + 1 };
enum { irq_waiting = 0 };
static const unsigned int no_irq = INT_MAX / 2 + 1;
static const unsigned int irq_waiting = 0;
nes_time_t earliest_irq( nes_time_t ) const;
// Count number of DMC reads that would occur if 'run_until( t )' were executed.
// If last_read is not NULL, set *last_read to the earliest time that
// 'count_dmc_reads( time )' would result in the same result.
int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const;
// Time when next DMC memory read will occur
nes_time_t next_dmc_read_time() const;
// Run DMC until specified time, so that any DMC memory reads can be
// accounted for (i.e. inserting CPU wait states).
void run_until( nes_time_t );
public:
Nes_Apu();
BLARGG_DISABLE_NOTHROW
@ -103,18 +103,18 @@ private:
static double nonlinear_tnd_gain() { return 0.75; }
private:
friend struct Nes_Dmc;
// noncopyable
Nes_Apu( const Nes_Apu& );
Nes_Apu& operator = ( const Nes_Apu& );
Nes_Osc* oscs [osc_count];
Nes_Square square1;
Nes_Square square2;
Nes_Noise noise;
Nes_Triangle triangle;
Nes_Dmc dmc;
double tempo_;
nes_time_t last_time; // has been run until this time in current frame
nes_time_t last_dmc_time;
@ -129,11 +129,11 @@ private:
void (*irq_notifier_)( void* user_data );
void* irq_data;
Nes_Square::Synth square_synth; // shared by squares
void irq_changed();
void state_restored();
void run_until_( nes_time_t );
// TODO: remove
friend class Nes_Core;
};
@ -165,12 +165,12 @@ inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) co
{
return dmc.count_reads( time, last_read );
}
inline nes_time_t Nes_Dmc::next_read_time() const
{
if ( length_counter == 0 )
return Nes_Apu::no_irq; // not reading
return apu->last_dmc_time + delay + long (bits_remain - 1) * period;
}

View file

@ -53,14 +53,16 @@ inline void Nes_Cpu::set_code_page( int i, void const* p )
state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size );
}
int const st_n = 0x80;
int const st_v = 0x40;
int const st_r = 0x20;
int const st_b = 0x10;
int const st_d = 0x08;
int const st_i = 0x04;
int const st_z = 0x02;
int const st_c = 0x01;
enum {
st_n = 0x80,
st_v = 0x40,
st_r = 0x20,
st_b = 0x10,
st_d = 0x08,
st_i = 0x04,
st_z = 0x02,
st_c = 0x01
};
void Nes_Cpu::reset( void const* unmapped_page )
{
@ -77,12 +79,12 @@ void Nes_Cpu::reset( void const* unmapped_page )
irq_time_ = future_nes_time;
end_time_ = future_nes_time;
error_count_ = 0;
assert( page_size == 0x800 ); // assumes this
blaarg_static_assert( page_size == 0x800, "NES set to use unhandled page size" ); // assumes this
set_code_page( page_count, unmapped_page );
map_code( 0x2000, 0xE000, unmapped_page, true );
map_code( 0x0000, 0x2000, low_mem, true );
blargg_verify_byte_order();
}
@ -92,7 +94,7 @@ void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool
require( start % page_size == 0 );
require( size % page_size == 0 );
require( start + size <= 0x10000 );
unsigned page = start / page_size;
for ( unsigned n = size / page_size; n; --n )
{
@ -121,7 +123,7 @@ bool Nes_Cpu::run( nes_time_t end_time )
this->state = &s;
// even on x86, using s.time in place of s_time was slower
int16_t s_time = s.time;
// registers
uint16_t pc = r.pc;
uint8_t a = r.a;
@ -129,10 +131,10 @@ bool Nes_Cpu::run( nes_time_t end_time )
uint8_t y = r.y;
uint16_t sp;
SET_SP( r.sp );
// status flags
#define IS_NEG (nz & 0x8080)
#define CALC_STATUS( out ) do {\
out = status & (st_v | st_d | st_i);\
out |= ((nz >> 8) | nz) & st_n;\
@ -146,7 +148,7 @@ bool Nes_Cpu::run( nes_time_t end_time )
c = nz;\
nz |= ~in & st_z;\
} while ( 0 )
uint8_t status;
uint16_t c; // carry set if (c & 0x100) != 0
uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
@ -154,22 +156,22 @@ bool Nes_Cpu::run( nes_time_t end_time )
uint8_t temp = r.status;
SET_STATUS( temp );
}
goto loop;
dec_clock_loop:
s_time--;
loop:
check( (unsigned) GET_SP() < 0x100 );
check( (unsigned) pc < 0x10000 );
check( (unsigned) a < 0x100 );
check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 );
check( -32768 <= s_time && s_time < 32767 );
uint8_t const* instr = s.code_map [pc >> page_bits];
uint8_t opcode;
// TODO: eliminate this special case
#if BLARGG_NONPORTABLE
opcode = instr [pc];
@ -180,7 +182,7 @@ loop:
opcode = *instr++;
pc++;
#endif
static uint8_t const clock_table [256] =
{// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
@ -200,16 +202,16 @@ loop:
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
}; // 0x00 was 7 and 0xF2 was 2
uint16_t data;
#if !BLARGG_CPU_X86
if ( s_time >= 0 )
goto out_of_time;
s_time += clock_table [opcode];
data = *instr;
switch ( opcode )
{
#else
@ -218,9 +220,9 @@ loop:
if ( (s_time += data) >= 0 )
goto possibly_out_of_time;
almost_out_of_time:
data = *instr;
switch ( opcode )
{
possibly_out_of_time:
@ -246,12 +248,12 @@ possibly_out_of_time:
out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
cross( temp );\
}
#define IND_X( out ) {\
uint16_t temp = data + x;\
out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\
}
#define ARITH_ADDR_MODES( op )\
case op - 0x04: /* (ind,x) */\
IND_X( data )\
@ -297,15 +299,15 @@ imm##op:
a = nz = READ_LOW( uint8_t (data + x) );
pc++;
goto loop;
case 0xA5: // LDA zp
a = nz = READ_LOW( data );
pc++;
goto loop;
case 0xD0: // BNE
BRANCH( (uint8_t) nz );
case 0x20: { // JSR
uint16_t temp = pc + 1;
pc = GET_ADDR();
@ -314,37 +316,37 @@ imm##op:
WRITE_LOW( sp, temp );
goto loop;
}
case 0x4C: // JMP abs
pc = GET_ADDR();
goto loop;
case 0xE8: // INX
INC_DEC_XY( x, 1 )
case 0x10: // BPL
BRANCH( !IS_NEG )
ARITH_ADDR_MODES( 0xC5 ) // CMP
nz = a - data;
pc++;
c = ~nz;
nz &= 0xFF;
goto loop;
case 0x30: // BMI
BRANCH( IS_NEG )
case 0xF0: // BEQ
BRANCH( !(uint8_t) nz );
case 0x95: // STA zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0x85: // STA zp
pc++;
WRITE_LOW( data, a );
goto loop;
case 0xC8: // INY
INC_DEC_XY( y, 1 )
@ -352,12 +354,12 @@ imm##op:
y = a;
nz = a;
goto loop;
case 0x98: // TYA
a = y;
nz = y;
goto loop;
case 0xAD:{// LDA abs
unsigned addr = GET_ADDR();
pc += 2;
@ -365,16 +367,16 @@ imm##op:
a = nz;
goto loop;
}
case 0x60: // RTS
pc = 1 + READ_LOW( sp );
pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
sp = (sp - 0xFE) | 0x100;
goto loop;
{
uint16_t addr;
case 0x99: // STA abs,Y
addr = y + GET_ADDR();
pc += 2;
@ -384,7 +386,7 @@ imm##op:
goto loop;
}
goto sta_ptr;
case 0x8D: // STA abs
addr = GET_ADDR();
pc += 2;
@ -394,7 +396,7 @@ imm##op:
goto loop;
}
goto sta_ptr;
case 0x9D: // STA abs,X (slightly more common than STA abs)
addr = x + GET_ADDR();
pc += 2;
@ -408,19 +410,19 @@ imm##op:
WRITE( addr, a );
CACHE_TIME();
goto loop;
case 0x91: // STA (ind),Y
IND_Y( NO_PAGE_CROSSING, addr )
pc++;
goto sta_ptr;
case 0x81: // STA (ind,X)
IND_X( addr )
pc++;
goto sta_ptr;
}
case 0xA9: // LDA #imm
pc++;
a = data;
@ -430,12 +432,12 @@ imm##op:
// common read instructions
{
uint16_t addr;
case 0xA1: // LDA (ind,X)
IND_X( addr )
pc++;
goto a_nz_read_addr;
case 0xB1:// LDA (ind),Y
addr = READ_LOW( data ) + y;
HANDLE_PAGE_CROSSING( addr );
@ -445,7 +447,7 @@ imm##op:
if ( (addr ^ 0x8000) <= 0x9FFF )
goto loop;
goto a_nz_read_addr;
case 0xB9: // LDA abs,Y
HANDLE_PAGE_CROSSING( data + y );
addr = GET_ADDR() + y;
@ -454,7 +456,7 @@ imm##op:
if ( (addr ^ 0x8000) <= 0x9FFF )
goto loop;
goto a_nz_read_addr;
case 0xBD: // LDA abs,X
HANDLE_PAGE_CROSSING( data + x );
addr = GET_ADDR() + x;
@ -467,39 +469,39 @@ imm##op:
a = nz = READ( addr );
CACHE_TIME();
goto loop;
}
// Branch
case 0x50: // BVC
BRANCH( !(status & st_v) )
case 0x70: // BVS
BRANCH( status & st_v )
case 0xB0: // BCS
BRANCH( c & 0x100 )
case 0x90: // BCC
BRANCH( !(c & 0x100) )
// Load/store
case 0x94: // STY zp,x
data = uint8_t (data + x); // FALLTHRU
case 0x84: // STY zp
pc++;
WRITE_LOW( data, y );
goto loop;
case 0x96: // STX zp,y
data = uint8_t (data + y); // FALLTHRU
case 0x86: // STX zp
pc++;
WRITE_LOW( data, x );
goto loop;
case 0xB6: // LDX zp,y
data = uint8_t (data + y); // FALLTHRU
case 0xA6: // LDX zp
@ -509,7 +511,7 @@ imm##op:
x = data;
nz = data;
goto loop;
case 0xB4: // LDY zp,x
data = uint8_t (data + x); // FALLTHRU
case 0xA4: // LDY zp
@ -519,7 +521,7 @@ imm##op:
y = data;
nz = data;
goto loop;
case 0xBC: // LDY abs,X
data += x;
HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
@ -531,7 +533,7 @@ imm##op:
CACHE_TIME();
goto loop;
}
case 0xBE: // LDX abs,y
data += y;
HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
@ -543,13 +545,13 @@ imm##op:
CACHE_TIME();
goto loop;
}
{
uint8_t temp;
case 0x8C: // STY abs
temp = y;
goto store_abs;
case 0x8E: // STX abs
temp = x;
store_abs:
@ -576,7 +578,7 @@ imm##op:
CACHE_TIME();
goto cpx_data;
}
case 0xE4: // CPX zp
data = READ_LOW( data );/*FALLTHRU*/
case 0xE0: // CPX #imm
@ -586,7 +588,7 @@ imm##op:
c = ~nz;
nz &= 0xFF;
goto loop;
case 0xCC:{// CPY abs
unsigned addr = GET_ADDR();
pc++;
@ -595,7 +597,7 @@ imm##op:
CACHE_TIME();
goto cpy_data;
}
case 0xC4: // CPY zp
data = READ_LOW( data );/*FALLTHRU*/
case 0xC0: // CPY #imm
@ -605,24 +607,24 @@ imm##op:
c = ~nz;
nz &= 0xFF;
goto loop;
// Logical
ARITH_ADDR_MODES( 0x25 ) // AND
nz = (a &= data);
pc++;
goto loop;
ARITH_ADDR_MODES( 0x45 ) // EOR
nz = (a ^= data);
pc++;
goto loop;
ARITH_ADDR_MODES( 0x05 ) // ORA
nz = (a |= data);
pc++;
goto loop;
case 0x2C:{// BIT abs
unsigned addr = GET_ADDR();
pc += 2;
@ -634,7 +636,7 @@ imm##op:
nz <<= 8; // result must be zero, even if N bit is set
goto loop;
}
case 0x24: // BIT zp
nz = READ_LOW( data );
pc++;
@ -644,14 +646,14 @@ imm##op:
goto loop;
nz <<= 8; // result must be zero, even if N bit is set
goto loop;
// Add/subtract
ARITH_ADDR_MODES( 0xE5 ) // SBC
case 0xEB: // unofficial equivalent
data ^= 0xFF;
goto adc_imm;
ARITH_ADDR_MODES( 0x65 ) // ADC
adc_imm: {
int16_t carry = c >> 8 & 1;
@ -663,7 +665,7 @@ imm##op:
a = (uint8_t) nz;
goto loop;
}
// Shift/rotate
case 0x4A: // LSR A
@ -689,7 +691,7 @@ imm##op:
a = (uint8_t) nz;
goto loop;
}
case 0x5E: // LSR abs,X
data += x;/*FALLTHRU*/
case 0x4E: // LSR abs
@ -703,11 +705,11 @@ imm##op:
c = temp << 8;
goto rotate_common;
}
case 0x3E: // ROL abs,X
data += x;
goto rol_abs;
case 0x1E: // ASL abs,X
data += x;/*FALLTHRU*/
case 0x0E: // ASL abs
@ -723,15 +725,15 @@ imm##op:
WRITE( data, (uint8_t) nz );
CACHE_TIME();
goto loop;
case 0x7E: // ROR abs,X
data += x;
goto ror_abs;
case 0x76: // ROR zp,x
data = uint8_t (data + x);
goto ror_zp;
case 0x56: // LSR zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0x46: // LSR zp
@ -743,11 +745,11 @@ imm##op:
c = temp << 8;
goto write_nz_zp;
}
case 0x36: // ROL zp,x
data = uint8_t (data + x);
goto rol_zp;
case 0x16: // ASL zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0x06: // ASL zp
@ -757,21 +759,21 @@ imm##op:
nz = c >> 8 & 1;
nz |= (c = READ_LOW( data ) << 1);
goto write_nz_zp;
// Increment/decrement
case 0xCA: // DEX
INC_DEC_XY( x, -1 )
case 0x88: // DEY
INC_DEC_XY( y, -1 )
case 0xF6: // INC zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0xE6: // INC zp
nz = 1;
goto add_nz_zp;
case 0xD6: // DEC zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0xC6: // DEC zp
@ -782,21 +784,21 @@ imm##op:
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:
@ -808,14 +810,14 @@ imm##op:
WRITE( data, (uint8_t) nz );
CACHE_TIME();
goto loop;
// Transfer
case 0xAA: // TAX
x = a;
nz = a;
goto loop;
case 0x8A: // TXA
a = x;
nz = x;
@ -824,22 +826,22 @@ imm##op:
case 0x9A: // TXS
SET_SP( x ); // verified (no flag change)
goto loop;
case 0xBA: // TSX
x = nz = GET_SP();
goto loop;
// Stack
case 0x48: // PHA
PUSH( a ); // verified
goto loop;
case 0x68: // PLA
a = nz = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100;
goto loop;
case 0x40:{// RTI
uint8_t temp = READ_LOW( sp );
pc = READ_LOW( 0x100 | (sp - 0xFF) );
@ -849,14 +851,14 @@ imm##op:
SET_STATUS( temp );
if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - irq_time_;
int32_t delta = s.base - irq_time_;
if ( delta <= 0 ) goto loop;
if ( status & st_i ) goto loop;
s_time += delta;
s.base = irq_time_;
goto loop;
}
case 0x28:{// PLP
uint8_t temp = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100;
@ -868,14 +870,14 @@ imm##op:
goto handle_sei;
goto handle_cli;
}
case 0x08: { // PHP
uint8_t temp;
CALC_STATUS( temp );
PUSH( temp | (st_b | st_r) );
goto loop;
}
case 0x6C:{// JMP (ind)
data = GET_ADDR();
check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space
@ -885,32 +887,32 @@ imm##op:
pc |= page [PAGE_OFFSET( data )] << 8;
goto loop;
}
case 0x00: // BRK
goto handle_brk;
// Flags
case 0x38: // SEC
c = (uint16_t) ~0;
goto loop;
case 0x18: // CLC
c = 0;
goto loop;
case 0xB8: // CLV
status &= ~st_v;
goto loop;
case 0xD8: // CLD
status &= ~st_d;
goto loop;
case 0xF8: // SED
status |= st_d;
goto loop;
case 0x58: // CLI
if ( !(status & st_i) )
goto loop;
@ -918,7 +920,7 @@ imm##op:
handle_cli: {
//debug_printf( "CLI at %d\n", TIME );
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - irq_time_;
int32_t delta = s.base - irq_time_;
if ( delta <= 0 )
{
if ( TIME < irq_time_ )
@ -929,38 +931,74 @@ imm##op:
s_time += delta;
if ( s_time < 0 )
goto loop;
if ( delta >= s_time + 1 )
{
s.base += s_time + 1;
s_time = -1;
goto loop;
}
// TODO: implement
delayed_cli:
debug_printf( "Delayed CLI not emulated\n" );
goto loop;
}
case 0x78: // SEI
if ( status & st_i )
goto loop;
status |= st_i;
handle_sei: {
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - end_time_;
int32_t delta = s.base - end_time_;
s.base = end_time_;
s_time += delta;
if ( s_time < 0 )
goto loop;
debug_printf( "Delayed SEI not emulated\n" );
goto loop;
}
// Unofficial
case 0xB3: { // LAX (ind),Y
uint16_t addr = READ_LOW( data ) + y;
HANDLE_PAGE_CROSSING( addr );
addr += 0x100 * READ_LOW( (uint8_t) (data + 1) );
pc++;
a = x = nz = READ_PROG( addr );
if ( (addr ^ 0x8000) <= 0x9FFF )
goto loop;
FLUSH_TIME();
a = x = nz = READ( addr );
CACHE_TIME();
goto loop;
}
case 0x8F: { // SAX abs
uint16_t addr = GET_ADDR();
uint8_t temp = a & x;
pc += 2;
if ( addr <= 0x7FF )
{
WRITE_LOW( addr, temp );
goto loop;
}
FLUSH_TIME();
WRITE( addr, temp );
CACHE_TIME();
goto loop;
}
case 0xCB: // SBX #imm
x = nz = (a & x) - data;
pc++;
c = ~nz;
nz &= 0xFF;
goto loop;
// SKW - Skip word
case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/
@ -971,7 +1009,7 @@ imm##op:
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;
@ -981,9 +1019,9 @@ imm##op:
case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2:
goto stop;
// Unimplemented
case 0xFF: // force 256-entry jump table for optimization purposes
c |= 1;/*FALLTHRU*/
default:
@ -998,31 +1036,29 @@ imm##op:
len = 2;
pc += len;
error_count_++;
if ( (opcode >> 4) == 0x0B )
{
if ( opcode == 0xB3 )
data = READ_LOW( data );
if ( opcode != 0xB7 )
HANDLE_PAGE_CROSSING( data + y );
}
goto loop;
}
assert( false );
int result_;
handle_brk:
pc++;
result_ = 4;
interrupt:
{
s_time += 7;
WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
WRITE_LOW( 0x100 | (sp - 2), pc );
pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
sp = (sp - 3) | 0x100;
uint8_t temp;
CALC_STATUS( temp );
@ -1030,15 +1066,15 @@ interrupt:
if ( result_ )
temp |= st_b; // TODO: incorrectly sets B flag for IRQ
WRITE_LOW( sp, temp );
this->r.status = status |= st_i;
blargg_long delta = s.base - end_time_;
int32_t delta = s.base - end_time_;
if ( delta >= 0 ) goto loop;
s_time += delta;
s.base = end_time_;
goto loop;
}
out_of_time:
pc--;
FLUSH_TIME();
@ -1048,26 +1084,26 @@ out_of_time:
goto interrupt;
if ( s_time < 0 )
goto loop;
stop:
s.time = s_time;
r.pc = pc;
r.sp = GET_SP();
r.a = a;
r.x = x;
r.y = y;
{
uint8_t temp;
CALC_STATUS( temp );
r.status = temp;
}
this->state_ = s;
this->state = &this->state_;
return s_time < 0;
}

View file

@ -6,7 +6,7 @@
#include "blargg_common.h"
typedef blargg_long nes_time_t; // clock cycle count
typedef int32_t nes_time_t; // clock cycle count
typedef unsigned nes_addr_t; // 16-bit address
enum { future_nes_time = INT_MAX / 2 + 1 };
@ -15,19 +15,19 @@ public:
// Clear registers, map low memory and its three mirrors to address 0,
// and mirror unmapped_page in remaining memory
void reset( void const* unmapped_page = 0 );
// Map code memory (memory accessed via the program counter). Start and size
// must be multiple of page_size. If mirror is true, repeats code page
// throughout address range.
enum { page_size = 0x800 };
void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false );
// Access emulated memory as CPU does
uint8_t const* get_code( nes_addr_t );
// 2KB of RAM at address 0
uint8_t low_mem [0x800];
// NES 6502 registers. Not kept updated during a call to run().
struct registers_t {
uint16_t pc;
@ -38,29 +38,29 @@ public:
uint8_t sp;
};
registers_t r;
// Set end_time and run CPU from current time. Returns true if execution
// stopped due to encountering bad_opcode.
bool run( nes_time_t end_time );
// Time of beginning of next instruction to be executed
nes_time_t time() const { return state->time + state->base; }
void set_time( nes_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; }
nes_time_t irq_time() const { return irq_time_; }
void set_irq_time( nes_time_t );
nes_time_t end_time() const { return end_time_; }
void set_end_time( nes_time_t );
// Number of undefined instructions encountered and skipped
void clear_error_count() { error_count_ = 0; }
unsigned long error_count() const { return error_count_; }
// CPU invokes bad opcode handler if it encounters this
enum { bad_opcode = 0xF2 };
public:
Nes_Cpu() { state = &state_; }
enum { page_bits = 11 };
@ -77,7 +77,7 @@ private:
nes_time_t irq_time_;
nes_time_t end_time_;
unsigned long error_count_;
void set_code_page( int, void const* );
inline int update_end_time( nes_time_t end, nes_time_t irq );
};

View file

@ -17,13 +17,13 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include <string.h>
int const fract_range = 65536;
static 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;
@ -33,7 +33,7 @@ void Nes_Fds_Apu::reset()
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
@ -70,19 +70,19 @@ void Nes_Fds_Apu::write_( unsigned addr, int data )
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 )
{
@ -117,36 +117,36 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
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
{
@ -161,7 +161,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
else
regs (0x4084) |= 0x80; // optimization only
}
// envelope
if ( env_time <= end_time )
{
@ -173,13 +173,13 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
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 )
@ -188,7 +188,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
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;
@ -196,7 +196,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
{
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);
@ -205,7 +205,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
new_sweep_bias = 0;
regs (0x4085) = new_sweep_bias;
}
// apply frequency modulation
sweep_bias = (sweep_bias ^ 0x40) - 0x40;
int factor = sweep_bias * sweep_gain;
@ -223,26 +223,26 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
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
@ -254,27 +254,27 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
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 );
}

View file

@ -6,66 +6,70 @@
#define NES_FDS_APU_H
#include "blargg_common.h"
#include "blargg_source.h"
#include "Blip_Buffer.h"
class Nes_Fds_Apu {
public:
// setup
void set_tempo( double );
enum { osc_count = 1 };
static const int osc_count = 1;
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 };
static const unsigned int io_addr = 0x4040;
static const unsigned int 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 );
// FDS has a RAM area at $8000-DFFF
enum { sram_addr = 0x8000 };
byte sram [0x6000];
public:
Nes_Fds_Apu();
void write_( unsigned addr, int data );
BLARGG_DISABLE_NOTHROW
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 };
static const unsigned int wave_size = 0x40;
static const unsigned int master_vol_max = 10;
static const int vol_max = 0x20;
static const unsigned int 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()
static const unsigned int 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<blip_med_quality,1> 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 );
};
@ -76,6 +80,9 @@ inline void Nes_Fds_Apu::volume( double v )
inline void Nes_Fds_Apu::osc_output( int i, Blip_Buffer* buf )
{
#ifdef NDEBUG
(void) i;
#endif
assert( (unsigned) i < osc_count );
output_ = buf;
}
@ -97,24 +104,24 @@ inline void Nes_Fds_Apu::write( blip_time_t time, unsigned addr, int 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;
}

View file

@ -20,10 +20,10 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
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 );
}
@ -41,28 +41,28 @@ unsigned char const Nes_Fme7_Apu::amp_table [16] =
void Nes_Fme7_Apu::run_until( blip_time_t end_time )
{
require( end_time >= last_time );
for ( int index = 0; index < osc_count; index++ )
{
int mode = regs [7] >> index;
int vol_mode = regs [010 + index];
int volume = amp_table [vol_mode & 0x0F];
Blip_Buffer* const osc_output = oscs [index].output;
if ( !osc_output )
continue;
osc_output->set_modified();
// check for unsupported mode
#ifndef NDEBUG
if ( (mode & 011) <= 001 && vol_mode & 0x1F )
debug_printf( "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 +
@ -73,7 +73,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time )
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] )
@ -86,7 +86,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time )
synth.offset( last_time, delta, osc_output );
}
}
blip_time_t time = last_time + delays [index];
if ( time < end_time )
{
@ -100,7 +100,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time )
time += period;
}
while ( time < end_time );
oscs [index].last_amp = (delta + volume) >> 1;
phases [index] = (delta > 0);
}
@ -109,13 +109,13 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time )
// maintain phase when silent
int count = (end_time - time + period - 1) / period;
phases [index] ^= count & 1;
time += (blargg_long) count * period;
time += (int32_t) count * period;
}
}
delays [index] = time - end_time;
}
last_time = end_time;
}

View file

@ -9,7 +9,7 @@
struct fme7_apu_state_t
{
enum { reg_count = 14 };
static const unsigned int reg_count = 14;
uint8_t regs [reg_count];
uint8_t phases [3]; // 0 or 1
uint8_t latch;
@ -23,23 +23,23 @@ public:
void volume( double );
void treble_eq( blip_eq_t const& );
void output( Blip_Buffer* );
enum { osc_count = 3 };
static const int osc_count = 3;
void osc_output( int index, Blip_Buffer* );
void end_frame( blip_time_t );
void save_state( fme7_apu_state_t* ) const;
void load_state( fme7_apu_state_t const& );
// Mask and addresses of registers
enum { addr_mask = 0xE000 };
enum { data_addr = 0xE000 };
enum { latch_addr = 0xC000 };
static const unsigned int addr_mask = 0xE000;
static const unsigned int data_addr = 0xE000;
static const unsigned int 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
@ -47,18 +47,18 @@ 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
static const unsigned int amp_range = 192; // can be any value; this gives best error/quality tradeoff
Blip_Synth<blip_good_quality,1> synth;
void run_until( blip_time_t );
};
@ -102,7 +102,7 @@ inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data )
#endif
return;
}
run_until( time );
regs [latch] = data;
}
@ -111,7 +111,7 @@ 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;
}

View file

@ -12,11 +12,11 @@ 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 osc_output( int i, Blip_Buffer* );
enum { exram_size = 1024 };
unsigned char exram [exram_size];
};
@ -47,10 +47,10 @@ inline void Nes_Mmc5_Apu::write_register( blip_time_t time, unsigned addr, int d
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:
debug_printf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data );

View file

@ -26,11 +26,11 @@ 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];
@ -50,12 +50,12 @@ void Nes_Namco_Apu::output( Blip_Buffer* 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 );
@ -68,7 +68,7 @@ void Nes_Namco_Apu::end_frame( blip_time_t time )
{
if ( time > last_time )
run_until( time );
assert( last_time >= time );
last_time -= time;
}
@ -83,7 +83,7 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time )
if ( !output )
continue;
output->set_modified();
blip_resampled_time_t time =
output->resampled_time( last_time ) + osc.delay;
blip_resampled_time_t end_time = output->resampled_time( nes_end_time );
@ -93,24 +93,24 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time )
const uint8_t* osc_reg = &reg [i * 8 + 0x40];
if ( !(osc_reg [4] & 0xE0) )
continue;
int volume = osc_reg [7] & 15;
if ( !volume )
continue;
blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
int32_t freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0];
if ( freq < 64 * active_oscs )
continue; // prevent low frequencies from excessively delaying freq changes
blip_resampled_time_t period =
output->resampled_duration( 983040 ) / freq * active_oscs;
int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
if ( !wave_size )
continue;
int last_amp = osc.last_amp;
int wave_pos = osc.wave_pos;
do
{
// read wave sample
@ -118,7 +118,7 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time )
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 )
@ -126,20 +126,20 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time )
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;
}

View file

@ -15,24 +15,24 @@ public:
void volume( double );
void treble_eq( const blip_eq_t& );
void output( Blip_Buffer* );
enum { osc_count = 8 };
static const int osc_count = 8;
void osc_output( int index, Blip_Buffer* );
void reset();
void end_frame( blip_time_t );
// Read/write data register is at 0x4800
enum { data_reg_addr = 0x4800 };
static const unsigned int 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 };
static const unsigned int 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
@ -40,23 +40,23 @@ private:
// noncopyable
Nes_Namco_Apu( const Nes_Namco_Apu& );
Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& );
struct Namco_Osc {
blargg_long delay;
int32_t 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 };
static const int reg_count = 0x80;
uint8_t reg [reg_count];
Blip_Synth<blip_good_quality,15> synth;
uint8_t& access();
void run_until( blip_time_t );
};

View file

@ -48,20 +48,20 @@ int Nes_Envelope::volume() const
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;
@ -71,7 +71,7 @@ void Nes_Square::clock_sweep( int negative_adjust )
}
}
}
if ( reg_written [1] ) {
reg_written [1] = false;
sweep_delay = (sweep >> 4) & 7;
@ -87,7 +87,7 @@ inline nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_ti
{
int count = (remain + timer_period - 1) / timer_period;
phase = (phase + count) & (phase_range - 1);
time += (blargg_long) count * timer_period;
time += (int32_t) count * timer_period;
}
return time;
}
@ -96,19 +96,19 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time )
{
const int period = this->period();
const int timer_period = (period + 1) * 2;
if ( !output )
{
delay = maintain_phase( time + delay, end_time, timer_period ) - end_time;
return;
}
output->set_modified();
int offset = period >> (regs [1] & shift_mask);
if ( regs [1] & negate_flag )
offset = 0;
const int volume = this->volume();
if ( volume == 0 || period < 8 || (period + offset) >= 0x800 )
{
@ -116,7 +116,7 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time )
synth.offset( time, -last_amp, output );
last_amp = 0;
}
time += delay;
time = maintain_phase( time, end_time, timer_period );
}
@ -132,13 +132,13 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time )
}
if ( phase < duty )
amp ^= volume;
{
int delta = update_amp( amp );
if ( delta )
synth.offset( time, delta, output );
}
time += delay;
if ( time < end_time )
{
@ -146,7 +146,7 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time )
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 ) {
@ -156,12 +156,12 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time )
time += timer_period;
}
while ( time < end_time );
last_amp = (delta + volume) >> 1;
this->phase = phase;
}
}
delay = time - end_time;
}
@ -173,7 +173,7 @@ void Nes_Triangle::clock_linear_counter()
linear_counter = regs [0] & 0x7F;
else if ( linear_counter )
linear_counter--;
if ( !(regs [0] & 0x80) )
reg_written [3] = false;
}
@ -196,7 +196,7 @@ inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_
int count = (remain + timer_period - 1) / timer_period;
phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1);
phase++;
time += (blargg_long) count * timer_period;
time += (int32_t) count * timer_period;
}
return time;
}
@ -212,16 +212,16 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time )
delay = maintain_phase( time, end_time, timer_period ) - end_time;
return;
}
output->set_modified();
// to do: track phase when period < 3
// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
int delta = update_amp( calc_amp() );
if ( delta )
synth.offset( time, delta, output );
time += delay;
if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 )
{
@ -230,14 +230,14 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time )
else if ( time < end_time )
{
Blip_Buffer* const output = this->output;
int phase = this->phase;
int volume = 1;
if ( phase > phase_range ) {
phase -= phase_range;
volume = -volume;
}
do {
if ( --phase == 0 ) {
phase = phase_range;
@ -246,11 +246,11 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time )
else {
synth.offset_inline( time, volume, output );
}
time += timer_period;
}
while ( time < end_time );
if ( volume < 0 )
phase += phase_range;
this->phase = phase;
@ -273,7 +273,7 @@ void Nes_Dmc::reset()
next_irq = Nes_Apu::no_irq;
irq_flag = false;
irq_enabled = false;
Nes_Osc::reset();
period = 0x1AC;
}
@ -294,19 +294,19 @@ 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;
@ -314,7 +314,7 @@ int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const
check( count == count_reads( *last_read, NULL ) );
check( count - 1 == count_reads( *last_read - 1, NULL ) );
}
return count;
}
@ -357,7 +357,7 @@ void Nes_Dmc::write_register( int addr, int data )
{
int old_dac = dac;
dac = data & 0x7F;
// adjust last_amp so that "pop" amplitude will be properly non-linear
// with respect to change in dac
int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]);
@ -409,7 +409,7 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
if ( delta )
synth.offset( time, delta, output );
}
time += delay;
if ( time < end_time )
{
@ -426,7 +426,7 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
const int period = this->period;
int bits = this->bits;
int dac = this->dac;
do
{
if ( !silence )
@ -438,9 +438,9 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
synth.offset_inline( time, step, output );
}
}
time += period;
if ( --bits_remain == 0 )
{
bits_remain = 8;
@ -458,7 +458,7 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
}
}
while ( time < end_time );
this->dac = dac;
this->last_amp = dac;
this->bits = bits;
@ -478,7 +478,7 @@ static short const noise_period_table [16] = {
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
@ -486,9 +486,9 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time )
delay = time + (end_time - time + period - 1) / period * period - end_time;
return;
}
output->set_modified();
const int volume = this->volume();
int amp = (noise & 1) ? volume : 0;
{
@ -496,17 +496,17 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time )
if ( delta )
synth.offset( time, delta, output );
}
time += delay;
if ( time < end_time )
{
const int mode_flag = 0x80;
if ( !volume )
{
// round to next multiple of period
time += (end_time - time + period - 1) / period * period;
// approximate noise cycling while muted, by shuffling up noise register
// to do: precise muted noise cycling?
if ( !(regs [2] & mode_flag) ) {
@ -517,35 +517,35 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time )
else
{
Blip_Buffer* const output = this->output;
// using resampled time avoids conversion in synth.offset()
blip_resampled_time_t rperiod = output->resampled_duration( period );
blip_resampled_time_t rtime = output->resampled_time( time );
int noise = this->noise;
int delta = amp * 2 - volume;
const int tap = (regs [2] & mode_flag ? 8 : 13);
do {
int feedback = (noise << tap) ^ (noise << 14);
time += period;
if ( (noise + 1) & 2 ) {
// bits 0 and 1 of noise differ
delta = -delta;
synth.offset_resampled( rtime, delta, output );
}
rtime += rperiod;
noise = (feedback & 0x4000) | (noise >> 1);
}
while ( time < end_time );
last_amp = (delta + volume) >> 1;
this->noise = noise;
}
}
delay = time - end_time;
}

View file

@ -17,7 +17,7 @@ struct Nes_Osc
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);
@ -37,7 +37,7 @@ struct Nes_Envelope : Nes_Osc
{
int envelope;
int env_delay;
void clock_envelope();
int volume() const;
void reset() {
@ -55,12 +55,12 @@ struct Nes_Square : Nes_Envelope
enum { phase_range = 8 };
int phase;
int sweep_delay;
typedef Blip_Synth<blip_good_quality,1> 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() {
@ -78,7 +78,7 @@ struct Nes_Triangle : Nes_Osc
int phase;
int linear_counter;
Blip_Synth<blip_med_quality,1> synth;
int calc_amp() const;
void run( nes_time_t, nes_time_t );
void clock_linear_counter();
@ -96,7 +96,7 @@ struct Nes_Noise : Nes_Envelope
{
int noise;
Blip_Synth<blip_med_quality,1> synth;
void run( nes_time_t, nes_time_t );
void reset() {
noise = 1 << 14;
@ -115,24 +115,24 @@ struct Nes_Dmc : Nes_Osc
int bits;
bool buf_full;
bool silence;
enum { loop_flag = 0x40 };
int dac;
nes_time_t next_irq;
bool irq_enabled;
bool irq_flag;
bool pal_mode;
bool nonlinear;
int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function
void* prg_reader_data;
Nes_Apu* apu;
Blip_Synth<blip_med_quality,1> synth;
void start();
void write_register( int, int );
void run( nes_time_t, nes_time_t );

View file

@ -56,7 +56,7 @@ 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;
}
@ -65,21 +65,21 @@ 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 );
blaarg_static_assert( sizeof (vrc6_apu_state_t) == 20, "VRC APU State layout incorrect!" );
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;
}
@ -94,7 +94,7 @@ void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in )
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];
}
@ -108,11 +108,11 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time )
if ( !output )
return;
output->set_modified();
int volume = osc.regs [0] & 15;
if ( !(osc.regs [2] & 0x80) )
volume = 0;
int gate = osc.regs [0] & 0x80;
int duty = ((osc.regs [0] >> 4) & 7) + 1;
int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp;
@ -122,7 +122,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time )
osc.last_amp += delta;
square_synth.offset( time, delta, output );
}
time += osc.delay;
osc.delay = 0;
int period = osc.period();
@ -131,7 +131,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time )
if ( time < end_time )
{
int phase = osc.phase;
do
{
phase++;
@ -149,7 +149,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time )
time += period;
}
while ( time < end_time );
osc.phase = phase;
}
osc.delay = time - end_time;
@ -163,7 +163,7 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time )
if ( !output )
return;
output->set_modified();
int amp = osc.amp;
int amp_step = osc.regs [0] & 0x3F;
blip_time_t time = last_time;
@ -182,7 +182,7 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time )
{
int period = osc.period() * 2;
int phase = osc.phase;
do
{
if ( --phase == 0 )
@ -190,26 +190,26 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time )
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;
}

View file

@ -21,7 +21,7 @@ public:
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
@ -29,7 +29,7 @@ public:
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
@ -37,7 +37,7 @@ private:
// noncopyable
Nes_Vrc6_Apu( const Nes_Vrc6_Apu& );
Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& );
struct Vrc6_Osc
{
uint8_t regs [3];
@ -46,19 +46,19 @@ private:
int last_amp;
int phase;
int amp; // only used by saw
int period() const
{
return (regs [2] & 0x0F) * 0x100L + regs [1] + 1;
}
};
Vrc6_Osc oscs [osc_count];
blip_time_t last_time;
Blip_Synth<blip_med_quality,1> saw_synth;
Blip_Synth<blip_good_quality,1> square_synth;
void run_until( blip_time_t );
void run_square( Vrc6_Osc& osc, blip_time_t );
void run_saw( blip_time_t );

View file

@ -1,7 +1,7 @@
#include "Nes_Vrc7_Apu.h"
extern "C" {
#include "../ext/emu2413.h"
#include "ext/emu2413.h"
}
#include <string.h>
@ -10,10 +10,10 @@ extern "C" {
static unsigned char vrc7_inst[(16 + 3) * 8] =
{
#include "../ext/vrc7tone.h"
#include "ext/vrc7tone.h"
};
int const period = 36; // NES CPU clocks per FM clock
static int const period = 36; // NES CPU clocks per FM clock
Nes_Vrc7_Apu::Nes_Vrc7_Apu()
{

View file

@ -13,7 +13,7 @@ struct vrc7_snapshot_t;
class Nes_Vrc7_Apu {
public:
blargg_err_t init();
// See Nes_Apu.h for reference
void reset();
void volume( double );
@ -24,10 +24,10 @@ public:
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();
@ -36,14 +36,14 @@ private:
// noncopyable
Nes_Vrc7_Apu( const Nes_Vrc7_Apu& );
Nes_Vrc7_Apu& operator = ( const Nes_Vrc7_Apu& );
struct Vrc7_Osc
{
uint8_t regs [3];
Blip_Buffer* output;
int last_amp;
};
Vrc7_Osc oscs [osc_count];
uint8_t kon;
uint8_t inst [8];
@ -54,9 +54,9 @@ private:
Blip_Buffer* output;
int last_amp;
} mono;
Blip_Synth<blip_med_quality,1> synth;
void run_until( blip_time_t );
void output_changed();
};

View file

@ -29,14 +29,14 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const vrc6_flag = 0x01;
int const vrc7_flag = 0x02;
int const fds_flag = 0x04;
int const mmc5_flag = 0x08;
int const namco_flag = 0x10;
int const fme7_flag = 0x20;
static int const vrc6_flag = 0x01;
static int const vrc7_flag = 0x02;
static int const fds_flag = 0x04;
static int const mmc5_flag = 0x08;
static int const namco_flag = 0x10;
static int const fme7_flag = 0x20;
long const clock_divisor = 12;
static long const clock_divisor = 12;
using std::min;
using std::max;
@ -76,24 +76,24 @@ void Nsf_Emu::unload()
{
delete vrc6;
vrc6 = 0;
delete namco;
namco = 0;
delete fme7;
fme7 = 0;
delete fds;
fds = 0;
delete mmc5;
mmc5 = 0;
delete vrc7;
vrc7 = 0;
}
#endif
rom.clear();
Music_Emu::unload();
}
@ -125,22 +125,22 @@ static blargg_err_t check_nsf_header( void const* header )
struct Nsf_File : Gme_Info_
{
Nsf_Emu::header_t h;
Nsf_File() { set_type( gme_nsf_type ); }
blargg_err_t load_( Data_Reader& in )
{
blargg_err_t err = in.read( &h, Nsf_Emu::header_size );
if ( err )
return (err == in.eof_error ? gme_wrong_file_type : err);
if ( h.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag | fds_flag | mmc5_flag | vrc7_flag) )
set_warning( "Uses unsupported audio expansion hardware" );
set_track_count( h.track_count );
return check_nsf_header( &h );
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
copy_nsf_fields( h, out );
@ -163,7 +163,7 @@ void Nsf_Emu::set_tempo_( double t )
unsigned standard_rate = 0x411A;
clock_rate_ = 1789772.72727;
play_period = 262 * 341L * 4 - 2; // two fewer PPU clocks every four frames
if ( pal_only )
{
play_period = 33247 * clock_divisor;
@ -171,10 +171,10 @@ void Nsf_Emu::set_tempo_( double t )
standard_rate = 0x4E20;
playback_rate = get_le16( header_.pal_speed );
}
if ( !playback_rate )
playback_rate = standard_rate;
if ( playback_rate != standard_rate || t != 1.0 )
play_period = long (playback_rate * clock_rate_ / (1000000.0 / clock_divisor * t));
@ -205,9 +205,10 @@ blargg_err_t Nsf_Emu::init_sound()
apu_names[count + 2] = "Triangle";
apu_names[count + 3] = "Noise";
apu_names[count + 4] = "DMC";
apu_names[count + 5] = "FM";
count += Nes_Apu::osc_count;
}
static int const types [] = {
wave_type | 1, wave_type | 2, wave_type | 0,
noise_type | 0, mixed_type | 1,
@ -216,9 +217,9 @@ blargg_err_t Nsf_Emu::init_sound()
wave_type |10, wave_type |11, wave_type |12, wave_type |13
};
set_voice_types( types ); // common to all sound chip configurations
double adjusted_gain = gain();
#if NSF_EMU_APU_ONLY
{
if ( header_.chip_flags )
@ -256,17 +257,17 @@ blargg_err_t Nsf_Emu::init_sound()
count += Nes_Namco_Apu::osc_count;
}
if ( header_.chip_flags & fme7_flag )
{
fme7 = BLARGG_NEW Nes_Fme7_Apu;
CHECK_ALLOC( fme7 );
adjusted_gain *= 0.75;
apu_names[count + 0] = "Square 3";
apu_names[count + 1] = "Square 4";
apu_names[count + 2] = "Square 5";
count += Nes_Fme7_Apu::osc_count;
}
@ -275,45 +276,42 @@ blargg_err_t Nsf_Emu::init_sound()
fds = BLARGG_NEW Nes_Fds_Apu;
CHECK_ALLOC( fds );
adjusted_gain *= 0.75;
apu_names[count + 0] = "Wave";
count += Nes_Fds_Apu::osc_count;
}
if ( header_.chip_flags & mmc5_flag )
{
mmc5 = BLARGG_NEW Nes_Mmc5_Apu;
CHECK_ALLOC( mmc5 );
adjusted_gain *= 0.75;
apu_names[count + 0] = "Square 3";
apu_names[count + 1] = "Square 4";
apu_names[count + 2] = "PCM";
count += Nes_Mmc5_Apu::osc_count;
}
if ( header_.chip_flags & vrc7_flag )
{
vrc7 = BLARGG_NEW Nes_Vrc7_Apu;
CHECK_ALLOC( vrc7 );
RETURN_ERR( vrc7->init() );
adjusted_gain *= 0.75;
apu_names[count + 0] = "FM 1";
apu_names[count + 1] = "FM 2";
apu_names[count + 2] = "FM 3";
apu_names[count + 3] = "FM 4";
apu_names[count + 4] = "FM 5";
apu_names[count + 5] = "FM 6";
count += Nes_Vrc7_Apu::osc_count;
}
set_voice_count( count );
set_voice_names( &apu_names[0] );
if ( namco ) namco->volume( adjusted_gain );
if ( vrc6 ) vrc6 ->volume( adjusted_gain );
if ( fme7 ) fme7 ->volume( adjusted_gain );
@ -322,28 +320,31 @@ blargg_err_t Nsf_Emu::init_sound()
if ( vrc7 ) vrc7 ->volume( adjusted_gain );
}
#endif
set_voice_count( count );
set_voice_names( &apu_names[0] );
apu.volume( adjusted_gain );
return 0;
}
blargg_err_t Nsf_Emu::load_( Data_Reader& in )
{
assert( offsetof (header_t,unused [4]) == header_size );
blaarg_static_assert( offsetof (header_t,unused [4]) == header_size, "NSF Header layout incorrect!" );
RETURN_ERR( rom.load( in, header_size, &header_, 0 ) );
set_track_count( header_.track_count );
RETURN_ERR( check_nsf_header( &header_ ) );
if ( header_.vers != 1 )
set_warning( "Unknown file version" );
// sound and memory
blargg_err_t err = init_sound();
if ( err )
return err;
// set up data
nes_addr_t load_addr = get_le16( header_.load_addr );
init_addr = get_le16( header_.init_addr );
@ -358,10 +359,10 @@ blargg_err_t Nsf_Emu::load_( Data_Reader& in )
w = "Corrupt file (invalid load/init/play address)";
return w;
}
rom.set_addr( load_addr % bank_size );
int total_banks = rom.size() / bank_size;
// bank switching
int first_bank = (load_addr - rom_begin) / bank_size;
for ( int i = 0; i < bank_count; i++ )
@ -370,7 +371,7 @@ blargg_err_t Nsf_Emu::load_( Data_Reader& in )
if ( bank >= (unsigned) total_banks )
bank = 0;
initial_banks [i] = bank;
if ( header_.banks [i] )
{
// bank-switched
@ -378,22 +379,22 @@ blargg_err_t Nsf_Emu::load_( Data_Reader& in )
break;
}
}
pal_only = (header_.speed_flags & 3) == 1;
#if !NSF_EMU_EXTRA_FLAGS
header_.speed_flags = 0;
#endif
set_tempo( tempo() );
return setup_buffer( (long) (clock_rate_ + 0.5) );
}
void Nsf_Emu::update_eq( blip_eq_t const& eq )
{
apu.treble_eq( eq );
#if !NSF_EMU_APU_ONLY
{
if ( namco ) namco->treble_eq( eq );
@ -414,7 +415,7 @@ void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* )
return;
}
i -= Nes_Apu::osc_count;
#if !NSF_EMU_APU_ONLY
#define HANDLE_CHIP(class, object) \
if ( object ) \
@ -468,7 +469,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
return;
}
}
if ( namco )
{
switch ( addr )
@ -476,13 +477,13 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
case Nes_Namco_Apu::data_reg_addr:
namco->write_data( time(), data );
return;
case Nes_Namco_Apu::addr_reg_addr:
namco->write_addr( data );
return;
}
}
if ( addr >= Nes_Fme7_Apu::latch_addr && fme7 )
{
switch ( addr & Nes_Fme7_Apu::addr_mask )
@ -490,13 +491,13 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
case Nes_Fme7_Apu::latch_addr:
fme7->write_latch( data );
return;
case Nes_Fme7_Apu::data_addr:
fme7->write_data( time(), data );
return;
}
}
if ( vrc6 )
{
unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1);
@ -507,7 +508,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
return;
}
}
if ( mmc5 )
{
if ( (unsigned) (addr - mmc5->regs_addr) < mmc5->regs_size)
@ -515,14 +516,14 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
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 )
{
@ -530,7 +531,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
return;
}
}
if ( vrc7 )
{
if ( addr == 0x9010 )
@ -538,7 +539,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
vrc7->write_reg( data );
return;
}
if ( (unsigned) (addr - 0x9028) <= 0x08 )
{
vrc7->write_data( time(), data );
@ -547,20 +548,23 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
}
}
#endif
// unmapped write
#ifndef NDEBUG
{
// some games write to $8000 and $8001 repeatedly
if ( addr == 0x8000 || addr == 0x8001 ) return;
// probably namco sound mistakenly turned on in mck
if ( addr == 0x4800 || addr == 0xF800 ) return;
// memory mapper?
if ( addr == 0xFFF8 ) return;
// FDS memory
if ( fds && (unsigned) (addr - 0x8000) < 0x6000 ) return;
debug_printf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data );
}
#endif
@ -569,15 +573,39 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data )
blargg_err_t Nsf_Emu::start_track_( int track )
{
RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( low_mem, 0, sizeof low_mem );
memset( sram, 0, sizeof sram );
cpu::reset( unmapped_code ); // also maps low_mem
cpu::map_code( sram_addr, sizeof sram, sram );
for ( int i = 0; i < bank_count; ++i )
cpu_write( bank_select_addr + i, initial_banks [i] );
if (fds)
{
memset( fds->sram, 0, sizeof fds->sram );
// For FDS the initial load values at $076 and $077
// specify the banks used for $6000-7FFF as well as $E000-FFFF
cpu_write( bank_select_addr - 2, initial_banks [bank_count - 2] );
cpu_write( bank_select_addr - 1, initial_banks [bank_count - 1] );
for ( int i = 0; i < bank_count; ++i )
{
byte* out = fds->sram;
unsigned bank = i;
if ( bank >= 6 )
{
out = sram;
bank -= 6;
}
int32_t offset = rom.mask_addr( initial_banks [i] * (int32_t) bank_size );
if ( offset >= rom.size() )
set_warning( "Invalid bank" );
memcpy( &out [bank * bank_size], rom.at_addr( offset ), bank_size );
}
cpu::map_code( fds->sram_addr, sizeof fds->sram, fds->sram );
}
apu.reset( pal_only, (header_.speed_flags & 0x20) ? 0x3F : 0 );
apu.write_register( 0, 0x4015, 0x0F );
apu.write_register( 0, 0x4017, (header_.speed_flags & 0x10) ? 0x80 : 0 );
@ -588,7 +616,7 @@ blargg_err_t Nsf_Emu::start_track_( int track )
mmc5_mul [1] = 0;
memset( mmc5->exram, 0, mmc5->exram_size );
}
{
if ( namco ) namco->reset();
if ( vrc6 ) vrc6 ->reset();
@ -598,11 +626,11 @@ blargg_err_t Nsf_Emu::start_track_( int track )
if ( vrc7 ) vrc7 ->reset();
}
#endif
play_ready = 4;
play_extra = 0;
next_play = play_period / clock_divisor;
saved_state.pc = badop_addr;
low_mem [0x1FF] = (badop_addr - 1) >> 8;
low_mem [0x1FE] = (badop_addr - 1) & 0xFF;
@ -610,7 +638,7 @@ blargg_err_t Nsf_Emu::start_track_( int track )
r.pc = init_addr;
r.a = track;
r.x = pal_only;
return 0;
}
@ -642,7 +670,7 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int )
}
}
}
if ( time() >= next_play )
{
nes_time_t period = (play_period + play_extra) / clock_divisor;
@ -653,7 +681,7 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int )
check( saved_state.pc == badop_addr );
if ( r.pc != badop_addr )
saved_state = cpu::r;
r.pc = play_addr;
low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8;
low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF;
@ -661,21 +689,21 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int )
}
}
}
if ( cpu::error_count() )
{
cpu::clear_error_count();
set_warning( "Emulation error (illegal instruction)" );
}
duration = time();
next_play -= duration;
check( next_play >= 0 );
if ( next_play < 0 )
next_play = 0;
apu.end_frame( duration );
#if !NSF_EMU_APU_ONLY
{
if ( namco ) namco->end_frame( duration );
@ -686,6 +714,6 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int )
if ( vrc7 ) vrc7 ->end_frame( duration );
}
#endif
return 0;
}

View file

@ -14,7 +14,7 @@ public:
// Equalizer profiles for US NES and Japanese Famicom
static equalizer_t const nes_eq;
static equalizer_t const famicom_eq;
// NSF file header
enum { header_size = 0x80 };
struct header_t
@ -36,12 +36,12 @@ public:
byte chip_flags;
byte unused [4];
};
// Header for currently loaded file
header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_nsf_type; }
public:
// deprecated
using Music_Emu::load;
@ -68,26 +68,26 @@ protected:
nes_addr_t play_addr;
double clock_rate_;
bool pal_only;
// timing
Nes_Cpu::registers_t saved_state;
nes_time_t next_play;
nes_time_t play_period;
int play_extra;
int play_ready;
enum { rom_begin = 0x8000 };
enum { bank_select_addr = 0x5FF8 };
enum { bank_size = 0x1000 };
Rom_Data<bank_size> rom;
public: private: friend class Nes_Cpu;
void cpu_jsr( nes_addr_t );
int cpu_read( nes_addr_t );
void cpu_write( nes_addr_t, int );
void cpu_write_misc( nes_addr_t, int );
enum { badop_addr = bank_select_addr };
private:
byte mmc5_mul [2];
@ -101,9 +101,9 @@ private:
blargg_vector<const char*> apu_names;
static int pcm_read( void*, nes_addr_t );
blargg_err_t init_sound();
header_t header_;
enum { sram_addr = 0x6000 };
byte sram [0x2000];
byte unmapped_code [Nes_Cpu::page_size + 8];

View file

@ -58,7 +58,7 @@ static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector<char>&
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++ )
@ -69,7 +69,7 @@ static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector<char>&
while ( i < size && chars [i] )
i++;
}
return strs.resize( count );
}
@ -96,8 +96,8 @@ struct nsfe_info_t
blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
{
int const nsfe_info_size = 16;
assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size );
blaarg_static_assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size, "NSFE Info header layout incorrect!" );
// check header
byte signature [4];
blargg_err_t err = in.read( signature, sizeof signature );
@ -105,13 +105,13 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
return (err == in.eof_error ? gme_wrong_file_type : err);
if ( memcmp( signature, "NSFE", 4 ) )
return gme_wrong_file_type;
// free previous info
track_name_data.clear();
track_names.clear();
playlist.clear();
track_times.clear();
// default nsf header
static const Nsf_Emu::header_t base_header =
{
@ -128,7 +128,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
};
Nsf_Emu::header_t& header = info;
header = base_header;
// parse tags
int phase = 0;
while ( phase != 3 )
@ -136,26 +136,26 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
// read size and tag
byte block_header [2] [4];
RETURN_ERR( in.read( block_header, sizeof block_header ) );
blargg_long size = get_le32( block_header [0] );
blargg_long tag = get_le32( block_header [1] );
int32_t size = get_le32( block_header [0] );
int32_t tag = get_le32( block_header [1] );
if ( size < 0 )
return "Corrupt file";
//debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) );
switch ( tag )
{
case BLARGG_4CHAR('O','F','N','I'): {
check( phase == 0 );
if ( size < 8 )
return "Corrupt file";
nsfe_info_t finfo;
finfo.track_count = 1;
finfo.first_track = 0;
RETURN_ERR( in.read( &finfo, min( size, (blargg_long) nsfe_info_size ) ) );
RETURN_ERR( in.read( &finfo, min( size, (int32_t) nsfe_info_size ) ) );
if ( size > nsfe_info_size )
RETURN_ERR( in.skip( size - nsfe_info_size ) );
phase = 1;
@ -167,48 +167,48 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
memcpy( info.load_addr, finfo.load_addr, 2 * 3 );
break;
}
case BLARGG_4CHAR('K','N','A','B'):
if ( size > (int) sizeof info.banks )
return "Corrupt file";
RETURN_ERR( in.read( info.banks, size ) );
break;
case BLARGG_4CHAR('h','t','u','a'): {
blargg_vector<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;
@ -225,12 +225,12 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
}
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 ) );
@ -238,7 +238,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
break;
}
}
return 0;
}
@ -253,7 +253,7 @@ blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const
}
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 );
@ -284,9 +284,9 @@ blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const
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 ) );
@ -294,7 +294,7 @@ struct Nsfe_File : Gme_Info_
set_track_count( info.info.track_count );
return 0;
}
blargg_err_t track_info_( track_info_t* out, int track ) const
{
return info.track_info_( out, track );
@ -312,7 +312,7 @@ blargg_err_t Nsfe_Emu::load_( Data_Reader& in )
{
if ( loading )
return Nsf_Emu::load_( in );
// TODO: this hacky recursion-avoidance could have subtle problems
loading = true;
blargg_err_t err = info.load( in, this );

View file

@ -11,7 +11,7 @@
class Nsfe_Info {
public:
blargg_err_t load( Data_Reader&, Nsf_Emu* );
struct info_t : Nsf_Emu::header_t
{
char game [256];
@ -19,15 +19,15 @@ public:
char copyright [256];
char dumper [256];
} info;
void disable_playlist( bool = true );
blargg_err_t track_info_( track_info_t* out, int track ) const;
int remap_track( int i ) const;
void unload();
Nsfe_Info();
~Nsfe_Info();
private:
@ -42,7 +42,7 @@ private:
class Nsfe_Emu : public Nsf_Emu {
public:
static gme_type_t static_type() { return gme_nsfe_type; }
public:
// deprecated
struct header_t { char tag [4]; };

View file

@ -17,11 +17,11 @@ 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 int const max_frequency = 12000; // pure waves above this frequency are silenced
static void gen_poly( blargg_ulong mask, int count, byte* out )
static void gen_poly( uint32_t mask, int count, byte* out )
{
blargg_ulong n = 1;
uint32_t n = 1;
do
{
int bits = 0;
@ -30,7 +30,7 @@ static void gen_poly( blargg_ulong mask, int count, byte* out )
{
// implemented using "Galios configuration"
bits |= (n & 1) << b;
n = (n >> 1) ^ (mask & -(n & 1));
n = (n >> 1) ^ (mask & uMinus(n & 1));
}
while ( b++ < 7 );
*out++ = bits;
@ -39,11 +39,11 @@ static void gen_poly( blargg_ulong mask, int count, byte* out )
}
// poly5
int const poly5_len = (1 << 5) - 1;
blargg_ulong const poly5_mask = (1UL << poly5_len) - 1;
blargg_ulong const poly5 = 0x167C6EA1;
static int const poly5_len = (1 << 5) - 1;
static uint32_t const poly5_mask = (1UL << poly5_len) - 1;
static uint32_t const poly5 = 0x167C6EA1;
inline blargg_ulong run_poly5( blargg_ulong in, int shift )
static inline uint32_t run_poly5( uint32_t in, int shift )
{
return (in << shift & poly5_mask) | (in >> (poly5_len - shift));
}
@ -56,17 +56,17 @@ Sap_Apu_Impl::Sap_Apu_Impl()
gen_poly( POLY_MASK( 4, 1, 0 ), sizeof poly4, poly4 );
gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 );
gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 );
if ( 0 ) // comment out to recauculate poly5 constant
{
byte poly5 [4];
gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 );
blargg_ulong n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L +
uint32_t n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L +
poly5 [1] * 0x100L + poly5 [0];
blargg_ulong rev = n & 1;
uint32_t rev = n & 1;
for ( int i = 1; i < poly5_len; i++ )
rev |= (n >> i & 1) << (poly5_len - i);
debug_printf( "poly5: 0x%08lX\n", rev );
debug_printf( "poly5: 0x%08lX\n", (long unsigned int)rev );
}
}
@ -85,7 +85,7 @@ void Sap_Apu::reset( Sap_Apu_Impl* new_impl )
poly4_pos = 0;
polym_pos = 0;
control = 0;
for ( int i = 0; i < osc_count; i++ )
memset( &oscs [i], 0, offsetof (osc_t,output) );
}
@ -96,13 +96,13 @@ inline void Sap_Apu::calc_periods()
int divider = 28;
if ( this->control & 1 )
divider = 114;
for ( int i = 0; i < osc_count; i++ )
{
osc_t* const osc = &oscs [i];
int const osc_reload = osc->regs [0]; // cache
blargg_long period = (osc_reload + 1) * divider;
int32_t 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] )
{
@ -112,7 +112,7 @@ inline void Sap_Apu::calc_periods()
period = osc_reload * 0x100L + osc [-1].regs [0] + 7;
if ( !(this->control & fast_bits [i - 1]) )
period = (period - 6) * divider;
if ( (osc [-1].regs [1] & 0x1F) > 0x10 )
debug_printf( "Use of slave channel in 16-bit mode not supported\n" );
}
@ -125,7 +125,7 @@ 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;
@ -135,19 +135,19 @@ void Sap_Apu::run_until( blip_time_t end_time )
polym = impl->poly9;
}
polym_pos %= polym_len;
for ( int i = 0; i < osc_count; i++ )
{
osc_t* const osc = &oscs [i];
blip_time_t time = last_time + osc->delay;
blip_time_t const period = osc->period;
// output
Blip_Buffer* output = osc->output;
if ( output )
{
output->set_modified();
int const osc_control = osc->regs [1]; // cache
int volume = (osc_control & 0x0F) * 2;
if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency
@ -155,14 +155,14 @@ void Sap_Apu::run_until( blip_time_t end_time )
{
if ( !(osc_control & 0x10) )
volume >>= 1; // inaudible frequency = half volume
int delta = volume - osc->last_amp;
if ( delta )
{
osc->last_amp = volume;
impl->synth.offset( last_time, delta, output );
}
// TODO: doesn't maintain high pass flip-flop (very minor issue)
}
else
@ -182,7 +182,7 @@ void Sap_Apu::run_until( blip_time_t end_time )
volume = -volume;
}
}
if ( time < end_time || time2 < end_time )
{
// poly source
@ -206,9 +206,9 @@ void Sap_Apu::run_until( blip_time_t end_time )
poly_pos = (poly_pos + osc->delay) % poly_len;
}
poly_inc -= poly_len; // allows more optimized inner loop below
// square/poly5 wave
blargg_ulong wave = poly5;
uint32_t wave = poly5;
check( poly5 & 1 ); // low bit is set for pure wave
int poly5_inc = 0;
if ( !(osc_control & 0x80) )
@ -216,7 +216,7 @@ void Sap_Apu::run_until( blip_time_t end_time )
wave = run_poly5( wave, (osc->delay + poly5_pos) % poly5_len );
poly5_inc = period % poly5_len;
}
// Run wave and high pass interleved with each catching up to the other.
// Disabled high pass has no performance effect since inner wave loop
// makes no compromise for high pass, and only runs once in that case.
@ -238,7 +238,7 @@ void Sap_Apu::run_until( blip_time_t end_time )
}
while ( time2 <= time ) // must advance *past* time to avoid hang
time2 += period2;
// run wave
blip_time_t end = end_time;
if ( end > time2 )
@ -262,11 +262,11 @@ void Sap_Apu::run_until( blip_time_t end_time )
}
}
while ( time < end_time || time2 < end_time );
osc->phase = poly_pos;
osc->last_amp = osc_last_amp;
}
osc->invert = 0;
if ( volume < 0 )
{
@ -276,18 +276,18 @@ void Sap_Apu::run_until( blip_time_t end_time )
}
}
}
// maintain divider
blip_time_t remain = end_time - time;
if ( remain > 0 )
{
blargg_long count = (remain + period - 1) / period;
int32_t 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;
@ -329,6 +329,6 @@ void Sap_Apu::end_frame( blip_time_t end_time )
{
if ( end_time > last_time )
run_until( end_time );
last_time -= end_time;
}

View file

@ -11,17 +11,17 @@ class Sap_Apu_Impl;
class Sap_Apu {
public:
enum { osc_count = 4 };
static const int osc_count = 4;
void osc_output( int index, Blip_Buffer* );
void reset( Sap_Apu_Impl* );
enum { start_addr = 0xD200 };
enum { end_addr = 0xD209 };
static const unsigned int start_addr = 0xD200;
static const unsigned int end_addr = 0xD209;
void write_data( blip_time_t, unsigned addr, int data );
void end_frame( blip_time_t );
public:
Sap_Apu();
private:
@ -42,13 +42,13 @@ private:
int poly4_pos;
int polym_pos;
int control;
void calc_periods();
void run_until( blip_time_t );
enum { poly4_len = (1L << 4) - 1 };
enum { poly9_len = (1L << 9) - 1 };
enum { poly17_len = (1L << 17) - 1 };
static const unsigned int poly4_len = (1L << 4) - 1;
static const unsigned int poly9_len = (1L << 9) - 1;
static const unsigned int poly17_len = (1L << 17) - 1;
friend class Sap_Apu_Impl;
};
@ -56,10 +56,10 @@ private:
class Sap_Apu_Impl {
public:
Blip_Synth<blip_good_quality,1> synth;
Sap_Apu_Impl();
void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); }
private:
typedef unsigned char byte;
byte poly4 [Sap_Apu::poly4_len / 8 + 1];

View file

@ -29,14 +29,16 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
int const st_n = 0x80;
int const st_v = 0x40;
int const st_r = 0x20;
int const st_b = 0x10;
int const st_d = 0x08;
int const st_i = 0x04;
int const st_z = 0x02;
int const st_c = 0x01;
enum {
st_n = 0x80,
st_v = 0x40,
st_r = 0x20,
st_b = 0x10,
st_d = 0x08,
st_i = 0x04,
st_z = 0x02,
st_c = 0x01
};
void Sap_Cpu::reset( void* new_mem )
{
@ -53,7 +55,7 @@ void Sap_Cpu::reset( void* new_mem )
state_.base = 0;
irq_time_ = future_sap_time;
end_time_ = future_sap_time;
blargg_verify_byte_order();
}
@ -76,7 +78,7 @@ bool Sap_Cpu::run( sap_time_t end_time )
this->state = &s;
int32_t s_time = s.time;
uint8_t* const mem = this->mem; // cache
// registers
uint16_t pc = r.pc;
uint8_t a = r.a;
@ -84,10 +86,10 @@ bool Sap_Cpu::run( sap_time_t end_time )
uint8_t y = r.y;
uint16_t sp;
SET_SP( r.sp );
// status flags
#define IS_NEG (nz & 0x8080)
#define CALC_STATUS( out ) do {\
out = status & (st_v | st_d | st_i);\
out |= ((nz >> 8) | nz) & st_n;\
@ -101,7 +103,7 @@ bool Sap_Cpu::run( sap_time_t end_time )
c = nz;\
nz |= ~in & st_z;\
} while ( 0 )
uint8_t status;
uint16_t c; // carry set if (c & 0x100) != 0
uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0
@ -109,12 +111,12 @@ bool Sap_Cpu::run( sap_time_t end_time )
uint8_t temp = r.status;
SET_STATUS( temp );
}
goto loop;
dec_clock_loop:
s_time--;
loop:
#ifndef NDEBUG
{
sap_time_t correct = end_time_;
@ -123,16 +125,16 @@ loop:
check( s.base == correct );
}
#endif
check( (unsigned) GET_SP() < 0x100 );
check( (unsigned) a < 0x100 );
check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 );
uint8_t opcode = mem [pc];
pc++;
uint8_t const* instr = mem + pc;
static uint8_t const clock_table [256] =
{// 0 1 2 3 4 5 6 7 8 9 A B C D E F
0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0
@ -152,19 +154,19 @@ loop:
2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E
3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
}; // 0x00 was 7
uint16_t data;
data = clock_table [opcode];
if ( (s_time += data) >= 0 )
goto possibly_out_of_time;
almost_out_of_time:
data = *instr;
#ifdef NES_CPU_LOG_H
nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] );
#endif
switch ( opcode )
{
possibly_out_of_time:
@ -189,12 +191,12 @@ possibly_out_of_time:
out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
cross( temp );\
}
#define IND_X( out ) {\
uint16_t temp = data + x;\
out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\
}
#define ARITH_ADDR_MODES( op )\
case op - 0x04: /* (ind,x) */\
IND_X( data )\
@ -240,15 +242,15 @@ imm##op:
a = nz = READ_LOW( uint8_t (data + x) );
pc++;
goto loop;
case 0xA5: // LDA zp
a = nz = READ_LOW( data );
pc++;
goto loop;
case 0xD0: // BNE
BRANCH( (uint8_t) nz );
case 0x20: { // JSR
uint16_t temp = pc + 1;
pc = GET_ADDR();
@ -257,37 +259,37 @@ imm##op:
WRITE_LOW( sp, temp );
goto loop;
}
case 0x4C: // JMP abs
pc = GET_ADDR();
goto loop;
case 0xE8: // INX
INC_DEC_XY( x, 1 )
case 0x10: // BPL
BRANCH( !IS_NEG )
ARITH_ADDR_MODES( 0xC5 ) // CMP
nz = a - data;
pc++;
c = ~nz;
nz &= 0xFF;
goto loop;
case 0x30: // BMI
BRANCH( IS_NEG )
case 0xF0: // BEQ
BRANCH( !(uint8_t) nz );
case 0x95: // STA zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0x85: // STA zp
pc++;
WRITE_LOW( data, a );
goto loop;
case 0xC8: // INY
INC_DEC_XY( y, 1 )
@ -295,12 +297,12 @@ imm##op:
y = a;
nz = a;
goto loop;
case 0x98: // TYA
a = y;
nz = y;
goto loop;
case 0xAD:{// LDA abs
unsigned addr = GET_ADDR();
pc += 2;
@ -308,16 +310,16 @@ imm##op:
a = nz;
goto loop;
}
case 0x60: // RTS
pc = 1 + READ_LOW( sp );
pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
sp = (sp - 0xFE) | 0x100;
goto loop;
{
uint16_t addr;
case 0x99: // STA abs,Y
addr = y + GET_ADDR();
pc += 2;
@ -327,7 +329,7 @@ imm##op:
goto loop;
}
goto sta_ptr;
case 0x8D: // STA abs
addr = GET_ADDR();
pc += 2;
@ -337,7 +339,7 @@ imm##op:
goto loop;
}
goto sta_ptr;
case 0x9D: // STA abs,X (slightly more common than STA abs)
addr = x + GET_ADDR();
pc += 2;
@ -351,19 +353,19 @@ imm##op:
WRITE( addr, a );
CACHE_TIME();
goto loop;
case 0x91: // STA (ind),Y
IND_Y( NO_PAGE_CROSSING, addr )
pc++;
goto sta_ptr;
case 0x81: // STA (ind,X)
IND_X( addr )
pc++;
goto sta_ptr;
}
case 0xA9: // LDA #imm
pc++;
a = data;
@ -373,12 +375,12 @@ imm##op:
// common read instructions
{
uint16_t addr;
case 0xA1: // LDA (ind,X)
IND_X( addr )
pc++;
goto a_nz_read_addr;
case 0xB1:// LDA (ind),Y
addr = READ_LOW( data ) + y;
HANDLE_PAGE_CROSSING( addr );
@ -388,7 +390,7 @@ imm##op:
if ( (addr ^ 0x8000) <= 0x9FFF )
goto loop;
goto a_nz_read_addr;
case 0xB9: // LDA abs,Y
HANDLE_PAGE_CROSSING( data + y );
addr = GET_ADDR() + y;
@ -397,7 +399,7 @@ imm##op:
if ( (addr ^ 0x8000) <= 0x9FFF )
goto loop;
goto a_nz_read_addr;
case 0xBD: // LDA abs,X
HANDLE_PAGE_CROSSING( data + x );
addr = GET_ADDR() + x;
@ -410,39 +412,39 @@ imm##op:
a = nz = READ( addr );
CACHE_TIME();
goto loop;
}
// Branch
case 0x50: // BVC
BRANCH( !(status & st_v) )
case 0x70: // BVS
BRANCH( status & st_v )
case 0xB0: // BCS
BRANCH( c & 0x100 )
case 0x90: // BCC
BRANCH( !(c & 0x100) )
// Load/store
case 0x94: // STY zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0x84: // STY zp
pc++;
WRITE_LOW( data, y );
goto loop;
case 0x96: // STX zp,y
data = uint8_t (data + y);/*FALLTHRU*/
case 0x86: // STX zp
pc++;
WRITE_LOW( data, x );
goto loop;
case 0xB6: // LDX zp,y
data = uint8_t (data + y);/*FALLTHRU*/
case 0xA6: // LDX zp
@ -452,7 +454,7 @@ imm##op:
x = data;
nz = data;
goto loop;
case 0xB4: // LDY zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0xA4: // LDY zp
@ -462,7 +464,7 @@ imm##op:
y = data;
nz = data;
goto loop;
case 0xBC: // LDY abs,X
data += x;
HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
@ -474,7 +476,7 @@ imm##op:
CACHE_TIME();
goto loop;
}
case 0xBE: // LDX abs,y
data += y;
HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
@ -486,13 +488,13 @@ imm##op:
CACHE_TIME();
goto loop;
}
{
uint8_t temp;
case 0x8C: // STY abs
temp = y;
goto store_abs;
case 0x8E: // STX abs
temp = x;
store_abs:
@ -519,7 +521,7 @@ imm##op:
CACHE_TIME();
goto cpx_data;
}
case 0xE4: // CPX zp
data = READ_LOW( data );/*FALLTHRU*/
case 0xE0: // CPX #imm
@ -529,7 +531,7 @@ imm##op:
c = ~nz;
nz &= 0xFF;
goto loop;
case 0xCC:{// CPY abs
unsigned addr = GET_ADDR();
pc++;
@ -538,7 +540,7 @@ imm##op:
CACHE_TIME();
goto cpy_data;
}
case 0xC4: // CPY zp
data = READ_LOW( data ); // FALLTHRU
case 0xC0: // CPY #imm
@ -548,24 +550,24 @@ imm##op:
c = ~nz;
nz &= 0xFF;
goto loop;
// Logical
ARITH_ADDR_MODES( 0x25 ) // AND
nz = (a &= data);
pc++;
goto loop;
ARITH_ADDR_MODES( 0x45 ) // EOR
nz = (a ^= data);
pc++;
goto loop;
ARITH_ADDR_MODES( 0x05 ) // ORA
nz = (a |= data);
pc++;
goto loop;
case 0x2C:{// BIT abs
unsigned addr = GET_ADDR();
pc += 2;
@ -577,7 +579,7 @@ imm##op:
nz <<= 8; // result must be zero, even if N bit is set
goto loop;
}
case 0x24: // BIT zp
nz = READ_LOW( data );
pc++;
@ -587,14 +589,14 @@ imm##op:
goto loop;
nz <<= 8; // result must be zero, even if N bit is set
goto loop;
// Add/subtract
ARITH_ADDR_MODES( 0xE5 ) // SBC
case 0xEB: // unofficial equivalent
data ^= 0xFF;
goto adc_imm;
ARITH_ADDR_MODES( 0x65 ) // ADC
adc_imm: {
check( !(status & st_d) );
@ -607,7 +609,7 @@ imm##op:
a = (uint8_t) nz;
goto loop;
}
// Shift/rotate
case 0x4A: // LSR A
@ -633,7 +635,7 @@ imm##op:
a = (uint8_t) nz;
goto loop;
}
case 0x5E: // LSR abs,X
data += x;/*FALLTHRU*/
case 0x4E: // LSR abs
@ -647,11 +649,11 @@ imm##op:
c = temp << 8;
goto rotate_common;
}
case 0x3E: // ROL abs,X
data += x;
goto rol_abs;
case 0x1E: // ASL abs,X
data += x;/*FALLTHRU*/
case 0x0E: // ASL abs
@ -667,15 +669,15 @@ imm##op:
WRITE( data, (uint8_t) nz );
CACHE_TIME();
goto loop;
case 0x7E: // ROR abs,X
data += x;
goto ror_abs;
case 0x76: // ROR zp,x
data = uint8_t (data + x);
goto ror_zp;
case 0x56: // LSR zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0x46: // LSR zp
@ -687,11 +689,11 @@ imm##op:
c = temp << 8;
goto write_nz_zp;
}
case 0x36: // ROL zp,x
data = uint8_t (data + x);
goto rol_zp;
case 0x16: // ASL zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0x06: // ASL zp
@ -701,21 +703,21 @@ imm##op:
nz = c >> 8 & 1;
nz |= (c = READ_LOW( data ) << 1);
goto write_nz_zp;
// Increment/decrement
case 0xCA: // DEX
INC_DEC_XY( x, -1 )
case 0x88: // DEY
INC_DEC_XY( y, -1 )
case 0xF6: // INC zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0xE6: // INC zp
nz = 1;
goto add_nz_zp;
case 0xD6: // DEC zp,x
data = uint8_t (data + x);/*FALLTHRU*/
case 0xC6: // DEC zp
@ -726,21 +728,21 @@ imm##op:
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:
@ -752,14 +754,14 @@ imm##op:
WRITE( data, (uint8_t) nz );
CACHE_TIME();
goto loop;
// Transfer
case 0xAA: // TAX
x = a;
nz = a;
goto loop;
case 0x8A: // TXA
a = x;
nz = x;
@ -768,22 +770,22 @@ imm##op:
case 0x9A: // TXS
SET_SP( x ); // verified (no flag change)
goto loop;
case 0xBA: // TSX
x = nz = GET_SP();
goto loop;
// Stack
case 0x48: // PHA
PUSH( a ); // verified
goto loop;
case 0x68: // PLA
a = nz = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100;
goto loop;
case 0x40:{// RTI
uint8_t temp = READ_LOW( sp );
pc = READ_LOW( 0x100 | (sp - 0xFF) );
@ -797,13 +799,13 @@ imm##op:
sap_time_t new_time = end_time_;
if ( !(status & st_i) && new_time > irq_time_ )
new_time = irq_time_;
blargg_long delta = s.base - new_time;
int32_t delta = s.base - new_time;
s.base = new_time;
s_time += delta;
}
goto loop;
}
case 0x28:{// PLP
uint8_t temp = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100;
@ -815,14 +817,14 @@ imm##op:
goto handle_sei;
goto handle_cli;
}
case 0x08: { // PHP
uint8_t temp;
CALC_STATUS( temp );
PUSH( temp | (st_b | st_r) );
goto loop;
}
case 0x6C:{// JMP (ind)
data = GET_ADDR();
pc = READ_PROG( data );
@ -830,39 +832,39 @@ imm##op:
pc |= 0x100 * READ_PROG( data );
goto loop;
}
case 0x00: // BRK
goto handle_brk;
// Flags
case 0x38: // SEC
c = (uint16_t) ~0;
goto loop;
case 0x18: // CLC
c = 0;
goto loop;
case 0xB8: // CLV
status &= ~st_v;
goto loop;
case 0xD8: // CLD
status &= ~st_d;
goto loop;
case 0xF8: // SED
status |= st_d;
goto loop;
case 0x58: // CLI
if ( !(status & st_i) )
goto loop;
status &= ~st_i;
handle_cli: {
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - irq_time_;
int32_t delta = s.base - irq_time_;
if ( delta <= 0 )
{
if ( TIME < irq_time_ )
@ -873,7 +875,7 @@ imm##op:
s_time += delta;
if ( s_time < 0 )
goto loop;
if ( delta >= s_time + 1 )
{
// delayed irq until after next instruction
@ -886,14 +888,14 @@ imm##op:
debug_printf( "Delayed CLI not emulated\n" );
goto loop;
}
case 0x78: // SEI
if ( status & st_i )
goto loop;
status |= st_i;
handle_sei: {
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - end_time_;
int32_t delta = s.base - end_time_;
s.base = end_time_;
s_time += delta;
if ( s_time < 0 )
@ -901,9 +903,9 @@ imm##op:
debug_printf( "Delayed SEI not emulated\n" );
goto loop;
}
// Unofficial
// SKW - Skip word
case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/
@ -914,24 +916,24 @@ imm##op:
case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
pc++;
goto loop;
// NOP
case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
goto loop;
// Unimplemented
// halt
//case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
//case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2:
default:
illegal_encountered = true;
pc--;
goto stop;
}
assert( false );
int result_;
handle_brk:
if ( (pc - 1) >= idle_addr )
@ -939,15 +941,15 @@ handle_brk:
pc++;
result_ = 4;
debug_printf( "BRK executed\n" );
interrupt:
{
s_time += 7;
WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
WRITE_LOW( 0x100 | (sp - 2), pc );
pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
sp = (sp - 3) | 0x100;
uint8_t temp;
CALC_STATUS( temp );
@ -955,17 +957,17 @@ interrupt:
if ( result_ )
temp |= st_b; // TODO: incorrectly sets B flag for IRQ
WRITE_LOW( sp, temp );
status &= ~st_d;
status |= st_i;
this->r.status = status; // update externally-visible I flag
blargg_long delta = s.base - end_time_;
int32_t delta = s.base - end_time_;
s.base = end_time_;
s_time += delta;
goto loop;
}
idle_done:
//s_time = 0;
pc--;
@ -979,26 +981,26 @@ out_of_time:
goto interrupt;
if ( s_time < 0 )
goto loop;
stop:
s.time = s_time;
r.pc = pc;
r.sp = GET_SP();
r.a = a;
r.x = x;
r.y = y;
{
uint8_t temp;
CALC_STATUS( temp );
r.status = temp;
}
this->state_ = s;
this->state = &this->state_;
return illegal_encountered;
}

View file

@ -6,7 +6,7 @@
#include "blargg_common.h"
typedef blargg_long sap_time_t; // clock cycle count
typedef int32_t sap_time_t; // clock cycle count
typedef unsigned sap_addr_t; // 16-bit address
enum { future_sap_time = INT_MAX / 2 + 1 };
@ -14,11 +14,11 @@ class Sap_Cpu {
public:
// Clear all registers and keep pointer to 64K memory passed in
void reset( void* mem_64k );
// Run until specified time is reached. Returns true if suspicious/unsupported
// instruction was encountered at any point during run.
bool run( sap_time_t end_time );
// Registers are not updated until run() returns (except I flag in status)
struct registers_t {
uint16_t pc;
@ -29,20 +29,20 @@ public:
uint8_t sp;
};
registers_t r;
enum { idle_addr = 0xFEFF };
// Time of beginning of next instruction to be executed
sap_time_t time() const { return state->time + state->base; }
void set_time( sap_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; }
sap_time_t irq_time() const { return irq_time_; }
void set_irq_time( sap_time_t );
sap_time_t end_time() const { return end_time_; }
void set_end_time( sap_time_t );
public:
Sap_Cpu() { state = &state_; }
enum { irq_inhibit = 0x04 };
@ -56,7 +56,7 @@ private:
sap_time_t irq_time_;
sap_time_t end_time_;
uint8_t* mem;
inline sap_time_t update_end_time( sap_time_t end, sap_time_t irq );
};

View file

@ -19,7 +19,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
long const base_scanline_period = 114;
static long const base_scanline_period = 114;
using std::min;
using std::max;
@ -27,13 +27,13 @@ using std::max;
Sap_Emu::Sap_Emu()
{
set_type( gme_sap_type );
static const char* const names [Sap_Apu::osc_count * 2] = {
"Wave 1", "Wave 2", "Wave 3", "Wave 4",
"Wave 5", "Wave 6", "Wave 7", "Wave 8",
};
set_voice_names( names );
static int const types [Sap_Apu::osc_count * 2] = {
wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0,
wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4,
@ -72,7 +72,7 @@ static int from_dec( byte const* in, byte const* end )
{
if ( in >= end )
return -1;
int n = 0;
while ( in < end )
{
@ -108,10 +108,10 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out
out->author [0] = 0;
out->name [0] = 0;
out->copyright [0] = 0;
if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) )
return gme_wrong_file_type;
byte const* file_end = in + size - 5;
in += 5;
while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) )
@ -119,14 +119,14 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out
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
@ -162,10 +162,10 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out
case 'C':
case 'B':
break;
case 'D':
return "Digimusic not supported";
default:
return "Unsupported player type";
}
@ -192,14 +192,14 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out
{
parse_string( in, line_end, sizeof out->copyright, out->copyright );
}
in = line_end + 2;
}
if ( in [0] != 0xFF || in [1] != 0xFF )
return "ROM data missing";
out->rom_data = in + 2;
return 0;
}
@ -219,16 +219,16 @@ blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const
struct Sap_File : Gme_Info_
{
Sap_Emu::info_t info;
Sap_File() { set_type( gme_sap_type ); }
blargg_err_t load_mem_( byte const* begin, long size )
{
RETURN_ERR( parse_info( begin, size, &info ) );
set_track_count( info.track_count );
return 0;
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
copy_sap_fields( info, out );
@ -247,7 +247,7 @@ extern gme_type_t const gme_sap_type = &gme_sap_type_;
blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
{
file_end = in + size;
info.warning = 0;
info.type = 'B';
info.stereo = false;
@ -256,12 +256,12 @@ blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
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 );
set_voice_count( Sap_Apu::osc_count << static_cast<int>(info.stereo) );
apu_impl.volume( gain() );
return setup_buffer( 1773447 );
}
@ -315,7 +315,7 @@ inline void Sap_Emu::call_init( int track )
r.a = track;
run_routine( info.init_addr );
break;
case 'C':
r.a = 0x70;
r.x = info.music_addr&0xFF;
@ -331,7 +331,7 @@ inline void Sap_Emu::call_init( int track )
blargg_err_t Sap_Emu::start_track_( int track )
{
RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( &mem, 0, sizeof mem );
byte const* in = info.rom_data;
@ -352,22 +352,22 @@ blargg_err_t Sap_Emu::start_track_( int track )
set_warning( "Invalid file data block" );
break;
}
memcpy( mem.ram + start, in, len );
in += len;
if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
in += 2;
}
apu.reset( &apu_impl );
apu2.reset( &apu_impl );
cpu::reset( mem.ram );
time_mask = 0; // disables sound during init
call_init( track );
time_mask = -1;
next_play = play_period();
return 0;
}
@ -383,7 +383,7 @@ void Sap_Emu::cpu_write_( sap_addr_t addr, int data )
apu.write_data( time() & time_mask, addr, data );
return;
}
if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) &&
info.stereo )
{
@ -403,7 +403,7 @@ inline void Sap_Emu::call_play()
case 'B':
cpu_jsr( info.play_addr );
break;
case 'C':
cpu_jsr( info.play_addr + 6 );
break;
@ -417,7 +417,7 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
{
if ( cpu::run( duration ) || r.pc > idle_addr )
return "Emulation error (illegal instruction)";
if ( r.pc == idle_addr )
{
if ( next_play <= duration )
@ -433,7 +433,7 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
}
}
}
duration = time();
next_play -= duration;
check( next_play >= 0 );
@ -442,6 +442,6 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
apu.end_frame( duration );
if ( info.stereo )
apu2.end_frame( duration );
return 0;
}

View file

@ -43,21 +43,21 @@ public: private: friend class Sap_Cpu;
void cpu_write_( sap_addr_t, int );
private:
info_t info;
byte const* file_end;
sap_time_t scanline_period;
sap_time_t next_play;
sap_time_t time_mask;
Sap_Apu apu;
Sap_Apu apu2;
// large items
struct {
byte padding1 [0x100];
byte ram [0x10000 + 0x100];
} mem;
Sap_Apu_Impl apu_impl;
sap_time_t play_period() const;
void call_play();
void cpu_jsr( sap_addr_t );

View file

@ -78,7 +78,7 @@ void Sms_Square::run( blip_time_t time, blip_time_t end_time )
synth->offset( time, delta, output );
}
}
time += delay;
if ( time < end_time )
{
@ -115,7 +115,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
int amp = volume;
if ( shifter & 1 )
amp = -amp;
{
int delta = amp - last_amp;
if ( delta )
@ -124,11 +124,11 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
synth.offset( time, delta, output );
}
}
time += delay;
if ( !volume )
time = end_time;
if ( time < end_time )
{
Blip_Buffer* const output = this->output;
@ -137,11 +137,11 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
int period = *this->period * 2;
if ( !period )
period = 16;
do
{
int changed = shifter + 1;
shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1);
shifter = (feedback & uMinus(shifter & 1)) ^ (shifter >> 1);
if ( changed & 2 ) // true if bits 0 and 1 differ
{
delta = -delta;
@ -150,7 +150,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
time += period;
}
while ( time < end_time );
this->shifter = shifter;
this->last_amp = delta >> 1;
}
@ -167,7 +167,7 @@ Sms_Apu::Sms_Apu()
oscs [i] = &squares [i];
}
oscs [3] = &noise;
volume( 1.0 );
reset();
}
@ -210,7 +210,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width )
{
last_time = 0;
latch = 0;
if ( !feedback || !noise_width )
{
feedback = 0x0009;
@ -224,7 +224,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width )
noise_feedback = (noise_feedback << 1) | (feedback & 1);
feedback >>= 1;
}
squares [0].reset();
squares [1].reset();
squares [2].reset();
@ -234,7 +234,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width )
void Sms_Apu::run_until( blip_time_t end_time )
{
require( end_time >= last_time ); // end_time must not be before previous time
if ( end_time > last_time )
{
// run oscillators
@ -250,7 +250,7 @@ void Sms_Apu::run_until( blip_time_t end_time )
noise.run( last_time, end_time );
}
}
last_time = end_time;
}
}
@ -259,7 +259,7 @@ void Sms_Apu::end_frame( blip_time_t end_time )
{
if ( end_time > last_time )
run_until( end_time );
assert( last_time >= end_time );
last_time -= end_time;
}
@ -267,9 +267,9 @@ void Sms_Apu::end_frame( blip_time_t end_time )
void Sms_Apu::write_ggstereo( blip_time_t time, int data )
{
require( (unsigned) data <= 0xFF );
run_until( time );
for ( int i = 0; i < osc_count; i++ )
{
Sms_Osc& osc = *oscs [i];
@ -297,12 +297,12 @@ static unsigned char const volumes [16] = {
void Sms_Apu::write_data( blip_time_t time, int data )
{
require( (unsigned) data <= 0xFF );
run_until( time );
if ( data & 0x80 )
latch = data;
int index = (latch >> 5) & 3;
if ( latch & 0x10 )
{
@ -323,7 +323,7 @@ void Sms_Apu::write_data( blip_time_t time, int data )
noise.period = &noise_periods [select];
else
noise.period = &squares [2].period;
noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback;
noise.shifter = 0x8000;
}

View file

@ -10,34 +10,34 @@ class Sms_Apu {
public:
// Set overall volume of all oscillators, where 1.0 is full volume
void volume( double );
// Set treble equalization
void treble_eq( const blip_eq_t& );
// Outputs can be assigned to a single buffer for mono output, or to three
// buffers for stereo output (using Stereo_Buffer to do the mixing).
// Assign all oscillator outputs to specified buffer(s). If buffer
// is NULL, silences all oscillators.
void output( Blip_Buffer* mono );
void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Assign single oscillator output to buffer(s). Valid indicies are 0 to 3,
// which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL,
// silences oscillator.
enum { osc_count = 4 };
void osc_output( int index, Blip_Buffer* mono );
void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Reset oscillators and internal state
void reset( unsigned noise_feedback = 0, int noise_width = 0 );
// Write GameGear left/right assignment byte
void write_ggstereo( blip_time_t, int );
// Write to data port
void write_data( blip_time_t, int );
// Run all oscillators up to specified time, end current frame, then
// start a new frame at time 0.
void end_frame( blip_time_t );
@ -49,7 +49,7 @@ private:
// noncopyable
Sms_Apu( const Sms_Apu& );
Sms_Apu& operator = ( const Sms_Apu& );
Sms_Osc* oscs [osc_count];
Sms_Square squares [3];
Sms_Square::Synth square_synth; // used by squares
@ -58,7 +58,7 @@ private:
Sms_Noise noise;
unsigned noise_feedback;
unsigned looped_feedback;
void run_until( blip_time_t );
};

View file

@ -12,11 +12,11 @@ struct Sms_Osc
Blip_Buffer* outputs [4]; // NULL, right, left, center
Blip_Buffer* output;
int output_select;
int delay;
int last_amp;
int volume;
Sms_Osc();
void reset();
};
@ -25,10 +25,10 @@ struct Sms_Square : Sms_Osc
{
int period;
int phase;
typedef Blip_Synth<blip_good_quality,1> Synth;
const Synth* synth;
void reset();
void run( blip_time_t, blip_time_t );
};
@ -38,10 +38,10 @@ struct Sms_Noise : Sms_Osc
const int* period;
unsigned shifter;
unsigned feedback;
typedef Blip_Synth<blip_med_quality,1> Synth;
Synth synth;
void reset();
void run( blip_time_t, blip_time_t );
};

View file

@ -33,14 +33,14 @@ 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
@ -60,7 +60,7 @@ blargg_err_t Snes_Spc::init()
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++ )
{
@ -68,11 +68,11 @@ blargg_err_t Snes_Spc::init()
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 0;
}
@ -87,7 +87,7 @@ 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;
@ -117,7 +117,7 @@ void Snes_Spc::timers_loaded()
t->enabled = REGS [r_control] >> i & 1;
t->counter = REGS_IN [r_t0out + i] & 0x0F;
}
set_tempo( m.tempo );
}
@ -126,7 +126,7 @@ 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;
@ -141,7 +141,7 @@ 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.ram + 0x10000, cpu_pad_fill, sizeof m.ram.padding1 );
@ -163,16 +163,16 @@ void Snes_Spc::reset_time_regs()
#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();
}
@ -182,16 +182,16 @@ 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();
}
@ -215,17 +215,17 @@ char const Snes_Spc::signature [signature_size + 1] =
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 );
blaarg_static_assert( sizeof (spc_file_t) == spc_min_file_size + 0x80, "SPC File header layout incorrect!" );
// 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;
@ -233,16 +233,16 @@ blargg_err_t Snes_Spc::load_spc( void const* data, long size )
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 0;
}
@ -270,42 +270,42 @@ void Snes_Spc::reset_buf()
sample_t* out = m.extra_buf;
while ( out < &m.extra_buf [extra_size / 2] )
*out++ = 0;
m.extra_pos = out;
m.buf_begin = 0;
dsp.set_output( 0, 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
@ -324,7 +324,7 @@ void Snes_Spc::save_extra()
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;
@ -332,7 +332,7 @@ void Snes_Spc::save_extra()
*out++ = *in;
for ( in = dsp.extra(); in < dsp_end ; in++ )
*out++ = *in;
m.extra_pos = out;
assert( out <= &m.extra_buf [extra_size] );
}
@ -345,7 +345,7 @@ blargg_err_t Snes_Spc::play( int count, sample_t* out )
set_output( out, count );
end_frame( count * (clocks_per_sample / 2) );
}
const char* err = m.cpu_error;
m.cpu_error = 0;
return err;
@ -357,27 +357,27 @@ blargg_err_t Snes_Spc::skip( int count )
if ( count > 2 * sample_rate * 2 )
{
set_output( 0, 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, 0 );
}

View file

@ -13,12 +13,12 @@ struct Snes_Spc {
public:
// 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 };
@ -43,7 +43,7 @@ public:
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 );
@ -51,20 +51,22 @@ public:
// 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.
// Only supported by fast DSP.
void disable_surround( bool disable = true );
void disable_echo( bool disable = true );
// Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc.
enum { tempo_unit = 0x100 };
static const unsigned int tempo_unit = 0x100;
void set_tempo( int );
// SPC music files
@ -73,17 +75,17 @@ public:
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
@ -91,7 +93,7 @@ public:
enum { state_size = 67 * 1024L }; // 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 );
@ -116,18 +118,18 @@ public:
uint8_t 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
// 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
@ -140,46 +142,46 @@ public:
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 -- but this is
@ -190,14 +192,14 @@ private:
} 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,
@ -207,7 +209,7 @@ private:
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();
@ -217,7 +219,7 @@ private:
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 );
@ -229,10 +231,10 @@ private:
int cpu_read_smp_reg ( int i, rel_time_t );
int cpu_read ( uint16_t addr, rel_time_t );
unsigned CPU_mem_bit ( uint16_t 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];
@ -252,7 +254,7 @@ private:
};
static char const signature [signature_size + 1];
void save_regs( uint8_t out [reg_count] );
};
@ -273,9 +275,11 @@ inline void Snes_Spc::write_port( time_t t, int port, int 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::disable_echo( bool disable ) { dsp.disable_echo( disable ); }
#if !SPC_NO_COPY_STATE_FUNCS
inline bool Snes_Spc::check_kon() { return dsp.check_kon(); }
#endif

View file

@ -51,7 +51,7 @@ 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 );
@ -94,8 +94,8 @@ void Snes_Spc::enable_rom( int enable )
//// DSP
#if SPC_LESS_ACCURATE
int const max_reg_time = 29;
static int const max_reg_time = 29;
signed char const Snes_Spc::reg_times_ [256] =
{
-1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22,
@ -106,7 +106,7 @@ void Snes_Spc::enable_rom( int enable )
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,
@ -116,7 +116,7 @@ void Snes_Spc::enable_rom( int enable )
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 )\
@ -141,13 +141,13 @@ void Snes_Spc::enable_rom( int enable )
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;
}
@ -160,7 +160,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t 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;
@ -168,11 +168,11 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time )
}
}
#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 )
@ -193,7 +193,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t 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) )
@ -212,7 +212,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time )
}
return false;
}
#define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
#else
#define MEM_ACCESS( time, addr )
@ -224,56 +224,62 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time )
#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,
{
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
@ -282,7 +288,7 @@ static unsigned char const glitch_probs [3] [256] =
// by compiler.
// If write isn't preceded by read, data has this added to it
int const no_read_before_write = 0x2000;
static int const no_read_before_write = 0x2000;
void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr )
{
@ -304,7 +310,7 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr )
((period - 1) | ~0x0F) & period )
{
//debug_printf( "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;
@ -312,13 +318,13 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr )
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;
}
@ -327,28 +333,28 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr )
}
break;
}
case r_t0out:
case r_t1out:
case r_t2out:
if ( !SPC_MORE_ACCURACY )
debug_printf( "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 )
debug_printf( "SPC wrote to test register\n" );
break;
case r_control:
// port clears
if ( data & 0x10 )
@ -361,7 +367,7 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr )
REGS_IN [r_cpuio2] = 0;
REGS_IN [r_cpuio3] = 0;
}
// timers
{
for ( int i = 0; i < timer_count; i++ )
@ -404,7 +410,7 @@ void Snes_Spc::cpu_write_high( int data, uint8_t i )
void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time )
{
MEM_ACCESS( time, addr )
// RAM
RAM [addr] = (uint8_t) data;
if ( addr >= 0xF0 ) // 64%
@ -414,14 +420,14 @@ void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time )
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 > 7) ) // 36%
cpu_write_smp_reg( data, time, reg );
@ -452,7 +458,7 @@ inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time )
int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time )
{
MEM_ACCESS( time, addr )
// RAM
int result = RAM [addr];
int reg = addr - 0xF0;
@ -462,7 +468,7 @@ int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time )
if ( (unsigned) reg >= 0xFF00 ) // 21%
{
reg += 0x10 - r_t0out;
// Timers
if ( (unsigned) reg < timer_count ) // 90%
{
@ -484,7 +490,7 @@ int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time )
}
}
}
return result;
}
@ -516,7 +522,7 @@ uint8_t* Snes_Spc::run_until_( time_t end_time )\
#ifndef NDEBUG
// Used only for assert
int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
static int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks
#endif
@ -526,25 +532,25 @@ void Snes_Spc::end_frame( time_t end_time )
// 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();

View file

@ -147,16 +147,16 @@ SPC_CPU_RUN_FUNC
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 += (int8_t) ram [pc];
inc_pc_loop:
@ -165,15 +165,15 @@ loop:
{
unsigned opcode;
unsigned data;
check( (unsigned) a < 0x100 );
check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 );
opcode = ram [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
@ -186,18 +186,18 @@ loop:
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)
pc++;
data = ram [pc];
switch ( opcode )
{
// Common instructions
#define BRANCH( cond )\
@ -213,17 +213,17 @@ 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
{
uint8_t l, h;
@ -232,13 +232,13 @@ loop:
SET_PC( l | (h << 8) );
}
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 );
@ -248,7 +248,7 @@ loop:
case 0x8F:{// MOV dp,#imm
int temp = READ_PC( pc + 1 );
pc += 2;
#if !SPC_MORE_ACCURACY
{
int i = dp + temp;
@ -257,7 +257,7 @@ loop:
if ( (unsigned) i < 0x10 ) // 76%
{
REGS [i] = (uint8_t) data;
// Registers other than $F2 and $F4-$F7
if ( i != 2 && (i < 4 || i > 7)) // 12%
cpu_write_smp_reg( data, rel_time, i );
@ -268,7 +268,7 @@ loop:
#endif
goto loop;
}
case 0xC4: // MOV dp,a
++pc;
#if !SPC_MORE_ACCURACY
@ -280,7 +280,7 @@ loop:
{
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
@ -291,7 +291,7 @@ loop:
WRITE_DP( 0, data, a );
#endif
goto loop;
#define CASE( n ) /*FALLTHRU*/case n:
// Define common address modes based on opcode for immediate mode. Execution
@ -335,25 +335,25 @@ loop:
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);/*FALLTHRU*/
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;
@ -362,7 +362,7 @@ loop:
x = data;
nz = data;
goto inc_pc_loop;
case 0xFB: // MOV Y,dp+X
data = (uint8_t) (data + x);/*FALLTHRU*/
case 0xEB: // MOV Y,dp
@ -370,7 +370,7 @@ loop:
pc++;
READ_DP_TIMER( 0, data, y = nz );
goto loop;
case 0xEC:{// MOV Y,abs
int temp = READ_PC16( pc );
pc += 2;
@ -378,18 +378,18 @@ loop:
//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
@ -402,13 +402,13 @@ loop:
pc += 2;
goto loop;
}
case 0xD9: // MOV dp+Y,X
data = (uint8_t) (data + y);/*FALLTHRU*/
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);/*FALLTHRU*/
case 0xCB: // MOV dp,Y
@ -416,44 +416,44 @@ loop:
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 = (uint8_t) (x + 1);
goto loop;
// 5. 8-BIT LOGIC OPERATION COMMANDS
#define LOGICAL_OP( op, func )\
ADDR_MODES( op ) /* addr */\
data = READ( 0, data );/*FALLTHRU*/\
@ -477,13 +477,13 @@ loop:
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
@ -493,14 +493,14 @@ loop:
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 );/*FALLTHRU*/
case 0x78: // CMP dp,imm
@ -508,7 +508,7 @@ loop:
c = ~nz;
nz &= 0xFF;
goto inc_pc_loop;
case 0x3E: // CMP X,dp
data += dp;
goto cmp_x_addr;
@ -522,7 +522,7 @@ loop:
c = ~nz;
nz &= 0xFF;
goto inc_pc_loop;
case 0x7E: // CMP Y,dp
data += dp;
goto cmp_y_addr;
@ -536,7 +536,7 @@ loop:
c = ~nz;
nz &= 0xFF;
goto inc_pc_loop;
{
int addr;
case 0xB9: // SBC (x),(y)
@ -554,7 +554,7 @@ loop:
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:
@ -568,11 +568,11 @@ loop:
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);
@ -585,9 +585,9 @@ loop:
WRITE( 0, addr, /*(uint8_t)*/ nz );
goto inc_pc_loop;
}
}
// 6. ADDITION & SUBTRACTION COMMANDS
#define INC_DEC_REG( reg, op )\
@ -598,7 +598,7 @@ 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
@ -619,7 +619,7 @@ loop:
nz += READ( -1, data );
WRITE( 0, data, /*(uint8_t)*/ nz );
goto inc_pc_loop;
// 7. SHIFT, ROTATION COMMANDS
case 0x5C: // LSR A
@ -630,7 +630,7 @@ loop:
a = nz;
goto loop;
}
case 0x1C: // ASL A
c = 0; /*fallthrough*/
case 0x3C:{// ROL A
@ -640,7 +640,7 @@ loop:
a = (uint8_t) nz;
goto loop;
}
case 0x0B: // ASL dp
c = 0;
data += dp;
@ -662,7 +662,7 @@ loop:
nz |= (c = READ( -1, data ) << 1);
WRITE( 0, data, /*(uint8_t)*/ nz );
goto inc_pc_loop;
case 0x4B: // LSR dp
c = 0;
data += dp;
@ -699,12 +699,12 @@ loop:
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
@ -716,33 +716,33 @@ loop:
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);
@ -751,10 +751,10 @@ loop:
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;
@ -765,7 +765,7 @@ loop:
nz &= 0xFF;
goto inc_pc_loop;
}
// 10. MULTIPLICATION & DIVISON COMMANDS
case 0xCF: { // MUL YA
@ -776,19 +776,19 @@ loop:
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;
@ -799,16 +799,16 @@ loop:
a = 255 - (ya - x * 0x200) / (256 - x);
y = x + (ya - x * 0x200) % (256 - x);
}
nz = (uint8_t) a;
a = (uint8_t) a;
y = (uint8_t) y;
goto loop;
}
// 11. DECIMAL COMPENSATION COMMANDS
case 0xDF: // DAA
SUSPICIOUS_OPCODE( "DAA" );
if ( a > 0x99 || c & 0x100 )
@ -816,14 +816,14 @@ loop:
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) )
@ -831,38 +831,38 @@ loop:
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 += (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++;\
@ -871,7 +871,7 @@ loop:
rel_time -= 2;\
goto inc_pc_loop;\
}
case 0x03: // BBS dp.bit,rel
case 0x23:
case 0x43:
@ -881,7 +881,7 @@ loop:
case 0xC3:
case 0xE3:
CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 )
case 0x13: // BBC dp.bit,rel
case 0x33:
case 0x53:
@ -891,7 +891,7 @@ loop:
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
@ -901,26 +901,26 @@ loop:
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();
@ -932,14 +932,14 @@ loop:
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:
@ -961,7 +961,7 @@ loop:
PUSH16( ret_addr );
goto loop;
}
// 14. STACK OPERATION COMMANDS
{
@ -979,7 +979,7 @@ loop:
SET_PSW( temp );
goto loop;
}
case 0x0D: { // PUSH PSW
int temp;
GET_PSW( temp );
@ -990,27 +990,27 @@ 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
@ -1037,7 +1037,7 @@ loop:
WRITE( 0, data, (READ( -1, data ) & mask) | bit );
goto inc_pc_loop;
}
case 0x0E: // TSET1 abs
case 0x4E: // TCLR1 abs
data = READ_PC16( pc );
@ -1051,32 +1051,32 @@ loop:
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;
@ -1086,7 +1086,7 @@ loop:
WRITE( 0, data & 0x1FFF, temp );
}
goto loop;
case 0xCA: // MOV1 mem.bit,C
data = READ_PC16( pc );
pc += 2;
@ -1097,53 +1097,53 @@ loop:
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
if ( pc == 0x0000 )
@ -1160,13 +1160,13 @@ loop:
m.cpu_error = "SPC emulation error";
goto stop;
} // switch
assert( 0 ); // catch any unhandled instructions
}
out_of_time:
rel_time -= m.cycle_table [ ram [pc] ]; // undo partial execution of opcode
stop:
// Uncache registers
m.cpu_regs.pc = (uint16_t) GET_PC();
m.cpu_regs.sp = ( uint8_t) GET_SP();

View file

@ -155,9 +155,9 @@ inline void Spc_Dsp::init_counter()
// counters start out with this synchronization
m.counters [0] = 1;
m.counters [1] = 0;
m.counters [2] = -0x20u;
m.counters [2] = uMinus(0x20u);
m.counters [3] = 0x0B;
int n = 2;
for ( int i = 1; i < 32; i++ )
{
@ -190,7 +190,7 @@ void Spc_Dsp::run( int clock_count )
m.phase = new_phase & 31;
if ( !count )
return;
uint8_t* const ram = m.ram;
#ifdef SPC_ISOLATED_ECHO_BUFFER
uint8_t* const echo_ram = m.echo_ram;
@ -198,13 +198,24 @@ void Spc_Dsp::run( int clock_count )
uint8_t const* const dir = &ram [REG(dir) * 0x100];
int const slow_gaussian = (REG(pmon) >> 1) | REG(non);
int const noise_rate = REG(flg) & 0x1F;
// Global volume
int mvoll = (int8_t) REG(mvoll);
int mvolr = (int8_t) REG(mvolr);
int evoll = (int8_t) REG(evoll);
int evolr = (int8_t) REG(evolr);
if ( !m.echo_enable)
{
mvoll = 127;
mvolr = 127;
evoll = 0;
evolr = 0;
}
if ( mvoll * mvolr < m.surround_threshold )
mvoll = -mvoll; // eliminate surround
do
{
// KON/KOFF reading
@ -212,20 +223,20 @@ void Spc_Dsp::run( int clock_count )
{
m.new_kon &= ~m.kon;
m.kon = m.new_kon;
m.t_koff = REG(koff);
m.t_koff = REG(koff);
}
run_counter( 1 );
run_counter( 2 );
run_counter( 3 );
// Noise
if ( !READ_COUNTER( noise_rate ) )
{
int feedback = (m.noise << 13) ^ (m.noise << 14);
m.noise = (feedback & 0x4000) ^ (m.noise >> 1);
}
// Voices
int pmon_input = 0;
int main_out_l = 0;
@ -238,20 +249,20 @@ void Spc_Dsp::run( int clock_count )
do
{
#define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] )
int brr_header = ram [v->brr_addr];
int kon_delay = v->kon_delay;
// Pitch
int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF;
if ( REG(pmon) & vbit )
pitch += ((pmon_input >> 5) * pitch) >> 10;
// KON phases
if ( --kon_delay >= 0 )
{
v->kon_delay = kon_delay;
// Get ready to start BRR decoding on next sample
if ( kon_delay == 4 )
{
@ -260,20 +271,20 @@ void Spc_Dsp::run( int clock_count )
v->buf_pos = v->buf;
brr_header = 0; // header is ignored on this sample
}
// Envelope is never run during KON
v->env = 0;
v->hidden_env = 0;
// Disable BRR decoding until last three samples
v->interp_pos = (kon_delay & 3 ? 0x4000 : 0);
// Pitch is never added during KON
pitch = 0;
}
int env = v->env;
// Gaussian interpolation
{
int output = 0;
@ -284,9 +295,9 @@ void Spc_Dsp::run( int clock_count )
int offset = (unsigned) v->interp_pos >> 3 & 0x1FE;
short const* fwd = interleved_gauss + offset;
short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian
int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12];
if ( !(slow_gaussian & vbit) ) // 99%
{
// Faster approximation when exact sample value isn't necessary for pitch mod
@ -306,44 +317,44 @@ void Spc_Dsp::run( int clock_count )
output += (rev [1] * in [2]) >> 11;
output = (int16_t) output;
output += (rev [0] * in [3]) >> 11;
CLAMP16( output );
output &= ~1;
}
output = (output * env) >> 11 & ~1;
}
// Output
int l = output * v->volume [0];
int r = output * v->volume [1];
main_out_l += l;
main_out_r += r;
if ( REG(eon) & vbit )
{
echo_out_l += l;
echo_out_r += r;
}
}
pmon_input = output;
VREG(v_regs,outx) = (uint8_t) (output >> 8);
}
// Soft reset or end of sample
if ( REG(flg) & 0x80 || (brr_header & 3) == 1 )
{
v->env_mode = env_release;
env = 0;
}
if ( m.every_other_sample )
{
// KOFF
if ( m.t_koff & vbit )
v->env_mode = env_release;
// KON
if ( m.kon & vbit )
{
@ -352,7 +363,7 @@ void Spc_Dsp::run( int clock_count )
REG(endx) &= ~vbit;
}
}
// Envelope
if ( !v->kon_delay )
{
@ -378,7 +389,7 @@ void Spc_Dsp::run( int clock_count )
env--;
env -= env >> 8;
rate = env_data & 0x1F;
// optimized handling
v->hidden_env = env;
if ( READ_COUNTER( rate ) )
@ -428,13 +439,13 @@ void Spc_Dsp::run( int clock_count )
}
}
}
// 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 )
{
@ -442,13 +453,13 @@ void Spc_Dsp::run( int clock_count )
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
}
}
exit_env:
{
// Apply pitch
int old_pos = v->interp_pos;
@ -456,14 +467,14 @@ void Spc_Dsp::run( int clock_count )
if ( interp_pos > 0x7FFF )
interp_pos = 0x7FFF;
v->interp_pos = interp_pos;
// BRR decode if necessary
if ( old_pos >= 0x4000 )
{
// Arrange the four input nybbles in 0xABCD order for easy decoding
int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 +
ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];
// Advance read position
int const brr_block_size = 9;
int brr_offset = v->brr_offset;
@ -482,9 +493,9 @@ void Spc_Dsp::run( int clock_count )
brr_offset = 1;
}
v->brr_offset = brr_offset;
// Decode
// 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11
static unsigned char const shifts [16 * 2] = {
13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16,
@ -493,18 +504,18 @@ void Spc_Dsp::run( int clock_count )
int const scale = brr_header >> 4;
int const right_shift = shifts [scale];
int const left_shift = shifts [scale + 16];
// Write to next four samples in circular buffer
int* pos = v->buf_pos;
int* end;
// Decode four samples
for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
{
// Extract upper nybble and scale appropriately. Every cast is
// necessary to maintain correctness and avoid undef behavior
int s = int16_t(uint16_t((int16_t) nybbles >> right_shift) << left_shift);
// Apply IIR filter (8 is the most commonly used)
int const filter = brr_header & 0x0C;
int const p1 = pos [brr_buf_size - 1];
@ -529,13 +540,13 @@ void Spc_Dsp::run( int clock_count )
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
}
if ( pos >= &v->buf [brr_buf_size] )
pos = v->buf;
v->buf_pos = pos;
@ -548,7 +559,7 @@ skip_brr:
v++;
}
while ( vbit < 0x100 );
// Echo position
int echo_offset = m.echo_offset;
#ifdef SPC_ISOLATED_ECHO_BUFFER
@ -563,23 +574,23 @@ skip_brr:
if ( echo_offset >= m.echo_length )
echo_offset = 0;
m.echo_offset = echo_offset;
// FIR
int echo_in_l = GET_LE16SA( echo_ptr + 0 );
int echo_in_r = GET_LE16SA( echo_ptr + 2 );
int (*echo_hist_pos) [2] = m.echo_hist_pos;
if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] )
echo_hist_pos = m.echo_hist;
m.echo_hist_pos = echo_hist_pos;
echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l;
echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r;
#define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10))
echo_in_l = CALC_FIR_( 7, echo_in_l );
echo_in_r = CALC_FIR_( 7, echo_in_r );
#define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] )
#define DO_FIR( i )\
echo_in_l += CALC_FIR( i, 0 );\
@ -594,39 +605,39 @@ skip_brr:
DO_FIR( 4 );
DO_FIR( 5 );
DO_FIR( 6 );
// Echo out
if ( !(REG(flg) & 0x20) )
{
int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14);
int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14);
// just to help pass more validation tests
#if SPC_MORE_ACCURACY
l &= ~1;
r &= ~1;
#endif
CLAMP16( l );
CLAMP16( r );
SET_LE16A( echo_ptr + 0, l );
SET_LE16A( echo_ptr + 2, r );
}
// Sound out
int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14;
int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14;
int l = (main_out_l * mvoll + echo_in_l * evoll) >> 14;
int r = (main_out_r * mvolr + echo_in_r * evolr) >> 14;
CLAMP16( l );
CLAMP16( r );
if ( (REG(flg) & 0x40) )
{
l = 0;
r = 0;
}
sample_t* out = m.out;
WRITE_SAMPLES( l, r, out );
m.out = out;
@ -647,26 +658,32 @@ void Spc_Dsp::mute_voices( int mask )
}
}
Spc_Dsp::Spc_Dsp()
{
memset(&m, 0, sizeof(state_t));
}
void Spc_Dsp::init( void* ram_64k )
{
m.ram = (uint8_t*) ram_64k;
mute_voices( 0 );
disable_surround( false );
disable_echo( false );
set_output( 0, 0 );
reset();
// be sure this sign-extends
blaarg_static_assert( (int16_t) 0x8000 == -0x8000, "This compiler doesn't sign-extend during integer promotion" );
// be sure right shift preserves sign
blaarg_static_assert( (-1 >> 1) == -1, "This compiler doesn't preserve sign on right-shift" );
#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
}
@ -674,13 +691,13 @@ void Spc_Dsp::init( void* ram_64k )
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();
}
@ -694,7 +711,7 @@ 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
int i;
for ( i = voice_count; --i >= 0; )
@ -704,7 +721,7 @@ void Spc_Dsp::load( uint8_t const regs [register_count] )
v.buf_pos = v.buf;
}
m.new_kon = REG(kon);
mute_voices( m.mute_mask );
soft_reset_common();
}

View file

@ -8,8 +8,9 @@
struct Spc_Dsp {
public:
Spc_Dsp();
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
@ -24,13 +25,13 @@ public:
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 spc_run_dsp()
// to catch the DSP up to present.
int read ( int addr ) const;
@ -50,8 +51,10 @@ public:
// If true, prevents channels and global volumes from being phase-negated
void disable_surround( bool disable = true );
void disable_echo( bool disable = true );
// State
// Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 };
void load( uint8_t const regs [register_count] );
@ -86,9 +89,9 @@ public:
sample_t const* out_pos() const { return m.out; }
public:
BLARGG_DISABLE_NOTHROW
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
@ -109,16 +112,16 @@ private:
struct state_t
{
uint8_t regs [register_count];
#ifdef SPC_ISOLATED_ECHO_BUFFER
// Echo buffer, for dodgy SPC rips that were only made to work in dodgy emulators
uint8_t echo_ram [64 * 1024];
#endif
// 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;
@ -126,25 +129,26 @@ private:
int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31)
unsigned counters [4];
int new_kon;
int t_koff;
voice_t voices [voice_count];
unsigned* counter_select [32];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
int surround_threshold;
int echo_enable;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;
sample_t extra [extra_size];
};
state_t m;
void init_counter();
void run_counter( int );
void soft_reset_common();
@ -166,14 +170,14 @@ inline void Spc_Dsp::update_voice_vol( int addr )
{
int l = (int8_t) m.regs [addr + v_voll];
int r = (int8_t) m.regs [addr + v_volr];
if ( l * r < m.surround_threshold )
{
// signs differ, so negate those that are negative
l ^= l >> 7;
r ^= r >> 7;
}
voice_t& v = m.voices [addr >> 4];
int enabled = v.enabled;
v.volume [0] = l & enabled;
@ -183,7 +187,7 @@ inline void Spc_Dsp::update_voice_vol( int addr )
inline void Spc_Dsp::write( int addr, int data )
{
assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data;
int low = addr & 0x0F;
if ( low < 0x2 ) // voice volumes
@ -194,7 +198,7 @@ inline void Spc_Dsp::write( int addr, int data )
{
if ( addr == r_kon )
m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written
m.regs [r_endx] = 0;
}
@ -205,6 +209,11 @@ inline void Spc_Dsp::disable_surround( bool disable )
m.surround_threshold = disable ? 0 : -0x4000;
}
inline void Spc_Dsp::disable_echo( bool disable )
{
m.echo_enable = !disable;
}
#define SPC_NO_COPY_STATE_FUNCS 1
#define SPC_LESS_ACCURATE 1

View file

@ -7,16 +7,6 @@
#include <string.h>
#include <algorithm>
#ifdef RARDLL
#define PASCAL
#define CALLBACK
#define LONG long
#define HANDLE void *
#define LPARAM intptr_t
#define UINT __attribute__((unused)) unsigned int
#include <dll.hpp>
#endif
/* 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
@ -35,17 +25,17 @@ using std::max;
// TODO: support Spc_Filter's bass
Spc_Emu::Spc_Emu( gme_type_t type )
Spc_Emu::Spc_Emu()
{
set_type( type );
set_type( gme_spc_type );
static const char* const names [SuperFamicom::SPC_DSP::voice_count] = {
"DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8"
};
set_voice_names( names );
set_gain( 1.4 );
enable_echo( true );
}
@ -60,19 +50,6 @@ byte const* Spc_Emu::trailer() const { return &file_data [min( file_size, spc_si
long Spc_Emu::trailer_size() const { return max( 0L, file_size - spc_size ); }
byte const* Rsn_Emu::trailer( int track ) const
{
const byte *track_data = spc[track];
long track_size = spc[track + 1] - spc[track];
return &track_data [min( track_size, spc_size )];
}
long Rsn_Emu::trailer_size( int track ) const
{
long track_size = spc[track + 1] - spc[track];
return max( 0L, track_size - spc_size );
}
static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
{
// header
@ -89,13 +66,13 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
debug_printf( "Extra data after SPC xid6 info\n" );
end = in + info_size;
}
int year = 0;
char copyright [256 + 5];
int copyright_len = 0;
int const year_len = 5;
int disc = 0, track = 0;
while ( end - in >= 4 )
{
// header
@ -109,7 +86,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
check( false );
break; // block goes past end of data
}
// handle specific block types
char* field = 0;
switch ( id )
@ -123,7 +100,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
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
/*
@ -141,7 +118,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
}
break;
*/
case 0x33:
check( len == 4 );
if ( len >= 4 )
@ -149,12 +126,12 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
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 )
@ -166,10 +143,10 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
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 )
@ -183,7 +160,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
}
}
}
char* p = &copyright [year_len];
if ( year )
{
@ -197,13 +174,13 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
}
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];
@ -217,7 +194,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
}
memcpy( out->track, p, &copyright [4] - p );
}
check( in == end );
}
@ -245,7 +222,7 @@ static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid
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++ )
{
@ -265,15 +242,15 @@ static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid
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 );
}
@ -284,12 +261,6 @@ blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const
return 0;
}
blargg_err_t Rsn_Emu::track_info_( track_info_t* out, int track ) const
{
get_spc_info( header( track ), trailer( track ), trailer_size( track ), out );
return 0;
}
static blargg_err_t check_spc_header( void const* header )
{
if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) )
@ -301,15 +272,12 @@ struct Spc_File : Gme_Info_
{
Spc_Emu::header_t header;
blargg_vector<byte> xid6;
Spc_File( gme_type_t type ) { set_type( type ); }
Spc_File() : Spc_File( gme_spc_type ) {}
Spc_File() { set_type( gme_spc_type ); }
blargg_err_t load_( Data_Reader& in )
{
long file_size = in.remain();
if ( is_archive )
return 0;
if ( file_size < 0x10180 )
return gme_wrong_file_type;
RETURN_ERR( in.read( &header, head_size ) );
@ -323,7 +291,7 @@ struct Spc_File : Gme_Info_
}
return 0;
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
get_spc_info( header, xid6.begin(), xid6.size(), out );
@ -338,106 +306,6 @@ static gme_type_t_ const gme_spc_type_ = { "Super Nintendo", 1, &new_spc_emu, &n
extern gme_type_t const gme_spc_type = &gme_spc_type_;
#ifdef RARDLL
static int CALLBACK call_rsn(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2)
{
byte **bp = (byte **)UserData;
unsigned char *addr = (unsigned char *)P1;
memcpy( *bp, addr, P2 );
*bp += P2;
return 0;
}
#endif
struct Rsn_File : Spc_File
{
blargg_vector<byte*> spc;
Rsn_File() : Spc_File( gme_rsn_type ) { is_archive = true; }
blargg_err_t load_archive( const char* path )
{
#ifdef RARDLL
struct RAROpenArchiveData data = {
.ArcName = (char *)path,
.OpenMode = RAR_OM_LIST, .OpenResult = 0,
.CmtBuf = 0, .CmtBufSize = 0, .CmtSize = 0, .CmtState = 0
};
// get the size of all unpacked headers combined
long pos = 0;
int count = 0;
unsigned biggest = 0;
blargg_vector<byte> temp;
HANDLE PASCAL rar = RAROpenArchive( &data );
struct RARHeaderData head;
for ( ; RARReadHeader( rar, &head ) == ERAR_SUCCESS; count++ )
{
RARProcessFile( rar, RAR_SKIP, 0, 0 );
long xid6_size = head.UnpSize - spc_size;
if ( xid6_size > 0 )
pos += xid6_size;
pos += head_size;
biggest = max( biggest, head.UnpSize );
}
xid6.resize( pos );
spc.resize( count + 1 );
temp.resize( biggest );
RARCloseArchive( rar );
// copy the headers/xid6 and index them
byte *bp;
data.OpenMode = RAR_OM_EXTRACT;
rar = RAROpenArchive( &data );
RARSetCallback( rar, call_rsn, (intptr_t)&bp );
for ( count = 0, pos = 0; RARReadHeader( rar, &head ) == ERAR_SUCCESS; )
{
bp = &temp[0];
RARProcessFile( rar, RAR_TEST, 0, 0 );
if ( !check_spc_header( bp - head.UnpSize ) )
{
spc[count++] = &xid6[pos];
memcpy( &xid6[pos], &temp[0], head_size );
pos += head_size;
long xid6_size = head.UnpSize - spc_size;
if ( xid6_size > 0 )
{
memcpy( &xid6[pos], &temp[spc_size], xid6_size );
pos += xid6_size;
}
}
}
spc[count] = &xid6[pos];
set_track_count( count );
RARCloseArchive( rar );
return 0;
#else
(void) path;
return gme_wrong_file_type;
#endif
}
blargg_err_t track_info_( track_info_t* out, int track ) const
{
if ( static_cast<size_t>(track) >= spc.size() )
return "Invalid track";
long xid6_size = spc[track + 1] - ( spc[track] + head_size );
get_spc_info(
*(Spc_Emu::header_t const*) spc[track],
spc[track] + head_size, xid6_size, out
);
return 0;
}
};
static Music_Emu* new_rsn_emu () { return BLARGG_NEW Rsn_Emu ; }
static Music_Emu* new_rsn_file() { return BLARGG_NEW Rsn_File; }
static gme_type_t_ const gme_rsn_type_ = { "Super Nintendo", 0, &new_rsn_emu, &new_rsn_file, "RSN", 0 };
extern gme_type_t const gme_rsn_type = &gme_rsn_type_;
// Setup
blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate )
@ -466,14 +334,17 @@ void Spc_Emu::mute_voices_( int m )
smp.dsp.channel_enable( i, !( m & j ) );
}
void Spc_Emu::disable_echo_( bool disable )
{
smp.dsp.spc_dsp.enable_echo( !disable );
}
blargg_err_t Spc_Emu::load_mem_( byte const* in, long size )
{
assert( offsetof (header_t,unused2 [46]) == header_size );
blaarg_static_assert( offsetof (header_t,unused2 [46]) == header_size, "SPC Header layout incorrect!" );
file_data = in;
file_size = size;
set_voice_count( SuperFamicom::SPC_DSP::voice_count );
if ( is_archive )
return 0;
if ( size < 0x10180 )
return gme_wrong_file_type;
return check_spc_header( in );
@ -493,36 +364,36 @@ blargg_err_t Spc_Emu::start_track_( int track )
filter.clear();
smp.reset();
const byte * ptr = file_data;
Spc_Emu::header_t & header = *(Spc_Emu::header_t*)ptr;
ptr += sizeof(header);
smp.regs.pc = header.pc[0] + header.pc[1] * 0x100;
smp.regs.a = header.a;
smp.regs.x = header.x;
smp.regs.y = header.y;
smp.regs.p = header.psw;
smp.regs.s = header.sp;
memcpy( smp.apuram, ptr, sizeof smp.apuram );
// clear input ports that contain out port data from dump
memset( smp.apuram + 0xF4, 0, 4 );
memcpy( smp.sfm_last, ptr + 0xF4, 4 );
static const uint8_t regs_to_copy[][2] = {
{0xFC,0xFF}, {0xFB,0xFF}, {0xFA,0xFF}, {0xF9,0xFF},
{0xF8,0xFF}, {0xF2,0xFF}, {0xF1,0x87}
};
for (auto n : regs_to_copy)
smp.op_buswrite( n[0], ptr[ n[0] ] & n[1] );
smp.timer0.stage3_ticks = ptr[ 0xFD ] & 0x0F;
smp.timer1.stage3_ticks = ptr[ 0xFE ] & 0x0F;
smp.timer2.stage3_ticks = ptr[ 0xFF ] & 0x0F;
ptr += sizeof smp.apuram;
smp.dsp.spc_dsp.load( ptr );
#if 1
@ -542,7 +413,7 @@ blargg_err_t Spc_Emu::start_track_( int track )
filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) );
track_info_t spc_info;
RETURN_ERR( track_info_( &spc_info, track ) );
// Set a default track length, need a non-zero fadeout
if ( autoload_playback_limit() && ( spc_info.length > 0 ) )
set_fade ( spc_info.length, 50 );
@ -563,15 +434,15 @@ blargg_err_t Spc_Emu::skip_( long count )
count = long (count * resampler.ratio()) & ~1;
count -= resampler.skip_input( count );
}
// TODO: shouldn't skip be adjusted for the 64 samples read afterwards?
if ( count > 0 )
{
smp.skip( count );
filter.clear();
}
// eliminate pop due to resampler
if ( sample_rate() != native_sample_rate )
{
@ -587,7 +458,7 @@ blargg_err_t Spc_Emu::play_( long count, sample_t* out )
{
if ( sample_rate() == native_sample_rate )
return play_and_filter( count, out );
long remain = count;
while ( remain > 0 )
{
@ -602,60 +473,3 @@ blargg_err_t Spc_Emu::play_( long count, sample_t* out )
check( remain == 0 );
return 0;
}
blargg_err_t Rsn_Emu::load_archive( const char* path )
{
#ifdef RARDLL
struct RAROpenArchiveData data = {
.ArcName = (char *)path,
.OpenMode = RAR_OM_LIST, .OpenResult = 0,
.CmtBuf = 0, .CmtBufSize = 0, .CmtSize = 0, .CmtState = 0
};
// get the file count and unpacked size
long pos = 0;
int count = 0;
HANDLE PASCAL rar = RAROpenArchive( &data );
struct RARHeaderData head;
for ( ; RARReadHeader( rar, &head ) == ERAR_SUCCESS; count++ )
{
RARProcessFile( rar, RAR_SKIP, 0, 0 );
pos += head.UnpSize;
}
rsn.resize( pos );
spc.resize( count + 1 );
RARCloseArchive( rar );
// copy the stream and index the tracks
byte *bp = &rsn[0];
data.OpenMode = RAR_OM_EXTRACT;
rar = RAROpenArchive( &data );
RARSetCallback( rar, call_rsn, (intptr_t)&bp );
for ( count = 0, pos = 0; RARReadHeader( rar, &head ) == ERAR_SUCCESS; )
{
RARProcessFile( rar, RAR_TEST, 0, 0 );
if ( !check_spc_header( bp - head.UnpSize ) )
spc[count++] = &rsn[pos];
pos += head.UnpSize;
}
spc[count] = &rsn[pos];
set_track_count( count );
RARCloseArchive( rar );
return 0;
#else
(void) path;
return gme_wrong_file_type;
#endif
}
blargg_err_t Rsn_Emu::start_track_( int track )
{
if ( static_cast<size_t>(track) >= spc.size() )
return "Invalid track requested";
file_data = spc[track];
file_size = spc[track + 1] - spc[track];
return Spc_Emu::start_track_( track );
}
Rsn_Emu::~Rsn_Emu() { }

View file

@ -14,7 +14,7 @@ public:
// The Super Nintendo hardware samples at 32kHz. Other sample rates are
// handled by resampling the 32kHz output; emulation accuracy is not affected.
enum { native_sample_rate = 32000 };
// SPC file header
enum { header_size = 0x100 };
struct header_t
@ -37,26 +37,26 @@ public:
byte emulator;
byte unused2 [46];
};
// Header for currently loaded file
header_t const& header() const { return *(header_t const*) file_data; }
// Prevents channels and global volumes from being phase-negated
void disable_surround( bool disable = true );
// Enables gaussian=0, cubic=1 or sinc=2 interpolation
// Or negative levels for worse quality, linear=-1 or nearest=-2
void interpolation_level( int level = 0 );
// Enables native echo
void enable_echo( bool enable = true );
void mute_effects( bool mute );
SuperFamicom::SMP const* get_smp() const;
SuperFamicom::SMP * get_smp();
static gme_type_t static_type() { return gme_spc_type; }
public:
// deprecated
using Music_Emu::load;
@ -66,8 +66,7 @@ public:
long trailer_size() const;
public:
Spc_Emu( gme_type_t );
Spc_Emu() : Spc_Emu( gme_spc_type ) {}
Spc_Emu();
~Spc_Emu();
protected:
blargg_err_t load_mem_( byte const*, long );
@ -77,15 +76,16 @@ protected:
blargg_err_t play_( long, sample_t* );
blargg_err_t skip_( long );
void mute_voices_( int );
void disable_echo_( bool disable );
void set_tempo_( double );
void enable_accuracy_( bool );
private:
byte const* file_data;
long file_size;
private:
Fir_Resampler<24> resampler;
SPC_Filter filter;
SuperFamicom::SMP smp;
blargg_err_t play_and_filter( long count, sample_t out [] );
};
@ -96,20 +96,4 @@ inline void Spc_Emu::mute_effects( bool mute ) { enable_echo(!mute); }
inline SuperFamicom::SMP const* Spc_Emu::get_smp() const { return &smp; }
inline SuperFamicom::SMP * Spc_Emu::get_smp() { return &smp; }
class Rsn_Emu : public Spc_Emu {
public:
Rsn_Emu() : Spc_Emu( gme_rsn_type ) { is_archive = true; }
~Rsn_Emu();
blargg_err_t load_archive( const char* );
header_t const& header( int track ) const { return *(header_t const*) spc[track]; }
byte const* trailer( int ) const; // use track_info()
long trailer_size( int ) const;
protected:
blargg_err_t track_info_( track_info_t*, int ) const;
blargg_err_t start_track_( int );
private:
blargg_vector<byte> rsn;
blargg_vector<byte*> spc;
};
#endif

View file

@ -30,7 +30,7 @@ SPC_Filter::SPC_Filter()
void SPC_Filter::run( short* io, int count )
{
require( (count & 1) == 0 ); // must be even
int const gain = this->gain;
if ( enabled )
{
@ -42,26 +42,26 @@ void SPC_Filter::run( short* io, int count )
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;

View file

@ -8,35 +8,35 @@
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 };
static const unsigned int 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 };
static const unsigned int bass_none = 0;
static const unsigned int bass_norm = 8; // normal amount
static const unsigned int bass_max = 31;
void set_bass( int bass );
public:
SPC_Filter();
BLARGG_DISABLE_NOTHROW
private:
enum { gain_bits = 8 };
static const unsigned int gain_bits = 8;
int gain;
int bass;
bool enabled;

View file

@ -20,9 +20,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h"
double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
double const rolloff = 0.990;
double const oversample_factor = 1.0;
static double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
static double const rolloff = 0.990;
static double const oversample_factor = 1.0;
using std::min;
using std::max;
@ -30,16 +30,18 @@ using std::max;
Vgm_Emu::Vgm_Emu()
{
disable_oversampling_ = false;
psg_dual = false;
psg_t6w28 = false;
psg_rate = 0;
set_type( gme_vgm_type );
static int const types [8] = {
wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0
};
set_voice_types( types );
set_silence_lookahead( 1 ); // tracks should already be trimmed
set_equalizer( make_equalizer( -14.0, 80 ) );
}
@ -88,17 +90,17 @@ static void parse_gd3( byte const* in, byte const* end, track_info_t* out )
in = get_gd3_str ( in, end, out->comment );
}
int const gd3_header_size = 12;
static int const gd3_header_size = 12;
static long check_gd3_header( byte const* h, long remain )
{
if ( remain < gd3_header_size ) return 0;
if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
if ( get_le32( h + 4 ) >= 0x200 ) return 0;
long gd3_size = get_le32( h + 8 );
if ( gd3_size > remain - gd3_header_size ) return 0;
return gd3_size;
}
@ -106,19 +108,19 @@ byte const* Vgm_Emu::gd3_data( int* size ) const
{
if ( size )
*size = 0;
long gd3_offset = get_le32( header().gd3_offset ) - 0x2C;
if ( gd3_offset < 0 )
return 0;
byte const* gd3 = data + header_size + gd3_offset;
long gd3_size = check_gd3_header( gd3, data_end - gd3 );
if ( !gd3_size )
return 0;
if ( size )
*size = gd3_size + gd3_header_size;
return gd3;
}
@ -145,12 +147,12 @@ static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out )
blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const
{
get_vgm_length( header(), out );
int size;
byte const* gd3 = gd3_data( &size );
if ( gd3 )
parse_gd3( gd3 + gd3_header_size, gd3 + size, out );
return 0;
}
@ -165,18 +167,18 @@ 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 )
{
long file_size = in.remain();
if ( file_size <= Vgm_Emu::header_size )
return gme_wrong_file_type;
RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) );
RETURN_ERR( check_vgm_header( h ) );
long gd3_offset = get_le32( h.gd3_offset ) - 0x2C;
long remain = file_size - Vgm_Emu::header_size - gd3_offset;
byte gd3_h [gd3_header_size];
@ -193,7 +195,7 @@ struct Vgm_File : Gme_Info_
}
return 0;
}
blargg_err_t track_info_( track_info_t* out, int ) const
{
get_vgm_length( h, out );
@ -226,7 +228,7 @@ void Vgm_Emu::set_tempo_( double t )
// TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only)
//blip_time_factor = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 );
//vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 );
fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 );
}
}
@ -324,17 +326,17 @@ void Vgm_Emu::mute_voices_( int mask )
blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size )
{
assert( offsetof (header_t,unused2 [8]) == header_size );
blaarg_static_assert( offsetof (header_t,unused2 [8]) == header_size, "VGM Header layout incorrect!" );
if ( new_size <= header_size )
return gme_wrong_file_type;
header_t const& h = *(header_t const*) new_data;
RETURN_ERR( check_vgm_header( h ) );
check( get_le32( h.version ) <= 0x150 );
// psg rate
psg_rate = get_le32( h.psg_rate );
if ( !psg_rate )
@ -343,25 +345,25 @@ blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size )
psg_t6w28 = ( psg_rate & 0x80000000 ) != 0;
psg_rate &= 0x0FFFFFFF;
blip_buf.clock_rate( psg_rate );
data = new_data;
data_end = new_data + new_size;
// get loop
loop_begin = data_end;
if ( get_le32( h.loop_offset ) )
loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
set_voice_count( psg[0].osc_count );
RETURN_ERR( setup_fm() );
static const char* const fm_names [] = {
"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG"
};
static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
set_voice_names( uses_fm ? fm_names : psg_names );
// do after FM in case output buffer is changed
return Classic_Emu::setup_buffer( psg_rate );
}
@ -374,11 +376,11 @@ blargg_err_t Vgm_Emu::setup_fm()
bool ym2413_dual = ( ym2413_rate & 0x40000000 ) != 0;
if ( ym2413_rate && get_le32( header().version ) < 0x110 )
update_fm_rates( &ym2413_rate, &ym2612_rate );
uses_fm = false;
fm_rate = blip_buf.sample_rate() * oversample_factor;
if ( ym2612_rate )
{
ym2612_rate &= ~0xC0000000;
@ -395,7 +397,7 @@ blargg_err_t Vgm_Emu::setup_fm()
}
set_voice_count( 8 );
}
if ( !uses_fm && ym2413_rate )
{
ym2413_rate &= ~0xC0000000;
@ -418,7 +420,7 @@ blargg_err_t Vgm_Emu::setup_fm()
}
set_voice_count( 8 );
}
if ( uses_fm )
{
RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) );
@ -435,7 +437,7 @@ blargg_err_t Vgm_Emu::setup_fm()
psg[0].volume( gain() );
psg[1].volume( gain() );
}
return 0;
}
@ -447,7 +449,7 @@ blargg_err_t Vgm_Emu::start_track_( int track )
psg[0].reset( get_le16( header().noise_feedback ), header().noise_width );
if ( psg_dual )
psg[1].reset( get_le16( header().noise_feedback ), header().noise_width );
dac_disabled = -1;
pos = data + header_size;
pcm_data = pos;
@ -461,7 +463,7 @@ blargg_err_t Vgm_Emu::start_track_( int track )
if ( data_offset )
pos += data_offset + offsetof (header_t,data_offset) - 0x40;
}
if ( uses_fm )
{
if ( ym2413[0].enabled() )
@ -469,13 +471,13 @@ blargg_err_t Vgm_Emu::start_track_( int track )
if ( ym2413[1].enabled() )
ym2413[1].reset();
if ( ym2612[0].enabled() )
ym2612[0].reset();
if ( ym2612[1].enabled() )
ym2612[1].reset();
fm_time_offset = 0;
blip_buf.clear();
Dual_Resampler::clear();
@ -496,7 +498,7 @@ blargg_err_t Vgm_Emu::play_( long count, sample_t* out )
{
if ( !uses_fm )
return Classic_Emu::play_( count, out );
Dual_Resampler::dual_play( count, out, blip_buf );
return 0;
}

View file

@ -16,13 +16,13 @@ public:
// True if custom buffer and custom equalization are supported
// TODO: move into Music_Emu and rename to something like supports_custom_buffer()
bool is_classic_emu() const { return !uses_fm; }
blargg_err_t set_multi_channel ( bool is_enabled ) override;
// Disable running FM chips at higher than normal rate. Will result in slightly
// more aliasing of high notes.
void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; }
// VGM header format
enum { header_size = 0x40 };
struct header_t
@ -45,12 +45,12 @@ public:
byte data_offset [4];
byte unused2 [8];
};
// Header for currently loaded file
header_t const& header() const { return *(header_t const*) data; }
static gme_type_t static_type() { return gme_vgm_type; }
public:
// deprecated
using Music_Emu::load;

View file

@ -35,7 +35,7 @@ enum {
cmd_short_delay = 0x70,
cmd_pcm_delay = 0x80,
cmd_pcm_seek = 0xE0,
cmd_gg_stereo_2 = 0x3F,
cmd_psg_2 = 0x30,
cmd_ym2413_2 = 0xA1,
@ -46,28 +46,28 @@ enum {
ym2612_dac_port = 0x2A
};
inline int command_len( int command )
static inline int command_len( int command )
{
switch ( command >> 4 )
{
case 0x03:
case 0x04:
return 2;
case 0x05:
case 0x0A:
case 0x0B:
return 3;
case 0x0C:
case 0x0D:
return 4;
case 0x0E:
case 0x0F:
return 5;
}
check( false );
return 1;
}
@ -95,7 +95,7 @@ inline int Ym_Emu<Emu>::run_until( int time )
}
return true;
}
inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const
{
return (t * fm_time_factor + fm_time_offset) >> fm_time_bits;
@ -120,7 +120,7 @@ void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp )
blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
{
vgm_time_t vgm_time = this->vgm_time;
vgm_time_t vgm_time = this->vgm_time;
byte const* pos = this->pos;
if ( pos >= data_end )
{
@ -128,7 +128,7 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
if ( pos > data_end )
set_warning( "Stream lacked end event" );
}
while ( vgm_time < end_time && pos < data_end )
{
// TODO: be sure there are enough bytes left in stream for particular command
@ -138,23 +138,23 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
case cmd_end:
pos = loop_begin; // if not looped, loop_begin == data_end
break;
case cmd_delay_735:
vgm_time += 735;
break;
case cmd_delay_882:
vgm_time += 882;
break;
case cmd_gg_stereo:
psg[0].write_ggstereo( to_blip_time( vgm_time ), *pos++ );
break;
case cmd_psg:
psg[0].write_data( to_blip_time( vgm_time ), *pos++ );
break;
case cmd_gg_stereo_2:
psg[1].write_ggstereo( to_blip_time( vgm_time ), *pos++ );
break;
@ -167,11 +167,11 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
vgm_time += pos [1] * 0x100L + pos [0];
pos += 2;
break;
case cmd_byte_delay:
vgm_time += *pos++;
break;
case cmd_ym2413:
if ( ym2413[0].run_until( to_fm_time( vgm_time ) ) )
ym2413[0].write( pos [0], pos [1] );
@ -183,7 +183,7 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
ym2413[1].write( pos [0], pos [1] );
pos += 2;
break;
case cmd_ym2612_port0:
if ( pos [0] == ym2612_dac_port )
{
@ -200,7 +200,7 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
}
pos += 2;
break;
case cmd_ym2612_port1:
if ( ym2612[0].run_until( to_fm_time( vgm_time ) ) )
ym2612[0].write1( pos [0], pos [1] );
@ -240,13 +240,13 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
pos += size;
break;
}
case cmd_pcm_seek:
pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L +
pos [1] * 0x100L + pos [0];
pos += 4;
break;
default:
int cmd = pos [-1];
switch ( cmd & 0xF0 )
@ -255,15 +255,15 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
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" );
@ -273,14 +273,14 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time )
vgm_time -= end_time;
this->pos = pos;
this->vgm_time = vgm_time;
return to_blip_time( end_time );
}
int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf )
{
// to do: timing is working mostly by luck
int min_pairs = sample_count >> 1;
int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1;
assert( to_fm_time( vgm_time ) <= min_pairs );
@ -288,7 +288,7 @@ int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t*
while ( (pairs = to_fm_time( vgm_time )) < min_pairs )
vgm_time++;
//debug_printf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs );
if ( ym2612[0].enabled() )
{
ym2612[0].begin_frame( buf );
@ -303,7 +303,7 @@ int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t*
ym2413[1].begin_frame( buf );
memset( buf, 0, pairs * stereo * sizeof *buf );
}
run_commands( vgm_time );
if ( ym2612[0].enabled() )
@ -315,14 +315,14 @@ int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t*
ym2413[0].run_until( pairs );
if ( ym2413[1].enabled() )
ym2413[1].run_until( pairs );
fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) -
((long) pairs << fm_time_bits);
psg[0].end_frame( blip_time );
if ( psg_dual )
psg[1].end_frame( blip_time );
return pairs * stereo;
}
@ -336,35 +336,35 @@ void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const
{
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 );
}

View file

@ -29,44 +29,44 @@ public:
typedef Classic_Emu::sample_t sample_t;
protected:
enum { stereo = 2 };
typedef int vgm_time_t;
enum { fm_time_bits = 12 };
typedef int fm_time_t;
long fm_time_offset;
int fm_time_factor;
fm_time_t to_fm_time( vgm_time_t ) const;
enum { blip_time_bits = 12 };
int blip_time_factor;
blip_time_t to_blip_time( vgm_time_t ) const;
byte const* data;
byte const* loop_begin;
byte const* data_end;
void update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const;
vgm_time_t vgm_time;
byte const* pos;
blip_time_t run_commands( vgm_time_t );
int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf );
byte const* pcm_data;
byte const* pcm_pos;
int dac_amp;
int dac_disabled; // -1 if disabled
void write_pcm( vgm_time_t, int amp );
Ym_Emu<Ym2612_Emu> ym2612[2];
Ym_Emu<Ym2413_Emu> ym2413[2];
Blip_Buffer blip_buf;
Sms_Apu psg[2];
bool psg_dual;
bool psg_t6w28;
Blip_Synth<blip_med_quality,1> dac_synth;
friend class Vgm_Emu;
};

View file

@ -9,21 +9,21 @@ class Ym2413_Emu {
public:
Ym2413_Emu();
~Ym2413_Emu();
// Set output sample rate and chip clock rates, in Hz. Returns non-zero
// if error.
int set_rate( double sample_rate, double clock_rate );
// Reset to power-up state
void reset();
// Mute voice n if bit n (1 << n) of mask is set
enum { channel_count = 14 };
void mute_voices( int mask );
// Write 'data' to 'addr'
void write( int addr, int data );
// Run and write pair_count samples to output
typedef short sample_t;
enum { out_chan_count = 2 }; // stereo

View file

@ -2,17 +2,30 @@
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/
#if !defined(VGM_YM2612_GENS) && !defined(VGM_YM2612_NUKED) && !defined(VGM_YM2612_MAME)
#define VGM_YM2612_NUKED
#endif
#ifdef VGM_YM2612_GENS // LGPL v2.1+ license
# if defined(VGM_YM2612_NUKED) || defined(VGM_YM2612_MAME)
# error Only one of VGM_YM2612_GENS, VGM_YM2612_NUKED or VGM_YM2612_MAME can be defined
# endif
#include "Ym2612_GENS.h"
typedef Ym2612_GENS_Emu Ym2612_Emu;
#endif
#ifdef VGM_YM2612_NUKED // LGPL v2.1+ license
# if defined(VGM_YM2612_GENS) || defined(VGM_YM2612_MAME)
# error Only one of VGM_YM2612_GENS, VGM_YM2612_NUKED or VGM_YM2612_MAME can be defined
# endif
#include "Ym2612_Nuked.h"
typedef Ym2612_Nuked_Emu Ym2612_Emu;
#endif
#ifdef VGM_YM2612_MAME // GPL v2+ license
# if defined(VGM_YM2612_GENS) || defined(VGM_YM2612_NUKED)
# error Only one of VGM_YM2612_GENS, VGM_YM2612_NUKED or VGM_YM2612_MAME can be defined
# endif
#include "Ym2612_MAME.h"
typedef Ym2612_MAME_Emu Ym2612_Emu;
#endif

View file

@ -2,6 +2,8 @@
// Based on Gens 2.10 ym2612.c
#ifdef VGM_YM2612_GENS
#include "Ym2612_GENS.h"
#include <assert.h>
@ -36,7 +38,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include BLARGG_ENABLE_OPTIMIZER
#endif
const int output_bits = 14;
static const int output_bits = 14;
struct slot_t
{
@ -179,7 +181,7 @@ struct state_t
#define S2 1
#define S3 3
inline void set_seg( slot_t& s, int seg )
static inline void set_seg( slot_t& s, int seg )
{
s.env_xor = 0;
s.env_max = INT_MAX;
@ -253,7 +255,7 @@ static const unsigned char LFO_FMS_TAB [8] =
LFO_FMS_BASE * 12, LFO_FMS_BASE * 24
};
inline void YM2612_Special_Update() { }
static inline void YM2612_Special_Update() { }
struct Ym2612_GENS_Impl
{
@ -1024,7 +1026,7 @@ static void update_envelope_( slot_t* sl )
}
}
inline void update_envelope( slot_t& sl )
static inline void update_envelope( slot_t& sl )
{
int ecmp = sl.Ecmp;
if ( (sl.Ecnt += sl.Einc) >= ecmp )
@ -1317,3 +1319,5 @@ void Ym2612_GENS_Impl::run( int pair_count, Ym2612_GENS_Emu::sample_t* out )
}
void Ym2612_GENS_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); }
#endif /* VGM_YM2612_GENS */

View file

@ -2,6 +2,8 @@
// Based on Mame YM2612 ym2612.c
#ifdef VGM_YM2612_MAME
#include "Ym2612_MAME.h"
/*
@ -2578,7 +2580,7 @@ void ym2612_pre_generate(void *chip)
refresh_fc_eg_chan( OPN, &cch[5] );
}
void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[])
void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[2])
{
YM2612 *F2612 = (YM2612 *)chip;
FM_OPN *OPN = &F2612->OPN;
@ -2819,7 +2821,7 @@ static void * ym2612_init(void *param, int clock, int rate,
F2612->OPN.ST.clock = clock;
#if RSM_ENABLE
F2612->OPN.ST.rate = 53267;
F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / clock);
F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / 7670454);
F2612->OPN.ST.framecnt = 1 << RSM_FRAC;
memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2);
memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2);
@ -3107,3 +3109,5 @@ void Ym2612_MAME_Emu::run(int pair_count, Ym2612_MAME_Emu::sample_t *out)
(void) &Ym2612_MameImpl::TimerBOver; // squelch clang warning, which appears to be from a config choice
if ( impl ) Ym2612_MameImpl::ym2612_generate( impl, out, pair_count, 1);
}
#endif /* VGM_YM2612_MAME */

View file

@ -2,6 +2,8 @@
// Based on Nuked OPN2 ym3438.c and ym3438.h
#ifdef VGM_YM2612_NUKED
#include "Ym2612_Nuked.h"
/*
@ -1839,7 +1841,7 @@ const char *Ym2612_Nuked_Emu::set_rate(double sample_rate, double clock_rate)
void Ym2612_Nuked_Emu::reset()
{
Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast<Ym2612_NukedImpl::ym3438_t*>(impl);
if ( !chip_r ) Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast<Bit32u>(prev_sample_rate), static_cast<Bit32u>(prev_clock_rate) );
if ( chip_r ) Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast<Bit32u>(prev_sample_rate), static_cast<Bit32u>(prev_clock_rate) );
}
void Ym2612_Nuked_Emu::mute_voices(int mask)
@ -1870,3 +1872,5 @@ void Ym2612_Nuked_Emu::run(int pair_count, Ym2612_Nuked_Emu::sample_t *out)
if ( !chip_r ) return;
Ym2612_NukedImpl::OPN2_GenerateStreamMix(chip_r, out, pair_count);
}
#endif /* VGM_YM2612_NUKED */

Some files were not shown because too many files have changed in this diff Show more