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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 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 */; }; 83FC5D5E181B47FB00B917E5 /* dsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D3B181B47FB00B917E5 /* dsp.cpp */; };
83FC5D5F181B47FB00B917E5 /* dsp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5D3C181B47FB00B917E5 /* dsp.hpp */; }; 83FC5D5F181B47FB00B917E5 /* dsp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5D3C181B47FB00B917E5 /* dsp.hpp */; };
83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D57181B47FB00B917E5 /* smp.cpp */; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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; }; 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; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 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>"; }; 83FC5D3C181B47FB00B917E5 /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = "<group>"; };
@ -339,7 +344,7 @@
17C8F1860CBED26C008D969D /* Source */ = { 17C8F1860CBED26C008D969D /* Source */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
83489CF02783DC4F00BDCEA2 /* ext */, 83BDCDCF2E41A269003FC007 /* ext */,
83FC5D35181B47FB00B917E5 /* higan */, 83FC5D35181B47FB00B917E5 /* higan */,
17C8F18B0CBED286008D969D /* Ay_Apu.cpp */, 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */,
17C8F18C0CBED286008D969D /* Ay_Apu.h */, 17C8F18C0CBED286008D969D /* Ay_Apu.h */,
@ -447,22 +452,6 @@
name = Frameworks; name = Frameworks;
sourceTree = "<group>"; 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 */ = { 83747B7E2862D4DB0021245F /* Xcode-config */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -472,6 +461,25 @@
path = "../../Xcode-config"; path = "../../Xcode-config";
sourceTree = "<group>"; 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 */ = { 83FC5D35181B47FB00B917E5 /* higan */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
@ -546,22 +554,25 @@
17C8F1FF0CBED286008D969D /* Blip_Buffer.h in Headers */, 17C8F1FF0CBED286008D969D /* Blip_Buffer.h in Headers */,
17C8F2010CBED286008D969D /* Classic_Emu.h in Headers */, 17C8F2010CBED286008D969D /* Classic_Emu.h in Headers */,
17C8F2030CBED286008D969D /* Data_Reader.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 */, 17C8F2050CBED286008D969D /* Dual_Resampler.h in Headers */,
17C8F2070CBED286008D969D /* Effects_Buffer.h in Headers */, 17C8F2070CBED286008D969D /* Effects_Buffer.h in Headers */,
17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */, 17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */,
83489CD82783C98600BDCEA2 /* Nes_Vrc7_Apu.h in Headers */, 83489CD82783C98600BDCEA2 /* Nes_Vrc7_Apu.h in Headers */,
83489CEB2783CADC00BDCEA2 /* panning.h in Headers */,
83FC5DAE181B8B1900B917E5 /* spc700.hpp in Headers */, 83FC5DAE181B8B1900B917E5 /* spc700.hpp in Headers */,
17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */, 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */,
83FC5DAB181B8B1900B917E5 /* registers.hpp in Headers */, 83FC5DAB181B8B1900B917E5 /* registers.hpp in Headers */,
17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */, 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */,
83489CC22783015300BDCEA2 /* sap_cpu_io.h in Headers */, 83489CC22783015300BDCEA2 /* sap_cpu_io.h in Headers */,
8302AF4F2784668C0066143E /* vrc7tone.h in Headers */,
83489CBD2783015300BDCEA2 /* hes_cpu_io.h in Headers */, 83489CBD2783015300BDCEA2 /* hes_cpu_io.h in Headers */,
17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */, 17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */,
17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */, 17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */,
83489CE02783CAC100BDCEA2 /* gb_cpu_io.h in Headers */, 83489CE02783CAC100BDCEA2 /* gb_cpu_io.h in Headers */,
83489CEF2783D89300BDCEA2 /* emutypes.h in Headers */,
83489CD12783BF6D00BDCEA2 /* Nes_Mmc5_Apu.h in Headers */, 83489CD12783BF6D00BDCEA2 /* Nes_Mmc5_Apu.h in Headers */,
83489CC02783015300BDCEA2 /* Ay_Cpu.h in Headers */, 83489CC02783015300BDCEA2 /* Ay_Cpu.h in Headers */,
83FC5D9A181B675900B917E5 /* SPC_DSP.h in Headers */, 83FC5D9A181B675900B917E5 /* SPC_DSP.h in Headers */,
@ -578,9 +589,7 @@
17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */, 17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */,
17C8F22B0CBED286008D969D /* Multi_Buffer.h in Headers */, 17C8F22B0CBED286008D969D /* Multi_Buffer.h in Headers */,
17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */, 17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */,
83489CED2783D86700BDCEA2 /* mamedef.h in Headers */,
17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */, 17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */,
83489CE32783CAC100BDCEA2 /* emu2413.h in Headers */,
17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */, 17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */,
17C8F2340CBED286008D969D /* Nes_Fme7_Apu.h in Headers */, 17C8F2340CBED286008D969D /* Nes_Fme7_Apu.h in Headers */,
17C8F2360CBED286008D969D /* Nes_Namco_Apu.h in Headers */, 17C8F2360CBED286008D969D /* Nes_Namco_Apu.h in Headers */,
@ -595,7 +604,6 @@
8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */, 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */,
17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */, 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */,
83489CC12783015300BDCEA2 /* Sms_Oscs.h in Headers */, 83489CC12783015300BDCEA2 /* Sms_Oscs.h in Headers */,
83489CE12783CAC100BDCEA2 /* 2413tone.h in Headers */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@ -656,6 +664,9 @@
isa = PBXResourcesBuildPhase; isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
83BDCDD02E41A269003FC007 /* LICENSE in Resources */,
83BDCDD12E41A269003FC007 /* README.md in Resources */,
83BDCDD22E41A269003FC007 /* emu2413_NESpatches.txt in Resources */,
8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
@ -672,14 +683,14 @@
17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */, 17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */,
17C8F1FE0CBED286008D969D /* Blip_Buffer.cpp in Sources */, 17C8F1FE0CBED286008D969D /* Blip_Buffer.cpp in Sources */,
17C8F2000CBED286008D969D /* Classic_Emu.cpp in Sources */, 17C8F2000CBED286008D969D /* Classic_Emu.cpp in Sources */,
83489CEA2783CADC00BDCEA2 /* panning.c in Sources */,
8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */, 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */,
17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */, 17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */,
83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */, 83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */,
17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */, 17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */,
17C8F2060CBED286008D969D /* Effects_Buffer.cpp in Sources */, 17C8F2060CBED286008D969D /* Effects_Buffer.cpp in Sources */,
17C8F2080CBED286008D969D /* Fir_Resampler.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 */, 17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */,
17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */, 17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */,
17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */, 17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */,
@ -756,6 +767,7 @@
HAVE_STDINT_H, HAVE_STDINT_H,
"DEBUG=1", "DEBUG=1",
"VGM_YM2612_NUKED=1", "VGM_YM2612_NUKED=1",
"BLARGG_LITTLE_ENDIAN=1",
); );
INFOPLIST_FILE = Info.plist; INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@loader_path/../Frameworks"; INSTALL_PATH = "@loader_path/../Frameworks";
@ -789,6 +801,7 @@
HAVE_STDINT_H, HAVE_STDINT_H,
NDEBUG, NDEBUG,
"VGM_YM2612_NUKED=1", "VGM_YM2612_NUKED=1",
"BLARGG_LITTLE_ENDIAN=1",
); );
INFOPLIST_FILE = Info.plist; INFOPLIST_FILE = Info.plist;
INSTALL_PATH = "@loader_path/../Frameworks"; 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. // Tones above this frequency are treated as disabled tone at half volume.
// Power of two is more efficient (avoids division). // 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] = 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.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500),
ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000), ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000),
ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000), ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000),
/* /*
// Measured from an AY-3-8910A chip with date code 8611. // Measured from an AY-3-8910A chip with date code 8611.
// Direct voltages without any load (very linear) // Direct voltages without any load (very linear)
ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785), ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785),
ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032), ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032),
@ -88,7 +88,7 @@ Ay_Apu::Ay_Apu()
flags >>= 2; flags >>= 2;
} }
} }
output( 0 ); output( 0 );
volume( 1.0 ); volume( 1.0 );
reset(); reset();
@ -99,7 +99,7 @@ void Ay_Apu::reset()
last_time = 0; last_time = 0;
noise.delay = 0; noise.delay = 0;
noise.lfsr = 1; noise.lfsr = 1;
osc_t* osc = &oscs [osc_count]; osc_t* osc = &oscs [osc_count];
do do
{ {
@ -110,7 +110,7 @@ void Ay_Apu::reset()
osc->phase = 0; osc->phase = 0;
} }
while ( osc != oscs ); while ( osc != oscs );
for ( int i = sizeof regs; --i >= 0; ) for ( int i = sizeof regs; --i >= 0; )
regs [i] = 0; regs [i] = 0;
regs [7] = 0xFF; regs [7] = 0xFF;
@ -120,14 +120,14 @@ void Ay_Apu::reset()
void Ay_Apu::write_data_( int addr, int data ) void Ay_Apu::write_data_( int addr, int data )
{ {
assert( (unsigned) addr < reg_count ); assert( (unsigned) addr < reg_count );
if ( (unsigned) addr >= 14 ) if ( (unsigned) addr >= 14 )
{ {
#ifdef debug_printf #ifdef debug_printf
debug_printf( "Wrote to I/O port %02X\n", (int) addr ); debug_printf( "Wrote to I/O port %02X\n", (int) addr );
#endif #endif
} }
// envelope mode // envelope mode
if ( addr == 13 ) 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() env.delay = 0; // will get set to envelope period in run_until()
} }
regs [addr] = data; regs [addr] = data;
// handle period changes accurately // handle period changes accurately
int i = addr >> 1; int i = addr >> 1;
if ( i < osc_count ) if ( i < osc_count )
@ -147,32 +147,32 @@ void Ay_Apu::write_data_( int addr, int data )
regs [i * 2] * period_factor; regs [i * 2] * period_factor;
if ( !period ) if ( !period )
period = period_factor; period = period_factor;
// adjust time of next timer expiration based on change in period // adjust time of next timer expiration based on change in period
osc_t& osc = oscs [i]; osc_t& osc = oscs [i];
if ( (osc.delay += period - osc.period) < 0 ) if ( (osc.delay += period - osc.period) < 0 )
osc.delay = 0; osc.delay = 0;
osc.period = period; osc.period = period;
} }
// TODO: same as above for envelope timer, and it also has a divide by two after it // TODO: same as above for envelope timer, and it also has a divide by two after it
} }
int const noise_off = 0x08; static int const noise_off = 0x08;
int const tone_off = 0x01; static int const tone_off = 0x01;
void Ay_Apu::run_until( blip_time_t final_end_time ) void Ay_Apu::run_until( blip_time_t final_end_time )
{ {
require( final_end_time >= last_time ); require( final_end_time >= last_time );
// noise period and initial values // noise period and initial values
blip_time_t const noise_period_factor = period_factor * 2; // verified blip_time_t const noise_period_factor = period_factor * 2; // verified
blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor; blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor;
if ( !noise_period ) if ( !noise_period )
noise_period = noise_period_factor; noise_period = noise_period_factor;
blip_time_t const old_noise_delay = noise.delay; 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 // envelope period
blip_time_t const env_period_factor = period_factor * 2; // verified blip_time_t const env_period_factor = period_factor * 2; // verified
blip_time_t env_period = (regs [12] * 0x100L + regs [11]) * env_period_factor; 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 env_period = env_period_factor; // same as period 1 on my AY chip
if ( !env.delay ) if ( !env.delay )
env.delay = env_period; env.delay = env_period;
// run each osc separately // run each osc separately
for ( int index = 0; index < osc_count; index++ ) for ( int index = 0; index < osc_count; index++ )
{ {
osc_t* const osc = &oscs [index]; osc_t* const osc = &oscs [index];
int osc_mode = regs [7] >> index; int osc_mode = regs [7] >> index;
// output // output
Blip_Buffer* const osc_output = osc->output; Blip_Buffer* const osc_output = osc->output;
if ( !osc_output ) if ( !osc_output )
continue; continue;
osc_output->set_modified(); osc_output->set_modified();
// period // period
int half_vol = 0; 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); inaudible_freq) / (inaudible_freq * 2);
if ( osc->period <= inaudible_period && !(osc_mode & tone_off) ) if ( osc->period <= inaudible_period && !(osc_mode & tone_off) )
{ {
half_vol = 1; // Actually around 60%, but 50% is close enough half_vol = 1; // Actually around 60%, but 50% is close enough
osc_mode |= tone_off; osc_mode |= tone_off;
} }
// envelope // envelope
blip_time_t start_time = last_time; blip_time_t start_time = last_time;
blip_time_t end_time = final_end_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; end_time = start_time + env.delay;
if ( end_time >= final_end_time ) if ( end_time >= final_end_time )
end_time = final_end_time; end_time = final_end_time;
//if ( !(regs [12] | regs [11]) ) //if ( !(regs [12] | regs [11]) )
// debug_printf( "Used envelope period 0\n" ); // 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; osc_mode = noise_off | tone_off;
} }
// tone time // tone time
blip_time_t const period = osc->period; blip_time_t const period = osc->period;
blip_time_t time = start_time + osc->delay; blip_time_t time = start_time + osc->delay;
if ( osc_mode & tone_off ) // maintain tone's phase when off 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; time += count * period;
osc->phase ^= count & 1; osc->phase ^= count & 1;
} }
// noise time // noise time
blip_time_t ntime = final_end_time; blip_time_t ntime = final_end_time;
blargg_ulong noise_lfsr = 1; uint32_t noise_lfsr = 1;
if ( !(osc_mode & noise_off) ) if ( !(osc_mode & noise_off) )
{ {
ntime = start_time + old_noise_delay; 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 ) //if ( (regs [6] & 0x1F) == 0 )
// debug_printf( "Used noise period 0\n" ); // debug_printf( "Used noise period 0\n" );
} }
// The following efficiently handles several cases (least demanding first): // The following efficiently handles several cases (least demanding first):
// * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC // * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC
// * Just tone or just noise, envelope disabled // * 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 disabled, envelope enabled with high frequency
// * Tone and noise together // * Tone and noise together
// * Tone and noise together with envelope // * Tone and noise together with envelope
// This loop only runs one iteration if envelope is disabled. If 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 // 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. // 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 ); synth_.offset( start_time, delta, osc_output );
} }
} }
// Run wave and noise interleved with each catching up to the other. // 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, // If one or both are disabled, their "current time" will be past end time,
// so there will be no significant performance hit. // 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 while ( ntime <= end ) // must advance *past* time to avoid hang
{ {
int changed = noise_lfsr + 1; 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 ) if ( changed & 2 )
{ {
delta = -delta; delta = -delta;
@ -311,12 +311,12 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
else else
{ {
// 20 or more noise periods on average for some music // 20 or more noise periods on average for some music
blargg_long remain = end - ntime; int32_t remain = end - ntime;
blargg_long count = remain / noise_period; int32_t count = remain / noise_period;
if ( remain >= 0 ) if ( remain >= 0 )
ntime += noise_period + count * noise_period; ntime += noise_period + count * noise_period;
} }
// run tone // run tone
end = end_time; end = end_time;
if ( end_time > ntime ) end = ntime; 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 // loop usually runs less than once
//SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period ); //SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period );
while ( time < end ) while ( time < end )
{ {
time += period; time += period;
@ -346,41 +346,41 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
} }
} }
while ( time < end_time || ntime < end_time ); while ( time < end_time || ntime < end_time );
osc->last_amp = (delta + volume) >> 1; osc->last_amp = (delta + volume) >> 1;
if ( !(osc_mode & tone_off) ) if ( !(osc_mode & tone_off) )
osc->phase = phase; osc->phase = phase;
} }
if ( end_time >= final_end_time ) if ( end_time >= final_end_time )
break; // breaks first time when envelope is disabled break; // breaks first time when envelope is disabled
// next envelope step // next envelope step
if ( ++osc_env_pos >= 0 ) if ( ++osc_env_pos >= 0 )
osc_env_pos -= 32; osc_env_pos -= 32;
volume = env.wave [osc_env_pos] >> half_vol; volume = env.wave [osc_env_pos] >> half_vol;
start_time = end_time; start_time = end_time;
end_time += env_period; end_time += env_period;
if ( end_time > final_end_time ) if ( end_time > final_end_time )
end_time = final_end_time; end_time = final_end_time;
} }
osc->delay = time - final_end_time; osc->delay = time - final_end_time;
if ( !(osc_mode & noise_off) ) if ( !(osc_mode & noise_off) )
{ {
noise.delay = ntime - final_end_time; noise.delay = ntime - final_end_time;
noise.lfsr = noise_lfsr; noise.lfsr = noise_lfsr;
} }
} }
// TODO: optimized saw wave envelope? // TODO: optimized saw wave envelope?
// maintain envelope phase // maintain envelope phase
blip_time_t remain = final_end_time - last_time - env.delay; blip_time_t remain = final_end_time - last_time - env.delay;
if ( remain >= 0 ) if ( remain >= 0 )
{ {
blargg_long count = (remain + env_period) / env_period; int32_t count = (remain + env_period) / env_period;
env.pos += count; env.pos += count;
if ( env.pos >= 0 ) if ( env.pos >= 0 )
env.pos = (env.pos & 31) - 32; env.pos = (env.pos & 31) - 32;
@ -390,6 +390,6 @@ void Ay_Apu::run_until( blip_time_t final_end_time )
env.delay = -remain; env.delay = -remain;
assert( env.delay > 0 ); assert( env.delay > 0 );
assert( env.pos < 0 ); assert( env.pos < 0 );
last_time = final_end_time; last_time = final_end_time;
} }

View file

@ -11,32 +11,32 @@ class Ay_Apu {
public: public:
// Set buffer to generate all sound into, or disable sound if NULL // Set buffer to generate all sound into, or disable sound if NULL
void output( Blip_Buffer* ); void output( Blip_Buffer* );
// Reset sound chip // Reset sound chip
void reset(); void reset();
// Write to register at specified time // 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 ); void write( blip_time_t time, int addr, int data );
// Run sound to specified time, end current time frame, then start a new // 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 // time frame at time 0. Time frames have no effect on emulation and each
// can be whatever length is convenient. // can be whatever length is convenient.
void end_frame( blip_time_t length ); void end_frame( blip_time_t length );
// Additional features // Additional features
// Set sound output of specific oscillator to buffer, where index is // Set sound output of specific oscillator to buffer, where index is
// 0, 1, or 2. If buffer is NULL, the specified oscillator is muted. // 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* ); void osc_output( int index, Blip_Buffer* );
// Set overall volume (default is 1.0) // Set overall volume (default is 1.0)
void volume( double ); void volume( double );
// Set treble equalization (see documentation) // Set treble equalization (see documentation)
void treble_eq( blip_eq_t const& ); void treble_eq( blip_eq_t const& );
public: public:
Ay_Apu(); Ay_Apu();
typedef unsigned char byte; typedef unsigned char byte;
@ -51,23 +51,23 @@ private:
} oscs [osc_count]; } oscs [osc_count];
blip_time_t last_time; blip_time_t last_time;
byte regs [reg_count]; byte regs [reg_count];
struct { struct {
blip_time_t delay; blip_time_t delay;
blargg_ulong lfsr; uint32_t lfsr;
} noise; } noise;
struct { struct {
blip_time_t delay; blip_time_t delay;
byte const* wave; byte const* wave;
int pos; int pos;
byte modes [8] [48]; // values already passed through volume table byte modes [8] [48]; // values already passed through volume table
} env; } env;
void run_until( blip_time_t ); void run_until( blip_time_t );
void write_data_( int addr, int data ); void write_data_( int addr, int data );
public: public:
enum { amp_range = 255 }; static const int amp_range = 255;
Blip_Synth<blip_good_quality,1> synth_; 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 ) if ( time > last_time )
run_until( time ); run_until( time );
assert( last_time >= time ); assert( last_time >= time );
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" #include "blargg_endian.h"
typedef blargg_long cpu_time_t; typedef int32_t cpu_time_t;
// must be defined by caller // must be defined by caller
void ay_cpu_out( class Ay_Cpu*, cpu_time_t, unsigned addr, int data ); void ay_cpu_out( class Ay_Cpu*, cpu_time_t, unsigned addr, int data );
@ -16,27 +16,27 @@ class Ay_Cpu {
public: public:
// Clear all registers and keep pointer to 64K memory passed in // Clear all registers and keep pointer to 64K memory passed in
void reset( void* mem_64k ); void reset( void* mem_64k );
// Run until specified time is reached. Returns true if suspicious/unsupported // Run until specified time is reached. Returns true if suspicious/unsupported
// instruction was encountered at any point during run. // instruction was encountered at any point during run.
bool run( cpu_time_t end_time ); bool run( cpu_time_t end_time );
// Time of beginning of next instruction // Time of beginning of next instruction
cpu_time_t time() const { return state->time + state->base; } cpu_time_t time() const { return state->time + state->base; }
// Alter current time. Not supported during run() call. // Alter current time. Not supported during run() call.
void set_time( cpu_time_t t ) { state->time = t - state->base; } void set_time( cpu_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; } void adjust_time( int delta ) { state->time += delta; }
#if BLARGG_BIG_ENDIAN #if BLARGG_BIG_ENDIAN
struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; struct regs_t { uint8_t b, c, d, e, h, l, flags, a; };
#else #else
struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; struct regs_t { uint8_t c, b, e, d, l, h, a, flags; };
#endif #endif
static_assert( sizeof (regs_t) == 8, "Invalid register size, padding issue?" ); static_assert( sizeof (regs_t) == 8, "Invalid register size, padding issue?" );
struct pairs_t { uint16_t bc, de, hl, fa; }; struct pairs_t { uint16_t bc, de, hl, fa; };
// Registers are not updated until run() returns // Registers are not updated until run() returns
struct registers_t { struct registers_t {
uint16_t pc; uint16_t pc;
@ -58,10 +58,10 @@ public:
uint8_t im; uint8_t im;
}; };
//registers_t r; (below for efficiency) //registers_t r; (below for efficiency)
// can read this far past end of memory // can read this far past end of memory
enum { cpu_padding = 0x100 }; enum { cpu_padding = 0x100 };
public: public:
Ay_Cpu(); Ay_Cpu();
private: private:

View file

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

View file

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

View file

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

View file

@ -1,6 +1,7 @@
// Band-limited sound synthesis buffer // Band-limited sound synthesis buffer
// Blip_Buffer 0.4.1 // Blip_Buffer 0.4.1
#ifndef BLIP_BUFFER_H #ifndef BLIP_BUFFER_H
#define BLIP_BUFFER_H #define BLIP_BUFFER_H
@ -9,7 +10,7 @@
#if INT_MAX < 0x7FFFFFFF #if INT_MAX < 0x7FFFFFFF
#error "int must be at least 32 bits" #error "int must be at least 32 bits"
#endif #endif
typedef int blip_long; typedef int blip_long;
typedef unsigned blip_ulong; typedef unsigned blip_ulong;
@ -23,66 +24,66 @@ enum { blip_sample_max = 32767 };
class Blip_Buffer { class Blip_Buffer {
public: public:
typedef const char* blargg_err_t; typedef const char* blargg_err_t;
// Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults // 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 // 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. // 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 ); blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 );
// Set number of source time units per second // Set number of source time units per second
void clock_rate( long ); void clock_rate( long );
// End current time frame of specified duration and make its samples available // End current time frame of specified duration and make its samples available
// (along with any still-unread samples) for reading with read_samples(). Begins // (along with any still-unread samples) for reading with read_samples(). Begins
// a new time frame at the end of the current frame. // a new time frame at the end of the current frame.
void end_frame( blip_time_t time ); void end_frame( blip_time_t time );
// Read at most 'max_samples' out of buffer into 'dest', removing them from from // 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 // 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 // true, increments 'dest' one extra time after writing each sample, to allow
// easy interleving of two channels into a stereo output buffer. // easy interleving of two channels into a stereo output buffer.
long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 );
// Additional optional features // Additional optional features
// Current output sample rate // Current output sample rate
long sample_rate() const; long sample_rate() const;
// Length of buffer, in milliseconds // Length of buffer, in milliseconds
int length() const; int length() const;
// Number of source time units per second // Number of source time units per second
long clock_rate() const; long clock_rate() const;
// Set frequency high-pass filter frequency, where higher values reduce bass more // Set frequency high-pass filter frequency, where higher values reduce bass more
void bass_freq( int frequency ); void bass_freq( int frequency );
// Number of samples delay from synthesis to samples read out // Number of samples delay from synthesis to samples read out
int output_latency() const; int output_latency() const;
// Remove all available samples and clear buffer to silence. If 'entire_buffer' is // 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. // false, just clears out any samples waiting rather than the entire buffer.
void clear( int entire_buffer = 1 ); void clear( int entire_buffer = 1 );
// Number of samples available for reading with read_samples() // Number of samples available for reading with read_samples()
long samples_avail() const; long samples_avail() const;
// Remove 'count' samples from those waiting to be read // Remove 'count' samples from those waiting to be read
void remove_samples( long count ); void remove_samples( long count );
// Experimental features // Experimental features
// Count number of clocks needed until 'count' samples will be available. // Count number of clocks needed until 'count' samples will be available.
// If buffer can't even hold 'count' samples, returns number of clocks until // If buffer can't even hold 'count' samples, returns number of clocks until
// buffer becomes full. // buffer becomes full.
blip_time_t count_clocks( long count ) const; blip_time_t count_clocks( long count ) const;
// Number of raw samples that can be mixed within frame of specified duration. // Number of raw samples that can be mixed within frame of specified duration.
long count_samples( blip_time_t duration ) const; long count_samples( blip_time_t duration ) const;
// Mix 'count' samples from 'buf' into buffer. // Mix 'count' samples from 'buf' into buffer.
void mix_samples( blip_sample_t const* buf, long count ); void mix_samples( blip_sample_t const* buf, long count );
// not documented yet // not documented yet
void set_modified() { modified_ = 1; } void set_modified() { modified_ = 1; }
int clear_modified() { int b = modified_; modified_ = 0; return b; } int clear_modified() { int b = modified_; modified_ = 0; return b; }
@ -94,7 +95,7 @@ public:
public: public:
Blip_Buffer(); Blip_Buffer();
~Blip_Buffer(); ~Blip_Buffer();
// Deprecated // Deprecated
typedef blip_resampled_time_t resampled_time_t; typedef blip_resampled_time_t resampled_time_t;
blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); }
@ -120,9 +121,7 @@ private:
friend class Blip_Reader; friend class Blip_Reader;
}; };
#ifdef HAVE_CONFIG_H #include "blargg_config.h"
#include "config.h"
#endif
// Number of bits in resample ratio fraction. Higher values give a more accurate ratio // Number of bits in resample ratio fraction. Higher values give a more accurate ratio
// but reduce maximum buffer size. // but reduce maximum buffer size.
@ -147,24 +146,24 @@ private:
int const blip_buffer_extra_ = blip_widest_impulse_ + 2; int const blip_buffer_extra_ = blip_widest_impulse_ + 2;
int const blip_res = 1 << BLIP_PHASE_BITS; int const blip_res = 1 << BLIP_PHASE_BITS;
class blip_eq_t; class blip_eq_t;
class Blip_Synth_Fast_ { class Blip_Synth_Fast_ {
public: public:
Blip_Buffer* buf; Blip_Buffer* buf;
int last_amp; int last_amp;
int delta_factor; int delta_factor;
void volume_unit( double ); void volume_unit( double );
Blip_Synth_Fast_(); Blip_Synth_Fast_();
void treble_eq( blip_eq_t const& ) { } void treble_eq( blip_eq_t const& ) { }
}; };
class Blip_Synth_ { class Blip_Synth_ {
public: public:
Blip_Buffer* buf; Blip_Buffer* buf;
int last_amp; int last_amp;
int delta_factor; int delta_factor;
void volume_unit( double ); void volume_unit( double );
Blip_Synth_( short* impulses, int width ); Blip_Synth_( short* impulses, int width );
void treble_eq( blip_eq_t const& ); void treble_eq( blip_eq_t const& );
@ -190,14 +189,14 @@ class Blip_Synth {
public: public:
// Set overall volume of waveform // Set overall volume of waveform
void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); }
// Configure low-pass filter (see blip_buffer.txt) // Configure low-pass filter (see blip_buffer.txt)
void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); }
// Get/set Blip_Buffer used for output // Get/set Blip_Buffer used for output
Blip_Buffer* output() const { return impl.buf; } Blip_Buffer* output() const { return impl.buf; }
void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; }
// Update amplitude of waveform at given time. Using this requires a separate // Update amplitude of waveform at given time. Using this requires a separate
// Blip_Synth for each waveform. // Blip_Synth for each waveform.
void update( blip_time_t time, int amplitude ); void update( blip_time_t time, int amplitude );
@ -209,10 +208,10 @@ public:
// The actual change in amplitude is delta * (volume / range) // The actual change in amplitude is delta * (volume / range)
void offset( blip_time_t, int delta, Blip_Buffer* ) const; void offset( blip_time_t, int delta, Blip_Buffer* ) const;
void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } 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. // 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; void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const;
// Same as offset(), except code is inlined for higher performance // Same as offset(), except code is inlined for higher performance
void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const {
offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); offset_resampled( t * buf->factor_ + buf->offset_, delta, buf );
@ -220,7 +219,7 @@ public:
void offset_inline( blip_time_t t, int delta ) const { void offset_inline( blip_time_t t, int delta ) const {
offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf );
} }
private: private:
#if BLIP_BUFFER_FAST #if BLIP_BUFFER_FAST
Blip_Synth_Fast_ impl; Blip_Synth_Fast_ impl;
@ -233,9 +232,9 @@ public:
#endif #endif
// disable broken defaulted constructors, Blip_Synth_ isn't safe to move/copy // disable broken defaulted constructors, Blip_Synth_ isn't safe to move/copy
Blip_Synth<quality, range> (const Blip_Synth<quality, range> &) = delete; Blip_Synth (const Blip_Synth &) = delete;
Blip_Synth<quality, range> ( Blip_Synth<quality, range> &&) = delete; Blip_Synth ( Blip_Synth &&) = delete;
Blip_Synth<quality, range>& operator=(const Blip_Synth<quality, range> &) = delete; Blip_Synth& operator=(const Blip_Synth &) = delete;
}; };
// Low-pass equalization parameters // Low-pass equalization parameters
@ -244,10 +243,10 @@ public:
// Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce
// treble, small positive values (0 to 5.0) increase treble. // treble, small positive values (0 to 5.0) increase treble.
blip_eq_t( double treble_db = 0 ); blip_eq_t( double treble_db = 0 );
// See blip_buffer.txt // See blip_buffer.txt
blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 );
private: private:
double treble; double treble;
long rolloff_freq; long rolloff_freq;
@ -268,15 +267,16 @@ public:
blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); blargg_err_t set_sample_rate( long samples_per_sec, int msec_length );
blip_time_t count_clocks( long count ) const; blip_time_t count_clocks( long count ) const;
void mix_samples( blip_sample_t const* buf, long count ); void mix_samples( blip_sample_t const* buf, long count );
Silent_Blip_Buffer(); Silent_Blip_Buffer();
}; };
#if defined (__GNUC__) || _MSC_VER >= 1100 #if (defined(__GNUC__) && (__GNUC__ >= 3)) || \
#define BLIP_RESTRICT __restrict (defined(_MSC_VER) && (_MSC_VER >= 1100))
#else #define BLIP_RESTRICT __restrict
#define BLIP_RESTRICT #else
#endif #define BLIP_RESTRICT
#endif
// Optimized reading from Blip_Buffer, for use in custom sample output // Optimized reading from Blip_Buffer, for use in custom sample output
@ -328,7 +328,7 @@ public:
blip_long read_raw() const { return accum; } blip_long read_raw() const { return accum; }
void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); }
void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } void end( Blip_Buffer& b ) { b.reader_accum_ = accum; }
private: private:
const Blip_Buffer::buf_t_* buf; const Blip_Buffer::buf_t_* buf;
blip_long accum; blip_long accum;
@ -351,14 +351,14 @@ inline void Blip_Synth<quality,range>::offset_resampled( blip_resampled_time_t t
#if BLIP_BUFFER_FAST #if BLIP_BUFFER_FAST
blip_long left = buf [0] + delta; blip_long left = buf [0] + delta;
// Kind of crappy, but doing shift after multiply results in overflow. // Kind of crappy, but doing shift after multiply results in overflow.
// Alternate way of delaying multiply by delta_factor results in worse // Alternate way of delaying multiply by delta_factor results in worse
// sub-sample resolution. // sub-sample resolution.
blip_long right = (delta >> BLIP_PHASE_BITS) * phase; blip_long right = (delta >> BLIP_PHASE_BITS) * phase;
left -= right; left -= right;
right += buf [1]; right += buf [1];
buf [0] = left; buf [0] = left;
buf [1] = right; buf [1] = right;
#else #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 fwd = (blip_widest_impulse_ - quality) / 2;
int const rev = fwd + quality - 2; int const rev = fwd + quality - 2;
int const mid = quality / 2 - 1; int const mid = quality / 2 - 1;
imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase;
#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \
defined (__x86_64__) || defined (__ia64__) || defined (__i386__) defined (__x86_64__) || defined (__ia64__) || defined (__i386__)
// straight forward implementation resulted in better code on GCC for x86 // straight forward implementation resulted in better code on GCC for x86
#define ADD_IMP( out, in ) \ #define ADD_IMP( out, in ) \
buf [out] += (blip_long) imp [blip_res * (in)] * delta buf [out] += (blip_long) imp [blip_res * (in)] * delta
#define BLIP_FWD( i ) {\ #define BLIP_FWD( i ) {\
ADD_IMP( fwd + i, i );\ ADD_IMP( fwd + i, i );\
ADD_IMP( fwd + 1 + i, i + 1 );\ 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 > 12 ) BLIP_REV( 6 )
if ( quality > 8 ) BLIP_REV( 4 ) if ( quality > 8 ) BLIP_REV( 4 )
BLIP_REV( 2 ) BLIP_REV( 2 )
ADD_IMP( rev , 1 ); ADD_IMP( rev , 1 );
ADD_IMP( rev + 1, 0 ); ADD_IMP( rev + 1, 0 );
#else #else
// for RISC processors, help compiler by reading ahead of writes // for RISC processors, help compiler by reading ahead of writes
#define BLIP_FWD( i ) {\ #define BLIP_FWD( i ) {\
blip_long t0 = i0 * delta + buf [fwd + i];\ blip_long t0 = i0 * delta + buf [fwd + i];\
blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + 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 - r] = t0;\
buf [rev + 1 - r] = t1;\ buf [rev + 1 - r] = t1;\
} }
blip_long i0 = *imp; blip_long i0 = *imp;
BLIP_FWD( 0 ) BLIP_FWD( 0 )
if ( quality > 8 ) BLIP_FWD( 2 ) 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 > 12 ) BLIP_REV( 6 )
if ( quality > 8 ) BLIP_REV( 4 ) if ( quality > 8 ) BLIP_REV( 4 )
BLIP_REV( 2 ) BLIP_REV( 2 )
blip_long t0 = i0 * delta + buf [rev ]; blip_long t0 = i0 * delta + buf [rev ];
blip_long t1 = *imp * delta + buf [rev + 1]; blip_long t1 = *imp * delta + buf [rev + 1];
buf [rev ] = t0; buf [rev ] = t0;
buf [rev + 1] = t1; buf [rev + 1] = t1;
#endif #endif
#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 ) { } treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { }
inline int Blip_Buffer::length() const { return length_; } 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 long Blip_Buffer::sample_rate() const { return sample_rate_; }
inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; }
inline long Blip_Buffer::clock_rate() const { return clock_rate_; } 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 # List of source files required by libgme and any emulators
# This is not 100% accurate (Fir_Resampler for instance) but # This is not 100% accurate (Fir_Resampler for instance) but
# you'll be OK. # you'll be OK.
set(libgme_SRCS Blip_Buffer.cpp set(libgme_SRCS Blip_Buffer.cpp
Blip_Buffer.h
Classic_Emu.cpp Classic_Emu.cpp
Classic_Emu.h
Data_Reader.cpp Data_Reader.cpp
Data_Reader.h
Dual_Resampler.cpp Dual_Resampler.cpp
Dual_Resampler.h
Effects_Buffer.cpp Effects_Buffer.cpp
Effects_Buffer.h
Fir_Resampler.cpp Fir_Resampler.cpp
Fir_Resampler.h
gme.cpp gme.cpp
gme.h
gme_types.h
Gme_File.cpp Gme_File.cpp
Gme_File.h
M3u_Playlist.cpp M3u_Playlist.cpp
M3u_Playlist.h
Multi_Buffer.cpp Multi_Buffer.cpp
Multi_Buffer.h
Music_Emu.cpp 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 # Ay_Apu is very popular around here
if (USE_GME_AY OR USE_GME_KSS) if(USE_GME_AY OR USE_GME_KSS)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Ay_Apu.cpp Ay_Apu.cpp
Ay_Apu.h
) )
endif() endif()
# so is Ym2612_Emu # 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") if(GME_YM2612_EMU STREQUAL "Nuked")
add_definitions(-DVGM_YM2612_NUKED) add_definitions(-DVGM_YM2612_NUKED)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Ym2612_Nuked.cpp 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") elseif(GME_YM2612_EMU STREQUAL "MAME")
add_definitions(-DVGM_YM2612_MAME) add_definitions(-DVGM_YM2612_MAME)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Ym2612_MAME.cpp 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() else()
add_definitions(-DVGM_YM2612_GENS) add_definitions(-DVGM_YM2612_GENS)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Ym2612_GENS.cpp 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()
endif() endif()
# But none are as popular as Sms_Apu # But none are as popular as Sms_Apu
if (USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS) if(USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Sms_Apu.cpp Sms_Apu.cpp
Sms_Apu.h
) )
endif() endif()
if (USE_GME_AY) if(USE_GME_AY)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
# Ay_Apu.cpp included earlier # Ay_Apu.cpp included earlier
Ay_Cpu.cpp Ay_Cpu.cpp
Ay_Cpu.h
Ay_Emu.cpp Ay_Emu.cpp
Ay_Emu.h
) )
endif() endif()
if (USE_GME_GBS) if(USE_GME_GBS)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Gb_Apu.cpp Gb_Apu.cpp
Gb_Apu.h
Gb_Cpu.cpp Gb_Cpu.cpp
Gb_Cpu.h
Gb_Oscs.cpp Gb_Oscs.cpp
Gb_Oscs.h
Gbs_Emu.cpp Gbs_Emu.cpp
Gbs_Emu.h
gb_cpu_io.h
) )
endif() endif()
if (USE_GME_GYM) if(USE_GME_GYM)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
# Sms_Apu.cpp included earlier # Sms_Apu.cpp included earlier
# Ym2612_Emu.cpp included earlier # Ym2612_Emu.cpp included earlier
Gym_Emu.cpp Gym_Emu.cpp
Gym_Emu.h
) )
endif() endif()
if (USE_GME_HES) if(USE_GME_HES)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Hes_Apu.cpp Hes_Apu.cpp
Hes_Apu.h
Hes_Cpu.cpp Hes_Cpu.cpp
Hes_Cpu.h
Hes_Emu.cpp Hes_Emu.cpp
Hes_Emu.h
hes_cpu_io.h
) )
endif() endif()
if (USE_GME_KSS) if(USE_GME_KSS)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
# Ay_Apu.cpp included earlier # Ay_Apu.cpp included earlier
# Sms_Apu.cpp included earlier # Sms_Apu.cpp included earlier
Kss_Cpu.cpp Kss_Cpu.cpp
Kss_Cpu.h
Kss_Emu.cpp Kss_Emu.cpp
Kss_Emu.h
Kss_Scc_Apu.cpp Kss_Scc_Apu.cpp
Kss_Scc_Apu.h
) )
endif() endif()
if (USE_GME_NSF OR USE_GME_NSFE) if(USE_GME_NSF OR USE_GME_NSFE)
set(libgme_SRCS ${libgme_SRCS} list(APPEND libgme_SRCS
Nes_Apu.cpp Nes_Apu.cpp
Nes_Apu.h
Nes_Cpu.cpp Nes_Cpu.cpp
Nes_Cpu.h
Nes_Fme7_Apu.cpp Nes_Fme7_Apu.cpp
Nes_Fme7_Apu.h
Nes_Namco_Apu.cpp Nes_Namco_Apu.cpp
Nes_Namco_Apu.h
Nes_Oscs.cpp Nes_Oscs.cpp
Nes_Oscs.h
Nes_Vrc6_Apu.cpp Nes_Vrc6_Apu.cpp
Nes_Vrc6_Apu.h
Nes_Fds_Apu.cpp Nes_Fds_Apu.cpp
Nes_Fds_Apu.h
Nes_Vrc7_Apu.cpp Nes_Vrc7_Apu.cpp
../ext/emu2413.c Nes_Vrc7_Apu.h
../ext/panning.c 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.cpp
Nsf_Emu.h
) )
endif() endif()
if (USE_GME_NSFE) if (USE_GME_NSFE)
set(libgme_SRCS ${libgme_SRCS} set(libgme_SRCS ${libgme_SRCS}
Nsfe_Emu.cpp Nsfe_Emu.cpp
Nsfe_Emu.h
) )
endif() endif()
@ -123,6 +181,7 @@ if (USE_GME_SAP)
Sap_Apu.cpp Sap_Apu.cpp
Sap_Cpu.cpp Sap_Cpu.cpp
Sap_Emu.cpp Sap_Emu.cpp
sap_cpu_io.h
) )
endif() endif()
@ -135,12 +194,19 @@ if (USE_GME_SPC)
../higan/dsp/dsp.cpp ../higan/dsp/dsp.cpp
../higan/dsp/SPC_DSP.cpp ../higan/dsp/SPC_DSP.cpp
Snes_Spc.cpp Snes_Spc.cpp
Snes_Spc.h
Spc_Cpu.cpp Spc_Cpu.cpp
Spc_Cpu.h
Spc_Dsp.cpp Spc_Dsp.cpp
Spc_Dsp.h
Spc_Emu.cpp Spc_Emu.cpp
Spc_Emu.h
Spc_Filter.cpp Spc_Filter.cpp
Spc_Filter.h
Bml_Parser.cpp Bml_Parser.cpp
Bml_Parser.h
Spc_Sfm.cpp Spc_Sfm.cpp
Spc_Sfm.h
) )
if (GME_SPC_ISOLATED_ECHO_BUFFER) if (GME_SPC_ISOLATED_ECHO_BUFFER)
add_definitions(-DSPC_ISOLATED_ECHO_BUFFER) add_definitions(-DSPC_ISOLATED_ECHO_BUFFER)
@ -152,88 +218,265 @@ if (USE_GME_VGM)
# Sms_Apu.cpp included earlier # Sms_Apu.cpp included earlier
# Ym2612_Emu.cpp included earlier # Ym2612_Emu.cpp included earlier
Vgm_Emu.cpp Vgm_Emu.cpp
Vgm_Emu.h
Vgm_Emu_Impl.cpp Vgm_Emu_Impl.cpp
Vgm_Emu_Impl.h
Ym2413_Emu.cpp Ym2413_Emu.cpp
Ym2413_Emu.h
) )
endif() endif()
# These headers are part of the generic gme interface. # 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 # while building a macOS framework, exported headers must be in the source
# list, or the header files aren't copied to the bundle. # 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}) set(libgme_SRCS ${libgme_SRCS} ${EXPORTED_HEADERS})
endif() endif()
# On some platforms we may need to change headers or whatnot based on whether # Extract symbols from gme.exports
# we're building the library or merely using the library. The following is file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/gme.exports" gme_exports_strings)
# only defined when building the library to allow us to tell which is which. set(gme_exports)
add_definitions(-DBLARGG_BUILD_DLL) 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_deps INTERFACE)
add_library(gme ${libgme_SRCS})
if(ZLIB_FOUND) ## FIXME: Properly find the C++ library !!!
message(" ** ZLib library located, compressed file formats will be supported") set(PC_LIBS -lstdc++)
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
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() else()
message("ZLib library not found, disabling support for compressed formats such as VGZ") message(STATUS "Zlib-Compressed formats excluded")
endif() endif()
if(USE_GME_SPC) if(NOT MSVC)
if(UNRAR_FOUND) # Link with -no-undefined, if available
message(" ** unRAR library located, the RSN file format will be supported") if(NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*")
target_compile_definitions(gme PRIVATE -DRARDLL) cmake_push_check_state()
target_include_directories(gme PRIVATE ${UNRAR_INCLUDE_DIRS}) set(CMAKE_REQUIRED_FLAGS "-Wl,-no-undefined")
target_link_libraries(gme ${UNRAR_LIBRARIES}) check_cxx_source_compiles("int main(void) { return 0;}" LINKER_SUPPORTS_NO_UNDEFINED)
# Is not to be installed though cmake_pop_check_state()
endif()
set(PKG_CONFIG_UNRAR -lunrar) # evaluated in libgme.pc.in # Link to libm, if necessary
else() check_cxx_source_compiles("
message("unRAR library not found, disabling support for the RSN file format") #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()
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 # Add a version script to hide the c++ STL
if(BUILD_FRAMEWORK) if(APPLE)
set_target_properties(gme set(gme_syms "")
PROPERTIES FRAMEWORK TRUE foreach(gme_export ${gme_exports})
FRAMEWORK_VERSION A set(gme_syms "${gme_syms}_${gme_export}\n")
MACOSX_FRAMEWORK_IDENTIFIER net.mpyne.gme endforeach()
VERSION ${GME_VERSION}
SOVERSION 0 # Use an intermediate 'gme_generated.sym' file to avoid a relink every time CMake runs
PUBLIC_HEADER "${EXPORTED_HEADERS}") 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() endif()
install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX}
RUNTIME DESTINATION bin # DLL platforms set(INSTALL_TARGETS)
ARCHIVE DESTINATION lib # DLL platforms
FRAMEWORK DESTINATION /Library/Frameworks) # macOS framework # 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 # Run during cmake phase, so this is available during make
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gme_types.h.in configure_file(gme_types.h.in gen_types.h @ONLY)
${CMAKE_CURRENT_BINARY_DIR}/gme_types.h)
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in set(TMP_PC_LIBS "")
${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY) 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) configure_file(libgme.pc.in libgme.pc @ONLY)
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig)
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; buf = 0;
stereo_buffer = 0; stereo_buffer = 0;
voice_types = 0; voice_types = 0;
// avoid inconsistency in our duplicated constants // avoid inconsistency in our duplicated constants
assert( (int) wave_type == (int) Multi_Buffer::wave_type ); blaarg_static_assert( (int) wave_type == (int) Multi_Buffer::wave_type, "wave_type inconsistent across two classes using it" );
assert( (int) noise_type == (int) Multi_Buffer::noise_type ); blaarg_static_assert( (int) noise_type == (int) Multi_Buffer::noise_type, "noise_type inconsistent across two classes using it" );
assert( (int) mixed_type == (int) Multi_Buffer::mixed_type ); blaarg_static_assert( (int) mixed_type == (int) Multi_Buffer::mixed_type, "mixed_type inconsistent across two classes using it" );
} }
Classic_Emu::~Classic_Emu() Classic_Emu::~Classic_Emu()
@ -42,7 +42,7 @@ void Classic_Emu::set_equalizer_( equalizer_t const& eq )
if ( buf ) if ( buf )
buf->bass_freq( (int) equalizer().bass ); buf->bass_freq( (int) equalizer().bass );
} }
blargg_err_t Classic_Emu::set_sample_rate_( long rate ) blargg_err_t Classic_Emu::set_sample_rate_( long rate )
{ {
if ( !buf ) if ( !buf )
@ -115,7 +115,7 @@ blargg_err_t Classic_Emu::play_( long count, sample_t* out )
remute_voices(); remute_voices();
} }
int msec = buf->length(); 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 ) ); RETURN_ERR( run_clocks( clocks_emulated, msec ) );
assert( clocks_emulated ); assert( clocks_emulated );
buf->end_frame( 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 ) int header_size, void* header_out, int fill, long pad_size )
{ {
long file_offset = pad_size - header_size; long file_offset = pad_size - header_size;
rom_addr = 0; rom_addr = 0;
mask = 0; mask = 0;
size_ = 0; size_ = 0;
rom.clear(); rom.clear();
file_size_ = in.remain(); file_size_ = in.remain();
if ( file_size_ <= header_size ) // <= because there must be data after header if ( file_size_ <= header_size ) // <= because there must be data after header
return gme_wrong_file_type; return gme_wrong_file_type;
@ -147,20 +147,20 @@ blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in,
rom.clear(); rom.clear();
return err; return err;
} }
file_size_ -= header_size; file_size_ -= header_size;
memcpy( header_out, &rom [file_offset], header_size ); memcpy( header_out, &rom [file_offset], header_size );
memset( rom.begin() , fill, pad_size ); memset( rom.begin() , fill, pad_size );
memset( rom.end() - pad_size, fill, pad_size ); memset( rom.end() - pad_size, fill, pad_size );
return 0; return 0;
} }
void Rom_Data_::set_addr_( long addr, int unit ) void Rom_Data_::set_addr_( long addr, int unit )
{ {
rom_addr = addr - unit - pad_extra; rom_addr = addr - unit - pad_extra;
long rounded = (addr + file_size_ + unit - 1) / unit * unit; long rounded = (addr + file_size_ + unit - 1) / unit * unit;
if ( rounded <= 0 ) if ( rounded <= 0 )
{ {
@ -174,7 +174,7 @@ void Rom_Data_::set_addr_( long addr, int unit )
shift++; shift++;
mask = (1L << shift) - 1; mask = (1L << shift) - 1;
} }
if ( addr < 0 ) if ( addr < 0 )
addr = 0; addr = 0;
size_ = rounded; size_ = rounded;
@ -182,9 +182,9 @@ void Rom_Data_::set_addr_( long addr, int unit )
if ( 0 ) if ( 0 )
{ {
debug_printf( "addr: %X\n", addr ); debug_printf( "addr: %X\n", (int)addr );
debug_printf( "file_size: %d\n", file_size_ ); debug_printf( "file_size: %ld\n", file_size_ );
debug_printf( "rounded: %d\n", rounded ); debug_printf( "rounded: %ld\n", rounded );
debug_printf( "mask: $%X\n", mask ); debug_printf( "mask: $%X\n", mask );
} }
} }

View file

@ -21,7 +21,7 @@ protected:
blargg_err_t setup_buffer( long clock_rate ); blargg_err_t setup_buffer( long clock_rate );
long clock_rate() const { return clock_rate_; } long clock_rate() const { return clock_rate_; }
void change_clock_rate( long ); // experimental void change_clock_rate( long ); // experimental
// Overridable // Overridable
virtual void set_voice( int index, Blip_Buffer* center, virtual void set_voice( int index, Blip_Buffer* center,
Blip_Buffer* left, Blip_Buffer* right ) = 0; Blip_Buffer* left, Blip_Buffer* right ) = 0;
@ -58,10 +58,10 @@ protected:
enum { pad_extra = 8 }; enum { pad_extra = 8 };
blargg_vector<byte> rom; blargg_vector<byte> rom;
long file_size_; long file_size_;
blargg_long rom_addr; int32_t rom_addr;
blargg_long mask; int32_t mask;
blargg_long size_; // TODO: eliminate int32_t size_; // TODO: eliminate
blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out, blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out,
int fill, long pad_size ); int fill, long pad_size );
void set_addr_( long addr, int unit ); void set_addr_( long addr, int unit );
@ -77,39 +77,39 @@ public:
{ {
return load_rom_data_( in, header_size, header_out, fill, pad_size ); return load_rom_data_( in, header_size, header_out, fill, pad_size );
} }
// Size of file data read in (excluding header) // Size of file data read in (excluding header)
long file_size() const { return file_size_; } long file_size() const { return file_size_; }
// Pointer to beginning of file data // Pointer to beginning of file data
byte* begin() const { return rom.begin() + pad_size; } byte* begin() const { return rom.begin() + pad_size; }
// Set address that file data should start at // Set address that file data should start at
void set_addr( long addr ) { set_addr_( addr, unit ); } void set_addr( long addr ) { set_addr_( addr, unit ); }
// Free data // Free data
void clear() { rom.clear(); } void clear() { rom.clear(); }
// Size of data + start addr, rounded to a multiple of unit // Size of data + start addr, rounded to a multiple of unit
long size() const { return size_; } long size() const { return size_; }
// Pointer to unmapped page filled with same value // Pointer to unmapped page filled with same value
byte* unmapped() { return rom.begin(); } byte* unmapped() { return rom.begin(); }
// Mask address to nearest power of two greater than size() // 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 #ifdef check
check( addr <= mask ); check( addr <= mask );
#endif #endif
return addr & mask; return addr & mask;
} }
// Pointer to page starting at addr. Returns unmapped() if outside data. // 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; uint32_t offset = mask_addr( addr ) - rom_addr;
if ( offset > blargg_ulong (rom.size() - pad_size) ) if ( offset > uint32_t (rom.size() - pad_size) )
offset = 0; // unmapped offset = 0; // unmapped
return &rom [offset]; 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 long Std_File_Reader::size() const
{ {
if ( !file_ )
return -1L;
#ifdef HAVE_ZLIB_H #ifdef HAVE_ZLIB_H
if ( file_ ) return size_; // Set for both compressed and uncompressed modes
return size_; // Set for both compressed and uncompressed modes #else
#endif
long pos = tell(); long pos = tell();
fseek( (FILE*) file_, 0, SEEK_END ); fseek( (FILE*) file_, 0, SEEK_END );
long result = tell(); long result = tell();
fseek( (FILE*) file_, pos, SEEK_SET ); fseek( (FILE*) file_, pos, SEEK_SET );
return result; return result;
#endif
} }
long Std_File_Reader::read_avail( void* p, long s ) 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 ) 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 ); RETURN_VALIDITY_CHECK( s > 0 && static_cast<unsigned long>(s) <= UINT_MAX );
#ifdef HAVE_ZLIB_H #ifdef HAVE_ZLIB_H
if ( file_ ) const auto &gzfile = reinterpret_cast<gzFile>( file_ );
{ if ( s == gzread( gzfile, p, static_cast<unsigned>( s ) ) )
const auto &gzfile = reinterpret_cast<gzFile>( file_ ); return nullptr;
if ( s == gzread( gzfile, p, static_cast<unsigned>( s ) ) ) if ( gzeof( gzfile ) )
return nullptr; return eof_error;
if ( gzeof( gzfile ) ) return "Couldn't read from GZ file";
return eof_error; #else
return "Couldn't read from GZ file";
}
#endif
const auto &file = reinterpret_cast<FILE*>( file_ ); const auto &file = reinterpret_cast<FILE*>( file_ );
if ( s == static_cast<long>( fread( p, 1, static_cast<size_t>(s), file ) ) ) if ( s == static_cast<long>( fread( p, 1, static_cast<size_t>(s), file ) ) )
return 0; return 0;
if ( feof( file ) ) if ( feof( file ) )
return eof_error; return eof_error;
return "Couldn't read from file"; return "Couldn't read from file";
#endif
} }
long Std_File_Reader::tell() const long Std_File_Reader::tell() const
{ {
if ( !file_ )
return -1L;
#ifdef HAVE_ZLIB_H #ifdef HAVE_ZLIB_H
if ( file_ ) return gztell( reinterpret_cast<gzFile>( file_ ) );
return gztell( reinterpret_cast<gzFile>( file_ ) ); #else
#endif
return ftell( reinterpret_cast<FILE*>( file_ ) ); return ftell( reinterpret_cast<FILE*>( file_ ) );
#endif
} }
blargg_err_t Std_File_Reader::seek( long n ) blargg_err_t Std_File_Reader::seek( long n )
{ {
if ( !file_ )
return "NULL FILE pointer";
#ifdef HAVE_ZLIB_H #ifdef HAVE_ZLIB_H
if ( file_ ) if ( gzseek( reinterpret_cast<gzFile>( file_ ), n, SEEK_SET ) >= 0 )
{ return nullptr;
if ( gzseek( reinterpret_cast<gzFile>( file_ ), n, SEEK_SET ) >= 0 ) if ( n > size_ )
return nullptr; return eof_error;
if ( n > size_ ) return "Error seeking in GZ file";
return eof_error; #else
return "Error seeking in GZ file";
}
#endif
if ( !fseek( reinterpret_cast<FILE*>( file_ ), n, SEEK_SET ) ) if ( !fseek( reinterpret_cast<FILE*>( file_ ), n, SEEK_SET ) )
return nullptr; return nullptr;
if ( n > size() ) if ( n > size() )
return eof_error; return eof_error;
return "Error seeking in file"; return "Error seeking in file";
#endif
} }
void Std_File_Reader::close() void Std_File_Reader::close()
@ -450,4 +455,3 @@ void Std_File_Reader::close()
file_ = nullptr; file_ = nullptr;
} }
} }

View file

@ -14,21 +14,21 @@
class Data_Reader { class Data_Reader {
public: public:
virtual ~Data_Reader() { } virtual ~Data_Reader() { }
static const char eof_error []; // returned by read() when request goes beyond end 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 // Read at most count bytes and return number actually read, or <= 0 if error
virtual long read_avail( void*, long n ) = 0; virtual long read_avail( void*, long n ) = 0;
// Read exactly count bytes and return error if they couldn't be read // Read exactly count bytes and return error if they couldn't be read
virtual blargg_err_t read( void*, long count ); virtual blargg_err_t read( void*, long count );
// Number of bytes remaining until end of file // Number of bytes remaining until end of file
virtual long remain() const = 0; virtual long remain() const = 0;
// Read and discard count bytes // Read and discard count bytes
virtual blargg_err_t skip( long count ); virtual blargg_err_t skip( long count );
public: public:
Data_Reader() { } Data_Reader() { }
typedef blargg_err_t error_t; // deprecated typedef blargg_err_t error_t; // deprecated
@ -43,13 +43,13 @@ class File_Reader : public Data_Reader {
public: public:
// Size of file // Size of file
virtual long size() const = 0; virtual long size() const = 0;
// Current position in file // Current position in file
virtual long tell() const = 0; virtual long tell() const = 0;
// Go to new position // Go to new position
virtual blargg_err_t seek( long ) = 0; virtual blargg_err_t seek( long ) = 0;
long remain() const; long remain() const;
blargg_err_t skip( long n ); blargg_err_t skip( long n );
}; };
@ -59,7 +59,7 @@ class Std_File_Reader : public File_Reader {
public: public:
blargg_err_t open( const char* path ); blargg_err_t open( const char* path );
void close(); void close();
public: public:
Std_File_Reader(); Std_File_Reader();
~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; long pair_count = sample_buf_size >> 1;
blip_time_t blip_time = blip_buf.count_clocks( pair_count ); blip_time_t blip_time = blip_buf.count_clocks( pair_count );
int sample_count = oversamples_per_frame - resampler.written(); int sample_count = oversamples_per_frame - resampler.written();
int new_count = play_frame( blip_time, sample_count, resampler.buffer() ); int new_count = play_frame( blip_time, sample_count, resampler.buffer() );
assert( new_count < resampler_size ); assert( new_count < resampler_size );
blip_buf.end_frame( blip_time ); blip_buf.end_frame( blip_time );
assert( blip_buf.samples_avail() == pair_count ); assert( blip_buf.samples_avail() == pair_count );
resampler.write( new_count ); resampler.write( new_count );
#ifdef NDEBUG // Avoid warning when asserts are disabled #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 ); long count = resampler.read( sample_buf.begin(), sample_buf_size );
assert( count == (long) sample_buf_size ); assert( count == (long) sample_buf_size );
#endif #endif
mix_samples( blip_buf, out ); mix_samples( blip_buf, out );
blip_buf.remove_samples( pair_count ); 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; out += remain;
buf_pos += remain; buf_pos += remain;
} }
// entire frames // entire frames
while ( count >= (long) sample_buf_size ) 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; out += sample_buf_size;
count -= sample_buf_size; count -= sample_buf_size;
} }
// extra // extra
if ( count ) if ( count )
{ {
@ -115,25 +115,25 @@ void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out )
Blip_Reader sn; Blip_Reader sn;
int bass = sn.begin( blip_buf ); int bass = sn.begin( blip_buf );
const dsample_t* in = sample_buf.begin(); const dsample_t* in = sample_buf.begin();
for ( int n = sample_buf_size >> 1; n--; ) for ( int n = sample_buf_size >> 1; n--; )
{ {
int s = sn.read(); 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 ) if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24); l = 0x7FFF - (l >> 24);
sn.next( bass ); 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 ) if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24); r = 0x7FFF - (r >> 24);
in += 2; in += 2;
out [0] = l; out [0] = l;
out [1] = r; out [1] = r;
out += 2; out += 2;
} }
sn.end( blip_buf ); sn.end( blip_buf );
} }

View file

@ -11,26 +11,26 @@ class Dual_Resampler {
public: public:
Dual_Resampler(); Dual_Resampler();
virtual ~Dual_Resampler(); virtual ~Dual_Resampler();
typedef short dsample_t; typedef short dsample_t;
double setup( double oversample, double rolloff, double gain ); double setup( double oversample, double rolloff, double gain );
blargg_err_t reset( int max_pairs ); blargg_err_t reset( int max_pairs );
void resize( int pairs_per_frame ); void resize( int pairs_per_frame );
void clear(); void clear();
void dual_play( long count, dsample_t* out, Blip_Buffer& ); void dual_play( long count, dsample_t* out, Blip_Buffer& );
protected: protected:
virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0; virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0;
private: private:
blargg_vector<dsample_t> sample_buf; blargg_vector<dsample_t> sample_buf;
int sample_buf_size; int sample_buf_size;
int oversamples_per_frame; int oversamples_per_frame;
int buf_pos; int buf_pos;
int resampler_size; int resampler_size;
Fir_Resampler<12> resampler; Fir_Resampler<12> resampler;
void mix_samples( Blip_Buffer&, dsample_t* ); void mix_samples( Blip_Buffer&, dsample_t* );
void play_frame_( 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 #include BLARGG_ENABLE_OPTIMIZER
#endif #endif
typedef blargg_long fixed_t; typedef int32_t fixed_t;
using std::min; using std::min;
using std::max; using std::max;
@ -30,12 +30,12 @@ using std::max;
#define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) #define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5)
#define FMUL( x, y ) (((x) * (y)) >> 15) #define FMUL( x, y ) (((x) * (y)) >> 15)
const unsigned echo_size = 4096; static const unsigned echo_size = 4096;
const unsigned echo_mask = echo_size - 1; static const unsigned echo_mask = echo_size - 1;
static_assert( (echo_size & echo_mask) == 0, "echo_size must be a power of 2" ); static_assert( (echo_size & echo_mask) == 0, "echo_size must be a power of 2" );
const unsigned reverb_size = 8192 * 2; static const unsigned reverb_size = 8192 * 2;
const unsigned reverb_mask = reverb_size - 1; static const unsigned reverb_mask = reverb_size - 1;
static_assert( (reverb_size & reverb_mask) == 0, "reverb_size must be a power of 2" ); static_assert( (reverb_size & reverb_mask) == 0, "reverb_size must be a power of 2" );
Effects_Buffer::config_t::config_t() Effects_Buffer::config_t::config_t()
@ -88,34 +88,32 @@ Effects_Buffer::Effects_Buffer( int num_voices, bool center_only )
Effects_Buffer::~Effects_Buffer() 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 );
{ }
echo_buf[i].resize( echo_size );
} if ( !reverb_buf[i].size() )
{
if ( !reverb_buf[i].size() ) reverb_buf[i].resize( reverb_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++ ) for ( int i = 0; i < buf_count; i++ )
RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) ); RETURN_ERR( bufs [i].set_sample_rate( rate, msec ) );
config( config_ ); config( config_ );
clear(); clear();
return Multi_Buffer::set_sample_rate( bufs [0].sample_rate(), bufs [0].length() ); 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() ) if ( echo_buf[i].size() )
memset( &echo_buf[i][0], 0, echo_size * sizeof echo_buf[i][0] ); memset( &echo_buf[i][0], 0, echo_size * sizeof echo_buf[i][0] );
if ( reverb_buf[i].size() ) if ( reverb_buf[i].size() )
memset( &reverb_buf[i][0], 0, reverb_size * sizeof reverb_buf[i][0] ); 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 ) void Effects_Buffer::config( const config_t& cfg )
{ {
channels_changed(); channels_changed();
// clear echo and reverb buffers // clear echo and reverb buffers
// ensure the echo/reverb buffers have already been allocated, so this method can be // ensure the echo/reverb buffers have already been allocated, so this method can be
// called before set_sample_rate is called // called before set_sample_rate is called
@ -175,49 +173,49 @@ void Effects_Buffer::config( const config_t& cfg )
} }
config_ = cfg; config_ = cfg;
if ( config_.effects_enabled ) if ( config_.effects_enabled )
{ {
// convert to internal format // convert to internal format
chans.pan_1_levels [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_1 ); 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_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 [0] = TO_FIXED( 1 ) - TO_FIXED( config_.pan_2 );
chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0]; chans.pan_2_levels [1] = TO_FIXED( 2 ) - chans.pan_2_levels [0];
chans.reverb_level = TO_FIXED( config_.reverb_level ); chans.reverb_level = TO_FIXED( config_.reverb_level );
chans.echo_level = TO_FIXED( config_.echo_level ); chans.echo_level = TO_FIXED( config_.echo_level );
int delay_offset = int (1.0 / 2000 * config_.delay_variance * sample_rate()); 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()); int reverb_sample_delay = int (1.0 / 1000 * config_.reverb_delay * sample_rate());
chans.reverb_delay_l = pin_range( reverb_size - chans.reverb_delay_l = pin_range( reverb_size -
(reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 ); (reverb_sample_delay - delay_offset) * 2, reverb_size - 2, 0 );
chans.reverb_delay_r = pin_range( reverb_size + 1 - chans.reverb_delay_r = pin_range( reverb_size + 1 -
(reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 ); (reverb_sample_delay + delay_offset) * 2, reverb_size - 1, 1 );
int echo_sample_delay = int (1.0 / 1000 * config_.echo_delay * sample_rate()); 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), chans.echo_delay_l = pin_range( echo_size - 1 - (echo_sample_delay - delay_offset),
echo_size - 1 ); echo_size - 1 );
chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset), chans.echo_delay_r = pin_range( echo_size - 1 - (echo_sample_delay + delay_offset),
echo_size - 1 ); echo_size - 1 );
for(int i=0; i<max_voices; i++) 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].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].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+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].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].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+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].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].left = &bufs [i*max_buf_count+5];
chan_types [i*chan_types_count+2].right = &bufs [i*max_buf_count+6]; 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 else
{ {
@ -233,7 +231,7 @@ void Effects_Buffer::config( const config_t& cfg )
} }
} }
} }
if ( buf_count < max_buf_count ) // if center_only if ( buf_count < max_buf_count ) // if center_only
{ {
for(int i=0; i<max_voices; i++) 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]; return chan_types [(i%max_voices)*chan_types_count+out];
} }
void Effects_Buffer::end_frame( blip_time_t clock_count ) void Effects_Buffer::end_frame( blip_time_t clock_count )
{ {
int bufs_used = 0; int bufs_used = 0;
@ -284,7 +282,7 @@ void Effects_Buffer::end_frame( blip_time_t clock_count )
} }
bufs_used = 0; bufs_used = 0;
} }
effects_enabled = config_.effects_enabled; 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; int active_bufs = buf_count_per_voice;
long count = remain; long count = remain;
// optimizing mixing to skip any channels which had nothing added // optimizing mixing to skip any channels which had nothing added
if ( effect_remain ) if ( effect_remain )
{ {
if ( count > effect_remain ) if ( count > effect_remain )
count = effect_remain; count = effect_remain;
if ( stereo_remain ) if ( stereo_remain )
{ {
mix_enhanced( out, count ); mix_enhanced( out, count );
@ -327,25 +325,25 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
else if ( stereo_remain ) else if ( stereo_remain )
{ {
mix_stereo( out, count ); mix_stereo( out, count );
active_bufs = 3; active_bufs = 3;
} }
else else
{ {
mix_mono( out, count ); mix_mono( out, count );
active_bufs = 1; active_bufs = 1;
} }
out += count * n_channels; out += count * n_channels;
remain -= count; remain -= count;
stereo_remain -= count; stereo_remain -= count;
if ( stereo_remain < 0 ) if ( stereo_remain < 0 )
stereo_remain = 0; stereo_remain = 0;
effect_remain -= count; effect_remain -= count;
if ( effect_remain < 0 ) if ( effect_remain < 0 )
effect_remain = 0; effect_remain = 0;
// skip the output from any buffers that didn't contribute to the sound output // 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 // during this frame (e.g. if we only render mono then only the very first buf
// is 'active') // is 'active')
@ -360,56 +358,52 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples )
} }
} }
} }
return total_samples * n_channels; 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++) for(int i=0; i<max_voices; i++)
{ {
blip_sample_t* BLIP_RESTRICT out = out_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+0] ); int const bass = BLIP_READER_BASS( bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( c, bufs [i*max_buf_count+0] ); BLIP_READER_BEGIN( c, bufs [i*max_buf_count+0] );
// unrolled loop // 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 ); BLIP_READER_NEXT( c, bass );
blargg_long cs1 = BLIP_READER_READ( c ); int32_t cs1 = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass ); BLIP_READER_NEXT( c, bass );
if ( (int16_t) cs0 != cs0 ) if ( (int16_t) cs0 != cs0 )
cs0 = 0x7FFF - (cs0 >> 24); 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 ) if ( (int16_t) cs1 != cs1 )
cs1 = 0x7FFF - (cs1 >> 24); 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; out += max_voices*4;
} }
if ( count & 1 ) if ( count & 1 )
{ {
int s = BLIP_READER_READ( c ); int s = BLIP_READER_READ( c );
BLIP_READER_NEXT( c, bass ); BLIP_READER_NEXT( c, bass );
if ( (int16_t) s != s )
s = 0x7FFF - (s >> 24);
out [i*2+0] = s; out [i*2+0] = s;
out [i*2+1] = 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] ); 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++) 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 ); int right = cs + BLIP_READER_READ( r );
BLIP_READER_NEXT( l, bass ); BLIP_READER_NEXT( l, bass );
BLIP_READER_NEXT( r, bass ); BLIP_READER_NEXT( r, bass );
if ( (int16_t) left != left ) if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24); left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right ) if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24); right = 0x7FFF - (right >> 24);
out [i*2+0] = left; out [i*2+0] = left;
out [i*2+1] = right; out [i*2+1] = right;
out += max_voices*2; out += max_voices*2;
} }
BLIP_READER_END( r, bufs [i*max_buf_count+2] ); BLIP_READER_END( r, bufs [i*max_buf_count+2] );
BLIP_READER_END( l, bufs [i*max_buf_count+1] ); BLIP_READER_END( l, bufs [i*max_buf_count+1] );
BLIP_READER_END( c, bufs [i*max_buf_count+0] ); 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++) 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( center, bufs [i*max_buf_count+2] );
BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] ); BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] ); 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 reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = &this->echo_buf[i][0]; blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos[i]; int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos[i]; int reverb_pos = this->reverb_pos[i];
int count = frames; int count = frames;
while ( count-- ) while ( count-- )
{ {
int sum1_s = BLIP_READER_READ( sq1 ); int sum1_s = BLIP_READER_READ( sq1 );
int sum2_s = BLIP_READER_READ( sq2 ); int sum2_s = BLIP_READER_READ( sq2 );
BLIP_READER_NEXT( sq1, bass ); BLIP_READER_NEXT( sq1, bass );
BLIP_READER_NEXT( sq2, bass ); BLIP_READER_NEXT( sq2, bass );
int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
FMUL( sum2_s, chans.pan_2_levels [0] ) + FMUL( sum2_s, chans.pan_2_levels [0] ) +
reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
FMUL( sum2_s, chans.pan_2_levels [1] ) + FMUL( sum2_s, chans.pan_2_levels [1] ) +
reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
fixed_t reverb_level = chans.reverb_level; fixed_t reverb_level = chans.reverb_level;
reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, 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_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level );
reverb_pos = (reverb_pos + 2) & reverb_mask; reverb_pos = (reverb_pos + 2) & reverb_mask;
int sum3_s = BLIP_READER_READ( center ); int sum3_s = BLIP_READER_READ( center );
BLIP_READER_NEXT( center, bass ); BLIP_READER_NEXT( center, bass );
int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, int left = new_reverb_l + sum3_s + FMUL( chans.echo_level,
echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] );
int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, 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 + chans.echo_delay_r) & echo_mask] );
echo_buf [echo_pos] = sum3_s; echo_buf [echo_pos] = sum3_s;
echo_pos = (echo_pos + 1) & echo_mask; echo_pos = (echo_pos + 1) & echo_mask;
if ( (int16_t) left != left ) if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24); left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right ) if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24); 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->reverb_pos[i] = reverb_pos;
this->echo_pos[i] = echo_pos; this->echo_pos[i] = echo_pos;
BLIP_READER_END( sq1, bufs [i*max_buf_count+0] ); BLIP_READER_END( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_END( sq2, bufs [i*max_buf_count+1] ); BLIP_READER_END( sq2, bufs [i*max_buf_count+1] );
BLIP_READER_END( center, bufs [i*max_buf_count+2] ); 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++) 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( r2, bufs [i*max_buf_count+6] );
BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] ); BLIP_READER_BEGIN( sq1, bufs [i*max_buf_count+0] );
BLIP_READER_BEGIN( sq2, bufs [i*max_buf_count+1] ); 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 reverb_buf = &this->reverb_buf[i][0];
blip_sample_t* const echo_buf = &this->echo_buf[i][0]; blip_sample_t* const echo_buf = &this->echo_buf[i][0];
int echo_pos = this->echo_pos[i]; int echo_pos = this->echo_pos[i];
int reverb_pos = this->reverb_pos[i]; int reverb_pos = this->reverb_pos[i];
int count = frames; int count = frames;
while ( count-- ) while ( count-- )
{ {
int sum1_s = BLIP_READER_READ( sq1 ); int sum1_s = BLIP_READER_READ( sq1 );
int sum2_s = BLIP_READER_READ( sq2 ); int sum2_s = BLIP_READER_READ( sq2 );
BLIP_READER_NEXT( sq1, bass ); BLIP_READER_NEXT( sq1, bass );
BLIP_READER_NEXT( sq2, bass ); BLIP_READER_NEXT( sq2, bass );
int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) +
FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) + FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) +
reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask];
int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) +
FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) + FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) +
reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask];
BLIP_READER_NEXT( l1, bass ); BLIP_READER_NEXT( l1, bass );
BLIP_READER_NEXT( r1, bass ); BLIP_READER_NEXT( r1, bass );
fixed_t reverb_level = chans.reverb_level; fixed_t reverb_level = chans.reverb_level;
reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, 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_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level );
reverb_pos = (reverb_pos + 2) & reverb_mask; reverb_pos = (reverb_pos + 2) & reverb_mask;
int sum3_s = BLIP_READER_READ( center ); int sum3_s = BLIP_READER_READ( center );
BLIP_READER_NEXT( center, bass ); BLIP_READER_NEXT( center, bass );
int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level, 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] ); 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, 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] ); echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] );
BLIP_READER_NEXT( l2, bass ); BLIP_READER_NEXT( l2, bass );
BLIP_READER_NEXT( r2, bass ); BLIP_READER_NEXT( r2, bass );
echo_buf [echo_pos] = sum3_s; echo_buf [echo_pos] = sum3_s;
echo_pos = (echo_pos + 1) & echo_mask; echo_pos = (echo_pos + 1) & echo_mask;
if ( (int16_t) left != left ) if ( (int16_t) left != left )
left = 0x7FFF - (left >> 24); left = 0x7FFF - (left >> 24);
if ( (int16_t) right != right ) if ( (int16_t) right != right )
right = 0x7FFF - (right >> 24); 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->reverb_pos[i] = reverb_pos;
this->echo_pos[i] = echo_pos; this->echo_pos[i] = echo_pos;
BLIP_READER_END( l1, bufs [i*max_buf_count+3] ); BLIP_READER_END( l1, bufs [i*max_buf_count+3] );
BLIP_READER_END( r1, bufs [i*max_buf_count+4] ); BLIP_READER_END( r1, bufs [i*max_buf_count+4] );
BLIP_READER_END( l2, bufs [i*max_buf_count+5] ); 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 // If center_only is true, only center buffers are created and
// less memory is used. // less memory is used.
Effects_Buffer( int nVoices = 1, bool center_only = false ); Effects_Buffer( int nVoices = 1, bool center_only = false );
// Channel Effect Center Pan // Channel Effect Center Pan
// --------------------------------- // ---------------------------------
// 0,5 reverb pan_1 // 0,5 reverb pan_1
@ -24,7 +24,7 @@ public:
// 2,7 echo - // 2,7 echo -
// 3 echo - // 3 echo -
// 4 echo - // 4 echo -
// Channel configuration // Channel configuration
struct config_t { struct config_t {
double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right 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 bool effects_enabled; // if false, use optimized simple mixer
config_t(); config_t();
}; };
// Set configuration of buffer // Set configuration of buffer
virtual void config( const config_t& ); virtual void config( const config_t& );
void set_depth( double ); void set_depth( double );
public: public:
~Effects_Buffer(); ~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 clock_rate( long );
void bass_freq( int ); void bass_freq( int );
void clear(); void clear();
@ -64,12 +64,12 @@ private:
long effect_remain; long effect_remain;
int buf_count; int buf_count;
bool effects_enabled; bool effects_enabled;
std::vector<std::vector<blip_sample_t> > reverb_buf; std::vector<std::vector<blip_sample_t> > reverb_buf;
std::vector<std::vector<blip_sample_t> > echo_buf; std::vector<std::vector<blip_sample_t> > echo_buf;
std::vector<int> reverb_pos; std::vector<int> reverb_pos;
std::vector<int> echo_pos; std::vector<int> echo_pos;
struct { struct {
fixed_t pan_1_levels [2]; fixed_t pan_1_levels [2];
fixed_t pan_2_levels [2]; fixed_t pan_2_levels [2];
@ -80,11 +80,11 @@ private:
int reverb_delay_r; int reverb_delay_r;
fixed_t reverb_level; fixed_t reverb_level;
} chans; } chans;
void mix_mono( blip_sample_t*, blargg_long ); void mix_mono( blip_sample_t*, int32_t );
void mix_stereo( blip_sample_t*, blargg_long ); void mix_stereo( blip_sample_t*, int32_t );
void mix_enhanced( blip_sample_t*, blargg_long ); void mix_enhanced( blip_sample_t*, int32_t );
void mix_mono_enhanced( blip_sample_t*, blargg_long ); void mix_mono_enhanced( blip_sample_t*, int32_t );
}; };
#endif #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 to_w = maxh * 2 / width;
double const pow_a_n = pow( rolloff, maxh ); double const pow_a_n = pow( rolloff, maxh );
scale /= maxh * 2; scale /= maxh * 2;
double angle = (count / 2 - 1 + offset) * -step; double angle = (count / 2 - 1 + offset) * -step;
while ( count-- ) 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 ); pow_a_n * rolloff * cos( (maxh - 1) * angle );
double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff;
double sinc = scale * num / den - scale; double sinc = scale * num / den - scale;
out [-1] = (short) (cos( w ) * sinc + sinc); out [-1] = (short) (cos( w ) * sinc + sinc);
} }
angle += step; angle += step;
@ -83,11 +83,11 @@ blargg_err_t Fir_Resampler_::buffer_size( int new_size )
clear(); clear();
return 0; return 0;
} }
double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain )
{ {
ratio_ = new_factor; ratio_ = new_factor;
double fstep = 0.0; double fstep = 0.0;
{ {
double least_error = 2; double least_error = 2;
@ -106,14 +106,14 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai
} }
} }
} }
skip_bits = 0; skip_bits = 0;
step = stereo * (int) floor( fstep ); step = stereo * (int) floor( fstep );
ratio_ = fstep; ratio_ = fstep;
fstep = fmod( fstep, 1.0 ); fstep = fmod( fstep, 1.0 );
double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_;
double pos = 0.0; double pos = 0.0;
input_per_cycle = 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, gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter,
double (0x7FFF * gain * filter), double (0x7FFF * gain * filter),
(int) width_, impulses + i * width_ ); (int) width_, impulses + i * width_ );
pos += fstep; pos += fstep;
input_per_cycle += step; input_per_cycle += step;
if ( pos >= 0.9999999 ) if ( pos >= 0.9999999 )
@ -132,16 +132,16 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai
input_per_cycle++; input_per_cycle++;
} }
} }
clear(); clear();
return ratio_; 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; unsigned long skip = skip_bits >> imp_phase;
int remain = res - imp_phase; int remain = res - imp_phase;
while ( (output_count -= 2) > 0 ) while ( (output_count -= 2) > 0 )
@ -155,20 +155,20 @@ int Fir_Resampler_::input_needed( blargg_long output_count ) const
} }
output_count -= 2; output_count -= 2;
} }
long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]);
if ( input_extra < 0 ) if ( input_extra < 0 )
input_extra = 0; input_extra = 0;
return input_extra; 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 cycle_count = input_count / input_per_cycle;
int output_count = cycle_count * res * stereo; int output_count = cycle_count * res * stereo;
input_count -= cycle_count * input_per_cycle; 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; int remain = res - imp_phase;
while ( input_count >= 0 ) while ( input_count >= 0 )
{ {
@ -188,12 +188,14 @@ int Fir_Resampler_::skip_input( long count )
{ {
int remain = write_pos - buf.begin(); int remain = write_pos - buf.begin();
int max_count = remain - width_ * stereo; int max_count = remain - width_ * stereo;
if ( max_count < 0 )
max_count = 0;
if ( count > max_count ) if ( count > max_count )
count = max_count; count = max_count;
remain -= count; remain -= count;
write_pos = &buf [remain]; write_pos = &buf [remain];
memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); memmove( buf.begin(), &buf [count], remain * sizeof buf [0] );
return count; return count;
} }

View file

@ -9,50 +9,50 @@
class Fir_Resampler_ { class Fir_Resampler_ {
public: public:
// Use Fir_Resampler<width> (below) // Use Fir_Resampler<width> (below)
// Set input/output resampling ratio and optionally low-pass rolloff and gain. // Set input/output resampling ratio and optionally low-pass rolloff and gain.
// Returns actual ratio used (rounded to internal precision). // Returns actual ratio used (rounded to internal precision).
double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 ); double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 );
// Current input/output ratio // Current input/output ratio
double ratio() const { return ratio_; } double ratio() const { return ratio_; }
// Input // Input
typedef short sample_t; typedef short sample_t;
// Resize and clear input buffer // Resize and clear input buffer
blargg_err_t buffer_size( int ); blargg_err_t buffer_size( int );
// Clear input buffer. At least two output samples will be available after // Clear input buffer. At least two output samples will be available after
// two input samples are written. // two input samples are written.
void clear(); void clear();
// Number of input samples that can be written // Number of input samples that can be written
int max_write() const { return buf.end() - write_pos; } int max_write() const { return buf.end() - write_pos; }
// Pointer to place to write input samples // Pointer to place to write input samples
sample_t* buffer() { return write_pos; } sample_t* buffer() { return write_pos; }
// Notify resampler that 'count' input samples have been written // Notify resampler that 'count' input samples have been written
void write( long count ); void write( long count );
// Number of input samples in buffer // Number of input samples in buffer
int written() const { return write_pos - &buf [write_offset]; } int written() const { return write_pos - &buf [write_offset]; }
// Skip 'count' input samples. Returns number of samples actually skipped. // Skip 'count' input samples. Returns number of samples actually skipped.
int skip_input( long count ); int skip_input( long count );
// Output // Output
// Number of extra input samples needed until 'count' output samples are available // 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 // Number of output samples available
int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); }
public: public:
~Fir_Resampler_(); ~Fir_Resampler_();
protected: protected:
@ -64,14 +64,14 @@ protected:
int imp_phase; int imp_phase;
int const width_; int const width_;
int const write_offset; int const write_offset;
blargg_ulong skip_bits; uint32_t skip_bits;
int step; int step;
int input_per_cycle; int input_per_cycle;
double ratio_; double ratio_;
sample_t* impulses; sample_t* impulses;
Fir_Resampler_( int width, sample_t* ); 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 // 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]; short impulses [max_res] [width];
public: public:
Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { } Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { }
// Read at most 'count' samples. Returns number of samples actually read. // Read at most 'count' samples. Returns number of samples actually read.
typedef short sample_t; typedef short sample_t;
int read( sample_t* out, blargg_long count ); int read( sample_t* out, int32_t count );
}; };
// End of public interface // End of public interface
@ -97,24 +97,24 @@ inline void Fir_Resampler_::write( long count )
} }
template<int width> 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; sample_t* out = out_begin;
const sample_t* in = buf.begin(); const sample_t* in = buf.begin();
sample_t* end_pos = write_pos; 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]; sample_t const* imp = impulses [imp_phase];
int remain = res - imp_phase; int remain = res - imp_phase;
int const step = this->step; int const step = this->step;
count >>= 1; count >>= 1;
// Resampling can add noise so don't actually do it if we've matched sample // Resampling can add noise so don't actually do it if we've matched sample
// rate // rate
const double ratio1 = ratio() - 1.0; const double ratio1 = ratio() - 1.0;
const bool should_resample = const bool should_resample =
( ratio1 >= 0 ? ratio1 : -ratio1 ) >= 0.00001; ( ratio1 >= 0 ? ratio1 : -ratio1 ) >= 0.00001;
if ( end_pos - in >= width * stereo ) if ( end_pos - in >= width * stereo )
{ {
end_pos -= width * stereo; end_pos -= width * stereo;
@ -123,7 +123,7 @@ int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
count--; count--;
if ( count < 0 ) if ( count < 0 )
break; break;
if( !should_resample ) if( !should_resample )
{ {
out [0] = static_cast<sample_t>( in [0] ); 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 else
{ {
// accumulate in extended precision // accumulate in extended precision
blargg_long l = 0; int32_t l = 0;
blargg_long r = 0; int32_t r = 0;
const sample_t* i = in; const sample_t* i = in;
for ( int n = width / 2; n; --n ) for ( int n = width / 2; n; --n )
{ {
int pt0 = imp [0]; int pt0 = imp [0];
@ -148,38 +148,38 @@ int Fir_Resampler<width>::read( sample_t* out_begin, blargg_long count )
r += pt1 * i [3]; r += pt1 * i [3];
i += 4; i += 4;
} }
remain--; remain--;
l >>= 15; l >>= 15;
r >>= 15; r >>= 15;
in += (skip * stereo) & stereo; in += (skip * stereo) & stereo;
skip >>= 1; skip >>= 1;
if ( !remain ) if ( !remain )
{ {
imp = impulses [0]; imp = impulses [0];
skip = skip_bits; skip = skip_bits;
remain = res; remain = res;
} }
out [0] = (sample_t) l; out [0] = (sample_t) l;
out [1] = (sample_t) r; out [1] = (sample_t) r;
} }
in += step; in += step;
out += 2; out += 2;
} }
while ( in <= end_pos ); while ( in <= end_pos );
} }
imp_phase = res - remain; imp_phase = res - remain;
int left = write_pos - in; int left = write_pos - in;
write_pos = &buf [left]; write_pos = &buf [left];
memmove( buf.begin(), in, left * sizeof *in ); memmove( buf.begin(), in, left * sizeof *in );
return out - out_begin; 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" #include "blargg_source.h"
unsigned const vol_reg = 0xFF24; static unsigned const vol_reg = 0xFF24;
unsigned const status_reg = 0xFF26; static unsigned const status_reg = 0xFF26;
using std::min; using std::min;
using std::max; using std::max;
Gb_Apu::Gb_Apu() Gb_Apu::Gb_Apu()
{ {
memset(regs, 0, sizeof(regs));
square1.synth = &square_synth; square1.synth = &square_synth;
square2.synth = &square_synth; square2.synth = &square_synth;
wave.synth = &other_synth; wave.synth = &other_synth;
noise.synth = &other_synth; noise.synth = &other_synth;
oscs [0] = &square1; oscs [0] = &square1;
oscs [1] = &square2; oscs [1] = &square2;
oscs [2] = &wave; oscs [2] = &wave;
oscs [3] = &noise; oscs [3] = &noise;
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
{ {
Gb_Osc& osc = *oscs [i]; Gb_Osc& osc = *oscs [i];
@ -46,7 +48,7 @@ Gb_Apu::Gb_Apu()
osc.outputs [2] = 0; osc.outputs [2] = 0;
osc.outputs [3] = 0; osc.outputs [3] = 0;
} }
set_tempo( 1.0 ); set_tempo( 1.0 );
volume( 1.0 ); volume( 1.0 );
reset(); reset();
@ -108,21 +110,21 @@ void Gb_Apu::reset()
next_frame_time = 0; next_frame_time = 0;
last_time = 0; last_time = 0;
frame_count = 0; frame_count = 0;
square1.reset(); square1.reset();
square2.reset(); square2.reset();
wave.reset(); wave.reset();
noise.reset(); noise.reset();
noise.bits = 1; noise.bits = 1;
wave.wave_pos = 0; wave.wave_pos = 0;
// avoid click at beginning // avoid click at beginning
regs [vol_reg - start_addr] = 0x77; regs [vol_reg - start_addr] = 0x77;
update_volume(); update_volume();
regs [status_reg - start_addr] = 0x01; // force power regs [status_reg - start_addr] = 0x01; // force power
write_register( 0, status_reg, 0x00 ); write_register( 0, status_reg, 0x00 );
static unsigned char const initial_wave [] = { static unsigned char const initial_wave [] = {
0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table
0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA 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 require( end_time >= last_time ); // end_time must not be before previous time
if ( end_time == last_time ) if ( end_time == last_time )
return; return;
while ( true ) while ( true )
{ {
blip_time_t time = next_frame_time; blip_time_t time = next_frame_time;
if ( time > end_time ) if ( time > end_time )
time = end_time; time = end_time;
// run oscillators // run oscillators
for ( int i = 0; i < osc_count; ++i ) 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; last_time = time;
if ( time == end_time ) if ( time == end_time )
break; break;
next_frame_time += frame_period; next_frame_time += frame_period;
// 256 Hz actions // 256 Hz actions
square1.clock_length(); square1.clock_length();
square2.clock_length(); square2.clock_length();
wave.clock_length(); wave.clock_length();
noise.clock_length(); noise.clock_length();
frame_count = (frame_count + 1) & 3; frame_count = (frame_count + 1) & 3;
if ( frame_count == 0 ) if ( frame_count == 0 )
{ {
@ -183,7 +185,7 @@ void Gb_Apu::run_until( blip_time_t end_time )
square2.clock_envelope(); square2.clock_envelope();
noise.clock_envelope(); noise.clock_envelope();
} }
if ( frame_count & 1 ) if ( frame_count & 1 )
square1.clock_sweep(); // 128 Hz action 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 ) if ( end_time > last_time )
run_until( end_time ); run_until( end_time );
assert( next_frame_time >= end_time ); assert( next_frame_time >= end_time );
next_frame_time -= end_time; next_frame_time -= end_time;
assert( last_time >= end_time ); assert( last_time >= end_time );
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 ) void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data )
{ {
require( (unsigned) data < 0x100 ); require( (unsigned) data < 0x100 );
int reg = addr - start_addr; int reg = addr - start_addr;
if ( (unsigned) reg >= register_count ) if ( (unsigned) reg >= register_count )
return; return;
run_until( time ); run_until( time );
int old_reg = regs [reg]; int old_reg = regs [reg];
regs [reg] = data; regs [reg] = data;
if ( addr < vol_reg ) if ( addr < vol_reg )
{ {
write_osc( reg / 5, reg, data ); 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 ) if ( amp && osc.enabled && osc.output )
other_synth.offset( time, -amp, osc.output ); other_synth.offset( time, -amp, osc.output );
} }
if ( wave.outputs [3] ) if ( wave.outputs [3] )
other_synth.offset( time, 30, wave.outputs [3] ); other_synth.offset( time, 30, wave.outputs [3] );
update_volume(); update_volume();
if ( wave.outputs [3] ) if ( wave.outputs [3] )
other_synth.offset( time, -30, wave.outputs [3] ); other_synth.offset( time, -30, wave.outputs [3] );
// oscs will update with new amplitude when next run // oscs will update with new amplitude when next run
} }
else if ( addr == 0xFF25 || addr == status_reg ) else if ( addr == 0xFF25 || addr == status_reg )
{ {
int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0;
int flags = regs [0xFF25 - start_addr] & mask; int flags = regs [0xFF25 - start_addr] & mask;
// left/right assignments // left/right assignments
for ( int i = 0; i < osc_count; i++ ) 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 ); other_synth.offset( time, -amp, old_output );
} }
} }
if ( addr == status_reg && data != old_reg ) if ( addr == status_reg && data != old_reg )
{ {
if ( !(data & 0x80) ) 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 ) int Gb_Apu::read_register( blip_time_t time, unsigned addr )
{ {
run_until( time ); run_until( time );
int index = addr - start_addr; int index = addr - start_addr;
require( (unsigned) index < register_count ); require( (unsigned) index < register_count );
int data = regs [index]; int data = regs [index];
if ( addr == status_reg ) if ( addr == status_reg )
{ {
data = (data & 0x80) | 0x70; data = (data & 0x80) | 0x70;
@ -305,6 +307,6 @@ int Gb_Apu::read_register( blip_time_t time, unsigned addr )
data |= 1 << i; data |= 1 << i;
} }
} }
return data; return data;
} }

View file

@ -8,62 +8,62 @@
class Gb_Apu { class Gb_Apu {
public: public:
// Set overall volume of all oscillators, where 1.0 is full volume // Set overall volume of all oscillators, where 1.0 is full volume
void volume( double ); void volume( double );
// Set treble equalization // Set treble equalization
void treble_eq( const blip_eq_t& ); void treble_eq( const blip_eq_t& );
// Outputs can be assigned to a single buffer for mono output, or to three // 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). // buffers for stereo output (using Stereo_Buffer to do the mixing).
// Assign all oscillator outputs to specified buffer(s). If buffer // Assign all oscillator outputs to specified buffer(s). If buffer
// is NULL, silences all oscillators. // is NULL, silences all oscillators.
void output( Blip_Buffer* mono ); void output( Blip_Buffer* mono );
void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, // 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, // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL,
// silences oscillator. // 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* mono );
void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Reset oscillators and internal state // Reset oscillators and internal state
void reset(); void reset();
// Reads and writes at addr must satisfy start_addr <= addr <= end_addr // Reads and writes at addr must satisfy start_addr <= addr <= end_addr
enum { start_addr = 0xFF10 }; static const unsigned int start_addr = 0xFF10;
enum { end_addr = 0xFF3F }; static const unsigned int end_addr = 0xFF3F;
enum { register_count = end_addr - start_addr + 1 }; static const unsigned int register_count = end_addr - start_addr + 1;
// Write 'data' to address at specified time // Write 'data' to address at specified time
void write_register( blip_time_t, unsigned addr, int data ); void write_register( blip_time_t, unsigned addr, int data );
// Read from address at specified time // Read from address at specified time
int read_register( blip_time_t, unsigned addr ); int read_register( blip_time_t, unsigned addr );
// Run all oscillators up to specified time, end current time frame, then // Run all oscillators up to specified time, end current time frame, then
// start a new frame at time 0. // start a new frame at time 0.
void end_frame( blip_time_t ); void end_frame( blip_time_t );
void set_tempo( double ); void set_tempo( double );
public: public:
Gb_Apu(); Gb_Apu();
private: private:
// noncopyable // noncopyable
Gb_Apu( const Gb_Apu& ); Gb_Apu( const Gb_Apu& );
Gb_Apu& operator = ( const Gb_Apu& ); Gb_Apu& operator = ( const Gb_Apu& );
Gb_Osc* oscs [osc_count]; Gb_Osc* oscs [osc_count];
blip_time_t next_frame_time; blip_time_t next_frame_time;
blip_time_t last_time; blip_time_t last_time;
blip_time_t frame_period; blip_time_t frame_period;
double volume_unit; double volume_unit;
int frame_count; int frame_count;
Gb_Square square1; Gb_Square square1;
Gb_Square square2; Gb_Square square2;
Gb_Wave wave; Gb_Wave wave;
@ -71,14 +71,14 @@ private:
uint8_t regs [register_count]; uint8_t regs [register_count];
Gb_Square::Synth square_synth; // used by squares Gb_Square::Synth square_synth; // used by squares
Gb_Wave::Synth other_synth; // used by wave and noise Gb_Wave::Synth other_synth; // used by wave and noise
void update_volume(); void update_volume();
void run_until( blip_time_t ); void run_until( blip_time_t );
void write_osc( int index, int reg, int data ); 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::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::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); }
inline void Gb_Apu::volume( double vol ) 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 ) 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 ) void Gb_Cpu::reset( void* unmapped )
{ {
check( state == &state_ ); check( state == &state_ );
state = &state_; state = &state_;
state_.remain = 0; state_.remain = 0;
for ( int i = 0; i < page_count + 1; i++ ) for ( int i = 0; i < page_count + 1; i++ )
set_code_page( i, (uint8_t*) unmapped ); set_code_page( i, (uint8_t*) unmapped );
memset( &r, 0, sizeof r ); memset( &r, 0, sizeof r );
//interrupts_enabled = false; //interrupts_enabled = false;
blargg_verify_byte_order(); 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 // address range must begin and end on page boundaries
require( start % page_size == 0 ); require( start % page_size == 0 );
require( size % page_size == 0 ); require( size % page_size == 0 );
unsigned first_page = start / page_size; unsigned first_page = start / page_size;
for ( unsigned i = size / page_size; i--; ) for ( unsigned i = size / page_size; i--; )
set_code_page( first_page + i, (uint8_t*) data + i * page_size ); 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_FAST( addr, out ) CPU_READ_FAST( this, (addr), s.remain, out )
#define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) #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; state_t s;
this->state = &s; this->state = &s;
memcpy( &s, &this->state_, sizeof s ); memcpy( &s, &this->state_, sizeof s );
#if BLARGG_BIG_ENDIAN #if BLARGG_BIG_ENDIAN
#define R8( n ) (r8_ [n]) #define R8( n ) (r8_ [n])
#elif BLARGG_LITTLE_ENDIAN #elif BLARGG_LITTLE_ENDIAN
#define R8( n ) (r8_ [(n) ^ 1]) #define R8( n ) (r8_ [(n) ^ 1])
#else #else
#error "Byte order of CPU must be known" #error "Byte order of CPU must be known"
#endif #endif
union { union {
core_regs_t rg; // individual registers core_regs_t rg; // individual registers
struct { struct {
uint16_t bc, de, hl, unused; // pairs uint16_t bc, de, hl, unused; // pairs
} rp; } rp;
uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence)
uint16_t r16 [4]; // indexed pairs uint16_t r16 [4]; // indexed pairs
}; };
static_assert( sizeof rg == 8 && sizeof rp == 8, "Unhandled word size" ); static_assert( sizeof rg == 8 && sizeof rp == 8, "Unhandled word size" );
rg = r; rg = r;
unsigned pc = r.pc; unsigned pc = r.pc;
unsigned sp = r.sp; unsigned sp = r.sp;
unsigned flags = r.flags; unsigned flags = r.flags;
loop: loop:
check( (unsigned long) pc < 0x10000 ); check( (unsigned long) pc < 0x10000 );
check( (unsigned long) sp < 0x10000 ); check( (unsigned long) sp < 0x10000 );
check( (flags & ~0xF0) == 0 ); check( (flags & ~0xF0) == 0 );
uint8_t const* instr = s.code_map [pc >> page_shift]; uint8_t const* instr = s.code_map [pc >> page_shift];
unsigned op; unsigned op;
// TODO: eliminate this special case // TODO: eliminate this special case
#if BLARGG_NONPORTABLE #if BLARGG_NONPORTABLE
op = instr [pc]; op = instr [pc];
@ -140,19 +141,19 @@ loop:
op = *instr++; op = *instr++;
pc++; pc++;
#endif #endif
#define GET_ADDR() GET_LE16( instr ) #define GET_ADDR() GET_LE16( instr )
if ( !--s.remain ) if ( !--s.remain )
goto stop; goto stop;
unsigned data; unsigned data;
data = *instr; data = *instr;
#ifdef GB_CPU_LOG_H #ifdef GB_CPU_LOG_H
gb_cpu_log( "new", pc - 1, op, data, instr [1] ); gb_cpu_log( "new", pc - 1, op, data, instr [1] );
#endif #endif
switch ( op ) switch ( op )
{ {
@ -170,44 +171,44 @@ loop:
case 0x20: // JR NZ case 0x20: // JR NZ
BRANCH( !(flags & z_flag) ) BRANCH( !(flags & z_flag) )
case 0x21: // LD HL,IMM (common) case 0x21: // LD HL,IMM (common)
rp.hl = GET_ADDR(); rp.hl = GET_ADDR();
pc += 2; pc += 2;
goto loop; goto loop;
case 0x28: // JR Z case 0x28: // JR Z
BRANCH( flags & z_flag ) BRANCH( flags & z_flag )
{ {
unsigned temp; unsigned temp;
case 0xF0: // LD A,(0xFF00+imm) case 0xF0: // LD A,(0xFF00+imm)
temp = data | 0xFF00; temp = data | 0xFF00;
pc++; pc++;
goto ld_a_ind_comm; goto ld_a_ind_comm;
case 0xF2: // LD A,(0xFF00+C) case 0xF2: // LD A,(0xFF00+C)
temp = rg.c | 0xFF00; temp = rg.c | 0xFF00;
goto ld_a_ind_comm; goto ld_a_ind_comm;
case 0x0A: // LD A,(BC) case 0x0A: // LD A,(BC)
temp = rp.bc; temp = rp.bc;
goto ld_a_ind_comm; goto ld_a_ind_comm;
case 0x3A: // LD A,(HL-) case 0x3A: // LD A,(HL-)
temp = rp.hl; temp = rp.hl;
rp.hl = temp - 1; rp.hl = temp - 1;
goto ld_a_ind_comm; goto ld_a_ind_comm;
case 0x1A: // LD A,(DE) case 0x1A: // LD A,(DE)
temp = rp.de; temp = rp.de;
goto ld_a_ind_comm; goto ld_a_ind_comm;
case 0x2A: // LD A,(HL+) (common) case 0x2A: // LD A,(HL+) (common)
temp = rp.hl; temp = rp.hl;
rp.hl = temp + 1; rp.hl = temp + 1;
goto ld_a_ind_comm; goto ld_a_ind_comm;
case 0xFA: // LD A,IND16 (common) case 0xFA: // LD A,IND16 (common)
temp = GET_ADDR(); temp = GET_ADDR();
pc += 2; pc += 2;
@ -215,11 +216,11 @@ loop:
READ_FAST( temp, rg.a ); READ_FAST( temp, rg.a );
goto loop; goto loop;
} }
case 0xBE: // CMP (HL) case 0xBE: // CMP (HL)
data = READ( rp.hl ); data = READ( rp.hl );
goto cmp_comm; goto cmp_comm;
case 0xB8: // CMP B case 0xB8: // CMP B
case 0xB9: // CMP C case 0xB9: // CMP C
case 0xBA: // CMP D case 0xBA: // CMP D
@ -228,7 +229,7 @@ loop:
case 0xBD: // CMP L case 0xBD: // CMP L
data = R8( op & 7 ); data = R8( op & 7 );
goto cmp_comm; goto cmp_comm;
case 0xFE: // CMP IMM case 0xFE: // CMP IMM
pc++; pc++;
cmp_comm: cmp_comm:
@ -254,7 +255,7 @@ loop:
READ_FAST( addr, R8( (op >> 3) & 7 ) ); READ_FAST( addr, R8( (op >> 3) & 7 ) );
goto loop; goto loop;
} }
case 0xC4: // CNZ (next-most-common) case 0xC4: // CNZ (next-most-common)
pc += 2; pc += 2;
if ( flags & z_flag ) if ( flags & z_flag )
@ -270,7 +271,7 @@ loop:
sp = (sp - 1) & 0xFFFF; sp = (sp - 1) & 0xFFFF;
WRITE( sp, data & 0xFF ); WRITE( sp, data & 0xFF );
goto loop; goto loop;
case 0xC8: // RNZ (next-most-common) case 0xC8: // RNZ (next-most-common)
if ( !(flags & z_flag) ) if ( !(flags & z_flag) )
goto loop; goto loop;
@ -281,7 +282,7 @@ loop:
pc += 0x100 * READ( sp + 1 ); pc += 0x100 * READ( sp + 1 );
sp = (sp + 2) & 0xFFFF; sp = (sp + 2) & 0xFFFF;
goto loop; goto loop;
case 0x00: // NOP case 0x00: // NOP
case 0x40: // LD B,B case 0x40: // LD B,B
case 0x49: // LD C,C case 0x49: // LD C,C
@ -291,14 +292,14 @@ loop:
case 0x6D: // LD L,L case 0x6D: // LD L,L
case 0x7F: // LD A,A case 0x7F: // LD A,A
goto loop; goto loop;
// CB Instructions // CB Instructions
case 0xCB: case 0xCB:
pc++; pc++;
// now data is the opcode // now data is the opcode
switch ( data ) { switch ( data ) {
{ {
int temp; int temp;
case 0x46: // BIT b,(HL) case 0x46: // BIT b,(HL)
@ -314,7 +315,7 @@ loop:
READ_FAST( addr, temp ); READ_FAST( addr, temp );
goto bit_comm; goto bit_comm;
} }
case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r
case 0x44: case 0x45: case 0x47: case 0x48: case 0x44: case 0x45: case 0x47: case 0x48:
case 0x49: case 0x4A: case 0x4B: case 0x4C: case 0x49: case 0x4A: case 0x4B: case 0x4C:
@ -337,7 +338,7 @@ loop:
flags ^= (temp << bit) & z_flag; flags ^= (temp << bit) & z_flag;
goto loop; goto loop;
} }
case 0x86: // RES b,(HL) case 0x86: // RES b,(HL)
case 0x8E: case 0x8E:
case 0x96: case 0x96:
@ -362,7 +363,7 @@ loop:
WRITE( rp.hl, temp | bit ); WRITE( rp.hl, temp | bit );
goto loop; goto loop;
} }
case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r
case 0xC4: case 0xC5: case 0xC7: case 0xC8: case 0xC4: case 0xC5: case 0xC7: case 0xC8:
case 0xC9: case 0xCA: case 0xCB: case 0xCC: case 0xC9: case 0xCA: case 0xCB: case 0xCC:
@ -396,13 +397,13 @@ loop:
case 0xBB: case 0xBC: case 0xBD: case 0xBF: case 0xBB: case 0xBC: case 0xBD: case 0xBF:
R8( data & 7 ) &= ~(1 << ((data >> 3) & 7)); R8( data & 7 ) &= ~(1 << ((data >> 3) & 7));
goto loop; goto loop;
{ {
int temp; int temp;
case 0x36: // SWAP (HL) case 0x36: // SWAP (HL)
temp = READ( rp.hl ); temp = READ( rp.hl );
goto swap_comm; goto swap_comm;
case 0x30: // SWAP B case 0x30: // SWAP B
case 0x31: // SWAP C case 0x31: // SWAP C
case 0x32: // SWAP D case 0x32: // SWAP D
@ -416,7 +417,7 @@ loop:
flags = 0; flags = 0;
goto shift_comm; goto shift_comm;
} }
// Shift/Rotate // Shift/Rotate
case 0x06: // RLC (HL) case 0x06: // RLC (HL)
@ -424,13 +425,13 @@ loop:
case 0x26: // SLA (HL) case 0x26: // SLA (HL)
op = READ( rp.hl ); op = READ( rp.hl );
goto rl_comm; goto rl_comm;
case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A 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 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 case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A
op = R8( data & 7 ); op = R8( data & 7 );
goto rl_comm; goto rl_comm;
case 0x3E: // SRL (HL) case 0x3E: // SRL (HL)
data += 0x10; /* fallthrough */ // bump up to 0x4n to avoid preserving sign bit data += 0x10; /* fallthrough */ // bump up to 0x4n to avoid preserving sign bit
case 0x1E: // RR (HL) case 0x1E: // RR (HL)
@ -438,7 +439,7 @@ loop:
case 0x2E: // SRA (HL) case 0x2E: // SRA (HL)
op = READ( rp.hl ); op = READ( rp.hl );
goto rr_comm; goto rr_comm;
case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A
data += 0x10; /* fallthrough */ // bump up to 0x4n data += 0x10; /* fallthrough */ // bump up to 0x4n
case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A 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 case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A
op = R8( data & 7 ); op = R8( data & 7 );
goto rr_comm; goto rr_comm;
} // CB op } // CB op
assert( false ); // unhandled CB op assert( false ); // unhandled CB op
// fallthrough // fallthrough
@ -463,7 +464,7 @@ loop:
op |= op >> 8; op |= op >> 8;
// SLA doesn't fill lower bit // SLA doesn't fill lower bit
goto shift_comm; goto shift_comm;
case 0x0F: // RRCA case 0x0F: // RRCA
case 0x1F: // RRA case 0x1F: // RRA
data = op; data = op;
@ -516,7 +517,7 @@ loop:
data++; data++;
WRITE( data, sp >> 8 ); WRITE( data, sp >> 8 );
goto loop; goto loop;
case 0xF9: // LD SP,HL case 0xF9: // LD SP,HL
sp = rp.hl; sp = rp.hl;
goto loop; goto loop;
@ -525,20 +526,20 @@ loop:
sp = GET_ADDR(); sp = GET_ADDR();
pc += 2; pc += 2;
goto loop; goto loop;
case 0x01: // LD BC,IMM case 0x01: // LD BC,IMM
case 0x11: // LD DE,IMM case 0x11: // LD DE,IMM
r16 [op >> 4] = GET_ADDR(); r16 [op >> 4] = GET_ADDR();
pc += 2; pc += 2;
goto loop; goto loop;
{ {
unsigned temp; unsigned temp;
case 0xE0: // LD (0xFF00+imm),A case 0xE0: // LD (0xFF00+imm),A
temp = data | 0xFF00; temp = data | 0xFF00;
pc++; pc++;
goto write_data_rg_a; goto write_data_rg_a;
case 0xE2: // LD (0xFF00+C),A case 0xE2: // LD (0xFF00+C),A
temp = rg.c | 0xFF00; temp = rg.c | 0xFF00;
goto write_data_rg_a; goto write_data_rg_a;
@ -547,20 +548,20 @@ loop:
temp = rp.hl; temp = rp.hl;
rp.hl = temp - 1; rp.hl = temp - 1;
goto write_data_rg_a; goto write_data_rg_a;
case 0x02: // LD (BC),A case 0x02: // LD (BC),A
temp = rp.bc; temp = rp.bc;
goto write_data_rg_a; goto write_data_rg_a;
case 0x12: // LD (DE),A case 0x12: // LD (DE),A
temp = rp.de; temp = rp.de;
goto write_data_rg_a; goto write_data_rg_a;
case 0x22: // LD (HL+),A case 0x22: // LD (HL+),A
temp = rp.hl; temp = rp.hl;
rp.hl = temp + 1; rp.hl = temp + 1;
goto write_data_rg_a; goto write_data_rg_a;
case 0xEA: // LD IND16,A (common) case 0xEA: // LD IND16,A (common)
temp = GET_ADDR(); temp = GET_ADDR();
pc += 2; pc += 2;
@ -568,42 +569,42 @@ loop:
WRITE( temp, rg.a ); WRITE( temp, rg.a );
goto loop; goto loop;
} }
case 0x06: // LD B,IMM case 0x06: // LD B,IMM
rg.b = data; rg.b = data;
pc++; pc++;
goto loop; goto loop;
case 0x0E: // LD C,IMM case 0x0E: // LD C,IMM
rg.c = data; rg.c = data;
pc++; pc++;
goto loop; goto loop;
case 0x16: // LD D,IMM case 0x16: // LD D,IMM
rg.d = data; rg.d = data;
pc++; pc++;
goto loop; goto loop;
case 0x1E: // LD E,IMM case 0x1E: // LD E,IMM
rg.e = data; rg.e = data;
pc++; pc++;
goto loop; goto loop;
case 0x26: // LD H,IMM case 0x26: // LD H,IMM
rg.h = data; rg.h = data;
pc++; pc++;
goto loop; goto loop;
case 0x2E: // LD L,IMM case 0x2E: // LD L,IMM
rg.l = data; rg.l = data;
pc++; pc++;
goto loop; goto loop;
case 0x36: // LD (HL),IMM case 0x36: // LD (HL),IMM
WRITE( rp.hl, data ); WRITE( rp.hl, data );
pc++; pc++;
goto loop; goto loop;
case 0x3E: // LD A,IMM case 0x3E: // LD A,IMM
rg.a = data; rg.a = data;
pc++; pc++;
@ -616,7 +617,7 @@ loop:
case 0x23: // INC HL case 0x23: // INC HL
r16 [op >> 4]++; r16 [op >> 4]++;
goto loop; goto loop;
case 0x33: // INC SP case 0x33: // INC SP
sp = (sp + 1) & 0xFFFF; sp = (sp + 1) & 0xFFFF;
goto loop; goto loop;
@ -626,18 +627,18 @@ loop:
case 0x2B: // DEC HL case 0x2B: // DEC HL
r16 [op >> 4]--; r16 [op >> 4]--;
goto loop; goto loop;
case 0x3B: // DEC SP case 0x3B: // DEC SP
sp = (sp - 1) & 0xFFFF; sp = (sp - 1) & 0xFFFF;
goto loop; goto loop;
case 0x34: // INC (HL) case 0x34: // INC (HL)
op = rp.hl; op = rp.hl;
data = READ( op ); data = READ( op );
data++; data++;
WRITE( op, data & 0xFF ); WRITE( op, data & 0xFF );
goto inc_comm; goto inc_comm;
case 0x04: // INC B case 0x04: // INC B
case 0x0C: // INC C (common) case 0x0C: // INC C (common)
case 0x14: // INC D case 0x14: // INC D
@ -650,14 +651,14 @@ loop:
inc_comm: inc_comm:
flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag); flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag);
goto loop; goto loop;
case 0x35: // DEC (HL) case 0x35: // DEC (HL)
op = rp.hl; op = rp.hl;
data = READ( op ); data = READ( op );
data--; data--;
WRITE( op, data & 0xFF ); WRITE( op, data & 0xFF );
goto dec_comm; goto dec_comm;
case 0x05: // DEC B case 0x05: // DEC B
case 0x0D: // DEC C case 0x0D: // DEC C
case 0x15: // DEC D case 0x15: // DEC D
@ -678,9 +679,9 @@ loop:
// Add 16-bit // 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; unsigned prev;
case 0xF8: // LD HL,SP+imm case 0xF8: // LD HL,SP+imm
temp = int8_t (data); // sign-extend to 16 bits temp = int8_t (data); // sign-extend to 16 bits
pc++; pc++;
@ -688,7 +689,7 @@ loop:
temp += sp; temp += sp;
prev = sp; prev = sp;
goto add_16_hl; goto add_16_hl;
case 0xE8: // ADD SP,IMM case 0xE8: // ADD SP,IMM
temp = int8_t (data); // sign-extend to 16 bits temp = int8_t (data); // sign-extend to 16 bits
pc++; pc++;
@ -701,7 +702,7 @@ loop:
case 0x39: // ADD HL,SP case 0x39: // ADD HL,SP
temp = sp; temp = sp;
goto add_hl_comm; goto add_hl_comm;
case 0x09: // ADD HL,BC case 0x09: // ADD HL,BC
case 0x19: // ADD HL,DE case 0x19: // ADD HL,DE
case 0x29: // ADD HL,HL case 0x29: // ADD HL,HL
@ -717,11 +718,11 @@ loop:
flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag;
goto loop; goto loop;
} }
case 0x86: // ADD (HL) case 0x86: // ADD (HL)
data = READ( rp.hl ); data = READ( rp.hl );
goto add_comm; goto add_comm;
case 0x80: // ADD B case 0x80: // ADD B
case 0x81: // ADD C case 0x81: // ADD C
case 0x82: // ADD D case 0x82: // ADD D
@ -731,7 +732,7 @@ loop:
case 0x87: // ADD A case 0x87: // ADD A
data = R8( op & 7 ); data = R8( op & 7 );
goto add_comm; goto add_comm;
case 0xC6: // ADD IMM case 0xC6: // ADD IMM
pc++; pc++;
add_comm: add_comm:
@ -750,7 +751,7 @@ loop:
case 0x8E: // ADC (HL) case 0x8E: // ADC (HL)
data = READ( rp.hl ); data = READ( rp.hl );
goto adc_comm; goto adc_comm;
case 0x88: // ADC B case 0x88: // ADC B
case 0x89: // ADC C case 0x89: // ADC C
case 0x8A: // ADC D case 0x8A: // ADC D
@ -760,7 +761,7 @@ loop:
case 0x8F: // ADC A case 0x8F: // ADC A
data = R8( op & 7 ); data = R8( op & 7 );
goto adc_comm; goto adc_comm;
case 0xCE: // ADC IMM case 0xCE: // ADC IMM
pc++; pc++;
adc_comm: adc_comm:
@ -771,7 +772,7 @@ loop:
case 0x96: // SUB (HL) case 0x96: // SUB (HL)
data = READ( rp.hl ); data = READ( rp.hl );
goto sub_comm; goto sub_comm;
case 0x90: // SUB B case 0x90: // SUB B
case 0x91: // SUB C case 0x91: // SUB C
case 0x92: // SUB D case 0x92: // SUB D
@ -781,7 +782,7 @@ loop:
case 0x97: // SUB A case 0x97: // SUB A
data = R8( op & 7 ); data = R8( op & 7 );
goto sub_comm; goto sub_comm;
case 0xD6: // SUB IMM case 0xD6: // SUB IMM
pc++; pc++;
sub_comm: sub_comm:
@ -793,7 +794,7 @@ loop:
case 0x9E: // SBC (HL) case 0x9E: // SBC (HL)
data = READ( rp.hl ); data = READ( rp.hl );
goto sbc_comm; goto sbc_comm;
case 0x98: // SBC B case 0x98: // SBC B
case 0x99: // SBC C case 0x99: // SBC C
case 0x9A: // SBC D case 0x9A: // SBC D
@ -803,7 +804,7 @@ loop:
case 0x9F: // SBC A case 0x9F: // SBC A
data = R8( op & 7 ); data = R8( op & 7 );
goto sbc_comm; goto sbc_comm;
case 0xDE: // SBC IMM case 0xDE: // SBC IMM
pc++; pc++;
sbc_comm: sbc_comm:
@ -821,7 +822,7 @@ loop:
case 0xA5: // AND L case 0xA5: // AND L
data = R8( op & 7 ); data = R8( op & 7 );
goto and_comm; goto and_comm;
case 0xA6: // AND (HL) case 0xA6: // AND (HL)
data = READ( rp.hl ); data = READ( rp.hl );
pc--; // FALLTHRU pc--; // FALLTHRU
@ -841,7 +842,7 @@ loop:
case 0xB5: // OR L case 0xB5: // OR L
data = R8( op & 7 ); data = R8( op & 7 );
goto or_comm; goto or_comm;
case 0xB6: // OR (HL) case 0xB6: // OR (HL)
data = READ( rp.hl ); data = READ( rp.hl );
pc--; // FALLTHRU pc--; // FALLTHRU
@ -861,7 +862,7 @@ loop:
case 0xAD: // XOR L case 0xAD: // XOR L
data = R8( op & 7 ); data = R8( op & 7 );
goto xor_comm; goto xor_comm;
case 0xAE: // XOR (HL) case 0xAE: // XOR (HL)
data = READ( rp.hl ); data = READ( rp.hl );
pc--; // FALLTHRU pc--; // FALLTHRU
@ -873,7 +874,7 @@ loop:
data--; data--;
flags = (data >> 1) & z_flag; flags = (data >> 1) & z_flag;
goto loop; goto loop;
case 0xAF: // XOR A case 0xAF: // XOR A
rg.a = 0; rg.a = 0;
flags = z_flag; flags = z_flag;
@ -893,25 +894,25 @@ loop:
flags = rg.a & 0xF0; flags = rg.a & 0xF0;
rg.a = rg.flags; rg.a = rg.flags;
goto loop; goto loop;
case 0xC5: // PUSH BC case 0xC5: // PUSH BC
data = rp.bc; data = rp.bc;
goto push; goto push;
case 0xD5: // PUSH DE case 0xD5: // PUSH DE
data = rp.de; data = rp.de;
goto push; goto push;
case 0xE5: // PUSH HL case 0xE5: // PUSH HL
data = rp.hl; data = rp.hl;
goto push; goto push;
case 0xF5: // PUSH AF case 0xF5: // PUSH AF
data = (rg.a << 8) | flags; data = (rg.a << 8) | flags;
goto push; goto push;
// Flow control // Flow control
case 0xFF: case 0xFF:
if ( pc == idle_addr + 1 ) if ( pc == idle_addr + 1 )
goto stop; goto stop;
@ -921,19 +922,19 @@ loop:
data = pc; data = pc;
pc = (op & 0x38) + rst_base; pc = (op & 0x38) + rst_base;
goto push; goto push;
case 0xCC: // CZ case 0xCC: // CZ
pc += 2; pc += 2;
if ( flags & z_flag ) if ( flags & z_flag )
goto call; goto call;
goto loop; goto loop;
case 0xD4: // CNC case 0xD4: // CNC
pc += 2; pc += 2;
if ( !(flags & c_flag) ) if ( !(flags & c_flag) )
goto call; goto call;
goto loop; goto loop;
case 0xDC: // CC case 0xDC: // CC
pc += 2; pc += 2;
if ( flags & c_flag ) if ( flags & c_flag )
@ -943,17 +944,17 @@ loop:
case 0xD9: // RETI case 0xD9: // RETI
//interrupts_enabled = 1; //interrupts_enabled = 1;
goto ret; goto ret;
case 0xC0: // RZ case 0xC0: // RZ
if ( !(flags & z_flag) ) if ( !(flags & z_flag) )
goto ret; goto ret;
goto loop; goto loop;
case 0xD0: // RNC case 0xD0: // RNC
if ( !(flags & c_flag) ) if ( !(flags & c_flag) )
goto ret; goto ret;
goto loop; goto loop;
case 0xD8: // RC case 0xD8: // RC
if ( flags & c_flag ) if ( flags & c_flag )
goto ret; goto ret;
@ -961,13 +962,13 @@ loop:
case 0x18: // JR case 0x18: // JR
BRANCH( true ) BRANCH( true )
case 0x30: // JR NC case 0x30: // JR NC
BRANCH( !(flags & c_flag) ) BRANCH( !(flags & c_flag) )
case 0x38: // JR C case 0x38: // JR C
BRANCH( flags & c_flag ) BRANCH( flags & c_flag )
case 0xE9: // JP_HL case 0xE9: // JP_HL
pc = rp.hl; pc = rp.hl;
goto loop; goto loop;
@ -975,13 +976,13 @@ loop:
case 0xC3: // JP (next-most-common) case 0xC3: // JP (next-most-common)
pc = GET_ADDR(); pc = GET_ADDR();
goto loop; goto loop;
case 0xC2: // JP NZ case 0xC2: // JP NZ
pc += 2; pc += 2;
if ( !(flags & z_flag) ) if ( !(flags & z_flag) )
goto jp_taken; goto jp_taken;
goto loop; goto loop;
case 0xCA: // JP Z (most common) case 0xCA: // JP Z (most common)
pc += 2; pc += 2;
if ( !(flags & z_flag) ) if ( !(flags & z_flag) )
@ -990,13 +991,13 @@ loop:
pc -= 2; pc -= 2;
pc = GET_ADDR(); pc = GET_ADDR();
goto loop; goto loop;
case 0xD2: // JP NC case 0xD2: // JP NC
pc += 2; pc += 2;
if ( !(flags & c_flag) ) if ( !(flags & c_flag) )
goto jp_taken; goto jp_taken;
goto loop; goto loop;
case 0xDA: // JP C case 0xDA: // JP C
pc += 2; pc += 2;
if ( flags & c_flag ) if ( flags & c_flag )
@ -1038,21 +1039,21 @@ loop:
s.remain++; s.remain++;
goto stop; goto stop;
} }
// If this fails then the case above is missing an opcode // If this fails then the case above is missing an opcode
assert( false ); assert( false );
stop: stop:
pc--; pc--;
// copy state back // copy state back
STATIC_CAST(core_regs_t&,r) = rg; STATIC_CAST(core_regs_t&,r) = rg;
r.pc = pc; r.pc = pc;
r.sp = sp; r.sp = sp;
r.flags = flags; r.flags = flags;
this->state = &state_; this->state = &state_;
memcpy( &this->state_, &s, sizeof this->state_ ); memcpy( &this->state_, &s, sizeof this->state_ );
return s.remain > 0; return s.remain > 0;
} }

View file

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

View file

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

View file

@ -11,18 +11,18 @@ struct Gb_Osc
{ {
enum { trigger = 0x80 }; enum { trigger = 0x80 };
enum { len_enabled_mask = 0x40 }; enum { len_enabled_mask = 0x40 };
Blip_Buffer* outputs [4]; // NULL, right, left, center Blip_Buffer* outputs [4]; // NULL, right, left, center
Blip_Buffer* output; Blip_Buffer* output;
int output_select; int output_select;
uint8_t* regs; // osc's 5 registers uint8_t* regs; // osc's 5 registers
int delay; int delay;
int last_amp; int last_amp;
int volume; int volume;
int length; int length;
int enabled; int enabled;
void reset(); void reset();
void clock_length(); void clock_length();
int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; }
@ -31,7 +31,7 @@ struct Gb_Osc
struct Gb_Env : Gb_Osc struct Gb_Env : Gb_Osc
{ {
int env_delay; int env_delay;
void reset(); void reset();
void clock_envelope(); void clock_envelope();
bool write_register( int, int ); bool write_register( int, int );
@ -41,13 +41,13 @@ struct Gb_Square : Gb_Env
{ {
enum { period_mask = 0x70 }; enum { period_mask = 0x70 };
enum { shift_mask = 0x07 }; enum { shift_mask = 0x07 };
typedef Blip_Synth<blip_good_quality,1> Synth; typedef Blip_Synth<blip_good_quality,1> Synth;
Synth const* synth; Synth const* synth;
int sweep_delay; int sweep_delay;
int sweep_freq; int sweep_freq;
int phase; int phase;
void reset(); void reset();
void clock_sweep(); void clock_sweep();
void run( blip_time_t, blip_time_t, int playing ); 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; typedef Blip_Synth<blip_med_quality,1> Synth;
Synth const* synth; Synth const* synth;
unsigned bits; unsigned bits;
void run( blip_time_t, blip_time_t, int playing ); void run( blip_time_t, blip_time_t, int playing );
}; };
@ -69,7 +69,7 @@ struct Gb_Wave : Gb_Osc
int wave_pos; int wave_pos;
enum { wave_size = 32 }; enum { wave_size = 32 };
uint8_t wave [wave_size]; uint8_t wave [wave_size];
void write_register( int, int ); void write_register( int, int );
void run( blip_time_t, blip_time_t, int playing ); 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() Gbs_Emu::Gbs_Emu()
{ {
set_type( gme_gbs_type ); set_type( gme_gbs_type );
static const char* const names [Gb_Apu::osc_count] = { static const char* const names [Gb_Apu::osc_count] = {
"Square 1", "Square 2", "Wave", "Noise" "Square 1", "Square 2", "Wave", "Noise"
}; };
set_voice_names( names ); set_voice_names( names );
static int const types [Gb_Apu::osc_count] = { static int const types [Gb_Apu::osc_count] = {
wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0 wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0
}; };
set_voice_types( types ); set_voice_types( types );
set_silence_lookahead( 6 ); set_silence_lookahead( 6 );
set_max_initial_silence( 21 ); set_max_initial_silence( 21 );
set_gain( 1.2 ); set_gain( 1.2 );
set_equalizer( make_equalizer( -1.0, 120 ) ); 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_ struct Gbs_File : Gme_Info_
{ {
Gbs_Emu::header_t h; Gbs_Emu::header_t h;
Gbs_File() { set_type( gme_gbs_type ); } Gbs_File() { set_type( gme_gbs_type ); }
blargg_err_t load_( Data_Reader& in ) blargg_err_t load_( Data_Reader& in )
{ {
blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); blargg_err_t err = in.read( &h, Gbs_Emu::header_size );
if ( err ) if ( err )
return (err == in.eof_error ? gme_wrong_file_type : err); return (err == in.eof_error ? gme_wrong_file_type : err);
set_track_count( h.track_count ); set_track_count( h.track_count );
return check_gbs_header( &h ); return check_gbs_header( &h );
} }
blargg_err_t track_info_( track_info_t* out, int ) const blargg_err_t track_info_( track_info_t* out, int ) const
{ {
copy_gbs_fields( h, out ); 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 ) 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 ) ); RETURN_ERR( rom.load( in, header_size, &header_, 0 ) );
set_track_count( header_.track_count ); set_track_count( header_.track_count );
RETURN_ERR( check_gbs_header( &header_ ) ); RETURN_ERR( check_gbs_header( &header_ ) );
if ( header_.vers != 1 ) if ( header_.vers != 1 )
set_warning( "Unknown file version" ); set_warning( "Unknown file version" );
if ( header_.timer_mode & 0x78 ) if ( header_.timer_mode & 0x78 )
set_warning( "Invalid timer mode" ); set_warning( "Invalid timer mode" );
unsigned load_addr = get_le16( header_.load_addr ); unsigned load_addr = get_le16( header_.load_addr );
if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F ||
load_addr < 0x400 ) load_addr < 0x400 )
set_warning( "Invalid load/init/play address" ); set_warning( "Invalid load/init/play address" );
set_voice_count( Gb_Apu::osc_count ); set_voice_count( Gb_Apu::osc_count );
apu.volume( gain() ); apu.volume( gain() );
return setup_buffer( 4194304 ); return setup_buffer( 4194304 );
} }
@ -153,8 +153,8 @@ void Gbs_Emu::set_bank( int n )
{ {
n = 1; n = 1;
} }
blargg_long addr = n * (blargg_long) bank_size; int32_t addr = n * (int32_t) bank_size;
if (addr > rom.size()) if (addr > rom.size())
{ {
return; return;
@ -206,37 +206,37 @@ void Gbs_Emu::set_tempo_( double t )
blargg_err_t Gbs_Emu::start_track_( int track ) blargg_err_t Gbs_Emu::start_track_( int track )
{ {
RETURN_ERR( Classic_Emu::start_track_( track ) ); RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( ram, 0, 0x4000 ); memset( ram, 0, 0x4000 );
memset( ram + 0x4000, 0xFF, 0x1F00 ); memset( ram + 0x4000, 0xFF, 0x1F00 );
memset( ram + 0x5F00, 0, sizeof ram - 0x5F00 ); memset( ram + 0x5F00, 0, sizeof ram - 0x5F00 );
ram [hi_page] = 0; // joypad reads back as 0 ram [hi_page] = 0; // joypad reads back as 0
apu.reset(); apu.reset();
for ( int i = 0; i < (int) sizeof sound_data; i++ ) for ( int i = 0; i < (int) sizeof sound_data; i++ )
apu.write_register( 0, i + apu.start_addr, sound_data [i] ); apu.write_register( 0, i + apu.start_addr, sound_data [i] );
unsigned load_addr = get_le16( header_.load_addr ); unsigned load_addr = get_le16( header_.load_addr );
rom.set_addr( load_addr ); rom.set_addr( load_addr );
cpu::rst_base = load_addr; cpu::rst_base = load_addr;
cpu::reset( rom.unmapped() ); cpu::reset( rom.unmapped() );
cpu::map_code( ram_addr, 0x10000 - ram_addr, ram ); cpu::map_code( ram_addr, 0x10000 - ram_addr, ram );
cpu::map_code( 0, bank_size, rom.at_addr( 0 ) ); cpu::map_code( 0, bank_size, rom.at_addr( 0 ) );
set_bank( rom.size() > bank_size ); set_bank( rom.size() > bank_size );
ram [hi_page + 6] = header_.timer_modulo; ram [hi_page + 6] = header_.timer_modulo;
ram [hi_page + 7] = header_.timer_mode; ram [hi_page + 7] = header_.timer_mode;
update_timer(); update_timer();
next_play = play_period; next_play = play_period;
cpu::r.a = track; cpu::r.a = track;
cpu::r.pc = idle_addr; cpu::r.pc = idle_addr;
cpu::r.sp = get_le16( header_.stack_ptr ); cpu::r.sp = get_le16( header_.stack_ptr );
cpu_time = 0; cpu_time = 0;
cpu_jsr( get_le16( header_.init_addr ) ); cpu_jsr( get_le16( header_.init_addr ) );
return 0; return 0;
} }
@ -249,7 +249,7 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
cpu_time = duration; cpu_time = duration;
bool result = cpu::run( count ); bool result = cpu::run( count );
cpu_time -= cpu::remain(); cpu_time -= cpu::remain();
if ( result ) if ( result )
{ {
if ( cpu::r.pc == idle_addr ) 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; cpu_time = duration;
break; break;
} }
if ( cpu_time < next_play ) if ( cpu_time < next_play )
cpu_time = next_play; cpu_time = next_play;
next_play += play_period; next_play += play_period;
@ -282,12 +282,12 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int )
} }
} }
} }
duration = cpu_time; duration = cpu_time;
next_play -= cpu_time; next_play -= cpu_time;
if ( next_play < 0 ) // could go negative if routine is taking too long to return if ( next_play < 0 ) // could go negative if routine is taking too long to return
next_play = 0; next_play = 0;
apu.end_frame( cpu_time ); apu.end_frame( cpu_time );
return 0; return 0;
} }

View file

@ -14,7 +14,7 @@ public:
// Equalizer profiles for Game Boy Color speaker and headphones // Equalizer profiles for Game Boy Color speaker and headphones
static equalizer_t const handheld_eq; static equalizer_t const handheld_eq;
static equalizer_t const headphones_eq; static equalizer_t const headphones_eq;
// GBS file header // GBS file header
enum { header_size = 112 }; enum { header_size = 112 };
struct header_t struct header_t
@ -33,12 +33,12 @@ public:
char author [32]; char author [32];
char copyright [32]; char copyright [32];
}; };
// Header for currently loaded file // Header for currently loaded file
header_t const& header() const { return header_; } header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_gbs_type; } static gme_type_t static_type() { return gme_gbs_type; }
public: public:
// deprecated // deprecated
using Music_Emu::load; using Music_Emu::load;
@ -62,25 +62,25 @@ private:
enum { bank_size = 0x4000 }; enum { bank_size = 0x4000 };
Rom_Data<bank_size> rom; Rom_Data<bank_size> rom;
void set_bank( int ); void set_bank( int );
// timer // timer
blip_time_t cpu_time; blip_time_t cpu_time;
blip_time_t play_period; blip_time_t play_period;
blip_time_t next_play; blip_time_t next_play;
void update_timer(); void update_timer();
header_t header_; header_t header_;
void cpu_jsr( gb_addr_t ); void cpu_jsr( gb_addr_t );
public: private: friend class Gb_Cpu; public: private: friend class Gb_Cpu;
blip_time_t clock() const { return cpu_time - cpu::remain(); } blip_time_t clock() const { return cpu_time - cpu::remain(); }
enum { joypad_addr = 0xFF00 }; enum { joypad_addr = 0xFF00 };
enum { ram_addr = 0xA000 }; enum { ram_addr = 0xA000 };
enum { hi_page = 0xFF00 - ram_addr }; enum { hi_page = 0xFF00 - ram_addr };
byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding];
Gb_Apu apu; Gb_Apu apu;
int cpu_read( gb_addr_t ); int cpu_read( gb_addr_t );
void cpu_write( gb_addr_t, int ); 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( file_data.resize( in.remain() ) );
RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); 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() ); 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_(); post_load_();
else else
unload(); unload();
return err; 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 ) ); 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 ) blargg_err_t Gme_File::load( Data_Reader& in )
{ {
pre_load(); pre_load();
@ -116,31 +137,31 @@ void Gme_File::copy_field_( char* out, const char* in, int in_size )
{ {
if ( !in || !*in ) if ( !in || !*in )
return; return;
// remove spaces/junk from beginning // remove spaces/junk from beginning
while ( in_size && unsigned (*in - 1) <= ' ' - 1 ) while ( in_size && unsigned (*in - 1) <= ' ' - 1 )
{ {
in++; in++;
in_size--; in_size--;
} }
// truncate // truncate
if ( in_size > max_field_ ) if ( in_size > max_field_ )
in_size = max_field_; in_size = max_field_;
// find terminator // find terminator
int len = 0; int len = 0;
while ( len < in_size && in [len] ) while ( len < in_size && in [len] )
len++; len++;
// remove spaces/junk from end // remove spaces/junk from end
while ( len && unsigned (in [len - 1]) <= ' ' ) while ( len && unsigned (in [len - 1]) <= ' ' )
len--; len--;
// copy // copy
out [len] = 0; out [len] = 0;
memcpy( out, in, len ); memcpy( out, in, len );
// strip out stupid fields that should have been left blank // strip out stupid fields that should have been left blank
if ( !strcmp( out, "?" ) || !strcmp( out, "<?>" ) || !strcmp( out, "< ? >" ) ) if ( !strcmp( out, "?" ) || !strcmp( out, "<?>" ) || !strcmp( out, "< ? >" ) )
out [0] = 0; 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() ) if ( (unsigned) *track_io >= (unsigned) track_count() )
return "Invalid track"; return "Invalid track";
if ( (unsigned) *track_io < (unsigned) playlist.size() ) if ( (unsigned) *track_io < (unsigned) playlist.size() )
{ {
M3u_Playlist::entry_t const& e = playlist [*track_io]; 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->play_length = -1;
out->repeat_count = -1; out->repeat_count = -1;
out->song [0] = 0; out->song [0] = 0;
out->game [0] = 0; out->game [0] = 0;
out->author [0] = 0; out->author [0] = 0;
out->composer [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->disc [0] = 0;
out->track [0] = 0; out->track [0] = 0;
out->ost [0] = 0; out->ost [0] = 0;
copy_field_( out->system, type()->system ); copy_field_( out->system, type()->system );
int remapped = track; int remapped = track;
RETURN_ERR( remap_track_( &remapped ) ); RETURN_ERR( remap_track_( &remapped ) );
RETURN_ERR( track_info_( out, remapped ) ); RETURN_ERR( track_info_( out, remapped ) );
// override with m3u info // override with m3u info
if ( playlist.size() ) 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->dumper, i.ripping );
copy_field_( out->tagger, i.tagging ); copy_field_( out->tagger, i.tagging );
copy_field_( out->date, i.date ); copy_field_( out->date, i.date );
M3u_Playlist::entry_t const& e = playlist [track]; M3u_Playlist::entry_t const& e = playlist [track];
copy_field_( out->song, e.name ); copy_field_( out->song, e.name );
if ( e.length >= 0 ) out->length = e.length; 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 */ 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_emu)(); /* Create new emulator for this type (useful in C++ only) */
Music_Emu* (*new_info)(); /* Create new info reader for this type */ Music_Emu* (*new_info)(); /* Create new info reader for this type */
/* internal */ /* internal */
const char* extension_; const char* extension_;
int flags_; int flags_;
@ -27,18 +27,18 @@ struct gme_type_t_
struct track_info_t struct track_info_t
{ {
long track_count; long track_count;
/* times in milliseconds; -1 if unknown */ /* times in milliseconds; -1 if unknown */
long length; long length;
long intro_length; long intro_length;
long loop_length; long loop_length;
long fade_length; long fade_length;
long repeat_count; long repeat_count;
/* Length if available, otherwise intro_length+loop_length*2 if available, /* Length if available, otherwise intro_length+loop_length*2 if available,
* otherwise a default of 150000 (2.5 minutes) */ * otherwise a default of 150000 (2.5 minutes) */
long play_length; long play_length;
/* empty string if not available */ /* empty string if not available */
char system [256]; char system [256];
char game [256]; char game [256];
@ -61,60 +61,61 @@ enum { gme_max_field = 255 };
struct Gme_File { struct Gme_File {
public: public:
// File loading // File loading
// Each loads game music data from a file and returns an error if // 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 // file is wrong type or is seriously corrupt. They also set warning
// string for minor problems. // string for minor problems.
// Load from file on disk // Load from file on disk
blargg_err_t load_file( const char* path ); blargg_err_t load_file( const char* path );
// Load from custom data source (see Data_Reader.h) // Load from custom data source (see Data_Reader.h)
blargg_err_t load( Data_Reader& ); blargg_err_t load( Data_Reader& );
// Load from file already read into memory. Keeps pointer to data, so you // Load from file already read into memory. Keeps pointer to data, so you
// must not free it until you're done with the file. // must not free it until you're done with the file.
blargg_err_t load_mem( void const* data, long size ); 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. // 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( const char* path );
blargg_err_t load_m3u( Data_Reader& in ); blargg_err_t load_m3u( Data_Reader& in );
// Clears any loaded m3u playlist and any internal playlist that the music // Clears any loaded m3u playlist and any internal playlist that the music
// format supports (NSFE for example). // format supports (NSFE for example).
void clear_playlist(); void clear_playlist();
// Informational // Informational
// Type of emulator. For example if this returns gme_nsfe_type, this object // 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. // is an NSFE emulator, and you can cast to an Nsfe_Emu* if necessary.
gme_type_t type() const; gme_type_t type() const;
// Most recent warning string, or NULL if none. Clears current warning after // Most recent warning string, or NULL if none. Clears current warning after
// returning. // returning.
const char* warning(); const char* warning();
// Number of tracks or 0 if no file has been loaded // Number of tracks or 0 if no file has been loaded
int track_count() const; int track_count() const;
// Get information for a track (length, name, author, etc.) // Get information for a track (length, name, author, etc.)
// See gme.h for definition of struct track_info_t. // See gme.h for definition of struct track_info_t.
blargg_err_t track_info( track_info_t* out, int track ) const; blargg_err_t track_info( track_info_t* out, int track ) const;
// User data/cleanup // User data/cleanup
// Set/get pointer to data you want to associate with this emulator. // Set/get pointer to data you want to associate with this emulator.
// You can use this for whatever you want. // You can use this for whatever you want.
void set_user_data( void* p ) { user_data_ = p; } void set_user_data( void* p ) { user_data_ = p; }
void* user_data() const { return user_data_; } void* user_data() const { return user_data_; }
// Register cleanup function to be called when deleting emulator, or NULL to // Register cleanup function to be called when deleting emulator, or NULL to
// clear it. Passes user_data to cleanup function. // clear it. Passes user_data to cleanup function.
void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } 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: public:
// deprecated // deprecated
int error_count() const; // use warning() int error_count() const; // use warning()
@ -129,7 +130,10 @@ protected:
void set_warning( const char* s ) { warning_ = s; } void set_warning( const char* s ) { warning_ = s; }
void set_type( gme_type_t t ) { type_ = t; } void set_type( gme_type_t t ) { type_ = t; }
blargg_err_t load_remaining_( void const* header, long header_size, Data_Reader& remaining ); 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 // Overridable
virtual void unload(); // called before loading file and if loading fails virtual void unload(); // called before loading file and if loading fails
virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_()
@ -138,14 +142,14 @@ protected:
virtual void pre_load(); virtual void pre_load();
virtual void post_load_(); virtual void post_load_();
virtual void clear_playlist_() { } virtual void clear_playlist_() { }
public: public:
blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu
private: private:
// noncopyable // noncopyable
Gme_File( const Gme_File& ); Gme_File( const Gme_File& );
Gme_File& operator = ( const Gme_File& ); Gme_File& operator = ( const Gme_File& );
gme_type_t type_; gme_type_t type_;
int track_count_; int track_count_;
int raw_track_count_; int raw_track_count_;
@ -155,7 +159,8 @@ private:
M3u_Playlist playlist; M3u_Playlist playlist;
char playlist_warning [64]; char playlist_warning [64];
blargg_vector<byte> file_data; // only if loaded into memory using default load 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 load_m3u_( blargg_err_t );
blargg_err_t post_load( blargg_err_t err ); blargg_err_t post_load( blargg_err_t err );
public: public:

View file

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

View file

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

View file

@ -17,7 +17,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h" #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() Hes_Apu::Hes_Apu()
{ {
@ -32,7 +32,7 @@ Hes_Apu::Hes_Apu()
osc->chans [2] = 0; osc->chans [2] = 0;
} }
while ( osc != oscs ); while ( osc != oscs );
reset(); reset();
} }
@ -40,7 +40,7 @@ void Hes_Apu::reset()
{ {
latch = 0; latch = 0;
balance = 0xFF; balance = 0xFF;
Hes_Osc* osc = &oscs [osc_count]; Hes_Osc* osc = &oscs [osc_count];
do 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 [0] = center;
oscs [index].chans [1] = left; oscs [index].chans [1] = left;
oscs [index].chans [2] = right; oscs [index].chans [2] = right;
Hes_Osc* osc = &oscs [osc_count]; Hes_Osc* osc = &oscs [osc_count];
do do
{ {
@ -75,7 +75,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
if ( osc_outputs_0 && control & 0x80 ) if ( osc_outputs_0 && control & 0x80 )
{ {
int dac = this->dac; int dac = this->dac;
int const volume_0 = volume [0]; int const volume_0 = volume [0];
{ {
int delta = dac * volume_0 - last_amp [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 ); synth_.offset( last_time, delta, osc_outputs_0 );
osc_outputs_0->set_modified(); osc_outputs_0->set_modified();
} }
Blip_Buffer* const osc_outputs_1 = outputs [1]; Blip_Buffer* const osc_outputs_1 = outputs [1];
int const volume_1 = volume [1]; int const volume_1 = volume [1];
if ( osc_outputs_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 ); synth_.offset( last_time, delta, osc_outputs_1 );
osc_outputs_1->set_modified(); osc_outputs_1->set_modified();
} }
blip_time_t time = last_time + delay; blip_time_t time = last_time + delay;
if ( time < end_time ) 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; unsigned noise_lfsr = this->noise_lfsr;
do do
{ {
int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); int new_dac = 0x1F & uMinus(noise_lfsr >> 1 & 1);
// Implemented using "Galios configuration" // Implemented using "Galios configuration"
// TODO: find correct LFSR algorithm // 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)); //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1));
int delta = new_dac - dac; int delta = new_dac - dac;
if ( delta ) if ( delta )
@ -122,7 +122,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
time += period; time += period;
} }
while ( time < end_time ); while ( time < end_time );
this->noise_lfsr = noise_lfsr; this->noise_lfsr = noise_lfsr;
assert( 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) ) //if ( !(volume_0 | volume_1) )
// debug_printf( "Used period 0\n" ); // debug_printf( "Used period 0\n" );
} }
// maintain phase when silent // 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 phase += count; // phase will be masked below
time += count * period; time += count * period;
} }
@ -173,7 +172,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time )
if ( time < 0 ) if ( time < 0 )
time = 0; time = 0;
delay = time; delay = time;
this->dac = dac; this->dac = dac;
last_amp [0] = dac * volume_0; last_amp [0] = dac * volume_0;
last_amp [1] = dac * volume_1; 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 ), ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ),
#undef ENTRY #undef ENTRY
}; };
int vol = (osc.control & 0x1F) - 0x1E * 2; int vol = (osc.control & 0x1F) - 0x1E * 2;
int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E); int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E);
if ( left < 0 ) left = 0; if ( left < 0 ) left = 0;
int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E); int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E);
if ( right < 0 ) right = 0; if ( right < 0 ) right = 0;
left = log_table [left ]; left = log_table [left ];
right = log_table [right]; right = log_table [right];
// optimizing for the common case of being centered also allows easy // optimizing for the common case of being centered also allows easy
// panning using Effects_Buffer // panning using Effects_Buffer
osc.outputs [0] = osc.chans [0]; // center 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 [0] = osc.chans [1]; // left
osc.outputs [1] = osc.chans [2]; // right osc.outputs [1] = osc.chans [2]; // right
} }
if ( center_waves ) if ( center_waves )
{ {
osc.last_amp [0] += (left - osc.volume [0]) * 16; osc.last_amp [0] += (left - osc.volume [0]) * 16;
osc.last_amp [1] += (right - osc.volume [1]) * 16; osc.last_amp [1] += (right - osc.volume [1]) * 16;
} }
osc.volume [0] = left; osc.volume [0] = left;
osc.volume [1] = right; osc.volume [1] = right;
} }
@ -238,7 +237,7 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data )
if ( balance != data ) if ( balance != data )
{ {
balance = data; balance = data;
Hes_Osc* osc = &oscs [osc_count]; Hes_Osc* osc = &oscs [osc_count];
do do
{ {
@ -258,23 +257,23 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data )
case 0x802: case 0x802:
osc.period = (osc.period & 0xF00) | data; osc.period = (osc.period & 0xF00) | data;
break; break;
case 0x803: case 0x803:
osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8); osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8);
break; break;
case 0x804: case 0x804:
if ( osc.control & 0x40 & ~data ) if ( osc.control & 0x40 & ~data )
osc.phase = 0; osc.phase = 0;
osc.control = data; osc.control = data;
balance_changed( osc ); balance_changed( osc );
break; break;
case 0x805: case 0x805:
osc.balance = data; osc.balance = data;
balance_changed( osc ); balance_changed( osc );
break; break;
case 0x806: case 0x806:
data &= 0x1F; data &= 0x1F;
if ( !(osc.control & 0x40) ) if ( !(osc.control & 0x40) )
@ -287,15 +286,15 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data )
osc.dac = data; osc.dac = data;
} }
break; break;
case 0x807: case 0x807:
if ( &osc >= &oscs [4] ) if ( &osc >= &oscs [4] )
osc.noise = data; osc.noise = data;
break; break;
case 0x809: case 0x809:
if ( !(data & 0x80) && (data & 0x03) != 0 ) if ( !(data & 0x80) && (data & 0x03) != 0 )
debug_printf( "HES LFO not supported\n" ); debug_printf( "HES LFO not supported\n" );
} }
} }
} }

View file

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

View file

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

View file

@ -6,23 +6,23 @@
#include "blargg_common.h" #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 typedef unsigned hes_addr_t; // 16-bit address
enum { future_hes_time = INT_MAX / 2 + 1 }; enum { future_hes_time = INT_MAX / 2 + 1 };
class Hes_Cpu { class Hes_Cpu {
public: public:
void reset(); void reset();
enum { page_size = 0x2000 }; enum { page_size = 0x2000 };
enum { page_shift = 13 }; enum { page_shift = 13 };
enum { page_count = 8 }; enum { page_count = 8 };
void set_mmr( int reg, int bank ); void set_mmr( int reg, int bank );
uint8_t const* get_code( hes_addr_t ); uint8_t const* get_code( hes_addr_t );
uint8_t ram [page_size]; uint8_t ram [page_size];
// not kept updated during a call to run() // not kept updated during a call to run()
struct registers_t { struct registers_t {
uint16_t pc; uint16_t pc;
@ -33,35 +33,35 @@ public:
uint8_t sp; uint8_t sp;
}; };
registers_t r; registers_t r;
// page mapping registers // page mapping registers
uint8_t mmr [page_count + 1]; uint8_t mmr [page_count + 1];
// Set end_time and run CPU from current time. Returns true if any illegal // Set end_time and run CPU from current time. Returns true if any illegal
// instructions were encountered. // instructions were encountered.
bool run( hes_time_t end_time ); bool run( hes_time_t end_time );
// Time of beginning of next instruction to be executed // Time of beginning of next instruction to be executed
hes_time_t time() const { return state->time + state->base; } hes_time_t time() const { return state->time + state->base; }
void set_time( hes_time_t t ) { state->time = t - state->base; } void set_time( hes_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; } void adjust_time( int delta ) { state->time += delta; }
hes_time_t irq_time() const { return irq_time_; } hes_time_t irq_time() const { return irq_time_; }
void set_irq_time( hes_time_t ); void set_irq_time( hes_time_t );
hes_time_t end_time() const { return end_time_; } hes_time_t end_time() const { return end_time_; }
void set_end_time( hes_time_t ); void set_end_time( hes_time_t );
void end_frame( hes_time_t ); void end_frame( hes_time_t );
// Attempt to execute instruction here results in CPU advancing time to // Attempt to execute instruction here results in CPU advancing time to
// lesser of irq_time() and end_time() (or end_time() if IRQs are // lesser of irq_time() and end_time() (or end_time() if IRQs are
// disabled) // disabled)
enum { idle_addr = 0x1FFF }; enum { idle_addr = 0x1FFF };
// Can read this many bytes past end of a page // Can read this many bytes past end of a page
enum { cpu_padding = 8 }; enum { cpu_padding = 8 };
public: public:
Hes_Cpu() { state = &state_; } Hes_Cpu() { state = &state_; }
enum { irq_inhibit = 0x04 }; enum { irq_inhibit = 0x04 };
@ -69,17 +69,17 @@ private:
// noncopyable // noncopyable
Hes_Cpu( const Hes_Cpu& ); Hes_Cpu( const Hes_Cpu& );
Hes_Cpu& operator = ( const Hes_Cpu& ); Hes_Cpu& operator = ( const Hes_Cpu& );
struct state_t { struct state_t {
uint8_t const* code_map [page_count + 1]; uint8_t const* code_map [page_count + 1];
hes_time_t base; 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; // points to state_ or a local copy within run()
state_t state_; state_t state_;
hes_time_t irq_time_; hes_time_t irq_time_;
hes_time_t end_time_; hes_time_t end_time_;
void set_code_page( int, void const* ); void set_code_page( int, void const* );
inline int update_end_time( hes_time_t end, hes_time_t irq ); 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" #include "blargg_source.h"
int const timer_mask = 0x04; static int const timer_mask = 0x04;
int const vdp_mask = 0x02; static int const vdp_mask = 0x02;
int const i_flag_mask = 0x04; static int const i_flag_mask = 0x04;
int const unmapped = 0xFF; 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::min;
using std::max; using std::max;
@ -33,12 +33,12 @@ Hes_Emu::Hes_Emu()
{ {
timer.raw_load = 0; timer.raw_load = 0;
set_type( gme_hes_type ); set_type( gme_hes_type );
static const char* const names [Hes_Apu::osc_count] = { static const char* const names [Hes_Apu::osc_count] = {
"Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2" "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2"
}; };
set_voice_names( names ); set_voice_names( names );
static int const types [Hes_Apu::osc_count] = { static int const types [Hes_Apu::osc_count] = {
wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3,
mixed_type | 0, mixed_type | 1 mixed_type | 0, mixed_type | 1
@ -65,19 +65,19 @@ static byte const* copy_field( byte const* in, char* out )
int len = 0x20; int len = 0x20;
if ( in [0x1F] && !in [0x2F] ) if ( in [0x1F] && !in [0x2F] )
len = 0x30; // fields are sometimes 16 bytes longer (ugh) len = 0x30; // fields are sometimes 16 bytes longer (ugh)
// since text fields are where any data could be, detect non-text // since text fields are where any data could be, detect non-text
// and fields with data after zero byte terminator // and fields with data after zero byte terminator
int i = 0; int i = 0;
for ( i = 0; i < len && in [i]; i++ ) for ( i = 0; i < len && in [i]; i++ )
if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text
return 0; // non-ASCII found return 0; // non-ASCII found
for ( ; i < len; i++ ) for ( ; i < len; i++ )
if ( in [i] ) if ( in [i] )
return 0; // data after terminator return 0; // data after terminator
Gme_File::copy_field_( out, (char const*) in, len ); Gme_File::copy_field_( out, (char const*) in, len );
in += len; in += len;
} }
@ -114,18 +114,18 @@ struct Hes_File : Gme_Info_
char unused [0x20]; char unused [0x20];
byte fields [0x30 * 3]; byte fields [0x30 * 3];
} h; } h;
Hes_File() { set_type( gme_hes_type ); } Hes_File() { set_type( gme_hes_type ); }
blargg_err_t load_( Data_Reader& in ) 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 ); blargg_err_t err = in.read( &h, sizeof h );
if ( err ) if ( err )
return (err == in.eof_error ? gme_wrong_file_type : err); return (err == in.eof_error ? gme_wrong_file_type : err);
return check_hes_header( &h ); return check_hes_header( &h );
} }
blargg_err_t track_info_( track_info_t* out, int ) const blargg_err_t track_info_( track_info_t* out, int ) const
{ {
copy_hes_fields( h.fields, out ); 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 ) 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( rom.load( in, header_size, &header_, unmapped ) );
RETURN_ERR( check_hes_header( header_.tag ) ); RETURN_ERR( check_hes_header( header_.tag ) );
if ( header_.vers != 0 ) if ( header_.vers != 0 )
set_warning( "Unknown file version" ); set_warning( "Unknown file version" );
if ( memcmp( header_.data_tag, "DATA", 4 ) ) if ( memcmp( header_.data_tag, "DATA", 4 ) )
set_warning( "Data header missing" ); set_warning( "Data header missing" );
if ( memcmp( header_.unused, "\0\0\0\0", 4 ) ) if ( memcmp( header_.unused, "\0\0\0\0", 4 ) )
set_warning( "Unknown header data" ); set_warning( "Unknown header data" );
// File spec supports multiple blocks, but I haven't found any, and // 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 // 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. // just try to load the damn data as best as possible.
long addr = get_le32( header_.addr ); long addr = get_le32( header_.addr );
long size = get_le32( header_.size ); long size = get_le32( header_.size );
long const rom_max = 0x100000; 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 ) if ( (unsigned long) (addr + size) > (unsigned long) rom_max )
set_warning( "Invalid size" ); set_warning( "Invalid size" );
if ( size != rom.file_size() ) if ( size != rom.file_size() )
{ {
if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) ) 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 else
set_warning( "Missing file data" ); set_warning( "Missing file data" );
} }
rom.set_addr( addr ); rom.set_addr( addr );
set_voice_count( apu.osc_count ); set_voice_count( apu.osc_count );
apu.volume( gain() ); apu.volume( gain() );
return setup_buffer( 7159091 ); return setup_buffer( 7159091 );
} }
@ -219,40 +219,40 @@ void Hes_Emu::set_tempo_( double t )
blargg_err_t Hes_Emu::start_track_( int track ) blargg_err_t Hes_Emu::start_track_( int track )
{ {
RETURN_ERR( Classic_Emu::start_track_( track ) ); RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( ram, 0, sizeof ram ); // some HES music relies on zero fill memset( ram, 0, sizeof ram ); // some HES music relies on zero fill
memset( sgx, 0, sizeof sgx ); memset( sgx, 0, sizeof sgx );
apu.reset(); apu.reset();
cpu::reset(); cpu::reset();
for ( unsigned i = 0; i < sizeof header_.banks; i++ ) for ( unsigned i = 0; i < sizeof header_.banks; i++ )
set_mmr( i, header_.banks [i] ); set_mmr( i, header_.banks [i] );
set_mmr( page_count, 0xFF ); // unmapped beyond end of address space set_mmr( page_count, 0xFF ); // unmapped beyond end of address space
irq.disables = timer_mask | vdp_mask; irq.disables = timer_mask | vdp_mask;
irq.timer = future_hes_time; irq.timer = future_hes_time;
irq.vdp = future_hes_time; irq.vdp = future_hes_time;
timer.enabled = false; timer.enabled = false;
timer.raw_load= 0x80; timer.raw_load= 0x80;
timer.count = timer.load; timer.count = timer.load;
timer.fired = false; timer.fired = false;
timer.last_time = 0; timer.last_time = 0;
vdp.latch = 0; vdp.latch = 0;
vdp.control = 0; vdp.control = 0;
vdp.next_vbl = 0; vdp.next_vbl = 0;
ram [0x1FF] = (idle_addr - 1) >> 8; ram [0x1FF] = (idle_addr - 1) >> 8;
ram [0x1FE] = (idle_addr - 1) & 0xFF; ram [0x1FE] = (idle_addr - 1) & 0xFF;
r.sp = 0xFD; r.sp = 0xFD;
r.pc = get_le16( header_.init_addr ); r.pc = get_le16( header_.init_addr );
r.a = track; r.a = track;
recalc_timer_load(); recalc_timer_load();
last_frame_hook = 0; last_frame_hook = 0;
return 0; return 0;
} }
@ -265,7 +265,7 @@ void Hes_Emu::cpu_write_vdp( int addr, int data )
case 0: case 0:
vdp.latch = data & 0x1F; vdp.latch = data & 0x1F;
break; break;
case 2: case 2:
if ( vdp.latch == 5 ) 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 ); debug_printf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data );
} }
break; break;
case 3: case 3:
debug_printf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); debug_printf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data );
break; break;
@ -297,7 +297,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
apu.write_data( t, addr, data ); apu.write_data( t, addr, data );
return; return;
} }
hes_time_t time = this->time(); hes_time_t time = this->time();
switch ( addr ) switch ( addr )
{ {
@ -306,7 +306,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
case 0x0003: case 0x0003:
cpu_write_vdp( addr, data ); cpu_write_vdp( addr, data );
return; return;
case 0x0C00: { case 0x0C00: {
run_until( time ); run_until( time );
timer.raw_load = (data & 0x7F) + 1; 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; timer.count = timer.load;
break; break;
} }
case 0x0C01: case 0x0C01:
data &= 1; data &= 1;
if ( timer.enabled == data ) if ( timer.enabled == data )
@ -324,21 +324,21 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
if ( data ) if ( data )
timer.count = timer.load; timer.count = timer.load;
break; break;
case 0x1402: case 0x1402:
run_until( time ); run_until( time );
irq.disables = data; irq.disables = data;
if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values
debug_printf( "Int mask: $%02X\n", data ); debug_printf( "Int mask: $%02X\n", data );
break; break;
case 0x1403: case 0x1403:
run_until( time ); run_until( time );
if ( timer.enabled ) if ( timer.enabled )
timer.count = timer.load; timer.count = timer.load;
timer.fired = false; timer.fired = false;
break; break;
#ifndef NDEBUG #ifndef NDEBUG
case 0x1000: // I/O port case 0x1000: // I/O port
case 0x0402: // palette case 0x0402: // palette
@ -346,13 +346,13 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data )
case 0x0404: case 0x0404:
case 0x0405: case 0x0405:
return; return;
default: default:
debug_printf( "unmapped write $%04X <- $%02X\n", addr, data ); debug_printf( "unmapped write $%04X <- $%02X\n", addr, data );
return; return;
#endif #endif
} }
irq_changed(); irq_changed();
} }
@ -369,22 +369,22 @@ int Hes_Emu::cpu_read_( hes_addr_t addr )
run_until( time ); run_until( time );
irq_changed(); irq_changed();
return 0x20; return 0x20;
case 0x0002: case 0x0002:
case 0x0003: case 0x0003:
debug_printf( "VDP read not supported: %d\n", addr ); debug_printf( "VDP read not supported: %d\n", addr );
return 0; return 0;
case 0x0C01: case 0x0C01:
//return timer.enabled; // TODO: remove? //return timer.enabled; // TODO: remove?
case 0x0C00: case 0x0C00:
run_until( time ); run_until( time );
debug_printf( "Timer count read\n" ); debug_printf( "Timer count read\n" );
return (unsigned) (timer.count - 1) / timer_base; return (unsigned) (timer.count - 1) / timer_base;
case 0x1402: case 0x1402:
return irq.disables; return irq.disables;
case 0x1403: case 0x1403:
{ {
int status = 0; int status = 0;
@ -392,18 +392,18 @@ int Hes_Emu::cpu_read_( hes_addr_t addr )
if ( irq.vdp <= time ) status |= vdp_mask; if ( irq.vdp <= time ) status |= vdp_mask;
return status; return status;
} }
#ifndef NDEBUG #ifndef NDEBUG
case 0x1000: // I/O port case 0x1000: // I/O port
case 0x180C: // CD-ROM case 0x180C: // CD-ROM
case 0x180D: case 0x180D:
break; break;
default: default:
debug_printf( "unmapped read $%04X\n", addr ); debug_printf( "unmapped read $%04X\n", addr );
#endif #endif
} }
return unmapped; return unmapped;
} }
@ -415,7 +415,7 @@ void Hes_Emu::run_until( hes_time_t present )
{ {
while ( vdp.next_vbl < present ) while ( vdp.next_vbl < present )
vdp.next_vbl += play_period; vdp.next_vbl += play_period;
hes_time_t elapsed = present - timer.last_time; hes_time_t elapsed = present - timer.last_time;
if ( elapsed > 0 ) if ( elapsed > 0 )
{ {
@ -432,25 +432,25 @@ void Hes_Emu::run_until( hes_time_t present )
void Hes_Emu::irq_changed() void Hes_Emu::irq_changed()
{ {
hes_time_t present = time(); hes_time_t present = time();
if ( irq.timer > present ) if ( irq.timer > present )
{ {
irq.timer = future_hes_time; irq.timer = future_hes_time;
if ( timer.enabled && !timer.fired ) if ( timer.enabled && !timer.fired )
irq.timer = present + timer.count; irq.timer = present + timer.count;
} }
if ( irq.vdp > present ) if ( irq.vdp > present )
{ {
irq.vdp = future_hes_time; irq.vdp = future_hes_time;
if ( vdp.control & 0x08 ) if ( vdp.control & 0x08 )
irq.vdp = vdp.next_vbl; irq.vdp = vdp.next_vbl;
} }
hes_time_t time = future_hes_time; hes_time_t time = future_hes_time;
if ( !(irq.disables & timer_mask) ) time = irq.timer; if ( !(irq.disables & timer_mask) ) time = irq.timer;
if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp );
set_irq_time( time ); set_irq_time( time );
} }
@ -458,11 +458,11 @@ int Hes_Emu::cpu_done()
{ {
check( time() >= end_time() || check( time() >= end_time() ||
(!(r.status & i_flag_mask) && time() >= irq_time()) ); (!(r.status & i_flag_mask) && time() >= irq_time()) );
if ( !(r.status & i_flag_mask) ) if ( !(r.status & i_flag_mask) )
{ {
hes_time_t present = time(); hes_time_t present = time();
if ( irq.timer <= present && !(irq.disables & timer_mask) ) if ( irq.timer <= present && !(irq.disables & timer_mask) )
{ {
timer.fired = true; timer.fired = true;
@ -481,7 +481,7 @@ int Hes_Emu::cpu_done()
#endif #endif
return 0x0A; return 0x0A;
} }
if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) if ( irq.vdp <= present && !(irq.disables & vdp_mask) )
{ {
// work around for bugs with music not acknowledging VDP // work around for bugs with music not acknowledging VDP
@ -498,7 +498,7 @@ int Hes_Emu::cpu_done()
return 0; 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 ) 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 ) blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int )
{ {
blip_time_t const duration = duration_; // cache blip_time_t const duration = duration_; // cache
if ( cpu::run( duration ) ) if ( cpu::run( duration ) )
set_warning( "Emulation error (illegal instruction)" ); set_warning( "Emulation error (illegal instruction)" );
check( time() >= duration ); check( time() >= duration );
//check( time() - duration < 20 ); // Txx instruction could cause going way over //check( time() - duration < 20 ); // Txx instruction could cause going way over
run_until( duration ); run_until( duration );
// end time frame // end time frame
timer.last_time -= duration; timer.last_time -= duration;
vdp.next_vbl -= 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.timer, duration );
::adjust_time( irq.vdp, duration ); ::adjust_time( irq.vdp, duration );
apu.end_frame( duration ); apu.end_frame( duration );
return 0; return 0;
} }

View file

@ -25,10 +25,10 @@ public:
byte addr [4]; byte addr [4];
byte unused [4]; byte unused [4];
}; };
// Header for currently loaded file // Header for currently loaded file
header_t const& header() const { return header_; } header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_hes_type; } static gme_type_t static_type() { return gme_hes_type; }
public: public:
@ -45,7 +45,7 @@ protected:
void unload(); void unload();
public: private: friend class Hes_Cpu; public: private: friend class Hes_Cpu;
byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space 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 );
int cpu_read( hes_addr_t ); int cpu_read( hes_addr_t );
void cpu_write_( hes_addr_t, int data ); void cpu_write_( hes_addr_t, int data );
@ -59,34 +59,34 @@ private:
hes_time_t play_period; hes_time_t play_period;
hes_time_t last_frame_hook; hes_time_t last_frame_hook;
int timer_base; int timer_base;
struct { struct {
hes_time_t last_time; hes_time_t last_time;
blargg_long count; int32_t count;
blargg_long load; int32_t load;
int raw_load; int raw_load;
byte enabled; byte enabled;
byte fired; byte fired;
} timer; } timer;
struct { struct {
hes_time_t next_vbl; hes_time_t next_vbl;
byte latch; byte latch;
byte control; byte control;
} vdp; } vdp;
struct { struct {
hes_time_t timer; hes_time_t timer;
hes_time_t vdp; hes_time_t vdp;
byte disables; byte disables;
} irq; } irq;
void recalc_timer_load(); void recalc_timer_load();
// large items // large items
Hes_Apu apu; Hes_Apu apu;
byte sgx [3 * page_size + cpu_padding]; byte sgx [3 * page_size + cpu_padding];
void irq_changed(); void irq_changed();
void run_until( hes_time_t ); 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" #include "blargg_endian.h"
typedef blargg_long cpu_time_t; typedef int32_t cpu_time_t;
// must be defined by caller // must be defined by caller
void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data );
@ -17,35 +17,35 @@ class Kss_Cpu {
public: public:
// Clear registers and map all pages to unmapped // Clear registers and map all pages to unmapped
void reset( void* unmapped_write, void const* unmapped_read ); void reset( void* unmapped_write, void const* unmapped_read );
// Map memory. Start and size must be multiple of page_size. // Map memory. Start and size must be multiple of page_size.
enum { page_size = 0x2000 }; static const unsigned int page_size = 0x2000;
void map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ); void map_mem( unsigned addr, uint32_t size, void* write, void const* read );
// Map address to page // Map address to page
uint8_t* write( unsigned addr ); uint8_t* write( unsigned addr );
uint8_t const* read( unsigned addr ); uint8_t const* read( unsigned addr );
// Run until specified time is reached. Returns true if suspicious/unsupported // Run until specified time is reached. Returns true if suspicious/unsupported
// instruction was encountered at any point during run. // instruction was encountered at any point during run.
bool run( cpu_time_t end_time ); bool run( cpu_time_t end_time );
// Time of beginning of next instruction // Time of beginning of next instruction
cpu_time_t time() const { return state->time + state->base; } cpu_time_t time() const { return state->time + state->base; }
// Alter current time. Not supported during run() call. // Alter current time. Not supported during run() call.
void set_time( cpu_time_t t ) { state->time = t - state->base; } void set_time( cpu_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; } void adjust_time( int delta ) { state->time += delta; }
#if BLARGG_BIG_ENDIAN #if BLARGG_BIG_ENDIAN
struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; struct regs_t { uint8_t b, c, d, e, h, l, flags, a; };
#else #else
struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; struct regs_t { uint8_t c, b, e, d, l, h, a, flags; };
#endif #endif
static_assert( sizeof (regs_t) == 8, "Invalid registers size, padding issue?" ); static_assert( sizeof (regs_t) == 8, "Invalid registers size, padding issue?" );
struct pairs_t { uint16_t bc, de, hl, fa; }; struct pairs_t { uint16_t bc, de, hl, fa; };
// Registers are not updated until run() returns // Registers are not updated until run() returns
struct registers_t { struct registers_t {
uint16_t pc; uint16_t pc;
@ -67,16 +67,16 @@ public:
uint8_t im; uint8_t im;
}; };
//registers_t r; (below for efficiency) //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 // can read this far past end of a page
enum { cpu_padding = 0x100 }; static const unsigned int cpu_padding = 0x100;
public: public:
Kss_Cpu(); Kss_Cpu();
enum { page_shift = 13 }; static const unsigned int page_shift = 13;
enum { page_count = 0x10000 >> page_shift }; static const int page_count = 0x10000 >> page_shift;
private: private:
uint8_t szpc [0x200]; uint8_t szpc [0x200];
cpu_time_t end_time_; 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" #include "blargg_source.h"
long const clock_rate = 3579545; static long const clock_rate = 3579545;
int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; static int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count;
using std::min; using std::min;
using std::max; using std::max;
@ -35,13 +35,13 @@ Kss_Emu::Kss_Emu()
"Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5" "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5"
}; };
set_voice_names( names ); set_voice_names( names );
static int const types [osc_count] = { static int const types [osc_count] = {
wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 0, wave_type | 1, wave_type | 2,
wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7 wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7
}; };
set_voice_types( types ); set_voice_types( types );
memset( unmapped_read, 0xFF, sizeof unmapped_read ); 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_ struct Kss_File : Gme_Info_
{ {
Kss_Emu::header_t header_; Kss_Emu::header_t header_;
Kss_File() { set_type( gme_kss_type ); } Kss_File() { set_type( gme_kss_type ); }
blargg_err_t load_( Data_Reader& in ) blargg_err_t load_( Data_Reader& in )
{ {
blargg_err_t err = in.read( &header_, Kss_Emu::header_size ); 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 (err == in.eof_error ? gme_wrong_file_type : err);
return check_kss_header( &header_ ); return check_kss_header( &header_ );
} }
blargg_err_t track_info_( track_info_t* out, int ) const blargg_err_t track_info_( track_info_t* out, int ) const
{ {
copy_kss_fields( header_, out ); copy_kss_fields( header_, out );
@ -125,12 +125,12 @@ void Kss_Emu::update_gain()
blargg_err_t Kss_Emu::load_( Data_Reader& in ) blargg_err_t Kss_Emu::load_( Data_Reader& in )
{ {
memset( &header_, 0, sizeof header_ ); memset( &header_, 0, sizeof header_ );
assert( offsetof (header_t,device_flags) == header_size - 1 ); blaarg_static_assert( offsetof (header_t,device_flags) == header_size - 1, "KSS Header layout incorrect!" );
assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 ); 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( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) );
RETURN_ERR( check_kss_header( header_.tag ) ); RETURN_ERR( check_kss_header( header_.tag ) );
if ( header_.tag [3] == 'C' ) if ( header_.tag [3] == 'C' )
{ {
if ( header_.extra_header ) if ( header_.extra_header )
@ -151,19 +151,19 @@ blargg_err_t Kss_Emu::load_( Data_Reader& in )
if ( header_.extra_header > 0x10 ) if ( header_.extra_header > 0x10 )
set_warning( "Unknown data in header" ); set_warning( "Unknown data in header" );
} }
if ( header_.device_flags & 0x09 ) if ( header_.device_flags & 0x09 )
set_warning( "FM sound not supported" ); set_warning( "FM sound not supported" );
scc_enabled = 0xC000; scc_enabled = 0xC000;
if ( header_.device_flags & 0x04 ) if ( header_.device_flags & 0x04 )
scc_enabled = 0; scc_enabled = 0;
if ( header_.device_flags & 0x02 && !sn ) if ( header_.device_flags & 0x02 && !sn )
CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) ); CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) );
set_voice_count( osc_count ); set_voice_count( osc_count );
return setup_buffer( ::clock_rate ); return setup_buffer( ::clock_rate );
} }
@ -201,7 +201,7 @@ blargg_err_t Kss_Emu::start_track_( int track )
memset( ram, 0xC9, 0x4000 ); memset( ram, 0xC9, 0x4000 );
memset( ram + 0x4000, 0, sizeof ram - 0x4000 ); memset( ram + 0x4000, 0, sizeof ram - 0x4000 );
// copy driver code to lo RAM // copy driver code to lo RAM
static byte const bios [] = { static byte const bios [] = {
0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG 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 + 0x01, bios, sizeof bios );
memcpy( ram + 0x93, vectors, sizeof vectors ); memcpy( ram + 0x93, vectors, sizeof vectors );
// copy non-banked data into RAM // copy non-banked data into RAM
unsigned load_addr = get_le16( header_.load_addr ); unsigned load_addr = get_le16( header_.load_addr );
long orig_load_size = get_le16( header_.load_size ); 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 ) if ( load_size != orig_load_size )
set_warning( "Excessive data size" ); set_warning( "Excessive data size" );
memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size ); memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size );
rom.set_addr( -load_size - header_.extra_header ); rom.set_addr( -load_size - header_.extra_header );
// check available bank data // 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; int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size;
bank_count = header_.bank_mode & 0x7F; bank_count = header_.bank_mode & 0x7F;
if ( bank_count > max_banks ) 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( "load_size : $%X\n", load_size );
//debug_printf( "bank_size : $%X\n", bank_size ); //debug_printf( "bank_size : $%X\n", bank_size );
//debug_printf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F ); //debug_printf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F );
ram [idle_addr] = 0xFF; ram [idle_addr] = 0xFF;
cpu::reset( unmapped_write, unmapped_read ); cpu::reset( unmapped_write, unmapped_read );
cpu::map_mem( 0, mem_size, ram, ram ); cpu::map_mem( 0, mem_size, ram, ram );
ay.reset(); ay.reset();
scc.reset(); scc.reset();
if ( sn ) if ( sn )
@ -256,18 +256,18 @@ blargg_err_t Kss_Emu::start_track_( int track )
gain_updated = false; gain_updated = false;
update_gain(); update_gain();
ay_latch = 0; ay_latch = 0;
return 0; return 0;
} }
void Kss_Emu::set_bank( int logical, int physical ) void Kss_Emu::set_bank( int logical, int physical )
{ {
unsigned const bank_size = this->bank_size(); unsigned const bank_size = this->bank_size();
unsigned addr = 0x8000; unsigned addr = 0x8000;
if ( logical && bank_size == 8 * 1024 ) if ( logical && bank_size == 8 * 1024 )
addr = 0xA000; addr = 0xA000;
physical -= header_.first_bank; physical -= header_.first_bank;
if ( (unsigned) physical >= (unsigned) bank_count ) if ( (unsigned) physical >= (unsigned) bank_count )
{ {
@ -276,7 +276,7 @@ void Kss_Emu::set_bank( int logical, int physical )
} }
else 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 ) for ( unsigned offset = 0; offset < bank_size; offset += page_size )
cpu::map_mem( addr + offset, page_size, cpu::map_mem( addr + offset, page_size,
unmapped_write, rom.at_addr( phys + offset ) ); unmapped_write, rom.at_addr( phys + offset ) );
@ -291,12 +291,12 @@ void Kss_Emu::cpu_write( unsigned addr, int data )
case 0x9000: case 0x9000:
set_bank( 0, data ); set_bank( 0, data );
return; return;
case 0xB000: case 0xB000:
set_bank( 1, data ); set_bank( 1, data );
return; return;
} }
int scc_addr = (addr & 0xDFFF) ^ 0x9800; int scc_addr = (addr & 0xDFFF) ^ 0x9800;
if ( scc_addr < scc.reg_count ) 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 ); scc.write( time(), scc_addr, data );
return; return;
} }
debug_printf( "LD ($%04X),$%02X\n", addr, data ); 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: case 0xA0:
emu.ay_latch = data & 0x0F; emu.ay_latch = data & 0x0F;
return; return;
case 0xA1: case 0xA1:
GME_APU_HOOK( &emu, emu.ay_latch, data ); GME_APU_HOOK( &emu, emu.ay_latch, data );
emu.ay.write( time, emu.ay_latch, data ); emu.ay.write( time, emu.ay_latch, data );
return; return;
case 0x06: case 0x06:
if ( emu.sn && (emu.header_.device_flags & 0x04) ) 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; return;
} }
break; break;
case 0x7E: case 0x7E:
case 0x7F: case 0x7F:
if ( emu.sn ) if ( emu.sn )
@ -347,11 +347,11 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data )
return; return;
} }
break; break;
case 0xFE: case 0xFE:
emu.set_bank( 0, data ); emu.set_bank( 0, data );
return; return;
#ifndef NDEBUG #ifndef NDEBUG
case 0xF1: // FM data case 0xF1: // FM data
if ( data ) if ( data )
@ -361,7 +361,7 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data )
return; return;
#endif #endif
} }
debug_printf( "OUT $%04X,$%02X\n", addr, data ); 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 ) //switch ( addr & 0xFF )
//{ //{
//} //}
debug_printf( "IN $%04X\n", addr ); debug_printf( "IN $%04X\n", addr );
return 0; return 0;
} }
@ -386,7 +386,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
cpu::run( min( duration, next_play ) ); cpu::run( min( duration, next_play ) );
if ( r.pc == idle_addr ) if ( r.pc == idle_addr )
set_time( end ); set_time( end );
if ( time() >= next_play ) if ( time() >= next_play )
{ {
next_play += play_period; next_play += play_period;
@ -398,7 +398,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
if ( scc_accessed ) if ( scc_accessed )
update_gain(); update_gain();
} }
ram [--r.sp] = idle_addr >> 8; ram [--r.sp] = idle_addr >> 8;
ram [--r.sp] = idle_addr & 0xFF; ram [--r.sp] = idle_addr & 0xFF;
r.pc = get_le16( header_.play_addr ); 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(); duration = time();
next_play -= duration; next_play -= duration;
check( next_play >= 0 ); check( next_play >= 0 );
@ -415,6 +415,6 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int )
scc.end_frame( duration ); scc.end_frame( duration );
if ( sn ) if ( sn )
sn->end_frame( duration ); sn->end_frame( duration );
return 0; return 0;
} }

View file

@ -27,7 +27,7 @@ public:
byte extra_header; byte extra_header;
byte device_flags; byte device_flags;
}; };
enum { ext_header_size = 0x10 }; enum { ext_header_size = 0x10 };
struct ext_header_t struct ext_header_t
{ {
@ -40,12 +40,12 @@ public:
byte msx_music_vol; byte msx_music_vol;
byte msx_audio_vol; byte msx_audio_vol;
}; };
struct composite_header_t : header_t, ext_header_t { }; struct composite_header_t : header_t, ext_header_t { };
// Header for currently loaded file // Header for currently loaded file
composite_header_t const& header() const { return header_; } composite_header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_kss_type; } static gme_type_t static_type() { return gme_kss_type; }
public: public:
Kss_Emu(); Kss_Emu();
@ -62,29 +62,29 @@ protected:
private: private:
Rom_Data<page_size> rom; Rom_Data<page_size> rom;
composite_header_t header_; composite_header_t header_;
bool scc_accessed; bool scc_accessed;
bool gain_updated; bool gain_updated;
void update_gain(); void update_gain();
unsigned scc_enabled; // 0 or 0xC000 unsigned scc_enabled; // 0 or 0xC000
int bank_count; int bank_count;
void set_bank( int logical, int physical ); 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 play_period;
blip_time_t next_play; blip_time_t next_play;
int ay_latch; int ay_latch;
friend void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); 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 ); friend int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr );
void cpu_write( unsigned addr, int data ); void cpu_write( unsigned addr, int data );
friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data );
// large items // large items
enum { mem_size = 0x10000 }; static const unsigned int mem_size = 0x10000;
byte ram [mem_size + cpu_padding]; byte ram [mem_size + cpu_padding];
Ay_Apu ay; Ay_Apu ay;
Scc_Apu scc; Scc_Apu scc;
Sms_Apu* sn; 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. // Tones above this frequency are treated as disabled tone at half volume.
// Power of two is more efficient (avoids division). // 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 ) void Scc_Apu::run_until( blip_time_t end_time )
{ {
for ( int index = 0; index < osc_count; index++ ) for ( int index = 0; index < osc_count; index++ )
{ {
osc_t& osc = oscs [index]; osc_t& osc = oscs [index];
Blip_Buffer* const output = osc.output; Blip_Buffer* const output = osc.output;
if ( !output ) if ( !output )
continue; continue;
output->set_modified(); output->set_modified();
blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 + blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 +
regs [0x80 + index * 2] + 1; regs [0x80 + index * 2] + 1;
int volume = 0; int volume = 0;
if ( regs [0x8F] & (1 << index) ) 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); inaudible_freq * 32) / (inaudible_freq * 16);
if ( period > inaudible_period ) if ( period > inaudible_period )
volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15); volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15);
} }
int8_t const* wave = (int8_t*) regs + index * wave_size; int8_t const* wave = (int8_t*) regs + index * wave_size;
if ( index == osc_count - 1 ) if ( index == osc_count - 1 )
wave -= wave_size; // last two oscs share wave 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 ); synth.offset( last_time, delta, output );
} }
} }
blip_time_t time = last_time + osc.delay; blip_time_t time = last_time + osc.delay;
if ( time < end_time ) if ( time < end_time )
{ {
if ( !volume ) if ( !volume )
{ {
// maintain phase // 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); osc.phase = (osc.phase + count) & (wave_size - 1);
time += count * period; time += count * period;
} }
else else
{ {
int phase = osc.phase; int phase = osc.phase;
int last_wave = wave [phase]; int last_wave = wave [phase];
phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop
do do
{ {
int amp = wave [phase]; int amp = wave [phase];
@ -86,7 +86,7 @@ void Scc_Apu::run_until( blip_time_t end_time )
time += period; time += period;
} }
while ( time < end_time ); while ( time < end_time );
osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance
osc.last_amp = wave [phase] * volume; osc.last_amp = wave [phase] * volume;
} }

View file

@ -12,36 +12,36 @@ class Scc_Apu {
public: public:
// Set buffer to generate all sound into, or disable sound if NULL // Set buffer to generate all sound into, or disable sound if NULL
void output( Blip_Buffer* ); void output( Blip_Buffer* );
// Reset sound chip // Reset sound chip
void reset(); void reset();
// Write to register at specified time // 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 ); void write( blip_time_t time, int reg, int data );
// Run sound to specified time, end current time frame, then start a new // 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 // time frame at time 0. Time frames have no effect on emulation and each
// can be whatever length is convenient. // can be whatever length is convenient.
void end_frame( blip_time_t length ); void end_frame( blip_time_t length );
// Additional features // Additional features
// Set sound output of specific oscillator to buffer, where index is // Set sound output of specific oscillator to buffer, where index is
// 0 to 4. If buffer is NULL, the specified oscillator is muted. // 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* ); void osc_output( int index, Blip_Buffer* );
// Set overall volume (default is 1.0) // Set overall volume (default is 1.0)
void volume( double ); void volume( double );
// Set treble equalization (see documentation) // Set treble equalization (see documentation)
void treble_eq( blip_eq_t const& ); void treble_eq( blip_eq_t const& );
public: public:
Scc_Apu(); Scc_Apu();
private: private:
enum { amp_range = 0x8000 }; static const unsigned int amp_range = 0x8000;
struct osc_t struct osc_t
{ {
int delay; int delay;
@ -53,7 +53,7 @@ private:
blip_time_t last_time; blip_time_t last_time;
unsigned char regs [reg_count]; unsigned char regs [reg_count];
Blip_Synth<blip_med_quality,1> synth; Blip_Synth<blip_med_quality,1> synth;
void run_until( blip_time_t ); void run_until( blip_time_t );
}; };
@ -96,10 +96,10 @@ inline Scc_Apu::Scc_Apu()
inline void Scc_Apu::reset() inline void Scc_Apu::reset()
{ {
last_time = 0; last_time = 0;
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
memset( &oscs [i], 0, offsetof (osc_t,output) ); memset( &oscs [i], 0, offsetof (osc_t,output) );
memset( regs, 0, sizeof regs ); 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 ) blargg_err_t Gme_File::load_m3u_( blargg_err_t err )
{ {
require( raw_track_count_ ); // file must be loaded first require( raw_track_count_ ); // file must be loaded first
if ( !err ) if ( !err )
{ {
if ( playlist.size() ) if ( playlist.size() )
track_count_ = playlist.size(); track_count_ = playlist.size();
int line = playlist.first_error(); int line = playlist.first_error();
if ( line ) if ( line )
{ {
@ -38,7 +38,7 @@ blargg_err_t Gme_File::load_m3u_( blargg_err_t err )
do { do {
*--out = line % 10 + '0'; *--out = line % 10 + '0';
} while ( (line /= 10) > 0 ); } while ( (line /= 10) > 0 );
static const char str [] = "Problem in m3u at line "; static const char str [] = "Problem in m3u at line ";
out -= sizeof str - 1; out -= sizeof str - 1;
memcpy( out, str, 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 ); return me->load_m3u( in );
} }
static char* skip_white( char* in ) static char* skip_white( char* in )
{ {
while ( *in == ' ' ) while ( *in == ' ' )
@ -69,7 +67,7 @@ static char* skip_white( char* in )
return 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 ) 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; int c = *in;
if ( !c ) break; if ( !c ) break;
in++; in++;
if ( c == ',' ) // commas in filename if ( c == ',' ) // commas in filename
{ {
char* p = skip_white( in ); char* p = skip_white( in );
@ -91,7 +89,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry )
break; break;
} }
} }
if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix
{ {
entry.type = ++in; entry.type = ++in;
@ -104,7 +102,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry )
} }
break; break;
} }
if ( c == '\\' ) // \ prefix for special characters if ( c == '\\' ) // \ prefix for special characters
{ {
c = *in; c = *in;
@ -122,16 +120,16 @@ static char* next_field( char* in, int* result )
while ( 1 ) while ( 1 )
{ {
in = skip_white( in ); in = skip_white( in );
if ( !*in ) if ( !*in )
break; break;
if ( *in == ',' ) if ( *in == ',' )
{ {
in++; in++;
break; break;
} }
*result = 1; *result = 1;
in++; in++;
} }
@ -176,7 +174,7 @@ static char* parse_int( char* in, int* out, int* result )
} }
// Returns 16 or greater if not hex // Returns 16 or greater if not hex
inline int from_hex_char( int h ) static inline int from_hex_char( int h )
{ {
h -= 0x30; h -= 0x30;
if ( (unsigned) h > 9 ) if ( (unsigned) h > 9 )
@ -250,7 +248,7 @@ static char* parse_name( char* in )
int c = *in; int c = *in;
if ( !c ) break; if ( !c ) break;
in++; in++;
if ( c == ',' ) // commas in string if ( c == ',' ) // commas in string
{ {
char* p = skip_white( in ); char* p = skip_white( in );
@ -260,7 +258,7 @@ static char* parse_name( char* in )
break; break;
} }
} }
if ( c == '\\' ) // \ prefix for special characters if ( c == '\\' ) // \ prefix for special characters
{ {
c = *in; c = *in;
@ -276,25 +274,25 @@ static char* parse_name( char* in )
static int parse_line( char* in, M3u_Playlist::entry_t& entry ) static int parse_line( char* in, M3u_Playlist::entry_t& entry )
{ {
int result = 0; int result = 0;
// file // file
entry.file = in; entry.file = in;
entry.type = ""; entry.type = "";
in = parse_filename( in, entry ); in = parse_filename( in, entry );
// track // track
entry.track = -1; entry.track = -1;
entry.decimal_track = 0; entry.decimal_track = 0;
in = parse_track( in, entry, &result ); in = parse_track( in, entry, &result );
// name // name
entry.name = in; entry.name = in;
in = parse_name( in ); in = parse_name( in );
// time // time
entry.length = -1; entry.length = -1;
in = parse_time( in, &entry.length, &result ); in = parse_time( in, &entry.length, &result );
// loop // loop
entry.intro = -1; entry.intro = -1;
entry.loop = -1; entry.loop = -1;
@ -309,7 +307,7 @@ static int parse_line( char* in, M3u_Playlist::entry_t& entry )
if ( entry.loop >= 0 ) if ( entry.loop >= 0 )
{ {
entry.intro = 0; entry.intro = 0;
if ( *in == '-' ) // trailing '-' means that intro length was specified if ( *in == '-' ) // trailing '-' means that intro length was specified
{ {
in++; in++;
entry.intro = entry.loop; entry.intro = entry.loop;
@ -318,15 +316,15 @@ static int parse_line( char* in, M3u_Playlist::entry_t& entry )
} }
} }
in = next_field( in, &result ); in = next_field( in, &result );
// fade // fade
entry.fade = -1; entry.fade = -1;
in = parse_time( in, &entry.fade, &result ); in = parse_time( in, &entry.fade, &result );
// repeat // repeat
entry.repeat = -1; entry.repeat = -1;
in = parse_int( in, &entry.repeat, &result ); in = parse_int( in, &entry.repeat, &result );
return result; return result;
} }
@ -337,7 +335,7 @@ static void parse_comment( char* in, M3u_Playlist::info_t& info, char *& last_co
if ( *field != '@' ) if ( *field != '@' )
while ( *in && *in != ':' ) while ( *in && *in != ':' )
in++; in++;
if ( *in == ':' ) if ( *in == ':' )
{ {
const char* text = skip_white( in + 1 ); 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; last_comment_value[ len + 2 + field_len ] = 0;
return; return;
} }
if ( first ) if ( first )
info.title = field; info.title = field;
} }
@ -411,12 +409,12 @@ blargg_err_t M3u_Playlist::parse_()
info_.ripping = ""; info_.ripping = "";
info_.tagging = ""; info_.tagging = "";
info_.copyright = ""; info_.copyright = "";
int const CR = 13; int const CR = 13;
int const LF = 10; int const LF = 10;
data.end() [-1] = LF; // terminate input data.end() [-1] = LF; // terminate input
first_error_ = 0; first_error_ = 0;
bool first_comment = true; bool first_comment = true;
int line = 0; 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 if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line
*in++ = 0; *in++ = 0;
*in++ = 0; *in++ = 0;
// parse line // parse line
if ( *begin == '#' ) if ( *begin == '#' )
{ {
@ -448,7 +446,7 @@ blargg_err_t M3u_Playlist::parse_()
{ {
if ( (int) entries.size() <= count ) if ( (int) entries.size() <= count )
RETURN_ERR( entries.resize( count * 2 + 64 ) ); RETURN_ERR( entries.resize( count * 2 + 64 ) );
if ( !parse_line( begin, entries [count] ) ) if ( !parse_line( begin, entries [count] ) )
count++; count++;
else if ( !first_error_ ) else if ( !first_error_ )
@ -459,10 +457,10 @@ blargg_err_t M3u_Playlist::parse_()
} }
if ( count <= 0 ) if ( count <= 0 )
return "Not an m3u playlist"; return "Not an m3u playlist";
if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) ) if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) )
info_.title = ""; info_.title = "";
return entries.resize( count ); return entries.resize( count );
} }

View file

@ -13,11 +13,11 @@ public:
blargg_err_t load( const char* path ); blargg_err_t load( const char* path );
blargg_err_t load( Data_Reader& in ); blargg_err_t load( Data_Reader& in );
blargg_err_t load( void const* data, long size ); blargg_err_t load( void const* data, long size );
// Line number of first parse error, 0 if no error. Any lines with parse // Line number of first parse error, 0 if no error. Any lines with parse
// errors are ignored. // errors are ignored.
int first_error() const { return first_error_; } int first_error() const { return first_error_; }
struct info_t struct info_t
{ {
const char* title; const char* title;
@ -31,7 +31,7 @@ public:
const char* copyright; const char* copyright;
}; };
info_t const& info() const { return info_; } info_t const& info() const { return info_; }
struct entry_t struct entry_t
{ {
const char* file; // filename without stupid ::TYPE suffix const char* file; // filename without stupid ::TYPE suffix
@ -48,15 +48,15 @@ public:
}; };
entry_t const& operator [] ( int i ) const { return entries [i]; } entry_t const& operator [] ( int i ) const { return entries [i]; }
int size() const { return entries.size(); } int size() const { return entries.size(); }
void clear(); void clear();
private: private:
blargg_vector<entry_t> entries; blargg_vector<entry_t> entries;
blargg_vector<char> data; blargg_vector<char> data;
int first_error_; int first_error_;
info_t info_; info_t info_;
blargg_err_t parse(); blargg_err_t parse();
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 require( !(count & 1) ); // count must be even
count = (unsigned) count / 2; count = (unsigned) count / 2;
long avail = bufs [0].samples_avail(); long avail = bufs [0].samples_avail();
if ( count > avail ) if ( count > avail )
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 [1].remove_samples( count );
bufs [2].remove_samples( count ); bufs [2].remove_samples( count );
} }
// to do: this might miss opportunities for optimization // to do: this might miss opportunities for optimization
if ( !bufs [0].samples_avail() ) if ( !bufs [0].samples_avail() )
{ {
@ -144,89 +144,89 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count )
stereo_added = 0; stereo_added = 0;
} }
} }
return count * 2; 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_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [1] ); int const bass = BLIP_READER_BASS( bufs [1] );
BLIP_READER_BEGIN( left, bufs [1] ); BLIP_READER_BEGIN( left, bufs [1] );
BLIP_READER_BEGIN( right, bufs [2] ); BLIP_READER_BEGIN( right, bufs [2] );
BLIP_READER_BEGIN( center, bufs [0] ); BLIP_READER_BEGIN( center, bufs [0] );
for ( ; count; --count ) for ( ; count; --count )
{ {
int c = BLIP_READER_READ( center ); int c = BLIP_READER_READ( center );
blargg_long l = c + BLIP_READER_READ( left ); int32_t l = c + BLIP_READER_READ( left );
blargg_long r = c + BLIP_READER_READ( right ); int32_t r = c + BLIP_READER_READ( right );
if ( (int16_t) l != l ) if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24); l = 0x7FFF - (l >> 24);
BLIP_READER_NEXT( center, bass ); BLIP_READER_NEXT( center, bass );
if ( (int16_t) r != r ) if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24); r = 0x7FFF - (r >> 24);
BLIP_READER_NEXT( left, bass ); BLIP_READER_NEXT( left, bass );
BLIP_READER_NEXT( right, bass ); BLIP_READER_NEXT( right, bass );
out [0] = l; out [0] = l;
out [1] = r; out [1] = r;
out += 2; out += 2;
} }
BLIP_READER_END( center, bufs [0] ); BLIP_READER_END( center, bufs [0] );
BLIP_READER_END( right, bufs [2] ); BLIP_READER_END( right, bufs [2] );
BLIP_READER_END( left, bufs [1] ); 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_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [1] ); int const bass = BLIP_READER_BASS( bufs [1] );
BLIP_READER_BEGIN( left, bufs [1] ); BLIP_READER_BEGIN( left, bufs [1] );
BLIP_READER_BEGIN( right, bufs [2] ); BLIP_READER_BEGIN( right, bufs [2] );
for ( ; count; --count ) for ( ; count; --count )
{ {
blargg_long l = BLIP_READER_READ( left ); int32_t l = BLIP_READER_READ( left );
if ( (int16_t) l != l ) if ( (int16_t) l != l )
l = 0x7FFF - (l >> 24); l = 0x7FFF - (l >> 24);
blargg_long r = BLIP_READER_READ( right ); int32_t r = BLIP_READER_READ( right );
if ( (int16_t) r != r ) if ( (int16_t) r != r )
r = 0x7FFF - (r >> 24); r = 0x7FFF - (r >> 24);
BLIP_READER_NEXT( left, bass ); BLIP_READER_NEXT( left, bass );
BLIP_READER_NEXT( right, bass ); BLIP_READER_NEXT( right, bass );
out [0] = l; out [0] = l;
out [1] = r; out [1] = r;
out += 2; out += 2;
} }
BLIP_READER_END( right, bufs [2] ); BLIP_READER_END( right, bufs [2] );
BLIP_READER_END( left, bufs [1] ); 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_; blip_sample_t* BLIP_RESTRICT out = out_;
int const bass = BLIP_READER_BASS( bufs [0] ); int const bass = BLIP_READER_BASS( bufs [0] );
BLIP_READER_BEGIN( center, bufs [0] ); BLIP_READER_BEGIN( center, bufs [0] );
for ( ; count; --count ) for ( ; count; --count )
{ {
blargg_long s = BLIP_READER_READ( center ); int32_t s = BLIP_READER_READ( center );
if ( (int16_t) s != s ) if ( (int16_t) s != s )
s = 0x7FFF - (s >> 24); s = 0x7FFF - (s >> 24);
BLIP_READER_NEXT( center, bass ); BLIP_READER_NEXT( center, bass );
out [0] = s; out [0] = s;
out [1] = s; out [1] = s;
out += 2; out += 2;
} }
BLIP_READER_END( center, bufs [0] ); BLIP_READER_END( center, bufs [0] );
} }

View file

@ -13,10 +13,10 @@ class Multi_Buffer {
public: public:
Multi_Buffer( int samples_per_frame ); Multi_Buffer( int samples_per_frame );
virtual ~Multi_Buffer() { } virtual ~Multi_Buffer() { }
// Set the number of channels available // Set the number of channels available
virtual blargg_err_t set_channel_count( int ); virtual blargg_err_t set_channel_count( int );
// Get indexed channel, from 0 to channel count - 1 // Get indexed channel, from 0 to channel count - 1
struct channel_t { struct channel_t {
Blip_Buffer* center; Blip_Buffer* center;
@ -26,31 +26,31 @@ public:
enum { type_index_mask = 0xFF }; enum { type_index_mask = 0xFF };
enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type };
virtual channel_t channel( int index, int type ) = 0; virtual channel_t channel( int index, int type ) = 0;
// See Blip_Buffer.h // See Blip_Buffer.h
virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0;
virtual void clock_rate( long ) = 0; virtual void clock_rate( long ) = 0;
virtual void bass_freq( int ) = 0; virtual void bass_freq( int ) = 0;
virtual void clear() = 0; virtual void clear() = 0;
long sample_rate() const; long sample_rate() const;
// Length of buffer, in milliseconds // Length of buffer, in milliseconds
int length() const; int length() const;
// See Blip_Buffer.h // See Blip_Buffer.h
virtual void end_frame( blip_time_t ) = 0; virtual void end_frame( blip_time_t ) = 0;
// Number of samples per output frame (1 = mono, 2 = stereo) // Number of samples per output frame (1 = mono, 2 = stereo)
int samples_per_frame() const; int samples_per_frame() const;
// Count of changes to channel configuration. Incremented whenever // Count of changes to channel configuration. Incremented whenever
// a change is made to any of the Blip_Buffers for any channel. // a change is made to any of the Blip_Buffers for any channel.
unsigned channels_changed_count() { return channels_changed_count_; } unsigned channels_changed_count() { return channels_changed_count_; }
// See Blip_Buffer.h // See Blip_Buffer.h
virtual long read_samples( blip_sample_t*, long ) = 0; virtual long read_samples( blip_sample_t*, long ) = 0;
virtual long samples_avail() const = 0; virtual long samples_avail() const = 0;
public: public:
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
protected: protected:
@ -59,7 +59,7 @@ private:
// noncopyable // noncopyable
Multi_Buffer( const Multi_Buffer& ); Multi_Buffer( const Multi_Buffer& );
Multi_Buffer& operator = ( const Multi_Buffer& ); Multi_Buffer& operator = ( const Multi_Buffer& );
unsigned channels_changed_count_; unsigned channels_changed_count_;
long sample_rate_; long sample_rate_;
int length_; int length_;
@ -73,7 +73,7 @@ class Mono_Buffer : public Multi_Buffer {
public: public:
// Buffer used for all channels // Buffer used for all channels
Blip_Buffer* center() { return &buf; } Blip_Buffer* center() { return &buf; }
public: public:
Mono_Buffer(); Mono_Buffer();
~Mono_Buffer(); ~Mono_Buffer();
@ -90,12 +90,12 @@ public:
// Uses three buffers (one for center) and outputs stereo sample pairs. // Uses three buffers (one for center) and outputs stereo sample pairs.
class Stereo_Buffer : public Multi_Buffer { class Stereo_Buffer : public Multi_Buffer {
public: public:
// Buffers used for all channels // Buffers used for all channels
Blip_Buffer* center() { return &bufs [0]; } Blip_Buffer* center() { return &bufs [0]; }
Blip_Buffer* left() { return &bufs [1]; } Blip_Buffer* left() { return &bufs [1]; }
Blip_Buffer* right() { return &bufs [2]; } Blip_Buffer* right() { return &bufs [2]; }
public: public:
Stereo_Buffer(); Stereo_Buffer();
~Stereo_Buffer(); ~Stereo_Buffer();
@ -105,20 +105,20 @@ public:
void clear(); void clear();
channel_t channel( int, int ) { return chan; } channel_t channel( int, int ) { return chan; }
void end_frame( blip_time_t ); void end_frame( blip_time_t );
long samples_avail() const { return bufs [0].samples_avail() * 2; } long samples_avail() const { return bufs [0].samples_avail() * 2; }
long read_samples( blip_sample_t*, long ); long read_samples( blip_sample_t*, long );
private: private:
enum { buf_count = 3 }; enum { buf_count = 3 };
Blip_Buffer bufs [buf_count]; Blip_Buffer bufs [buf_count];
channel_t chan; channel_t chan;
int stereo_added; int stereo_added;
int was_stereo; int was_stereo;
void mix_stereo_no_center( blip_sample_t*, blargg_long ); void mix_stereo_no_center( blip_sample_t*, int32_t );
void mix_stereo( blip_sample_t*, blargg_long ); void mix_stereo( blip_sample_t*, int32_t );
void mix_mono( blip_sample_t*, blargg_long ); void mix_mono( blip_sample_t*, int32_t );
}; };
// Silent_Buffer generates no samples, useful where no sound is wanted // 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" #include "blargg_source.h"
int const silence_max = 6; // seconds static int const silence_max = 6; // seconds
int const silence_threshold = 0x10; static int const silence_threshold = 0x10;
long const fade_block_size = 512; static long const fade_block_size = 512;
int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) static int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift)
using std::min; using std::min;
using std::max; using std::max;
@ -34,6 +34,7 @@ void Music_Emu::clear_track_vars()
{ {
current_track_ = -1; current_track_ = -1;
out_time = 0; out_time = 0;
out_time_scaled = 0;
emu_time = 0; emu_time = 0;
emu_track_ended_ = true; emu_track_ended_ = true;
track_ended_ = true; track_ended_ = true;
@ -60,14 +61,14 @@ Music_Emu::Music_Emu()
mute_mask_ = 0; mute_mask_ = 0;
tempo_ = 1.0; tempo_ = 1.0;
gain_ = 1.0; gain_ = 1.0;
// defaults // defaults
max_initial_silence = 2; max_initial_silence = 2;
silence_lookahead = 3; silence_lookahead = 3;
ignore_silence_ = false; ignore_silence_ = false;
equalizer_.treble = -1.0; equalizer_.treble = -1.0;
equalizer_.bass = 60; equalizer_.bass = 60;
emu_autoload_playback_limit_ = true; emu_autoload_playback_limit_ = true;
static const char* const names [] = { static const char* const names [] = {
@ -137,6 +138,11 @@ void Music_Emu::mute_voices( int mask )
mute_voices_( mask ); mute_voices_( mask );
} }
void Music_Emu::disable_echo( bool disable )
{
disable_echo_( disable );
}
void Music_Emu::set_tempo( double t ) void Music_Emu::set_tempo( double t )
{ {
require( sample_rate() ); // sample rate must be set first 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 ) blargg_err_t Music_Emu::start_track( int track )
{ {
clear_track_vars(); clear_track_vars();
int remapped = track; int remapped = track;
RETURN_ERR( remap_track_( &remapped ) ); RETURN_ERR( remap_track_( &remapped ) );
current_track_ = track; current_track_ = track;
RETURN_ERR( start_track_( remapped ) ); RETURN_ERR( start_track_( remapped ) );
emu_track_ended_ = false; emu_track_ended_ = false;
track_ended_ = false; track_ended_ = false;
if ( !ignore_silence_ ) if ( !ignore_silence_ )
{ {
// play until non-silence or end of track // 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_ ) if ( buf_remain | (int) emu_track_ended_ )
break; break;
} }
emu_time = buf_remain; emu_time = buf_remain;
out_time = 0; out_time = 0;
silence_time = 0; out_time_scaled = 0;
silence_count = 0; silence_time = 0;
silence_count = 0;
} }
return track_ended() ? warning() : 0; return track_ended() ? warning() : 0;
} }
@ -205,9 +212,9 @@ void Music_Emu::set_autoload_playback_limit( bool do_autoload_limit )
// Tell/Seek // 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; msec -= sec * 1000;
return (sec * sample_rate() + msec * sample_rate() / 1000) * out_channels(); 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 long Music_Emu::tell() const
{ {
blargg_long rate = sample_rate() * out_channels(); int32_t rate = sample_rate() * out_channels();
blargg_long sec = out_time / rate; int32_t sec = out_time / rate;
return sec * 1000 + (out_time - sec * rate) * 1000 / 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 ) blargg_err_t Music_Emu::seek_samples( long time )
{ {
if ( time < out_time ) if ( time < out_time )
@ -236,31 +248,43 @@ blargg_err_t Music_Emu::seek( long msec )
return seek_samples( msec_to_samples( 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 ) blargg_err_t Music_Emu::skip( long count )
{ {
require( current_track() >= 0 ); // start_track() must have been called already require( current_track() >= 0 ); // start_track() must have been called already
out_time += count; out_time += count;
out_time_scaled += count * tempo_ / out_channels();
// remove from silence and buf first // remove from silence and buf first
{ {
long n = min( count, silence_count ); long n = min( count, silence_count );
silence_count -= n; silence_count -= n;
count -= n; count -= n;
n = min( count, buf_remain ); n = min( count, buf_remain );
buf_remain -= n; buf_remain -= n;
count -= n; count -= n;
} }
if ( count && !emu_track_ended_ ) if ( count && !emu_track_ended_ )
{ {
emu_time += count; emu_time += count;
end_track_if_error( skip_( count ) ); end_track_if_error( skip_( count ) );
} }
if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended
track_ended_ |= emu_track_ended_; track_ended_ |= emu_track_ended_;
return 0; return 0;
} }
@ -272,16 +296,16 @@ blargg_err_t Music_Emu::skip_( long count )
{ {
int saved_mute = mute_mask_; int saved_mute = mute_mask_;
mute_voices( ~0 ); mute_voices( ~0 );
while ( count > threshold / 2 && !emu_track_ended_ ) while ( count > threshold / 2 && !emu_track_ended_ )
{ {
RETURN_ERR( play_( buf_size, buf.begin() ) ); RETURN_ERR( play_( buf_size, buf.begin() ) );
count -= buf_size; count -= buf_size;
} }
mute_voices( saved_mute ); mute_voices( saved_mute );
} }
while ( count && !emu_track_ended_ ) while ( count && !emu_track_ended_ )
{ {
long n = buf_size; 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 ) // 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 shift = x / step;
int fraction = (x - shift * step) * unit / 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 ); fade_step, unit );
if ( gain < (unit >> fade_shift) ) if ( gain < (unit >> fade_shift) )
track_ended_ = emu_track_ended_ = true; track_ended_ = emu_track_ended_ = true;
sample_t* io = &out [i]; sample_t* io = &out [i];
for ( int count = min( fade_block_size, out_count - i ); count; --count ) 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( current_track() >= 0 );
require( out_count % out_channels() == 0 ); require( out_count % out_channels() == 0 );
assert( emu_time >= out_time ); assert( emu_time >= out_time );
// prints nifty graph of how far ahead we are when searching for silence // 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()), "*" ); //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" );
long pos = 0; long pos = 0;
if ( silence_count ) if ( silence_count )
{ {
// during a run of silence, run emulator at >=2x speed so it gets ahead // 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; 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_buf();
// fill with silence // fill with silence
pos = min( silence_count, out_count ); pos = min( silence_count, out_count );
memset( out, 0, pos * sizeof *out ); memset( out, 0, pos * sizeof *out );
silence_count -= pos; 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; track_ended_ = emu_track_ended_ = true;
silence_count = 0; silence_count = 0;
buf_remain = 0; buf_remain = 0;
} }
} }
if ( buf_remain ) if ( buf_remain )
{ {
// empty silence buf // empty silence buf
@ -421,30 +445,31 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out )
buf_remain -= n; buf_remain -= n;
pos += n; pos += n;
} }
// generate remaining samples normally // generate remaining samples normally
long remain = out_count - pos; long remain = out_count - pos;
if ( remain ) if ( remain )
{ {
emu_play( remain, out + pos ); emu_play( remain, out + pos );
track_ended_ |= emu_track_ended_; track_ended_ |= emu_track_ended_;
if ( !ignore_silence_ || out_time > fade_start ) if ( !ignore_silence_ || out_time > fade_start )
{ {
// check end for a new run of silence // check end for a new run of silence
long silence = count_silence( out + pos, remain ); long silence = count_silence( out + pos, remain );
if ( silence < remain ) if ( silence < remain )
silence_time = emu_time - silence; silence_time = emu_time - silence;
if ( emu_time - silence_time >= buf_size ) if ( emu_time - silence_time >= buf_size )
fill_buf(); // cause silence detection on next play() fill_buf(); // cause silence detection on next play()
} }
} }
if ( fade_start >= 0 && out_time > fade_start ) if ( fade_start >= 0 && out_time > fade_start )
handle_fade( out_count, out ); handle_fade( out_count, out );
} }
out_time += out_count; out_time += out_count;
out_time_scaled += out_count * tempo_ / out_channels();
return 0; return 0;
} }
@ -459,3 +484,4 @@ void Gme_Info_::mute_voices_( int ) { check( false ); }
void Gme_Info_::set_tempo_( double ) { } 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_::start_track_( int ) { return "Use full emulator for playback"; }
blargg_err_t Gme_Info_::play_( long, sample_t* ) { 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) // 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 // derived emus must override this if they support multichannel rendering
virtual blargg_err_t set_multi_channel( bool is_enabled ); virtual blargg_err_t set_multi_channel( bool is_enabled );
// Start a track, where 0 is the first track. Also clears warning string. // Start a track, where 0 is the first track. Also clears warning string.
blargg_err_t start_track( int ); blargg_err_t start_track( int );
// Generate 'count' samples info 'buf'. Output is in stereo. Any emulation // Generate 'count' samples info 'buf'. Output is in stereo. Any emulation
// errors set warning string, and major errors also end track. // errors set warning string, and major errors also end track.
typedef short sample_t; typedef short sample_t;
blargg_err_t play( long count, sample_t* buf ); blargg_err_t play( long count, sample_t* buf );
// Informational // Informational
// Sample rate sound is generated at // Sample rate sound is generated at
long sample_rate() const; long sample_rate() const;
// Index of current track or -1 if one hasn't been started // Index of current track or -1 if one hasn't been started
int current_track() const; int current_track() const;
// Number of voices used by currently loaded file // Number of voices used by currently loaded file
int voice_count() const; int voice_count() const;
// Names of voices // Names of voices
const char** voice_names() const; const char** voice_names() const;
bool multi_channel() const; bool multi_channel() const;
// Track status/control // Track status/control
// Number of milliseconds (1000 msec = 1 second) played since beginning of track // Number of milliseconds (1000 msec = 1 second) played since beginning of track
long tell() const; long tell() const;
// Number of samples generated since beginning of track // Number of samples generated since beginning of track
long tell_samples() const; 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. // Seek to new time in track. Seeking backwards or far forward can take a while.
blargg_err_t seek( long msec ); blargg_err_t seek( long msec );
// Equivalent to restarting track then skipping n samples // Equivalent to restarting track then skipping n samples
blargg_err_t seek_samples( long n ); 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 // Skip n samples
blargg_err_t skip( long n ); blargg_err_t skip( long n );
// True if a track has reached its end // True if a track has reached its end
bool track_ended() const; bool track_ended() const;
// Set start time and length of track fade out. Once fade ends track_ended() returns // 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. // true. Fade time can be changed while track is playing.
void set_fade( long start_msec, long length_msec = 8000 ); void set_fade( long start_msec, long length_msec = 8000 );
// Controls whether or not to automatically load and obey track length // Controls whether or not to automatically load and obey track length
// metadata for supported emulators. // metadata for supported emulators.
// //
@ -76,45 +82,48 @@ public:
// Disable automatic end-of-track detection and skipping of silence at beginning // Disable automatic end-of-track detection and skipping of silence at beginning
void ignore_silence( bool disable = true ); void ignore_silence( bool disable = true );
// Info for current track // Info for current track
using Gme_File::track_info; using Gme_File::track_info;
blargg_err_t track_info( track_info_t* out ) const; blargg_err_t track_info( track_info_t* out ) const;
// Sound customization // Sound customization
// Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. // 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. // Track length as returned by track_info() assumes a tempo of 1.0.
void set_tempo( double ); void set_tempo( double );
// Mute/unmute voice i, where voice 0 is first voice // Mute/unmute voice i, where voice 0 is first voice
void mute_voice( int index, bool mute = true ); 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, // 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. // 0 unmutes them all, 0x01 mutes just the first voice, etc.
void mute_voices( int mask ); 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. // Change overall output amplitude, where 1.0 results in minimal clamping.
// Must be called before set_sample_rate(). // Must be called before set_sample_rate().
void set_gain( double ); void set_gain( double );
// Request use of custom multichannel buffer. Only supported by "classic" emulators; // 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(). // on others this has no effect. Should be called only once *before* set_sample_rate().
virtual void set_buffer( Multi_Buffer* ) { } virtual void set_buffer( Multi_Buffer* ) { }
// Enables/disables accurate emulation options, if any are supported. Might change // Enables/disables accurate emulation options, if any are supported. Might change
// equalizer settings. // equalizer settings.
void enable_accuracy( bool enable = true ); void enable_accuracy( bool enable = true );
// Sound equalization (treble/bass) // Sound equalization (treble/bass)
// Frequency equalizer parameters (see gme.txt) // Frequency equalizer parameters (see gme.txt)
// See gme.h for definition of struct gme_equalizer_t. // See gme.h for definition of struct gme_equalizer_t.
typedef gme_equalizer_t equalizer_t; typedef gme_equalizer_t equalizer_t;
// Current frequency equalizater parameters // Current frequency equalizater parameters
equalizer_t const& equalizer() const; equalizer_t const& equalizer() const;
// Set frequency equalizer parameters // Set frequency equalizer parameters
void set_equalizer( equalizer_t const& ); 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 }; 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 };
return e; return e;
} }
// Equalizer settings for TV speaker // Equalizer settings for TV speaker
static equalizer_t const tv_eq; static equalizer_t const tv_eq;
public: public:
Music_Emu(); Music_Emu();
~Music_Emu(); ~Music_Emu();
@ -142,13 +151,14 @@ protected:
double tempo() const { return tempo_; } double tempo() const { return tempo_; }
void remute_voices(); void remute_voices();
blargg_err_t set_multi_channel_( bool is_enabled ); blargg_err_t set_multi_channel_( bool is_enabled );
virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0;
virtual void set_equalizer_( equalizer_t const& ) { } virtual void set_equalizer_( equalizer_t const& ) { }
virtual void enable_accuracy_( bool /* enable */ ) { } virtual void enable_accuracy_( bool /* enable */ ) { }
virtual void mute_voices_( int mask ) = 0; virtual void mute_voices_( int mask );
virtual void set_tempo_( double ) = 0; virtual void disable_echo_( bool /* disable */);
virtual blargg_err_t start_track_( int ) = 0; // tempo is set before this 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 play_( long count, sample_t* out ) = 0;
virtual blargg_err_t skip_( long count ); virtual blargg_err_t skip_( long count );
protected: protected:
@ -170,23 +180,24 @@ private:
int out_channels() const { return this->multi_channel() ? 2*8 : 2; } int out_channels() const { return this->multi_channel() ? 2*8 : 2; }
long sample_rate_; long sample_rate_;
blargg_long msec_to_samples( blargg_long msec ) const; int32_t msec_to_samples( int32_t msec ) const;
// track-specific // track-specific
int current_track_; int current_track_;
blargg_long out_time; // number of samples played since start of track int32_t out_time; // number of samples played since start of track
blargg_long emu_time; // number of samples emulator has generated since start of track int32_t out_time_scaled; // number of samples played since start of track (scaled with tempo)
bool emu_track_ended_; // emulator has reached end of track 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 bool emu_autoload_playback_limit_; // whether to load and obey track length by default
volatile bool track_ended_; volatile bool track_ended_;
void clear_track_vars(); void clear_track_vars();
void end_track_if_error( blargg_err_t ); void end_track_if_error( blargg_err_t );
// fading // fading
blargg_long fade_start; int32_t fade_start;
int fade_step; int fade_step;
void handle_fade( long count, sample_t* out ); void handle_fade( long count, sample_t* out );
// silence detection // silence detection
int silence_lookahead; // speed to run emulator when looking ahead for silence int silence_lookahead; // speed to run emulator when looking ahead for silence
bool ignore_silence_; bool ignore_silence_;
@ -197,7 +208,7 @@ private:
blargg_vector<sample_t> buf; blargg_vector<sample_t> buf;
void fill_buf(); void fill_buf();
void emu_play( long count, sample_t* out ); void emu_play( long count, sample_t* out );
Multi_Buffer* effects_buffer; Multi_Buffer* effects_buffer;
friend Music_Emu* gme_internal_new_emu_( gme_type_t, int, bool ); friend Music_Emu* gme_internal_new_emu_( gme_type_t, int, bool );
friend void gme_set_stereo_depth( Music_Emu*, double ); 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::set_tempo_( double t ) { tempo_ = t; }
inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); }
inline void Music_Emu::ignore_silence( bool b ) { ignore_silence_ = b; } 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 ) 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::mute_voices_( int ) { }
inline void Music_Emu::disable_echo_( bool ) { }
inline void Music_Emu::set_gain( double g ) inline void Music_Emu::set_gain( double g )
{ {
assert( !sample_rate() ); // you must set gain before setting sample rate 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" #include "blargg_source.h"
int const amp_range = 15; static int const amp_range = 15;
Nes_Apu::Nes_Apu() : Nes_Apu::Nes_Apu() :
square1( &square_synth ), square1( &square_synth ),
@ -25,13 +25,13 @@ Nes_Apu::Nes_Apu() :
dmc.apu = this; dmc.apu = this;
dmc.prg_reader = NULL; dmc.prg_reader = NULL;
irq_notifier_ = NULL; irq_notifier_ = NULL;
oscs [0] = &square1; oscs [0] = &square1;
oscs [1] = &square2; oscs [1] = &square2;
oscs [2] = &triangle; oscs [2] = &triangle;
oscs [3] = &noise; oscs [3] = &noise;
oscs [4] = &dmc; oscs [4] = &dmc;
output( NULL ); output( NULL );
volume( 1.0 ); volume( 1.0 );
reset( false ); reset( false );
@ -49,12 +49,12 @@ void Nes_Apu::enable_nonlinear( double v )
{ {
dmc.nonlinear = true; dmc.nonlinear = true;
square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v );
const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); const double tnd = 0.48 / 202 * nonlinear_tnd_gain();
triangle.synth.volume( 3.0 * tnd ); triangle.synth.volume( 3.0 * tnd );
noise.synth.volume( 2.0 * tnd ); noise.synth.volume( 2.0 * tnd );
dmc.synth.volume( tnd ); dmc.synth.volume( tnd );
square1 .last_amp = 0; square1 .last_amp = 0;
square2 .last_amp = 0; square2 .last_amp = 0;
triangle.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; dmc.pal_mode = pal_mode;
set_tempo( tempo_ ); set_tempo( tempo_ );
square1.reset(); square1.reset();
square2.reset(); square2.reset();
triangle.reset(); triangle.reset();
noise.reset(); noise.reset();
dmc.reset(); dmc.reset();
last_time = 0; last_time = 0;
last_dmc_time = 0; last_dmc_time = 0;
osc_enables = 0; osc_enables = 0;
@ -104,10 +104,10 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac )
frame_delay = 1; frame_delay = 1;
write_register( 0, 0x4017, 0x00 ); write_register( 0, 0x4017, 0x00 );
write_register( 0, 0x4015, 0x00 ); write_register( 0, 0x4015, 0x00 );
for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ )
write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 );
dmc.dac = initial_dmc_dac; dmc.dac = initial_dmc_dac;
if ( !dmc.nonlinear ) if ( !dmc.nonlinear )
triangle.last_amp = 15; triangle.last_amp = 15;
@ -124,7 +124,7 @@ void Nes_Apu::irq_changed()
else if ( new_irq > next_irq ) { else if ( new_irq > next_irq ) {
new_irq = next_irq; new_irq = next_irq;
} }
if ( new_irq != earliest_irq_ ) { if ( new_irq != earliest_irq_ ) {
earliest_irq_ = new_irq; earliest_irq_ = new_irq;
if ( irq_notifier_ ) 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 ) void Nes_Apu::run_until_( nes_time_t end_time )
{ {
require( end_time >= last_time ); require( end_time >= last_time );
if ( end_time == last_time ) if ( end_time == last_time )
return; return;
if ( last_dmc_time < end_time ) if ( last_dmc_time < end_time )
{ {
nes_time_t start = last_dmc_time; nes_time_t start = last_dmc_time;
last_dmc_time = end_time; last_dmc_time = end_time;
dmc.run( start, end_time ); dmc.run( start, end_time );
} }
while ( true ) while ( true )
{ {
// earlier of next frame time or end time // 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 ) if ( time > end_time )
time = end_time; time = end_time;
frame_delay -= time - last_time; frame_delay -= time - last_time;
// run oscs to present // run oscs to present
square1.run( last_time, time ); square1.run( last_time, time );
square2.run( last_time, time ); square2.run( last_time, time );
triangle.run( last_time, time ); triangle.run( last_time, time );
noise.run( last_time, time ); noise.run( last_time, time );
last_time = time; last_time = time;
if ( time == end_time ) if ( time == end_time )
break; // no more frames to run break; // no more frames to run
// take frame-specific actions // take frame-specific actions
frame_delay = frame_period; frame_delay = frame_period;
switch ( frame++ ) switch ( frame++ )
@ -193,30 +193,30 @@ void Nes_Apu::run_until_( nes_time_t end_time )
square2.clock_length( 0x20 ); square2.clock_length( 0x20 );
noise.clock_length( 0x20 ); noise.clock_length( 0x20 );
triangle.clock_length( 0x80 ); // different bit for halt flag on triangle triangle.clock_length( 0x80 ); // different bit for halt flag on triangle
square1.clock_sweep( -1 ); square1.clock_sweep( -1 );
square2.clock_sweep( 0 ); square2.clock_sweep( 0 );
// frame 2 is slightly shorter in mode 1 // frame 2 is slightly shorter in mode 1
if ( dmc.pal_mode && frame == 3 ) if ( dmc.pal_mode && frame == 3 )
frame_delay -= 2; frame_delay -= 2;
break; break;
case 1: case 1:
// frame 1 is slightly shorter in mode 0 // frame 1 is slightly shorter in mode 0
if ( !dmc.pal_mode ) if ( !dmc.pal_mode )
frame_delay -= 2; frame_delay -= 2;
break; break;
case 3: case 3:
frame = 0; frame = 0;
// frame 3 is almost twice as long in mode 1 // frame 3 is almost twice as long in mode 1
if ( frame_mode & 0x80 ) if ( frame_mode & 0x80 )
frame_delay += frame_period - (dmc.pal_mode ? 2 : 6); frame_delay += frame_period - (dmc.pal_mode ? 2 : 6);
break; break;
} }
// clock envelopes and linear counter every frame // clock envelopes and linear counter every frame
triangle.clock_linear_counter(); triangle.clock_linear_counter();
square1.clock_envelope(); square1.clock_envelope();
@ -239,7 +239,7 @@ void Nes_Apu::end_frame( nes_time_t end_time )
{ {
if ( end_time > last_time ) if ( end_time > last_time )
run_until_( end_time ); run_until_( end_time );
if ( dmc.nonlinear ) if ( dmc.nonlinear )
{ {
zero_apu_osc( &square1, last_time ); 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( &noise, last_time );
zero_apu_osc( &dmc, last_time ); zero_apu_osc( &dmc, last_time );
} }
// make times relative to new frame // make times relative to new frame
last_time -= end_time; last_time -= end_time;
require( last_time >= 0 ); require( last_time >= 0 );
last_dmc_time -= end_time; last_dmc_time -= end_time;
require( last_dmc_time >= 0 ); require( last_dmc_time >= 0 );
if ( next_irq != no_irq ) { if ( next_irq != no_irq ) {
next_irq -= end_time; next_irq -= end_time;
check( next_irq >= 0 ); 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] = { static const unsigned char length_table [0x20] = {
0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, 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, 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16,
0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E 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( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx)
require( (unsigned) data <= 0xFF ); require( (unsigned) data <= 0xFF );
// Ignore addresses outside range // Ignore addresses outside range
if ( unsigned (addr - start_addr) > end_addr - start_addr ) if ( unsigned (addr - start_addr) > end_addr - start_addr )
return; return;
run_until_( time ); run_until_( time );
if ( addr < 0x4014 ) if ( addr < 0x4014 )
{ {
// Write to channel // Write to channel
int osc_index = (addr - start_addr) >> 2; int osc_index = (addr - start_addr) >> 2;
Nes_Osc* osc = oscs [osc_index]; Nes_Osc* osc = oscs [osc_index];
int reg = addr & 3; int reg = addr & 3;
osc->regs [reg] = data; osc->regs [reg] = data;
osc->reg_written [reg] = true; osc->reg_written [reg] = true;
if ( osc_index == 4 ) if ( osc_index == 4 )
{ {
// handle DMC specially // 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 // load length counter
if ( (osc_enables >> osc_index) & 1 ) if ( (osc_enables >> osc_index) & 1 )
osc->length_counter = length_table [(data >> 3) & 0x1F]; osc->length_counter = length_table [(data >> 3) & 0x1F];
// reset square phase // reset square phase
if ( osc_index < 2 ) if ( osc_index < 2 )
((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; ((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--; ) for ( int i = osc_count; i--; )
if ( !((data >> i) & 1) ) if ( !((data >> i) & 1) )
oscs [i]->length_counter = 0; oscs [i]->length_counter = 0;
bool recalc_irq = dmc.irq_flag; bool recalc_irq = dmc.irq_flag;
dmc.irq_flag = false; dmc.irq_flag = false;
int old_enables = osc_enables; int old_enables = osc_enables;
osc_enables = data; osc_enables = data;
if ( !(data & 0x10) ) { 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) ) { else if ( !(old_enables & 0x10) ) {
dmc.start(); // dmc just enabled dmc.start(); // dmc just enabled
} }
if ( recalc_irq ) if ( recalc_irq )
irq_changed(); 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
frame_mode = data; frame_mode = data;
bool irq_enabled = !(data & 0x40); bool irq_enabled = !(data & 0x40);
irq_flag &= irq_enabled; irq_flag &= irq_enabled;
next_irq = no_irq; next_irq = no_irq;
// mode 1 // mode 1
frame_delay = (frame_delay & 1); frame_delay = (frame_delay & 1);
frame = 0; frame = 0;
if ( !(data & 0x80) ) if ( !(data & 0x80) )
{ {
// mode 0 // mode 0
@ -361,7 +361,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data )
if ( irq_enabled ) if ( irq_enabled )
next_irq = time + frame_delay + frame_period * 3 + 1; next_irq = time + frame_delay + frame_period * 3 + 1;
} }
irq_changed(); 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 ) int Nes_Apu::read_status( nes_time_t time )
{ {
run_until_( time - 1 ); run_until_( time - 1 );
int result = (dmc.irq_flag << 7) | (irq_flag << 6); int result = (dmc.irq_flag << 7) | (irq_flag << 6);
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
if ( oscs [i]->length_counter ) if ( oscs [i]->length_counter )
result |= 1 << i; result |= 1 << i;
run_until_( time ); run_until_( time );
if ( irq_flag ) if ( irq_flag )
{ {
result |= 0x40; result |= 0x40;
irq_flag = false; irq_flag = false;
irq_changed(); irq_changed();
} }
//debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result ); //debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result );
return result; return result;
} }

View file

@ -6,7 +6,7 @@
#include "blargg_common.h" #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 typedef unsigned nes_addr_t; // 16-bit memory address
#include "Nes_Oscs.h" #include "Nes_Oscs.h"
@ -18,30 +18,30 @@ class Nes_Apu {
public: public:
// Set buffer to generate all sound into, or disable sound if NULL // Set buffer to generate all sound into, or disable sound if NULL
void output( Blip_Buffer* ); void output( Blip_Buffer* );
// Set memory reader callback used by DMC oscillator to fetch samples. // Set memory reader callback used by DMC oscillator to fetch samples.
// When callback is invoked, 'user_data' is passed unchanged as the // When callback is invoked, 'user_data' is passed unchanged as the
// first parameter. // first parameter.
void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL ); 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 // All time values are the number of CPU clock cycles relative to the
// beginning of the current time frame. Before resetting the CPU clock // beginning of the current time frame. Before resetting the CPU clock
// count, call end_frame( last_cpu_time ). // count, call end_frame( last_cpu_time ).
// Write to register (0x4000-0x4017, except 0x4014 and 0x4016) // Write to register (0x4000-0x4017, except 0x4014 and 0x4016)
enum { start_addr = 0x4000 }; static const unsigned int start_addr = 0x4000;
enum { end_addr = 0x4017 }; static const unsigned int end_addr = 0x4017;
void write_register( nes_time_t, nes_addr_t, int data ); void write_register( nes_time_t, nes_addr_t, int data );
// Read from status register at 0x4015 // Read from status register at 0x4015
enum { status_addr = 0x4015 }; static const unsigned int status_addr = 0x4015;
int read_status( nes_time_t ); int read_status( nes_time_t );
// Run all oscillators up to specified time, end current time frame, then // 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 // start a new time frame at time 0. Time frames have no effect on emulation
// and each can be whatever length is convenient. // and each can be whatever length is convenient.
void end_frame( nes_time_t ); void end_frame( nes_time_t );
// Additional optional features (can be ignored without any problem) // Additional optional features (can be ignored without any problem)
// Reset internal frame counter, registers, and all oscillators. // 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 // Set the DMC oscillator's initial DAC value to initial_dmc_dac without
// any audible click. // any audible click.
void reset( bool pal_mode = false, int initial_dmc_dac = 0 ); void reset( bool pal_mode = false, int initial_dmc_dac = 0 );
// Adjust frame period // Adjust frame period
void set_tempo( double ); void set_tempo( double );
// Save/load exact emulation state // Save/load exact emulation state
void save_state( apu_state_t* out ) const; void save_state( apu_state_t* out ) const;
void load_state( apu_state_t const& ); void load_state( apu_state_t const& );
// Set overall volume (default is 1.0) // Set overall volume (default is 1.0)
void volume( double ); void volume( double );
// Set treble equalization (see notes.txt) // Set treble equalization (see notes.txt)
void treble_eq( const blip_eq_t& ); void treble_eq( const blip_eq_t& );
// Set sound output of specific oscillator to buffer. If buffer is NULL, // Set sound output of specific oscillator to buffer. If buffer is NULL,
// the specified oscillator is muted and emulation accuracy is reduced. // the specified oscillator is muted and emulation accuracy is reduced.
// The oscillators are indexed as follows: 0) Square 1, 1) Square 2, // The oscillators are indexed as follows: 0) Square 1, 1) Square 2,
// 2) Triangle, 3) Noise, 4) DMC. // 2) Triangle, 3) Noise, 4) DMC.
enum { osc_count = 5 }; static const int osc_count = 5;
void osc_output( int index, Blip_Buffer* buffer ); void osc_output( int index, Blip_Buffer* buffer );
// Set IRQ time callback that is invoked when the time of earliest IRQ // Set IRQ time callback that is invoked when the time of earliest IRQ
// may have changed, or NULL to disable. When callback is invoked, // may have changed, or NULL to disable. When callback is invoked,
// 'user_data' is passed unchanged as the first parameter. // 'user_data' is passed unchanged as the first parameter.
void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); 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 // 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 // or writes occur. If IRQ is already pending, returns irq_waiting. If no
// IRQ will occur, returns no_irq. // IRQ will occur, returns no_irq.
enum { no_irq = INT_MAX / 2 + 1 }; static const unsigned int no_irq = INT_MAX / 2 + 1;
enum { irq_waiting = 0 }; static const unsigned int irq_waiting = 0;
nes_time_t earliest_irq( nes_time_t ) const; nes_time_t earliest_irq( nes_time_t ) const;
// Count number of DMC reads that would occur if 'run_until( t )' were executed. // 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 // If last_read is not NULL, set *last_read to the earliest time that
// 'count_dmc_reads( time )' would result in the same result. // '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; int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const;
// Time when next DMC memory read will occur // Time when next DMC memory read will occur
nes_time_t next_dmc_read_time() const; nes_time_t next_dmc_read_time() const;
// Run DMC until specified time, so that any DMC memory reads can be // Run DMC until specified time, so that any DMC memory reads can be
// accounted for (i.e. inserting CPU wait states). // accounted for (i.e. inserting CPU wait states).
void run_until( nes_time_t ); void run_until( nes_time_t );
public: public:
Nes_Apu(); Nes_Apu();
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
@ -103,18 +103,18 @@ private:
static double nonlinear_tnd_gain() { return 0.75; } static double nonlinear_tnd_gain() { return 0.75; }
private: private:
friend struct Nes_Dmc; friend struct Nes_Dmc;
// noncopyable // noncopyable
Nes_Apu( const Nes_Apu& ); Nes_Apu( const Nes_Apu& );
Nes_Apu& operator = ( const Nes_Apu& ); Nes_Apu& operator = ( const Nes_Apu& );
Nes_Osc* oscs [osc_count]; Nes_Osc* oscs [osc_count];
Nes_Square square1; Nes_Square square1;
Nes_Square square2; Nes_Square square2;
Nes_Noise noise; Nes_Noise noise;
Nes_Triangle triangle; Nes_Triangle triangle;
Nes_Dmc dmc; Nes_Dmc dmc;
double tempo_; double tempo_;
nes_time_t last_time; // has been run until this time in current frame nes_time_t last_time; // has been run until this time in current frame
nes_time_t last_dmc_time; nes_time_t last_dmc_time;
@ -129,11 +129,11 @@ private:
void (*irq_notifier_)( void* user_data ); void (*irq_notifier_)( void* user_data );
void* irq_data; void* irq_data;
Nes_Square::Synth square_synth; // shared by squares Nes_Square::Synth square_synth; // shared by squares
void irq_changed(); void irq_changed();
void state_restored(); void state_restored();
void run_until_( nes_time_t ); void run_until_( nes_time_t );
// TODO: remove // TODO: remove
friend class Nes_Core; 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 ); return dmc.count_reads( time, last_read );
} }
inline nes_time_t Nes_Dmc::next_read_time() const inline nes_time_t Nes_Dmc::next_read_time() const
{ {
if ( length_counter == 0 ) if ( length_counter == 0 )
return Nes_Apu::no_irq; // not reading return Nes_Apu::no_irq; // not reading
return apu->last_dmc_time + delay + long (bits_remain - 1) * period; 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 ); state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size );
} }
int const st_n = 0x80; enum {
int const st_v = 0x40; st_n = 0x80,
int const st_r = 0x20; st_v = 0x40,
int const st_b = 0x10; st_r = 0x20,
int const st_d = 0x08; st_b = 0x10,
int const st_i = 0x04; st_d = 0x08,
int const st_z = 0x02; st_i = 0x04,
int const st_c = 0x01; st_z = 0x02,
st_c = 0x01
};
void Nes_Cpu::reset( void const* unmapped_page ) 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; irq_time_ = future_nes_time;
end_time_ = future_nes_time; end_time_ = future_nes_time;
error_count_ = 0; 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 ); set_code_page( page_count, unmapped_page );
map_code( 0x2000, 0xE000, unmapped_page, true ); map_code( 0x2000, 0xE000, unmapped_page, true );
map_code( 0x0000, 0x2000, low_mem, true ); map_code( 0x0000, 0x2000, low_mem, true );
blargg_verify_byte_order(); 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( start % page_size == 0 );
require( size % page_size == 0 ); require( size % page_size == 0 );
require( start + size <= 0x10000 ); require( start + size <= 0x10000 );
unsigned page = start / page_size; unsigned page = start / page_size;
for ( unsigned n = size / page_size; n; --n ) for ( unsigned n = size / page_size; n; --n )
{ {
@ -121,7 +123,7 @@ bool Nes_Cpu::run( nes_time_t end_time )
this->state = &s; this->state = &s;
// even on x86, using s.time in place of s_time was slower // even on x86, using s.time in place of s_time was slower
int16_t s_time = s.time; int16_t s_time = s.time;
// registers // registers
uint16_t pc = r.pc; uint16_t pc = r.pc;
uint8_t a = r.a; uint8_t a = r.a;
@ -129,10 +131,10 @@ bool Nes_Cpu::run( nes_time_t end_time )
uint8_t y = r.y; uint8_t y = r.y;
uint16_t sp; uint16_t sp;
SET_SP( r.sp ); SET_SP( r.sp );
// status flags // status flags
#define IS_NEG (nz & 0x8080) #define IS_NEG (nz & 0x8080)
#define CALC_STATUS( out ) do {\ #define CALC_STATUS( out ) do {\
out = status & (st_v | st_d | st_i);\ out = status & (st_v | st_d | st_i);\
out |= ((nz >> 8) | nz) & st_n;\ out |= ((nz >> 8) | nz) & st_n;\
@ -146,7 +148,7 @@ bool Nes_Cpu::run( nes_time_t end_time )
c = nz;\ c = nz;\
nz |= ~in & st_z;\ nz |= ~in & st_z;\
} while ( 0 ) } while ( 0 )
uint8_t status; uint8_t status;
uint16_t c; // carry set if (c & 0x100) != 0 uint16_t c; // carry set if (c & 0x100) != 0
uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 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; uint8_t temp = r.status;
SET_STATUS( temp ); SET_STATUS( temp );
} }
goto loop; goto loop;
dec_clock_loop: dec_clock_loop:
s_time--; s_time--;
loop: loop:
check( (unsigned) GET_SP() < 0x100 ); check( (unsigned) GET_SP() < 0x100 );
check( (unsigned) pc < 0x10000 ); check( (unsigned) pc < 0x10000 );
check( (unsigned) a < 0x100 ); check( (unsigned) a < 0x100 );
check( (unsigned) x < 0x100 ); check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 ); check( (unsigned) y < 0x100 );
check( -32768 <= s_time && s_time < 32767 ); check( -32768 <= s_time && s_time < 32767 );
uint8_t const* instr = s.code_map [pc >> page_bits]; uint8_t const* instr = s.code_map [pc >> page_bits];
uint8_t opcode; uint8_t opcode;
// TODO: eliminate this special case // TODO: eliminate this special case
#if BLARGG_NONPORTABLE #if BLARGG_NONPORTABLE
opcode = instr [pc]; opcode = instr [pc];
@ -180,7 +182,7 @@ loop:
opcode = *instr++; opcode = *instr++;
pc++; pc++;
#endif #endif
static uint8_t const clock_table [256] = static uint8_t const clock_table [256] =
{// 0 1 2 3 4 5 6 7 8 9 A B C D E F {// 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 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 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 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F
}; // 0x00 was 7 and 0xF2 was 2 }; // 0x00 was 7 and 0xF2 was 2
uint16_t data; uint16_t data;
#if !BLARGG_CPU_X86 #if !BLARGG_CPU_X86
if ( s_time >= 0 ) if ( s_time >= 0 )
goto out_of_time; goto out_of_time;
s_time += clock_table [opcode]; s_time += clock_table [opcode];
data = *instr; data = *instr;
switch ( opcode ) switch ( opcode )
{ {
#else #else
@ -218,9 +220,9 @@ loop:
if ( (s_time += data) >= 0 ) if ( (s_time += data) >= 0 )
goto possibly_out_of_time; goto possibly_out_of_time;
almost_out_of_time: almost_out_of_time:
data = *instr; data = *instr;
switch ( opcode ) switch ( opcode )
{ {
possibly_out_of_time: possibly_out_of_time:
@ -246,12 +248,12 @@ possibly_out_of_time:
out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\
cross( temp );\ cross( temp );\
} }
#define IND_X( out ) {\ #define IND_X( out ) {\
uint16_t temp = data + x;\ uint16_t temp = data + x;\
out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\
} }
#define ARITH_ADDR_MODES( op )\ #define ARITH_ADDR_MODES( op )\
case op - 0x04: /* (ind,x) */\ case op - 0x04: /* (ind,x) */\
IND_X( data )\ IND_X( data )\
@ -297,15 +299,15 @@ imm##op:
a = nz = READ_LOW( uint8_t (data + x) ); a = nz = READ_LOW( uint8_t (data + x) );
pc++; pc++;
goto loop; goto loop;
case 0xA5: // LDA zp case 0xA5: // LDA zp
a = nz = READ_LOW( data ); a = nz = READ_LOW( data );
pc++; pc++;
goto loop; goto loop;
case 0xD0: // BNE case 0xD0: // BNE
BRANCH( (uint8_t) nz ); BRANCH( (uint8_t) nz );
case 0x20: { // JSR case 0x20: { // JSR
uint16_t temp = pc + 1; uint16_t temp = pc + 1;
pc = GET_ADDR(); pc = GET_ADDR();
@ -314,37 +316,37 @@ imm##op:
WRITE_LOW( sp, temp ); WRITE_LOW( sp, temp );
goto loop; goto loop;
} }
case 0x4C: // JMP abs case 0x4C: // JMP abs
pc = GET_ADDR(); pc = GET_ADDR();
goto loop; goto loop;
case 0xE8: // INX case 0xE8: // INX
INC_DEC_XY( x, 1 ) INC_DEC_XY( x, 1 )
case 0x10: // BPL case 0x10: // BPL
BRANCH( !IS_NEG ) BRANCH( !IS_NEG )
ARITH_ADDR_MODES( 0xC5 ) // CMP ARITH_ADDR_MODES( 0xC5 ) // CMP
nz = a - data; nz = a - data;
pc++; pc++;
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto loop; goto loop;
case 0x30: // BMI case 0x30: // BMI
BRANCH( IS_NEG ) BRANCH( IS_NEG )
case 0xF0: // BEQ case 0xF0: // BEQ
BRANCH( !(uint8_t) nz ); BRANCH( !(uint8_t) nz );
case 0x95: // STA zp,x case 0x95: // STA zp,x
data = uint8_t (data + x);/*FALLTHRU*/ data = uint8_t (data + x);/*FALLTHRU*/
case 0x85: // STA zp case 0x85: // STA zp
pc++; pc++;
WRITE_LOW( data, a ); WRITE_LOW( data, a );
goto loop; goto loop;
case 0xC8: // INY case 0xC8: // INY
INC_DEC_XY( y, 1 ) INC_DEC_XY( y, 1 )
@ -352,12 +354,12 @@ imm##op:
y = a; y = a;
nz = a; nz = a;
goto loop; goto loop;
case 0x98: // TYA case 0x98: // TYA
a = y; a = y;
nz = y; nz = y;
goto loop; goto loop;
case 0xAD:{// LDA abs case 0xAD:{// LDA abs
unsigned addr = GET_ADDR(); unsigned addr = GET_ADDR();
pc += 2; pc += 2;
@ -365,16 +367,16 @@ imm##op:
a = nz; a = nz;
goto loop; goto loop;
} }
case 0x60: // RTS case 0x60: // RTS
pc = 1 + READ_LOW( sp ); pc = 1 + READ_LOW( sp );
pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) );
sp = (sp - 0xFE) | 0x100; sp = (sp - 0xFE) | 0x100;
goto loop; goto loop;
{ {
uint16_t addr; uint16_t addr;
case 0x99: // STA abs,Y case 0x99: // STA abs,Y
addr = y + GET_ADDR(); addr = y + GET_ADDR();
pc += 2; pc += 2;
@ -384,7 +386,7 @@ imm##op:
goto loop; goto loop;
} }
goto sta_ptr; goto sta_ptr;
case 0x8D: // STA abs case 0x8D: // STA abs
addr = GET_ADDR(); addr = GET_ADDR();
pc += 2; pc += 2;
@ -394,7 +396,7 @@ imm##op:
goto loop; goto loop;
} }
goto sta_ptr; goto sta_ptr;
case 0x9D: // STA abs,X (slightly more common than STA abs) case 0x9D: // STA abs,X (slightly more common than STA abs)
addr = x + GET_ADDR(); addr = x + GET_ADDR();
pc += 2; pc += 2;
@ -408,19 +410,19 @@ imm##op:
WRITE( addr, a ); WRITE( addr, a );
CACHE_TIME(); CACHE_TIME();
goto loop; goto loop;
case 0x91: // STA (ind),Y case 0x91: // STA (ind),Y
IND_Y( NO_PAGE_CROSSING, addr ) IND_Y( NO_PAGE_CROSSING, addr )
pc++; pc++;
goto sta_ptr; goto sta_ptr;
case 0x81: // STA (ind,X) case 0x81: // STA (ind,X)
IND_X( addr ) IND_X( addr )
pc++; pc++;
goto sta_ptr; goto sta_ptr;
} }
case 0xA9: // LDA #imm case 0xA9: // LDA #imm
pc++; pc++;
a = data; a = data;
@ -430,12 +432,12 @@ imm##op:
// common read instructions // common read instructions
{ {
uint16_t addr; uint16_t addr;
case 0xA1: // LDA (ind,X) case 0xA1: // LDA (ind,X)
IND_X( addr ) IND_X( addr )
pc++; pc++;
goto a_nz_read_addr; goto a_nz_read_addr;
case 0xB1:// LDA (ind),Y case 0xB1:// LDA (ind),Y
addr = READ_LOW( data ) + y; addr = READ_LOW( data ) + y;
HANDLE_PAGE_CROSSING( addr ); HANDLE_PAGE_CROSSING( addr );
@ -445,7 +447,7 @@ imm##op:
if ( (addr ^ 0x8000) <= 0x9FFF ) if ( (addr ^ 0x8000) <= 0x9FFF )
goto loop; goto loop;
goto a_nz_read_addr; goto a_nz_read_addr;
case 0xB9: // LDA abs,Y case 0xB9: // LDA abs,Y
HANDLE_PAGE_CROSSING( data + y ); HANDLE_PAGE_CROSSING( data + y );
addr = GET_ADDR() + y; addr = GET_ADDR() + y;
@ -454,7 +456,7 @@ imm##op:
if ( (addr ^ 0x8000) <= 0x9FFF ) if ( (addr ^ 0x8000) <= 0x9FFF )
goto loop; goto loop;
goto a_nz_read_addr; goto a_nz_read_addr;
case 0xBD: // LDA abs,X case 0xBD: // LDA abs,X
HANDLE_PAGE_CROSSING( data + x ); HANDLE_PAGE_CROSSING( data + x );
addr = GET_ADDR() + x; addr = GET_ADDR() + x;
@ -467,39 +469,39 @@ imm##op:
a = nz = READ( addr ); a = nz = READ( addr );
CACHE_TIME(); CACHE_TIME();
goto loop; goto loop;
} }
// Branch // Branch
case 0x50: // BVC case 0x50: // BVC
BRANCH( !(status & st_v) ) BRANCH( !(status & st_v) )
case 0x70: // BVS case 0x70: // BVS
BRANCH( status & st_v ) BRANCH( status & st_v )
case 0xB0: // BCS case 0xB0: // BCS
BRANCH( c & 0x100 ) BRANCH( c & 0x100 )
case 0x90: // BCC case 0x90: // BCC
BRANCH( !(c & 0x100) ) BRANCH( !(c & 0x100) )
// Load/store // Load/store
case 0x94: // STY zp,x case 0x94: // STY zp,x
data = uint8_t (data + x); // FALLTHRU data = uint8_t (data + x); // FALLTHRU
case 0x84: // STY zp case 0x84: // STY zp
pc++; pc++;
WRITE_LOW( data, y ); WRITE_LOW( data, y );
goto loop; goto loop;
case 0x96: // STX zp,y case 0x96: // STX zp,y
data = uint8_t (data + y); // FALLTHRU data = uint8_t (data + y); // FALLTHRU
case 0x86: // STX zp case 0x86: // STX zp
pc++; pc++;
WRITE_LOW( data, x ); WRITE_LOW( data, x );
goto loop; goto loop;
case 0xB6: // LDX zp,y case 0xB6: // LDX zp,y
data = uint8_t (data + y); // FALLTHRU data = uint8_t (data + y); // FALLTHRU
case 0xA6: // LDX zp case 0xA6: // LDX zp
@ -509,7 +511,7 @@ imm##op:
x = data; x = data;
nz = data; nz = data;
goto loop; goto loop;
case 0xB4: // LDY zp,x case 0xB4: // LDY zp,x
data = uint8_t (data + x); // FALLTHRU data = uint8_t (data + x); // FALLTHRU
case 0xA4: // LDY zp case 0xA4: // LDY zp
@ -519,7 +521,7 @@ imm##op:
y = data; y = data;
nz = data; nz = data;
goto loop; goto loop;
case 0xBC: // LDY abs,X case 0xBC: // LDY abs,X
data += x; data += x;
HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
@ -531,7 +533,7 @@ imm##op:
CACHE_TIME(); CACHE_TIME();
goto loop; goto loop;
} }
case 0xBE: // LDX abs,y case 0xBE: // LDX abs,y
data += y; data += y;
HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/
@ -543,13 +545,13 @@ imm##op:
CACHE_TIME(); CACHE_TIME();
goto loop; goto loop;
} }
{ {
uint8_t temp; uint8_t temp;
case 0x8C: // STY abs case 0x8C: // STY abs
temp = y; temp = y;
goto store_abs; goto store_abs;
case 0x8E: // STX abs case 0x8E: // STX abs
temp = x; temp = x;
store_abs: store_abs:
@ -576,7 +578,7 @@ imm##op:
CACHE_TIME(); CACHE_TIME();
goto cpx_data; goto cpx_data;
} }
case 0xE4: // CPX zp case 0xE4: // CPX zp
data = READ_LOW( data );/*FALLTHRU*/ data = READ_LOW( data );/*FALLTHRU*/
case 0xE0: // CPX #imm case 0xE0: // CPX #imm
@ -586,7 +588,7 @@ imm##op:
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto loop; goto loop;
case 0xCC:{// CPY abs case 0xCC:{// CPY abs
unsigned addr = GET_ADDR(); unsigned addr = GET_ADDR();
pc++; pc++;
@ -595,7 +597,7 @@ imm##op:
CACHE_TIME(); CACHE_TIME();
goto cpy_data; goto cpy_data;
} }
case 0xC4: // CPY zp case 0xC4: // CPY zp
data = READ_LOW( data );/*FALLTHRU*/ data = READ_LOW( data );/*FALLTHRU*/
case 0xC0: // CPY #imm case 0xC0: // CPY #imm
@ -605,24 +607,24 @@ imm##op:
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto loop; goto loop;
// Logical // Logical
ARITH_ADDR_MODES( 0x25 ) // AND ARITH_ADDR_MODES( 0x25 ) // AND
nz = (a &= data); nz = (a &= data);
pc++; pc++;
goto loop; goto loop;
ARITH_ADDR_MODES( 0x45 ) // EOR ARITH_ADDR_MODES( 0x45 ) // EOR
nz = (a ^= data); nz = (a ^= data);
pc++; pc++;
goto loop; goto loop;
ARITH_ADDR_MODES( 0x05 ) // ORA ARITH_ADDR_MODES( 0x05 ) // ORA
nz = (a |= data); nz = (a |= data);
pc++; pc++;
goto loop; goto loop;
case 0x2C:{// BIT abs case 0x2C:{// BIT abs
unsigned addr = GET_ADDR(); unsigned addr = GET_ADDR();
pc += 2; pc += 2;
@ -634,7 +636,7 @@ imm##op:
nz <<= 8; // result must be zero, even if N bit is set nz <<= 8; // result must be zero, even if N bit is set
goto loop; goto loop;
} }
case 0x24: // BIT zp case 0x24: // BIT zp
nz = READ_LOW( data ); nz = READ_LOW( data );
pc++; pc++;
@ -644,14 +646,14 @@ imm##op:
goto loop; goto loop;
nz <<= 8; // result must be zero, even if N bit is set nz <<= 8; // result must be zero, even if N bit is set
goto loop; goto loop;
// Add/subtract // Add/subtract
ARITH_ADDR_MODES( 0xE5 ) // SBC ARITH_ADDR_MODES( 0xE5 ) // SBC
case 0xEB: // unofficial equivalent case 0xEB: // unofficial equivalent
data ^= 0xFF; data ^= 0xFF;
goto adc_imm; goto adc_imm;
ARITH_ADDR_MODES( 0x65 ) // ADC ARITH_ADDR_MODES( 0x65 ) // ADC
adc_imm: { adc_imm: {
int16_t carry = c >> 8 & 1; int16_t carry = c >> 8 & 1;
@ -663,7 +665,7 @@ imm##op:
a = (uint8_t) nz; a = (uint8_t) nz;
goto loop; goto loop;
} }
// Shift/rotate // Shift/rotate
case 0x4A: // LSR A case 0x4A: // LSR A
@ -689,7 +691,7 @@ imm##op:
a = (uint8_t) nz; a = (uint8_t) nz;
goto loop; goto loop;
} }
case 0x5E: // LSR abs,X case 0x5E: // LSR abs,X
data += x;/*FALLTHRU*/ data += x;/*FALLTHRU*/
case 0x4E: // LSR abs case 0x4E: // LSR abs
@ -703,11 +705,11 @@ imm##op:
c = temp << 8; c = temp << 8;
goto rotate_common; goto rotate_common;
} }
case 0x3E: // ROL abs,X case 0x3E: // ROL abs,X
data += x; data += x;
goto rol_abs; goto rol_abs;
case 0x1E: // ASL abs,X case 0x1E: // ASL abs,X
data += x;/*FALLTHRU*/ data += x;/*FALLTHRU*/
case 0x0E: // ASL abs case 0x0E: // ASL abs
@ -723,15 +725,15 @@ imm##op:
WRITE( data, (uint8_t) nz ); WRITE( data, (uint8_t) nz );
CACHE_TIME(); CACHE_TIME();
goto loop; goto loop;
case 0x7E: // ROR abs,X case 0x7E: // ROR abs,X
data += x; data += x;
goto ror_abs; goto ror_abs;
case 0x76: // ROR zp,x case 0x76: // ROR zp,x
data = uint8_t (data + x); data = uint8_t (data + x);
goto ror_zp; goto ror_zp;
case 0x56: // LSR zp,x case 0x56: // LSR zp,x
data = uint8_t (data + x);/*FALLTHRU*/ data = uint8_t (data + x);/*FALLTHRU*/
case 0x46: // LSR zp case 0x46: // LSR zp
@ -743,11 +745,11 @@ imm##op:
c = temp << 8; c = temp << 8;
goto write_nz_zp; goto write_nz_zp;
} }
case 0x36: // ROL zp,x case 0x36: // ROL zp,x
data = uint8_t (data + x); data = uint8_t (data + x);
goto rol_zp; goto rol_zp;
case 0x16: // ASL zp,x case 0x16: // ASL zp,x
data = uint8_t (data + x);/*FALLTHRU*/ data = uint8_t (data + x);/*FALLTHRU*/
case 0x06: // ASL zp case 0x06: // ASL zp
@ -757,21 +759,21 @@ imm##op:
nz = c >> 8 & 1; nz = c >> 8 & 1;
nz |= (c = READ_LOW( data ) << 1); nz |= (c = READ_LOW( data ) << 1);
goto write_nz_zp; goto write_nz_zp;
// Increment/decrement // Increment/decrement
case 0xCA: // DEX case 0xCA: // DEX
INC_DEC_XY( x, -1 ) INC_DEC_XY( x, -1 )
case 0x88: // DEY case 0x88: // DEY
INC_DEC_XY( y, -1 ) INC_DEC_XY( y, -1 )
case 0xF6: // INC zp,x case 0xF6: // INC zp,x
data = uint8_t (data + x);/*FALLTHRU*/ data = uint8_t (data + x);/*FALLTHRU*/
case 0xE6: // INC zp case 0xE6: // INC zp
nz = 1; nz = 1;
goto add_nz_zp; goto add_nz_zp;
case 0xD6: // DEC zp,x case 0xD6: // DEC zp,x
data = uint8_t (data + x);/*FALLTHRU*/ data = uint8_t (data + x);/*FALLTHRU*/
case 0xC6: // DEC zp case 0xC6: // DEC zp
@ -782,21 +784,21 @@ imm##op:
pc++; pc++;
WRITE_LOW( data, nz ); WRITE_LOW( data, nz );
goto loop; goto loop;
case 0xFE: // INC abs,x case 0xFE: // INC abs,x
data = x + GET_ADDR(); data = x + GET_ADDR();
goto inc_ptr; goto inc_ptr;
case 0xEE: // INC abs case 0xEE: // INC abs
data = GET_ADDR(); data = GET_ADDR();
inc_ptr: inc_ptr:
nz = 1; nz = 1;
goto inc_common; goto inc_common;
case 0xDE: // DEC abs,x case 0xDE: // DEC abs,x
data = x + GET_ADDR(); data = x + GET_ADDR();
goto dec_ptr; goto dec_ptr;
case 0xCE: // DEC abs case 0xCE: // DEC abs
data = GET_ADDR(); data = GET_ADDR();
dec_ptr: dec_ptr:
@ -808,14 +810,14 @@ imm##op:
WRITE( data, (uint8_t) nz ); WRITE( data, (uint8_t) nz );
CACHE_TIME(); CACHE_TIME();
goto loop; goto loop;
// Transfer // Transfer
case 0xAA: // TAX case 0xAA: // TAX
x = a; x = a;
nz = a; nz = a;
goto loop; goto loop;
case 0x8A: // TXA case 0x8A: // TXA
a = x; a = x;
nz = x; nz = x;
@ -824,22 +826,22 @@ imm##op:
case 0x9A: // TXS case 0x9A: // TXS
SET_SP( x ); // verified (no flag change) SET_SP( x ); // verified (no flag change)
goto loop; goto loop;
case 0xBA: // TSX case 0xBA: // TSX
x = nz = GET_SP(); x = nz = GET_SP();
goto loop; goto loop;
// Stack // Stack
case 0x48: // PHA case 0x48: // PHA
PUSH( a ); // verified PUSH( a ); // verified
goto loop; goto loop;
case 0x68: // PLA case 0x68: // PLA
a = nz = READ_LOW( sp ); a = nz = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100; sp = (sp - 0xFF) | 0x100;
goto loop; goto loop;
case 0x40:{// RTI case 0x40:{// RTI
uint8_t temp = READ_LOW( sp ); uint8_t temp = READ_LOW( sp );
pc = READ_LOW( 0x100 | (sp - 0xFF) ); pc = READ_LOW( 0x100 | (sp - 0xFF) );
@ -849,14 +851,14 @@ imm##op:
SET_STATUS( temp ); SET_STATUS( temp );
if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change
this->r.status = status; // update externally-visible I flag 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 ( delta <= 0 ) goto loop;
if ( status & st_i ) goto loop; if ( status & st_i ) goto loop;
s_time += delta; s_time += delta;
s.base = irq_time_; s.base = irq_time_;
goto loop; goto loop;
} }
case 0x28:{// PLP case 0x28:{// PLP
uint8_t temp = READ_LOW( sp ); uint8_t temp = READ_LOW( sp );
sp = (sp - 0xFF) | 0x100; sp = (sp - 0xFF) | 0x100;
@ -868,14 +870,14 @@ imm##op:
goto handle_sei; goto handle_sei;
goto handle_cli; goto handle_cli;
} }
case 0x08: { // PHP case 0x08: { // PHP
uint8_t temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
PUSH( temp | (st_b | st_r) ); PUSH( temp | (st_b | st_r) );
goto loop; goto loop;
} }
case 0x6C:{// JMP (ind) case 0x6C:{// JMP (ind)
data = GET_ADDR(); data = GET_ADDR();
check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space
@ -885,32 +887,32 @@ imm##op:
pc |= page [PAGE_OFFSET( data )] << 8; pc |= page [PAGE_OFFSET( data )] << 8;
goto loop; goto loop;
} }
case 0x00: // BRK case 0x00: // BRK
goto handle_brk; goto handle_brk;
// Flags // Flags
case 0x38: // SEC case 0x38: // SEC
c = (uint16_t) ~0; c = (uint16_t) ~0;
goto loop; goto loop;
case 0x18: // CLC case 0x18: // CLC
c = 0; c = 0;
goto loop; goto loop;
case 0xB8: // CLV case 0xB8: // CLV
status &= ~st_v; status &= ~st_v;
goto loop; goto loop;
case 0xD8: // CLD case 0xD8: // CLD
status &= ~st_d; status &= ~st_d;
goto loop; goto loop;
case 0xF8: // SED case 0xF8: // SED
status |= st_d; status |= st_d;
goto loop; goto loop;
case 0x58: // CLI case 0x58: // CLI
if ( !(status & st_i) ) if ( !(status & st_i) )
goto loop; goto loop;
@ -918,7 +920,7 @@ imm##op:
handle_cli: { handle_cli: {
//debug_printf( "CLI at %d\n", TIME ); //debug_printf( "CLI at %d\n", TIME );
this->r.status = status; // update externally-visible I flag 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 ( delta <= 0 )
{ {
if ( TIME < irq_time_ ) if ( TIME < irq_time_ )
@ -929,38 +931,74 @@ imm##op:
s_time += delta; s_time += delta;
if ( s_time < 0 ) if ( s_time < 0 )
goto loop; goto loop;
if ( delta >= s_time + 1 ) if ( delta >= s_time + 1 )
{ {
s.base += s_time + 1; s.base += s_time + 1;
s_time = -1; s_time = -1;
goto loop; goto loop;
} }
// TODO: implement // TODO: implement
delayed_cli: delayed_cli:
debug_printf( "Delayed CLI not emulated\n" ); debug_printf( "Delayed CLI not emulated\n" );
goto loop; goto loop;
} }
case 0x78: // SEI case 0x78: // SEI
if ( status & st_i ) if ( status & st_i )
goto loop; goto loop;
status |= st_i; status |= st_i;
handle_sei: { handle_sei: {
this->r.status = status; // update externally-visible I flag 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.base = end_time_;
s_time += delta; s_time += delta;
if ( s_time < 0 ) if ( s_time < 0 )
goto loop; goto loop;
debug_printf( "Delayed SEI not emulated\n" ); debug_printf( "Delayed SEI not emulated\n" );
goto loop; goto loop;
} }
// Unofficial // 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 // SKW - Skip word
case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC:
HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/ 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: case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4:
pc++; pc++;
goto loop; goto loop;
// NOP // NOP
case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA:
goto loop; goto loop;
@ -981,9 +1019,9 @@ imm##op:
case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52:
case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2:
goto stop; goto stop;
// Unimplemented // Unimplemented
case 0xFF: // force 256-entry jump table for optimization purposes case 0xFF: // force 256-entry jump table for optimization purposes
c |= 1;/*FALLTHRU*/ c |= 1;/*FALLTHRU*/
default: default:
@ -998,31 +1036,29 @@ imm##op:
len = 2; len = 2;
pc += len; pc += len;
error_count_++; error_count_++;
if ( (opcode >> 4) == 0x0B ) if ( (opcode >> 4) == 0x0B )
{ {
if ( opcode == 0xB3 )
data = READ_LOW( data );
if ( opcode != 0xB7 ) if ( opcode != 0xB7 )
HANDLE_PAGE_CROSSING( data + y ); HANDLE_PAGE_CROSSING( data + y );
} }
goto loop; goto loop;
} }
assert( false ); assert( false );
int result_; int result_;
handle_brk: handle_brk:
pc++; pc++;
result_ = 4; result_ = 4;
interrupt: interrupt:
{ {
s_time += 7; s_time += 7;
WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); WRITE_LOW( 0x100 | (sp - 1), pc >> 8 );
WRITE_LOW( 0x100 | (sp - 2), pc ); WRITE_LOW( 0x100 | (sp - 2), pc );
pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ );
sp = (sp - 3) | 0x100; sp = (sp - 3) | 0x100;
uint8_t temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
@ -1030,15 +1066,15 @@ interrupt:
if ( result_ ) if ( result_ )
temp |= st_b; // TODO: incorrectly sets B flag for IRQ temp |= st_b; // TODO: incorrectly sets B flag for IRQ
WRITE_LOW( sp, temp ); WRITE_LOW( sp, temp );
this->r.status = status |= st_i; 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; if ( delta >= 0 ) goto loop;
s_time += delta; s_time += delta;
s.base = end_time_; s.base = end_time_;
goto loop; goto loop;
} }
out_of_time: out_of_time:
pc--; pc--;
FLUSH_TIME(); FLUSH_TIME();
@ -1048,26 +1084,26 @@ out_of_time:
goto interrupt; goto interrupt;
if ( s_time < 0 ) if ( s_time < 0 )
goto loop; goto loop;
stop: stop:
s.time = s_time; s.time = s_time;
r.pc = pc; r.pc = pc;
r.sp = GET_SP(); r.sp = GET_SP();
r.a = a; r.a = a;
r.x = x; r.x = x;
r.y = y; r.y = y;
{ {
uint8_t temp; uint8_t temp;
CALC_STATUS( temp ); CALC_STATUS( temp );
r.status = temp; r.status = temp;
} }
this->state_ = s; this->state_ = s;
this->state = &this->state_; this->state = &this->state_;
return s_time < 0; return s_time < 0;
} }

View file

@ -6,7 +6,7 @@
#include "blargg_common.h" #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 typedef unsigned nes_addr_t; // 16-bit address
enum { future_nes_time = INT_MAX / 2 + 1 }; 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, // Clear registers, map low memory and its three mirrors to address 0,
// and mirror unmapped_page in remaining memory // and mirror unmapped_page in remaining memory
void reset( void const* unmapped_page = 0 ); void reset( void const* unmapped_page = 0 );
// Map code memory (memory accessed via the program counter). Start and size // 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 // must be multiple of page_size. If mirror is true, repeats code page
// throughout address range. // throughout address range.
enum { page_size = 0x800 }; enum { page_size = 0x800 };
void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false ); void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false );
// Access emulated memory as CPU does // Access emulated memory as CPU does
uint8_t const* get_code( nes_addr_t ); uint8_t const* get_code( nes_addr_t );
// 2KB of RAM at address 0 // 2KB of RAM at address 0
uint8_t low_mem [0x800]; uint8_t low_mem [0x800];
// NES 6502 registers. Not kept updated during a call to run(). // NES 6502 registers. Not kept updated during a call to run().
struct registers_t { struct registers_t {
uint16_t pc; uint16_t pc;
@ -38,29 +38,29 @@ public:
uint8_t sp; uint8_t sp;
}; };
registers_t r; registers_t r;
// Set end_time and run CPU from current time. Returns true if execution // Set end_time and run CPU from current time. Returns true if execution
// stopped due to encountering bad_opcode. // stopped due to encountering bad_opcode.
bool run( nes_time_t end_time ); bool run( nes_time_t end_time );
// Time of beginning of next instruction to be executed // Time of beginning of next instruction to be executed
nes_time_t time() const { return state->time + state->base; } nes_time_t time() const { return state->time + state->base; }
void set_time( nes_time_t t ) { state->time = t - state->base; } void set_time( nes_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; } void adjust_time( int delta ) { state->time += delta; }
nes_time_t irq_time() const { return irq_time_; } nes_time_t irq_time() const { return irq_time_; }
void set_irq_time( nes_time_t ); void set_irq_time( nes_time_t );
nes_time_t end_time() const { return end_time_; } nes_time_t end_time() const { return end_time_; }
void set_end_time( nes_time_t ); void set_end_time( nes_time_t );
// Number of undefined instructions encountered and skipped // Number of undefined instructions encountered and skipped
void clear_error_count() { error_count_ = 0; } void clear_error_count() { error_count_ = 0; }
unsigned long error_count() const { return error_count_; } unsigned long error_count() const { return error_count_; }
// CPU invokes bad opcode handler if it encounters this // CPU invokes bad opcode handler if it encounters this
enum { bad_opcode = 0xF2 }; enum { bad_opcode = 0xF2 };
public: public:
Nes_Cpu() { state = &state_; } Nes_Cpu() { state = &state_; }
enum { page_bits = 11 }; enum { page_bits = 11 };
@ -77,7 +77,7 @@ private:
nes_time_t irq_time_; nes_time_t irq_time_;
nes_time_t end_time_; nes_time_t end_time_;
unsigned long error_count_; unsigned long error_count_;
void set_code_page( int, void const* ); void set_code_page( int, void const* );
inline int update_end_time( nes_time_t end, nes_time_t irq ); 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> #include <string.h>
int const fract_range = 65536; static int const fract_range = 65536;
void Nes_Fds_Apu::reset() void Nes_Fds_Apu::reset()
{ {
memset( regs_, 0, sizeof regs_ ); memset( regs_, 0, sizeof regs_ );
memset( mod_wave, 0, sizeof mod_wave ); memset( mod_wave, 0, sizeof mod_wave );
last_time = 0; last_time = 0;
env_delay = 0; env_delay = 0;
sweep_delay = 0; sweep_delay = 0;
@ -33,7 +33,7 @@ void Nes_Fds_Apu::reset()
mod_fract = fract_range; mod_fract = fract_range;
mod_pos = 0; mod_pos = 0;
mod_write_pos = 0; mod_write_pos = 0;
static byte const initial_regs [0x0B] = { static byte const initial_regs [0x0B] = {
0x80, // disable envelope 0x80, // disable envelope
0, 0, 0xC0, // disable wave and lfo 0, 0, 0xC0, // disable wave and lfo
@ -70,19 +70,19 @@ void Nes_Fds_Apu::write_( unsigned addr, int data )
else else
env_speed = (data & 0x3F) + 1; env_speed = (data & 0x3F) + 1;
break; break;
case 0x4084: case 0x4084:
if ( data & 0x80 ) if ( data & 0x80 )
sweep_gain = data & 0x3F; sweep_gain = data & 0x3F;
else else
sweep_speed = (data & 0x3F) + 1; sweep_speed = (data & 0x3F) + 1;
break; break;
case 0x4085: case 0x4085:
mod_pos = mod_write_pos; mod_pos = mod_write_pos;
regs (0x4085) = data & 0x7F; regs (0x4085) = data & 0x7F;
break; break;
case 0x4088: case 0x4088:
if ( regs (0x4087) & 0x80 ) 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) ) if ( wave_freq && output_ && !((regs (0x4089) | regs (0x4083)) & 0x80) )
{ {
output_->set_modified(); output_->set_modified();
// master_volume // master_volume
#define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100 #define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100
static unsigned char const master_volumes [4] = { static unsigned char const master_volumes [4] = {
MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 ) MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 )
}; };
int const master_volume = master_volumes [regs (0x4089) & 0x03]; int const master_volume = master_volumes [regs (0x4089) & 0x03];
// lfo_period // lfo_period
blip_time_t lfo_period = regs (0x408A) * lfo_tempo; blip_time_t lfo_period = regs (0x408A) * lfo_tempo;
if ( regs (0x4083) & 0x40 ) if ( regs (0x4083) & 0x40 )
lfo_period = 0; lfo_period = 0;
// sweep setup // sweep setup
blip_time_t sweep_time = last_time + sweep_delay; blip_time_t sweep_time = last_time + sweep_delay;
blip_time_t const sweep_period = lfo_period * sweep_speed; blip_time_t const sweep_period = lfo_period * sweep_speed;
if ( !sweep_period || regs (0x4084) & 0x80 ) if ( !sweep_period || regs (0x4084) & 0x80 )
sweep_time = final_end_time; sweep_time = final_end_time;
// envelope setup // envelope setup
blip_time_t env_time = last_time + env_delay; blip_time_t env_time = last_time + env_delay;
blip_time_t const env_period = lfo_period * env_speed; blip_time_t const env_period = lfo_period * env_speed;
if ( !env_period || regs (0x4080) & 0x80 ) if ( !env_period || regs (0x4080) & 0x80 )
env_time = final_end_time; env_time = final_end_time;
// modulation // modulation
int mod_freq = 0; int mod_freq = 0;
if ( !(regs (0x4087) & 0x80) ) if ( !(regs (0x4087) & 0x80) )
mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086); mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086);
blip_time_t end_time = last_time; blip_time_t end_time = last_time;
do do
{ {
@ -161,7 +161,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
else else
regs (0x4084) |= 0x80; // optimization only regs (0x4084) |= 0x80; // optimization only
} }
// envelope // envelope
if ( env_time <= end_time ) if ( env_time <= end_time )
{ {
@ -173,13 +173,13 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
else else
regs (0x4080) |= 0x80; // optimization only regs (0x4080) |= 0x80; // optimization only
} }
// new end_time // new end_time
blip_time_t const start_time = end_time; blip_time_t const start_time = end_time;
end_time = final_end_time; end_time = final_end_time;
if ( end_time > env_time ) end_time = env_time; if ( end_time > env_time ) end_time = env_time;
if ( end_time > sweep_time ) end_time = sweep_time; if ( end_time > sweep_time ) end_time = sweep_time;
// frequency modulation // frequency modulation
int freq = wave_freq; int freq = wave_freq;
if ( mod_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; blip_time_t mod_time = start_time + (mod_fract + mod_freq - 1) / mod_freq;
if ( end_time > mod_time ) if ( end_time > mod_time )
end_time = mod_time; end_time = mod_time;
// run modulator up to next clock and save old sweep_bias // run modulator up to next clock and save old sweep_bias
int sweep_bias = regs (0x4085); int sweep_bias = regs (0x4085);
mod_fract -= (end_time - start_time) * mod_freq; 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; mod_fract += fract_range;
check( (unsigned) mod_fract <= fract_range ); check( (unsigned) mod_fract <= fract_range );
static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 }; static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 };
int mod = mod_wave [mod_pos]; int mod = mod_wave [mod_pos];
mod_pos = (mod_pos + 1) & (wave_size - 1); 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; new_sweep_bias = 0;
regs (0x4085) = new_sweep_bias; regs (0x4085) = new_sweep_bias;
} }
// apply frequency modulation // apply frequency modulation
sweep_bias = (sweep_bias ^ 0x40) - 0x40; sweep_bias = (sweep_bias ^ 0x40) - 0x40;
int factor = sweep_bias * sweep_gain; 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 ) if ( freq <= 0 )
continue; continue;
} }
// wave // wave
int wave_fract = this->wave_fract; int wave_fract = this->wave_fract;
blip_time_t delay = (wave_fract + freq - 1) / freq; blip_time_t delay = (wave_fract + freq - 1) / freq;
blip_time_t time = start_time + delay; blip_time_t time = start_time + delay;
if ( time <= end_time ) if ( time <= end_time )
{ {
// at least one wave clock within start_time...end_time // at least one wave clock within start_time...end_time
blip_time_t const min_delay = fract_range / freq; blip_time_t const min_delay = fract_range / freq;
int wave_pos = this->wave_pos; int wave_pos = this->wave_pos;
int volume = env_gain; int volume = env_gain;
if ( volume > vol_max ) if ( volume > vol_max )
volume = vol_max; volume = vol_max;
volume *= master_volume; volume *= master_volume;
int const min_fract = min_delay * freq; int const min_fract = min_delay * freq;
do do
{ {
// clock wave // clock wave
@ -254,27 +254,27 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time )
last_amp = amp; last_amp = amp;
synth.offset_inline( time, delta, output_ ); synth.offset_inline( time, delta, output_ );
} }
wave_fract += fract_range - delay * freq; wave_fract += fract_range - delay * freq;
check( unsigned (fract_range - wave_fract) < freq ); check( unsigned (fract_range - wave_fract) < freq );
// delay until next clock // delay until next clock
delay = min_delay; delay = min_delay;
if ( wave_fract > min_fract ) if ( wave_fract > min_fract )
delay++; delay++;
check( delay && delay == (wave_fract + freq - 1) / freq ); check( delay && delay == (wave_fract + freq - 1) / freq );
time += delay; time += delay;
} }
while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong
this->wave_pos = wave_pos; this->wave_pos = wave_pos;
} }
this->wave_fract = wave_fract - (end_time - (time - delay)) * freq; this->wave_fract = wave_fract - (end_time - (time - delay)) * freq;
check( this->wave_fract > 0 ); check( this->wave_fract > 0 );
} }
while ( end_time < final_end_time ); while ( end_time < final_end_time );
env_delay = env_time - final_end_time; check( env_delay >= 0 ); env_delay = env_time - final_end_time; check( env_delay >= 0 );
sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 ); sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 );
} }

View file

@ -6,66 +6,70 @@
#define NES_FDS_APU_H #define NES_FDS_APU_H
#include "blargg_common.h" #include "blargg_common.h"
#include "blargg_source.h"
#include "Blip_Buffer.h" #include "Blip_Buffer.h"
class Nes_Fds_Apu { class Nes_Fds_Apu {
public: public:
// setup // setup
void set_tempo( double ); void set_tempo( double );
enum { osc_count = 1 }; static const int osc_count = 1;
void volume( double ); void volume( double );
void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); }
// emulation // emulation
void reset(); void reset();
enum { io_addr = 0x4040 }; static const unsigned int io_addr = 0x4040;
enum { io_size = 0x53 }; static const unsigned int io_size = 0x53;
void write( blip_time_t time, unsigned addr, int data ); void write( blip_time_t time, unsigned addr, int data );
int read( blip_time_t time, unsigned addr ); int read( blip_time_t time, unsigned addr );
void end_frame( blip_time_t ); void end_frame( blip_time_t );
// FDS has a RAM area at $8000-DFFF
enum { sram_addr = 0x8000 };
byte sram [0x6000];
public: public:
Nes_Fds_Apu(); Nes_Fds_Apu();
void write_( unsigned addr, int data ); void write_( unsigned addr, int data );
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
void osc_output( int, Blip_Buffer* ); void osc_output( int, Blip_Buffer* );
private: private:
enum { wave_size = 0x40 }; static const unsigned int wave_size = 0x40;
enum { master_vol_max = 10 }; static const unsigned int master_vol_max = 10;
enum { vol_max = 0x20 }; static const int vol_max = 0x20;
enum { wave_sample_max = 0x3F }; static const unsigned int wave_sample_max = 0x3F;
unsigned char regs_ [io_size];// last written value to registers unsigned char regs_ [io_size];// last written value to registers
enum { lfo_base_tempo = 8 }; static const unsigned int lfo_base_tempo = 8;
int lfo_tempo; // normally 8; adjusted by set_tempo() int lfo_tempo; // normally 8; adjusted by set_tempo()
int env_delay; int env_delay;
int env_speed; int env_speed;
int env_gain; int env_gain;
int sweep_delay; int sweep_delay;
int sweep_speed; int sweep_speed;
int sweep_gain; int sweep_gain;
int wave_pos; int wave_pos;
int last_amp; int last_amp;
blip_time_t wave_fract; blip_time_t wave_fract;
int mod_fract; int mod_fract;
int mod_pos; int mod_pos;
int mod_write_pos; int mod_write_pos;
unsigned char mod_wave [wave_size]; unsigned char mod_wave [wave_size];
// synthesis // synthesis
blip_time_t last_time; blip_time_t last_time;
Blip_Buffer* output_; Blip_Buffer* output_;
Blip_Synth<blip_med_quality,1> synth; Blip_Synth<blip_med_quality,1> synth;
// allow access to registers by absolute address (i.e. 0x4080) // allow access to registers by absolute address (i.e. 0x4080)
unsigned char& regs( unsigned addr ) { return regs_ [addr - io_addr]; } unsigned char& regs( unsigned addr ) { return regs_ [addr - io_addr]; }
void run_until( blip_time_t ); 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 ) inline void Nes_Fds_Apu::osc_output( int i, Blip_Buffer* buf )
{ {
#ifdef NDEBUG
(void) i;
#endif
assert( (unsigned) i < osc_count ); assert( (unsigned) i < osc_count );
output_ = buf; 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 ) inline int Nes_Fds_Apu::read( blip_time_t time, unsigned addr )
{ {
run_until( time ); run_until( time );
int result = 0xFF; int result = 0xFF;
switch ( addr ) switch ( addr )
{ {
case 0x4090: case 0x4090:
result = env_gain; result = env_gain;
break; break;
case 0x4092: case 0x4092:
result = sweep_gain; result = sweep_gain;
break; break;
default: default:
unsigned i = addr - io_addr; unsigned i = addr - io_addr;
if ( i < wave_size ) if ( i < wave_size )
result = regs_ [i]; result = regs_ [i];
} }
return result | 0x40; 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() void Nes_Fme7_Apu::reset()
{ {
last_time = 0; last_time = 0;
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
oscs [i].last_amp = 0; oscs [i].last_amp = 0;
fme7_apu_state_t* state = this; fme7_apu_state_t* state = this;
memset( state, 0, sizeof *state ); 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 ) void Nes_Fme7_Apu::run_until( blip_time_t end_time )
{ {
require( end_time >= last_time ); require( end_time >= last_time );
for ( int index = 0; index < osc_count; index++ ) for ( int index = 0; index < osc_count; index++ )
{ {
int mode = regs [7] >> index; int mode = regs [7] >> index;
int vol_mode = regs [010 + index]; int vol_mode = regs [010 + index];
int volume = amp_table [vol_mode & 0x0F]; int volume = amp_table [vol_mode & 0x0F];
Blip_Buffer* const osc_output = oscs [index].output; Blip_Buffer* const osc_output = oscs [index].output;
if ( !osc_output ) if ( !osc_output )
continue; continue;
osc_output->set_modified(); osc_output->set_modified();
// check for unsupported mode // check for unsupported mode
#ifndef NDEBUG #ifndef NDEBUG
if ( (mode & 011) <= 001 && vol_mode & 0x1F ) if ( (mode & 011) <= 001 && vol_mode & 0x1F )
debug_printf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", debug_printf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n",
mode, vol_mode & 0x1F ); mode, vol_mode & 0x1F );
#endif #endif
if ( (mode & 001) | (vol_mode & 0x10) ) if ( (mode & 001) | (vol_mode & 0x10) )
volume = 0; // noise and envelope aren't supported volume = 0; // noise and envelope aren't supported
// period // period
int const period_factor = 16; int const period_factor = 16;
unsigned period = (regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor + 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 if ( !period ) // on my AY-3-8910A, period doesn't have extra one added
period = period_factor; period = period_factor;
} }
// current amplitude // current amplitude
int amp = volume; int amp = volume;
if ( !phases [index] ) 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 ); synth.offset( last_time, delta, osc_output );
} }
} }
blip_time_t time = last_time + delays [index]; blip_time_t time = last_time + delays [index];
if ( time < end_time ) if ( time < end_time )
{ {
@ -100,7 +100,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time )
time += period; time += period;
} }
while ( time < end_time ); while ( time < end_time );
oscs [index].last_amp = (delta + volume) >> 1; oscs [index].last_amp = (delta + volume) >> 1;
phases [index] = (delta > 0); phases [index] = (delta > 0);
} }
@ -109,13 +109,13 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time )
// maintain phase when silent // maintain phase when silent
int count = (end_time - time + period - 1) / period; int count = (end_time - time + period - 1) / period;
phases [index] ^= count & 1; phases [index] ^= count & 1;
time += (blargg_long) count * period; time += (int32_t) count * period;
} }
} }
delays [index] = time - end_time; delays [index] = time - end_time;
} }
last_time = end_time; last_time = end_time;
} }

View file

@ -9,7 +9,7 @@
struct fme7_apu_state_t struct fme7_apu_state_t
{ {
enum { reg_count = 14 }; static const unsigned int reg_count = 14;
uint8_t regs [reg_count]; uint8_t regs [reg_count];
uint8_t phases [3]; // 0 or 1 uint8_t phases [3]; // 0 or 1
uint8_t latch; uint8_t latch;
@ -23,23 +23,23 @@ public:
void volume( double ); void volume( double );
void treble_eq( blip_eq_t const& ); void treble_eq( blip_eq_t const& );
void output( Blip_Buffer* ); void output( Blip_Buffer* );
enum { osc_count = 3 }; static const int osc_count = 3;
void osc_output( int index, Blip_Buffer* ); void osc_output( int index, Blip_Buffer* );
void end_frame( blip_time_t ); void end_frame( blip_time_t );
void save_state( fme7_apu_state_t* ) const; void save_state( fme7_apu_state_t* ) const;
void load_state( fme7_apu_state_t const& ); void load_state( fme7_apu_state_t const& );
// Mask and addresses of registers // Mask and addresses of registers
enum { addr_mask = 0xE000 }; static const unsigned int addr_mask = 0xE000;
enum { data_addr = 0xE000 }; static const unsigned int data_addr = 0xE000;
enum { latch_addr = 0xC000 }; static const unsigned int latch_addr = 0xC000;
// (addr & addr_mask) == latch_addr // (addr & addr_mask) == latch_addr
void write_latch( int ); void write_latch( int );
// (addr & addr_mask) == data_addr // (addr & addr_mask) == data_addr
void write_data( blip_time_t, int data ); void write_data( blip_time_t, int data );
public: public:
Nes_Fme7_Apu(); Nes_Fme7_Apu();
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
@ -47,18 +47,18 @@ private:
// noncopyable // noncopyable
Nes_Fme7_Apu( const Nes_Fme7_Apu& ); Nes_Fme7_Apu( const Nes_Fme7_Apu& );
Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& ); Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& );
static unsigned char const amp_table [16]; static unsigned char const amp_table [16];
struct { struct {
Blip_Buffer* output; Blip_Buffer* output;
int last_amp; int last_amp;
} oscs [osc_count]; } oscs [osc_count];
blip_time_t last_time; 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; Blip_Synth<blip_good_quality,1> synth;
void run_until( blip_time_t ); void run_until( blip_time_t );
}; };
@ -102,7 +102,7 @@ inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data )
#endif #endif
return; return;
} }
run_until( time ); run_until( time );
regs [latch] = data; regs [latch] = data;
} }
@ -111,7 +111,7 @@ inline void Nes_Fme7_Apu::end_frame( blip_time_t time )
{ {
if ( time > last_time ) if ( time > last_time )
run_until( time ); run_until( time );
assert( last_time >= time ); assert( last_time >= time );
last_time -= time; last_time -= time;
} }

View file

@ -12,11 +12,11 @@ class Nes_Mmc5_Apu : public Nes_Apu {
public: public:
enum { regs_addr = 0x5000 }; enum { regs_addr = 0x5000 };
enum { regs_size = 0x16 }; enum { regs_size = 0x16 };
enum { osc_count = 3 }; enum { osc_count = 3 };
void write_register( blip_time_t, unsigned addr, int data ); void write_register( blip_time_t, unsigned addr, int data );
void osc_output( int i, Blip_Buffer* ); void osc_output( int i, Blip_Buffer* );
enum { exram_size = 1024 }; enum { exram_size = 1024 };
unsigned char exram [exram_size]; 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 case 0x5011: // DAC
Nes_Apu::write_register( time, addr - 0x1000, data ); Nes_Apu::write_register( time, addr - 0x1000, data );
break; break;
case 0x5010: // some things write to this for some reason case 0x5010: // some things write to this for some reason
break; break;
#ifdef BLARGG_DEBUG_H #ifdef BLARGG_DEBUG_H
default: default:
debug_printf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data ); 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; last_time = 0;
addr_reg = 0; addr_reg = 0;
int i; int i;
for ( i = 0; i < reg_count; i++ ) for ( i = 0; i < reg_count; i++ )
reg [i] = 0; reg [i] = 0;
for ( i = 0; i < osc_count; i++ ) for ( i = 0; i < osc_count; i++ )
{ {
Namco_Osc& osc = oscs [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 ) void Nes_Namco_Apu::reflect_state( Tagged_Data& data )
{ {
reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg ); reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg );
static const char hex [17] = "0123456789ABCDEF"; static const char hex [17] = "0123456789ABCDEF";
int i; int i;
for ( i = 0; i < reg_count; i++ ) for ( i = 0; i < reg_count; i++ )
reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], &reg [i] ); reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], &reg [i] );
for ( i = 0; i < osc_count; i++ ) for ( i = 0; i < osc_count; i++ )
{ {
reflect_int32( data, BLARGG_4CHAR('D','L','Y','0') + i, &oscs [i].delay ); 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 ) if ( time > last_time )
run_until( time ); run_until( time );
assert( last_time >= time ); assert( last_time >= time );
last_time -= time; last_time -= time;
} }
@ -83,7 +83,7 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time )
if ( !output ) if ( !output )
continue; continue;
output->set_modified(); output->set_modified();
blip_resampled_time_t time = blip_resampled_time_t time =
output->resampled_time( last_time ) + osc.delay; output->resampled_time( last_time ) + osc.delay;
blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); 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]; const uint8_t* osc_reg = &reg [i * 8 + 0x40];
if ( !(osc_reg [4] & 0xE0) ) if ( !(osc_reg [4] & 0xE0) )
continue; continue;
int volume = osc_reg [7] & 15; int volume = osc_reg [7] & 15;
if ( !volume ) if ( !volume )
continue; 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 ) if ( freq < 64 * active_oscs )
continue; // prevent low frequencies from excessively delaying freq changes continue; // prevent low frequencies from excessively delaying freq changes
blip_resampled_time_t period = blip_resampled_time_t period =
output->resampled_duration( 983040 ) / freq * active_oscs; output->resampled_duration( 983040 ) / freq * active_oscs;
int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4;
if ( !wave_size ) if ( !wave_size )
continue; continue;
int last_amp = osc.last_amp; int last_amp = osc.last_amp;
int wave_pos = osc.wave_pos; int wave_pos = osc.wave_pos;
do do
{ {
// read wave sample // 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); int sample = reg [addr >> 1] >> (addr << 2 & 4);
wave_pos++; wave_pos++;
sample = (sample & 15) * volume; sample = (sample & 15) * volume;
// output impulse if amplitude changed // output impulse if amplitude changed
int delta = sample - last_amp; int delta = sample - last_amp;
if ( delta ) if ( delta )
@ -126,20 +126,20 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time )
last_amp = sample; last_amp = sample;
synth.offset_resampled( time, delta, output ); synth.offset_resampled( time, delta, output );
} }
// next sample // next sample
time += period; time += period;
if ( wave_pos >= wave_size ) if ( wave_pos >= wave_size )
wave_pos = 0; wave_pos = 0;
} }
while ( time < end_time ); while ( time < end_time );
osc.wave_pos = wave_pos; osc.wave_pos = wave_pos;
osc.last_amp = last_amp; osc.last_amp = last_amp;
} }
osc.delay = time - end_time; osc.delay = time - end_time;
} }
last_time = nes_end_time; last_time = nes_end_time;
} }

View file

@ -15,24 +15,24 @@ public:
void volume( double ); void volume( double );
void treble_eq( const blip_eq_t& ); void treble_eq( const blip_eq_t& );
void output( Blip_Buffer* ); void output( Blip_Buffer* );
enum { osc_count = 8 }; static const int osc_count = 8;
void osc_output( int index, Blip_Buffer* ); void osc_output( int index, Blip_Buffer* );
void reset(); void reset();
void end_frame( blip_time_t ); void end_frame( blip_time_t );
// Read/write data register is at 0x4800 // 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 ); void write_data( blip_time_t, int );
int read_data(); int read_data();
// Write-only address register is at 0xF800 // Write-only address register is at 0xF800
enum { addr_reg_addr = 0xF800 }; static const unsigned int addr_reg_addr = 0xF800;
void write_addr( int ); void write_addr( int );
// to do: implement save/restore // to do: implement save/restore
void save_state( namco_state_t* out ) const; void save_state( namco_state_t* out ) const;
void load_state( namco_state_t const& ); void load_state( namco_state_t const& );
public: public:
Nes_Namco_Apu(); Nes_Namco_Apu();
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
@ -40,23 +40,23 @@ private:
// noncopyable // noncopyable
Nes_Namco_Apu( const Nes_Namco_Apu& ); Nes_Namco_Apu( const Nes_Namco_Apu& );
Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& );
struct Namco_Osc { struct Namco_Osc {
blargg_long delay; int32_t delay;
Blip_Buffer* output; Blip_Buffer* output;
short last_amp; short last_amp;
short wave_pos; short wave_pos;
}; };
Namco_Osc oscs [osc_count]; Namco_Osc oscs [osc_count];
blip_time_t last_time; blip_time_t last_time;
int addr_reg; int addr_reg;
enum { reg_count = 0x80 }; static const int reg_count = 0x80;
uint8_t reg [reg_count]; uint8_t reg [reg_count];
Blip_Synth<blip_good_quality,15> synth; Blip_Synth<blip_good_quality,15> synth;
uint8_t& access(); uint8_t& access();
void run_until( blip_time_t ); 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 ) void Nes_Square::clock_sweep( int negative_adjust )
{ {
int sweep = regs [1]; int sweep = regs [1];
if ( --sweep_delay < 0 ) if ( --sweep_delay < 0 )
{ {
reg_written [1] = true; reg_written [1] = true;
int period = this->period(); int period = this->period();
int shift = sweep & shift_mask; int shift = sweep & shift_mask;
if ( shift && (sweep & 0x80) && period >= 8 ) if ( shift && (sweep & 0x80) && period >= 8 )
{ {
int offset = period >> shift; int offset = period >> shift;
if ( sweep & negate_flag ) if ( sweep & negate_flag )
offset = negative_adjust - offset; offset = negative_adjust - offset;
if ( period + offset < 0x800 ) if ( period + offset < 0x800 )
{ {
period += offset; period += offset;
@ -71,7 +71,7 @@ void Nes_Square::clock_sweep( int negative_adjust )
} }
} }
} }
if ( reg_written [1] ) { if ( reg_written [1] ) {
reg_written [1] = false; reg_written [1] = false;
sweep_delay = (sweep >> 4) & 7; 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; int count = (remain + timer_period - 1) / timer_period;
phase = (phase + count) & (phase_range - 1); phase = (phase + count) & (phase_range - 1);
time += (blargg_long) count * timer_period; time += (int32_t) count * timer_period;
} }
return time; 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 period = this->period();
const int timer_period = (period + 1) * 2; const int timer_period = (period + 1) * 2;
if ( !output ) if ( !output )
{ {
delay = maintain_phase( time + delay, end_time, timer_period ) - end_time; delay = maintain_phase( time + delay, end_time, timer_period ) - end_time;
return; return;
} }
output->set_modified(); output->set_modified();
int offset = period >> (regs [1] & shift_mask); int offset = period >> (regs [1] & shift_mask);
if ( regs [1] & negate_flag ) if ( regs [1] & negate_flag )
offset = 0; offset = 0;
const int volume = this->volume(); const int volume = this->volume();
if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) 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 ); synth.offset( time, -last_amp, output );
last_amp = 0; last_amp = 0;
} }
time += delay; time += delay;
time = maintain_phase( time, end_time, timer_period ); 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 ) if ( phase < duty )
amp ^= volume; amp ^= volume;
{ {
int delta = update_amp( amp ); int delta = update_amp( amp );
if ( delta ) if ( delta )
synth.offset( time, delta, output ); synth.offset( time, delta, output );
} }
time += delay; time += delay;
if ( time < end_time ) 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; const Synth& synth = this->synth;
int delta = amp * 2 - volume; int delta = amp * 2 - volume;
int phase = this->phase; int phase = this->phase;
do { do {
phase = (phase + 1) & (phase_range - 1); phase = (phase + 1) & (phase_range - 1);
if ( phase == 0 || phase == duty ) { 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; time += timer_period;
} }
while ( time < end_time ); while ( time < end_time );
last_amp = (delta + volume) >> 1; last_amp = (delta + volume) >> 1;
this->phase = phase; this->phase = phase;
} }
} }
delay = time - end_time; delay = time - end_time;
} }
@ -173,7 +173,7 @@ void Nes_Triangle::clock_linear_counter()
linear_counter = regs [0] & 0x7F; linear_counter = regs [0] & 0x7F;
else if ( linear_counter ) else if ( linear_counter )
linear_counter--; linear_counter--;
if ( !(regs [0] & 0x80) ) if ( !(regs [0] & 0x80) )
reg_written [3] = false; 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; int count = (remain + timer_period - 1) / timer_period;
phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1);
phase++; phase++;
time += (blargg_long) count * timer_period; time += (int32_t) count * timer_period;
} }
return time; 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; delay = maintain_phase( time, end_time, timer_period ) - end_time;
return; return;
} }
output->set_modified(); output->set_modified();
// to do: track phase when period < 3 // to do: track phase when period < 3
// to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks.
int delta = update_amp( calc_amp() ); int delta = update_amp( calc_amp() );
if ( delta ) if ( delta )
synth.offset( time, delta, output ); synth.offset( time, delta, output );
time += delay; time += delay;
if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) 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 ) else if ( time < end_time )
{ {
Blip_Buffer* const output = this->output; Blip_Buffer* const output = this->output;
int phase = this->phase; int phase = this->phase;
int volume = 1; int volume = 1;
if ( phase > phase_range ) { if ( phase > phase_range ) {
phase -= phase_range; phase -= phase_range;
volume = -volume; volume = -volume;
} }
do { do {
if ( --phase == 0 ) { if ( --phase == 0 ) {
phase = phase_range; phase = phase_range;
@ -246,11 +246,11 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time )
else { else {
synth.offset_inline( time, volume, output ); synth.offset_inline( time, volume, output );
} }
time += timer_period; time += timer_period;
} }
while ( time < end_time ); while ( time < end_time );
if ( volume < 0 ) if ( volume < 0 )
phase += phase_range; phase += phase_range;
this->phase = phase; this->phase = phase;
@ -273,7 +273,7 @@ void Nes_Dmc::reset()
next_irq = Nes_Apu::no_irq; next_irq = Nes_Apu::no_irq;
irq_flag = false; irq_flag = false;
irq_enabled = false; irq_enabled = false;
Nes_Osc::reset(); Nes_Osc::reset();
period = 0x1AC; period = 0x1AC;
} }
@ -294,19 +294,19 @@ int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const
{ {
if ( last_read ) if ( last_read )
*last_read = time; *last_read = time;
if ( length_counter == 0 ) if ( length_counter == 0 )
return 0; // not reading return 0; // not reading
nes_time_t first_read = next_read_time(); nes_time_t first_read = next_read_time();
nes_time_t avail = time - first_read; nes_time_t avail = time - first_read;
if ( avail <= 0 ) if ( avail <= 0 )
return 0; return 0;
int count = (avail - 1) / (period * 8) + 1; int count = (avail - 1) / (period * 8) + 1;
if ( !(regs [0] & loop_flag) && count > length_counter ) if ( !(regs [0] & loop_flag) && count > length_counter )
count = length_counter; count = length_counter;
if ( last_read ) if ( last_read )
{ {
*last_read = first_read + (count - 1) * (period * 8) + 1; *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 == count_reads( *last_read, NULL ) );
check( count - 1 == count_reads( *last_read - 1, NULL ) ); check( count - 1 == count_reads( *last_read - 1, NULL ) );
} }
return count; return count;
} }
@ -357,7 +357,7 @@ void Nes_Dmc::write_register( int addr, int data )
{ {
int old_dac = dac; int old_dac = dac;
dac = data & 0x7F; dac = data & 0x7F;
// adjust last_amp so that "pop" amplitude will be properly non-linear // adjust last_amp so that "pop" amplitude will be properly non-linear
// with respect to change in dac // with respect to change in dac
int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_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 ) if ( delta )
synth.offset( time, delta, output ); synth.offset( time, delta, output );
} }
time += delay; time += delay;
if ( time < end_time ) 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; const int period = this->period;
int bits = this->bits; int bits = this->bits;
int dac = this->dac; int dac = this->dac;
do do
{ {
if ( !silence ) 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 ); synth.offset_inline( time, step, output );
} }
} }
time += period; time += period;
if ( --bits_remain == 0 ) if ( --bits_remain == 0 )
{ {
bits_remain = 8; bits_remain = 8;
@ -458,7 +458,7 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time )
} }
} }
while ( time < end_time ); while ( time < end_time );
this->dac = dac; this->dac = dac;
this->last_amp = dac; this->last_amp = dac;
this->bits = bits; 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 ) void Nes_Noise::run( nes_time_t time, nes_time_t end_time )
{ {
int period = noise_period_table [regs [2] & 15]; int period = noise_period_table [regs [2] & 15];
if ( !output ) if ( !output )
{ {
// TODO: clean up // 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; delay = time + (end_time - time + period - 1) / period * period - end_time;
return; return;
} }
output->set_modified(); output->set_modified();
const int volume = this->volume(); const int volume = this->volume();
int amp = (noise & 1) ? volume : 0; 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 ) if ( delta )
synth.offset( time, delta, output ); synth.offset( time, delta, output );
} }
time += delay; time += delay;
if ( time < end_time ) if ( time < end_time )
{ {
const int mode_flag = 0x80; const int mode_flag = 0x80;
if ( !volume ) if ( !volume )
{ {
// round to next multiple of period // round to next multiple of period
time += (end_time - time + period - 1) / period * period; time += (end_time - time + period - 1) / period * period;
// approximate noise cycling while muted, by shuffling up noise register // approximate noise cycling while muted, by shuffling up noise register
// to do: precise muted noise cycling? // to do: precise muted noise cycling?
if ( !(regs [2] & mode_flag) ) { if ( !(regs [2] & mode_flag) ) {
@ -517,35 +517,35 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time )
else else
{ {
Blip_Buffer* const output = this->output; Blip_Buffer* const output = this->output;
// using resampled time avoids conversion in synth.offset() // using resampled time avoids conversion in synth.offset()
blip_resampled_time_t rperiod = output->resampled_duration( period ); blip_resampled_time_t rperiod = output->resampled_duration( period );
blip_resampled_time_t rtime = output->resampled_time( time ); blip_resampled_time_t rtime = output->resampled_time( time );
int noise = this->noise; int noise = this->noise;
int delta = amp * 2 - volume; int delta = amp * 2 - volume;
const int tap = (regs [2] & mode_flag ? 8 : 13); const int tap = (regs [2] & mode_flag ? 8 : 13);
do { do {
int feedback = (noise << tap) ^ (noise << 14); int feedback = (noise << tap) ^ (noise << 14);
time += period; time += period;
if ( (noise + 1) & 2 ) { if ( (noise + 1) & 2 ) {
// bits 0 and 1 of noise differ // bits 0 and 1 of noise differ
delta = -delta; delta = -delta;
synth.offset_resampled( rtime, delta, output ); synth.offset_resampled( rtime, delta, output );
} }
rtime += rperiod; rtime += rperiod;
noise = (feedback & 0x4000) | (noise >> 1); noise = (feedback & 0x4000) | (noise >> 1);
} }
while ( time < end_time ); while ( time < end_time );
last_amp = (delta + volume) >> 1; last_amp = (delta + volume) >> 1;
this->noise = noise; this->noise = noise;
} }
} }
delay = time - end_time; delay = time - end_time;
} }

View file

@ -17,7 +17,7 @@ struct Nes_Osc
int length_counter;// length counter (0 if unused by oscillator) int length_counter;// length counter (0 if unused by oscillator)
int delay; // delay until next (potential) transition int delay; // delay until next (potential) transition
int last_amp; // last amplitude oscillator was outputting int last_amp; // last amplitude oscillator was outputting
void clock_length( int halt_mask ); void clock_length( int halt_mask );
int period() const { int period() const {
return (regs [3] & 7) * 0x100 + (regs [2] & 0xFF); return (regs [3] & 7) * 0x100 + (regs [2] & 0xFF);
@ -37,7 +37,7 @@ struct Nes_Envelope : Nes_Osc
{ {
int envelope; int envelope;
int env_delay; int env_delay;
void clock_envelope(); void clock_envelope();
int volume() const; int volume() const;
void reset() { void reset() {
@ -55,12 +55,12 @@ struct Nes_Square : Nes_Envelope
enum { phase_range = 8 }; enum { phase_range = 8 };
int phase; int phase;
int sweep_delay; int sweep_delay;
typedef Blip_Synth<blip_good_quality,1> Synth; typedef Blip_Synth<blip_good_quality,1> Synth;
Synth const& synth; // shared between squares Synth const& synth; // shared between squares
Nes_Square( Synth const* s ) : synth( *s ) { } Nes_Square( Synth const* s ) : synth( *s ) { }
void clock_sweep( int adjust ); void clock_sweep( int adjust );
void run( nes_time_t, nes_time_t ); void run( nes_time_t, nes_time_t );
void reset() { void reset() {
@ -78,7 +78,7 @@ struct Nes_Triangle : Nes_Osc
int phase; int phase;
int linear_counter; int linear_counter;
Blip_Synth<blip_med_quality,1> synth; Blip_Synth<blip_med_quality,1> synth;
int calc_amp() const; int calc_amp() const;
void run( nes_time_t, nes_time_t ); void run( nes_time_t, nes_time_t );
void clock_linear_counter(); void clock_linear_counter();
@ -96,7 +96,7 @@ struct Nes_Noise : Nes_Envelope
{ {
int noise; int noise;
Blip_Synth<blip_med_quality,1> synth; Blip_Synth<blip_med_quality,1> synth;
void run( nes_time_t, nes_time_t ); void run( nes_time_t, nes_time_t );
void reset() { void reset() {
noise = 1 << 14; noise = 1 << 14;
@ -115,24 +115,24 @@ struct Nes_Dmc : Nes_Osc
int bits; int bits;
bool buf_full; bool buf_full;
bool silence; bool silence;
enum { loop_flag = 0x40 }; enum { loop_flag = 0x40 };
int dac; int dac;
nes_time_t next_irq; nes_time_t next_irq;
bool irq_enabled; bool irq_enabled;
bool irq_flag; bool irq_flag;
bool pal_mode; bool pal_mode;
bool nonlinear; bool nonlinear;
int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function
void* prg_reader_data; void* prg_reader_data;
Nes_Apu* apu; Nes_Apu* apu;
Blip_Synth<blip_med_quality,1> synth; Blip_Synth<blip_med_quality,1> synth;
void start(); void start();
void write_register( int, int ); void write_register( int, int );
void run( nes_time_t, nes_time_t ); 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) osc_index < osc_count );
require( (unsigned) reg < reg_count ); require( (unsigned) reg < reg_count );
run_until( time ); run_until( time );
oscs [osc_index].regs [reg] = data; oscs [osc_index].regs [reg] = data;
} }
@ -65,21 +65,21 @@ void Nes_Vrc6_Apu::end_frame( blip_time_t time )
{ {
if ( time > last_time ) if ( time > last_time )
run_until( time ); run_until( time );
assert( last_time >= time ); assert( last_time >= time );
last_time -= time; last_time -= time;
} }
void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const 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; out->saw_amp = oscs [2].amp;
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
{ {
Vrc6_Osc const& osc = oscs [i]; Vrc6_Osc const& osc = oscs [i];
for ( int r = 0; r < reg_count; r++ ) for ( int r = 0; r < reg_count; r++ )
out->regs [i] [r] = osc.regs [r]; out->regs [i] [r] = osc.regs [r];
out->delays [i] = osc.delay; out->delays [i] = osc.delay;
out->phases [i] = osc.phase; 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]; Vrc6_Osc& osc = oscs [i];
for ( int r = 0; r < reg_count; r++ ) for ( int r = 0; r < reg_count; r++ )
osc.regs [r] = in.regs [i] [r]; osc.regs [r] = in.regs [i] [r];
osc.delay = in.delays [i]; osc.delay = in.delays [i];
osc.phase = in.phases [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 ) if ( !output )
return; return;
output->set_modified(); output->set_modified();
int volume = osc.regs [0] & 15; int volume = osc.regs [0] & 15;
if ( !(osc.regs [2] & 0x80) ) if ( !(osc.regs [2] & 0x80) )
volume = 0; volume = 0;
int gate = osc.regs [0] & 0x80; int gate = osc.regs [0] & 0x80;
int duty = ((osc.regs [0] >> 4) & 7) + 1; int duty = ((osc.regs [0] >> 4) & 7) + 1;
int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; 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; osc.last_amp += delta;
square_synth.offset( time, delta, output ); square_synth.offset( time, delta, output );
} }
time += osc.delay; time += osc.delay;
osc.delay = 0; osc.delay = 0;
int period = osc.period(); 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 ) if ( time < end_time )
{ {
int phase = osc.phase; int phase = osc.phase;
do do
{ {
phase++; phase++;
@ -149,7 +149,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time )
time += period; time += period;
} }
while ( time < end_time ); while ( time < end_time );
osc.phase = phase; osc.phase = phase;
} }
osc.delay = time - end_time; osc.delay = time - end_time;
@ -163,7 +163,7 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time )
if ( !output ) if ( !output )
return; return;
output->set_modified(); output->set_modified();
int amp = osc.amp; int amp = osc.amp;
int amp_step = osc.regs [0] & 0x3F; int amp_step = osc.regs [0] & 0x3F;
blip_time_t time = last_time; 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 period = osc.period() * 2;
int phase = osc.phase; int phase = osc.phase;
do do
{ {
if ( --phase == 0 ) if ( --phase == 0 )
@ -190,26 +190,26 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time )
phase = 7; phase = 7;
amp = 0; amp = 0;
} }
int delta = (amp >> 3) - last_amp; int delta = (amp >> 3) - last_amp;
if ( delta ) if ( delta )
{ {
last_amp = amp >> 3; last_amp = amp >> 3;
saw_synth.offset( time, delta, output ); saw_synth.offset( time, delta, output );
} }
time += period; time += period;
amp = (amp + amp_step) & 0xFF; amp = (amp + amp_step) & 0xFF;
} }
while ( time < end_time ); while ( time < end_time );
osc.phase = phase; osc.phase = phase;
osc.amp = amp; osc.amp = amp;
} }
osc.delay = time - end_time; osc.delay = time - end_time;
} }
osc.last_amp = last_amp; osc.last_amp = last_amp;
} }

View file

@ -21,7 +21,7 @@ public:
void end_frame( blip_time_t ); void end_frame( blip_time_t );
void save_state( vrc6_apu_state_t* ) const; void save_state( vrc6_apu_state_t* ) const;
void load_state( vrc6_apu_state_t const& ); void load_state( vrc6_apu_state_t const& );
// Oscillator 0 write-only registers are at $9000-$9002 // Oscillator 0 write-only registers are at $9000-$9002
// Oscillator 1 write-only registers are at $A000-$A002 // Oscillator 1 write-only registers are at $A000-$A002
// Oscillator 2 write-only registers are at $B000-$B002 // Oscillator 2 write-only registers are at $B000-$B002
@ -29,7 +29,7 @@ public:
enum { base_addr = 0x9000 }; enum { base_addr = 0x9000 };
enum { addr_step = 0x1000 }; enum { addr_step = 0x1000 };
void write_osc( blip_time_t, int osc, int reg, int data ); void write_osc( blip_time_t, int osc, int reg, int data );
public: public:
Nes_Vrc6_Apu(); Nes_Vrc6_Apu();
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
@ -37,7 +37,7 @@ private:
// noncopyable // noncopyable
Nes_Vrc6_Apu( const Nes_Vrc6_Apu& ); Nes_Vrc6_Apu( const Nes_Vrc6_Apu& );
Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& ); Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& );
struct Vrc6_Osc struct Vrc6_Osc
{ {
uint8_t regs [3]; uint8_t regs [3];
@ -46,19 +46,19 @@ private:
int last_amp; int last_amp;
int phase; int phase;
int amp; // only used by saw int amp; // only used by saw
int period() const int period() const
{ {
return (regs [2] & 0x0F) * 0x100L + regs [1] + 1; return (regs [2] & 0x0F) * 0x100L + regs [1] + 1;
} }
}; };
Vrc6_Osc oscs [osc_count]; Vrc6_Osc oscs [osc_count];
blip_time_t last_time; blip_time_t last_time;
Blip_Synth<blip_med_quality,1> saw_synth; Blip_Synth<blip_med_quality,1> saw_synth;
Blip_Synth<blip_good_quality,1> square_synth; Blip_Synth<blip_good_quality,1> square_synth;
void run_until( blip_time_t ); void run_until( blip_time_t );
void run_square( Vrc6_Osc& osc, blip_time_t ); void run_square( Vrc6_Osc& osc, blip_time_t );
void run_saw( blip_time_t ); void run_saw( blip_time_t );

View file

@ -1,7 +1,7 @@
#include "Nes_Vrc7_Apu.h" #include "Nes_Vrc7_Apu.h"
extern "C" { extern "C" {
#include "../ext/emu2413.h" #include "ext/emu2413.h"
} }
#include <string.h> #include <string.h>
@ -10,10 +10,10 @@ extern "C" {
static unsigned char vrc7_inst[(16 + 3) * 8] = 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() Nes_Vrc7_Apu::Nes_Vrc7_Apu()
{ {

View file

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

View file

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

View file

@ -14,7 +14,7 @@ public:
// Equalizer profiles for US NES and Japanese Famicom // Equalizer profiles for US NES and Japanese Famicom
static equalizer_t const nes_eq; static equalizer_t const nes_eq;
static equalizer_t const famicom_eq; static equalizer_t const famicom_eq;
// NSF file header // NSF file header
enum { header_size = 0x80 }; enum { header_size = 0x80 };
struct header_t struct header_t
@ -36,12 +36,12 @@ public:
byte chip_flags; byte chip_flags;
byte unused [4]; byte unused [4];
}; };
// Header for currently loaded file // Header for currently loaded file
header_t const& header() const { return header_; } header_t const& header() const { return header_; }
static gme_type_t static_type() { return gme_nsf_type; } static gme_type_t static_type() { return gme_nsf_type; }
public: public:
// deprecated // deprecated
using Music_Emu::load; using Music_Emu::load;
@ -68,26 +68,26 @@ protected:
nes_addr_t play_addr; nes_addr_t play_addr;
double clock_rate_; double clock_rate_;
bool pal_only; bool pal_only;
// timing // timing
Nes_Cpu::registers_t saved_state; Nes_Cpu::registers_t saved_state;
nes_time_t next_play; nes_time_t next_play;
nes_time_t play_period; nes_time_t play_period;
int play_extra; int play_extra;
int play_ready; int play_ready;
enum { rom_begin = 0x8000 }; enum { rom_begin = 0x8000 };
enum { bank_select_addr = 0x5FF8 }; enum { bank_select_addr = 0x5FF8 };
enum { bank_size = 0x1000 }; enum { bank_size = 0x1000 };
Rom_Data<bank_size> rom; Rom_Data<bank_size> rom;
public: private: friend class Nes_Cpu; public: private: friend class Nes_Cpu;
void cpu_jsr( nes_addr_t ); void cpu_jsr( nes_addr_t );
int cpu_read( nes_addr_t ); int cpu_read( nes_addr_t );
void cpu_write( nes_addr_t, int ); void cpu_write( nes_addr_t, int );
void cpu_write_misc( nes_addr_t, int ); void cpu_write_misc( nes_addr_t, int );
enum { badop_addr = bank_select_addr }; enum { badop_addr = bank_select_addr };
private: private:
byte mmc5_mul [2]; byte mmc5_mul [2];
@ -101,9 +101,9 @@ private:
blargg_vector<const char*> apu_names; blargg_vector<const char*> apu_names;
static int pcm_read( void*, nes_addr_t ); static int pcm_read( void*, nes_addr_t );
blargg_err_t init_sound(); blargg_err_t init_sound();
header_t header_; header_t header_;
enum { sram_addr = 0x6000 }; enum { sram_addr = 0x6000 };
byte sram [0x2000]; byte sram [0x2000];
byte unmapped_code [Nes_Cpu::page_size + 8]; 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 ) ); RETURN_ERR( chars.resize( size + 1 ) );
chars [size] = 0; // in case last string doesn't have terminator chars [size] = 0; // in case last string doesn't have terminator
RETURN_ERR( in.read( &chars [0], size ) ); RETURN_ERR( in.read( &chars [0], size ) );
RETURN_ERR( strs.resize( 128 ) ); RETURN_ERR( strs.resize( 128 ) );
int count = 0; int count = 0;
for ( int i = 0; i < size; i++ ) 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] ) while ( i < size && chars [i] )
i++; i++;
} }
return strs.resize( count ); 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 ) blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
{ {
int const nsfe_info_size = 16; 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 // check header
byte signature [4]; byte signature [4];
blargg_err_t err = in.read( signature, sizeof signature ); 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); return (err == in.eof_error ? gme_wrong_file_type : err);
if ( memcmp( signature, "NSFE", 4 ) ) if ( memcmp( signature, "NSFE", 4 ) )
return gme_wrong_file_type; return gme_wrong_file_type;
// free previous info // free previous info
track_name_data.clear(); track_name_data.clear();
track_names.clear(); track_names.clear();
playlist.clear(); playlist.clear();
track_times.clear(); track_times.clear();
// default nsf header // default nsf header
static const Nsf_Emu::header_t base_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; Nsf_Emu::header_t& header = info;
header = base_header; header = base_header;
// parse tags // parse tags
int phase = 0; int phase = 0;
while ( phase != 3 ) while ( phase != 3 )
@ -136,26 +136,26 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
// read size and tag // read size and tag
byte block_header [2] [4]; byte block_header [2] [4];
RETURN_ERR( in.read( block_header, sizeof block_header ) ); RETURN_ERR( in.read( block_header, sizeof block_header ) );
blargg_long size = get_le32( block_header [0] ); int32_t size = get_le32( block_header [0] );
blargg_long tag = get_le32( block_header [1] ); int32_t tag = get_le32( block_header [1] );
if ( size < 0 ) if ( size < 0 )
return "Corrupt file"; return "Corrupt file";
//debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); //debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) );
switch ( tag ) switch ( tag )
{ {
case BLARGG_4CHAR('O','F','N','I'): { case BLARGG_4CHAR('O','F','N','I'): {
check( phase == 0 ); check( phase == 0 );
if ( size < 8 ) if ( size < 8 )
return "Corrupt file"; return "Corrupt file";
nsfe_info_t finfo; nsfe_info_t finfo;
finfo.track_count = 1; finfo.track_count = 1;
finfo.first_track = 0; 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 ) if ( size > nsfe_info_size )
RETURN_ERR( in.skip( size - nsfe_info_size ) ); RETURN_ERR( in.skip( size - nsfe_info_size ) );
phase = 1; 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 ); memcpy( info.load_addr, finfo.load_addr, 2 * 3 );
break; break;
} }
case BLARGG_4CHAR('K','N','A','B'): case BLARGG_4CHAR('K','N','A','B'):
if ( size > (int) sizeof info.banks ) if ( size > (int) sizeof info.banks )
return "Corrupt file"; return "Corrupt file";
RETURN_ERR( in.read( info.banks, size ) ); RETURN_ERR( in.read( info.banks, size ) );
break; break;
case BLARGG_4CHAR('h','t','u','a'): { case BLARGG_4CHAR('h','t','u','a'): {
blargg_vector<char> chars; blargg_vector<char> chars;
blargg_vector<const char*> strs; blargg_vector<const char*> strs;
RETURN_ERR( read_strs( in, size, chars, strs ) ); RETURN_ERR( read_strs( in, size, chars, strs ) );
int n = strs.size(); int n = strs.size();
if ( n > 3 ) if ( n > 3 )
copy_str( strs [3], info.dumper, sizeof info.dumper ); copy_str( strs [3], info.dumper, sizeof info.dumper );
if ( n > 2 ) if ( n > 2 )
copy_str( strs [2], info.copyright, sizeof info.copyright ); copy_str( strs [2], info.copyright, sizeof info.copyright );
if ( n > 1 ) if ( n > 1 )
copy_str( strs [1], info.author, sizeof info.author ); copy_str( strs [1], info.author, sizeof info.author );
if ( n > 0 ) if ( n > 0 )
copy_str( strs [0], info.game, sizeof info.game ); copy_str( strs [0], info.game, sizeof info.game );
break; break;
} }
case BLARGG_4CHAR('e','m','i','t'): case BLARGG_4CHAR('e','m','i','t'):
RETURN_ERR( track_times.resize( size / 4 ) ); RETURN_ERR( track_times.resize( size / 4 ) );
RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) );
break; break;
case BLARGG_4CHAR('l','b','l','t'): case BLARGG_4CHAR('l','b','l','t'):
RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); RETURN_ERR( read_strs( in, size, track_name_data, track_names ) );
break; break;
case BLARGG_4CHAR('t','s','l','p'): case BLARGG_4CHAR('t','s','l','p'):
RETURN_ERR( playlist.resize( size ) ); RETURN_ERR( playlist.resize( size ) );
RETURN_ERR( in.read( &playlist [0], size ) ); RETURN_ERR( in.read( &playlist [0], size ) );
break; break;
case BLARGG_4CHAR('A','T','A','D'): { case BLARGG_4CHAR('A','T','A','D'): {
check( phase == 1 ); check( phase == 1 );
phase = 2; phase = 2;
@ -225,12 +225,12 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
} }
break; break;
} }
case BLARGG_4CHAR('D','N','E','N'): case BLARGG_4CHAR('D','N','E','N'):
check( phase == 2 ); check( phase == 2 );
phase = 3; phase = 3;
break; break;
default: default:
// tags that can be skipped start with a lowercase character // tags that can be skipped start with a lowercase character
check( islower( (tag >> 24) & 0xFF ) ); check( islower( (tag >> 24) & 0xFF ) );
@ -238,7 +238,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu )
break; break;
} }
} }
return 0; 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() ) if ( (unsigned) remapped < track_names.size() )
Gme_File::copy_field_( out->song, track_names [remapped] ); Gme_File::copy_field_( out->song, track_names [remapped] );
GME_COPY_FIELD( info, out, game ); GME_COPY_FIELD( info, out, game );
GME_COPY_FIELD( info, out, author ); GME_COPY_FIELD( info, out, author );
GME_COPY_FIELD( info, out, copyright ); 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_ struct Nsfe_File : Gme_Info_
{ {
Nsfe_Info info; Nsfe_Info info;
Nsfe_File() { set_type( gme_nsfe_type ); } Nsfe_File() { set_type( gme_nsfe_type ); }
blargg_err_t load_( Data_Reader& in ) blargg_err_t load_( Data_Reader& in )
{ {
RETURN_ERR( info.load( in, 0 ) ); RETURN_ERR( info.load( in, 0 ) );
@ -294,7 +294,7 @@ struct Nsfe_File : Gme_Info_
set_track_count( info.info.track_count ); set_track_count( info.info.track_count );
return 0; return 0;
} }
blargg_err_t track_info_( track_info_t* out, int track ) const blargg_err_t track_info_( track_info_t* out, int track ) const
{ {
return info.track_info_( out, track ); return info.track_info_( out, track );
@ -312,7 +312,7 @@ blargg_err_t Nsfe_Emu::load_( Data_Reader& in )
{ {
if ( loading ) if ( loading )
return Nsf_Emu::load_( in ); return Nsf_Emu::load_( in );
// TODO: this hacky recursion-avoidance could have subtle problems // TODO: this hacky recursion-avoidance could have subtle problems
loading = true; loading = true;
blargg_err_t err = info.load( in, this ); blargg_err_t err = info.load( in, this );

View file

@ -11,7 +11,7 @@
class Nsfe_Info { class Nsfe_Info {
public: public:
blargg_err_t load( Data_Reader&, Nsf_Emu* ); blargg_err_t load( Data_Reader&, Nsf_Emu* );
struct info_t : Nsf_Emu::header_t struct info_t : Nsf_Emu::header_t
{ {
char game [256]; char game [256];
@ -19,15 +19,15 @@ public:
char copyright [256]; char copyright [256];
char dumper [256]; char dumper [256];
} info; } info;
void disable_playlist( bool = true ); void disable_playlist( bool = true );
blargg_err_t track_info_( track_info_t* out, int track ) const; blargg_err_t track_info_( track_info_t* out, int track ) const;
int remap_track( int i ) const; int remap_track( int i ) const;
void unload(); void unload();
Nsfe_Info(); Nsfe_Info();
~Nsfe_Info(); ~Nsfe_Info();
private: private:
@ -42,7 +42,7 @@ private:
class Nsfe_Emu : public Nsf_Emu { class Nsfe_Emu : public Nsf_Emu {
public: public:
static gme_type_t static_type() { return gme_nsfe_type; } static gme_type_t static_type() { return gme_nsfe_type; }
public: public:
// deprecated // deprecated
struct header_t { char tag [4]; }; 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" #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 do
{ {
int bits = 0; int bits = 0;
@ -30,7 +30,7 @@ static void gen_poly( blargg_ulong mask, int count, byte* out )
{ {
// implemented using "Galios configuration" // implemented using "Galios configuration"
bits |= (n & 1) << b; bits |= (n & 1) << b;
n = (n >> 1) ^ (mask & -(n & 1)); n = (n >> 1) ^ (mask & uMinus(n & 1));
} }
while ( b++ < 7 ); while ( b++ < 7 );
*out++ = bits; *out++ = bits;
@ -39,11 +39,11 @@ static void gen_poly( blargg_ulong mask, int count, byte* out )
} }
// poly5 // poly5
int const poly5_len = (1 << 5) - 1; static int const poly5_len = (1 << 5) - 1;
blargg_ulong const poly5_mask = (1UL << poly5_len) - 1; static uint32_t const poly5_mask = (1UL << poly5_len) - 1;
blargg_ulong const poly5 = 0x167C6EA1; 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)); 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( 4, 1, 0 ), sizeof poly4, poly4 );
gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 ); gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 );
gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 ); gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 );
if ( 0 ) // comment out to recauculate poly5 constant if ( 0 ) // comment out to recauculate poly5 constant
{ {
byte poly5 [4]; byte poly5 [4];
gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 ); 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]; poly5 [1] * 0x100L + poly5 [0];
blargg_ulong rev = n & 1; uint32_t rev = n & 1;
for ( int i = 1; i < poly5_len; i++ ) for ( int i = 1; i < poly5_len; i++ )
rev |= (n >> i & 1) << (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; poly4_pos = 0;
polym_pos = 0; polym_pos = 0;
control = 0; control = 0;
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
memset( &oscs [i], 0, offsetof (osc_t,output) ); memset( &oscs [i], 0, offsetof (osc_t,output) );
} }
@ -96,13 +96,13 @@ inline void Sap_Apu::calc_periods()
int divider = 28; int divider = 28;
if ( this->control & 1 ) if ( this->control & 1 )
divider = 114; divider = 114;
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
{ {
osc_t* const osc = &oscs [i]; osc_t* const osc = &oscs [i];
int const osc_reload = osc->regs [0]; // cache 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 }; static byte const fast_bits [osc_count] = { 1 << 6, 1 << 4, 1 << 5, 1 << 3 };
if ( this->control & fast_bits [i] ) 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; period = osc_reload * 0x100L + osc [-1].regs [0] + 7;
if ( !(this->control & fast_bits [i - 1]) ) if ( !(this->control & fast_bits [i - 1]) )
period = (period - 6) * divider; period = (period - 6) * divider;
if ( (osc [-1].regs [1] & 0x1F) > 0x10 ) if ( (osc [-1].regs [1] & 0x1F) > 0x10 )
debug_printf( "Use of slave channel in 16-bit mode not supported\n" ); 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(); calc_periods();
Sap_Apu_Impl* const impl = this->impl; // cache Sap_Apu_Impl* const impl = this->impl; // cache
// 17/9-bit poly selection // 17/9-bit poly selection
byte const* polym = impl->poly17; byte const* polym = impl->poly17;
int polym_len = poly17_len; int polym_len = poly17_len;
@ -135,19 +135,19 @@ void Sap_Apu::run_until( blip_time_t end_time )
polym = impl->poly9; polym = impl->poly9;
} }
polym_pos %= polym_len; polym_pos %= polym_len;
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
{ {
osc_t* const osc = &oscs [i]; osc_t* const osc = &oscs [i];
blip_time_t time = last_time + osc->delay; blip_time_t time = last_time + osc->delay;
blip_time_t const period = osc->period; blip_time_t const period = osc->period;
// output // output
Blip_Buffer* output = osc->output; Blip_Buffer* output = osc->output;
if ( output ) if ( output )
{ {
output->set_modified(); output->set_modified();
int const osc_control = osc->regs [1]; // cache int const osc_control = osc->regs [1]; // cache
int volume = (osc_control & 0x0F) * 2; int volume = (osc_control & 0x0F) * 2;
if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency 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) ) if ( !(osc_control & 0x10) )
volume >>= 1; // inaudible frequency = half volume volume >>= 1; // inaudible frequency = half volume
int delta = volume - osc->last_amp; int delta = volume - osc->last_amp;
if ( delta ) if ( delta )
{ {
osc->last_amp = volume; osc->last_amp = volume;
impl->synth.offset( last_time, delta, output ); impl->synth.offset( last_time, delta, output );
} }
// TODO: doesn't maintain high pass flip-flop (very minor issue) // TODO: doesn't maintain high pass flip-flop (very minor issue)
} }
else else
@ -182,7 +182,7 @@ void Sap_Apu::run_until( blip_time_t end_time )
volume = -volume; volume = -volume;
} }
} }
if ( time < end_time || time2 < end_time ) if ( time < end_time || time2 < end_time )
{ {
// poly source // 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_pos = (poly_pos + osc->delay) % poly_len;
} }
poly_inc -= poly_len; // allows more optimized inner loop below poly_inc -= poly_len; // allows more optimized inner loop below
// square/poly5 wave // square/poly5 wave
blargg_ulong wave = poly5; uint32_t wave = poly5;
check( poly5 & 1 ); // low bit is set for pure wave check( poly5 & 1 ); // low bit is set for pure wave
int poly5_inc = 0; int poly5_inc = 0;
if ( !(osc_control & 0x80) ) 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 ); wave = run_poly5( wave, (osc->delay + poly5_pos) % poly5_len );
poly5_inc = period % poly5_len; poly5_inc = period % poly5_len;
} }
// Run wave and high pass interleved with each catching up to the other. // Run wave and high pass interleved with each catching up to the other.
// Disabled high pass has no performance effect since inner wave loop // Disabled high pass has no performance effect since inner wave loop
// makes no compromise for high pass, and only runs once in that case. // 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 while ( time2 <= time ) // must advance *past* time to avoid hang
time2 += period2; time2 += period2;
// run wave // run wave
blip_time_t end = end_time; blip_time_t end = end_time;
if ( end > time2 ) if ( end > time2 )
@ -262,11 +262,11 @@ void Sap_Apu::run_until( blip_time_t end_time )
} }
} }
while ( time < end_time || time2 < end_time ); while ( time < end_time || time2 < end_time );
osc->phase = poly_pos; osc->phase = poly_pos;
osc->last_amp = osc_last_amp; osc->last_amp = osc_last_amp;
} }
osc->invert = 0; osc->invert = 0;
if ( volume < 0 ) if ( volume < 0 )
{ {
@ -276,18 +276,18 @@ void Sap_Apu::run_until( blip_time_t end_time )
} }
} }
} }
// maintain divider // maintain divider
blip_time_t remain = end_time - time; blip_time_t remain = end_time - time;
if ( remain > 0 ) if ( remain > 0 )
{ {
blargg_long count = (remain + period - 1) / period; int32_t count = (remain + period - 1) / period;
osc->phase ^= count; osc->phase ^= count;
time += count * period; time += count * period;
} }
osc->delay = time - end_time; osc->delay = time - end_time;
} }
// advance polies // advance polies
blip_time_t duration = end_time - last_time; blip_time_t duration = end_time - last_time;
last_time = end_time; last_time = end_time;
@ -329,6 +329,6 @@ void Sap_Apu::end_frame( blip_time_t end_time )
{ {
if ( end_time > last_time ) if ( end_time > last_time )
run_until( end_time ); run_until( end_time );
last_time -= end_time; last_time -= end_time;
} }

View file

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

View file

@ -6,7 +6,7 @@
#include "blargg_common.h" #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 typedef unsigned sap_addr_t; // 16-bit address
enum { future_sap_time = INT_MAX / 2 + 1 }; enum { future_sap_time = INT_MAX / 2 + 1 };
@ -14,11 +14,11 @@ class Sap_Cpu {
public: public:
// Clear all registers and keep pointer to 64K memory passed in // Clear all registers and keep pointer to 64K memory passed in
void reset( void* mem_64k ); void reset( void* mem_64k );
// Run until specified time is reached. Returns true if suspicious/unsupported // Run until specified time is reached. Returns true if suspicious/unsupported
// instruction was encountered at any point during run. // instruction was encountered at any point during run.
bool run( sap_time_t end_time ); bool run( sap_time_t end_time );
// Registers are not updated until run() returns (except I flag in status) // Registers are not updated until run() returns (except I flag in status)
struct registers_t { struct registers_t {
uint16_t pc; uint16_t pc;
@ -29,20 +29,20 @@ public:
uint8_t sp; uint8_t sp;
}; };
registers_t r; registers_t r;
enum { idle_addr = 0xFEFF }; enum { idle_addr = 0xFEFF };
// Time of beginning of next instruction to be executed // Time of beginning of next instruction to be executed
sap_time_t time() const { return state->time + state->base; } sap_time_t time() const { return state->time + state->base; }
void set_time( sap_time_t t ) { state->time = t - state->base; } void set_time( sap_time_t t ) { state->time = t - state->base; }
void adjust_time( int delta ) { state->time += delta; } void adjust_time( int delta ) { state->time += delta; }
sap_time_t irq_time() const { return irq_time_; } sap_time_t irq_time() const { return irq_time_; }
void set_irq_time( sap_time_t ); void set_irq_time( sap_time_t );
sap_time_t end_time() const { return end_time_; } sap_time_t end_time() const { return end_time_; }
void set_end_time( sap_time_t ); void set_end_time( sap_time_t );
public: public:
Sap_Cpu() { state = &state_; } Sap_Cpu() { state = &state_; }
enum { irq_inhibit = 0x04 }; enum { irq_inhibit = 0x04 };
@ -56,7 +56,7 @@ private:
sap_time_t irq_time_; sap_time_t irq_time_;
sap_time_t end_time_; sap_time_t end_time_;
uint8_t* mem; uint8_t* mem;
inline sap_time_t update_end_time( sap_time_t end, sap_time_t irq ); 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" #include "blargg_source.h"
long const base_scanline_period = 114; static long const base_scanline_period = 114;
using std::min; using std::min;
using std::max; using std::max;
@ -27,13 +27,13 @@ using std::max;
Sap_Emu::Sap_Emu() Sap_Emu::Sap_Emu()
{ {
set_type( gme_sap_type ); set_type( gme_sap_type );
static const char* const names [Sap_Apu::osc_count * 2] = { static const char* const names [Sap_Apu::osc_count * 2] = {
"Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 1", "Wave 2", "Wave 3", "Wave 4",
"Wave 5", "Wave 6", "Wave 7", "Wave 8", "Wave 5", "Wave 6", "Wave 7", "Wave 8",
}; };
set_voice_names( names ); set_voice_names( names );
static int const types [Sap_Apu::osc_count * 2] = { static int const types [Sap_Apu::osc_count * 2] = {
wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0,
wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4, 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 ) if ( in >= end )
return -1; return -1;
int n = 0; int n = 0;
while ( in < end ) 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->author [0] = 0;
out->name [0] = 0; out->name [0] = 0;
out->copyright [0] = 0; out->copyright [0] = 0;
if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) )
return gme_wrong_file_type; return gme_wrong_file_type;
byte const* file_end = in + size - 5; byte const* file_end = in + size - 5;
in += 5; in += 5;
while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) ) 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; byte const* line_end = in;
while ( line_end < file_end && *line_end != 0x0D ) while ( line_end < file_end && *line_end != 0x0D )
line_end++; line_end++;
char const* tag = (char const*) in; char const* tag = (char const*) in;
while ( in < line_end && *in > ' ' ) while ( in < line_end && *in > ' ' )
in++; in++;
int tag_len = (char const*) in - tag; int tag_len = (char const*) in - tag;
while ( in < line_end && *in <= ' ' ) in++; while ( in < line_end && *in <= ' ' ) in++;
if ( tag_len <= 0 ) if ( tag_len <= 0 )
{ {
// skip line // 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 'C':
case 'B': case 'B':
break; break;
case 'D': case 'D':
return "Digimusic not supported"; return "Digimusic not supported";
default: default:
return "Unsupported player type"; 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 ); parse_string( in, line_end, sizeof out->copyright, out->copyright );
} }
in = line_end + 2; in = line_end + 2;
} }
if ( in [0] != 0xFF || in [1] != 0xFF ) if ( in [0] != 0xFF || in [1] != 0xFF )
return "ROM data missing"; return "ROM data missing";
out->rom_data = in + 2; out->rom_data = in + 2;
return 0; return 0;
} }
@ -219,16 +219,16 @@ blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const
struct Sap_File : Gme_Info_ struct Sap_File : Gme_Info_
{ {
Sap_Emu::info_t info; Sap_Emu::info_t info;
Sap_File() { set_type( gme_sap_type ); } Sap_File() { set_type( gme_sap_type ); }
blargg_err_t load_mem_( byte const* begin, long size ) blargg_err_t load_mem_( byte const* begin, long size )
{ {
RETURN_ERR( parse_info( begin, size, &info ) ); RETURN_ERR( parse_info( begin, size, &info ) );
set_track_count( info.track_count ); set_track_count( info.track_count );
return 0; return 0;
} }
blargg_err_t track_info_( track_info_t* out, int ) const blargg_err_t track_info_( track_info_t* out, int ) const
{ {
copy_sap_fields( info, out ); 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 ) blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
{ {
file_end = in + size; file_end = in + size;
info.warning = 0; info.warning = 0;
info.type = 'B'; info.type = 'B';
info.stereo = false; info.stereo = false;
@ -256,12 +256,12 @@ blargg_err_t Sap_Emu::load_mem_( byte const* in, long size )
info.music_addr = -1; info.music_addr = -1;
info.fastplay = 312; info.fastplay = 312;
RETURN_ERR( parse_info( in, size, &info ) ); RETURN_ERR( parse_info( in, size, &info ) );
set_warning( info.warning ); set_warning( info.warning );
set_track_count( info.track_count ); 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() ); apu_impl.volume( gain() );
return setup_buffer( 1773447 ); return setup_buffer( 1773447 );
} }
@ -315,7 +315,7 @@ inline void Sap_Emu::call_init( int track )
r.a = track; r.a = track;
run_routine( info.init_addr ); run_routine( info.init_addr );
break; break;
case 'C': case 'C':
r.a = 0x70; r.a = 0x70;
r.x = info.music_addr&0xFF; 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 ) blargg_err_t Sap_Emu::start_track_( int track )
{ {
RETURN_ERR( Classic_Emu::start_track_( track ) ); RETURN_ERR( Classic_Emu::start_track_( track ) );
memset( &mem, 0, sizeof mem ); memset( &mem, 0, sizeof mem );
byte const* in = info.rom_data; 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" ); set_warning( "Invalid file data block" );
break; break;
} }
memcpy( mem.ram + start, in, len ); memcpy( mem.ram + start, in, len );
in += len; in += len;
if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF )
in += 2; in += 2;
} }
apu.reset( &apu_impl ); apu.reset( &apu_impl );
apu2.reset( &apu_impl ); apu2.reset( &apu_impl );
cpu::reset( mem.ram ); cpu::reset( mem.ram );
time_mask = 0; // disables sound during init time_mask = 0; // disables sound during init
call_init( track ); call_init( track );
time_mask = -1; time_mask = -1;
next_play = play_period(); next_play = play_period();
return 0; 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 ); apu.write_data( time() & time_mask, addr, data );
return; return;
} }
if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) && if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) &&
info.stereo ) info.stereo )
{ {
@ -403,7 +403,7 @@ inline void Sap_Emu::call_play()
case 'B': case 'B':
cpu_jsr( info.play_addr ); cpu_jsr( info.play_addr );
break; break;
case 'C': case 'C':
cpu_jsr( info.play_addr + 6 ); cpu_jsr( info.play_addr + 6 );
break; 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 ) if ( cpu::run( duration ) || r.pc > idle_addr )
return "Emulation error (illegal instruction)"; return "Emulation error (illegal instruction)";
if ( r.pc == idle_addr ) if ( r.pc == idle_addr )
{ {
if ( next_play <= duration ) if ( next_play <= duration )
@ -433,7 +433,7 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
} }
} }
} }
duration = time(); duration = time();
next_play -= duration; next_play -= duration;
check( next_play >= 0 ); check( next_play >= 0 );
@ -442,6 +442,6 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int )
apu.end_frame( duration ); apu.end_frame( duration );
if ( info.stereo ) if ( info.stereo )
apu2.end_frame( duration ); apu2.end_frame( duration );
return 0; return 0;
} }

View file

@ -43,21 +43,21 @@ public: private: friend class Sap_Cpu;
void cpu_write_( sap_addr_t, int ); void cpu_write_( sap_addr_t, int );
private: private:
info_t info; info_t info;
byte const* file_end; byte const* file_end;
sap_time_t scanline_period; sap_time_t scanline_period;
sap_time_t next_play; sap_time_t next_play;
sap_time_t time_mask; sap_time_t time_mask;
Sap_Apu apu; Sap_Apu apu;
Sap_Apu apu2; Sap_Apu apu2;
// large items // large items
struct { struct {
byte padding1 [0x100]; byte padding1 [0x100];
byte ram [0x10000 + 0x100]; byte ram [0x10000 + 0x100];
} mem; } mem;
Sap_Apu_Impl apu_impl; Sap_Apu_Impl apu_impl;
sap_time_t play_period() const; sap_time_t play_period() const;
void call_play(); void call_play();
void cpu_jsr( sap_addr_t ); 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 ); synth->offset( time, delta, output );
} }
} }
time += delay; time += delay;
if ( time < end_time ) if ( time < end_time )
{ {
@ -115,7 +115,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
int amp = volume; int amp = volume;
if ( shifter & 1 ) if ( shifter & 1 )
amp = -amp; amp = -amp;
{ {
int delta = amp - last_amp; int delta = amp - last_amp;
if ( delta ) if ( delta )
@ -124,11 +124,11 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
synth.offset( time, delta, output ); synth.offset( time, delta, output );
} }
} }
time += delay; time += delay;
if ( !volume ) if ( !volume )
time = end_time; time = end_time;
if ( time < end_time ) if ( time < end_time )
{ {
Blip_Buffer* const output = this->output; 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; int period = *this->period * 2;
if ( !period ) if ( !period )
period = 16; period = 16;
do do
{ {
int changed = shifter + 1; 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 if ( changed & 2 ) // true if bits 0 and 1 differ
{ {
delta = -delta; delta = -delta;
@ -150,7 +150,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time )
time += period; time += period;
} }
while ( time < end_time ); while ( time < end_time );
this->shifter = shifter; this->shifter = shifter;
this->last_amp = delta >> 1; this->last_amp = delta >> 1;
} }
@ -167,7 +167,7 @@ Sms_Apu::Sms_Apu()
oscs [i] = &squares [i]; oscs [i] = &squares [i];
} }
oscs [3] = &noise; oscs [3] = &noise;
volume( 1.0 ); volume( 1.0 );
reset(); reset();
} }
@ -210,7 +210,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width )
{ {
last_time = 0; last_time = 0;
latch = 0; latch = 0;
if ( !feedback || !noise_width ) if ( !feedback || !noise_width )
{ {
feedback = 0x0009; feedback = 0x0009;
@ -224,7 +224,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width )
noise_feedback = (noise_feedback << 1) | (feedback & 1); noise_feedback = (noise_feedback << 1) | (feedback & 1);
feedback >>= 1; feedback >>= 1;
} }
squares [0].reset(); squares [0].reset();
squares [1].reset(); squares [1].reset();
squares [2].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 ) void Sms_Apu::run_until( blip_time_t end_time )
{ {
require( end_time >= last_time ); // end_time must not be before previous time require( end_time >= last_time ); // end_time must not be before previous time
if ( end_time > last_time ) if ( end_time > last_time )
{ {
// run oscillators // run oscillators
@ -250,7 +250,7 @@ void Sms_Apu::run_until( blip_time_t end_time )
noise.run( last_time, end_time ); noise.run( last_time, end_time );
} }
} }
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 ) if ( end_time > last_time )
run_until( end_time ); run_until( end_time );
assert( last_time >= end_time ); assert( last_time >= end_time );
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 ) void Sms_Apu::write_ggstereo( blip_time_t time, int data )
{ {
require( (unsigned) data <= 0xFF ); require( (unsigned) data <= 0xFF );
run_until( time ); run_until( time );
for ( int i = 0; i < osc_count; i++ ) for ( int i = 0; i < osc_count; i++ )
{ {
Sms_Osc& osc = *oscs [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 ) void Sms_Apu::write_data( blip_time_t time, int data )
{ {
require( (unsigned) data <= 0xFF ); require( (unsigned) data <= 0xFF );
run_until( time ); run_until( time );
if ( data & 0x80 ) if ( data & 0x80 )
latch = data; latch = data;
int index = (latch >> 5) & 3; int index = (latch >> 5) & 3;
if ( latch & 0x10 ) if ( latch & 0x10 )
{ {
@ -323,7 +323,7 @@ void Sms_Apu::write_data( blip_time_t time, int data )
noise.period = &noise_periods [select]; noise.period = &noise_periods [select];
else else
noise.period = &squares [2].period; noise.period = &squares [2].period;
noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback; noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback;
noise.shifter = 0x8000; noise.shifter = 0x8000;
} }

View file

@ -10,34 +10,34 @@ class Sms_Apu {
public: public:
// Set overall volume of all oscillators, where 1.0 is full volume // Set overall volume of all oscillators, where 1.0 is full volume
void volume( double ); void volume( double );
// Set treble equalization // Set treble equalization
void treble_eq( const blip_eq_t& ); void treble_eq( const blip_eq_t& );
// Outputs can be assigned to a single buffer for mono output, or to three // 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). // buffers for stereo output (using Stereo_Buffer to do the mixing).
// Assign all oscillator outputs to specified buffer(s). If buffer // Assign all oscillator outputs to specified buffer(s). If buffer
// is NULL, silences all oscillators. // is NULL, silences all oscillators.
void output( Blip_Buffer* mono ); void output( Blip_Buffer* mono );
void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, // 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, // which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL,
// silences oscillator. // silences oscillator.
enum { osc_count = 4 }; enum { osc_count = 4 };
void osc_output( int index, Blip_Buffer* mono ); void osc_output( int index, Blip_Buffer* mono );
void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right );
// Reset oscillators and internal state // Reset oscillators and internal state
void reset( unsigned noise_feedback = 0, int noise_width = 0 ); void reset( unsigned noise_feedback = 0, int noise_width = 0 );
// Write GameGear left/right assignment byte // Write GameGear left/right assignment byte
void write_ggstereo( blip_time_t, int ); void write_ggstereo( blip_time_t, int );
// Write to data port // Write to data port
void write_data( blip_time_t, int ); void write_data( blip_time_t, int );
// Run all oscillators up to specified time, end current frame, then // Run all oscillators up to specified time, end current frame, then
// start a new frame at time 0. // start a new frame at time 0.
void end_frame( blip_time_t ); void end_frame( blip_time_t );
@ -49,7 +49,7 @@ private:
// noncopyable // noncopyable
Sms_Apu( const Sms_Apu& ); Sms_Apu( const Sms_Apu& );
Sms_Apu& operator = ( const Sms_Apu& ); Sms_Apu& operator = ( const Sms_Apu& );
Sms_Osc* oscs [osc_count]; Sms_Osc* oscs [osc_count];
Sms_Square squares [3]; Sms_Square squares [3];
Sms_Square::Synth square_synth; // used by squares Sms_Square::Synth square_synth; // used by squares
@ -58,7 +58,7 @@ private:
Sms_Noise noise; Sms_Noise noise;
unsigned noise_feedback; unsigned noise_feedback;
unsigned looped_feedback; unsigned looped_feedback;
void run_until( blip_time_t ); 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* outputs [4]; // NULL, right, left, center
Blip_Buffer* output; Blip_Buffer* output;
int output_select; int output_select;
int delay; int delay;
int last_amp; int last_amp;
int volume; int volume;
Sms_Osc(); Sms_Osc();
void reset(); void reset();
}; };
@ -25,10 +25,10 @@ struct Sms_Square : Sms_Osc
{ {
int period; int period;
int phase; int phase;
typedef Blip_Synth<blip_good_quality,1> Synth; typedef Blip_Synth<blip_good_quality,1> Synth;
const Synth* synth; const Synth* synth;
void reset(); void reset();
void run( blip_time_t, blip_time_t ); void run( blip_time_t, blip_time_t );
}; };
@ -38,10 +38,10 @@ struct Sms_Noise : Sms_Osc
const int* period; const int* period;
unsigned shifter; unsigned shifter;
unsigned feedback; unsigned feedback;
typedef Blip_Synth<blip_med_quality,1> Synth; typedef Blip_Synth<blip_med_quality,1> Synth;
Synth synth; Synth synth;
void reset(); void reset();
void run( blip_time_t, blip_time_t ); 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 ); memset( &m, 0, sizeof m );
dsp.init( RAM ); dsp.init( RAM );
m.tempo = tempo_unit; m.tempo = tempo_unit;
// Most SPC music doesn't need ROM, and almost all the rest only rely // Most SPC music doesn't need ROM, and almost all the rest only rely
// on these two bytes // on these two bytes
m.rom [0x3E] = 0xFF; m.rom [0x3E] = 0xFF;
m.rom [0x3F] = 0xC0; m.rom [0x3F] = 0xC0;
static unsigned char const cycle_table [128] = static unsigned char const cycle_table [128] =
{// 01 23 45 67 89 AB CD EF {// 01 23 45 67 89 AB CD EF
0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0 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 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E
0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F
}; };
// unpack cycle table // unpack cycle table
for ( int i = 0; i < 128; i++ ) 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 + 0] = n >> 4;
m.cycle_table [i * 2 + 1] = n & 0x0F; m.cycle_table [i * 2 + 1] = n & 0x0F;
} }
#if SPC_LESS_ACCURATE #if SPC_LESS_ACCURATE
memcpy( reg_times, reg_times_, sizeof reg_times ); memcpy( reg_times, reg_times_, sizeof reg_times );
#endif #endif
reset(); reset();
return 0; return 0;
} }
@ -87,7 +87,7 @@ void Snes_Spc::set_tempo( int t )
m.tempo = t; m.tempo = t;
int const timer2_shift = 4; // 64 kHz int const timer2_shift = 4; // 64 kHz
int const other_shift = 3; // 8 kHz int const other_shift = 3; // 8 kHz
#if SPC_DISABLE_TEMPO #if SPC_DISABLE_TEMPO
m.timers [2].prescaler = timer2_shift; m.timers [2].prescaler = timer2_shift;
m.timers [1].prescaler = timer2_shift + other_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->enabled = REGS [r_control] >> i & 1;
t->counter = REGS_IN [r_t0out + i] & 0x0F; t->counter = REGS_IN [r_t0out + i] & 0x0F;
} }
set_tempo( m.tempo ); 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, reg_count );
memcpy( REGS_IN, REGS, reg_count ); memcpy( REGS_IN, REGS, reg_count );
// These always read back as 0 // These always read back as 0
REGS_IN [r_test ] = 0; REGS_IN [r_test ] = 0;
REGS_IN [r_control ] = 0; REGS_IN [r_control ] = 0;
@ -141,7 +141,7 @@ void Snes_Spc::ram_loaded()
{ {
m.rom_enabled = 0; m.rom_enabled = 0;
load_regs( &RAM [0xF0] ); load_regs( &RAM [0xF0] );
// Put STOP instruction around memory to catch PC underflow/overflow // Put STOP instruction around memory to catch PC underflow/overflow
memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 );
memset( m.ram.ram + 0x10000, 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 #if SPC_LESS_ACCURATE
m.dsp_time = clocks_per_sample + 1; m.dsp_time = clocks_per_sample + 1;
#endif #endif
for ( int i = 0; i < timer_count; i++ ) for ( int i = 0; i < timer_count; i++ )
{ {
Timer* t = &m.timers [i]; Timer* t = &m.timers [i];
t->next_time = 1; t->next_time = 1;
t->divider = 0; t->divider = 0;
} }
regs_loaded(); regs_loaded();
m.extra_clocks = 0; m.extra_clocks = 0;
reset_buf(); reset_buf();
} }
@ -182,16 +182,16 @@ void Snes_Spc::reset_common( int timer_counter_init )
int i; int i;
for ( i = 0; i < timer_count; i++ ) for ( i = 0; i < timer_count; i++ )
REGS_IN [r_t0out + i] = timer_counter_init; REGS_IN [r_t0out + i] = timer_counter_init;
// Run IPL ROM // Run IPL ROM
memset( &m.cpu_regs, 0, sizeof m.cpu_regs ); memset( &m.cpu_regs, 0, sizeof m.cpu_regs );
m.cpu_regs.pc = rom_addr; m.cpu_regs.pc = rom_addr;
REGS [r_test ] = 0x0A; REGS [r_test ] = 0x0A;
REGS [r_control] = 0xB0; // ROM enabled, clear ports REGS [r_control] = 0xB0; // ROM enabled, clear ports
for ( i = 0; i < port_count; i++ ) for ( i = 0; i < port_count; i++ )
REGS_IN [r_cpuio0 + i] = 0; REGS_IN [r_cpuio0 + i] = 0;
reset_time_regs(); 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 ) blargg_err_t Snes_Spc::load_spc( void const* data, long size )
{ {
spc_file_t const* const spc = (spc_file_t const*) data; spc_file_t const* const spc = (spc_file_t const*) data;
// be sure compiler didn't insert any padding into fle_t // 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 // Check signature and file size
if ( size < signature_size || memcmp( spc, signature, 27 ) ) if ( size < signature_size || memcmp( spc, signature, 27 ) )
return "Not an SPC file"; return "Not an SPC file";
if ( size < spc_min_file_size ) if ( size < spc_min_file_size )
return "Corrupt SPC file"; return "Corrupt SPC file";
// CPU registers // CPU registers
m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl; m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl;
m.cpu_regs.a = spc->a; 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.y = spc->y;
m.cpu_regs.psw = spc->psw; m.cpu_regs.psw = spc->psw;
m.cpu_regs.sp = spc->sp; m.cpu_regs.sp = spc->sp;
// RAM and registers // RAM and registers
memcpy( RAM, spc->ram, 0x10000 ); memcpy( RAM, spc->ram, 0x10000 );
ram_loaded(); ram_loaded();
// DSP registers // DSP registers
dsp.load( spc->dsp ); dsp.load( spc->dsp );
reset_time_regs(); reset_time_regs();
return 0; return 0;
} }
@ -270,42 +270,42 @@ void Snes_Spc::reset_buf()
sample_t* out = m.extra_buf; sample_t* out = m.extra_buf;
while ( out < &m.extra_buf [extra_size / 2] ) while ( out < &m.extra_buf [extra_size / 2] )
*out++ = 0; *out++ = 0;
m.extra_pos = out; m.extra_pos = out;
m.buf_begin = 0; m.buf_begin = 0;
dsp.set_output( 0, 0 ); dsp.set_output( 0, 0 );
} }
void Snes_Spc::set_output( sample_t* out, int size ) void Snes_Spc::set_output( sample_t* out, int size )
{ {
require( (size & 1) == 0 ); // size must be even require( (size & 1) == 0 ); // size must be even
m.extra_clocks &= clocks_per_sample - 1; m.extra_clocks &= clocks_per_sample - 1;
if ( out ) if ( out )
{ {
sample_t const* out_end = out + size; sample_t const* out_end = out + size;
m.buf_begin = out; m.buf_begin = out;
m.buf_end = out_end; m.buf_end = out_end;
// Copy extra to output // Copy extra to output
sample_t const* in = m.extra_buf; sample_t const* in = m.extra_buf;
while ( in < m.extra_pos && out < out_end ) while ( in < m.extra_pos && out < out_end )
*out++ = *in++; *out++ = *in++;
// Handle output being full already // Handle output being full already
if ( out >= out_end ) if ( out >= out_end )
{ {
// Have DSP write to remaining extra space // Have DSP write to remaining extra space
out = dsp.extra(); out = dsp.extra();
out_end = &dsp.extra() [extra_size]; out_end = &dsp.extra() [extra_size];
// Copy any remaining extra samples as if DSP wrote them // Copy any remaining extra samples as if DSP wrote them
while ( in < m.extra_pos ) while ( in < m.extra_pos )
*out++ = *in++; *out++ = *in++;
assert( out <= out_end ); assert( out <= out_end );
} }
dsp.set_output( out, out_end - out ); dsp.set_output( out, out_end - out );
} }
else else
@ -324,7 +324,7 @@ void Snes_Spc::save_extra()
main_end = dsp_end; main_end = dsp_end;
dsp_end = dsp.extra(); // nothing in DSP's extra dsp_end = dsp.extra(); // nothing in DSP's extra
} }
// Copy any extra samples at these ends into extra_buf // Copy any extra samples at these ends into extra_buf
sample_t* out = m.extra_buf; sample_t* out = m.extra_buf;
sample_t const* in; sample_t const* in;
@ -332,7 +332,7 @@ void Snes_Spc::save_extra()
*out++ = *in; *out++ = *in;
for ( in = dsp.extra(); in < dsp_end ; in++ ) for ( in = dsp.extra(); in < dsp_end ; in++ )
*out++ = *in; *out++ = *in;
m.extra_pos = out; m.extra_pos = out;
assert( out <= &m.extra_buf [extra_size] ); 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 ); set_output( out, count );
end_frame( count * (clocks_per_sample / 2) ); end_frame( count * (clocks_per_sample / 2) );
} }
const char* err = m.cpu_error; const char* err = m.cpu_error;
m.cpu_error = 0; m.cpu_error = 0;
return err; return err;
@ -357,27 +357,27 @@ blargg_err_t Snes_Spc::skip( int count )
if ( count > 2 * sample_rate * 2 ) if ( count > 2 * sample_rate * 2 )
{ {
set_output( 0, 0 ); set_output( 0, 0 );
// Skip a multiple of 4 samples // Skip a multiple of 4 samples
time_t end = count; time_t end = count;
count = (count & 3) + 1 * sample_rate * 2; count = (count & 3) + 1 * sample_rate * 2;
end = (end - count) * (clocks_per_sample / 2); end = (end - count) * (clocks_per_sample / 2);
m.skipped_kon = 0; m.skipped_kon = 0;
m.skipped_koff = 0; m.skipped_koff = 0;
// Preserve DSP and timer synchronization // Preserve DSP and timer synchronization
// TODO: verify that this really preserves it // TODO: verify that this really preserves it
int old_dsp_time = m.dsp_time + m.spc_time; int old_dsp_time = m.dsp_time + m.spc_time;
m.dsp_time = end - m.spc_time + skipping_time; m.dsp_time = end - m.spc_time + skipping_time;
end_frame( end ); end_frame( end );
m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; 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_koff, m.skipped_koff & ~m.skipped_kon );
dsp.write( Spc_Dsp::r_kon , m.skipped_kon ); dsp.write( Spc_Dsp::r_kon , m.skipped_kon );
clear_echo(); clear_echo();
} }
#endif #endif
return play( count, 0 ); return play( count, 0 );
} }

View file

@ -13,12 +13,12 @@ struct Snes_Spc {
public: public:
// Must be called once before using // Must be called once before using
blargg_err_t init(); blargg_err_t init();
// Sample pairs generated per second // Sample pairs generated per second
enum { sample_rate = 32000 }; enum { sample_rate = 32000 };
// Emulator use // Emulator use
// Sets IPL ROM data. Library does not include ROM data. Most SPC music files // 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. // don't need ROM, but a full emulator must provide this.
enum { rom_size = 0x40 }; enum { rom_size = 0x40 };
@ -43,7 +43,7 @@ public:
typedef int time_t; typedef int time_t;
enum { clock_rate = 1024000 }; enum { clock_rate = 1024000 };
enum { clocks_per_sample = 32 }; enum { clocks_per_sample = 32 };
// Emulated port read/write at specified time // Emulated port read/write at specified time
enum { port_count = 4 }; enum { port_count = 4 };
int read_port ( time_t, int port ); 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 // Runs SPC to end_time and starts a new time frame at 0
void end_frame( time_t end_time ); void end_frame( time_t end_time );
// Sound control // Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy. // Reduces emulation accuracy.
enum { voice_count = 8 }; enum { voice_count = 8 };
void mute_voices( int mask ); void mute_voices( int mask );
// If true, prevents channels and global volumes from being phase-negated. // If true, prevents channels and global volumes from being phase-negated.
// Only supported by fast DSP. // Only supported by fast DSP.
void disable_surround( bool disable = true ); void disable_surround( bool disable = true );
void disable_echo( bool disable = true );
// Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. // 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 ); void set_tempo( int );
// SPC music files // SPC music files
@ -73,17 +75,17 @@ public:
enum { spc_min_file_size = 0x10180 }; enum { spc_min_file_size = 0x10180 };
enum { spc_file_size = 0x10200 }; enum { spc_file_size = 0x10200 };
blargg_err_t load_spc( void const* in, long size ); blargg_err_t load_spc( void const* in, long size );
// Clears echo region. Useful after loading an SPC as many have garbage in echo. // Clears echo region. Useful after loading an SPC as many have garbage in echo.
void clear_echo(); void clear_echo();
// Plays for count samples and write samples to out. Discards samples if out // 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. // is NULL. Count must be a multiple of 2 since output is stereo.
blargg_err_t play( int count, sample_t* out ); blargg_err_t play( int count, sample_t* out );
// Skips count samples. Several times faster than play() when using fast DSP. // Skips count samples. Several times faster than play() when using fast DSP.
blargg_err_t skip( int count ); blargg_err_t skip( int count );
// State save/load (only available with accurate DSP) // State save/load (only available with accurate DSP)
#if !SPC_NO_COPY_STATE_FUNCS #if !SPC_NO_COPY_STATE_FUNCS
@ -91,7 +93,7 @@ public:
enum { state_size = 67 * 1024L }; // maximum space needed when saving enum { state_size = 67 * 1024L }; // maximum space needed when saving
typedef Spc_Dsp::copy_func_t copy_func_t; typedef Spc_Dsp::copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t ); void copy_state( unsigned char** io, copy_func_t );
// Writes minimal header to spc_out // Writes minimal header to spc_out
static void init_header( void* spc_out ); static void init_header( void* spc_out );
@ -116,18 +118,18 @@ public:
uint8_t sp; uint8_t sp;
}; };
regs_t& smp_regs() { return m.cpu_regs; } regs_t& smp_regs() { return m.cpu_regs; }
uint8_t* smp_ram() { return m.ram.ram; } uint8_t* smp_ram() { return m.ram.ram; }
void run_until( time_t t ) { run_until_( t ); } void run_until( time_t t ) { run_until_( t ); }
public: public:
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
// Time relative to m_spc_time. Speeds up code a bit by eliminating need to // 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 // 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. // 0 to eliminate reloading end time every instruction. It pays off.
typedef int rel_time_t; typedef int rel_time_t;
struct Timer struct Timer
{ {
rel_time_t next_time; // time of next event rel_time_t next_time; // time of next event
@ -140,46 +142,46 @@ public:
enum { reg_count = 0x10 }; enum { reg_count = 0x10 };
enum { timer_count = 3 }; enum { timer_count = 3 };
enum { extra_size = Spc_Dsp::extra_size }; enum { extra_size = Spc_Dsp::extra_size };
enum { signature_size = 35 }; enum { signature_size = 35 };
private: private:
Spc_Dsp dsp; Spc_Dsp dsp;
#if SPC_LESS_ACCURATE #if SPC_LESS_ACCURATE
static signed char const reg_times_ [256]; static signed char const reg_times_ [256];
signed char reg_times [256]; signed char reg_times [256];
#endif #endif
struct state_t struct state_t
{ {
Timer timers [timer_count]; Timer timers [timer_count];
uint8_t smp_regs [2] [reg_count]; uint8_t smp_regs [2] [reg_count];
regs_t cpu_regs; regs_t cpu_regs;
rel_time_t dsp_time; rel_time_t dsp_time;
time_t spc_time; time_t spc_time;
bool echo_accessed; bool echo_accessed;
int tempo; int tempo;
int skipped_kon; int skipped_kon;
int skipped_koff; int skipped_koff;
const char* cpu_error; const char* cpu_error;
int extra_clocks; int extra_clocks;
sample_t* buf_begin; sample_t* buf_begin;
sample_t const* buf_end; sample_t const* buf_end;
sample_t* extra_pos; sample_t* extra_pos;
sample_t extra_buf [extra_size]; sample_t extra_buf [extra_size];
int rom_enabled; int rom_enabled;
uint8_t rom [rom_size]; uint8_t rom [rom_size];
uint8_t hi_ram [rom_size]; uint8_t hi_ram [rom_size];
unsigned char cycle_table [256]; unsigned char cycle_table [256];
struct struct
{ {
// padding to neutralize address overflow -- but this is // padding to neutralize address overflow -- but this is
@ -190,14 +192,14 @@ private:
} ram; } ram;
}; };
state_t m; state_t m;
enum { rom_addr = 0xFFC0 }; enum { rom_addr = 0xFFC0 };
enum { skipping_time = 127 }; enum { skipping_time = 127 };
// Value that padding should be filled with // Value that padding should be filled with
enum { cpu_pad_fill = 0xFF }; enum { cpu_pad_fill = 0xFF };
enum { enum {
r_test = 0x0, r_control = 0x1, r_test = 0x0, r_control = 0x1,
r_dspaddr = 0x2, r_dspdata = 0x3, r_dspaddr = 0x2, r_dspdata = 0x3,
@ -207,7 +209,7 @@ private:
r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC,
r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF
}; };
void timers_loaded(); void timers_loaded();
void enable_rom( int enable ); void enable_rom( int enable );
void reset_buf(); void reset_buf();
@ -217,7 +219,7 @@ private:
void regs_loaded(); void regs_loaded();
void reset_time_regs(); void reset_time_regs();
void reset_common( int timer_counter_init ); void reset_common( int timer_counter_init );
Timer* run_timer_ ( Timer* t, rel_time_t ); Timer* run_timer_ ( Timer* t, rel_time_t );
Timer* run_timer ( Timer* t, rel_time_t ); Timer* run_timer ( Timer* t, rel_time_t );
int dsp_read ( 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_smp_reg ( int i, rel_time_t );
int cpu_read ( uint16_t addr, rel_time_t ); int cpu_read ( uint16_t addr, rel_time_t );
unsigned CPU_mem_bit ( uint16_t pc, rel_time_t ); unsigned CPU_mem_bit ( uint16_t pc, rel_time_t );
bool check_echo_access ( int addr ); bool check_echo_access ( int addr );
uint8_t* run_until_( time_t end_time ); uint8_t* run_until_( time_t end_time );
struct spc_file_t struct spc_file_t
{ {
char signature [signature_size]; char signature [signature_size];
@ -252,7 +254,7 @@ private:
}; };
static char const signature [signature_size + 1]; static char const signature [signature_size + 1];
void save_regs( uint8_t out [reg_count] ); 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::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_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 #if !SPC_NO_COPY_STATE_FUNCS
inline bool Snes_Spc::check_kon() { return dsp.check_kon(); } inline bool Snes_Spc::check_kon() { return dsp.check_kon(); }
#endif #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; int elapsed = TIMER_DIV( t, time - t->next_time ) + 1;
t->next_time += TIMER_MUL( t, elapsed ); t->next_time += TIMER_MUL( t, elapsed );
if ( t->enabled ) if ( t->enabled )
{ {
int remain = IF_0_THEN_256( t->period - t->divider ); int remain = IF_0_THEN_256( t->period - t->divider );
@ -94,8 +94,8 @@ void Snes_Spc::enable_rom( int enable )
//// DSP //// DSP
#if SPC_LESS_ACCURATE #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] = signed char const Snes_Spc::reg_times_ [256] =
{ {
-1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22, -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, 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, 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, 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29,
@ -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,
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 ) \ #define RUN_DSP( time, offset ) \
int count = (time) - (offset) - m.dsp_time;\ int count = (time) - (offset) - m.dsp_time;\
if ( count >= 0 )\ if ( count >= 0 )\
@ -141,13 +141,13 @@ void Snes_Spc::enable_rom( int enable )
int Snes_Spc::dsp_read( rel_time_t time ) int Snes_Spc::dsp_read( rel_time_t time )
{ {
RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] ); RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] );
int result = dsp.read( REGS [r_dspaddr] & 0x7F ); int result = dsp.read( REGS [r_dspaddr] & 0x7F );
#ifdef SPC_DSP_READ_HOOK #ifdef SPC_DSP_READ_HOOK
SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result ); SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result );
#endif #endif
return result; return result;
} }
@ -160,7 +160,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time )
int r = REGS [r_dspaddr]; int r = REGS [r_dspaddr];
if ( r == Spc_Dsp::r_kon ) if ( r == Spc_Dsp::r_kon )
m.skipped_kon |= data & ~dsp.read( Spc_Dsp::r_koff ); m.skipped_kon |= data & ~dsp.read( Spc_Dsp::r_koff );
if ( r == Spc_Dsp::r_koff ) if ( r == Spc_Dsp::r_koff )
{ {
m.skipped_koff |= data; m.skipped_koff |= data;
@ -168,11 +168,11 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time )
} }
} }
#endif #endif
#ifdef SPC_DSP_WRITE_HOOK #ifdef SPC_DSP_WRITE_HOOK
SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data ); SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data );
#endif #endif
if ( REGS [r_dspaddr] <= 0x7F ) if ( REGS [r_dspaddr] <= 0x7F )
dsp.write( REGS [r_dspaddr], data ); dsp.write( REGS [r_dspaddr], data );
else if ( !SPC_MORE_ACCURACY ) else if ( !SPC_MORE_ACCURACY )
@ -193,7 +193,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time )
#elif !defined (NDEBUG) #elif !defined (NDEBUG)
// Debug-only check for read/write within echo buffer, since this might result in // 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. // inaccurate emulation due to the DSP not being caught up to the present.
bool Snes_Spc::check_echo_access( int addr ) bool Snes_Spc::check_echo_access( int addr )
{ {
if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) 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; return false;
} }
#define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) ); #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) );
#else #else
#define MEM_ACCESS( time, addr ) #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 #if SPC_MORE_ACCURACY
static unsigned char const glitch_probs [3] [256] = 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, 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B,
0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08,
0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09,
0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01,
0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05,
0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07,
0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07,
0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01,
0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09,
0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08,
0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03,
0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03,
0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07,
0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02,
0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01, 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, 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07,
0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06,
0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09,
0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03,
0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07,
0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03,
0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06,
0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03,
0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05,
0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04,
0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05,
0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01,
0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,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,
0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03,
0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01
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, 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A,
0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A,
0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A,
0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09,
0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09,
0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02,
0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07,
0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04,
0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A,
0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07,
0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03, 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 #endif
@ -282,7 +288,7 @@ static unsigned char const glitch_probs [3] [256] =
// by compiler. // by compiler.
// If write isn't preceded by read, data has this added to it // 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 ) 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 ) ((period - 1) | ~0x0F) & period )
{ {
//debug_printf( "SPC pathological timer target write\n" ); //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, // If the period is 3, 5, or 9, there's a probability this behavior won't occur,
// based on the previous period // based on the previous period
int prob = 0xFF; 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 == 3 ) prob = glitch_probs [0] [old_period];
if ( period == 5 ) prob = glitch_probs [1] [old_period]; if ( period == 5 ) prob = glitch_probs [1] [old_period];
if ( period == 9 ) prob = glitch_probs [2] [old_period]; if ( period == 9 ) prob = glitch_probs [2] [old_period];
// The glitch suppresses incrementing of one of the counter bits, based on // The glitch suppresses incrementing of one of the counter bits, based on
// the lowest set bit in the new period // the lowest set bit in the new period
int b = 1; int b = 1;
while ( !(period & b) ) while ( !(period & b) )
b <<= 1; b <<= 1;
if ( (rand() >> 4 & 0xFF) <= prob ) if ( (rand() >> 4 & 0xFF) <= prob )
t->divider = (t->divider - b) & 0xFF; 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; break;
} }
case r_t0out: case r_t0out:
case r_t1out: case r_t1out:
case r_t2out: case r_t2out:
if ( !SPC_MORE_ACCURACY ) if ( !SPC_MORE_ACCURACY )
debug_printf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); debug_printf( "SPC wrote to counter %d\n", (int) addr - r_t0out );
if ( data < no_read_before_write / 2 ) if ( data < no_read_before_write / 2 )
run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0;
break; break;
// Registers that act like RAM // Registers that act like RAM
case 0x8: case 0x8:
case 0x9: case 0x9:
REGS_IN [addr] = (uint8_t) data; REGS_IN [addr] = (uint8_t) data;
break; break;
case r_test: case r_test:
if ( (uint8_t) data != 0x0A ) if ( (uint8_t) data != 0x0A )
debug_printf( "SPC wrote to test register\n" ); debug_printf( "SPC wrote to test register\n" );
break; break;
case r_control: case r_control:
// port clears // port clears
if ( data & 0x10 ) 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_cpuio2] = 0;
REGS_IN [r_cpuio3] = 0; REGS_IN [r_cpuio3] = 0;
} }
// timers // timers
{ {
for ( int i = 0; i < timer_count; i++ ) 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 ) void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time )
{ {
MEM_ACCESS( time, addr ) MEM_ACCESS( time, addr )
// RAM // RAM
RAM [addr] = (uint8_t) data; RAM [addr] = (uint8_t) data;
if ( addr >= 0xF0 ) // 64% 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% if ( reg < reg_count ) // 87%
{ {
REGS [reg] = (uint8_t) data; REGS [reg] = (uint8_t) data;
// Ports // Ports
#ifdef SPC_PORT_WRITE_HOOK #ifdef SPC_PORT_WRITE_HOOK
if ( (unsigned) (reg - r_cpuio0) < port_count ) if ( (unsigned) (reg - r_cpuio0) < port_count )
SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0),
(uint8_t) data, &REGS [r_cpuio0] ); (uint8_t) data, &REGS [r_cpuio0] );
#endif #endif
// Registers other than $F2 and $F4-$F7 // Registers other than $F2 and $F4-$F7
if ( reg != 2 && (reg < 4 || reg > 7) ) // 36% if ( reg != 2 && (reg < 4 || reg > 7) ) // 36%
cpu_write_smp_reg( data, time, reg ); 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 ) int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time )
{ {
MEM_ACCESS( time, addr ) MEM_ACCESS( time, addr )
// RAM // RAM
int result = RAM [addr]; int result = RAM [addr];
int reg = addr - 0xF0; 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% if ( (unsigned) reg >= 0xFF00 ) // 21%
{ {
reg += 0x10 - r_t0out; reg += 0x10 - r_t0out;
// Timers // Timers
if ( (unsigned) reg < timer_count ) // 90% if ( (unsigned) reg < timer_count ) // 90%
{ {
@ -484,7 +490,7 @@ int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time )
} }
} }
} }
return result; return result;
} }
@ -516,7 +522,7 @@ uint8_t* Snes_Spc::run_until_( time_t end_time )\
#ifndef NDEBUG #ifndef NDEBUG
// Used only for assert // 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 #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. // would exceed end, does NOT execute it and leaves m.spc_time < end.
if ( end_time > m.spc_time ) if ( end_time > m.spc_time )
run_until_( end_time ); run_until_( end_time );
m.spc_time -= end_time; m.spc_time -= end_time;
m.extra_clocks += end_time; m.extra_clocks += end_time;
// Greatest number of clocks early that emulation can stop early due to // Greatest number of clocks early that emulation can stop early due to
// not being able to execute current instruction without going over // not being able to execute current instruction without going over
// allowed time. // allowed time.
assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 );
// Catch timers up to CPU // Catch timers up to CPU
for ( int i = 0; i < timer_count; i++ ) for ( int i = 0; i < timer_count; i++ )
run_timer( &m.timers [i], 0 ); run_timer( &m.timers [i], 0 );
// Catch DSP up to CPU // Catch DSP up to CPU
if ( m.dsp_time < 0 ) if ( m.dsp_time < 0 )
{ {
RUN_DSP( 0, max_reg_time ); RUN_DSP( 0, max_reg_time );
} }
// Save any extra samples beyond what should be generated // Save any extra samples beyond what should be generated
if ( m.buf_begin ) if ( m.buf_begin )
save_extra(); save_extra();

View file

@ -147,16 +147,16 @@ SPC_CPU_RUN_FUNC
int c; int c;
int nz; int nz;
int dp; int dp;
SET_PC( m.cpu_regs.pc ); SET_PC( m.cpu_regs.pc );
SET_SP( m.cpu_regs.sp ); SET_SP( m.cpu_regs.sp );
SET_PSW( m.cpu_regs.psw ); SET_PSW( m.cpu_regs.psw );
goto loop; goto loop;
// Main loop // Main loop
cbranch_taken_loop: cbranch_taken_loop:
pc += (int8_t) ram [pc]; pc += (int8_t) ram [pc];
inc_pc_loop: inc_pc_loop:
@ -165,15 +165,15 @@ loop:
{ {
unsigned opcode; unsigned opcode;
unsigned data; unsigned data;
check( (unsigned) a < 0x100 ); check( (unsigned) a < 0x100 );
check( (unsigned) x < 0x100 ); check( (unsigned) x < 0x100 );
check( (unsigned) y < 0x100 ); check( (unsigned) y < 0x100 );
opcode = ram [pc]; opcode = ram [pc];
if ( (rel_time += m.cycle_table [opcode]) > 0 ) if ( (rel_time += m.cycle_table [opcode]) > 0 )
goto out_of_time; goto out_of_time;
#ifdef SPC_CPU_OPCODE_HOOK #ifdef SPC_CPU_OPCODE_HOOK
SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); SPC_CPU_OPCODE_HOOK( GET_PC(), opcode );
#endif #endif
@ -186,18 +186,18 @@ loop:
pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\
SUB_CASE_COUNTER( op && cond );\ SUB_CASE_COUNTER( op && cond );\
} }
PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 );
PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 );
PROFILE_TIMER_LOOP( 0xE4, 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) // TODO: if PC is at end of memory, this will get wrong operand (very obscure)
pc++; pc++;
data = ram [pc]; data = ram [pc];
switch ( opcode ) switch ( opcode )
{ {
// Common instructions // Common instructions
#define BRANCH( cond )\ #define BRANCH( cond )\
@ -213,17 +213,17 @@ loop:
case 0xF0: // BEQ case 0xF0: // BEQ
BRANCH( !(uint8_t) nz ) // 89% taken BRANCH( !(uint8_t) nz ) // 89% taken
case 0xD0: // BNE case 0xD0: // BNE
BRANCH( (uint8_t) nz ) BRANCH( (uint8_t) nz )
case 0x3F:{// CALL case 0x3F:{// CALL
int old_addr = GET_PC() + 2; int old_addr = GET_PC() + 2;
SET_PC( READ_PC16( pc ) ); SET_PC( READ_PC16( pc ) );
PUSH16( old_addr ); PUSH16( old_addr );
goto loop; goto loop;
} }
case 0x6F:// RET case 0x6F:// RET
{ {
uint8_t l, h; uint8_t l, h;
@ -232,13 +232,13 @@ loop:
SET_PC( l | (h << 8) ); SET_PC( l | (h << 8) );
} }
goto loop; goto loop;
case 0xE4: // MOV a,dp case 0xE4: // MOV a,dp
++pc; ++pc;
// 80% from timer // 80% from timer
READ_DP_TIMER( 0, data, a = nz ); READ_DP_TIMER( 0, data, a = nz );
goto loop; goto loop;
case 0xFA:{// MOV dp,dp case 0xFA:{// MOV dp,dp
int temp; int temp;
READ_DP_TIMER( -2, data, temp ); READ_DP_TIMER( -2, data, temp );
@ -248,7 +248,7 @@ loop:
case 0x8F:{// MOV dp,#imm case 0x8F:{// MOV dp,#imm
int temp = READ_PC( pc + 1 ); int temp = READ_PC( pc + 1 );
pc += 2; pc += 2;
#if !SPC_MORE_ACCURACY #if !SPC_MORE_ACCURACY
{ {
int i = dp + temp; int i = dp + temp;
@ -257,7 +257,7 @@ loop:
if ( (unsigned) i < 0x10 ) // 76% if ( (unsigned) i < 0x10 ) // 76%
{ {
REGS [i] = (uint8_t) data; REGS [i] = (uint8_t) data;
// Registers other than $F2 and $F4-$F7 // Registers other than $F2 and $F4-$F7
if ( i != 2 && (i < 4 || i > 7)) // 12% if ( i != 2 && (i < 4 || i > 7)) // 12%
cpu_write_smp_reg( data, rel_time, i ); cpu_write_smp_reg( data, rel_time, i );
@ -268,7 +268,7 @@ loop:
#endif #endif
goto loop; goto loop;
} }
case 0xC4: // MOV dp,a case 0xC4: // MOV dp,a
++pc; ++pc;
#if !SPC_MORE_ACCURACY #if !SPC_MORE_ACCURACY
@ -280,7 +280,7 @@ loop:
{ {
unsigned sel = i - 2; unsigned sel = i - 2;
REGS [i] = (uint8_t) a; REGS [i] = (uint8_t) a;
if ( sel == 1 ) // 51% $F3 if ( sel == 1 ) // 51% $F3
dsp_write( a, rel_time ); dsp_write( a, rel_time );
else if ( sel > 1 ) // 1% not $F2 or $F3 else if ( sel > 1 ) // 1% not $F2 or $F3
@ -291,7 +291,7 @@ loop:
WRITE_DP( 0, data, a ); WRITE_DP( 0, data, a );
#endif #endif
goto loop; goto loop;
#define CASE( n ) /*FALLTHRU*/case n: #define CASE( n ) /*FALLTHRU*/case n:
// Define common address modes based on opcode for immediate mode. Execution // Define common address modes based on opcode for immediate mode. Execution
@ -335,25 +335,25 @@ loop:
ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr
a = nz = READ( 0, data ); a = nz = READ( 0, data );
goto inc_pc_loop; goto inc_pc_loop;
case 0xBF:{// MOV A,(X)+ case 0xBF:{// MOV A,(X)+
int temp = x + dp; int temp = x + dp;
x = (uint8_t) (x + 1); x = (uint8_t) (x + 1);
a = nz = READ( -1, temp ); a = nz = READ( -1, temp );
goto loop; goto loop;
} }
case 0xE8: // MOV A,imm case 0xE8: // MOV A,imm
a = data; a = data;
nz = data; nz = data;
goto inc_pc_loop; goto inc_pc_loop;
case 0xF9: // MOV X,dp+Y case 0xF9: // MOV X,dp+Y
data = (uint8_t) (data + y);/*FALLTHRU*/ data = (uint8_t) (data + y);/*FALLTHRU*/
case 0xF8: // MOV X,dp case 0xF8: // MOV X,dp
READ_DP_TIMER( 0, data, x = nz ); READ_DP_TIMER( 0, data, x = nz );
goto inc_pc_loop; goto inc_pc_loop;
case 0xE9: // MOV X,abs case 0xE9: // MOV X,abs
data = READ_PC16( pc ); data = READ_PC16( pc );
++pc; ++pc;
@ -362,7 +362,7 @@ loop:
x = data; x = data;
nz = data; nz = data;
goto inc_pc_loop; goto inc_pc_loop;
case 0xFB: // MOV Y,dp+X case 0xFB: // MOV Y,dp+X
data = (uint8_t) (data + x);/*FALLTHRU*/ data = (uint8_t) (data + x);/*FALLTHRU*/
case 0xEB: // MOV Y,dp case 0xEB: // MOV Y,dp
@ -370,7 +370,7 @@ loop:
pc++; pc++;
READ_DP_TIMER( 0, data, y = nz ); READ_DP_TIMER( 0, data, y = nz );
goto loop; goto loop;
case 0xEC:{// MOV Y,abs case 0xEC:{// MOV Y,abs
int temp = READ_PC16( pc ); int temp = READ_PC16( pc );
pc += 2; pc += 2;
@ -378,18 +378,18 @@ loop:
//y = nz = READ( 0, temp ); //y = nz = READ( 0, temp );
goto loop; goto loop;
} }
case 0x8D: // MOV Y,imm case 0x8D: // MOV Y,imm
y = data; y = data;
nz = data; nz = data;
goto inc_pc_loop; goto inc_pc_loop;
// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 // 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2
ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A
WRITE( 0, data, a ); WRITE( 0, data, a );
goto inc_pc_loop; goto inc_pc_loop;
{ {
int temp; int temp;
case 0xCC: // MOV abs,Y case 0xCC: // MOV abs,Y
@ -402,13 +402,13 @@ loop:
pc += 2; pc += 2;
goto loop; goto loop;
} }
case 0xD9: // MOV dp+Y,X case 0xD9: // MOV dp+Y,X
data = (uint8_t) (data + y);/*FALLTHRU*/ data = (uint8_t) (data + y);/*FALLTHRU*/
case 0xD8: // MOV dp,X case 0xD8: // MOV dp,X
WRITE( 0, data + dp, x ); WRITE( 0, data + dp, x );
goto inc_pc_loop; goto inc_pc_loop;
case 0xDB: // MOV dp+X,Y case 0xDB: // MOV dp+X,Y
data = (uint8_t) (data + x);/*FALLTHRU*/ data = (uint8_t) (data + x);/*FALLTHRU*/
case 0xCB: // MOV dp,Y case 0xCB: // MOV dp,Y
@ -416,44 +416,44 @@ loop:
goto inc_pc_loop; goto inc_pc_loop;
// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. // 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3.
case 0x7D: // MOV A,X case 0x7D: // MOV A,X
a = x; a = x;
nz = x; nz = x;
goto loop; goto loop;
case 0xDD: // MOV A,Y case 0xDD: // MOV A,Y
a = y; a = y;
nz = y; nz = y;
goto loop; goto loop;
case 0x5D: // MOV X,A case 0x5D: // MOV X,A
x = a; x = a;
nz = a; nz = a;
goto loop; goto loop;
case 0xFD: // MOV Y,A case 0xFD: // MOV Y,A
y = a; y = a;
nz = a; nz = a;
goto loop; goto loop;
case 0x9D: // MOV X,SP case 0x9D: // MOV X,SP
x = nz = GET_SP(); x = nz = GET_SP();
goto loop; goto loop;
case 0xBD: // MOV SP,X case 0xBD: // MOV SP,X
SET_SP( x ); SET_SP( x );
goto loop; goto loop;
//case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2)
case 0xAF: // MOV (X)+,A case 0xAF: // MOV (X)+,A
WRITE_DP( 0, x, a + no_read_before_write ); WRITE_DP( 0, x, a + no_read_before_write );
x = (uint8_t) (x + 1); x = (uint8_t) (x + 1);
goto loop; goto loop;
// 5. 8-BIT LOGIC OPERATION COMMANDS // 5. 8-BIT LOGIC OPERATION COMMANDS
#define LOGICAL_OP( op, func )\ #define LOGICAL_OP( op, func )\
ADDR_MODES( op ) /* addr */\ ADDR_MODES( op ) /* addr */\
data = READ( 0, data );/*FALLTHRU*/\ data = READ( 0, data );/*FALLTHRU*/\
@ -477,13 +477,13 @@ loop:
WRITE( 0, addr, nz );\ WRITE( 0, addr, nz );\
goto loop;\ goto loop;\
} }
LOGICAL_OP( 0x28, & ); // AND LOGICAL_OP( 0x28, & ); // AND
LOGICAL_OP( 0x08, | ); // OR LOGICAL_OP( 0x08, | ); // OR
LOGICAL_OP( 0x48, ^ ); // EOR LOGICAL_OP( 0x48, ^ ); // EOR
// 4. 8-BIT ARITHMETIC OPERATION COMMANDS // 4. 8-BIT ARITHMETIC OPERATION COMMANDS
ADDR_MODES( 0x68 ) // CMP addr ADDR_MODES( 0x68 ) // CMP addr
@ -493,14 +493,14 @@ loop:
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto inc_pc_loop; goto inc_pc_loop;
case 0x79: // CMP (X),(Y) case 0x79: // CMP (X),(Y)
data = READ_DP( -2, y ); data = READ_DP( -2, y );
nz = READ_DP( -1, x ) - data; nz = READ_DP( -1, x ) - data;
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto loop; goto loop;
case 0x69: // CMP dp,dp case 0x69: // CMP dp,dp
data = READ_DP( -3, data );/*FALLTHRU*/ data = READ_DP( -3, data );/*FALLTHRU*/
case 0x78: // CMP dp,imm case 0x78: // CMP dp,imm
@ -508,7 +508,7 @@ loop:
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto inc_pc_loop; goto inc_pc_loop;
case 0x3E: // CMP X,dp case 0x3E: // CMP X,dp
data += dp; data += dp;
goto cmp_x_addr; goto cmp_x_addr;
@ -522,7 +522,7 @@ loop:
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto inc_pc_loop; goto inc_pc_loop;
case 0x7E: // CMP Y,dp case 0x7E: // CMP Y,dp
data += dp; data += dp;
goto cmp_y_addr; goto cmp_y_addr;
@ -536,7 +536,7 @@ loop:
c = ~nz; c = ~nz;
nz &= 0xFF; nz &= 0xFF;
goto inc_pc_loop; goto inc_pc_loop;
{ {
int addr; int addr;
case 0xB9: // SBC (x),(y) case 0xB9: // SBC (x),(y)
@ -554,7 +554,7 @@ loop:
adc_addr: adc_addr:
nz = READ( -1, addr ); nz = READ( -1, addr );
goto adc_data; goto adc_data;
// catch ADC and SBC together, then decode later based on operand // catch ADC and SBC together, then decode later based on operand
#undef CASE #undef CASE
#define CASE( n ) case n: case (n) + 0x20: #define CASE( n ) case n: case (n) + 0x20:
@ -568,11 +568,11 @@ loop:
int flags; int flags;
if ( opcode >= 0xA0 ) // SBC if ( opcode >= 0xA0 ) // SBC
data ^= 0xFF; data ^= 0xFF;
flags = data ^ nz; flags = data ^ nz;
nz += data + (c >> 8 & 1); nz += data + (c >> 8 & 1);
flags ^= nz; flags ^= nz;
psw = (psw & ~(v40 | h08)) | psw = (psw & ~(v40 | h08)) |
(flags >> 1 & h08) | (flags >> 1 & h08) |
((flags + 0x80) >> 2 & v40); ((flags + 0x80) >> 2 & v40);
@ -585,9 +585,9 @@ loop:
WRITE( 0, addr, /*(uint8_t)*/ nz ); WRITE( 0, addr, /*(uint8_t)*/ nz );
goto inc_pc_loop; goto inc_pc_loop;
} }
} }
// 6. ADDITION & SUBTRACTION COMMANDS // 6. ADDITION & SUBTRACTION COMMANDS
#define INC_DEC_REG( reg, op )\ #define INC_DEC_REG( reg, op )\
@ -598,7 +598,7 @@ loop:
case 0xBC: INC_DEC_REG( a, + 1 ) // INC A case 0xBC: INC_DEC_REG( a, + 1 ) // INC A
case 0x3D: INC_DEC_REG( x, + 1 ) // INC X case 0x3D: INC_DEC_REG( x, + 1 ) // INC X
case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y
case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A
case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X
case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y
@ -619,7 +619,7 @@ loop:
nz += READ( -1, data ); nz += READ( -1, data );
WRITE( 0, data, /*(uint8_t)*/ nz ); WRITE( 0, data, /*(uint8_t)*/ nz );
goto inc_pc_loop; goto inc_pc_loop;
// 7. SHIFT, ROTATION COMMANDS // 7. SHIFT, ROTATION COMMANDS
case 0x5C: // LSR A case 0x5C: // LSR A
@ -630,7 +630,7 @@ loop:
a = nz; a = nz;
goto loop; goto loop;
} }
case 0x1C: // ASL A case 0x1C: // ASL A
c = 0; /*fallthrough*/ c = 0; /*fallthrough*/
case 0x3C:{// ROL A case 0x3C:{// ROL A
@ -640,7 +640,7 @@ loop:
a = (uint8_t) nz; a = (uint8_t) nz;
goto loop; goto loop;
} }
case 0x0B: // ASL dp case 0x0B: // ASL dp
c = 0; c = 0;
data += dp; data += dp;
@ -662,7 +662,7 @@ loop:
nz |= (c = READ( -1, data ) << 1); nz |= (c = READ( -1, data ) << 1);
WRITE( 0, data, /*(uint8_t)*/ nz ); WRITE( 0, data, /*(uint8_t)*/ nz );
goto inc_pc_loop; goto inc_pc_loop;
case 0x4B: // LSR dp case 0x4B: // LSR dp
c = 0; c = 0;
data += dp; data += dp;
@ -699,12 +699,12 @@ loop:
y = READ_DP( 0, (uint8_t) (data + 1) ); y = READ_DP( 0, (uint8_t) (data + 1) );
nz |= y; nz |= y;
goto inc_pc_loop; goto inc_pc_loop;
case 0xDA: // MOVW dp,YA case 0xDA: // MOVW dp,YA
WRITE_DP( -1, data, a ); WRITE_DP( -1, data, a );
WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write );
goto inc_pc_loop; goto inc_pc_loop;
// 9. 16-BIT OPERATION COMMANDS // 9. 16-BIT OPERATION COMMANDS
case 0x3A: // INCW dp case 0x3A: // INCW dp
@ -716,33 +716,33 @@ loop:
temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW
nz = ((temp >> 1) | temp) & 0x7F; nz = ((temp >> 1) | temp) & 0x7F;
WRITE( -2, data, /*(uint8_t)*/ temp ); WRITE( -2, data, /*(uint8_t)*/ temp );
// high byte // high byte
data = (uint8_t) (data + 1) + dp; data = (uint8_t) (data + 1) + dp;
temp = (uint8_t) ((temp >> 8) + READ( -1, data )); temp = (uint8_t) ((temp >> 8) + READ( -1, data ));
nz |= temp; nz |= temp;
WRITE( 0, data, temp ); WRITE( 0, data, temp );
goto inc_pc_loop; goto inc_pc_loop;
} }
case 0x7A: // ADDW YA,dp case 0x7A: // ADDW YA,dp
case 0x9A:{// SUBW YA,dp case 0x9A:{// SUBW YA,dp
int lo = READ_DP( -2, data ); int lo = READ_DP( -2, data );
int hi = READ_DP( 0, (uint8_t) (data + 1) ); int hi = READ_DP( 0, (uint8_t) (data + 1) );
int result; int result;
int flags; int flags;
if ( opcode == 0x9A ) // SUBW if ( opcode == 0x9A ) // SUBW
{ {
lo = (lo ^ 0xFF) + 1; lo = (lo ^ 0xFF) + 1;
hi ^= 0xFF; hi ^= 0xFF;
} }
lo += a; lo += a;
result = y + hi + (lo >> 8); result = y + hi + (lo >> 8);
flags = hi ^ y ^ result; flags = hi ^ y ^ result;
psw = (psw & ~(v40 | h08)) | psw = (psw & ~(v40 | h08)) |
(flags >> 1 & h08) | (flags >> 1 & h08) |
((flags + 0x80) >> 2 & v40); ((flags + 0x80) >> 2 & v40);
@ -751,10 +751,10 @@ loop:
result = (uint8_t) result; result = (uint8_t) result;
y = result; y = result;
nz = (((lo >> 1) | lo) & 0x7F) | result; nz = (((lo >> 1) | lo) & 0x7F) | result;
goto inc_pc_loop; goto inc_pc_loop;
} }
case 0x5A: { // CMPW YA,dp case 0x5A: { // CMPW YA,dp
int temp = a - READ_DP( -1, data ); int temp = a - READ_DP( -1, data );
nz = ((temp >> 1) | temp) & 0x7F; nz = ((temp >> 1) | temp) & 0x7F;
@ -765,7 +765,7 @@ loop:
nz &= 0xFF; nz &= 0xFF;
goto inc_pc_loop; goto inc_pc_loop;
} }
// 10. MULTIPLICATION & DIVISON COMMANDS // 10. MULTIPLICATION & DIVISON COMMANDS
case 0xCF: { // MUL YA case 0xCF: { // MUL YA
@ -776,19 +776,19 @@ loop:
nz |= y; nz |= y;
goto loop; goto loop;
} }
case 0x9E: // DIV YA,X case 0x9E: // DIV YA,X
{ {
unsigned ya = y * 0x100 + a; unsigned ya = y * 0x100 + a;
psw &= ~(h08 | v40); psw &= ~(h08 | v40);
if ( y >= x ) if ( y >= x )
psw |= v40; psw |= v40;
if ( (y & 15) >= (x & 15) ) if ( (y & 15) >= (x & 15) )
psw |= h08; psw |= h08;
if ( y < x * 2 ) if ( y < x * 2 )
{ {
a = ya / x; a = ya / x;
@ -799,16 +799,16 @@ loop:
a = 255 - (ya - x * 0x200) / (256 - x); a = 255 - (ya - x * 0x200) / (256 - x);
y = x + (ya - x * 0x200) % (256 - x); y = x + (ya - x * 0x200) % (256 - x);
} }
nz = (uint8_t) a; nz = (uint8_t) a;
a = (uint8_t) a; a = (uint8_t) a;
y = (uint8_t) y; y = (uint8_t) y;
goto loop; goto loop;
} }
// 11. DECIMAL COMPENSATION COMMANDS // 11. DECIMAL COMPENSATION COMMANDS
case 0xDF: // DAA case 0xDF: // DAA
SUSPICIOUS_OPCODE( "DAA" ); SUSPICIOUS_OPCODE( "DAA" );
if ( a > 0x99 || c & 0x100 ) if ( a > 0x99 || c & 0x100 )
@ -816,14 +816,14 @@ loop:
a += 0x60; a += 0x60;
c = 0x100; c = 0x100;
} }
if ( (a & 0x0F) > 9 || psw & h08 ) if ( (a & 0x0F) > 9 || psw & h08 )
a += 0x06; a += 0x06;
nz = a; nz = a;
a = (uint8_t) a; a = (uint8_t) a;
goto loop; goto loop;
case 0xBE: // DAS case 0xBE: // DAS
SUSPICIOUS_OPCODE( "DAS" ); SUSPICIOUS_OPCODE( "DAS" );
if ( a > 0x99 || !(c & 0x100) ) if ( a > 0x99 || !(c & 0x100) )
@ -831,38 +831,38 @@ loop:
a -= 0x60; a -= 0x60;
c = 0; c = 0;
} }
if ( (a & 0x0F) > 9 || !(psw & h08) ) if ( (a & 0x0F) > 9 || !(psw & h08) )
a -= 0x06; a -= 0x06;
nz = a; nz = a;
a = (uint8_t) a; a = (uint8_t) a;
goto loop; goto loop;
// 12. BRANCHING COMMANDS // 12. BRANCHING COMMANDS
case 0x2F: // BRA rel case 0x2F: // BRA rel
pc += (int8_t) data; pc += (int8_t) data;
goto inc_pc_loop; goto inc_pc_loop;
case 0x30: // BMI case 0x30: // BMI
BRANCH( (nz & nz_neg_mask) ) BRANCH( (nz & nz_neg_mask) )
case 0x10: // BPL case 0x10: // BPL
BRANCH( !(nz & nz_neg_mask) ) BRANCH( !(nz & nz_neg_mask) )
case 0xB0: // BCS case 0xB0: // BCS
BRANCH( c & 0x100 ) BRANCH( c & 0x100 )
case 0x90: // BCC case 0x90: // BCC
BRANCH( !(c & 0x100) ) BRANCH( !(c & 0x100) )
case 0x70: // BVS case 0x70: // BVS
BRANCH( psw & v40 ) BRANCH( psw & v40 )
case 0x50: // BVC case 0x50: // BVC
BRANCH( !(psw & v40) ) BRANCH( !(psw & v40) )
#define CBRANCH( cond )\ #define CBRANCH( cond )\
{\ {\
pc++;\ pc++;\
@ -871,7 +871,7 @@ loop:
rel_time -= 2;\ rel_time -= 2;\
goto inc_pc_loop;\ goto inc_pc_loop;\
} }
case 0x03: // BBS dp.bit,rel case 0x03: // BBS dp.bit,rel
case 0x23: case 0x23:
case 0x43: case 0x43:
@ -881,7 +881,7 @@ loop:
case 0xC3: case 0xC3:
case 0xE3: case 0xE3:
CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 )
case 0x13: // BBC dp.bit,rel case 0x13: // BBC dp.bit,rel
case 0x33: case 0x33:
case 0x53: case 0x53:
@ -891,7 +891,7 @@ loop:
case 0xD3: case 0xD3:
case 0xF3: case 0xF3:
CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) )
case 0xDE: // CBNE dp+X,rel case 0xDE: // CBNE dp+X,rel
data = (uint8_t) (data + x); data = (uint8_t) (data + x);
// fall through // fall through
@ -901,26 +901,26 @@ loop:
READ_DP_TIMER( -4, data, temp ); READ_DP_TIMER( -4, data, temp );
CBRANCH( temp != a ) CBRANCH( temp != a )
} }
case 0x6E: { // DBNZ dp,rel case 0x6E: { // DBNZ dp,rel
unsigned temp = READ_DP( -4, data ) - 1; unsigned temp = READ_DP( -4, data ) - 1;
WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write );
CBRANCH( temp ) CBRANCH( temp )
} }
case 0xFE: // DBNZ Y,rel case 0xFE: // DBNZ Y,rel
y = (uint8_t) (y - 1); y = (uint8_t) (y - 1);
BRANCH( y ) BRANCH( y )
case 0x1F: // JMP [abs+X] case 0x1F: // JMP [abs+X]
SET_PC( READ_PC16( pc ) + x ); SET_PC( READ_PC16( pc ) + x );
// fall through // fall through
case 0x5F: // JMP abs case 0x5F: // JMP abs
SET_PC( READ_PC16( pc ) ); SET_PC( READ_PC16( pc ) );
goto loop; goto loop;
// 13. SUB-ROUTINE CALL RETURN COMMANDS // 13. SUB-ROUTINE CALL RETURN COMMANDS
case 0x0F:{// BRK case 0x0F:{// BRK
int temp; int temp;
int ret_addr = GET_PC(); int ret_addr = GET_PC();
@ -932,14 +932,14 @@ loop:
PUSH( temp ); PUSH( temp );
goto loop; goto loop;
} }
case 0x4F:{// PCALL offset case 0x4F:{// PCALL offset
int ret_addr = GET_PC() + 1; int ret_addr = GET_PC() + 1;
SET_PC( 0xFF00 | data ); SET_PC( 0xFF00 | data );
PUSH16( ret_addr ); PUSH16( ret_addr );
goto loop; goto loop;
} }
case 0x01: // TCALL n case 0x01: // TCALL n
case 0x11: case 0x11:
case 0x21: case 0x21:
@ -961,7 +961,7 @@ loop:
PUSH16( ret_addr ); PUSH16( ret_addr );
goto loop; goto loop;
} }
// 14. STACK OPERATION COMMANDS // 14. STACK OPERATION COMMANDS
{ {
@ -979,7 +979,7 @@ loop:
SET_PSW( temp ); SET_PSW( temp );
goto loop; goto loop;
} }
case 0x0D: { // PUSH PSW case 0x0D: { // PUSH PSW
int temp; int temp;
GET_PSW( temp ); GET_PSW( temp );
@ -990,27 +990,27 @@ loop:
case 0x2D: // PUSH A case 0x2D: // PUSH A
PUSH( a ); PUSH( a );
goto loop; goto loop;
case 0x4D: // PUSH X case 0x4D: // PUSH X
PUSH( x ); PUSH( x );
goto loop; goto loop;
case 0x6D: // PUSH Y case 0x6D: // PUSH Y
PUSH( y ); PUSH( y );
goto loop; goto loop;
case 0xAE: // POP A case 0xAE: // POP A
POP( a ); POP( a );
goto loop; goto loop;
case 0xCE: // POP X case 0xCE: // POP X
POP( x ); POP( x );
goto loop; goto loop;
case 0xEE: // POP Y case 0xEE: // POP Y
POP( y ); POP( y );
goto loop; goto loop;
// 15. BIT OPERATION COMMANDS // 15. BIT OPERATION COMMANDS
case 0x02: // SET1 case 0x02: // SET1
@ -1037,7 +1037,7 @@ loop:
WRITE( 0, data, (READ( -1, data ) & mask) | bit ); WRITE( 0, data, (READ( -1, data ) & mask) | bit );
goto inc_pc_loop; goto inc_pc_loop;
} }
case 0x0E: // TSET1 abs case 0x0E: // TSET1 abs
case 0x4E: // TCLR1 abs case 0x4E: // TCLR1 abs
data = READ_PC16( pc ); data = READ_PC16( pc );
@ -1051,32 +1051,32 @@ loop:
WRITE( 0, data, temp ); WRITE( 0, data, temp );
} }
goto loop; goto loop;
case 0x4A: // AND1 C,mem.bit case 0x4A: // AND1 C,mem.bit
c &= MEM_BIT( 0 ); c &= MEM_BIT( 0 );
pc += 2; pc += 2;
goto loop; goto loop;
case 0x6A: // AND1 C,/mem.bit case 0x6A: // AND1 C,/mem.bit
c &= ~MEM_BIT( 0 ); c &= ~MEM_BIT( 0 );
pc += 2; pc += 2;
goto loop; goto loop;
case 0x0A: // OR1 C,mem.bit case 0x0A: // OR1 C,mem.bit
c |= MEM_BIT( -1 ); c |= MEM_BIT( -1 );
pc += 2; pc += 2;
goto loop; goto loop;
case 0x2A: // OR1 C,/mem.bit case 0x2A: // OR1 C,/mem.bit
c |= ~MEM_BIT( -1 ); c |= ~MEM_BIT( -1 );
pc += 2; pc += 2;
goto loop; goto loop;
case 0x8A: // EOR1 C,mem.bit case 0x8A: // EOR1 C,mem.bit
c ^= MEM_BIT( -1 ); c ^= MEM_BIT( -1 );
pc += 2; pc += 2;
goto loop; goto loop;
case 0xEA: // NOT1 mem.bit case 0xEA: // NOT1 mem.bit
data = READ_PC16( pc ); data = READ_PC16( pc );
pc += 2; pc += 2;
@ -1086,7 +1086,7 @@ loop:
WRITE( 0, data & 0x1FFF, temp ); WRITE( 0, data & 0x1FFF, temp );
} }
goto loop; goto loop;
case 0xCA: // MOV1 mem.bit,C case 0xCA: // MOV1 mem.bit,C
data = READ_PC16( pc ); data = READ_PC16( pc );
pc += 2; pc += 2;
@ -1097,53 +1097,53 @@ loop:
WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); WRITE( 0, data & 0x1FFF, temp + no_read_before_write );
} }
goto loop; goto loop;
case 0xAA: // MOV1 C,mem.bit case 0xAA: // MOV1 C,mem.bit
c = MEM_BIT( 0 ); c = MEM_BIT( 0 );
pc += 2; pc += 2;
goto loop; goto loop;
// 16. PROGRAM PSW FLAG OPERATION COMMANDS // 16. PROGRAM PSW FLAG OPERATION COMMANDS
case 0x60: // CLRC case 0x60: // CLRC
c = 0; c = 0;
goto loop; goto loop;
case 0x80: // SETC case 0x80: // SETC
c = ~0; c = ~0;
goto loop; goto loop;
case 0xED: // NOTC case 0xED: // NOTC
c ^= 0x100; c ^= 0x100;
goto loop; goto loop;
case 0xE0: // CLRV case 0xE0: // CLRV
psw &= ~(v40 | h08); psw &= ~(v40 | h08);
goto loop; goto loop;
case 0x20: // CLRP case 0x20: // CLRP
dp = 0; dp = 0;
goto loop; goto loop;
case 0x40: // SETP case 0x40: // SETP
dp = 0x100; dp = 0x100;
goto loop; goto loop;
case 0xA0: // EI case 0xA0: // EI
SUSPICIOUS_OPCODE( "EI" ); SUSPICIOUS_OPCODE( "EI" );
psw |= i04; psw |= i04;
goto loop; goto loop;
case 0xC0: // DI case 0xC0: // DI
SUSPICIOUS_OPCODE( "DI" ); SUSPICIOUS_OPCODE( "DI" );
psw &= ~i04; psw &= ~i04;
goto loop; goto loop;
// 17. OTHER COMMANDS // 17. OTHER COMMANDS
case 0x00: // NOP case 0x00: // NOP
goto loop; goto loop;
case 0xFF:{// STOP case 0xFF:{// STOP
// handle PC wrap-around // handle PC wrap-around
if ( pc == 0x0000 ) if ( pc == 0x0000 )
@ -1160,13 +1160,13 @@ loop:
m.cpu_error = "SPC emulation error"; m.cpu_error = "SPC emulation error";
goto stop; goto stop;
} // switch } // switch
assert( 0 ); // catch any unhandled instructions assert( 0 ); // catch any unhandled instructions
} }
out_of_time: out_of_time:
rel_time -= m.cycle_table [ ram [pc] ]; // undo partial execution of opcode rel_time -= m.cycle_table [ ram [pc] ]; // undo partial execution of opcode
stop: stop:
// Uncache registers // Uncache registers
m.cpu_regs.pc = (uint16_t) GET_PC(); m.cpu_regs.pc = (uint16_t) GET_PC();
m.cpu_regs.sp = ( uint8_t) GET_SP(); 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 // counters start out with this synchronization
m.counters [0] = 1; m.counters [0] = 1;
m.counters [1] = 0; m.counters [1] = 0;
m.counters [2] = -0x20u; m.counters [2] = uMinus(0x20u);
m.counters [3] = 0x0B; m.counters [3] = 0x0B;
int n = 2; int n = 2;
for ( int i = 1; i < 32; i++ ) for ( int i = 1; i < 32; i++ )
{ {
@ -190,7 +190,7 @@ void Spc_Dsp::run( int clock_count )
m.phase = new_phase & 31; m.phase = new_phase & 31;
if ( !count ) if ( !count )
return; return;
uint8_t* const ram = m.ram; uint8_t* const ram = m.ram;
#ifdef SPC_ISOLATED_ECHO_BUFFER #ifdef SPC_ISOLATED_ECHO_BUFFER
uint8_t* const echo_ram = m.echo_ram; 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]; uint8_t const* const dir = &ram [REG(dir) * 0x100];
int const slow_gaussian = (REG(pmon) >> 1) | REG(non); int const slow_gaussian = (REG(pmon) >> 1) | REG(non);
int const noise_rate = REG(flg) & 0x1F; int const noise_rate = REG(flg) & 0x1F;
// Global volume // Global volume
int mvoll = (int8_t) REG(mvoll); int mvoll = (int8_t) REG(mvoll);
int mvolr = (int8_t) REG(mvolr); 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 ) if ( mvoll * mvolr < m.surround_threshold )
mvoll = -mvoll; // eliminate surround mvoll = -mvoll; // eliminate surround
do do
{ {
// KON/KOFF reading // KON/KOFF reading
@ -212,20 +223,20 @@ void Spc_Dsp::run( int clock_count )
{ {
m.new_kon &= ~m.kon; m.new_kon &= ~m.kon;
m.kon = m.new_kon; m.kon = m.new_kon;
m.t_koff = REG(koff); m.t_koff = REG(koff);
} }
run_counter( 1 ); run_counter( 1 );
run_counter( 2 ); run_counter( 2 );
run_counter( 3 ); run_counter( 3 );
// Noise // Noise
if ( !READ_COUNTER( noise_rate ) ) if ( !READ_COUNTER( noise_rate ) )
{ {
int feedback = (m.noise << 13) ^ (m.noise << 14); int feedback = (m.noise << 13) ^ (m.noise << 14);
m.noise = (feedback & 0x4000) ^ (m.noise >> 1); m.noise = (feedback & 0x4000) ^ (m.noise >> 1);
} }
// Voices // Voices
int pmon_input = 0; int pmon_input = 0;
int main_out_l = 0; int main_out_l = 0;
@ -238,20 +249,20 @@ void Spc_Dsp::run( int clock_count )
do do
{ {
#define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] ) #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] )
int brr_header = ram [v->brr_addr]; int brr_header = ram [v->brr_addr];
int kon_delay = v->kon_delay; int kon_delay = v->kon_delay;
// Pitch // Pitch
int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF; int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF;
if ( REG(pmon) & vbit ) if ( REG(pmon) & vbit )
pitch += ((pmon_input >> 5) * pitch) >> 10; pitch += ((pmon_input >> 5) * pitch) >> 10;
// KON phases // KON phases
if ( --kon_delay >= 0 ) if ( --kon_delay >= 0 )
{ {
v->kon_delay = kon_delay; v->kon_delay = kon_delay;
// Get ready to start BRR decoding on next sample // Get ready to start BRR decoding on next sample
if ( kon_delay == 4 ) if ( kon_delay == 4 )
{ {
@ -260,20 +271,20 @@ void Spc_Dsp::run( int clock_count )
v->buf_pos = v->buf; v->buf_pos = v->buf;
brr_header = 0; // header is ignored on this sample brr_header = 0; // header is ignored on this sample
} }
// Envelope is never run during KON // Envelope is never run during KON
v->env = 0; v->env = 0;
v->hidden_env = 0; v->hidden_env = 0;
// Disable BRR decoding until last three samples // Disable BRR decoding until last three samples
v->interp_pos = (kon_delay & 3 ? 0x4000 : 0); v->interp_pos = (kon_delay & 3 ? 0x4000 : 0);
// Pitch is never added during KON // Pitch is never added during KON
pitch = 0; pitch = 0;
} }
int env = v->env; int env = v->env;
// Gaussian interpolation // Gaussian interpolation
{ {
int output = 0; int output = 0;
@ -284,9 +295,9 @@ void Spc_Dsp::run( int clock_count )
int offset = (unsigned) v->interp_pos >> 3 & 0x1FE; int offset = (unsigned) v->interp_pos >> 3 & 0x1FE;
short const* fwd = interleved_gauss + offset; short const* fwd = interleved_gauss + offset;
short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian
int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12]; int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12];
if ( !(slow_gaussian & vbit) ) // 99% if ( !(slow_gaussian & vbit) ) // 99%
{ {
// Faster approximation when exact sample value isn't necessary for pitch mod // 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 += (rev [1] * in [2]) >> 11;
output = (int16_t) output; output = (int16_t) output;
output += (rev [0] * in [3]) >> 11; output += (rev [0] * in [3]) >> 11;
CLAMP16( output ); CLAMP16( output );
output &= ~1; output &= ~1;
} }
output = (output * env) >> 11 & ~1; output = (output * env) >> 11 & ~1;
} }
// Output // Output
int l = output * v->volume [0]; int l = output * v->volume [0];
int r = output * v->volume [1]; int r = output * v->volume [1];
main_out_l += l; main_out_l += l;
main_out_r += r; main_out_r += r;
if ( REG(eon) & vbit ) if ( REG(eon) & vbit )
{ {
echo_out_l += l; echo_out_l += l;
echo_out_r += r; echo_out_r += r;
} }
} }
pmon_input = output; pmon_input = output;
VREG(v_regs,outx) = (uint8_t) (output >> 8); VREG(v_regs,outx) = (uint8_t) (output >> 8);
} }
// Soft reset or end of sample // Soft reset or end of sample
if ( REG(flg) & 0x80 || (brr_header & 3) == 1 ) if ( REG(flg) & 0x80 || (brr_header & 3) == 1 )
{ {
v->env_mode = env_release; v->env_mode = env_release;
env = 0; env = 0;
} }
if ( m.every_other_sample ) if ( m.every_other_sample )
{ {
// KOFF // KOFF
if ( m.t_koff & vbit ) if ( m.t_koff & vbit )
v->env_mode = env_release; v->env_mode = env_release;
// KON // KON
if ( m.kon & vbit ) if ( m.kon & vbit )
{ {
@ -352,7 +363,7 @@ void Spc_Dsp::run( int clock_count )
REG(endx) &= ~vbit; REG(endx) &= ~vbit;
} }
} }
// Envelope // Envelope
if ( !v->kon_delay ) if ( !v->kon_delay )
{ {
@ -378,7 +389,7 @@ void Spc_Dsp::run( int clock_count )
env--; env--;
env -= env >> 8; env -= env >> 8;
rate = env_data & 0x1F; rate = env_data & 0x1F;
// optimized handling // optimized handling
v->hidden_env = env; v->hidden_env = env;
if ( READ_COUNTER( rate ) ) if ( READ_COUNTER( rate ) )
@ -428,13 +439,13 @@ void Spc_Dsp::run( int clock_count )
} }
} }
} }
// Sustain level // Sustain level
if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay )
v->env_mode = env_sustain; v->env_mode = env_sustain;
v->hidden_env = env; v->hidden_env = env;
// unsigned cast because linear decrease going negative also triggers this // unsigned cast because linear decrease going negative also triggers this
if ( (unsigned) env > 0x7FF ) if ( (unsigned) env > 0x7FF )
{ {
@ -442,13 +453,13 @@ void Spc_Dsp::run( int clock_count )
if ( v->env_mode == env_attack ) if ( v->env_mode == env_attack )
v->env_mode = env_decay; v->env_mode = env_decay;
} }
if ( !READ_COUNTER( rate ) ) if ( !READ_COUNTER( rate ) )
v->env = env; // nothing else is controlled by the counter v->env = env; // nothing else is controlled by the counter
} }
} }
exit_env: exit_env:
{ {
// Apply pitch // Apply pitch
int old_pos = v->interp_pos; int old_pos = v->interp_pos;
@ -456,14 +467,14 @@ void Spc_Dsp::run( int clock_count )
if ( interp_pos > 0x7FFF ) if ( interp_pos > 0x7FFF )
interp_pos = 0x7FFF; interp_pos = 0x7FFF;
v->interp_pos = interp_pos; v->interp_pos = interp_pos;
// BRR decode if necessary // BRR decode if necessary
if ( old_pos >= 0x4000 ) if ( old_pos >= 0x4000 )
{ {
// Arrange the four input nybbles in 0xABCD order for easy decoding // Arrange the four input nybbles in 0xABCD order for easy decoding
int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 + int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 +
ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF];
// Advance read position // Advance read position
int const brr_block_size = 9; int const brr_block_size = 9;
int brr_offset = v->brr_offset; int brr_offset = v->brr_offset;
@ -482,9 +493,9 @@ void Spc_Dsp::run( int clock_count )
brr_offset = 1; brr_offset = 1;
} }
v->brr_offset = brr_offset; v->brr_offset = brr_offset;
// Decode // Decode
// 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11 // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11
static unsigned char const shifts [16 * 2] = { static unsigned char const shifts [16 * 2] = {
13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16, 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 scale = brr_header >> 4;
int const right_shift = shifts [scale]; int const right_shift = shifts [scale];
int const left_shift = shifts [scale + 16]; int const left_shift = shifts [scale + 16];
// Write to next four samples in circular buffer // Write to next four samples in circular buffer
int* pos = v->buf_pos; int* pos = v->buf_pos;
int* end; int* end;
// Decode four samples // Decode four samples
for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 )
{ {
// Extract upper nybble and scale appropriately. Every cast is // Extract upper nybble and scale appropriately. Every cast is
// necessary to maintain correctness and avoid undef behavior // necessary to maintain correctness and avoid undef behavior
int s = int16_t(uint16_t((int16_t) nybbles >> right_shift) << left_shift); int s = int16_t(uint16_t((int16_t) nybbles >> right_shift) << left_shift);
// Apply IIR filter (8 is the most commonly used) // Apply IIR filter (8 is the most commonly used)
int const filter = brr_header & 0x0C; int const filter = brr_header & 0x0C;
int const p1 = pos [brr_buf_size - 1]; int const p1 = pos [brr_buf_size - 1];
@ -529,13 +540,13 @@ void Spc_Dsp::run( int clock_count )
s += p1 >> 1; s += p1 >> 1;
s += (-p1) >> 5; s += (-p1) >> 5;
} }
// Adjust and write sample // Adjust and write sample
CLAMP16( s ); CLAMP16( s );
s = (int16_t) (s * 2); s = (int16_t) (s * 2);
pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around
} }
if ( pos >= &v->buf [brr_buf_size] ) if ( pos >= &v->buf [brr_buf_size] )
pos = v->buf; pos = v->buf;
v->buf_pos = pos; v->buf_pos = pos;
@ -548,7 +559,7 @@ skip_brr:
v++; v++;
} }
while ( vbit < 0x100 ); while ( vbit < 0x100 );
// Echo position // Echo position
int echo_offset = m.echo_offset; int echo_offset = m.echo_offset;
#ifdef SPC_ISOLATED_ECHO_BUFFER #ifdef SPC_ISOLATED_ECHO_BUFFER
@ -563,23 +574,23 @@ skip_brr:
if ( echo_offset >= m.echo_length ) if ( echo_offset >= m.echo_length )
echo_offset = 0; echo_offset = 0;
m.echo_offset = echo_offset; m.echo_offset = echo_offset;
// FIR // FIR
int echo_in_l = GET_LE16SA( echo_ptr + 0 ); int echo_in_l = GET_LE16SA( echo_ptr + 0 );
int echo_in_r = GET_LE16SA( echo_ptr + 2 ); int echo_in_r = GET_LE16SA( echo_ptr + 2 );
int (*echo_hist_pos) [2] = m.echo_hist_pos; int (*echo_hist_pos) [2] = m.echo_hist_pos;
if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] ) if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] )
echo_hist_pos = m.echo_hist; echo_hist_pos = m.echo_hist;
m.echo_hist_pos = echo_hist_pos; m.echo_hist_pos = echo_hist_pos;
echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l; 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; 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)) #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10))
echo_in_l = CALC_FIR_( 7, echo_in_l ); echo_in_l = CALC_FIR_( 7, echo_in_l );
echo_in_r = CALC_FIR_( 7, echo_in_r ); echo_in_r = CALC_FIR_( 7, echo_in_r );
#define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] ) #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] )
#define DO_FIR( i )\ #define DO_FIR( i )\
echo_in_l += CALC_FIR( i, 0 );\ echo_in_l += CALC_FIR( i, 0 );\
@ -594,39 +605,39 @@ skip_brr:
DO_FIR( 4 ); DO_FIR( 4 );
DO_FIR( 5 ); DO_FIR( 5 );
DO_FIR( 6 ); DO_FIR( 6 );
// Echo out // Echo out
if ( !(REG(flg) & 0x20) ) if ( !(REG(flg) & 0x20) )
{ {
int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14); 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); int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14);
// just to help pass more validation tests // just to help pass more validation tests
#if SPC_MORE_ACCURACY #if SPC_MORE_ACCURACY
l &= ~1; l &= ~1;
r &= ~1; r &= ~1;
#endif #endif
CLAMP16( l ); CLAMP16( l );
CLAMP16( r ); CLAMP16( r );
SET_LE16A( echo_ptr + 0, l ); SET_LE16A( echo_ptr + 0, l );
SET_LE16A( echo_ptr + 2, r ); SET_LE16A( echo_ptr + 2, r );
} }
// Sound out // Sound out
int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14; int l = (main_out_l * mvoll + echo_in_l * evoll) >> 14;
int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14; int r = (main_out_r * mvolr + echo_in_r * evolr) >> 14;
CLAMP16( l ); CLAMP16( l );
CLAMP16( r ); CLAMP16( r );
if ( (REG(flg) & 0x40) ) if ( (REG(flg) & 0x40) )
{ {
l = 0; l = 0;
r = 0; r = 0;
} }
sample_t* out = m.out; sample_t* out = m.out;
WRITE_SAMPLES( l, r, out ); WRITE_SAMPLES( l, r, out );
m.out = 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 ) void Spc_Dsp::init( void* ram_64k )
{ {
m.ram = (uint8_t*) ram_64k; m.ram = (uint8_t*) ram_64k;
mute_voices( 0 ); mute_voices( 0 );
disable_surround( false ); disable_surround( false );
disable_echo( false );
set_output( 0, 0 ); set_output( 0, 0 );
reset(); 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 #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 // check clamp macro
int i; int i;
i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF );
i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); i = -0x8001; CLAMP16( i ); assert( i == -0x8000 );
blargg_verify_byte_order(); blargg_verify_byte_order();
#endif #endif
} }
@ -674,13 +691,13 @@ void Spc_Dsp::init( void* ram_64k )
void Spc_Dsp::soft_reset_common() void Spc_Dsp::soft_reset_common()
{ {
require( m.ram ); // init() must have been called already require( m.ram ); // init() must have been called already
m.noise = 0x4000; m.noise = 0x4000;
m.echo_hist_pos = m.echo_hist; m.echo_hist_pos = m.echo_hist;
m.every_other_sample = 1; m.every_other_sample = 1;
m.echo_offset = 0; m.echo_offset = 0;
m.phase = 0; m.phase = 0;
init_counter(); init_counter();
} }
@ -694,7 +711,7 @@ void Spc_Dsp::load( uint8_t const regs [register_count] )
{ {
memcpy( m.regs, regs, sizeof m.regs ); memcpy( m.regs, regs, sizeof m.regs );
memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count );
// Internal state // Internal state
int i; int i;
for ( i = voice_count; --i >= 0; ) 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; v.buf_pos = v.buf;
} }
m.new_kon = REG(kon); m.new_kon = REG(kon);
mute_voices( m.mute_mask ); mute_voices( m.mute_mask );
soft_reset_common(); soft_reset_common();
} }

View file

@ -8,8 +8,9 @@
struct Spc_Dsp { struct Spc_Dsp {
public: public:
Spc_Dsp();
// Setup // Setup
// Initializes DSP and has it use the 64K RAM provided // Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k ); void init( void* ram_64k );
@ -24,13 +25,13 @@ public:
int sample_count() const; int sample_count() const;
// Emulation // Emulation
// Resets DSP to power-on state // Resets DSP to power-on state
void reset(); void reset();
// Emulates pressing reset switch on SNES // Emulates pressing reset switch on SNES
void soft_reset(); void soft_reset();
// Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp()
// to catch the DSP up to present. // to catch the DSP up to present.
int read ( int addr ) const; int read ( int addr ) const;
@ -50,8 +51,10 @@ public:
// If true, prevents channels and global volumes from being phase-negated // If true, prevents channels and global volumes from being phase-negated
void disable_surround( bool disable = true ); void disable_surround( bool disable = true );
void disable_echo( bool disable = true );
// State // State
// Resets DSP and uses supplied values to initialize registers // Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 }; enum { register_count = 128 };
void load( uint8_t const regs [register_count] ); void load( uint8_t const regs [register_count] );
@ -86,9 +89,9 @@ public:
sample_t const* out_pos() const { return m.out; } sample_t const* out_pos() const { return m.out; }
public: public:
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
enum { echo_hist_size = 8 }; enum { echo_hist_size = 8 };
enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
enum { brr_buf_size = 12 }; enum { brr_buf_size = 12 };
struct voice_t struct voice_t
@ -109,16 +112,16 @@ private:
struct state_t struct state_t
{ {
uint8_t regs [register_count]; uint8_t regs [register_count];
#ifdef SPC_ISOLATED_ECHO_BUFFER #ifdef SPC_ISOLATED_ECHO_BUFFER
// Echo buffer, for dodgy SPC rips that were only made to work in dodgy emulators // Echo buffer, for dodgy SPC rips that were only made to work in dodgy emulators
uint8_t echo_ram [64 * 1024]; uint8_t echo_ram [64 * 1024];
#endif #endif
// Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) // 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 [echo_hist_size * 2] [2];
int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
int every_other_sample; // toggles every sample int every_other_sample; // toggles every sample
int kon; // KON value when last checked int kon; // KON value when last checked
int noise; int noise;
@ -126,25 +129,26 @@ private:
int echo_length; // number of bytes that echo_offset will stop at int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31) int phase; // next clock cycle to run (0-31)
unsigned counters [4]; unsigned counters [4];
int new_kon; int new_kon;
int t_koff; int t_koff;
voice_t voices [voice_count]; voice_t voices [voice_count];
unsigned* counter_select [32]; unsigned* counter_select [32];
// non-emulation state // non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask; int mute_mask;
int surround_threshold; int surround_threshold;
int echo_enable;
sample_t* out; sample_t* out;
sample_t* out_end; sample_t* out_end;
sample_t* out_begin; sample_t* out_begin;
sample_t extra [extra_size]; sample_t extra [extra_size];
}; };
state_t m; state_t m;
void init_counter(); void init_counter();
void run_counter( int ); void run_counter( int );
void soft_reset_common(); 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 l = (int8_t) m.regs [addr + v_voll];
int r = (int8_t) m.regs [addr + v_volr]; int r = (int8_t) m.regs [addr + v_volr];
if ( l * r < m.surround_threshold ) if ( l * r < m.surround_threshold )
{ {
// signs differ, so negate those that are negative // signs differ, so negate those that are negative
l ^= l >> 7; l ^= l >> 7;
r ^= r >> 7; r ^= r >> 7;
} }
voice_t& v = m.voices [addr >> 4]; voice_t& v = m.voices [addr >> 4];
int enabled = v.enabled; int enabled = v.enabled;
v.volume [0] = l & 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 ) inline void Spc_Dsp::write( int addr, int data )
{ {
assert( (unsigned) addr < register_count ); assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data; m.regs [addr] = (uint8_t) data;
int low = addr & 0x0F; int low = addr & 0x0F;
if ( low < 0x2 ) // voice volumes if ( low < 0x2 ) // voice volumes
@ -194,7 +198,7 @@ inline void Spc_Dsp::write( int addr, int data )
{ {
if ( addr == r_kon ) if ( addr == r_kon )
m.new_kon = (uint8_t) data; m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written if ( addr == r_endx ) // always cleared, regardless of data written
m.regs [r_endx] = 0; m.regs [r_endx] = 0;
} }
@ -205,6 +209,11 @@ inline void Spc_Dsp::disable_surround( bool disable )
m.surround_threshold = disable ? 0 : -0x4000; 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_NO_COPY_STATE_FUNCS 1
#define SPC_LESS_ACCURATE 1 #define SPC_LESS_ACCURATE 1

View file

@ -7,16 +7,6 @@
#include <string.h> #include <string.h>
#include <algorithm> #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 /* 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 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 General Public License as published by the Free Software Foundation; either
@ -35,17 +25,17 @@ using std::max;
// TODO: support Spc_Filter's bass // 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] = { 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" "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8"
}; };
set_voice_names( names ); set_voice_names( names );
set_gain( 1.4 ); set_gain( 1.4 );
enable_echo( true ); 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 ); } 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 ) static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
{ {
// header // 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" ); debug_printf( "Extra data after SPC xid6 info\n" );
end = in + info_size; end = in + info_size;
} }
int year = 0; int year = 0;
char copyright [256 + 5]; char copyright [256 + 5];
int copyright_len = 0; int copyright_len = 0;
int const year_len = 5; int const year_len = 5;
int disc = 0, track = 0; int disc = 0, track = 0;
while ( end - in >= 4 ) while ( end - in >= 4 )
{ {
// header // header
@ -109,7 +86,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
check( false ); check( false );
break; // block goes past end of data break; // block goes past end of data
} }
// handle specific block types // handle specific block types
char* field = 0; char* field = 0;
switch ( id ) 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 0x11: disc = data; break;
case 0x12: track = data; break; case 0x12: track = data; break;
case 0x14: year = data; break; case 0x14: year = data; break;
//case 0x30: // intro length //case 0x30: // intro length
// Many SPCs have intro length set wrong for looped tracks, making it useless // 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; break;
*/ */
case 0x33: case 0x33:
check( len == 4 ); check( len == 4 );
if ( 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; out->fade_length = get_le32( in ) / 64;
} }
break; break;
case 0x13: case 0x13:
copyright_len = min( len, (int) sizeof copyright - year_len ); copyright_len = min( len, (int) sizeof copyright - year_len );
memcpy( &copyright [year_len], in, copyright_len ); memcpy( &copyright [year_len], in, copyright_len );
break; break;
default: default:
if ( id < 0x01 || (id > 0x07 && id < 0x10) || if ( id < 0x01 || (id > 0x07 && id < 0x10) ||
(id > 0x14 && id < 0x30) || id > 0x36 ) (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 ); check( type == 1 );
Gme_File::copy_field_( field, (char const*) in, len ); Gme_File::copy_field_( field, (char const*) in, len );
} }
// skip to next block // skip to next block
in += len; in += len;
// blocks are supposed to be 4-byte aligned with zero-padding... // blocks are supposed to be 4-byte aligned with zero-padding...
byte const* unaligned = in; byte const* unaligned = in;
while ( (in - begin) & 3 && in < end ) 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]; char* p = &copyright [year_len];
if ( year ) if ( year )
{ {
@ -197,13 +174,13 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out )
} }
if ( copyright_len ) if ( copyright_len )
Gme_File::copy_field_( out->copyright, p, copyright_len ); Gme_File::copy_field_( out->copyright, p, copyright_len );
if ( disc > 0 && disc <= 9 ) if ( disc > 0 && disc <= 9 )
{ {
out->disc [0] = disc + '0'; out->disc [0] = disc + '0';
out->disc [1] = 0; out->disc [1] = 0;
} }
if ( track > 255 && track < ( ( 100 << 8 ) - 1 ) ) if ( track > 255 && track < ( ( 100 << 8 ) - 1 ) )
{ {
char* p = &copyright [3]; 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 ); memcpy( out->track, p, &copyright [4] - p );
} }
check( in == end ); 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 ); len_secs = get_le16( h.len_secs );
if ( len_secs < 0x1FFF ) if ( len_secs < 0x1FFF )
out->length = len_secs * 1000; out->length = len_secs * 1000;
long fade_msec = 0; long fade_msec = 0;
for ( i = 0; i < 4; i++ ) 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 ); fade_msec = get_le32( h.fade_msec );
if ( fade_msec < 0x7FFF ) if ( fade_msec < 0x7FFF )
out->fade_length = fade_msec; out->fade_length = fade_msec;
int offset = (h.author [0] < ' ' || unsigned (h.author [0] - '0') <= 9); 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_File::copy_field_( out->author, &h.author [offset], sizeof h.author - offset );
GME_COPY_FIELD( h, out, song ); GME_COPY_FIELD( h, out, song );
GME_COPY_FIELD( h, out, game ); GME_COPY_FIELD( h, out, game );
GME_COPY_FIELD( h, out, dumper ); GME_COPY_FIELD( h, out, dumper );
GME_COPY_FIELD( h, out, comment ); GME_COPY_FIELD( h, out, comment );
if ( xid6_size ) if ( xid6_size )
get_spc_xid6( xid6, xid6_size, out ); 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; 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 ) static blargg_err_t check_spc_header( void const* header )
{ {
if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) )
@ -301,15 +272,12 @@ struct Spc_File : Gme_Info_
{ {
Spc_Emu::header_t header; Spc_Emu::header_t header;
blargg_vector<byte> xid6; blargg_vector<byte> xid6;
Spc_File( gme_type_t type ) { set_type( type ); } Spc_File() { set_type( gme_spc_type ); }
Spc_File() : Spc_File( gme_spc_type ) {}
blargg_err_t load_( Data_Reader& in ) blargg_err_t load_( Data_Reader& in )
{ {
long file_size = in.remain(); long file_size = in.remain();
if ( is_archive )
return 0;
if ( file_size < 0x10180 ) if ( file_size < 0x10180 )
return gme_wrong_file_type; return gme_wrong_file_type;
RETURN_ERR( in.read( &header, head_size ) ); RETURN_ERR( in.read( &header, head_size ) );
@ -323,7 +291,7 @@ struct Spc_File : Gme_Info_
} }
return 0; return 0;
} }
blargg_err_t track_info_( track_info_t* out, int ) const blargg_err_t track_info_( track_info_t* out, int ) const
{ {
get_spc_info( header, xid6.begin(), xid6.size(), out ); 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_; 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 // Setup
blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate ) 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 ) ); 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 ) 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_data = in;
file_size = size; file_size = size;
set_voice_count( SuperFamicom::SPC_DSP::voice_count ); set_voice_count( SuperFamicom::SPC_DSP::voice_count );
if ( is_archive )
return 0;
if ( size < 0x10180 ) if ( size < 0x10180 )
return gme_wrong_file_type; return gme_wrong_file_type;
return check_spc_header( in ); return check_spc_header( in );
@ -493,36 +364,36 @@ blargg_err_t Spc_Emu::start_track_( int track )
filter.clear(); filter.clear();
smp.reset(); smp.reset();
const byte * ptr = file_data; const byte * ptr = file_data;
Spc_Emu::header_t & header = *(Spc_Emu::header_t*)ptr; Spc_Emu::header_t & header = *(Spc_Emu::header_t*)ptr;
ptr += sizeof(header); ptr += sizeof(header);
smp.regs.pc = header.pc[0] + header.pc[1] * 0x100; smp.regs.pc = header.pc[0] + header.pc[1] * 0x100;
smp.regs.a = header.a; smp.regs.a = header.a;
smp.regs.x = header.x; smp.regs.x = header.x;
smp.regs.y = header.y; smp.regs.y = header.y;
smp.regs.p = header.psw; smp.regs.p = header.psw;
smp.regs.s = header.sp; smp.regs.s = header.sp;
memcpy( smp.apuram, ptr, sizeof smp.apuram ); memcpy( smp.apuram, ptr, sizeof smp.apuram );
// clear input ports that contain out port data from dump // clear input ports that contain out port data from dump
memset( smp.apuram + 0xF4, 0, 4 ); memset( smp.apuram + 0xF4, 0, 4 );
memcpy( smp.sfm_last, ptr + 0xF4, 4 ); memcpy( smp.sfm_last, ptr + 0xF4, 4 );
static const uint8_t regs_to_copy[][2] = { static const uint8_t regs_to_copy[][2] = {
{0xFC,0xFF}, {0xFB,0xFF}, {0xFA,0xFF}, {0xF9,0xFF}, {0xFC,0xFF}, {0xFB,0xFF}, {0xFA,0xFF}, {0xF9,0xFF},
{0xF8,0xFF}, {0xF2,0xFF}, {0xF1,0x87} {0xF8,0xFF}, {0xF2,0xFF}, {0xF1,0x87}
}; };
for (auto n : regs_to_copy) for (auto n : regs_to_copy)
smp.op_buswrite( n[0], ptr[ n[0] ] & n[1] ); smp.op_buswrite( n[0], ptr[ n[0] ] & n[1] );
smp.timer0.stage3_ticks = ptr[ 0xFD ] & 0x0F; smp.timer0.stage3_ticks = ptr[ 0xFD ] & 0x0F;
smp.timer1.stage3_ticks = ptr[ 0xFE ] & 0x0F; smp.timer1.stage3_ticks = ptr[ 0xFE ] & 0x0F;
smp.timer2.stage3_ticks = ptr[ 0xFF ] & 0x0F; smp.timer2.stage3_ticks = ptr[ 0xFF ] & 0x0F;
ptr += sizeof smp.apuram; ptr += sizeof smp.apuram;
smp.dsp.spc_dsp.load( ptr ); smp.dsp.spc_dsp.load( ptr );
#if 1 #if 1
@ -542,7 +413,7 @@ blargg_err_t Spc_Emu::start_track_( int track )
filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) ); filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) );
track_info_t spc_info; track_info_t spc_info;
RETURN_ERR( track_info_( &spc_info, track ) ); RETURN_ERR( track_info_( &spc_info, track ) );
// Set a default track length, need a non-zero fadeout // Set a default track length, need a non-zero fadeout
if ( autoload_playback_limit() && ( spc_info.length > 0 ) ) if ( autoload_playback_limit() && ( spc_info.length > 0 ) )
set_fade ( spc_info.length, 50 ); 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 = long (count * resampler.ratio()) & ~1;
count -= resampler.skip_input( count ); count -= resampler.skip_input( count );
} }
// TODO: shouldn't skip be adjusted for the 64 samples read afterwards? // TODO: shouldn't skip be adjusted for the 64 samples read afterwards?
if ( count > 0 ) if ( count > 0 )
{ {
smp.skip( count ); smp.skip( count );
filter.clear(); filter.clear();
} }
// eliminate pop due to resampler // eliminate pop due to resampler
if ( sample_rate() != native_sample_rate ) 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 ) if ( sample_rate() == native_sample_rate )
return play_and_filter( count, out ); return play_and_filter( count, out );
long remain = count; long remain = count;
while ( remain > 0 ) while ( remain > 0 )
{ {
@ -602,60 +473,3 @@ blargg_err_t Spc_Emu::play_( long count, sample_t* out )
check( remain == 0 ); check( remain == 0 );
return 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 // The Super Nintendo hardware samples at 32kHz. Other sample rates are
// handled by resampling the 32kHz output; emulation accuracy is not affected. // handled by resampling the 32kHz output; emulation accuracy is not affected.
enum { native_sample_rate = 32000 }; enum { native_sample_rate = 32000 };
// SPC file header // SPC file header
enum { header_size = 0x100 }; enum { header_size = 0x100 };
struct header_t struct header_t
@ -37,26 +37,26 @@ public:
byte emulator; byte emulator;
byte unused2 [46]; byte unused2 [46];
}; };
// Header for currently loaded file // Header for currently loaded file
header_t const& header() const { return *(header_t const*) file_data; } header_t const& header() const { return *(header_t const*) file_data; }
// Prevents channels and global volumes from being phase-negated // Prevents channels and global volumes from being phase-negated
void disable_surround( bool disable = true ); void disable_surround( bool disable = true );
// Enables gaussian=0, cubic=1 or sinc=2 interpolation // Enables gaussian=0, cubic=1 or sinc=2 interpolation
// Or negative levels for worse quality, linear=-1 or nearest=-2 // Or negative levels for worse quality, linear=-1 or nearest=-2
void interpolation_level( int level = 0 ); void interpolation_level( int level = 0 );
// Enables native echo // Enables native echo
void enable_echo( bool enable = true ); void enable_echo( bool enable = true );
void mute_effects( bool mute ); void mute_effects( bool mute );
SuperFamicom::SMP const* get_smp() const; SuperFamicom::SMP const* get_smp() const;
SuperFamicom::SMP * get_smp(); SuperFamicom::SMP * get_smp();
static gme_type_t static_type() { return gme_spc_type; } static gme_type_t static_type() { return gme_spc_type; }
public: public:
// deprecated // deprecated
using Music_Emu::load; using Music_Emu::load;
@ -66,8 +66,7 @@ public:
long trailer_size() const; long trailer_size() const;
public: public:
Spc_Emu( gme_type_t ); Spc_Emu();
Spc_Emu() : Spc_Emu( gme_spc_type ) {}
~Spc_Emu(); ~Spc_Emu();
protected: protected:
blargg_err_t load_mem_( byte const*, long ); blargg_err_t load_mem_( byte const*, long );
@ -77,15 +76,16 @@ protected:
blargg_err_t play_( long, sample_t* ); blargg_err_t play_( long, sample_t* );
blargg_err_t skip_( long ); blargg_err_t skip_( long );
void mute_voices_( int ); void mute_voices_( int );
void disable_echo_( bool disable );
void set_tempo_( double ); void set_tempo_( double );
void enable_accuracy_( bool ); void enable_accuracy_( bool );
private:
byte const* file_data; byte const* file_data;
long file_size; long file_size;
private:
Fir_Resampler<24> resampler; Fir_Resampler<24> resampler;
SPC_Filter filter; SPC_Filter filter;
SuperFamicom::SMP smp; SuperFamicom::SMP smp;
blargg_err_t play_and_filter( long count, sample_t out [] ); 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 const* Spc_Emu::get_smp() const { return &smp; }
inline SuperFamicom::SMP * Spc_Emu::get_smp() { 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 #endif

View file

@ -30,7 +30,7 @@ SPC_Filter::SPC_Filter()
void SPC_Filter::run( short* io, int count ) void SPC_Filter::run( short* io, int count )
{ {
require( (count & 1) == 0 ); // must be even require( (count & 1) == 0 ); // must be even
int const gain = this->gain; int const gain = this->gain;
if ( enabled ) if ( enabled )
{ {
@ -42,26 +42,26 @@ void SPC_Filter::run( short* io, int count )
int sum = (--c)->sum; int sum = (--c)->sum;
int pp1 = c->pp1; int pp1 = c->pp1;
int p1 = c->p1; int p1 = c->p1;
for ( int i = 0; i < count; i += 2 ) for ( int i = 0; i < count; i += 2 )
{ {
// Low-pass filter (two point FIR with coeffs 0.25, 0.75) // Low-pass filter (two point FIR with coeffs 0.25, 0.75)
int f = io [i] + p1; int f = io [i] + p1;
p1 = io [i] * 3; p1 = io [i] * 3;
// High-pass filter ("leaky integrator") // High-pass filter ("leaky integrator")
int delta = f - pp1; int delta = f - pp1;
pp1 = f; pp1 = f;
int s = sum >> (gain_bits + 2); int s = sum >> (gain_bits + 2);
sum += (delta * gain) - (sum >> bass); sum += (delta * gain) - (sum >> bass);
// Clamp to 16 bits // Clamp to 16 bits
if ( (short) s != s ) if ( (short) s != s )
s = (s >> 31) ^ 0x7FFF; s = (s >> 31) ^ 0x7FFF;
io [i] = (short) s; io [i] = (short) s;
} }
c->p1 = p1; c->p1 = p1;
c->pp1 = pp1; c->pp1 = pp1;
c->sum = sum; c->sum = sum;

View file

@ -8,35 +8,35 @@
struct SPC_Filter { struct SPC_Filter {
public: public:
// Filters count samples of stereo sound in place. Count must be a multiple of 2. // Filters count samples of stereo sound in place. Count must be a multiple of 2.
typedef short sample_t; typedef short sample_t;
void run( sample_t* io, int count ); void run( sample_t* io, int count );
// Optional features // Optional features
// Clears filter to silence // Clears filter to silence
void clear(); void clear();
// Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit
// are fine, since output is clamped to 16-bit sample range. // 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 ); void set_gain( int gain );
// Enables/disables filtering (when disabled, gain is still applied) // Enables/disables filtering (when disabled, gain is still applied)
void enable( bool b ); void enable( bool b );
// Sets amount of bass (logarithmic scale) // Sets amount of bass (logarithmic scale)
enum { bass_none = 0 }; static const unsigned int bass_none = 0;
enum { bass_norm = 8 }; // normal amount static const unsigned int bass_norm = 8; // normal amount
enum { bass_max = 31 }; static const unsigned int bass_max = 31;
void set_bass( int bass ); void set_bass( int bass );
public: public:
SPC_Filter(); SPC_Filter();
BLARGG_DISABLE_NOTHROW BLARGG_DISABLE_NOTHROW
private: private:
enum { gain_bits = 8 }; static const unsigned int gain_bits = 8;
int gain; int gain;
int bass; int bass;
bool enabled; bool enabled;

View file

@ -20,9 +20,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include "blargg_source.h" #include "blargg_source.h"
double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow static double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow
double const rolloff = 0.990; static double const rolloff = 0.990;
double const oversample_factor = 1.0; static double const oversample_factor = 1.0;
using std::min; using std::min;
using std::max; using std::max;
@ -30,16 +30,18 @@ using std::max;
Vgm_Emu::Vgm_Emu() Vgm_Emu::Vgm_Emu()
{ {
disable_oversampling_ = false; disable_oversampling_ = false;
psg_dual = false;
psg_t6w28 = false;
psg_rate = 0; psg_rate = 0;
set_type( gme_vgm_type ); set_type( gme_vgm_type );
static int const types [8] = { static int const types [8] = {
wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0 wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0
}; };
set_voice_types( types ); set_voice_types( types );
set_silence_lookahead( 1 ); // tracks should already be trimmed set_silence_lookahead( 1 ); // tracks should already be trimmed
set_equalizer( make_equalizer( -14.0, 80 ) ); 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 ); 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 ) static long check_gd3_header( byte const* h, long remain )
{ {
if ( remain < gd3_header_size ) return 0; if ( remain < gd3_header_size ) return 0;
if ( memcmp( h, "Gd3 ", 4 ) ) return 0; if ( memcmp( h, "Gd3 ", 4 ) ) return 0;
if ( get_le32( h + 4 ) >= 0x200 ) return 0; if ( get_le32( h + 4 ) >= 0x200 ) return 0;
long gd3_size = get_le32( h + 8 ); long gd3_size = get_le32( h + 8 );
if ( gd3_size > remain - gd3_header_size ) return 0; if ( gd3_size > remain - gd3_header_size ) return 0;
return gd3_size; return gd3_size;
} }
@ -106,19 +108,19 @@ byte const* Vgm_Emu::gd3_data( int* size ) const
{ {
if ( size ) if ( size )
*size = 0; *size = 0;
long gd3_offset = get_le32( header().gd3_offset ) - 0x2C; long gd3_offset = get_le32( header().gd3_offset ) - 0x2C;
if ( gd3_offset < 0 ) if ( gd3_offset < 0 )
return 0; return 0;
byte const* gd3 = data + header_size + gd3_offset; byte const* gd3 = data + header_size + gd3_offset;
long gd3_size = check_gd3_header( gd3, data_end - gd3 ); long gd3_size = check_gd3_header( gd3, data_end - gd3 );
if ( !gd3_size ) if ( !gd3_size )
return 0; return 0;
if ( size ) if ( size )
*size = gd3_size + gd3_header_size; *size = gd3_size + gd3_header_size;
return gd3; 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 blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const
{ {
get_vgm_length( header(), out ); get_vgm_length( header(), out );
int size; int size;
byte const* gd3 = gd3_data( &size ); byte const* gd3 = gd3_data( &size );
if ( gd3 ) if ( gd3 )
parse_gd3( gd3 + gd3_header_size, gd3 + size, out ); parse_gd3( gd3 + gd3_header_size, gd3 + size, out );
return 0; return 0;
} }
@ -165,18 +167,18 @@ struct Vgm_File : Gme_Info_
{ {
Vgm_Emu::header_t h; Vgm_Emu::header_t h;
blargg_vector<byte> gd3; blargg_vector<byte> gd3;
Vgm_File() { set_type( gme_vgm_type ); } Vgm_File() { set_type( gme_vgm_type ); }
blargg_err_t load_( Data_Reader& in ) blargg_err_t load_( Data_Reader& in )
{ {
long file_size = in.remain(); long file_size = in.remain();
if ( file_size <= Vgm_Emu::header_size ) if ( file_size <= Vgm_Emu::header_size )
return gme_wrong_file_type; return gme_wrong_file_type;
RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) ); RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) );
RETURN_ERR( check_vgm_header( h ) ); RETURN_ERR( check_vgm_header( h ) );
long gd3_offset = get_le32( h.gd3_offset ) - 0x2C; long gd3_offset = get_le32( h.gd3_offset ) - 0x2C;
long remain = file_size - Vgm_Emu::header_size - gd3_offset; long remain = file_size - Vgm_Emu::header_size - gd3_offset;
byte gd3_h [gd3_header_size]; byte gd3_h [gd3_header_size];
@ -193,7 +195,7 @@ struct Vgm_File : Gme_Info_
} }
return 0; return 0;
} }
blargg_err_t track_info_( track_info_t* out, int ) const blargg_err_t track_info_( track_info_t* out, int ) const
{ {
get_vgm_length( h, out ); 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) // 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 ); //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 ); //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 ); 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 ) 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 ) if ( new_size <= header_size )
return gme_wrong_file_type; return gme_wrong_file_type;
header_t const& h = *(header_t const*) new_data; header_t const& h = *(header_t const*) new_data;
RETURN_ERR( check_vgm_header( h ) ); RETURN_ERR( check_vgm_header( h ) );
check( get_le32( h.version ) <= 0x150 ); check( get_le32( h.version ) <= 0x150 );
// psg rate // psg rate
psg_rate = get_le32( h.psg_rate ); psg_rate = get_le32( h.psg_rate );
if ( !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_t6w28 = ( psg_rate & 0x80000000 ) != 0;
psg_rate &= 0x0FFFFFFF; psg_rate &= 0x0FFFFFFF;
blip_buf.clock_rate( psg_rate ); blip_buf.clock_rate( psg_rate );
data = new_data; data = new_data;
data_end = new_data + new_size; data_end = new_data + new_size;
// get loop // get loop
loop_begin = data_end; loop_begin = data_end;
if ( get_le32( h.loop_offset ) ) if ( get_le32( h.loop_offset ) )
loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)];
set_voice_count( psg[0].osc_count ); set_voice_count( psg[0].osc_count );
RETURN_ERR( setup_fm() ); RETURN_ERR( setup_fm() );
static const char* const fm_names [] = { static const char* const fm_names [] = {
"FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" "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" }; static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" };
set_voice_names( uses_fm ? fm_names : psg_names ); set_voice_names( uses_fm ? fm_names : psg_names );
// do after FM in case output buffer is changed // do after FM in case output buffer is changed
return Classic_Emu::setup_buffer( psg_rate ); 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; bool ym2413_dual = ( ym2413_rate & 0x40000000 ) != 0;
if ( ym2413_rate && get_le32( header().version ) < 0x110 ) if ( ym2413_rate && get_le32( header().version ) < 0x110 )
update_fm_rates( &ym2413_rate, &ym2612_rate ); update_fm_rates( &ym2413_rate, &ym2612_rate );
uses_fm = false; uses_fm = false;
fm_rate = blip_buf.sample_rate() * oversample_factor; fm_rate = blip_buf.sample_rate() * oversample_factor;
if ( ym2612_rate ) if ( ym2612_rate )
{ {
ym2612_rate &= ~0xC0000000; ym2612_rate &= ~0xC0000000;
@ -395,7 +397,7 @@ blargg_err_t Vgm_Emu::setup_fm()
} }
set_voice_count( 8 ); set_voice_count( 8 );
} }
if ( !uses_fm && ym2413_rate ) if ( !uses_fm && ym2413_rate )
{ {
ym2413_rate &= ~0xC0000000; ym2413_rate &= ~0xC0000000;
@ -418,7 +420,7 @@ blargg_err_t Vgm_Emu::setup_fm()
} }
set_voice_count( 8 ); set_voice_count( 8 );
} }
if ( uses_fm ) if ( uses_fm )
{ {
RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); 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[0].volume( gain() );
psg[1].volume( gain() ); psg[1].volume( gain() );
} }
return 0; 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 ); psg[0].reset( get_le16( header().noise_feedback ), header().noise_width );
if ( psg_dual ) if ( psg_dual )
psg[1].reset( get_le16( header().noise_feedback ), header().noise_width ); psg[1].reset( get_le16( header().noise_feedback ), header().noise_width );
dac_disabled = -1; dac_disabled = -1;
pos = data + header_size; pos = data + header_size;
pcm_data = pos; pcm_data = pos;
@ -461,7 +463,7 @@ blargg_err_t Vgm_Emu::start_track_( int track )
if ( data_offset ) if ( data_offset )
pos += data_offset + offsetof (header_t,data_offset) - 0x40; pos += data_offset + offsetof (header_t,data_offset) - 0x40;
} }
if ( uses_fm ) if ( uses_fm )
{ {
if ( ym2413[0].enabled() ) if ( ym2413[0].enabled() )
@ -469,13 +471,13 @@ blargg_err_t Vgm_Emu::start_track_( int track )
if ( ym2413[1].enabled() ) if ( ym2413[1].enabled() )
ym2413[1].reset(); ym2413[1].reset();
if ( ym2612[0].enabled() ) if ( ym2612[0].enabled() )
ym2612[0].reset(); ym2612[0].reset();
if ( ym2612[1].enabled() ) if ( ym2612[1].enabled() )
ym2612[1].reset(); ym2612[1].reset();
fm_time_offset = 0; fm_time_offset = 0;
blip_buf.clear(); blip_buf.clear();
Dual_Resampler::clear(); Dual_Resampler::clear();
@ -496,7 +498,7 @@ blargg_err_t Vgm_Emu::play_( long count, sample_t* out )
{ {
if ( !uses_fm ) if ( !uses_fm )
return Classic_Emu::play_( count, out ); return Classic_Emu::play_( count, out );
Dual_Resampler::dual_play( count, out, blip_buf ); Dual_Resampler::dual_play( count, out, blip_buf );
return 0; return 0;
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -2,17 +2,30 @@
// Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ // 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 #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" #include "Ym2612_GENS.h"
typedef Ym2612_GENS_Emu Ym2612_Emu; typedef Ym2612_GENS_Emu Ym2612_Emu;
#endif #endif
#ifdef VGM_YM2612_NUKED // LGPL v2.1+ license #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" #include "Ym2612_Nuked.h"
typedef Ym2612_Nuked_Emu Ym2612_Emu; typedef Ym2612_Nuked_Emu Ym2612_Emu;
#endif #endif
#ifdef VGM_YM2612_MAME // GPL v2+ license #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" #include "Ym2612_MAME.h"
typedef Ym2612_MAME_Emu Ym2612_Emu; typedef Ym2612_MAME_Emu Ym2612_Emu;
#endif #endif

View file

@ -2,6 +2,8 @@
// Based on Gens 2.10 ym2612.c // Based on Gens 2.10 ym2612.c
#ifdef VGM_YM2612_GENS
#include "Ym2612_GENS.h" #include "Ym2612_GENS.h"
#include <assert.h> #include <assert.h>
@ -36,7 +38,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
#include BLARGG_ENABLE_OPTIMIZER #include BLARGG_ENABLE_OPTIMIZER
#endif #endif
const int output_bits = 14; static const int output_bits = 14;
struct slot_t struct slot_t
{ {
@ -179,7 +181,7 @@ struct state_t
#define S2 1 #define S2 1
#define S3 3 #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_xor = 0;
s.env_max = INT_MAX; 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 LFO_FMS_BASE * 12, LFO_FMS_BASE * 24
}; };
inline void YM2612_Special_Update() { } static inline void YM2612_Special_Update() { }
struct Ym2612_GENS_Impl 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; int ecmp = sl.Ecmp;
if ( (sl.Ecnt += sl.Einc) >= 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 ); } 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 // Based on Mame YM2612 ym2612.c
#ifdef VGM_YM2612_MAME
#include "Ym2612_MAME.h" #include "Ym2612_MAME.h"
/* /*
@ -2578,7 +2580,7 @@ void ym2612_pre_generate(void *chip)
refresh_fc_eg_chan( OPN, &cch[5] ); 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; YM2612 *F2612 = (YM2612 *)chip;
FM_OPN *OPN = &F2612->OPN; FM_OPN *OPN = &F2612->OPN;
@ -2819,7 +2821,7 @@ static void * ym2612_init(void *param, int clock, int rate,
F2612->OPN.ST.clock = clock; F2612->OPN.ST.clock = clock;
#if RSM_ENABLE #if RSM_ENABLE
F2612->OPN.ST.rate = 53267; 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; F2612->OPN.ST.framecnt = 1 << RSM_FRAC;
memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2);
memset(&(F2612->OPN.ST.prev_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 (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); 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 // Based on Nuked OPN2 ym3438.c and ym3438.h
#ifdef VGM_YM2612_NUKED
#include "Ym2612_Nuked.h" #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() void Ym2612_Nuked_Emu::reset()
{ {
Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast<Ym2612_NukedImpl::ym3438_t*>(impl); 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) 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; if ( !chip_r ) return;
Ym2612_NukedImpl::OPN2_GenerateStreamMix(chip_r, out, pair_count); 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