diff --git a/Frameworks/GME/GME.xcodeproj/project.pbxproj b/Frameworks/GME/GME.xcodeproj/project.pbxproj index 1203d2d8a..fc9473d9c 100644 --- a/Frameworks/GME/GME.xcodeproj/project.pbxproj +++ b/Frameworks/GME/GME.xcodeproj/project.pbxproj @@ -82,7 +82,6 @@ 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DE0CBED286008D969D /* Sms_Apu.h */; }; 17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */; }; 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E70CBED286008D969D /* Spc_Emu.h */; }; - 8302AF4F2784668C0066143E /* vrc7tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 8302AF4E2784668C0066143E /* vrc7tone.h */; }; 83489CBB2783015300BDCEA2 /* gme_types.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CB12783015200BDCEA2 /* gme_types.h */; }; 83489CBC2783015300BDCEA2 /* nes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CB22783015300BDCEA2 /* nes_cpu_io.h */; }; 83489CBD2783015300BDCEA2 /* hes_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CB32783015300BDCEA2 /* hes_cpu_io.h */; }; @@ -100,17 +99,21 @@ 83489CD82783C98600BDCEA2 /* Nes_Vrc7_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CD62783C98600BDCEA2 /* Nes_Vrc7_Apu.h */; }; 83489CD92783C98600BDCEA2 /* Nes_Vrc7_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83489CD72783C98600BDCEA2 /* Nes_Vrc7_Apu.cpp */; }; 83489CE02783CAC100BDCEA2 /* gb_cpu_io.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CDA2783CAC000BDCEA2 /* gb_cpu_io.h */; }; - 83489CE12783CAC100BDCEA2 /* 2413tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CDB2783CAC000BDCEA2 /* 2413tone.h */; }; - 83489CE32783CAC100BDCEA2 /* emu2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CDD2783CAC100BDCEA2 /* emu2413.h */; }; - 83489CE52783CAC100BDCEA2 /* emu2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 83489CDF2783CAC100BDCEA2 /* emu2413.c */; }; - 83489CEA2783CADC00BDCEA2 /* panning.c in Sources */ = {isa = PBXBuildFile; fileRef = 83489CE72783CADC00BDCEA2 /* panning.c */; }; - 83489CEB2783CADC00BDCEA2 /* panning.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CE82783CADC00BDCEA2 /* panning.h */; }; - 83489CED2783D86700BDCEA2 /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CEC2783D86700BDCEA2 /* mamedef.h */; }; - 83489CEF2783D89300BDCEA2 /* emutypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83489CEE2783D89300BDCEA2 /* emutypes.h */; }; 8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */; }; 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70B17F615FE001A4D7A /* Spc_Filter.h */; }; 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B70C17F615FE001A4D7A /* Spc_Sfm.cpp */; }; 8370B7B017F615FE001A4D7A /* Spc_Sfm.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B70D17F615FE001A4D7A /* Spc_Sfm.h */; }; + 83BDCDD02E41A269003FC007 /* LICENSE in Resources */ = {isa = PBXBuildFile; fileRef = 83BDCDC92E41A269003FC007 /* LICENSE */; }; + 83BDCDD12E41A269003FC007 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 83BDCDCD2E41A269003FC007 /* README.md */; }; + 83BDCDD22E41A269003FC007 /* emu2413_NESpatches.txt in Resources */ = {isa = PBXBuildFile; fileRef = 83BDCDC72E41A269003FC007 /* emu2413_NESpatches.txt */; }; + 83BDCDD32E41A269003FC007 /* panning.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDCB2E41A269003FC007 /* panning.h */; }; + 83BDCDD42E41A269003FC007 /* 2413tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDC42E41A269003FC007 /* 2413tone.h */; }; + 83BDCDD52E41A269003FC007 /* emutypes.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDC82E41A269003FC007 /* emutypes.h */; }; + 83BDCDD62E41A269003FC007 /* mamedef.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDCA2E41A269003FC007 /* mamedef.h */; }; + 83BDCDD72E41A269003FC007 /* emu2413.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDC52E41A269003FC007 /* emu2413.h */; }; + 83BDCDD82E41A269003FC007 /* vrc7tone.h in Headers */ = {isa = PBXBuildFile; fileRef = 83BDCDCE2E41A269003FC007 /* vrc7tone.h */; }; + 83BDCDD92E41A269003FC007 /* emu2413.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BDCDC62E41A269003FC007 /* emu2413.c */; }; + 83BDCDDA2E41A269003FC007 /* panning.c in Sources */ = {isa = PBXBuildFile; fileRef = 83BDCDCC2E41A269003FC007 /* panning.c */; }; 83FC5D5E181B47FB00B917E5 /* dsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D3B181B47FB00B917E5 /* dsp.cpp */; }; 83FC5D5F181B47FB00B917E5 /* dsp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5D3C181B47FB00B917E5 /* dsp.hpp */; }; 83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D57181B47FB00B917E5 /* smp.cpp */; }; @@ -203,7 +206,6 @@ 17C8F1DE0CBED286008D969D /* Sms_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Apu.h; path = gme/Sms_Apu.h; sourceTree = ""; }; 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Emu.cpp; path = gme/Spc_Emu.cpp; sourceTree = ""; }; 17C8F1E70CBED286008D969D /* Spc_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Emu.h; path = gme/Spc_Emu.h; sourceTree = ""; }; - 8302AF4E2784668C0066143E /* vrc7tone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vrc7tone.h; sourceTree = ""; }; 833F68361CDBCAB200AFB9F0 /* es */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = ""; }; 83489CB12783015200BDCEA2 /* gme_types.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gme_types.h; path = gme/gme_types.h; sourceTree = ""; }; 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 = ""; }; @@ -222,14 +224,6 @@ 83489CD62783C98600BDCEA2 /* Nes_Vrc7_Apu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Nes_Vrc7_Apu.h; path = gme/Nes_Vrc7_Apu.h; sourceTree = SOURCE_ROOT; }; 83489CD72783C98600BDCEA2 /* Nes_Vrc7_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Nes_Vrc7_Apu.cpp; path = gme/Nes_Vrc7_Apu.cpp; sourceTree = SOURCE_ROOT; }; 83489CDA2783CAC000BDCEA2 /* gb_cpu_io.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = gb_cpu_io.h; path = gme/gb_cpu_io.h; sourceTree = ""; }; - 83489CDB2783CAC000BDCEA2 /* 2413tone.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = 2413tone.h; sourceTree = ""; }; - 83489CDC2783CAC100BDCEA2 /* emu2413_NESpatches.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = emu2413_NESpatches.txt; sourceTree = ""; }; - 83489CDD2783CAC100BDCEA2 /* emu2413.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emu2413.h; sourceTree = ""; }; - 83489CDF2783CAC100BDCEA2 /* emu2413.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = emu2413.c; sourceTree = ""; }; - 83489CE72783CADC00BDCEA2 /* panning.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = panning.c; sourceTree = ""; }; - 83489CE82783CADC00BDCEA2 /* panning.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = panning.h; sourceTree = ""; }; - 83489CEC2783D86700BDCEA2 /* mamedef.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mamedef.h; sourceTree = ""; }; - 83489CEE2783D89300BDCEA2 /* emutypes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = emutypes.h; sourceTree = ""; }; 835C889022CC1884001B4B3F /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 8370B70A17F615FE001A4D7A /* Spc_Filter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Filter.cpp; path = gme/Spc_Filter.cpp; sourceTree = ""; }; 8370B70B17F615FE001A4D7A /* Spc_Filter.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Spc_Filter.h; path = gme/Spc_Filter.h; sourceTree = ""; }; @@ -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 = ""; }; 83747B7F2862D4DB0021245F /* Shared.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Shared.xcconfig; sourceTree = ""; }; 838EE8BD29A8600A00CD0580 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = ""; }; + 83BDCDC42E41A269003FC007 /* 2413tone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = 2413tone.h; sourceTree = ""; }; + 83BDCDC52E41A269003FC007 /* emu2413.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = emu2413.h; sourceTree = ""; }; + 83BDCDC62E41A269003FC007 /* emu2413.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = emu2413.c; sourceTree = ""; }; + 83BDCDC72E41A269003FC007 /* emu2413_NESpatches.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = emu2413_NESpatches.txt; sourceTree = ""; }; + 83BDCDC82E41A269003FC007 /* emutypes.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = emutypes.h; sourceTree = ""; }; + 83BDCDC92E41A269003FC007 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; + 83BDCDCA2E41A269003FC007 /* mamedef.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = mamedef.h; sourceTree = ""; }; + 83BDCDCB2E41A269003FC007 /* panning.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = panning.h; sourceTree = ""; }; + 83BDCDCC2E41A269003FC007 /* panning.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = panning.c; sourceTree = ""; }; + 83BDCDCD2E41A269003FC007 /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; + 83BDCDCE2E41A269003FC007 /* vrc7tone.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vrc7tone.h; sourceTree = ""; }; 83F0E6B7287CAB4100D84594 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = ""; }; 83FC5D3B181B47FB00B917E5 /* dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp.cpp; sourceTree = ""; }; 83FC5D3C181B47FB00B917E5 /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = ""; }; @@ -339,7 +344,7 @@ 17C8F1860CBED26C008D969D /* Source */ = { isa = PBXGroup; children = ( - 83489CF02783DC4F00BDCEA2 /* ext */, + 83BDCDCF2E41A269003FC007 /* ext */, 83FC5D35181B47FB00B917E5 /* higan */, 17C8F18B0CBED286008D969D /* Ay_Apu.cpp */, 17C8F18C0CBED286008D969D /* Ay_Apu.h */, @@ -447,22 +452,6 @@ name = Frameworks; sourceTree = ""; }; - 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 = ""; - }; 83747B7E2862D4DB0021245F /* Xcode-config */ = { isa = PBXGroup; children = ( @@ -472,6 +461,25 @@ path = "../../Xcode-config"; sourceTree = ""; }; + 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 = ""; + }; 83FC5D35181B47FB00B917E5 /* higan */ = { isa = PBXGroup; children = ( @@ -546,22 +554,25 @@ 17C8F1FF0CBED286008D969D /* Blip_Buffer.h in Headers */, 17C8F2010CBED286008D969D /* Classic_Emu.h in Headers */, 17C8F2030CBED286008D969D /* Data_Reader.h in Headers */, + 83BDCDD32E41A269003FC007 /* panning.h in Headers */, + 83BDCDD42E41A269003FC007 /* 2413tone.h in Headers */, + 83BDCDD52E41A269003FC007 /* emutypes.h in Headers */, + 83BDCDD62E41A269003FC007 /* mamedef.h in Headers */, + 83BDCDD72E41A269003FC007 /* emu2413.h in Headers */, + 83BDCDD82E41A269003FC007 /* vrc7tone.h in Headers */, 17C8F2050CBED286008D969D /* Dual_Resampler.h in Headers */, 17C8F2070CBED286008D969D /* Effects_Buffer.h in Headers */, 17C8F2090CBED286008D969D /* Fir_Resampler.h in Headers */, 83489CD82783C98600BDCEA2 /* Nes_Vrc7_Apu.h in Headers */, - 83489CEB2783CADC00BDCEA2 /* panning.h in Headers */, 83FC5DAE181B8B1900B917E5 /* spc700.hpp in Headers */, 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */, 83FC5DAB181B8B1900B917E5 /* registers.hpp in Headers */, 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */, 83489CC22783015300BDCEA2 /* sap_cpu_io.h in Headers */, - 8302AF4F2784668C0066143E /* vrc7tone.h in Headers */, 83489CBD2783015300BDCEA2 /* hes_cpu_io.h in Headers */, 17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */, 17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */, 83489CE02783CAC100BDCEA2 /* gb_cpu_io.h in Headers */, - 83489CEF2783D89300BDCEA2 /* emutypes.h in Headers */, 83489CD12783BF6D00BDCEA2 /* Nes_Mmc5_Apu.h in Headers */, 83489CC02783015300BDCEA2 /* Ay_Cpu.h in Headers */, 83FC5D9A181B675900B917E5 /* SPC_DSP.h in Headers */, @@ -578,9 +589,7 @@ 17C8F2290CBED286008D969D /* M3u_Playlist.h in Headers */, 17C8F22B0CBED286008D969D /* Multi_Buffer.h in Headers */, 17C8F22D0CBED286008D969D /* Music_Emu.h in Headers */, - 83489CED2783D86700BDCEA2 /* mamedef.h in Headers */, 17C8F22F0CBED286008D969D /* Nes_Apu.h in Headers */, - 83489CE32783CAC100BDCEA2 /* emu2413.h in Headers */, 17C8F2320CBED286008D969D /* Nes_Cpu.h in Headers */, 17C8F2340CBED286008D969D /* Nes_Fme7_Apu.h in Headers */, 17C8F2360CBED286008D969D /* Nes_Namco_Apu.h in Headers */, @@ -595,7 +604,6 @@ 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */, 17C8F2500CBED286008D969D /* Spc_Emu.h in Headers */, 83489CC12783015300BDCEA2 /* Sms_Oscs.h in Headers */, - 83489CE12783CAC100BDCEA2 /* 2413tone.h in Headers */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -656,6 +664,9 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 83BDCDD02E41A269003FC007 /* LICENSE in Resources */, + 83BDCDD12E41A269003FC007 /* README.md in Resources */, + 83BDCDD22E41A269003FC007 /* emu2413_NESpatches.txt in Resources */, 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -672,14 +683,14 @@ 17C8F1F80CBED286008D969D /* Ay_Emu.cpp in Sources */, 17C8F1FE0CBED286008D969D /* Blip_Buffer.cpp in Sources */, 17C8F2000CBED286008D969D /* Classic_Emu.cpp in Sources */, - 83489CEA2783CADC00BDCEA2 /* panning.c in Sources */, 8370B7AF17F615FE001A4D7A /* Spc_Sfm.cpp in Sources */, 17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */, 83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */, 17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */, 17C8F2060CBED286008D969D /* Effects_Buffer.cpp in Sources */, 17C8F2080CBED286008D969D /* Fir_Resampler.cpp in Sources */, - 83489CE52783CAC100BDCEA2 /* emu2413.c in Sources */, + 83BDCDD92E41A269003FC007 /* emu2413.c in Sources */, + 83BDCDDA2E41A269003FC007 /* panning.c in Sources */, 17C8F20A0CBED286008D969D /* Gb_Apu.cpp in Sources */, 17C8F20D0CBED286008D969D /* Gb_Cpu.cpp in Sources */, 17C8F20F0CBED286008D969D /* Gb_Oscs.cpp in Sources */, @@ -756,6 +767,7 @@ HAVE_STDINT_H, "DEBUG=1", "VGM_YM2612_NUKED=1", + "BLARGG_LITTLE_ENDIAN=1", ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; @@ -789,6 +801,7 @@ HAVE_STDINT_H, NDEBUG, "VGM_YM2612_NUKED=1", + "BLARGG_LITTLE_ENDIAN=1", ); INFOPLIST_FILE = Info.plist; INSTALL_PATH = "@loader_path/../Frameworks"; diff --git a/Frameworks/GME/ext/mamedef.h b/Frameworks/GME/ext/mamedef.h deleted file mode 100644 index bc6cfbf59..000000000 --- a/Frameworks/GME/ext/mamedef.h +++ /dev/null @@ -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__ diff --git a/Frameworks/GME/gme/Ay_Apu.cpp b/Frameworks/GME/gme/Ay_Apu.cpp index d132c42f9..4290f43d2 100644 --- a/Frameworks/GME/gme/Ay_Apu.cpp +++ b/Frameworks/GME/gme/Ay_Apu.cpp @@ -22,9 +22,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // Tones above this frequency are treated as disabled tone at half volume. // Power of two is more efficient (avoids division). -unsigned const inaudible_freq = 16384; +static unsigned const inaudible_freq = 16384; -int const period_factor = 16; +static int const period_factor = 16; static byte const amp_table [16] = { @@ -35,10 +35,10 @@ static byte const amp_table [16] = ENTRY(0.022097),ENTRY(0.031250),ENTRY(0.044194),ENTRY(0.062500), ENTRY(0.088388),ENTRY(0.125000),ENTRY(0.176777),ENTRY(0.250000), ENTRY(0.353553),ENTRY(0.500000),ENTRY(0.707107),ENTRY(1.000000), - + /* // Measured from an AY-3-8910A chip with date code 8611. - + // Direct voltages without any load (very linear) ENTRY(0.000000),ENTRY(0.046237),ENTRY(0.064516),ENTRY(0.089785), ENTRY(0.124731),ENTRY(0.173118),ENTRY(0.225806),ENTRY(0.329032), @@ -88,7 +88,7 @@ Ay_Apu::Ay_Apu() flags >>= 2; } } - + output( 0 ); volume( 1.0 ); reset(); @@ -99,7 +99,7 @@ void Ay_Apu::reset() last_time = 0; noise.delay = 0; noise.lfsr = 1; - + osc_t* osc = &oscs [osc_count]; do { @@ -110,7 +110,7 @@ void Ay_Apu::reset() osc->phase = 0; } while ( osc != oscs ); - + for ( int i = sizeof regs; --i >= 0; ) regs [i] = 0; regs [7] = 0xFF; @@ -120,14 +120,14 @@ void Ay_Apu::reset() void Ay_Apu::write_data_( int addr, int data ) { assert( (unsigned) addr < reg_count ); - + if ( (unsigned) addr >= 14 ) { #ifdef debug_printf debug_printf( "Wrote to I/O port %02X\n", (int) addr ); #endif } - + // envelope mode if ( addr == 13 ) { @@ -138,7 +138,7 @@ void Ay_Apu::write_data_( int addr, int data ) env.delay = 0; // will get set to envelope period in run_until() } regs [addr] = data; - + // handle period changes accurately int i = addr >> 1; if ( i < osc_count ) @@ -147,32 +147,32 @@ void Ay_Apu::write_data_( int addr, int data ) regs [i * 2] * period_factor; if ( !period ) period = period_factor; - + // adjust time of next timer expiration based on change in period osc_t& osc = oscs [i]; if ( (osc.delay += period - osc.period) < 0 ) osc.delay = 0; osc.period = period; } - + // TODO: same as above for envelope timer, and it also has a divide by two after it } -int const noise_off = 0x08; -int const tone_off = 0x01; +static int const noise_off = 0x08; +static int const tone_off = 0x01; void Ay_Apu::run_until( blip_time_t final_end_time ) { require( final_end_time >= last_time ); - + // noise period and initial values blip_time_t const noise_period_factor = period_factor * 2; // verified blip_time_t noise_period = (regs [6] & 0x1F) * noise_period_factor; if ( !noise_period ) noise_period = noise_period_factor; blip_time_t const old_noise_delay = noise.delay; - blargg_ulong const old_noise_lfsr = noise.lfsr; - + uint32_t const old_noise_lfsr = noise.lfsr; + // envelope period blip_time_t const env_period_factor = period_factor * 2; // verified blip_time_t env_period = (regs [12] * 0x100L + regs [11]) * env_period_factor; @@ -180,29 +180,29 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) env_period = env_period_factor; // same as period 1 on my AY chip if ( !env.delay ) env.delay = env_period; - + // run each osc separately for ( int index = 0; index < osc_count; index++ ) { osc_t* const osc = &oscs [index]; int osc_mode = regs [7] >> index; - + // output Blip_Buffer* const osc_output = osc->output; if ( !osc_output ) continue; osc_output->set_modified(); - + // period int half_vol = 0; - blip_time_t inaudible_period = (blargg_ulong) (osc_output->clock_rate() + + blip_time_t inaudible_period = (uint32_t) (osc_output->clock_rate() + inaudible_freq) / (inaudible_freq * 2); if ( osc->period <= inaudible_period && !(osc_mode & tone_off) ) { half_vol = 1; // Actually around 60%, but 50% is close enough osc_mode |= tone_off; } - + // envelope blip_time_t start_time = last_time; blip_time_t end_time = final_end_time; @@ -218,7 +218,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) end_time = start_time + env.delay; if ( end_time >= final_end_time ) end_time = final_end_time; - + //if ( !(regs [12] | regs [11]) ) // debug_printf( "Used envelope period 0\n" ); } @@ -231,20 +231,20 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) { osc_mode = noise_off | tone_off; } - + // tone time blip_time_t const period = osc->period; blip_time_t time = start_time + osc->delay; if ( osc_mode & tone_off ) // maintain tone's phase when off { - blargg_long count = (final_end_time - time + period - 1) / period; + int32_t count = (final_end_time - time + period - 1) / period; time += count * period; osc->phase ^= count & 1; } - + // noise time blip_time_t ntime = final_end_time; - blargg_ulong noise_lfsr = 1; + uint32_t noise_lfsr = 1; if ( !(osc_mode & noise_off) ) { ntime = start_time + old_noise_delay; @@ -252,7 +252,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) //if ( (regs [6] & 0x1F) == 0 ) // debug_printf( "Used noise period 0\n" ); } - + // The following efficiently handles several cases (least demanding first): // * Tone, noise, and envelope disabled, where channel acts as 4-bit DAC // * Just tone or just noise, envelope disabled @@ -260,7 +260,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) // * Tone and noise disabled, envelope enabled with high frequency // * Tone and noise together // * Tone and noise together with envelope - + // This loop only runs one iteration if envelope is disabled. If envelope // is being used as a waveform (tone and noise disabled), this loop will // still be reasonably efficient since the bulk of it will be skipped. @@ -278,7 +278,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) synth_.offset( start_time, delta, osc_output ); } } - + // Run wave and noise interleved with each catching up to the other. // If one or both are disabled, their "current time" will be past end time, // so there will be no significant performance hit. @@ -299,7 +299,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) while ( ntime <= end ) // must advance *past* time to avoid hang { int changed = noise_lfsr + 1; - noise_lfsr = (-(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1); + noise_lfsr = (uMinus(noise_lfsr & 1) & 0x12000) ^ (noise_lfsr >> 1); if ( changed & 2 ) { delta = -delta; @@ -311,12 +311,12 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) else { // 20 or more noise periods on average for some music - blargg_long remain = end - ntime; - blargg_long count = remain / noise_period; + int32_t remain = end - ntime; + int32_t count = remain / noise_period; if ( remain >= 0 ) ntime += noise_period + count * noise_period; } - + // run tone end = end_time; if ( end_time > ntime ) end = ntime; @@ -337,7 +337,7 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) { // loop usually runs less than once //SUB_CASE_COUNTER( (time < end) * (end - time + period - 1) / period ); - + while ( time < end ) { time += period; @@ -346,41 +346,41 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) } } while ( time < end_time || ntime < end_time ); - + osc->last_amp = (delta + volume) >> 1; if ( !(osc_mode & tone_off) ) osc->phase = phase; } - + if ( end_time >= final_end_time ) break; // breaks first time when envelope is disabled - + // next envelope step if ( ++osc_env_pos >= 0 ) osc_env_pos -= 32; volume = env.wave [osc_env_pos] >> half_vol; - + start_time = end_time; end_time += env_period; if ( end_time > final_end_time ) end_time = final_end_time; } osc->delay = time - final_end_time; - + if ( !(osc_mode & noise_off) ) { noise.delay = ntime - final_end_time; noise.lfsr = noise_lfsr; } } - + // TODO: optimized saw wave envelope? - + // maintain envelope phase blip_time_t remain = final_end_time - last_time - env.delay; if ( remain >= 0 ) { - blargg_long count = (remain + env_period) / env_period; + int32_t count = (remain + env_period) / env_period; env.pos += count; if ( env.pos >= 0 ) env.pos = (env.pos & 31) - 32; @@ -390,6 +390,6 @@ void Ay_Apu::run_until( blip_time_t final_end_time ) env.delay = -remain; assert( env.delay > 0 ); assert( env.pos < 0 ); - + last_time = final_end_time; } diff --git a/Frameworks/GME/gme/Ay_Apu.h b/Frameworks/GME/gme/Ay_Apu.h index ad2d83692..65f3b7f8c 100644 --- a/Frameworks/GME/gme/Ay_Apu.h +++ b/Frameworks/GME/gme/Ay_Apu.h @@ -11,32 +11,32 @@ class Ay_Apu { public: // Set buffer to generate all sound into, or disable sound if NULL void output( Blip_Buffer* ); - + // Reset sound chip void reset(); - + // Write to register at specified time - enum { reg_count = 16 }; + static const unsigned int reg_count = 16; void write( blip_time_t time, int addr, int data ); - + // Run sound to specified time, end current time frame, then start a new // time frame at time 0. Time frames have no effect on emulation and each // can be whatever length is convenient. void end_frame( blip_time_t length ); - + // Additional features - + // Set sound output of specific oscillator to buffer, where index is // 0, 1, or 2. If buffer is NULL, the specified oscillator is muted. - enum { osc_count = 3 }; + static const int osc_count = 3; void osc_output( int index, Blip_Buffer* ); - + // Set overall volume (default is 1.0) void volume( double ); - + // Set treble equalization (see documentation) void treble_eq( blip_eq_t const& ); - + public: Ay_Apu(); typedef unsigned char byte; @@ -51,23 +51,23 @@ private: } oscs [osc_count]; blip_time_t last_time; byte regs [reg_count]; - + struct { blip_time_t delay; - blargg_ulong lfsr; + uint32_t lfsr; } noise; - + struct { blip_time_t delay; byte const* wave; int pos; byte modes [8] [48]; // values already passed through volume table } env; - + void run_until( blip_time_t ); void write_data_( int addr, int data ); public: - enum { amp_range = 255 }; + static const int amp_range = 255; Blip_Synth synth_; }; @@ -98,7 +98,7 @@ inline void Ay_Apu::end_frame( blip_time_t time ) { if ( time > last_time ) run_until( time ); - + assert( last_time >= time ); last_time -= time; } diff --git a/Frameworks/GME/gme/Ay_Cpu.cpp b/Frameworks/GME/gme/Ay_Cpu.cpp index da825dd15..a2c6b3b53 100644 --- a/Frameworks/GME/gme/Ay_Cpu.cpp +++ b/Frameworks/GME/gme/Ay_Cpu.cpp @@ -38,15 +38,17 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" // flags, named with hex value for clarity -int const S80 = 0x80; -int const Z40 = 0x40; -int const F20 = 0x20; -int const H10 = 0x10; -int const F08 = 0x08; -int const V04 = 0x04; -int const P04 = 0x04; -int const N02 = 0x02; -int const C01 = 0x01; +enum { + S80 = 0x80, + Z40 = 0x40, + F20 = 0x20, + H10 = 0x10, + F08 = 0x08, + V04 = 0x04, + P04 = 0x04, + N02 = 0x02, + C01 = 0x01 +}; #define SZ28P( n ) szpc [n] #define SZ28PC( n ) szpc [n] @@ -75,13 +77,13 @@ Ay_Cpu::Ay_Cpu() void Ay_Cpu::reset( void* m ) { mem = (uint8_t*) m; - + check( state == &state_ ); state = &state_; state_.time = 0; state_.base = 0; end_time_ = 0; - + memset( &r, 0, sizeof r ); } @@ -97,9 +99,9 @@ void Ay_Cpu::reset( void* m ) #define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) #if BLARGG_BIG_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [n]) + #define R8( n, offset ) ((r8_ - offset) [n]) #elif BLARGG_LITTLE_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) + #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) #else #error "Byte order of CPU must be known" #endif @@ -142,7 +144,7 @@ bool Ay_Cpu::run( cpu_time_t end_time ) state_t s = this->state_; this->state = &s; bool warning = false; - + union { regs_t rg; pairs_t rp; @@ -150,7 +152,7 @@ bool Ay_Cpu::run( cpu_time_t end_time ) uint16_t r16_ [4]; }; rg = this->r.b; - + cpu_time_t s_time = s.time; uint8_t* const mem = this->mem; // cache uint16_t pc = r.pc; @@ -158,27 +160,27 @@ bool Ay_Cpu::run( cpu_time_t end_time ) uint16_t ix = r.ix; // TODO: keep in memory for direct access? uint16_t iy = r.iy; int flags = r.b.flags; - + goto loop; jr_not_taken: s_time -= 5; goto loop; call_not_taken: - s_time -= 7; + s_time -= 7; jp_not_taken: pc += 2; loop: - + check( (unsigned long) pc < 0x10000 ); check( (unsigned long) sp < 0x10000 ); check( (unsigned) flags < 0x100 ); check( (unsigned) ix < 0x10000 ); check( (unsigned) iy < 0x10000 ); - + uint8_t opcode; opcode = READ_PROG( pc ); pc++; - + static byte const base_timing [0x100] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 @@ -198,22 +200,22 @@ loop: 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F }; - + uint16_t data; data = base_timing [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; almost_out_of_time: - + data = READ_PROG( pc ); - + #ifdef Z80_CPU_LOG_H //log_opcode( opcode, READ_PROG( pc ) ); z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); #endif - + switch ( opcode ) { possibly_out_of_time: @@ -227,40 +229,40 @@ possibly_out_of_time: case 0x00: // NOP CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. goto loop; - + case 0x08:{// EX AF,AF' int temp = r.alt.b.a; r.alt.b.a = rg.a; rg.a = temp; - + temp = r.alt.b.flags; r.alt.b.flags = flags; flags = temp; goto loop; } - + case 0xD3: // OUT (imm),A pc++; OUT( data + rg.a * 0x100, rg.a ); goto loop; - + case 0x2E: // LD L,imm pc++; rg.l = data; goto loop; - + case 0x3E: // LD A,imm pc++; rg.a = data; goto loop; - + case 0x3A:{// LD A,(addr) uint16_t addr = GET_ADDR(); pc += 2; rg.a = READ( addr ); goto loop; } - + // Conditional #define ZERO (flags & Z40) @@ -277,7 +279,7 @@ possibly_out_of_time: pc += disp;\ goto loop;\ } - + case 0x20: JR( !ZERO ) // JR NZ,disp case 0x28: JR( ZERO ) // JR Z,disp case 0x30: JR( !CARRY ) // JR NC,disp @@ -289,10 +291,10 @@ possibly_out_of_time: rg.b = temp; JR( temp ) } - + // JP #define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; - + case 0xC2: JP( !ZERO ) // JP NZ,addr case 0xCA: JP( ZERO ) // JP Z,addr case 0xD2: JP( !CARRY ) // JP NC,addr @@ -301,18 +303,18 @@ possibly_out_of_time: case 0xEA: JP( EVEN ) // JP PE,addr case 0xF2: JP( !MINUS ) // JP P,addr case 0xFA: JP( MINUS ) // JP M,addr - + case 0xC3: // JP addr pc = GET_ADDR(); goto loop; - + case 0xE9: // JP HL pc = rp.hl; goto loop; // RET #define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; - + case 0xC0: RET( !ZERO ) // RET NZ case 0xC8: RET( ZERO ) // RET Z case 0xD0: RET( !CARRY ) // RET NC @@ -321,13 +323,13 @@ possibly_out_of_time: case 0xE8: RET( EVEN ) // RET PE case 0xF0: RET( !MINUS ) // RET P case 0xF8: RET( MINUS ) // RET M - + case 0xC9: // RET ret_taken: pc = READ_WORD( sp ); sp = uint16_t (sp + 2); goto loop; - + // CALL #define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; @@ -339,7 +341,7 @@ possibly_out_of_time: case 0xEC: CALL( EVEN ) // CALL PE,addr case 0xF4: CALL( !MINUS ) // CALL P,addr case 0xFC: CALL( MINUS ) // CALL M,addr - + case 0xCD:{// CALL addr call_taken: uint16_t addr = pc + 2; @@ -348,7 +350,7 @@ possibly_out_of_time: WRITE_WORD( sp, addr ); goto loop; } - + case 0xFF: // RST if ( (pc - 1) > 0xFFFF ) { @@ -365,7 +367,7 @@ possibly_out_of_time: case 0xF5: // PUSH AF data = rg.a * 0x100u + flags; goto push_data; - + case 0xC5: // PUSH BC case 0xD5: // PUSH DE case 0xE5: // PUSH HL @@ -374,20 +376,20 @@ possibly_out_of_time: sp = uint16_t (sp - 2); WRITE_WORD( sp, data ); goto loop; - + case 0xF1: // POP AF flags = READ( sp ); rg.a = READ( sp + 1 ); sp = uint16_t (sp + 2); goto loop; - + case 0xC1: // POP BC case 0xD1: // POP DE case 0xE1: // POP HL R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); sp = uint16_t (sp + 2); goto loop; - + // ADC/ADD/SBC/SUB case 0x96: // SUB (HL) case 0x86: // ADD (HL) @@ -396,7 +398,7 @@ possibly_out_of_time: case 0x8E: // ADC (HL) data = READ( rp.hl ); goto adc_data; - + case 0xD6: // SUB A,imm case 0xC6: // ADD imm flags &= ~C01; /* fallthrough */ @@ -404,7 +406,7 @@ possibly_out_of_time: case 0xCE: // ADC imm pc++; goto adc_data; - + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r flags &= ~C01; /* fallthrough */ @@ -430,11 +432,11 @@ possibly_out_of_time: case 0xBE: // CP (HL) data = READ( rp.hl ); goto cp_data; - + case 0xFE: // CP imm pc++; goto cp_data; - + CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r data = R8( opcode, 0xB8 ); cp_data: { @@ -448,19 +450,19 @@ possibly_out_of_time: flags |= Z40; goto loop; } - + // ADD HL,rp - + case 0x39: // ADD HL,SP data = sp; goto add_hl_data; - + case 0x09: // ADD HL,BC case 0x19: // ADD HL,DE case 0x29: // ADD HL,HL data = R16( opcode, 4, 0x09 ); add_hl_data: { - blargg_ulong sum = rp.hl + data; + uint32_t sum = rp.hl + data; data ^= rp.hl; rp.hl = sum; flags = (flags & (S80 | Z40 | V04)) | @@ -469,21 +471,21 @@ possibly_out_of_time: ((data ^ sum) >> 8 & H10); goto loop; } - + case 0x27:{// DAA int a = rg.a; if ( a > 0x99 ) flags |= C01; - + int adjust = 0x60 & -(flags & C01); - + if ( flags & H10 || (a & 0x0F) > 9 ) adjust |= 0x06; - + if ( flags & N02 ) adjust = -adjust; a += adjust; - + flags = (flags & (C01 | N02)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); @@ -495,26 +497,26 @@ possibly_out_of_time: // more optimized, but probably not worth the obscurity int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 - + if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 adjust |= 0x06; - + if ( f & N02 ) adjust = -adjust; int a = rg.a + adjust; - + flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); rg.a = a; goto loop; } */ - + // INC/DEC case 0x34: // INC (HL) data = READ( rp.hl ) + 1; WRITE( rp.hl, data ); goto inc_set_flags; - + CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r data = ++R8( opcode >> 3, 0 ); inc_set_flags: @@ -525,12 +527,12 @@ possibly_out_of_time: goto loop; flags |= V04; goto loop; - + case 0x35: // DEC (HL) data = READ( rp.hl ) - 1; WRITE( rp.hl, data ); goto dec_set_flags; - + CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r data = --R8( opcode >> 3, 0 ); dec_set_flags: @@ -547,46 +549,46 @@ possibly_out_of_time: case 0x23: // INC HL R16( opcode, 4, 0x03 )++; goto loop; - + case 0x33: // INC SP sp = uint16_t (sp + 1); goto loop; - + case 0x0B: // DEC BC case 0x1B: // DEC DE case 0x2B: // DEC HL R16( opcode, 4, 0x0B )--; goto loop; - + case 0x3B: // DEC SP sp = uint16_t (sp - 1); goto loop; - + // AND case 0xA6: // AND (HL) data = READ( rp.hl ); goto and_data; - + case 0xE6: // AND imm pc++; goto and_data; - + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r data = R8( opcode, 0xA0 ); and_data: rg.a &= data; flags = SZ28P( rg.a ) | H10; goto loop; - + // OR case 0xB6: // OR (HL) data = READ( rp.hl ); goto or_data; - + case 0xF6: // OR imm pc++; goto or_data; - + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r data = R8( opcode, 0xB0 ); or_data: @@ -598,11 +600,11 @@ possibly_out_of_time: case 0xAE: // XOR (HL) data = READ( rp.hl ); goto xor_data; - + case 0xEE: // XOR imm pc++; goto xor_data; - + CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r data = R8( opcode, 0xA8 ); xor_data: @@ -614,7 +616,7 @@ possibly_out_of_time: CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r WRITE( rp.hl, R8( opcode, 0x70 ) ); goto loop; - + CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r @@ -624,70 +626,70 @@ possibly_out_of_time: CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); goto loop; - + CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm R8( opcode >> 3, 0 ) = data; pc++; goto loop; - + case 0x36: // LD (HL),imm pc++; WRITE( rp.hl, data ); goto loop; - + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) R8( opcode >> 3, 8 ) = READ( rp.hl ); goto loop; - + case 0x01: // LD rp,imm case 0x11: case 0x21: R16( opcode, 4, 0x01 ) = GET_ADDR(); pc += 2; goto loop; - + case 0x31: // LD sp,imm sp = GET_ADDR(); pc += 2; goto loop; - + case 0x2A:{// LD HL,(addr) uint16_t addr = GET_ADDR(); pc += 2; rp.hl = READ_WORD( addr ); goto loop; } - + case 0x32:{// LD (addr),A uint16_t addr = GET_ADDR(); pc += 2; WRITE( addr, rg.a ); goto loop; } - + case 0x22:{// LD (addr),HL uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, rp.hl ); goto loop; } - + case 0x02: // LD (BC),A case 0x12: // LD (DE),A WRITE( R16( opcode, 4, 0x02 ), rg.a ); goto loop; - + case 0x0A: // LD A,(BC) case 0x1A: // LD A,(DE) rg.a = READ( R16( opcode, 4, 0x0A ) ); goto loop; - + case 0xF9: // LD SP,HL sp = rp.hl; goto loop; - + // Rotate - + case 0x07:{// RLCA uint16_t temp = rg.a; temp = (temp << 1) | (temp >> 7); @@ -696,7 +698,7 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x0F:{// RRCA uint16_t temp = rg.a; flags = (flags & (S80 | Z40 | P04)) | @@ -706,16 +708,16 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x17:{// RLA - blargg_ulong temp = (rg.a << 1) | (flags & C01); + uint32_t temp = (rg.a << 1) | (flags & C01); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (temp >> 8); rg.a = temp; goto loop; } - + case 0x1F:{// RRA uint16_t temp = (flags << 7) | (rg.a >> 1); flags = (flags & (S80 | Z40 | P04)) | @@ -724,7 +726,7 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + // Misc case 0x2F:{// CPL uint16_t temp = ~rg.a; @@ -734,19 +736,19 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x3F:{// CCF flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | (flags << 4 & H10) | (rg.a & (F20 | F08)); goto loop; } - + case 0x37: // SCF flags = (flags & (S80 | Z40 | P04)) | C01 | (rg.a & (F20 | F08)); goto loop; - + case 0xDB: // IN A,(imm) pc++; rg.a = IN( data + rg.a * 0x100 ); @@ -758,43 +760,43 @@ possibly_out_of_time: rp.hl = temp; goto loop; } - + case 0xEB:{// EX DE,HL uint16_t temp = rp.hl; rp.hl = rp.de; rp.de = temp; goto loop; } - + case 0xD9:{// EXX DE,HL uint16_t temp = r.alt.w.bc; r.alt.w.bc = rp.bc; rp.bc = temp; - + temp = r.alt.w.de; r.alt.w.de = rp.de; rp.de = temp; - + temp = r.alt.w.hl; r.alt.w.hl = rp.hl; rp.hl = temp; goto loop; } - + case 0xF3: // DI r.iff1 = 0; r.iff2 = 0; goto loop; - + case 0xFB: // EI r.iff1 = 1; r.iff2 = 1; // TODO: delayed effect goto loop; - + case 0x76: // HALT goto halt; - + //////////////////////////////////////// CB prefix { case 0xCB: @@ -804,9 +806,9 @@ possibly_out_of_time: pc++; switch ( data ) { - + // Rotate left - + #define RLC( read, write ) {\ uint8_t result = read;\ result = uint8_t (result << 1) | (result >> 7);\ @@ -814,67 +816,67 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x06: // RLC (HL) s_time += 7; data = rp.hl; rlc_data_addr: RLC( READ( data ), WRITE( data, result ) ) - + CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r uint8_t& reg = R8( data, 0 ); RLC( reg, reg = result ) } - + #define RL( read, write ) {\ uint16_t result = (read << 1) | (flags & C01);\ flags = SZ28PC( result );\ write;\ goto loop;\ } - + case 0x16: // RL (HL) s_time += 7; data = rp.hl; rl_data_addr: RL( READ( data ), WRITE( data, result ) ) - + CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r uint8_t& reg = R8( data, 0x10 ); RL( reg, reg = result ) } - + #define SLA( read, add, write ) {\ uint16_t result = (read << 1) | add;\ flags = SZ28PC( result );\ write;\ goto loop;\ } - + case 0x26: // SLA (HL) s_time += 7; data = rp.hl; sla_data_addr: SLA( READ( data ), 0, WRITE( data, result ) ) - + CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r uint8_t& reg = R8( data, 0x20 ); SLA( reg, 0, reg = result ) } - + case 0x36: // SLL (HL) s_time += 7; data = rp.hl; sll_data_addr: SLA( READ( data ), 1, WRITE( data, result ) ) - + CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r uint8_t& reg = R8( data, 0x30 ); SLA( reg, 1, reg = result ) } - + // Rotate right - + #define RRC( read, write ) {\ uint8_t result = read;\ flags = result & C01;\ @@ -883,18 +885,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x0E: // RRC (HL) s_time += 7; data = rp.hl; rrc_data_addr: RRC( READ( data ), WRITE( data, result ) ) - + CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r uint8_t& reg = R8( data, 0x08 ); RRC( reg, reg = result ) } - + #define RR( read, write ) {\ uint8_t result = read;\ uint8_t temp = result & C01;\ @@ -903,18 +905,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x1E: // RR (HL) s_time += 7; data = rp.hl; rr_data_addr: RR( READ( data ), WRITE( data, result ) ) - + CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r uint8_t& reg = R8( data, 0x18 ); RR( reg, reg = result ) } - + #define SRA( read, write ) {\ uint8_t result = read;\ flags = result & C01;\ @@ -923,18 +925,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x2E: // SRA (HL) data = rp.hl; s_time += 7; sra_data_addr: SRA( READ( data ), WRITE( data, result ) ) - + CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r uint8_t& reg = R8( data, 0x28 ); SRA( reg, reg = result ) } - + #define SRL( read, write ) {\ uint8_t result = read;\ flags = result & C01;\ @@ -943,18 +945,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x3E: // SRL (HL) s_time += 7; data = rp.hl; srl_data_addr: SRL( READ( data ), WRITE( data, result ) ) - + CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r uint8_t& reg = R8( data, 0x38 ); SRL( reg, reg = result ) } - + // BIT { unsigned temp; @@ -979,7 +981,7 @@ possibly_out_of_time: ((masked - 1) >> 8 & (Z40 | P04)); goto loop; } - + // SET/RES CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) @@ -992,7 +994,7 @@ possibly_out_of_time: WRITE( rp.hl, temp ); goto loop; } - + CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r @@ -1003,7 +1005,7 @@ possibly_out_of_time: CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); goto loop; - + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r @@ -1026,7 +1028,7 @@ possibly_out_of_time: switch ( data ) { { - blargg_ulong temp; + uint32_t temp; case 0x72: // SBC HL,SP case 0x7A: // ADC HL,SP temp = sp; @@ -1038,10 +1040,10 @@ possibly_out_of_time: case 0x5A: // ADC HL,DE case 0x6A: // ADC HL,HL temp = R16( data >> 3 & 6, 1, 0 ); - blargg_ulong sum = temp + (flags & C01); + uint32_t sum = temp + (flags & C01); flags = ~data >> 2 & N02; if ( flags ) - sum = -sum; + sum = uMinus(sum); sum += rp.hl; temp ^= rp.hl; temp ^= sum; @@ -1055,7 +1057,7 @@ possibly_out_of_time: flags |= Z40; goto loop; } - + CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) int temp = IN( rp.bc ); R8( data >> 3, 8 ) = temp; @@ -1068,7 +1070,7 @@ possibly_out_of_time: CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r OUT( rp.bc, R8( data >> 3, 8 ) ); goto loop; - + { unsigned temp; case 0x73: // LD (ADDR),SP @@ -1082,7 +1084,7 @@ possibly_out_of_time: WRITE_WORD( addr, temp ); goto loop; } - + case 0x4B: // LD BC,(ADDR) case 0x5B:{// LD DE,(ADDR) uint16_t addr = GET_ADDR(); @@ -1090,14 +1092,14 @@ possibly_out_of_time: R16( data, 4, 0x4B ) = READ_WORD( addr ); goto loop; } - + case 0x7B:{// LD SP,(ADDR) uint16_t addr = GET_ADDR(); pc += 2; sp = READ_WORD( addr ); goto loop; } - + case 0x67:{// RRD uint8_t temp = READ( rp.hl ); WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); @@ -1106,7 +1108,7 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x6F:{// RLD uint8_t temp = READ( rp.hl ); WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); @@ -1115,14 +1117,14 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG opcode = 0x10; // flag to do SBC instead of ADC flags &= ~C01; data = rg.a; rg.a = 0; goto adc_data; - + { int inc; case 0xA9: // CPD @@ -1135,27 +1137,27 @@ possibly_out_of_time: uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); - + int result = rg.a - temp; flags = (flags & C01) | N02 | ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); - + if ( !(uint8_t) result ) flags |= Z40; result -= (flags & H10) >> 4; flags |= result & F08; flags |= result << 4 & F20; if ( !--rp.bc ) goto loop; - + flags |= V04; if ( flags & Z40 || data < 0xB0 ) goto loop; - + pc -= 2; s_time += 5; goto loop; } - + { int inc; case 0xA8: // LDD @@ -1168,26 +1170,26 @@ possibly_out_of_time: uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); - + addr = rp.de; rp.de = addr + inc; WRITE( addr, temp ); - + temp += rg.a; flags = (flags & (S80 | Z40 | C01)) | (temp & F08) | (temp << 4 & F20); if ( !--rp.bc ) goto loop; - + flags |= V04; if ( data < 0xB0 ) goto loop; - + pc -= 2; s_time += 5; goto loop; } - + { int inc; case 0xAB: // OUTD @@ -1200,7 +1202,7 @@ possibly_out_of_time: uint16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); - + int b = --rg.b; flags = (temp >> 6 & N02) | SZ28( b ); if ( b && data >= 0xB0 ) @@ -1208,11 +1210,11 @@ possibly_out_of_time: pc -= 2; s_time += 5; } - + OUT( rp.bc, temp ); goto loop; } - + { int inc; case 0xAA: // IND @@ -1222,12 +1224,12 @@ possibly_out_of_time: case 0xA2: // INI case 0xB2: // INIR inc = +1; - + uint16_t addr = rp.hl; rp.hl = addr + inc; - + int temp = IN( rp.bc ); - + int b = --rg.b; flags = (temp >> 6 & N02) | SZ28( b ); if ( b && data >= 0xB0 ) @@ -1235,25 +1237,25 @@ possibly_out_of_time: pc -= 2; s_time += 5; } - + WRITE( addr, temp ); goto loop; } - + case 0x47: // LD I,A r.i = rg.a; goto loop; - + case 0x4F: // LD R,A SET_R( rg.a ); debug_printf( "LD R,A not supported\n" ); warning = true; goto loop; - + case 0x57: // LD A,I rg.a = r.i; goto ld_ai_common; - + case 0x5F: // LD A,R rg.a = GET_R(); debug_printf( "LD A,R not supported\n" ); @@ -1261,23 +1263,23 @@ possibly_out_of_time: ld_ai_common: flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); goto loop; - + CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN r.iff1 = r.iff2; goto ret_taken; - + case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 r.im = 0; goto loop; - + case 0x56: case 0x76: // IM 1 r.im = 1; goto loop; - + case 0x5E: case 0x7E: // IM 2 r.im = 2; goto loop; - + default: debug_printf( "Opcode $ED $%02X not supported\n", data ); warning = true; @@ -1302,11 +1304,11 @@ possibly_out_of_time: { // TODO: more efficient way of avoid negative address #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) - + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; - + // ADD/ADC/SUB/SBC - + case 0x96: // SUB (IXY+disp) case 0x86: // ADD (IXY+disp) flags &= ~C01; /*fallthrough*/ @@ -1316,7 +1318,7 @@ possibly_out_of_time: opcode = data; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto adc_data; - + case 0x94: // SUB HXY case 0x84: // ADD HXY flags &= ~C01; /*fallthrough*/ @@ -1325,7 +1327,7 @@ possibly_out_of_time: opcode = data; data = ixy >> 8; goto adc_data; - + case 0x95: // SUB LXY case 0x85: // ADD LXY flags &= ~C01; /* fallthrough */ @@ -1334,22 +1336,22 @@ possibly_out_of_time: opcode = data; data = (uint8_t) ixy; goto adc_data; - + { unsigned temp; case 0x39: // ADD IXY,SP temp = sp; goto add_ixy_data; - + case 0x29: // ADD IXY,HL temp = ixy; goto add_ixy_data; - + case 0x09: // ADD IXY,BC case 0x19: // ADD IXY,DE temp = R16( data, 4, 0x09 ); add_ixy_data: { - blargg_ulong sum = ixy + temp; + uint32_t sum = ixy + temp; temp ^= ixy; ixy = (uint16_t) sum; flags = (flags & (S80 | Z40 | V04)) | @@ -1359,63 +1361,63 @@ possibly_out_of_time: goto set_ixy; } } - + // AND case 0xA6: // AND (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto and_data; - + case 0xA4: // AND HXY data = ixy >> 8; goto and_data; - + case 0xA5: // AND LXY data = (uint8_t) ixy; goto and_data; - + // OR case 0xB6: // OR (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto or_data; - + case 0xB4: // OR HXY data = ixy >> 8; goto or_data; - + case 0xB5: // OR LXY data = (uint8_t) ixy; goto or_data; - + // XOR case 0xAE: // XOR (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto xor_data; - + case 0xAC: // XOR HXY data = ixy >> 8; goto xor_data; - + case 0xAD: // XOR LXY data = (uint8_t) ixy; goto xor_data; - + // CP case 0xBE: // CP (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto cp_data; - + case 0xBC: // CP HXY data = ixy >> 8; goto cp_data; - + case 0xBD: // CP LXY data = (uint8_t) ixy; goto cp_data; - + // LD CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r data = R8( data, 0x70 ); @@ -1429,42 +1431,42 @@ possibly_out_of_time: CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY R8( data >> 3, 8 ) = ixy >> 8; goto loop; - + case 0x64: // LD HXY,HXY case 0x6D: // LD LXY,LXY goto loop; - + CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY R8( data >> 3, 8 ) = ixy; goto loop; - + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) pc++; R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto loop; - + case 0x26: // LD HXY,imm pc++; goto ld_hxy_data; - + case 0x65: // LD HXY,LXY data2 = (uint8_t) ixy; goto ld_hxy_data; - + CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r data2 = R8( data, 0x60 ); ld_hxy_data: ixy = (uint8_t) ixy | (data2 << 8); goto set_ixy; - + case 0x2E: // LD LXY,imm pc++; goto ld_lxy_data; - + case 0x6C: // LD LXY,HXY data2 = ixy >> 8; goto ld_lxy_data; - + CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r data2 = R8( data, 0x68 ); ld_lxy_data: @@ -1481,26 +1483,26 @@ possibly_out_of_time: case 0xF9: // LD SP,IXY sp = ixy; goto loop; - + case 0x22:{// LD (ADDR),IXY uint16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, ixy ); goto loop; } - + case 0x21: // LD IXY,imm ixy = GET_ADDR(); pc += 2; goto set_ixy; - + case 0x2A:{// LD IXY,(addr) uint16_t addr = GET_ADDR(); ixy = READ_WORD( addr ); pc += 2; goto set_ixy; } - + // DD/FD CB prefix case 0xCB: { data = IXY_DISP( ixy, (int8_t) data2 ); @@ -1517,7 +1519,7 @@ possibly_out_of_time: case 0x1E: goto rr_data_addr; // RR (IXY) case 0x2E: goto sra_data_addr; // SRA (IXY) case 0x3E: goto srl_data_addr; // SRL (IXY) - + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) uint8_t temp = READ( data ); int masked = temp & 1 << (data2 >> 3 & 7); @@ -1526,7 +1528,7 @@ possibly_out_of_time: ((masked - 1) >> 8 & (Z40 | P04)); goto loop; } - + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) int temp = READ( data ); @@ -1537,7 +1539,7 @@ possibly_out_of_time: WRITE( data, temp ); goto loop; } - + default: debug_printf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); warning = true; @@ -1545,35 +1547,35 @@ possibly_out_of_time: } assert( false ); } - + // INC/DEC case 0x23: // INC IXY ixy = uint16_t (ixy + 1); goto set_ixy; - + case 0x2B: // DEC IXY ixy = uint16_t (ixy - 1); goto set_ixy; - + case 0x34: // INC (IXY+disp) ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) + 1; WRITE( ixy, data ); goto inc_set_flags; - + case 0x35: // DEC (IXY+disp) ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) - 1; WRITE( ixy, data ); goto dec_set_flags; - + case 0x24: // INC HXY ixy = uint16_t (ixy + 0x100); data = ixy >> 8; goto inc_xy_common; - + case 0x2C: // INC LXY data = uint8_t (ixy + 1); ixy = (ixy & 0xFF00) | data; @@ -1585,12 +1587,12 @@ possibly_out_of_time: } iy = ixy; goto inc_set_flags; - + case 0x25: // DEC HXY ixy = uint16_t (ixy - 0x100); data = ixy >> 8; goto dec_xy_common; - + case 0x2D: // DEC LXY data = uint8_t (ixy - 1); ixy = (ixy & 0xFF00) | data; @@ -1602,31 +1604,31 @@ possibly_out_of_time: } iy = ixy; goto dec_set_flags; - + // PUSH/POP case 0xE5: // PUSH IXY data = ixy; goto push_data; - + case 0xE1:{// POP IXY ixy = READ_WORD( sp ); sp = uint16_t (sp + 2); goto set_ixy; } - + // Misc - + case 0xE9: // JP (IXY) pc = ixy; goto loop; - + case 0xE3:{// EX (SP),IXY uint16_t temp = READ_WORD( sp ); WRITE_WORD( sp, ixy ); ixy = temp; goto set_ixy; } - + default: debug_printf( "Unnecessary DD/FD prefix encountered\n" ); warning = true; @@ -1635,16 +1637,16 @@ possibly_out_of_time: } assert( false ); } - + } debug_printf( "Unhandled main opcode: $%02X\n", opcode ); assert( false ); - + halt: s_time &= 3; // increment by multiple of 4 out_of_time: pc--; - + s.time = s_time; rg.flags = flags; r.ix = ix; @@ -1654,6 +1656,6 @@ out_of_time: this->r.b = rg; this->state_ = s; this->state = &this->state_; - + return warning; } diff --git a/Frameworks/GME/gme/Ay_Cpu.h b/Frameworks/GME/gme/Ay_Cpu.h index 93493ae02..5180a0d6b 100644 --- a/Frameworks/GME/gme/Ay_Cpu.h +++ b/Frameworks/GME/gme/Ay_Cpu.h @@ -6,7 +6,7 @@ #include "blargg_endian.h" -typedef blargg_long cpu_time_t; +typedef int32_t cpu_time_t; // must be defined by caller void ay_cpu_out( class Ay_Cpu*, cpu_time_t, unsigned addr, int data ); @@ -16,27 +16,27 @@ class Ay_Cpu { public: // Clear all registers and keep pointer to 64K memory passed in void reset( void* mem_64k ); - + // Run until specified time is reached. Returns true if suspicious/unsupported // instruction was encountered at any point during run. bool run( cpu_time_t end_time ); - + // Time of beginning of next instruction cpu_time_t time() const { return state->time + state->base; } - + // Alter current time. Not supported during run() call. void set_time( cpu_time_t t ) { state->time = t - state->base; } void adjust_time( int delta ) { state->time += delta; } - + #if BLARGG_BIG_ENDIAN struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; #else struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; #endif static_assert( sizeof (regs_t) == 8, "Invalid register size, padding issue?" ); - + struct pairs_t { uint16_t bc, de, hl, fa; }; - + // Registers are not updated until run() returns struct registers_t { uint16_t pc; @@ -58,10 +58,10 @@ public: uint8_t im; }; //registers_t r; (below for efficiency) - + // can read this far past end of memory enum { cpu_padding = 0x100 }; - + public: Ay_Cpu(); private: diff --git a/Frameworks/GME/gme/Ay_Emu.cpp b/Frameworks/GME/gme/Ay_Emu.cpp index b247e1f51..e99b6fdc8 100644 --- a/Frameworks/GME/gme/Ay_Emu.cpp +++ b/Frameworks/GME/gme/Ay_Emu.cpp @@ -20,11 +20,11 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -long const spectrum_clock = 3546900; -long const cpc_clock = 2000000; +static long const spectrum_clock = 3546900; +static long const cpc_clock = 2000000; -unsigned const ram_start = 0x4000; -int const osc_count = Ay_Apu::osc_count + 1; +static unsigned const ram_start = 0x4000; +static int const osc_count = Ay_Apu::osc_count + 1; using std::min; using std::max; @@ -33,12 +33,12 @@ Ay_Emu::Ay_Emu() { beeper_output = 0; set_type( gme_ay_type ); - + static const char* const names [osc_count] = { "Wave 1", "Wave 2", "Wave 3", "Beeper" }; set_voice_names( names ); - + static int const types [osc_count] = { wave_type | 0, wave_type | 1, wave_type | 2, mixed_type | 0 }; @@ -56,7 +56,7 @@ static byte const* get_data( Ay_Emu::file_t const& file, byte const* ptr, int mi long file_size = file.end - (byte const*) file.header; assert( (unsigned long) pos <= (unsigned long) file_size - 2 ); int offset = (int16_t) get_be16( ptr ); - if ( !offset || blargg_ulong (pos + offset) > blargg_ulong (file_size - min_size) ) + if ( !offset || uint32_t (pos + offset) > uint32_t (file_size - min_size) ) return 0; return ptr + offset; } @@ -66,18 +66,18 @@ static blargg_err_t parse_header( byte const* in, long size, Ay_Emu::file_t* out typedef Ay_Emu::header_t header_t; out->header = (header_t const*) in; out->end = in + size; - + if ( size < Ay_Emu::header_size ) return gme_wrong_file_type; - + header_t const& h = *(header_t const*) in; if ( memcmp( h.tag, "ZXAYEMUL", 8 ) ) return gme_wrong_file_type; - + out->tracks = get_data( *out, h.track_info, (h.max_track + 1) * 4 ); if ( !out->tracks ) return "Missing track data"; - + return 0; } @@ -87,7 +87,7 @@ static void copy_ay_fields( Ay_Emu::file_t const& file, track_info_t* out, int t byte const* track_info = get_data( file, file.tracks + track * 4 + 2, 6 ); if ( track_info ) out->length = get_be16( track_info + 4 ) * (1000L / 50); // frames to msec - + Gme_File::copy_field_( out->author, (char const*) get_data( file, file.header->author, 1 ) ); Gme_File::copy_field_( out->comment, (char const*) get_data( file, file.header->comment, 1 ) ); } @@ -101,16 +101,16 @@ blargg_err_t Ay_Emu::track_info_( track_info_t* out, int track ) const struct Ay_File : Gme_Info_ { Ay_Emu::file_t file; - + Ay_File() { set_type( gme_ay_type ); } - + blargg_err_t load_mem_( byte const* begin, long size ) { RETURN_ERR( parse_header( begin, size, &file ) ); set_track_count( file.header->max_track + 1 ); return 0; } - + blargg_err_t track_info_( track_info_t* out, int track ) const { copy_ay_fields( file, out, track ); @@ -128,20 +128,20 @@ extern gme_type_t const gme_ay_type = &gme_ay_type_; blargg_err_t Ay_Emu::load_mem_( byte const* in, long size ) { - assert( offsetof (header_t,track_info [2]) == header_size ); - + blaarg_static_assert( offsetof (header_t,track_info [2]) == header_size, "AY Header layout incorrect!" ); + RETURN_ERR( parse_header( in, size, &file ) ); set_track_count( file.header->max_track + 1 ); - + if ( file.header->vers > 2 ) set_warning( "Unknown file version" ); - + set_voice_count( osc_count ); apu.volume( gain() ); - + return setup_buffer( spectrum_clock ); } - + void Ay_Emu::update_eq( blip_eq_t const& eq ) { apu.treble_eq( eq ); @@ -165,23 +165,23 @@ void Ay_Emu::set_tempo_( double t ) blargg_err_t Ay_Emu::start_track_( int track ) { RETURN_ERR( Classic_Emu::start_track_( track ) ); - + memset( mem.ram + 0x0000, 0xC9, 0x100 ); // fill RST vectors with RET memset( mem.ram + 0x0100, 0xFF, 0x4000 - 0x100 ); memset( mem.ram + ram_start, 0x00, sizeof mem.ram - ram_start ); memset( mem.padding1, 0xFF, sizeof mem.padding1 ); memset( mem.ram + 0x10000, 0xFF, sizeof mem.ram - 0x10000 ); - + // locate data blocks byte const* const data = get_data( file, file.tracks + track * 4 + 2, 14 ); if ( !data ) return "File data missing"; - + byte const* const more_data = get_data( file, data + 10, 6 ); if ( !more_data ) return "File data missing"; - + byte const* blocks = get_data( file, data + 12, 8 ); if ( !blocks ) return "File data missing"; - + // initial addresses cpu::reset( mem.ram ); r.sp = get_be16( more_data ); @@ -189,14 +189,14 @@ blargg_err_t Ay_Emu::start_track_( int track ) r.b.flags = r.b.c = r.b.e = r.b.l = data [9]; r.alt.w = r.w; r.ix = r.iy = r.w.hl; - + unsigned addr = get_be16( blocks ); if ( !addr ) return "File data missing"; - + unsigned init = get_be16( more_data + 2 ); if ( !init ) init = addr; - + // copy blocks into memory do { @@ -209,7 +209,7 @@ blargg_err_t Ay_Emu::start_track_( int track ) } check( len ); byte const* in = get_data( file, blocks, 0 ); blocks += 2; - if ( len > blargg_ulong (file.end - in) ) + if ( len > uint32_t (file.end - in) ) { set_warning( "Missing file data" ); len = file.end - in; @@ -218,7 +218,7 @@ blargg_err_t Ay_Emu::start_track_( int track ) if ( addr < ram_start && addr >= 0x400 ) // several tracks use low data debug_printf( "Block addr in ROM\n" ); memcpy( mem.ram + addr, in, len ); - + if ( file.end - blocks < 8 ) { set_warning( "Missing file data" ); @@ -226,7 +226,7 @@ blargg_err_t Ay_Emu::start_track_( int track ) } } while ( (addr = get_be16( blocks )) != 0 ); - + // copy and configure driver static byte const passive [] = { 0xF3, // DI @@ -256,24 +256,24 @@ blargg_err_t Ay_Emu::start_track_( int track ) } mem.ram [2] = init; mem.ram [3] = init >> 8; - + mem.ram [0x38] = 0xFB; // Put EI at interrupt vector (followed by RET) - + memcpy( mem.ram + 0x10000, mem.ram, 0x80 ); // some code wraps around (ugh) - + beeper_delta = int (apu.amp_range * 0.65); last_beeper = 0; apu.reset(); next_play = play_period; - + // start at spectrum speed change_clock_rate( spectrum_clock ); set_tempo( tempo() ); - + spectrum_mode = false; cpc_mode = false; cpc_latch = 0; - + return 0; } @@ -289,14 +289,14 @@ void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data ) spectrum_mode = true; apu_addr = data & 0x0F; return; - + case 0xBEFD: spectrum_mode = true; apu.write( time, apu_addr, data ); return; } } - + if ( !spectrum_mode ) { switch ( addr >> 8 ) @@ -307,22 +307,22 @@ void Ay_Emu::cpu_out_misc( cpu_time_t time, unsigned addr, int data ) case 0xC0: apu_addr = cpc_latch & 0x0F; goto enable_cpc; - + case 0x80: apu.write( time, apu_addr, cpc_latch ); goto enable_cpc; } break; - + case 0xF4: cpc_latch = data; goto enable_cpc; } } - + debug_printf( "Unmapped OUT: $%04X <- $%02X\n", addr, data ); return; - + enable_cpc: if ( !cpc_mode ) { @@ -335,7 +335,7 @@ enable_cpc: void ay_cpu_out( Ay_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) { Ay_Emu& emu = STATIC_CAST(Ay_Emu&,*cpu); - + if ( (addr & 0xFF) == 0xFE && !emu.cpc_mode ) { int delta = emu.beeper_delta; @@ -360,7 +360,7 @@ int ay_cpu_in( Ay_Cpu*, unsigned addr ) // keyboard read and other things if ( (addr & 0xFF) == 0xFE ) return 0xFF; // other values break some beeper tunes - + debug_printf( "Unmapped IN : $%04X\n", addr ); return 0xFF; } @@ -370,22 +370,22 @@ blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) set_time( 0 ); if ( !(spectrum_mode | cpc_mode) ) duration /= 2; // until mode is set, leave room for halved clock rate - + while ( time() < duration ) { cpu::run( min( duration, (blip_time_t) next_play ) ); - + if ( time() >= next_play ) { next_play += play_period; - + if ( r.iff1 ) { if ( mem.ram [r.pc] == 0x76 ) r.pc++; - + r.iff1 = r.iff2 = 0; - + mem.ram [--r.sp] = uint8_t (r.pc >> 8); mem.ram [--r.sp] = uint8_t (r.pc); r.pc = 0x38; @@ -403,8 +403,8 @@ blargg_err_t Ay_Emu::run_clocks( blip_time_t& duration, int ) next_play -= duration; check( next_play >= 0 ); adjust_time( -duration ); - + apu.end_frame( duration ); - + return 0; } diff --git a/Frameworks/GME/gme/Ay_Emu.h b/Frameworks/GME/gme/Ay_Emu.h index 6726f0157..5266d9cb9 100644 --- a/Frameworks/GME/gme/Ay_Emu.h +++ b/Frameworks/GME/gme/Ay_Emu.h @@ -25,7 +25,7 @@ public: byte first_track; byte track_info [2]; }; - + static gme_type_t static_type() { return gme_ay_type; } public: Ay_Emu(); @@ -45,7 +45,7 @@ protected: void update_eq( blip_eq_t const& ); private: file_t file; - + cpu_time_t play_period; cpu_time_t next_play; Blip_Buffer* beeper_output; @@ -55,7 +55,7 @@ private: int cpc_latch; bool spectrum_mode; bool cpc_mode; - + // large items struct { byte padding1 [0x100]; diff --git a/Frameworks/GME/gme/Blip_Buffer.cpp b/Frameworks/GME/gme/Blip_Buffer.cpp index 2b88cd4f8..34de533bf 100644 --- a/Frameworks/GME/gme/Blip_Buffer.cpp +++ b/Frameworks/GME/gme/Blip_Buffer.cpp @@ -23,7 +23,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include BLARGG_ENABLE_OPTIMIZER #endif -int const silent_buf_size = 1; // size used for Silent_Blip_Buffer +static int const silent_buf_size = 1; // size used for Silent_Blip_Buffer Blip_Buffer::Blip_Buffer() { @@ -37,13 +37,13 @@ Blip_Buffer::Blip_Buffer() clock_rate_ = 0; bass_freq_ = 16; length_ = 0; - + // assumptions code makes about implementation-defined features #ifndef NDEBUG // right shift of negative value preserves sign buf_t_ i = -0x7FFFFFFE; assert( (i >> 1) == -0x3FFFFFFF ); - + // casting to short truncates to 16 bits and sign-extends i = 0x18000; assert( (short) i == -0x8000 ); @@ -83,7 +83,7 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec assert( 0 ); return "Internal (tried to resize Silent_Blip_Buffer)"; } - + // start with maximum length that resampled time can represent long new_size = (UINT_MAX >> BLIP_BUFFER_ACCURACY) - blip_buffer_extra_ - 64; if ( msec != blip_max_length ) @@ -94,7 +94,7 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec else assert( 0 ); // fails if requested buffer length exceeds limit } - + if ( buffer_size_ != new_size ) { void* p = realloc( buffer_, (new_size + blip_buffer_extra_) * sizeof *buffer_ ); @@ -102,10 +102,10 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec return "Out of memory"; buffer_ = (buf_t_*) p; } - + buffer_size_ = new_size; assert( buffer_size_ != silent_buf_size ); - + // update things based on the sample rate sample_rate_ = new_rate; length_ = new_size * 1000 / new_rate - 1; @@ -114,9 +114,9 @@ Blip_Buffer::blargg_err_t Blip_Buffer::set_sample_rate( long new_rate, int msec if ( clock_rate_ ) clock_rate( clock_rate_ ); bass_freq( bass_freq_ ); - + clear(); - + return 0; // success } @@ -144,7 +144,6 @@ void Blip_Buffer::bass_freq( int freq ) void Blip_Buffer::end_frame( blip_time_t t ) { offset_ += t * factor_; - assert( samples_avail() <= (long) buffer_size_ ); // time outside buffer length } void Blip_Buffer::remove_silence( long count ) @@ -167,7 +166,7 @@ blip_time_t Blip_Buffer::count_clocks( long count ) const assert( 0 ); // sample rate and clock rates must be set first return 0; } - + if ( count > buffer_size_ ) count = buffer_size_; blip_resampled_time_t time = (blip_resampled_time_t) count << BLIP_BUFFER_ACCURACY; @@ -179,7 +178,7 @@ void Blip_Buffer::remove_samples( long count ) if ( count ) { remove_silence( count ); - + // copy remaining samples to beginning and clear old samples long remain = samples_avail() + blip_buffer_extra_; memmove( buffer_, buffer_ + count, remain * sizeof *buffer_ ); @@ -221,12 +220,12 @@ static void gen_sinc( float* out, int count, double oversample, double treble, d { if ( cutoff >= 0.999 ) cutoff = 0.999; - + if ( treble < -300.0 ) treble = -300.0; if ( treble > 5.0 ) treble = 5.0; - + double const maxh = 4096.0; double const rolloff = pow( 10.0, 1.0 / (maxh * 20.0) * treble / (1.0 - cutoff) ); double const pow_a_n = pow( rolloff, maxh - maxh * cutoff ); @@ -236,17 +235,17 @@ static void gen_sinc( float* out, int count, double oversample, double treble, d double angle = ((i - count) * 2 + 1) * to_angle; double angle_maxh = angle * maxh; double angle_maxh_mid = angle_maxh * cutoff; - + double y = maxh; - + // 0 to Fs/2*cutoff, flat if ( angle_maxh_mid ) // unstable at t=0 y *= sin( angle_maxh_mid ) / angle_maxh_mid; - + // Fs/2*cutoff to Fs/2, logarithmic rolloff double cosa = cos( angle ); double den = 1 + rolloff * (rolloff - cosa - cosa); - + // Becomes unstable when rolloff is near 1.0 and t is near 0, // which is the only time den becomes small if ( den > 1e-13 ) @@ -254,10 +253,10 @@ static void gen_sinc( float* out, int count, double oversample, double treble, d double num = (cos( angle_maxh - angle ) * rolloff - cos( angle_maxh )) * pow_a_n - cos( angle_maxh_mid - angle ) * rolloff + cos( angle_maxh_mid ); - + y = y * cutoff + num / den; } - + out [i] = (float) y; } } @@ -271,9 +270,9 @@ void blip_eq_t::generate( float* out, int count ) const if ( cutoff_freq ) oversample = half_rate / cutoff_freq; double cutoff = rolloff_freq * oversample / half_rate; - + gen_sinc( out, count, blip_res * oversample, treble, cutoff ); - + // apply (half of) hamming window double to_fraction = PI / (count - 1); for ( int i = count; i--; ) @@ -298,7 +297,7 @@ void Blip_Synth_::adjust_impulse() impulses [size - blip_res + p] += (short) error; //printf( "error: %ld\n", error ); } - + //for ( int i = blip_res; i--; printf( "\n" ) ) // for ( int j = 0; j < width / 2; j++ ) // printf( "%5ld,", impulses [j * blip_res + i + 1] ); @@ -307,31 +306,31 @@ void Blip_Synth_::adjust_impulse() void Blip_Synth_::treble_eq( blip_eq_t const& eq ) { float fimpulse [blip_res / 2 * (blip_widest_impulse_ - 1) + blip_res * 2]; - + int const half_size = blip_res / 2 * (width - 1); eq.generate( &fimpulse [blip_res], half_size ); - + int i; - + // need mirror slightly past center for calculation for ( i = blip_res; i--; ) fimpulse [blip_res + half_size + i] = fimpulse [blip_res + half_size - 1 - i]; - + // starts at 0 for ( i = 0; i < blip_res; i++ ) fimpulse [i] = 0.0f; - + // find rescale factor double total = 0.0; for ( i = 0; i < half_size; i++ ) total += fimpulse [blip_res + i]; - + //double const base_unit = 44800.0 - 128 * 18; // allows treble up to +0 dB //double const base_unit = 37888.0; // allows treble to +5 dB double const base_unit = 32768.0; // necessary for blip_unscaled to work double rescale = base_unit / 2 / total; kernel_unit = (long) base_unit; - + // integrate, first difference, rescale, convert to int double sum = 0.0; double next = 0.0; @@ -343,7 +342,7 @@ void Blip_Synth_::treble_eq( blip_eq_t const& eq ) next += fimpulse [i + blip_res]; } adjust_impulse(); - + // volume might require rescaling double vol = volume_unit_; if ( vol ) @@ -360,26 +359,26 @@ void Blip_Synth_::volume_unit( double new_unit ) // use default eq if it hasn't been set yet if ( !kernel_unit ) treble_eq( -8.0 ); - + volume_unit_ = new_unit; double factor = new_unit * (1L << blip_sample_bits) / kernel_unit; - + if ( factor > 0.0 ) { int shift = 0; - + // if unit is really small, might need to attenuate kernel while ( factor < 2.0 ) { shift++; factor *= 2.0; } - + if ( shift ) { kernel_unit >>= shift; assert( kernel_unit > 0 ); // fails if volume unit is too low - + // keep values positive to avoid round-towards-zero of sign-preserving // right shift for negative values long offset = 0x8000 + (1 << (shift - 1)); @@ -400,12 +399,12 @@ long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_sampl long count = samples_avail(); if ( count > max_samples ) count = max_samples; - + if ( count ) { int const bass = BLIP_READER_BASS( *this ); BLIP_READER_BEGIN( reader, *this ); - + if ( !stereo ) { for ( blip_long n = count; n; --n ) @@ -430,7 +429,7 @@ long Blip_Buffer::read_samples( blip_sample_t* BLIP_RESTRICT out, long max_sampl } } BLIP_READER_END( reader, *this ); - + remove_samples( count ); } return count; @@ -443,9 +442,9 @@ void Blip_Buffer::mix_samples( blip_sample_t const* in, long count ) assert( 0 ); return; } - + buf_t_* out = buffer_ + (offset_ >> BLIP_BUFFER_ACCURACY) + blip_widest_impulse_ / 2; - + int const sample_shift = blip_sample_bits - 16; int prev = 0; while ( count-- ) diff --git a/Frameworks/GME/gme/Blip_Buffer.h b/Frameworks/GME/gme/Blip_Buffer.h index 475a37521..50e31e867 100644 --- a/Frameworks/GME/gme/Blip_Buffer.h +++ b/Frameworks/GME/gme/Blip_Buffer.h @@ -1,6 +1,7 @@ // Band-limited sound synthesis buffer // Blip_Buffer 0.4.1 + #ifndef BLIP_BUFFER_H #define BLIP_BUFFER_H @@ -9,7 +10,7 @@ #if INT_MAX < 0x7FFFFFFF #error "int must be at least 32 bits" #endif - + typedef int blip_long; typedef unsigned blip_ulong; @@ -23,66 +24,66 @@ enum { blip_sample_max = 32767 }; class Blip_Buffer { public: typedef const char* blargg_err_t; - + // Set output sample rate and buffer length in milliseconds (1/1000 sec, defaults // to 1/4 second), then clear buffer. Returns NULL on success, otherwise if there // isn't enough memory, returns error without affecting current buffer setup. blargg_err_t set_sample_rate( long samples_per_sec, int msec_length = 1000 / 4 ); - + // Set number of source time units per second void clock_rate( long ); - + // End current time frame of specified duration and make its samples available // (along with any still-unread samples) for reading with read_samples(). Begins // a new time frame at the end of the current frame. void end_frame( blip_time_t time ); - + // Read at most 'max_samples' out of buffer into 'dest', removing them from from // the buffer. Returns number of samples actually read and removed. If stereo is // true, increments 'dest' one extra time after writing each sample, to allow // easy interleving of two channels into a stereo output buffer. long read_samples( blip_sample_t* dest, long max_samples, int stereo = 0 ); - + // Additional optional features // Current output sample rate long sample_rate() const; - + // Length of buffer, in milliseconds int length() const; - + // Number of source time units per second long clock_rate() const; - + // Set frequency high-pass filter frequency, where higher values reduce bass more void bass_freq( int frequency ); - + // Number of samples delay from synthesis to samples read out int output_latency() const; - + // Remove all available samples and clear buffer to silence. If 'entire_buffer' is // false, just clears out any samples waiting rather than the entire buffer. void clear( int entire_buffer = 1 ); - + // Number of samples available for reading with read_samples() long samples_avail() const; - + // Remove 'count' samples from those waiting to be read void remove_samples( long count ); - + // Experimental features - + // Count number of clocks needed until 'count' samples will be available. // If buffer can't even hold 'count' samples, returns number of clocks until // buffer becomes full. blip_time_t count_clocks( long count ) const; - + // Number of raw samples that can be mixed within frame of specified duration. long count_samples( blip_time_t duration ) const; - + // Mix 'count' samples from 'buf' into buffer. void mix_samples( blip_sample_t const* buf, long count ); - + // not documented yet void set_modified() { modified_ = 1; } int clear_modified() { int b = modified_; modified_ = 0; return b; } @@ -94,7 +95,7 @@ public: public: Blip_Buffer(); ~Blip_Buffer(); - + // Deprecated typedef blip_resampled_time_t resampled_time_t; blargg_err_t sample_rate( long r ) { return set_sample_rate( r ); } @@ -120,9 +121,7 @@ private: friend class Blip_Reader; }; -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif +#include "blargg_config.h" // Number of bits in resample ratio fraction. Higher values give a more accurate ratio // but reduce maximum buffer size. @@ -147,24 +146,24 @@ private: int const blip_buffer_extra_ = blip_widest_impulse_ + 2; int const blip_res = 1 << BLIP_PHASE_BITS; class blip_eq_t; - + class Blip_Synth_Fast_ { public: Blip_Buffer* buf; int last_amp; int delta_factor; - + void volume_unit( double ); Blip_Synth_Fast_(); void treble_eq( blip_eq_t const& ) { } }; - + class Blip_Synth_ { public: Blip_Buffer* buf; int last_amp; int delta_factor; - + void volume_unit( double ); Blip_Synth_( short* impulses, int width ); void treble_eq( blip_eq_t const& ); @@ -190,14 +189,14 @@ class Blip_Synth { public: // Set overall volume of waveform void volume( double v ) { impl.volume_unit( v * (1.0 / (range < 0 ? -range : range)) ); } - + // Configure low-pass filter (see blip_buffer.txt) void treble_eq( blip_eq_t const& eq ) { impl.treble_eq( eq ); } - + // Get/set Blip_Buffer used for output Blip_Buffer* output() const { return impl.buf; } void output( Blip_Buffer* b ) { impl.buf = b; impl.last_amp = 0; } - + // Update amplitude of waveform at given time. Using this requires a separate // Blip_Synth for each waveform. void update( blip_time_t time, int amplitude ); @@ -209,10 +208,10 @@ public: // The actual change in amplitude is delta * (volume / range) void offset( blip_time_t, int delta, Blip_Buffer* ) const; void offset( blip_time_t t, int delta ) const { offset( t, delta, impl.buf ); } - + // Works directly in terms of fractional output samples. Contact author for more info. void offset_resampled( blip_resampled_time_t, int delta, Blip_Buffer* ) const; - + // Same as offset(), except code is inlined for higher performance void offset_inline( blip_time_t t, int delta, Blip_Buffer* buf ) const { offset_resampled( t * buf->factor_ + buf->offset_, delta, buf ); @@ -220,7 +219,7 @@ public: void offset_inline( blip_time_t t, int delta ) const { offset_resampled( t * impl.buf->factor_ + impl.buf->offset_, delta, impl.buf ); } - + private: #if BLIP_BUFFER_FAST Blip_Synth_Fast_ impl; @@ -233,9 +232,9 @@ public: #endif // disable broken defaulted constructors, Blip_Synth_ isn't safe to move/copy - Blip_Synth (const Blip_Synth &) = delete; - Blip_Synth ( Blip_Synth &&) = delete; - Blip_Synth& operator=(const Blip_Synth &) = delete; + Blip_Synth (const Blip_Synth &) = delete; + Blip_Synth ( Blip_Synth &&) = delete; + Blip_Synth& operator=(const Blip_Synth &) = delete; }; // Low-pass equalization parameters @@ -244,10 +243,10 @@ public: // Logarithmic rolloff to treble dB at half sampling rate. Negative values reduce // treble, small positive values (0 to 5.0) increase treble. blip_eq_t( double treble_db = 0 ); - + // See blip_buffer.txt blip_eq_t( double treble, long rolloff_freq, long sample_rate, long cutoff_freq = 0 ); - + private: double treble; long rolloff_freq; @@ -268,15 +267,16 @@ public: blargg_err_t set_sample_rate( long samples_per_sec, int msec_length ); blip_time_t count_clocks( long count ) const; void mix_samples( blip_sample_t const* buf, long count ); - + Silent_Blip_Buffer(); }; - #if defined (__GNUC__) || _MSC_VER >= 1100 - #define BLIP_RESTRICT __restrict - #else - #define BLIP_RESTRICT - #endif +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || \ + (defined(_MSC_VER) && (_MSC_VER >= 1100)) + #define BLIP_RESTRICT __restrict +#else + #define BLIP_RESTRICT +#endif // Optimized reading from Blip_Buffer, for use in custom sample output @@ -328,7 +328,7 @@ public: blip_long read_raw() const { return accum; } void next( int bass_shift = 9 ) { accum += *buf++ - (accum >> bass_shift); } void end( Blip_Buffer& b ) { b.reader_accum_ = accum; } - + private: const Blip_Buffer::buf_t_* buf; blip_long accum; @@ -351,14 +351,14 @@ inline void Blip_Synth::offset_resampled( blip_resampled_time_t t #if BLIP_BUFFER_FAST blip_long left = buf [0] + delta; - + // Kind of crappy, but doing shift after multiply results in overflow. // Alternate way of delaying multiply by delta_factor results in worse // sub-sample resolution. blip_long right = (delta >> BLIP_PHASE_BITS) * phase; left -= right; right += buf [1]; - + buf [0] = left; buf [1] = right; #else @@ -366,17 +366,17 @@ inline void Blip_Synth::offset_resampled( blip_resampled_time_t t int const fwd = (blip_widest_impulse_ - quality) / 2; int const rev = fwd + quality - 2; int const mid = quality / 2 - 1; - + imp_t const* BLIP_RESTRICT imp = impulses + blip_res - phase; - + #if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ defined (__x86_64__) || defined (__ia64__) || defined (__i386__) - + // straight forward implementation resulted in better code on GCC for x86 - + #define ADD_IMP( out, in ) \ buf [out] += (blip_long) imp [blip_res * (in)] * delta - + #define BLIP_FWD( i ) {\ ADD_IMP( fwd + i, i );\ ADD_IMP( fwd + 1 + i, i + 1 );\ @@ -397,14 +397,14 @@ inline void Blip_Synth::offset_resampled( blip_resampled_time_t t if ( quality > 12 ) BLIP_REV( 6 ) if ( quality > 8 ) BLIP_REV( 4 ) BLIP_REV( 2 ) - + ADD_IMP( rev , 1 ); ADD_IMP( rev + 1, 0 ); - + #else - + // for RISC processors, help compiler by reading ahead of writes - + #define BLIP_FWD( i ) {\ blip_long t0 = i0 * delta + buf [fwd + i];\ blip_long t1 = imp [blip_res * (i + 1)] * delta + buf [fwd + 1 + i];\ @@ -419,7 +419,7 @@ inline void Blip_Synth::offset_resampled( blip_resampled_time_t t buf [rev - r] = t0;\ buf [rev + 1 - r] = t1;\ } - + blip_long i0 = *imp; BLIP_FWD( 0 ) if ( quality > 8 ) BLIP_FWD( 2 ) @@ -435,13 +435,13 @@ inline void Blip_Synth::offset_resampled( blip_resampled_time_t t if ( quality > 12 ) BLIP_REV( 6 ) if ( quality > 8 ) BLIP_REV( 4 ) BLIP_REV( 2 ) - + blip_long t0 = i0 * delta + buf [rev ]; blip_long t1 = *imp * delta + buf [rev + 1]; buf [rev ] = t0; buf [rev + 1] = t1; #endif - + #endif } @@ -474,7 +474,11 @@ inline blip_eq_t::blip_eq_t( double t, long rf, long sr, long cf ) : treble( t ), rolloff_freq( rf ), sample_rate( sr ), cutoff_freq( cf ) { } inline int Blip_Buffer::length() const { return length_; } -inline long Blip_Buffer::samples_avail() const { return (long) (offset_ >> BLIP_BUFFER_ACCURACY); } +inline long Blip_Buffer::samples_avail() const +{ + long samples = (long) (offset_ >> BLIP_BUFFER_ACCURACY); + return samples <= (long) buffer_size_ ? samples : 0; +} inline long Blip_Buffer::sample_rate() const { return sample_rate_; } inline int Blip_Buffer::output_latency() const { return blip_widest_impulse_ / 2; } inline long Blip_Buffer::clock_rate() const { return clock_rate_; } diff --git a/Frameworks/GME/gme/CMakeLists.txt b/Frameworks/GME/gme/CMakeLists.txt index 5b4c644e6..fe2c35433 100644 --- a/Frameworks/GME/gme/CMakeLists.txt +++ b/Frameworks/GME/gme/CMakeLists.txt @@ -1,120 +1,178 @@ + +if(NOT GME_BUILD_SHARED AND NOT GME_BUILD_STATIC) + message(FATAL_ERROR "Both shared and static builds disabled. Please enable build of shared and/or static build of the GME.") +endif() + +if(GME_ZLIB) + find_package(ZLIB QUIET) +endif() + # List of source files required by libgme and any emulators # This is not 100% accurate (Fir_Resampler for instance) but # you'll be OK. set(libgme_SRCS Blip_Buffer.cpp + Blip_Buffer.h Classic_Emu.cpp + Classic_Emu.h Data_Reader.cpp + Data_Reader.h Dual_Resampler.cpp + Dual_Resampler.h Effects_Buffer.cpp + Effects_Buffer.h Fir_Resampler.cpp + Fir_Resampler.h gme.cpp + gme.h + gme_types.h Gme_File.cpp + Gme_File.h M3u_Playlist.cpp + M3u_Playlist.h Multi_Buffer.cpp + Multi_Buffer.h Music_Emu.cpp + Music_Emu.h + blargg_common.h + blargg_config.h + blargg_endian.h + blargg_source.h ) -find_package(ZLIB QUIET) - # Ay_Apu is very popular around here -if (USE_GME_AY OR USE_GME_KSS) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_AY OR USE_GME_KSS) + list(APPEND libgme_SRCS Ay_Apu.cpp + Ay_Apu.h ) endif() # so is Ym2612_Emu -if (USE_GME_VGM OR USE_GME_GYM) +if(USE_GME_VGM OR USE_GME_GYM) if(GME_YM2612_EMU STREQUAL "Nuked") add_definitions(-DVGM_YM2612_NUKED) - set(libgme_SRCS ${libgme_SRCS} + list(APPEND libgme_SRCS Ym2612_Nuked.cpp + Ym2612_Nuked.h ) - message("VGM/GYM: Nuked OPN2 emulator will be used") + message(STATUS "VGM/GYM: Nuked OPN2 emulator will be used") elseif(GME_YM2612_EMU STREQUAL "MAME") add_definitions(-DVGM_YM2612_MAME) - set(libgme_SRCS ${libgme_SRCS} + list(APPEND libgme_SRCS Ym2612_MAME.cpp + Ym2612_MAME.h ) - message("VGM/GYM: MAME YM2612 emulator will be used") + message(STATUS "VGM/GYM: MAME YM2612 emulator will be used") else() add_definitions(-DVGM_YM2612_GENS) - set(libgme_SRCS ${libgme_SRCS} + list(APPEND libgme_SRCS Ym2612_GENS.cpp + Ym2612_GENS.h ) - message("VGM/GYM: GENS 2.10 emulator will be used") + message(STATUS "VGM/GYM: GENS 2.10 emulator will be used") endif() endif() # But none are as popular as Sms_Apu -if (USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_VGM OR USE_GME_GYM OR USE_GME_KSS) + list(APPEND libgme_SRCS Sms_Apu.cpp + Sms_Apu.h ) endif() -if (USE_GME_AY) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_AY) + list(APPEND libgme_SRCS # Ay_Apu.cpp included earlier Ay_Cpu.cpp + Ay_Cpu.h Ay_Emu.cpp + Ay_Emu.h ) endif() -if (USE_GME_GBS) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_GBS) + list(APPEND libgme_SRCS Gb_Apu.cpp + Gb_Apu.h Gb_Cpu.cpp + Gb_Cpu.h Gb_Oscs.cpp + Gb_Oscs.h Gbs_Emu.cpp + Gbs_Emu.h + gb_cpu_io.h ) endif() -if (USE_GME_GYM) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_GYM) + list(APPEND libgme_SRCS # Sms_Apu.cpp included earlier # Ym2612_Emu.cpp included earlier Gym_Emu.cpp + Gym_Emu.h ) endif() -if (USE_GME_HES) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_HES) + list(APPEND libgme_SRCS Hes_Apu.cpp + Hes_Apu.h Hes_Cpu.cpp + Hes_Cpu.h Hes_Emu.cpp + Hes_Emu.h + hes_cpu_io.h ) endif() -if (USE_GME_KSS) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_KSS) + list(APPEND libgme_SRCS # Ay_Apu.cpp included earlier # Sms_Apu.cpp included earlier Kss_Cpu.cpp + Kss_Cpu.h Kss_Emu.cpp + Kss_Emu.h Kss_Scc_Apu.cpp + Kss_Scc_Apu.h ) endif() -if (USE_GME_NSF OR USE_GME_NSFE) - set(libgme_SRCS ${libgme_SRCS} +if(USE_GME_NSF OR USE_GME_NSFE) + list(APPEND libgme_SRCS Nes_Apu.cpp + Nes_Apu.h Nes_Cpu.cpp + Nes_Cpu.h Nes_Fme7_Apu.cpp + Nes_Fme7_Apu.h Nes_Namco_Apu.cpp + Nes_Namco_Apu.h Nes_Oscs.cpp + Nes_Oscs.h Nes_Vrc6_Apu.cpp + Nes_Vrc6_Apu.h Nes_Fds_Apu.cpp + Nes_Fds_Apu.h Nes_Vrc7_Apu.cpp - ../ext/emu2413.c - ../ext/panning.c + Nes_Vrc7_Apu.h + ext/emu2413.c + ext/emu2413.h + ext/panning.c + ext/panning.h + ext/emutypes.h + ext/2413tone.h + ext/vrc7tone.h Nsf_Emu.cpp + Nsf_Emu.h ) endif() if (USE_GME_NSFE) set(libgme_SRCS ${libgme_SRCS} Nsfe_Emu.cpp + Nsfe_Emu.h ) endif() @@ -123,6 +181,7 @@ if (USE_GME_SAP) Sap_Apu.cpp Sap_Cpu.cpp Sap_Emu.cpp + sap_cpu_io.h ) endif() @@ -135,12 +194,19 @@ if (USE_GME_SPC) ../higan/dsp/dsp.cpp ../higan/dsp/SPC_DSP.cpp Snes_Spc.cpp + Snes_Spc.h Spc_Cpu.cpp + Spc_Cpu.h Spc_Dsp.cpp + Spc_Dsp.h Spc_Emu.cpp + Spc_Emu.h Spc_Filter.cpp + Spc_Filter.h Bml_Parser.cpp + Bml_Parser.h Spc_Sfm.cpp + Spc_Sfm.h ) if (GME_SPC_ISOLATED_ECHO_BUFFER) add_definitions(-DSPC_ISOLATED_ECHO_BUFFER) @@ -152,88 +218,265 @@ if (USE_GME_VGM) # Sms_Apu.cpp included earlier # Ym2612_Emu.cpp included earlier Vgm_Emu.cpp + Vgm_Emu.h Vgm_Emu_Impl.cpp + Vgm_Emu_Impl.h Ym2413_Emu.cpp + Ym2413_Emu.h ) endif() # These headers are part of the generic gme interface. -set (EXPORTED_HEADERS gme.h blargg_source.h) +set (EXPORTED_HEADERS gme.h) # while building a macOS framework, exported headers must be in the source # list, or the header files aren't copied to the bundle. -if (BUILD_FRAMEWORK) +if(GME_BUILD_FRAMEWORK) set(libgme_SRCS ${libgme_SRCS} ${EXPORTED_HEADERS}) endif() -# On some platforms we may need to change headers or whatnot based on whether -# we're building the library or merely using the library. The following is -# only defined when building the library to allow us to tell which is which. -add_definitions(-DBLARGG_BUILD_DLL) +# Extract symbols from gme.exports +file(STRINGS "${CMAKE_CURRENT_SOURCE_DIR}/gme.exports" gme_exports_strings) +set(gme_exports) +foreach(gme_export_string ${gme_exports_strings}) + string(STRIP "${gme_export_string}" gme_export_string) + string(SUBSTRING "${gme_export_string}" 0 1 gme_export_first_char) + if(gme_export_string AND NOT gme_export_first_char STREQUAL "#") + list(APPEND gme_exports ${gme_export_string}) + endif() +endforeach() -# For the gme_types.h -include_directories(${CMAKE_CURRENT_BINARY_DIR}) -# Add library to be compiled. -add_library(gme ${libgme_SRCS}) +add_library(gme_deps INTERFACE) -if(ZLIB_FOUND) - message(" ** ZLib library located, compressed file formats will be supported") - target_compile_definitions(gme PRIVATE -DHAVE_ZLIB_H) - target_include_directories(gme PRIVATE ${ZLIB_INCLUDE_DIRS}) - target_link_libraries(gme ${ZLIB_LIBRARIES}) - # Is not to be installed though +## FIXME: Properly find the C++ library !!! +set(PC_LIBS -lstdc++) - set(PKG_CONFIG_ZLIB -lz) # evaluated in libgme.pc.in +if(GME_ZLIB) + if(ZLIB_FOUND) + message(STATUS "ZLib library located, compressed file formats will be supported") + target_compile_definitions(gme_deps INTERFACE HAVE_ZLIB_H) + target_link_libraries(gme_deps INTERFACE ZLIB::ZLIB) + # Is not to be installed though + list(APPEND PC_LIBS -lz) # for libgme.pc + else() + message(STATUS "** ZLib library not found, disabling support for compressed formats such as VGZ") + endif() else() - message("ZLib library not found, disabling support for compressed formats such as VGZ") + message(STATUS "Zlib-Compressed formats excluded") endif() -if(USE_GME_SPC) - if(UNRAR_FOUND) - message(" ** unRAR library located, the RSN file format will be supported") - target_compile_definitions(gme PRIVATE -DRARDLL) - target_include_directories(gme PRIVATE ${UNRAR_INCLUDE_DIRS}) - target_link_libraries(gme ${UNRAR_LIBRARIES}) - # Is not to be installed though +if(NOT MSVC) + # Link with -no-undefined, if available + if(NOT APPLE AND NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*") + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "-Wl,-no-undefined") + check_cxx_source_compiles("int main(void) { return 0;}" LINKER_SUPPORTS_NO_UNDEFINED) + cmake_pop_check_state() + endif() - set(PKG_CONFIG_UNRAR -lunrar) # evaluated in libgme.pc.in - else() - message("unRAR library not found, disabling support for the RSN file format") + # Link to libm, if necessary + check_cxx_source_compiles(" + #include + 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 + int main(int argc, char** argv) { + return (int) pow(argc, 2.5); + }" HAVE_LIBM) + cmake_pop_check_state() + if(HAVE_LIBM) + list(APPEND PC_LIBS -lm) # for libgme.pc + endif() endif() endif() -# The version is the release. The "soversion" is the API version. As long -# as only build fixes are performed (i.e. no backwards-incompatible changes -# to the API), the SOVERSION should be the same even when bumping up VERSION. -# The way gme.h is designed, SOVERSION should very rarely be bumped, if ever. -# Hopefully the API can stay compatible with old versions. -set_target_properties(gme - PROPERTIES VERSION ${GME_VERSION} - SOVERSION 1) -# macOS framework build -if(BUILD_FRAMEWORK) - set_target_properties(gme - PROPERTIES FRAMEWORK TRUE - FRAMEWORK_VERSION A - MACOSX_FRAMEWORK_IDENTIFIER net.mpyne.gme - VERSION ${GME_VERSION} - SOVERSION 0 - PUBLIC_HEADER "${EXPORTED_HEADERS}") +# Add a version script to hide the c++ STL +if(APPLE) + set(gme_syms "") + foreach(gme_export ${gme_exports}) + set(gme_syms "${gme_syms}_${gme_export}\n") + endforeach() + + # Use an intermediate 'gme_generated.sym' file to avoid a relink every time CMake runs + set(temporary_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme_generated.sym") + set(generated_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme.sym") + file(WRITE "${temporary_sym_file}" "${gme_syms}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${temporary_sym_file}" "${generated_sym_file}") +elseif(UNIX) + set(gme_syms "{\n global:\n") + foreach(gme_export ${gme_exports}) + set(gme_syms "${gme_syms} ${gme_export};\n") + endforeach() + set(gme_syms "${gme_syms} local: *;\n};\n") + + # Use an intermediate 'gme_generated.sym' file to avoid a relink every time CMake runs + set(temporary_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme_generated.sym") + set(generated_sym_file "${CMAKE_CURRENT_BINARY_DIR}/gme.sym") + file(WRITE "${temporary_sym_file}" "${gme_syms}") + execute_process(COMMAND "${CMAKE_COMMAND}" -E copy_if_different "${temporary_sym_file}" "${generated_sym_file}") + + cmake_push_check_state() + set(CMAKE_REQUIRED_FLAGS "-Wl,-version-script='${generated_sym_file}'") + check_cxx_source_compiles("int main() { return 0;}" LINKER_SUPPORTS_VERSION_SCRIPT) + cmake_pop_check_state() endif() -install(TARGETS gme LIBRARY DESTINATION lib${LIB_SUFFIX} - RUNTIME DESTINATION bin # DLL platforms - ARCHIVE DESTINATION lib # DLL platforms - FRAMEWORK DESTINATION /Library/Frameworks) # macOS framework + +set(INSTALL_TARGETS) + +# Shared properties for shared and static builds of libGME +macro(set_gme_props gme_target) + list(APPEND INSTALL_TARGETS ${gme_target}) + + set_property(TARGET ${gme_target} PROPERTY C_VISIBILITY_PRESET "hidden") + set_property(TARGET ${gme_target} PROPERTY VISIBILITY_INLINES_HIDDEN TRUE) + set_property(TARGET ${gme_target} PROPERTY CXX_VISIBILITY_PRESET "hidden") + + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_definitions(${gme_target} PRIVATE LIBGME_VISIBILITY) + endif() + + target_compile_definitions(${gme_target} PRIVATE GEN_TYPES_H) + if(WORDS_BIGENDIAN) + target_compile_definitions(${gme_target} PRIVATE BLARGG_BIG_ENDIAN=1) + else() + target_compile_definitions(${gme_target} PRIVATE BLARGG_LITTLE_ENDIAN=1) + endif() + + # Try to protect against undefined behavior from signed integer overflow + # This has caused miscompilation of code already and there are other + # potential uses; see https://bitbucket.org/mpyne/game-music-emu/issues/18/ + # (https://github.com/libgme/game-music-emu/issues/20 ) + if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang") + target_compile_options(${gme_target} PRIVATE -fwrapv) + endif() + + target_include_directories(${gme_target} + PRIVATE "$" # For gen_types.h + INTERFACE "$" + INTERFACE "$" + ) + + if(NOT MSVC) + # Link with -no-undefined, if available + if(APPLE) + set_property(TARGET ${gme_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-undefined,error") + elseif(NOT CMAKE_SYSTEM_NAME MATCHES ".*OpenBSD.*" AND LINKER_SUPPORTS_NO_UNDEFINED) + set_property(TARGET ${gme_target} APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-no-undefined") + endif() + endif() + +endmacro() + +# Building the Shared version of GME +if(GME_BUILD_SHARED) + add_library(gme_shared SHARED ${libgme_SRCS}) + set_target_properties(gme_shared PROPERTIES OUTPUT_NAME gme) + + if(WIN32) + set_property(TARGET gme_shared PROPERTY DEFINE_SYMBOL BLARGG_BUILD_DLL) + endif() + + set_gme_props(gme_shared) + + # Add a version script to hide the c++ STL + if(APPLE) + set_property(TARGET gme_shared APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-exported_symbols_list,'${generated_sym_file}'") + set_property(TARGET gme_shared APPEND PROPERTY LINK_DEPENDS "${generated_sym_file}") + elseif(UNIX AND LINKER_SUPPORTS_VERSION_SCRIPT) + set_property(TARGET gme_shared APPEND_STRING PROPERTY LINK_FLAGS " -Wl,-version-script='${generated_sym_file}'") + set_property(TARGET gme_shared APPEND PROPERTY LINK_DEPENDS "${generated_sym_file}") + endif() + + target_link_libraries(gme_shared PRIVATE gme_deps) + + if(HAVE_LIBM) + target_link_libraries(gme_shared PRIVATE -lm) + endif() + + # The version is the release. The "soversion" is the API version. As long + # as only build fixes are performed (i.e. no backwards-incompatible changes + # to the API), the SOVERSION should be the same even when bumping up VERSION. + # The way gme.h is designed, SOVERSION should very rarely be bumped, if ever. + # Hopefully the API can stay compatible with old versions. + set_target_properties(gme_shared PROPERTIES VERSION ${GME_VERSION} SOVERSION 0) + + # macOS framework build + if(GME_BUILD_FRAMEWORK) + set_target_properties(gme_shared + PROPERTIES FRAMEWORK TRUE + FRAMEWORK_VERSION A + MACOSX_FRAMEWORK_IDENTIFIER net.mpyne.gme + VERSION ${GME_VERSION} + SOVERSION 0 + PUBLIC_HEADER "${EXPORTED_HEADERS}") + endif() +endif() + +# Building the Static version of GME +if(GME_BUILD_STATIC) + # Static build. + add_library(gme_static STATIC ${libgme_SRCS}) + + # Static builds need to find static zlib (and static forms of other needed + # libraries. Ensure CMake looks only for static libs if we're doing a static + # build. See https://stackoverflow.com/a/44738756 + if(MSVC) + set(CMAKE_FIND_LIBRARY_SUFFIXES ".lib") + else() + set(CMAKE_FIND_LIBRARY_SUFFIXES ".a") + endif() + + if(MSVC AND GME_BUILD_SHARED) + set_target_properties(gme_static PROPERTIES OUTPUT_NAME gme-static) + else() + set_target_properties(gme_static PROPERTIES OUTPUT_NAME gme) + endif() + + set_gme_props(gme_static) + + target_link_libraries(gme_static PUBLIC gme_deps) + + if(HAVE_LIBM) + target_link_libraries(gme_static PUBLIC -lm) + endif() +endif() + +if(GME_BUILD_SHARED) + add_library(gme::gme ALIAS gme_shared) +else() + add_library(gme::gme ALIAS gme_static) +endif() + + +install(TARGETS ${INSTALL_TARGETS} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} # DLL platforms + ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR} # DLL platforms + FRAMEWORK DESTINATION /Library/Frameworks) # macOS framework # Run during cmake phase, so this is available during make -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/gme_types.h.in - ${CMAKE_CURRENT_BINARY_DIR}/gme_types.h) +configure_file(gme_types.h.in gen_types.h @ONLY) -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/libgme.pc.in - ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc @ONLY) +set(TMP_PC_LIBS "") +foreach(PC_LIB ${PC_LIBS}) + if(NOT "${TMP_PC_LIBS}" STREQUAL "") + set(TMP_PC_LIBS "${TMP_PC_LIBS} ${PC_LIB}") + else() + set(TMP_PC_LIBS "${PC_LIB}") + endif() +endforeach() +set(PC_LIBS "${TMP_PC_LIBS}") +unset(TMP_PC_LIBS) -install(FILES ${EXPORTED_HEADERS} DESTINATION include/gme) -install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION lib${LIB_SUFFIX}/pkgconfig) +configure_file(libgme.pc.in libgme.pc @ONLY) + +install(FILES ${EXPORTED_HEADERS} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/gme) +install(FILES ${CMAKE_CURRENT_BINARY_DIR}/libgme.pc DESTINATION ${CMAKE_INSTALL_LIBDIR}/pkgconfig) diff --git a/Frameworks/GME/gme/Classic_Emu.cpp b/Frameworks/GME/gme/Classic_Emu.cpp index c572d9b5c..716157e70 100644 --- a/Frameworks/GME/gme/Classic_Emu.cpp +++ b/Frameworks/GME/gme/Classic_Emu.cpp @@ -23,11 +23,11 @@ Classic_Emu::Classic_Emu() buf = 0; stereo_buffer = 0; voice_types = 0; - + // avoid inconsistency in our duplicated constants - assert( (int) wave_type == (int) Multi_Buffer::wave_type ); - assert( (int) noise_type == (int) Multi_Buffer::noise_type ); - assert( (int) mixed_type == (int) Multi_Buffer::mixed_type ); + blaarg_static_assert( (int) wave_type == (int) Multi_Buffer::wave_type, "wave_type inconsistent across two classes using it" ); + blaarg_static_assert( (int) noise_type == (int) Multi_Buffer::noise_type, "noise_type inconsistent across two classes using it" ); + blaarg_static_assert( (int) mixed_type == (int) Multi_Buffer::mixed_type, "mixed_type inconsistent across two classes using it" ); } Classic_Emu::~Classic_Emu() @@ -42,7 +42,7 @@ void Classic_Emu::set_equalizer_( equalizer_t const& eq ) if ( buf ) buf->bass_freq( (int) equalizer().bass ); } - + blargg_err_t Classic_Emu::set_sample_rate_( long rate ) { if ( !buf ) @@ -115,7 +115,7 @@ blargg_err_t Classic_Emu::play_( long count, sample_t* out ) remute_voices(); } int msec = buf->length(); - blip_time_t clocks_emulated = (blargg_long) msec * clock_rate_ / 1000; + blip_time_t clocks_emulated = (int32_t) msec * clock_rate_ / 1000; RETURN_ERR( run_clocks( clocks_emulated, msec ) ); assert( clocks_emulated ); buf->end_frame( clocks_emulated ); @@ -130,12 +130,12 @@ blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in, int header_size, void* header_out, int fill, long pad_size ) { long file_offset = pad_size - header_size; - + rom_addr = 0; mask = 0; size_ = 0; rom.clear(); - + file_size_ = in.remain(); if ( file_size_ <= header_size ) // <= because there must be data after header return gme_wrong_file_type; @@ -147,20 +147,20 @@ blargg_err_t Rom_Data_::load_rom_data_( Data_Reader& in, rom.clear(); return err; } - + file_size_ -= header_size; memcpy( header_out, &rom [file_offset], header_size ); - + memset( rom.begin() , fill, pad_size ); memset( rom.end() - pad_size, fill, pad_size ); - + return 0; } void Rom_Data_::set_addr_( long addr, int unit ) { rom_addr = addr - unit - pad_extra; - + long rounded = (addr + file_size_ + unit - 1) / unit * unit; if ( rounded <= 0 ) { @@ -174,7 +174,7 @@ void Rom_Data_::set_addr_( long addr, int unit ) shift++; mask = (1L << shift) - 1; } - + if ( addr < 0 ) addr = 0; size_ = rounded; @@ -182,9 +182,9 @@ void Rom_Data_::set_addr_( long addr, int unit ) if ( 0 ) { - debug_printf( "addr: %X\n", addr ); - debug_printf( "file_size: %d\n", file_size_ ); - debug_printf( "rounded: %d\n", rounded ); + debug_printf( "addr: %X\n", (int)addr ); + debug_printf( "file_size: %ld\n", file_size_ ); + debug_printf( "rounded: %ld\n", rounded ); debug_printf( "mask: $%X\n", mask ); } } diff --git a/Frameworks/GME/gme/Classic_Emu.h b/Frameworks/GME/gme/Classic_Emu.h index dadeca099..f0b4cbb1d 100644 --- a/Frameworks/GME/gme/Classic_Emu.h +++ b/Frameworks/GME/gme/Classic_Emu.h @@ -21,7 +21,7 @@ protected: blargg_err_t setup_buffer( long clock_rate ); long clock_rate() const { return clock_rate_; } void change_clock_rate( long ); // experimental - + // Overridable virtual void set_voice( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ) = 0; @@ -58,10 +58,10 @@ protected: enum { pad_extra = 8 }; blargg_vector rom; long file_size_; - blargg_long rom_addr; - blargg_long mask; - blargg_long size_; // TODO: eliminate - + int32_t rom_addr; + int32_t mask; + int32_t size_; // TODO: eliminate + blargg_err_t load_rom_data_( Data_Reader& in, int header_size, void* header_out, int fill, long pad_size ); void set_addr_( long addr, int unit ); @@ -77,39 +77,39 @@ public: { return load_rom_data_( in, header_size, header_out, fill, pad_size ); } - + // Size of file data read in (excluding header) long file_size() const { return file_size_; } - + // Pointer to beginning of file data byte* begin() const { return rom.begin() + pad_size; } - + // Set address that file data should start at void set_addr( long addr ) { set_addr_( addr, unit ); } - + // Free data void clear() { rom.clear(); } - + // Size of data + start addr, rounded to a multiple of unit long size() const { return size_; } - + // Pointer to unmapped page filled with same value byte* unmapped() { return rom.begin(); } - + // Mask address to nearest power of two greater than size() - blargg_long mask_addr( blargg_long addr ) const + int32_t mask_addr( int32_t addr ) const { #ifdef check check( addr <= mask ); #endif return addr & mask; } - + // Pointer to page starting at addr. Returns unmapped() if outside data. - byte* at_addr( blargg_long addr ) + byte* at_addr( int32_t addr ) { - blargg_ulong offset = mask_addr( addr ) - rom_addr; - if ( offset > blargg_ulong (rom.size() - pad_size) ) + uint32_t offset = mask_addr( addr ) - rom_addr; + if ( offset > uint32_t (rom.size() - pad_size) ) offset = 0; // unmapped return &rom [offset]; } diff --git a/Frameworks/GME/gme/Data_Reader.cpp b/Frameworks/GME/gme/Data_Reader.cpp index f6e5d363e..6c4e608cc 100644 --- a/Frameworks/GME/gme/Data_Reader.cpp +++ b/Frameworks/GME/gme/Data_Reader.cpp @@ -362,15 +362,17 @@ blargg_err_t Std_File_Reader::open( const char* path ) long Std_File_Reader::size() const { + if ( !file_ ) + return -1L; #ifdef HAVE_ZLIB_H - if ( file_ ) - return size_; // Set for both compressed and uncompressed modes -#endif + return size_; // Set for both compressed and uncompressed modes +#else long pos = tell(); fseek( (FILE*) file_, 0, SEEK_END ); long result = tell(); fseek( (FILE*) file_, pos, SEEK_SET ); return result; +#endif } long Std_File_Reader::read_avail( void* p, long s ) @@ -390,52 +392,55 @@ long Std_File_Reader::read_avail( void* p, long s ) blargg_err_t Std_File_Reader::read( void* p, long s ) { + if ( !file_ ) + return "NULL FILE pointer"; + RETURN_VALIDITY_CHECK( s > 0 && static_cast(s) <= UINT_MAX ); #ifdef HAVE_ZLIB_H - if ( file_ ) - { - const auto &gzfile = reinterpret_cast( file_ ); - if ( s == gzread( gzfile, p, static_cast( s ) ) ) - return nullptr; - if ( gzeof( gzfile ) ) - return eof_error; - return "Couldn't read from GZ file"; - } -#endif + const auto &gzfile = reinterpret_cast( file_ ); + if ( s == gzread( gzfile, p, static_cast( s ) ) ) + return nullptr; + if ( gzeof( gzfile ) ) + return eof_error; + return "Couldn't read from GZ file"; +#else const auto &file = reinterpret_cast( file_ ); if ( s == static_cast( fread( p, 1, static_cast(s), file ) ) ) return 0; if ( feof( file ) ) return eof_error; return "Couldn't read from file"; +#endif } long Std_File_Reader::tell() const { + if ( !file_ ) + return -1L; #ifdef HAVE_ZLIB_H - if ( file_ ) - return gztell( reinterpret_cast( file_ ) ); -#endif + return gztell( reinterpret_cast( file_ ) ); +#else return ftell( reinterpret_cast( file_ ) ); +#endif } blargg_err_t Std_File_Reader::seek( long n ) { + if ( !file_ ) + return "NULL FILE pointer"; #ifdef HAVE_ZLIB_H - if ( file_ ) - { - if ( gzseek( reinterpret_cast( file_ ), n, SEEK_SET ) >= 0 ) - return nullptr; - if ( n > size_ ) - return eof_error; - return "Error seeking in GZ file"; - } -#endif + if ( gzseek( reinterpret_cast( file_ ), n, SEEK_SET ) >= 0 ) + return nullptr; + if ( n > size_ ) + return eof_error; + return "Error seeking in GZ file"; +#else if ( !fseek( reinterpret_cast( file_ ), n, SEEK_SET ) ) return nullptr; if ( n > size() ) return eof_error; return "Error seeking in file"; +#endif } void Std_File_Reader::close() @@ -450,4 +455,3 @@ void Std_File_Reader::close() file_ = nullptr; } } - diff --git a/Frameworks/GME/gme/Data_Reader.h b/Frameworks/GME/gme/Data_Reader.h index 6bec1e57f..45a87f292 100644 --- a/Frameworks/GME/gme/Data_Reader.h +++ b/Frameworks/GME/gme/Data_Reader.h @@ -14,21 +14,21 @@ class Data_Reader { public: virtual ~Data_Reader() { } - + static const char eof_error []; // returned by read() when request goes beyond end - + // Read at most count bytes and return number actually read, or <= 0 if error virtual long read_avail( void*, long n ) = 0; - + // Read exactly count bytes and return error if they couldn't be read virtual blargg_err_t read( void*, long count ); - + // Number of bytes remaining until end of file virtual long remain() const = 0; - + // Read and discard count bytes virtual blargg_err_t skip( long count ); - + public: Data_Reader() { } typedef blargg_err_t error_t; // deprecated @@ -43,13 +43,13 @@ class File_Reader : public Data_Reader { public: // Size of file virtual long size() const = 0; - + // Current position in file virtual long tell() const = 0; - + // Go to new position virtual blargg_err_t seek( long ) = 0; - + long remain() const; blargg_err_t skip( long n ); }; @@ -59,7 +59,7 @@ class Std_File_Reader : public File_Reader { public: blargg_err_t open( const char* path ); void close(); - + public: Std_File_Reader(); ~Std_File_Reader(); diff --git a/Frameworks/GME/gme/Dual_Resampler.cpp b/Frameworks/GME/gme/Dual_Resampler.cpp index f6a468a8f..9af40be7d 100644 --- a/Frameworks/GME/gme/Dual_Resampler.cpp +++ b/Frameworks/GME/gme/Dual_Resampler.cpp @@ -58,13 +58,13 @@ void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out ) long pair_count = sample_buf_size >> 1; blip_time_t blip_time = blip_buf.count_clocks( pair_count ); int sample_count = oversamples_per_frame - resampler.written(); - + int new_count = play_frame( blip_time, sample_count, resampler.buffer() ); assert( new_count < resampler_size ); - + blip_buf.end_frame( blip_time ); assert( blip_buf.samples_avail() == pair_count ); - + resampler.write( new_count ); #ifdef NDEBUG // Avoid warning when asserts are disabled @@ -73,7 +73,7 @@ void Dual_Resampler::play_frame_( Blip_Buffer& blip_buf, dsample_t* out ) long count = resampler.read( sample_buf.begin(), sample_buf_size ); assert( count == (long) sample_buf_size ); #endif - + mix_samples( blip_buf, out ); blip_buf.remove_samples( pair_count ); } @@ -91,7 +91,7 @@ void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_bu out += remain; buf_pos += remain; } - + // entire frames while ( count >= (long) sample_buf_size ) { @@ -99,7 +99,7 @@ void Dual_Resampler::dual_play( long count, dsample_t* out, Blip_Buffer& blip_bu out += sample_buf_size; count -= sample_buf_size; } - + // extra if ( count ) { @@ -115,25 +115,25 @@ void Dual_Resampler::mix_samples( Blip_Buffer& blip_buf, dsample_t* out ) Blip_Reader sn; int bass = sn.begin( blip_buf ); const dsample_t* in = sample_buf.begin(); - + for ( int n = sample_buf_size >> 1; n--; ) { int s = sn.read(); - blargg_long l = (blargg_long) in [0] * 2 + s; + int32_t l = (int32_t) in [0] * 2 + s; if ( (int16_t) l != l ) l = 0x7FFF - (l >> 24); - + sn.next( bass ); - blargg_long r = (blargg_long) in [1] * 2 + s; + int32_t r = (int32_t) in [1] * 2 + s; if ( (int16_t) r != r ) r = 0x7FFF - (r >> 24); - + in += 2; out [0] = l; out [1] = r; out += 2; } - + sn.end( blip_buf ); } diff --git a/Frameworks/GME/gme/Dual_Resampler.h b/Frameworks/GME/gme/Dual_Resampler.h index 512fd97d0..9979b330e 100644 --- a/Frameworks/GME/gme/Dual_Resampler.h +++ b/Frameworks/GME/gme/Dual_Resampler.h @@ -11,26 +11,26 @@ class Dual_Resampler { public: Dual_Resampler(); virtual ~Dual_Resampler(); - + typedef short dsample_t; - + double setup( double oversample, double rolloff, double gain ); blargg_err_t reset( int max_pairs ); void resize( int pairs_per_frame ); void clear(); - + void dual_play( long count, dsample_t* out, Blip_Buffer& ); - + protected: virtual int play_frame( blip_time_t, int pcm_count, dsample_t* pcm_out ) = 0; private: - + blargg_vector sample_buf; int sample_buf_size; int oversamples_per_frame; int buf_pos; int resampler_size; - + Fir_Resampler<12> resampler; void mix_samples( Blip_Buffer&, dsample_t* ); void play_frame_( Blip_Buffer&, dsample_t* ); diff --git a/Frameworks/GME/gme/Effects_Buffer.cpp b/Frameworks/GME/gme/Effects_Buffer.cpp index 01d694ff3..3b8fffad1 100644 --- a/Frameworks/GME/gme/Effects_Buffer.cpp +++ b/Frameworks/GME/gme/Effects_Buffer.cpp @@ -22,7 +22,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include BLARGG_ENABLE_OPTIMIZER #endif -typedef blargg_long fixed_t; +typedef int32_t fixed_t; using std::min; using std::max; @@ -30,12 +30,12 @@ using std::max; #define TO_FIXED( f ) fixed_t ((f) * (1L << 15) + 0.5) #define FMUL( x, y ) (((x) * (y)) >> 15) -const unsigned echo_size = 4096; -const unsigned echo_mask = echo_size - 1; +static const unsigned echo_size = 4096; +static const unsigned echo_mask = echo_size - 1; static_assert( (echo_size & echo_mask) == 0, "echo_size must be a power of 2" ); -const unsigned reverb_size = 8192 * 2; -const unsigned reverb_mask = reverb_size - 1; +static const unsigned reverb_size = 8192 * 2; +static const unsigned reverb_mask = reverb_size - 1; static_assert( (reverb_size & reverb_mask) == 0, "reverb_size must be a power of 2" ); Effects_Buffer::config_t::config_t() @@ -88,34 +88,32 @@ Effects_Buffer::Effects_Buffer( int num_voices, bool center_only ) Effects_Buffer::~Effects_Buffer() {} -blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) +blargg_err_t Effects_Buffer::set_sample_rate( long rate, int msec ) noexcept { - try + for(int i=0; i= 3, "Need at least three audio channels for effects processing" ); } else { @@ -233,7 +231,7 @@ void Effects_Buffer::config( const config_t& cfg ) } } } - + if ( buf_count < max_buf_count ) // if center_only { for(int i=0; i effect_remain ) count = effect_remain; - + if ( stereo_remain ) { mix_enhanced( out, count ); @@ -327,25 +325,25 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) else if ( stereo_remain ) { mix_stereo( out, count ); - active_bufs = 3; + active_bufs = 3; } else { mix_mono( out, count ); - active_bufs = 1; + active_bufs = 1; } - + out += count * n_channels; remain -= count; - + stereo_remain -= count; if ( stereo_remain < 0 ) stereo_remain = 0; - + effect_remain -= count; if ( effect_remain < 0 ) effect_remain = 0; - + // skip the output from any buffers that didn't contribute to the sound output // during this frame (e.g. if we only render mono then only the very first buf // is 'active') @@ -360,56 +358,52 @@ long Effects_Buffer::read_samples( blip_sample_t* out, long total_samples ) } } } - + return total_samples * n_channels; } -void Effects_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +void Effects_Buffer::mix_mono( blip_sample_t* out_, int32_t count ) { for(int i=0; i> 1; n; --n ) + for ( int32_t n = count >> 1; n; --n ) { - blargg_long cs0 = BLIP_READER_READ( c ); + int32_t cs0 = BLIP_READER_READ( c ); BLIP_READER_NEXT( c, bass ); - - blargg_long cs1 = BLIP_READER_READ( c ); + + int32_t cs1 = BLIP_READER_READ( c ); BLIP_READER_NEXT( c, bass ); - + if ( (int16_t) cs0 != cs0 ) cs0 = 0x7FFF - (cs0 >> 24); - ((uint32_t*) out) [i*2+0] = ((uint16_t) cs0) | (uint16_t(cs0) << 16); - + ((uint32_t*) out) [i] = ((uint16_t) cs0) | (uint16_t(cs0) << 16); + if ( (int16_t) cs1 != cs1 ) cs1 = 0x7FFF - (cs1 >> 24); - ((uint32_t*) out) [i*2+1] = ((uint16_t) cs1) | (uint16_t(cs1) << 16); + ((uint32_t*) out) [i+max_voices] = ((uint16_t) cs1) | (uint16_t(cs1) << 16); out += max_voices*4; } - + if ( count & 1 ) { int s = BLIP_READER_READ( c ); BLIP_READER_NEXT( c, bass ); + if ( (int16_t) s != s ) + s = 0x7FFF - (s >> 24); out [i*2+0] = s; out [i*2+1] = s; - if ( (int16_t) s != s ) - { - s = 0x7FFF - (s >> 24); - out [i*2+0] = s; - out [i*2+1] = s; - } } - + BLIP_READER_END( c, bufs [i*max_buf_count+0] ); } } -void Effects_Buffer::mix_stereo( blip_sample_t* out_, blargg_long frames ) +void Effects_Buffer::mix_stereo( blip_sample_t* out_, int32_t frames ) { for(int i=0; i> 24); - + if ( (int16_t) right != right ) right = 0x7FFF - (right >> 24); out [i*2+0] = left; out [i*2+1] = right; - + out += max_voices*2; - + } - + BLIP_READER_END( r, bufs [i*max_buf_count+2] ); BLIP_READER_END( l, bufs [i*max_buf_count+1] ); BLIP_READER_END( c, bufs [i*max_buf_count+0] ); } } -void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames ) +void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, int32_t frames ) { for(int i=0; ireverb_buf[i][0]; blip_sample_t* const echo_buf = &this->echo_buf[i][0]; int echo_pos = this->echo_pos[i]; int reverb_pos = this->reverb_pos[i]; - + int count = frames; while ( count-- ) { int sum1_s = BLIP_READER_READ( sq1 ); int sum2_s = BLIP_READER_READ( sq2 ); - + BLIP_READER_NEXT( sq1, bass ); BLIP_READER_NEXT( sq2, bass ); - + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + FMUL( sum2_s, chans.pan_2_levels [0] ) + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + FMUL( sum2_s, chans.pan_2_levels [1] ) + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - + fixed_t reverb_level = chans.reverb_level; reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); reverb_pos = (reverb_pos + 2) & reverb_mask; - + int sum3_s = BLIP_READER_READ( center ); BLIP_READER_NEXT( center, bass ); - + int left = new_reverb_l + sum3_s + FMUL( chans.echo_level, echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); int right = new_reverb_r + sum3_s + FMUL( chans.echo_level, echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - + echo_buf [echo_pos] = sum3_s; echo_pos = (echo_pos + 1) & echo_mask; - + if ( (int16_t) left != left ) left = 0x7FFF - (left >> 24); - + if ( (int16_t) right != right ) right = 0x7FFF - (right >> 24); @@ -508,14 +502,14 @@ void Effects_Buffer::mix_mono_enhanced( blip_sample_t* out_, blargg_long frames } this->reverb_pos[i] = reverb_pos; this->echo_pos[i] = echo_pos; - + BLIP_READER_END( sq1, bufs [i*max_buf_count+0] ); BLIP_READER_END( sq2, bufs [i*max_buf_count+1] ); BLIP_READER_END( center, bufs [i*max_buf_count+2] ); } } -void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames ) +void Effects_Buffer::mix_enhanced( blip_sample_t* out_, int32_t frames ) { for(int i=0; ireverb_buf[i][0]; blip_sample_t* const echo_buf = &this->echo_buf[i][0]; int echo_pos = this->echo_pos[i]; int reverb_pos = this->reverb_pos[i]; - + int count = frames; while ( count-- ) { int sum1_s = BLIP_READER_READ( sq1 ); int sum2_s = BLIP_READER_READ( sq2 ); - + BLIP_READER_NEXT( sq1, bass ); BLIP_READER_NEXT( sq2, bass ); - + int new_reverb_l = FMUL( sum1_s, chans.pan_1_levels [0] ) + FMUL( sum2_s, chans.pan_2_levels [0] ) + BLIP_READER_READ( l1 ) + reverb_buf [(reverb_pos + chans.reverb_delay_l) & reverb_mask]; - + int new_reverb_r = FMUL( sum1_s, chans.pan_1_levels [1] ) + FMUL( sum2_s, chans.pan_2_levels [1] ) + BLIP_READER_READ( r1 ) + reverb_buf [(reverb_pos + chans.reverb_delay_r) & reverb_mask]; - + BLIP_READER_NEXT( l1, bass ); BLIP_READER_NEXT( r1, bass ); - + fixed_t reverb_level = chans.reverb_level; reverb_buf [reverb_pos] = (blip_sample_t) FMUL( new_reverb_l, reverb_level ); reverb_buf [reverb_pos + 1] = (blip_sample_t) FMUL( new_reverb_r, reverb_level ); reverb_pos = (reverb_pos + 2) & reverb_mask; - + int sum3_s = BLIP_READER_READ( center ); BLIP_READER_NEXT( center, bass ); - + int left = new_reverb_l + sum3_s + BLIP_READER_READ( l2 ) + FMUL( chans.echo_level, echo_buf [(echo_pos + chans.echo_delay_l) & echo_mask] ); int right = new_reverb_r + sum3_s + BLIP_READER_READ( r2 ) + FMUL( chans.echo_level, echo_buf [(echo_pos + chans.echo_delay_r) & echo_mask] ); - + BLIP_READER_NEXT( l2, bass ); BLIP_READER_NEXT( r2, bass ); - + echo_buf [echo_pos] = sum3_s; echo_pos = (echo_pos + 1) & echo_mask; - + if ( (int16_t) left != left ) left = 0x7FFF - (left >> 24); - + if ( (int16_t) right != right ) right = 0x7FFF - (right >> 24); @@ -586,7 +580,7 @@ void Effects_Buffer::mix_enhanced( blip_sample_t* out_, blargg_long frames ) } this->reverb_pos[i] = reverb_pos; this->echo_pos[i] = echo_pos; - + BLIP_READER_END( l1, bufs [i*max_buf_count+3] ); BLIP_READER_END( r1, bufs [i*max_buf_count+4] ); BLIP_READER_END( l2, bufs [i*max_buf_count+5] ); diff --git a/Frameworks/GME/gme/Effects_Buffer.h b/Frameworks/GME/gme/Effects_Buffer.h index ec634d622..5a75af82e 100644 --- a/Frameworks/GME/gme/Effects_Buffer.h +++ b/Frameworks/GME/gme/Effects_Buffer.h @@ -16,7 +16,7 @@ public: // If center_only is true, only center buffers are created and // less memory is used. Effects_Buffer( int nVoices = 1, bool center_only = false ); - + // Channel Effect Center Pan // --------------------------------- // 0,5 reverb pan_1 @@ -24,7 +24,7 @@ public: // 2,7 echo - // 3 echo - // 4 echo - - + // Channel configuration struct config_t { double pan_1; // -1.0 = left, 0.0 = center, 1.0 = right @@ -37,14 +37,14 @@ public: bool effects_enabled; // if false, use optimized simple mixer config_t(); }; - + // Set configuration of buffer virtual void config( const config_t& ); void set_depth( double ); - + public: ~Effects_Buffer(); - blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ); + blargg_err_t set_sample_rate( long samples_per_sec, int msec = blip_default_length ) noexcept; void clock_rate( long ); void bass_freq( int ); void clear(); @@ -64,12 +64,12 @@ private: long effect_remain; int buf_count; bool effects_enabled; - + std::vector > reverb_buf; std::vector > echo_buf; std::vector reverb_pos; std::vector echo_pos; - + struct { fixed_t pan_1_levels [2]; fixed_t pan_2_levels [2]; @@ -80,11 +80,11 @@ private: int reverb_delay_r; fixed_t reverb_level; } chans; - - void mix_mono( blip_sample_t*, blargg_long ); - void mix_stereo( blip_sample_t*, blargg_long ); - void mix_enhanced( blip_sample_t*, blargg_long ); - void mix_mono_enhanced( blip_sample_t*, blargg_long ); + + void mix_mono( blip_sample_t*, int32_t ); + void mix_stereo( blip_sample_t*, int32_t ); + void mix_enhanced( blip_sample_t*, int32_t ); + void mix_mono_enhanced( blip_sample_t*, int32_t ); }; #endif diff --git a/Frameworks/GME/gme/Fir_Resampler.cpp b/Frameworks/GME/gme/Fir_Resampler.cpp index d8dd6837c..915017e06 100644 --- a/Frameworks/GME/gme/Fir_Resampler.cpp +++ b/Frameworks/GME/gme/Fir_Resampler.cpp @@ -31,7 +31,7 @@ static void gen_sinc( double rolloff, int width, double offset, double spacing, double const to_w = maxh * 2 / width; double const pow_a_n = pow( rolloff, maxh ); scale /= maxh * 2; - + double angle = (count / 2 - 1 + offset) * -step; while ( count-- ) { @@ -45,7 +45,7 @@ static void gen_sinc( double rolloff, int width, double offset, double spacing, pow_a_n * rolloff * cos( (maxh - 1) * angle ); double den = 1 - rolloff_cos_a - rolloff_cos_a + rolloff * rolloff; double sinc = scale * num / den - scale; - + out [-1] = (short) (cos( w ) * sinc + sinc); } angle += step; @@ -83,11 +83,11 @@ blargg_err_t Fir_Resampler_::buffer_size( int new_size ) clear(); return 0; } - + double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gain ) { ratio_ = new_factor; - + double fstep = 0.0; { double least_error = 2; @@ -106,14 +106,14 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai } } } - + skip_bits = 0; - + step = stereo * (int) floor( fstep ); - + ratio_ = fstep; fstep = fmod( fstep, 1.0 ); - + double filter = (ratio_ < 1.0) ? 1.0 : 1.0 / ratio_; double pos = 0.0; input_per_cycle = 0; @@ -122,7 +122,7 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai gen_sinc( rolloff, int (width_ * filter + 1) & ~1, pos, filter, double (0x7FFF * gain * filter), (int) width_, impulses + i * width_ ); - + pos += fstep; input_per_cycle += step; if ( pos >= 0.9999999 ) @@ -132,16 +132,16 @@ double Fir_Resampler_::time_ratio( double new_factor, double rolloff, double gai input_per_cycle++; } } - + clear(); - + return ratio_; } -int Fir_Resampler_::input_needed( blargg_long output_count ) const +int Fir_Resampler_::input_needed( int32_t output_count ) const { - blargg_long input_count = 0; - + int32_t input_count = 0; + unsigned long skip = skip_bits >> imp_phase; int remain = res - imp_phase; while ( (output_count -= 2) > 0 ) @@ -155,20 +155,20 @@ int Fir_Resampler_::input_needed( blargg_long output_count ) const } output_count -= 2; } - + long input_extra = input_count - (write_pos - &buf [(width_ - 1) * stereo]); if ( input_extra < 0 ) input_extra = 0; return input_extra; } -int Fir_Resampler_::avail_( blargg_long input_count ) const +int Fir_Resampler_::avail_( int32_t input_count ) const { int cycle_count = input_count / input_per_cycle; int output_count = cycle_count * res * stereo; input_count -= cycle_count * input_per_cycle; - - blargg_ulong skip = skip_bits >> imp_phase; + + uint32_t skip = skip_bits >> imp_phase; int remain = res - imp_phase; while ( input_count >= 0 ) { @@ -188,12 +188,14 @@ int Fir_Resampler_::skip_input( long count ) { int remain = write_pos - buf.begin(); int max_count = remain - width_ * stereo; + if ( max_count < 0 ) + max_count = 0; if ( count > max_count ) count = max_count; - + remain -= count; write_pos = &buf [remain]; memmove( buf.begin(), &buf [count], remain * sizeof buf [0] ); - + return count; } diff --git a/Frameworks/GME/gme/Fir_Resampler.h b/Frameworks/GME/gme/Fir_Resampler.h index 9fcd01cb8..9ec50c8c0 100644 --- a/Frameworks/GME/gme/Fir_Resampler.h +++ b/Frameworks/GME/gme/Fir_Resampler.h @@ -9,50 +9,50 @@ class Fir_Resampler_ { public: - + // Use Fir_Resampler (below) - + // Set input/output resampling ratio and optionally low-pass rolloff and gain. // Returns actual ratio used (rounded to internal precision). double time_ratio( double factor, double rolloff = 0.999, double gain = 1.0 ); - + // Current input/output ratio double ratio() const { return ratio_; } - + // Input - + typedef short sample_t; - + // Resize and clear input buffer blargg_err_t buffer_size( int ); - + // Clear input buffer. At least two output samples will be available after // two input samples are written. void clear(); - + // Number of input samples that can be written int max_write() const { return buf.end() - write_pos; } - + // Pointer to place to write input samples sample_t* buffer() { return write_pos; } - + // Notify resampler that 'count' input samples have been written void write( long count ); - + // Number of input samples in buffer int written() const { return write_pos - &buf [write_offset]; } - + // Skip 'count' input samples. Returns number of samples actually skipped. int skip_input( long count ); - + // Output - + // Number of extra input samples needed until 'count' output samples are available - int input_needed( blargg_long count ) const; - + int input_needed( int32_t count ) const; + // Number of output samples available int avail() const { return avail_( write_pos - &buf [width_ * stereo] ); } - + public: ~Fir_Resampler_(); protected: @@ -64,14 +64,14 @@ protected: int imp_phase; int const width_; int const write_offset; - blargg_ulong skip_bits; + uint32_t skip_bits; int step; int input_per_cycle; double ratio_; sample_t* impulses; - + Fir_Resampler_( int width, sample_t* ); - int avail_( blargg_long input_count ) const; + int avail_( int32_t input_count ) const; }; // Width is number of points in FIR. Must be even and 4 or more. More points give @@ -82,10 +82,10 @@ class Fir_Resampler : public Fir_Resampler_ { short impulses [max_res] [width]; public: Fir_Resampler() : Fir_Resampler_( width, impulses [0] ) { } - + // Read at most 'count' samples. Returns number of samples actually read. typedef short sample_t; - int read( sample_t* out, blargg_long count ); + int read( sample_t* out, int32_t count ); }; // End of public interface @@ -97,24 +97,24 @@ inline void Fir_Resampler_::write( long count ) } template -int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) +int Fir_Resampler::read( sample_t* out_begin, int32_t count ) { sample_t* out = out_begin; const sample_t* in = buf.begin(); sample_t* end_pos = write_pos; - blargg_ulong skip = skip_bits >> imp_phase; + uint32_t skip = skip_bits >> imp_phase; sample_t const* imp = impulses [imp_phase]; int remain = res - imp_phase; int const step = this->step; - + count >>= 1; - + // Resampling can add noise so don't actually do it if we've matched sample // rate const double ratio1 = ratio() - 1.0; const bool should_resample = ( ratio1 >= 0 ? ratio1 : -ratio1 ) >= 0.00001; - + if ( end_pos - in >= width * stereo ) { end_pos -= width * stereo; @@ -123,7 +123,7 @@ int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) count--; if ( count < 0 ) break; - + if( !should_resample ) { out [0] = static_cast( in [0] ); @@ -132,11 +132,11 @@ int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) else { // accumulate in extended precision - blargg_long l = 0; - blargg_long r = 0; - + int32_t l = 0; + int32_t r = 0; + const sample_t* i = in; - + for ( int n = width / 2; n; --n ) { int pt0 = imp [0]; @@ -148,38 +148,38 @@ int Fir_Resampler::read( sample_t* out_begin, blargg_long count ) r += pt1 * i [3]; i += 4; } - + remain--; - + l >>= 15; r >>= 15; - + in += (skip * stereo) & stereo; skip >>= 1; - + if ( !remain ) { imp = impulses [0]; skip = skip_bits; remain = res; } - + out [0] = (sample_t) l; out [1] = (sample_t) r; } - + in += step; out += 2; } while ( in <= end_pos ); } - + imp_phase = res - remain; - + int left = write_pos - in; write_pos = &buf [left]; memmove( buf.begin(), in, left * sizeof *in ); - + return out - out_begin; } diff --git a/Frameworks/GME/gme/Gb_Apu.cpp b/Frameworks/GME/gme/Gb_Apu.cpp index e06645850..60688e4aa 100644 --- a/Frameworks/GME/gme/Gb_Apu.cpp +++ b/Frameworks/GME/gme/Gb_Apu.cpp @@ -18,24 +18,26 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -unsigned const vol_reg = 0xFF24; -unsigned const status_reg = 0xFF26; +static unsigned const vol_reg = 0xFF24; +static unsigned const status_reg = 0xFF26; using std::min; using std::max; Gb_Apu::Gb_Apu() { + memset(regs, 0, sizeof(regs)); + square1.synth = &square_synth; square2.synth = &square_synth; wave.synth = &other_synth; noise.synth = &other_synth; - + oscs [0] = &square1; oscs [1] = &square2; oscs [2] = &wave; oscs [3] = &noise; - + for ( int i = 0; i < osc_count; i++ ) { Gb_Osc& osc = *oscs [i]; @@ -46,7 +48,7 @@ Gb_Apu::Gb_Apu() osc.outputs [2] = 0; osc.outputs [3] = 0; } - + set_tempo( 1.0 ); volume( 1.0 ); reset(); @@ -108,21 +110,21 @@ void Gb_Apu::reset() next_frame_time = 0; last_time = 0; frame_count = 0; - + square1.reset(); square2.reset(); wave.reset(); noise.reset(); noise.bits = 1; wave.wave_pos = 0; - + // avoid click at beginning regs [vol_reg - start_addr] = 0x77; update_volume(); - + regs [status_reg - start_addr] = 0x01; // force power write_register( 0, status_reg, 0x00 ); - + static unsigned char const initial_wave [] = { 0x84,0x40,0x43,0xAA,0x2D,0x78,0x92,0x3C, // wave table 0x60,0x59,0x59,0xB0,0x34,0xB8,0x2E,0xDA @@ -135,13 +137,13 @@ void Gb_Apu::run_until( blip_time_t end_time ) require( end_time >= last_time ); // end_time must not be before previous time if ( end_time == last_time ) return; - + while ( true ) { blip_time_t time = next_frame_time; if ( time > end_time ) time = end_time; - + // run oscillators for ( int i = 0; i < osc_count; ++i ) { @@ -163,18 +165,18 @@ void Gb_Apu::run_until( blip_time_t end_time ) } } last_time = time; - + if ( time == end_time ) break; - + next_frame_time += frame_period; - + // 256 Hz actions square1.clock_length(); square2.clock_length(); wave.clock_length(); noise.clock_length(); - + frame_count = (frame_count + 1) & 3; if ( frame_count == 0 ) { @@ -183,7 +185,7 @@ void Gb_Apu::run_until( blip_time_t end_time ) square2.clock_envelope(); noise.clock_envelope(); } - + if ( frame_count & 1 ) square1.clock_sweep(); // 128 Hz action } @@ -193,10 +195,10 @@ void Gb_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until( end_time ); - + assert( next_frame_time >= end_time ); next_frame_time -= end_time; - + assert( last_time >= end_time ); last_time -= end_time; } @@ -204,16 +206,16 @@ void Gb_Apu::end_frame( blip_time_t end_time ) void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) { require( (unsigned) data < 0x100 ); - + int reg = addr - start_addr; if ( (unsigned) reg >= register_count ) return; - + run_until( time ); - + int old_reg = regs [reg]; regs [reg] = data; - + if ( addr < vol_reg ) { write_osc( reg / 5, reg, data ); @@ -229,22 +231,22 @@ void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) if ( amp && osc.enabled && osc.output ) other_synth.offset( time, -amp, osc.output ); } - + if ( wave.outputs [3] ) other_synth.offset( time, 30, wave.outputs [3] ); - + update_volume(); - + if ( wave.outputs [3] ) other_synth.offset( time, -30, wave.outputs [3] ); - + // oscs will update with new amplitude when next run } else if ( addr == 0xFF25 || addr == status_reg ) { int mask = (regs [status_reg - start_addr] & 0x80) ? ~0 : 0; int flags = regs [0xFF25 - start_addr] & mask; - + // left/right assignments for ( int i = 0; i < osc_count; i++ ) { @@ -262,7 +264,7 @@ void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) other_synth.offset( time, -amp, old_output ); } } - + if ( addr == status_reg && data != old_reg ) { if ( !(data & 0x80) ) @@ -290,11 +292,11 @@ void Gb_Apu::write_register( blip_time_t time, unsigned addr, int data ) int Gb_Apu::read_register( blip_time_t time, unsigned addr ) { run_until( time ); - + int index = addr - start_addr; require( (unsigned) index < register_count ); int data = regs [index]; - + if ( addr == status_reg ) { data = (data & 0x80) | 0x70; @@ -305,6 +307,6 @@ int Gb_Apu::read_register( blip_time_t time, unsigned addr ) data |= 1 << i; } } - + return data; } diff --git a/Frameworks/GME/gme/Gb_Apu.h b/Frameworks/GME/gme/Gb_Apu.h index 9b251262f..cde3d8027 100644 --- a/Frameworks/GME/gme/Gb_Apu.h +++ b/Frameworks/GME/gme/Gb_Apu.h @@ -8,62 +8,62 @@ class Gb_Apu { public: - + // Set overall volume of all oscillators, where 1.0 is full volume void volume( double ); - + // Set treble equalization void treble_eq( const blip_eq_t& ); - + // Outputs can be assigned to a single buffer for mono output, or to three // buffers for stereo output (using Stereo_Buffer to do the mixing). - + // Assign all oscillator outputs to specified buffer(s). If buffer // is NULL, silences all oscillators. void output( Blip_Buffer* mono ); void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - + // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, // which refer to Square 1, Square 2, Wave, and Noise. If buffer is NULL, // silences oscillator. - enum { osc_count = 4 }; + static const int osc_count = 4; void osc_output( int index, Blip_Buffer* mono ); void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - + // Reset oscillators and internal state void reset(); - + // Reads and writes at addr must satisfy start_addr <= addr <= end_addr - enum { start_addr = 0xFF10 }; - enum { end_addr = 0xFF3F }; - enum { register_count = end_addr - start_addr + 1 }; - + static const unsigned int start_addr = 0xFF10; + static const unsigned int end_addr = 0xFF3F; + static const unsigned int register_count = end_addr - start_addr + 1; + // Write 'data' to address at specified time void write_register( blip_time_t, unsigned addr, int data ); - + // Read from address at specified time int read_register( blip_time_t, unsigned addr ); - + // Run all oscillators up to specified time, end current time frame, then // start a new frame at time 0. void end_frame( blip_time_t ); - + void set_tempo( double ); - + public: Gb_Apu(); private: // noncopyable Gb_Apu( const Gb_Apu& ); Gb_Apu& operator = ( const Gb_Apu& ); - + Gb_Osc* oscs [osc_count]; blip_time_t next_frame_time; blip_time_t last_time; blip_time_t frame_period; double volume_unit; int frame_count; - + Gb_Square square1; Gb_Square square2; Gb_Wave wave; @@ -71,14 +71,14 @@ private: uint8_t regs [register_count]; Gb_Square::Synth square_synth; // used by squares Gb_Wave::Synth other_synth; // used by wave and noise - + void update_volume(); void run_until( blip_time_t ); void write_osc( int index, int reg, int data ); }; inline void Gb_Apu::output( Blip_Buffer* b ) { output( b, b, b ); } - + inline void Gb_Apu::osc_output( int i, Blip_Buffer* b ) { osc_output( i, b, b, b ); } inline void Gb_Apu::volume( double vol ) diff --git a/Frameworks/GME/gme/Gb_Cpu.cpp b/Frameworks/GME/gme/Gb_Cpu.cpp index b8787b2b2..cea1a6a13 100644 --- a/Frameworks/GME/gme/Gb_Cpu.cpp +++ b/Frameworks/GME/gme/Gb_Cpu.cpp @@ -49,22 +49,22 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ inline void Gb_Cpu::set_code_page( int i, uint8_t* p ) { - state->code_map [i] = p - PAGE_OFFSET( i * (blargg_long) page_size ); + state->code_map [i] = p - PAGE_OFFSET( i * (int32_t) page_size ); } void Gb_Cpu::reset( void* unmapped ) { check( state == &state_ ); state = &state_; - + state_.remain = 0; - + for ( int i = 0; i < page_count + 1; i++ ) set_code_page( i, (uint8_t*) unmapped ); - + memset( &r, 0, sizeof r ); //interrupts_enabled = false; - + blargg_verify_byte_order(); } @@ -73,7 +73,7 @@ void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data ) // address range must begin and end on page boundaries require( start % page_size == 0 ); require( size % page_size == 0 ); - + unsigned first_page = start / page_size; for ( unsigned i = size / page_size; i--; ) set_code_page( first_page + i, (uint8_t*) data + i * page_size ); @@ -84,52 +84,53 @@ void Gb_Cpu::map_code( gb_addr_t start, unsigned size, void* data ) #define READ_FAST( addr, out ) CPU_READ_FAST( this, (addr), s.remain, out ) #define READ_PROG( addr ) (s.code_map [(addr) >> page_shift] [PAGE_OFFSET( addr )]) -unsigned const z_flag = 0x80; -unsigned const n_flag = 0x40; -unsigned const h_flag = 0x20; -unsigned const c_flag = 0x10; -bool Gb_Cpu::run( blargg_long cycle_count ) +static unsigned const z_flag = 0x80; +static unsigned const n_flag = 0x40; +static unsigned const h_flag = 0x20; +static unsigned const c_flag = 0x10; + +bool Gb_Cpu::run( int32_t cycle_count ) { - state_.remain = blargg_ulong (cycle_count + clocks_per_instr) / clocks_per_instr; + state_.remain = uint32_t (cycle_count + clocks_per_instr) / clocks_per_instr; state_t s; this->state = &s; memcpy( &s, &this->state_, sizeof s ); - + #if BLARGG_BIG_ENDIAN - #define R8( n ) (r8_ [n]) + #define R8( n ) (r8_ [n]) #elif BLARGG_LITTLE_ENDIAN - #define R8( n ) (r8_ [(n) ^ 1]) + #define R8( n ) (r8_ [(n) ^ 1]) #else #error "Byte order of CPU must be known" #endif - + union { core_regs_t rg; // individual registers - + struct { uint16_t bc, de, hl, unused; // pairs } rp; - + uint8_t r8_ [8]; // indexed registers (use R8 macro due to endian dependence) uint16_t r16 [4]; // indexed pairs }; static_assert( sizeof rg == 8 && sizeof rp == 8, "Unhandled word size" ); - + rg = r; unsigned pc = r.pc; unsigned sp = r.sp; unsigned flags = r.flags; - + loop: - + check( (unsigned long) pc < 0x10000 ); check( (unsigned long) sp < 0x10000 ); check( (flags & ~0xF0) == 0 ); - + uint8_t const* instr = s.code_map [pc >> page_shift]; unsigned op; - + // TODO: eliminate this special case #if BLARGG_NONPORTABLE op = instr [pc]; @@ -140,19 +141,19 @@ loop: op = *instr++; pc++; #endif - + #define GET_ADDR() GET_LE16( instr ) - + if ( !--s.remain ) goto stop; - + unsigned data; data = *instr; - + #ifdef GB_CPU_LOG_H gb_cpu_log( "new", pc - 1, op, data, instr [1] ); #endif - + switch ( op ) { @@ -170,44 +171,44 @@ loop: case 0x20: // JR NZ BRANCH( !(flags & z_flag) ) - + case 0x21: // LD HL,IMM (common) rp.hl = GET_ADDR(); pc += 2; goto loop; - + case 0x28: // JR Z BRANCH( flags & z_flag ) - + { unsigned temp; case 0xF0: // LD A,(0xFF00+imm) temp = data | 0xFF00; pc++; goto ld_a_ind_comm; - + case 0xF2: // LD A,(0xFF00+C) temp = rg.c | 0xFF00; goto ld_a_ind_comm; - + case 0x0A: // LD A,(BC) temp = rp.bc; goto ld_a_ind_comm; - + case 0x3A: // LD A,(HL-) temp = rp.hl; rp.hl = temp - 1; goto ld_a_ind_comm; - + case 0x1A: // LD A,(DE) temp = rp.de; goto ld_a_ind_comm; - + case 0x2A: // LD A,(HL+) (common) temp = rp.hl; rp.hl = temp + 1; goto ld_a_ind_comm; - + case 0xFA: // LD A,IND16 (common) temp = GET_ADDR(); pc += 2; @@ -215,11 +216,11 @@ loop: READ_FAST( temp, rg.a ); goto loop; } - + case 0xBE: // CMP (HL) data = READ( rp.hl ); goto cmp_comm; - + case 0xB8: // CMP B case 0xB9: // CMP C case 0xBA: // CMP D @@ -228,7 +229,7 @@ loop: case 0xBD: // CMP L data = R8( op & 7 ); goto cmp_comm; - + case 0xFE: // CMP IMM pc++; cmp_comm: @@ -254,7 +255,7 @@ loop: READ_FAST( addr, R8( (op >> 3) & 7 ) ); goto loop; } - + case 0xC4: // CNZ (next-most-common) pc += 2; if ( flags & z_flag ) @@ -270,7 +271,7 @@ loop: sp = (sp - 1) & 0xFFFF; WRITE( sp, data & 0xFF ); goto loop; - + case 0xC8: // RNZ (next-most-common) if ( !(flags & z_flag) ) goto loop; @@ -281,7 +282,7 @@ loop: pc += 0x100 * READ( sp + 1 ); sp = (sp + 2) & 0xFFFF; goto loop; - + case 0x00: // NOP case 0x40: // LD B,B case 0x49: // LD C,C @@ -291,14 +292,14 @@ loop: case 0x6D: // LD L,L case 0x7F: // LD A,A goto loop; - + // CB Instructions case 0xCB: pc++; // now data is the opcode switch ( data ) { - + { int temp; case 0x46: // BIT b,(HL) @@ -314,7 +315,7 @@ loop: READ_FAST( addr, temp ); goto bit_comm; } - + case 0x40: case 0x41: case 0x42: case 0x43: // BIT b,r case 0x44: case 0x45: case 0x47: case 0x48: case 0x49: case 0x4A: case 0x4B: case 0x4C: @@ -337,7 +338,7 @@ loop: flags ^= (temp << bit) & z_flag; goto loop; } - + case 0x86: // RES b,(HL) case 0x8E: case 0x96: @@ -362,7 +363,7 @@ loop: WRITE( rp.hl, temp | bit ); goto loop; } - + case 0xC0: case 0xC1: case 0xC2: case 0xC3: // SET b,r case 0xC4: case 0xC5: case 0xC7: case 0xC8: case 0xC9: case 0xCA: case 0xCB: case 0xCC: @@ -396,13 +397,13 @@ loop: case 0xBB: case 0xBC: case 0xBD: case 0xBF: R8( data & 7 ) &= ~(1 << ((data >> 3) & 7)); goto loop; - + { int temp; case 0x36: // SWAP (HL) temp = READ( rp.hl ); goto swap_comm; - + case 0x30: // SWAP B case 0x31: // SWAP C case 0x32: // SWAP D @@ -416,7 +417,7 @@ loop: flags = 0; goto shift_comm; } - + // Shift/Rotate case 0x06: // RLC (HL) @@ -424,13 +425,13 @@ loop: case 0x26: // SLA (HL) op = READ( rp.hl ); goto rl_comm; - + case 0x20: case 0x21: case 0x22: case 0x23: case 0x24: case 0x25: case 0x27: // SLA A case 0x00: case 0x01: case 0x02: case 0x03: case 0x04: case 0x05: case 0x07: // RLC A case 0x10: case 0x11: case 0x12: case 0x13: case 0x14: case 0x15: case 0x17: // RL A op = R8( data & 7 ); goto rl_comm; - + case 0x3E: // SRL (HL) data += 0x10; /* fallthrough */ // bump up to 0x4n to avoid preserving sign bit case 0x1E: // RR (HL) @@ -438,7 +439,7 @@ loop: case 0x2E: // SRA (HL) op = READ( rp.hl ); goto rr_comm; - + case 0x38: case 0x39: case 0x3A: case 0x3B: case 0x3C: case 0x3D: case 0x3F: // SRL A data += 0x10; /* fallthrough */ // bump up to 0x4n case 0x18: case 0x19: case 0x1A: case 0x1B: case 0x1C: case 0x1D: case 0x1F: // RR A @@ -446,7 +447,7 @@ loop: case 0x28: case 0x29: case 0x2A: case 0x2B: case 0x2C: case 0x2D: case 0x2F: // SRA A op = R8( data & 7 ); goto rr_comm; - + } // CB op assert( false ); // unhandled CB op // fallthrough @@ -463,7 +464,7 @@ loop: op |= op >> 8; // SLA doesn't fill lower bit goto shift_comm; - + case 0x0F: // RRCA case 0x1F: // RRA data = op; @@ -516,7 +517,7 @@ loop: data++; WRITE( data, sp >> 8 ); goto loop; - + case 0xF9: // LD SP,HL sp = rp.hl; goto loop; @@ -525,20 +526,20 @@ loop: sp = GET_ADDR(); pc += 2; goto loop; - + case 0x01: // LD BC,IMM case 0x11: // LD DE,IMM r16 [op >> 4] = GET_ADDR(); pc += 2; goto loop; - + { unsigned temp; case 0xE0: // LD (0xFF00+imm),A temp = data | 0xFF00; pc++; goto write_data_rg_a; - + case 0xE2: // LD (0xFF00+C),A temp = rg.c | 0xFF00; goto write_data_rg_a; @@ -547,20 +548,20 @@ loop: temp = rp.hl; rp.hl = temp - 1; goto write_data_rg_a; - + case 0x02: // LD (BC),A temp = rp.bc; goto write_data_rg_a; - + case 0x12: // LD (DE),A temp = rp.de; goto write_data_rg_a; - + case 0x22: // LD (HL+),A temp = rp.hl; rp.hl = temp + 1; goto write_data_rg_a; - + case 0xEA: // LD IND16,A (common) temp = GET_ADDR(); pc += 2; @@ -568,42 +569,42 @@ loop: WRITE( temp, rg.a ); goto loop; } - + case 0x06: // LD B,IMM rg.b = data; pc++; goto loop; - + case 0x0E: // LD C,IMM rg.c = data; pc++; goto loop; - + case 0x16: // LD D,IMM rg.d = data; pc++; goto loop; - + case 0x1E: // LD E,IMM rg.e = data; pc++; goto loop; - + case 0x26: // LD H,IMM rg.h = data; pc++; goto loop; - + case 0x2E: // LD L,IMM rg.l = data; pc++; goto loop; - + case 0x36: // LD (HL),IMM WRITE( rp.hl, data ); pc++; goto loop; - + case 0x3E: // LD A,IMM rg.a = data; pc++; @@ -616,7 +617,7 @@ loop: case 0x23: // INC HL r16 [op >> 4]++; goto loop; - + case 0x33: // INC SP sp = (sp + 1) & 0xFFFF; goto loop; @@ -626,18 +627,18 @@ loop: case 0x2B: // DEC HL r16 [op >> 4]--; goto loop; - + case 0x3B: // DEC SP sp = (sp - 1) & 0xFFFF; goto loop; - + case 0x34: // INC (HL) op = rp.hl; data = READ( op ); data++; WRITE( op, data & 0xFF ); goto inc_comm; - + case 0x04: // INC B case 0x0C: // INC C (common) case 0x14: // INC D @@ -650,14 +651,14 @@ loop: inc_comm: flags = (flags & c_flag) | (((data & 15) - 1) & h_flag) | ((data >> 1) & z_flag); goto loop; - + case 0x35: // DEC (HL) op = rp.hl; data = READ( op ); data--; WRITE( op, data & 0xFF ); goto dec_comm; - + case 0x05: // DEC B case 0x0D: // DEC C case 0x15: // DEC D @@ -678,9 +679,9 @@ loop: // Add 16-bit { - blargg_ulong temp; // need more than 16 bits for carry + uint32_t temp; // need more than 16 bits for carry unsigned prev; - + case 0xF8: // LD HL,SP+imm temp = int8_t (data); // sign-extend to 16 bits pc++; @@ -688,7 +689,7 @@ loop: temp += sp; prev = sp; goto add_16_hl; - + case 0xE8: // ADD SP,IMM temp = int8_t (data); // sign-extend to 16 bits pc++; @@ -701,7 +702,7 @@ loop: case 0x39: // ADD HL,SP temp = sp; goto add_hl_comm; - + case 0x09: // ADD HL,BC case 0x19: // ADD HL,DE case 0x29: // ADD HL,HL @@ -717,11 +718,11 @@ loop: flags |= (((temp & 0x0FFF) - (prev & 0x0FFF)) >> 7) & h_flag; goto loop; } - + case 0x86: // ADD (HL) data = READ( rp.hl ); goto add_comm; - + case 0x80: // ADD B case 0x81: // ADD C case 0x82: // ADD D @@ -731,7 +732,7 @@ loop: case 0x87: // ADD A data = R8( op & 7 ); goto add_comm; - + case 0xC6: // ADD IMM pc++; add_comm: @@ -750,7 +751,7 @@ loop: case 0x8E: // ADC (HL) data = READ( rp.hl ); goto adc_comm; - + case 0x88: // ADC B case 0x89: // ADC C case 0x8A: // ADC D @@ -760,7 +761,7 @@ loop: case 0x8F: // ADC A data = R8( op & 7 ); goto adc_comm; - + case 0xCE: // ADC IMM pc++; adc_comm: @@ -771,7 +772,7 @@ loop: case 0x96: // SUB (HL) data = READ( rp.hl ); goto sub_comm; - + case 0x90: // SUB B case 0x91: // SUB C case 0x92: // SUB D @@ -781,7 +782,7 @@ loop: case 0x97: // SUB A data = R8( op & 7 ); goto sub_comm; - + case 0xD6: // SUB IMM pc++; sub_comm: @@ -793,7 +794,7 @@ loop: case 0x9E: // SBC (HL) data = READ( rp.hl ); goto sbc_comm; - + case 0x98: // SBC B case 0x99: // SBC C case 0x9A: // SBC D @@ -803,7 +804,7 @@ loop: case 0x9F: // SBC A data = R8( op & 7 ); goto sbc_comm; - + case 0xDE: // SBC IMM pc++; sbc_comm: @@ -821,7 +822,7 @@ loop: case 0xA5: // AND L data = R8( op & 7 ); goto and_comm; - + case 0xA6: // AND (HL) data = READ( rp.hl ); pc--; // FALLTHRU @@ -841,7 +842,7 @@ loop: case 0xB5: // OR L data = R8( op & 7 ); goto or_comm; - + case 0xB6: // OR (HL) data = READ( rp.hl ); pc--; // FALLTHRU @@ -861,7 +862,7 @@ loop: case 0xAD: // XOR L data = R8( op & 7 ); goto xor_comm; - + case 0xAE: // XOR (HL) data = READ( rp.hl ); pc--; // FALLTHRU @@ -873,7 +874,7 @@ loop: data--; flags = (data >> 1) & z_flag; goto loop; - + case 0xAF: // XOR A rg.a = 0; flags = z_flag; @@ -893,25 +894,25 @@ loop: flags = rg.a & 0xF0; rg.a = rg.flags; goto loop; - + case 0xC5: // PUSH BC data = rp.bc; goto push; - + case 0xD5: // PUSH DE data = rp.de; goto push; - + case 0xE5: // PUSH HL data = rp.hl; goto push; - + case 0xF5: // PUSH AF data = (rg.a << 8) | flags; goto push; // Flow control - + case 0xFF: if ( pc == idle_addr + 1 ) goto stop; @@ -921,19 +922,19 @@ loop: data = pc; pc = (op & 0x38) + rst_base; goto push; - + case 0xCC: // CZ pc += 2; if ( flags & z_flag ) goto call; goto loop; - + case 0xD4: // CNC pc += 2; if ( !(flags & c_flag) ) goto call; goto loop; - + case 0xDC: // CC pc += 2; if ( flags & c_flag ) @@ -943,17 +944,17 @@ loop: case 0xD9: // RETI //interrupts_enabled = 1; goto ret; - + case 0xC0: // RZ if ( !(flags & z_flag) ) goto ret; goto loop; - + case 0xD0: // RNC if ( !(flags & c_flag) ) goto ret; goto loop; - + case 0xD8: // RC if ( flags & c_flag ) goto ret; @@ -961,13 +962,13 @@ loop: case 0x18: // JR BRANCH( true ) - + case 0x30: // JR NC BRANCH( !(flags & c_flag) ) - + case 0x38: // JR C BRANCH( flags & c_flag ) - + case 0xE9: // JP_HL pc = rp.hl; goto loop; @@ -975,13 +976,13 @@ loop: case 0xC3: // JP (next-most-common) pc = GET_ADDR(); goto loop; - + case 0xC2: // JP NZ pc += 2; if ( !(flags & z_flag) ) goto jp_taken; goto loop; - + case 0xCA: // JP Z (most common) pc += 2; if ( !(flags & z_flag) ) @@ -990,13 +991,13 @@ loop: pc -= 2; pc = GET_ADDR(); goto loop; - + case 0xD2: // JP NC pc += 2; if ( !(flags & c_flag) ) goto jp_taken; goto loop; - + case 0xDA: // JP C pc += 2; if ( flags & c_flag ) @@ -1038,21 +1039,21 @@ loop: s.remain++; goto stop; } - + // If this fails then the case above is missing an opcode assert( false ); - + stop: pc--; - + // copy state back STATIC_CAST(core_regs_t&,r) = rg; r.pc = pc; r.sp = sp; r.flags = flags; - + this->state = &state_; memcpy( &this->state_, &s, sizeof this->state_ ); - + return s.remain > 0; } diff --git a/Frameworks/GME/gme/Gb_Cpu.h b/Frameworks/GME/gme/Gb_Cpu.h index d3df30cac..6c2e0616f 100644 --- a/Frameworks/GME/gme/Gb_Cpu.h +++ b/Frameworks/GME/gme/Gb_Cpu.h @@ -15,17 +15,17 @@ class Gb_Cpu { public: // Clear registers and map all pages to unmapped void reset( void* unmapped = 0 ); - + // Map code memory (memory accessed via the program counter). Start and size // must be multiple of page_size. enum { page_size = 0x2000 }; void map_code( gb_addr_t start, unsigned size, void* code ); - + uint8_t* get_code( gb_addr_t ); - + // Push a byte on the stack void push_byte( int ); - + // Game Boy Z80 registers. *Not* kept updated during a call to run(). struct core_regs_t { #if BLARGG_BIG_ENDIAN @@ -34,32 +34,32 @@ public: uint8_t c, b, e, d, l, h, a, flags; #endif }; - + struct registers_t : core_regs_t { long pc; // more than 16 bits to allow overflow detection uint16_t sp; }; registers_t r; - + // Interrupt enable flag set by EI and cleared by DI //bool interrupts_enabled; // unused - + // Base address for RST vectors (normally 0) gb_addr_t rst_base; - + // If CPU executes opcode 0xFF at this address, it treats as illegal instruction enum { idle_addr = 0xF00D }; - + // Run CPU for at least 'count' cycles and return false, or return true if // illegal instruction is encountered. - bool run( blargg_long count ); - + bool run( int32_t count ); + // Number of clock cycles remaining for most recent run() call - blargg_long remain() const { return state->remain * clocks_per_instr; } - + int32_t remain() const { return state->remain * clocks_per_instr; } + // Can read this many bytes past end of a page enum { cpu_padding = 8 }; - + public: Gb_Cpu() : rst_base( 0 ) { state = &state_; } enum { page_shift = 13 }; @@ -68,14 +68,14 @@ private: // noncopyable Gb_Cpu( const Gb_Cpu& ); Gb_Cpu& operator = ( const Gb_Cpu& ); - + struct state_t { uint8_t* code_map [page_count + 1]; - blargg_long remain; + int32_t remain; }; state_t* state; // points to state_ or a local copy within run() state_t state_; - + void set_code_page( int, uint8_t* ); }; diff --git a/Frameworks/GME/gme/Gb_Oscs.cpp b/Frameworks/GME/gme/Gb_Oscs.cpp index 39d2d6b8f..e38ccb237 100644 --- a/Frameworks/GME/gme/Gb_Oscs.cpp +++ b/Frameworks/GME/gme/Gb_Oscs.cpp @@ -54,12 +54,12 @@ bool Gb_Env::write_register( int reg, int data ) case 1: length = 64 - (regs [1] & 0x3F); break; - + case 2: if ( !(data >> 4) ) enabled = false; break; - + case 4: if ( data & trigger ) { @@ -92,12 +92,12 @@ void Gb_Square::clock_sweep() sweep_delay = sweep_period; regs [3] = sweep_freq & 0xFF; regs [4] = (regs [4] & ~0x07) | (sweep_freq >> 8 & 0x07); - + int offset = sweep_freq >> (regs [0] & shift_mask); if ( regs [0] & 0x08 ) offset = -offset; sweep_freq += offset; - + if ( sweep_freq < 0 ) { sweep_freq = 0; @@ -114,13 +114,13 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) { if ( sweep_freq == 2048 ) playing = false; - + static unsigned char const table [4] = { 1, 2, 4, 6 }; int const duty = table [regs [1] >> 6]; int amp = volume & playing; if ( phase >= duty ) amp = -amp; - + int frequency = this->frequency(); if ( unsigned (frequency - 1) > 2040 ) // frequency < 1 || frequency > 2041 { @@ -128,7 +128,7 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) amp = volume >> 1; playing = false; } - + { int delta = amp - last_amp; if ( delta ) @@ -137,11 +137,11 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) synth->offset( time, delta, output ); } } - + time += delay; if ( !playing ) time = end_time; - + if ( time < end_time ) { int const period = (2048 - frequency) * 4; @@ -159,7 +159,7 @@ void Gb_Square::run( blip_time_t time, blip_time_t end_time, int playing ) time += period; } while ( time < end_time ); - + this->phase = phase; last_amp = delta >> 1; } @@ -174,7 +174,7 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) int tap = 13 - (regs [3] & 8); if ( bits >> tap & 2 ) amp = -amp; - + { int delta = amp - last_amp; if ( delta ) @@ -183,16 +183,16 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) synth->offset( time, delta, output ); } } - + time += delay; if ( !playing ) time = end_time; - + if ( time < end_time ) { static unsigned char const table [8] = { 8, 16, 32, 48, 64, 80, 96, 112 }; int period = table [regs [3] & 7] << (regs [3] >> 4); - + // keep parallel resampled time to eliminate time conversion in the loop Blip_Buffer* const output = this->output; const blip_resampled_time_t resampled_period = @@ -200,7 +200,7 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) blip_resampled_time_t resampled_time = output->resampled_time( time ); unsigned bits = this->bits; int delta = amp * 2; - + do { unsigned changed = (bits >> tap) + 1; @@ -215,7 +215,7 @@ void Gb_Noise::run( blip_time_t time, blip_time_t end_time, int playing ) resampled_time += resampled_period; } while ( time < end_time ); - + this->bits = bits; last_amp = delta >> 1; } @@ -232,15 +232,15 @@ inline void Gb_Wave::write_register( int reg, int data ) if ( !(data & 0x80) ) enabled = false; break; - + case 1: length = 256 - regs [1]; break; - + case 2: volume = data >> 5 & 3; break; - + case 4: if ( data & trigger & regs [0] ) { @@ -258,14 +258,14 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) int frequency; { int amp = (wave [wave_pos] >> volume_shift & playing) * 2; - + frequency = this->frequency(); if ( unsigned (frequency - 1) > 2044 ) // frequency < 1 || frequency > 2045 { amp = 30 >> volume_shift & playing; playing = false; } - + int delta = amp - last_amp; if ( delta ) { @@ -273,17 +273,17 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) synth->offset( time, delta, output ); } } - + time += delay; if ( !playing ) time = end_time; - + if ( time < end_time ) { Blip_Buffer* const output = this->output; int const period = (2048 - frequency) * 2; int wave_pos = (this->wave_pos + 1) & (wave_size - 1); - + do { int amp = (wave [wave_pos] >> volume_shift) * 2; @@ -297,7 +297,7 @@ void Gb_Wave::run( blip_time_t time, blip_time_t end_time, int playing ) time += period; } while ( time < end_time ); - + this->wave_pos = (wave_pos - 1) & (wave_size - 1); } delay = time - end_time; @@ -324,11 +324,11 @@ void Gb_Apu::write_osc( int index, int reg, int data ) } } break; - + case 2: wave.write_register( reg, data ); break; - + case 3: if ( noise.write_register( reg, data ) ) noise.bits = 0x7FFF; diff --git a/Frameworks/GME/gme/Gb_Oscs.h b/Frameworks/GME/gme/Gb_Oscs.h index 8cb026c3e..cd5c1c3ee 100644 --- a/Frameworks/GME/gme/Gb_Oscs.h +++ b/Frameworks/GME/gme/Gb_Oscs.h @@ -11,18 +11,18 @@ struct Gb_Osc { enum { trigger = 0x80 }; enum { len_enabled_mask = 0x40 }; - + Blip_Buffer* outputs [4]; // NULL, right, left, center Blip_Buffer* output; int output_select; uint8_t* regs; // osc's 5 registers - + int delay; int last_amp; int volume; int length; int enabled; - + void reset(); void clock_length(); int frequency() const { return (regs [4] & 7) * 0x100 + regs [3]; } @@ -31,7 +31,7 @@ struct Gb_Osc struct Gb_Env : Gb_Osc { int env_delay; - + void reset(); void clock_envelope(); bool write_register( int, int ); @@ -41,13 +41,13 @@ struct Gb_Square : Gb_Env { enum { period_mask = 0x70 }; enum { shift_mask = 0x07 }; - + typedef Blip_Synth Synth; Synth const* synth; int sweep_delay; int sweep_freq; int phase; - + void reset(); void clock_sweep(); void run( blip_time_t, blip_time_t, int playing ); @@ -58,7 +58,7 @@ struct Gb_Noise : Gb_Env typedef Blip_Synth Synth; Synth const* synth; unsigned bits; - + void run( blip_time_t, blip_time_t, int playing ); }; @@ -69,7 +69,7 @@ struct Gb_Wave : Gb_Osc int wave_pos; enum { wave_size = 32 }; uint8_t wave [wave_size]; - + void write_register( int, int ); void run( blip_time_t, blip_time_t, int playing ); }; diff --git a/Frameworks/GME/gme/Gbs_Emu.cpp b/Frameworks/GME/gme/Gbs_Emu.cpp index df96e1d03..3e2a7eab6 100644 --- a/Frameworks/GME/gme/Gbs_Emu.cpp +++ b/Frameworks/GME/gme/Gbs_Emu.cpp @@ -26,21 +26,21 @@ Gbs_Emu::equalizer_t const Gbs_Emu::headphones_eq = Gbs_Emu::Gbs_Emu() { set_type( gme_gbs_type ); - + static const char* const names [Gb_Apu::osc_count] = { "Square 1", "Square 2", "Wave", "Noise" }; set_voice_names( names ); - + static int const types [Gb_Apu::osc_count] = { wave_type | 1, wave_type | 2, wave_type | 0, mixed_type | 0 }; set_voice_types( types ); - + set_silence_lookahead( 6 ); set_max_initial_silence( 21 ); set_gain( 1.2 ); - + set_equalizer( make_equalizer( -1.0, 120 ) ); } @@ -77,19 +77,19 @@ static blargg_err_t check_gbs_header( void const* header ) struct Gbs_File : Gme_Info_ { Gbs_Emu::header_t h; - + Gbs_File() { set_type( gme_gbs_type ); } - + blargg_err_t load_( Data_Reader& in ) { blargg_err_t err = in.read( &h, Gbs_Emu::header_size ); if ( err ) return (err == in.eof_error ? gme_wrong_file_type : err); - + set_track_count( h.track_count ); return check_gbs_header( &h ); } - + blargg_err_t track_info_( track_info_t* out, int ) const { copy_gbs_fields( h, out ); @@ -107,27 +107,27 @@ extern gme_type_t const gme_gbs_type = &gme_gbs_type_; blargg_err_t Gbs_Emu::load_( Data_Reader& in ) { - assert( offsetof (header_t,copyright [32]) == header_size ); + blaarg_static_assert( offsetof (header_t,copyright [32]) == header_size, "GBS Header layout incorrect!" ); RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); - + set_track_count( header_.track_count ); RETURN_ERR( check_gbs_header( &header_ ) ); - + if ( header_.vers != 1 ) set_warning( "Unknown file version" ); - + if ( header_.timer_mode & 0x78 ) set_warning( "Invalid timer mode" ); - + unsigned load_addr = get_le16( header_.load_addr ); if ( (header_.load_addr [1] | header_.init_addr [1] | header_.play_addr [1]) > 0x7F || load_addr < 0x400 ) set_warning( "Invalid load/init/play address" ); - + set_voice_count( Gb_Apu::osc_count ); - + apu.volume( gain() ); - + return setup_buffer( 4194304 ); } @@ -153,8 +153,8 @@ void Gbs_Emu::set_bank( int n ) { n = 1; } - - blargg_long addr = n * (blargg_long) bank_size; + + int32_t addr = n * (int32_t) bank_size; if (addr > rom.size()) { return; @@ -206,37 +206,37 @@ void Gbs_Emu::set_tempo_( double t ) blargg_err_t Gbs_Emu::start_track_( int track ) { RETURN_ERR( Classic_Emu::start_track_( track ) ); - + memset( ram, 0, 0x4000 ); memset( ram + 0x4000, 0xFF, 0x1F00 ); memset( ram + 0x5F00, 0, sizeof ram - 0x5F00 ); ram [hi_page] = 0; // joypad reads back as 0 - + apu.reset(); for ( int i = 0; i < (int) sizeof sound_data; i++ ) apu.write_register( 0, i + apu.start_addr, sound_data [i] ); - + unsigned load_addr = get_le16( header_.load_addr ); rom.set_addr( load_addr ); cpu::rst_base = load_addr; - + cpu::reset( rom.unmapped() ); - + cpu::map_code( ram_addr, 0x10000 - ram_addr, ram ); cpu::map_code( 0, bank_size, rom.at_addr( 0 ) ); set_bank( rom.size() > bank_size ); - + ram [hi_page + 6] = header_.timer_modulo; ram [hi_page + 7] = header_.timer_mode; update_timer(); next_play = play_period; - + cpu::r.a = track; cpu::r.pc = idle_addr; cpu::r.sp = get_le16( header_.stack_ptr ); cpu_time = 0; cpu_jsr( get_le16( header_.init_addr ) ); - + return 0; } @@ -249,7 +249,7 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) cpu_time = duration; bool result = cpu::run( count ); cpu_time -= cpu::remain(); - + if ( result ) { if ( cpu::r.pc == idle_addr ) @@ -259,7 +259,7 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) cpu_time = duration; break; } - + if ( cpu_time < next_play ) cpu_time = next_play; next_play += play_period; @@ -282,12 +282,12 @@ blargg_err_t Gbs_Emu::run_clocks( blip_time_t& duration, int ) } } } - + duration = cpu_time; next_play -= cpu_time; if ( next_play < 0 ) // could go negative if routine is taking too long to return next_play = 0; apu.end_frame( cpu_time ); - + return 0; } diff --git a/Frameworks/GME/gme/Gbs_Emu.h b/Frameworks/GME/gme/Gbs_Emu.h index 580f395c6..d963f4077 100644 --- a/Frameworks/GME/gme/Gbs_Emu.h +++ b/Frameworks/GME/gme/Gbs_Emu.h @@ -14,7 +14,7 @@ public: // Equalizer profiles for Game Boy Color speaker and headphones static equalizer_t const handheld_eq; static equalizer_t const headphones_eq; - + // GBS file header enum { header_size = 112 }; struct header_t @@ -33,12 +33,12 @@ public: char author [32]; char copyright [32]; }; - + // Header for currently loaded file header_t const& header() const { return header_; } - + static gme_type_t static_type() { return gme_gbs_type; } - + public: // deprecated using Music_Emu::load; @@ -62,25 +62,25 @@ private: enum { bank_size = 0x4000 }; Rom_Data rom; void set_bank( int ); - + // timer blip_time_t cpu_time; blip_time_t play_period; blip_time_t next_play; void update_timer(); - + header_t header_; void cpu_jsr( gb_addr_t ); - + public: private: friend class Gb_Cpu; blip_time_t clock() const { return cpu_time - cpu::remain(); } - + enum { joypad_addr = 0xFF00 }; enum { ram_addr = 0xA000 }; enum { hi_page = 0xFF00 - ram_addr }; byte ram [0x4000 + 0x2000 + Gb_Cpu::cpu_padding]; Gb_Apu apu; - + int cpu_read( gb_addr_t ); void cpu_write( gb_addr_t, int ); }; diff --git a/Frameworks/GME/gme/Gme_File.cpp b/Frameworks/GME/gme/Gme_File.cpp index 24bcada6f..d027b5aaf 100644 --- a/Frameworks/GME/gme/Gme_File.cpp +++ b/Frameworks/GME/gme/Gme_File.cpp @@ -61,6 +61,11 @@ blargg_err_t Gme_File::load_( Data_Reader& in ) { RETURN_ERR( file_data.resize( in.remain() ) ); RETURN_ERR( in.read( file_data.begin(), file_data.size() ) ); + if ( type()->track_count == 1 ) + { + RETURN_ERR( tracks.resize( 2 ) ); + tracks[0] = 0, tracks[1] = file_data.size(); + } return load_mem_( file_data.begin(), file_data.size() ); } @@ -78,7 +83,7 @@ blargg_err_t Gme_File::post_load( blargg_err_t err ) post_load_(); else unload(); - + return err; } @@ -90,6 +95,22 @@ blargg_err_t Gme_File::load_mem( void const* in, long size ) return post_load( load_mem_( (byte const*) in, size ) ); } +blargg_err_t Gme_File::load_tracks( void const* in, long* sizes, int count ) +{ + pre_load(); + if ( type()->track_count != 1 ) + return "File type must have a fixed track count of 1"; + set_track_count( count ); + RETURN_ERR( tracks.resize( count + 1 ) ); + long size = 0; + for ( int i = 0; i < count; size += sizes[i++] ) + tracks[i] = size; + tracks[count] = size; + RETURN_ERR( file_data.resize( size ) ); + memcpy( file_data.begin(), in, size ); + return post_load( load_mem_( file_data.begin(), tracks[1] ) ); +} + blargg_err_t Gme_File::load( Data_Reader& in ) { pre_load(); @@ -116,31 +137,31 @@ void Gme_File::copy_field_( char* out, const char* in, int in_size ) { if ( !in || !*in ) return; - + // remove spaces/junk from beginning while ( in_size && unsigned (*in - 1) <= ' ' - 1 ) { in++; in_size--; } - + // truncate if ( in_size > max_field_ ) in_size = max_field_; - + // find terminator int len = 0; while ( len < in_size && in [len] ) len++; - + // remove spaces/junk from end while ( len && unsigned (in [len - 1]) <= ' ' ) len--; - + // copy out [len] = 0; memcpy( out, in, len ); - + // strip out stupid fields that should have been left blank if ( !strcmp( out, "?" ) || !strcmp( out, "" ) || !strcmp( out, "< ? >" ) ) out [0] = 0; @@ -155,7 +176,7 @@ blargg_err_t Gme_File::remap_track_( int* track_io ) const { if ( (unsigned) *track_io >= (unsigned) track_count() ) return "Invalid track"; - + if ( (unsigned) *track_io < (unsigned) playlist.size() ) { M3u_Playlist::entry_t const& e = playlist [*track_io]; @@ -186,7 +207,7 @@ blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const out->play_length = -1; out->repeat_count = -1; out->song [0] = 0; - + out->game [0] = 0; out->author [0] = 0; out->composer [0] = 0; @@ -201,13 +222,13 @@ blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const out->disc [0] = 0; out->track [0] = 0; out->ost [0] = 0; - + copy_field_( out->system, type()->system ); - + int remapped = track; RETURN_ERR( remap_track_( &remapped ) ); RETURN_ERR( track_info_( out, remapped ) ); - + // override with m3u info if ( playlist.size() ) { @@ -221,7 +242,7 @@ blargg_err_t Gme_File::track_info( track_info_t* out, int track ) const copy_field_( out->dumper, i.ripping ); copy_field_( out->tagger, i.tagging ); copy_field_( out->date, i.date ); - + M3u_Playlist::entry_t const& e = playlist [track]; copy_field_( out->song, e.name ); if ( e.length >= 0 ) out->length = e.length; diff --git a/Frameworks/GME/gme/Gme_File.h b/Frameworks/GME/gme/Gme_File.h index b1ff0ba05..6ce3935a1 100644 --- a/Frameworks/GME/gme/Gme_File.h +++ b/Frameworks/GME/gme/Gme_File.h @@ -18,7 +18,7 @@ struct gme_type_t_ int track_count; /* non-zero for formats with a fixed number of tracks */ Music_Emu* (*new_emu)(); /* Create new emulator for this type (useful in C++ only) */ Music_Emu* (*new_info)(); /* Create new info reader for this type */ - + /* internal */ const char* extension_; int flags_; @@ -27,18 +27,18 @@ struct gme_type_t_ struct track_info_t { long track_count; - + /* times in milliseconds; -1 if unknown */ long length; long intro_length; long loop_length; long fade_length; long repeat_count; - + /* Length if available, otherwise intro_length+loop_length*2 if available, * otherwise a default of 150000 (2.5 minutes) */ long play_length; - + /* empty string if not available */ char system [256]; char game [256]; @@ -61,60 +61,61 @@ enum { gme_max_field = 255 }; struct Gme_File { public: // File loading - + // Each loads game music data from a file and returns an error if // file is wrong type or is seriously corrupt. They also set warning // string for minor problems. - + // Load from file on disk blargg_err_t load_file( const char* path ); - + // Load from custom data source (see Data_Reader.h) blargg_err_t load( Data_Reader& ); - + // Load from file already read into memory. Keeps pointer to data, so you // must not free it until you're done with the file. blargg_err_t load_mem( void const* data, long size ); - + + // Load from multiple files already read into memory. + // Should only be used for file types with a fixed track count of 1. + blargg_err_t load_tracks( void const* in, long* sizes, int count ); + // Load an m3u playlist. Must be done after loading main music file. blargg_err_t load_m3u( const char* path ); blargg_err_t load_m3u( Data_Reader& in ); - + // Clears any loaded m3u playlist and any internal playlist that the music // format supports (NSFE for example). void clear_playlist(); - + // Informational - + // Type of emulator. For example if this returns gme_nsfe_type, this object // is an NSFE emulator, and you can cast to an Nsfe_Emu* if necessary. gme_type_t type() const; - + // Most recent warning string, or NULL if none. Clears current warning after // returning. const char* warning(); - + // Number of tracks or 0 if no file has been loaded int track_count() const; - + // Get information for a track (length, name, author, etc.) // See gme.h for definition of struct track_info_t. blargg_err_t track_info( track_info_t* out, int track ) const; - + // User data/cleanup - + // Set/get pointer to data you want to associate with this emulator. // You can use this for whatever you want. void set_user_data( void* p ) { user_data_ = p; } void* user_data() const { return user_data_; } - + // Register cleanup function to be called when deleting emulator, or NULL to // clear it. Passes user_data to cleanup function. void set_user_cleanup( gme_user_cleanup_t func ) { user_cleanup_ = func; } - - bool is_archive = false; - virtual blargg_err_t load_archive( const char* ) { return gme_wrong_file_type; } - + public: // deprecated int error_count() const; // use warning() @@ -129,7 +130,10 @@ protected: void set_warning( const char* s ) { warning_ = s; } void set_type( gme_type_t t ) { type_ = t; } blargg_err_t load_remaining_( void const* header, long header_size, Data_Reader& remaining ); - + + const byte* track_pos( int i ) { return &file_data[tracks[i]]; } + long track_size( int i ) { return tracks[i + 1] - tracks[i]; } + // Overridable virtual void unload(); // called before loading file and if loading fails virtual blargg_err_t load_( Data_Reader& ); // default loads then calls load_mem_() @@ -138,14 +142,14 @@ protected: virtual void pre_load(); virtual void post_load_(); virtual void clear_playlist_() { } - + public: blargg_err_t remap_track_( int* track_io ) const; // need by Music_Emu private: // noncopyable Gme_File( const Gme_File& ); Gme_File& operator = ( const Gme_File& ); - + gme_type_t type_; int track_count_; int raw_track_count_; @@ -155,7 +159,8 @@ private: M3u_Playlist playlist; char playlist_warning [64]; blargg_vector file_data; // only if loaded into memory using default load - + blargg_vector tracks; // file start indexes of `file_data` + blargg_err_t load_m3u_( blargg_err_t ); blargg_err_t post_load( blargg_err_t err ); public: diff --git a/Frameworks/GME/gme/Gym_Emu.cpp b/Frameworks/GME/gme/Gym_Emu.cpp index 98be18410..2e12fb3eb 100644 --- a/Frameworks/GME/gme/Gym_Emu.cpp +++ b/Frameworks/GME/gme/Gym_Emu.cpp @@ -18,19 +18,19 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -double const min_tempo = 0.25; -double const oversample_factor = 5 / 3.0; -double const fm_gain = 3.0; +static double const min_tempo = 0.25; +static double const oversample_factor = 5 / 3.0; +static double const fm_gain = 3.0; -const long base_clock = 53700300; -const long clock_rate = base_clock / 15; +static const long base_clock = 53700300; +static const long clock_rate = base_clock / 15; Gym_Emu::Gym_Emu() { data = 0; pos = 0; set_type( gme_gym_type ); - + static const char* const names [] = { "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" }; @@ -59,20 +59,20 @@ static void get_gym_info( Gym_Emu::header_t const& h, long length, track_info_t* out->intro_length = length; // make it clear that track is no longer than length out->loop_length = 0; } - + // more stupidity where the field should have been left if ( strcmp( h.song, "Unknown Song" ) ) GME_COPY_FIELD( h, out, song ); - + if ( strcmp( h.game, "Unknown Game" ) ) GME_COPY_FIELD( h, out, game ); - + if ( strcmp( h.copyright, "Unknown Publisher" ) ) GME_COPY_FIELD( h, out, copyright ); - + if ( strcmp( h.dumper, "Unknown Person" ) ) GME_COPY_FIELD( h, out, dumper ); - + if ( strcmp( h.comment, "Header added by YMAMP" ) ) GME_COPY_FIELD( h, out, comment ); } @@ -94,12 +94,12 @@ static long gym_track_length( byte const* p, byte const* end ) case 0: time++; break; - + case 1: case 2: p += 2; break; - + case 3: p += 1; break; @@ -114,15 +114,15 @@ static blargg_err_t check_header( byte const* in, long size, int* data_offset = { if ( size < 4 ) return gme_wrong_file_type; - + if ( memcmp( in, "GYMX", 4 ) == 0 ) { if ( size < Gym_Emu::header_size + 1 ) return gme_wrong_file_type; - + if ( memcmp( ((Gym_Emu::header_t const*) in)->packed, "\0\0\0\0", 4 ) != 0 ) return "Packed GYM file not supported"; - + if ( data_offset ) *data_offset = Gym_Emu::header_size; } @@ -130,7 +130,7 @@ static blargg_err_t check_header( byte const* in, long size, int* data_offset = { return gme_wrong_file_type; } - + return 0; } @@ -139,9 +139,9 @@ struct Gym_File : Gme_Info_ byte const* file_begin; byte const* file_end; int data_offset; - + Gym_File() { set_type( gme_gym_type ); } - + blargg_err_t load_mem_( byte const* in, long size ) { file_begin = in; @@ -149,7 +149,7 @@ struct Gym_File : Gme_Info_ data_offset = 0; return check_header( in, size, &data_offset ); } - + blargg_err_t track_info_( track_info_t* out, int ) const { long length = gym_track_length( &file_begin [data_offset], file_end ); @@ -175,13 +175,13 @@ blargg_err_t Gym_Emu::set_sample_rate_( long sample_rate ) dac_synth.volume( 0.125 / 256 * fm_gain * gain() ); double factor = Dual_Resampler::setup( oversample_factor, 0.990, fm_gain * gain() ); fm_sample_rate = sample_rate * factor; - + RETURN_ERR( blip_buf.set_sample_rate( sample_rate, int (1000 / 60.0 / min_tempo) ) ); blip_buf.clock_rate( clock_rate ); - + RETURN_ERR( fm.set_rate( fm_sample_rate, base_clock / 7.0 ) ); RETURN_ERR( Dual_Resampler::reset( long (1.0 / 60 / min_tempo * sample_rate) ) ); - + return 0; } @@ -192,7 +192,7 @@ void Gym_Emu::set_tempo_( double t ) set_tempo( min_tempo ); return; } - + if ( blip_buf.sample_rate() ) { clocks_per_frame = long (clock_rate / 60 / tempo()); @@ -210,20 +210,20 @@ void Gym_Emu::mute_voices_( int mask ) blargg_err_t Gym_Emu::load_mem_( byte const* in, long size ) { - assert( offsetof (header_t,packed [4]) == header_size ); + blaarg_static_assert( offsetof (header_t,packed [4]) == header_size, "GYM Header layout incorrect!" ); int offset = 0; RETURN_ERR( check_header( in, size, &offset ) ); set_voice_count( 8 ); - + data = in + offset; data_end = in + size; loop_begin = 0; - + if ( offset ) header_ = *(header_t const*) in; else memset( &header_, 0, sizeof header_ ); - + return 0; } @@ -232,14 +232,14 @@ blargg_err_t Gym_Emu::load_mem_( byte const* in, long size ) blargg_err_t Gym_Emu::start_track_( int track ) { RETURN_ERR( Music_Emu::start_track_( track ) ); - + pos = data; loop_remain = get_le32( header_.loop_start ); - + prev_dac_count = 0; dac_enabled = false; dac_amp = -1; - + fm.reset(); apu.reset(); blip_buf.clear(); @@ -250,7 +250,7 @@ blargg_err_t Gym_Emu::start_track_( int track ) void Gym_Emu::run_dac( int dac_count ) { // Guess beginning and end of sample and adjust rate and buffer position accordingly. - + // count dac samples in next frame int next_dac_count = 0; const byte* p = this->pos; @@ -263,7 +263,7 @@ void Gym_Emu::run_dac( int dac_count ) if ( cmd == 1 && data == 0x2A ) next_dac_count++; } - + // detect beginning and end of sample int rate_count = dac_count; int start = 0; @@ -276,17 +276,17 @@ void Gym_Emu::run_dac( int dac_count ) { rate_count = prev_dac_count; } - + // Evenly space samples within buffer section being used blip_resampled_time_t period = blip_buf.resampled_duration( clocks_per_frame ) / rate_count; - + blip_resampled_time_t time = blip_buf.resampled_time( 0 ) + period * start + (period >> 1); - + int dac_amp = this->dac_amp; if ( dac_amp < 0 ) dac_amp = dac_buf [0]; - + for ( int i = 0; i < dac_count; i++ ) { int delta = dac_buf [i] - dac_amp; @@ -301,10 +301,10 @@ void Gym_Emu::parse_frame() { int dac_count = 0; const byte* pos = this->pos; - + if ( loop_remain && !--loop_remain ) loop_begin = pos; // find loop on first time through sequence - + int cmd; while ( (cmd = *pos++) != 0 ) { @@ -316,7 +316,7 @@ void Gym_Emu::parse_frame() { if ( data == 0x2B ) dac_enabled = (data2 & 0x80) != 0; - + fm.write0( data, data2 ); } else if ( dac_count < (int) sizeof dac_buf ) @@ -337,23 +337,23 @@ void Gym_Emu::parse_frame() { // to do: many GYM streams are full of errors, and error count should // reflect cases where music is really having problems - //log_error(); + //log_error(); --pos; // put data back } } - + // loop if ( pos >= data_end ) { check( pos == data_end ); - + if ( loop_begin ) pos = loop_begin; else set_track_ended(); } this->pos = pos; - + // dac if ( dac_count && !dac_muted ) run_dac( dac_count ); @@ -364,12 +364,12 @@ int Gym_Emu::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf { if ( !track_ended() ) parse_frame(); - + apu.end_frame( blip_time ); - + memset( buf, 0, sample_count * sizeof *buf ); fm.run( sample_count >> 1, buf ); - + return sample_count; } diff --git a/Frameworks/GME/gme/Gym_Emu.h b/Frameworks/GME/gme/Gym_Emu.h index 290f57f5c..8d74adb37 100644 --- a/Frameworks/GME/gme/Gym_Emu.h +++ b/Frameworks/GME/gme/Gym_Emu.h @@ -26,18 +26,18 @@ public: byte loop_start [4]; // in 1/60 seconds, 0 if not looped byte packed [4]; }; - + // Header for currently loaded file header_t const& header() const { return header_; } - + static gme_type_t static_type() { return gme_gym_type; } - + public: // deprecated using Music_Emu::load; blargg_err_t load( header_t const& h, Data_Reader& in ) // use Remaining_Reader { return load_remaining_( &h, sizeof h, in ); } - enum { gym_rate = 60 }; + enum { gym_rate = 60 }; long track_length() const; // use track_info() public: @@ -58,19 +58,19 @@ private: const byte* loop_begin; const byte* pos; const byte* data_end; - blargg_long loop_remain; // frames remaining until loop beginning has been located + int32_t loop_remain; // frames remaining until loop beginning has been located header_t header_; double fm_sample_rate; - blargg_long clocks_per_frame; + int32_t clocks_per_frame; void parse_frame(); - + // dac (pcm) int dac_amp; int prev_dac_count; bool dac_enabled; bool dac_muted; void run_dac( int ); - + // sound Blip_Buffer blip_buf; Ym2612_Emu fm; diff --git a/Frameworks/GME/gme/Hes_Apu.cpp b/Frameworks/GME/gme/Hes_Apu.cpp index 1df811592..39b3fd2d9 100644 --- a/Frameworks/GME/gme/Hes_Apu.cpp +++ b/Frameworks/GME/gme/Hes_Apu.cpp @@ -17,7 +17,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -bool const center_waves = true; // reduces asymmetry and clamping when starting notes +static bool const center_waves = true; // reduces asymmetry and clamping when starting notes Hes_Apu::Hes_Apu() { @@ -32,7 +32,7 @@ Hes_Apu::Hes_Apu() osc->chans [2] = 0; } while ( osc != oscs ); - + reset(); } @@ -40,7 +40,7 @@ void Hes_Apu::reset() { latch = 0; balance = 0xFF; - + Hes_Osc* osc = &oscs [osc_count]; do { @@ -59,7 +59,7 @@ void Hes_Apu::osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Bli oscs [index].chans [0] = center; oscs [index].chans [1] = left; oscs [index].chans [2] = right; - + Hes_Osc* osc = &oscs [osc_count]; do { @@ -75,7 +75,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) if ( osc_outputs_0 && control & 0x80 ) { int dac = this->dac; - + int const volume_0 = volume [0]; { int delta = dac * volume_0 - last_amp [0]; @@ -83,7 +83,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) synth_.offset( last_time, delta, osc_outputs_0 ); osc_outputs_0->set_modified(); } - + Blip_Buffer* const osc_outputs_1 = outputs [1]; int const volume_1 = volume [1]; if ( osc_outputs_1 ) @@ -93,7 +93,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) synth_.offset( last_time, delta, osc_outputs_1 ); osc_outputs_1->set_modified(); } - + blip_time_t time = last_time + delay; if ( time < end_time ) { @@ -106,10 +106,10 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) unsigned noise_lfsr = this->noise_lfsr; do { - int new_dac = 0x1F & -(noise_lfsr >> 1 & 1); + int new_dac = 0x1F & uMinus(noise_lfsr >> 1 & 1); // Implemented using "Galios configuration" // TODO: find correct LFSR algorithm - noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & -(noise_lfsr & 1)); + noise_lfsr = (noise_lfsr >> 1) ^ (0xE008 & uMinus(noise_lfsr & 1)); //noise_lfsr = (noise_lfsr >> 1) ^ (0x6000 & -(noise_lfsr & 1)); int delta = new_dac - dac; if ( delta ) @@ -122,7 +122,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) time += period; } while ( time < end_time ); - + this->noise_lfsr = noise_lfsr; assert( noise_lfsr ); } @@ -160,9 +160,8 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) //if ( !(volume_0 | volume_1) ) // debug_printf( "Used period 0\n" ); } - // maintain phase when silent - blargg_long count = (end_time - time + period - 1) / period; + int32_t count = (end_time - time + period - 1) / period; phase += count; // phase will be masked below time += count * period; } @@ -173,7 +172,7 @@ void Hes_Osc::run_until( synth_t& synth_, blip_time_t end_time ) if ( time < 0 ) time = 0; delay = time; - + this->dac = dac; last_amp [0] = dac * volume_0; last_amp [1] = dac * volume_1; @@ -195,18 +194,18 @@ void Hes_Apu::balance_changed( Hes_Osc& osc ) ENTRY( 0.594604 ),ENTRY( 0.707107 ),ENTRY( 0.840896 ),ENTRY( 1.000000 ), #undef ENTRY }; - + int vol = (osc.control & 0x1F) - 0x1E * 2; - + int left = vol + (osc.balance >> 3 & 0x1E) + (balance >> 3 & 0x1E); if ( left < 0 ) left = 0; - + int right = vol + (osc.balance << 1 & 0x1E) + (balance << 1 & 0x1E); if ( right < 0 ) right = 0; - + left = log_table [left ]; right = log_table [right]; - + // optimizing for the common case of being centered also allows easy // panning using Effects_Buffer osc.outputs [0] = osc.chans [0]; // center @@ -216,13 +215,13 @@ void Hes_Apu::balance_changed( Hes_Osc& osc ) osc.outputs [0] = osc.chans [1]; // left osc.outputs [1] = osc.chans [2]; // right } - + if ( center_waves ) { osc.last_amp [0] += (left - osc.volume [0]) * 16; osc.last_amp [1] += (right - osc.volume [1]) * 16; } - + osc.volume [0] = left; osc.volume [1] = right; } @@ -238,7 +237,7 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) if ( balance != data ) { balance = data; - + Hes_Osc* osc = &oscs [osc_count]; do { @@ -258,23 +257,23 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) case 0x802: osc.period = (osc.period & 0xF00) | data; break; - + case 0x803: osc.period = (osc.period & 0x0FF) | ((data & 0x0F) << 8); break; - + case 0x804: if ( osc.control & 0x40 & ~data ) osc.phase = 0; osc.control = data; balance_changed( osc ); break; - + case 0x805: osc.balance = data; balance_changed( osc ); break; - + case 0x806: data &= 0x1F; if ( !(osc.control & 0x40) ) @@ -287,15 +286,15 @@ void Hes_Apu::write_data( blip_time_t time, int addr, int data ) osc.dac = data; } break; - - case 0x807: - if ( &osc >= &oscs [4] ) - osc.noise = data; - break; - - case 0x809: - if ( !(data & 0x80) && (data & 0x03) != 0 ) - debug_printf( "HES LFO not supported\n" ); + + case 0x807: + if ( &osc >= &oscs [4] ) + osc.noise = data; + break; + + case 0x809: + if ( !(data & 0x80) && (data & 0x03) != 0 ) + debug_printf( "HES LFO not supported\n" ); } } } diff --git a/Frameworks/GME/gme/Hes_Apu.h b/Frameworks/GME/gme/Hes_Apu.h index 1efc0a064..4d9d1d158 100644 --- a/Frameworks/GME/gme/Hes_Apu.h +++ b/Frameworks/GME/gme/Hes_Apu.h @@ -19,15 +19,15 @@ struct Hes_Osc unsigned char balance; unsigned char dac; blip_time_t last_time; - + Blip_Buffer* outputs [2]; Blip_Buffer* chans [3]; unsigned noise_lfsr; unsigned char control; - - enum { amp_range = 0x8000 }; + + static const unsigned int amp_range = 0x8000; typedef Blip_Synth synth_t; - + void run_until( synth_t& synth, blip_time_t ); }; @@ -35,18 +35,18 @@ class Hes_Apu { public: void treble_eq( blip_eq_t const& ); void volume( double ); - - enum { osc_count = 6 }; + + static const int osc_count = 6; void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - + void reset(); - - enum { start_addr = 0x0800 }; - enum { end_addr = 0x0809 }; + + static const unsigned int start_addr = 0x0800; + static const unsigned int end_addr = 0x0809; void write_data( blip_time_t, int addr, int data ); - + void end_frame( blip_time_t ); - + public: Hes_Apu(); private: @@ -54,7 +54,7 @@ private: int latch; int balance; Hes_Osc::synth_t synth; - + void balance_changed( Hes_Osc& ); void recalc_chans(); }; diff --git a/Frameworks/GME/gme/Hes_Cpu.cpp b/Frameworks/GME/gme/Hes_Cpu.cpp index 0c5808413..985f033aa 100644 --- a/Frameworks/GME/gme/Hes_Cpu.cpp +++ b/Frameworks/GME/gme/Hes_Cpu.cpp @@ -37,14 +37,16 @@ int const ram_addr = 0x2000; #endif // status flags -BLARGG_MAYBE_UNUSED int const st_n = 0x80; -BLARGG_MAYBE_UNUSED int const st_v = 0x40; -BLARGG_MAYBE_UNUSED int const st_t = 0x20; -BLARGG_MAYBE_UNUSED int const st_b = 0x10; -BLARGG_MAYBE_UNUSED int const st_d = 0x08; -BLARGG_MAYBE_UNUSED int const st_i = 0x04; -BLARGG_MAYBE_UNUSED int const st_z = 0x02; -BLARGG_MAYBE_UNUSED int const st_c = 0x01; +enum { + st_n = 0x80, + st_v = 0x40, + st_t = 0x20, + st_b = 0x10, + st_d = 0x08, + st_i = 0x04, + st_z = 0x02, + st_c = 0x01 +}; void Hes_Cpu::reset() { @@ -94,7 +96,7 @@ bool Hes_Cpu::run( hes_time_t end_time ) state_t s = this->state_; this->state = &s; // even on x86, using s.time in place of s_time was slower - blargg_long s_time = s.time; + int32_t s_time = s.time; // registers uint_fast16_t pc = r.pc; @@ -994,7 +996,7 @@ possibly_out_of_time: hes_time_t new_time = end_time_; if ( !(status & st_i) && new_time > irq_time_ ) new_time = irq_time_; - blargg_long delta = s.base - new_time; + int32_t delta = s.base - new_time; s.base = new_time; s_time += delta; } @@ -1062,7 +1064,7 @@ possibly_out_of_time: status &= ~st_i; handle_cli: { this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; + int32_t delta = s.base - irq_time_; if ( delta <= 0 ) { if ( TIME < irq_time_ ) @@ -1093,7 +1095,7 @@ possibly_out_of_time: status |= st_i; handle_sei: { this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; + int32_t delta = s.base - end_time_; s.base = end_time_; s_time += delta; if ( s_time < 0 ) @@ -1260,7 +1262,7 @@ interrupt: status |= st_i; this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; + int32_t delta = s.base - end_time_; s.base = end_time_; s_time += delta; goto loop; diff --git a/Frameworks/GME/gme/Hes_Cpu.h b/Frameworks/GME/gme/Hes_Cpu.h index cec46fa9e..9f0b05555 100644 --- a/Frameworks/GME/gme/Hes_Cpu.h +++ b/Frameworks/GME/gme/Hes_Cpu.h @@ -6,23 +6,23 @@ #include "blargg_common.h" -typedef blargg_long hes_time_t; // clock cycle count +typedef int32_t hes_time_t; // clock cycle count typedef unsigned hes_addr_t; // 16-bit address enum { future_hes_time = INT_MAX / 2 + 1 }; class Hes_Cpu { public: void reset(); - + enum { page_size = 0x2000 }; enum { page_shift = 13 }; enum { page_count = 8 }; void set_mmr( int reg, int bank ); - + uint8_t const* get_code( hes_addr_t ); - + uint8_t ram [page_size]; - + // not kept updated during a call to run() struct registers_t { uint16_t pc; @@ -33,35 +33,35 @@ public: uint8_t sp; }; registers_t r; - + // page mapping registers uint8_t mmr [page_count + 1]; - + // Set end_time and run CPU from current time. Returns true if any illegal // instructions were encountered. bool run( hes_time_t end_time ); - + // Time of beginning of next instruction to be executed hes_time_t time() const { return state->time + state->base; } void set_time( hes_time_t t ) { state->time = t - state->base; } void adjust_time( int delta ) { state->time += delta; } - + hes_time_t irq_time() const { return irq_time_; } void set_irq_time( hes_time_t ); - + hes_time_t end_time() const { return end_time_; } void set_end_time( hes_time_t ); - + void end_frame( hes_time_t ); - + // Attempt to execute instruction here results in CPU advancing time to // lesser of irq_time() and end_time() (or end_time() if IRQs are // disabled) enum { idle_addr = 0x1FFF }; - + // Can read this many bytes past end of a page enum { cpu_padding = 8 }; - + public: Hes_Cpu() { state = &state_; } enum { irq_inhibit = 0x04 }; @@ -69,17 +69,17 @@ private: // noncopyable Hes_Cpu( const Hes_Cpu& ); Hes_Cpu& operator = ( const Hes_Cpu& ); - + struct state_t { uint8_t const* code_map [page_count + 1]; hes_time_t base; - blargg_long time; + int32_t time; }; state_t* state; // points to state_ or a local copy within run() state_t state_; hes_time_t irq_time_; hes_time_t end_time_; - + void set_code_page( int, void const* ); inline int update_end_time( hes_time_t end, hes_time_t irq ); }; diff --git a/Frameworks/GME/gme/Hes_Emu.cpp b/Frameworks/GME/gme/Hes_Emu.cpp index d0a4ffa12..b68cef6ab 100644 --- a/Frameworks/GME/gme/Hes_Emu.cpp +++ b/Frameworks/GME/gme/Hes_Emu.cpp @@ -19,12 +19,12 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -int const timer_mask = 0x04; -int const vdp_mask = 0x02; -int const i_flag_mask = 0x04; -int const unmapped = 0xFF; +static int const timer_mask = 0x04; +static int const vdp_mask = 0x02; +static int const i_flag_mask = 0x04; +static int const unmapped = 0xFF; -long const period_60hz = 262 * 455L; // scanlines * clocks per scanline +static long const period_60hz = 262 * 455L; // scanlines * clocks per scanline using std::min; using std::max; @@ -33,12 +33,12 @@ Hes_Emu::Hes_Emu() { timer.raw_load = 0; set_type( gme_hes_type ); - + static const char* const names [Hes_Apu::osc_count] = { "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Multi 1", "Multi 2" }; set_voice_names( names ); - + static int const types [Hes_Apu::osc_count] = { wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, mixed_type | 0, mixed_type | 1 @@ -65,19 +65,19 @@ static byte const* copy_field( byte const* in, char* out ) int len = 0x20; if ( in [0x1F] && !in [0x2F] ) len = 0x30; // fields are sometimes 16 bytes longer (ugh) - + // since text fields are where any data could be, detect non-text // and fields with data after zero byte terminator - + int i = 0; for ( i = 0; i < len && in [i]; i++ ) if ( ((in [i] + 1) & 0xFF) < ' ' + 1 ) // also treat 0xFF as non-text return 0; // non-ASCII found - + for ( ; i < len; i++ ) if ( in [i] ) return 0; // data after terminator - + Gme_File::copy_field_( out, (char const*) in, len ); in += len; } @@ -114,18 +114,18 @@ struct Hes_File : Gme_Info_ char unused [0x20]; byte fields [0x30 * 3]; } h; - + Hes_File() { set_type( gme_hes_type ); } - + blargg_err_t load_( Data_Reader& in ) { - assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20 ); + blaarg_static_assert( offsetof (header_t,fields) == Hes_Emu::header_size + 0x20, "HES header layout is incorrect!" ); blargg_err_t err = in.read( &h, sizeof h ); if ( err ) return (err == in.eof_error ? gme_wrong_file_type : err); return check_hes_header( &h ); } - + blargg_err_t track_info_( track_info_t* out, int ) const { copy_hes_fields( h.fields, out ); @@ -144,24 +144,24 @@ extern gme_type_t const gme_hes_type = &gme_hes_type_; blargg_err_t Hes_Emu::load_( Data_Reader& in ) { - assert( offsetof (header_t,unused [4]) == header_size ); + blaarg_static_assert( offsetof (header_t,unused [4]) == header_size, "HES header layout is incorrect!" ); RETURN_ERR( rom.load( in, header_size, &header_, unmapped ) ); - + RETURN_ERR( check_hes_header( header_.tag ) ); - + if ( header_.vers != 0 ) set_warning( "Unknown file version" ); - + if ( memcmp( header_.data_tag, "DATA", 4 ) ) set_warning( "Data header missing" ); - + if ( memcmp( header_.unused, "\0\0\0\0", 4 ) ) set_warning( "Unknown header data" ); - + // File spec supports multiple blocks, but I haven't found any, and // many files have bad sizes in the only block, so it's simpler to // just try to load the damn data as best as possible. - + long addr = get_le32( header_.addr ); long size = get_le32( header_.size ); long const rom_max = 0x100000; @@ -172,7 +172,7 @@ blargg_err_t Hes_Emu::load_( Data_Reader& in ) } if ( (unsigned long) (addr + size) > (unsigned long) rom_max ) set_warning( "Invalid size" ); - + if ( size != rom.file_size() ) { if ( size <= rom.file_size() - 4 && !memcmp( rom.begin() + size, "DATA", 4 ) ) @@ -182,13 +182,13 @@ blargg_err_t Hes_Emu::load_( Data_Reader& in ) else set_warning( "Missing file data" ); } - + rom.set_addr( addr ); - + set_voice_count( apu.osc_count ); - + apu.volume( gain() ); - + return setup_buffer( 7159091 ); } @@ -219,40 +219,40 @@ void Hes_Emu::set_tempo_( double t ) blargg_err_t Hes_Emu::start_track_( int track ) { RETURN_ERR( Classic_Emu::start_track_( track ) ); - + memset( ram, 0, sizeof ram ); // some HES music relies on zero fill memset( sgx, 0, sizeof sgx ); - + apu.reset(); cpu::reset(); - + for ( unsigned i = 0; i < sizeof header_.banks; i++ ) set_mmr( i, header_.banks [i] ); set_mmr( page_count, 0xFF ); // unmapped beyond end of address space - + irq.disables = timer_mask | vdp_mask; irq.timer = future_hes_time; irq.vdp = future_hes_time; - + timer.enabled = false; timer.raw_load= 0x80; timer.count = timer.load; timer.fired = false; timer.last_time = 0; - + vdp.latch = 0; vdp.control = 0; vdp.next_vbl = 0; - + ram [0x1FF] = (idle_addr - 1) >> 8; ram [0x1FE] = (idle_addr - 1) & 0xFF; r.sp = 0xFD; r.pc = get_le16( header_.init_addr ); r.a = track; - + recalc_timer_load(); last_frame_hook = 0; - + return 0; } @@ -265,7 +265,7 @@ void Hes_Emu::cpu_write_vdp( int addr, int data ) case 0: vdp.latch = data & 0x1F; break; - + case 2: if ( vdp.latch == 5 ) { @@ -280,7 +280,7 @@ void Hes_Emu::cpu_write_vdp( int addr, int data ) debug_printf( "VDP not supported: $%02X <- $%02X\n", vdp.latch, data ); } break; - + case 3: debug_printf( "VDP MSB not supported: $%02X <- $%02X\n", vdp.latch, data ); break; @@ -297,7 +297,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data ) apu.write_data( t, addr, data ); return; } - + hes_time_t time = this->time(); switch ( addr ) { @@ -306,7 +306,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data ) case 0x0003: cpu_write_vdp( addr, data ); return; - + case 0x0C00: { run_until( time ); timer.raw_load = (data & 0x7F) + 1; @@ -314,7 +314,7 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data ) timer.count = timer.load; break; } - + case 0x0C01: data &= 1; if ( timer.enabled == data ) @@ -324,21 +324,21 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data ) if ( data ) timer.count = timer.load; break; - + case 0x1402: run_until( time ); irq.disables = data; if ( (data & 0xF8) && (data & 0xF8) != 0xF8 ) // flag questionable values debug_printf( "Int mask: $%02X\n", data ); break; - + case 0x1403: run_until( time ); if ( timer.enabled ) timer.count = timer.load; timer.fired = false; break; - + #ifndef NDEBUG case 0x1000: // I/O port case 0x0402: // palette @@ -346,13 +346,13 @@ void Hes_Emu::cpu_write_( hes_addr_t addr, int data ) case 0x0404: case 0x0405: return; - + default: debug_printf( "unmapped write $%04X <- $%02X\n", addr, data ); return; #endif } - + irq_changed(); } @@ -369,22 +369,22 @@ int Hes_Emu::cpu_read_( hes_addr_t addr ) run_until( time ); irq_changed(); return 0x20; - + case 0x0002: case 0x0003: debug_printf( "VDP read not supported: %d\n", addr ); return 0; - + case 0x0C01: //return timer.enabled; // TODO: remove? case 0x0C00: run_until( time ); debug_printf( "Timer count read\n" ); return (unsigned) (timer.count - 1) / timer_base; - + case 0x1402: return irq.disables; - + case 0x1403: { int status = 0; @@ -392,18 +392,18 @@ int Hes_Emu::cpu_read_( hes_addr_t addr ) if ( irq.vdp <= time ) status |= vdp_mask; return status; } - + #ifndef NDEBUG case 0x1000: // I/O port case 0x180C: // CD-ROM case 0x180D: break; - + default: debug_printf( "unmapped read $%04X\n", addr ); #endif } - + return unmapped; } @@ -415,7 +415,7 @@ void Hes_Emu::run_until( hes_time_t present ) { while ( vdp.next_vbl < present ) vdp.next_vbl += play_period; - + hes_time_t elapsed = present - timer.last_time; if ( elapsed > 0 ) { @@ -432,25 +432,25 @@ void Hes_Emu::run_until( hes_time_t present ) void Hes_Emu::irq_changed() { hes_time_t present = time(); - + if ( irq.timer > present ) { irq.timer = future_hes_time; if ( timer.enabled && !timer.fired ) irq.timer = present + timer.count; } - + if ( irq.vdp > present ) { irq.vdp = future_hes_time; if ( vdp.control & 0x08 ) irq.vdp = vdp.next_vbl; } - + hes_time_t time = future_hes_time; if ( !(irq.disables & timer_mask) ) time = irq.timer; if ( !(irq.disables & vdp_mask) ) time = min( time, irq.vdp ); - + set_irq_time( time ); } @@ -458,11 +458,11 @@ int Hes_Emu::cpu_done() { check( time() >= end_time() || (!(r.status & i_flag_mask) && time() >= irq_time()) ); - + if ( !(r.status & i_flag_mask) ) { hes_time_t present = time(); - + if ( irq.timer <= present && !(irq.disables & timer_mask) ) { timer.fired = true; @@ -481,7 +481,7 @@ int Hes_Emu::cpu_done() #endif return 0x0A; } - + if ( irq.vdp <= present && !(irq.disables & vdp_mask) ) { // work around for bugs with music not acknowledging VDP @@ -498,7 +498,7 @@ int Hes_Emu::cpu_done() return 0; } -static void adjust_time( blargg_long& time, hes_time_t delta ) +static void adjust_time( int32_t& time, hes_time_t delta ) { if ( time < future_hes_time ) { @@ -511,15 +511,15 @@ static void adjust_time( blargg_long& time, hes_time_t delta ) blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) { blip_time_t const duration = duration_; // cache - + if ( cpu::run( duration ) ) set_warning( "Emulation error (illegal instruction)" ); - + check( time() >= duration ); //check( time() - duration < 20 ); // Txx instruction could cause going way over - + run_until( duration ); - + // end time frame timer.last_time -= duration; vdp.next_vbl -= duration; @@ -530,6 +530,6 @@ blargg_err_t Hes_Emu::run_clocks( blip_time_t& duration_, int ) ::adjust_time( irq.timer, duration ); ::adjust_time( irq.vdp, duration ); apu.end_frame( duration ); - + return 0; } diff --git a/Frameworks/GME/gme/Hes_Emu.h b/Frameworks/GME/gme/Hes_Emu.h index 08c1370d4..2b7a093b5 100644 --- a/Frameworks/GME/gme/Hes_Emu.h +++ b/Frameworks/GME/gme/Hes_Emu.h @@ -25,10 +25,10 @@ public: byte addr [4]; byte unused [4]; }; - + // Header for currently loaded file header_t const& header() const { return header_; } - + static gme_type_t static_type() { return gme_hes_type; } public: @@ -45,7 +45,7 @@ protected: void unload(); public: private: friend class Hes_Cpu; byte* write_pages [page_count + 1]; // 0 if unmapped or I/O space - + int cpu_read_( hes_addr_t ); int cpu_read( hes_addr_t ); void cpu_write_( hes_addr_t, int data ); @@ -59,34 +59,34 @@ private: hes_time_t play_period; hes_time_t last_frame_hook; int timer_base; - + struct { hes_time_t last_time; - blargg_long count; - blargg_long load; + int32_t count; + int32_t load; int raw_load; byte enabled; byte fired; } timer; - + struct { hes_time_t next_vbl; byte latch; byte control; } vdp; - + struct { hes_time_t timer; hes_time_t vdp; byte disables; } irq; - + void recalc_timer_load(); - + // large items Hes_Apu apu; byte sgx [3 * page_size + cpu_padding]; - + void irq_changed(); void run_until( hes_time_t ); }; diff --git a/Frameworks/GME/gme/Kss_Cpu.cpp b/Frameworks/GME/gme/Kss_Cpu.cpp index 2ae62276d..e88369026 100644 --- a/Frameworks/GME/gme/Kss_Cpu.cpp +++ b/Frameworks/GME/gme/Kss_Cpu.cpp @@ -41,15 +41,17 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" // flags, named with hex value for clarity -int const S80 = 0x80; -int const Z40 = 0x40; -int const F20 = 0x20; -int const H10 = 0x10; -int const F08 = 0x08; -int const V04 = 0x04; -int const P04 = 0x04; -int const N02 = 0x02; -int const C01 = 0x01; +enum { + S80 = 0x80, + Z40 = 0x40, + F20 = 0x20, + H10 = 0x10, + F08 = 0x08, + V04 = 0x04, + P04 = 0x04, + N02 = 0x02, + C01 = 0x01 +}; #define SZ28P( n ) szpc [n] #define SZ28PC( n ) szpc [n] @@ -62,7 +64,7 @@ int const C01 = 0x01; Kss_Cpu::Kss_Cpu() { state = &state_; - + for ( int i = 0x100; --i >= 0; ) { int even = 1; @@ -78,7 +80,7 @@ Kss_Cpu::Kss_Cpu() inline void Kss_Cpu::set_page( int i, void* write, void const* read ) { - blargg_long offset = KSS_CPU_PAGE_OFFSET( i * (blargg_long) page_size ); + int32_t offset = KSS_CPU_PAGE_OFFSET( i * (int32_t) page_size ); state->write [i] = (byte *) write - offset; state->read [i] = (byte const*) read - offset; } @@ -90,23 +92,23 @@ void Kss_Cpu::reset( void* unmapped_write, void const* unmapped_read ) state_.time = 0; state_.base = 0; end_time_ = 0; - + for ( int i = 0; i < page_count + 1; i++ ) set_page( i, unmapped_write, unmapped_read ); - + memset( &r, 0, sizeof r ); } -void Kss_Cpu::map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ) +void Kss_Cpu::map_mem( unsigned addr, uint32_t size, void* write, void const* read ) { // address range must begin and end on page boundaries require( addr % page_size == 0 ); require( size % page_size == 0 ); - + unsigned first_page = addr / page_size; for ( unsigned i = size / page_size; i--; ) { - blargg_long offset = i * (blargg_long) page_size; + int32_t offset = i * (int32_t) page_size; set_page( first_page + i, (byte*) write + offset, (byte const*) read + offset ); } } @@ -123,9 +125,9 @@ void Kss_Cpu::map_mem( unsigned addr, blargg_ulong size, void* write, void const #define OUT( addr, data ) CPU_OUT( this, addr, data, TIME ) #if BLARGG_BIG_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [n]) + #define R8( n, offset ) ((r8_ - offset) [n]) #elif BLARGG_LITTLE_ENDIAN - #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) + #define R8( n, offset ) ((r8_ - offset) [(n) ^ 1]) #else #error "Byte order of CPU must be known" #endif @@ -168,7 +170,7 @@ bool Kss_Cpu::run( cpu_time_t end_time ) state_t s = this->state_; this->state = &s; bool warning = false; - + union { regs_t rg; pairs_t rp; @@ -176,35 +178,35 @@ bool Kss_Cpu::run( cpu_time_t end_time ) uint16_t r16_ [4]; }; rg = this->r.b; - + cpu_time_t s_time = s.time; uint_fast32_t pc = r.pc; uint_fast32_t sp = r.sp; uint_fast32_t ix = r.ix; // TODO: keep in memory for direct access? uint_fast32_t iy = r.iy; int flags = r.b.flags; - + goto loop; jr_not_taken: s_time -= 5; goto loop; call_not_taken: - s_time -= 7; + s_time -= 7; jp_not_taken: pc += 2; loop: - + check( (unsigned long) pc < 0x10000 ); check( (unsigned long) sp < 0x10000 ); check( (unsigned) flags < 0x100 ); check( (unsigned) ix < 0x10000 ); check( (unsigned) iy < 0x10000 ); - + uint8_t const* instr = s.read [pc >> page_shift]; #define GET_ADDR() GET_LE16( instr ) - + uint_fast8_t opcode; - + // TODO: eliminate this special case #if BLARGG_NONPORTABLE opcode = instr [pc]; @@ -215,7 +217,7 @@ loop: opcode = *instr++; pc++; #endif - + static byte const base_timing [0x100] = { // 0 1 2 3 4 5 6 7 8 9 A B C D E F 4,10, 7, 6, 4, 4, 7, 4, 4,11, 7, 6, 4, 4, 7, 4, // 0 @@ -235,22 +237,22 @@ loop: 11,10,10,19,17,11, 7,11,11, 4,10, 4,17, 8, 7,11, // E 11,10,10, 4,17,11, 7,11,11, 6,10, 4,17, 8, 7,11, // F }; - + uint_fast16_t data; data = base_timing [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; almost_out_of_time: - + data = READ_PROG( pc ); - + #ifdef Z80_CPU_LOG_H //log_opcode( opcode, READ_PROG( pc ) ); z80_log_regs( rg.a, rp.bc, rp.de, rp.hl, sp, ix, iy ); z80_cpu_log( "new", pc - 1, opcode, READ_PROG( pc ), READ_PROG( pc + 1 ), READ_PROG( pc + 2 ) ); #endif - + switch ( opcode ) { possibly_out_of_time: @@ -264,40 +266,40 @@ possibly_out_of_time: case 0x00: // NOP CASE7( 40, 49, 52, 5B, 64, 6D, 7F ): // LD B,B etc. goto loop; - + case 0x08:{// EX AF,AF' int temp = r.alt.b.a; r.alt.b.a = rg.a; rg.a = temp; - + temp = r.alt.b.flags; r.alt.b.flags = flags; flags = temp; goto loop; } - + case 0xD3: // OUT (imm),A pc++; OUT( data + rg.a * 0x100, rg.a ); goto loop; - + case 0x2E: // LD L,imm pc++; rg.l = data; goto loop; - + case 0x3E: // LD A,imm pc++; rg.a = data; goto loop; - + case 0x3A:{// LD A,(addr) uint_fast16_t addr = GET_ADDR(); pc += 2; rg.a = READ( addr ); goto loop; } - + // Conditional #define ZERO (flags & Z40) @@ -315,7 +317,7 @@ possibly_out_of_time: pc = uint16_t (pc + offset);\ goto loop;\ } - + case 0x20: JR( !ZERO ) // JR NZ,disp case 0x28: JR( ZERO ) // JR Z,disp case 0x30: JR( !CARRY ) // JR NC,disp @@ -327,10 +329,10 @@ possibly_out_of_time: rg.b = temp; JR( temp ) } - + // JP #define JP( cond ) if ( !(cond) ) goto jp_not_taken; pc = GET_ADDR(); goto loop; - + case 0xC2: JP( !ZERO ) // JP NZ,addr case 0xCA: JP( ZERO ) // JP Z,addr case 0xD2: JP( !CARRY ) // JP NC,addr @@ -339,18 +341,18 @@ possibly_out_of_time: case 0xEA: JP( EVEN ) // JP PE,addr case 0xF2: JP( !MINUS ) // JP P,addr case 0xFA: JP( MINUS ) // JP M,addr - + case 0xC3: // JP addr pc = GET_ADDR(); goto loop; - + case 0xE9: // JP HL pc = rp.hl; goto loop; // RET #define RET( cond ) if ( cond ) goto ret_taken; s_time -= 6; goto loop; - + case 0xC0: RET( !ZERO ) // RET NZ case 0xC8: RET( ZERO ) // RET Z case 0xD0: RET( !CARRY ) // RET NC @@ -359,13 +361,13 @@ possibly_out_of_time: case 0xE8: RET( EVEN ) // RET PE case 0xF0: RET( !MINUS ) // RET P case 0xF8: RET( MINUS ) // RET M - + case 0xC9: // RET ret_taken: pc = READ_WORD( sp ); sp = uint16_t (sp + 2); goto loop; - + // CALL #define CALL( cond ) if ( cond ) goto call_taken; goto call_not_taken; @@ -377,7 +379,7 @@ possibly_out_of_time: case 0xEC: CALL( EVEN ) // CALL PE,addr case 0xF4: CALL( !MINUS ) // CALL P,addr case 0xFC: CALL( MINUS ) // CALL M,addr - + case 0xCD:{// CALL addr call_taken: uint_fast16_t addr = pc + 2; @@ -386,7 +388,7 @@ possibly_out_of_time: WRITE_WORD( sp, addr ); goto loop; } - + case 0xFF: // RST if ( pc > idle_addr ) goto hit_idle_addr; @@ -400,7 +402,7 @@ possibly_out_of_time: case 0xF5: // PUSH AF data = rg.a * 0x100u + flags; goto push_data; - + case 0xC5: // PUSH BC case 0xD5: // PUSH DE case 0xE5: // PUSH HL @@ -409,20 +411,20 @@ possibly_out_of_time: sp = uint16_t (sp - 2); WRITE_WORD( sp, data ); goto loop; - + case 0xF1: // POP AF flags = READ( sp ); rg.a = READ( sp + 1 ); sp = uint16_t (sp + 2); goto loop; - + case 0xC1: // POP BC case 0xD1: // POP DE case 0xE1: // POP HL R16( opcode, 4, 0xC1 ) = READ_WORD( sp ); sp = uint16_t (sp + 2); goto loop; - + // ADC/ADD/SBC/SUB case 0x96: // SUB (HL) case 0x86: // ADD (HL) @@ -431,7 +433,7 @@ possibly_out_of_time: case 0x8E: // ADC (HL) data = READ( rp.hl ); goto adc_data; - + case 0xD6: // SUB A,imm case 0xC6: // ADD imm flags &= ~C01; // FALLTHRU @@ -439,7 +441,7 @@ possibly_out_of_time: case 0xCE: // ADC imm pc++; goto adc_data; - + CASE7( 90, 91, 92, 93, 94, 95, 97 ): // SUB r CASE7( 80, 81, 82, 83, 84, 85, 87 ): // ADD r flags &= ~C01; @@ -465,11 +467,11 @@ possibly_out_of_time: case 0xBE: // CP (HL) data = READ( rp.hl ); goto cp_data; - + case 0xFE: // CP imm pc++; goto cp_data; - + CASE7( B8, B9, BA, BB, BC, BD, BF ): // CP r data = R8( opcode, 0xB8 ); cp_data: { @@ -483,19 +485,18 @@ possibly_out_of_time: flags |= Z40; goto loop; } - + // ADD HL,rp - case 0x39: // ADD HL,SP data = sp; goto add_hl_data; - + case 0x09: // ADD HL,BC case 0x19: // ADD HL,DE case 0x29: // ADD HL,HL data = R16( opcode, 4, 0x09 ); add_hl_data: { - blargg_ulong sum = rp.hl + data; + uint32_t sum = rp.hl + data; data ^= rp.hl; rp.hl = sum; flags = (flags & (S80 | Z40 | V04)) | @@ -504,21 +505,21 @@ possibly_out_of_time: ((data ^ sum) >> 8 & H10); goto loop; } - + case 0x27:{// DAA int a = rg.a; if ( a > 0x99 ) flags |= C01; - + int adjust = 0x60 & -(flags & C01); - + if ( flags & H10 || (a & 0x0F) > 9 ) adjust |= 0x06; - + if ( flags & N02 ) adjust = -adjust; a += adjust; - + flags = (flags & (C01 | N02)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); @@ -530,26 +531,26 @@ possibly_out_of_time: // more optimized, but probably not worth the obscurity int f = (rg.a + (0xFF - 0x99)) >> 8 | flags; // (a > 0x99 ? C01 : 0) | flags int adjust = 0x60 & -(f & C01); // f & C01 ? 0x60 : 0 - + if ( (((rg.a + (0x0F - 9)) ^ rg.a) | f) & H10 ) // flags & H10 || (rg.a & 0x0F) > 9 adjust |= 0x06; - + if ( f & N02 ) adjust = -adjust; int a = rg.a + adjust; - + flags = (f & (N02 | C01)) | ((rg.a ^ a) & H10) | SZ28P( (uint8_t) a ); rg.a = a; goto loop; } */ - + // INC/DEC case 0x34: // INC (HL) data = READ( rp.hl ) + 1; WRITE( rp.hl, data ); goto inc_set_flags; - + CASE7( 04, 0C, 14, 1C, 24, 2C, 3C ): // INC r data = ++R8( opcode >> 3, 0 ); inc_set_flags: @@ -560,12 +561,12 @@ possibly_out_of_time: goto loop; flags |= V04; goto loop; - + case 0x35: // DEC (HL) data = READ( rp.hl ) - 1; WRITE( rp.hl, data ); goto dec_set_flags; - + CASE7( 05, 0D, 15, 1D, 25, 2D, 3D ): // DEC r data = --R8( opcode >> 3, 0 ); dec_set_flags: @@ -582,46 +583,46 @@ possibly_out_of_time: case 0x23: // INC HL R16( opcode, 4, 0x03 )++; goto loop; - + case 0x33: // INC SP sp = uint16_t (sp + 1); goto loop; - + case 0x0B: // DEC BC case 0x1B: // DEC DE case 0x2B: // DEC HL R16( opcode, 4, 0x0B )--; goto loop; - + case 0x3B: // DEC SP sp = uint16_t (sp - 1); goto loop; - + // AND case 0xA6: // AND (HL) data = READ( rp.hl ); goto and_data; - + case 0xE6: // AND imm pc++; goto and_data; - + CASE7( A0, A1, A2, A3, A4, A5, A7 ): // AND r data = R8( opcode, 0xA0 ); and_data: rg.a &= data; flags = SZ28P( rg.a ) | H10; goto loop; - + // OR case 0xB6: // OR (HL) data = READ( rp.hl ); goto or_data; - + case 0xF6: // OR imm pc++; goto or_data; - + CASE7( B0, B1, B2, B3, B4, B5, B7 ): // OR r data = R8( opcode, 0xB0 ); or_data: @@ -633,11 +634,11 @@ possibly_out_of_time: case 0xAE: // XOR (HL) data = READ( rp.hl ); goto xor_data; - + case 0xEE: // XOR imm pc++; goto xor_data; - + CASE7( A8, A9, AA, AB, AC, AD, AF ): // XOR r data = R8( opcode, 0xA8 ); xor_data: @@ -649,7 +650,7 @@ possibly_out_of_time: CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (HL),r WRITE( rp.hl, R8( opcode, 0x70 ) ); goto loop; - + CASE6( 41, 42, 43, 44, 45, 47 ): // LD B,r CASE6( 48, 4A, 4B, 4C, 4D, 4F ): // LD C,r CASE6( 50, 51, 53, 54, 55, 57 ): // LD D,r @@ -659,70 +660,70 @@ possibly_out_of_time: CASE6( 78, 79, 7A, 7B, 7C, 7D ): // LD A,r R8( opcode >> 3 & 7, 0 ) = R8( opcode & 7, 0 ); goto loop; - + CASE5( 06, 0E, 16, 1E, 26 ): // LD r,imm R8( opcode >> 3, 0 ) = data; pc++; goto loop; - + case 0x36: // LD (HL),imm pc++; WRITE( rp.hl, data ); goto loop; - + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(HL) R8( opcode >> 3, 8 ) = READ( rp.hl ); goto loop; - + case 0x01: // LD rp,imm case 0x11: case 0x21: R16( opcode, 4, 0x01 ) = GET_ADDR(); pc += 2; goto loop; - + case 0x31: // LD sp,imm sp = GET_ADDR(); pc += 2; goto loop; - + case 0x2A:{// LD HL,(addr) uint_fast16_t addr = GET_ADDR(); pc += 2; rp.hl = READ_WORD( addr ); goto loop; } - + case 0x32:{// LD (addr),A uint_fast16_t addr = GET_ADDR(); pc += 2; WRITE( addr, rg.a ); goto loop; } - + case 0x22:{// LD (addr),HL uint_fast16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, rp.hl ); goto loop; } - + case 0x02: // LD (BC),A case 0x12: // LD (DE),A WRITE( R16( opcode, 4, 0x02 ), rg.a ); goto loop; - + case 0x0A: // LD A,(BC) case 0x1A: // LD A,(DE) rg.a = READ( R16( opcode, 4, 0x0A ) ); goto loop; - + case 0xF9: // LD SP,HL sp = rp.hl; goto loop; - + // Rotate - + case 0x07:{// RLCA uint_fast16_t temp = rg.a; temp = (temp << 1) | (temp >> 7); @@ -731,7 +732,7 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x0F:{// RRCA uint_fast16_t temp = rg.a; flags = (flags & (S80 | Z40 | P04)) | @@ -741,16 +742,16 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x17:{// RLA - blargg_ulong temp = (rg.a << 1) | (flags & C01); + uint32_t temp = (rg.a << 1) | (flags & C01); flags = (flags & (S80 | Z40 | P04)) | (temp & (F20 | F08)) | (temp >> 8); rg.a = (uint8_t)temp; goto loop; } - + case 0x1F:{// RRA uint_fast16_t temp = (flags << 7) | (rg.a >> 1); flags = (flags & (S80 | Z40 | P04)) | @@ -759,7 +760,7 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + // Misc case 0x2F:{// CPL uint_fast16_t temp = ~rg.a; @@ -769,19 +770,19 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x3F:{// CCF flags = ((flags & (S80 | Z40 | P04 | C01)) ^ C01) | (flags << 4 & H10) | (rg.a & (F20 | F08)); goto loop; } - + case 0x37: // SCF flags = (flags & (S80 | Z40 | P04)) | C01 | (rg.a & (F20 | F08)); goto loop; - + case 0xDB: // IN A,(imm) pc++; rg.a = IN( data + rg.a * 0x100 ); @@ -793,43 +794,43 @@ possibly_out_of_time: rp.hl = temp; goto loop; } - + case 0xEB:{// EX DE,HL uint_fast16_t temp = rp.hl; rp.hl = rp.de; rp.de = temp; goto loop; } - + case 0xD9:{// EXX DE,HL uint_fast16_t temp = r.alt.w.bc; r.alt.w.bc = rp.bc; rp.bc = temp; - + temp = r.alt.w.de; r.alt.w.de = rp.de; rp.de = temp; - + temp = r.alt.w.hl; r.alt.w.hl = rp.hl; rp.hl = temp; goto loop; } - + case 0xF3: // DI r.iff1 = 0; r.iff2 = 0; goto loop; - + case 0xFB: // EI r.iff1 = 1; r.iff2 = 1; // TODO: delayed effect goto loop; - + case 0x76: // HALT goto halt; - + //////////////////////////////////////// CB prefix { case 0xCB: @@ -839,9 +840,9 @@ possibly_out_of_time: pc++; switch ( data ) { - + // Rotate left - + #define RLC( read, write ) {\ uint_fast8_t result = read;\ result = uint8_t (result << 1) | (result >> 7);\ @@ -849,67 +850,67 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x06: // RLC (HL) s_time += 7; data = rp.hl; rlc_data_addr: RLC( READ( data ), WRITE( data, result ) ) - + CASE7( 00, 01, 02, 03, 04, 05, 07 ):{// RLC r uint8_t& reg = R8( data, 0 ); RLC( reg, reg = result ) } - + #define RL( read, write ) {\ uint_fast16_t result = (read << 1) | (flags & C01);\ flags = SZ28PC( result );\ write;\ goto loop;\ } - + case 0x16: // RL (HL) s_time += 7; data = rp.hl; rl_data_addr: RL( READ( data ), WRITE( data, result ) ) - + CASE7( 10, 11, 12, 13, 14, 15, 17 ):{// RL r uint8_t& reg = R8( data, 0x10 ); RL( reg, reg = result ) } - + #define SLA( read, add, write ) {\ uint_fast16_t result = (read << 1) | add;\ flags = SZ28PC( result );\ write;\ goto loop;\ } - + case 0x26: // SLA (HL) s_time += 7; data = rp.hl; sla_data_addr: SLA( READ( data ), 0, WRITE( data, result ) ) - + CASE7( 20, 21, 22, 23, 24, 25, 27 ):{// SLA r uint8_t& reg = R8( data, 0x20 ); SLA( reg, 0, reg = result ) } - + case 0x36: // SLL (HL) s_time += 7; data = rp.hl; sll_data_addr: SLA( READ( data ), 1, WRITE( data, result ) ) - + CASE7( 30, 31, 32, 33, 34, 35, 37 ):{// SLL r uint8_t& reg = R8( data, 0x30 ); SLA( reg, 1, reg = result ) } - + // Rotate right - + #define RRC( read, write ) {\ uint_fast8_t result = read;\ flags = result & C01;\ @@ -918,18 +919,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x0E: // RRC (HL) s_time += 7; data = rp.hl; rrc_data_addr: RRC( READ( data ), WRITE( data, result ) ) - + CASE7( 08, 09, 0A, 0B, 0C, 0D, 0F ):{// RRC r uint8_t& reg = R8( data, 0x08 ); RRC( reg, reg = result ) } - + #define RR( read, write ) {\ uint_fast8_t result = read;\ uint_fast8_t temp = result & C01;\ @@ -938,18 +939,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x1E: // RR (HL) s_time += 7; data = rp.hl; rr_data_addr: RR( READ( data ), WRITE( data, result ) ) - + CASE7( 18, 19, 1A, 1B, 1C, 1D, 1F ):{// RR r uint8_t& reg = R8( data, 0x18 ); RR( reg, reg = result ) } - + #define SRA( read, write ) {\ uint_fast8_t result = read;\ flags = result & C01;\ @@ -958,18 +959,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x2E: // SRA (HL) data = rp.hl; s_time += 7; sra_data_addr: SRA( READ( data ), WRITE( data, result ) ) - + CASE7( 28, 29, 2A, 2B, 2C, 2D, 2F ):{// SRA r uint8_t& reg = R8( data, 0x28 ); SRA( reg, reg = result ) } - + #define SRL( read, write ) {\ uint_fast8_t result = read;\ flags = result & C01;\ @@ -978,18 +979,18 @@ possibly_out_of_time: write;\ goto loop;\ } - + case 0x3E: // SRL (HL) s_time += 7; data = rp.hl; srl_data_addr: SRL( READ( data ), WRITE( data, result ) ) - + CASE7( 38, 39, 3A, 3B, 3C, 3D, 3F ):{// SRL r uint8_t& reg = R8( data, 0x38 ); SRL( reg, reg = result ) } - + // BIT { unsigned temp; @@ -1014,7 +1015,7 @@ possibly_out_of_time: ((masked - 1) >> 8 & (Z40 | P04)); goto loop; } - + // SET/RES CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(HL) CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(HL) @@ -1027,7 +1028,7 @@ possibly_out_of_time: WRITE( rp.hl, temp ); goto loop; } - + CASE7( C0, C1, C2, C3, C4, C5, C7 ): // SET 0,r CASE7( C8, C9, CA, CB, CC, CD, CF ): // SET 1,r CASE7( D0, D1, D2, D3, D4, D5, D7 ): // SET 2,r @@ -1038,7 +1039,7 @@ possibly_out_of_time: CASE7( F8, F9, FA, FB, FC, FD, FF ): // SET 7,r R8( data & 7, 0 ) |= 1 << (data >> 3 & 7); goto loop; - + CASE7( 80, 81, 82, 83, 84, 85, 87 ): // RES 0,r CASE7( 88, 89, 8A, 8B, 8C, 8D, 8F ): // RES 1,r CASE7( 90, 91, 92, 93, 94, 95, 97 ): // RES 2,r @@ -1064,7 +1065,7 @@ possibly_out_of_time: switch ( data ) { { - blargg_ulong temp; + uint32_t temp; case 0x72: // SBC HL,SP case 0x7A: // ADC HL,SP temp = sp; @@ -1076,10 +1077,10 @@ possibly_out_of_time: case 0x5A: // ADC HL,DE case 0x6A: // ADC HL,HL temp = R16( data >> 3 & 6, 1, 0 ); - blargg_ulong sum = temp + (flags & C01); + uint32_t sum = temp + (flags & C01); flags = ~data >> 2 & N02; if ( flags ) - sum = -sum; + sum = uMinus(sum); sum += rp.hl; temp ^= rp.hl; temp ^= sum; @@ -1093,20 +1094,20 @@ possibly_out_of_time: flags |= Z40; goto loop; } - + CASE8( 40, 48, 50, 58, 60, 68, 70, 78 ):{// IN r,(C) int temp = IN( rp.bc ); R8( data >> 3, 8 ) = temp; flags = (flags & C01) | SZ28P( temp ); goto loop; } - + case 0x71: // OUT (C),0 rg.flags = 0; CASE7( 41, 49, 51, 59, 61, 69, 79 ): // OUT (C),r OUT( rp.bc, R8( data >> 3, 8 ) ); goto loop; - + { unsigned temp; case 0x73: // LD (ADDR),SP @@ -1120,7 +1121,7 @@ possibly_out_of_time: WRITE_WORD( addr, temp ); goto loop; } - + case 0x4B: // LD BC,(ADDR) case 0x5B:{// LD DE,(ADDR) uint_fast16_t addr = GET_ADDR(); @@ -1128,14 +1129,14 @@ possibly_out_of_time: R16( data, 4, 0x4B ) = READ_WORD( addr ); goto loop; } - + case 0x7B:{// LD SP,(ADDR) uint_fast16_t addr = GET_ADDR(); pc += 2; sp = READ_WORD( addr ); goto loop; } - + case 0x67:{// RRD uint_fast8_t temp = READ( rp.hl ); WRITE( rp.hl, (rg.a << 4) | (temp >> 4) ); @@ -1144,7 +1145,7 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + case 0x6F:{// RLD uint_fast8_t temp = READ( rp.hl ); WRITE( rp.hl, (temp << 4) | (rg.a & 0x0F) ); @@ -1153,14 +1154,14 @@ possibly_out_of_time: rg.a = temp; goto loop; } - + CASE8( 44, 4C, 54, 5C, 64, 6C, 74, 7C ): // NEG opcode = 0x10; // flag to do SBC instead of ADC flags &= ~C01; data = rg.a; rg.a = 0; goto adc_data; - + { int inc; case 0xA9: // CPD @@ -1173,27 +1174,27 @@ possibly_out_of_time: uint_fast16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); - + int result = rg.a - temp; flags = (flags & C01) | N02 | ((((temp ^ rg.a) & H10) ^ result) & (S80 | H10)); - + if ( !(uint8_t) result ) flags |= Z40; result -= (flags & H10) >> 4; flags |= result & F08; flags |= result << 4 & F20; if ( !--rp.bc ) goto loop; - + flags |= V04; if ( flags & Z40 || data < 0xB0 ) goto loop; - + pc -= 2; s_time += 5; goto loop; } - + { int inc; case 0xA8: // LDD @@ -1206,26 +1207,26 @@ possibly_out_of_time: uint_fast16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); - + addr = rp.de; rp.de = addr + inc; WRITE( addr, temp ); - + temp += rg.a; flags = (flags & (S80 | Z40 | C01)) | (temp & F08) | (temp << 4 & F20); if ( !--rp.bc ) goto loop; - + flags |= V04; if ( data < 0xB0 ) goto loop; - + pc -= 2; s_time += 5; goto loop; } - + { int inc; case 0xAB: // OUTD @@ -1238,7 +1239,7 @@ possibly_out_of_time: uint_fast16_t addr = rp.hl; rp.hl = addr + inc; int temp = READ( addr ); - + int b = --rg.b; flags = (temp >> 6 & N02) | SZ28( b ); if ( b && data >= 0xB0 ) @@ -1246,11 +1247,11 @@ possibly_out_of_time: pc -= 2; s_time += 5; } - + OUT( rp.bc, temp ); goto loop; } - + { int inc; case 0xAA: // IND @@ -1260,12 +1261,12 @@ possibly_out_of_time: case 0xA2: // INI case 0xB2: // INIR inc = +1; - + uint_fast16_t addr = rp.hl; rp.hl = addr + inc; - + int temp = IN( rp.bc ); - + int b = --rg.b; flags = (temp >> 6 & N02) | SZ28( b ); if ( b && data >= 0xB0 ) @@ -1273,25 +1274,25 @@ possibly_out_of_time: pc -= 2; s_time += 5; } - + WRITE( addr, temp ); goto loop; } - + case 0x47: // LD I,A r.i = rg.a; goto loop; - + case 0x4F: // LD R,A SET_R( rg.a ); debug_printf( "LD R,A not supported\n" ); warning = true; goto loop; - + case 0x57: // LD A,I rg.a = r.i; goto ld_ai_common; - + case 0x5F: // LD A,R rg.a = GET_R(); debug_printf( "LD A,R not supported\n" ); @@ -1299,25 +1300,25 @@ possibly_out_of_time: ld_ai_common: flags = (flags & C01) | SZ28( rg.a ) | (r.iff2 << 2 & V04); goto loop; - + CASE8( 45, 4D, 55, 5D, 65, 6D, 75, 7D ): // RETI/RETN r.iff1 = r.iff2; goto ret_taken; - + case 0x46: case 0x4E: case 0x66: case 0x6E: // IM 0 r.im = 0; goto loop; - + case 0x56: case 0x76: // IM 1 r.im = 1; goto loop; - + case 0x5E: case 0x7E: // IM 2 r.im = 2; goto loop; - + default: - debug_printf( "Opcode $ED $%02X not supported\n", data ); + debug_printf( "Opcode $ED $%02X not supported\n", (unsigned int)data ); warning = true; goto loop; } @@ -1341,11 +1342,10 @@ possibly_out_of_time: // TODO: more efficient way of avoid negative address // TODO: avoid using this as argument to READ() since it is evaluated twice #define IXY_DISP( ixy, disp ) uint16_t ((ixy) + (disp)) - + #define SET_IXY( in ) if ( opcode == 0xDD ) ix = in; else iy = in; - + // ADD/ADC/SUB/SBC - case 0x96: // SUB (IXY+disp) case 0x86: // ADD (IXY+disp) flags &= ~C01; // FALLTHRU @@ -1355,7 +1355,7 @@ possibly_out_of_time: opcode = data; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto adc_data; - + case 0x94: // SUB HXY case 0x84: // ADD HXY flags &= ~C01; // FALLTHRU @@ -1364,7 +1364,7 @@ possibly_out_of_time: opcode = data; data = ixy >> 8; goto adc_data; - + case 0x95: // SUB LXY case 0x85: // ADD LXY flags &= ~C01; // FALLTHRU @@ -1373,22 +1373,22 @@ possibly_out_of_time: opcode = data; data = (uint8_t) ixy; goto adc_data; - + { unsigned temp; case 0x39: // ADD IXY,SP temp = sp; goto add_ixy_data; - + case 0x29: // ADD IXY,HL temp = ixy; goto add_ixy_data; - + case 0x09: // ADD IXY,BC case 0x19: // ADD IXY,DE temp = R16( data, 4, 0x09 ); - add_ixy_data: { - blargg_ulong sum = ixy + temp; + add_ixy_data: { + uint32_t sum = ixy + temp; temp ^= ixy; ixy = (uint16_t) sum; flags = (flags & (S80 | Z40 | V04)) | @@ -1396,65 +1396,65 @@ possibly_out_of_time: (sum >> 8 & (F20 | F08)) | ((temp ^ sum) >> 8 & H10); goto set_ixy; + } } - } - + // AND case 0xA6: // AND (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto and_data; - + case 0xA4: // AND HXY data = ixy >> 8; goto and_data; - + case 0xA5: // AND LXY data = (uint8_t) ixy; goto and_data; - + // OR case 0xB6: // OR (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto or_data; - + case 0xB4: // OR HXY data = ixy >> 8; goto or_data; - + case 0xB5: // OR LXY data = (uint8_t) ixy; goto or_data; - + // XOR case 0xAE: // XOR (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto xor_data; - + case 0xAC: // XOR HXY data = ixy >> 8; goto xor_data; - + case 0xAD: // XOR LXY data = (uint8_t) ixy; goto xor_data; - + // CP case 0xBE: // CP (IXY+disp) pc++; data = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto cp_data; - + case 0xBC: // CP HXY data = ixy >> 8; goto cp_data; - + case 0xBD: // CP LXY data = (uint8_t) ixy; goto cp_data; - + // LD CASE7( 70, 71, 72, 73, 74, 75, 77 ): // LD (IXY+disp),r data = R8( data, 0x70 ); @@ -1468,42 +1468,42 @@ possibly_out_of_time: CASE5( 44, 4C, 54, 5C, 7C ): // LD r,HXY R8( data >> 3, 8 ) = ixy >> 8; goto loop; - + case 0x64: // LD HXY,HXY case 0x6D: // LD LXY,LXY goto loop; - + CASE5( 45, 4D, 55, 5D, 7D ): // LD r,LXY R8( data >> 3, 8 ) = ixy; goto loop; - + CASE7( 46, 4E, 56, 5E, 66, 6E, 7E ): // LD r,(IXY+disp) pc++; R8( data >> 3, 8 ) = READ( IXY_DISP( ixy, (int8_t) data2 ) ); goto loop; - + case 0x26: // LD HXY,imm pc++; goto ld_hxy_data; - + case 0x65: // LD HXY,LXY data2 = (uint8_t) ixy; goto ld_hxy_data; - + CASE5( 60, 61, 62, 63, 67 ): // LD HXY,r data2 = R8( data, 0x60 ); ld_hxy_data: ixy = (uint8_t) ixy | (data2 << 8); goto set_ixy; - + case 0x2E: // LD LXY,imm pc++; goto ld_lxy_data; - + case 0x6C: // LD LXY,HXY data2 = ixy >> 8; goto ld_lxy_data; - + CASE5( 68, 69, 6A, 6B, 6F ): // LD LXY,r data2 = R8( data, 0x68 ); ld_lxy_data: @@ -1520,26 +1520,26 @@ possibly_out_of_time: case 0xF9: // LD SP,IXY sp = ixy; goto loop; - + case 0x22:{// LD (ADDR),IXY uint_fast16_t addr = GET_ADDR(); pc += 2; WRITE_WORD( addr, ixy ); goto loop; } - + case 0x21: // LD IXY,imm ixy = GET_ADDR(); pc += 2; goto set_ixy; - + case 0x2A:{// LD IXY,(addr) uint_fast16_t addr = GET_ADDR(); ixy = READ_WORD( addr ); pc += 2; goto set_ixy; } - + // DD/FD CB prefix case 0xCB: { data = IXY_DISP( ixy, (int8_t) data2 ); @@ -1556,7 +1556,7 @@ possibly_out_of_time: case 0x1E: goto rr_data_addr; // RR (IXY) case 0x2E: goto sra_data_addr; // SRA (IXY) case 0x3E: goto srl_data_addr; // SRL (IXY) - + CASE8( 46, 4E, 56, 5E, 66, 6E, 76, 7E ):{// BIT b,(IXY+disp) uint_fast8_t temp = READ( data ); int masked = temp & 1 << (data2 >> 3 & 7); @@ -1565,7 +1565,7 @@ possibly_out_of_time: ((masked - 1) >> 8 & (Z40 | P04)); goto loop; } - + CASE8( 86, 8E, 96, 9E, A6, AE, B6, BE ): // RES b,(IXY+disp) CASE8( C6, CE, D6, DE, E6, EE, F6, FE ):{// SET b,(IXY+disp) int temp = READ( data ); @@ -1576,7 +1576,7 @@ possibly_out_of_time: WRITE( data, temp ); goto loop; } - + default: debug_printf( "Opcode $%02X $CB $%02X not supported\n", opcode, data2 ); warning = true; @@ -1584,35 +1584,35 @@ possibly_out_of_time: } assert( false ); } - + // INC/DEC case 0x23: // INC IXY ixy = uint16_t (ixy + 1); goto set_ixy; - + case 0x2B: // DEC IXY ixy = uint16_t (ixy - 1); goto set_ixy; - + case 0x34: // INC (IXY+disp) ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) + 1; WRITE( ixy, data ); goto inc_set_flags; - + case 0x35: // DEC (IXY+disp) ixy = IXY_DISP( ixy, (int8_t) data2 ); pc++; data = READ( ixy ) - 1; WRITE( ixy, data ); goto dec_set_flags; - + case 0x24: // INC HXY ixy = uint16_t (ixy + 0x100); data = ixy >> 8; goto inc_xy_common; - + case 0x2C: // INC LXY data = uint8_t (ixy + 1); ixy = (ixy & 0xFF00) | data; @@ -1624,12 +1624,12 @@ possibly_out_of_time: } iy = ixy; goto inc_set_flags; - + case 0x25: // DEC HXY ixy = uint16_t (ixy - 0x100); data = ixy >> 8; goto dec_xy_common; - + case 0x2D: // DEC LXY data = uint8_t (ixy - 1); ixy = (ixy & 0xFF00) | data; @@ -1641,31 +1641,30 @@ possibly_out_of_time: } iy = ixy; goto dec_set_flags; - + // PUSH/POP case 0xE5: // PUSH IXY data = ixy; goto push_data; - + case 0xE1:{// POP IXY ixy = READ_WORD( sp ); sp = uint16_t (sp + 2); goto set_ixy; } - + // Misc - case 0xE9: // JP (IXY) pc = ixy; goto loop; - + case 0xE3:{// EX (SP),IXY uint_fast16_t temp = READ_WORD( sp ); WRITE_WORD( sp, ixy ); ixy = temp; goto set_ixy; } - + default: debug_printf( "Unnecessary DD/FD prefix encountered\n" ); warning = true; @@ -1674,11 +1673,11 @@ possibly_out_of_time: } assert( false ); } - + } debug_printf( "Unhandled main opcode: $%02X\n", opcode ); assert( false ); - + hit_idle_addr: s_time -= 11; goto out_of_time; @@ -1686,7 +1685,7 @@ halt: s_time &= 3; // increment by multiple of 4 out_of_time: pc--; - + s.time = s_time; rg.flags = flags; r.ix = ix; @@ -1696,6 +1695,6 @@ out_of_time: this->r.b = rg; this->state_ = s; this->state = &this->state_; - + return warning; } diff --git a/Frameworks/GME/gme/Kss_Cpu.h b/Frameworks/GME/gme/Kss_Cpu.h index e4d58db65..40d754f3d 100644 --- a/Frameworks/GME/gme/Kss_Cpu.h +++ b/Frameworks/GME/gme/Kss_Cpu.h @@ -6,7 +6,7 @@ #include "blargg_endian.h" -typedef blargg_long cpu_time_t; +typedef int32_t cpu_time_t; // must be defined by caller void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); @@ -17,35 +17,35 @@ class Kss_Cpu { public: // Clear registers and map all pages to unmapped void reset( void* unmapped_write, void const* unmapped_read ); - + // Map memory. Start and size must be multiple of page_size. - enum { page_size = 0x2000 }; - void map_mem( unsigned addr, blargg_ulong size, void* write, void const* read ); - + static const unsigned int page_size = 0x2000; + void map_mem( unsigned addr, uint32_t size, void* write, void const* read ); + // Map address to page uint8_t* write( unsigned addr ); uint8_t const* read( unsigned addr ); - + // Run until specified time is reached. Returns true if suspicious/unsupported // instruction was encountered at any point during run. bool run( cpu_time_t end_time ); - + // Time of beginning of next instruction cpu_time_t time() const { return state->time + state->base; } - + // Alter current time. Not supported during run() call. void set_time( cpu_time_t t ) { state->time = t - state->base; } void adjust_time( int delta ) { state->time += delta; } - + #if BLARGG_BIG_ENDIAN struct regs_t { uint8_t b, c, d, e, h, l, flags, a; }; #else struct regs_t { uint8_t c, b, e, d, l, h, a, flags; }; #endif static_assert( sizeof (regs_t) == 8, "Invalid registers size, padding issue?" ); - + struct pairs_t { uint16_t bc, de, hl, fa; }; - + // Registers are not updated until run() returns struct registers_t { uint16_t pc; @@ -67,16 +67,16 @@ public: uint8_t im; }; //registers_t r; (below for efficiency) - - enum { idle_addr = 0xFFFF }; - + + static const unsigned int idle_addr = 0xFFFF; + // can read this far past end of a page - enum { cpu_padding = 0x100 }; - + static const unsigned int cpu_padding = 0x100; + public: Kss_Cpu(); - enum { page_shift = 13 }; - enum { page_count = 0x10000 >> page_shift }; + static const unsigned int page_shift = 13; + static const int page_count = 0x10000 >> page_shift; private: uint8_t szpc [0x200]; cpu_time_t end_time_; diff --git a/Frameworks/GME/gme/Kss_Emu.cpp b/Frameworks/GME/gme/Kss_Emu.cpp index 7fa49373f..6375b5088 100644 --- a/Frameworks/GME/gme/Kss_Emu.cpp +++ b/Frameworks/GME/gme/Kss_Emu.cpp @@ -19,8 +19,8 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -long const clock_rate = 3579545; -int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; +static long const clock_rate = 3579545; +static int const osc_count = Ay_Apu::osc_count + Scc_Apu::osc_count; using std::min; using std::max; @@ -35,13 +35,13 @@ Kss_Emu::Kss_Emu() "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5" }; set_voice_names( names ); - + static int const types [osc_count] = { wave_type | 0, wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 4, wave_type | 5, wave_type | 6, wave_type | 7 }; set_voice_types( types ); - + memset( unmapped_read, 0xFF, sizeof unmapped_read ); } @@ -84,9 +84,9 @@ static blargg_err_t check_kss_header( void const* header ) struct Kss_File : Gme_Info_ { Kss_Emu::header_t header_; - + Kss_File() { set_type( gme_kss_type ); } - + blargg_err_t load_( Data_Reader& in ) { blargg_err_t err = in.read( &header_, Kss_Emu::header_size ); @@ -94,7 +94,7 @@ struct Kss_File : Gme_Info_ return (err == in.eof_error ? gme_wrong_file_type : err); return check_kss_header( &header_ ); } - + blargg_err_t track_info_( track_info_t* out, int ) const { copy_kss_fields( header_, out ); @@ -125,12 +125,12 @@ void Kss_Emu::update_gain() blargg_err_t Kss_Emu::load_( Data_Reader& in ) { memset( &header_, 0, sizeof header_ ); - assert( offsetof (header_t,device_flags) == header_size - 1 ); - assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1 ); + blaarg_static_assert( offsetof (header_t,device_flags) == header_size - 1, "KSS Header layout incorrect!" ); + blaarg_static_assert( offsetof (ext_header_t,msx_audio_vol) == ext_header_size - 1, "KSS Extended Header layout incorrect!" ); RETURN_ERR( rom.load( in, header_size, STATIC_CAST(header_t*,&header_), 0 ) ); - + RETURN_ERR( check_kss_header( header_.tag ) ); - + if ( header_.tag [3] == 'C' ) { if ( header_.extra_header ) @@ -151,19 +151,19 @@ blargg_err_t Kss_Emu::load_( Data_Reader& in ) if ( header_.extra_header > 0x10 ) set_warning( "Unknown data in header" ); } - + if ( header_.device_flags & 0x09 ) set_warning( "FM sound not supported" ); - + scc_enabled = 0xC000; if ( header_.device_flags & 0x04 ) scc_enabled = 0; - + if ( header_.device_flags & 0x02 && !sn ) CHECK_ALLOC( sn = BLARGG_NEW( Sms_Apu ) ); - + set_voice_count( osc_count ); - + return setup_buffer( ::clock_rate ); } @@ -201,7 +201,7 @@ blargg_err_t Kss_Emu::start_track_( int track ) memset( ram, 0xC9, 0x4000 ); memset( ram + 0x4000, 0, sizeof ram - 0x4000 ); - + // copy driver code to lo RAM static byte const bios [] = { 0xD3, 0xA0, 0xF5, 0x7B, 0xD3, 0xA1, 0xF1, 0xC9, // $0001: WRTPSG @@ -213,7 +213,7 @@ blargg_err_t Kss_Emu::start_track_( int track ) }; memcpy( ram + 0x01, bios, sizeof bios ); memcpy( ram + 0x93, vectors, sizeof vectors ); - + // copy non-banked data into RAM unsigned load_addr = get_le16( header_.load_addr ); long orig_load_size = get_le16( header_.load_size ); @@ -222,11 +222,11 @@ blargg_err_t Kss_Emu::start_track_( int track ) if ( load_size != orig_load_size ) set_warning( "Excessive data size" ); memcpy( ram + load_addr, rom.begin() + header_.extra_header, load_size ); - + rom.set_addr( -load_size - header_.extra_header ); - + // check available bank data - blargg_long const bank_size = this->bank_size(); + int32_t const bank_size = this->bank_size(); int max_banks = (rom.file_size() - load_size + bank_size - 1) / bank_size; bank_count = header_.bank_mode & 0x7F; if ( bank_count > max_banks ) @@ -237,11 +237,11 @@ blargg_err_t Kss_Emu::start_track_( int track ) //debug_printf( "load_size : $%X\n", load_size ); //debug_printf( "bank_size : $%X\n", bank_size ); //debug_printf( "bank_count: %d (%d claimed)\n", bank_count, header_.bank_mode & 0x7F ); - + ram [idle_addr] = 0xFF; cpu::reset( unmapped_write, unmapped_read ); cpu::map_mem( 0, mem_size, ram, ram ); - + ay.reset(); scc.reset(); if ( sn ) @@ -256,18 +256,18 @@ blargg_err_t Kss_Emu::start_track_( int track ) gain_updated = false; update_gain(); ay_latch = 0; - + return 0; } void Kss_Emu::set_bank( int logical, int physical ) { unsigned const bank_size = this->bank_size(); - + unsigned addr = 0x8000; if ( logical && bank_size == 8 * 1024 ) addr = 0xA000; - + physical -= header_.first_bank; if ( (unsigned) physical >= (unsigned) bank_count ) { @@ -276,7 +276,7 @@ void Kss_Emu::set_bank( int logical, int physical ) } else { - long phys = physical * (blargg_long) bank_size; + long phys = physical * (int32_t) bank_size; for ( unsigned offset = 0; offset < bank_size; offset += page_size ) cpu::map_mem( addr + offset, page_size, unmapped_write, rom.at_addr( phys + offset ) ); @@ -291,12 +291,12 @@ void Kss_Emu::cpu_write( unsigned addr, int data ) case 0x9000: set_bank( 0, data ); return; - + case 0xB000: set_bank( 1, data ); return; } - + int scc_addr = (addr & 0xDFFF) ^ 0x9800; if ( scc_addr < scc.reg_count ) { @@ -304,7 +304,7 @@ void Kss_Emu::cpu_write( unsigned addr, int data ) scc.write( time(), scc_addr, data ); return; } - + debug_printf( "LD ($%04X),$%02X\n", addr, data ); } @@ -324,12 +324,12 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) case 0xA0: emu.ay_latch = data & 0x0F; return; - + case 0xA1: GME_APU_HOOK( &emu, emu.ay_latch, data ); emu.ay.write( time, emu.ay_latch, data ); return; - + case 0x06: if ( emu.sn && (emu.header_.device_flags & 0x04) ) { @@ -337,7 +337,7 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) return; } break; - + case 0x7E: case 0x7F: if ( emu.sn ) @@ -347,11 +347,11 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) return; } break; - + case 0xFE: emu.set_bank( 0, data ); return; - + #ifndef NDEBUG case 0xF1: // FM data if ( data ) @@ -361,7 +361,7 @@ void kss_cpu_out( Kss_Cpu* cpu, cpu_time_t time, unsigned addr, int data ) return; #endif } - + debug_printf( "OUT $%04X,$%02X\n", addr, data ); } @@ -371,7 +371,7 @@ int kss_cpu_in( Kss_Cpu*, cpu_time_t, unsigned addr ) //switch ( addr & 0xFF ) //{ //} - + debug_printf( "IN $%04X\n", addr ); return 0; } @@ -386,7 +386,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) cpu::run( min( duration, next_play ) ); if ( r.pc == idle_addr ) set_time( end ); - + if ( time() >= next_play ) { next_play += play_period; @@ -398,7 +398,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) if ( scc_accessed ) update_gain(); } - + ram [--r.sp] = idle_addr >> 8; ram [--r.sp] = idle_addr & 0xFF; r.pc = get_le16( header_.play_addr ); @@ -406,7 +406,7 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) } } } - + duration = time(); next_play -= duration; check( next_play >= 0 ); @@ -415,6 +415,6 @@ blargg_err_t Kss_Emu::run_clocks( blip_time_t& duration, int ) scc.end_frame( duration ); if ( sn ) sn->end_frame( duration ); - + return 0; } diff --git a/Frameworks/GME/gme/Kss_Emu.h b/Frameworks/GME/gme/Kss_Emu.h index 467b28abd..7bc7fb4a7 100644 --- a/Frameworks/GME/gme/Kss_Emu.h +++ b/Frameworks/GME/gme/Kss_Emu.h @@ -27,7 +27,7 @@ public: byte extra_header; byte device_flags; }; - + enum { ext_header_size = 0x10 }; struct ext_header_t { @@ -40,12 +40,12 @@ public: byte msx_music_vol; byte msx_audio_vol; }; - + struct composite_header_t : header_t, ext_header_t { }; - + // Header for currently loaded file composite_header_t const& header() const { return header_; } - + static gme_type_t static_type() { return gme_kss_type; } public: Kss_Emu(); @@ -62,29 +62,29 @@ protected: private: Rom_Data rom; composite_header_t header_; - + bool scc_accessed; bool gain_updated; void update_gain(); - + unsigned scc_enabled; // 0 or 0xC000 int bank_count; void set_bank( int logical, int physical ); - blargg_long bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); } - + int32_t bank_size() const { return (16 * 1024L) >> (header_.bank_mode >> 7 & 1); } + blip_time_t play_period; blip_time_t next_play; int ay_latch; - + friend void kss_cpu_out( class Kss_Cpu*, cpu_time_t, unsigned addr, int data ); friend int kss_cpu_in( class Kss_Cpu*, cpu_time_t, unsigned addr ); void cpu_write( unsigned addr, int data ); friend void kss_cpu_write( class Kss_Cpu*, unsigned addr, int data ); - + // large items - enum { mem_size = 0x10000 }; + static const unsigned int mem_size = 0x10000; byte ram [mem_size + cpu_padding]; - + Ay_Apu ay; Scc_Apu scc; Sms_Apu* sn; diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.cpp b/Frameworks/GME/gme/Kss_Scc_Apu.cpp index bb84b3250..37c87857d 100644 --- a/Frameworks/GME/gme/Kss_Scc_Apu.cpp +++ b/Frameworks/GME/gme/Kss_Scc_Apu.cpp @@ -17,32 +17,32 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ // Tones above this frequency are treated as disabled tone at half volume. // Power of two is more efficient (avoids division). -unsigned const inaudible_freq = 16384; +static unsigned const inaudible_freq = 16384; -int const wave_size = 0x20; +static int const wave_size = 0x20; void Scc_Apu::run_until( blip_time_t end_time ) { for ( int index = 0; index < osc_count; index++ ) { osc_t& osc = oscs [index]; - + Blip_Buffer* const output = osc.output; if ( !output ) continue; output->set_modified(); - + blip_time_t period = (regs [0x80 + index * 2 + 1] & 0x0F) * 0x100 + regs [0x80 + index * 2] + 1; int volume = 0; if ( regs [0x8F] & (1 << index) ) { - blip_time_t inaudible_period = (blargg_ulong) (output->clock_rate() + + blip_time_t inaudible_period = (uint32_t) (output->clock_rate() + inaudible_freq * 32) / (inaudible_freq * 16); if ( period > inaudible_period ) volume = (regs [0x8A + index] & 0x0F) * (amp_range / 256 / 15); } - + int8_t const* wave = (int8_t*) regs + index * wave_size; if ( index == osc_count - 1 ) wave -= wave_size; // last two oscs share wave @@ -55,24 +55,24 @@ void Scc_Apu::run_until( blip_time_t end_time ) synth.offset( last_time, delta, output ); } } - + blip_time_t time = last_time + osc.delay; if ( time < end_time ) { if ( !volume ) { // maintain phase - blargg_long count = (end_time - time + period - 1) / period; + int32_t count = (end_time - time + period - 1) / period; osc.phase = (osc.phase + count) & (wave_size - 1); time += count * period; } else { - + int phase = osc.phase; int last_wave = wave [phase]; phase = (phase + 1) & (wave_size - 1); // pre-advance for optimal inner loop - + do { int amp = wave [phase]; @@ -86,7 +86,7 @@ void Scc_Apu::run_until( blip_time_t end_time ) time += period; } while ( time < end_time ); - + osc.phase = phase = (phase - 1) & (wave_size - 1); // undo pre-advance osc.last_amp = wave [phase] * volume; } diff --git a/Frameworks/GME/gme/Kss_Scc_Apu.h b/Frameworks/GME/gme/Kss_Scc_Apu.h index eda5747fe..865a300fd 100644 --- a/Frameworks/GME/gme/Kss_Scc_Apu.h +++ b/Frameworks/GME/gme/Kss_Scc_Apu.h @@ -12,36 +12,36 @@ class Scc_Apu { public: // Set buffer to generate all sound into, or disable sound if NULL void output( Blip_Buffer* ); - + // Reset sound chip void reset(); - + // Write to register at specified time - enum { reg_count = 0x90 }; + static const int reg_count = 0x90; void write( blip_time_t time, int reg, int data ); - + // Run sound to specified time, end current time frame, then start a new // time frame at time 0. Time frames have no effect on emulation and each // can be whatever length is convenient. void end_frame( blip_time_t length ); // Additional features - + // Set sound output of specific oscillator to buffer, where index is // 0 to 4. If buffer is NULL, the specified oscillator is muted. - enum { osc_count = 5 }; + static const int osc_count = 5; void osc_output( int index, Blip_Buffer* ); - + // Set overall volume (default is 1.0) void volume( double ); - + // Set treble equalization (see documentation) void treble_eq( blip_eq_t const& ); - + public: Scc_Apu(); private: - enum { amp_range = 0x8000 }; + static const unsigned int amp_range = 0x8000; struct osc_t { int delay; @@ -53,7 +53,7 @@ private: blip_time_t last_time; unsigned char regs [reg_count]; Blip_Synth synth; - + void run_until( blip_time_t ); }; @@ -96,10 +96,10 @@ inline Scc_Apu::Scc_Apu() inline void Scc_Apu::reset() { last_time = 0; - + for ( int i = 0; i < osc_count; i++ ) memset( &oscs [i], 0, offsetof (osc_t,output) ); - + memset( regs, 0, sizeof regs ); } diff --git a/Frameworks/GME/gme/M3u_Playlist.cpp b/Frameworks/GME/gme/M3u_Playlist.cpp index 8759c6e95..a076f009d 100644 --- a/Frameworks/GME/gme/M3u_Playlist.cpp +++ b/Frameworks/GME/gme/M3u_Playlist.cpp @@ -23,12 +23,12 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) { require( raw_track_count_ ); // file must be loaded first - + if ( !err ) { if ( playlist.size() ) track_count_ = playlist.size(); - + int line = playlist.first_error(); if ( line ) { @@ -38,7 +38,7 @@ blargg_err_t Gme_File::load_m3u_( blargg_err_t err ) do { *--out = line % 10 + '0'; } while ( (line /= 10) > 0 ); - + static const char str [] = "Problem in m3u at line "; out -= sizeof str - 1; memcpy( out, str, sizeof str - 1 ); @@ -60,8 +60,6 @@ gme_err_t gme_load_m3u_data( Music_Emu* me, const void* data, long size ) return me->load_m3u( in ); } - - static char* skip_white( char* in ) { while ( *in == ' ' ) @@ -69,7 +67,7 @@ static char* skip_white( char* in ) return in; } -inline unsigned from_dec( unsigned n ) { return n - '0'; } +static inline unsigned from_dec( unsigned n ) { return n - '0'; } static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) { @@ -81,7 +79,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) int c = *in; if ( !c ) break; in++; - + if ( c == ',' ) // commas in filename { char* p = skip_white( in ); @@ -91,7 +89,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) break; } } - + if ( c == ':' && in [0] == ':' && in [1] && in [2] != ',' ) // ::type suffix { entry.type = ++in; @@ -104,7 +102,7 @@ static char* parse_filename( char* in, M3u_Playlist::entry_t& entry ) } break; } - + if ( c == '\\' ) // \ prefix for special characters { c = *in; @@ -122,16 +120,16 @@ static char* next_field( char* in, int* result ) while ( 1 ) { in = skip_white( in ); - + if ( !*in ) break; - + if ( *in == ',' ) { in++; break; } - + *result = 1; in++; } @@ -176,7 +174,7 @@ static char* parse_int( char* in, int* out, int* result ) } // Returns 16 or greater if not hex -inline int from_hex_char( int h ) +static inline int from_hex_char( int h ) { h -= 0x30; if ( (unsigned) h > 9 ) @@ -250,7 +248,7 @@ static char* parse_name( char* in ) int c = *in; if ( !c ) break; in++; - + if ( c == ',' ) // commas in string { char* p = skip_white( in ); @@ -260,7 +258,7 @@ static char* parse_name( char* in ) break; } } - + if ( c == '\\' ) // \ prefix for special characters { c = *in; @@ -276,25 +274,25 @@ static char* parse_name( char* in ) static int parse_line( char* in, M3u_Playlist::entry_t& entry ) { int result = 0; - + // file entry.file = in; entry.type = ""; in = parse_filename( in, entry ); - + // track entry.track = -1; entry.decimal_track = 0; in = parse_track( in, entry, &result ); - + // name entry.name = in; in = parse_name( in ); - + // time entry.length = -1; in = parse_time( in, &entry.length, &result ); - + // loop entry.intro = -1; entry.loop = -1; @@ -309,7 +307,7 @@ static int parse_line( char* in, M3u_Playlist::entry_t& entry ) if ( entry.loop >= 0 ) { entry.intro = 0; - if ( *in == '-' ) // trailing '-' means that intro length was specified + if ( *in == '-' ) // trailing '-' means that intro length was specified { in++; entry.intro = entry.loop; @@ -318,15 +316,15 @@ static int parse_line( char* in, M3u_Playlist::entry_t& entry ) } } in = next_field( in, &result ); - + // fade entry.fade = -1; in = parse_time( in, &entry.fade, &result ); - + // repeat entry.repeat = -1; in = parse_int( in, &entry.repeat, &result ); - + return result; } @@ -337,7 +335,7 @@ static void parse_comment( char* in, M3u_Playlist::info_t& info, char *& last_co if ( *field != '@' ) while ( *in && *in != ':' ) in++; - + if ( *in == ':' ) { const char* text = skip_white( in + 1 ); @@ -395,7 +393,7 @@ static void parse_comment( char* in, M3u_Playlist::info_t& info, char *& last_co last_comment_value[ len + 2 + field_len ] = 0; return; } - + if ( first ) info.title = field; } @@ -411,12 +409,12 @@ blargg_err_t M3u_Playlist::parse_() info_.ripping = ""; info_.tagging = ""; info_.copyright = ""; - + int const CR = 13; int const LF = 10; - + data.end() [-1] = LF; // terminate input - + first_error_ = 0; bool first_comment = true; int line = 0; @@ -437,7 +435,7 @@ blargg_err_t M3u_Playlist::parse_() if ( in [0] == CR && in [1] == LF ) // treat CR,LF as a single line *in++ = 0; *in++ = 0; - + // parse line if ( *begin == '#' ) { @@ -448,7 +446,7 @@ blargg_err_t M3u_Playlist::parse_() { if ( (int) entries.size() <= count ) RETURN_ERR( entries.resize( count * 2 + 64 ) ); - + if ( !parse_line( begin, entries [count] ) ) count++; else if ( !first_error_ ) @@ -459,10 +457,10 @@ blargg_err_t M3u_Playlist::parse_() } if ( count <= 0 ) return "Not an m3u playlist"; - + if ( !(info_.composer [0] | info_.engineer [0] | info_.ripping [0] | info_.tagging [0]) ) info_.title = ""; - + return entries.resize( count ); } diff --git a/Frameworks/GME/gme/M3u_Playlist.h b/Frameworks/GME/gme/M3u_Playlist.h index 63e199a16..2af748085 100644 --- a/Frameworks/GME/gme/M3u_Playlist.h +++ b/Frameworks/GME/gme/M3u_Playlist.h @@ -13,11 +13,11 @@ public: blargg_err_t load( const char* path ); blargg_err_t load( Data_Reader& in ); blargg_err_t load( void const* data, long size ); - + // Line number of first parse error, 0 if no error. Any lines with parse // errors are ignored. int first_error() const { return first_error_; } - + struct info_t { const char* title; @@ -31,7 +31,7 @@ public: const char* copyright; }; info_t const& info() const { return info_; } - + struct entry_t { const char* file; // filename without stupid ::TYPE suffix @@ -48,15 +48,15 @@ public: }; entry_t const& operator [] ( int i ) const { return entries [i]; } int size() const { return entries.size(); } - + void clear(); - + private: blargg_vector entries; blargg_vector data; int first_error_; info_t info_; - + blargg_err_t parse(); blargg_err_t parse_(); }; diff --git a/Frameworks/GME/gme/Multi_Buffer.cpp b/Frameworks/GME/gme/Multi_Buffer.cpp index 5f000ceeb..a6c9349a4 100644 --- a/Frameworks/GME/gme/Multi_Buffer.cpp +++ b/Frameworks/GME/gme/Multi_Buffer.cpp @@ -107,7 +107,7 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) { require( !(count & 1) ); // count must be even count = (unsigned) count / 2; - + long avail = bufs [0].samples_avail(); if ( count > avail ) count = avail; @@ -136,7 +136,7 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) bufs [1].remove_samples( count ); bufs [2].remove_samples( count ); } - + // to do: this might miss opportunities for optimization if ( !bufs [0].samples_avail() ) { @@ -144,89 +144,89 @@ long Stereo_Buffer::read_samples( blip_sample_t* out, long count ) stereo_added = 0; } } - + return count * 2; } -void Stereo_Buffer::mix_stereo( blip_sample_t* out_, blargg_long count ) +void Stereo_Buffer::mix_stereo( blip_sample_t* out_, int32_t count ) { blip_sample_t* BLIP_RESTRICT out = out_; int const bass = BLIP_READER_BASS( bufs [1] ); BLIP_READER_BEGIN( left, bufs [1] ); BLIP_READER_BEGIN( right, bufs [2] ); BLIP_READER_BEGIN( center, bufs [0] ); - + for ( ; count; --count ) { int c = BLIP_READER_READ( center ); - blargg_long l = c + BLIP_READER_READ( left ); - blargg_long r = c + BLIP_READER_READ( right ); + int32_t l = c + BLIP_READER_READ( left ); + int32_t r = c + BLIP_READER_READ( right ); if ( (int16_t) l != l ) l = 0x7FFF - (l >> 24); - + BLIP_READER_NEXT( center, bass ); if ( (int16_t) r != r ) r = 0x7FFF - (r >> 24); - + BLIP_READER_NEXT( left, bass ); BLIP_READER_NEXT( right, bass ); - + out [0] = l; out [1] = r; out += 2; } - + BLIP_READER_END( center, bufs [0] ); BLIP_READER_END( right, bufs [2] ); BLIP_READER_END( left, bufs [1] ); } -void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, blargg_long count ) +void Stereo_Buffer::mix_stereo_no_center( blip_sample_t* out_, int32_t count ) { blip_sample_t* BLIP_RESTRICT out = out_; int const bass = BLIP_READER_BASS( bufs [1] ); BLIP_READER_BEGIN( left, bufs [1] ); BLIP_READER_BEGIN( right, bufs [2] ); - + for ( ; count; --count ) { - blargg_long l = BLIP_READER_READ( left ); + int32_t l = BLIP_READER_READ( left ); if ( (int16_t) l != l ) l = 0x7FFF - (l >> 24); - - blargg_long r = BLIP_READER_READ( right ); + + int32_t r = BLIP_READER_READ( right ); if ( (int16_t) r != r ) r = 0x7FFF - (r >> 24); - + BLIP_READER_NEXT( left, bass ); BLIP_READER_NEXT( right, bass ); - + out [0] = l; out [1] = r; out += 2; } - + BLIP_READER_END( right, bufs [2] ); BLIP_READER_END( left, bufs [1] ); } -void Stereo_Buffer::mix_mono( blip_sample_t* out_, blargg_long count ) +void Stereo_Buffer::mix_mono( blip_sample_t* out_, int32_t count ) { blip_sample_t* BLIP_RESTRICT out = out_; int const bass = BLIP_READER_BASS( bufs [0] ); BLIP_READER_BEGIN( center, bufs [0] ); - + for ( ; count; --count ) { - blargg_long s = BLIP_READER_READ( center ); + int32_t s = BLIP_READER_READ( center ); if ( (int16_t) s != s ) s = 0x7FFF - (s >> 24); - + BLIP_READER_NEXT( center, bass ); out [0] = s; out [1] = s; out += 2; } - + BLIP_READER_END( center, bufs [0] ); } diff --git a/Frameworks/GME/gme/Multi_Buffer.h b/Frameworks/GME/gme/Multi_Buffer.h index 82c8b3ab5..29f65bdf6 100644 --- a/Frameworks/GME/gme/Multi_Buffer.h +++ b/Frameworks/GME/gme/Multi_Buffer.h @@ -13,10 +13,10 @@ class Multi_Buffer { public: Multi_Buffer( int samples_per_frame ); virtual ~Multi_Buffer() { } - + // Set the number of channels available virtual blargg_err_t set_channel_count( int ); - + // Get indexed channel, from 0 to channel count - 1 struct channel_t { Blip_Buffer* center; @@ -26,31 +26,31 @@ public: enum { type_index_mask = 0xFF }; enum { wave_type = 0x100, noise_type = 0x200, mixed_type = wave_type | noise_type }; virtual channel_t channel( int index, int type ) = 0; - + // See Blip_Buffer.h virtual blargg_err_t set_sample_rate( long rate, int msec = blip_default_length ) = 0; virtual void clock_rate( long ) = 0; virtual void bass_freq( int ) = 0; virtual void clear() = 0; long sample_rate() const; - + // Length of buffer, in milliseconds int length() const; - + // See Blip_Buffer.h virtual void end_frame( blip_time_t ) = 0; - + // Number of samples per output frame (1 = mono, 2 = stereo) int samples_per_frame() const; - + // Count of changes to channel configuration. Incremented whenever // a change is made to any of the Blip_Buffers for any channel. unsigned channels_changed_count() { return channels_changed_count_; } - + // See Blip_Buffer.h virtual long read_samples( blip_sample_t*, long ) = 0; virtual long samples_avail() const = 0; - + public: BLARGG_DISABLE_NOTHROW protected: @@ -59,7 +59,7 @@ private: // noncopyable Multi_Buffer( const Multi_Buffer& ); Multi_Buffer& operator = ( const Multi_Buffer& ); - + unsigned channels_changed_count_; long sample_rate_; int length_; @@ -73,7 +73,7 @@ class Mono_Buffer : public Multi_Buffer { public: // Buffer used for all channels Blip_Buffer* center() { return &buf; } - + public: Mono_Buffer(); ~Mono_Buffer(); @@ -90,12 +90,12 @@ public: // Uses three buffers (one for center) and outputs stereo sample pairs. class Stereo_Buffer : public Multi_Buffer { public: - + // Buffers used for all channels Blip_Buffer* center() { return &bufs [0]; } Blip_Buffer* left() { return &bufs [1]; } Blip_Buffer* right() { return &bufs [2]; } - + public: Stereo_Buffer(); ~Stereo_Buffer(); @@ -105,20 +105,20 @@ public: void clear(); channel_t channel( int, int ) { return chan; } void end_frame( blip_time_t ); - + long samples_avail() const { return bufs [0].samples_avail() * 2; } long read_samples( blip_sample_t*, long ); - + private: enum { buf_count = 3 }; Blip_Buffer bufs [buf_count]; channel_t chan; int stereo_added; int was_stereo; - - void mix_stereo_no_center( blip_sample_t*, blargg_long ); - void mix_stereo( blip_sample_t*, blargg_long ); - void mix_mono( blip_sample_t*, blargg_long ); + + void mix_stereo_no_center( blip_sample_t*, int32_t ); + void mix_stereo( blip_sample_t*, int32_t ); + void mix_mono( blip_sample_t*, int32_t ); }; // Silent_Buffer generates no samples, useful where no sound is wanted diff --git a/Frameworks/GME/gme/Music_Emu.cpp b/Frameworks/GME/gme/Music_Emu.cpp index 2b0f35bec..50bbcf9ec 100644 --- a/Frameworks/GME/gme/Music_Emu.cpp +++ b/Frameworks/GME/gme/Music_Emu.cpp @@ -19,10 +19,10 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -int const silence_max = 6; // seconds -int const silence_threshold = 0x10; -long const fade_block_size = 512; -int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) +static int const silence_max = 6; // seconds +static int const silence_threshold = 0x10; +static long const fade_block_size = 512; +static int const fade_shift = 8; // fade ends with gain at 1.0 / (1 << fade_shift) using std::min; using std::max; @@ -34,6 +34,7 @@ void Music_Emu::clear_track_vars() { current_track_ = -1; out_time = 0; + out_time_scaled = 0; emu_time = 0; emu_track_ended_ = true; track_ended_ = true; @@ -60,14 +61,14 @@ Music_Emu::Music_Emu() mute_mask_ = 0; tempo_ = 1.0; gain_ = 1.0; - + // defaults max_initial_silence = 2; silence_lookahead = 3; ignore_silence_ = false; equalizer_.treble = -1.0; equalizer_.bass = 60; - + emu_autoload_playback_limit_ = true; static const char* const names [] = { @@ -137,6 +138,11 @@ void Music_Emu::mute_voices( int mask ) mute_voices_( mask ); } +void Music_Emu::disable_echo( bool disable ) +{ + disable_echo_( disable ); +} + void Music_Emu::set_tempo( double t ) { require( sample_rate() ); // sample rate must be set first @@ -157,15 +163,15 @@ void Music_Emu::post_load_() blargg_err_t Music_Emu::start_track( int track ) { clear_track_vars(); - + int remapped = track; RETURN_ERR( remap_track_( &remapped ) ); current_track_ = track; RETURN_ERR( start_track_( remapped ) ); - + emu_track_ended_ = false; track_ended_ = false; - + if ( !ignore_silence_ ) { // play until non-silence or end of track @@ -175,11 +181,12 @@ blargg_err_t Music_Emu::start_track( int track ) if ( buf_remain | (int) emu_track_ended_ ) break; } - - emu_time = buf_remain; - out_time = 0; - silence_time = 0; - silence_count = 0; + + emu_time = buf_remain; + out_time = 0; + out_time_scaled = 0; + silence_time = 0; + silence_count = 0; } return track_ended() ? warning() : 0; } @@ -205,9 +212,9 @@ void Music_Emu::set_autoload_playback_limit( bool do_autoload_limit ) // Tell/Seek -blargg_long Music_Emu::msec_to_samples( blargg_long msec ) const +int32_t Music_Emu::msec_to_samples( int32_t msec ) const { - blargg_long sec = msec / 1000; + int32_t sec = msec / 1000; msec -= sec * 1000; return (sec * sample_rate() + msec * sample_rate() / 1000) * out_channels(); } @@ -219,11 +226,16 @@ long Music_Emu::tell_samples() const long Music_Emu::tell() const { - blargg_long rate = sample_rate() * out_channels(); - blargg_long sec = out_time / rate; + int32_t rate = sample_rate() * out_channels(); + int32_t sec = out_time / rate; return sec * 1000 + (out_time - sec * rate) * 1000 / rate; } +long Music_Emu::tell_scaled() const +{ + return out_time_scaled / (sample_rate() / 1000.0); +} + blargg_err_t Music_Emu::seek_samples( long time ) { if ( time < out_time ) @@ -236,31 +248,43 @@ blargg_err_t Music_Emu::seek( long msec ) return seek_samples( msec_to_samples( msec ) ); } +blargg_err_t Music_Emu::seek_scaled( long msec ) +{ + require( tempo_ > 0 ); + int32_t frames = (msec / 1000.0) * sample_rate(); + if ( frames < out_time_scaled ) + RETURN_ERR( start_track( current_track_ ) ); + int samples_to_skip = (frames - out_time_scaled) * out_channels() / tempo_; + samples_to_skip += samples_to_skip % out_channels(); + return skip( samples_to_skip ); +} + blargg_err_t Music_Emu::skip( long count ) { require( current_track() >= 0 ); // start_track() must have been called already out_time += count; - + out_time_scaled += count * tempo_ / out_channels(); + // remove from silence and buf first { long n = min( count, silence_count ); silence_count -= n; count -= n; - + n = min( count, buf_remain ); buf_remain -= n; count -= n; } - + if ( count && !emu_track_ended_ ) { emu_time += count; end_track_if_error( skip_( count ) ); } - + if ( !(silence_count | buf_remain) ) // caught up to emulator, so update track ended track_ended_ |= emu_track_ended_; - + return 0; } @@ -272,16 +296,16 @@ blargg_err_t Music_Emu::skip_( long count ) { int saved_mute = mute_mask_; mute_voices( ~0 ); - + while ( count > threshold / 2 && !emu_track_ended_ ) { RETURN_ERR( play_( buf_size, buf.begin() ) ); count -= buf_size; } - + mute_voices( saved_mute ); } - + while ( count && !emu_track_ended_ ) { long n = buf_size; @@ -302,7 +326,7 @@ void Music_Emu::set_fade( long start_msec, long length_msec ) } // unit / pow( 2.0, (double) x / step ) -static int int_log( blargg_long x, int step, int unit ) +static int int_log( int32_t x, int step, int unit ) { int shift = x / step; int fraction = (x - shift * step) * unit / step; @@ -325,7 +349,7 @@ void Music_Emu::handle_fade( long out_count, sample_t* out ) fade_step, unit ); if ( gain < (unit >> fade_shift) ) track_ended_ = emu_track_ended_ = true; - + sample_t* io = &out [i]; for ( int count = min( fade_block_size, out_count - i ); count; --count ) { @@ -386,33 +410,33 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out ) { require( current_track() >= 0 ); require( out_count % out_channels() == 0 ); - + assert( emu_time >= out_time ); - + // prints nifty graph of how far ahead we are when searching for silence //debug_printf( "%*s \n", int ((emu_time - out_time) * 7 / sample_rate()), "*" ); - + long pos = 0; if ( silence_count ) { // during a run of silence, run emulator at >=2x speed so it gets ahead long ahead_time = silence_lookahead * (out_time + out_count - silence_time) + silence_time; - while ( emu_time < ahead_time && !(buf_remain | emu_track_ended_) ) + while ( emu_time < ahead_time && !(buf_remain | static_cast(emu_track_ended_)) ) fill_buf(); - + // fill with silence pos = min( silence_count, out_count ); memset( out, 0, pos * sizeof *out ); silence_count -= pos; - - if ( emu_time - silence_time > silence_max * out_channels() * sample_rate() ) + + if ( !ignore_silence_ && emu_time - silence_time > silence_max * out_channels() * sample_rate() ) { track_ended_ = emu_track_ended_ = true; silence_count = 0; buf_remain = 0; } } - + if ( buf_remain ) { // empty silence buf @@ -421,30 +445,31 @@ blargg_err_t Music_Emu::play( long out_count, sample_t* out ) buf_remain -= n; pos += n; } - + // generate remaining samples normally long remain = out_count - pos; if ( remain ) { emu_play( remain, out + pos ); track_ended_ |= emu_track_ended_; - + if ( !ignore_silence_ || out_time > fade_start ) { // check end for a new run of silence long silence = count_silence( out + pos, remain ); if ( silence < remain ) silence_time = emu_time - silence; - + if ( emu_time - silence_time >= buf_size ) fill_buf(); // cause silence detection on next play() } } - + if ( fade_start >= 0 && out_time > fade_start ) handle_fade( out_count, out ); } out_time += out_count; + out_time_scaled += out_count * tempo_ / out_channels(); return 0; } @@ -459,3 +484,4 @@ void Gme_Info_::mute_voices_( int ) { check( false ); } void Gme_Info_::set_tempo_( double ) { } blargg_err_t Gme_Info_::start_track_( int ) { return "Use full emulator for playback"; } blargg_err_t Gme_Info_::play_( long, sample_t* ) { return "Use full emulator for playback"; } + diff --git a/Frameworks/GME/gme/Music_Emu.h b/Frameworks/GME/gme/Music_Emu.h index 3aafa5ec1..91af0149a 100644 --- a/Frameworks/GME/gme/Music_Emu.h +++ b/Frameworks/GME/gme/Music_Emu.h @@ -18,55 +18,61 @@ public: // default implementation of Music_Emu always returns not supported error (i.e. no multichannel support by default) // derived emus must override this if they support multichannel rendering virtual blargg_err_t set_multi_channel( bool is_enabled ); - + // Start a track, where 0 is the first track. Also clears warning string. blargg_err_t start_track( int ); - + // Generate 'count' samples info 'buf'. Output is in stereo. Any emulation // errors set warning string, and major errors also end track. typedef short sample_t; blargg_err_t play( long count, sample_t* buf ); - + // Informational - + // Sample rate sound is generated at long sample_rate() const; - + // Index of current track or -1 if one hasn't been started int current_track() const; - + // Number of voices used by currently loaded file int voice_count() const; - + // Names of voices const char** voice_names() const; bool multi_channel() const; - + // Track status/control // Number of milliseconds (1000 msec = 1 second) played since beginning of track long tell() const; - + // Number of samples generated since beginning of track long tell_samples() const; + // Number of milliseconds played since beginning of track (scaled with tempo). + long tell_scaled() const; + // Seek to new time in track. Seeking backwards or far forward can take a while. blargg_err_t seek( long msec ); - + // Equivalent to restarting track then skipping n samples blargg_err_t seek_samples( long n ); - + + // Seek to new time in track (scaled with tempo). + blargg_err_t seek_scaled( long msec ); + // Skip n samples blargg_err_t skip( long n ); - + // True if a track has reached its end bool track_ended() const; - + // Set start time and length of track fade out. Once fade ends track_ended() returns // true. Fade time can be changed while track is playing. void set_fade( long start_msec, long length_msec = 8000 ); - + // Controls whether or not to automatically load and obey track length // metadata for supported emulators. // @@ -76,45 +82,48 @@ public: // Disable automatic end-of-track detection and skipping of silence at beginning void ignore_silence( bool disable = true ); - + // Info for current track using Gme_File::track_info; blargg_err_t track_info( track_info_t* out ) const; - + // Sound customization - + // Adjust song tempo, where 1.0 = normal, 0.5 = half speed, 2.0 = double speed. // Track length as returned by track_info() assumes a tempo of 1.0. void set_tempo( double ); - + // Mute/unmute voice i, where voice 0 is first voice void mute_voice( int index, bool mute = true ); - + // Set muting state of all voices at once using a bit mask, where -1 mutes them all, // 0 unmutes them all, 0x01 mutes just the first voice, etc. void mute_voices( int mask ); - + + // Disables echo effect at SPC files + void disable_echo( bool disable ); + // Change overall output amplitude, where 1.0 results in minimal clamping. // Must be called before set_sample_rate(). void set_gain( double ); - + // Request use of custom multichannel buffer. Only supported by "classic" emulators; // on others this has no effect. Should be called only once *before* set_sample_rate(). virtual void set_buffer( Multi_Buffer* ) { } - + // Enables/disables accurate emulation options, if any are supported. Might change // equalizer settings. void enable_accuracy( bool enable = true ); - + // Sound equalization (treble/bass) // Frequency equalizer parameters (see gme.txt) // See gme.h for definition of struct gme_equalizer_t. typedef gme_equalizer_t equalizer_t; - + // Current frequency equalizater parameters equalizer_t const& equalizer() const; - + // Set frequency equalizer parameters void set_equalizer( equalizer_t const& ); @@ -125,10 +134,10 @@ public: 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0 }; return e; } - + // Equalizer settings for TV speaker static equalizer_t const tv_eq; - + public: Music_Emu(); ~Music_Emu(); @@ -142,13 +151,14 @@ protected: double tempo() const { return tempo_; } void remute_voices(); blargg_err_t set_multi_channel_( bool is_enabled ); - + virtual blargg_err_t set_sample_rate_( long sample_rate ) = 0; virtual void set_equalizer_( equalizer_t const& ) { } virtual void enable_accuracy_( bool /* enable */ ) { } - virtual void mute_voices_( int mask ) = 0; - virtual void set_tempo_( double ) = 0; - virtual blargg_err_t start_track_( int ) = 0; // tempo is set before this + virtual void mute_voices_( int mask ); + virtual void disable_echo_( bool /* disable */); + virtual void set_tempo_( double ); + virtual blargg_err_t start_track_( int ); // tempo is set before this virtual blargg_err_t play_( long count, sample_t* out ) = 0; virtual blargg_err_t skip_( long count ); protected: @@ -170,23 +180,24 @@ private: int out_channels() const { return this->multi_channel() ? 2*8 : 2; } long sample_rate_; - blargg_long msec_to_samples( blargg_long msec ) const; - + int32_t msec_to_samples( int32_t msec ) const; + // track-specific int current_track_; - blargg_long out_time; // number of samples played since start of track - blargg_long emu_time; // number of samples emulator has generated since start of track - bool emu_track_ended_; // emulator has reached end of track + int32_t out_time; // number of samples played since start of track + int32_t out_time_scaled; // number of samples played since start of track (scaled with tempo) + int32_t emu_time; // number of samples emulator has generated since start of track + bool emu_track_ended_; // emulator has reached end of track bool emu_autoload_playback_limit_; // whether to load and obey track length by default volatile bool track_ended_; void clear_track_vars(); void end_track_if_error( blargg_err_t ); - + // fading - blargg_long fade_start; + int32_t fade_start; int fade_step; void handle_fade( long count, sample_t* out ); - + // silence detection int silence_lookahead; // speed to run emulator when looking ahead for silence bool ignore_silence_; @@ -197,7 +208,7 @@ private: blargg_vector buf; void fill_buf(); void emu_play( long count, sample_t* out ); - + Multi_Buffer* effects_buffer; friend Music_Emu* gme_internal_new_emu_( gme_type_t, int, bool ); friend void gme_set_stereo_depth( Music_Emu*, double ); @@ -233,7 +244,12 @@ inline void Music_Emu::enable_accuracy( bool b ) { enable_accuracy_( b ); } inline void Music_Emu::set_tempo_( double t ) { tempo_ = t; } inline void Music_Emu::remute_voices() { mute_voices( mute_mask_ ); } inline void Music_Emu::ignore_silence( bool b ) { ignore_silence_ = b; } -inline blargg_err_t Music_Emu::start_track_( int ) { return 0; } +inline blargg_err_t Music_Emu::start_track_( int track ) +{ + if ( type()->track_count == 1 ) + return load_mem_( track_pos( track ), track_size( track ) ); + return 0; +} inline void Music_Emu::set_voice_names( const char* const* names ) { @@ -243,6 +259,8 @@ inline void Music_Emu::set_voice_names( const char* const* names ) inline void Music_Emu::mute_voices_( int ) { } +inline void Music_Emu::disable_echo_( bool ) { } + inline void Music_Emu::set_gain( double g ) { assert( !sample_rate() ); // you must set gain before setting sample rate diff --git a/Frameworks/GME/gme/Nes_Apu.cpp b/Frameworks/GME/gme/Nes_Apu.cpp index 68edb446d..0718aa051 100644 --- a/Frameworks/GME/gme/Nes_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Apu.cpp @@ -15,7 +15,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -int const amp_range = 15; +static int const amp_range = 15; Nes_Apu::Nes_Apu() : square1( &square_synth ), @@ -25,13 +25,13 @@ Nes_Apu::Nes_Apu() : dmc.apu = this; dmc.prg_reader = NULL; irq_notifier_ = NULL; - + oscs [0] = &square1; oscs [1] = &square2; oscs [2] = ▵ oscs [3] = &noise; oscs [4] = &dmc; - + output( NULL ); volume( 1.0 ); reset( false ); @@ -49,12 +49,12 @@ void Nes_Apu::enable_nonlinear( double v ) { dmc.nonlinear = true; square_synth.volume( 1.3 * 0.25751258 / 0.742467605 * 0.25 / amp_range * v ); - + const double tnd = 0.48 / 202 * nonlinear_tnd_gain(); triangle.synth.volume( 3.0 * tnd ); noise.synth.volume( 2.0 * tnd ); dmc.synth.volume( tnd ); - + square1 .last_amp = 0; square2 .last_amp = 0; triangle.last_amp = 0; @@ -89,13 +89,13 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) { dmc.pal_mode = pal_mode; set_tempo( tempo_ ); - + square1.reset(); square2.reset(); triangle.reset(); noise.reset(); dmc.reset(); - + last_time = 0; last_dmc_time = 0; osc_enables = 0; @@ -104,10 +104,10 @@ void Nes_Apu::reset( bool pal_mode, int initial_dmc_dac ) frame_delay = 1; write_register( 0, 0x4017, 0x00 ); write_register( 0, 0x4015, 0x00 ); - + for ( nes_addr_t addr = start_addr; addr <= 0x4013; addr++ ) write_register( 0, addr, (addr & 3) ? 0x00 : 0x10 ); - + dmc.dac = initial_dmc_dac; if ( !dmc.nonlinear ) triangle.last_amp = 15; @@ -124,7 +124,7 @@ void Nes_Apu::irq_changed() else if ( new_irq > next_irq ) { new_irq = next_irq; } - + if ( new_irq != earliest_irq_ ) { earliest_irq_ = new_irq; if ( irq_notifier_ ) @@ -148,17 +148,17 @@ void Nes_Apu::run_until( nes_time_t end_time ) void Nes_Apu::run_until_( nes_time_t end_time ) { require( end_time >= last_time ); - + if ( end_time == last_time ) return; - + if ( last_dmc_time < end_time ) { nes_time_t start = last_dmc_time; last_dmc_time = end_time; dmc.run( start, end_time ); } - + while ( true ) { // earlier of next frame time or end time @@ -166,17 +166,17 @@ void Nes_Apu::run_until_( nes_time_t end_time ) if ( time > end_time ) time = end_time; frame_delay -= time - last_time; - + // run oscs to present square1.run( last_time, time ); square2.run( last_time, time ); triangle.run( last_time, time ); noise.run( last_time, time ); last_time = time; - + if ( time == end_time ) break; // no more frames to run - + // take frame-specific actions frame_delay = frame_period; switch ( frame++ ) @@ -193,30 +193,30 @@ void Nes_Apu::run_until_( nes_time_t end_time ) square2.clock_length( 0x20 ); noise.clock_length( 0x20 ); triangle.clock_length( 0x80 ); // different bit for halt flag on triangle - + square1.clock_sweep( -1 ); square2.clock_sweep( 0 ); - + // frame 2 is slightly shorter in mode 1 if ( dmc.pal_mode && frame == 3 ) frame_delay -= 2; break; - + case 1: // frame 1 is slightly shorter in mode 0 if ( !dmc.pal_mode ) frame_delay -= 2; break; - + case 3: frame = 0; - + // frame 3 is almost twice as long in mode 1 if ( frame_mode & 0x80 ) frame_delay += frame_period - (dmc.pal_mode ? 2 : 6); break; } - + // clock envelopes and linear counter every frame triangle.clock_linear_counter(); square1.clock_envelope(); @@ -239,7 +239,7 @@ void Nes_Apu::end_frame( nes_time_t end_time ) { if ( end_time > last_time ) run_until_( end_time ); - + if ( dmc.nonlinear ) { zero_apu_osc( &square1, last_time ); @@ -248,14 +248,14 @@ void Nes_Apu::end_frame( nes_time_t end_time ) zero_apu_osc( &noise, last_time ); zero_apu_osc( &dmc, last_time ); } - + // make times relative to new frame last_time -= end_time; require( last_time >= 0 ); - + last_dmc_time -= end_time; require( last_dmc_time >= 0 ); - + if ( next_irq != no_irq ) { next_irq -= end_time; check( next_irq >= 0 ); @@ -275,7 +275,7 @@ void Nes_Apu::end_frame( nes_time_t end_time ) static const unsigned char length_table [0x20] = { 0x0A, 0xFE, 0x14, 0x02, 0x28, 0x04, 0x50, 0x06, - 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, + 0xA0, 0x08, 0x3C, 0x0A, 0x0E, 0x0C, 0x1A, 0x0E, 0x0C, 0x10, 0x18, 0x12, 0x30, 0x14, 0x60, 0x16, 0xC0, 0x18, 0x48, 0x1A, 0x10, 0x1C, 0x20, 0x1E }; @@ -284,23 +284,23 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) { require( addr > 0x20 ); // addr must be actual address (i.e. 0x40xx) require( (unsigned) data <= 0xFF ); - + // Ignore addresses outside range if ( unsigned (addr - start_addr) > end_addr - start_addr ) return; - + run_until_( time ); - + if ( addr < 0x4014 ) { // Write to channel int osc_index = (addr - start_addr) >> 2; Nes_Osc* osc = oscs [osc_index]; - + int reg = addr & 3; osc->regs [reg] = data; osc->reg_written [reg] = true; - + if ( osc_index == 4 ) { // handle DMC specially @@ -311,7 +311,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) // load length counter if ( (osc_enables >> osc_index) & 1 ) osc->length_counter = length_table [(data >> 3) & 0x1F]; - + // reset square phase if ( osc_index < 2 ) ((Nes_Square*) osc)->phase = Nes_Square::phase_range - 1; @@ -323,10 +323,10 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) for ( int i = osc_count; i--; ) if ( !((data >> i) & 1) ) oscs [i]->length_counter = 0; - + bool recalc_irq = dmc.irq_flag; dmc.irq_flag = false; - + int old_enables = osc_enables; osc_enables = data; if ( !(data & 0x10) ) { @@ -336,7 +336,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) else if ( !(old_enables & 0x10) ) { dmc.start(); // dmc just enabled } - + if ( recalc_irq ) irq_changed(); } @@ -344,15 +344,15 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) { // Frame mode frame_mode = data; - + bool irq_enabled = !(data & 0x40); irq_flag &= irq_enabled; next_irq = no_irq; - + // mode 1 frame_delay = (frame_delay & 1); frame = 0; - + if ( !(data & 0x80) ) { // mode 0 @@ -361,7 +361,7 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) if ( irq_enabled ) next_irq = time + frame_delay + frame_period * 3 + 1; } - + irq_changed(); } } @@ -369,23 +369,23 @@ void Nes_Apu::write_register( nes_time_t time, nes_addr_t addr, int data ) int Nes_Apu::read_status( nes_time_t time ) { run_until_( time - 1 ); - + int result = (dmc.irq_flag << 7) | (irq_flag << 6); - + for ( int i = 0; i < osc_count; i++ ) if ( oscs [i]->length_counter ) result |= 1 << i; - + run_until_( time ); - + if ( irq_flag ) { result |= 0x40; irq_flag = false; irq_changed(); } - + //debug_printf( "%6d/%d Read $4015->$%02X\n", frame_delay, frame, result ); - + return result; } diff --git a/Frameworks/GME/gme/Nes_Apu.h b/Frameworks/GME/gme/Nes_Apu.h index 5e722248f..1650e6be8 100644 --- a/Frameworks/GME/gme/Nes_Apu.h +++ b/Frameworks/GME/gme/Nes_Apu.h @@ -6,7 +6,7 @@ #include "blargg_common.h" -typedef blargg_long nes_time_t; // CPU clock cycle count +typedef int32_t nes_time_t; // CPU clock cycle count typedef unsigned nes_addr_t; // 16-bit memory address #include "Nes_Oscs.h" @@ -18,30 +18,30 @@ class Nes_Apu { public: // Set buffer to generate all sound into, or disable sound if NULL void output( Blip_Buffer* ); - + // Set memory reader callback used by DMC oscillator to fetch samples. // When callback is invoked, 'user_data' is passed unchanged as the // first parameter. void dmc_reader( int (*callback)( void* user_data, nes_addr_t ), void* user_data = NULL ); - + // All time values are the number of CPU clock cycles relative to the // beginning of the current time frame. Before resetting the CPU clock // count, call end_frame( last_cpu_time ). - + // Write to register (0x4000-0x4017, except 0x4014 and 0x4016) - enum { start_addr = 0x4000 }; - enum { end_addr = 0x4017 }; + static const unsigned int start_addr = 0x4000; + static const unsigned int end_addr = 0x4017; void write_register( nes_time_t, nes_addr_t, int data ); - + // Read from status register at 0x4015 - enum { status_addr = 0x4015 }; + static const unsigned int status_addr = 0x4015; int read_status( nes_time_t ); - + // Run all oscillators up to specified time, end current time frame, then // start a new time frame at time 0. Time frames have no effect on emulation // and each can be whatever length is convenient. void end_frame( nes_time_t ); - + // Additional optional features (can be ignored without any problem) // Reset internal frame counter, registers, and all oscillators. @@ -49,51 +49,51 @@ public: // Set the DMC oscillator's initial DAC value to initial_dmc_dac without // any audible click. void reset( bool pal_mode = false, int initial_dmc_dac = 0 ); - + // Adjust frame period void set_tempo( double ); - + // Save/load exact emulation state void save_state( apu_state_t* out ) const; void load_state( apu_state_t const& ); - + // Set overall volume (default is 1.0) void volume( double ); - + // Set treble equalization (see notes.txt) void treble_eq( const blip_eq_t& ); - + // Set sound output of specific oscillator to buffer. If buffer is NULL, // the specified oscillator is muted and emulation accuracy is reduced. // The oscillators are indexed as follows: 0) Square 1, 1) Square 2, // 2) Triangle, 3) Noise, 4) DMC. - enum { osc_count = 5 }; + static const int osc_count = 5; void osc_output( int index, Blip_Buffer* buffer ); - + // Set IRQ time callback that is invoked when the time of earliest IRQ // may have changed, or NULL to disable. When callback is invoked, // 'user_data' is passed unchanged as the first parameter. void irq_notifier( void (*callback)( void* user_data ), void* user_data = NULL ); - + // Get time that APU-generated IRQ will occur if no further register reads // or writes occur. If IRQ is already pending, returns irq_waiting. If no // IRQ will occur, returns no_irq. - enum { no_irq = INT_MAX / 2 + 1 }; - enum { irq_waiting = 0 }; + static const unsigned int no_irq = INT_MAX / 2 + 1; + static const unsigned int irq_waiting = 0; nes_time_t earliest_irq( nes_time_t ) const; - + // Count number of DMC reads that would occur if 'run_until( t )' were executed. // If last_read is not NULL, set *last_read to the earliest time that // 'count_dmc_reads( time )' would result in the same result. int count_dmc_reads( nes_time_t t, nes_time_t* last_read = NULL ) const; - + // Time when next DMC memory read will occur nes_time_t next_dmc_read_time() const; - + // Run DMC until specified time, so that any DMC memory reads can be // accounted for (i.e. inserting CPU wait states). void run_until( nes_time_t ); - + public: Nes_Apu(); BLARGG_DISABLE_NOTHROW @@ -103,18 +103,18 @@ private: static double nonlinear_tnd_gain() { return 0.75; } private: friend struct Nes_Dmc; - + // noncopyable Nes_Apu( const Nes_Apu& ); Nes_Apu& operator = ( const Nes_Apu& ); - + Nes_Osc* oscs [osc_count]; Nes_Square square1; Nes_Square square2; Nes_Noise noise; Nes_Triangle triangle; Nes_Dmc dmc; - + double tempo_; nes_time_t last_time; // has been run until this time in current frame nes_time_t last_dmc_time; @@ -129,11 +129,11 @@ private: void (*irq_notifier_)( void* user_data ); void* irq_data; Nes_Square::Synth square_synth; // shared by squares - + void irq_changed(); void state_restored(); void run_until_( nes_time_t ); - + // TODO: remove friend class Nes_Core; }; @@ -165,12 +165,12 @@ inline int Nes_Apu::count_dmc_reads( nes_time_t time, nes_time_t* last_read ) co { return dmc.count_reads( time, last_read ); } - + inline nes_time_t Nes_Dmc::next_read_time() const { if ( length_counter == 0 ) return Nes_Apu::no_irq; // not reading - + return apu->last_dmc_time + delay + long (bits_remain - 1) * period; } diff --git a/Frameworks/GME/gme/Nes_Cpu.cpp b/Frameworks/GME/gme/Nes_Cpu.cpp index 976397290..c3c7e8aa3 100644 --- a/Frameworks/GME/gme/Nes_Cpu.cpp +++ b/Frameworks/GME/gme/Nes_Cpu.cpp @@ -53,14 +53,16 @@ inline void Nes_Cpu::set_code_page( int i, void const* p ) state->code_map [i] = (uint8_t const*) p - PAGE_OFFSET( i * page_size ); } -int const st_n = 0x80; -int const st_v = 0x40; -int const st_r = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; +enum { + st_n = 0x80, + st_v = 0x40, + st_r = 0x20, + st_b = 0x10, + st_d = 0x08, + st_i = 0x04, + st_z = 0x02, + st_c = 0x01 +}; void Nes_Cpu::reset( void const* unmapped_page ) { @@ -77,12 +79,12 @@ void Nes_Cpu::reset( void const* unmapped_page ) irq_time_ = future_nes_time; end_time_ = future_nes_time; error_count_ = 0; - - assert( page_size == 0x800 ); // assumes this + + blaarg_static_assert( page_size == 0x800, "NES set to use unhandled page size" ); // assumes this set_code_page( page_count, unmapped_page ); map_code( 0x2000, 0xE000, unmapped_page, true ); map_code( 0x0000, 0x2000, low_mem, true ); - + blargg_verify_byte_order(); } @@ -92,7 +94,7 @@ void Nes_Cpu::map_code( nes_addr_t start, unsigned size, void const* data, bool require( start % page_size == 0 ); require( size % page_size == 0 ); require( start + size <= 0x10000 ); - + unsigned page = start / page_size; for ( unsigned n = size / page_size; n; --n ) { @@ -121,7 +123,7 @@ bool Nes_Cpu::run( nes_time_t end_time ) this->state = &s; // even on x86, using s.time in place of s_time was slower int16_t s_time = s.time; - + // registers uint16_t pc = r.pc; uint8_t a = r.a; @@ -129,10 +131,10 @@ bool Nes_Cpu::run( nes_time_t end_time ) uint8_t y = r.y; uint16_t sp; SET_SP( r.sp ); - + // status flags #define IS_NEG (nz & 0x8080) - + #define CALC_STATUS( out ) do {\ out = status & (st_v | st_d | st_i);\ out |= ((nz >> 8) | nz) & st_n;\ @@ -146,7 +148,7 @@ bool Nes_Cpu::run( nes_time_t end_time ) c = nz;\ nz |= ~in & st_z;\ } while ( 0 ) - + uint8_t status; uint16_t c; // carry set if (c & 0x100) != 0 uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 @@ -154,22 +156,22 @@ bool Nes_Cpu::run( nes_time_t end_time ) uint8_t temp = r.status; SET_STATUS( temp ); } - + goto loop; dec_clock_loop: s_time--; loop: - + check( (unsigned) GET_SP() < 0x100 ); check( (unsigned) pc < 0x10000 ); check( (unsigned) a < 0x100 ); check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); check( -32768 <= s_time && s_time < 32767 ); - + uint8_t const* instr = s.code_map [pc >> page_bits]; uint8_t opcode; - + // TODO: eliminate this special case #if BLARGG_NONPORTABLE opcode = instr [pc]; @@ -180,7 +182,7 @@ loop: opcode = *instr++; pc++; #endif - + static uint8_t const clock_table [256] = {// 0 1 2 3 4 5 6 7 8 9 A B C D E F 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 @@ -200,16 +202,16 @@ loop: 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E 3,5,0,8,4,4,6,6,2,4,2,7,4,4,7,7 // F }; // 0x00 was 7 and 0xF2 was 2 - + uint16_t data; - + #if !BLARGG_CPU_X86 if ( s_time >= 0 ) goto out_of_time; s_time += clock_table [opcode]; - + data = *instr; - + switch ( opcode ) { #else @@ -218,9 +220,9 @@ loop: if ( (s_time += data) >= 0 ) goto possibly_out_of_time; almost_out_of_time: - + data = *instr; - + switch ( opcode ) { possibly_out_of_time: @@ -246,12 +248,12 @@ possibly_out_of_time: out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ cross( temp );\ } - + #define IND_X( out ) {\ uint16_t temp = data + x;\ out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ } - + #define ARITH_ADDR_MODES( op )\ case op - 0x04: /* (ind,x) */\ IND_X( data )\ @@ -297,15 +299,15 @@ imm##op: a = nz = READ_LOW( uint8_t (data + x) ); pc++; goto loop; - + case 0xA5: // LDA zp a = nz = READ_LOW( data ); pc++; goto loop; - + case 0xD0: // BNE BRANCH( (uint8_t) nz ); - + case 0x20: { // JSR uint16_t temp = pc + 1; pc = GET_ADDR(); @@ -314,37 +316,37 @@ imm##op: WRITE_LOW( sp, temp ); goto loop; } - + case 0x4C: // JMP abs pc = GET_ADDR(); goto loop; - + case 0xE8: // INX INC_DEC_XY( x, 1 ) - + case 0x10: // BPL BRANCH( !IS_NEG ) - + ARITH_ADDR_MODES( 0xC5 ) // CMP nz = a - data; pc++; c = ~nz; nz &= 0xFF; goto loop; - + case 0x30: // BMI BRANCH( IS_NEG ) - + case 0xF0: // BEQ BRANCH( !(uint8_t) nz ); - + case 0x95: // STA zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0x85: // STA zp pc++; WRITE_LOW( data, a ); goto loop; - + case 0xC8: // INY INC_DEC_XY( y, 1 ) @@ -352,12 +354,12 @@ imm##op: y = a; nz = a; goto loop; - + case 0x98: // TYA a = y; nz = y; goto loop; - + case 0xAD:{// LDA abs unsigned addr = GET_ADDR(); pc += 2; @@ -365,16 +367,16 @@ imm##op: a = nz; goto loop; } - + case 0x60: // RTS pc = 1 + READ_LOW( sp ); pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); sp = (sp - 0xFE) | 0x100; goto loop; - + { uint16_t addr; - + case 0x99: // STA abs,Y addr = y + GET_ADDR(); pc += 2; @@ -384,7 +386,7 @@ imm##op: goto loop; } goto sta_ptr; - + case 0x8D: // STA abs addr = GET_ADDR(); pc += 2; @@ -394,7 +396,7 @@ imm##op: goto loop; } goto sta_ptr; - + case 0x9D: // STA abs,X (slightly more common than STA abs) addr = x + GET_ADDR(); pc += 2; @@ -408,19 +410,19 @@ imm##op: WRITE( addr, a ); CACHE_TIME(); goto loop; - + case 0x91: // STA (ind),Y IND_Y( NO_PAGE_CROSSING, addr ) pc++; goto sta_ptr; - + case 0x81: // STA (ind,X) IND_X( addr ) pc++; goto sta_ptr; - + } - + case 0xA9: // LDA #imm pc++; a = data; @@ -430,12 +432,12 @@ imm##op: // common read instructions { uint16_t addr; - + case 0xA1: // LDA (ind,X) IND_X( addr ) pc++; goto a_nz_read_addr; - + case 0xB1:// LDA (ind),Y addr = READ_LOW( data ) + y; HANDLE_PAGE_CROSSING( addr ); @@ -445,7 +447,7 @@ imm##op: if ( (addr ^ 0x8000) <= 0x9FFF ) goto loop; goto a_nz_read_addr; - + case 0xB9: // LDA abs,Y HANDLE_PAGE_CROSSING( data + y ); addr = GET_ADDR() + y; @@ -454,7 +456,7 @@ imm##op: if ( (addr ^ 0x8000) <= 0x9FFF ) goto loop; goto a_nz_read_addr; - + case 0xBD: // LDA abs,X HANDLE_PAGE_CROSSING( data + x ); addr = GET_ADDR() + x; @@ -467,39 +469,39 @@ imm##op: a = nz = READ( addr ); CACHE_TIME(); goto loop; - + } // Branch case 0x50: // BVC BRANCH( !(status & st_v) ) - + case 0x70: // BVS BRANCH( status & st_v ) - + case 0xB0: // BCS BRANCH( c & 0x100 ) - + case 0x90: // BCC BRANCH( !(c & 0x100) ) - + // Load/store - + case 0x94: // STY zp,x data = uint8_t (data + x); // FALLTHRU case 0x84: // STY zp pc++; WRITE_LOW( data, y ); goto loop; - + case 0x96: // STX zp,y data = uint8_t (data + y); // FALLTHRU case 0x86: // STX zp pc++; WRITE_LOW( data, x ); goto loop; - + case 0xB6: // LDX zp,y data = uint8_t (data + y); // FALLTHRU case 0xA6: // LDX zp @@ -509,7 +511,7 @@ imm##op: x = data; nz = data; goto loop; - + case 0xB4: // LDY zp,x data = uint8_t (data + x); // FALLTHRU case 0xA4: // LDY zp @@ -519,7 +521,7 @@ imm##op: y = data; nz = data; goto loop; - + case 0xBC: // LDY abs,X data += x; HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ @@ -531,7 +533,7 @@ imm##op: CACHE_TIME(); goto loop; } - + case 0xBE: // LDX abs,y data += y; HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ @@ -543,13 +545,13 @@ imm##op: CACHE_TIME(); goto loop; } - + { uint8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; - + case 0x8E: // STX abs temp = x; store_abs: @@ -576,7 +578,7 @@ imm##op: CACHE_TIME(); goto cpx_data; } - + case 0xE4: // CPX zp data = READ_LOW( data );/*FALLTHRU*/ case 0xE0: // CPX #imm @@ -586,7 +588,7 @@ imm##op: c = ~nz; nz &= 0xFF; goto loop; - + case 0xCC:{// CPY abs unsigned addr = GET_ADDR(); pc++; @@ -595,7 +597,7 @@ imm##op: CACHE_TIME(); goto cpy_data; } - + case 0xC4: // CPY zp data = READ_LOW( data );/*FALLTHRU*/ case 0xC0: // CPY #imm @@ -605,24 +607,24 @@ imm##op: c = ~nz; nz &= 0xFF; goto loop; - + // Logical ARITH_ADDR_MODES( 0x25 ) // AND nz = (a &= data); pc++; goto loop; - + ARITH_ADDR_MODES( 0x45 ) // EOR nz = (a ^= data); pc++; goto loop; - + ARITH_ADDR_MODES( 0x05 ) // ORA nz = (a |= data); pc++; goto loop; - + case 0x2C:{// BIT abs unsigned addr = GET_ADDR(); pc += 2; @@ -634,7 +636,7 @@ imm##op: nz <<= 8; // result must be zero, even if N bit is set goto loop; } - + case 0x24: // BIT zp nz = READ_LOW( data ); pc++; @@ -644,14 +646,14 @@ imm##op: goto loop; nz <<= 8; // result must be zero, even if N bit is set goto loop; - + // Add/subtract ARITH_ADDR_MODES( 0xE5 ) // SBC case 0xEB: // unofficial equivalent data ^= 0xFF; goto adc_imm; - + ARITH_ADDR_MODES( 0x65 ) // ADC adc_imm: { int16_t carry = c >> 8 & 1; @@ -663,7 +665,7 @@ imm##op: a = (uint8_t) nz; goto loop; } - + // Shift/rotate case 0x4A: // LSR A @@ -689,7 +691,7 @@ imm##op: a = (uint8_t) nz; goto loop; } - + case 0x5E: // LSR abs,X data += x;/*FALLTHRU*/ case 0x4E: // LSR abs @@ -703,11 +705,11 @@ imm##op: c = temp << 8; goto rotate_common; } - + case 0x3E: // ROL abs,X data += x; goto rol_abs; - + case 0x1E: // ASL abs,X data += x;/*FALLTHRU*/ case 0x0E: // ASL abs @@ -723,15 +725,15 @@ imm##op: WRITE( data, (uint8_t) nz ); CACHE_TIME(); goto loop; - + case 0x7E: // ROR abs,X data += x; goto ror_abs; - + case 0x76: // ROR zp,x data = uint8_t (data + x); goto ror_zp; - + case 0x56: // LSR zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0x46: // LSR zp @@ -743,11 +745,11 @@ imm##op: c = temp << 8; goto write_nz_zp; } - + case 0x36: // ROL zp,x data = uint8_t (data + x); goto rol_zp; - + case 0x16: // ASL zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0x06: // ASL zp @@ -757,21 +759,21 @@ imm##op: nz = c >> 8 & 1; nz |= (c = READ_LOW( data ) << 1); goto write_nz_zp; - + // Increment/decrement case 0xCA: // DEX INC_DEC_XY( x, -1 ) - + case 0x88: // DEY INC_DEC_XY( y, -1 ) - + case 0xF6: // INC zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0xE6: // INC zp nz = 1; goto add_nz_zp; - + case 0xD6: // DEC zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0xC6: // DEC zp @@ -782,21 +784,21 @@ imm##op: pc++; WRITE_LOW( data, nz ); goto loop; - + case 0xFE: // INC abs,x data = x + GET_ADDR(); goto inc_ptr; - + case 0xEE: // INC abs data = GET_ADDR(); inc_ptr: nz = 1; goto inc_common; - + case 0xDE: // DEC abs,x data = x + GET_ADDR(); goto dec_ptr; - + case 0xCE: // DEC abs data = GET_ADDR(); dec_ptr: @@ -808,14 +810,14 @@ imm##op: WRITE( data, (uint8_t) nz ); CACHE_TIME(); goto loop; - + // Transfer case 0xAA: // TAX x = a; nz = a; goto loop; - + case 0x8A: // TXA a = x; nz = x; @@ -824,22 +826,22 @@ imm##op: case 0x9A: // TXS SET_SP( x ); // verified (no flag change) goto loop; - + case 0xBA: // TSX x = nz = GET_SP(); goto loop; - + // Stack - + case 0x48: // PHA PUSH( a ); // verified goto loop; - + case 0x68: // PLA a = nz = READ_LOW( sp ); sp = (sp - 0xFF) | 0x100; goto loop; - + case 0x40:{// RTI uint8_t temp = READ_LOW( sp ); pc = READ_LOW( 0x100 | (sp - 0xFF) ); @@ -849,14 +851,14 @@ imm##op: SET_STATUS( temp ); if ( !((data ^ status) & st_i) ) goto loop; // I flag didn't change this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; + int32_t delta = s.base - irq_time_; if ( delta <= 0 ) goto loop; if ( status & st_i ) goto loop; s_time += delta; s.base = irq_time_; goto loop; } - + case 0x28:{// PLP uint8_t temp = READ_LOW( sp ); sp = (sp - 0xFF) | 0x100; @@ -868,14 +870,14 @@ imm##op: goto handle_sei; goto handle_cli; } - + case 0x08: { // PHP uint8_t temp; CALC_STATUS( temp ); PUSH( temp | (st_b | st_r) ); goto loop; } - + case 0x6C:{// JMP (ind) data = GET_ADDR(); check( unsigned (data - 0x2000) >= 0x4000 ); // ensure it's outside I/O space @@ -885,32 +887,32 @@ imm##op: pc |= page [PAGE_OFFSET( data )] << 8; goto loop; } - + case 0x00: // BRK goto handle_brk; - + // Flags case 0x38: // SEC c = (uint16_t) ~0; goto loop; - + case 0x18: // CLC c = 0; goto loop; - + case 0xB8: // CLV status &= ~st_v; goto loop; - + case 0xD8: // CLD status &= ~st_d; goto loop; - + case 0xF8: // SED status |= st_d; goto loop; - + case 0x58: // CLI if ( !(status & st_i) ) goto loop; @@ -918,7 +920,7 @@ imm##op: handle_cli: { //debug_printf( "CLI at %d\n", TIME ); this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; + int32_t delta = s.base - irq_time_; if ( delta <= 0 ) { if ( TIME < irq_time_ ) @@ -929,38 +931,74 @@ imm##op: s_time += delta; if ( s_time < 0 ) goto loop; - + if ( delta >= s_time + 1 ) { s.base += s_time + 1; s_time = -1; goto loop; } - + // TODO: implement delayed_cli: debug_printf( "Delayed CLI not emulated\n" ); goto loop; } - + case 0x78: // SEI if ( status & st_i ) goto loop; status |= st_i; handle_sei: { this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; + int32_t delta = s.base - end_time_; s.base = end_time_; s_time += delta; if ( s_time < 0 ) goto loop; - + debug_printf( "Delayed SEI not emulated\n" ); goto loop; } - + // Unofficial - + + case 0xB3: { // LAX (ind),Y + uint16_t addr = READ_LOW( data ) + y; + HANDLE_PAGE_CROSSING( addr ); + addr += 0x100 * READ_LOW( (uint8_t) (data + 1) ); + pc++; + a = x = nz = READ_PROG( addr ); + if ( (addr ^ 0x8000) <= 0x9FFF ) + goto loop; + FLUSH_TIME(); + a = x = nz = READ( addr ); + CACHE_TIME(); + goto loop; + } + + case 0x8F: { // SAX abs + uint16_t addr = GET_ADDR(); + uint8_t temp = a & x; + pc += 2; + if ( addr <= 0x7FF ) + { + WRITE_LOW( addr, temp ); + goto loop; + } + FLUSH_TIME(); + WRITE( addr, temp ); + CACHE_TIME(); + goto loop; + } + + case 0xCB: // SBX #imm + x = nz = (a & x) - data; + pc++; + c = ~nz; + nz &= 0xFF; + goto loop; + // SKW - Skip word case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/ @@ -971,7 +1009,7 @@ imm##op: case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: pc++; goto loop; - + // NOP case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: goto loop; @@ -981,9 +1019,9 @@ imm##op: case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: goto stop; - + // Unimplemented - + case 0xFF: // force 256-entry jump table for optimization purposes c |= 1;/*FALLTHRU*/ default: @@ -998,31 +1036,29 @@ imm##op: len = 2; pc += len; error_count_++; - + if ( (opcode >> 4) == 0x0B ) { - if ( opcode == 0xB3 ) - data = READ_LOW( data ); if ( opcode != 0xB7 ) HANDLE_PAGE_CROSSING( data + y ); } goto loop; } assert( false ); - + int result_; handle_brk: pc++; result_ = 4; - + interrupt: { s_time += 7; - + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); WRITE_LOW( 0x100 | (sp - 2), pc ); pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); - + sp = (sp - 3) | 0x100; uint8_t temp; CALC_STATUS( temp ); @@ -1030,15 +1066,15 @@ interrupt: if ( result_ ) temp |= st_b; // TODO: incorrectly sets B flag for IRQ WRITE_LOW( sp, temp ); - + this->r.status = status |= st_i; - blargg_long delta = s.base - end_time_; + int32_t delta = s.base - end_time_; if ( delta >= 0 ) goto loop; s_time += delta; s.base = end_time_; goto loop; } - + out_of_time: pc--; FLUSH_TIME(); @@ -1048,26 +1084,26 @@ out_of_time: goto interrupt; if ( s_time < 0 ) goto loop; - + stop: - + s.time = s_time; - + r.pc = pc; r.sp = GET_SP(); r.a = a; r.x = x; r.y = y; - + { uint8_t temp; CALC_STATUS( temp ); r.status = temp; } - + this->state_ = s; this->state = &this->state_; - + return s_time < 0; } diff --git a/Frameworks/GME/gme/Nes_Cpu.h b/Frameworks/GME/gme/Nes_Cpu.h index 878b5ba5c..231a9f454 100644 --- a/Frameworks/GME/gme/Nes_Cpu.h +++ b/Frameworks/GME/gme/Nes_Cpu.h @@ -6,7 +6,7 @@ #include "blargg_common.h" -typedef blargg_long nes_time_t; // clock cycle count +typedef int32_t nes_time_t; // clock cycle count typedef unsigned nes_addr_t; // 16-bit address enum { future_nes_time = INT_MAX / 2 + 1 }; @@ -15,19 +15,19 @@ public: // Clear registers, map low memory and its three mirrors to address 0, // and mirror unmapped_page in remaining memory void reset( void const* unmapped_page = 0 ); - + // Map code memory (memory accessed via the program counter). Start and size // must be multiple of page_size. If mirror is true, repeats code page // throughout address range. enum { page_size = 0x800 }; void map_code( nes_addr_t start, unsigned size, void const* code, bool mirror = false ); - + // Access emulated memory as CPU does uint8_t const* get_code( nes_addr_t ); - + // 2KB of RAM at address 0 uint8_t low_mem [0x800]; - + // NES 6502 registers. Not kept updated during a call to run(). struct registers_t { uint16_t pc; @@ -38,29 +38,29 @@ public: uint8_t sp; }; registers_t r; - + // Set end_time and run CPU from current time. Returns true if execution // stopped due to encountering bad_opcode. bool run( nes_time_t end_time ); - + // Time of beginning of next instruction to be executed nes_time_t time() const { return state->time + state->base; } void set_time( nes_time_t t ) { state->time = t - state->base; } void adjust_time( int delta ) { state->time += delta; } - + nes_time_t irq_time() const { return irq_time_; } void set_irq_time( nes_time_t ); - + nes_time_t end_time() const { return end_time_; } void set_end_time( nes_time_t ); - + // Number of undefined instructions encountered and skipped void clear_error_count() { error_count_ = 0; } unsigned long error_count() const { return error_count_; } - + // CPU invokes bad opcode handler if it encounters this enum { bad_opcode = 0xF2 }; - + public: Nes_Cpu() { state = &state_; } enum { page_bits = 11 }; @@ -77,7 +77,7 @@ private: nes_time_t irq_time_; nes_time_t end_time_; unsigned long error_count_; - + void set_code_page( int, void const* ); inline int update_end_time( nes_time_t end, nes_time_t irq ); }; diff --git a/Frameworks/GME/gme/Nes_Fds_Apu.cpp b/Frameworks/GME/gme/Nes_Fds_Apu.cpp index 30ffedded..82632a6b3 100644 --- a/Frameworks/GME/gme/Nes_Fds_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Fds_Apu.cpp @@ -17,13 +17,13 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include -int const fract_range = 65536; +static int const fract_range = 65536; void Nes_Fds_Apu::reset() { memset( regs_, 0, sizeof regs_ ); memset( mod_wave, 0, sizeof mod_wave ); - + last_time = 0; env_delay = 0; sweep_delay = 0; @@ -33,7 +33,7 @@ void Nes_Fds_Apu::reset() mod_fract = fract_range; mod_pos = 0; mod_write_pos = 0; - + static byte const initial_regs [0x0B] = { 0x80, // disable envelope 0, 0, 0xC0, // disable wave and lfo @@ -70,19 +70,19 @@ void Nes_Fds_Apu::write_( unsigned addr, int data ) else env_speed = (data & 0x3F) + 1; break; - + case 0x4084: if ( data & 0x80 ) sweep_gain = data & 0x3F; else sweep_speed = (data & 0x3F) + 1; break; - + case 0x4085: mod_pos = mod_write_pos; regs (0x4085) = data & 0x7F; break; - + case 0x4088: if ( regs (0x4087) & 0x80 ) { @@ -117,36 +117,36 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) if ( wave_freq && output_ && !((regs (0x4089) | regs (0x4083)) & 0x80) ) { output_->set_modified(); - + // master_volume #define MVOL_ENTRY( percent ) (master_vol_max * percent + 50) / 100 static unsigned char const master_volumes [4] = { MVOL_ENTRY( 100 ), MVOL_ENTRY( 67 ), MVOL_ENTRY( 50 ), MVOL_ENTRY( 40 ) }; int const master_volume = master_volumes [regs (0x4089) & 0x03]; - + // lfo_period blip_time_t lfo_period = regs (0x408A) * lfo_tempo; if ( regs (0x4083) & 0x40 ) lfo_period = 0; - + // sweep setup blip_time_t sweep_time = last_time + sweep_delay; blip_time_t const sweep_period = lfo_period * sweep_speed; if ( !sweep_period || regs (0x4084) & 0x80 ) sweep_time = final_end_time; - + // envelope setup blip_time_t env_time = last_time + env_delay; blip_time_t const env_period = lfo_period * env_speed; if ( !env_period || regs (0x4080) & 0x80 ) env_time = final_end_time; - + // modulation int mod_freq = 0; if ( !(regs (0x4087) & 0x80) ) mod_freq = (regs (0x4087) & 0x0F) * 0x100 + regs (0x4086); - + blip_time_t end_time = last_time; do { @@ -161,7 +161,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) else regs (0x4084) |= 0x80; // optimization only } - + // envelope if ( env_time <= end_time ) { @@ -173,13 +173,13 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) else regs (0x4080) |= 0x80; // optimization only } - + // new end_time blip_time_t const start_time = end_time; end_time = final_end_time; if ( end_time > env_time ) end_time = env_time; if ( end_time > sweep_time ) end_time = sweep_time; - + // frequency modulation int freq = wave_freq; if ( mod_freq ) @@ -188,7 +188,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) blip_time_t mod_time = start_time + (mod_fract + mod_freq - 1) / mod_freq; if ( end_time > mod_time ) end_time = mod_time; - + // run modulator up to next clock and save old sweep_bias int sweep_bias = regs (0x4085); mod_fract -= (end_time - start_time) * mod_freq; @@ -196,7 +196,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) { mod_fract += fract_range; check( (unsigned) mod_fract <= fract_range ); - + static short const mod_table [8] = { 0, +1, +2, +4, 0, -4, -2, -1 }; int mod = mod_wave [mod_pos]; mod_pos = (mod_pos + 1) & (wave_size - 1); @@ -205,7 +205,7 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) new_sweep_bias = 0; regs (0x4085) = new_sweep_bias; } - + // apply frequency modulation sweep_bias = (sweep_bias ^ 0x40) - 0x40; int factor = sweep_bias * sweep_gain; @@ -223,26 +223,26 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) if ( freq <= 0 ) continue; } - + // wave int wave_fract = this->wave_fract; blip_time_t delay = (wave_fract + freq - 1) / freq; blip_time_t time = start_time + delay; - + if ( time <= end_time ) { // at least one wave clock within start_time...end_time - + blip_time_t const min_delay = fract_range / freq; int wave_pos = this->wave_pos; - + int volume = env_gain; if ( volume > vol_max ) volume = vol_max; volume *= master_volume; - + int const min_fract = min_delay * freq; - + do { // clock wave @@ -254,27 +254,27 @@ void Nes_Fds_Apu::run_until( blip_time_t final_end_time ) last_amp = amp; synth.offset_inline( time, delta, output_ ); } - + wave_fract += fract_range - delay * freq; check( unsigned (fract_range - wave_fract) < freq ); - + // delay until next clock delay = min_delay; if ( wave_fract > min_fract ) delay++; check( delay && delay == (wave_fract + freq - 1) / freq ); - + time += delay; } while ( time <= end_time ); // TODO: using < breaks things, but <= is wrong - + this->wave_pos = wave_pos; } this->wave_fract = wave_fract - (end_time - (time - delay)) * freq; check( this->wave_fract > 0 ); } while ( end_time < final_end_time ); - + env_delay = env_time - final_end_time; check( env_delay >= 0 ); sweep_delay = sweep_time - final_end_time; check( sweep_delay >= 0 ); } diff --git a/Frameworks/GME/gme/Nes_Fds_Apu.h b/Frameworks/GME/gme/Nes_Fds_Apu.h index 0dc7cc71b..364493c71 100644 --- a/Frameworks/GME/gme/Nes_Fds_Apu.h +++ b/Frameworks/GME/gme/Nes_Fds_Apu.h @@ -6,66 +6,70 @@ #define NES_FDS_APU_H #include "blargg_common.h" +#include "blargg_source.h" #include "Blip_Buffer.h" class Nes_Fds_Apu { public: // setup void set_tempo( double ); - enum { osc_count = 1 }; + static const int osc_count = 1; void volume( double ); void treble_eq( blip_eq_t const& eq ) { synth.treble_eq( eq ); } - + // emulation void reset(); - enum { io_addr = 0x4040 }; - enum { io_size = 0x53 }; + static const unsigned int io_addr = 0x4040; + static const unsigned int io_size = 0x53; void write( blip_time_t time, unsigned addr, int data ); int read( blip_time_t time, unsigned addr ); void end_frame( blip_time_t ); - + + // FDS has a RAM area at $8000-DFFF + enum { sram_addr = 0x8000 }; + byte sram [0x6000]; public: Nes_Fds_Apu(); void write_( unsigned addr, int data ); BLARGG_DISABLE_NOTHROW - + void osc_output( int, Blip_Buffer* ); private: - enum { wave_size = 0x40 }; - enum { master_vol_max = 10 }; - enum { vol_max = 0x20 }; - enum { wave_sample_max = 0x3F }; - + static const unsigned int wave_size = 0x40; + static const unsigned int master_vol_max = 10; + static const int vol_max = 0x20; + static const unsigned int wave_sample_max = 0x3F; + unsigned char regs_ [io_size];// last written value to registers - - enum { lfo_base_tempo = 8 }; - int lfo_tempo; // normally 8; adjusted by set_tempo() - + + static const unsigned int lfo_base_tempo = 8; + int lfo_tempo; // normally 8; adjusted by set_tempo() + int env_delay; int env_speed; int env_gain; - + int sweep_delay; int sweep_speed; int sweep_gain; - + int wave_pos; int last_amp; blip_time_t wave_fract; - + int mod_fract; int mod_pos; int mod_write_pos; unsigned char mod_wave [wave_size]; - + // synthesis blip_time_t last_time; Blip_Buffer* output_; Blip_Synth synth; - + // allow access to registers by absolute address (i.e. 0x4080) unsigned char& regs( unsigned addr ) { return regs_ [addr - io_addr]; } - + void run_until( blip_time_t ); }; @@ -76,6 +80,9 @@ inline void Nes_Fds_Apu::volume( double v ) inline void Nes_Fds_Apu::osc_output( int i, Blip_Buffer* buf ) { +#ifdef NDEBUG + (void) i; +#endif assert( (unsigned) i < osc_count ); output_ = buf; } @@ -97,24 +104,24 @@ inline void Nes_Fds_Apu::write( blip_time_t time, unsigned addr, int data ) inline int Nes_Fds_Apu::read( blip_time_t time, unsigned addr ) { run_until( time ); - + int result = 0xFF; switch ( addr ) { case 0x4090: result = env_gain; break; - + case 0x4092: result = sweep_gain; break; - + default: unsigned i = addr - io_addr; if ( i < wave_size ) result = regs_ [i]; } - + return result | 0x40; } diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp index 93973e409..a51f54e79 100644 --- a/Frameworks/GME/gme/Nes_Fme7_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.cpp @@ -20,10 +20,10 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ void Nes_Fme7_Apu::reset() { last_time = 0; - + for ( int i = 0; i < osc_count; i++ ) oscs [i].last_amp = 0; - + fme7_apu_state_t* state = this; memset( state, 0, sizeof *state ); } @@ -41,28 +41,28 @@ unsigned char const Nes_Fme7_Apu::amp_table [16] = void Nes_Fme7_Apu::run_until( blip_time_t end_time ) { require( end_time >= last_time ); - + for ( int index = 0; index < osc_count; index++ ) { int mode = regs [7] >> index; int vol_mode = regs [010 + index]; int volume = amp_table [vol_mode & 0x0F]; - + Blip_Buffer* const osc_output = oscs [index].output; if ( !osc_output ) continue; osc_output->set_modified(); - + // check for unsupported mode #ifndef NDEBUG if ( (mode & 011) <= 001 && vol_mode & 0x1F ) debug_printf( "FME7 used unimplemented sound mode: %02X, vol_mode: %02X\n", mode, vol_mode & 0x1F ); #endif - + if ( (mode & 001) | (vol_mode & 0x10) ) volume = 0; // noise and envelope aren't supported - + // period int const period_factor = 16; unsigned period = (regs [index * 2 + 1] & 0x0F) * 0x100 * period_factor + @@ -73,7 +73,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) if ( !period ) // on my AY-3-8910A, period doesn't have extra one added period = period_factor; } - + // current amplitude int amp = volume; if ( !phases [index] ) @@ -86,7 +86,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) synth.offset( last_time, delta, osc_output ); } } - + blip_time_t time = last_time + delays [index]; if ( time < end_time ) { @@ -100,7 +100,7 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) time += period; } while ( time < end_time ); - + oscs [index].last_amp = (delta + volume) >> 1; phases [index] = (delta > 0); } @@ -109,13 +109,13 @@ void Nes_Fme7_Apu::run_until( blip_time_t end_time ) // maintain phase when silent int count = (end_time - time + period - 1) / period; phases [index] ^= count & 1; - time += (blargg_long) count * period; + time += (int32_t) count * period; } } - + delays [index] = time - end_time; } - + last_time = end_time; } diff --git a/Frameworks/GME/gme/Nes_Fme7_Apu.h b/Frameworks/GME/gme/Nes_Fme7_Apu.h index b79ed6f5e..adadc1d7f 100644 --- a/Frameworks/GME/gme/Nes_Fme7_Apu.h +++ b/Frameworks/GME/gme/Nes_Fme7_Apu.h @@ -9,7 +9,7 @@ struct fme7_apu_state_t { - enum { reg_count = 14 }; + static const unsigned int reg_count = 14; uint8_t regs [reg_count]; uint8_t phases [3]; // 0 or 1 uint8_t latch; @@ -23,23 +23,23 @@ public: void volume( double ); void treble_eq( blip_eq_t const& ); void output( Blip_Buffer* ); - enum { osc_count = 3 }; + static const int osc_count = 3; void osc_output( int index, Blip_Buffer* ); void end_frame( blip_time_t ); void save_state( fme7_apu_state_t* ) const; void load_state( fme7_apu_state_t const& ); - + // Mask and addresses of registers - enum { addr_mask = 0xE000 }; - enum { data_addr = 0xE000 }; - enum { latch_addr = 0xC000 }; - + static const unsigned int addr_mask = 0xE000; + static const unsigned int data_addr = 0xE000; + static const unsigned int latch_addr = 0xC000; + // (addr & addr_mask) == latch_addr void write_latch( int ); - + // (addr & addr_mask) == data_addr void write_data( blip_time_t, int data ); - + public: Nes_Fme7_Apu(); BLARGG_DISABLE_NOTHROW @@ -47,18 +47,18 @@ private: // noncopyable Nes_Fme7_Apu( const Nes_Fme7_Apu& ); Nes_Fme7_Apu& operator = ( const Nes_Fme7_Apu& ); - + static unsigned char const amp_table [16]; - + struct { Blip_Buffer* output; int last_amp; } oscs [osc_count]; blip_time_t last_time; - - enum { amp_range = 192 }; // can be any value; this gives best error/quality tradeoff + + static const unsigned int amp_range = 192; // can be any value; this gives best error/quality tradeoff Blip_Synth synth; - + void run_until( blip_time_t ); }; @@ -102,7 +102,7 @@ inline void Nes_Fme7_Apu::write_data( blip_time_t time, int data ) #endif return; } - + run_until( time ); regs [latch] = data; } @@ -111,7 +111,7 @@ inline void Nes_Fme7_Apu::end_frame( blip_time_t time ) { if ( time > last_time ) run_until( time ); - + assert( last_time >= time ); last_time -= time; } diff --git a/Frameworks/GME/gme/Nes_Mmc5_Apu.h b/Frameworks/GME/gme/Nes_Mmc5_Apu.h index a5c7cf55e..c33507198 100644 --- a/Frameworks/GME/gme/Nes_Mmc5_Apu.h +++ b/Frameworks/GME/gme/Nes_Mmc5_Apu.h @@ -12,11 +12,11 @@ class Nes_Mmc5_Apu : public Nes_Apu { public: enum { regs_addr = 0x5000 }; enum { regs_size = 0x16 }; - + enum { osc_count = 3 }; void write_register( blip_time_t, unsigned addr, int data ); void osc_output( int i, Blip_Buffer* ); - + enum { exram_size = 1024 }; unsigned char exram [exram_size]; }; @@ -47,10 +47,10 @@ inline void Nes_Mmc5_Apu::write_register( blip_time_t time, unsigned addr, int d case 0x5011: // DAC Nes_Apu::write_register( time, addr - 0x1000, data ); break; - + case 0x5010: // some things write to this for some reason break; - + #ifdef BLARGG_DEBUG_H default: debug_printf( "Unmapped MMC5 APU write: $%04X <- $%02X\n", addr, data ); diff --git a/Frameworks/GME/gme/Nes_Namco_Apu.cpp b/Frameworks/GME/gme/Nes_Namco_Apu.cpp index 3e5fc1491..61d59312d 100644 --- a/Frameworks/GME/gme/Nes_Namco_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Namco_Apu.cpp @@ -26,11 +26,11 @@ void Nes_Namco_Apu::reset() { last_time = 0; addr_reg = 0; - + int i; for ( i = 0; i < reg_count; i++ ) reg [i] = 0; - + for ( i = 0; i < osc_count; i++ ) { Namco_Osc& osc = oscs [i]; @@ -50,12 +50,12 @@ void Nes_Namco_Apu::output( Blip_Buffer* buf ) void Nes_Namco_Apu::reflect_state( Tagged_Data& data ) { reflect_int16( data, BLARGG_4CHAR('A','D','D','R'), &addr_reg ); - + static const char hex [17] = "0123456789ABCDEF"; int i; for ( i = 0; i < reg_count; i++ ) reflect_int16( data, 'RG\0\0' + hex [i >> 4] * 0x100 + hex [i & 15], ® [i] ); - + for ( i = 0; i < osc_count; i++ ) { reflect_int32( data, BLARGG_4CHAR('D','L','Y','0') + i, &oscs [i].delay ); @@ -68,7 +68,7 @@ void Nes_Namco_Apu::end_frame( blip_time_t time ) { if ( time > last_time ) run_until( time ); - + assert( last_time >= time ); last_time -= time; } @@ -83,7 +83,7 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) if ( !output ) continue; output->set_modified(); - + blip_resampled_time_t time = output->resampled_time( last_time ) + osc.delay; blip_resampled_time_t end_time = output->resampled_time( nes_end_time ); @@ -93,24 +93,24 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) const uint8_t* osc_reg = ® [i * 8 + 0x40]; if ( !(osc_reg [4] & 0xE0) ) continue; - + int volume = osc_reg [7] & 15; if ( !volume ) continue; - - blargg_long freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; + + int32_t freq = (osc_reg [4] & 3) * 0x10000 + osc_reg [2] * 0x100L + osc_reg [0]; if ( freq < 64 * active_oscs ) continue; // prevent low frequencies from excessively delaying freq changes blip_resampled_time_t period = output->resampled_duration( 983040 ) / freq * active_oscs; - + int wave_size = 32 - (osc_reg [4] >> 2 & 7) * 4; if ( !wave_size ) continue; - + int last_amp = osc.last_amp; int wave_pos = osc.wave_pos; - + do { // read wave sample @@ -118,7 +118,7 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) int sample = reg [addr >> 1] >> (addr << 2 & 4); wave_pos++; sample = (sample & 15) * volume; - + // output impulse if amplitude changed int delta = sample - last_amp; if ( delta ) @@ -126,20 +126,20 @@ void Nes_Namco_Apu::run_until( blip_time_t nes_end_time ) last_amp = sample; synth.offset_resampled( time, delta, output ); } - + // next sample time += period; if ( wave_pos >= wave_size ) wave_pos = 0; } while ( time < end_time ); - + osc.wave_pos = wave_pos; osc.last_amp = last_amp; } osc.delay = time - end_time; } - + last_time = nes_end_time; } diff --git a/Frameworks/GME/gme/Nes_Namco_Apu.h b/Frameworks/GME/gme/Nes_Namco_Apu.h index 876d85e0a..83be1ab17 100644 --- a/Frameworks/GME/gme/Nes_Namco_Apu.h +++ b/Frameworks/GME/gme/Nes_Namco_Apu.h @@ -15,24 +15,24 @@ public: void volume( double ); void treble_eq( const blip_eq_t& ); void output( Blip_Buffer* ); - enum { osc_count = 8 }; + static const int osc_count = 8; void osc_output( int index, Blip_Buffer* ); void reset(); void end_frame( blip_time_t ); - + // Read/write data register is at 0x4800 - enum { data_reg_addr = 0x4800 }; + static const unsigned int data_reg_addr = 0x4800; void write_data( blip_time_t, int ); int read_data(); - + // Write-only address register is at 0xF800 - enum { addr_reg_addr = 0xF800 }; + static const unsigned int addr_reg_addr = 0xF800; void write_addr( int ); - + // to do: implement save/restore void save_state( namco_state_t* out ) const; void load_state( namco_state_t const& ); - + public: Nes_Namco_Apu(); BLARGG_DISABLE_NOTHROW @@ -40,23 +40,23 @@ private: // noncopyable Nes_Namco_Apu( const Nes_Namco_Apu& ); Nes_Namco_Apu& operator = ( const Nes_Namco_Apu& ); - + struct Namco_Osc { - blargg_long delay; + int32_t delay; Blip_Buffer* output; short last_amp; short wave_pos; }; - + Namco_Osc oscs [osc_count]; - + blip_time_t last_time; int addr_reg; - - enum { reg_count = 0x80 }; + + static const int reg_count = 0x80; uint8_t reg [reg_count]; Blip_Synth synth; - + uint8_t& access(); void run_until( blip_time_t ); }; diff --git a/Frameworks/GME/gme/Nes_Oscs.cpp b/Frameworks/GME/gme/Nes_Oscs.cpp index 1ad3f59c0..153606d0a 100644 --- a/Frameworks/GME/gme/Nes_Oscs.cpp +++ b/Frameworks/GME/gme/Nes_Oscs.cpp @@ -48,20 +48,20 @@ int Nes_Envelope::volume() const void Nes_Square::clock_sweep( int negative_adjust ) { int sweep = regs [1]; - + if ( --sweep_delay < 0 ) { reg_written [1] = true; - + int period = this->period(); int shift = sweep & shift_mask; if ( shift && (sweep & 0x80) && period >= 8 ) { int offset = period >> shift; - + if ( sweep & negate_flag ) offset = negative_adjust - offset; - + if ( period + offset < 0x800 ) { period += offset; @@ -71,7 +71,7 @@ void Nes_Square::clock_sweep( int negative_adjust ) } } } - + if ( reg_written [1] ) { reg_written [1] = false; sweep_delay = (sweep >> 4) & 7; @@ -87,7 +87,7 @@ inline nes_time_t Nes_Square::maintain_phase( nes_time_t time, nes_time_t end_ti { int count = (remain + timer_period - 1) / timer_period; phase = (phase + count) & (phase_range - 1); - time += (blargg_long) count * timer_period; + time += (int32_t) count * timer_period; } return time; } @@ -96,19 +96,19 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) { const int period = this->period(); const int timer_period = (period + 1) * 2; - + if ( !output ) { delay = maintain_phase( time + delay, end_time, timer_period ) - end_time; return; } - + output->set_modified(); - + int offset = period >> (regs [1] & shift_mask); if ( regs [1] & negate_flag ) offset = 0; - + const int volume = this->volume(); if ( volume == 0 || period < 8 || (period + offset) >= 0x800 ) { @@ -116,7 +116,7 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) synth.offset( time, -last_amp, output ); last_amp = 0; } - + time += delay; time = maintain_phase( time, end_time, timer_period ); } @@ -132,13 +132,13 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) } if ( phase < duty ) amp ^= volume; - + { int delta = update_amp( amp ); if ( delta ) synth.offset( time, delta, output ); } - + time += delay; if ( time < end_time ) { @@ -146,7 +146,7 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) const Synth& synth = this->synth; int delta = amp * 2 - volume; int phase = this->phase; - + do { phase = (phase + 1) & (phase_range - 1); if ( phase == 0 || phase == duty ) { @@ -156,12 +156,12 @@ void Nes_Square::run( nes_time_t time, nes_time_t end_time ) time += timer_period; } while ( time < end_time ); - + last_amp = (delta + volume) >> 1; this->phase = phase; } } - + delay = time - end_time; } @@ -173,7 +173,7 @@ void Nes_Triangle::clock_linear_counter() linear_counter = regs [0] & 0x7F; else if ( linear_counter ) linear_counter--; - + if ( !(regs [0] & 0x80) ) reg_written [3] = false; } @@ -196,7 +196,7 @@ inline nes_time_t Nes_Triangle::maintain_phase( nes_time_t time, nes_time_t end_ int count = (remain + timer_period - 1) / timer_period; phase = ((unsigned) phase + 1 - count) & (phase_range * 2 - 1); phase++; - time += (blargg_long) count * timer_period; + time += (int32_t) count * timer_period; } return time; } @@ -212,16 +212,16 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) delay = maintain_phase( time, end_time, timer_period ) - end_time; return; } - + output->set_modified(); - + // to do: track phase when period < 3 // to do: Output 7.5 on dac when period < 2? More accurate, but results in more clicks. - + int delta = update_amp( calc_amp() ); if ( delta ) synth.offset( time, delta, output ); - + time += delay; if ( length_counter == 0 || linear_counter == 0 || timer_period < 3 ) { @@ -230,14 +230,14 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) else if ( time < end_time ) { Blip_Buffer* const output = this->output; - + int phase = this->phase; int volume = 1; if ( phase > phase_range ) { phase -= phase_range; volume = -volume; } - + do { if ( --phase == 0 ) { phase = phase_range; @@ -246,11 +246,11 @@ void Nes_Triangle::run( nes_time_t time, nes_time_t end_time ) else { synth.offset_inline( time, volume, output ); } - + time += timer_period; } while ( time < end_time ); - + if ( volume < 0 ) phase += phase_range; this->phase = phase; @@ -273,7 +273,7 @@ void Nes_Dmc::reset() next_irq = Nes_Apu::no_irq; irq_flag = false; irq_enabled = false; - + Nes_Osc::reset(); period = 0x1AC; } @@ -294,19 +294,19 @@ int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const { if ( last_read ) *last_read = time; - + if ( length_counter == 0 ) return 0; // not reading - + nes_time_t first_read = next_read_time(); nes_time_t avail = time - first_read; if ( avail <= 0 ) return 0; - + int count = (avail - 1) / (period * 8) + 1; if ( !(regs [0] & loop_flag) && count > length_counter ) count = length_counter; - + if ( last_read ) { *last_read = first_read + (count - 1) * (period * 8) + 1; @@ -314,7 +314,7 @@ int Nes_Dmc::count_reads( nes_time_t time, nes_time_t* last_read ) const check( count == count_reads( *last_read, NULL ) ); check( count - 1 == count_reads( *last_read - 1, NULL ) ); } - + return count; } @@ -357,7 +357,7 @@ void Nes_Dmc::write_register( int addr, int data ) { int old_dac = dac; dac = data & 0x7F; - + // adjust last_amp so that "pop" amplitude will be properly non-linear // with respect to change in dac int faked_nonlinear = dac - (dac_table [dac] - dac_table [old_dac]); @@ -409,7 +409,7 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) if ( delta ) synth.offset( time, delta, output ); } - + time += delay; if ( time < end_time ) { @@ -426,7 +426,7 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) const int period = this->period; int bits = this->bits; int dac = this->dac; - + do { if ( !silence ) @@ -438,9 +438,9 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) synth.offset_inline( time, step, output ); } } - + time += period; - + if ( --bits_remain == 0 ) { bits_remain = 8; @@ -458,7 +458,7 @@ void Nes_Dmc::run( nes_time_t time, nes_time_t end_time ) } } while ( time < end_time ); - + this->dac = dac; this->last_amp = dac; this->bits = bits; @@ -478,7 +478,7 @@ static short const noise_period_table [16] = { void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) { int period = noise_period_table [regs [2] & 15]; - + if ( !output ) { // TODO: clean up @@ -486,9 +486,9 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) delay = time + (end_time - time + period - 1) / period * period - end_time; return; } - + output->set_modified(); - + const int volume = this->volume(); int amp = (noise & 1) ? volume : 0; { @@ -496,17 +496,17 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) if ( delta ) synth.offset( time, delta, output ); } - + time += delay; if ( time < end_time ) { const int mode_flag = 0x80; - + if ( !volume ) { // round to next multiple of period time += (end_time - time + period - 1) / period * period; - + // approximate noise cycling while muted, by shuffling up noise register // to do: precise muted noise cycling? if ( !(regs [2] & mode_flag) ) { @@ -517,35 +517,35 @@ void Nes_Noise::run( nes_time_t time, nes_time_t end_time ) else { Blip_Buffer* const output = this->output; - + // using resampled time avoids conversion in synth.offset() blip_resampled_time_t rperiod = output->resampled_duration( period ); blip_resampled_time_t rtime = output->resampled_time( time ); - + int noise = this->noise; int delta = amp * 2 - volume; const int tap = (regs [2] & mode_flag ? 8 : 13); - + do { int feedback = (noise << tap) ^ (noise << 14); time += period; - + if ( (noise + 1) & 2 ) { // bits 0 and 1 of noise differ delta = -delta; synth.offset_resampled( rtime, delta, output ); } - + rtime += rperiod; noise = (feedback & 0x4000) | (noise >> 1); } while ( time < end_time ); - + last_amp = (delta + volume) >> 1; this->noise = noise; } } - + delay = time - end_time; } diff --git a/Frameworks/GME/gme/Nes_Oscs.h b/Frameworks/GME/gme/Nes_Oscs.h index b675bfb47..36a0d7d77 100644 --- a/Frameworks/GME/gme/Nes_Oscs.h +++ b/Frameworks/GME/gme/Nes_Oscs.h @@ -17,7 +17,7 @@ struct Nes_Osc int length_counter;// length counter (0 if unused by oscillator) int delay; // delay until next (potential) transition int last_amp; // last amplitude oscillator was outputting - + void clock_length( int halt_mask ); int period() const { return (regs [3] & 7) * 0x100 + (regs [2] & 0xFF); @@ -37,7 +37,7 @@ struct Nes_Envelope : Nes_Osc { int envelope; int env_delay; - + void clock_envelope(); int volume() const; void reset() { @@ -55,12 +55,12 @@ struct Nes_Square : Nes_Envelope enum { phase_range = 8 }; int phase; int sweep_delay; - + typedef Blip_Synth Synth; Synth const& synth; // shared between squares - + Nes_Square( Synth const* s ) : synth( *s ) { } - + void clock_sweep( int adjust ); void run( nes_time_t, nes_time_t ); void reset() { @@ -78,7 +78,7 @@ struct Nes_Triangle : Nes_Osc int phase; int linear_counter; Blip_Synth synth; - + int calc_amp() const; void run( nes_time_t, nes_time_t ); void clock_linear_counter(); @@ -96,7 +96,7 @@ struct Nes_Noise : Nes_Envelope { int noise; Blip_Synth synth; - + void run( nes_time_t, nes_time_t ); void reset() { noise = 1 << 14; @@ -115,24 +115,24 @@ struct Nes_Dmc : Nes_Osc int bits; bool buf_full; bool silence; - + enum { loop_flag = 0x40 }; - + int dac; - + nes_time_t next_irq; bool irq_enabled; bool irq_flag; bool pal_mode; bool nonlinear; - + int (*prg_reader)( void*, nes_addr_t ); // needs to be initialized to prg read function void* prg_reader_data; - + Nes_Apu* apu; - + Blip_Synth synth; - + void start(); void write_register( int, int ); void run( nes_time_t, nes_time_t ); diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp index d178407c3..d0268a489 100644 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.cpp @@ -56,7 +56,7 @@ void Nes_Vrc6_Apu::write_osc( blip_time_t time, int osc_index, int reg, int data { require( (unsigned) osc_index < osc_count ); require( (unsigned) reg < reg_count ); - + run_until( time ); oscs [osc_index].regs [reg] = data; } @@ -65,21 +65,21 @@ void Nes_Vrc6_Apu::end_frame( blip_time_t time ) { if ( time > last_time ) run_until( time ); - + assert( last_time >= time ); last_time -= time; } void Nes_Vrc6_Apu::save_state( vrc6_apu_state_t* out ) const { - assert( sizeof (vrc6_apu_state_t) == 20 ); + blaarg_static_assert( sizeof (vrc6_apu_state_t) == 20, "VRC APU State layout incorrect!" ); out->saw_amp = oscs [2].amp; for ( int i = 0; i < osc_count; i++ ) { Vrc6_Osc const& osc = oscs [i]; for ( int r = 0; r < reg_count; r++ ) out->regs [i] [r] = osc.regs [r]; - + out->delays [i] = osc.delay; out->phases [i] = osc.phase; } @@ -94,7 +94,7 @@ void Nes_Vrc6_Apu::load_state( vrc6_apu_state_t const& in ) Vrc6_Osc& osc = oscs [i]; for ( int r = 0; r < reg_count; r++ ) osc.regs [r] = in.regs [i] [r]; - + osc.delay = in.delays [i]; osc.phase = in.phases [i]; } @@ -108,11 +108,11 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) if ( !output ) return; output->set_modified(); - + int volume = osc.regs [0] & 15; if ( !(osc.regs [2] & 0x80) ) volume = 0; - + int gate = osc.regs [0] & 0x80; int duty = ((osc.regs [0] >> 4) & 7) + 1; int delta = ((gate || osc.phase < duty) ? volume : 0) - osc.last_amp; @@ -122,7 +122,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) osc.last_amp += delta; square_synth.offset( time, delta, output ); } - + time += osc.delay; osc.delay = 0; int period = osc.period(); @@ -131,7 +131,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) if ( time < end_time ) { int phase = osc.phase; - + do { phase++; @@ -149,7 +149,7 @@ void Nes_Vrc6_Apu::run_square( Vrc6_Osc& osc, blip_time_t end_time ) time += period; } while ( time < end_time ); - + osc.phase = phase; } osc.delay = time - end_time; @@ -163,7 +163,7 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time ) if ( !output ) return; output->set_modified(); - + int amp = osc.amp; int amp_step = osc.regs [0] & 0x3F; blip_time_t time = last_time; @@ -182,7 +182,7 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time ) { int period = osc.period() * 2; int phase = osc.phase; - + do { if ( --phase == 0 ) @@ -190,26 +190,26 @@ void Nes_Vrc6_Apu::run_saw( blip_time_t end_time ) phase = 7; amp = 0; } - + int delta = (amp >> 3) - last_amp; if ( delta ) { last_amp = amp >> 3; saw_synth.offset( time, delta, output ); } - + time += period; amp = (amp + amp_step) & 0xFF; } while ( time < end_time ); - + osc.phase = phase; osc.amp = amp; } - + osc.delay = time - end_time; } - + osc.last_amp = last_amp; } diff --git a/Frameworks/GME/gme/Nes_Vrc6_Apu.h b/Frameworks/GME/gme/Nes_Vrc6_Apu.h index 23a6519fc..59520440e 100644 --- a/Frameworks/GME/gme/Nes_Vrc6_Apu.h +++ b/Frameworks/GME/gme/Nes_Vrc6_Apu.h @@ -21,7 +21,7 @@ public: void end_frame( blip_time_t ); void save_state( vrc6_apu_state_t* ) const; void load_state( vrc6_apu_state_t const& ); - + // Oscillator 0 write-only registers are at $9000-$9002 // Oscillator 1 write-only registers are at $A000-$A002 // Oscillator 2 write-only registers are at $B000-$B002 @@ -29,7 +29,7 @@ public: enum { base_addr = 0x9000 }; enum { addr_step = 0x1000 }; void write_osc( blip_time_t, int osc, int reg, int data ); - + public: Nes_Vrc6_Apu(); BLARGG_DISABLE_NOTHROW @@ -37,7 +37,7 @@ private: // noncopyable Nes_Vrc6_Apu( const Nes_Vrc6_Apu& ); Nes_Vrc6_Apu& operator = ( const Nes_Vrc6_Apu& ); - + struct Vrc6_Osc { uint8_t regs [3]; @@ -46,19 +46,19 @@ private: int last_amp; int phase; int amp; // only used by saw - + int period() const { return (regs [2] & 0x0F) * 0x100L + regs [1] + 1; } }; - + Vrc6_Osc oscs [osc_count]; blip_time_t last_time; - + Blip_Synth saw_synth; Blip_Synth square_synth; - + void run_until( blip_time_t ); void run_square( Vrc6_Osc& osc, blip_time_t ); void run_saw( blip_time_t ); diff --git a/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp b/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp index 179827136..bd0bd6543 100644 --- a/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp +++ b/Frameworks/GME/gme/Nes_Vrc7_Apu.cpp @@ -1,7 +1,7 @@ #include "Nes_Vrc7_Apu.h" extern "C" { -#include "../ext/emu2413.h" +#include "ext/emu2413.h" } #include @@ -10,10 +10,10 @@ extern "C" { static unsigned char vrc7_inst[(16 + 3) * 8] = { -#include "../ext/vrc7tone.h" +#include "ext/vrc7tone.h" }; -int const period = 36; // NES CPU clocks per FM clock +static int const period = 36; // NES CPU clocks per FM clock Nes_Vrc7_Apu::Nes_Vrc7_Apu() { diff --git a/Frameworks/GME/gme/Nes_Vrc7_Apu.h b/Frameworks/GME/gme/Nes_Vrc7_Apu.h index 5e44e6b2a..52a3489c3 100644 --- a/Frameworks/GME/gme/Nes_Vrc7_Apu.h +++ b/Frameworks/GME/gme/Nes_Vrc7_Apu.h @@ -13,7 +13,7 @@ struct vrc7_snapshot_t; class Nes_Vrc7_Apu { public: blargg_err_t init(); - + // See Nes_Apu.h for reference void reset(); void volume( double ); @@ -24,10 +24,10 @@ public: void end_frame( blip_time_t ); void save_snapshot( vrc7_snapshot_t* ) const; void load_snapshot( vrc7_snapshot_t const& ); - + void write_reg( int reg ); void write_data( blip_time_t, int data ); - + public: Nes_Vrc7_Apu(); ~Nes_Vrc7_Apu(); @@ -36,14 +36,14 @@ private: // noncopyable Nes_Vrc7_Apu( const Nes_Vrc7_Apu& ); Nes_Vrc7_Apu& operator = ( const Nes_Vrc7_Apu& ); - + struct Vrc7_Osc { uint8_t regs [3]; Blip_Buffer* output; int last_amp; }; - + Vrc7_Osc oscs [osc_count]; uint8_t kon; uint8_t inst [8]; @@ -54,9 +54,9 @@ private: Blip_Buffer* output; int last_amp; } mono; - + Blip_Synth synth; - + void run_until( blip_time_t ); void output_changed(); }; diff --git a/Frameworks/GME/gme/Nsf_Emu.cpp b/Frameworks/GME/gme/Nsf_Emu.cpp index 6d19ee1a2..26f7e3898 100644 --- a/Frameworks/GME/gme/Nsf_Emu.cpp +++ b/Frameworks/GME/gme/Nsf_Emu.cpp @@ -29,14 +29,14 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -int const vrc6_flag = 0x01; -int const vrc7_flag = 0x02; -int const fds_flag = 0x04; -int const mmc5_flag = 0x08; -int const namco_flag = 0x10; -int const fme7_flag = 0x20; +static int const vrc6_flag = 0x01; +static int const vrc7_flag = 0x02; +static int const fds_flag = 0x04; +static int const mmc5_flag = 0x08; +static int const namco_flag = 0x10; +static int const fme7_flag = 0x20; -long const clock_divisor = 12; +static long const clock_divisor = 12; using std::min; using std::max; @@ -76,24 +76,24 @@ void Nsf_Emu::unload() { delete vrc6; vrc6 = 0; - + delete namco; namco = 0; - + delete fme7; fme7 = 0; - + delete fds; fds = 0; - + delete mmc5; mmc5 = 0; - + delete vrc7; vrc7 = 0; } #endif - + rom.clear(); Music_Emu::unload(); } @@ -125,22 +125,22 @@ static blargg_err_t check_nsf_header( void const* header ) struct Nsf_File : Gme_Info_ { Nsf_Emu::header_t h; - + Nsf_File() { set_type( gme_nsf_type ); } - + blargg_err_t load_( Data_Reader& in ) { blargg_err_t err = in.read( &h, Nsf_Emu::header_size ); if ( err ) return (err == in.eof_error ? gme_wrong_file_type : err); - + if ( h.chip_flags & ~(namco_flag | vrc6_flag | fme7_flag | fds_flag | mmc5_flag | vrc7_flag) ) set_warning( "Uses unsupported audio expansion hardware" ); - + set_track_count( h.track_count ); return check_nsf_header( &h ); } - + blargg_err_t track_info_( track_info_t* out, int ) const { copy_nsf_fields( h, out ); @@ -163,7 +163,7 @@ void Nsf_Emu::set_tempo_( double t ) unsigned standard_rate = 0x411A; clock_rate_ = 1789772.72727; play_period = 262 * 341L * 4 - 2; // two fewer PPU clocks every four frames - + if ( pal_only ) { play_period = 33247 * clock_divisor; @@ -171,10 +171,10 @@ void Nsf_Emu::set_tempo_( double t ) standard_rate = 0x4E20; playback_rate = get_le16( header_.pal_speed ); } - + if ( !playback_rate ) playback_rate = standard_rate; - + if ( playback_rate != standard_rate || t != 1.0 ) play_period = long (playback_rate * clock_rate_ / (1000000.0 / clock_divisor * t)); @@ -205,9 +205,10 @@ blargg_err_t Nsf_Emu::init_sound() apu_names[count + 2] = "Triangle"; apu_names[count + 3] = "Noise"; apu_names[count + 4] = "DMC"; + apu_names[count + 5] = "FM"; count += Nes_Apu::osc_count; } - + static int const types [] = { wave_type | 1, wave_type | 2, wave_type | 0, noise_type | 0, mixed_type | 1, @@ -216,9 +217,9 @@ blargg_err_t Nsf_Emu::init_sound() wave_type |10, wave_type |11, wave_type |12, wave_type |13 }; set_voice_types( types ); // common to all sound chip configurations - + double adjusted_gain = gain(); - + #if NSF_EMU_APU_ONLY { if ( header_.chip_flags ) @@ -256,17 +257,17 @@ blargg_err_t Nsf_Emu::init_sound() count += Nes_Namco_Apu::osc_count; } - + if ( header_.chip_flags & fme7_flag ) { fme7 = BLARGG_NEW Nes_Fme7_Apu; CHECK_ALLOC( fme7 ); adjusted_gain *= 0.75; - + apu_names[count + 0] = "Square 3"; apu_names[count + 1] = "Square 4"; apu_names[count + 2] = "Square 5"; - + count += Nes_Fme7_Apu::osc_count; } @@ -275,45 +276,42 @@ blargg_err_t Nsf_Emu::init_sound() fds = BLARGG_NEW Nes_Fds_Apu; CHECK_ALLOC( fds ); adjusted_gain *= 0.75; - + apu_names[count + 0] = "Wave"; - + count += Nes_Fds_Apu::osc_count; } - + if ( header_.chip_flags & mmc5_flag ) { mmc5 = BLARGG_NEW Nes_Mmc5_Apu; CHECK_ALLOC( mmc5 ); adjusted_gain *= 0.75; - + apu_names[count + 0] = "Square 3"; apu_names[count + 1] = "Square 4"; apu_names[count + 2] = "PCM"; - + count += Nes_Mmc5_Apu::osc_count; } - + if ( header_.chip_flags & vrc7_flag ) { vrc7 = BLARGG_NEW Nes_Vrc7_Apu; CHECK_ALLOC( vrc7 ); RETURN_ERR( vrc7->init() ); adjusted_gain *= 0.75; - + apu_names[count + 0] = "FM 1"; apu_names[count + 1] = "FM 2"; apu_names[count + 2] = "FM 3"; apu_names[count + 3] = "FM 4"; apu_names[count + 4] = "FM 5"; apu_names[count + 5] = "FM 6"; - + count += Nes_Vrc7_Apu::osc_count; } - - set_voice_count( count ); - set_voice_names( &apu_names[0] ); - + if ( namco ) namco->volume( adjusted_gain ); if ( vrc6 ) vrc6 ->volume( adjusted_gain ); if ( fme7 ) fme7 ->volume( adjusted_gain ); @@ -322,28 +320,31 @@ blargg_err_t Nsf_Emu::init_sound() if ( vrc7 ) vrc7 ->volume( adjusted_gain ); } #endif - + + set_voice_count( count ); + set_voice_names( &apu_names[0] ); + apu.volume( adjusted_gain ); - + return 0; } blargg_err_t Nsf_Emu::load_( Data_Reader& in ) { - assert( offsetof (header_t,unused [4]) == header_size ); + blaarg_static_assert( offsetof (header_t,unused [4]) == header_size, "NSF Header layout incorrect!" ); RETURN_ERR( rom.load( in, header_size, &header_, 0 ) ); - + set_track_count( header_.track_count ); RETURN_ERR( check_nsf_header( &header_ ) ); - + if ( header_.vers != 1 ) set_warning( "Unknown file version" ); - + // sound and memory blargg_err_t err = init_sound(); if ( err ) return err; - + // set up data nes_addr_t load_addr = get_le16( header_.load_addr ); init_addr = get_le16( header_.init_addr ); @@ -358,10 +359,10 @@ blargg_err_t Nsf_Emu::load_( Data_Reader& in ) w = "Corrupt file (invalid load/init/play address)"; return w; } - + rom.set_addr( load_addr % bank_size ); int total_banks = rom.size() / bank_size; - + // bank switching int first_bank = (load_addr - rom_begin) / bank_size; for ( int i = 0; i < bank_count; i++ ) @@ -370,7 +371,7 @@ blargg_err_t Nsf_Emu::load_( Data_Reader& in ) if ( bank >= (unsigned) total_banks ) bank = 0; initial_banks [i] = bank; - + if ( header_.banks [i] ) { // bank-switched @@ -378,22 +379,22 @@ blargg_err_t Nsf_Emu::load_( Data_Reader& in ) break; } } - + pal_only = (header_.speed_flags & 3) == 1; - + #if !NSF_EMU_EXTRA_FLAGS header_.speed_flags = 0; #endif - + set_tempo( tempo() ); - + return setup_buffer( (long) (clock_rate_ + 0.5) ); } void Nsf_Emu::update_eq( blip_eq_t const& eq ) { apu.treble_eq( eq ); - + #if !NSF_EMU_APU_ONLY { if ( namco ) namco->treble_eq( eq ); @@ -414,7 +415,7 @@ void Nsf_Emu::set_voice( int i, Blip_Buffer* buf, Blip_Buffer*, Blip_Buffer* ) return; } i -= Nes_Apu::osc_count; - + #if !NSF_EMU_APU_ONLY #define HANDLE_CHIP(class, object) \ if ( object ) \ @@ -468,7 +469,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) return; } } - + if ( namco ) { switch ( addr ) @@ -476,13 +477,13 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) case Nes_Namco_Apu::data_reg_addr: namco->write_data( time(), data ); return; - + case Nes_Namco_Apu::addr_reg_addr: namco->write_addr( data ); return; } } - + if ( addr >= Nes_Fme7_Apu::latch_addr && fme7 ) { switch ( addr & Nes_Fme7_Apu::addr_mask ) @@ -490,13 +491,13 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) case Nes_Fme7_Apu::latch_addr: fme7->write_latch( data ); return; - + case Nes_Fme7_Apu::data_addr: fme7->write_data( time(), data ); return; } } - + if ( vrc6 ) { unsigned reg = addr & (Nes_Vrc6_Apu::addr_step - 1); @@ -507,7 +508,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) return; } } - + if ( mmc5 ) { if ( (unsigned) (addr - mmc5->regs_addr) < mmc5->regs_size) @@ -515,14 +516,14 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) mmc5->write_register( time(), addr, data ); return; } - + int m = addr - 0x5205; if ( (unsigned) m < 2 ) { mmc5_mul [m] = data; return; } - + int i = addr - 0x5C00; if ( (unsigned) i < mmc5->exram_size ) { @@ -530,7 +531,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) return; } } - + if ( vrc7 ) { if ( addr == 0x9010 ) @@ -538,7 +539,7 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) vrc7->write_reg( data ); return; } - + if ( (unsigned) (addr - 0x9028) <= 0x08 ) { vrc7->write_data( time(), data ); @@ -547,20 +548,23 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) } } #endif - + // unmapped write - + #ifndef NDEBUG { // some games write to $8000 and $8001 repeatedly if ( addr == 0x8000 || addr == 0x8001 ) return; - + // probably namco sound mistakenly turned on in mck if ( addr == 0x4800 || addr == 0xF800 ) return; - + // memory mapper? if ( addr == 0xFFF8 ) return; - + + // FDS memory + if ( fds && (unsigned) (addr - 0x8000) < 0x6000 ) return; + debug_printf( "write_unmapped( 0x%04X, 0x%02X )\n", (unsigned) addr, (unsigned) data ); } #endif @@ -569,15 +573,39 @@ void Nsf_Emu::cpu_write_misc( nes_addr_t addr, int data ) blargg_err_t Nsf_Emu::start_track_( int track ) { RETURN_ERR( Classic_Emu::start_track_( track ) ); - + memset( low_mem, 0, sizeof low_mem ); memset( sram, 0, sizeof sram ); - + cpu::reset( unmapped_code ); // also maps low_mem cpu::map_code( sram_addr, sizeof sram, sram ); for ( int i = 0; i < bank_count; ++i ) cpu_write( bank_select_addr + i, initial_banks [i] ); - + + if (fds) + { + memset( fds->sram, 0, sizeof fds->sram ); + // For FDS the initial load values at $076 and $077 + // specify the banks used for $6000-7FFF as well as $E000-FFFF + cpu_write( bank_select_addr - 2, initial_banks [bank_count - 2] ); + cpu_write( bank_select_addr - 1, initial_banks [bank_count - 1] ); + for ( int i = 0; i < bank_count; ++i ) + { + byte* out = fds->sram; + unsigned bank = i; + if ( bank >= 6 ) + { + out = sram; + bank -= 6; + } + int32_t offset = rom.mask_addr( initial_banks [i] * (int32_t) bank_size ); + if ( offset >= rom.size() ) + set_warning( "Invalid bank" ); + memcpy( &out [bank * bank_size], rom.at_addr( offset ), bank_size ); + } + cpu::map_code( fds->sram_addr, sizeof fds->sram, fds->sram ); + } + apu.reset( pal_only, (header_.speed_flags & 0x20) ? 0x3F : 0 ); apu.write_register( 0, 0x4015, 0x0F ); apu.write_register( 0, 0x4017, (header_.speed_flags & 0x10) ? 0x80 : 0 ); @@ -588,7 +616,7 @@ blargg_err_t Nsf_Emu::start_track_( int track ) mmc5_mul [1] = 0; memset( mmc5->exram, 0, mmc5->exram_size ); } - + { if ( namco ) namco->reset(); if ( vrc6 ) vrc6 ->reset(); @@ -598,11 +626,11 @@ blargg_err_t Nsf_Emu::start_track_( int track ) if ( vrc7 ) vrc7 ->reset(); } #endif - + play_ready = 4; play_extra = 0; next_play = play_period / clock_divisor; - + saved_state.pc = badop_addr; low_mem [0x1FF] = (badop_addr - 1) >> 8; low_mem [0x1FE] = (badop_addr - 1) & 0xFF; @@ -610,7 +638,7 @@ blargg_err_t Nsf_Emu::start_track_( int track ) r.pc = init_addr; r.a = track; r.x = pal_only; - + return 0; } @@ -642,7 +670,7 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) } } } - + if ( time() >= next_play ) { nes_time_t period = (play_period + play_extra) / clock_divisor; @@ -653,7 +681,7 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) check( saved_state.pc == badop_addr ); if ( r.pc != badop_addr ) saved_state = cpu::r; - + r.pc = play_addr; low_mem [0x100 + r.sp--] = (badop_addr - 1) >> 8; low_mem [0x100 + r.sp--] = (badop_addr - 1) & 0xFF; @@ -661,21 +689,21 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) } } } - + if ( cpu::error_count() ) { cpu::clear_error_count(); set_warning( "Emulation error (illegal instruction)" ); } - + duration = time(); next_play -= duration; check( next_play >= 0 ); if ( next_play < 0 ) next_play = 0; - + apu.end_frame( duration ); - + #if !NSF_EMU_APU_ONLY { if ( namco ) namco->end_frame( duration ); @@ -686,6 +714,6 @@ blargg_err_t Nsf_Emu::run_clocks( blip_time_t& duration, int ) if ( vrc7 ) vrc7 ->end_frame( duration ); } #endif - + return 0; } diff --git a/Frameworks/GME/gme/Nsf_Emu.h b/Frameworks/GME/gme/Nsf_Emu.h index 0feec1fa6..4bad33458 100644 --- a/Frameworks/GME/gme/Nsf_Emu.h +++ b/Frameworks/GME/gme/Nsf_Emu.h @@ -14,7 +14,7 @@ public: // Equalizer profiles for US NES and Japanese Famicom static equalizer_t const nes_eq; static equalizer_t const famicom_eq; - + // NSF file header enum { header_size = 0x80 }; struct header_t @@ -36,12 +36,12 @@ public: byte chip_flags; byte unused [4]; }; - + // Header for currently loaded file header_t const& header() const { return header_; } - + static gme_type_t static_type() { return gme_nsf_type; } - + public: // deprecated using Music_Emu::load; @@ -68,26 +68,26 @@ protected: nes_addr_t play_addr; double clock_rate_; bool pal_only; - + // timing Nes_Cpu::registers_t saved_state; nes_time_t next_play; nes_time_t play_period; int play_extra; int play_ready; - + enum { rom_begin = 0x8000 }; enum { bank_select_addr = 0x5FF8 }; enum { bank_size = 0x1000 }; Rom_Data rom; - + public: private: friend class Nes_Cpu; void cpu_jsr( nes_addr_t ); int cpu_read( nes_addr_t ); void cpu_write( nes_addr_t, int ); void cpu_write_misc( nes_addr_t, int ); enum { badop_addr = bank_select_addr }; - + private: byte mmc5_mul [2]; @@ -101,9 +101,9 @@ private: blargg_vector apu_names; static int pcm_read( void*, nes_addr_t ); blargg_err_t init_sound(); - + header_t header_; - + enum { sram_addr = 0x6000 }; byte sram [0x2000]; byte unmapped_code [Nes_Cpu::page_size + 8]; diff --git a/Frameworks/GME/gme/Nsfe_Emu.cpp b/Frameworks/GME/gme/Nsfe_Emu.cpp index 5f20aa4da..8f36605b6 100644 --- a/Frameworks/GME/gme/Nsfe_Emu.cpp +++ b/Frameworks/GME/gme/Nsfe_Emu.cpp @@ -58,7 +58,7 @@ static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector& RETURN_ERR( chars.resize( size + 1 ) ); chars [size] = 0; // in case last string doesn't have terminator RETURN_ERR( in.read( &chars [0], size ) ); - + RETURN_ERR( strs.resize( 128 ) ); int count = 0; for ( int i = 0; i < size; i++ ) @@ -69,7 +69,7 @@ static blargg_err_t read_strs( Data_Reader& in, long size, blargg_vector& while ( i < size && chars [i] ) i++; } - + return strs.resize( count ); } @@ -96,8 +96,8 @@ struct nsfe_info_t blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) { int const nsfe_info_size = 16; - assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size ); - + blaarg_static_assert( offsetof (nsfe_info_t,unused [6]) == nsfe_info_size, "NSFE Info header layout incorrect!" ); + // check header byte signature [4]; blargg_err_t err = in.read( signature, sizeof signature ); @@ -105,13 +105,13 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) return (err == in.eof_error ? gme_wrong_file_type : err); if ( memcmp( signature, "NSFE", 4 ) ) return gme_wrong_file_type; - + // free previous info track_name_data.clear(); track_names.clear(); playlist.clear(); track_times.clear(); - + // default nsf header static const Nsf_Emu::header_t base_header = { @@ -128,7 +128,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) }; Nsf_Emu::header_t& header = info; header = base_header; - + // parse tags int phase = 0; while ( phase != 3 ) @@ -136,26 +136,26 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) // read size and tag byte block_header [2] [4]; RETURN_ERR( in.read( block_header, sizeof block_header ) ); - blargg_long size = get_le32( block_header [0] ); - blargg_long tag = get_le32( block_header [1] ); + int32_t size = get_le32( block_header [0] ); + int32_t tag = get_le32( block_header [1] ); if ( size < 0 ) return "Corrupt file"; - + //debug_printf( "tag: %c%c%c%c\n", char(tag), char(tag>>8), char(tag>>16), char(tag>>24) ); - + switch ( tag ) { case BLARGG_4CHAR('O','F','N','I'): { check( phase == 0 ); if ( size < 8 ) return "Corrupt file"; - + nsfe_info_t finfo; finfo.track_count = 1; finfo.first_track = 0; - - RETURN_ERR( in.read( &finfo, min( size, (blargg_long) nsfe_info_size ) ) ); + + RETURN_ERR( in.read( &finfo, min( size, (int32_t) nsfe_info_size ) ) ); if ( size > nsfe_info_size ) RETURN_ERR( in.skip( size - nsfe_info_size ) ); phase = 1; @@ -167,48 +167,48 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) memcpy( info.load_addr, finfo.load_addr, 2 * 3 ); break; } - + case BLARGG_4CHAR('K','N','A','B'): if ( size > (int) sizeof info.banks ) return "Corrupt file"; RETURN_ERR( in.read( info.banks, size ) ); break; - + case BLARGG_4CHAR('h','t','u','a'): { blargg_vector chars; blargg_vector strs; RETURN_ERR( read_strs( in, size, chars, strs ) ); int n = strs.size(); - + if ( n > 3 ) copy_str( strs [3], info.dumper, sizeof info.dumper ); - + if ( n > 2 ) copy_str( strs [2], info.copyright, sizeof info.copyright ); - + if ( n > 1 ) copy_str( strs [1], info.author, sizeof info.author ); - + if ( n > 0 ) copy_str( strs [0], info.game, sizeof info.game ); - + break; } - + case BLARGG_4CHAR('e','m','i','t'): RETURN_ERR( track_times.resize( size / 4 ) ); RETURN_ERR( in.read( track_times.begin(), track_times.size() * 4 ) ); break; - + case BLARGG_4CHAR('l','b','l','t'): RETURN_ERR( read_strs( in, size, track_name_data, track_names ) ); break; - + case BLARGG_4CHAR('t','s','l','p'): RETURN_ERR( playlist.resize( size ) ); RETURN_ERR( in.read( &playlist [0], size ) ); break; - + case BLARGG_4CHAR('A','T','A','D'): { check( phase == 1 ); phase = 2; @@ -225,12 +225,12 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) } break; } - + case BLARGG_4CHAR('D','N','E','N'): check( phase == 2 ); phase = 3; break; - + default: // tags that can be skipped start with a lowercase character check( islower( (tag >> 24) & 0xFF ) ); @@ -238,7 +238,7 @@ blargg_err_t Nsfe_Info::load( Data_Reader& in, Nsf_Emu* nsf_emu ) break; } } - + return 0; } @@ -253,7 +253,7 @@ blargg_err_t Nsfe_Info::track_info_( track_info_t* out, int track ) const } if ( (unsigned) remapped < track_names.size() ) Gme_File::copy_field_( out->song, track_names [remapped] ); - + GME_COPY_FIELD( info, out, game ); GME_COPY_FIELD( info, out, author ); GME_COPY_FIELD( info, out, copyright ); @@ -284,9 +284,9 @@ blargg_err_t Nsfe_Emu::track_info_( track_info_t* out, int track ) const struct Nsfe_File : Gme_Info_ { Nsfe_Info info; - + Nsfe_File() { set_type( gme_nsfe_type ); } - + blargg_err_t load_( Data_Reader& in ) { RETURN_ERR( info.load( in, 0 ) ); @@ -294,7 +294,7 @@ struct Nsfe_File : Gme_Info_ set_track_count( info.info.track_count ); return 0; } - + blargg_err_t track_info_( track_info_t* out, int track ) const { return info.track_info_( out, track ); @@ -312,7 +312,7 @@ blargg_err_t Nsfe_Emu::load_( Data_Reader& in ) { if ( loading ) return Nsf_Emu::load_( in ); - + // TODO: this hacky recursion-avoidance could have subtle problems loading = true; blargg_err_t err = info.load( in, this ); diff --git a/Frameworks/GME/gme/Nsfe_Emu.h b/Frameworks/GME/gme/Nsfe_Emu.h index fd65f0af8..3ca616781 100644 --- a/Frameworks/GME/gme/Nsfe_Emu.h +++ b/Frameworks/GME/gme/Nsfe_Emu.h @@ -11,7 +11,7 @@ class Nsfe_Info { public: blargg_err_t load( Data_Reader&, Nsf_Emu* ); - + struct info_t : Nsf_Emu::header_t { char game [256]; @@ -19,15 +19,15 @@ public: char copyright [256]; char dumper [256]; } info; - + void disable_playlist( bool = true ); - + blargg_err_t track_info_( track_info_t* out, int track ) const; - + int remap_track( int i ) const; - + void unload(); - + Nsfe_Info(); ~Nsfe_Info(); private: @@ -42,7 +42,7 @@ private: class Nsfe_Emu : public Nsf_Emu { public: static gme_type_t static_type() { return gme_nsfe_type; } - + public: // deprecated struct header_t { char tag [4]; }; diff --git a/Frameworks/GME/gme/Sap_Apu.cpp b/Frameworks/GME/gme/Sap_Apu.cpp index 26fa2d13f..f3122582e 100644 --- a/Frameworks/GME/gme/Sap_Apu.cpp +++ b/Frameworks/GME/gme/Sap_Apu.cpp @@ -17,11 +17,11 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -int const max_frequency = 12000; // pure waves above this frequency are silenced +static int const max_frequency = 12000; // pure waves above this frequency are silenced -static void gen_poly( blargg_ulong mask, int count, byte* out ) +static void gen_poly( uint32_t mask, int count, byte* out ) { - blargg_ulong n = 1; + uint32_t n = 1; do { int bits = 0; @@ -30,7 +30,7 @@ static void gen_poly( blargg_ulong mask, int count, byte* out ) { // implemented using "Galios configuration" bits |= (n & 1) << b; - n = (n >> 1) ^ (mask & -(n & 1)); + n = (n >> 1) ^ (mask & uMinus(n & 1)); } while ( b++ < 7 ); *out++ = bits; @@ -39,11 +39,11 @@ static void gen_poly( blargg_ulong mask, int count, byte* out ) } // poly5 -int const poly5_len = (1 << 5) - 1; -blargg_ulong const poly5_mask = (1UL << poly5_len) - 1; -blargg_ulong const poly5 = 0x167C6EA1; +static int const poly5_len = (1 << 5) - 1; +static uint32_t const poly5_mask = (1UL << poly5_len) - 1; +static uint32_t const poly5 = 0x167C6EA1; -inline blargg_ulong run_poly5( blargg_ulong in, int shift ) +static inline uint32_t run_poly5( uint32_t in, int shift ) { return (in << shift & poly5_mask) | (in >> (poly5_len - shift)); } @@ -56,17 +56,17 @@ Sap_Apu_Impl::Sap_Apu_Impl() gen_poly( POLY_MASK( 4, 1, 0 ), sizeof poly4, poly4 ); gen_poly( POLY_MASK( 9, 5, 0 ), sizeof poly9, poly9 ); gen_poly( POLY_MASK( 17, 5, 0 ), sizeof poly17, poly17 ); - + if ( 0 ) // comment out to recauculate poly5 constant { byte poly5 [4]; gen_poly( POLY_MASK( 5, 2, 0 ), sizeof poly5, poly5 ); - blargg_ulong n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L + + uint32_t n = poly5 [3] * 0x1000000L + poly5 [2] * 0x10000L + poly5 [1] * 0x100L + poly5 [0]; - blargg_ulong rev = n & 1; + uint32_t rev = n & 1; for ( int i = 1; i < poly5_len; i++ ) rev |= (n >> i & 1) << (poly5_len - i); - debug_printf( "poly5: 0x%08lX\n", rev ); + debug_printf( "poly5: 0x%08lX\n", (long unsigned int)rev ); } } @@ -85,7 +85,7 @@ void Sap_Apu::reset( Sap_Apu_Impl* new_impl ) poly4_pos = 0; polym_pos = 0; control = 0; - + for ( int i = 0; i < osc_count; i++ ) memset( &oscs [i], 0, offsetof (osc_t,output) ); } @@ -96,13 +96,13 @@ inline void Sap_Apu::calc_periods() int divider = 28; if ( this->control & 1 ) divider = 114; - + for ( int i = 0; i < osc_count; i++ ) { osc_t* const osc = &oscs [i]; - + int const osc_reload = osc->regs [0]; // cache - blargg_long period = (osc_reload + 1) * divider; + int32_t period = (osc_reload + 1) * divider; static byte const fast_bits [osc_count] = { 1 << 6, 1 << 4, 1 << 5, 1 << 3 }; if ( this->control & fast_bits [i] ) { @@ -112,7 +112,7 @@ inline void Sap_Apu::calc_periods() period = osc_reload * 0x100L + osc [-1].regs [0] + 7; if ( !(this->control & fast_bits [i - 1]) ) period = (period - 6) * divider; - + if ( (osc [-1].regs [1] & 0x1F) > 0x10 ) debug_printf( "Use of slave channel in 16-bit mode not supported\n" ); } @@ -125,7 +125,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) { calc_periods(); Sap_Apu_Impl* const impl = this->impl; // cache - + // 17/9-bit poly selection byte const* polym = impl->poly17; int polym_len = poly17_len; @@ -135,19 +135,19 @@ void Sap_Apu::run_until( blip_time_t end_time ) polym = impl->poly9; } polym_pos %= polym_len; - + for ( int i = 0; i < osc_count; i++ ) { osc_t* const osc = &oscs [i]; blip_time_t time = last_time + osc->delay; blip_time_t const period = osc->period; - + // output Blip_Buffer* output = osc->output; if ( output ) { output->set_modified(); - + int const osc_control = osc->regs [1]; // cache int volume = (osc_control & 0x0F) * 2; if ( !volume || osc_control & 0x10 || // silent, DAC mode, or inaudible frequency @@ -155,14 +155,14 @@ void Sap_Apu::run_until( blip_time_t end_time ) { if ( !(osc_control & 0x10) ) volume >>= 1; // inaudible frequency = half volume - + int delta = volume - osc->last_amp; if ( delta ) { osc->last_amp = volume; impl->synth.offset( last_time, delta, output ); } - + // TODO: doesn't maintain high pass flip-flop (very minor issue) } else @@ -182,7 +182,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) volume = -volume; } } - + if ( time < end_time || time2 < end_time ) { // poly source @@ -206,9 +206,9 @@ void Sap_Apu::run_until( blip_time_t end_time ) poly_pos = (poly_pos + osc->delay) % poly_len; } poly_inc -= poly_len; // allows more optimized inner loop below - + // square/poly5 wave - blargg_ulong wave = poly5; + uint32_t wave = poly5; check( poly5 & 1 ); // low bit is set for pure wave int poly5_inc = 0; if ( !(osc_control & 0x80) ) @@ -216,7 +216,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) wave = run_poly5( wave, (osc->delay + poly5_pos) % poly5_len ); poly5_inc = period % poly5_len; } - + // Run wave and high pass interleved with each catching up to the other. // Disabled high pass has no performance effect since inner wave loop // makes no compromise for high pass, and only runs once in that case. @@ -238,7 +238,7 @@ void Sap_Apu::run_until( blip_time_t end_time ) } while ( time2 <= time ) // must advance *past* time to avoid hang time2 += period2; - + // run wave blip_time_t end = end_time; if ( end > time2 ) @@ -262,11 +262,11 @@ void Sap_Apu::run_until( blip_time_t end_time ) } } while ( time < end_time || time2 < end_time ); - + osc->phase = poly_pos; osc->last_amp = osc_last_amp; } - + osc->invert = 0; if ( volume < 0 ) { @@ -276,18 +276,18 @@ void Sap_Apu::run_until( blip_time_t end_time ) } } } - + // maintain divider blip_time_t remain = end_time - time; if ( remain > 0 ) { - blargg_long count = (remain + period - 1) / period; + int32_t count = (remain + period - 1) / period; osc->phase ^= count; time += count * period; } osc->delay = time - end_time; } - + // advance polies blip_time_t duration = end_time - last_time; last_time = end_time; @@ -329,6 +329,6 @@ void Sap_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until( end_time ); - + last_time -= end_time; } diff --git a/Frameworks/GME/gme/Sap_Apu.h b/Frameworks/GME/gme/Sap_Apu.h index 1b67571bc..f45114353 100644 --- a/Frameworks/GME/gme/Sap_Apu.h +++ b/Frameworks/GME/gme/Sap_Apu.h @@ -11,17 +11,17 @@ class Sap_Apu_Impl; class Sap_Apu { public: - enum { osc_count = 4 }; + static const int osc_count = 4; void osc_output( int index, Blip_Buffer* ); - + void reset( Sap_Apu_Impl* ); - - enum { start_addr = 0xD200 }; - enum { end_addr = 0xD209 }; + + static const unsigned int start_addr = 0xD200; + static const unsigned int end_addr = 0xD209; void write_data( blip_time_t, unsigned addr, int data ); - + void end_frame( blip_time_t ); - + public: Sap_Apu(); private: @@ -42,13 +42,13 @@ private: int poly4_pos; int polym_pos; int control; - + void calc_periods(); void run_until( blip_time_t ); - - enum { poly4_len = (1L << 4) - 1 }; - enum { poly9_len = (1L << 9) - 1 }; - enum { poly17_len = (1L << 17) - 1 }; + + static const unsigned int poly4_len = (1L << 4) - 1; + static const unsigned int poly9_len = (1L << 9) - 1; + static const unsigned int poly17_len = (1L << 17) - 1; friend class Sap_Apu_Impl; }; @@ -56,10 +56,10 @@ private: class Sap_Apu_Impl { public: Blip_Synth synth; - + Sap_Apu_Impl(); void volume( double d ) { synth.volume( 1.0 / Sap_Apu::osc_count / 30 * d ); } - + private: typedef unsigned char byte; byte poly4 [Sap_Apu::poly4_len / 8 + 1]; diff --git a/Frameworks/GME/gme/Sap_Cpu.cpp b/Frameworks/GME/gme/Sap_Cpu.cpp index 87b685b71..4febfdba1 100644 --- a/Frameworks/GME/gme/Sap_Cpu.cpp +++ b/Frameworks/GME/gme/Sap_Cpu.cpp @@ -29,14 +29,16 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -int const st_n = 0x80; -int const st_v = 0x40; -int const st_r = 0x20; -int const st_b = 0x10; -int const st_d = 0x08; -int const st_i = 0x04; -int const st_z = 0x02; -int const st_c = 0x01; +enum { + st_n = 0x80, + st_v = 0x40, + st_r = 0x20, + st_b = 0x10, + st_d = 0x08, + st_i = 0x04, + st_z = 0x02, + st_c = 0x01 +}; void Sap_Cpu::reset( void* new_mem ) { @@ -53,7 +55,7 @@ void Sap_Cpu::reset( void* new_mem ) state_.base = 0; irq_time_ = future_sap_time; end_time_ = future_sap_time; - + blargg_verify_byte_order(); } @@ -76,7 +78,7 @@ bool Sap_Cpu::run( sap_time_t end_time ) this->state = &s; int32_t s_time = s.time; uint8_t* const mem = this->mem; // cache - + // registers uint16_t pc = r.pc; uint8_t a = r.a; @@ -84,10 +86,10 @@ bool Sap_Cpu::run( sap_time_t end_time ) uint8_t y = r.y; uint16_t sp; SET_SP( r.sp ); - + // status flags #define IS_NEG (nz & 0x8080) - + #define CALC_STATUS( out ) do {\ out = status & (st_v | st_d | st_i);\ out |= ((nz >> 8) | nz) & st_n;\ @@ -101,7 +103,7 @@ bool Sap_Cpu::run( sap_time_t end_time ) c = nz;\ nz |= ~in & st_z;\ } while ( 0 ) - + uint8_t status; uint16_t c; // carry set if (c & 0x100) != 0 uint16_t nz; // Z set if (nz & 0xFF) == 0, N set if (nz & 0x8080) != 0 @@ -109,12 +111,12 @@ bool Sap_Cpu::run( sap_time_t end_time ) uint8_t temp = r.status; SET_STATUS( temp ); } - + goto loop; dec_clock_loop: s_time--; loop: - + #ifndef NDEBUG { sap_time_t correct = end_time_; @@ -123,16 +125,16 @@ loop: check( s.base == correct ); } #endif - + check( (unsigned) GET_SP() < 0x100 ); check( (unsigned) a < 0x100 ); check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); - + uint8_t opcode = mem [pc]; pc++; uint8_t const* instr = mem + pc; - + static uint8_t const clock_table [256] = {// 0 1 2 3 4 5 6 7 8 9 A B C D E F 0,6,2,8,3,3,5,5,3,2,2,2,4,4,6,6,// 0 @@ -152,19 +154,19 @@ loop: 2,6,2,8,3,3,5,5,2,2,2,2,4,4,6,6,// E 3,5,2,8,4,4,6,6,2,4,2,7,4,4,7,7 // F }; // 0x00 was 7 - + uint16_t data; data = clock_table [opcode]; if ( (s_time += data) >= 0 ) goto possibly_out_of_time; almost_out_of_time: - + data = *instr; - + #ifdef NES_CPU_LOG_H nes_cpu_log( "cpu_log", pc - 1, opcode, instr [0], instr [1] ); #endif - + switch ( opcode ) { possibly_out_of_time: @@ -189,12 +191,12 @@ possibly_out_of_time: out = temp + 0x100 * READ_LOW( uint8_t (data + 1) );\ cross( temp );\ } - + #define IND_X( out ) {\ uint16_t temp = data + x;\ out = 0x100 * READ_LOW( uint8_t (temp + 1) ) + READ_LOW( uint8_t (temp) );\ } - + #define ARITH_ADDR_MODES( op )\ case op - 0x04: /* (ind,x) */\ IND_X( data )\ @@ -240,15 +242,15 @@ imm##op: a = nz = READ_LOW( uint8_t (data + x) ); pc++; goto loop; - + case 0xA5: // LDA zp a = nz = READ_LOW( data ); pc++; goto loop; - + case 0xD0: // BNE BRANCH( (uint8_t) nz ); - + case 0x20: { // JSR uint16_t temp = pc + 1; pc = GET_ADDR(); @@ -257,37 +259,37 @@ imm##op: WRITE_LOW( sp, temp ); goto loop; } - + case 0x4C: // JMP abs pc = GET_ADDR(); goto loop; - + case 0xE8: // INX INC_DEC_XY( x, 1 ) - + case 0x10: // BPL BRANCH( !IS_NEG ) - + ARITH_ADDR_MODES( 0xC5 ) // CMP nz = a - data; pc++; c = ~nz; nz &= 0xFF; goto loop; - + case 0x30: // BMI BRANCH( IS_NEG ) - + case 0xF0: // BEQ BRANCH( !(uint8_t) nz ); - + case 0x95: // STA zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0x85: // STA zp pc++; WRITE_LOW( data, a ); goto loop; - + case 0xC8: // INY INC_DEC_XY( y, 1 ) @@ -295,12 +297,12 @@ imm##op: y = a; nz = a; goto loop; - + case 0x98: // TYA a = y; nz = y; goto loop; - + case 0xAD:{// LDA abs unsigned addr = GET_ADDR(); pc += 2; @@ -308,16 +310,16 @@ imm##op: a = nz; goto loop; } - + case 0x60: // RTS pc = 1 + READ_LOW( sp ); pc += 0x100 * READ_LOW( 0x100 | (sp - 0xFF) ); sp = (sp - 0xFE) | 0x100; goto loop; - + { uint16_t addr; - + case 0x99: // STA abs,Y addr = y + GET_ADDR(); pc += 2; @@ -327,7 +329,7 @@ imm##op: goto loop; } goto sta_ptr; - + case 0x8D: // STA abs addr = GET_ADDR(); pc += 2; @@ -337,7 +339,7 @@ imm##op: goto loop; } goto sta_ptr; - + case 0x9D: // STA abs,X (slightly more common than STA abs) addr = x + GET_ADDR(); pc += 2; @@ -351,19 +353,19 @@ imm##op: WRITE( addr, a ); CACHE_TIME(); goto loop; - + case 0x91: // STA (ind),Y IND_Y( NO_PAGE_CROSSING, addr ) pc++; goto sta_ptr; - + case 0x81: // STA (ind,X) IND_X( addr ) pc++; goto sta_ptr; - + } - + case 0xA9: // LDA #imm pc++; a = data; @@ -373,12 +375,12 @@ imm##op: // common read instructions { uint16_t addr; - + case 0xA1: // LDA (ind,X) IND_X( addr ) pc++; goto a_nz_read_addr; - + case 0xB1:// LDA (ind),Y addr = READ_LOW( data ) + y; HANDLE_PAGE_CROSSING( addr ); @@ -388,7 +390,7 @@ imm##op: if ( (addr ^ 0x8000) <= 0x9FFF ) goto loop; goto a_nz_read_addr; - + case 0xB9: // LDA abs,Y HANDLE_PAGE_CROSSING( data + y ); addr = GET_ADDR() + y; @@ -397,7 +399,7 @@ imm##op: if ( (addr ^ 0x8000) <= 0x9FFF ) goto loop; goto a_nz_read_addr; - + case 0xBD: // LDA abs,X HANDLE_PAGE_CROSSING( data + x ); addr = GET_ADDR() + x; @@ -410,39 +412,39 @@ imm##op: a = nz = READ( addr ); CACHE_TIME(); goto loop; - + } // Branch case 0x50: // BVC BRANCH( !(status & st_v) ) - + case 0x70: // BVS BRANCH( status & st_v ) - + case 0xB0: // BCS BRANCH( c & 0x100 ) - + case 0x90: // BCC BRANCH( !(c & 0x100) ) - + // Load/store - + case 0x94: // STY zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0x84: // STY zp pc++; WRITE_LOW( data, y ); goto loop; - + case 0x96: // STX zp,y data = uint8_t (data + y);/*FALLTHRU*/ case 0x86: // STX zp pc++; WRITE_LOW( data, x ); goto loop; - + case 0xB6: // LDX zp,y data = uint8_t (data + y);/*FALLTHRU*/ case 0xA6: // LDX zp @@ -452,7 +454,7 @@ imm##op: x = data; nz = data; goto loop; - + case 0xB4: // LDY zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0xA4: // LDY zp @@ -462,7 +464,7 @@ imm##op: y = data; nz = data; goto loop; - + case 0xBC: // LDY abs,X data += x; HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ @@ -474,7 +476,7 @@ imm##op: CACHE_TIME(); goto loop; } - + case 0xBE: // LDX abs,y data += y; HANDLE_PAGE_CROSSING( data );/*FALLTHRU*/ @@ -486,13 +488,13 @@ imm##op: CACHE_TIME(); goto loop; } - + { uint8_t temp; case 0x8C: // STY abs temp = y; goto store_abs; - + case 0x8E: // STX abs temp = x; store_abs: @@ -519,7 +521,7 @@ imm##op: CACHE_TIME(); goto cpx_data; } - + case 0xE4: // CPX zp data = READ_LOW( data );/*FALLTHRU*/ case 0xE0: // CPX #imm @@ -529,7 +531,7 @@ imm##op: c = ~nz; nz &= 0xFF; goto loop; - + case 0xCC:{// CPY abs unsigned addr = GET_ADDR(); pc++; @@ -538,7 +540,7 @@ imm##op: CACHE_TIME(); goto cpy_data; } - + case 0xC4: // CPY zp data = READ_LOW( data ); // FALLTHRU case 0xC0: // CPY #imm @@ -548,24 +550,24 @@ imm##op: c = ~nz; nz &= 0xFF; goto loop; - + // Logical ARITH_ADDR_MODES( 0x25 ) // AND nz = (a &= data); pc++; goto loop; - + ARITH_ADDR_MODES( 0x45 ) // EOR nz = (a ^= data); pc++; goto loop; - + ARITH_ADDR_MODES( 0x05 ) // ORA nz = (a |= data); pc++; goto loop; - + case 0x2C:{// BIT abs unsigned addr = GET_ADDR(); pc += 2; @@ -577,7 +579,7 @@ imm##op: nz <<= 8; // result must be zero, even if N bit is set goto loop; } - + case 0x24: // BIT zp nz = READ_LOW( data ); pc++; @@ -587,14 +589,14 @@ imm##op: goto loop; nz <<= 8; // result must be zero, even if N bit is set goto loop; - + // Add/subtract ARITH_ADDR_MODES( 0xE5 ) // SBC case 0xEB: // unofficial equivalent data ^= 0xFF; goto adc_imm; - + ARITH_ADDR_MODES( 0x65 ) // ADC adc_imm: { check( !(status & st_d) ); @@ -607,7 +609,7 @@ imm##op: a = (uint8_t) nz; goto loop; } - + // Shift/rotate case 0x4A: // LSR A @@ -633,7 +635,7 @@ imm##op: a = (uint8_t) nz; goto loop; } - + case 0x5E: // LSR abs,X data += x;/*FALLTHRU*/ case 0x4E: // LSR abs @@ -647,11 +649,11 @@ imm##op: c = temp << 8; goto rotate_common; } - + case 0x3E: // ROL abs,X data += x; goto rol_abs; - + case 0x1E: // ASL abs,X data += x;/*FALLTHRU*/ case 0x0E: // ASL abs @@ -667,15 +669,15 @@ imm##op: WRITE( data, (uint8_t) nz ); CACHE_TIME(); goto loop; - + case 0x7E: // ROR abs,X data += x; goto ror_abs; - + case 0x76: // ROR zp,x data = uint8_t (data + x); goto ror_zp; - + case 0x56: // LSR zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0x46: // LSR zp @@ -687,11 +689,11 @@ imm##op: c = temp << 8; goto write_nz_zp; } - + case 0x36: // ROL zp,x data = uint8_t (data + x); goto rol_zp; - + case 0x16: // ASL zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0x06: // ASL zp @@ -701,21 +703,21 @@ imm##op: nz = c >> 8 & 1; nz |= (c = READ_LOW( data ) << 1); goto write_nz_zp; - + // Increment/decrement case 0xCA: // DEX INC_DEC_XY( x, -1 ) - + case 0x88: // DEY INC_DEC_XY( y, -1 ) - + case 0xF6: // INC zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0xE6: // INC zp nz = 1; goto add_nz_zp; - + case 0xD6: // DEC zp,x data = uint8_t (data + x);/*FALLTHRU*/ case 0xC6: // DEC zp @@ -726,21 +728,21 @@ imm##op: pc++; WRITE_LOW( data, nz ); goto loop; - + case 0xFE: // INC abs,x data = x + GET_ADDR(); goto inc_ptr; - + case 0xEE: // INC abs data = GET_ADDR(); inc_ptr: nz = 1; goto inc_common; - + case 0xDE: // DEC abs,x data = x + GET_ADDR(); goto dec_ptr; - + case 0xCE: // DEC abs data = GET_ADDR(); dec_ptr: @@ -752,14 +754,14 @@ imm##op: WRITE( data, (uint8_t) nz ); CACHE_TIME(); goto loop; - + // Transfer case 0xAA: // TAX x = a; nz = a; goto loop; - + case 0x8A: // TXA a = x; nz = x; @@ -768,22 +770,22 @@ imm##op: case 0x9A: // TXS SET_SP( x ); // verified (no flag change) goto loop; - + case 0xBA: // TSX x = nz = GET_SP(); goto loop; - + // Stack - + case 0x48: // PHA PUSH( a ); // verified goto loop; - + case 0x68: // PLA a = nz = READ_LOW( sp ); sp = (sp - 0xFF) | 0x100; goto loop; - + case 0x40:{// RTI uint8_t temp = READ_LOW( sp ); pc = READ_LOW( 0x100 | (sp - 0xFF) ); @@ -797,13 +799,13 @@ imm##op: sap_time_t new_time = end_time_; if ( !(status & st_i) && new_time > irq_time_ ) new_time = irq_time_; - blargg_long delta = s.base - new_time; + int32_t delta = s.base - new_time; s.base = new_time; s_time += delta; } goto loop; } - + case 0x28:{// PLP uint8_t temp = READ_LOW( sp ); sp = (sp - 0xFF) | 0x100; @@ -815,14 +817,14 @@ imm##op: goto handle_sei; goto handle_cli; } - + case 0x08: { // PHP uint8_t temp; CALC_STATUS( temp ); PUSH( temp | (st_b | st_r) ); goto loop; } - + case 0x6C:{// JMP (ind) data = GET_ADDR(); pc = READ_PROG( data ); @@ -830,39 +832,39 @@ imm##op: pc |= 0x100 * READ_PROG( data ); goto loop; } - + case 0x00: // BRK goto handle_brk; - + // Flags case 0x38: // SEC c = (uint16_t) ~0; goto loop; - + case 0x18: // CLC c = 0; goto loop; - + case 0xB8: // CLV status &= ~st_v; goto loop; - + case 0xD8: // CLD status &= ~st_d; goto loop; - + case 0xF8: // SED status |= st_d; goto loop; - + case 0x58: // CLI if ( !(status & st_i) ) goto loop; status &= ~st_i; handle_cli: { this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - irq_time_; + int32_t delta = s.base - irq_time_; if ( delta <= 0 ) { if ( TIME < irq_time_ ) @@ -873,7 +875,7 @@ imm##op: s_time += delta; if ( s_time < 0 ) goto loop; - + if ( delta >= s_time + 1 ) { // delayed irq until after next instruction @@ -886,14 +888,14 @@ imm##op: debug_printf( "Delayed CLI not emulated\n" ); goto loop; } - + case 0x78: // SEI if ( status & st_i ) goto loop; status |= st_i; handle_sei: { this->r.status = status; // update externally-visible I flag - blargg_long delta = s.base - end_time_; + int32_t delta = s.base - end_time_; s.base = end_time_; s_time += delta; if ( s_time < 0 ) @@ -901,9 +903,9 @@ imm##op: debug_printf( "Delayed SEI not emulated\n" ); goto loop; } - + // Unofficial - + // SKW - Skip word case 0x1C: case 0x3C: case 0x5C: case 0x7C: case 0xDC: case 0xFC: HANDLE_PAGE_CROSSING( data + x );/*FALLTHRU*/ @@ -914,24 +916,24 @@ imm##op: case 0x80: case 0x82: case 0x89: case 0xC2: case 0xD4: case 0xE2: case 0xF4: pc++; goto loop; - + // NOP case 0xEA: case 0x1A: case 0x3A: case 0x5A: case 0x7A: case 0xDA: case 0xFA: goto loop; - + // Unimplemented - + // halt //case 0x02: case 0x12: case 0x22: case 0x32: case 0x42: case 0x52: //case 0x62: case 0x72: case 0x92: case 0xB2: case 0xD2: case 0xF2: - + default: illegal_encountered = true; pc--; goto stop; } assert( false ); - + int result_; handle_brk: if ( (pc - 1) >= idle_addr ) @@ -939,15 +941,15 @@ handle_brk: pc++; result_ = 4; debug_printf( "BRK executed\n" ); - + interrupt: { s_time += 7; - + WRITE_LOW( 0x100 | (sp - 1), pc >> 8 ); WRITE_LOW( 0x100 | (sp - 2), pc ); pc = GET_LE16( &READ_PROG( 0xFFFA ) + result_ ); - + sp = (sp - 3) | 0x100; uint8_t temp; CALC_STATUS( temp ); @@ -955,17 +957,17 @@ interrupt: if ( result_ ) temp |= st_b; // TODO: incorrectly sets B flag for IRQ WRITE_LOW( sp, temp ); - + status &= ~st_d; status |= st_i; this->r.status = status; // update externally-visible I flag - - blargg_long delta = s.base - end_time_; + + int32_t delta = s.base - end_time_; s.base = end_time_; s_time += delta; goto loop; } - + idle_done: //s_time = 0; pc--; @@ -979,26 +981,26 @@ out_of_time: goto interrupt; if ( s_time < 0 ) goto loop; - + stop: - + s.time = s_time; - + r.pc = pc; r.sp = GET_SP(); r.a = a; r.x = x; r.y = y; - + { uint8_t temp; CALC_STATUS( temp ); r.status = temp; } - + this->state_ = s; this->state = &this->state_; - + return illegal_encountered; } diff --git a/Frameworks/GME/gme/Sap_Cpu.h b/Frameworks/GME/gme/Sap_Cpu.h index fdfb9a310..355b6e1fa 100644 --- a/Frameworks/GME/gme/Sap_Cpu.h +++ b/Frameworks/GME/gme/Sap_Cpu.h @@ -6,7 +6,7 @@ #include "blargg_common.h" -typedef blargg_long sap_time_t; // clock cycle count +typedef int32_t sap_time_t; // clock cycle count typedef unsigned sap_addr_t; // 16-bit address enum { future_sap_time = INT_MAX / 2 + 1 }; @@ -14,11 +14,11 @@ class Sap_Cpu { public: // Clear all registers and keep pointer to 64K memory passed in void reset( void* mem_64k ); - + // Run until specified time is reached. Returns true if suspicious/unsupported // instruction was encountered at any point during run. bool run( sap_time_t end_time ); - + // Registers are not updated until run() returns (except I flag in status) struct registers_t { uint16_t pc; @@ -29,20 +29,20 @@ public: uint8_t sp; }; registers_t r; - + enum { idle_addr = 0xFEFF }; - + // Time of beginning of next instruction to be executed sap_time_t time() const { return state->time + state->base; } void set_time( sap_time_t t ) { state->time = t - state->base; } void adjust_time( int delta ) { state->time += delta; } - + sap_time_t irq_time() const { return irq_time_; } void set_irq_time( sap_time_t ); - + sap_time_t end_time() const { return end_time_; } void set_end_time( sap_time_t ); - + public: Sap_Cpu() { state = &state_; } enum { irq_inhibit = 0x04 }; @@ -56,7 +56,7 @@ private: sap_time_t irq_time_; sap_time_t end_time_; uint8_t* mem; - + inline sap_time_t update_end_time( sap_time_t end, sap_time_t irq ); }; diff --git a/Frameworks/GME/gme/Sap_Emu.cpp b/Frameworks/GME/gme/Sap_Emu.cpp index 1c0990e36..c1a2c8722 100644 --- a/Frameworks/GME/gme/Sap_Emu.cpp +++ b/Frameworks/GME/gme/Sap_Emu.cpp @@ -19,7 +19,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -long const base_scanline_period = 114; +static long const base_scanline_period = 114; using std::min; using std::max; @@ -27,13 +27,13 @@ using std::max; Sap_Emu::Sap_Emu() { set_type( gme_sap_type ); - + static const char* const names [Sap_Apu::osc_count * 2] = { "Wave 1", "Wave 2", "Wave 3", "Wave 4", "Wave 5", "Wave 6", "Wave 7", "Wave 8", }; set_voice_names( names ); - + static int const types [Sap_Apu::osc_count * 2] = { wave_type | 1, wave_type | 2, wave_type | 3, wave_type | 0, wave_type | 5, wave_type | 6, wave_type | 7, wave_type | 4, @@ -72,7 +72,7 @@ static int from_dec( byte const* in, byte const* end ) { if ( in >= end ) return -1; - + int n = 0; while ( in < end ) { @@ -108,10 +108,10 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out out->author [0] = 0; out->name [0] = 0; out->copyright [0] = 0; - + if ( size < 16 || memcmp( in, "SAP\x0D\x0A", 5 ) ) return gme_wrong_file_type; - + byte const* file_end = in + size - 5; in += 5; while ( in < file_end && (in [0] != 0xFF || in [1] != 0xFF) ) @@ -119,14 +119,14 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out byte const* line_end = in; while ( line_end < file_end && *line_end != 0x0D ) line_end++; - + char const* tag = (char const*) in; while ( in < line_end && *in > ' ' ) in++; int tag_len = (char const*) in - tag; - + while ( in < line_end && *in <= ' ' ) in++; - + if ( tag_len <= 0 ) { // skip line @@ -162,10 +162,10 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out case 'C': case 'B': break; - + case 'D': return "Digimusic not supported"; - + default: return "Unsupported player type"; } @@ -192,14 +192,14 @@ static blargg_err_t parse_info( byte const* in, long size, Sap_Emu::info_t* out { parse_string( in, line_end, sizeof out->copyright, out->copyright ); } - + in = line_end + 2; } - + if ( in [0] != 0xFF || in [1] != 0xFF ) return "ROM data missing"; out->rom_data = in + 2; - + return 0; } @@ -219,16 +219,16 @@ blargg_err_t Sap_Emu::track_info_( track_info_t* out, int ) const struct Sap_File : Gme_Info_ { Sap_Emu::info_t info; - + Sap_File() { set_type( gme_sap_type ); } - + blargg_err_t load_mem_( byte const* begin, long size ) { RETURN_ERR( parse_info( begin, size, &info ) ); set_track_count( info.track_count ); return 0; } - + blargg_err_t track_info_( track_info_t* out, int ) const { copy_sap_fields( info, out ); @@ -247,7 +247,7 @@ extern gme_type_t const gme_sap_type = &gme_sap_type_; blargg_err_t Sap_Emu::load_mem_( byte const* in, long size ) { file_end = in + size; - + info.warning = 0; info.type = 'B'; info.stereo = false; @@ -256,12 +256,12 @@ blargg_err_t Sap_Emu::load_mem_( byte const* in, long size ) info.music_addr = -1; info.fastplay = 312; RETURN_ERR( parse_info( in, size, &info ) ); - + set_warning( info.warning ); set_track_count( info.track_count ); - set_voice_count( Sap_Apu::osc_count << info.stereo ); + set_voice_count( Sap_Apu::osc_count << static_cast(info.stereo) ); apu_impl.volume( gain() ); - + return setup_buffer( 1773447 ); } @@ -315,7 +315,7 @@ inline void Sap_Emu::call_init( int track ) r.a = track; run_routine( info.init_addr ); break; - + case 'C': r.a = 0x70; r.x = info.music_addr&0xFF; @@ -331,7 +331,7 @@ inline void Sap_Emu::call_init( int track ) blargg_err_t Sap_Emu::start_track_( int track ) { RETURN_ERR( Classic_Emu::start_track_( track ) ); - + memset( &mem, 0, sizeof mem ); byte const* in = info.rom_data; @@ -352,22 +352,22 @@ blargg_err_t Sap_Emu::start_track_( int track ) set_warning( "Invalid file data block" ); break; } - + memcpy( mem.ram + start, in, len ); in += len; if ( file_end - in >= 2 && in [0] == 0xFF && in [1] == 0xFF ) in += 2; } - + apu.reset( &apu_impl ); apu2.reset( &apu_impl ); cpu::reset( mem.ram ); time_mask = 0; // disables sound during init call_init( track ); time_mask = -1; - + next_play = play_period(); - + return 0; } @@ -383,7 +383,7 @@ void Sap_Emu::cpu_write_( sap_addr_t addr, int data ) apu.write_data( time() & time_mask, addr, data ); return; } - + if ( (addr ^ (Sap_Apu::start_addr + 0x10)) <= (Sap_Apu::end_addr - Sap_Apu::start_addr) && info.stereo ) { @@ -403,7 +403,7 @@ inline void Sap_Emu::call_play() case 'B': cpu_jsr( info.play_addr ); break; - + case 'C': cpu_jsr( info.play_addr + 6 ); break; @@ -417,7 +417,7 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) { if ( cpu::run( duration ) || r.pc > idle_addr ) return "Emulation error (illegal instruction)"; - + if ( r.pc == idle_addr ) { if ( next_play <= duration ) @@ -433,7 +433,7 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) } } } - + duration = time(); next_play -= duration; check( next_play >= 0 ); @@ -442,6 +442,6 @@ blargg_err_t Sap_Emu::run_clocks( blip_time_t& duration, int ) apu.end_frame( duration ); if ( info.stereo ) apu2.end_frame( duration ); - + return 0; } diff --git a/Frameworks/GME/gme/Sap_Emu.h b/Frameworks/GME/gme/Sap_Emu.h index f75312713..d4be14a1b 100644 --- a/Frameworks/GME/gme/Sap_Emu.h +++ b/Frameworks/GME/gme/Sap_Emu.h @@ -43,21 +43,21 @@ public: private: friend class Sap_Cpu; void cpu_write_( sap_addr_t, int ); private: info_t info; - + byte const* file_end; sap_time_t scanline_period; sap_time_t next_play; sap_time_t time_mask; Sap_Apu apu; Sap_Apu apu2; - + // large items struct { byte padding1 [0x100]; byte ram [0x10000 + 0x100]; } mem; Sap_Apu_Impl apu_impl; - + sap_time_t play_period() const; void call_play(); void cpu_jsr( sap_addr_t ); diff --git a/Frameworks/GME/gme/Sms_Apu.cpp b/Frameworks/GME/gme/Sms_Apu.cpp index b41fdec41..f42bdad1b 100644 --- a/Frameworks/GME/gme/Sms_Apu.cpp +++ b/Frameworks/GME/gme/Sms_Apu.cpp @@ -78,7 +78,7 @@ void Sms_Square::run( blip_time_t time, blip_time_t end_time ) synth->offset( time, delta, output ); } } - + time += delay; if ( time < end_time ) { @@ -115,7 +115,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) int amp = volume; if ( shifter & 1 ) amp = -amp; - + { int delta = amp - last_amp; if ( delta ) @@ -124,11 +124,11 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) synth.offset( time, delta, output ); } } - + time += delay; if ( !volume ) time = end_time; - + if ( time < end_time ) { Blip_Buffer* const output = this->output; @@ -137,11 +137,11 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) int period = *this->period * 2; if ( !period ) period = 16; - + do { int changed = shifter + 1; - shifter = (feedback & -(shifter & 1)) ^ (shifter >> 1); + shifter = (feedback & uMinus(shifter & 1)) ^ (shifter >> 1); if ( changed & 2 ) // true if bits 0 and 1 differ { delta = -delta; @@ -150,7 +150,7 @@ void Sms_Noise::run( blip_time_t time, blip_time_t end_time ) time += period; } while ( time < end_time ); - + this->shifter = shifter; this->last_amp = delta >> 1; } @@ -167,7 +167,7 @@ Sms_Apu::Sms_Apu() oscs [i] = &squares [i]; } oscs [3] = &noise; - + volume( 1.0 ); reset(); } @@ -210,7 +210,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width ) { last_time = 0; latch = 0; - + if ( !feedback || !noise_width ) { feedback = 0x0009; @@ -224,7 +224,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width ) noise_feedback = (noise_feedback << 1) | (feedback & 1); feedback >>= 1; } - + squares [0].reset(); squares [1].reset(); squares [2].reset(); @@ -234,7 +234,7 @@ void Sms_Apu::reset( unsigned feedback, int noise_width ) void Sms_Apu::run_until( blip_time_t end_time ) { require( end_time >= last_time ); // end_time must not be before previous time - + if ( end_time > last_time ) { // run oscillators @@ -250,7 +250,7 @@ void Sms_Apu::run_until( blip_time_t end_time ) noise.run( last_time, end_time ); } } - + last_time = end_time; } } @@ -259,7 +259,7 @@ void Sms_Apu::end_frame( blip_time_t end_time ) { if ( end_time > last_time ) run_until( end_time ); - + assert( last_time >= end_time ); last_time -= end_time; } @@ -267,9 +267,9 @@ void Sms_Apu::end_frame( blip_time_t end_time ) void Sms_Apu::write_ggstereo( blip_time_t time, int data ) { require( (unsigned) data <= 0xFF ); - + run_until( time ); - + for ( int i = 0; i < osc_count; i++ ) { Sms_Osc& osc = *oscs [i]; @@ -297,12 +297,12 @@ static unsigned char const volumes [16] = { void Sms_Apu::write_data( blip_time_t time, int data ) { require( (unsigned) data <= 0xFF ); - + run_until( time ); - + if ( data & 0x80 ) latch = data; - + int index = (latch >> 5) & 3; if ( latch & 0x10 ) { @@ -323,7 +323,7 @@ void Sms_Apu::write_data( blip_time_t time, int data ) noise.period = &noise_periods [select]; else noise.period = &squares [2].period; - + noise.feedback = (data & 0x04) ? noise_feedback : looped_feedback; noise.shifter = 0x8000; } diff --git a/Frameworks/GME/gme/Sms_Apu.h b/Frameworks/GME/gme/Sms_Apu.h index 3c11a9c3c..ec8bc87ec 100644 --- a/Frameworks/GME/gme/Sms_Apu.h +++ b/Frameworks/GME/gme/Sms_Apu.h @@ -10,34 +10,34 @@ class Sms_Apu { public: // Set overall volume of all oscillators, where 1.0 is full volume void volume( double ); - + // Set treble equalization void treble_eq( const blip_eq_t& ); - + // Outputs can be assigned to a single buffer for mono output, or to three // buffers for stereo output (using Stereo_Buffer to do the mixing). - + // Assign all oscillator outputs to specified buffer(s). If buffer // is NULL, silences all oscillators. void output( Blip_Buffer* mono ); void output( Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - + // Assign single oscillator output to buffer(s). Valid indicies are 0 to 3, // which refer to Square 1, Square 2, Square 3, and Noise. If buffer is NULL, // silences oscillator. enum { osc_count = 4 }; void osc_output( int index, Blip_Buffer* mono ); void osc_output( int index, Blip_Buffer* center, Blip_Buffer* left, Blip_Buffer* right ); - + // Reset oscillators and internal state void reset( unsigned noise_feedback = 0, int noise_width = 0 ); - + // Write GameGear left/right assignment byte void write_ggstereo( blip_time_t, int ); - + // Write to data port void write_data( blip_time_t, int ); - + // Run all oscillators up to specified time, end current frame, then // start a new frame at time 0. void end_frame( blip_time_t ); @@ -49,7 +49,7 @@ private: // noncopyable Sms_Apu( const Sms_Apu& ); Sms_Apu& operator = ( const Sms_Apu& ); - + Sms_Osc* oscs [osc_count]; Sms_Square squares [3]; Sms_Square::Synth square_synth; // used by squares @@ -58,7 +58,7 @@ private: Sms_Noise noise; unsigned noise_feedback; unsigned looped_feedback; - + void run_until( blip_time_t ); }; diff --git a/Frameworks/GME/gme/Sms_Oscs.h b/Frameworks/GME/gme/Sms_Oscs.h index 2a896fef3..282cb18a0 100644 --- a/Frameworks/GME/gme/Sms_Oscs.h +++ b/Frameworks/GME/gme/Sms_Oscs.h @@ -12,11 +12,11 @@ struct Sms_Osc Blip_Buffer* outputs [4]; // NULL, right, left, center Blip_Buffer* output; int output_select; - + int delay; int last_amp; int volume; - + Sms_Osc(); void reset(); }; @@ -25,10 +25,10 @@ struct Sms_Square : Sms_Osc { int period; int phase; - + typedef Blip_Synth Synth; const Synth* synth; - + void reset(); void run( blip_time_t, blip_time_t ); }; @@ -38,10 +38,10 @@ struct Sms_Noise : Sms_Osc const int* period; unsigned shifter; unsigned feedback; - + typedef Blip_Synth Synth; Synth synth; - + void reset(); void run( blip_time_t, blip_time_t ); }; diff --git a/Frameworks/GME/gme/Snes_Spc.cpp b/Frameworks/GME/gme/Snes_Spc.cpp index 064a0fde7..a2a7597ef 100644 --- a/Frameworks/GME/gme/Snes_Spc.cpp +++ b/Frameworks/GME/gme/Snes_Spc.cpp @@ -33,14 +33,14 @@ blargg_err_t Snes_Spc::init() { memset( &m, 0, sizeof m ); dsp.init( RAM ); - + m.tempo = tempo_unit; - + // Most SPC music doesn't need ROM, and almost all the rest only rely // on these two bytes m.rom [0x3E] = 0xFF; m.rom [0x3F] = 0xC0; - + static unsigned char const cycle_table [128] = {// 01 23 45 67 89 AB CD EF 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0 @@ -60,7 +60,7 @@ blargg_err_t Snes_Spc::init() 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F }; - + // unpack cycle table for ( int i = 0; i < 128; i++ ) { @@ -68,11 +68,11 @@ blargg_err_t Snes_Spc::init() m.cycle_table [i * 2 + 0] = n >> 4; m.cycle_table [i * 2 + 1] = n & 0x0F; } - + #if SPC_LESS_ACCURATE memcpy( reg_times, reg_times_, sizeof reg_times ); #endif - + reset(); return 0; } @@ -87,7 +87,7 @@ void Snes_Spc::set_tempo( int t ) m.tempo = t; int const timer2_shift = 4; // 64 kHz int const other_shift = 3; // 8 kHz - + #if SPC_DISABLE_TEMPO m.timers [2].prescaler = timer2_shift; m.timers [1].prescaler = timer2_shift + other_shift; @@ -117,7 +117,7 @@ void Snes_Spc::timers_loaded() t->enabled = REGS [r_control] >> i & 1; t->counter = REGS_IN [r_t0out + i] & 0x0F; } - + set_tempo( m.tempo ); } @@ -126,7 +126,7 @@ void Snes_Spc::load_regs( uint8_t const in [reg_count] ) { memcpy( REGS, in, reg_count ); memcpy( REGS_IN, REGS, reg_count ); - + // These always read back as 0 REGS_IN [r_test ] = 0; REGS_IN [r_control ] = 0; @@ -141,7 +141,7 @@ void Snes_Spc::ram_loaded() { m.rom_enabled = 0; load_regs( &RAM [0xF0] ); - + // Put STOP instruction around memory to catch PC underflow/overflow memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); memset( m.ram.ram + 0x10000, cpu_pad_fill, sizeof m.ram.padding1 ); @@ -163,16 +163,16 @@ void Snes_Spc::reset_time_regs() #if SPC_LESS_ACCURATE m.dsp_time = clocks_per_sample + 1; #endif - + for ( int i = 0; i < timer_count; i++ ) { Timer* t = &m.timers [i]; t->next_time = 1; t->divider = 0; } - + regs_loaded(); - + m.extra_clocks = 0; reset_buf(); } @@ -182,16 +182,16 @@ void Snes_Spc::reset_common( int timer_counter_init ) int i; for ( i = 0; i < timer_count; i++ ) REGS_IN [r_t0out + i] = timer_counter_init; - + // Run IPL ROM memset( &m.cpu_regs, 0, sizeof m.cpu_regs ); m.cpu_regs.pc = rom_addr; - + REGS [r_test ] = 0x0A; REGS [r_control] = 0xB0; // ROM enabled, clear ports for ( i = 0; i < port_count; i++ ) REGS_IN [r_cpuio0 + i] = 0; - + reset_time_regs(); } @@ -215,17 +215,17 @@ char const Snes_Spc::signature [signature_size + 1] = blargg_err_t Snes_Spc::load_spc( void const* data, long size ) { spc_file_t const* const spc = (spc_file_t const*) data; - + // be sure compiler didn't insert any padding into fle_t - assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 ); - + blaarg_static_assert( sizeof (spc_file_t) == spc_min_file_size + 0x80, "SPC File header layout incorrect!" ); + // Check signature and file size if ( size < signature_size || memcmp( spc, signature, 27 ) ) return "Not an SPC file"; - + if ( size < spc_min_file_size ) return "Corrupt SPC file"; - + // CPU registers m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl; m.cpu_regs.a = spc->a; @@ -233,16 +233,16 @@ blargg_err_t Snes_Spc::load_spc( void const* data, long size ) m.cpu_regs.y = spc->y; m.cpu_regs.psw = spc->psw; m.cpu_regs.sp = spc->sp; - + // RAM and registers memcpy( RAM, spc->ram, 0x10000 ); ram_loaded(); - + // DSP registers dsp.load( spc->dsp ); - + reset_time_regs(); - + return 0; } @@ -270,42 +270,42 @@ void Snes_Spc::reset_buf() sample_t* out = m.extra_buf; while ( out < &m.extra_buf [extra_size / 2] ) *out++ = 0; - + m.extra_pos = out; m.buf_begin = 0; - + dsp.set_output( 0, 0 ); } void Snes_Spc::set_output( sample_t* out, int size ) { require( (size & 1) == 0 ); // size must be even - + m.extra_clocks &= clocks_per_sample - 1; if ( out ) { sample_t const* out_end = out + size; m.buf_begin = out; m.buf_end = out_end; - + // Copy extra to output sample_t const* in = m.extra_buf; while ( in < m.extra_pos && out < out_end ) *out++ = *in++; - + // Handle output being full already if ( out >= out_end ) { // Have DSP write to remaining extra space out = dsp.extra(); out_end = &dsp.extra() [extra_size]; - + // Copy any remaining extra samples as if DSP wrote them while ( in < m.extra_pos ) *out++ = *in++; assert( out <= out_end ); } - + dsp.set_output( out, out_end - out ); } else @@ -324,7 +324,7 @@ void Snes_Spc::save_extra() main_end = dsp_end; dsp_end = dsp.extra(); // nothing in DSP's extra } - + // Copy any extra samples at these ends into extra_buf sample_t* out = m.extra_buf; sample_t const* in; @@ -332,7 +332,7 @@ void Snes_Spc::save_extra() *out++ = *in; for ( in = dsp.extra(); in < dsp_end ; in++ ) *out++ = *in; - + m.extra_pos = out; assert( out <= &m.extra_buf [extra_size] ); } @@ -345,7 +345,7 @@ blargg_err_t Snes_Spc::play( int count, sample_t* out ) set_output( out, count ); end_frame( count * (clocks_per_sample / 2) ); } - + const char* err = m.cpu_error; m.cpu_error = 0; return err; @@ -357,27 +357,27 @@ blargg_err_t Snes_Spc::skip( int count ) if ( count > 2 * sample_rate * 2 ) { set_output( 0, 0 ); - + // Skip a multiple of 4 samples time_t end = count; count = (count & 3) + 1 * sample_rate * 2; end = (end - count) * (clocks_per_sample / 2); - + m.skipped_kon = 0; m.skipped_koff = 0; - + // Preserve DSP and timer synchronization // TODO: verify that this really preserves it int old_dsp_time = m.dsp_time + m.spc_time; m.dsp_time = end - m.spc_time + skipping_time; end_frame( end ); m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; - + dsp.write( Spc_Dsp::r_koff, m.skipped_koff & ~m.skipped_kon ); dsp.write( Spc_Dsp::r_kon , m.skipped_kon ); clear_echo(); } #endif - + return play( count, 0 ); } diff --git a/Frameworks/GME/gme/Snes_Spc.h b/Frameworks/GME/gme/Snes_Spc.h index 68c780ab7..ec2db1b63 100644 --- a/Frameworks/GME/gme/Snes_Spc.h +++ b/Frameworks/GME/gme/Snes_Spc.h @@ -13,12 +13,12 @@ struct Snes_Spc { public: // Must be called once before using blargg_err_t init(); - + // Sample pairs generated per second enum { sample_rate = 32000 }; - + // Emulator use - + // Sets IPL ROM data. Library does not include ROM data. Most SPC music files // don't need ROM, but a full emulator must provide this. enum { rom_size = 0x40 }; @@ -43,7 +43,7 @@ public: typedef int time_t; enum { clock_rate = 1024000 }; enum { clocks_per_sample = 32 }; - + // Emulated port read/write at specified time enum { port_count = 4 }; int read_port ( time_t, int port ); @@ -51,20 +51,22 @@ public: // Runs SPC to end_time and starts a new time frame at 0 void end_frame( time_t end_time ); - + // Sound control - + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). // Reduces emulation accuracy. enum { voice_count = 8 }; void mute_voices( int mask ); - + // If true, prevents channels and global volumes from being phase-negated. // Only supported by fast DSP. void disable_surround( bool disable = true ); - + + void disable_echo( bool disable = true ); + // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. - enum { tempo_unit = 0x100 }; + static const unsigned int tempo_unit = 0x100; void set_tempo( int ); // SPC music files @@ -73,17 +75,17 @@ public: enum { spc_min_file_size = 0x10180 }; enum { spc_file_size = 0x10200 }; blargg_err_t load_spc( void const* in, long size ); - + // Clears echo region. Useful after loading an SPC as many have garbage in echo. void clear_echo(); // Plays for count samples and write samples to out. Discards samples if out // is NULL. Count must be a multiple of 2 since output is stereo. blargg_err_t play( int count, sample_t* out ); - + // Skips count samples. Several times faster than play() when using fast DSP. blargg_err_t skip( int count ); - + // State save/load (only available with accurate DSP) #if !SPC_NO_COPY_STATE_FUNCS @@ -91,7 +93,7 @@ public: enum { state_size = 67 * 1024L }; // maximum space needed when saving typedef Spc_Dsp::copy_func_t copy_func_t; void copy_state( unsigned char** io, copy_func_t ); - + // Writes minimal header to spc_out static void init_header( void* spc_out ); @@ -116,18 +118,18 @@ public: uint8_t sp; }; regs_t& smp_regs() { return m.cpu_regs; } - + uint8_t* smp_ram() { return m.ram.ram; } - + void run_until( time_t t ) { run_until_( t ); } public: BLARGG_DISABLE_NOTHROW - + // Time relative to m_spc_time. Speeds up code a bit by eliminating need to // constantly add m_spc_time to time from CPU. CPU uses time that ends at // 0 to eliminate reloading end time every instruction. It pays off. typedef int rel_time_t; - + struct Timer { rel_time_t next_time; // time of next event @@ -140,46 +142,46 @@ public: enum { reg_count = 0x10 }; enum { timer_count = 3 }; enum { extra_size = Spc_Dsp::extra_size }; - + enum { signature_size = 35 }; - + private: Spc_Dsp dsp; - + #if SPC_LESS_ACCURATE static signed char const reg_times_ [256]; signed char reg_times [256]; #endif - + struct state_t { Timer timers [timer_count]; - + uint8_t smp_regs [2] [reg_count]; - + regs_t cpu_regs; - + rel_time_t dsp_time; time_t spc_time; bool echo_accessed; - + int tempo; int skipped_kon; int skipped_koff; const char* cpu_error; - + int extra_clocks; sample_t* buf_begin; sample_t const* buf_end; sample_t* extra_pos; sample_t extra_buf [extra_size]; - + int rom_enabled; uint8_t rom [rom_size]; uint8_t hi_ram [rom_size]; - + unsigned char cycle_table [256]; - + struct { // padding to neutralize address overflow -- but this is @@ -190,14 +192,14 @@ private: } ram; }; state_t m; - + enum { rom_addr = 0xFFC0 }; - + enum { skipping_time = 127 }; - + // Value that padding should be filled with enum { cpu_pad_fill = 0xFF }; - + enum { r_test = 0x0, r_control = 0x1, r_dspaddr = 0x2, r_dspdata = 0x3, @@ -207,7 +209,7 @@ private: r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF }; - + void timers_loaded(); void enable_rom( int enable ); void reset_buf(); @@ -217,7 +219,7 @@ private: void regs_loaded(); void reset_time_regs(); void reset_common( int timer_counter_init ); - + Timer* run_timer_ ( Timer* t, rel_time_t ); Timer* run_timer ( Timer* t, rel_time_t ); int dsp_read ( rel_time_t ); @@ -229,10 +231,10 @@ private: int cpu_read_smp_reg ( int i, rel_time_t ); int cpu_read ( uint16_t addr, rel_time_t ); unsigned CPU_mem_bit ( uint16_t pc, rel_time_t ); - + bool check_echo_access ( int addr ); uint8_t* run_until_( time_t end_time ); - + struct spc_file_t { char signature [signature_size]; @@ -252,7 +254,7 @@ private: }; static char const signature [signature_size + 1]; - + void save_regs( uint8_t out [reg_count] ); }; @@ -273,9 +275,11 @@ inline void Snes_Spc::write_port( time_t t, int port, int data ) } inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); } - + inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } +inline void Snes_Spc::disable_echo( bool disable ) { dsp.disable_echo( disable ); } + #if !SPC_NO_COPY_STATE_FUNCS inline bool Snes_Spc::check_kon() { return dsp.check_kon(); } #endif diff --git a/Frameworks/GME/gme/Spc_Cpu.cpp b/Frameworks/GME/gme/Spc_Cpu.cpp index 5944ff516..206601b52 100644 --- a/Frameworks/GME/gme/Spc_Cpu.cpp +++ b/Frameworks/GME/gme/Spc_Cpu.cpp @@ -51,7 +51,7 @@ Snes_Spc::Timer* Snes_Spc::run_timer_( Timer* t, rel_time_t time ) { int elapsed = TIMER_DIV( t, time - t->next_time ) + 1; t->next_time += TIMER_MUL( t, elapsed ); - + if ( t->enabled ) { int remain = IF_0_THEN_256( t->period - t->divider ); @@ -94,8 +94,8 @@ void Snes_Spc::enable_rom( int enable ) //// DSP #if SPC_LESS_ACCURATE - int const max_reg_time = 29; - + static int const max_reg_time = 29; + signed char const Snes_Spc::reg_times_ [256] = { -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22, @@ -106,7 +106,7 @@ void Snes_Spc::enable_rom( int enable ) 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24, 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25, 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25, - + 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, @@ -116,7 +116,7 @@ void Snes_Spc::enable_rom( int enable ) 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, }; - + #define RUN_DSP( time, offset ) \ int count = (time) - (offset) - m.dsp_time;\ if ( count >= 0 )\ @@ -141,13 +141,13 @@ void Snes_Spc::enable_rom( int enable ) int Snes_Spc::dsp_read( rel_time_t time ) { RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] ); - + int result = dsp.read( REGS [r_dspaddr] & 0x7F ); - + #ifdef SPC_DSP_READ_HOOK SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result ); #endif - + return result; } @@ -160,7 +160,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time ) int r = REGS [r_dspaddr]; if ( r == Spc_Dsp::r_kon ) m.skipped_kon |= data & ~dsp.read( Spc_Dsp::r_koff ); - + if ( r == Spc_Dsp::r_koff ) { m.skipped_koff |= data; @@ -168,11 +168,11 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time ) } } #endif - + #ifdef SPC_DSP_WRITE_HOOK SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data ); #endif - + if ( REGS [r_dspaddr] <= 0x7F ) dsp.write( REGS [r_dspaddr], data ); else if ( !SPC_MORE_ACCURACY ) @@ -193,7 +193,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time ) #elif !defined (NDEBUG) // Debug-only check for read/write within echo buffer, since this might result in // inaccurate emulation due to the DSP not being caught up to the present. - + bool Snes_Spc::check_echo_access( int addr ) { if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) @@ -212,7 +212,7 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time ) } return false; } - + #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) ); #else #define MEM_ACCESS( time, addr ) @@ -224,56 +224,62 @@ inline void Snes_Spc::dsp_write( int data, rel_time_t time ) #if SPC_MORE_ACCURACY static unsigned char const glitch_probs [3] [256] = { - 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B, - 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08, - 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, - 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, - 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, - 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, - 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, - 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, - 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, - 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, - 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, - 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, - 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, - 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, - 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, - 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01, - - 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07, - 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06, - 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09, - 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03, - 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, - 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, - 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, - 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, - 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, - 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, - 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, - 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, - 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, - 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, - 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, - 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01, - - 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, - 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, - 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A, - 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09, - 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09, - 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02, - 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, - 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, - 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, - 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, - 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, - 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, - 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, - 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, - 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, - 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03, + { + 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B, + 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08, + 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, + 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, + 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, + 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, + 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, + 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, + 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, + 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, + 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, + 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, + 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, + 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, + 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, + 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01 + }, + + { + 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07, + 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06, + 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09, + 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03, + 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, + 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, + 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, + 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, + 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, + 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, + 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, + 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, + 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, + 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, + 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, + 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01 + }, + + { + 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, + 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, + 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A, + 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09, + 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09, + 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02, + 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, + 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, + 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, + 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, + 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, + 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, + 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, + 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, + 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, + 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03 + } }; #endif @@ -282,7 +288,7 @@ static unsigned char const glitch_probs [3] [256] = // by compiler. // If write isn't preceded by read, data has this added to it -int const no_read_before_write = 0x2000; +static int const no_read_before_write = 0x2000; void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) { @@ -304,7 +310,7 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) ((period - 1) | ~0x0F) & period ) { //debug_printf( "SPC pathological timer target write\n" ); - + // If the period is 3, 5, or 9, there's a probability this behavior won't occur, // based on the previous period int prob = 0xFF; @@ -312,13 +318,13 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) if ( period == 3 ) prob = glitch_probs [0] [old_period]; if ( period == 5 ) prob = glitch_probs [1] [old_period]; if ( period == 9 ) prob = glitch_probs [2] [old_period]; - + // The glitch suppresses incrementing of one of the counter bits, based on // the lowest set bit in the new period int b = 1; while ( !(period & b) ) b <<= 1; - + if ( (rand() >> 4 & 0xFF) <= prob ) t->divider = (t->divider - b) & 0xFF; } @@ -327,28 +333,28 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) } break; } - + case r_t0out: case r_t1out: case r_t2out: if ( !SPC_MORE_ACCURACY ) debug_printf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); - + if ( data < no_read_before_write / 2 ) run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; break; - + // Registers that act like RAM case 0x8: case 0x9: REGS_IN [addr] = (uint8_t) data; break; - + case r_test: if ( (uint8_t) data != 0x0A ) debug_printf( "SPC wrote to test register\n" ); break; - + case r_control: // port clears if ( data & 0x10 ) @@ -361,7 +367,7 @@ void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, uint16_t addr ) REGS_IN [r_cpuio2] = 0; REGS_IN [r_cpuio3] = 0; } - + // timers { for ( int i = 0; i < timer_count; i++ ) @@ -404,7 +410,7 @@ void Snes_Spc::cpu_write_high( int data, uint8_t i ) void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time ) { MEM_ACCESS( time, addr ) - + // RAM RAM [addr] = (uint8_t) data; if ( addr >= 0xF0 ) // 64% @@ -414,14 +420,14 @@ void Snes_Spc::cpu_write( int data, uint16_t addr, rel_time_t time ) if ( reg < reg_count ) // 87% { REGS [reg] = (uint8_t) data; - + // Ports #ifdef SPC_PORT_WRITE_HOOK if ( (unsigned) (reg - r_cpuio0) < port_count ) SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), (uint8_t) data, ®S [r_cpuio0] ); #endif - + // Registers other than $F2 and $F4-$F7 if ( reg != 2 && (reg < 4 || reg > 7) ) // 36% cpu_write_smp_reg( data, time, reg ); @@ -452,7 +458,7 @@ inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time ) int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time ) { MEM_ACCESS( time, addr ) - + // RAM int result = RAM [addr]; int reg = addr - 0xF0; @@ -462,7 +468,7 @@ int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time ) if ( (unsigned) reg >= 0xFF00 ) // 21% { reg += 0x10 - r_t0out; - + // Timers if ( (unsigned) reg < timer_count ) // 90% { @@ -484,7 +490,7 @@ int Snes_Spc::cpu_read( uint16_t addr, rel_time_t time ) } } } - + return result; } @@ -516,7 +522,7 @@ uint8_t* Snes_Spc::run_until_( time_t end_time )\ #ifndef NDEBUG // Used only for assert -int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks +static int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks #endif @@ -526,25 +532,25 @@ void Snes_Spc::end_frame( time_t end_time ) // would exceed end, does NOT execute it and leaves m.spc_time < end. if ( end_time > m.spc_time ) run_until_( end_time ); - + m.spc_time -= end_time; m.extra_clocks += end_time; - + // Greatest number of clocks early that emulation can stop early due to // not being able to execute current instruction without going over // allowed time. assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); - + // Catch timers up to CPU for ( int i = 0; i < timer_count; i++ ) run_timer( &m.timers [i], 0 ); - + // Catch DSP up to CPU if ( m.dsp_time < 0 ) { RUN_DSP( 0, max_reg_time ); } - + // Save any extra samples beyond what should be generated if ( m.buf_begin ) save_extra(); diff --git a/Frameworks/GME/gme/Spc_Cpu.h b/Frameworks/GME/gme/Spc_Cpu.h index ec56146e7..48e173e60 100644 --- a/Frameworks/GME/gme/Spc_Cpu.h +++ b/Frameworks/GME/gme/Spc_Cpu.h @@ -147,16 +147,16 @@ SPC_CPU_RUN_FUNC int c; int nz; int dp; - + SET_PC( m.cpu_regs.pc ); SET_SP( m.cpu_regs.sp ); SET_PSW( m.cpu_regs.psw ); - + goto loop; - - + + // Main loop - + cbranch_taken_loop: pc += (int8_t) ram [pc]; inc_pc_loop: @@ -165,15 +165,15 @@ loop: { unsigned opcode; unsigned data; - + check( (unsigned) a < 0x100 ); check( (unsigned) x < 0x100 ); check( (unsigned) y < 0x100 ); - + opcode = ram [pc]; if ( (rel_time += m.cycle_table [opcode]) > 0 ) goto out_of_time; - + #ifdef SPC_CPU_OPCODE_HOOK SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); #endif @@ -186,18 +186,18 @@ loop: pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ SUB_CASE_COUNTER( op && cond );\ } - + PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); */ - + // TODO: if PC is at end of memory, this will get wrong operand (very obscure) pc++; data = ram [pc]; switch ( opcode ) { - + // Common instructions #define BRANCH( cond )\ @@ -213,17 +213,17 @@ loop: case 0xF0: // BEQ BRANCH( !(uint8_t) nz ) // 89% taken - + case 0xD0: // BNE BRANCH( (uint8_t) nz ) - + case 0x3F:{// CALL int old_addr = GET_PC() + 2; SET_PC( READ_PC16( pc ) ); PUSH16( old_addr ); goto loop; } - + case 0x6F:// RET { uint8_t l, h; @@ -232,13 +232,13 @@ loop: SET_PC( l | (h << 8) ); } goto loop; - + case 0xE4: // MOV a,dp ++pc; // 80% from timer READ_DP_TIMER( 0, data, a = nz ); goto loop; - + case 0xFA:{// MOV dp,dp int temp; READ_DP_TIMER( -2, data, temp ); @@ -248,7 +248,7 @@ loop: case 0x8F:{// MOV dp,#imm int temp = READ_PC( pc + 1 ); pc += 2; - + #if !SPC_MORE_ACCURACY { int i = dp + temp; @@ -257,7 +257,7 @@ loop: if ( (unsigned) i < 0x10 ) // 76% { REGS [i] = (uint8_t) data; - + // Registers other than $F2 and $F4-$F7 if ( i != 2 && (i < 4 || i > 7)) // 12% cpu_write_smp_reg( data, rel_time, i ); @@ -268,7 +268,7 @@ loop: #endif goto loop; } - + case 0xC4: // MOV dp,a ++pc; #if !SPC_MORE_ACCURACY @@ -280,7 +280,7 @@ loop: { unsigned sel = i - 2; REGS [i] = (uint8_t) a; - + if ( sel == 1 ) // 51% $F3 dsp_write( a, rel_time ); else if ( sel > 1 ) // 1% not $F2 or $F3 @@ -291,7 +291,7 @@ loop: WRITE_DP( 0, data, a ); #endif goto loop; - + #define CASE( n ) /*FALLTHRU*/case n: // Define common address modes based on opcode for immediate mode. Execution @@ -335,25 +335,25 @@ loop: ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr a = nz = READ( 0, data ); goto inc_pc_loop; - + case 0xBF:{// MOV A,(X)+ int temp = x + dp; x = (uint8_t) (x + 1); a = nz = READ( -1, temp ); goto loop; } - + case 0xE8: // MOV A,imm a = data; nz = data; goto inc_pc_loop; - + case 0xF9: // MOV X,dp+Y data = (uint8_t) (data + y);/*FALLTHRU*/ case 0xF8: // MOV X,dp READ_DP_TIMER( 0, data, x = nz ); goto inc_pc_loop; - + case 0xE9: // MOV X,abs data = READ_PC16( pc ); ++pc; @@ -362,7 +362,7 @@ loop: x = data; nz = data; goto inc_pc_loop; - + case 0xFB: // MOV Y,dp+X data = (uint8_t) (data + x);/*FALLTHRU*/ case 0xEB: // MOV Y,dp @@ -370,7 +370,7 @@ loop: pc++; READ_DP_TIMER( 0, data, y = nz ); goto loop; - + case 0xEC:{// MOV Y,abs int temp = READ_PC16( pc ); pc += 2; @@ -378,18 +378,18 @@ loop: //y = nz = READ( 0, temp ); goto loop; } - + case 0x8D: // MOV Y,imm y = data; nz = data; goto inc_pc_loop; - + // 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A WRITE( 0, data, a ); goto inc_pc_loop; - + { int temp; case 0xCC: // MOV abs,Y @@ -402,13 +402,13 @@ loop: pc += 2; goto loop; } - + case 0xD9: // MOV dp+Y,X data = (uint8_t) (data + y);/*FALLTHRU*/ case 0xD8: // MOV dp,X WRITE( 0, data + dp, x ); goto inc_pc_loop; - + case 0xDB: // MOV dp+X,Y data = (uint8_t) (data + x);/*FALLTHRU*/ case 0xCB: // MOV dp,Y @@ -416,44 +416,44 @@ loop: goto inc_pc_loop; // 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. - + case 0x7D: // MOV A,X a = x; nz = x; goto loop; - + case 0xDD: // MOV A,Y a = y; nz = y; goto loop; - + case 0x5D: // MOV X,A x = a; nz = a; goto loop; - + case 0xFD: // MOV Y,A y = a; nz = a; goto loop; - + case 0x9D: // MOV X,SP x = nz = GET_SP(); goto loop; - + case 0xBD: // MOV SP,X SET_SP( x ); goto loop; - + //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) - + case 0xAF: // MOV (X)+,A WRITE_DP( 0, x, a + no_read_before_write ); x = (uint8_t) (x + 1); goto loop; - + // 5. 8-BIT LOGIC OPERATION COMMANDS - + #define LOGICAL_OP( op, func )\ ADDR_MODES( op ) /* addr */\ data = READ( 0, data );/*FALLTHRU*/\ @@ -477,13 +477,13 @@ loop: WRITE( 0, addr, nz );\ goto loop;\ } - + LOGICAL_OP( 0x28, & ); // AND - + LOGICAL_OP( 0x08, | ); // OR - + LOGICAL_OP( 0x48, ^ ); // EOR - + // 4. 8-BIT ARITHMETIC OPERATION COMMANDS ADDR_MODES( 0x68 ) // CMP addr @@ -493,14 +493,14 @@ loop: c = ~nz; nz &= 0xFF; goto inc_pc_loop; - + case 0x79: // CMP (X),(Y) data = READ_DP( -2, y ); nz = READ_DP( -1, x ) - data; c = ~nz; nz &= 0xFF; goto loop; - + case 0x69: // CMP dp,dp data = READ_DP( -3, data );/*FALLTHRU*/ case 0x78: // CMP dp,imm @@ -508,7 +508,7 @@ loop: c = ~nz; nz &= 0xFF; goto inc_pc_loop; - + case 0x3E: // CMP X,dp data += dp; goto cmp_x_addr; @@ -522,7 +522,7 @@ loop: c = ~nz; nz &= 0xFF; goto inc_pc_loop; - + case 0x7E: // CMP Y,dp data += dp; goto cmp_y_addr; @@ -536,7 +536,7 @@ loop: c = ~nz; nz &= 0xFF; goto inc_pc_loop; - + { int addr; case 0xB9: // SBC (x),(y) @@ -554,7 +554,7 @@ loop: adc_addr: nz = READ( -1, addr ); goto adc_data; - + // catch ADC and SBC together, then decode later based on operand #undef CASE #define CASE( n ) case n: case (n) + 0x20: @@ -568,11 +568,11 @@ loop: int flags; if ( opcode >= 0xA0 ) // SBC data ^= 0xFF; - + flags = data ^ nz; nz += data + (c >> 8 & 1); flags ^= nz; - + psw = (psw & ~(v40 | h08)) | (flags >> 1 & h08) | ((flags + 0x80) >> 2 & v40); @@ -585,9 +585,9 @@ loop: WRITE( 0, addr, /*(uint8_t)*/ nz ); goto inc_pc_loop; } - + } - + // 6. ADDITION & SUBTRACTION COMMANDS #define INC_DEC_REG( reg, op )\ @@ -598,7 +598,7 @@ loop: case 0xBC: INC_DEC_REG( a, + 1 ) // INC A case 0x3D: INC_DEC_REG( x, + 1 ) // INC X case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y - + case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y @@ -619,7 +619,7 @@ loop: nz += READ( -1, data ); WRITE( 0, data, /*(uint8_t)*/ nz ); goto inc_pc_loop; - + // 7. SHIFT, ROTATION COMMANDS case 0x5C: // LSR A @@ -630,7 +630,7 @@ loop: a = nz; goto loop; } - + case 0x1C: // ASL A c = 0; /*fallthrough*/ case 0x3C:{// ROL A @@ -640,7 +640,7 @@ loop: a = (uint8_t) nz; goto loop; } - + case 0x0B: // ASL dp c = 0; data += dp; @@ -662,7 +662,7 @@ loop: nz |= (c = READ( -1, data ) << 1); WRITE( 0, data, /*(uint8_t)*/ nz ); goto inc_pc_loop; - + case 0x4B: // LSR dp c = 0; data += dp; @@ -699,12 +699,12 @@ loop: y = READ_DP( 0, (uint8_t) (data + 1) ); nz |= y; goto inc_pc_loop; - + case 0xDA: // MOVW dp,YA WRITE_DP( -1, data, a ); WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); goto inc_pc_loop; - + // 9. 16-BIT OPERATION COMMANDS case 0x3A: // INCW dp @@ -716,33 +716,33 @@ loop: temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW nz = ((temp >> 1) | temp) & 0x7F; WRITE( -2, data, /*(uint8_t)*/ temp ); - + // high byte data = (uint8_t) (data + 1) + dp; temp = (uint8_t) ((temp >> 8) + READ( -1, data )); nz |= temp; WRITE( 0, data, temp ); - + goto inc_pc_loop; } - + case 0x7A: // ADDW YA,dp case 0x9A:{// SUBW YA,dp int lo = READ_DP( -2, data ); int hi = READ_DP( 0, (uint8_t) (data + 1) ); int result; int flags; - + if ( opcode == 0x9A ) // SUBW { lo = (lo ^ 0xFF) + 1; hi ^= 0xFF; } - + lo += a; result = y + hi + (lo >> 8); flags = hi ^ y ^ result; - + psw = (psw & ~(v40 | h08)) | (flags >> 1 & h08) | ((flags + 0x80) >> 2 & v40); @@ -751,10 +751,10 @@ loop: result = (uint8_t) result; y = result; nz = (((lo >> 1) | lo) & 0x7F) | result; - + goto inc_pc_loop; } - + case 0x5A: { // CMPW YA,dp int temp = a - READ_DP( -1, data ); nz = ((temp >> 1) | temp) & 0x7F; @@ -765,7 +765,7 @@ loop: nz &= 0xFF; goto inc_pc_loop; } - + // 10. MULTIPLICATION & DIVISON COMMANDS case 0xCF: { // MUL YA @@ -776,19 +776,19 @@ loop: nz |= y; goto loop; } - + case 0x9E: // DIV YA,X { unsigned ya = y * 0x100 + a; - + psw &= ~(h08 | v40); - + if ( y >= x ) psw |= v40; - + if ( (y & 15) >= (x & 15) ) psw |= h08; - + if ( y < x * 2 ) { a = ya / x; @@ -799,16 +799,16 @@ loop: a = 255 - (ya - x * 0x200) / (256 - x); y = x + (ya - x * 0x200) % (256 - x); } - + nz = (uint8_t) a; a = (uint8_t) a; y = (uint8_t) y; - + goto loop; } - + // 11. DECIMAL COMPENSATION COMMANDS - + case 0xDF: // DAA SUSPICIOUS_OPCODE( "DAA" ); if ( a > 0x99 || c & 0x100 ) @@ -816,14 +816,14 @@ loop: a += 0x60; c = 0x100; } - + if ( (a & 0x0F) > 9 || psw & h08 ) a += 0x06; - + nz = a; a = (uint8_t) a; goto loop; - + case 0xBE: // DAS SUSPICIOUS_OPCODE( "DAS" ); if ( a > 0x99 || !(c & 0x100) ) @@ -831,38 +831,38 @@ loop: a -= 0x60; c = 0; } - + if ( (a & 0x0F) > 9 || !(psw & h08) ) a -= 0x06; - + nz = a; a = (uint8_t) a; goto loop; - + // 12. BRANCHING COMMANDS case 0x2F: // BRA rel pc += (int8_t) data; goto inc_pc_loop; - + case 0x30: // BMI BRANCH( (nz & nz_neg_mask) ) - + case 0x10: // BPL BRANCH( !(nz & nz_neg_mask) ) - + case 0xB0: // BCS BRANCH( c & 0x100 ) - + case 0x90: // BCC BRANCH( !(c & 0x100) ) - + case 0x70: // BVS BRANCH( psw & v40 ) - + case 0x50: // BVC BRANCH( !(psw & v40) ) - + #define CBRANCH( cond )\ {\ pc++;\ @@ -871,7 +871,7 @@ loop: rel_time -= 2;\ goto inc_pc_loop;\ } - + case 0x03: // BBS dp.bit,rel case 0x23: case 0x43: @@ -881,7 +881,7 @@ loop: case 0xC3: case 0xE3: CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) - + case 0x13: // BBC dp.bit,rel case 0x33: case 0x53: @@ -891,7 +891,7 @@ loop: case 0xD3: case 0xF3: CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) - + case 0xDE: // CBNE dp+X,rel data = (uint8_t) (data + x); // fall through @@ -901,26 +901,26 @@ loop: READ_DP_TIMER( -4, data, temp ); CBRANCH( temp != a ) } - + case 0x6E: { // DBNZ dp,rel unsigned temp = READ_DP( -4, data ) - 1; WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); CBRANCH( temp ) } - + case 0xFE: // DBNZ Y,rel y = (uint8_t) (y - 1); BRANCH( y ) - + case 0x1F: // JMP [abs+X] SET_PC( READ_PC16( pc ) + x ); // fall through case 0x5F: // JMP abs SET_PC( READ_PC16( pc ) ); goto loop; - + // 13. SUB-ROUTINE CALL RETURN COMMANDS - + case 0x0F:{// BRK int temp; int ret_addr = GET_PC(); @@ -932,14 +932,14 @@ loop: PUSH( temp ); goto loop; } - + case 0x4F:{// PCALL offset int ret_addr = GET_PC() + 1; SET_PC( 0xFF00 | data ); PUSH16( ret_addr ); goto loop; } - + case 0x01: // TCALL n case 0x11: case 0x21: @@ -961,7 +961,7 @@ loop: PUSH16( ret_addr ); goto loop; } - + // 14. STACK OPERATION COMMANDS { @@ -979,7 +979,7 @@ loop: SET_PSW( temp ); goto loop; } - + case 0x0D: { // PUSH PSW int temp; GET_PSW( temp ); @@ -990,27 +990,27 @@ loop: case 0x2D: // PUSH A PUSH( a ); goto loop; - + case 0x4D: // PUSH X PUSH( x ); goto loop; - + case 0x6D: // PUSH Y PUSH( y ); goto loop; - + case 0xAE: // POP A POP( a ); goto loop; - + case 0xCE: // POP X POP( x ); goto loop; - + case 0xEE: // POP Y POP( y ); goto loop; - + // 15. BIT OPERATION COMMANDS case 0x02: // SET1 @@ -1037,7 +1037,7 @@ loop: WRITE( 0, data, (READ( -1, data ) & mask) | bit ); goto inc_pc_loop; } - + case 0x0E: // TSET1 abs case 0x4E: // TCLR1 abs data = READ_PC16( pc ); @@ -1051,32 +1051,32 @@ loop: WRITE( 0, data, temp ); } goto loop; - + case 0x4A: // AND1 C,mem.bit c &= MEM_BIT( 0 ); pc += 2; goto loop; - + case 0x6A: // AND1 C,/mem.bit c &= ~MEM_BIT( 0 ); pc += 2; goto loop; - + case 0x0A: // OR1 C,mem.bit c |= MEM_BIT( -1 ); pc += 2; goto loop; - + case 0x2A: // OR1 C,/mem.bit c |= ~MEM_BIT( -1 ); pc += 2; goto loop; - + case 0x8A: // EOR1 C,mem.bit c ^= MEM_BIT( -1 ); pc += 2; goto loop; - + case 0xEA: // NOT1 mem.bit data = READ_PC16( pc ); pc += 2; @@ -1086,7 +1086,7 @@ loop: WRITE( 0, data & 0x1FFF, temp ); } goto loop; - + case 0xCA: // MOV1 mem.bit,C data = READ_PC16( pc ); pc += 2; @@ -1097,53 +1097,53 @@ loop: WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); } goto loop; - + case 0xAA: // MOV1 C,mem.bit c = MEM_BIT( 0 ); pc += 2; goto loop; - + // 16. PROGRAM PSW FLAG OPERATION COMMANDS case 0x60: // CLRC c = 0; goto loop; - + case 0x80: // SETC c = ~0; goto loop; - + case 0xED: // NOTC c ^= 0x100; goto loop; - + case 0xE0: // CLRV psw &= ~(v40 | h08); goto loop; - + case 0x20: // CLRP dp = 0; goto loop; - + case 0x40: // SETP dp = 0x100; goto loop; - + case 0xA0: // EI SUSPICIOUS_OPCODE( "EI" ); psw |= i04; goto loop; - + case 0xC0: // DI SUSPICIOUS_OPCODE( "DI" ); psw &= ~i04; goto loop; - + // 17. OTHER COMMANDS case 0x00: // NOP goto loop; - + case 0xFF:{// STOP // handle PC wrap-around if ( pc == 0x0000 ) @@ -1160,13 +1160,13 @@ loop: m.cpu_error = "SPC emulation error"; goto stop; } // switch - + assert( 0 ); // catch any unhandled instructions } out_of_time: rel_time -= m.cycle_table [ ram [pc] ]; // undo partial execution of opcode stop: - + // Uncache registers m.cpu_regs.pc = (uint16_t) GET_PC(); m.cpu_regs.sp = ( uint8_t) GET_SP(); diff --git a/Frameworks/GME/gme/Spc_Dsp.cpp b/Frameworks/GME/gme/Spc_Dsp.cpp index b4f85c0bd..6b41eadb2 100644 --- a/Frameworks/GME/gme/Spc_Dsp.cpp +++ b/Frameworks/GME/gme/Spc_Dsp.cpp @@ -155,9 +155,9 @@ inline void Spc_Dsp::init_counter() // counters start out with this synchronization m.counters [0] = 1; m.counters [1] = 0; - m.counters [2] = -0x20u; + m.counters [2] = uMinus(0x20u); m.counters [3] = 0x0B; - + int n = 2; for ( int i = 1; i < 32; i++ ) { @@ -190,7 +190,7 @@ void Spc_Dsp::run( int clock_count ) m.phase = new_phase & 31; if ( !count ) return; - + uint8_t* const ram = m.ram; #ifdef SPC_ISOLATED_ECHO_BUFFER uint8_t* const echo_ram = m.echo_ram; @@ -198,13 +198,24 @@ void Spc_Dsp::run( int clock_count ) uint8_t const* const dir = &ram [REG(dir) * 0x100]; int const slow_gaussian = (REG(pmon) >> 1) | REG(non); int const noise_rate = REG(flg) & 0x1F; - + // Global volume int mvoll = (int8_t) REG(mvoll); int mvolr = (int8_t) REG(mvolr); + int evoll = (int8_t) REG(evoll); + int evolr = (int8_t) REG(evolr); + + if ( !m.echo_enable) + { + mvoll = 127; + mvolr = 127; + evoll = 0; + evolr = 0; + } + if ( mvoll * mvolr < m.surround_threshold ) mvoll = -mvoll; // eliminate surround - + do { // KON/KOFF reading @@ -212,20 +223,20 @@ void Spc_Dsp::run( int clock_count ) { m.new_kon &= ~m.kon; m.kon = m.new_kon; - m.t_koff = REG(koff); + m.t_koff = REG(koff); } - + run_counter( 1 ); run_counter( 2 ); run_counter( 3 ); - + // Noise if ( !READ_COUNTER( noise_rate ) ) { int feedback = (m.noise << 13) ^ (m.noise << 14); m.noise = (feedback & 0x4000) ^ (m.noise >> 1); } - + // Voices int pmon_input = 0; int main_out_l = 0; @@ -238,20 +249,20 @@ void Spc_Dsp::run( int clock_count ) do { #define SAMPLE_PTR(i) GET_LE16A( &dir [VREG(v_regs,srcn) * 4 + i * 2] ) - + int brr_header = ram [v->brr_addr]; int kon_delay = v->kon_delay; - + // Pitch int pitch = GET_LE16A( &VREG(v_regs,pitchl) ) & 0x3FFF; if ( REG(pmon) & vbit ) pitch += ((pmon_input >> 5) * pitch) >> 10; - + // KON phases if ( --kon_delay >= 0 ) { v->kon_delay = kon_delay; - + // Get ready to start BRR decoding on next sample if ( kon_delay == 4 ) { @@ -260,20 +271,20 @@ void Spc_Dsp::run( int clock_count ) v->buf_pos = v->buf; brr_header = 0; // header is ignored on this sample } - + // Envelope is never run during KON v->env = 0; v->hidden_env = 0; - + // Disable BRR decoding until last three samples v->interp_pos = (kon_delay & 3 ? 0x4000 : 0); - + // Pitch is never added during KON pitch = 0; } - + int env = v->env; - + // Gaussian interpolation { int output = 0; @@ -284,9 +295,9 @@ void Spc_Dsp::run( int clock_count ) int offset = (unsigned) v->interp_pos >> 3 & 0x1FE; short const* fwd = interleved_gauss + offset; short const* rev = interleved_gauss + 510 - offset; // mirror left half of gaussian - + int const* in = &v->buf_pos [(unsigned) v->interp_pos >> 12]; - + if ( !(slow_gaussian & vbit) ) // 99% { // Faster approximation when exact sample value isn't necessary for pitch mod @@ -306,44 +317,44 @@ void Spc_Dsp::run( int clock_count ) output += (rev [1] * in [2]) >> 11; output = (int16_t) output; output += (rev [0] * in [3]) >> 11; - + CLAMP16( output ); output &= ~1; } output = (output * env) >> 11 & ~1; } - + // Output int l = output * v->volume [0]; int r = output * v->volume [1]; - + main_out_l += l; main_out_r += r; - + if ( REG(eon) & vbit ) { echo_out_l += l; echo_out_r += r; } } - + pmon_input = output; VREG(v_regs,outx) = (uint8_t) (output >> 8); } - + // Soft reset or end of sample if ( REG(flg) & 0x80 || (brr_header & 3) == 1 ) { v->env_mode = env_release; env = 0; } - + if ( m.every_other_sample ) { // KOFF if ( m.t_koff & vbit ) v->env_mode = env_release; - + // KON if ( m.kon & vbit ) { @@ -352,7 +363,7 @@ void Spc_Dsp::run( int clock_count ) REG(endx) &= ~vbit; } } - + // Envelope if ( !v->kon_delay ) { @@ -378,7 +389,7 @@ void Spc_Dsp::run( int clock_count ) env--; env -= env >> 8; rate = env_data & 0x1F; - + // optimized handling v->hidden_env = env; if ( READ_COUNTER( rate ) ) @@ -428,13 +439,13 @@ void Spc_Dsp::run( int clock_count ) } } } - + // Sustain level if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) v->env_mode = env_sustain; - + v->hidden_env = env; - + // unsigned cast because linear decrease going negative also triggers this if ( (unsigned) env > 0x7FF ) { @@ -442,13 +453,13 @@ void Spc_Dsp::run( int clock_count ) if ( v->env_mode == env_attack ) v->env_mode = env_decay; } - + if ( !READ_COUNTER( rate ) ) v->env = env; // nothing else is controlled by the counter } } exit_env: - + { // Apply pitch int old_pos = v->interp_pos; @@ -456,14 +467,14 @@ void Spc_Dsp::run( int clock_count ) if ( interp_pos > 0x7FFF ) interp_pos = 0x7FFF; v->interp_pos = interp_pos; - + // BRR decode if necessary if ( old_pos >= 0x4000 ) { // Arrange the four input nybbles in 0xABCD order for easy decoding int nybbles = ram [(v->brr_addr + v->brr_offset) & 0xFFFF] * 0x100 + ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; - + // Advance read position int const brr_block_size = 9; int brr_offset = v->brr_offset; @@ -482,9 +493,9 @@ void Spc_Dsp::run( int clock_count ) brr_offset = 1; } v->brr_offset = brr_offset; - + // Decode - + // 0: >>1 1: <<0 2: <<1 ... 12: <<11 13-15: >>4 <<11 static unsigned char const shifts [16 * 2] = { 13,12,12,12,12,12,12,12,12,12,12, 12, 12, 16, 16, 16, @@ -493,18 +504,18 @@ void Spc_Dsp::run( int clock_count ) int const scale = brr_header >> 4; int const right_shift = shifts [scale]; int const left_shift = shifts [scale + 16]; - + // Write to next four samples in circular buffer int* pos = v->buf_pos; int* end; - + // Decode four samples for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) { // Extract upper nybble and scale appropriately. Every cast is // necessary to maintain correctness and avoid undef behavior int s = int16_t(uint16_t((int16_t) nybbles >> right_shift) << left_shift); - + // Apply IIR filter (8 is the most commonly used) int const filter = brr_header & 0x0C; int const p1 = pos [brr_buf_size - 1]; @@ -529,13 +540,13 @@ void Spc_Dsp::run( int clock_count ) s += p1 >> 1; s += (-p1) >> 5; } - + // Adjust and write sample CLAMP16( s ); s = (int16_t) (s * 2); pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around } - + if ( pos >= &v->buf [brr_buf_size] ) pos = v->buf; v->buf_pos = pos; @@ -548,7 +559,7 @@ skip_brr: v++; } while ( vbit < 0x100 ); - + // Echo position int echo_offset = m.echo_offset; #ifdef SPC_ISOLATED_ECHO_BUFFER @@ -563,23 +574,23 @@ skip_brr: if ( echo_offset >= m.echo_length ) echo_offset = 0; m.echo_offset = echo_offset; - + // FIR int echo_in_l = GET_LE16SA( echo_ptr + 0 ); int echo_in_r = GET_LE16SA( echo_ptr + 2 ); - + int (*echo_hist_pos) [2] = m.echo_hist_pos; if ( ++echo_hist_pos >= &m.echo_hist [echo_hist_size] ) echo_hist_pos = m.echo_hist; m.echo_hist_pos = echo_hist_pos; - + echo_hist_pos [0] [0] = echo_hist_pos [8] [0] = echo_in_l; echo_hist_pos [0] [1] = echo_hist_pos [8] [1] = echo_in_r; - + #define CALC_FIR_( i, in ) ((in) * (int8_t) REG(fir + i * 0x10)) echo_in_l = CALC_FIR_( 7, echo_in_l ); echo_in_r = CALC_FIR_( 7, echo_in_r ); - + #define CALC_FIR( i, ch ) CALC_FIR_( i, echo_hist_pos [i + 1] [ch] ) #define DO_FIR( i )\ echo_in_l += CALC_FIR( i, 0 );\ @@ -594,39 +605,39 @@ skip_brr: DO_FIR( 4 ); DO_FIR( 5 ); DO_FIR( 6 ); - + // Echo out if ( !(REG(flg) & 0x20) ) { int l = (echo_out_l >> 7) + ((echo_in_l * (int8_t) REG(efb)) >> 14); int r = (echo_out_r >> 7) + ((echo_in_r * (int8_t) REG(efb)) >> 14); - + // just to help pass more validation tests #if SPC_MORE_ACCURACY l &= ~1; r &= ~1; #endif - + CLAMP16( l ); CLAMP16( r ); - + SET_LE16A( echo_ptr + 0, l ); SET_LE16A( echo_ptr + 2, r ); } - + // Sound out - int l = (main_out_l * mvoll + echo_in_l * (int8_t) REG(evoll)) >> 14; - int r = (main_out_r * mvolr + echo_in_r * (int8_t) REG(evolr)) >> 14; - + int l = (main_out_l * mvoll + echo_in_l * evoll) >> 14; + int r = (main_out_r * mvolr + echo_in_r * evolr) >> 14; + CLAMP16( l ); CLAMP16( r ); - + if ( (REG(flg) & 0x40) ) { l = 0; r = 0; } - + sample_t* out = m.out; WRITE_SAMPLES( l, r, out ); m.out = out; @@ -647,26 +658,32 @@ void Spc_Dsp::mute_voices( int mask ) } } +Spc_Dsp::Spc_Dsp() +{ + memset(&m, 0, sizeof(state_t)); +} + void Spc_Dsp::init( void* ram_64k ) { m.ram = (uint8_t*) ram_64k; mute_voices( 0 ); disable_surround( false ); + disable_echo( false ); set_output( 0, 0 ); reset(); - + + // be sure this sign-extends + blaarg_static_assert( (int16_t) 0x8000 == -0x8000, "This compiler doesn't sign-extend during integer promotion" ); + + // be sure right shift preserves sign + blaarg_static_assert( (-1 >> 1) == -1, "This compiler doesn't preserve sign on right-shift" ); + #ifndef NDEBUG - // be sure this sign-extends - assert( (int16_t) 0x8000 == -0x8000 ); - - // be sure right shift preserves sign - assert( (-1 >> 1) == -1 ); - // check clamp macro int i; i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); - + blargg_verify_byte_order(); #endif } @@ -674,13 +691,13 @@ void Spc_Dsp::init( void* ram_64k ) void Spc_Dsp::soft_reset_common() { require( m.ram ); // init() must have been called already - + m.noise = 0x4000; m.echo_hist_pos = m.echo_hist; m.every_other_sample = 1; m.echo_offset = 0; m.phase = 0; - + init_counter(); } @@ -694,7 +711,7 @@ void Spc_Dsp::load( uint8_t const regs [register_count] ) { memcpy( m.regs, regs, sizeof m.regs ); memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); - + // Internal state int i; for ( i = voice_count; --i >= 0; ) @@ -704,7 +721,7 @@ void Spc_Dsp::load( uint8_t const regs [register_count] ) v.buf_pos = v.buf; } m.new_kon = REG(kon); - + mute_voices( m.mute_mask ); soft_reset_common(); } diff --git a/Frameworks/GME/gme/Spc_Dsp.h b/Frameworks/GME/gme/Spc_Dsp.h index b4379ca09..8e0511451 100644 --- a/Frameworks/GME/gme/Spc_Dsp.h +++ b/Frameworks/GME/gme/Spc_Dsp.h @@ -8,8 +8,9 @@ struct Spc_Dsp { public: + Spc_Dsp(); // Setup - + // Initializes DSP and has it use the 64K RAM provided void init( void* ram_64k ); @@ -24,13 +25,13 @@ public: int sample_count() const; // Emulation - + // Resets DSP to power-on state void reset(); // Emulates pressing reset switch on SNES void soft_reset(); - + // Reads/writes DSP registers. For accuracy, you must first call spc_run_dsp() // to catch the DSP up to present. int read ( int addr ) const; @@ -50,8 +51,10 @@ public: // If true, prevents channels and global volumes from being phase-negated void disable_surround( bool disable = true ); + void disable_echo( bool disable = true ); + // State - + // Resets DSP and uses supplied values to initialize registers enum { register_count = 128 }; void load( uint8_t const regs [register_count] ); @@ -86,9 +89,9 @@ public: sample_t const* out_pos() const { return m.out; } public: BLARGG_DISABLE_NOTHROW - + enum { echo_hist_size = 8 }; - + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; enum { brr_buf_size = 12 }; struct voice_t @@ -109,16 +112,16 @@ private: struct state_t { uint8_t regs [register_count]; - + #ifdef SPC_ISOLATED_ECHO_BUFFER // Echo buffer, for dodgy SPC rips that were only made to work in dodgy emulators uint8_t echo_ram [64 * 1024]; #endif - + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) int echo_hist [echo_hist_size * 2] [2]; int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] - + int every_other_sample; // toggles every sample int kon; // KON value when last checked int noise; @@ -126,25 +129,26 @@ private: int echo_length; // number of bytes that echo_offset will stop at int phase; // next clock cycle to run (0-31) unsigned counters [4]; - + int new_kon; int t_koff; - + voice_t voices [voice_count]; - + unsigned* counter_select [32]; - + // non-emulation state uint8_t* ram; // 64K shared RAM between DSP and SMP int mute_mask; int surround_threshold; + int echo_enable; sample_t* out; sample_t* out_end; sample_t* out_begin; sample_t extra [extra_size]; }; state_t m; - + void init_counter(); void run_counter( int ); void soft_reset_common(); @@ -166,14 +170,14 @@ inline void Spc_Dsp::update_voice_vol( int addr ) { int l = (int8_t) m.regs [addr + v_voll]; int r = (int8_t) m.regs [addr + v_volr]; - + if ( l * r < m.surround_threshold ) { // signs differ, so negate those that are negative l ^= l >> 7; r ^= r >> 7; } - + voice_t& v = m.voices [addr >> 4]; int enabled = v.enabled; v.volume [0] = l & enabled; @@ -183,7 +187,7 @@ inline void Spc_Dsp::update_voice_vol( int addr ) inline void Spc_Dsp::write( int addr, int data ) { assert( (unsigned) addr < register_count ); - + m.regs [addr] = (uint8_t) data; int low = addr & 0x0F; if ( low < 0x2 ) // voice volumes @@ -194,7 +198,7 @@ inline void Spc_Dsp::write( int addr, int data ) { if ( addr == r_kon ) m.new_kon = (uint8_t) data; - + if ( addr == r_endx ) // always cleared, regardless of data written m.regs [r_endx] = 0; } @@ -205,6 +209,11 @@ inline void Spc_Dsp::disable_surround( bool disable ) m.surround_threshold = disable ? 0 : -0x4000; } +inline void Spc_Dsp::disable_echo( bool disable ) +{ + m.echo_enable = !disable; +} + #define SPC_NO_COPY_STATE_FUNCS 1 #define SPC_LESS_ACCURATE 1 diff --git a/Frameworks/GME/gme/Spc_Emu.cpp b/Frameworks/GME/gme/Spc_Emu.cpp index 3a9039181..256e6c226 100644 --- a/Frameworks/GME/gme/Spc_Emu.cpp +++ b/Frameworks/GME/gme/Spc_Emu.cpp @@ -7,16 +7,6 @@ #include #include -#ifdef RARDLL -#define PASCAL -#define CALLBACK -#define LONG long -#define HANDLE void * -#define LPARAM intptr_t -#define UINT __attribute__((unused)) unsigned int -#include -#endif - /* Copyright (C) 2004-2009 Shay Green. This module is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either @@ -35,17 +25,17 @@ using std::max; // TODO: support Spc_Filter's bass -Spc_Emu::Spc_Emu( gme_type_t type ) +Spc_Emu::Spc_Emu() { - set_type( type ); - + set_type( gme_spc_type ); + static const char* const names [SuperFamicom::SPC_DSP::voice_count] = { "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" }; set_voice_names( names ); - + set_gain( 1.4 ); - + enable_echo( true ); } @@ -60,19 +50,6 @@ byte const* Spc_Emu::trailer() const { return &file_data [min( file_size, spc_si long Spc_Emu::trailer_size() const { return max( 0L, file_size - spc_size ); } -byte const* Rsn_Emu::trailer( int track ) const -{ - const byte *track_data = spc[track]; - long track_size = spc[track + 1] - spc[track]; - return &track_data [min( track_size, spc_size )]; -} - -long Rsn_Emu::trailer_size( int track ) const -{ - long track_size = spc[track + 1] - spc[track]; - return max( 0L, track_size - spc_size ); -} - static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) { // header @@ -89,13 +66,13 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) debug_printf( "Extra data after SPC xid6 info\n" ); end = in + info_size; } - + int year = 0; char copyright [256 + 5]; int copyright_len = 0; int const year_len = 5; int disc = 0, track = 0; - + while ( end - in >= 4 ) { // header @@ -109,7 +86,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) check( false ); break; // block goes past end of data } - + // handle specific block types char* field = 0; switch ( id ) @@ -123,7 +100,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) case 0x11: disc = data; break; case 0x12: track = data; break; case 0x14: year = data; break; - + //case 0x30: // intro length // Many SPCs have intro length set wrong for looped tracks, making it useless /* @@ -141,7 +118,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) } break; */ - + case 0x33: check( len == 4 ); if ( len >= 4 ) @@ -149,12 +126,12 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) out->fade_length = get_le32( in ) / 64; } break; - + case 0x13: copyright_len = min( len, (int) sizeof copyright - year_len ); memcpy( ©right [year_len], in, copyright_len ); break; - + default: if ( id < 0x01 || (id > 0x07 && id < 0x10) || (id > 0x14 && id < 0x30) || id > 0x36 ) @@ -166,10 +143,10 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) check( type == 1 ); Gme_File::copy_field_( field, (char const*) in, len ); } - + // skip to next block in += len; - + // blocks are supposed to be 4-byte aligned with zero-padding... byte const* unaligned = in; while ( (in - begin) & 3 && in < end ) @@ -183,7 +160,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) } } } - + char* p = ©right [year_len]; if ( year ) { @@ -197,13 +174,13 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) } if ( copyright_len ) Gme_File::copy_field_( out->copyright, p, copyright_len ); - + if ( disc > 0 && disc <= 9 ) { out->disc [0] = disc + '0'; out->disc [1] = 0; } - + if ( track > 255 && track < ( ( 100 << 8 ) - 1 ) ) { char* p = ©right [3]; @@ -217,7 +194,7 @@ static void get_spc_xid6( byte const* begin, long size, track_info_t* out ) } memcpy( out->track, p, ©right [4] - p ); } - + check( in == end ); } @@ -245,7 +222,7 @@ static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid len_secs = get_le16( h.len_secs ); if ( len_secs < 0x1FFF ) out->length = len_secs * 1000; - + long fade_msec = 0; for ( i = 0; i < 4; i++ ) { @@ -265,15 +242,15 @@ static void get_spc_info( Spc_Emu::header_t const& h, byte const* xid6, long xid fade_msec = get_le32( h.fade_msec ); if ( fade_msec < 0x7FFF ) out->fade_length = fade_msec; - + int offset = (h.author [0] < ' ' || unsigned (h.author [0] - '0') <= 9); Gme_File::copy_field_( out->author, &h.author [offset], sizeof h.author - offset ); - + GME_COPY_FIELD( h, out, song ); GME_COPY_FIELD( h, out, game ); GME_COPY_FIELD( h, out, dumper ); GME_COPY_FIELD( h, out, comment ); - + if ( xid6_size ) get_spc_xid6( xid6, xid6_size, out ); } @@ -284,12 +261,6 @@ blargg_err_t Spc_Emu::track_info_( track_info_t* out, int ) const return 0; } -blargg_err_t Rsn_Emu::track_info_( track_info_t* out, int track ) const -{ - get_spc_info( header( track ), trailer( track ), trailer_size( track ), out ); - return 0; -} - static blargg_err_t check_spc_header( void const* header ) { if ( memcmp( header, "SNES-SPC700 Sound File Data", 27 ) ) @@ -301,15 +272,12 @@ struct Spc_File : Gme_Info_ { Spc_Emu::header_t header; blargg_vector xid6; - - Spc_File( gme_type_t type ) { set_type( type ); } - Spc_File() : Spc_File( gme_spc_type ) {} - + + Spc_File() { set_type( gme_spc_type ); } + blargg_err_t load_( Data_Reader& in ) { long file_size = in.remain(); - if ( is_archive ) - return 0; if ( file_size < 0x10180 ) return gme_wrong_file_type; RETURN_ERR( in.read( &header, head_size ) ); @@ -323,7 +291,7 @@ struct Spc_File : Gme_Info_ } return 0; } - + blargg_err_t track_info_( track_info_t* out, int ) const { get_spc_info( header, xid6.begin(), xid6.size(), out ); @@ -338,106 +306,6 @@ static gme_type_t_ const gme_spc_type_ = { "Super Nintendo", 1, &new_spc_emu, &n extern gme_type_t const gme_spc_type = &gme_spc_type_; -#ifdef RARDLL -static int CALLBACK call_rsn(UINT msg, LPARAM UserData, LPARAM P1, LPARAM P2) -{ - byte **bp = (byte **)UserData; - unsigned char *addr = (unsigned char *)P1; - memcpy( *bp, addr, P2 ); - *bp += P2; - return 0; -} -#endif - -struct Rsn_File : Spc_File -{ - blargg_vector 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 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(track) >= spc.size() ) - return "Invalid track"; - long xid6_size = spc[track + 1] - ( spc[track] + head_size ); - get_spc_info( - *(Spc_Emu::header_t const*) spc[track], - spc[track] + head_size, xid6_size, out - ); - return 0; - } -}; - -static Music_Emu* new_rsn_emu () { return BLARGG_NEW Rsn_Emu ; } -static Music_Emu* new_rsn_file() { return BLARGG_NEW Rsn_File; } - -static gme_type_t_ const gme_rsn_type_ = { "Super Nintendo", 0, &new_rsn_emu, &new_rsn_file, "RSN", 0 }; -extern gme_type_t const gme_rsn_type = &gme_rsn_type_; - - // Setup blargg_err_t Spc_Emu::set_sample_rate_( long sample_rate ) @@ -466,14 +334,17 @@ void Spc_Emu::mute_voices_( int m ) smp.dsp.channel_enable( i, !( m & j ) ); } +void Spc_Emu::disable_echo_( bool disable ) +{ + smp.dsp.spc_dsp.enable_echo( !disable ); +} + blargg_err_t Spc_Emu::load_mem_( byte const* in, long size ) { - assert( offsetof (header_t,unused2 [46]) == header_size ); + blaarg_static_assert( offsetof (header_t,unused2 [46]) == header_size, "SPC Header layout incorrect!" ); file_data = in; file_size = size; set_voice_count( SuperFamicom::SPC_DSP::voice_count ); - if ( is_archive ) - return 0; if ( size < 0x10180 ) return gme_wrong_file_type; return check_spc_header( in ); @@ -493,36 +364,36 @@ blargg_err_t Spc_Emu::start_track_( int track ) filter.clear(); smp.reset(); const byte * ptr = file_data; - + Spc_Emu::header_t & header = *(Spc_Emu::header_t*)ptr; ptr += sizeof(header); - + smp.regs.pc = header.pc[0] + header.pc[1] * 0x100; smp.regs.a = header.a; smp.regs.x = header.x; smp.regs.y = header.y; smp.regs.p = header.psw; smp.regs.s = header.sp; - + memcpy( smp.apuram, ptr, sizeof smp.apuram ); - + // clear input ports that contain out port data from dump memset( smp.apuram + 0xF4, 0, 4 ); memcpy( smp.sfm_last, ptr + 0xF4, 4 ); - + static const uint8_t regs_to_copy[][2] = { {0xFC,0xFF}, {0xFB,0xFF}, {0xFA,0xFF}, {0xF9,0xFF}, {0xF8,0xFF}, {0xF2,0xFF}, {0xF1,0x87} }; - + for (auto n : regs_to_copy) smp.op_buswrite( n[0], ptr[ n[0] ] & n[1] ); - + smp.timer0.stage3_ticks = ptr[ 0xFD ] & 0x0F; smp.timer1.stage3_ticks = ptr[ 0xFE ] & 0x0F; smp.timer2.stage3_ticks = ptr[ 0xFF ] & 0x0F; ptr += sizeof smp.apuram; - + smp.dsp.spc_dsp.load( ptr ); #if 1 @@ -542,7 +413,7 @@ blargg_err_t Spc_Emu::start_track_( int track ) filter.set_gain( (int) (gain() * SPC_Filter::gain_unit) ); track_info_t spc_info; RETURN_ERR( track_info_( &spc_info, track ) ); - + // Set a default track length, need a non-zero fadeout if ( autoload_playback_limit() && ( spc_info.length > 0 ) ) set_fade ( spc_info.length, 50 ); @@ -563,15 +434,15 @@ blargg_err_t Spc_Emu::skip_( long count ) count = long (count * resampler.ratio()) & ~1; count -= resampler.skip_input( count ); } - + // TODO: shouldn't skip be adjusted for the 64 samples read afterwards? - + if ( count > 0 ) { smp.skip( count ); filter.clear(); } - + // eliminate pop due to resampler if ( sample_rate() != native_sample_rate ) { @@ -587,7 +458,7 @@ blargg_err_t Spc_Emu::play_( long count, sample_t* out ) { if ( sample_rate() == native_sample_rate ) return play_and_filter( count, out ); - + long remain = count; while ( remain > 0 ) { @@ -602,60 +473,3 @@ blargg_err_t Spc_Emu::play_( long count, sample_t* out ) check( remain == 0 ); return 0; } - -blargg_err_t Rsn_Emu::load_archive( const char* path ) -{ -#ifdef RARDLL - struct RAROpenArchiveData data = { - .ArcName = (char *)path, - .OpenMode = RAR_OM_LIST, .OpenResult = 0, - .CmtBuf = 0, .CmtBufSize = 0, .CmtSize = 0, .CmtState = 0 - }; - - // get the file count and unpacked size - long pos = 0; - int count = 0; - HANDLE PASCAL rar = RAROpenArchive( &data ); - struct RARHeaderData head; - for ( ; RARReadHeader( rar, &head ) == ERAR_SUCCESS; count++ ) - { - RARProcessFile( rar, RAR_SKIP, 0, 0 ); - pos += head.UnpSize; - } - rsn.resize( pos ); - spc.resize( count + 1 ); - RARCloseArchive( rar ); - - // copy the stream and index the tracks - byte *bp = &rsn[0]; - data.OpenMode = RAR_OM_EXTRACT; - rar = RAROpenArchive( &data ); - RARSetCallback( rar, call_rsn, (intptr_t)&bp ); - for ( count = 0, pos = 0; RARReadHeader( rar, &head ) == ERAR_SUCCESS; ) - { - RARProcessFile( rar, RAR_TEST, 0, 0 ); - if ( !check_spc_header( bp - head.UnpSize ) ) - spc[count++] = &rsn[pos]; - pos += head.UnpSize; - } - spc[count] = &rsn[pos]; - set_track_count( count ); - RARCloseArchive( rar ); - - return 0; -#else - (void) path; - return gme_wrong_file_type; -#endif -} - -blargg_err_t Rsn_Emu::start_track_( int track ) -{ - if ( static_cast(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() { } diff --git a/Frameworks/GME/gme/Spc_Emu.h b/Frameworks/GME/gme/Spc_Emu.h index c8fcda082..68d455a5d 100644 --- a/Frameworks/GME/gme/Spc_Emu.h +++ b/Frameworks/GME/gme/Spc_Emu.h @@ -14,7 +14,7 @@ public: // The Super Nintendo hardware samples at 32kHz. Other sample rates are // handled by resampling the 32kHz output; emulation accuracy is not affected. enum { native_sample_rate = 32000 }; - + // SPC file header enum { header_size = 0x100 }; struct header_t @@ -37,26 +37,26 @@ public: byte emulator; byte unused2 [46]; }; - + // Header for currently loaded file header_t const& header() const { return *(header_t const*) file_data; } - + // Prevents channels and global volumes from being phase-negated void disable_surround( bool disable = true ); - + // Enables gaussian=0, cubic=1 or sinc=2 interpolation // Or negative levels for worse quality, linear=-1 or nearest=-2 void interpolation_level( int level = 0 ); - + // Enables native echo void enable_echo( bool enable = true ); void mute_effects( bool mute ); - + SuperFamicom::SMP const* get_smp() const; SuperFamicom::SMP * get_smp(); - + static gme_type_t static_type() { return gme_spc_type; } - + public: // deprecated using Music_Emu::load; @@ -66,8 +66,7 @@ public: long trailer_size() const; public: - Spc_Emu( gme_type_t ); - Spc_Emu() : Spc_Emu( gme_spc_type ) {} + Spc_Emu(); ~Spc_Emu(); protected: blargg_err_t load_mem_( byte const*, long ); @@ -77,15 +76,16 @@ protected: blargg_err_t play_( long, sample_t* ); blargg_err_t skip_( long ); void mute_voices_( int ); + void disable_echo_( bool disable ); void set_tempo_( double ); void enable_accuracy_( bool ); +private: byte const* file_data; long file_size; -private: Fir_Resampler<24> resampler; SPC_Filter filter; SuperFamicom::SMP smp; - + blargg_err_t play_and_filter( long count, sample_t out [] ); }; @@ -96,20 +96,4 @@ inline void Spc_Emu::mute_effects( bool mute ) { enable_echo(!mute); } inline SuperFamicom::SMP const* Spc_Emu::get_smp() const { return &smp; } inline SuperFamicom::SMP * Spc_Emu::get_smp() { return &smp; } -class Rsn_Emu : public Spc_Emu { -public: - Rsn_Emu() : Spc_Emu( gme_rsn_type ) { is_archive = true; } - ~Rsn_Emu(); - blargg_err_t load_archive( const char* ); - header_t const& header( int track ) const { return *(header_t const*) spc[track]; } - byte const* trailer( int ) const; // use track_info() - long trailer_size( int ) const; -protected: - blargg_err_t track_info_( track_info_t*, int ) const; - blargg_err_t start_track_( int ); -private: - blargg_vector rsn; - blargg_vector spc; -}; - #endif diff --git a/Frameworks/GME/gme/Spc_Filter.cpp b/Frameworks/GME/gme/Spc_Filter.cpp index 2cc77fc93..1951bd13a 100644 --- a/Frameworks/GME/gme/Spc_Filter.cpp +++ b/Frameworks/GME/gme/Spc_Filter.cpp @@ -30,7 +30,7 @@ SPC_Filter::SPC_Filter() void SPC_Filter::run( short* io, int count ) { require( (count & 1) == 0 ); // must be even - + int const gain = this->gain; if ( enabled ) { @@ -42,26 +42,26 @@ void SPC_Filter::run( short* io, int count ) int sum = (--c)->sum; int pp1 = c->pp1; int p1 = c->p1; - + for ( int i = 0; i < count; i += 2 ) { // Low-pass filter (two point FIR with coeffs 0.25, 0.75) int f = io [i] + p1; p1 = io [i] * 3; - + // High-pass filter ("leaky integrator") int delta = f - pp1; pp1 = f; int s = sum >> (gain_bits + 2); sum += (delta * gain) - (sum >> bass); - + // Clamp to 16 bits if ( (short) s != s ) s = (s >> 31) ^ 0x7FFF; - + io [i] = (short) s; } - + c->p1 = p1; c->pp1 = pp1; c->sum = sum; diff --git a/Frameworks/GME/gme/Spc_Filter.h b/Frameworks/GME/gme/Spc_Filter.h index d9994af5f..a4c682c96 100644 --- a/Frameworks/GME/gme/Spc_Filter.h +++ b/Frameworks/GME/gme/Spc_Filter.h @@ -8,35 +8,35 @@ struct SPC_Filter { public: - + // Filters count samples of stereo sound in place. Count must be a multiple of 2. typedef short sample_t; void run( sample_t* io, int count ); - + // Optional features // Clears filter to silence void clear(); - + // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit // are fine, since output is clamped to 16-bit sample range. - enum { gain_unit = 0x100 }; + static const unsigned int gain_unit = 0x100; void set_gain( int gain ); - + // Enables/disables filtering (when disabled, gain is still applied) void enable( bool b ); - + // Sets amount of bass (logarithmic scale) - enum { bass_none = 0 }; - enum { bass_norm = 8 }; // normal amount - enum { bass_max = 31 }; + static const unsigned int bass_none = 0; + static const unsigned int bass_norm = 8; // normal amount + static const unsigned int bass_max = 31; void set_bass( int bass ); - + public: SPC_Filter(); BLARGG_DISABLE_NOTHROW private: - enum { gain_bits = 8 }; + static const unsigned int gain_bits = 8; int gain; int bass; bool enabled; diff --git a/Frameworks/GME/gme/Vgm_Emu.cpp b/Frameworks/GME/gme/Vgm_Emu.cpp index af59c5e7e..bbaec3835 100644 --- a/Frameworks/GME/gme/Vgm_Emu.cpp +++ b/Frameworks/GME/gme/Vgm_Emu.cpp @@ -20,9 +20,9 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "blargg_source.h" -double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow -double const rolloff = 0.990; -double const oversample_factor = 1.0; +static double const fm_gain = 3.0; // FM emulators are internally quieter to avoid 16-bit overflow +static double const rolloff = 0.990; +static double const oversample_factor = 1.0; using std::min; using std::max; @@ -30,16 +30,18 @@ using std::max; Vgm_Emu::Vgm_Emu() { disable_oversampling_ = false; + psg_dual = false; + psg_t6w28 = false; psg_rate = 0; set_type( gme_vgm_type ); - + static int const types [8] = { wave_type | 1, wave_type | 0, wave_type | 2, noise_type | 0 }; set_voice_types( types ); - + set_silence_lookahead( 1 ); // tracks should already be trimmed - + set_equalizer( make_equalizer( -14.0, 80 ) ); } @@ -88,17 +90,17 @@ static void parse_gd3( byte const* in, byte const* end, track_info_t* out ) in = get_gd3_str ( in, end, out->comment ); } -int const gd3_header_size = 12; +static int const gd3_header_size = 12; static long check_gd3_header( byte const* h, long remain ) { if ( remain < gd3_header_size ) return 0; if ( memcmp( h, "Gd3 ", 4 ) ) return 0; if ( get_le32( h + 4 ) >= 0x200 ) return 0; - + long gd3_size = get_le32( h + 8 ); if ( gd3_size > remain - gd3_header_size ) return 0; - + return gd3_size; } @@ -106,19 +108,19 @@ byte const* Vgm_Emu::gd3_data( int* size ) const { if ( size ) *size = 0; - + long gd3_offset = get_le32( header().gd3_offset ) - 0x2C; if ( gd3_offset < 0 ) return 0; - + byte const* gd3 = data + header_size + gd3_offset; long gd3_size = check_gd3_header( gd3, data_end - gd3 ); if ( !gd3_size ) return 0; - + if ( size ) *size = gd3_size + gd3_header_size; - + return gd3; } @@ -145,12 +147,12 @@ static void get_vgm_length( Vgm_Emu::header_t const& h, track_info_t* out ) blargg_err_t Vgm_Emu::track_info_( track_info_t* out, int ) const { get_vgm_length( header(), out ); - + int size; byte const* gd3 = gd3_data( &size ); if ( gd3 ) parse_gd3( gd3 + gd3_header_size, gd3 + size, out ); - + return 0; } @@ -165,18 +167,18 @@ struct Vgm_File : Gme_Info_ { Vgm_Emu::header_t h; blargg_vector gd3; - + Vgm_File() { set_type( gme_vgm_type ); } - + blargg_err_t load_( Data_Reader& in ) { long file_size = in.remain(); if ( file_size <= Vgm_Emu::header_size ) return gme_wrong_file_type; - + RETURN_ERR( in.read( &h, Vgm_Emu::header_size ) ); RETURN_ERR( check_vgm_header( h ) ); - + long gd3_offset = get_le32( h.gd3_offset ) - 0x2C; long remain = file_size - Vgm_Emu::header_size - gd3_offset; byte gd3_h [gd3_header_size]; @@ -193,7 +195,7 @@ struct Vgm_File : Gme_Info_ } return 0; } - + blargg_err_t track_info_( track_info_t* out, int ) const { get_vgm_length( h, out ); @@ -226,7 +228,7 @@ void Vgm_Emu::set_tempo_( double t ) // TODO: remove? calculates vgm_rate more accurately (above differs at most by one Hz only) //blip_time_factor = (long) floor( double (1L << blip_time_bits) * psg_rate / 44100 / t + 0.5 ); //vgm_rate = (long) floor( double (1L << blip_time_bits) * psg_rate / blip_time_factor + 0.5 ); - + fm_time_factor = 2 + (long) floor( fm_rate * (1L << fm_time_bits) / vgm_rate + 0.5 ); } } @@ -324,17 +326,17 @@ void Vgm_Emu::mute_voices_( int mask ) blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size ) { - assert( offsetof (header_t,unused2 [8]) == header_size ); - + blaarg_static_assert( offsetof (header_t,unused2 [8]) == header_size, "VGM Header layout incorrect!" ); + if ( new_size <= header_size ) return gme_wrong_file_type; - + header_t const& h = *(header_t const*) new_data; - + RETURN_ERR( check_vgm_header( h ) ); - + check( get_le32( h.version ) <= 0x150 ); - + // psg rate psg_rate = get_le32( h.psg_rate ); if ( !psg_rate ) @@ -343,25 +345,25 @@ blargg_err_t Vgm_Emu::load_mem_( byte const* new_data, long new_size ) psg_t6w28 = ( psg_rate & 0x80000000 ) != 0; psg_rate &= 0x0FFFFFFF; blip_buf.clock_rate( psg_rate ); - + data = new_data; data_end = new_data + new_size; - + // get loop loop_begin = data_end; if ( get_le32( h.loop_offset ) ) loop_begin = &data [get_le32( h.loop_offset ) + offsetof (header_t,loop_offset)]; - + set_voice_count( psg[0].osc_count ); - + RETURN_ERR( setup_fm() ); - + static const char* const fm_names [] = { "FM 1", "FM 2", "FM 3", "FM 4", "FM 5", "FM 6", "PCM", "PSG" }; static const char* const psg_names [] = { "Square 1", "Square 2", "Square 3", "Noise" }; set_voice_names( uses_fm ? fm_names : psg_names ); - + // do after FM in case output buffer is changed return Classic_Emu::setup_buffer( psg_rate ); } @@ -374,11 +376,11 @@ blargg_err_t Vgm_Emu::setup_fm() bool ym2413_dual = ( ym2413_rate & 0x40000000 ) != 0; if ( ym2413_rate && get_le32( header().version ) < 0x110 ) update_fm_rates( &ym2413_rate, &ym2612_rate ); - + uses_fm = false; - + fm_rate = blip_buf.sample_rate() * oversample_factor; - + if ( ym2612_rate ) { ym2612_rate &= ~0xC0000000; @@ -395,7 +397,7 @@ blargg_err_t Vgm_Emu::setup_fm() } set_voice_count( 8 ); } - + if ( !uses_fm && ym2413_rate ) { ym2413_rate &= ~0xC0000000; @@ -418,7 +420,7 @@ blargg_err_t Vgm_Emu::setup_fm() } set_voice_count( 8 ); } - + if ( uses_fm ) { RETURN_ERR( Dual_Resampler::reset( blip_buf.length() * blip_buf.sample_rate() / 1000 ) ); @@ -435,7 +437,7 @@ blargg_err_t Vgm_Emu::setup_fm() psg[0].volume( gain() ); psg[1].volume( gain() ); } - + return 0; } @@ -447,7 +449,7 @@ blargg_err_t Vgm_Emu::start_track_( int track ) psg[0].reset( get_le16( header().noise_feedback ), header().noise_width ); if ( psg_dual ) psg[1].reset( get_le16( header().noise_feedback ), header().noise_width ); - + dac_disabled = -1; pos = data + header_size; pcm_data = pos; @@ -461,7 +463,7 @@ blargg_err_t Vgm_Emu::start_track_( int track ) if ( data_offset ) pos += data_offset + offsetof (header_t,data_offset) - 0x40; } - + if ( uses_fm ) { if ( ym2413[0].enabled() ) @@ -469,13 +471,13 @@ blargg_err_t Vgm_Emu::start_track_( int track ) if ( ym2413[1].enabled() ) ym2413[1].reset(); - + if ( ym2612[0].enabled() ) ym2612[0].reset(); if ( ym2612[1].enabled() ) ym2612[1].reset(); - + fm_time_offset = 0; blip_buf.clear(); Dual_Resampler::clear(); @@ -496,7 +498,7 @@ blargg_err_t Vgm_Emu::play_( long count, sample_t* out ) { if ( !uses_fm ) return Classic_Emu::play_( count, out ); - + Dual_Resampler::dual_play( count, out, blip_buf ); return 0; } diff --git a/Frameworks/GME/gme/Vgm_Emu.h b/Frameworks/GME/gme/Vgm_Emu.h index b84e1daa3..12a686f31 100644 --- a/Frameworks/GME/gme/Vgm_Emu.h +++ b/Frameworks/GME/gme/Vgm_Emu.h @@ -16,13 +16,13 @@ public: // True if custom buffer and custom equalization are supported // TODO: move into Music_Emu and rename to something like supports_custom_buffer() bool is_classic_emu() const { return !uses_fm; } - + blargg_err_t set_multi_channel ( bool is_enabled ) override; - + // Disable running FM chips at higher than normal rate. Will result in slightly // more aliasing of high notes. void disable_oversampling( bool disable = true ) { disable_oversampling_ = disable; } - + // VGM header format enum { header_size = 0x40 }; struct header_t @@ -45,12 +45,12 @@ public: byte data_offset [4]; byte unused2 [8]; }; - + // Header for currently loaded file header_t const& header() const { return *(header_t const*) data; } - + static gme_type_t static_type() { return gme_vgm_type; } - + public: // deprecated using Music_Emu::load; diff --git a/Frameworks/GME/gme/Vgm_Emu_Impl.cpp b/Frameworks/GME/gme/Vgm_Emu_Impl.cpp index 88a524459..81a3a61aa 100644 --- a/Frameworks/GME/gme/Vgm_Emu_Impl.cpp +++ b/Frameworks/GME/gme/Vgm_Emu_Impl.cpp @@ -35,7 +35,7 @@ enum { cmd_short_delay = 0x70, cmd_pcm_delay = 0x80, cmd_pcm_seek = 0xE0, - + cmd_gg_stereo_2 = 0x3F, cmd_psg_2 = 0x30, cmd_ym2413_2 = 0xA1, @@ -46,28 +46,28 @@ enum { ym2612_dac_port = 0x2A }; -inline int command_len( int command ) +static inline int command_len( int command ) { switch ( command >> 4 ) { case 0x03: case 0x04: return 2; - + case 0x05: case 0x0A: case 0x0B: return 3; - + case 0x0C: case 0x0D: return 4; - + case 0x0E: case 0x0F: return 5; } - + check( false ); return 1; } @@ -95,7 +95,7 @@ inline int Ym_Emu::run_until( int time ) } return true; } - + inline Vgm_Emu_Impl::fm_time_t Vgm_Emu_Impl::to_fm_time( vgm_time_t t ) const { return (t * fm_time_factor + fm_time_offset) >> fm_time_bits; @@ -120,7 +120,7 @@ void Vgm_Emu_Impl::write_pcm( vgm_time_t vgm_time, int amp ) blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) { - vgm_time_t vgm_time = this->vgm_time; + vgm_time_t vgm_time = this->vgm_time; byte const* pos = this->pos; if ( pos >= data_end ) { @@ -128,7 +128,7 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) if ( pos > data_end ) set_warning( "Stream lacked end event" ); } - + while ( vgm_time < end_time && pos < data_end ) { // TODO: be sure there are enough bytes left in stream for particular command @@ -138,23 +138,23 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) case cmd_end: pos = loop_begin; // if not looped, loop_begin == data_end break; - + case cmd_delay_735: vgm_time += 735; break; - + case cmd_delay_882: vgm_time += 882; break; - + case cmd_gg_stereo: psg[0].write_ggstereo( to_blip_time( vgm_time ), *pos++ ); break; - + case cmd_psg: psg[0].write_data( to_blip_time( vgm_time ), *pos++ ); break; - + case cmd_gg_stereo_2: psg[1].write_ggstereo( to_blip_time( vgm_time ), *pos++ ); break; @@ -167,11 +167,11 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) vgm_time += pos [1] * 0x100L + pos [0]; pos += 2; break; - + case cmd_byte_delay: vgm_time += *pos++; break; - + case cmd_ym2413: if ( ym2413[0].run_until( to_fm_time( vgm_time ) ) ) ym2413[0].write( pos [0], pos [1] ); @@ -183,7 +183,7 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) ym2413[1].write( pos [0], pos [1] ); pos += 2; break; - + case cmd_ym2612_port0: if ( pos [0] == ym2612_dac_port ) { @@ -200,7 +200,7 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) } pos += 2; break; - + case cmd_ym2612_port1: if ( ym2612[0].run_until( to_fm_time( vgm_time ) ) ) ym2612[0].write1( pos [0], pos [1] ); @@ -240,13 +240,13 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) pos += size; break; } - + case cmd_pcm_seek: pcm_pos = pcm_data + pos [3] * 0x1000000L + pos [2] * 0x10000L + pos [1] * 0x100L + pos [0]; pos += 4; break; - + default: int cmd = pos [-1]; switch ( cmd & 0xF0 ) @@ -255,15 +255,15 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) write_pcm( vgm_time, *pcm_pos++ ); vgm_time += cmd & 0x0F; break; - + case cmd_short_delay: vgm_time += (cmd & 0x0F) + 1; break; - + case 0x50: pos += 2; break; - + default: pos += command_len( cmd ) - 1; set_warning( "Unknown stream event" ); @@ -273,14 +273,14 @@ blip_time_t Vgm_Emu_Impl::run_commands( vgm_time_t end_time ) vgm_time -= end_time; this->pos = pos; this->vgm_time = vgm_time; - + return to_blip_time( end_time ); } int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ) { // to do: timing is working mostly by luck - + int min_pairs = sample_count >> 1; int vgm_time = ((long) min_pairs << fm_time_bits) / fm_time_factor - 1; assert( to_fm_time( vgm_time ) <= min_pairs ); @@ -288,7 +288,7 @@ int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* while ( (pairs = to_fm_time( vgm_time )) < min_pairs ) vgm_time++; //debug_printf( "pairs: %d, min_pairs: %d\n", pairs, min_pairs ); - + if ( ym2612[0].enabled() ) { ym2612[0].begin_frame( buf ); @@ -303,7 +303,7 @@ int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* ym2413[1].begin_frame( buf ); memset( buf, 0, pairs * stereo * sizeof *buf ); } - + run_commands( vgm_time ); if ( ym2612[0].enabled() ) @@ -315,14 +315,14 @@ int Vgm_Emu_Impl::play_frame( blip_time_t blip_time, int sample_count, sample_t* ym2413[0].run_until( pairs ); if ( ym2413[1].enabled() ) ym2413[1].run_until( pairs ); - + fm_time_offset = (vgm_time * fm_time_factor + fm_time_offset) - ((long) pairs << fm_time_bits); - + psg[0].end_frame( blip_time ); if ( psg_dual ) psg[1].end_frame( blip_time ); - + return pairs * stereo; } @@ -336,35 +336,35 @@ void Vgm_Emu_Impl::update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const { case cmd_end: return; - + case cmd_psg: case cmd_byte_delay: p += 2; break; - + case cmd_delay: p += 3; break; - + case cmd_data_block: p += 7 + get_le32( p + 3 ); break; - + case cmd_ym2413: *ym2612_rate = 0; return; - + case cmd_ym2612_port0: case cmd_ym2612_port1: *ym2612_rate = *ym2413_rate; *ym2413_rate = 0; return; - + case cmd_ym2151: *ym2413_rate = 0; *ym2612_rate = 0; return; - + default: p += command_len( *p ); } diff --git a/Frameworks/GME/gme/Vgm_Emu_Impl.h b/Frameworks/GME/gme/Vgm_Emu_Impl.h index 57805683f..a8588d474 100644 --- a/Frameworks/GME/gme/Vgm_Emu_Impl.h +++ b/Frameworks/GME/gme/Vgm_Emu_Impl.h @@ -29,44 +29,44 @@ public: typedef Classic_Emu::sample_t sample_t; protected: enum { stereo = 2 }; - + typedef int vgm_time_t; - + enum { fm_time_bits = 12 }; typedef int fm_time_t; long fm_time_offset; int fm_time_factor; fm_time_t to_fm_time( vgm_time_t ) const; - + enum { blip_time_bits = 12 }; int blip_time_factor; blip_time_t to_blip_time( vgm_time_t ) const; - + byte const* data; byte const* loop_begin; byte const* data_end; void update_fm_rates( long* ym2413_rate, long* ym2612_rate ) const; - + vgm_time_t vgm_time; byte const* pos; blip_time_t run_commands( vgm_time_t ); int play_frame( blip_time_t blip_time, int sample_count, sample_t* buf ); - + byte const* pcm_data; byte const* pcm_pos; int dac_amp; int dac_disabled; // -1 if disabled void write_pcm( vgm_time_t, int amp ); - + Ym_Emu ym2612[2]; Ym_Emu ym2413[2]; - + Blip_Buffer blip_buf; Sms_Apu psg[2]; bool psg_dual; bool psg_t6w28; Blip_Synth dac_synth; - + friend class Vgm_Emu; }; diff --git a/Frameworks/GME/gme/Ym2413_Emu.h b/Frameworks/GME/gme/Ym2413_Emu.h index ed4fd11df..c7024454f 100644 --- a/Frameworks/GME/gme/Ym2413_Emu.h +++ b/Frameworks/GME/gme/Ym2413_Emu.h @@ -9,21 +9,21 @@ class Ym2413_Emu { public: Ym2413_Emu(); ~Ym2413_Emu(); - + // Set output sample rate and chip clock rates, in Hz. Returns non-zero // if error. int set_rate( double sample_rate, double clock_rate ); - + // Reset to power-up state void reset(); - + // Mute voice n if bit n (1 << n) of mask is set enum { channel_count = 14 }; void mute_voices( int mask ); - + // Write 'data' to 'addr' void write( int addr, int data ); - + // Run and write pair_count samples to output typedef short sample_t; enum { out_chan_count = 2 }; // stereo diff --git a/Frameworks/GME/gme/Ym2612_Emu.h b/Frameworks/GME/gme/Ym2612_Emu.h index f62209a07..6acacc874 100644 --- a/Frameworks/GME/gme/Ym2612_Emu.h +++ b/Frameworks/GME/gme/Ym2612_Emu.h @@ -2,17 +2,30 @@ // Game_Music_Emu https://bitbucket.org/mpyne/game-music-emu/ +#if !defined(VGM_YM2612_GENS) && !defined(VGM_YM2612_NUKED) && !defined(VGM_YM2612_MAME) +#define VGM_YM2612_NUKED +#endif + #ifdef VGM_YM2612_GENS // LGPL v2.1+ license +# if defined(VGM_YM2612_NUKED) || defined(VGM_YM2612_MAME) +# error Only one of VGM_YM2612_GENS, VGM_YM2612_NUKED or VGM_YM2612_MAME can be defined +# endif #include "Ym2612_GENS.h" typedef Ym2612_GENS_Emu Ym2612_Emu; #endif #ifdef VGM_YM2612_NUKED // LGPL v2.1+ license +# if defined(VGM_YM2612_GENS) || defined(VGM_YM2612_MAME) +# error Only one of VGM_YM2612_GENS, VGM_YM2612_NUKED or VGM_YM2612_MAME can be defined +# endif #include "Ym2612_Nuked.h" typedef Ym2612_Nuked_Emu Ym2612_Emu; #endif #ifdef VGM_YM2612_MAME // GPL v2+ license +# if defined(VGM_YM2612_GENS) || defined(VGM_YM2612_NUKED) +# error Only one of VGM_YM2612_GENS, VGM_YM2612_NUKED or VGM_YM2612_MAME can be defined +# endif #include "Ym2612_MAME.h" typedef Ym2612_MAME_Emu Ym2612_Emu; #endif diff --git a/Frameworks/GME/gme/Ym2612_GENS.cpp b/Frameworks/GME/gme/Ym2612_GENS.cpp index d9930d62b..30fa8871d 100644 --- a/Frameworks/GME/gme/Ym2612_GENS.cpp +++ b/Frameworks/GME/gme/Ym2612_GENS.cpp @@ -2,6 +2,8 @@ // Based on Gens 2.10 ym2612.c +#ifdef VGM_YM2612_GENS + #include "Ym2612_GENS.h" #include @@ -36,7 +38,7 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include BLARGG_ENABLE_OPTIMIZER #endif -const int output_bits = 14; +static const int output_bits = 14; struct slot_t { @@ -179,7 +181,7 @@ struct state_t #define S2 1 #define S3 3 -inline void set_seg( slot_t& s, int seg ) +static inline void set_seg( slot_t& s, int seg ) { s.env_xor = 0; s.env_max = INT_MAX; @@ -253,7 +255,7 @@ static const unsigned char LFO_FMS_TAB [8] = LFO_FMS_BASE * 12, LFO_FMS_BASE * 24 }; -inline void YM2612_Special_Update() { } +static inline void YM2612_Special_Update() { } struct Ym2612_GENS_Impl { @@ -1024,7 +1026,7 @@ static void update_envelope_( slot_t* sl ) } } -inline void update_envelope( slot_t& sl ) +static inline void update_envelope( slot_t& sl ) { int ecmp = sl.Ecmp; if ( (sl.Ecnt += sl.Einc) >= ecmp ) @@ -1317,3 +1319,5 @@ void Ym2612_GENS_Impl::run( int pair_count, Ym2612_GENS_Emu::sample_t* out ) } void Ym2612_GENS_Emu::run( int pair_count, sample_t* out ) { impl->run( pair_count, out ); } + +#endif /* VGM_YM2612_GENS */ diff --git a/Frameworks/GME/gme/Ym2612_MAME.cpp b/Frameworks/GME/gme/Ym2612_MAME.cpp index d1cf21abd..7b99107d2 100644 --- a/Frameworks/GME/gme/Ym2612_MAME.cpp +++ b/Frameworks/GME/gme/Ym2612_MAME.cpp @@ -2,6 +2,8 @@ // Based on Mame YM2612 ym2612.c +#ifdef VGM_YM2612_MAME + #include "Ym2612_MAME.h" /* @@ -2578,7 +2580,7 @@ void ym2612_pre_generate(void *chip) refresh_fc_eg_chan( OPN, &cch[5] ); } -void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[]) +void ym2612_generate_one_native(void *chip, FMSAMPLE buffer[2]) { YM2612 *F2612 = (YM2612 *)chip; FM_OPN *OPN = &F2612->OPN; @@ -2819,7 +2821,7 @@ static void * ym2612_init(void *param, int clock, int rate, F2612->OPN.ST.clock = clock; #if RSM_ENABLE F2612->OPN.ST.rate = 53267; - F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / clock); + F2612->OPN.ST.rateratio = (INT32)(UINT32)((((UINT64)144 * rate) << RSM_FRAC) / 7670454); F2612->OPN.ST.framecnt = 1 << RSM_FRAC; memset(&(F2612->OPN.ST.cur_sample), 0x00, sizeof(FMSAMPLE) * 2); memset(&(F2612->OPN.ST.prev_sample), 0x00, sizeof(FMSAMPLE) * 2); @@ -3107,3 +3109,5 @@ void Ym2612_MAME_Emu::run(int pair_count, Ym2612_MAME_Emu::sample_t *out) (void) &Ym2612_MameImpl::TimerBOver; // squelch clang warning, which appears to be from a config choice if ( impl ) Ym2612_MameImpl::ym2612_generate( impl, out, pair_count, 1); } + +#endif /* VGM_YM2612_MAME */ diff --git a/Frameworks/GME/gme/Ym2612_Nuked.cpp b/Frameworks/GME/gme/Ym2612_Nuked.cpp index c408bd420..d691fe6b5 100644 --- a/Frameworks/GME/gme/Ym2612_Nuked.cpp +++ b/Frameworks/GME/gme/Ym2612_Nuked.cpp @@ -2,6 +2,8 @@ // Based on Nuked OPN2 ym3438.c and ym3438.h +#ifdef VGM_YM2612_NUKED + #include "Ym2612_Nuked.h" /* @@ -1839,7 +1841,7 @@ const char *Ym2612_Nuked_Emu::set_rate(double sample_rate, double clock_rate) void Ym2612_Nuked_Emu::reset() { Ym2612_NukedImpl::ym3438_t *chip_r = reinterpret_cast(impl); - if ( !chip_r ) Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast(prev_sample_rate), static_cast(prev_clock_rate) ); + if ( chip_r ) Ym2612_NukedImpl::OPN2_Reset( chip_r, static_cast(prev_sample_rate), static_cast(prev_clock_rate) ); } void Ym2612_Nuked_Emu::mute_voices(int mask) @@ -1870,3 +1872,5 @@ void Ym2612_Nuked_Emu::run(int pair_count, Ym2612_Nuked_Emu::sample_t *out) if ( !chip_r ) return; Ym2612_NukedImpl::OPN2_GenerateStreamMix(chip_r, out, pair_count); } + +#endif /* VGM_YM2612_NUKED */ diff --git a/Frameworks/GME/gme/blargg_common.h b/Frameworks/GME/gme/blargg_common.h index 11b86ce6c..5bcfd4809 100644 --- a/Frameworks/GME/gme/blargg_common.h +++ b/Frameworks/GME/gme/blargg_common.h @@ -4,19 +4,22 @@ #ifndef BLARGG_COMMON_H #define BLARGG_COMMON_H +#include "blargg_config.h" + #include #include #include #include -#undef BLARGG_COMMON_H -// allow blargg_config.h to #include blargg_common.h -#include "blargg_config.h" -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H +#if defined(__GNUC__) +#define BLARGG_PRINTFN(x,y) __attribute__((__format__(__printf__,x,y))) +#else +#define BLARGG_PRINTFN(x,y) +#endif // BLARGG_RESTRICT: equivalent to restrict, where supported -#if __GNUC__ >= 3 || _MSC_VER >= 1100 +#if (defined(__GNUC__) && (__GNUC__ >= 3)) || \ + (defined(_MSC_VER) && (_MSC_VER >= 1100)) #define BLARGG_RESTRICT __restrict #else #define BLARGG_RESTRICT @@ -27,11 +30,24 @@ #define STATIC_CAST(T,expr) ((T) (expr)) #endif +#if !defined(_MSC_VER) || _MSC_VER >= 1910 + #define blaarg_static_assert(cond, msg) static_assert(cond, msg) +#else + #define blaarg_static_assert(cond, msg) assert(cond) +#endif + // blargg_err_t (0 on success, otherwise error string) #ifndef blargg_err_t typedef const char* blargg_err_t; #endif +// Apply minus sign to unsigned type and prevent the warning being shown +template +inline T uMinus(T in) +{ + return ~(in - 1); +} + // blargg_vector - very lightweight vector of POD types (no constructor/destructor) template class blargg_vector { @@ -66,7 +82,8 @@ public: #define BLARGG_DISABLE_NOTHROW \ void* operator new ( size_t s ) noexcept { return malloc( s ); }\ void* operator new ( size_t s, const std::nothrow_t& ) noexcept { return malloc( s ); }\ - void operator delete ( void* p ) noexcept { free( p ); } + void operator delete ( void* p ) noexcept { free( p ); }\ + void operator delete ( void* p, const std::nothrow_t&) noexcept { free( p ); } #endif // Use to force disable exceptions for a specific allocation no matter what class @@ -79,74 +96,7 @@ public: #define BLARGG_2CHAR( a, b ) \ ((a&0xFF)*0x100L + (b&0xFF)) -// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, -// compiler is assumed to support bool. If undefined, availability is determined. -#ifndef BLARGG_COMPILER_HAS_BOOL - #if defined (__MWERKS__) - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (_MSC_VER) - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (__GNUC__) - // supports bool - #elif __cplusplus < 199711 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif -#endif -#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL - // If you get errors here, modify your blargg_config.h file - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -#if defined(__has_cpp_attribute) -# if __has_cpp_attribute(maybe_unused) -# define BLARGG_MAYBE_UNUSED [[maybe_unused]] -# endif -#endif - -#ifndef BLARGG_MAYBE_UNUSED -# define BLARGG_MAYBE_UNUSED -#endif - -// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough - -#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF - typedef long blargg_long; -#else - typedef int blargg_long; -#endif - -#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF - typedef unsigned long blargg_ulong; -#else - typedef unsigned blargg_ulong; -#endif - // int8_t etc. - -// TODO: Add CMake check for this, although I'd likely just point affected -// persons to a real compiler... -#if 1 || defined (HAVE_STDINT_H) - #include -#endif - -#if __GNUC__ >= 3 - #define BLARGG_DEPRECATED __attribute__ ((deprecated)) -#else - #define BLARGG_DEPRECATED -#endif - -// Use in place of "= 0;" for a pure virtual, since these cause calls to std C++ lib. -// During development, BLARGG_PURE( x ) expands to = 0; -// virtual int func() BLARGG_PURE( { return 0; } ) -#ifndef BLARGG_PURE - #define BLARGG_PURE( def ) def -#endif +#include #endif -#endif diff --git a/Frameworks/GME/gme/blargg_config.h b/Frameworks/GME/gme/blargg_config.h index 8487d3600..e9efa42bb 100644 --- a/Frameworks/GME/gme/blargg_config.h +++ b/Frameworks/GME/gme/blargg_config.h @@ -29,11 +29,9 @@ // Uncomment to use faster, lower quality sound synthesis //#define BLIP_BUFFER_FAST 1 -// Uncomment if automatic byte-order determination doesn't work +// Uncomment one of the following two if automatic byte-order determination doesn't work //#define BLARGG_BIG_ENDIAN 1 - -// Uncomment if you get errors in the bool section of blargg_common.h -//#define BLARGG_COMPILER_HAS_BOOL 1 +//#define BLARGG_LITTLE_ENDIAN 1 #define debug_printf(a, ...) diff --git a/Frameworks/GME/gme/blargg_endian.h b/Frameworks/GME/gme/blargg_endian.h index 46e58e2f0..c81f551a4 100644 --- a/Frameworks/GME/gme/blargg_endian.h +++ b/Frameworks/GME/gme/blargg_endian.h @@ -19,36 +19,12 @@ // BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only // one may be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) -#ifdef __GLIBC__ - // GCC handles this for us - #include - #if __BYTE_ORDER == __LITTLE_ENDIAN - #define BLARGG_LITTLE_ENDIAN 1 - #elif __BYTE_ORDER == __BIG_ENDIAN - #define BLARGG_BIG_ENDIAN 1 - #endif -#else - -#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ - (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) - #define BLARGG_LITTLE_ENDIAN 1 +// BLARGG_BIG_ENDIAN or BLARGG_LITTLE_ENDIAN must be defined by the build system. +#if !defined(BLARGG_BIG_ENDIAN) && !defined(BLARGG_LITTLE_ENDIAN) + #error Unspecified endianness. #endif - -#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ - defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#elif !defined (__mips__) - // No endian specified; assume little-endian, since it's most common - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#endif -#endif - -#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN - #undef BLARGG_LITTLE_ENDIAN - #undef BLARGG_BIG_ENDIAN +#if defined(BLARGG_BIG_ENDIAN) && defined(BLARGG_LITTLE_ENDIAN) + #error BLARGG_LITTLE_ENDIAN and BLARGG_BIG_ENDIAN are both defined. #endif inline void blargg_verify_byte_order() @@ -76,20 +52,20 @@ inline unsigned get_be16( void const* p ) (unsigned) ((unsigned char const*) p) [1]; } -inline blargg_ulong get_le32( void const* p ) +inline uint32_t get_le32( void const* p ) { - return (blargg_ulong) ((unsigned char const*) p) [3] << 24 | - (blargg_ulong) ((unsigned char const*) p) [2] << 16 | - (blargg_ulong) ((unsigned char const*) p) [1] << 8 | - (blargg_ulong) ((unsigned char const*) p) [0]; + return (uint32_t) ((unsigned char const*) p) [3] << 24 | + (uint32_t) ((unsigned char const*) p) [2] << 16 | + (uint32_t) ((unsigned char const*) p) [1] << 8 | + (uint32_t) ((unsigned char const*) p) [0]; } -inline blargg_ulong get_be32( void const* p ) +inline uint32_t get_be32( void const* p ) { - return (blargg_ulong) ((unsigned char const*) p) [0] << 24 | - (blargg_ulong) ((unsigned char const*) p) [1] << 16 | - (blargg_ulong) ((unsigned char const*) p) [2] << 8 | - (blargg_ulong) ((unsigned char const*) p) [3]; + return (uint32_t) ((unsigned char const*) p) [0] << 24 | + (uint32_t) ((unsigned char const*) p) [1] << 16 | + (uint32_t) ((unsigned char const*) p) [2] << 8 | + (uint32_t) ((unsigned char const*) p) [3]; } inline void set_le16( void* p, unsigned n ) @@ -104,7 +80,7 @@ inline void set_be16( void* p, unsigned n ) ((unsigned char*) p) [1] = (unsigned char) n; } -inline void set_le32( void* p, blargg_ulong n ) +inline void set_le32( void* p, uint32_t n ) { ((unsigned char*) p) [0] = (unsigned char) n; ((unsigned char*) p) [1] = (unsigned char) (n >> 8); @@ -112,7 +88,7 @@ inline void set_le32( void* p, blargg_ulong n ) ((unsigned char*) p) [3] = (unsigned char) (n >> 24); } -inline void set_be32( void* p, blargg_ulong n ) +inline void set_be32( void* p, uint32_t n ) { ((unsigned char*) p) [3] = (unsigned char) n; ((unsigned char*) p) [2] = (unsigned char) (n >> 8); @@ -132,7 +108,7 @@ inline void set_be32( void* p, blargg_ulong n ) #define GET_BE32( addr ) (*(uint32_t*) (addr)) #define SET_BE16( addr, data ) (void) (*(uint16_t*) (addr) = (data)) #define SET_BE32( addr, data ) (void) (*(uint32_t*) (addr) = (data)) - + #if BLARGG_CPU_POWERPC // PowerPC has special byte-reversed instructions #if defined (__MWERKS__) @@ -142,7 +118,7 @@ inline void set_be32( void* p, blargg_ulong n ) #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) #elif defined (__GNUC__) #define GET_LE16( addr ) ({unsigned short ppc_lhbrx_; __asm__ volatile( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr) : "memory" ); ppc_lhbrx_;}) - #define GET_LE32( addr ) ({unsigned short ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) + #define GET_LE32( addr ) ({unsigned int ppc_lwbrx_; __asm__ volatile( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr) : "memory" ); ppc_lwbrx_;}) #define SET_LE16( addr, in ) ({__asm__ volatile( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) #define SET_LE32( addr, in ) ({__asm__ volatile( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) : "memory" );}) #endif @@ -172,13 +148,13 @@ inline void set_be32( void* p, blargg_ulong n ) // auto-selecting versions -inline void set_le( uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } -inline void set_be( uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } -inline unsigned get_le( uint16_t* p ) { return GET_LE16( p ); } -inline blargg_ulong get_le( uint32_t* p ) { return GET_LE32( p ); } -inline unsigned get_be( uint16_t* p ) { return GET_BE16( p ); } -inline blargg_ulong get_be( uint32_t* p ) { return GET_BE32( p ); } +inline void set_le( uint16_t* p, unsigned n ) { SET_LE16( p, n ); } +inline void set_le( uint32_t* p, uint32_t n ) { SET_LE32( p, n ); } +inline void set_be( uint16_t* p, unsigned n ) { SET_BE16( p, n ); } +inline void set_be( uint32_t* p, uint32_t n ) { SET_BE32( p, n ); } +inline unsigned get_le( uint16_t* p ) { return GET_LE16( p ); } +inline uint32_t get_le( uint32_t* p ) { return GET_LE32( p ); } +inline unsigned get_be( uint16_t* p ) { return GET_BE16( p ); } +inline uint32_t get_be( uint32_t* p ) { return GET_BE32( p ); } #endif diff --git a/Frameworks/GME/gme/blargg_source.h b/Frameworks/GME/gme/blargg_source.h index deea91884..8c28a0d2d 100644 --- a/Frameworks/GME/gme/blargg_source.h +++ b/Frameworks/GME/gme/blargg_source.h @@ -34,11 +34,10 @@ /* Like printf() except output goes to debug log file. Might be defined to do * nothing (not even evaluate its arguments). * void debug_printf( const char* format, ... ); */ -#if defined(__cplusplus) && defined(BLARGG_BUILD_DLL) - static inline void blargg_dprintf_( const char* fmt_str, ... ) { (void) fmt_str; } - #undef debug_printf - #define debug_printf (1) ? (void) 0 : blargg_dprintf_ -#endif +static inline void BLARGG_PRINTFN(1,2) + blargg_dprintf_( const char* fmt_str, ... ) { (void) fmt_str; } +#undef debug_printf +#define debug_printf (1) ? (void) 0 : blargg_dprintf_ /* If enabled, evaluate expr and if false, make debug log entry with source file * and line. Meant for finding situations that should be examined further, but that @@ -62,21 +61,6 @@ #define byte byte_ typedef unsigned char byte; -/* Setup compiler defines useful for exporting required public API symbols in gme.cpp */ -#ifndef BLARGG_EXPORT - #if defined (_WIN32) - #if defined(BLARGG_BUILD_DLL) - #define BLARGG_EXPORT __declspec(dllexport) - #else - #define BLARGG_EXPORT /* Leave blank: friendly with both static and shared linking */ - #endif - #elif defined (LIBGME_VISIBILITY) && defined(__cplusplus) - #define BLARGG_EXPORT __attribute__((visibility ("default"))) - #else - #define BLARGG_EXPORT - #endif -#endif - /* deprecated */ #define BLARGG_CHECK_ALLOC CHECK_ALLOC #define BLARGG_RETURN_ERR RETURN_ERR diff --git a/Frameworks/GME/ext/2413tone.h b/Frameworks/GME/gme/ext/2413tone.h similarity index 95% rename from Frameworks/GME/ext/2413tone.h rename to Frameworks/GME/gme/ext/2413tone.h index fb42e3f10..c041dc9b7 100644 --- a/Frameworks/GME/ext/2413tone.h +++ b/Frameworks/GME/gme/ext/2413tone.h @@ -17,4 +17,4 @@ 0x21,0x01,0x89,0x03,0xf1,0xe4,0xf0,0x23, 0x07,0x21,0x14,0x00,0xee,0xf8,0xff,0xf8, 0x01,0x31,0x00,0x00,0xf8,0xf7,0xf8,0xf7, -0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, \ No newline at end of file +0x25,0x11,0x00,0x00,0xf8,0xfa,0xf8,0x55, diff --git a/Frameworks/GME/gme/ext/LICENSE b/Frameworks/GME/gme/ext/LICENSE new file mode 100644 index 000000000..df4ae1cbb --- /dev/null +++ b/Frameworks/GME/gme/ext/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2001-2019 Mitsutaka Okazaki + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/Frameworks/GME/gme/ext/README.md b/Frameworks/GME/gme/ext/README.md new file mode 100644 index 000000000..ea03e06f2 --- /dev/null +++ b/Frameworks/GME/gme/ext/README.md @@ -0,0 +1,8 @@ +# emu2413 + +A YM2413 emulator written in C by Mitsutaka Okazaki. + +This is the version 0.61, tweaked by Chrosipher Snowhill (@kode54). + +If you looking latest version of this module, please visit this repository: +https://github.com/digital-sound-antiques/emu2413 diff --git a/Frameworks/GME/ext/emu2413.c b/Frameworks/GME/gme/ext/emu2413.c similarity index 99% rename from Frameworks/GME/ext/emu2413.c rename to Frameworks/GME/gme/ext/emu2413.c index e41753fd9..402cb2b65 100644 --- a/Frameworks/GME/ext/emu2413.c +++ b/Frameworks/GME/gme/ext/emu2413.c @@ -55,8 +55,6 @@ #include #include #include -#include "mamedef.h" -#undef INLINE #include "emu2413.h" #include "panning.h" // Maxim @@ -169,7 +167,7 @@ static unsigned char default_inst[OPLL_TONE_NUM][(16 + 3) * 8] = { #define EXPAND_BITS_X(x,s,d) (((x)<<((d)-(s)))|((1<<((d)-(s)))-1)) /* Adjust envelope speed which depends on sampling rate. */ -#define RATE_ADJUST(x) (rate==49716?x:(e_uint32)((double)(x)*clk/72/rate + 0.5)) /* added 0.5 to round the value*/ +#define RATE_ADJUST(x) (rate==49716?(x):(e_uint32)((double)(x)*clk/72/rate + 0.5)) /* added 0.5 to round the value*/ #define MOD(o,x) (&(o)->slot[(x)<<1]) #define CAR(o,x) (&(o)->slot[((x)<<1)|1]) @@ -424,7 +422,8 @@ static double decaytime[16][4] = { static void makeDphaseARTable (void) { - e_int32 AR, Rks, RM, RL; + e_int32 AR, Rks, RM; + e_uint32 RL; #ifdef USE_SPEC_ENV_SPEED e_uint32 attacktable[16][4]; @@ -472,7 +471,8 @@ makeDphaseARTable (void) static void makeDphaseDRTable (void) { - e_int32 DR, Rks, RM, RL; + e_int32 DR, Rks, RM; + e_uint32 RL; #ifdef USE_SPEC_ENV_SPEED e_uint32 decaytable[16][4]; @@ -1000,7 +1000,7 @@ OPLL_new (e_uint32 clk, e_uint32 rate) maketables (clk, rate); - opll = (OPLL *) calloc (sizeof (OPLL), 1); + opll = (OPLL *) calloc (1, sizeof (OPLL)); if (opll == NULL) return NULL; diff --git a/Frameworks/GME/ext/emu2413.h b/Frameworks/GME/gme/ext/emu2413.h similarity index 100% rename from Frameworks/GME/ext/emu2413.h rename to Frameworks/GME/gme/ext/emu2413.h diff --git a/Frameworks/GME/ext/emu2413_NESpatches.txt b/Frameworks/GME/gme/ext/emu2413_NESpatches.txt similarity index 100% rename from Frameworks/GME/ext/emu2413_NESpatches.txt rename to Frameworks/GME/gme/ext/emu2413_NESpatches.txt diff --git a/Frameworks/GME/ext/emutypes.h b/Frameworks/GME/gme/ext/emutypes.h similarity index 100% rename from Frameworks/GME/ext/emutypes.h rename to Frameworks/GME/gme/ext/emutypes.h diff --git a/Frameworks/GME/gme/ext/mamedef.h b/Frameworks/GME/gme/ext/mamedef.h new file mode 100644 index 000000000..e69de29bb diff --git a/Frameworks/GME/ext/panning.c b/Frameworks/GME/gme/ext/panning.c similarity index 99% rename from Frameworks/GME/ext/panning.c rename to Frameworks/GME/gme/ext/panning.c index f438cbeef..ee8069758 100644 --- a/Frameworks/GME/ext/panning.c +++ b/Frameworks/GME/gme/ext/panning.c @@ -21,7 +21,7 @@ void calc_panning(float channels[2], int position) else if ( position < -RANGE / 2 ) position = -RANGE / 2; position += RANGE / 2; // make -256..0..256 -> 0..256..512 - + // Equal power law: equation is // right = sin( position / range * pi / 2) * sqrt( 2 ) // left is equivalent to right with position = range - position diff --git a/Frameworks/GME/ext/panning.h b/Frameworks/GME/gme/ext/panning.h similarity index 97% rename from Frameworks/GME/ext/panning.h rename to Frameworks/GME/gme/ext/panning.h index b6bab25ba..59a33d379 100644 --- a/Frameworks/GME/ext/panning.h +++ b/Frameworks/GME/gme/ext/panning.h @@ -10,4 +10,4 @@ void calc_panning(float channels[2], int position); void centre_panning(float channels[2]); -#endif \ No newline at end of file +#endif diff --git a/Frameworks/GME/ext/vrc7tone.h b/Frameworks/GME/gme/ext/vrc7tone.h similarity index 100% rename from Frameworks/GME/ext/vrc7tone.h rename to Frameworks/GME/gme/ext/vrc7tone.h diff --git a/Frameworks/GME/gme/gme.cpp b/Frameworks/GME/gme/gme.cpp index 9ba7c1e3b..948f9d698 100644 --- a/Frameworks/GME/gme/gme.cpp +++ b/Frameworks/GME/gme/gme.cpp @@ -2,7 +2,11 @@ #include "Music_Emu.h" +#ifdef GEN_TYPES_H +#include "gen_types.h" /* same as gme_types.h but generated by build system */ +#else #include "gme_types.h" +#endif #if !GME_DISABLE_STEREO_DEPTH #include "Effects_Buffer.h" #endif @@ -55,7 +59,6 @@ gme_type_t const* gme_type_list() #endif #ifdef USE_GME_SPC gme_spc_type, - gme_rsn_type, gme_sfm_type, #endif #ifdef USE_GME_VGM @@ -83,7 +86,6 @@ const char* gme_identify_header( void const* header ) case BLARGG_4CHAR('N','S','F','E'): return "NSFE"; case BLARGG_4CHAR('S','A','P',0x0D): return "SAP"; case BLARGG_4CHAR('S','N','E','S'): return "SPC"; - case BLARGG_4CHAR('R','a','r','!'): return "RSN"; case BLARGG_4CHAR('V','g','m',' '): return "VGM"; } if (get_be16(header) == BLARGG_2CHAR(0x1F, 0x8B)) @@ -180,9 +182,9 @@ gme_err_t gme_open_file( const char* path, Music_Emu** out, int sample_rate ) header_size = sizeof header; RETURN_ERR( in.read( header, sizeof header ) ); file_type = gme_identify_extension( gme_identify_header( header ) ); + if ( !file_type ) + return gme_wrong_file_type; } - if ( !file_type ) - return gme_wrong_file_type; Music_Emu* emu = gme_new_emu( file_type, sample_rate ); CHECK_ALLOC( emu ); @@ -192,9 +194,6 @@ gme_err_t gme_open_file( const char* path, Music_Emu** out, int sample_rate ) gme_err_t err = emu->load( rem ); in.close(); - if ( emu->is_archive ) - err = emu->load_archive( path ); - if ( err ) delete emu; else @@ -208,7 +207,7 @@ void gme_set_autoload_playback_limit( Music_Emu *emu, int do_autoload_limit ) emu->set_autoload_playback_limit( do_autoload_limit != 0 ); } -int gme_autoload_playback_limit( Music_Emu *const emu ) +int gme_autoload_playback_limit( Music_Emu const* emu ) { return emu->autoload_playback_limit(); } @@ -275,6 +274,17 @@ gme_err_t gme_load_data( Music_Emu* me, void const* data, long size ) return me->load( in ); } +gme_err_t gme_load_tracks( Music_Emu* me, void const* data, long* sizes, int count ) +{ + return me->load_tracks( data, sizes, count ); +} + +int gme_fixed_track_count( gme_type_t t ) +{ + assert( t ); + return t->track_count; +} + gme_err_t gme_load_custom( Music_Emu* me, gme_reader_t func, long size, void* data ) { Callback_Reader in( func, size, data ); @@ -381,17 +391,21 @@ void gme_set_user_cleanup(Music_Emu* me, gme_user_cleanup_t func ) { me->se gme_err_t gme_start_track ( Music_Emu* me, int index ) { return me->start_track( index ); } gme_err_t gme_play ( Music_Emu* me, int n, short* p ) { return me->play( n, p ); } -void gme_set_fade ( Music_Emu* me, int start_msec, int fade_msec ) { me->set_fade( start_msec, fade_msec ); } +void gme_set_fade ( Music_Emu* me, int start_msec ) { me->set_fade( start_msec ); } +void gme_set_fade_msecs ( Music_Emu* me, int start_msec, int fade_msec ) { me->set_fade( start_msec, fade_msec ); } int gme_track_ended ( Music_Emu const* me ) { return me->track_ended(); } int gme_tell ( Music_Emu const* me ) { return me->tell(); } int gme_tell_samples ( Music_Emu const* me ) { return me->tell_samples(); } +int gme_tell_scaled ( Music_Emu const* me ) { return me->tell_scaled(); } gme_err_t gme_seek ( Music_Emu* me, int msec ) { return me->seek( msec ); } gme_err_t gme_seek_samples ( Music_Emu* me, int n ) { return me->seek_samples( n ); } +gme_err_t gme_seek_scaled ( Music_Emu* me, int msec ) { return me->seek_scaled( msec ); } int gme_voice_count ( Music_Emu const* me ) { return me->voice_count(); } void gme_ignore_silence ( Music_Emu* me, int disable ) { me->ignore_silence( disable != 0 ); } void gme_set_tempo ( Music_Emu* me, double t ) { me->set_tempo( t ); } void gme_mute_voice ( Music_Emu* me, int index, int mute ) { me->mute_voice( index, mute != 0 ); } void gme_mute_voices ( Music_Emu* me, int mask ) { me->mute_voices( mask ); } +void gme_disable_echo ( Music_Emu* me, int disable ) { me->disable_echo( disable ); } void gme_enable_accuracy( Music_Emu* me, int enabled ) { me->enable_accuracy( enabled ); } void gme_clear_playlist ( Music_Emu* me ) { me->clear_playlist(); } int gme_type_multitrack( gme_type_t t ) { return t->track_count != 1; } diff --git a/Frameworks/GME/gme/gme.exports b/Frameworks/GME/gme/gme.exports new file mode 100644 index 000000000..2da5a1b18 --- /dev/null +++ b/Frameworks/GME/gme/gme.exports @@ -0,0 +1,68 @@ +# List of all exported symbols +gme_autoload_playback_limit +gme_clear_playlist +gme_delete +gme_enable_accuracy +gme_equalizer +gme_free_info +gme_identify_extension +gme_identify_file +gme_identify_header +gme_ignore_silence +gme_load_custom +gme_load_data +gme_load_file +gme_load_m3u +gme_load_m3u_data +gme_multi_channel +gme_mute_voice +gme_mute_voices +gme_new_emu +gme_new_emu_multi_channel +gme_open_data +gme_open_file +gme_play +gme_seek +gme_seek_samples +gme_set_autoload_playback_limit +gme_set_equalizer +gme_set_fade +gme_set_stereo_depth +gme_set_tempo +gme_set_user_cleanup +gme_set_user_data +gme_start_track +gme_tell +gme_tell_samples +gme_track_count +gme_track_ended +gme_track_info +gme_type +gme_type_extension +gme_type_list +gme_type_multitrack +gme_type_system +gme_user_data +gme_voice_count +gme_voice_name +gme_warning +gme_wrong_file_type +gme_ay_type +gme_gbs_type +gme_gym_type +gme_hes_type +gme_kss_type +gme_nsf_type +gme_nsfe_type +gme_sap_type +gme_spc_type +gme_vgm_type +gme_vgz_type +gme_disable_echo +gme_set_fade_msecs +gme_load_tracks +gme_fixed_track_count + +# Since 0.6.5 +gme_seek_scaled +gme_tell_scaled diff --git a/Frameworks/GME/gme/gme.h b/Frameworks/GME/gme/gme.h index 80bc82cca..e982cadd9 100644 --- a/Frameworks/GME/gme/gme.h +++ b/Frameworks/GME/gme/gme.h @@ -1,16 +1,14 @@ /* Game music emulator library C interface (also usable from C++) */ -/* Game_Music_Emu 0.7.0 */ +/* Game_Music_Emu 0.6.4 */ #ifndef GME_H #define GME_H -#include "blargg_source.h" - #ifdef __cplusplus extern "C" { #endif -#define GME_VERSION 0x000700 /* 1 byte major, 1 byte minor, 1 byte patch-level */ +#define GME_VERSION 0x000604 /* 1 byte major, 1 byte minor, 1 byte patch-level */ /* Error string returned by library functions, or NULL if no error (success) */ typedef const char* gme_err_t; @@ -19,6 +17,22 @@ typedef const char* gme_err_t; typedef struct Music_Emu Music_Emu; +/* Setup compiler defines useful for exporting required public API symbols in gme.cpp */ +#ifndef BLARGG_EXPORT + #if defined (_WIN32) + #if defined(BLARGG_BUILD_DLL) + #define BLARGG_EXPORT __declspec(dllexport) + #else + #define BLARGG_EXPORT /* Leave blank: friendly with both static and shared linking. */ + #endif + #elif defined (LIBGME_VISIBILITY) + #define BLARGG_EXPORT __attribute__((visibility ("default"))) + #else + #define BLARGG_EXPORT + #endif +#endif + + /******** Basic operations ********/ /* Create emulator and load game music file/data into it. Sets *out to new emulator. */ @@ -41,7 +55,12 @@ BLARGG_EXPORT void gme_delete( Music_Emu* ); /* Set time to start fading track out. Once fade ends track_ended() returns true. Fade time can be changed while track is playing. */ -BLARGG_EXPORT void gme_set_fade( Music_Emu*, int start_msec, int length_msec ); +BLARGG_EXPORT void gme_set_fade( Music_Emu*, int start_msec ); + +/** See gme_set_fade. + * @since 0.6.4 + */ +BLARGG_EXPORT void gme_set_fade_msecs( Music_Emu*, int start_msec, int length_msecs ); /** * If do_autoload_limit is nonzero, then automatically load track length @@ -53,12 +72,13 @@ BLARGG_EXPORT void gme_set_fade( Music_Emu*, int start_msec, int length_msec ); * * By default, playback limits are loaded and applied. * - * @since 0.6.2 + * @since 0.6.3 */ BLARGG_EXPORT void gme_set_autoload_playback_limit( Music_Emu *, int do_autoload_limit ); /** See gme_set_autoload_playback_limit. - * @since 0.6.2 + * (This was actually added in 0.6.3, but wasn't exported because of a typo.) + * @since 0.6.4 */ BLARGG_EXPORT int gme_autoload_playback_limit( Music_Emu const* ); @@ -71,12 +91,20 @@ BLARGG_EXPORT int gme_tell( Music_Emu const* ); /* Number of samples generated since beginning of track */ BLARGG_EXPORT int gme_tell_samples( Music_Emu const* ); +/* Number of milliseconds played since beginning of track (scaled with tempo). + * @since 0.6.5 */ +BLARGG_EXPORT int gme_tell_scaled( Music_Emu const* ); + /* Seek to new time in track. Seeking backwards or far forward can take a while. */ BLARGG_EXPORT gme_err_t gme_seek( Music_Emu*, int msec ); /* Equivalent to restarting track then skipping n samples */ BLARGG_EXPORT gme_err_t gme_seek_samples( Music_Emu*, int n ); +/* Seek to new time in track (scaled with tempo). + * @since 0.6.5 */ +BLARGG_EXPORT gme_err_t gme_seek_scaled( Music_Emu*, int msec ); + /******** Informational ********/ @@ -103,22 +131,22 @@ BLARGG_EXPORT gme_err_t gme_track_info( Music_Emu const*, gme_info_t** out, int /* Frees track information */ BLARGG_EXPORT void gme_free_info( gme_info_t* ); -struct BLARGG_EXPORT gme_info_t +struct gme_info_t { /* times in milliseconds; -1 if unknown */ int length; /* total length, if file specifies it */ int intro_length; /* length of song up to looping section */ int loop_length; /* length of looping section */ - + /* Length if available, otherwise intro_length+loop_length*2 if available, otherwise a default of 150000 (2.5 minutes). */ int play_length; /* fade length in milliseconds; -1 if unknown */ int fade_length; - + int i5,i6,i7,i8,i9,i10,i11,i12,i13,i14,i15; /* reserved */ - + /* empty string ("") if not available */ const char* system; const char* game; @@ -127,7 +155,7 @@ struct BLARGG_EXPORT gme_info_t const char* copyright; const char* comment; const char* dumper; - + const char *s7,*s8,*s9,*s10,*s11,*s12,*s13,*s14,*s15; /* reserved */ }; @@ -159,13 +187,17 @@ BLARGG_EXPORT void gme_mute_voice( Music_Emu*, int index, int mute ); voices, 0 unmutes them all, 0x01 mutes just the first voice, etc. */ BLARGG_EXPORT void gme_mute_voices( Music_Emu*, int muting_mask ); +/* Disable/Enable echo effect for SPC files */ +/* Available since 0.6.4 */ +BLARGG_EXPORT void gme_disable_echo( Music_Emu*, int disable ); + /* Frequency equalizer parameters (see gme.txt) */ /* Implementers: If modified, also adjust Music_Emu::make_equalizer as needed */ -typedef struct BLARGG_EXPORT gme_equalizer_t +typedef struct gme_equalizer_t { double treble; /* -50.0 = muffled, 0 = flat, +5.0 = extra-crisp */ double bass; /* 1 = full bass, 90 = average, 16000 = almost no bass */ - + double d2,d3,d4,d5,d6,d7,d8,d9; /* reserved */ } gme_equalizer_t; @@ -195,7 +227,6 @@ extern BLARGG_EXPORT const gme_type_t gme_nsfe_type, gme_sap_type, gme_spc_type, - gme_rsn_type, gme_sfm_type, gme_vgm_type, gme_vgz_type; @@ -215,7 +246,7 @@ BLARGG_EXPORT int gme_type_multitrack( gme_type_t ); /* whether the pcm output retrieved by gme_play() will have all 8 voices rendered to their * individual stereo channel or (if false) these voices get mixed into one single stereo channel - * @since 0.6.2 */ + * @since 0.6.3 */ BLARGG_EXPORT int gme_multi_channel( Music_Emu const* ); /******** Advanced file loading ********/ @@ -239,7 +270,7 @@ BLARGG_EXPORT gme_type_t gme_identify_extension( const char path_or_extension [] * Get typical file extension for a given music type. This is not a replacement * for a file content identification library (but see gme_identify_header). * - * @since 0.6.2 + * @since 0.6.3 */ BLARGG_EXPORT const char* gme_type_extension( gme_type_t music_type ); @@ -254,7 +285,7 @@ BLARGG_EXPORT Music_Emu* gme_new_emu( gme_type_t, int sample_rate ); /* Create new multichannel emulator and set sample rate. Returns NULL if out of memory. * If you only need track information, pass gme_info_only for sample_rate. * (see gme_multi_channel for more information on multichannel support) - * @since 0.6.2 + * @since 0.6.3 */ BLARGG_EXPORT Music_Emu* gme_new_emu_multi_channel( gme_type_t, int sample_rate ); @@ -264,6 +295,17 @@ BLARGG_EXPORT gme_err_t gme_load_file( Music_Emu*, const char path [] ); /* Load music file from memory into emulator. Makes a copy of data passed. */ BLARGG_EXPORT gme_err_t gme_load_data( Music_Emu*, void const* data, long size ); +/* Load multiple single-track music files from memory into emulator. + * @since 0.6.4 + */ +BLARGG_EXPORT gme_err_t gme_load_tracks( Music_Emu* me, + void const* data, long* sizes, int count ); + +/* Return the fixed track count of an emu file type + * @since 0.6.4 + */ +BLARGG_EXPORT int gme_fixed_track_count( gme_type_t ); + /* Load music file using custom data reader function that will be called to read file data. Most emulators load the entire file in one read call. */ typedef gme_err_t (*gme_reader_t)( void* your_data, void* out, int count ); diff --git a/Frameworks/GME/gme/hes_cpu_io.h b/Frameworks/GME/gme/hes_cpu_io.h index ce60ce8ef..db01f3d89 100644 --- a/Frameworks/GME/gme/hes_cpu_io.h +++ b/Frameworks/GME/gme/hes_cpu_io.h @@ -27,27 +27,27 @@ inline byte const* Hes_Emu::cpu_set_mmr( int page, int bank ) { write_pages [page] = 0; if ( bank < 0x80 ) - return rom.at_addr( bank * (blargg_long) page_size ); - + return rom.at_addr( bank * (int32_t) page_size ); + byte* data = 0; switch ( bank ) { case 0xF8: data = cpu::ram; break; - + case 0xF9: case 0xFA: case 0xFB: data = &sgx [(bank - 0xF9) * page_size]; break; - + default: if ( bank != 0xFF ) debug_printf( "Unmapped bank $%02X\n", bank ); return rom.unmapped(); } - + write_pages [page] = data; return data; } diff --git a/Frameworks/GME/gme/libgme.pc.in b/Frameworks/GME/gme/libgme.pc.in index 09a78b213..0f379d7f6 100644 --- a/Frameworks/GME/gme/libgme.pc.in +++ b/Frameworks/GME/gme/libgme.pc.in @@ -13,4 +13,4 @@ URL: https://bitbucket.org/mpyne/game-music-emu/wiki/Home Version: @GME_VERSION@ Cflags: -I${includedir} Libs: -L${libdir} -lgme -Libs.private: -lstdc++ @PKG_CONFIG_ZLIB@ @PKG_CONFIG_UNRAR@ +Libs.private: @PC_LIBS@ diff --git a/Frameworks/GME/gme/nes_cpu_io.h b/Frameworks/GME/gme/nes_cpu_io.h index 3d1b8e2e8..2b4e95b40 100644 --- a/Frameworks/GME/gme/nes_cpu_io.h +++ b/Frameworks/GME/gme/nes_cpu_io.h @@ -9,6 +9,8 @@ #include "blargg_source.h" +#include + int Nsf_Emu::cpu_read( nes_addr_t addr ) { int result, i; @@ -16,18 +18,18 @@ int Nsf_Emu::cpu_read( nes_addr_t addr ) result = cpu::low_mem [addr & 0x7FF]; if ( !(addr & 0xE000) ) goto exit; - + result = *cpu::get_code( addr ); if ( addr > 0x7FFF ) goto exit; - + result = sram [addr & (sizeof sram - 1)]; if ( addr > 0x5FFF ) goto exit; - + if ( addr == Nes_Apu::status_addr ) return apu.read_status( cpu::time() ); - + #if !NSF_EMU_APU_ONLY if ( addr == Nes_Namco_Apu::data_reg_addr && namco ) return namco->read_data(); @@ -43,12 +45,12 @@ int Nsf_Emu::cpu_read( nes_addr_t addr ) if ( (unsigned) i < 2 && mmc5 ) return ((mmc5_mul [0] * mmc5_mul [1]) >> (i * 8)) & 0xFF; #endif - + result = addr >> 8; // simulate open bus - + if ( addr != 0x2002 ) debug_printf( "Read unmapped $%.4X\n", (unsigned) addr ); - + exit: return result; } @@ -71,24 +73,40 @@ void Nsf_Emu::cpu_write( nes_addr_t addr, int data ) return; } } - + if ( unsigned (addr - Nes_Apu::start_addr) <= Nes_Apu::end_addr - Nes_Apu::start_addr ) { GME_APU_HOOK( this, addr - Nes_Apu::start_addr, data ); apu.write_register( cpu::time(), addr, data ); return; } - + + if ( fds && ( addr >= 0x5FF6 && addr <= 0x5FFD ) ) + { + int32_t offset = rom.mask_addr( data * (int32_t) bank_size ); + if ( offset >= rom.size() ) + set_warning( "Invalid bank" ); + unsigned bank = addr - 0x5FF6; + byte* out = sram; + if ( bank >= 2 ) + { + out = fds->sram; + bank -= 2; + } + memcpy( &out [bank * bank_size], rom.at_addr( offset ), bank_size ); + return; + } + unsigned bank = addr - bank_select_addr; if ( bank < bank_count ) { - blargg_long offset = rom.mask_addr( data * (blargg_long) bank_size ); + int32_t offset = rom.mask_addr( data * (int32_t) bank_size ); if ( offset >= rom.size() ) set_warning( "Invalid bank" ); cpu::map_code( (bank + 8) * bank_size, bank_size, rom.at_addr( offset ) ); return; } - + cpu_write_misc( addr, data ); } diff --git a/Frameworks/GME/gme/sap_cpu_io.h b/Frameworks/GME/gme/sap_cpu_io.h index d009d0d9b..3103ecdb6 100644 --- a/Frameworks/GME/gme/sap_cpu_io.h +++ b/Frameworks/GME/gme/sap_cpu_io.h @@ -16,7 +16,7 @@ void Sap_Emu::cpu_write( sap_addr_t addr, int data ) #define CPU_READ( cpu, addr, time ) READ_LOW( addr ) #else #define CPU_READ( cpu, addr, time ) STATIC_CAST(Sap_Emu&,*cpu).cpu_read( addr ) - + int Sap_Emu::cpu_read( sap_addr_t addr ) { if ( (addr & 0xF900) == 0xD000 ) diff --git a/Frameworks/GME/license.txt b/Frameworks/GME/license.txt index 5faba9d48..53afb04e5 100644 --- a/Frameworks/GME/license.txt +++ b/Frameworks/GME/license.txt @@ -146,7 +146,7 @@ such a program is covered only if its contents constitute a work based on the Library (independent of the use of the Library in a tool for writing it). Whether that is true depends on what the Library does and what the program that uses the Library does. - + 1. You may copy and distribute verbatim copies of the Library's complete source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an diff --git a/Plugins/GME/GameDecoder.m b/Plugins/GME/GameDecoder.m index 6c47b2afe..b10328f27 100644 --- a/Plugins/GME/GameDecoder.m +++ b/Plugins/GME/GameDecoder.m @@ -188,9 +188,9 @@ gme_err_t readCallback(void *data, void *out, int count) { } if(IsRepeatOneSet()) - gme_set_fade(emu, -1, 0); + gme_set_fade_msecs(emu, -1, 0); else - gme_set_fade(emu, (int)(length - fade), (int)fade); + gme_set_fade_msecs(emu, (int)(length - fade), (int)fade); double streamTimestamp = (double)(gme_tell(emu)) * 0.001;