diff --git a/Cog.xcodeproj/project.pbxproj b/Cog.xcodeproj/project.pbxproj index 4145f4a8b..ff30b8d30 100644 --- a/Cog.xcodeproj/project.pbxproj +++ b/Cog.xcodeproj/project.pbxproj @@ -2505,6 +2505,7 @@ C01FCF4B08A954540054247B /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + CODE_SIGN_IDENTITY = ""; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; FRAMEWORK_SEARCH_PATHS = ( diff --git a/Frameworks/GME/GME.xcodeproj/project.pbxproj b/Frameworks/GME/GME.xcodeproj/project.pbxproj index b4f247bfc..c2e29024f 100644 --- a/Frameworks/GME/GME.xcodeproj/project.pbxproj +++ b/Frameworks/GME/GME.xcodeproj/project.pbxproj @@ -82,12 +82,6 @@ 17C8F2450CBED286008D969D /* Sap_Emu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DC0CBED286008D969D /* Sap_Emu.h */; }; 17C8F2460CBED286008D969D /* Sms_Apu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */; }; 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1DE0CBED286008D969D /* Sms_Apu.h */; }; - 17C8F2490CBED286008D969D /* Snes_Spc.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */; }; - 17C8F24A0CBED286008D969D /* Snes_Spc.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E10CBED286008D969D /* Snes_Spc.h */; }; - 17C8F24B0CBED286008D969D /* Spc_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */; }; - 17C8F24C0CBED286008D969D /* Spc_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E30CBED286008D969D /* Spc_Cpu.h */; }; - 17C8F24D0CBED286008D969D /* Spc_Dsp.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1E40CBED286008D969D /* Spc_Dsp.cpp */; }; - 17C8F24E0CBED286008D969D /* Spc_Dsp.h in Headers */ = {isa = PBXBuildFile; fileRef = 17C8F1E50CBED286008D969D /* Spc_Dsp.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 */; }; 17C8F2530CBED286008D969D /* Vgm_Emu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */; }; @@ -255,6 +249,15 @@ 8370B7CF17F615FE001A4D7A /* Z80_Cpu_run.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */; }; 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */; }; 8370B7D117F615FE001A4D7A /* Z80_Cpu.h in Headers */ = {isa = PBXBuildFile; fileRef = 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */; }; + 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 */; }; + 83FC5D79181B47FB00B917E5 /* smp.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5D58181B47FB00B917E5 /* smp.hpp */; }; + 83FC5D99181B675900B917E5 /* SPC_DSP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5D97181B675900B917E5 /* SPC_DSP.cpp */; }; + 83FC5D9A181B675900B917E5 /* SPC_DSP.h in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5D98181B675900B917E5 /* SPC_DSP.h */; }; + 83FC5DAB181B8B1900B917E5 /* registers.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5DA3181B8B1900B917E5 /* registers.hpp */; }; + 83FC5DAD181B8B1900B917E5 /* spc700.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 83FC5DA5181B8B1900B917E5 /* spc700.cpp */; }; + 83FC5DAE181B8B1900B917E5 /* spc700.hpp in Headers */ = {isa = PBXBuildFile; fileRef = 83FC5DA6181B8B1900B917E5 /* spc700.hpp */; }; 8DC2EF530486A6940098B216 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C1666FE841158C02AAC07 /* InfoPlist.strings */; }; 8DC2EF570486A6940098B216 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7B1FEA5585E11CA2CBB /* Cocoa.framework */; }; /* End PBXBuildFile section */ @@ -339,12 +342,6 @@ 17C8F1DC0CBED286008D969D /* Sap_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sap_Emu.h; path = gme/Sap_Emu.h; sourceTree = ""; }; 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sms_Apu.cpp; path = gme/Sms_Apu.cpp; sourceTree = ""; }; 17C8F1DE0CBED286008D969D /* Sms_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Apu.h; path = gme/Sms_Apu.h; sourceTree = ""; }; - 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Snes_Spc.cpp; path = gme/Snes_Spc.cpp; sourceTree = ""; }; - 17C8F1E10CBED286008D969D /* Snes_Spc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Snes_Spc.h; path = gme/Snes_Spc.h; sourceTree = ""; }; - 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Cpu.cpp; path = gme/Spc_Cpu.cpp; sourceTree = ""; }; - 17C8F1E30CBED286008D969D /* Spc_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Cpu.h; path = gme/Spc_Cpu.h; sourceTree = ""; }; - 17C8F1E40CBED286008D969D /* Spc_Dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Dsp.cpp; path = gme/Spc_Dsp.cpp; sourceTree = ""; }; - 17C8F1E50CBED286008D969D /* Spc_Dsp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Dsp.h; path = gme/Spc_Dsp.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 = ""; }; 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Emu.cpp; path = gme/Vgm_Emu.cpp; sourceTree = ""; }; @@ -514,6 +511,20 @@ 8370B72C17F615FE001A4D7A /* Z80_Cpu_run.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu_run.h; path = gme/Z80_Cpu_run.h; sourceTree = ""; }; 8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80_Cpu.cpp; path = gme/Z80_Cpu.cpp; sourceTree = ""; }; 8370B72E17F615FE001A4D7A /* Z80_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu.h; path = gme/Z80_Cpu.h; 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 = ""; }; + 83FC5D57181B47FB00B917E5 /* smp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = smp.cpp; sourceTree = ""; }; + 83FC5D58181B47FB00B917E5 /* smp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = smp.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 83FC5D95181B638C00B917E5 /* memory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = memory.cpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 83FC5D96181B638C00B917E5 /* timing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = ""; }; + 83FC5D97181B675900B917E5 /* SPC_DSP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SPC_DSP.cpp; sourceTree = ""; }; + 83FC5D98181B675900B917E5 /* SPC_DSP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPC_DSP.h; sourceTree = ""; }; + 83FC5DA3181B8B1900B917E5 /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = registers.hpp; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.cpp; }; + 83FC5DA5181B8B1900B917E5 /* spc700.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spc700.cpp; sourceTree = ""; }; + 83FC5DA6181B8B1900B917E5 /* spc700.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spc700.hpp; sourceTree = ""; }; + 83FC5DB1181B93DD00B917E5 /* algorithms.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = algorithms.cpp; sourceTree = ""; }; + 83FC5DB3181B93DD00B917E5 /* instructions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = instructions.cpp; sourceTree = ""; }; + 83FC5DB4181B93DD00B917E5 /* memory.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = ""; }; 8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; 8DC2EF5B0486A6940098B216 /* GME.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = GME.framework; sourceTree = BUILT_PRODUCTS_DIR; }; D2F7E79907B2D74100F64583 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; }; @@ -598,6 +609,7 @@ 17C8F1860CBED26C008D969D /* Source */ = { isa = PBXGroup; children = ( + 83FC5D35181B47FB00B917E5 /* higan */, 8370B68C17F615FD001A4D7A /* adlib.h */, 8370B68D17F615FD001A4D7A /* Ay_Core.cpp */, 8370B68E17F615FD001A4D7A /* Ay_Core.h */, @@ -833,12 +845,6 @@ 17C8F1DC0CBED286008D969D /* Sap_Emu.h */, 17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */, 17C8F1DE0CBED286008D969D /* Sms_Apu.h */, - 17C8F1E00CBED286008D969D /* Snes_Spc.cpp */, - 17C8F1E10CBED286008D969D /* Snes_Spc.h */, - 17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */, - 17C8F1E30CBED286008D969D /* Spc_Cpu.h */, - 17C8F1E40CBED286008D969D /* Spc_Dsp.cpp */, - 17C8F1E50CBED286008D969D /* Spc_Dsp.h */, 17C8F1E60CBED286008D969D /* Spc_Emu.cpp */, 17C8F1E70CBED286008D969D /* Spc_Emu.h */, 17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */, @@ -851,6 +857,60 @@ name = Source; sourceTree = ""; }; + 83FC5D35181B47FB00B917E5 /* higan */ = { + isa = PBXGroup; + children = ( + 83FC5D9D181B8B1900B917E5 /* processor */, + 83FC5D36181B47FB00B917E5 /* dsp */, + 83FC5D40181B47FB00B917E5 /* smp */, + ); + name = higan; + path = gme/higan; + sourceTree = ""; + }; + 83FC5D36181B47FB00B917E5 /* dsp */ = { + isa = PBXGroup; + children = ( + 83FC5D97181B675900B917E5 /* SPC_DSP.cpp */, + 83FC5D98181B675900B917E5 /* SPC_DSP.h */, + 83FC5D3B181B47FB00B917E5 /* dsp.cpp */, + 83FC5D3C181B47FB00B917E5 /* dsp.hpp */, + ); + path = dsp; + sourceTree = ""; + }; + 83FC5D40181B47FB00B917E5 /* smp */ = { + isa = PBXGroup; + children = ( + 83FC5D95181B638C00B917E5 /* memory.cpp */, + 83FC5D96181B638C00B917E5 /* timing.cpp */, + 83FC5D57181B47FB00B917E5 /* smp.cpp */, + 83FC5D58181B47FB00B917E5 /* smp.hpp */, + ); + path = smp; + sourceTree = ""; + }; + 83FC5D9D181B8B1900B917E5 /* processor */ = { + isa = PBXGroup; + children = ( + 83FC5D9E181B8B1900B917E5 /* spc700 */, + ); + path = processor; + sourceTree = ""; + }; + 83FC5D9E181B8B1900B917E5 /* spc700 */ = { + isa = PBXGroup; + children = ( + 83FC5DB1181B93DD00B917E5 /* algorithms.cpp */, + 83FC5DB3181B93DD00B917E5 /* instructions.cpp */, + 83FC5DB4181B93DD00B917E5 /* memory.hpp */, + 83FC5DA3181B8B1900B917E5 /* registers.hpp */, + 83FC5DA5181B8B1900B917E5 /* spc700.cpp */, + 83FC5DA6181B8B1900B917E5 /* spc700.hpp */, + ); + path = spc700; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXHeadersBuildPhase section */ @@ -876,8 +936,10 @@ 17C8F1FB0CBED286008D969D /* blargg_config.h in Headers */, 8370B78F17F615FE001A4D7A /* rf5c68.h in Headers */, 17C8F1FC0CBED286008D969D /* blargg_endian.h in Headers */, + 83FC5D5F181B47FB00B917E5 /* dsp.hpp in Headers */, 17C8F1FD0CBED286008D969D /* blargg_source.h in Headers */, 8370B75617F615FE001A4D7A /* Hes_Core.h in Headers */, + 83FC5D79181B47FB00B917E5 /* smp.hpp in Headers */, 8370B76017F615FE001A4D7A /* K053260_Emu.h in Headers */, 8370B78317F615FE001A4D7A /* Pwm_Emu.h in Headers */, 8370B73617F615FE001A4D7A /* Blip_Buffer_impl2.h in Headers */, @@ -902,8 +964,10 @@ 8370B79317F615FE001A4D7A /* Rom_Data.h in Headers */, 8370B7B817F615FE001A4D7A /* Ym2151_Emu.h in Headers */, 8370B79B17F615FE001A4D7A /* s_opltbl.h in Headers */, + 83FC5DAE181B8B1900B917E5 /* spc700.hpp in Headers */, 17C8F20B0CBED286008D969D /* Gb_Apu.h in Headers */, 8370B7A517F615FE001A4D7A /* Sgc_Core.h in Headers */, + 83FC5DAB181B8B1900B917E5 /* registers.hpp in Headers */, 8370B77217F615FE001A4D7A /* nestypes.h in Headers */, 17C8F20E0CBED286008D969D /* Gb_Cpu.h in Headers */, 17C8F2100CBED286008D969D /* Gb_Oscs.h in Headers */, @@ -912,6 +976,7 @@ 8370B7B217F615FE001A4D7A /* Track_Filter.h in Headers */, 8370B74417F615FE001A4D7A /* Downsampler.h in Headers */, 17C8F2120CBED286008D969D /* Gbs_Emu.h in Headers */, + 83FC5D9A181B675900B917E5 /* SPC_DSP.h in Headers */, 17C8F2140CBED286008D969D /* Gme_File.h in Headers */, 8370B76217F615FE001A4D7A /* k053260.h in Headers */, 8370B7C017F615FE001A4D7A /* Ym2608_Emu.h in Headers */, @@ -966,17 +1031,14 @@ 17C8F2470CBED286008D969D /* Sms_Apu.h in Headers */, 8370B79917F615FE001A4D7A /* s_opl.h in Headers */, 8370B78917F615FE001A4D7A /* Qsound_Apu.h in Headers */, - 17C8F24A0CBED286008D969D /* Snes_Spc.h in Headers */, 8370B7AE17F615FE001A4D7A /* Spc_Filter.h in Headers */, 8370B7AA17F615FE001A4D7A /* Sgc_Impl.h in Headers */, - 17C8F24C0CBED286008D969D /* Spc_Cpu.h in Headers */, 8370B7CA17F615FE001A4D7A /* Ymf262_Emu.h in Headers */, 8370B79D17F615FE001A4D7A /* Sap_Core.h in Headers */, 8370B75E17F615FE001A4D7A /* k051649.h in Headers */, 8370B77D17F615FE001A4D7A /* Okim6295_Emu.h in Headers */, 8370B7C617F615FE001A4D7A /* Ym3812_Emu.h in Headers */, 8370B77717F615FE001A4D7A /* Nsf_Impl.h in Headers */, - 17C8F24E0CBED286008D969D /* Spc_Dsp.h in Headers */, 8370B79F17F615FE001A4D7A /* scd_pcm.h in Headers */, 8370B7C217F615FE001A4D7A /* Ym2610b_Emu.h in Headers */, 8370B77917F615FE001A4D7A /* Okim6258_Emu.h in Headers */, @@ -1079,6 +1141,7 @@ 17C8F2020CBED286008D969D /* Data_Reader.cpp in Sources */, 8370B7C117F615FE001A4D7A /* Ym2610b_Emu.cpp in Sources */, 8370B75F17F615FE001A4D7A /* K053260_Emu.cpp in Sources */, + 83FC5D78181B47FB00B917E5 /* smp.cpp in Sources */, 17C8F2040CBED286008D969D /* Dual_Resampler.cpp in Sources */, 8370B73217F615FE001A4D7A /* blargg_common.cpp in Sources */, 8370B74817F615FE001A4D7A /* fm2612.c in Sources */, @@ -1110,6 +1173,7 @@ 17C8F21D0CBED286008D969D /* Hes_Cpu.cpp in Sources */, 17C8F21F0CBED286008D969D /* Hes_Emu.cpp in Sources */, 8370B78617F615FE001A4D7A /* qmix.c in Sources */, + 83FC5D5E181B47FB00B917E5 /* dsp.cpp in Sources */, 17C8F2210CBED286008D969D /* Kss_Cpu.cpp in Sources */, 8370B76117F615FE001A4D7A /* k053260.c in Sources */, 8370B79A17F615FE001A4D7A /* s_opltbl.c in Sources */, @@ -1122,6 +1186,7 @@ 8370B7C717F615FE001A4D7A /* ymdeltat.cpp in Sources */, 17C8F22C0CBED286008D969D /* Music_Emu.cpp in Sources */, 8370B7C517F615FE001A4D7A /* Ym3812_Emu.cpp in Sources */, + 83FC5D99181B675900B917E5 /* SPC_DSP.cpp in Sources */, 17C8F22E0CBED286008D969D /* Nes_Apu.cpp in Sources */, 17C8F2310CBED286008D969D /* Nes_Cpu.cpp in Sources */, 17C8F2330CBED286008D969D /* Nes_Fme7_Apu.cpp in Sources */, @@ -1136,6 +1201,7 @@ 8370B75317F615FE001A4D7A /* Hes_Apu_Adpcm.cpp in Sources */, 8370B78817F615FE001A4D7A /* Qsound_Apu.cpp in Sources */, 17C8F23B0CBED286008D969D /* Nsf_Emu.cpp in Sources */, + 83FC5DAD181B8B1900B917E5 /* spc700.cpp in Sources */, 8370B78217F615FE001A4D7A /* Pwm_Emu.cpp in Sources */, 8370B78C17F615FE001A4D7A /* Rf5C68_Emu.cpp in Sources */, 8370B73017F615FE001A4D7A /* Ay_Core.cpp in Sources */, @@ -1155,16 +1221,13 @@ 8370B75517F615FE001A4D7A /* Hes_Core.cpp in Sources */, 8370B7D017F615FE001A4D7A /* Z80_Cpu.cpp in Sources */, 17C8F2460CBED286008D969D /* Sms_Apu.cpp in Sources */, - 17C8F2490CBED286008D969D /* Snes_Spc.cpp in Sources */, 8370B7B317F615FE001A4D7A /* Upsampler.cpp in Sources */, 8370B77817F615FE001A4D7A /* Okim6258_Emu.cpp in Sources */, 8370B7AD17F615FE001A4D7A /* Spc_Filter.cpp in Sources */, - 17C8F24B0CBED286008D969D /* Spc_Cpu.cpp in Sources */, 8370B7AB17F615FE001A4D7A /* Sms_Fm_Apu.cpp in Sources */, 8370B78017F615FE001A4D7A /* Opl_Apu.cpp in Sources */, 8370B7CB17F615FE001A4D7A /* Ymz280b_Emu.cpp in Sources */, 8370B79017F615FE001A4D7A /* Rf5C164_Emu.cpp in Sources */, - 17C8F24D0CBED286008D969D /* Spc_Dsp.cpp in Sources */, 17C8F24F0CBED286008D969D /* Spc_Emu.cpp in Sources */, 17C8F2530CBED286008D969D /* Vgm_Emu.cpp in Sources */, 17C8F2550CBED286008D969D /* Ym2413_Emu.cpp in Sources */, diff --git a/Frameworks/GME/gme/Snes_Spc.cpp b/Frameworks/GME/gme/Snes_Spc.cpp deleted file mode 100644 index 2efa1fd2f..000000000 --- a/Frameworks/GME/gme/Snes_Spc.cpp +++ /dev/null @@ -1,375 +0,0 @@ -// SPC emulation support: init, sample buffering, reset, SPC loading - -// snes_spc $vers. http://www.slack.net/~ant/ - -#include "Snes_Spc.h" - -/* Copyright (C) 2004-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#define RAM (m.ram.ram) -#define REGS (m.smp_regs [0]) -#define REGS_IN (m.smp_regs [1]) - -// (n ? n : 256) -#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) - - -//// Init - -blargg_err_t Snes_Spc::init() -{ - memset( &m, 0, sizeof m ); - dsp.init( RAM ); - - set_sfm_queue( 0, 0 ); - - m.tempo = tempo_unit; - - // Most SPC music doesn't need ROM, and almost all the rest only rely - // on these two bytes - m.rom [0x3E] = 0xFF; - m.rom [0x3F] = 0xC0; - - static unsigned char const cycle_table [128] = - {// 01 23 45 67 89 AB CD EF - 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x68, // 0 - 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x46, // 1 - 0x28,0x47,0x34,0x36,0x26,0x54,0x54,0x74, // 2 - 0x48,0x47,0x45,0x56,0x55,0x65,0x22,0x38, // 3 - 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x66, // 4 - 0x48,0x47,0x45,0x56,0x55,0x45,0x22,0x43, // 5 - 0x28,0x47,0x34,0x36,0x26,0x44,0x54,0x75, // 6 - 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x36, // 7 - 0x28,0x47,0x34,0x36,0x26,0x54,0x52,0x45, // 8 - 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0xC5, // 9 - 0x38,0x47,0x34,0x36,0x26,0x44,0x52,0x44, // A - 0x48,0x47,0x45,0x56,0x55,0x55,0x22,0x34, // B - 0x38,0x47,0x45,0x47,0x25,0x64,0x52,0x49, // C - 0x48,0x47,0x56,0x67,0x45,0x55,0x22,0x83, // D - 0x28,0x47,0x34,0x36,0x24,0x53,0x43,0x40, // E - 0x48,0x47,0x45,0x56,0x34,0x54,0x22,0x60, // F - }; - - // unpack cycle table - for ( int i = 0; i < 128; i++ ) - { - int n = cycle_table [i]; - m.cycle_table [i * 2 + 0] = n >> 4; - m.cycle_table [i * 2 + 1] = n & 0x0F; - } - - #if SPC_LESS_ACCURATE - memcpy( reg_times, reg_times_, sizeof reg_times ); - #endif - - reset(); - return blargg_ok; -} - -void Snes_Spc::init_rom( uint8_t const in [rom_size] ) -{ - memcpy( m.rom, in, sizeof m.rom ); -} - -void Snes_Spc::set_tempo( int t ) -{ - m.tempo = t; - int const timer2_shift = 4; // 64 kHz - int const other_shift = 3; // 8 kHz - - if ( !t ) - t = 1; - int const timer2_rate = 1 << timer2_shift; - int rate = (timer2_rate * tempo_unit + (t >> 1)) / t; - if ( rate < timer2_rate / 4 ) - rate = timer2_rate / 4; // max 4x tempo - m.timers [2].prescaler = rate; - m.timers [1].prescaler = rate << other_shift; - m.timers [0].prescaler = rate << other_shift; -} - -// Timer registers have been loaded. Applies these to the timers. Does not -// reset timer prescalers or dividers. -void Snes_Spc::timers_loaded() -{ - int i; - for ( i = 0; i < timer_count; i++ ) - { - Timer* t = &m.timers [i]; - t->period = IF_0_THEN_256( REGS [r_t0target + i] ); - t->enabled = REGS [r_control] >> i & 1; - t->counter = REGS_IN [r_t0out + i] & 0x0F; - } - - set_tempo( m.tempo ); -} - -// Loads registers from unified 16-byte format -void Snes_Spc::load_regs( uint8_t const in [reg_count] ) -{ - memcpy( REGS, in, reg_count ); - memcpy( REGS_IN, REGS, reg_count ); - - // These always read back as 0 - REGS_IN [r_test ] = 0; - REGS_IN [r_control ] = 0; - REGS_IN [r_t0target] = 0; - REGS_IN [r_t1target] = 0; - REGS_IN [r_t2target] = 0; -} - -// RAM was just loaded from SPC, with $F0-$FF containing SMP registers -// and timer counts. Copies these to proper registers. -void Snes_Spc::ram_loaded() -{ - m.rom_enabled = 0; - load_regs( &RAM [0xF0] ); - - // Put STOP instruction around memory to catch PC underflow/overflow - memset( m.ram.padding1, cpu_pad_fill, sizeof m.ram.padding1 ); - memset( m.ram.padding2, cpu_pad_fill, sizeof m.ram.padding2 ); -} - -// Registers were just loaded. Applies these new values. -void Snes_Spc::regs_loaded() -{ - enable_rom( REGS [r_control] & 0x80 ); - timers_loaded(); -} - -void Snes_Spc::reset_time_regs() -{ - m.cpu_error = NULL; - m.echo_accessed = 0; - m.spc_time = 0; - m.dsp_time = 0; - #if SPC_LESS_ACCURATE - m.dsp_time = clocks_per_sample + 1; - #endif - - for ( int i = 0; i < timer_count; i++ ) - { - Timer* t = &m.timers [i]; - t->next_time = 1; - t->divider = 0; - } - - regs_loaded(); - - m.extra_clocks = 0; - reset_buf(); -} - -void Snes_Spc::reset_common( int timer_counter_init ) -{ - int i; - for ( i = 0; i < timer_count; i++ ) - REGS_IN [r_t0out + i] = timer_counter_init; - - // Run IPL ROM - memset( &m.cpu_regs, 0, sizeof m.cpu_regs ); - m.cpu_regs.pc = rom_addr; - - REGS [r_test ] = 0x0A; - REGS [r_control] = 0xB0; // ROM enabled, clear ports - for ( i = 0; i < port_count; i++ ) - REGS_IN [r_cpuio0 + i] = 0; - - reset_time_regs(); -} - -void Snes_Spc::soft_reset() -{ - reset_common( 0 ); - dsp.soft_reset(); -} - -void Snes_Spc::reset() -{ - memset( RAM, 0xFF, 0x10000 ); - ram_loaded(); - reset_common( 0x0F ); - dsp.reset(); -} - -char const Snes_Spc::signature [signature_size + 1] = - "SNES-SPC700 Sound File Data v0.30\x1A\x1A"; - -blargg_err_t Snes_Spc::load_spc( void const* data, long size ) -{ - spc_file_t const* const spc = (spc_file_t const*) data; - - // be sure compiler didn't insert any padding into fle_t - assert( sizeof (spc_file_t) == spc_min_file_size + 0x80 ); - - // Check signature and file size - if ( size < signature_size || memcmp( spc, signature, 27 ) ) - return "Not an SPC file"; - - if ( size < spc_min_file_size ) - return "Corrupt SPC file"; - - // CPU registers - m.cpu_regs.pc = spc->pch * 0x100 + spc->pcl; - m.cpu_regs.a = spc->a; - m.cpu_regs.x = spc->x; - m.cpu_regs.y = spc->y; - m.cpu_regs.psw = spc->psw; - m.cpu_regs.sp = spc->sp; - - // RAM and registers - memcpy( RAM, spc->ram, 0x10000 ); - ram_loaded(); - - // DSP registers - dsp.load( spc->dsp ); - - reset_time_regs(); - - return blargg_ok; -} - -void Snes_Spc::clear_echo(bool force) -{ - if ( ( force || !m.echo_cleared ) && !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) - { - int addr = 0x100 * dsp.read( Spc_Dsp::r_esa ); - int end = addr + 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F); - if ( end > 0x10000 ) - end = 0x10000; - memset( &RAM [addr], 0xFF, end - addr ); - m.echo_cleared = true; - } -} - - -//// Sample output - -void Snes_Spc::reset_buf() -{ - // Start with half extra buffer of silence - sample_t* out = m.extra_buf; - while ( out < &m.extra_buf [extra_size / 2] ) - *out++ = 0; - - m.extra_pos = out; - m.buf_begin = NULL; - - dsp.set_output( NULL, 0 ); -} - -void Snes_Spc::set_output( sample_t out [], int size ) -{ - require( (size & 1) == 0 ); // size must be even - - m.extra_clocks &= clocks_per_sample - 1; - if ( out ) - { - sample_t const* out_end = out + size; - m.buf_begin = out; - m.buf_end = out_end; - - // Copy extra to output - sample_t const* in = m.extra_buf; - while ( in < m.extra_pos && out < out_end ) - *out++ = *in++; - - // Handle output being full already - if ( out >= out_end ) - { - // Have DSP write to remaining extra space - out = dsp.extra(); - out_end = &dsp.extra() [extra_size]; - - // Copy any remaining extra samples as if DSP wrote them - while ( in < m.extra_pos ) - *out++ = *in++; - assert( out <= out_end ); - } - - dsp.set_output( out, out_end - out ); - } - else - { - reset_buf(); - } -} - -void Snes_Spc::save_extra() -{ - // Get end pointers - sample_t const* main_end = m.buf_end; // end of data written to buf - sample_t const* dsp_end = dsp.out_pos(); // end of data written to dsp.extra() - if ( m.buf_begin <= dsp_end && dsp_end <= main_end ) - { - main_end = dsp_end; - dsp_end = dsp.extra(); // nothing in DSP's extra - } - - // Copy any extra samples at these ends into extra_buf - sample_t* out = m.extra_buf; - sample_t const* in; - for ( in = m.buf_begin + sample_count(); in < main_end; in++ ) - *out++ = *in; - for ( in = dsp.extra(); in < dsp_end ; in++ ) - *out++ = *in; - - m.extra_pos = out; - assert( out <= &m.extra_buf [extra_size] ); -} - -blargg_err_t Snes_Spc::play( int count, sample_t out [] ) -{ - require( (count & 1) == 0 ); // must be even - if ( count ) - { - set_output( out, count ); - end_frame( count * (clocks_per_sample / 2) ); - } - - const char* err = m.cpu_error; - m.cpu_error = NULL; - return err; -} - -blargg_err_t Snes_Spc::skip( int count ) -{ - #if SPC_LESS_ACCURATE - if ( count > 2 * sample_rate * 2 ) - { - set_output( NULL, 0 ); - - // Skip a multiple of 4 samples - time_t end = count; - count = (count & 3) + 1 * sample_rate * 2; - end = (end - count) * (clocks_per_sample / 2); - - m.skipped_kon = 0; - m.skipped_koff = 0; - - // Preserve DSP and timer synchronization - // TODO: verify that this really preserves it - int old_dsp_time = m.dsp_time + m.spc_time; - m.dsp_time = end - m.spc_time + skipping_time; - end_frame( end ); - m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; - - dsp.write( Spc_Dsp::r_koff, m.skipped_koff & ~m.skipped_kon ); - dsp.write( Spc_Dsp::r_kon , m.skipped_kon ); - clear_echo(); - } - #endif - - return play( count, NULL ); -} diff --git a/Frameworks/GME/gme/Snes_Spc.h b/Frameworks/GME/gme/Snes_Spc.h deleted file mode 100644 index 12679a038..000000000 --- a/Frameworks/GME/gme/Snes_Spc.h +++ /dev/null @@ -1,311 +0,0 @@ -// SNES SPC-700 APU emulator - -// snes_spc $vers -#ifndef SNES_SPC_H -#define SNES_SPC_H - -#include "Spc_Dsp.h" -#include "blargg_endian.h" - -class Sfm_Emu; - -struct Snes_Spc { - friend class Sfm_Emu; - -public: - typedef BOOST::uint8_t uint8_t; - - // Must be called once before using - blargg_err_t init(); - - // Sample pairs generated per second - enum { sample_rate = 32000 }; - -// Emulator use - - // Sets IPL ROM data. Library does not include ROM data. Most SPC music files - // don't need ROM, but a full emulator must provide this. - enum { rom_size = 0x40 }; - void init_rom( uint8_t const rom [rom_size] ); - - // Sets destination for output samples - typedef short sample_t; - void set_output( sample_t* out, int out_size ); - - // Number of samples written to output since last set - int sample_count() const; - - // Resets SPC to power-on state. This resets your output buffer, so you must - // call set_output() after this. - void reset(); - - // Emulates pressing reset switch on SNES. This resets your output buffer, so - // you must call set_output() after this. - void soft_reset(); - - // 1024000 SPC clocks per second, sample pair every 32 clocks - typedef int time_t; - enum { clock_rate = 1024000 }; - enum { clocks_per_sample = 32 }; - - // Emulated port read/write at specified time - enum { port_count = 4 }; - int read_port ( time_t, int port ); - void write_port( time_t, int port, int data ); - - // Runs SPC to end_time and starts a new time frame at 0 - void end_frame( time_t end_time ); - -// Sound control - - // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). - // Reduces emulation accuracy. - enum { voice_count = 8 }; - void mute_voices( int mask ); - - // If true, prevents channels and global volumes from being phase-negated. - void disable_surround( bool disable = true ); - - // If true, enables cubic interpolation - void interpolation_level( int level = 0 ); - - // Sets tempo, where tempo_unit = normal, tempo_unit / 2 = half speed, etc. - enum { tempo_unit = 0x100 }; - void set_tempo( int ); - -// SPC music files - - // Loads SPC data into emulator - enum { spc_min_file_size = 0x10180 }; - enum { spc_file_size = 0x10200 }; - blargg_err_t load_spc( void const* in, long size ); - - // Clears echo region. Useful after loading an SPC as many have garbage in echo. - void clear_echo(bool force = false); - - // 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 ); - - // blah - Spc_Dsp const* get_dsp() const; - Spc_Dsp * get_dsp(); - - // SFM Queue - void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end); - -// State save/load (only available with accurate DSP) - -#if !SPC_NO_COPY_STATE_FUNCS - // Saves/loads state - enum { state_size = 67 * 1024 }; // maximum space needed when saving - typedef Spc_Dsp::copy_func_t copy_func_t; - void copy_state( unsigned char** io, copy_func_t ); - - // Writes minimal header to spc_out - static void init_header( void* spc_out ); - - // Saves emulator state as SPC file data. Writes spc_file_size bytes to spc_out. - // Does not set up SPC header; use init_header() for that. - void save_spc( void* spc_out ); - - // Returns true if new key-on events occurred since last check. Useful for - // trimming silence while saving an SPC. - bool check_kon(); -#endif - -public: - // TODO: document - struct regs_t - { - int pc; - int a; - int x; - int y; - int psw; - int sp; - }; - regs_t& smp_regs() { return m.cpu_regs; } - - uint8_t* smp_ram() { return m.ram.ram; } - - void run_until( time_t t ) { run_until_( t ); } -public: - BLARGG_DISABLE_NOTHROW - - typedef BOOST::uint16_t uint16_t; - - // Time relative to m_spc_time. Speeds up code a bit by eliminating need to - // constantly add m_spc_time to time from CPU. CPU uses time that ends at - // 0 to eliminate reloading end time every instruction. It pays off. - typedef int rel_time_t; - - struct Timer - { - rel_time_t next_time; // time of next event - int prescaler; - int period; - int divider; - int enabled; - int counter; - }; - enum { reg_count = 0x10 }; - enum { timer_count = 3 }; - enum { extra_size = Spc_Dsp::extra_size }; - - enum { signature_size = 35 }; - -private: - Spc_Dsp dsp; - - #if SPC_LESS_ACCURATE - static signed char const reg_times_ [256]; - signed char reg_times [256]; - #endif - - struct state_t - { - Timer timers [timer_count]; - - uint8_t smp_regs [2] [reg_count]; - - regs_t cpu_regs; - - rel_time_t dsp_time; - time_t spc_time; - bool echo_accessed; - bool echo_cleared; - - 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]; - - uint8_t const* sfm_queue; - uint8_t const* sfm_queue_end; - - unsigned char cycle_table [256]; - - struct - { - // padding to neutralize address overflow - union { - uint8_t padding1 [0x100]; - uint16_t align; // makes compiler align data for 16-bit access - } padding1 [1]; - uint8_t ram [0x10000]; - uint8_t padding2 [0x100]; - } ram; - }; - state_t m; - - enum { rom_addr = 0xFFC0 }; - - enum { skipping_time = 127 }; - - // Value that padding should be filled with - enum { cpu_pad_fill = 0xFF }; - - enum { - r_test = 0x0, r_control = 0x1, - r_dspaddr = 0x2, r_dspdata = 0x3, - r_cpuio0 = 0x4, r_cpuio1 = 0x5, - r_cpuio2 = 0x6, r_cpuio3 = 0x7, - r_f8 = 0x8, r_f9 = 0x9, - r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, - r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF - }; - - void timers_loaded(); - void enable_rom( int enable ); - void reset_buf(); - void save_extra(); - void load_regs( uint8_t const in [reg_count] ); - void ram_loaded(); - void regs_loaded(); - void reset_time_regs(); - void reset_common( int timer_counter_init ); - - Timer* run_timer_ ( Timer* t, rel_time_t ); - Timer* run_timer ( Timer* t, rel_time_t ); - int dsp_read ( rel_time_t ); - void dsp_write ( int data, rel_time_t ); - void cpu_write_smp_reg_( int data, rel_time_t, int addr ); - void cpu_write_smp_reg ( int data, rel_time_t, int addr ); - void cpu_write_high ( int data, int i, rel_time_t ); - void cpu_write ( int data, int addr, rel_time_t ); - int cpu_read_smp_reg ( int i, rel_time_t ); - int cpu_read ( int addr, rel_time_t ); - unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); - - bool check_echo_access ( int addr ); - uint8_t* run_until_( time_t end_time ); - - struct spc_file_t - { - char signature [signature_size]; - uint8_t has_id666; - uint8_t version; - uint8_t pcl, pch; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t psw; - uint8_t sp; - char text [212]; - uint8_t ram [0x10000]; - uint8_t dsp [128]; - uint8_t unused [0x40]; - uint8_t ipl_rom [0x40]; - }; - - static char const signature [signature_size + 1]; - - void save_regs( uint8_t out [reg_count] ); -}; - -#include - -inline int Snes_Spc::sample_count() const { return (m.extra_clocks >> 5) * 2; } - -inline int Snes_Spc::read_port( time_t t, int port ) -{ - assert( (unsigned) port < port_count ); - return run_until_( t ) [port]; -} - -inline void Snes_Spc::write_port( time_t t, int port, int data ) -{ - assert( (unsigned) port < port_count ); - run_until_( t ) [0x10 + port] = data; -} - -inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); } - -inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); } - -inline void Snes_Spc::interpolation_level( int level ) { dsp.interpolation_level( level ); } - -inline Spc_Dsp const* Snes_Spc::get_dsp() const { return &dsp; } -inline Spc_Dsp * Snes_Spc::get_dsp() { return &dsp; } - -inline void Snes_Spc::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end) { m.sfm_queue = queue; m.sfm_queue_end = queue_end; } - -#if !SPC_NO_COPY_STATE_FUNCS -inline bool Snes_Spc::check_kon() { return dsp.check_kon(); } -#endif - -#endif diff --git a/Frameworks/GME/gme/Spc_Cpu.cpp b/Frameworks/GME/gme/Spc_Cpu.cpp deleted file mode 100644 index 8622af2ce..000000000 --- a/Frameworks/GME/gme/Spc_Cpu.cpp +++ /dev/null @@ -1,574 +0,0 @@ -// Core SPC emulation: CPU, timers, SMP registers, memory - -// snes_spc $vers. http://www.slack.net/~ant/ - -#include "Snes_Spc.h" - -/* Copyright (C) 2004-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#define RAM (m.ram.ram) -#define REGS (m.smp_regs [0]) -#define REGS_IN (m.smp_regs [1]) - -// (n ? n : 256) -#define IF_0_THEN_256( n ) ((uint8_t) ((n) - 1) + 1) - -// Note: SPC_MORE_ACCURACY exists mainly so I can run my validation tests, which -// do crazy echo buffer accesses. -#ifndef SPC_MORE_ACCURACY - #define SPC_MORE_ACCURACY 0 -#endif - - -//// Timers - -#define TIMER_DIV( t, n ) ((n) / t->prescaler) -#define TIMER_MUL( t, n ) ((n) * t->prescaler) - -Snes_Spc::Timer* Snes_Spc::run_timer_( Timer* t, rel_time_t time ) -{ - int elapsed = TIMER_DIV( t, time - t->next_time ) + 1; - t->next_time += TIMER_MUL( t, elapsed ); - - if ( t->enabled ) - { - int remain = IF_0_THEN_256( t->period - t->divider ); - int divider = t->divider + elapsed; - int over = elapsed - remain; - if ( over >= 0 ) - { - int n = over / t->period; - t->counter = (t->counter + 1 + n) & 0x0F; - divider = over - n * t->period; - } - t->divider = (uint8_t) divider; - } - return t; -} - -inline Snes_Spc::Timer* Snes_Spc::run_timer( Timer* t, rel_time_t time ) -{ - if ( time >= t->next_time ) - t = run_timer_( t, time ); - return t; -} - - -//// ROM - -void Snes_Spc::enable_rom( int enable ) -{ - if ( m.rom_enabled != enable ) - { - m.rom_enabled = enable; - if ( enable ) - memcpy( m.hi_ram, &RAM [rom_addr], sizeof m.hi_ram ); - memcpy( &RAM [rom_addr], (enable ? m.rom : m.hi_ram), rom_size ); - // TODO: ROM can still get overwritten when DSP writes to echo buffer - } -} - - -//// DSP - -#if SPC_LESS_ACCURATE - int const max_reg_time = 29; - - /* Fast DSP only runs every 32nd clock. By adjusting the end time based - on which register is being accessed, in most cases the register access - is emulated at the precise time. */ - signed char const Snes_Spc::reg_times_ [256] = - { - -1, 0,-11,-10,-15,-11, -2, -2, 4, 3, 14, 14, 26, 26, 14, 22, - 2, 3, 0, 1,-12, 0, 1, 1, 7, 6, 14, 14, 27, 14, 14, 23, - 5, 6, 3, 4, -1, 3, 4, 4, 10, 9, 14, 14, 26, -5, 14, 23, - 8, 9, 6, 7, 2, 6, 7, 7, 13, 12, 14, 14, 27, -4, 14, 24, - 11, 12, 9, 10, 5, 9, 10, 10, 16, 15, 14, 14, -2, -4, 14, 24, - 14, 15, 12, 13, 8, 12, 13, 13, 19, 18, 14, 14, -2,-36, 14, 24, - 17, 18, 15, 16, 11, 15, 16, 16, 22, 21, 14, 14, 28, -3, 14, 25, - 20, 21, 18, 19, 14, 18, 19, 19, 25, 24, 14, 14, 14, 29, 14, 25, - - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, 29, - }; - - #define RUN_DSP( time, offset ) \ - int count = (time) - (offset) - m.dsp_time;\ - if ( count >= 0 )\ - {\ - int clock_count = (count & ~(clocks_per_sample - 1)) + clocks_per_sample;\ - m.dsp_time += clock_count;\ - dsp.run( clock_count );\ - } -#else - #define RUN_DSP( time, offset ) \ - {\ - int count = (time) - m.dsp_time;\ - if ( !SPC_MORE_ACCURACY || count )\ - {\ - assert( count > 0 );\ - m.dsp_time = (time);\ - dsp.run( count );\ - }\ - } -#endif - -int Snes_Spc::dsp_read( rel_time_t time ) -{ - RUN_DSP( time, reg_times [REGS [r_dspaddr] & 0x7F] ); - - int result = dsp.read( REGS [r_dspaddr] & 0x7F ); - - #ifdef SPC_DSP_READ_HOOK - SPC_DSP_READ_HOOK( spc_time + time, (REGS [r_dspaddr] & 0x7F), result ); - #endif - - return result; -} - -inline void Snes_Spc::dsp_write( int data, rel_time_t time ) -{ - RUN_DSP( time, reg_times [REGS [r_dspaddr]] ) - #if SPC_LESS_ACCURATE - else if ( m.dsp_time == skipping_time ) - { - int r = REGS [r_dspaddr]; - if ( r == Spc_Dsp::r_kon ) - m.skipped_kon |= data & ~dsp.read( Spc_Dsp::r_koff ); - - if ( r == Spc_Dsp::r_koff ) - { - m.skipped_koff |= data; - m.skipped_kon &= ~data; - } - } - #endif - - #ifdef SPC_DSP_WRITE_HOOK - SPC_DSP_WRITE_HOOK( m.spc_time + time, REGS [r_dspaddr], (uint8_t) data ); - #endif - - if ( REGS [r_dspaddr] <= 0x7F ) - { - if ( REGS [r_dspaddr] != Spc_Dsp::r_flg ) - dsp.write( REGS [r_dspaddr], data ); - else - { - int prev = dsp.read( Spc_Dsp::r_flg ); - dsp.write( Spc_Dsp::r_flg, data ); - if ( ( data & 0x20 ) == ( ( data ^ prev ) & 0x20 ) ) clear_echo(); - } - } - else if ( !SPC_MORE_ACCURACY ) - dprintf( "SPC wrote to DSP register > $7F\n" ); -} - - -//// Memory access extras - -#if SPC_MORE_ACCURACY - #define MEM_ACCESS( time, addr ) \ - {\ - if ( time >= m.dsp_time )\ - {\ - RUN_DSP( time, max_reg_time );\ - }\ - } -#elif !defined (NDEBUG) - // Debug-only check for read/write within echo buffer, since this might result in - // inaccurate emulation due to the DSP not being caught up to the present. - - bool Snes_Spc::check_echo_access( int addr ) - { - if ( !(dsp.read( Spc_Dsp::r_flg ) & 0x20) ) - { - int start = 0x100 * dsp.read( Spc_Dsp::r_esa ); - int size = 0x800 * (dsp.read( Spc_Dsp::r_edl ) & 0x0F); - int end = start + (size ? size : 4); - if ( start <= addr && addr < end ) - { - if ( !m.echo_accessed ) - { - m.echo_accessed = 1; - return true; - } - } - } - return false; - } - - #define MEM_ACCESS( time, addr ) check( !check_echo_access( (uint16_t) addr ) ); -#else - #define MEM_ACCESS( time, addr ) -#endif - - -//// CPU write - -#if SPC_MORE_ACCURACY -static unsigned char const glitch_probs [3] [256] = -{ - 0xC3,0x92,0x5B,0x1C,0xD1,0x92,0x5B,0x1C,0xDB,0x9C,0x72,0x18,0xCD,0x5C,0x38,0x0B, - 0xE1,0x9C,0x74,0x17,0xCF,0x75,0x45,0x0C,0xCF,0x6E,0x4A,0x0D,0xA3,0x3A,0x1D,0x08, - 0xDB,0xA0,0x82,0x19,0xD9,0x73,0x3C,0x0E,0xCB,0x76,0x52,0x0B,0xA5,0x46,0x1D,0x09, - 0xDA,0x74,0x55,0x0F,0xA2,0x3F,0x21,0x05,0x9A,0x40,0x20,0x07,0x63,0x1E,0x10,0x01, - 0xDF,0xA9,0x85,0x1D,0xD3,0x84,0x4B,0x0E,0xCF,0x6F,0x49,0x0F,0xB3,0x48,0x1E,0x05, - 0xD8,0x77,0x52,0x12,0xB7,0x49,0x23,0x06,0xAA,0x45,0x28,0x07,0x7D,0x28,0x0F,0x07, - 0xCC,0x7B,0x4A,0x0E,0xB2,0x4F,0x24,0x07,0xAD,0x43,0x2C,0x06,0x86,0x29,0x11,0x07, - 0xAE,0x48,0x1F,0x0A,0x76,0x21,0x19,0x05,0x76,0x21,0x14,0x05,0x44,0x11,0x0B,0x01, - 0xE7,0xAD,0x96,0x23,0xDC,0x86,0x59,0x0E,0xDC,0x7C,0x5F,0x15,0xBB,0x53,0x2E,0x09, - 0xD6,0x7C,0x4A,0x16,0xBB,0x4A,0x25,0x08,0xB3,0x4F,0x28,0x0B,0x8E,0x23,0x15,0x08, - 0xCF,0x7F,0x57,0x11,0xB5,0x4A,0x23,0x0A,0xAA,0x42,0x28,0x05,0x7D,0x22,0x12,0x03, - 0xA6,0x49,0x28,0x09,0x82,0x2B,0x0D,0x04,0x7A,0x20,0x0F,0x04,0x3D,0x0F,0x09,0x03, - 0xD1,0x7C,0x4C,0x0F,0xAF,0x4E,0x21,0x09,0xA8,0x46,0x2A,0x07,0x85,0x1F,0x0E,0x07, - 0xA6,0x3F,0x26,0x07,0x7C,0x24,0x14,0x07,0x78,0x22,0x16,0x04,0x46,0x12,0x0A,0x02, - 0xA6,0x41,0x2C,0x0A,0x7E,0x28,0x11,0x05,0x73,0x1B,0x14,0x05,0x3D,0x11,0x0A,0x02, - 0x70,0x22,0x17,0x05,0x48,0x13,0x08,0x03,0x3C,0x07,0x0D,0x07,0x26,0x07,0x06,0x01, - - 0xE0,0x9F,0xDA,0x7C,0x4F,0x18,0x28,0x0D,0xE9,0x9F,0xDA,0x7C,0x4F,0x18,0x1F,0x07, - 0xE6,0x97,0xD8,0x72,0x64,0x13,0x26,0x09,0xDC,0x67,0xA9,0x38,0x21,0x07,0x15,0x06, - 0xE9,0x91,0xD2,0x6B,0x63,0x14,0x2B,0x0E,0xD6,0x61,0xB7,0x41,0x2B,0x0E,0x10,0x09, - 0xCF,0x59,0xB0,0x2F,0x35,0x08,0x0F,0x07,0xB6,0x30,0x7A,0x21,0x17,0x07,0x09,0x03, - 0xE7,0xA3,0xE5,0x6B,0x65,0x1F,0x34,0x09,0xD8,0x6B,0xBE,0x45,0x27,0x07,0x10,0x07, - 0xDA,0x54,0xB1,0x39,0x2E,0x0E,0x17,0x08,0xA9,0x3C,0x86,0x22,0x16,0x06,0x07,0x03, - 0xD4,0x51,0xBC,0x3D,0x38,0x0A,0x13,0x06,0xB2,0x37,0x79,0x1C,0x17,0x05,0x0E,0x06, - 0xA7,0x31,0x74,0x1C,0x11,0x06,0x0C,0x02,0x6D,0x1A,0x38,0x10,0x0B,0x05,0x06,0x03, - 0xEB,0x9A,0xE1,0x7A,0x6F,0x13,0x34,0x0E,0xE6,0x75,0xC5,0x45,0x3E,0x0B,0x1A,0x05, - 0xD8,0x63,0xC1,0x40,0x3C,0x1B,0x19,0x06,0xB3,0x42,0x83,0x29,0x18,0x0A,0x08,0x04, - 0xD4,0x58,0xBA,0x43,0x3F,0x0A,0x1F,0x09,0xB1,0x33,0x8A,0x1F,0x1F,0x06,0x0D,0x05, - 0xAF,0x3C,0x7A,0x1F,0x16,0x08,0x0A,0x01,0x72,0x1B,0x52,0x0D,0x0B,0x09,0x06,0x01, - 0xCF,0x63,0xB7,0x47,0x40,0x10,0x14,0x06,0xC0,0x41,0x96,0x20,0x1C,0x09,0x10,0x05, - 0xA6,0x35,0x82,0x1A,0x20,0x0C,0x0E,0x04,0x80,0x1F,0x53,0x0F,0x0B,0x02,0x06,0x01, - 0xA6,0x31,0x81,0x1B,0x1D,0x01,0x08,0x08,0x7B,0x20,0x4D,0x19,0x0E,0x05,0x07,0x03, - 0x6B,0x17,0x49,0x07,0x0E,0x03,0x0A,0x05,0x37,0x0B,0x1F,0x06,0x04,0x02,0x07,0x01, - - 0xF0,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x47,0x1E,0x6E,0x1B,0x32,0x0A, - 0xF0,0xD6,0xEA,0xA4,0xED,0xC4,0xDE,0x82,0x98,0x1F,0x50,0x13,0x52,0x15,0x2A,0x0A, - 0xF1,0xD1,0xEB,0xA2,0xEB,0xB7,0xD8,0x69,0xA2,0x1F,0x5B,0x18,0x55,0x18,0x2C,0x0A, - 0xED,0xB5,0xDE,0x7E,0xE6,0x85,0xD3,0x59,0x59,0x0F,0x2C,0x09,0x24,0x07,0x15,0x09, - 0xF1,0xD6,0xEA,0xA0,0xEC,0xBB,0xDA,0x77,0xA9,0x23,0x58,0x14,0x5D,0x12,0x2F,0x09, - 0xF1,0xC1,0xE3,0x86,0xE4,0x87,0xD2,0x4E,0x68,0x15,0x26,0x0B,0x27,0x09,0x15,0x02, - 0xEE,0xA6,0xE0,0x5C,0xE0,0x77,0xC3,0x41,0x67,0x1B,0x3C,0x07,0x2A,0x06,0x19,0x07, - 0xE4,0x75,0xC6,0x43,0xCC,0x50,0x95,0x23,0x35,0x09,0x14,0x04,0x15,0x05,0x0B,0x04, - 0xEE,0xD6,0xED,0xAD,0xEC,0xB1,0xEB,0x79,0xAC,0x22,0x56,0x14,0x5A,0x12,0x26,0x0A, - 0xEE,0xBB,0xE7,0x7E,0xE9,0x8D,0xCB,0x49,0x67,0x11,0x34,0x07,0x2B,0x0B,0x14,0x07, - 0xED,0xA7,0xE5,0x76,0xE3,0x7E,0xC4,0x4B,0x77,0x14,0x34,0x08,0x27,0x07,0x14,0x04, - 0xE7,0x8B,0xD2,0x4C,0xCA,0x56,0x9E,0x31,0x36,0x0C,0x11,0x07,0x14,0x04,0x0A,0x02, - 0xF0,0x9B,0xEA,0x6F,0xE5,0x81,0xC4,0x43,0x74,0x10,0x30,0x0B,0x2D,0x08,0x1B,0x06, - 0xE6,0x83,0xCA,0x48,0xD9,0x56,0xA7,0x23,0x3B,0x09,0x12,0x09,0x15,0x07,0x0A,0x03, - 0xE5,0x5F,0xCB,0x3C,0xCF,0x48,0x91,0x22,0x31,0x0A,0x17,0x08,0x15,0x04,0x0D,0x02, - 0xD1,0x43,0x91,0x20,0xA9,0x2D,0x54,0x12,0x17,0x07,0x09,0x02,0x0C,0x04,0x05,0x03, -}; -#endif - -// Read/write handlers are divided into multiple functions to keep rarely-used -// functionality separate so often-used functionality can be optimized better -// by compiler. - -// If write isn't preceded by read, data has this added to it -int const no_read_before_write = 0x2000; - -void Snes_Spc::cpu_write_smp_reg_( int data, rel_time_t time, int addr ) -{ - switch ( addr ) - { - case r_t0target: - case r_t1target: - case r_t2target: { - Timer* t = &m.timers [addr - r_t0target]; - int period = IF_0_THEN_256( data ); - if ( t->period != period ) - { - t = run_timer( t, time ); - #if SPC_MORE_ACCURACY - // Insane behavior when target is written just after counter is - // clocked and counter matches new period and new period isn't 1, 2, 4, or 8 - if ( t->divider == (period & 0xFF) && - t->next_time == time + TIMER_MUL( t, 1 ) && - ((period - 1) | ~0x0F) & period ) - { - //dprintf( "SPC pathological timer target write\n" ); - - // If the period is 3, 5, or 9, there's a probability this behavior won't occur, - // based on the previous period - int prob = 0xFF; - int old_period = t->period & 0xFF; - if ( period == 3 ) prob = glitch_probs [0] [old_period]; - if ( period == 5 ) prob = glitch_probs [1] [old_period]; - if ( period == 9 ) prob = glitch_probs [2] [old_period]; - - // The glitch suppresses incrementing of one of the counter bits, based on - // the lowest set bit in the new period - int b = 1; - while ( !(period & b) ) - b <<= 1; - - if ( (rand() >> 4 & 0xFF) <= prob ) - t->divider = (t->divider - b) & 0xFF; - } - #endif - t->period = period; - } - break; - } - - case r_t0out: - case r_t1out: - case r_t2out: - if ( !SPC_MORE_ACCURACY ) - dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); - - if ( data < no_read_before_write / 2 ) - run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; - break; - - // Registers that act like RAM - case 0x8: - case 0x9: - REGS_IN [addr] = (uint8_t) data; - break; - - case r_test: - if ( (uint8_t) data != 0x0A ) - dprintf( "SPC wrote to test register\n" ); - break; - - case r_control: - // port clears - if ( data & 0x10 ) - { - REGS_IN [r_cpuio0] = 0; - REGS_IN [r_cpuio1] = 0; - } - if ( data & 0x20 ) - { - REGS_IN [r_cpuio2] = 0; - REGS_IN [r_cpuio3] = 0; - } - - // timers - { - for ( int i = 0; i < timer_count; i++ ) - { - Timer* t = &m.timers [i]; - int enabled = data >> i & 1; - if ( t->enabled != enabled ) - { - t = run_timer( t, time ); - t->enabled = enabled; - if ( enabled ) - { - t->divider = 0; - t->counter = 0; - } - } - } - } - enable_rom( data & 0x80 ); - break; - } -} - -void Snes_Spc::cpu_write_smp_reg( int data, rel_time_t time, int addr ) -{ - if ( addr == r_dspdata ) // 99% - dsp_write( data, time ); - else - cpu_write_smp_reg_( data, time, addr ); -} - -void Snes_Spc::cpu_write_high( int data, int i, rel_time_t time ) -{ - if ( i < rom_size ) - { - m.hi_ram [i] = (uint8_t) data; - if ( m.rom_enabled ) - RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM - } - else - { - assert( RAM [i + rom_addr] == (uint8_t) data ); - RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding - cpu_write( data, i + rom_addr - 0x10000, time ); - } -} - -int const bits_in_int = CHAR_BIT * sizeof (int); - -void Snes_Spc::cpu_write( int data, int addr, rel_time_t time ) -{ - MEM_ACCESS( time, addr ) - - // RAM - RAM [addr] = (uint8_t) data; - int reg = addr - 0xF0; - if ( reg >= 0 ) // 64% - { - // $F0-$FF - if ( reg < reg_count ) // 87% - { - REGS [reg] = (uint8_t) data; - - // Ports - #ifdef SPC_PORT_WRITE_HOOK - if ( (unsigned) (reg - r_cpuio0) < port_count ) - SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), - (uint8_t) data, ®S [r_cpuio0] ); - #endif - - // Registers other than $F2 and $F4-$F7 - //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 ) - // TODO: this is a bit on the fragile side - if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36% - cpu_write_smp_reg( data, time, reg ); - } - // High mem/address wrap-around - else - { - reg -= rom_addr - 0xF0; - if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around - cpu_write_high( data, reg, time ); - } - } -} - - -//// CPU read - -inline int Snes_Spc::cpu_read_smp_reg( int reg, rel_time_t time ) -{ - int result = REGS_IN [reg]; - reg -= r_dspaddr; - // DSP addr and data - if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3 - { - result = REGS [r_dspaddr]; - if ( (unsigned) reg == 1 ) - result = dsp_read( time ); // 0xF3 - } - reg -= r_cpuio0 - r_dspaddr; - if ( (unsigned) reg <= 3 ) - { - if ( m.sfm_queue && m.sfm_queue < m.sfm_queue_end ) - { - result = *m.sfm_queue++; - } - } - return result; -} - -int Snes_Spc::cpu_read( int addr, rel_time_t time ) -{ - MEM_ACCESS( time, addr ) - - // RAM - int result = RAM [addr]; - int reg = addr - 0xF0; - if ( reg >= 0 ) // 40% - { - reg -= 0x10; - if ( (unsigned) reg >= 0xFF00 ) // 21% - { - reg += 0x10 - r_t0out; - - // Timers - if ( (unsigned) reg < timer_count ) // 90% - { - Timer* t = &m.timers [reg]; - if ( time >= t->next_time ) - t = run_timer_( t, time ); - result = t->counter; - t->counter = 0; - } - // Other registers - else if ( reg < 0 ) // 10% - { - result = cpu_read_smp_reg( reg + r_t0out, time ); - } - else // 1% - { - assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 ); - result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time ); - } - } - } - - return result; -} - - -//// Run - -// Prefix and suffix for CPU emulator function -#define SPC_CPU_RUN_FUNC \ -BOOST::uint8_t* Snes_Spc::run_until_( time_t end_time )\ -{\ - rel_time_t rel_time = m.spc_time - end_time;\ - assert( rel_time <= 0 );\ - m.spc_time = end_time;\ - m.dsp_time += rel_time;\ - m.timers [0].next_time += rel_time;\ - m.timers [1].next_time += rel_time;\ - m.timers [2].next_time += rel_time; - -#define SPC_CPU_RUN_FUNC_END \ - m.spc_time += rel_time;\ - m.dsp_time -= rel_time;\ - m.timers [0].next_time -= rel_time;\ - m.timers [1].next_time -= rel_time;\ - m.timers [2].next_time -= rel_time;\ - assert( m.spc_time <= end_time );\ - return ®S [r_cpuio0];\ -} - -int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks - -void Snes_Spc::end_frame( time_t end_time ) -{ - // Catch CPU up to as close to end as possible. If final instruction - // would exceed end, does NOT execute it and leaves m.spc_time < end. - if ( end_time > m.spc_time ) - run_until_( end_time ); - - m.spc_time -= end_time; - m.extra_clocks += end_time; - - // Greatest number of clocks early that emulation can stop early due to - // not being able to execute current instruction without going over - // allowed time. - assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); - - // Catch timers up to CPU - for ( int i = 0; i < timer_count; i++ ) - run_timer( &m.timers [i], 0 ); - - // Catch DSP up to CPU - if ( m.dsp_time < 0 ) - { - RUN_DSP( 0, max_reg_time ); - } - - // Save any extra samples beyond what should be generated - if ( m.buf_begin ) - save_extra(); -} - -// Inclusion here allows static memory access functions and better optimization -#include "Spc_Cpu.h" diff --git a/Frameworks/GME/gme/Spc_Cpu.h b/Frameworks/GME/gme/Spc_Cpu.h deleted file mode 100644 index dbd1bc2f8..000000000 --- a/Frameworks/GME/gme/Spc_Cpu.h +++ /dev/null @@ -1,1225 +0,0 @@ -// snes_spc $vers. http://www.slack.net/~ant/ - -/* Copyright (C) 2004-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -//// Memory access - -#if SPC_MORE_ACCURACY - #define SUSPICIOUS_OPCODE( name ) ((void) 0) -#else - #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" ) -#endif - -#define CPU_READ( time, offset, addr )\ - cpu_read( addr, time + offset ) - -#define CPU_WRITE( time, offset, addr, data )\ - cpu_write( data, addr, time + offset ) - -#if SPC_MORE_ACCURACY - #define CPU_READ_TIMER( time, offset, addr, out )\ - { out = CPU_READ( time, offset, addr ); } - -#else - // timers are by far the most common thing read from dp - #define CPU_READ_TIMER( time, offset, addr_, out )\ - {\ - rel_time_t adj_time = time + offset;\ - int dp_addr = addr_;\ - int ti = dp_addr - (r_t0out + 0xF0);\ - if ( (unsigned) ti < timer_count )\ - {\ - Timer* t = &m.timers [ti];\ - if ( adj_time >= t->next_time )\ - t = run_timer_( t, adj_time );\ - out = t->counter;\ - t->counter = 0;\ - }\ - else\ - {\ - out = ram [dp_addr];\ - int i = dp_addr - 0xF0;\ - if ( (unsigned) i < 0x10 )\ - out = cpu_read_smp_reg( i, adj_time );\ - }\ - } -#endif - -#define TIME_ADJ( n ) (n) - -#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) -#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) -#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) - -#define DP_ADDR( addr ) (dp + (addr)) - -#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) -#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) -#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) - -#define READ_PROG16( addr ) GET_LE16( ram + (addr) ) - -#define SET_PC( n ) (pc = ram + (n)) -#define GET_PC() (pc - ram) -#define READ_PC( pc ) (*(pc)) -#define READ_PC16( pc ) GET_LE16( pc ) - -// TODO: remove non-wrapping versions? -#define SPC_NO_SP_WRAPAROUND 0 - -#define SET_SP( v ) (sp = ram + 0x101 + (v)) -#define GET_SP() (sp - 0x101 - ram) - -#if SPC_NO_SP_WRAPAROUND -#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) -#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) -#define POP( out ) (void) ((out) = *sp++) - -#else -#define PUSH16( data )\ -{\ - int addr = (sp -= 2) - ram;\ - if ( addr > 0x100 )\ - {\ - SET_LE16( sp, data );\ - }\ - else\ - {\ - ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ - sp [1] = (uint8_t) (data >> 8);\ - sp += 0x100;\ - }\ -} - -#define PUSH( data )\ -{\ - *--sp = (uint8_t) (data);\ - if ( sp - ram == 0x100 )\ - sp += 0x100;\ -} - -#define POP( out )\ -{\ - out = *sp++;\ - if ( sp - ram == 0x201 )\ - {\ - out = sp [-0x101];\ - sp -= 0x100;\ - }\ -} - -#endif - -#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) - -unsigned Snes_Spc::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) -{ - unsigned addr = READ_PC16( pc ); - unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); - return t << 8 & 0x100; -} - -//// Status flag handling - -// Flags with hex value for clarity when used as mask. -// Stored in indicated variable during emulation. -int const n80 = 0x80; // nz -int const v40 = 0x40; // psw -int const p20 = 0x20; // dp -int const b10 = 0x10; // psw -int const h08 = 0x08; // psw -int const i04 = 0x04; // psw -int const z02 = 0x02; // nz -int const c01 = 0x01; // c - -int const nz_neg_mask = 0x880; // either bit set indicates N flag set - -#define GET_PSW( out )\ -{\ - out = psw & ~(n80 | p20 | z02 | c01);\ - out |= c >> 8 & c01;\ - out |= dp >> 3 & p20;\ - out |= ((nz >> 4) | nz) & n80;\ - if ( !(uint8_t) nz ) out |= z02;\ -} - -#define SET_PSW( in )\ -{\ - psw = in;\ - c = in << 8;\ - dp = in << 3 & 0x100;\ - nz = (in << 4 & 0x800) | (~in & z02);\ -} - -SPC_CPU_RUN_FUNC -{ - uint8_t* const ram = RAM; - int a = m.cpu_regs.a; - int x = m.cpu_regs.x; - int y = m.cpu_regs.y; - uint8_t const* pc; - uint8_t* sp; - int psw; - int c; - int nz; - int dp; - - SET_PC( m.cpu_regs.pc ); - SET_SP( m.cpu_regs.sp ); - SET_PSW( m.cpu_regs.psw ); - - goto loop; - - - // Main loop - -cbranch_taken_loop: - pc += *(BOOST::int8_t const*) pc; -inc_pc_loop: - pc++; -loop: -{ - unsigned opcode; - unsigned data; - - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - opcode = *pc; - if ( (rel_time += m.cycle_table [opcode]) > 0 ) - goto out_of_time; - - #ifdef SPC_CPU_OPCODE_HOOK - SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); - #endif - - #ifdef CPU_INSTR_HOOK - CPU_INSTR_HOOK( GET_PC(), pc, a, x, y, GET_SP(), rel_time ); - #endif - - /* - //SUB_CASE_COUNTER( 1 ); - #define PROFILE_TIMER_LOOP( op, addr, len )\ - if ( opcode == op )\ - {\ - int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ - pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ - SUB_CASE_COUNTER( op && cond );\ - } - - PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); - PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); - PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); - */ - - // TODO: if PC is at end of memory, this will get wrong operand (very obscure) - data = *++pc; - switch ( opcode ) - { - -// Common instructions - -#define BRANCH( cond )\ -{\ - pc++;\ - pc += (BOOST::int8_t) data;\ - if ( cond )\ - goto loop;\ - pc -= (BOOST::int8_t) data;\ - rel_time -= 2;\ - goto loop;\ -} - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ) // 89% taken - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ) - - case 0x3F:{// CALL - int old_addr = GET_PC() + 2; - SET_PC( READ_PC16( pc ) ); - PUSH16( old_addr ); - goto loop; - } - - case 0x6F:// RET - #if SPC_NO_SP_WRAPAROUND - { - SET_PC( GET_LE16( sp ) ); - sp += 2; - } - #else - { - int addr = sp - ram; - SET_PC( GET_LE16( sp ) ); - sp += 2; - if ( addr < 0x1FF ) - goto loop; - - SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); - sp -= 0x100; - } - #endif - goto loop; - - case 0xE4: // MOV a,dp - ++pc; - // 80% from timer - READ_DP_TIMER( 0, data, a = nz ); - goto loop; - - case 0xFA:{// MOV dp,dp - int temp; - READ_DP_TIMER( -2, data, temp ); - data = temp + no_read_before_write ; - } - // fall through - case 0x8F:{// MOV dp,#imm - int temp = READ_PC( pc + 1 ); - pc += 2; - - #if !SPC_MORE_ACCURACY - { - int i = dp + temp; - ram [i] = (uint8_t) data; - i -= 0xF0; - if ( (unsigned) i < 0x10 ) // 76% - { - REGS [i] = (uint8_t) data; - - // Registers other than $F2 and $F4-$F7 - //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) - if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% - cpu_write_smp_reg( data, rel_time, i ); - } - } - #else - WRITE_DP( 0, temp, data ); - #endif - goto loop; - } - - case 0xC4: // MOV dp,a - ++pc; - #if !SPC_MORE_ACCURACY - { - int i = dp + data; - ram [i] = (uint8_t) a; - i -= 0xF0; - if ( (unsigned) i < 0x10 ) // 39% - { - unsigned sel = i - 2; - REGS [i] = (uint8_t) a; - - if ( sel == 1 ) // 51% $F3 - dsp_write( a, rel_time ); - else if ( sel > 1 ) // 1% not $F2 or $F3 - cpu_write_smp_reg_( a, rel_time, i ); - } - } - #else - WRITE_DP( 0, data, a ); - #endif - goto loop; - -#define CASE( n ) case n: - -// Define common address modes based on opcode for immediate mode. Execution -// ends with data set to the address of the operand. -#define ADDR_MODES_( op )\ - CASE( op - 0x02 ) /* (X) */\ - data = x + dp;\ - pc--;\ - goto end_##op;\ - CASE( op + 0x0F ) /* (dp)+Y */\ - data = READ_PROG16( data + dp ) + y;\ - goto end_##op;\ - CASE( op - 0x01 ) /* (dp+X) */\ - data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ - goto end_##op;\ - CASE( op + 0x0E ) /* abs+Y */\ - data += y;\ - goto abs_##op;\ - CASE( op + 0x0D ) /* abs+X */\ - data += x;\ - CASE( op - 0x03 ) /* abs */\ - abs_##op:\ - data += 0x100 * READ_PC( ++pc );\ - goto end_##op;\ - CASE( op + 0x0C ) /* dp+X */\ - data = (uint8_t) (data + x); - -#define ADDR_MODES_NO_DP( op )\ - ADDR_MODES_( op )\ - data += dp;\ - end_##op: - -#define ADDR_MODES( op )\ - ADDR_MODES_( op )\ - CASE( op - 0x04 ) /* dp */\ - data += dp;\ - end_##op: - -// 1. 8-bit Data Transmission Commands. Group I - - ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr - a = nz = READ( 0, data ); - goto inc_pc_loop; - - case 0xBF:{// MOV A,(X)+ - int temp = x + dp; - x = (uint8_t) (x + 1); - a = nz = READ( -1, temp ); - goto loop; - } - - case 0xE8: // MOV A,imm - a = data; - nz = data; - goto inc_pc_loop; - - case 0xF9: // MOV X,dp+Y - data = (uint8_t) (data + y); - case 0xF8: // MOV X,dp - READ_DP_TIMER( 0, data, x = nz ); - goto inc_pc_loop; - - case 0xE9: // MOV X,abs - data = READ_PC16( pc ); - ++pc; - data = READ( 0, data ); - case 0xCD: // MOV X,imm - x = data; - nz = data; - goto inc_pc_loop; - - case 0xFB: // MOV Y,dp+X - data = (uint8_t) (data + x); - case 0xEB: // MOV Y,dp - // 70% from timer - pc++; - READ_DP_TIMER( 0, data, y = nz ); - goto loop; - - case 0xEC:{// MOV Y,abs - int temp = READ_PC16( pc ); - pc += 2; - READ_TIMER( 0, temp, y = nz ); - //y = nz = READ( 0, temp ); - goto loop; - } - - case 0x8D: // MOV Y,imm - y = data; - nz = data; - goto inc_pc_loop; - -// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 - - ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A - WRITE( 0, data, a ); - goto inc_pc_loop; - - { - int temp; - case 0xCC: // MOV abs,Y - temp = y; - goto mov_abs_temp; - case 0xC9: // MOV abs,X - temp = x; - mov_abs_temp: - WRITE( 0, READ_PC16( pc ), temp ); - pc += 2; - goto loop; - } - - case 0xD9: // MOV dp+Y,X - data = (uint8_t) (data + y); - case 0xD8: // MOV dp,X - WRITE( 0, data + dp, x ); - goto inc_pc_loop; - - case 0xDB: // MOV dp+X,Y - data = (uint8_t) (data + x); - case 0xCB: // MOV dp,Y - WRITE( 0, data + dp, y ); - goto inc_pc_loop; - -// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. - - case 0x7D: // MOV A,X - a = x; - nz = x; - goto loop; - - case 0xDD: // MOV A,Y - a = y; - nz = y; - goto loop; - - case 0x5D: // MOV X,A - x = a; - nz = a; - goto loop; - - case 0xFD: // MOV Y,A - y = a; - nz = a; - goto loop; - - case 0x9D: // MOV X,SP - x = nz = GET_SP(); - goto loop; - - case 0xBD: // MOV SP,X - SET_SP( x ); - goto loop; - - //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) - - case 0xAF: // MOV (X)+,A - WRITE_DP( 0, x, a + no_read_before_write ); - x++; - goto loop; - -// 5. 8-BIT LOGIC OPERATION COMMANDS - -#define LOGICAL_OP( op, func )\ - ADDR_MODES( op ) /* addr */\ - data = READ( 0, data );\ - case op: /* imm */\ - nz = a func##= data;\ - goto inc_pc_loop;\ - { unsigned addr;\ - case op + 0x11: /* X,Y */\ - data = READ_DP( -2, y );\ - addr = x + dp;\ - goto addr_##op;\ - case op + 0x01: /* dp,dp */\ - data = READ_DP( -3, data );\ - case op + 0x10:{/*dp,imm*/\ - uint8_t const* addr2 = pc + 1;\ - pc += 2;\ - addr = READ_PC( addr2 ) + dp;\ - }\ - addr_##op:\ - nz = data func READ( -1, addr );\ - WRITE( 0, addr, nz );\ - goto loop;\ - } - - LOGICAL_OP( 0x28, & ); // AND - - LOGICAL_OP( 0x08, | ); // OR - - LOGICAL_OP( 0x48, ^ ); // EOR - -// 4. 8-BIT ARITHMETIC OPERATION COMMANDS - - ADDR_MODES( 0x68 ) // CMP addr - data = READ( 0, data ); - case 0x68: // CMP imm - nz = a - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x79: // CMP (X),(Y) - data = READ_DP( -2, y ); - nz = READ_DP( -1, x ) - data; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x69: // CMP dp,dp - data = READ_DP( -3, data ); - case 0x78: // CMP dp,imm - nz = READ_DP( -1, READ_PC( ++pc ) ) - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x3E: // CMP X,dp - data += dp; - goto cmp_x_addr; - case 0x1E: // CMP X,abs - data = READ_PC16( pc ); - pc++; - cmp_x_addr: - data = READ( 0, data ); - case 0xC8: // CMP X,imm - nz = x - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x7E: // CMP Y,dp - data += dp; - goto cmp_y_addr; - case 0x5E: // CMP Y,abs - data = READ_PC16( pc ); - pc++; - cmp_y_addr: - data = READ( 0, data ); - case 0xAD: // CMP Y,imm - nz = y - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - { - int addr; - case 0xB9: // SBC (x),(y) - case 0x99: // ADC (x),(y) - pc--; // compensate for inc later - data = READ_DP( -2, y ); - addr = x + dp; - goto adc_addr; - case 0xA9: // SBC dp,dp - case 0x89: // ADC dp,dp - data = READ_DP( -3, data ); - case 0xB8: // SBC dp,imm - case 0x98: // ADC dp,imm - addr = READ_PC( ++pc ) + dp; - adc_addr: - nz = READ( -1, addr ); - goto adc_data; - -// catch ADC and SBC together, then decode later based on operand -#undef CASE -#define CASE( n ) case n: case (n) + 0x20: - ADDR_MODES( 0x88 ) // ADC/SBC addr - data = READ( 0, data ); - case 0xA8: // SBC imm - case 0x88: // ADC imm - addr = -1; // A - nz = a; - adc_data: { - int flags; - if ( opcode >= 0xA0 ) // SBC - data ^= 0xFF; - - flags = data ^ nz; - nz += data + (c >> 8 & 1); - flags ^= nz; - - psw = (psw & ~(v40 | h08)) | - (flags >> 1 & h08) | - ((flags + 0x80) >> 2 & v40); - c = nz; - if ( addr < 0 ) - { - a = (uint8_t) nz; - goto inc_pc_loop; - } - WRITE( 0, addr, /*(uint8_t)*/ nz ); - goto inc_pc_loop; - } - - } - -// 6. ADDITION & SUBTRACTION COMMANDS - -#define INC_DEC_REG( reg, op )\ - nz = reg op;\ - reg = (uint8_t) nz;\ - goto loop; - - case 0xBC: INC_DEC_REG( a, + 1 ) // INC A - case 0x3D: INC_DEC_REG( x, + 1 ) // INC X - case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y - - case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A - case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X - case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y - - case 0x9B: // DEC dp+X - case 0xBB: // INC dp+X - data = (uint8_t) (data + x); - case 0x8B: // DEC dp - case 0xAB: // INC dp - data += dp; - goto inc_abs; - case 0x8C: // DEC abs - case 0xAC: // INC abs - data = READ_PC16( pc ); - pc++; - inc_abs: - nz = (opcode >> 4 & 2) - 1; - nz += READ( -1, data ); - WRITE( 0, data, /*(uint8_t)*/ nz ); - goto inc_pc_loop; - -// 7. SHIFT, ROTATION COMMANDS - - case 0x5C: // LSR A - c = 0; - case 0x7C:{// ROR A - nz = (c >> 1 & 0x80) | (a >> 1); - c = a << 8; - a = nz; - goto loop; - } - - case 0x1C: // ASL A - c = 0; - case 0x3C:{// ROL A - int temp = c >> 8 & 1; - c = a << 1; - nz = c | temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x0B: // ASL dp - c = 0; - data += dp; - goto rol_mem; - case 0x1B: // ASL dp+X - c = 0; - case 0x3B: // ROL dp+X - data = (uint8_t) (data + x); - case 0x2B: // ROL dp - data += dp; - goto rol_mem; - case 0x0C: // ASL abs - c = 0; - case 0x2C: // ROL abs - data = READ_PC16( pc ); - pc++; - rol_mem: - nz = c >> 8 & 1; - nz |= (c = READ( -1, data ) << 1); - WRITE( 0, data, /*(uint8_t)*/ nz ); - goto inc_pc_loop; - - case 0x4B: // LSR dp - c = 0; - data += dp; - goto ror_mem; - case 0x5B: // LSR dp+X - c = 0; - case 0x7B: // ROR dp+X - data = (uint8_t) (data + x); - case 0x6B: // ROR dp - data += dp; - goto ror_mem; - case 0x4C: // LSR abs - c = 0; - case 0x6C: // ROR abs - data = READ_PC16( pc ); - pc++; - ror_mem: { - int temp = READ( -1, data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - WRITE( 0, data, nz ); - goto inc_pc_loop; - } - - case 0x9F: // XCN - nz = a = (a >> 4) | (uint8_t) (a << 4); - goto loop; - -// 8. 16-BIT TRANSMISION COMMANDS - - case 0xBA: // MOVW YA,dp - a = READ_DP( -2, data ); - nz = (a & 0x7F) | (a >> 1); - y = READ_DP( 0, (uint8_t) (data + 1) ); - nz |= y; - goto inc_pc_loop; - - case 0xDA: // MOVW dp,YA - WRITE_DP( -1, data, a ); - WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); - goto inc_pc_loop; - -// 9. 16-BIT OPERATION COMMANDS - - case 0x3A: // INCW dp - case 0x1A:{// DECW dp - int temp; - // low byte - data += dp; - temp = READ( -3, data ); - temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW - nz = ((temp >> 1) | temp) & 0x7F; - WRITE( -2, data, /*(uint8_t)*/ temp ); - - // high byte - data = (uint8_t) (data + 1) + dp; - temp = (uint8_t) ((temp >> 8) + READ( -1, data )); - nz |= temp; - WRITE( 0, data, temp ); - - goto inc_pc_loop; - } - - case 0x7A: // ADDW YA,dp - case 0x9A:{// SUBW YA,dp - int lo = READ_DP( -2, data ); - int hi = READ_DP( 0, (uint8_t) (data + 1) ); - int result; - int flags; - - if ( opcode == 0x9A ) // SUBW - { - lo = (lo ^ 0xFF) + 1; - hi ^= 0xFF; - } - - lo += a; - result = y + hi + (lo >> 8); - flags = hi ^ y ^ result; - - psw = (psw & ~(v40 | h08)) | - (flags >> 1 & h08) | - ((flags + 0x80) >> 2 & v40); - c = result; - a = (uint8_t) lo; - result = (uint8_t) result; - y = result; - nz = (((lo >> 1) | lo) & 0x7F) | result; - - goto inc_pc_loop; - } - - case 0x5A: { // CMPW YA,dp - int temp = a - READ_DP( -1, data ); - nz = ((temp >> 1) | temp) & 0x7F; - temp = y + (temp >> 8); - temp -= READ_DP( 0, (uint8_t) (data + 1) ); - nz |= temp; - c = ~temp; - nz &= 0xFF; - goto inc_pc_loop; - } - -// 10. MULTIPLICATION & DIVISON COMMANDS - - case 0xCF: { // MUL YA - unsigned temp = y * a; - a = (uint8_t) temp; - nz = ((temp >> 1) | temp) & 0x7F; - y = temp >> 8; - nz |= y; - goto loop; - } - - case 0x9E: // DIV YA,X - { - unsigned ya = y * 0x100 + a; - - psw &= ~(h08 | v40); - - if ( y >= x ) - psw |= v40; - - if ( (y & 15) >= (x & 15) ) - psw |= h08; - - if ( y < x * 2 ) - { - a = ya / x; - y = ya - a * x; - } - else - { - a = 255 - (ya - x * 0x200) / (256 - x); - y = x + (ya - x * 0x200) % (256 - x); - } - - nz = (uint8_t) a; - a = (uint8_t) a; - - goto loop; - } - -// 11. DECIMAL COMPENSATION COMMANDS - - case 0xDF: // DAA - SUSPICIOUS_OPCODE( "DAA" ); - if ( a > 0x99 || c & 0x100 ) - { - a += 0x60; - c = 0x100; - } - - if ( (a & 0x0F) > 9 || psw & h08 ) - a += 0x06; - - nz = a; - a = (uint8_t) a; - goto loop; - - case 0xBE: // DAS - SUSPICIOUS_OPCODE( "DAS" ); - if ( a > 0x99 || !(c & 0x100) ) - { - a -= 0x60; - c = 0; - } - - if ( (a & 0x0F) > 9 || !(psw & h08) ) - a -= 0x06; - - nz = a; - a = (uint8_t) a; - goto loop; - -// 12. BRANCHING COMMANDS - - case 0x2F: // BRA rel - pc += (BOOST::int8_t) data; - goto inc_pc_loop; - - case 0x30: // BMI - BRANCH( (nz & nz_neg_mask) ) - - case 0x10: // BPL - BRANCH( !(nz & nz_neg_mask) ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x70: // BVS - BRANCH( psw & v40 ) - - case 0x50: // BVC - BRANCH( !(psw & v40) ) - - #define CBRANCH( cond )\ - {\ - pc++;\ - if ( cond )\ - goto cbranch_taken_loop;\ - rel_time -= 2;\ - goto inc_pc_loop;\ - } - - case 0x03: // BBS dp.bit,rel - case 0x23: - case 0x43: - case 0x63: - case 0x83: - case 0xA3: - case 0xC3: - case 0xE3: - CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) - - case 0x13: // BBC dp.bit,rel - case 0x33: - case 0x53: - case 0x73: - case 0x93: - case 0xB3: - case 0xD3: - case 0xF3: - CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) - - case 0xDE: // CBNE dp+X,rel - data = (uint8_t) (data + x); - // fall through - case 0x2E:{// CBNE dp,rel - int temp; - // 61% from timer - READ_DP_TIMER( -4, data, temp ); - CBRANCH( temp != a ) - } - - case 0x6E: { // DBNZ dp,rel - unsigned temp = READ_DP( -4, data ) - 1; - WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); - CBRANCH( temp ) - } - - case 0xFE: // DBNZ Y,rel - y = (uint8_t) (y - 1); - BRANCH( y ) - - case 0x1F: // JMP [abs+X] - SET_PC( READ_PC16( pc ) + x ); - // fall through - case 0x5F: // JMP abs - SET_PC( READ_PC16( pc ) ); - goto loop; - -// 13. SUB-ROUTINE CALL RETURN COMMANDS - - case 0x0F:{// BRK - int temp; - int ret_addr = GET_PC(); - SUSPICIOUS_OPCODE( "BRK" ); - SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified - PUSH16( ret_addr ); - GET_PSW( temp ); - psw = (psw | b10) & ~i04; - PUSH( temp ); - goto loop; - } - - case 0x4F:{// PCALL offset - int ret_addr = GET_PC() + 1; - SET_PC( 0xFF00 | data ); - PUSH16( ret_addr ); - goto loop; - } - - case 0x01: // TCALL n - case 0x11: - case 0x21: - case 0x31: - case 0x41: - case 0x51: - case 0x61: - case 0x71: - case 0x81: - case 0x91: - case 0xA1: - case 0xB1: - case 0xC1: - case 0xD1: - case 0xE1: - case 0xF1: { - int ret_addr = GET_PC(); - SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); - PUSH16( ret_addr ); - goto loop; - } - -// 14. STACK OPERATION COMMANDS - - { - int temp; - case 0x7F: // RET1 - temp = *sp; - SET_PC( GET_LE16( sp + 1 ) ); - sp += 3; - goto set_psw; - case 0x8E: // POP PSW - POP( temp ); - set_psw: - SET_PSW( temp ); - goto loop; - } - - case 0x0D: { // PUSH PSW - int temp; - GET_PSW( temp ); - PUSH( temp ); - goto loop; - } - - case 0x2D: // PUSH A - PUSH( a ); - goto loop; - - case 0x4D: // PUSH X - PUSH( x ); - goto loop; - - case 0x6D: // PUSH Y - PUSH( y ); - goto loop; - - case 0xAE: // POP A - POP( a ); - goto loop; - - case 0xCE: // POP X - POP( x ); - goto loop; - - case 0xEE: // POP Y - POP( y ); - goto loop; - -// 15. BIT OPERATION COMMANDS - - case 0x02: // SET1 - case 0x22: - case 0x42: - case 0x62: - case 0x82: - case 0xA2: - case 0xC2: - case 0xE2: - case 0x12: // CLR1 - case 0x32: - case 0x52: - case 0x72: - case 0x92: - case 0xB2: - case 0xD2: - case 0xF2: { - int bit = 1 << (opcode >> 5); - int mask = ~bit; - if ( opcode & 0x10 ) - bit = 0; - data += dp; - WRITE( 0, data, (READ( -1, data ) & mask) | bit ); - goto inc_pc_loop; - } - - case 0x0E: // TSET1 abs - case 0x4E: // TCLR1 abs - data = READ_PC16( pc ); - pc += 2; - { - unsigned temp = READ( -2, data ); - nz = (uint8_t) (a - temp); - temp &= ~a; - if ( opcode == 0x0E ) - temp |= a; - WRITE( 0, data, temp ); - } - goto loop; - - case 0x4A: // AND1 C,mem.bit - c &= MEM_BIT( 0 ); - pc += 2; - goto loop; - - case 0x6A: // AND1 C,/mem.bit - c &= ~MEM_BIT( 0 ); - pc += 2; - goto loop; - - case 0x0A: // OR1 C,mem.bit - c |= MEM_BIT( -1 ); - pc += 2; - goto loop; - - case 0x2A: // OR1 C,/mem.bit - c |= ~MEM_BIT( -1 ); - pc += 2; - goto loop; - - case 0x8A: // EOR1 C,mem.bit - c ^= MEM_BIT( -1 ); - pc += 2; - goto loop; - - case 0xEA: // NOT1 mem.bit - data = READ_PC16( pc ); - pc += 2; - { - unsigned temp = READ( -1, data & 0x1FFF ); - temp ^= 1 << (data >> 13); - WRITE( 0, data & 0x1FFF, temp ); - } - goto loop; - - case 0xCA: // MOV1 mem.bit,C - data = READ_PC16( pc ); - pc += 2; - { - unsigned temp = READ( -2, data & 0x1FFF ); - unsigned bit = data >> 13; - temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); - WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); - } - goto loop; - - case 0xAA: // MOV1 C,mem.bit - c = MEM_BIT( 0 ); - pc += 2; - goto loop; - -// 16. PROGRAM PSW FLAG OPERATION COMMANDS - - case 0x60: // CLRC - c = 0; - goto loop; - - case 0x80: // SETC - c = ~0; - goto loop; - - case 0xED: // NOTC - c ^= 0x100; - goto loop; - - case 0xE0: // CLRV - psw &= ~(v40 | h08); - goto loop; - - case 0x20: // CLRP - dp = 0; - goto loop; - - case 0x40: // SETP - dp = 0x100; - goto loop; - - case 0xA0: // EI - SUSPICIOUS_OPCODE( "EI" ); - psw |= i04; - goto loop; - - case 0xC0: // DI - SUSPICIOUS_OPCODE( "DI" ); - psw &= ~i04; - goto loop; - -// 17. OTHER COMMANDS - - case 0x00: // NOP - goto loop; - - case 0xFF:{// STOP - // handle PC wrap-around - unsigned addr = GET_PC() - 1; - if ( addr >= 0x10000 ) - { - addr &= 0xFFFF; - SET_PC( addr ); - dprintf( "SPC: PC wrapped around\n" ); - goto loop; - } - } - // fall through - case 0xEF: // SLEEP - SUSPICIOUS_OPCODE( "STOP/SLEEP" ); - --pc; - rel_time = 0; - m.cpu_error = "SPC emulation error"; - goto stop; - } // switch - - assert( 0 ); // catch any unhandled instructions -} -out_of_time: - rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode -stop: - - // Uncache registers - if ( GET_PC() >= 0x10000 ) - dprintf( "SPC: PC wrapped around\n" ); - m.cpu_regs.pc = (uint16_t) GET_PC(); - m.cpu_regs.sp = ( uint8_t) GET_SP(); - m.cpu_regs.a = ( uint8_t) a; - m.cpu_regs.x = ( uint8_t) x; - m.cpu_regs.y = ( uint8_t) y; - { - int temp; - GET_PSW( temp ); - m.cpu_regs.psw = (uint8_t) temp; - } -} -SPC_CPU_RUN_FUNC_END diff --git a/Frameworks/GME/gme/Spc_Emu.cpp b/Frameworks/GME/gme/Spc_Emu.cpp index 330e9d758..b99b901f5 100644 --- a/Frameworks/GME/gme/Spc_Emu.cpp +++ b/Frameworks/GME/gme/Spc_Emu.cpp @@ -281,7 +281,7 @@ struct Spc_File : Gme_Info_ blargg_err_t load_( Data_Reader& in ) { int file_size = in.remain(); - if ( file_size < Snes_Spc::spc_min_file_size ) + if ( file_size < 0x10180 ) return blargg_err_file_type; RETURN_ERR( in.read( &header, header.size ) ); RETURN_ERR( check_spc_header( header.tag ) ); @@ -319,7 +319,7 @@ gme_type_t_ const gme_spc_type [1] = {{ "Super Nintendo", 1, &new_spc_emu, &new_ blargg_err_t Spc_Emu::set_sample_rate_( int sample_rate ) { - RETURN_ERR( apu.init() ); + smp.power(); if ( sample_rate != native_sample_rate ) { RETURN_ERR( resampler.resize_buffer( native_sample_rate / 20 * 2 ) ); @@ -331,17 +331,18 @@ blargg_err_t Spc_Emu::set_sample_rate_( int sample_rate ) void Spc_Emu::mute_voices_( int m ) { Music_Emu::mute_voices_( m ); - apu.mute_voices( m ); + for ( int i = 0, j = 1; i < 8; ++i, j <<= 1 ) + smp.dsp.channel_enable( i, !( m & j ) ); } blargg_err_t Spc_Emu::load_mem_( byte const in [], int size ) { assert( offsetof (header_t,unused2 [46]) == header_t::size ); - set_voice_count( Spc_Dsp::voice_count ); - if ( size < Snes_Spc::spc_min_file_size ) + set_voice_count( 8 ); + if ( size < 0x10180 ) return blargg_err_file_type; - static const char* const names [Spc_Dsp::voice_count] = { + static const char* const names [ 8 ] = { "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" }; set_voice_names( names ); @@ -353,7 +354,7 @@ blargg_err_t Spc_Emu::load_mem_( byte const in [], int size ) void Spc_Emu::set_tempo_( double t ) { - apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) ); + /*apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) );*/ } blargg_err_t Spc_Emu::start_track_( int track ) @@ -361,15 +362,46 @@ blargg_err_t Spc_Emu::start_track_( int track ) RETURN_ERR( Music_Emu::start_track_( track ) ); resampler.clear(); filter.clear(); - RETURN_ERR( apu.load_spc( file_begin(), file_size() ) ); + smp.reset(); + const byte * ptr = file_begin(); + + 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, 0x10000 ); + memcpy( smp.sfm_last, ptr + 0xF4, 4 ); + smp.op_buswrite( 0xF1, ptr[ 0xF1 ] ); + smp.op_buswrite( 0xF2, ptr[ 0xF2 ] ); + smp.op_buswrite( 0xFA, ptr[ 0xFA ] ); + smp.op_buswrite( 0xFB, ptr[ 0xFB ] ); + smp.op_buswrite( 0xFC, ptr[ 0xFC ] ); + ptr += 0x10000; + + smp.dsp.spc_dsp.load( ptr ); + + if ( !(smp.dsp.read( SuperFamicom::SPC_DSP::r_flg ) & 0x20) ) + { + int addr = 0x100 * smp.dsp.read( SuperFamicom::SPC_DSP::r_esa ); + int end = addr + 0x800 * (smp.dsp.read( SuperFamicom::SPC_DSP::r_edl ) & 0x0F); + if ( end > 0x10000 ) + end = 0x10000; + memset( &smp.apuram [addr], 0xFF, end - addr ); + } + filter.set_gain( (int) (gain() * Spc_Filter::gain_unit) ); - apu.clear_echo( true ); return blargg_ok; } blargg_err_t Spc_Emu::play_and_filter( int count, sample_t out [] ) { - RETURN_ERR( apu.play( count, out ) ); + smp.render( out, count ); filter.run( out, count ); return blargg_ok; } @@ -386,7 +418,7 @@ blargg_err_t Spc_Emu::skip_( int count ) if ( count > 0 ) { - RETURN_ERR( apu.skip( count ) ); + smp.skip( count ); filter.clear(); } diff --git a/Frameworks/GME/gme/Spc_Emu.h b/Frameworks/GME/gme/Spc_Emu.h index 02be3fc66..c0cf88d8f 100644 --- a/Frameworks/GME/gme/Spc_Emu.h +++ b/Frameworks/GME/gme/Spc_Emu.h @@ -5,7 +5,7 @@ #define SPC_EMU_H #include "Music_Emu.h" -#include "Snes_Spc.h" +#include "higan/smp/smp.hpp" #include "Spc_Filter.h" #if GME_SPC_FAST_RESAMPLER @@ -23,13 +23,13 @@ public: enum { native_sample_rate = 32000 }; // Disables annoying pseudo-surround effect some music uses - void disable_surround( bool disable = true ) { apu.disable_surround( disable ); } + void disable_surround( bool disable = true ) { smp.dsp.disable_surround( disable ); } // Enables gaussian, cubic or sinc interpolation - void interpolation_level( int level = 0 ) { apu.interpolation_level( level ); } + void interpolation_level( int level = 0 ) { /* smp.dsp.interpolation_level( level ); */ } - Snes_Spc const* get_apu() const; - Snes_Spc * get_apu(); + SuperFamicom::SMP const* get_smp() const; + SuperFamicom::SMP * get_smp(); // SPC file header struct header_t @@ -80,14 +80,14 @@ protected: private: Spc_Emu_Resampler resampler; Spc_Filter filter; - Snes_Spc apu; + SuperFamicom::SMP smp; byte const* trailer_() const; int trailer_size_() const; blargg_err_t play_and_filter( int count, sample_t out [] ); }; -inline Snes_Spc const* Spc_Emu::get_apu() const { return &apu; } -inline Snes_Spc * Spc_Emu::get_apu() { return &apu; } +inline SuperFamicom::SMP const* Spc_Emu::get_smp() const { return &smp; } +inline SuperFamicom::SMP * Spc_Emu::get_smp() { return &smp; } #endif diff --git a/Frameworks/GME/gme/Spc_Sfm.cpp b/Frameworks/GME/gme/Spc_Sfm.cpp index 91ce894ac..7f9c0794a 100644 --- a/Frameworks/GME/gme/Spc_Sfm.cpp +++ b/Frameworks/GME/gme/Spc_Sfm.cpp @@ -25,6 +25,8 @@ Sfm_Emu::Sfm_Emu() { set_type( gme_sfm_type ); set_gain( 1.4 ); + set_max_initial_silence( 30 ); + set_silence_lookahead( 30 ); // Some SFMs may have a lot of initialization code } Sfm_Emu::~Sfm_Emu() { } @@ -67,6 +69,8 @@ struct Sfm_File : Gme_Info_ RETURN_ERR( data.resize( file_size ) ); RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) ); RETURN_ERR( check_sfm_header( data.begin() ) ); + if ( file_size < 8 ) + return "SFM file too small"; int metadata_size = get_le32( data.begin() + 4 ); byte temp = data[ 8 + metadata_size ]; data[ 8 + metadata_size ] = '\0'; @@ -100,7 +104,7 @@ gme_type_t_ const gme_sfm_type [1] = {{ "Super Nintendo with log", 1, &new_sfm_e blargg_err_t Sfm_Emu::set_sample_rate_( int sample_rate ) { - RETURN_ERR( apu.init() ); + smp.power(); if ( sample_rate != native_sample_rate ) { RETURN_ERR( resampler.resize_buffer( native_sample_rate / 20 * 2 ) ); @@ -112,16 +116,17 @@ blargg_err_t Sfm_Emu::set_sample_rate_( int sample_rate ) void Sfm_Emu::mute_voices_( int m ) { Music_Emu::mute_voices_( m ); - apu.mute_voices( m ); + for ( int i = 0, j = 1; i < 8; ++i, j <<= 1 ) + smp.dsp.channel_enable( i, !( m & j ) ); } blargg_err_t Sfm_Emu::load_mem_( byte const in [], int size ) { - set_voice_count( Spc_Dsp::voice_count ); + set_voice_count( 8 ); if ( size < Sfm_Emu::sfm_min_file_size ) return blargg_err_file_type; - static const char* const names [Spc_Dsp::voice_count] = { + static const char* const names [ 8 ] = { "DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8" }; set_voice_names( names ); @@ -133,7 +138,7 @@ blargg_err_t Sfm_Emu::load_mem_( byte const in [], int size ) void Sfm_Emu::set_tempo_( double t ) { - apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) ); + /*apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) );*/ } // (n ? n : 256) @@ -159,6 +164,8 @@ blargg_err_t Sfm_Emu::start_track_( int track ) resampler.clear(); filter.clear(); const byte * ptr = file_begin(); + if ( file_size() < Sfm_Emu::sfm_min_file_size ) + return "SFM file too small"; int metadata_size = get_le32(ptr + 4); if ( file_size() < metadata_size + Sfm_Emu::sfm_min_file_size ) return "SFM file too small"; @@ -168,140 +175,127 @@ blargg_err_t Sfm_Emu::start_track_( int track ) metadata.parseDocument(temp); delete [] temp; - apu.init_rom( ipl_rom ); + memcpy( smp.iplrom, ipl_rom, 64 ); - apu.reset(); + smp.reset(); - memcpy( apu.m.ram.ram, ptr + 8 + metadata_size, 65536 ); + memcpy( smp.apuram, ptr + 8 + metadata_size, 65536 ); - memcpy( apu.dsp.m.regs, ptr + 8 + metadata_size + 65536, 128 ); + memcpy( smp.dsp.spc_dsp.m.regs, ptr + 8 + metadata_size + 65536, 128 ); - apu.set_sfm_queue( ptr + 8 + metadata_size + 65536 + 128, ptr + file_size() ); - - byte regs[Snes_Spc::reg_count] = {0}; + smp.set_sfm_queue( ptr + 8 + metadata_size + 65536 + 128, ptr + file_size() ); char * end; const char * value; - regs[Snes_Spc::r_test] = META_ENUM_INT("smp:test"); - regs[Snes_Spc::r_control] |= META_ENUM_INT("smp:iplrom") ? 0x80 : 0; - regs[Snes_Spc::r_dspaddr] = META_ENUM_INT("smp:dspaddr"); + uint32_t test = META_ENUM_INT("smp:test"); + smp.status.clock_speed = (test >> 6) & 3; + smp.status.timer_speed = (test >> 4) & 3; + smp.status.timers_enable = test & 0x08; + smp.status.ram_disable = test & 0x04; + smp.status.ram_writable = test & 0x02; + smp.status.timers_disable = test & 0x01; + + smp.status.iplrom_enable = META_ENUM_INT("smp:iplrom"); + smp.status.dsp_addr = META_ENUM_INT("smp:dspaddr"); value = metadata.enumValue("smp:ram"); if (value) { - regs[Snes_Spc::r_f8] = strtoul(value, &end, 10); + smp.status.ram00f8 = strtoul(value, &end, 10); if (*end) { value = end + 1; - regs[Snes_Spc::r_f9] = strtoul(value, &end, 10); + smp.status.ram00f9 = strtoul(value, &end, 10); } } char temp_path[256]; for (int i = 0; i < 3; ++i) { + SuperFamicom::SMP::Timer<192> &t = (i == 0 ? smp.timer0 : (i == 1 ? smp.timer1 : *(SuperFamicom::SMP::Timer<192>*)&smp.timer2)); sprintf(temp_path, "smp:timer[%u]:", i); size_t length = strlen(temp_path); strcpy(temp_path + length, "enable"); value = metadata.enumValue(temp_path); if (value) { - regs[Snes_Spc::r_control] |= strtoul(value, &end, 10) ? 1 << i : 0; + t.enable = !!strtoul(value, &end, 10); } strcpy(temp_path + length, "target"); value = metadata.enumValue(temp_path); if (value) { - regs[Snes_Spc::r_t0target + i] = strtoul(value, &end, 10); + t.target = strtoul(value, &end, 10); } strcpy(temp_path + length, "stage"); value = metadata.enumValue(temp_path); if (value) { - for (int j = 0; j < 3; ++j) - { - if (value) value = strchr(value, ','); - if (value) ++value; - } - if (value) - { - regs[Snes_Spc::r_t0out + i] = strtoul(value, &end, 10); - } + t.stage0_ticks = strtoul(value, &end, 10); + if (*end != ',') break; + value = end + 1; + t.stage1_ticks = strtoul(value, &end, 10); + if (*end != ',') break; + value = end + 1; + t.stage2_ticks = strtoul(value, &end, 10); + if (*end != ',') break; + value = end + 1; + t.stage3_ticks = strtoul(value, &end, 10); } - } - - apu.load_regs( regs ); - apu.m.rom_enabled = 0; - apu.regs_loaded(); - - for (int i = 0; i < 3; ++i) - { - sprintf(temp_path, "smp:timer[%u]:", i); - size_t length = strlen(temp_path); - strcpy(temp_path + length, "stage"); + strcpy(temp_path + length, "line"); value = metadata.enumValue(temp_path); if (value) { - const char * stage = value; - apu.m.timers[i].next_time = strtoul(stage, &end, 10) + 1; - for (int j = 0; j < 2; ++j) - { - if (stage) stage = strchr(stage, ','); - if (stage) ++stage; - } - if (stage) - { - apu.m.timers[i].divider = strtoul(value, &end, 10); - } + t.current_line = !!strtoul(value, &end, 10); } } - apu.dsp.m.echo_hist_pos = &apu.dsp.m.echo_hist[META_ENUM_INT("dsp:echohistaddr")]; + smp.dsp.spc_dsp.m.echo_hist_pos = &smp.dsp.spc_dsp.m.echo_hist[META_ENUM_INT("dsp:echohistaddr")]; value = metadata.enumValue("dsp:echohistdata"); if (value) { for (int i = 0; i < 8; ++i) { - apu.dsp.m.echo_hist[i][0] = strtoul(value, &end, 10); + smp.dsp.spc_dsp.m.echo_hist[i][0] = strtoul(value, &end, 10); value = strchr(value, ','); if (!value) break; ++value; - apu.dsp.m.echo_hist[i][1] = strtoul(value, &end, 10); + smp.dsp.spc_dsp.m.echo_hist[i][1] = strtoul(value, &end, 10); value = strchr(value, ','); if (!value) break; ++value; } } - apu.dsp.m.phase = META_ENUM_INT("dsp:sample"); - apu.dsp.m.kon = META_ENUM_INT("dsp:kon"); - apu.dsp.m.noise = META_ENUM_INT("dsp:noise"); - apu.dsp.m.counter = META_ENUM_INT("dsp:counter"); - apu.dsp.m.echo_offset = META_ENUM_INT("dsp:echooffset"); - apu.dsp.m.echo_length = META_ENUM_INT("dsp:echolength"); - apu.dsp.m.new_kon = META_ENUM_INT("dsp:koncache"); - apu.dsp.m.endx_buf = META_ENUM_INT("dsp:endx"); - apu.dsp.m.envx_buf = META_ENUM_INT("dsp:envx"); - apu.dsp.m.outx_buf = META_ENUM_INT("dsp:outx"); - apu.dsp.m.t_pmon = META_ENUM_INT("dsp:pmon"); - apu.dsp.m.t_non = META_ENUM_INT("dsp:non"); - apu.dsp.m.t_eon = META_ENUM_INT("dsp:eon"); - apu.dsp.m.t_dir = META_ENUM_INT("dsp:dir"); - apu.dsp.m.t_koff = META_ENUM_INT("dsp:koff"); - apu.dsp.m.t_brr_next_addr = META_ENUM_INT("dsp:brrnext"); - apu.dsp.m.t_adsr0 = META_ENUM_INT("dsp:adsr0"); - apu.dsp.m.t_brr_header = META_ENUM_INT("dsp:brrheader"); - apu.dsp.m.t_brr_byte = META_ENUM_INT("dsp:brrdata"); - apu.dsp.m.t_srcn = META_ENUM_INT("dsp:srcn"); - apu.dsp.m.t_esa = META_ENUM_INT("dsp:esa"); - apu.dsp.m.t_echo_enabled = !META_ENUM_INT("dsp:echodisable"); - apu.dsp.m.t_dir_addr = META_ENUM_INT("dsp:diraddr"); - apu.dsp.m.t_pitch = META_ENUM_INT("dsp:pitch"); - apu.dsp.m.t_output = META_ENUM_INT("dsp:output"); - apu.dsp.m.t_looped = META_ENUM_INT("dsp:looped"); - apu.dsp.m.t_echo_ptr = META_ENUM_INT("dsp:echoaddr"); + smp.dsp.spc_dsp.m.phase = META_ENUM_INT("dsp:sample"); + smp.dsp.spc_dsp.m.kon = META_ENUM_INT("dsp:kon"); + smp.dsp.spc_dsp.m.noise = META_ENUM_INT("dsp:noise"); + smp.dsp.spc_dsp.m.counter = META_ENUM_INT("dsp:counter"); + smp.dsp.spc_dsp.m.echo_offset = META_ENUM_INT("dsp:echooffset"); + smp.dsp.spc_dsp.m.echo_length = META_ENUM_INT("dsp:echolength"); + smp.dsp.spc_dsp.m.new_kon = META_ENUM_INT("dsp:koncache"); + smp.dsp.spc_dsp.m.endx_buf = META_ENUM_INT("dsp:endx"); + smp.dsp.spc_dsp.m.envx_buf = META_ENUM_INT("dsp:envx"); + smp.dsp.spc_dsp.m.outx_buf = META_ENUM_INT("dsp:outx"); + smp.dsp.spc_dsp.m.t_pmon = META_ENUM_INT("dsp:pmon"); + smp.dsp.spc_dsp.m.t_non = META_ENUM_INT("dsp:non"); + smp.dsp.spc_dsp.m.t_eon = META_ENUM_INT("dsp:eon"); + smp.dsp.spc_dsp.m.t_dir = META_ENUM_INT("dsp:dir"); + smp.dsp.spc_dsp.m.t_koff = META_ENUM_INT("dsp:koff"); + smp.dsp.spc_dsp.m.t_brr_next_addr = META_ENUM_INT("dsp:brrnext"); + smp.dsp.spc_dsp.m.t_adsr0 = META_ENUM_INT("dsp:adsr0"); + smp.dsp.spc_dsp.m.t_brr_header = META_ENUM_INT("dsp:brrheader"); + smp.dsp.spc_dsp.m.t_brr_byte = META_ENUM_INT("dsp:brrdata"); + smp.dsp.spc_dsp.m.t_srcn = META_ENUM_INT("dsp:srcn"); + smp.dsp.spc_dsp.m.t_esa = META_ENUM_INT("dsp:esa"); + smp.dsp.spc_dsp.m.t_echo_enabled = !META_ENUM_INT("dsp:echodisable"); + smp.dsp.spc_dsp.m.t_dir_addr = META_ENUM_INT("dsp:diraddr"); + smp.dsp.spc_dsp.m.t_pitch = META_ENUM_INT("dsp:pitch"); + smp.dsp.spc_dsp.m.t_output = META_ENUM_INT("dsp:output"); + smp.dsp.spc_dsp.m.t_looped = META_ENUM_INT("dsp:looped"); + smp.dsp.spc_dsp.m.t_echo_ptr = META_ENUM_INT("dsp:echoaddr"); #define META_ENUM_LEVELS(n, o) \ @@ -316,9 +310,9 @@ blargg_err_t Sfm_Emu::start_track_( int track ) } \ } - META_ENUM_LEVELS("dsp:mainout", apu.dsp.m.t_main_out); - META_ENUM_LEVELS("dsp:echoout", apu.dsp.m.t_echo_out); - META_ENUM_LEVELS("dsp:echoin", apu.dsp.m.t_echo_in); + META_ENUM_LEVELS("dsp:mainout", smp.dsp.spc_dsp.m.t_main_out); + META_ENUM_LEVELS("dsp:echoout", smp.dsp.spc_dsp.m.t_echo_out); + META_ENUM_LEVELS("dsp:echoin", smp.dsp.spc_dsp.m.t_echo_in); #undef META_ENUM_LEVELS @@ -326,7 +320,7 @@ blargg_err_t Sfm_Emu::start_track_( int track ) { sprintf(temp_path, "dsp:voice[%u]:", i); size_t length = strlen(temp_path); - Spc_Dsp::voice_t & voice = apu.dsp.m.voices[i]; + SuperFamicom::SPC_DSP::voice_t & voice = smp.dsp.spc_dsp.m.voices[i]; strcpy(temp_path + length, "brrhistaddr"); value = metadata.enumValue(temp_path); if (value) @@ -337,9 +331,9 @@ blargg_err_t Sfm_Emu::start_track_( int track ) value = metadata.enumValue(temp_path); if (value) { - for (int j = 0; j < Spc_Dsp::brr_buf_size; ++j) + for (int j = 0; j < SuperFamicom::SPC_DSP::brr_buf_size; ++j) { - voice.buf[j] = voice.buf[j + Spc_Dsp::brr_buf_size] = strtoul(value, &end, 10); + voice.buf[j] = voice.buf[j + SuperFamicom::SPC_DSP::brr_buf_size] = strtoul(value, &end, 10); if (!*end) break; value = end + 1; } @@ -353,11 +347,11 @@ blargg_err_t Sfm_Emu::start_track_( int track ) strcpy(temp_path + length, "vbit"); voice.vbit = META_ENUM_INT(temp_path); strcpy(temp_path + length, "vidx"); - voice.regs = &apu.dsp.m.regs[META_ENUM_INT(temp_path)]; + voice.regs = &smp.dsp.spc_dsp.m.regs[META_ENUM_INT(temp_path)]; strcpy(temp_path + length, "kondelay"); voice.kon_delay = META_ENUM_INT(temp_path); strcpy(temp_path + length, "envmode"); - voice.env_mode = (Spc_Dsp::env_mode_t) META_ENUM_INT(temp_path); + voice.env_mode = (SuperFamicom::SPC_DSP::env_mode_t) META_ENUM_INT(temp_path); strcpy(temp_path + length, "env"); voice.env = META_ENUM_INT(temp_path); strcpy(temp_path + length, "envxout"); @@ -367,7 +361,6 @@ blargg_err_t Sfm_Emu::start_track_( int track ) } filter.set_gain( (int) (gain() * Spc_Filter::gain_unit) ); - apu.clear_echo( true ); return blargg_ok; } @@ -375,7 +368,7 @@ blargg_err_t Sfm_Emu::start_track_( int track ) blargg_err_t Sfm_Emu::play_and_filter( int count, sample_t out [] ) { - RETURN_ERR( apu.play( count, out ) ); + smp.render( out, count ); filter.run( out, count ); return blargg_ok; } @@ -392,7 +385,7 @@ blargg_err_t Sfm_Emu::skip_( int count ) if ( count > 0 ) { - RETURN_ERR( apu.skip( count ) ); + smp.skip( count ); filter.clear(); } diff --git a/Frameworks/GME/gme/Spc_Sfm.h b/Frameworks/GME/gme/Spc_Sfm.h index d81318b02..adb870c95 100644 --- a/Frameworks/GME/gme/Spc_Sfm.h +++ b/Frameworks/GME/gme/Spc_Sfm.h @@ -5,7 +5,7 @@ #define SPC_SFM_H #include "Music_Emu.h" -#include "Snes_Spc.h" +#include "higan/smp/smp.hpp" #include "Spc_Filter.h" #include "Bml_Parser.h" @@ -28,12 +28,12 @@ public: enum { native_sample_rate = 32000 }; // Disables annoying pseudo-surround effect some music uses - void disable_surround( bool disable = true ) { apu.disable_surround( disable ); } + void disable_surround( bool disable = true ) { smp.dsp.disable_surround( disable ); } // Enables gaussian, cubic or sinc interpolation - void interpolation_level( int level = 0 ) { apu.interpolation_level( level ); } + void interpolation_level( int level = 0 ) { /*apu.interpolation_level( level );*/ } - const Snes_Spc * get_apu() const; + const SuperFamicom::SMP * get_smp() const; blargg_err_t hash_( Hash_Function& ) const; @@ -57,7 +57,7 @@ protected: private: Spc_Emu_Resampler resampler; Spc_Filter filter; - Snes_Spc apu; + SuperFamicom::SMP smp; Bml_Parser metadata; diff --git a/Frameworks/GME/gme/Spc_Dsp.cpp b/Frameworks/GME/gme/higan/dsp/SPC_DSP.cpp old mode 100644 new mode 100755 similarity index 51% rename from Frameworks/GME/gme/Spc_Dsp.cpp rename to Frameworks/GME/gme/higan/dsp/SPC_DSP.cpp index 5a6f98d88..283f9c5af --- a/Frameworks/GME/gme/Spc_Dsp.cpp +++ b/Frameworks/GME/gme/higan/dsp/SPC_DSP.cpp @@ -1,1419 +1,1044 @@ -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "Spc_Dsp.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -#if INT_MAX < 0x7FFFFFFF - #error "Requires that int type have at least 32 bits" -#endif - -// TODO: add to blargg_endian.h -#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) -#define GET_LE16A( addr ) GET_LE16( addr ) -#define SET_LE16A( addr, data ) SET_LE16( addr, data ) - -static BOOST::uint8_t const initial_regs [Spc_Dsp::register_count] = -{ - 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, - 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, - 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, - 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, - 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, - 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, - 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, - 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF -}; - -// if ( io < -32768 ) io = -32768; -// if ( io > 32767 ) io = 32767; -#define CLAMP16( io )\ -{\ - if ( (int16_t) io != io )\ - io = (io >> 31) ^ 0x7FFF;\ -} - -// Access global DSP register -#define REG(n) m.regs [r_##n] - -// Access voice DSP register -#define VREG(r,n) r [v_##n] - -#define WRITE_SAMPLES( l, r, out ) \ -{\ - out [0] = l;\ - out [1] = r;\ - out += 2;\ - if ( out >= m.out_end )\ - {\ - check( out == m.out_end );\ - check( m.out_end != &m.extra [extra_size] || \ - (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ - out = m.extra;\ - m.out_end = &m.extra [extra_size];\ - }\ -}\ - -void Spc_Dsp::set_output( sample_t* out, int size ) -{ - require( (size & 1) == 0 ); // must be even - if ( !out ) - { - out = m.extra; - size = extra_size; - } - m.out_begin = out; - m.out = out; - m.out_end = out + size; -} - -// Volume registers and efb are signed! Easy to forget int8_t cast. -// Prefixes are to avoid accidental use of locals with same names. - -// Gaussian interpolation - -static short const gauss [512] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, - 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, - 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, - 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, - 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, - 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, - 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, - 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, - 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, - 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, - 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, - 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, - 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, - 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, - 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, - 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, - 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, - 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, - 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, - 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, - 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, -1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, -1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, -1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, -1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, -1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, -1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, -1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, -}; - -static short const cubic [514] = -{ - 0, -4, -8, -12, -16, -20, -23, -27, -30, -34, -37, -41, -44, -47, -50, -53, - -56, -59, -62, -65, -68, -71, -73, -76, -78, -81, -84, -87, -89, -91, -93, -95, - -98,-100,-102,-104,-106,-109,-110,-112,-113,-116,-117,-119,-121,-122,-123,-125, --126,-128,-129,-131,-132,-134,-134,-136,-136,-138,-138,-140,-141,-141,-142,-143, --144,-144,-145,-146,-147,-148,-147,-148,-148,-149,-149,-150,-150,-150,-150,-151, --151,-151,-151,-151,-152,-152,-151,-152,-151,-152,-151,-151,-151,-151,-150,-150, --150,-149,-149,-149,-149,-148,-147,-147,-146,-146,-145,-145,-144,-144,-143,-142, --141,-141,-140,-139,-139,-138,-137,-136,-135,-135,-133,-133,-132,-131,-130,-129, --128,-127,-126,-125,-124,-123,-121,-121,-119,-118,-117,-116,-115,-114,-112,-111, --110,-109,-107,-106,-105,-104,-102,-102,-100, -99, -97, -97, -95, -94, -92, -91, - -90, -88, -87, -86, -85, -84, -82, -81, -79, -78, -76, -76, -74, -73, -71, -70, - -68, -67, -66, -65, -63, -62, -60, -60, -58, -57, -55, -55, -53, -52, -50, -49, - -48, -46, -45, -44, -43, -42, -40, -39, -38, -37, -36, -35, -34, -32, -31, -30, - -29, -28, -27, -26, -25, -24, -23, -22, -21, -20, -19, -19, -17, -16, -15, -14, - -14, -13, -12, -11, -11, -10, -9, -9, -8, -8, -7, -7, -6, -5, -4, -4, - -3, -3, -3, -2, -2, -2, -1, -1, 0, -1, 0, -1, 0, 0, 0, 0, - 0, -2048,2048,2048,2048,2047,2047,2046,2045,2043,2042,2041,2039,2037,2035,2033,2031, -2028,2026,2024,2021,2018,2015,2012,2009,2005,2002,1999,1995,1991,1987,1982,1978, -1974,1969,1965,1960,1955,1951,1946,1940,1934,1929,1924,1918,1912,1906,1900,1895, -1888,1882,1875,1869,1862,1856,1849,1842,1835,1828,1821,1814,1806,1799,1791,1783, -1776,1768,1760,1753,1744,1737,1728,1720,1711,1703,1695,1686,1677,1668,1659,1651, -1641,1633,1623,1614,1605,1596,1587,1577,1567,1559,1549,1539,1529,1520,1510,1499, -1490,1480,1470,1460,1450,1440,1430,1420,1408,1398,1389,1378,1367,1357,1346,1336, -1325,1315,1304,1293,1282,1272,1261,1250,1239,1229,1218,1207,1196,1185,1174,1163, -1152,1141,1130,1119,1108,1097,1086,1075,1063,1052,1042,1030,1019,1008, 997, 986, - 974, 964, 952, 941, 930, 919, 908, 897, 886, 875, 864, 853, 842, 831, 820, 809, - 798, 787, 776, 765, 754, 744, 733, 722, 711, 700, 690, 679, 668, 658, 647, 637, - 626, 616, 605, 595, 584, 574, 564, 554, 543, 534, 524, 514, 503, 494, 483, 473, - 464, 454, 444, 435, 425, 416, 407, 397, 387, 378, 370, 360, 351, 342, 333, 325, - 315, 307, 298, 290, 281, 273, 265, 256, 248, 241, 233, 225, 216, 209, 201, 193, - 186, 178, 171, 164, 157, 150, 143, 137, 129, 123, 117, 110, 103, 97, 91, 85, - 79, 74, 68, 62, 56, 51, 46, 41, 35, 31, 27, 22, 17, 13, 8, 4, - 0 -}; - -static short const sinc [2048] = -{ - 39, -315, 666, 15642, 666, -315, 39, -38, - 38, -302, 613, 15642, 718, -328, 41, -38, - 36, -288, 561, 15641, 772, -342, 42, -38, - 35, -275, 510, 15639, 826, -355, 44, -38, - 33, -263, 459, 15636, 880, -369, 46, -38, - 32, -250, 408, 15632, 935, -383, 47, -38, - 31, -237, 358, 15628, 990, -396, 49, -38, - 29, -224, 309, 15622, 1046, -410, 51, -38, - 28, -212, 259, 15616, 1103, -425, 53, -38, - 27, -200, 211, 15609, 1159, -439, 54, -38, - 25, -188, 163, 15601, 1216, -453, 56, -38, - 24, -175, 115, 15593, 1274, -467, 58, -38, - 23, -164, 68, 15583, 1332, -482, 60, -38, - 22, -152, 22, 15573, 1391, -496, 62, -37, - 21, -140, -24, 15562, 1450, -511, 64, -37, - 19, -128, -70, 15550, 1509, -526, 66, -37, - 18, -117, -115, 15538, 1569, -540, 68, -37, - 17, -106, -159, 15524, 1629, -555, 70, -37, - 16, -94, -203, 15510, 1690, -570, 72, -36, - 15, -83, -247, 15495, 1751, -585, 74, -36, - 14, -72, -289, 15479, 1813, -600, 76, -36, - 13, -62, -332, 15462, 1875, -616, 79, -36, - 12, -51, -374, 15445, 1937, -631, 81, -35, - 11, -40, -415, 15426, 2000, -646, 83, -35, - 11, -30, -456, 15407, 2063, -662, 85, -35, - 10, -20, -496, 15387, 2127, -677, 88, -34, - 9, -9, -536, 15366, 2191, -693, 90, -34, - 8, 1, -576, 15345, 2256, -708, 92, -34, - 7, 10, -614, 15323, 2321, -724, 95, -33, - 7, 20, -653, 15300, 2386, -740, 97, -33, - 6, 30, -690, 15276, 2451, -755, 99, -33, - 5, 39, -728, 15251, 2517, -771, 102, -32, - 5, 49, -764, 15226, 2584, -787, 104, -32, - 4, 58, -801, 15200, 2651, -803, 107, -32, - 3, 67, -836, 15173, 2718, -819, 109, -31, - 3, 76, -871, 15145, 2785, -835, 112, -31, - 2, 85, -906, 15117, 2853, -851, 115, -30, - 2, 93, -940, 15087, 2921, -867, 117, -30, - 1, 102, -974, 15057, 2990, -883, 120, -29, - 1, 110, -1007, 15027, 3059, -899, 122, -29, - 0, 118, -1039, 14995, 3128, -915, 125, -29, - 0, 127, -1071, 14963, 3198, -931, 128, -28, - -1, 135, -1103, 14930, 3268, -948, 131, -28, - -1, 142, -1134, 14896, 3338, -964, 133, -27, - -1, 150, -1164, 14862, 3409, -980, 136, -27, - -2, 158, -1194, 14827, 3480, -996, 139, -26, - -2, 165, -1224, 14791, 3551, -1013, 142, -26, - -3, 172, -1253, 14754, 3622, -1029, 144, -25, - -3, 179, -1281, 14717, 3694, -1045, 147, -25, - -3, 187, -1309, 14679, 3766, -1062, 150, -24, - -3, 193, -1337, 14640, 3839, -1078, 153, -24, - -4, 200, -1363, 14601, 3912, -1094, 156, -23, - -4, 207, -1390, 14561, 3985, -1110, 159, -23, - -4, 213, -1416, 14520, 4058, -1127, 162, -22, - -4, 220, -1441, 14479, 4131, -1143, 165, -22, - -4, 226, -1466, 14437, 4205, -1159, 168, -22, - -5, 232, -1490, 14394, 4279, -1175, 171, -21, - -5, 238, -1514, 14350, 4354, -1192, 174, -21, - -5, 244, -1537, 14306, 4428, -1208, 177, -20, - -5, 249, -1560, 14261, 4503, -1224, 180, -20, - -5, 255, -1583, 14216, 4578, -1240, 183, -19, - -5, 260, -1604, 14169, 4653, -1256, 186, -19, - -5, 265, -1626, 14123, 4729, -1272, 189, -18, - -5, 271, -1647, 14075, 4805, -1288, 192, -18, - -5, 276, -1667, 14027, 4881, -1304, 195, -17, - -6, 280, -1687, 13978, 4957, -1320, 198, -17, - -6, 285, -1706, 13929, 5033, -1336, 201, -16, - -6, 290, -1725, 13879, 5110, -1352, 204, -16, - -6, 294, -1744, 13829, 5186, -1368, 207, -15, - -6, 299, -1762, 13777, 5263, -1383, 210, -15, - -6, 303, -1779, 13726, 5340, -1399, 213, -14, - -6, 307, -1796, 13673, 5418, -1414, 216, -14, - -6, 311, -1813, 13620, 5495, -1430, 219, -13, - -5, 315, -1829, 13567, 5573, -1445, 222, -13, - -5, 319, -1844, 13512, 5651, -1461, 225, -13, - -5, 322, -1859, 13458, 5728, -1476, 229, -12, - -5, 326, -1874, 13402, 5806, -1491, 232, -12, - -5, 329, -1888, 13347, 5885, -1506, 235, -11, - -5, 332, -1902, 13290, 5963, -1521, 238, -11, - -5, 335, -1915, 13233, 6041, -1536, 241, -10, - -5, 338, -1928, 13176, 6120, -1551, 244, -10, - -5, 341, -1940, 13118, 6199, -1566, 247, -10, - -5, 344, -1952, 13059, 6277, -1580, 250, -9, - -5, 347, -1964, 13000, 6356, -1595, 253, -9, - -5, 349, -1975, 12940, 6435, -1609, 256, -8, - -4, 352, -1986, 12880, 6514, -1623, 259, -8, - -4, 354, -1996, 12819, 6594, -1637, 262, -8, - -4, 356, -2005, 12758, 6673, -1651, 265, -7, - -4, 358, -2015, 12696, 6752, -1665, 268, -7, - -4, 360, -2024, 12634, 6831, -1679, 271, -7, - -4, 362, -2032, 12572, 6911, -1693, 274, -6, - -4, 364, -2040, 12509, 6990, -1706, 277, -6, - -4, 366, -2048, 12445, 7070, -1719, 280, -6, - -3, 367, -2055, 12381, 7149, -1732, 283, -5, - -3, 369, -2062, 12316, 7229, -1745, 286, -5, - -3, 370, -2068, 12251, 7308, -1758, 289, -5, - -3, 371, -2074, 12186, 7388, -1771, 291, -4, - -3, 372, -2079, 12120, 7467, -1784, 294, -4, - -3, 373, -2084, 12054, 7547, -1796, 297, -4, - -3, 374, -2089, 11987, 7626, -1808, 300, -4, - -2, 375, -2094, 11920, 7706, -1820, 303, -3, - -2, 376, -2098, 11852, 7785, -1832, 305, -3, - -2, 376, -2101, 11785, 7865, -1844, 308, -3, - -2, 377, -2104, 11716, 7944, -1855, 311, -3, - -2, 377, -2107, 11647, 8024, -1866, 313, -2, - -2, 378, -2110, 11578, 8103, -1877, 316, -2, - -2, 378, -2112, 11509, 8182, -1888, 318, -2, - -1, 378, -2113, 11439, 8262, -1899, 321, -2, - -1, 378, -2115, 11369, 8341, -1909, 323, -2, - -1, 378, -2116, 11298, 8420, -1920, 326, -2, - -1, 378, -2116, 11227, 8499, -1930, 328, -1, - -1, 378, -2116, 11156, 8578, -1940, 331, -1, - -1, 378, -2116, 11084, 8656, -1949, 333, -1, - -1, 377, -2116, 11012, 8735, -1959, 335, -1, - -1, 377, -2115, 10940, 8814, -1968, 337, -1, - -1, 377, -2114, 10867, 8892, -1977, 340, -1, - -1, 376, -2112, 10795, 8971, -1985, 342, -1, - 0, 375, -2111, 10721, 9049, -1994, 344, -1, - 0, 375, -2108, 10648, 9127, -2002, 346, 0, - 0, 374, -2106, 10574, 9205, -2010, 348, 0, - 0, 373, -2103, 10500, 9283, -2018, 350, 0, - 0, 372, -2100, 10426, 9360, -2025, 352, 0, - 0, 371, -2097, 10351, 9438, -2032, 354, 0, - 0, 370, -2093, 10276, 9515, -2039, 355, 0, - 0, 369, -2089, 10201, 9592, -2046, 357, 0, - 0, 367, -2084, 10126, 9669, -2052, 359, 0, - 0, 366, -2080, 10050, 9745, -2058, 360, 0, - 0, 365, -2075, 9974, 9822, -2064, 362, 0, - 0, 363, -2070, 9898, 9898, -2070, 363, 0, - 0, 362, -2064, 9822, 9974, -2075, 365, 0, - 0, 360, -2058, 9745, 10050, -2080, 366, 0, - 0, 359, -2052, 9669, 10126, -2084, 367, 0, - 0, 357, -2046, 9592, 10201, -2089, 369, 0, - 0, 355, -2039, 9515, 10276, -2093, 370, 0, - 0, 354, -2032, 9438, 10351, -2097, 371, 0, - 0, 352, -2025, 9360, 10426, -2100, 372, 0, - 0, 350, -2018, 9283, 10500, -2103, 373, 0, - 0, 348, -2010, 9205, 10574, -2106, 374, 0, - 0, 346, -2002, 9127, 10648, -2108, 375, 0, - -1, 344, -1994, 9049, 10721, -2111, 375, 0, - -1, 342, -1985, 8971, 10795, -2112, 376, -1, - -1, 340, -1977, 8892, 10867, -2114, 377, -1, - -1, 337, -1968, 8814, 10940, -2115, 377, -1, - -1, 335, -1959, 8735, 11012, -2116, 377, -1, - -1, 333, -1949, 8656, 11084, -2116, 378, -1, - -1, 331, -1940, 8578, 11156, -2116, 378, -1, - -1, 328, -1930, 8499, 11227, -2116, 378, -1, - -2, 326, -1920, 8420, 11298, -2116, 378, -1, - -2, 323, -1909, 8341, 11369, -2115, 378, -1, - -2, 321, -1899, 8262, 11439, -2113, 378, -1, - -2, 318, -1888, 8182, 11509, -2112, 378, -2, - -2, 316, -1877, 8103, 11578, -2110, 378, -2, - -2, 313, -1866, 8024, 11647, -2107, 377, -2, - -3, 311, -1855, 7944, 11716, -2104, 377, -2, - -3, 308, -1844, 7865, 11785, -2101, 376, -2, - -3, 305, -1832, 7785, 11852, -2098, 376, -2, - -3, 303, -1820, 7706, 11920, -2094, 375, -2, - -4, 300, -1808, 7626, 11987, -2089, 374, -3, - -4, 297, -1796, 7547, 12054, -2084, 373, -3, - -4, 294, -1784, 7467, 12120, -2079, 372, -3, - -4, 291, -1771, 7388, 12186, -2074, 371, -3, - -5, 289, -1758, 7308, 12251, -2068, 370, -3, - -5, 286, -1745, 7229, 12316, -2062, 369, -3, - -5, 283, -1732, 7149, 12381, -2055, 367, -3, - -6, 280, -1719, 7070, 12445, -2048, 366, -4, - -6, 277, -1706, 6990, 12509, -2040, 364, -4, - -6, 274, -1693, 6911, 12572, -2032, 362, -4, - -7, 271, -1679, 6831, 12634, -2024, 360, -4, - -7, 268, -1665, 6752, 12696, -2015, 358, -4, - -7, 265, -1651, 6673, 12758, -2005, 356, -4, - -8, 262, -1637, 6594, 12819, -1996, 354, -4, - -8, 259, -1623, 6514, 12880, -1986, 352, -4, - -8, 256, -1609, 6435, 12940, -1975, 349, -5, - -9, 253, -1595, 6356, 13000, -1964, 347, -5, - -9, 250, -1580, 6277, 13059, -1952, 344, -5, - -10, 247, -1566, 6199, 13118, -1940, 341, -5, - -10, 244, -1551, 6120, 13176, -1928, 338, -5, - -10, 241, -1536, 6041, 13233, -1915, 335, -5, - -11, 238, -1521, 5963, 13290, -1902, 332, -5, - -11, 235, -1506, 5885, 13347, -1888, 329, -5, - -12, 232, -1491, 5806, 13402, -1874, 326, -5, - -12, 229, -1476, 5728, 13458, -1859, 322, -5, - -13, 225, -1461, 5651, 13512, -1844, 319, -5, - -13, 222, -1445, 5573, 13567, -1829, 315, -5, - -13, 219, -1430, 5495, 13620, -1813, 311, -6, - -14, 216, -1414, 5418, 13673, -1796, 307, -6, - -14, 213, -1399, 5340, 13726, -1779, 303, -6, - -15, 210, -1383, 5263, 13777, -1762, 299, -6, - -15, 207, -1368, 5186, 13829, -1744, 294, -6, - -16, 204, -1352, 5110, 13879, -1725, 290, -6, - -16, 201, -1336, 5033, 13929, -1706, 285, -6, - -17, 198, -1320, 4957, 13978, -1687, 280, -6, - -17, 195, -1304, 4881, 14027, -1667, 276, -5, - -18, 192, -1288, 4805, 14075, -1647, 271, -5, - -18, 189, -1272, 4729, 14123, -1626, 265, -5, - -19, 186, -1256, 4653, 14169, -1604, 260, -5, - -19, 183, -1240, 4578, 14216, -1583, 255, -5, - -20, 180, -1224, 4503, 14261, -1560, 249, -5, - -20, 177, -1208, 4428, 14306, -1537, 244, -5, - -21, 174, -1192, 4354, 14350, -1514, 238, -5, - -21, 171, -1175, 4279, 14394, -1490, 232, -5, - -22, 168, -1159, 4205, 14437, -1466, 226, -4, - -22, 165, -1143, 4131, 14479, -1441, 220, -4, - -22, 162, -1127, 4058, 14520, -1416, 213, -4, - -23, 159, -1110, 3985, 14561, -1390, 207, -4, - -23, 156, -1094, 3912, 14601, -1363, 200, -4, - -24, 153, -1078, 3839, 14640, -1337, 193, -3, - -24, 150, -1062, 3766, 14679, -1309, 187, -3, - -25, 147, -1045, 3694, 14717, -1281, 179, -3, - -25, 144, -1029, 3622, 14754, -1253, 172, -3, - -26, 142, -1013, 3551, 14791, -1224, 165, -2, - -26, 139, -996, 3480, 14827, -1194, 158, -2, - -27, 136, -980, 3409, 14862, -1164, 150, -1, - -27, 133, -964, 3338, 14896, -1134, 142, -1, - -28, 131, -948, 3268, 14930, -1103, 135, -1, - -28, 128, -931, 3198, 14963, -1071, 127, 0, - -29, 125, -915, 3128, 14995, -1039, 118, 0, - -29, 122, -899, 3059, 15027, -1007, 110, 1, - -29, 120, -883, 2990, 15057, -974, 102, 1, - -30, 117, -867, 2921, 15087, -940, 93, 2, - -30, 115, -851, 2853, 15117, -906, 85, 2, - -31, 112, -835, 2785, 15145, -871, 76, 3, - -31, 109, -819, 2718, 15173, -836, 67, 3, - -32, 107, -803, 2651, 15200, -801, 58, 4, - -32, 104, -787, 2584, 15226, -764, 49, 5, - -32, 102, -771, 2517, 15251, -728, 39, 5, - -33, 99, -755, 2451, 15276, -690, 30, 6, - -33, 97, -740, 2386, 15300, -653, 20, 7, - -33, 95, -724, 2321, 15323, -614, 10, 7, - -34, 92, -708, 2256, 15345, -576, 1, 8, - -34, 90, -693, 2191, 15366, -536, -9, 9, - -34, 88, -677, 2127, 15387, -496, -20, 10, - -35, 85, -662, 2063, 15407, -456, -30, 11, - -35, 83, -646, 2000, 15426, -415, -40, 11, - -35, 81, -631, 1937, 15445, -374, -51, 12, - -36, 79, -616, 1875, 15462, -332, -62, 13, - -36, 76, -600, 1813, 15479, -289, -72, 14, - -36, 74, -585, 1751, 15495, -247, -83, 15, - -36, 72, -570, 1690, 15510, -203, -94, 16, - -37, 70, -555, 1629, 15524, -159, -106, 17, - -37, 68, -540, 1569, 15538, -115, -117, 18, - -37, 66, -526, 1509, 15550, -70, -128, 19, - -37, 64, -511, 1450, 15562, -24, -140, 21, - -37, 62, -496, 1391, 15573, 22, -152, 22, - -38, 60, -482, 1332, 15583, 68, -164, 23, - -38, 58, -467, 1274, 15593, 115, -175, 24, - -38, 56, -453, 1216, 15601, 163, -188, 25, - -38, 54, -439, 1159, 15609, 211, -200, 27, - -38, 53, -425, 1103, 15616, 259, -212, 28, - -38, 51, -410, 1046, 15622, 309, -224, 29, - -38, 49, -396, 990, 15628, 358, -237, 31, - -38, 47, -383, 935, 15632, 408, -250, 32, - -38, 46, -369, 880, 15636, 459, -263, 33, - -38, 44, -355, 826, 15639, 510, -275, 35, - -38, 42, -342, 772, 15641, 561, -288, 36, - -38, 41, -328, 718, 15642, 613, -302, 38, -}; - -inline int Spc_Dsp::interpolate( voice_t const* v ) -{ - // Make pointers into gaussian based on fractional position between samples - int offset = v->interp_pos >> 4 & 0xFF; - short const* fwd = gauss + 255 - offset; - short const* rev = gauss + offset; // mirror left half of gaussian - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = (fwd [ 0] * in [0]) >> 11; - out += (fwd [256] * in [1]) >> 11; - out += (rev [256] * in [2]) >> 11; - out = (int16_t) out; - out += (rev [ 0] * in [3]) >> 11; - - CLAMP16( out ); - out &= ~1; - return out; -} - -inline int Spc_Dsp::interpolate_cubic( voice_t const* v ) -{ - // Make pointers into cubic based on fractional position between samples - int offset = v->interp_pos >> 4 & 0xFF; - short const* fwd = cubic + offset; - short const* rev = cubic + 256 - offset; // mirror left half of cubic - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = fwd [ 0] * in [0]; - out += fwd [257] * in [1]; - out += rev [257] * in [2]; - out += rev [ 0] * in [3]; - out >>= 11; - - CLAMP16( out ); - out &= ~1; - return out; -} - -inline int Spc_Dsp::interpolate_sinc( voice_t const* v ) -{ - // Make pointers into cubic based on fractional position between samples - int offset = v->interp_pos & 0xFF0; - short const* filt = (short const*) (((char const*)sinc) + offset); - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = filt [0] * in [0]; - out += filt [1] * in [1]; - out += filt [2] * in [2]; - out += filt [3] * in [3]; - out += filt [4] * in [4]; - out += filt [5] * in [5]; - out += filt [6] * in [6]; - out += filt [7] * in [7]; - out >>= 14; - - CLAMP16( out ); - out &= ~1; - return out; -} - -inline int Spc_Dsp::interpolate_linear( voice_t const* v ) -{ - int fract = v->interp_pos & 0xFFF; - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = (0x1000 - fract) * in [0]; - out += fract * in [1]; - out >>= 12; - - // no need to clamp - out &= ~1; - return out; -} - -inline int Spc_Dsp::interpolate_nearest( voice_t const* v ) -{ - return v->buf [(v->interp_pos >> 12) + v->buf_pos] & ~1; -} - -//// Counters - -int const simple_counter_range = 2048 * 5 * 3; // 30720 - -static unsigned const counter_rates [32] = -{ - simple_counter_range + 1, // never fires - 2048, 1536, - 1280, 1024, 768, - 640, 512, 384, - 320, 256, 192, - 160, 128, 96, - 80, 64, 48, - 40, 32, 24, - 20, 16, 12, - 10, 8, 6, - 5, 4, 3, - 2, - 1 -}; - -static unsigned const counter_offsets [32] = -{ - 1, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 0, - 0 -}; - -inline void Spc_Dsp::init_counter() -{ - m.counter = 0; -} - -inline void Spc_Dsp::run_counters() -{ - if ( --m.counter < 0 ) - m.counter = simple_counter_range - 1; -} - -inline unsigned Spc_Dsp::read_counter( int rate ) -{ - return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; -} - - -//// Envelope - -inline void Spc_Dsp::run_envelope( voice_t* const v ) -{ - int env = v->env; - if ( v->env_mode == env_release ) // 60% - { - if ( (env -= 0x8) < 0 ) - env = 0; - v->env = env; - } - else - { - int rate; - int env_data = VREG(v->regs,adsr1); - if ( m.t_adsr0 & 0x80 ) // 99% ADSR - { - if ( v->env_mode >= env_decay ) // 99% - { - env--; - env -= env >> 8; - rate = env_data & 0x1F; - if ( v->env_mode == env_decay ) // 1% - rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; - } - else // env_attack - { - rate = (m.t_adsr0 & 0x0F) * 2 + 1; - env += rate < 31 ? 0x20 : 0x400; - } - } - else // GAIN - { - int mode; - env_data = VREG(v->regs,gain); - mode = env_data >> 5; - if ( mode < 4 ) // direct - { - env = env_data * 0x10; - rate = 31; - } - else - { - rate = env_data & 0x1F; - if ( mode == 4 ) // 4: linear decrease - { - env -= 0x20; - } - else if ( mode < 6 ) // 5: exponential decrease - { - env--; - env -= env >> 8; - } - else // 6,7: linear increase - { - env += 0x20; - if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) - env += 0x8 - 0x20; // 7: two-slope linear increase - } - } - } - - // Sustain level - if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) - v->env_mode = env_sustain; - - v->hidden_env = env; - - // unsigned cast because linear decrease going negative also triggers this - if ( (unsigned) env > 0x7FF ) - { - env = (env < 0 ? 0 : 0x7FF); - if ( v->env_mode == env_attack ) - v->env_mode = env_decay; - } - - if ( !read_counter( rate ) ) - v->env = env; // nothing else is controlled by the counter - } -} - - -//// BRR Decoding - -inline void Spc_Dsp::decode_brr( voice_t* v ) -{ - // Arrange the four input nybbles in 0xABCD order for easy decoding - int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; - - int const header = m.t_brr_header; - - // Write to next four samples in circular buffer - int* pos = &v->buf [v->buf_pos]; - int* end; - if ( (v->buf_pos += 4) >= brr_buf_size ) - v->buf_pos = 0; - - // Decode four samples - for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) - { - // Extract nybble and sign-extend - int s = (int16_t) nybbles >> 12; - - // Shift sample based on header - int const shift = header >> 4; - s = (s << shift) >> 1; - if ( shift >= 0xD ) // handle invalid range - s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) - - // Apply IIR filter (8 is the most commonly used) - int const filter = header & 0x0C; - int const p1 = pos [brr_buf_size - 1]; - int const p2 = pos [brr_buf_size - 2] >> 1; - if ( filter >= 8 ) - { - s += p1; - s -= p2; - if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 - { - s += p2 >> 4; - s += (p1 * -3) >> 6; - } - else // s += p1 * 0.8984375 - p2 * 0.40625 - { - s += (p1 * -13) >> 7; - s += (p2 * 3) >> 4; - } - } - else if ( filter ) // s += p1 * 0.46875 - { - s += p1 >> 1; - s += (-p1) >> 5; - } - - // Adjust and write sample - CLAMP16( s ); - s = (int16_t) (s * 2); - pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around - } -} - - -//// Misc - -#define MISC_CLOCK( n ) inline void Spc_Dsp::misc_##n() - -MISC_CLOCK( 27 ) -{ - m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON -} -MISC_CLOCK( 28 ) -{ - m.t_non = REG(non); - m.t_eon = REG(eon); - m.t_dir = REG(dir); -} -MISC_CLOCK( 29 ) -{ - if ( (m.every_other_sample ^= 1) != 0 ) - m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read -} -MISC_CLOCK( 30 ) -{ - if ( m.every_other_sample ) - { - m.kon = m.new_kon; - m.t_koff = REG(koff) | m.mute_mask; - } - - run_counters(); - - // Noise - if ( !read_counter( REG(flg) & 0x1F ) ) - { - int feedback = (m.noise << 13) ^ (m.noise << 14); - m.noise = (feedback & 0x4000) ^ (m.noise >> 1); - } -} - - -//// Voices - -#define VOICE_CLOCK( n ) void Spc_Dsp::voice_##n( voice_t* const v ) - -inline VOICE_CLOCK( V1 ) -{ - m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; - m.t_srcn = VREG(v->regs,srcn); -} -inline VOICE_CLOCK( V2 ) -{ - // Read sample pointer (ignored if not needed) - uint8_t const* entry = &m.ram [m.t_dir_addr]; - if ( !v->kon_delay ) - entry += 2; - m.t_brr_next_addr = GET_LE16A( entry ); - - m.t_adsr0 = VREG(v->regs,adsr0); - - // Read pitch, spread over two clocks - m.t_pitch = VREG(v->regs,pitchl); -} -inline VOICE_CLOCK( V3a ) -{ - m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; -} -inline VOICE_CLOCK( V3b ) -{ - // Read BRR header and byte - m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; - m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking -} -VOICE_CLOCK( V3c ) -{ - // Pitch modulation using previous voice's output - if ( m.t_pmon & v->vbit ) - m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; - - if ( v->kon_delay ) - { - // Get ready to start BRR decoding on next sample - if ( v->kon_delay == 5 ) - { - v->brr_addr = m.t_brr_next_addr; - v->brr_offset = 1; - v->buf_pos = 0; - m.t_brr_header = 0; // header is ignored on this sample - m.kon_check = true; - } - - // Envelope is never run during KON - v->env = 0; - v->hidden_env = 0; - - // Disable BRR decoding until last three samples - v->interp_pos = 0; - if ( --v->kon_delay & 3 ) - v->interp_pos = 0x4000; - - // Pitch is never added during KON - m.t_pitch = 0; - } - - // Gaussian interpolation - { - int output; - - switch ( m.interpolation_level ) - { - case 0: - default: - output = interpolate( v ); - break; - - case 1: - output = interpolate_cubic( v ); - break; - - case 2: - output = interpolate_sinc( v ); - break; - - case -1: - output = interpolate_linear( v ); - break; - - case -2: - output = interpolate_nearest( v ); - break; - } - - // Noise - if ( m.t_non & v->vbit ) - output = (int16_t) (m.noise * 2); - - // Apply envelope - m.t_output = (output * v->env) >> 11 & ~1; - v->t_envx_out = (uint8_t) (v->env >> 4); - } - - // Immediate silence due to end of sample or soft reset - if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) - { - v->env_mode = env_release; - v->env = 0; - } - - if ( m.every_other_sample ) - { - // KOFF - if ( m.t_koff & v->vbit ) - v->env_mode = env_release; - - // KON - if ( m.kon & v->vbit ) - { - v->kon_delay = 5; - v->env_mode = env_attack; - } - } - - // Run envelope for next sample - if ( !v->kon_delay ) - run_envelope( v ); -} -inline void Spc_Dsp::voice_output( voice_t const* v, int ch ) -{ - // Check surround removal - int vol = (int8_t) VREG(v->regs,voll + ch); - int voln = (int8_t) VREG(v->regs,voll + ch ^ 1); - if ( vol * voln < m.surround_threshold ) - vol ^= vol >> 7; - - // Apply left/right volume - int amp = (m.t_output * vol) >> 7; - - int abs_amp = abs( amp ); - if ( abs_amp > m.max_level[v - (const Spc_Dsp::voice_t *)&m.voices][ch] ) - m.max_level[v - (const Spc_Dsp::voice_t *)&m.voices][ch] = abs_amp; - - // Add to output total - m.t_main_out [ch] += amp; - CLAMP16( m.t_main_out [ch] ); - - // Optionally add to echo total - if ( m.t_eon & v->vbit ) - { - m.t_echo_out [ch] += amp; - CLAMP16( m.t_echo_out [ch] ); - } -} -VOICE_CLOCK( V4 ) -{ - // Decode BRR - m.t_looped = 0; - if ( v->interp_pos >= 0x4000 ) - { - decode_brr( v ); - - if ( (v->brr_offset += 2) >= brr_block_size ) - { - // Start decoding next BRR block - assert( v->brr_offset == brr_block_size ); - v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; - if ( m.t_brr_header & 1 ) - { - v->brr_addr = m.t_brr_next_addr; - m.t_looped = v->vbit; - } - v->brr_offset = 1; - } - } - - // Apply pitch - v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; - - // Keep from getting too far ahead (when using pitch modulation) - if ( v->interp_pos > 0x7FFF ) - v->interp_pos = 0x7FFF; - - // Output left - voice_output( v, 0 ); -} -inline VOICE_CLOCK( V5 ) -{ - // Output right - voice_output( v, 1 ); - - // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier - int endx_buf = REG(endx) | m.t_looped; - - // Clear bit in ENDX if KON just began - if ( v->kon_delay == 5 ) - endx_buf &= ~v->vbit; - m.endx_buf = (uint8_t) endx_buf; -} -inline VOICE_CLOCK( V6 ) -{ - (void) v; // avoid compiler warning about unused v - m.outx_buf = (uint8_t) (m.t_output >> 8); -} -inline VOICE_CLOCK( V7 ) -{ - // Update ENDX - REG(endx) = m.endx_buf; - - m.envx_buf = v->t_envx_out; -} -inline VOICE_CLOCK( V8 ) -{ - // Update OUTX - VREG(v->regs,outx) = m.outx_buf; -} -inline VOICE_CLOCK( V9 ) -{ - // Update ENVX - VREG(v->regs,envx) = m.envx_buf; -} - -// Most voices do all these in one clock, so make a handy composite -inline VOICE_CLOCK( V3 ) -{ - voice_V3a( v ); - voice_V3b( v ); - voice_V3c( v ); -} - -// Common combinations of voice steps on different voices. This greatly reduces -// code size and allows everything to be inlined in these functions. -VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } -VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } -VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } - - -//// Echo - -// Current echo buffer pointer for left/right channel -#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) - -// Sample in echo history buffer, where 0 is the oldest -#define ECHO_FIR( i ) (m.echo_hist_pos [i]) - -// Calculate FIR point for left/right channel -#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) - -#define ECHO_CLOCK( n ) inline void Spc_Dsp::echo_##n() - -inline void Spc_Dsp::echo_read( int ch ) -{ - int s = GET_LE16SA( ECHO_PTR( ch ) ); - // second copy simplifies wrap-around handling - ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; -} - -ECHO_CLOCK( 22 ) -{ - // History - if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) - m.echo_hist_pos = m.echo_hist; - - m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; - echo_read( 0 ); - - // FIR (using l and r temporaries below helps compiler optimize) - int l = CALC_FIR( 0, 0 ); - int r = CALC_FIR( 0, 1 ); - - m.t_echo_in [0] = l; - m.t_echo_in [1] = r; -} -ECHO_CLOCK( 23 ) -{ - int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); - int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); - - m.t_echo_in [0] += l; - m.t_echo_in [1] += r; - - echo_read( 1 ); -} -ECHO_CLOCK( 24 ) -{ - int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); - int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); - - m.t_echo_in [0] += l; - m.t_echo_in [1] += r; -} -ECHO_CLOCK( 25 ) -{ - int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); - int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); - - l = (int16_t) l; - r = (int16_t) r; - - l += (int16_t) CALC_FIR( 7, 0 ); - r += (int16_t) CALC_FIR( 7, 1 ); - - CLAMP16( l ); - CLAMP16( r ); - - m.t_echo_in [0] = l & ~1; - m.t_echo_in [1] = r & ~1; -} -inline int Spc_Dsp::echo_output( int ch ) -{ - // Check surround removal - int vol = (int8_t) REG(mvoll + ch * 0x10); - int voln = (int8_t) REG(mvoll + ch * 0x10 ^ 0x10); - if ( vol * voln < m.surround_threshold ) - vol ^= vol >> 7; - - int out = (int16_t) ((m.t_main_out [ch] * vol) >> 7) + - (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); - CLAMP16( out ); - return out; -} -ECHO_CLOCK( 26 ) -{ - // Left output volumes - // (save sample for next clock so we can output both together) - m.t_main_out [0] = echo_output( 0 ); - - // Echo feedback - int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); - int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); - - CLAMP16( l ); - CLAMP16( r ); - - m.t_echo_out [0] = l & ~1; - m.t_echo_out [1] = r & ~1; -} -ECHO_CLOCK( 27 ) -{ - // Output - int l = m.t_main_out [0]; - int r = echo_output( 1 ); - m.t_main_out [0] = 0; - m.t_main_out [1] = 0; - - // TODO: global muting isn't this simple (turns DAC on and off - // or something, causing small ~37-sample pulse when first muted) - if ( REG(flg) & 0x40 ) - { - l = 0; - r = 0; - } - - // Output sample to DAC - #ifdef SPC_DSP_OUT_HOOK - SPC_DSP_OUT_HOOK( l, r ); - #else - sample_t* out = m.out; - WRITE_SAMPLES( l, r, out ); - m.out = out; - #endif -} -ECHO_CLOCK( 28 ) -{ - m.t_echo_enabled = REG(flg); -} -inline void Spc_Dsp::echo_write( int ch ) -{ - if ( !(m.t_echo_enabled & 0x20) ) - SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); - m.t_echo_out [ch] = 0; -} -ECHO_CLOCK( 29 ) -{ - m.t_esa = REG(esa); - - if ( !m.echo_offset ) - m.echo_length = (REG(edl) & 0x0F) * 0x800; - - m.echo_offset += 4; - if ( m.echo_offset >= m.echo_length ) - m.echo_offset = 0; - - // Write left echo - echo_write( 0 ); - - m.t_echo_enabled = REG(flg); -} -ECHO_CLOCK( 30 ) -{ - // Write right echo - echo_write( 1 ); -} - - -//// Timing - -// Execute clock for a particular voice -#define V( clock, voice ) voice_##clock( &m.voices [voice] ); - -/* The most common sequence of clocks uses composite operations -for efficiency. For example, the following are equivalent to the -individual steps on the right: - -V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) -V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) -V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ - -// Voice 0 1 2 3 4 5 6 7 -#define GEN_DSP_TIMING \ -PHASE( 0) V(V5,0)V(V2,1)\ -PHASE( 1) V(V6,0)V(V3,1)\ -PHASE( 2) V(V7_V4_V1,0)\ -PHASE( 3) V(V8_V5_V2,0)\ -PHASE( 4) V(V9_V6_V3,0)\ -PHASE( 5) V(V7_V4_V1,1)\ -PHASE( 6) V(V8_V5_V2,1)\ -PHASE( 7) V(V9_V6_V3,1)\ -PHASE( 8) V(V7_V4_V1,2)\ -PHASE( 9) V(V8_V5_V2,2)\ -PHASE(10) V(V9_V6_V3,2)\ -PHASE(11) V(V7_V4_V1,3)\ -PHASE(12) V(V8_V5_V2,3)\ -PHASE(13) V(V9_V6_V3,3)\ -PHASE(14) V(V7_V4_V1,4)\ -PHASE(15) V(V8_V5_V2,4)\ -PHASE(16) V(V9_V6_V3,4)\ -PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ -PHASE(18) V(V8_V5_V2,5)\ -PHASE(19) V(V9_V6_V3,5)\ -PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ -PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ -PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ -PHASE(23) V(V7,7) echo_23();\ -PHASE(24) V(V8,7) echo_24();\ -PHASE(25) V(V3b,0) V(V9,7) echo_25();\ -PHASE(26) echo_26();\ -PHASE(27) misc_27(); echo_27();\ -PHASE(28) misc_28(); echo_28();\ -PHASE(29) misc_29(); echo_29();\ -PHASE(30) misc_30();V(V3c,0) echo_30();\ -PHASE(31) V(V4,0) V(V1,2)\ - -#if !SPC_DSP_CUSTOM_RUN - -void Spc_Dsp::run( int clocks_remain ) -{ - require( clocks_remain > 0 ); - - int const phase = m.phase; - m.phase = (phase + clocks_remain) & 31; - switch ( phase ) - { - loop: - - #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: - GEN_DSP_TIMING - #undef PHASE - - if ( --clocks_remain ) - goto loop; - } -} - -#endif - - -//// Setup - -void Spc_Dsp::init( void* ram_64k ) -{ - m.ram = (uint8_t*) ram_64k; - mute_voices( 0 ); - disable_surround( false ); - interpolation_level( 0 ); - set_output( 0, 0 ); - reset(); - - #ifndef NDEBUG - // be sure this sign-extends - assert( (int16_t) 0x8000 == -0x8000 ); - - // be sure right shift preserves sign - assert( (-1 >> 1) == -1 ); - - // check clamp macro - int i; - i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); - i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); - - blargg_verify_byte_order(); - #endif -} - -void Spc_Dsp::soft_reset_common() -{ - require( m.ram ); // init() must have been called already - - m.noise = 0x4000; - m.echo_hist_pos = m.echo_hist; - m.every_other_sample = 1; - m.echo_offset = 0; - m.phase = 0; - - init_counter(); -} - -void Spc_Dsp::soft_reset() -{ - REG(flg) = 0xE0; - soft_reset_common(); -} - -void Spc_Dsp::load( uint8_t const regs [register_count] ) -{ - memcpy( m.regs, regs, sizeof m.regs ); - memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); - - // Internal state - for ( int i = voice_count; --i >= 0; ) - { - voice_t* v = &m.voices [i]; - v->brr_offset = 1; - v->vbit = 1 << i; - v->regs = &m.regs [i * 0x10]; - } - m.new_kon = REG(kon); - m.t_dir = REG(dir); - m.t_esa = REG(esa); - - soft_reset_common(); -} - -void Spc_Dsp::reset() { load( initial_regs ); } - - -//// State save/load - -#if !SPC_NO_COPY_STATE_FUNCS - -void SPC_State_Copier::copy( void* state, size_t size ) -{ - func( buf, state, size ); -} - -int SPC_State_Copier::copy_int( int state, int size ) -{ - BOOST::uint8_t s [2]; - SET_LE16( s, state ); - func( buf, &s, size ); - return GET_LE16( s ); -} - -void SPC_State_Copier::skip( int count ) -{ - if ( count > 0 ) - { - char temp [64]; - memset( temp, 0, sizeof temp ); - do - { - int n = sizeof temp; - if ( n > count ) - n = count; - count -= n; - func( buf, temp, n ); - } - while ( count ); - } -} - -void SPC_State_Copier::extra() -{ - int n = 0; - SPC_State_Copier& copier = *this; - SPC_COPY( uint8_t, n ); - skip( n ); -} - -void Spc_Dsp::copy_state( unsigned char** io, copy_func_t copy ) -{ - SPC_State_Copier copier( io, copy ); - - // DSP registers - copier.copy( m.regs, register_count ); - - // Internal state - - // Voices - int i; - for ( i = 0; i < voice_count; i++ ) - { - voice_t* v = &m.voices [i]; - - // BRR buffer - int i; - for ( i = 0; i < brr_buf_size; i++ ) - { - int s = v->buf [i]; - SPC_COPY( int16_t, s ); - v->buf [i] = v->buf [i + brr_buf_size] = s; - } - - SPC_COPY( uint16_t, v->interp_pos ); - SPC_COPY( uint16_t, v->brr_addr ); - SPC_COPY( uint16_t, v->env ); - SPC_COPY( int16_t, v->hidden_env ); - SPC_COPY( uint8_t, v->buf_pos ); - SPC_COPY( uint8_t, v->brr_offset ); - SPC_COPY( uint8_t, v->kon_delay ); - { - int m = v->env_mode; - SPC_COPY( uint8_t, m ); - v->env_mode = (enum env_mode_t) m; - } - SPC_COPY( uint8_t, v->t_envx_out ); - - copier.extra(); - } - - // Echo history - for ( i = 0; i < echo_hist_size; i++ ) - { - int j; - for ( j = 0; j < 2; j++ ) - { - int s = m.echo_hist_pos [i] [j]; - SPC_COPY( int16_t, s ); - m.echo_hist [i] [j] = s; // write back at offset 0 - } - } - m.echo_hist_pos = m.echo_hist; - memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); - - // Misc - SPC_COPY( uint8_t, m.every_other_sample ); - SPC_COPY( uint8_t, m.kon ); - - SPC_COPY( uint16_t, m.noise ); - SPC_COPY( uint16_t, m.counter ); - SPC_COPY( uint16_t, m.echo_offset ); - SPC_COPY( uint16_t, m.echo_length ); - SPC_COPY( uint8_t, m.phase ); - - SPC_COPY( uint8_t, m.new_kon ); - SPC_COPY( uint8_t, m.endx_buf ); - SPC_COPY( uint8_t, m.envx_buf ); - SPC_COPY( uint8_t, m.outx_buf ); - - SPC_COPY( uint8_t, m.t_pmon ); - SPC_COPY( uint8_t, m.t_non ); - SPC_COPY( uint8_t, m.t_eon ); - SPC_COPY( uint8_t, m.t_dir ); - SPC_COPY( uint8_t, m.t_koff ); - - SPC_COPY( uint16_t, m.t_brr_next_addr ); - SPC_COPY( uint8_t, m.t_adsr0 ); - SPC_COPY( uint8_t, m.t_brr_header ); - SPC_COPY( uint8_t, m.t_brr_byte ); - SPC_COPY( uint8_t, m.t_srcn ); - SPC_COPY( uint8_t, m.t_esa ); - SPC_COPY( uint8_t, m.t_echo_enabled ); - - SPC_COPY( int16_t, m.t_main_out [0] ); - SPC_COPY( int16_t, m.t_main_out [1] ); - SPC_COPY( int16_t, m.t_echo_out [0] ); - SPC_COPY( int16_t, m.t_echo_out [1] ); - SPC_COPY( int16_t, m.t_echo_in [0] ); - SPC_COPY( int16_t, m.t_echo_in [1] ); - - SPC_COPY( uint16_t, m.t_dir_addr ); - SPC_COPY( uint16_t, m.t_pitch ); - SPC_COPY( int16_t, m.t_output ); - SPC_COPY( uint16_t, m.t_echo_ptr ); - SPC_COPY( uint8_t, m.t_looped ); - - copier.extra(); -} -#endif +// snes_spc 0.9.0. http://www.slack.net/~ant/ + +#include "SPC_DSP.h" + +#include "blargg_endian.h" +#include + +/* Copyright (C) 2007 Shay Green. This module is free software; you +can redistribute it and/or modify it under the terms of the GNU Lesser +General Public License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. This +module is distributed in the hope that it will be useful, but WITHOUT ANY +WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more +details. You should have received a copy of the GNU Lesser General Public +License along with this module; if not, write to the Free Software Foundation, +Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ + +#include "blargg_source.h" + +namespace SuperFamicom { + +#ifdef BLARGG_ENABLE_OPTIMIZER + #include BLARGG_ENABLE_OPTIMIZER +#endif + +#if INT_MAX < 0x7FFFFFFF + #error "Requires that int type have at least 32 bits" +#endif + +// TODO: add to blargg_endian.h +#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) +#define GET_LE16A( addr ) GET_LE16( addr ) +#define SET_LE16A( addr, data ) SET_LE16( addr, data ) + +static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = +{ + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0xE0,0x00,0x00,0x00, + 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, + +// 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, +// 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, +// 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, +// 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, +// 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, +// 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, +// 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, +// 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF +}; + +// if ( io < -32768 ) io = -32768; +// if ( io > 32767 ) io = 32767; +#define CLAMP16( io )\ +{\ + if ( (int16_t) io != io )\ + io = (io >> 31) ^ 0x7FFF;\ +} + +// Access global DSP register +#define REG(n) m.regs [r_##n] + +// Access voice DSP register +#define VREG(r,n) r [v_##n] + +#define WRITE_SAMPLES( l, r, out ) \ +{\ + out [0] = l;\ + out [1] = r;\ + out += 2;\ + if ( out >= m.out_end )\ + {\ + check( out == m.out_end );\ + check( m.out_end != &m.extra [extra_size] || \ + (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ + out = m.extra;\ + m.out_end = &m.extra [extra_size];\ + }\ +}\ + +void SPC_DSP::set_output( sample_t* out, int size ) +{ + require( (size & 1) == 0 ); // must be even + if ( !out ) + { + out = m.extra; + size = extra_size; + } + m.out_begin = out; + m.out = out; + m.out_end = out + size; +} + +// Volume registers and efb are signed! Easy to forget int8_t cast. +// Prefixes are to avoid accidental use of locals with same names. + +// Gaussian interpolation + +static short const gauss [512] = +{ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, + 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, + 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, + 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, + 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, + 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, + 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, + 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, + 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, + 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, + 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, + 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, + 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, + 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, + 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, + 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, + 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, + 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, + 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, + 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, + 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, + 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, + 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, + 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, +1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, +1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, +1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, +1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, +1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, +1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, +1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, +}; + +inline int SPC_DSP::interpolate( voice_t const* v ) +{ + // Make pointers into gaussian based on fractional position between samples + int offset = v->interp_pos >> 4 & 0xFF; + short const* fwd = gauss + 255 - offset; + short const* rev = gauss + offset; // mirror left half of gaussian + + int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; + int out; + out = (fwd [ 0] * in [0]) >> 11; + out += (fwd [256] * in [1]) >> 11; + out += (rev [256] * in [2]) >> 11; + out = (int16_t) out; + out += (rev [ 0] * in [3]) >> 11; + + CLAMP16( out ); + out &= ~1; + return out; +} + + +//// Counters + +int const simple_counter_range = 2048 * 5 * 3; // 30720 + +static unsigned const counter_rates [32] = +{ + simple_counter_range + 1, // never fires + 2048, 1536, + 1280, 1024, 768, + 640, 512, 384, + 320, 256, 192, + 160, 128, 96, + 80, 64, 48, + 40, 32, 24, + 20, 16, 12, + 10, 8, 6, + 5, 4, 3, + 2, + 1 +}; + +static unsigned const counter_offsets [32] = +{ + 1, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 536, 0, 1040, + 0, + 0 +}; + +inline void SPC_DSP::init_counter() +{ + m.counter = 0; +} + +inline void SPC_DSP::run_counters() +{ + if ( --m.counter < 0 ) + m.counter = simple_counter_range - 1; +} + +inline unsigned SPC_DSP::read_counter( int rate ) +{ + return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; +} + + +//// Envelope + +inline void SPC_DSP::run_envelope( voice_t* const v ) +{ + int env = v->env; + if ( v->env_mode == env_release ) // 60% + { + if ( (env -= 0x8) < 0 ) + env = 0; + v->env = env; + } + else + { + int rate; + int env_data = VREG(v->regs,adsr1); + if ( m.t_adsr0 & 0x80 ) // 99% ADSR + { + if ( v->env_mode >= env_decay ) // 99% + { + env--; + env -= env >> 8; + rate = env_data & 0x1F; + if ( v->env_mode == env_decay ) // 1% + rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; + } + else // env_attack + { + rate = (m.t_adsr0 & 0x0F) * 2 + 1; + env += rate < 31 ? 0x20 : 0x400; + } + } + else // GAIN + { + int mode; + env_data = VREG(v->regs,gain); + mode = env_data >> 5; + if ( mode < 4 ) // direct + { + env = env_data * 0x10; + rate = 31; + } + else + { + rate = env_data & 0x1F; + if ( mode == 4 ) // 4: linear decrease + { + env -= 0x20; + } + else if ( mode < 6 ) // 5: exponential decrease + { + env--; + env -= env >> 8; + } + else // 6,7: linear increase + { + env += 0x20; + if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) + env += 0x8 - 0x20; // 7: two-slope linear increase + } + } + } + + // Sustain level + if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) + v->env_mode = env_sustain; + + v->hidden_env = env; + + // unsigned cast because linear decrease going negative also triggers this + if ( (unsigned) env > 0x7FF ) + { + env = (env < 0 ? 0 : 0x7FF); + if ( v->env_mode == env_attack ) + v->env_mode = env_decay; + } + + if ( !read_counter( rate ) ) + v->env = env; // nothing else is controlled by the counter + } +} + + +//// BRR Decoding + +inline void SPC_DSP::decode_brr( voice_t* v ) +{ + // Arrange the four input nybbles in 0xABCD order for easy decoding + int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; + + int const header = m.t_brr_header; + + // Write to next four samples in circular buffer + int* pos = &v->buf [v->buf_pos]; + int* end; + if ( (v->buf_pos += 4) >= brr_buf_size ) + v->buf_pos = 0; + + // Decode four samples + for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) + { + // Extract nybble and sign-extend + int s = (int16_t) nybbles >> 12; + + // Shift sample based on header + int const shift = header >> 4; + s = (s << shift) >> 1; + if ( shift >= 0xD ) // handle invalid range + s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) + + // Apply IIR filter (8 is the most commonly used) + int const filter = header & 0x0C; + int const p1 = pos [brr_buf_size - 1]; + int const p2 = pos [brr_buf_size - 2] >> 1; + if ( filter >= 8 ) + { + s += p1; + s -= p2; + if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 + { + s += p2 >> 4; + s += (p1 * -3) >> 6; + } + else // s += p1 * 0.8984375 - p2 * 0.40625 + { + s += (p1 * -13) >> 7; + s += (p2 * 3) >> 4; + } + } + else if ( filter ) // s += p1 * 0.46875 + { + s += p1 >> 1; + s += (-p1) >> 5; + } + + // Adjust and write sample + CLAMP16( s ); + s = (int16_t) (s * 2); + pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around + } +} + + +//// Misc + +#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n() + +MISC_CLOCK( 27 ) +{ + m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON +} +MISC_CLOCK( 28 ) +{ + m.t_non = REG(non); + m.t_eon = REG(eon); + m.t_dir = REG(dir); +} +MISC_CLOCK( 29 ) +{ + if ( (m.every_other_sample ^= 1) != 0 ) + m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read +} +MISC_CLOCK( 30 ) +{ + if ( m.every_other_sample ) + { + m.kon = m.new_kon; + m.t_koff = REG(koff) | m.mute_mask; + } + + run_counters(); + + // Noise + if ( !read_counter( REG(flg) & 0x1F ) ) + { + int feedback = (m.noise << 13) ^ (m.noise << 14); + m.noise = (feedback & 0x4000) ^ (m.noise >> 1); + } +} + + +//// Voices + +#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v ) + +inline VOICE_CLOCK( V1 ) +{ + m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; + m.t_srcn = VREG(v->regs,srcn); +} +inline VOICE_CLOCK( V2 ) +{ + // Read sample pointer (ignored if not needed) + uint8_t const* entry = &m.ram [m.t_dir_addr]; + if ( !v->kon_delay ) + entry += 2; + m.t_brr_next_addr = GET_LE16A( entry ); + + m.t_adsr0 = VREG(v->regs,adsr0); + + // Read pitch, spread over two clocks + m.t_pitch = VREG(v->regs,pitchl); +} +inline VOICE_CLOCK( V3a ) +{ + m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; +} +inline VOICE_CLOCK( V3b ) +{ + // Read BRR header and byte + m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; + m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking +} +VOICE_CLOCK( V3c ) +{ + // Pitch modulation using previous voice's output + if ( m.t_pmon & v->vbit ) + m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; + + if ( v->kon_delay ) + { + // Get ready to start BRR decoding on next sample + if ( v->kon_delay == 5 ) + { + v->brr_addr = m.t_brr_next_addr; + v->brr_offset = 1; + v->buf_pos = 0; + m.t_brr_header = 0; // header is ignored on this sample + m.kon_check = true; + } + + // Envelope is never run during KON + v->env = 0; + v->hidden_env = 0; + + // Disable BRR decoding until last three samples + v->interp_pos = 0; + if ( --v->kon_delay & 3 ) + v->interp_pos = 0x4000; + + // Pitch is never added during KON + m.t_pitch = 0; + } + + // Gaussian interpolation + { + int output = interpolate( v ); + + // Noise + if ( m.t_non & v->vbit ) + output = (int16_t) (m.noise * 2); + + // Apply envelope + m.t_output = (output * v->env) >> 11 & ~1; + v->t_envx_out = (uint8_t) (v->env >> 4); + } + + // Immediate silence due to end of sample or soft reset + if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) + { + v->env_mode = env_release; + v->env = 0; + } + + if ( m.every_other_sample ) + { + // KOFF + if ( m.t_koff & v->vbit ) + v->env_mode = env_release; + + // KON + if ( m.kon & v->vbit ) + { + v->kon_delay = 5; + v->env_mode = env_attack; + } + } + + // Run envelope for next sample + if ( !v->kon_delay ) + run_envelope( v ); +} +inline void SPC_DSP::voice_output( voice_t const* v, int ch ) +{ + // Check surround removal + int vol = (int8_t) VREG(v->regs,voll + ch); + int voln = (int8_t) VREG(v->regs,voll + ch ^ 1); + if ( vol * voln < m.surround_threshold ) + vol ^= vol >> 7; + + // Apply left/right volume + int amp = (m.t_output * vol) >> 7; + + // Add to output total + m.t_main_out [ch] += amp; + CLAMP16( m.t_main_out [ch] ); + + // Optionally add to echo total + if ( m.t_eon & v->vbit ) + { + m.t_echo_out [ch] += amp; + CLAMP16( m.t_echo_out [ch] ); + } +} +VOICE_CLOCK( V4 ) +{ + // Decode BRR + m.t_looped = 0; + if ( v->interp_pos >= 0x4000 ) + { + decode_brr( v ); + + if ( (v->brr_offset += 2) >= brr_block_size ) + { + // Start decoding next BRR block + assert( v->brr_offset == brr_block_size ); + v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; + if ( m.t_brr_header & 1 ) + { + v->brr_addr = m.t_brr_next_addr; + m.t_looped = v->vbit; + } + v->brr_offset = 1; + } + } + + // Apply pitch + v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; + + // Keep from getting too far ahead (when using pitch modulation) + if ( v->interp_pos > 0x7FFF ) + v->interp_pos = 0x7FFF; + + // Output left + voice_output( v, 0 ); +} +inline VOICE_CLOCK( V5 ) +{ + // Output right + voice_output( v, 1 ); + + // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier + int endx_buf = REG(endx) | m.t_looped; + + // Clear bit in ENDX if KON just began + if ( v->kon_delay == 5 ) + endx_buf &= ~v->vbit; + m.endx_buf = (uint8_t) endx_buf; +} +inline VOICE_CLOCK( V6 ) +{ + (void) v; // avoid compiler warning about unused v + m.outx_buf = (uint8_t) (m.t_output >> 8); +} +inline VOICE_CLOCK( V7 ) +{ + // Update ENDX + REG(endx) = m.endx_buf; + + m.envx_buf = v->t_envx_out; +} +inline VOICE_CLOCK( V8 ) +{ + // Update OUTX + VREG(v->regs,outx) = m.outx_buf; +} +inline VOICE_CLOCK( V9 ) +{ + // Update ENVX + VREG(v->regs,envx) = m.envx_buf; +} + +// Most voices do all these in one clock, so make a handy composite +inline VOICE_CLOCK( V3 ) +{ + voice_V3a( v ); + voice_V3b( v ); + voice_V3c( v ); +} + +// Common combinations of voice steps on different voices. This greatly reduces +// code size and allows everything to be inlined in these functions. +VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } +VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } +VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } + + +//// Echo + +// Current echo buffer pointer for left/right channel +#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) + +// Sample in echo history buffer, where 0 is the oldest +#define ECHO_FIR( i ) (m.echo_hist_pos [i]) + +// Calculate FIR point for left/right channel +#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) + +#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n() + +inline void SPC_DSP::echo_read( int ch ) +{ + int s = GET_LE16SA( ECHO_PTR( ch ) ); + // second copy simplifies wrap-around handling + ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; +} + +ECHO_CLOCK( 22 ) +{ + // History + if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) + m.echo_hist_pos = m.echo_hist; + + m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; + echo_read( 0 ); + + // FIR (using l and r temporaries below helps compiler optimize) + int l = CALC_FIR( 0, 0 ); + int r = CALC_FIR( 0, 1 ); + + m.t_echo_in [0] = l; + m.t_echo_in [1] = r; +} +ECHO_CLOCK( 23 ) +{ + int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); + int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; + + echo_read( 1 ); +} +ECHO_CLOCK( 24 ) +{ + int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); + int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); + + m.t_echo_in [0] += l; + m.t_echo_in [1] += r; +} +ECHO_CLOCK( 25 ) +{ + int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); + int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); + + l = (int16_t) l; + r = (int16_t) r; + + l += (int16_t) CALC_FIR( 7, 0 ); + r += (int16_t) CALC_FIR( 7, 1 ); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_in [0] = l & ~1; + m.t_echo_in [1] = r & ~1; +} +inline int SPC_DSP::echo_output( int ch ) +{ + // Check surround removal + int vol = (int8_t) REG(mvoll + ch * 0x10); + int voln = (int8_t) REG(mvoll + ch * 0x10 ^ 0x10); + if ( vol * voln < m.surround_threshold ) + vol ^= vol >> 7; + + int out = (int16_t) ((m.t_main_out [ch] * vol) >> 7) + + (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); + CLAMP16( out ); + return out; +} +ECHO_CLOCK( 26 ) +{ + // Left output volumes + // (save sample for next clock so we can output both together) + m.t_main_out [0] = echo_output( 0 ); + + // Echo feedback + int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); + int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); + + CLAMP16( l ); + CLAMP16( r ); + + m.t_echo_out [0] = l & ~1; + m.t_echo_out [1] = r & ~1; +} +ECHO_CLOCK( 27 ) +{ + // Output + int l = m.t_main_out [0]; + int r = echo_output( 1 ); + m.t_main_out [0] = 0; + m.t_main_out [1] = 0; + + // TODO: global muting isn't this simple (turns DAC on and off + // or something, causing small ~37-sample pulse when first muted) + if ( REG(flg) & 0x40 ) + { + l = 0; + r = 0; + } + + // Output sample to DAC + #ifdef SPC_DSP_OUT_HOOK + SPC_DSP_OUT_HOOK( l, r ); + #else + sample_t* out = m.out; + WRITE_SAMPLES( l, r, out ); + m.out = out; + #endif +} +ECHO_CLOCK( 28 ) +{ + m.t_echo_enabled = REG(flg); +} +inline void SPC_DSP::echo_write( int ch ) +{ + if ( !(m.t_echo_enabled & 0x20) ) + SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); + m.t_echo_out [ch] = 0; +} +ECHO_CLOCK( 29 ) +{ + m.t_esa = REG(esa); + + if ( !m.echo_offset ) + m.echo_length = (REG(edl) & 0x0F) * 0x800; + + m.echo_offset += 4; + if ( m.echo_offset >= m.echo_length ) + m.echo_offset = 0; + + // Write left echo + echo_write( 0 ); + + m.t_echo_enabled = REG(flg); +} +ECHO_CLOCK( 30 ) +{ + // Write right echo + echo_write( 1 ); +} + + +//// Timing + +// Execute clock for a particular voice +#define V( clock, voice ) voice_##clock( &m.voices [voice] ); + +/* The most common sequence of clocks uses composite operations +for efficiency. For example, the following are equivalent to the +individual steps on the right: + +V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) +V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) +V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ + +// Voice 0 1 2 3 4 5 6 7 +#define GEN_DSP_TIMING \ +PHASE( 0) V(V5,0)V(V2,1)\ +PHASE( 1) V(V6,0)V(V3,1)\ +PHASE( 2) V(V7_V4_V1,0)\ +PHASE( 3) V(V8_V5_V2,0)\ +PHASE( 4) V(V9_V6_V3,0)\ +PHASE( 5) V(V7_V4_V1,1)\ +PHASE( 6) V(V8_V5_V2,1)\ +PHASE( 7) V(V9_V6_V3,1)\ +PHASE( 8) V(V7_V4_V1,2)\ +PHASE( 9) V(V8_V5_V2,2)\ +PHASE(10) V(V9_V6_V3,2)\ +PHASE(11) V(V7_V4_V1,3)\ +PHASE(12) V(V8_V5_V2,3)\ +PHASE(13) V(V9_V6_V3,3)\ +PHASE(14) V(V7_V4_V1,4)\ +PHASE(15) V(V8_V5_V2,4)\ +PHASE(16) V(V9_V6_V3,4)\ +PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ +PHASE(18) V(V8_V5_V2,5)\ +PHASE(19) V(V9_V6_V3,5)\ +PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ +PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ +PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ +PHASE(23) V(V7,7) echo_23();\ +PHASE(24) V(V8,7) echo_24();\ +PHASE(25) V(V3b,0) V(V9,7) echo_25();\ +PHASE(26) echo_26();\ +PHASE(27) misc_27(); echo_27();\ +PHASE(28) misc_28(); echo_28();\ +PHASE(29) misc_29(); echo_29();\ +PHASE(30) misc_30();V(V3c,0) echo_30();\ +PHASE(31) V(V4,0) V(V1,2)\ + +#if !SPC_DSP_CUSTOM_RUN + +void SPC_DSP::run( int clocks_remain ) +{ + require( clocks_remain > 0 ); + + int const phase = m.phase; + m.phase = (phase + clocks_remain) & 31; + switch ( phase ) + { + loop: + + #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: + GEN_DSP_TIMING + #undef PHASE + + if ( --clocks_remain ) + goto loop; + } +} + +#endif + + +//// Setup + +void SPC_DSP::init( void* ram_64k ) +{ + m.ram = (uint8_t*) ram_64k; + mute_voices( 0 ); + disable_surround( false ); + set_output( 0, 0 ); + reset(); + + #ifndef NDEBUG + // be sure this sign-extends + assert( (int16_t) 0x8000 == -0x8000 ); + + // be sure right shift preserves sign + assert( (-1 >> 1) == -1 ); + + // check clamp macro + int i; + i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); + i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); + + blargg_verify_byte_order(); + #endif +} + +void SPC_DSP::soft_reset_common() +{ + require( m.ram ); // init() must have been called already + + m.noise = 0x4000; + m.echo_hist_pos = m.echo_hist; + m.every_other_sample = 1; + m.echo_offset = 0; + m.phase = 0; + + init_counter(); +} + +void SPC_DSP::soft_reset() +{ + REG(flg) = 0xE0; + soft_reset_common(); +} + +void SPC_DSP::load( uint8_t const regs [register_count] ) +{ + memcpy( m.regs, regs, sizeof m.regs ); + memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); + + // Internal state + for ( int i = voice_count; --i >= 0; ) + { + voice_t* v = &m.voices [i]; + v->brr_offset = 1; + v->vbit = 1 << i; + v->regs = &m.regs [i * 0x10]; + } + m.new_kon = REG(kon); + m.t_dir = REG(dir); + m.t_esa = REG(esa); + + soft_reset_common(); +} + +void SPC_DSP::reset() { load( initial_regs ); } + + +//// State save/load + +#if !SPC_NO_COPY_STATE_FUNCS + +void SPC_State_Copier::copy( void* state, size_t size ) +{ + func( buf, state, size ); +} + +int SPC_State_Copier::copy_int( int state, int size ) +{ + BOOST::uint8_t s [2]; + SET_LE16( s, state ); + func( buf, &s, size ); + return GET_LE16( s ); +} + +void SPC_State_Copier::skip( int count ) +{ + if ( count > 0 ) + { + char temp [64]; + memset( temp, 0, sizeof temp ); + do + { + int n = sizeof temp; + if ( n > count ) + n = count; + count -= n; + func( buf, temp, n ); + } + while ( count ); + } +} + +void SPC_State_Copier::extra() +{ + int n = 0; + SPC_State_Copier& copier = *this; + SPC_COPY( uint8_t, n ); + skip( n ); +} + +void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy ) +{ + SPC_State_Copier copier( io, copy ); + + // DSP registers + copier.copy( m.regs, register_count ); + + // Internal state + + // Voices + int i; + for ( i = 0; i < voice_count; i++ ) + { + voice_t* v = &m.voices [i]; + + // BRR buffer + int i; + for ( i = 0; i < brr_buf_size; i++ ) + { + int s = v->buf [i]; + SPC_COPY( int16_t, s ); + v->buf [i] = v->buf [i + brr_buf_size] = s; + } + + SPC_COPY( uint16_t, v->interp_pos ); + SPC_COPY( uint16_t, v->brr_addr ); + SPC_COPY( uint16_t, v->env ); + SPC_COPY( int16_t, v->hidden_env ); + SPC_COPY( uint8_t, v->buf_pos ); + SPC_COPY( uint8_t, v->brr_offset ); + SPC_COPY( uint8_t, v->kon_delay ); + { + int m = v->env_mode; + SPC_COPY( uint8_t, m ); + v->env_mode = (enum env_mode_t) m; + } + SPC_COPY( uint8_t, v->t_envx_out ); + + copier.extra(); + } + + // Echo history + for ( i = 0; i < echo_hist_size; i++ ) + { + int j; + for ( j = 0; j < 2; j++ ) + { + int s = m.echo_hist_pos [i] [j]; + SPC_COPY( int16_t, s ); + m.echo_hist [i] [j] = s; // write back at offset 0 + } + } + m.echo_hist_pos = m.echo_hist; + memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); + + // Misc + SPC_COPY( uint8_t, m.every_other_sample ); + SPC_COPY( uint8_t, m.kon ); + + SPC_COPY( uint16_t, m.noise ); + SPC_COPY( uint16_t, m.counter ); + SPC_COPY( uint16_t, m.echo_offset ); + SPC_COPY( uint16_t, m.echo_length ); + SPC_COPY( uint8_t, m.phase ); + + SPC_COPY( uint8_t, m.new_kon ); + SPC_COPY( uint8_t, m.endx_buf ); + SPC_COPY( uint8_t, m.envx_buf ); + SPC_COPY( uint8_t, m.outx_buf ); + + SPC_COPY( uint8_t, m.t_pmon ); + SPC_COPY( uint8_t, m.t_non ); + SPC_COPY( uint8_t, m.t_eon ); + SPC_COPY( uint8_t, m.t_dir ); + SPC_COPY( uint8_t, m.t_koff ); + + SPC_COPY( uint16_t, m.t_brr_next_addr ); + SPC_COPY( uint8_t, m.t_adsr0 ); + SPC_COPY( uint8_t, m.t_brr_header ); + SPC_COPY( uint8_t, m.t_brr_byte ); + SPC_COPY( uint8_t, m.t_srcn ); + SPC_COPY( uint8_t, m.t_esa ); + SPC_COPY( uint8_t, m.t_echo_enabled ); + + SPC_COPY( int16_t, m.t_main_out [0] ); + SPC_COPY( int16_t, m.t_main_out [1] ); + SPC_COPY( int16_t, m.t_echo_out [0] ); + SPC_COPY( int16_t, m.t_echo_out [1] ); + SPC_COPY( int16_t, m.t_echo_in [0] ); + SPC_COPY( int16_t, m.t_echo_in [1] ); + + SPC_COPY( uint16_t, m.t_dir_addr ); + SPC_COPY( uint16_t, m.t_pitch ); + SPC_COPY( int16_t, m.t_output ); + SPC_COPY( uint16_t, m.t_echo_ptr ); + SPC_COPY( uint8_t, m.t_looped ); + + copier.extra(); +} + +}; + +#endif diff --git a/Frameworks/GME/gme/Spc_Dsp.h b/Frameworks/GME/gme/higan/dsp/SPC_DSP.h old mode 100644 new mode 100755 similarity index 81% rename from Frameworks/GME/gme/Spc_Dsp.h rename to Frameworks/GME/gme/higan/dsp/SPC_DSP.h index d53c5ebe7..635df3142 --- a/Frameworks/GME/gme/Spc_Dsp.h +++ b/Frameworks/GME/gme/higan/dsp/SPC_DSP.h @@ -1,341 +1,316 @@ -// Highly accurate SNES SPC-700 DSP emulator - -// snes_spc 0.9.0 -#ifndef SPC_DSP_H -#define SPC_DSP_H - -#include "blargg_common.h" - -extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } - -class Sfm_Emu; - -class Spc_Dsp { - friend class Sfm_Emu; - -public: - typedef BOOST::uint8_t uint8_t; - -// Setup - - // Initializes DSP and has it use the 64K RAM provided - void init( void* ram_64k ); - - // Sets destination for output samples. If out is NULL or out_size is 0, - // doesn't generate any. - typedef short sample_t; - void set_output( sample_t* out, int out_size ); - - // Number of samples written to output since it was last set, always - // a multiple of 2. Undefined if more samples were generated than - // output buffer could hold. - int sample_count() const; - -// Emulation - - // Resets DSP to power-on state - void reset(); - - // Emulates pressing reset switch on SNES - void soft_reset(); - - // Reads/writes DSP registers. For accuracy, you must first call run() - // to catch the DSP up to present. - int read ( int addr ) const; - void write( int addr, int data ); - - // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks - // a pair of samples is be generated. - void run( int clock_count ); - -// Sound control - - // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). - // Reduces emulation accuracy. - enum { voice_count = 8 }; - void mute_voices( int mask ); - -// State - - // Resets DSP and uses supplied values to initialize registers - enum { register_count = 128 }; - void load( uint8_t const regs [register_count] ); - - // Saves/loads exact emulator state - enum { state_size = 640 }; // maximum space needed when saving - typedef dsp_copy_func_t copy_func_t; - void copy_state( unsigned char** io, copy_func_t ); - - // Returns non-zero if new key-on events occurred since last call - bool check_kon(); - -// DSP register addresses - - // Global registers - enum { - r_mvoll = 0x0C, r_mvolr = 0x1C, - r_evoll = 0x2C, r_evolr = 0x3C, - r_kon = 0x4C, r_koff = 0x5C, - r_flg = 0x6C, r_endx = 0x7C, - r_efb = 0x0D, r_pmon = 0x2D, - r_non = 0x3D, r_eon = 0x4D, - r_dir = 0x5D, r_esa = 0x6D, - r_edl = 0x7D, - r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F - }; - - // Voice registers - enum { - v_voll = 0x00, v_volr = 0x01, - v_pitchl = 0x02, v_pitchh = 0x03, - v_srcn = 0x04, v_adsr0 = 0x05, - v_adsr1 = 0x06, v_gain = 0x07, - v_envx = 0x08, v_outx = 0x09 - }; - -public: - enum { extra_size = 16 }; - sample_t* extra() { return m.extra; } - sample_t const* out_pos() const { return m.out; } - void disable_surround( bool disable = true ); - void interpolation_level( int level = 0 ) { m.interpolation_level = level; } -public: - BLARGG_DISABLE_NOTHROW - - typedef BOOST::int8_t int8_t; - typedef BOOST::int16_t int16_t; - - enum { echo_hist_size = 8 }; - - enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; - enum { brr_buf_size = 12 }; - struct voice_t - { - int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) - int buf_pos; // place in buffer where next samples will be decoded - int interp_pos; // relative fractional position in sample (0x1000 = 1.0) - int brr_addr; // address of current BRR block - int brr_offset; // current decoding offset in BRR block - uint8_t* regs; // pointer to voice's DSP registers - int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. - int kon_delay; // KON delay/current setup phase - env_mode_t env_mode; - int env; // current envelope level - int hidden_env; // used by GAIN mode 7, very obscure quirk - uint8_t t_envx_out; - }; - - // kill me now - const voice_t * get_voice( int ch ) const; - int get_max_level( int v, int ch ) const; -private: - enum { brr_block_size = 9 }; - - struct state_t - { - uint8_t regs [register_count]; - - // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) - int echo_hist [echo_hist_size * 2] [2]; - int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] - - int every_other_sample; // toggles every sample - int kon; // KON value when last checked - int noise; - int counter; - int echo_offset; // offset from ESA in echo buffer - int echo_length; // number of bytes that echo_offset will stop at - int phase; // next clock cycle to run (0-31) - bool kon_check; // set when a new KON occurs - - // Hidden registers also written to when main register is written to - int new_kon; - uint8_t endx_buf; - uint8_t envx_buf; - uint8_t outx_buf; - - // Temporary state between clocks - - // read once per sample - int t_pmon; - int t_non; - int t_eon; - int t_dir; - int t_koff; - - // read a few clocks ahead then used - int t_brr_next_addr; - int t_adsr0; - int t_brr_header; - int t_brr_byte; - int t_srcn; - int t_esa; - int t_echo_enabled; - - // internal state that is recalculated every sample - int t_dir_addr; - int t_pitch; - int t_output; - int t_looped; - int t_echo_ptr; - - // left/right sums - int t_main_out [2]; - int t_echo_out [2]; - int t_echo_in [2]; - - voice_t voices [voice_count]; - - // non-emulation state - uint8_t* ram; // 64K shared RAM between DSP and SMP - int mute_mask; - int surround_threshold; - int interpolation_level; - sample_t* out; - sample_t* out_end; - sample_t* out_begin; - sample_t extra [extra_size]; - - int max_level[voice_count][2]; - }; - state_t m; - - void init_counter(); - void run_counters(); - unsigned read_counter( int rate ); - - int interpolate( voice_t const* v ); - int interpolate_cubic( voice_t const* v ); - int interpolate_sinc( voice_t const* v ); - int interpolate_linear( voice_t const* v ); - int interpolate_nearest( voice_t const* v ); - void run_envelope( voice_t* const v ); - void decode_brr( voice_t* v ); - - void misc_27(); - void misc_28(); - void misc_29(); - void misc_30(); - - void voice_output( voice_t const* v, int ch ); - void voice_V1( voice_t* const ); - void voice_V2( voice_t* const ); - void voice_V3( voice_t* const ); - void voice_V3a( voice_t* const ); - void voice_V3b( voice_t* const ); - void voice_V3c( voice_t* const ); - void voice_V4( voice_t* const ); - void voice_V5( voice_t* const ); - void voice_V6( voice_t* const ); - void voice_V7( voice_t* const ); - void voice_V8( voice_t* const ); - void voice_V9( voice_t* const ); - void voice_V7_V4_V1( voice_t* const ); - void voice_V8_V5_V2( voice_t* const ); - void voice_V9_V6_V3( voice_t* const ); - - void echo_read( int ch ); - int echo_output( int ch ); - void echo_write( int ch ); - void echo_22(); - void echo_23(); - void echo_24(); - void echo_25(); - void echo_26(); - void echo_27(); - void echo_28(); - void echo_29(); - void echo_30(); - - void soft_reset_common(); -}; - -#include - -inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; } - -inline int Spc_Dsp::read( int addr ) const -{ - assert( (unsigned) addr < register_count ); - return m.regs [addr]; -} - -inline void Spc_Dsp::write( int addr, int data ) -{ - assert( (unsigned) addr < register_count ); - - m.regs [addr] = (uint8_t) data; - switch ( addr & 0x0F ) - { - case v_envx: - m.envx_buf = (uint8_t) data; - break; - - case v_outx: - m.outx_buf = (uint8_t) data; - break; - - case 0x0C: - if ( addr == r_kon ) - m.new_kon = (uint8_t) data; - - if ( addr == r_endx ) // always cleared, regardless of data written - { - m.endx_buf = 0; - m.regs [r_endx] = 0; - } - break; - } -} - -inline void Spc_Dsp::mute_voices( int mask ) { m.mute_mask = mask; } - -inline void Spc_Dsp::disable_surround( bool disable ) -{ - m.surround_threshold = disable ? 0 : -0x4000; -} - -inline const Spc_Dsp::voice_t * Spc_Dsp::get_voice( int ch ) const -{ - assert( (unsigned) ch < voice_count ); - return &m.voices[ ch ]; -} - -inline int Spc_Dsp::get_max_level( int v, int ch ) const -{ - assert( (unsigned) v < voice_count ); - assert( (unsigned) ch < 2 ); - int ret = m.max_level[ v ][ ch ]; - (( Spc_Dsp * )this)->m.max_level[ v ][ ch ] = 0; - return ret; -} - -inline bool Spc_Dsp::check_kon() -{ - bool old = m.kon_check; - m.kon_check = 0; - return old; -} - -#if !SPC_NO_COPY_STATE_FUNCS - -class SPC_State_Copier { - Spc_Dsp::copy_func_t func; - unsigned char** buf; -public: - SPC_State_Copier( unsigned char** p, Spc_Dsp::copy_func_t f ) { func = f; buf = p; } - void copy( void* state, size_t size ); - int copy_int( int state, int size ); - void skip( int count ); - void extra(); -}; - -#define SPC_COPY( type, state )\ -{\ - state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ - assert( (BOOST::type) state == state );\ -} - -#endif - -#endif +// Highly accurate SNES SPC-700 DSP emulator + +// snes_spc 0.9.0 +#ifndef SPC_DSP_H +#define SPC_DSP_H + +#include "blargg_common.h" + +extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); } + +namespace SuperFamicom { +class SPC_DSP { +public: + typedef BOOST::uint8_t uint8_t; + +// Setup + + // Initializes DSP and has it use the 64K RAM provided + void init( void* ram_64k ); + + // Sets destination for output samples. If out is NULL or out_size is 0, + // doesn't generate any. + typedef short sample_t; + void set_output( sample_t* out, int out_size ); + + // Number of samples written to output since it was last set, always + // a multiple of 2. Undefined if more samples were generated than + // output buffer could hold. + int sample_count() const; + +// Emulation + + // Resets DSP to power-on state + void reset(); + + // Emulates pressing reset switch on SNES + void soft_reset(); + + // Reads/writes DSP registers. For accuracy, you must first call run() + // to catch the DSP up to present. + int read ( int addr ) const; + void write( int addr, int data ); + + // Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks + // a pair of samples is be generated. + void run( int clock_count ); + +// Sound control + + // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). + // Reduces emulation accuracy. + enum { voice_count = 8 }; + void mute_voices( int mask ); + +// State + + // Resets DSP and uses supplied values to initialize registers + enum { register_count = 128 }; + void load( uint8_t const regs [register_count] ); + + // Saves/loads exact emulator state + enum { state_size = 640 }; // maximum space needed when saving + typedef dsp_copy_func_t copy_func_t; + void copy_state( unsigned char** io, copy_func_t ); + + // Returns non-zero if new key-on events occurred since last call + bool check_kon(); + +// DSP register addresses + + // Global registers + enum { + r_mvoll = 0x0C, r_mvolr = 0x1C, + r_evoll = 0x2C, r_evolr = 0x3C, + r_kon = 0x4C, r_koff = 0x5C, + r_flg = 0x6C, r_endx = 0x7C, + r_efb = 0x0D, r_pmon = 0x2D, + r_non = 0x3D, r_eon = 0x4D, + r_dir = 0x5D, r_esa = 0x6D, + r_edl = 0x7D, + r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F + }; + + // Voice registers + enum { + v_voll = 0x00, v_volr = 0x01, + v_pitchl = 0x02, v_pitchh = 0x03, + v_srcn = 0x04, v_adsr0 = 0x05, + v_adsr1 = 0x06, v_gain = 0x07, + v_envx = 0x08, v_outx = 0x09 + }; + +public: + enum { extra_size = 16 }; + sample_t* extra() { return m.extra; } + sample_t const* out_pos() const { return m.out; } + void disable_surround( bool disable = true ); +public: + BLARGG_DISABLE_NOTHROW + + typedef BOOST::int8_t int8_t; + typedef BOOST::int16_t int16_t; + + enum { echo_hist_size = 8 }; + + enum env_mode_t { env_release, env_attack, env_decay, env_sustain }; + enum { brr_buf_size = 12 }; + struct voice_t + { + int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling) + int buf_pos; // place in buffer where next samples will be decoded + int interp_pos; // relative fractional position in sample (0x1000 = 1.0) + int brr_addr; // address of current BRR block + int brr_offset; // current decoding offset in BRR block + uint8_t* regs; // pointer to voice's DSP registers + int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc. + int kon_delay; // KON delay/current setup phase + env_mode_t env_mode; + int env; // current envelope level + int hidden_env; // used by GAIN mode 7, very obscure quirk + uint8_t t_envx_out; + }; +//private: + enum { brr_block_size = 9 }; + + struct state_t + { + uint8_t regs [register_count]; + + // Echo history keeps most recent 8 samples (twice the size to simplify wrap handling) + int echo_hist [echo_hist_size * 2] [2]; + int (*echo_hist_pos) [2]; // &echo_hist [0 to 7] + + int every_other_sample; // toggles every sample + int kon; // KON value when last checked + int noise; + int counter; + int echo_offset; // offset from ESA in echo buffer + int echo_length; // number of bytes that echo_offset will stop at + int phase; // next clock cycle to run (0-31) + bool kon_check; // set when a new KON occurs + + // Hidden registers also written to when main register is written to + int new_kon; + uint8_t endx_buf; + uint8_t envx_buf; + uint8_t outx_buf; + + // Temporary state between clocks + + // read once per sample + int t_pmon; + int t_non; + int t_eon; + int t_dir; + int t_koff; + + // read a few clocks ahead then used + int t_brr_next_addr; + int t_adsr0; + int t_brr_header; + int t_brr_byte; + int t_srcn; + int t_esa; + int t_echo_enabled; + + // internal state that is recalculated every sample + int t_dir_addr; + int t_pitch; + int t_output; + int t_looped; + int t_echo_ptr; + + // left/right sums + int t_main_out [2]; + int t_echo_out [2]; + int t_echo_in [2]; + + voice_t voices [voice_count]; + + // non-emulation state + uint8_t* ram; // 64K shared RAM between DSP and SMP + int mute_mask; + int surround_threshold; + sample_t* out; + sample_t* out_end; + sample_t* out_begin; + sample_t extra [extra_size]; + }; + state_t m; + + void init_counter(); + void run_counters(); + unsigned read_counter( int rate ); + + int interpolate( voice_t const* v ); + void run_envelope( voice_t* const v ); + void decode_brr( voice_t* v ); + + void misc_27(); + void misc_28(); + void misc_29(); + void misc_30(); + + void voice_output( voice_t const* v, int ch ); + void voice_V1( voice_t* const ); + void voice_V2( voice_t* const ); + void voice_V3( voice_t* const ); + void voice_V3a( voice_t* const ); + void voice_V3b( voice_t* const ); + void voice_V3c( voice_t* const ); + void voice_V4( voice_t* const ); + void voice_V5( voice_t* const ); + void voice_V6( voice_t* const ); + void voice_V7( voice_t* const ); + void voice_V8( voice_t* const ); + void voice_V9( voice_t* const ); + void voice_V7_V4_V1( voice_t* const ); + void voice_V8_V5_V2( voice_t* const ); + void voice_V9_V6_V3( voice_t* const ); + + void echo_read( int ch ); + int echo_output( int ch ); + void echo_write( int ch ); + void echo_22(); + void echo_23(); + void echo_24(); + void echo_25(); + void echo_26(); + void echo_27(); + void echo_28(); + void echo_29(); + void echo_30(); + + void soft_reset_common(); + +public: + bool mute() { return m.regs[r_flg] & 0x40; } +}; + +#include + +inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } + +inline int SPC_DSP::read( int addr ) const +{ + assert( (unsigned) addr < register_count ); + return m.regs [addr]; +} + +inline void SPC_DSP::write( int addr, int data ) +{ + assert( (unsigned) addr < register_count ); + + m.regs [addr] = (uint8_t) data; + switch ( addr & 0x0F ) + { + case v_envx: + m.envx_buf = (uint8_t) data; + break; + + case v_outx: + m.outx_buf = (uint8_t) data; + break; + + case 0x0C: + if ( addr == r_kon ) + m.new_kon = (uint8_t) data; + + if ( addr == r_endx ) // always cleared, regardless of data written + { + m.endx_buf = 0; + m.regs [r_endx] = 0; + } + break; + } +} + +inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; } + +inline void SPC_DSP::disable_surround( bool disable ) +{ + m.surround_threshold = disable ? 0 : -0x4000; +} + +inline bool SPC_DSP::check_kon() +{ + bool old = m.kon_check; + m.kon_check = 0; + return old; +} + +#if !SPC_NO_COPY_STATE_FUNCS + +class SPC_State_Copier { + SPC_DSP::copy_func_t func; + unsigned char** buf; +public: + SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; } + void copy( void* state, size_t size ); + int copy_int( int state, int size ); + void skip( int count ); + void extra(); +}; + +#define SPC_COPY( type, state )\ +{\ + state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ + assert( (BOOST::type) state == state );\ +} + +#endif + +}; + +#endif diff --git a/Frameworks/GME/gme/higan/dsp/dsp.cpp b/Frameworks/GME/gme/higan/dsp/dsp.cpp new file mode 100755 index 000000000..44c5cad5e --- /dev/null +++ b/Frameworks/GME/gme/higan/dsp/dsp.cpp @@ -0,0 +1,66 @@ +#include "../smp/smp.hpp" +#include "dsp.hpp" + +namespace SuperFamicom { + +void DSP::step(unsigned clocks) { + clock += clocks; +} + +void DSP::synchronize_smp() { + while(clock >= 0) smp.enter(); +} + +void DSP::enter() { + spc_dsp.run(1); + step(24); + + signed count = spc_dsp.sample_count(); + if(count > 0) { + for(unsigned n = 0; n < count; n += 2) smp.sample(samplebuffer[n + 0], samplebuffer[n + 1]); + spc_dsp.set_output(samplebuffer, 8192); + } +} + +bool DSP::mute() { + return spc_dsp.mute(); +} + +uint8_t DSP::read(uint8_t addr) { + return spc_dsp.read(addr); +} + +void DSP::write(uint8_t addr, uint8_t data) { + spc_dsp.write(addr, data); +} + +void DSP::power() { + spc_dsp.init(smp.apuram); + spc_dsp.reset(); + spc_dsp.set_output(samplebuffer, 8192); +} + +void DSP::reset() { + spc_dsp.soft_reset(); + spc_dsp.set_output(samplebuffer, 8192); +} + +void DSP::channel_enable(unsigned channel, bool enable) { + channel_enabled[channel & 7] = enable; + unsigned mask = 0; + for(unsigned i = 0; i < 8; i++) { + if(channel_enabled[i] == false) mask |= 1 << i; + } + spc_dsp.mute_voices(mask); +} + +void DSP::disable_surround(bool disable) { + spc_dsp.disable_surround(disable); +} + +DSP::DSP(struct SMP & p_smp) + : smp( p_smp ), clock( 0 ) { + for(unsigned i = 0; i < 8; i++) channel_enabled[i] = true; +} + +} diff --git a/Frameworks/GME/gme/higan/dsp/dsp.hpp b/Frameworks/GME/gme/higan/dsp/dsp.hpp new file mode 100755 index 000000000..aa4fdf145 --- /dev/null +++ b/Frameworks/GME/gme/higan/dsp/dsp.hpp @@ -0,0 +1,39 @@ +#ifndef _higan_dsp_h_ +#define _higan_dsp_h_ + +#include "SPC_DSP.h" + +#include "blargg_common.h" + +namespace SuperFamicom { + +struct DSP { + long clock; + + inline void step(unsigned clocks); + inline void synchronize_smp(); + + bool mute(); + uint8_t read(uint8_t addr); + void write(uint8_t addr, uint8_t data); + + void enter(); + void power(); + void reset(); + + void channel_enable(unsigned channel, bool enable); + void disable_surround(bool disable = true); + + DSP(struct SMP&); + + SPC_DSP spc_dsp; + +private: + struct SMP & smp; + int16_t samplebuffer[8192]; + bool channel_enabled[8]; +}; + +}; + +#endif diff --git a/Frameworks/GME/gme/higan/processor/spc700/algorithms.cpp b/Frameworks/GME/gme/higan/processor/spc700/algorithms.cpp new file mode 100755 index 000000000..d3d554689 --- /dev/null +++ b/Frameworks/GME/gme/higan/processor/spc700/algorithms.cpp @@ -0,0 +1,134 @@ +uint8_t SPC700::op_adc(uint8_t x, uint8_t y) { + int r = x + y + regs.p.c; + regs.p.n = r & 0x80; + regs.p.v = ~(x ^ y) & (x ^ r) & 0x80; + regs.p.h = (x ^ y ^ r) & 0x10; + regs.p.z = (uint8_t)r == 0; + regs.p.c = r > 0xff; + return r; +} + +uint8_t SPC700::op_and(uint8_t x, uint8_t y) { + x &= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_asl(uint8_t x) { + regs.p.c = x & 0x80; + x <<= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_cmp(uint8_t x, uint8_t y) { + int r = x - y; + regs.p.n = r & 0x80; + regs.p.z = (uint8_t)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint8_t SPC700::op_dec(uint8_t x) { + x--; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_eor(uint8_t x, uint8_t y) { + x ^= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_inc(uint8_t x) { + x++; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_ld(uint8_t x, uint8_t y) { + regs.p.n = y & 0x80; + regs.p.z = y == 0; + return y; +} + +uint8_t SPC700::op_lsr(uint8_t x) { + regs.p.c = x & 0x01; + x >>= 1; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_or(uint8_t x, uint8_t y) { + x |= y; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_rol(uint8_t x) { + unsigned carry = regs.p.c << 0; + regs.p.c = x & 0x80; + x = (x << 1) | carry; + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_ror(uint8_t x) { + unsigned carry = regs.p.c << 7; + regs.p.c = x & 0x01; + x = carry | (x >> 1); + regs.p.n = x & 0x80; + regs.p.z = x == 0; + return x; +} + +uint8_t SPC700::op_sbc(uint8_t x, uint8_t y) { + return op_adc(x, ~y); +} + +uint8_t SPC700::op_st(uint8_t x, uint8_t y) { + return y; +} + +// + +uint16_t SPC700::op_adw(uint16_t x, uint16_t y) { + uint16_t r; + regs.p.c = 0; + r = op_adc(x, y); + r |= op_adc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} + +uint16_t SPC700::op_cpw(uint16_t x, uint16_t y) { + int r = x - y; + regs.p.n = r & 0x8000; + regs.p.z = (uint16_t)r == 0; + regs.p.c = r >= 0; + return x; +} + +uint16_t SPC700::op_ldw(uint16_t x, uint16_t y) { + regs.p.n = y & 0x8000; + regs.p.z = y == 0; + return y; +} + +uint16_t SPC700::op_sbw(uint16_t x, uint16_t y) { + uint16_t r; + regs.p.c = 1; + r = op_sbc(x, y); + r |= op_sbc(x >> 8, y >> 8) << 8; + regs.p.z = r == 0; + return r; +} diff --git a/Frameworks/GME/gme/higan/processor/spc700/instructions.cpp b/Frameworks/GME/gme/higan/processor/spc700/instructions.cpp new file mode 100755 index 000000000..bd75b0473 --- /dev/null +++ b/Frameworks/GME/gme/higan/processor/spc700/instructions.cpp @@ -0,0 +1,556 @@ +#define call (this->*op) + +template +void SPC700::op_adjust(uint8_t& r) { + op_io(); + r = call(r); +} + +template +void SPC700::op_adjust_addr() { + dp.l = op_readpc(); + dp.h = op_readpc(); + rd = op_read(dp); + rd = call(rd); + op_write(dp, rd); +} + +template +void SPC700::op_adjust_dp() { + dp = op_readpc(); + rd = op_readdp(dp); + rd = call(rd); + op_writedp(dp, rd); +} + +void SPC700::op_adjust_dpw(signed n) { + dp = op_readpc(); + rd.w = op_readdp(dp) + n; + op_writedp(dp++, rd.l); + rd.h += op_readdp(dp); + op_writedp(dp++, rd.h); + regs.p.n = rd & 0x8000; + regs.p.z = rd == 0; +} + +template +void SPC700::op_adjust_dpx() { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + regs.x); + rd = call(rd); + op_writedp(dp + regs.x, rd); +} + +void SPC700::op_branch(bool condition) { + rd = op_readpc(); + if(condition == false) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; +} + +void SPC700::op_branch_bit() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if((bool)(sp & (1 << (opcode >> 5))) == (bool)(opcode & 0x10)) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; +} + +void SPC700::op_pull(uint8_t& r) { + op_io(); + op_io(); + r = op_readsp(); +} + +void SPC700::op_push(uint8_t r) { + op_io(); + op_io(); + op_writesp(r); +} + +template +void SPC700::op_read_addr(uint8_t& r) { + dp.l = op_readpc(); + dp.h = op_readpc(); + rd = op_read(dp); + r = call(r, rd); +} + +template +void SPC700::op_read_addri(uint8_t& r) { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_io(); + rd = op_read(dp + r); + regs.a = call(regs.a, rd); +} + +template +void SPC700::op_read_const(uint8_t& r) { + rd = op_readpc(); + r = call(r, rd); +} + +template +void SPC700::op_read_dp(uint8_t& r) { + dp = op_readpc(); + rd = op_readdp(dp); + r = call(r, rd); +} + +template +void SPC700::op_read_dpi(uint8_t& r, uint8_t& i) { + dp = op_readpc(); + op_io(); + rd = op_readdp(dp + i); + r = call(r, rd); +} + +template +void SPC700::op_read_dpw() { + dp = op_readpc(); + rd.l = op_readdp(dp++); + if(op != &SPC700::op_cpw) op_io(); + rd.h = op_readdp(dp++); + regs.ya = call(regs.ya, rd); +} + +template +void SPC700::op_read_idpx() { + dp = op_readpc() + regs.x; + op_io(); + sp.l = op_readdp(dp++); + sp.h = op_readdp(dp++); + rd = op_read(sp); + regs.a = call(regs.a, rd); +} + +template +void SPC700::op_read_idpy() { + dp = op_readpc(); + op_io(); + sp.l = op_readdp(dp++); + sp.h = op_readdp(dp++); + rd = op_read(sp + regs.y); + regs.a = call(regs.a, rd); +} + +template +void SPC700::op_read_ix() { + op_io(); + rd = op_readdp(regs.x); + regs.a = call(regs.a, rd); +} + +void SPC700::op_set_addr_bit() { + dp.l = op_readpc(); + dp.h = op_readpc(); + bit = dp >> 13; + dp &= 0x1fff; + rd = op_read(dp); + switch(opcode >> 5) { + case 0: //orc addr:bit + case 1: //orc !addr:bit + op_io(); + regs.p.c |= (rd & (1 << bit)) ^ (bool)(opcode & 0x20); + break; + case 2: //and addr:bit + case 3: //and !addr:bit + regs.p.c &= (rd & (1 << bit)) ^ (bool)(opcode & 0x20); + break; + case 4: //eor addr:bit + op_io(); + regs.p.c ^= (bool)(rd & (1 << bit)); + break; + case 5: //ldc addr:bit + regs.p.c = (rd & (1 << bit)); + break; + case 6: //stc addr:bit + op_io(); + rd = (rd & ~(1 << bit)) | (regs.p.c << bit); + op_write(dp, rd); + break; + case 7: //not addr:bit + rd ^= 1 << bit; + op_write(dp, rd); + break; + } +} + +void SPC700::op_set_bit() { + dp = op_readpc(); + rd = op_readdp(dp) & ~(1 << (opcode >> 5)); + op_writedp(dp, rd | (!(opcode & 0x10) << (opcode >> 5))); +} + +void SPC700::op_set_flag(bool& flag, bool data) { + op_io(); + if(&flag == ®s.p.i) op_io(); + flag = data; +} + +void SPC700::op_test_addr(bool set) { + dp.l = op_readpc(); + dp.h = op_readpc(); + rd = op_read(dp); + regs.p.n = (regs.a - rd) & 0x80; + regs.p.z = (regs.a - rd) == 0; + op_read(dp); + op_write(dp, set ? rd | regs.a : rd & ~regs.a); +} + +void SPC700::op_transfer(uint8_t& from, uint8_t& to) { + op_io(); + to = from; + if(&to == ®s.s) return; + regs.p.n = (to & 0x80); + regs.p.z = (to == 0); +} + +void SPC700::op_write_addr(uint8_t& r) { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_read(dp); + op_write(dp, r); +} + +void SPC700::op_write_addri(uint8_t& i) { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_io(); + dp += i; + op_read(dp); + op_write(dp, regs.a); +} + +void SPC700::op_write_dp(uint8_t& r) { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp, r); +} + +void SPC700::op_write_dpi(uint8_t& r, uint8_t& i) { + dp = op_readpc() + i; + op_io(); + op_readdp(dp); + op_writedp(dp, r); +} + +template +void SPC700::op_write_dp_const() { + rd = op_readpc(); + dp = op_readpc(); + wr = op_readdp(dp); + wr = call(wr, rd); + op != &SPC700::op_cmp ? op_writedp(dp, wr) : op_io(); +} + +template +void SPC700::op_write_dp_dp() { + sp = op_readpc(); + rd = op_readdp(sp); + dp = op_readpc(); + if(op != &SPC700::op_st) wr = op_readdp(dp); + wr = call(wr, rd); + op != &SPC700::op_cmp ? op_writedp(dp, wr) : op_io(); +} + +template +void SPC700::op_write_ix_iy() { + op_io(); + rd = op_readdp(regs.y); + wr = op_readdp(regs.x); + wr = call(wr, rd); + op != &SPC700::op_cmp ? op_writedp(regs.x, wr) : op_io(); +} + +// + +void SPC700::op_bne_dp() { + dp = op_readpc(); + sp = op_readdp(dp); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; +} + +void SPC700::op_bne_dpdec() { + dp = op_readpc(); + wr = op_readdp(dp); + op_writedp(dp, --wr); + rd = op_readpc(); + if(wr == 0) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; +} + +void SPC700::op_bne_dpx() { + dp = op_readpc(); + op_io(); + sp = op_readdp(dp + regs.x); + rd = op_readpc(); + op_io(); + if(regs.a == sp) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; +} + +void SPC700::op_bne_ydec() { + rd = op_readpc(); + op_io(); + op_io(); + if(--regs.y == 0) return; + op_io(); + op_io(); + regs.pc += (int8_t)rd; +} + +void SPC700::op_brk() { + rd.l = op_read(0xffde); + rd.h = op_read(0xffdf); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + op_writesp(regs.p); + regs.pc = rd; + regs.p.b = 1; + regs.p.i = 0; +} + +void SPC700::op_clv() { + op_io(); + regs.p.v = 0; + regs.p.h = 0; +} + +void SPC700::op_cmc() { + op_io(); + op_io(); + regs.p.c = !regs.p.c; +} + +void SPC700::op_daa() { + op_io(); + op_io(); + if(regs.p.c || (regs.a) > 0x99) { + regs.a += 0x60; + regs.p.c = 1; + } + if(regs.p.h || (regs.a & 15) > 0x09) { + regs.a += 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SPC700::op_das() { + op_io(); + op_io(); + if(!regs.p.c || (regs.a) > 0x99) { + regs.a -= 0x60; + regs.p.c = 0; + } + if(!regs.p.h || (regs.a & 15) > 0x09) { + regs.a -= 0x06; + } + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SPC700::op_div_ya_x() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.ya; + //overflow set if quotient >= 256 + regs.p.v = (regs.y >= regs.x); + regs.p.h = ((regs.y & 15) >= (regs.x & 15)); + if(regs.y < (regs.x << 1)) { + //if quotient is <= 511 (will fit into 9-bit result) + regs.a = ya / regs.x; + regs.y = ya % regs.x; + } else { + //otherwise, the quotient won't fit into regs.p.v + regs.a + //this emulates the odd behavior of the S-SMP in this case + regs.a = 255 - (ya - (regs.x << 9)) / (256 - regs.x); + regs.y = regs.x + (ya - (regs.x << 9)) % (256 - regs.x); + } + //result is set based on a (quotient) only + regs.p.n = (regs.a & 0x80); + regs.p.z = (regs.a == 0); +} + +void SPC700::op_jmp_addr() { + rd.l = op_readpc(); + rd.h = op_readpc(); + regs.pc = rd; +} + +void SPC700::op_jmp_iaddrx() { + dp.l = op_readpc(); + dp.h = op_readpc(); + op_io(); + dp += regs.x; + rd.l = op_read(dp++); + rd.h = op_read(dp++); + regs.pc = rd; +} + +void SPC700::op_jsp_dp() { + rd = op_readpc(); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + regs.pc = 0xff00 | rd; +} + +void SPC700::op_jsr_addr() { + rd.l = op_readpc(); + rd.h = op_readpc(); + op_io(); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + regs.pc = rd; +} + +void SPC700::op_jst() { + dp = 0xffde - ((opcode >> 4) << 1); + rd.l = op_read(dp++); + rd.h = op_read(dp++); + op_io(); + op_io(); + op_io(); + op_writesp(regs.pc.h); + op_writesp(regs.pc.l); + regs.pc = rd; +} + +void SPC700::op_lda_ixinc() { + op_io(); + regs.a = op_readdp(regs.x++); + op_io(); + regs.p.n = regs.a & 0x80; + regs.p.z = regs.a == 0; +} + +void SPC700::op_mul_ya() { + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + op_io(); + ya = regs.y * regs.a; + regs.a = ya; + regs.y = ya >> 8; + //result is set based on y (high-byte) only + regs.p.n = (regs.y & 0x80); + regs.p.z = (regs.y == 0); +} + +void SPC700::op_nop() { + op_io(); +} + +void SPC700::op_plp() { + op_io(); + op_io(); + regs.p = op_readsp(); +} + +void SPC700::op_rti() { + regs.p = op_readsp(); + rd.l = op_readsp(); + rd.h = op_readsp(); + op_io(); + op_io(); + regs.pc = rd; +} + +void SPC700::op_rts() { + rd.l = op_readsp(); + rd.h = op_readsp(); + op_io(); + op_io(); + regs.pc = rd; +} + +void SPC700::op_sta_idpx() { + sp = op_readpc() + regs.x; + op_io(); + dp.l = op_readdp(sp++); + dp.h = op_readdp(sp++); + op_read(dp); + op_write(dp, regs.a); +} + +void SPC700::op_sta_idpy() { + sp = op_readpc(); + dp.l = op_readdp(sp++); + dp.h = op_readdp(sp++); + op_io(); + dp += regs.y; + op_read(dp); + op_write(dp, regs.a); +} + +void SPC700::op_sta_ix() { + op_io(); + op_readdp(regs.x); + op_writedp(regs.x, regs.a); +} + +void SPC700::op_sta_ixinc() { + op_io(); + op_io(); + op_writedp(regs.x++, regs.a); +} + +void SPC700::op_stw_dp() { + dp = op_readpc(); + op_readdp(dp); + op_writedp(dp++, regs.a); + op_writedp(dp++, regs.y); +} + +void SPC700::op_wait() { + op_io(); + op_io(); + regs.pc--; +} + +void SPC700::op_xcn() { + op_io(); + op_io(); + op_io(); + op_io(); + regs.a = (regs.a >> 4) | (regs.a << 4); + regs.p.n = regs.a & 0x80; + regs.p.z = regs.a == 0; +} + +#undef call diff --git a/Frameworks/GME/gme/higan/processor/spc700/memory.hpp b/Frameworks/GME/gme/higan/processor/spc700/memory.hpp new file mode 100755 index 000000000..f2cb7c8b4 --- /dev/null +++ b/Frameworks/GME/gme/higan/processor/spc700/memory.hpp @@ -0,0 +1,19 @@ +inline uint8_t op_readpc() { + return op_read(regs.pc++); +} + +inline uint8_t op_readsp() { + return op_read(0x0100 | ++regs.s); +} + +inline void op_writesp(uint8_t data) { + return op_write(0x0100 | regs.s--, data); +} + +inline uint8_t op_readdp(uint8_t addr) { + return op_read((regs.p.p << 8) + addr); +} + +inline void op_writedp(uint8_t addr, uint8_t data) { + return op_write((regs.p.p << 8) + addr, data); +} diff --git a/Frameworks/GME/gme/higan/processor/spc700/registers.hpp b/Frameworks/GME/gme/higan/processor/spc700/registers.hpp new file mode 100755 index 000000000..6827fc0a9 --- /dev/null +++ b/Frameworks/GME/gme/higan/processor/spc700/registers.hpp @@ -0,0 +1,59 @@ +struct flag_t { + bool n, v, p, b, h, i, z, c; + + inline operator unsigned() const { + return (n << 7) | (v << 6) | (p << 5) | (b << 4) + | (h << 3) | (i << 2) | (z << 1) | (c << 0); + } + + inline unsigned operator=(uint8_t data) { + n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10; + h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + inline unsigned operator|=(uint8_t data) { return operator=(operator unsigned() | data); } + inline unsigned operator^=(uint8_t data) { return operator=(operator unsigned() ^ data); } + inline unsigned operator&=(uint8_t data) { return operator=(operator unsigned() & data); } +}; + +struct word_t { + union { + uint16_t w; +#ifdef BLARGG_BIG_ENDIAN + struct { uint8_t h, l; }; +#else + struct { uint8_t l, h; }; +#endif + }; + + inline operator unsigned() const { return w; } + inline unsigned operator=(unsigned data) { return w = data; } + + inline unsigned operator++() { return ++w; } + inline unsigned operator--() { return --w; } + + inline unsigned operator++(int) { unsigned data = w++; return data; } + inline unsigned operator--(int) { unsigned data = w--; return data; } + + inline unsigned operator+=(unsigned data) { return w += data;; } + inline unsigned operator-=(unsigned data) { return w -= data;; } + + inline unsigned operator|=(unsigned data) { return w |= data; } + inline unsigned operator^=(unsigned data) { return w ^= data; } + inline unsigned operator&=(unsigned data) { return w &= data; } +}; + +struct regs_t { + word_t pc; + union { + uint16_t ya; +#ifdef BLARGG_BIG_ENDIAN + struct { uint8_t y, a; }; +#else + struct { uint8_t a, y; }; +#endif + }; + uint8_t x, s; + flag_t p; +}; diff --git a/Frameworks/GME/gme/higan/processor/spc700/spc700.cpp b/Frameworks/GME/gme/higan/processor/spc700/spc700.cpp new file mode 100755 index 000000000..0324c30d1 --- /dev/null +++ b/Frameworks/GME/gme/higan/processor/spc700/spc700.cpp @@ -0,0 +1,269 @@ +#include "spc700.hpp" + +namespace Processor { + +#include "algorithms.cpp" +#include "instructions.cpp" + +void SPC700::op_step() { + switch(opcode = op_readpc()) { + case 0x00: return op_nop(); + case 0x01: return op_jst(); + case 0x02: return op_set_bit(); + case 0x03: return op_branch_bit(); + case 0x04: return op_read_dp<&SPC700::op_or>(regs.a); + case 0x05: return op_read_addr<&SPC700::op_or>(regs.a); + case 0x06: return op_read_ix<&SPC700::op_or>(); + case 0x07: return op_read_idpx<&SPC700::op_or>(); + case 0x08: return op_read_const<&SPC700::op_or>(regs.a); + case 0x09: return op_write_dp_dp<&SPC700::op_or>(); + case 0x0a: return op_set_addr_bit(); + case 0x0b: return op_adjust_dp<&SPC700::op_asl>(); + case 0x0c: return op_adjust_addr<&SPC700::op_asl>(); + case 0x0d: return op_push(regs.p); + case 0x0e: return op_test_addr(1); + case 0x0f: return op_brk(); + case 0x10: return op_branch(regs.p.n == 0); + case 0x11: return op_jst(); + case 0x12: return op_set_bit(); + case 0x13: return op_branch_bit(); + case 0x14: return op_read_dpi<&SPC700::op_or>(regs.a, regs.x); + case 0x15: return op_read_addri<&SPC700::op_or>(regs.x); + case 0x16: return op_read_addri<&SPC700::op_or>(regs.y); + case 0x17: return op_read_idpy<&SPC700::op_or>(); + case 0x18: return op_write_dp_const<&SPC700::op_or>(); + case 0x19: return op_write_ix_iy<&SPC700::op_or>(); + case 0x1a: return op_adjust_dpw(-1); + case 0x1b: return op_adjust_dpx<&SPC700::op_asl>(); + case 0x1c: return op_adjust<&SPC700::op_asl>(regs.a); + case 0x1d: return op_adjust<&SPC700::op_dec>(regs.x); + case 0x1e: return op_read_addr<&SPC700::op_cmp>(regs.x); + case 0x1f: return op_jmp_iaddrx(); + case 0x20: return op_set_flag(regs.p.p, 0); + case 0x21: return op_jst(); + case 0x22: return op_set_bit(); + case 0x23: return op_branch_bit(); + case 0x24: return op_read_dp<&SPC700::op_and>(regs.a); + case 0x25: return op_read_addr<&SPC700::op_and>(regs.a); + case 0x26: return op_read_ix<&SPC700::op_and>(); + case 0x27: return op_read_idpx<&SPC700::op_and>(); + case 0x28: return op_read_const<&SPC700::op_and>(regs.a); + case 0x29: return op_write_dp_dp<&SPC700::op_and>(); + case 0x2a: return op_set_addr_bit(); + case 0x2b: return op_adjust_dp<&SPC700::op_rol>(); + case 0x2c: return op_adjust_addr<&SPC700::op_rol>(); + case 0x2d: return op_push(regs.a); + case 0x2e: return op_bne_dp(); + case 0x2f: return op_branch(true); + case 0x30: return op_branch(regs.p.n == 1); + case 0x31: return op_jst(); + case 0x32: return op_set_bit(); + case 0x33: return op_branch_bit(); + case 0x34: return op_read_dpi<&SPC700::op_and>(regs.a, regs.x); + case 0x35: return op_read_addri<&SPC700::op_and>(regs.x); + case 0x36: return op_read_addri<&SPC700::op_and>(regs.y); + case 0x37: return op_read_idpy<&SPC700::op_and>(); + case 0x38: return op_write_dp_const<&SPC700::op_and>(); + case 0x39: return op_write_ix_iy<&SPC700::op_and>(); + case 0x3a: return op_adjust_dpw(+1); + case 0x3b: return op_adjust_dpx<&SPC700::op_rol>(); + case 0x3c: return op_adjust<&SPC700::op_rol>(regs.a); + case 0x3d: return op_adjust<&SPC700::op_inc>(regs.x); + case 0x3e: return op_read_dp<&SPC700::op_cmp>(regs.x); + case 0x3f: return op_jsr_addr(); + case 0x40: return op_set_flag(regs.p.p, 1); + case 0x41: return op_jst(); + case 0x42: return op_set_bit(); + case 0x43: return op_branch_bit(); + case 0x44: return op_read_dp<&SPC700::op_eor>(regs.a); + case 0x45: return op_read_addr<&SPC700::op_eor>(regs.a); + case 0x46: return op_read_ix<&SPC700::op_eor>(); + case 0x47: return op_read_idpx<&SPC700::op_eor>(); + case 0x48: return op_read_const<&SPC700::op_eor>(regs.a); + case 0x49: return op_write_dp_dp<&SPC700::op_eor>(); + case 0x4a: return op_set_addr_bit(); + case 0x4b: return op_adjust_dp<&SPC700::op_lsr>(); + case 0x4c: return op_adjust_addr<&SPC700::op_lsr>(); + case 0x4d: return op_push(regs.x); + case 0x4e: return op_test_addr(0); + case 0x4f: return op_jsp_dp(); + case 0x50: return op_branch(regs.p.v == 0); + case 0x51: return op_jst(); + case 0x52: return op_set_bit(); + case 0x53: return op_branch_bit(); + case 0x54: return op_read_dpi<&SPC700::op_eor>(regs.a, regs.x); + case 0x55: return op_read_addri<&SPC700::op_eor>(regs.x); + case 0x56: return op_read_addri<&SPC700::op_eor>(regs.y); + case 0x57: return op_read_idpy<&SPC700::op_eor>(); + case 0x58: return op_write_dp_const<&SPC700::op_eor>(); + case 0x59: return op_write_ix_iy<&SPC700::op_eor>(); + case 0x5a: return op_read_dpw<&SPC700::op_cpw>(); + case 0x5b: return op_adjust_dpx<&SPC700::op_lsr>(); + case 0x5c: return op_adjust<&SPC700::op_lsr>(regs.a); + case 0x5d: return op_transfer(regs.a, regs.x); + case 0x5e: return op_read_addr<&SPC700::op_cmp>(regs.y); + case 0x5f: return op_jmp_addr(); + case 0x60: return op_set_flag(regs.p.c, 0); + case 0x61: return op_jst(); + case 0x62: return op_set_bit(); + case 0x63: return op_branch_bit(); + case 0x64: return op_read_dp<&SPC700::op_cmp>(regs.a); + case 0x65: return op_read_addr<&SPC700::op_cmp>(regs.a); + case 0x66: return op_read_ix<&SPC700::op_cmp>(); + case 0x67: return op_read_idpx<&SPC700::op_cmp>(); + case 0x68: return op_read_const<&SPC700::op_cmp>(regs.a); + case 0x69: return op_write_dp_dp<&SPC700::op_cmp>(); + case 0x6a: return op_set_addr_bit(); + case 0x6b: return op_adjust_dp<&SPC700::op_ror>(); + case 0x6c: return op_adjust_addr<&SPC700::op_ror>(); + case 0x6d: return op_push(regs.y); + case 0x6e: return op_bne_dpdec(); + case 0x6f: return op_rts(); + case 0x70: return op_branch(regs.p.v == 1); + case 0x71: return op_jst(); + case 0x72: return op_set_bit(); + case 0x73: return op_branch_bit(); + case 0x74: return op_read_dpi<&SPC700::op_cmp>(regs.a, regs.x); + case 0x75: return op_read_addri<&SPC700::op_cmp>(regs.x); + case 0x76: return op_read_addri<&SPC700::op_cmp>(regs.y); + case 0x77: return op_read_idpy<&SPC700::op_cmp>(); + case 0x78: return op_write_dp_const<&SPC700::op_cmp>(); + case 0x79: return op_write_ix_iy<&SPC700::op_cmp>(); + case 0x7a: return op_read_dpw<&SPC700::op_adw>(); + case 0x7b: return op_adjust_dpx<&SPC700::op_ror>(); + case 0x7c: return op_adjust<&SPC700::op_ror>(regs.a); + case 0x7d: return op_transfer(regs.x, regs.a); + case 0x7e: return op_read_dp<&SPC700::op_cmp>(regs.y); + case 0x7f: return op_rti(); + case 0x80: return op_set_flag(regs.p.c, 1); + case 0x81: return op_jst(); + case 0x82: return op_set_bit(); + case 0x83: return op_branch_bit(); + case 0x84: return op_read_dp<&SPC700::op_adc>(regs.a); + case 0x85: return op_read_addr<&SPC700::op_adc>(regs.a); + case 0x86: return op_read_ix<&SPC700::op_adc>(); + case 0x87: return op_read_idpx<&SPC700::op_adc>(); + case 0x88: return op_read_const<&SPC700::op_adc>(regs.a); + case 0x89: return op_write_dp_dp<&SPC700::op_adc>(); + case 0x8a: return op_set_addr_bit(); + case 0x8b: return op_adjust_dp<&SPC700::op_dec>(); + case 0x8c: return op_adjust_addr<&SPC700::op_dec>(); + case 0x8d: return op_read_const<&SPC700::op_ld>(regs.y); + case 0x8e: return op_plp(); + case 0x8f: return op_write_dp_const<&SPC700::op_st>(); + case 0x90: return op_branch(regs.p.c == 0); + case 0x91: return op_jst(); + case 0x92: return op_set_bit(); + case 0x93: return op_branch_bit(); + case 0x94: return op_read_dpi<&SPC700::op_adc>(regs.a, regs.x); + case 0x95: return op_read_addri<&SPC700::op_adc>(regs.x); + case 0x96: return op_read_addri<&SPC700::op_adc>(regs.y); + case 0x97: return op_read_idpy<&SPC700::op_adc>(); + case 0x98: return op_write_dp_const<&SPC700::op_adc>(); + case 0x99: return op_write_ix_iy<&SPC700::op_adc>(); + case 0x9a: return op_read_dpw<&SPC700::op_sbw>(); + case 0x9b: return op_adjust_dpx<&SPC700::op_dec>(); + case 0x9c: return op_adjust<&SPC700::op_dec>(regs.a); + case 0x9d: return op_transfer(regs.s, regs.x); + case 0x9e: return op_div_ya_x(); + case 0x9f: return op_xcn(); + case 0xa0: return op_set_flag(regs.p.i, 1); + case 0xa1: return op_jst(); + case 0xa2: return op_set_bit(); + case 0xa3: return op_branch_bit(); + case 0xa4: return op_read_dp<&SPC700::op_sbc>(regs.a); + case 0xa5: return op_read_addr<&SPC700::op_sbc>(regs.a); + case 0xa6: return op_read_ix<&SPC700::op_sbc>(); + case 0xa7: return op_read_idpx<&SPC700::op_sbc>(); + case 0xa8: return op_read_const<&SPC700::op_sbc>(regs.a); + case 0xa9: return op_write_dp_dp<&SPC700::op_sbc>(); + case 0xaa: return op_set_addr_bit(); + case 0xab: return op_adjust_dp<&SPC700::op_inc>(); + case 0xac: return op_adjust_addr<&SPC700::op_inc>(); + case 0xad: return op_read_const<&SPC700::op_cmp>(regs.y); + case 0xae: return op_pull(regs.a); + case 0xaf: return op_sta_ixinc(); + case 0xb0: return op_branch(regs.p.c == 1); + case 0xb1: return op_jst(); + case 0xb2: return op_set_bit(); + case 0xb3: return op_branch_bit(); + case 0xb4: return op_read_dpi<&SPC700::op_sbc>(regs.a, regs.x); + case 0xb5: return op_read_addri<&SPC700::op_sbc>(regs.x); + case 0xb6: return op_read_addri<&SPC700::op_sbc>(regs.y); + case 0xb7: return op_read_idpy<&SPC700::op_sbc>(); + case 0xb8: return op_write_dp_const<&SPC700::op_sbc>(); + case 0xb9: return op_write_ix_iy<&SPC700::op_sbc>(); + case 0xba: return op_read_dpw<&SPC700::op_ldw>(); + case 0xbb: return op_adjust_dpx<&SPC700::op_inc>(); + case 0xbc: return op_adjust<&SPC700::op_inc>(regs.a); + case 0xbd: return op_transfer(regs.x, regs.s); + case 0xbe: return op_das(); + case 0xbf: return op_lda_ixinc(); + case 0xc0: return op_set_flag(regs.p.i, 0); + case 0xc1: return op_jst(); + case 0xc2: return op_set_bit(); + case 0xc3: return op_branch_bit(); + case 0xc4: return op_write_dp(regs.a); + case 0xc5: return op_write_addr(regs.a); + case 0xc6: return op_sta_ix(); + case 0xc7: return op_sta_idpx(); + case 0xc8: return op_read_const<&SPC700::op_cmp>(regs.x); + case 0xc9: return op_write_addr(regs.x); + case 0xca: return op_set_addr_bit(); + case 0xcb: return op_write_dp(regs.y); + case 0xcc: return op_write_addr(regs.y); + case 0xcd: return op_read_const<&SPC700::op_ld>(regs.x); + case 0xce: return op_pull(regs.x); + case 0xcf: return op_mul_ya(); + case 0xd0: return op_branch(regs.p.z == 0); + case 0xd1: return op_jst(); + case 0xd2: return op_set_bit(); + case 0xd3: return op_branch_bit(); + case 0xd4: return op_write_dpi(regs.a, regs.x); + case 0xd5: return op_write_addri(regs.x); + case 0xd6: return op_write_addri(regs.y); + case 0xd7: return op_sta_idpy(); + case 0xd8: return op_write_dp(regs.x); + case 0xd9: return op_write_dpi(regs.x, regs.y); + case 0xda: return op_stw_dp(); + case 0xdb: return op_write_dpi(regs.y, regs.x); + case 0xdc: return op_adjust<&SPC700::op_dec>(regs.y); + case 0xdd: return op_transfer(regs.y, regs.a); + case 0xde: return op_bne_dpx(); + case 0xdf: return op_daa(); + case 0xe0: return op_clv(); + case 0xe1: return op_jst(); + case 0xe2: return op_set_bit(); + case 0xe3: return op_branch_bit(); + case 0xe4: return op_read_dp<&SPC700::op_ld>(regs.a); + case 0xe5: return op_read_addr<&SPC700::op_ld>(regs.a); + case 0xe6: return op_read_ix<&SPC700::op_ld>(); + case 0xe7: return op_read_idpx<&SPC700::op_ld>(); + case 0xe8: return op_read_const<&SPC700::op_ld>(regs.a); + case 0xe9: return op_read_addr<&SPC700::op_ld>(regs.x); + case 0xea: return op_set_addr_bit(); + case 0xeb: return op_read_dp<&SPC700::op_ld>(regs.y); + case 0xec: return op_read_addr<&SPC700::op_ld>(regs.y); + case 0xed: return op_cmc(); + case 0xee: return op_pull(regs.y); + case 0xef: return op_wait(); + case 0xf0: return op_branch(regs.p.z == 1); + case 0xf1: return op_jst(); + case 0xf2: return op_set_bit(); + case 0xf3: return op_branch_bit(); + case 0xf4: return op_read_dpi<&SPC700::op_ld>(regs.a, regs.x); + case 0xf5: return op_read_addri<&SPC700::op_ld>(regs.x); + case 0xf6: return op_read_addri<&SPC700::op_ld>(regs.y); + case 0xf7: return op_read_idpy<&SPC700::op_ld>(); + case 0xf8: return op_read_dp<&SPC700::op_ld>(regs.x); + case 0xf9: return op_read_dpi<&SPC700::op_ld>(regs.x, regs.y); + case 0xfa: return op_write_dp_dp<&SPC700::op_st>(); + case 0xfb: return op_read_dpi<&SPC700::op_ld>(regs.y, regs.x); + case 0xfc: return op_adjust<&SPC700::op_inc>(regs.y); + case 0xfd: return op_transfer(regs.a, regs.y); + case 0xfe: return op_bne_ydec(); + case 0xff: return op_wait(); + } +} + +} diff --git a/Frameworks/GME/gme/higan/processor/spc700/spc700.hpp b/Frameworks/GME/gme/higan/processor/spc700/spc700.hpp new file mode 100755 index 000000000..490ff0266 --- /dev/null +++ b/Frameworks/GME/gme/higan/processor/spc700/spc700.hpp @@ -0,0 +1,106 @@ +#ifndef PROCESSOR_SPC700_HPP +#define PROCESSOR_SPC700_HPP + +#include + +namespace Processor { + +struct SPC700 { + virtual void op_io() = 0; + virtual uint8_t op_read(uint16_t addr) = 0; + virtual void op_write(uint16_t addr, uint8_t data) = 0; + void op_step(); + + virtual uint8_t disassembler_read(uint16_t addr) = 0; + + #include "registers.hpp" + #include "memory.hpp" + + regs_t regs; + word_t dp, sp, rd, wr, bit, ya; + uint8_t opcode; + +protected: + uint8_t op_adc(uint8_t, uint8_t); + uint8_t op_and(uint8_t, uint8_t); + uint8_t op_asl(uint8_t); + uint8_t op_cmp(uint8_t, uint8_t); + uint8_t op_dec(uint8_t); + uint8_t op_eor(uint8_t, uint8_t); + uint8_t op_inc(uint8_t); + uint8_t op_ld (uint8_t, uint8_t); + uint8_t op_lsr(uint8_t); + uint8_t op_or (uint8_t, uint8_t); + uint8_t op_rol(uint8_t); + uint8_t op_ror(uint8_t); + uint8_t op_sbc(uint8_t, uint8_t); + uint8_t op_st (uint8_t, uint8_t); + uint16_t op_adw(uint16_t, uint16_t); + uint16_t op_cpw(uint16_t, uint16_t); + uint16_t op_ldw(uint16_t, uint16_t); + uint16_t op_sbw(uint16_t, uint16_t); + + template void op_adjust(uint8_t&); + template void op_adjust_addr(); + template void op_adjust_dp(); + void op_adjust_dpw(signed); + template void op_adjust_dpx(); + void op_branch(bool); + void op_branch_bit(); + void op_pull(uint8_t&); + void op_push(uint8_t); + template void op_read_addr(uint8_t&); + template void op_read_addri(uint8_t&); + template void op_read_const(uint8_t&); + template void op_read_dp(uint8_t&); + template void op_read_dpi(uint8_t&, uint8_t&); + template void op_read_dpw(); + template void op_read_idpx(); + template void op_read_idpy(); + template void op_read_ix(); + void op_set_addr_bit(); + void op_set_bit(); + void op_set_flag(bool&, bool); + void op_test_addr(bool); + void op_transfer(uint8_t&, uint8_t&); + void op_write_addr(uint8_t&); + void op_write_addri(uint8_t&); + void op_write_dp(uint8_t&); + void op_write_dpi(uint8_t&, uint8_t&); + template void op_write_dp_const(); + template void op_write_dp_dp(); + template void op_write_ix_iy(); + + void op_bne_dp(); + void op_bne_dpdec(); + void op_bne_dpx(); + void op_bne_ydec(); + void op_brk(); + void op_clv(); + void op_cmc(); + void op_daa(); + void op_das(); + void op_div_ya_x(); + void op_jmp_addr(); + void op_jmp_iaddrx(); + void op_jsp_dp(); + void op_jsr_addr(); + void op_jst(); + void op_lda_ixinc(); + void op_mul_ya(); + void op_nop(); + void op_plp(); + void op_rti(); + void op_rts(); + void op_sta_idpx(); + void op_sta_idpy(); + void op_sta_ix(); + void op_sta_ixinc(); + void op_stw_dp(); + void op_wait(); + void op_xcn(); +}; + +} + +#endif diff --git a/Frameworks/GME/gme/higan/smp.cpp b/Frameworks/GME/gme/higan/smp.cpp new file mode 100755 index 000000000..d4fac63f4 --- /dev/null +++ b/Frameworks/GME/gme/higan/smp.cpp @@ -0,0 +1,113 @@ +#define CYCLE_ACCURATE + +#include "smp.hpp" + +#define SMP_CPP +namespace SuperFamicom { + +#include "algorithms.cpp" +#include "core.cpp" +#include "memory.cpp" +#include "timing.cpp" + +void SMP::synchronize_dsp() { + while(dsp.clock < 0) dsp.enter(); +} + +void SMP::enter() { + while(clock < 0 && sample_buffer < sample_buffer_end) op_step(); +} + +void SMP::render(int16_t * buffer, unsigned count) +{ + while (count > 4096) { + sample_buffer = buffer; + sample_buffer_end = buffer + 4096; + buffer += 4096; + count -= 4096; + clock -= 32 * 24 * 4096; + enter(); + } + sample_buffer = buffer; + sample_buffer_end = buffer + count; + clock -= 32 * 24 * count; + enter(); +} + +void SMP::skip(unsigned count) +{ + while (count > 4096) { + sample_buffer = 0; + sample_buffer_end = (const int16_t *) 4096; + count -= 4096; + clock -= 32 * 24 * 4096; + enter(); + } + sample_buffer = 0; + sample_buffer_end = (const int16_t *) (intptr_t) count; + clock -= 32 * 24 * count; + enter(); +} + +void SMP::sample(int16_t left, int16_t right) +{ + if ( sample_buffer >= (const int16_t *) (intptr_t) 4096 ) { + if ( sample_buffer < sample_buffer_end ) *sample_buffer++ = left; + if ( sample_buffer < sample_buffer_end ) *sample_buffer++ = right; + } + else if ( sample_buffer < sample_buffer_end ){ + sample_buffer += 2; + } +} + +void SMP::power() { + timer0.target = 0; + timer1.target = 0; + timer2.target = 0; + + reset(); + + dsp.power(); +} + +void SMP::reset() { + for(unsigned n = 0x0000; n <= 0xffff; n++) apuram[n] = 0x00; + + opcode_number = 0; + opcode_cycle = 0; + + regs.pc = 0xffc0; + regs.sp = 0xef; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.p = 0x02; + + //$00f1 + status.iplrom_enable = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.ram00f8 = 0x00; + status.ram00f9 = 0x00; + + //timers + timer0.enable = timer1.enable = timer2.enable = false; + timer0.stage1_ticks = timer1.stage1_ticks = timer2.stage1_ticks = 0; + timer0.stage2_ticks = timer1.stage2_ticks = timer2.stage2_ticks = 0; + timer0.stage3_ticks = timer1.stage3_ticks = timer2.stage3_ticks = 0; +} + +SMP::SMP() : dsp( *this ), clock( 0 ) { + apuram = new uint8_t[64 * 1024]; + for(auto& byte : iplrom) byte = 0; + set_sfm_queue(0, 0); +} + +SMP::~SMP() { + delete [] apuram; +} + +} diff --git a/Frameworks/GME/gme/higan/smp.hpp b/Frameworks/GME/gme/higan/smp.hpp new file mode 100755 index 000000000..bd1a5714e --- /dev/null +++ b/Frameworks/GME/gme/higan/smp.hpp @@ -0,0 +1,144 @@ +#ifndef _higan_smp_h_ +#define _higan_smp_h_ + +#include "blargg_common.h" + +#include "../dsp/dsp.hpp" + +namespace SuperFamicom { + +struct SMP { + long clock; + + uint8_t iplrom[64]; + uint8_t* apuram; + + SuperFamicom::DSP dsp; + + inline void synchronize_dsp(); + + unsigned port_read(unsigned port); + void port_write(unsigned port, unsigned data); + + unsigned mmio_read(unsigned addr); + void mmio_write(unsigned addr, unsigned data); + + void enter(); + void power(); + void reset(); + + void render(int16_t * buffer, unsigned count); + void skip(unsigned count); + + uint8_t sfm_last[4]; +private: + uint8_t const* sfm_queue; + uint8_t const* sfm_queue_end; +public: + void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end); + +private: + int16_t * sample_buffer; + int16_t const* sample_buffer_end; +public: + void sample( int16_t, int16_t ); + + SMP(); + ~SMP(); + +//private: + struct Flags { + bool n, v, p, b, h, i, z, c; + + inline operator unsigned() const { + return (n << 7) | (v << 6) | (p << 5) | (b << 4) + | (h << 3) | (i << 2) | (z << 1) | (c << 0); + }; + + inline unsigned operator=(unsigned data) { + n = data & 0x80; v = data & 0x40; p = data & 0x20; b = data & 0x10; + h = data & 0x08; i = data & 0x04; z = data & 0x02; c = data & 0x01; + return data; + } + + inline unsigned operator|=(unsigned data) { return operator=(operator unsigned() | data); } + inline unsigned operator^=(unsigned data) { return operator=(operator unsigned() ^ data); } + inline unsigned operator&=(unsigned data) { return operator=(operator unsigned() & data); } + }; + + unsigned opcode_number; + unsigned opcode_cycle; + + struct Regs { + uint16_t pc; + uint8_t sp; + union { + uint16_t ya; +#ifdef BLARGG_BIG_ENDIAN + struct { uint8_t y, a; }; +#else + struct { uint8_t a, y; }; +#endif + }; + uint8_t x; + Flags p; + } regs; + + uint16_t rd, wr, dp, sp, ya, bit; + + struct Status { + //$00f1 + bool iplrom_enable; + + //$00f2 + unsigned dsp_addr; + + //$00f8,$00f9 + unsigned ram00f8; + unsigned ram00f9; + } status; + + template + struct Timer { + bool enable; + uint8_t target; + uint8_t stage1_ticks; + uint8_t stage2_ticks; + uint8_t stage3_ticks; + + void tick(); + void tick(unsigned clocks); + }; + + Timer<128> timer0; + Timer<128> timer1; + Timer< 16> timer2; + + void tick(); + inline void op_io(); + inline uint8_t op_read(uint16_t addr); + inline void op_write(uint16_t addr, uint8_t data); + inline void op_step(); + static const unsigned cycle_count_table[256]; + + uint8_t op_adc (uint8_t x, uint8_t y); + uint16_t op_addw(uint16_t x, uint16_t y); + uint8_t op_and (uint8_t x, uint8_t y); + uint8_t op_cmp (uint8_t x, uint8_t y); + uint16_t op_cmpw(uint16_t x, uint16_t y); + uint8_t op_eor (uint8_t x, uint8_t y); + uint8_t op_inc (uint8_t x); + uint8_t op_dec (uint8_t x); + uint8_t op_or (uint8_t x, uint8_t y); + uint8_t op_sbc (uint8_t x, uint8_t y); + uint16_t op_subw(uint16_t x, uint16_t y); + uint8_t op_asl (uint8_t x); + uint8_t op_lsr (uint8_t x); + uint8_t op_rol (uint8_t x); + uint8_t op_ror (uint8_t x); +}; + +inline void SMP::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end) { sfm_queue = queue; sfm_queue_end = queue_end; sfm_last[0] = 0; sfm_last[1] = 0; sfm_last[2] = 0; sfm_last[3] = 0; } +}; + +#endif diff --git a/Frameworks/GME/gme/higan/smp/memory.cpp b/Frameworks/GME/gme/higan/smp/memory.cpp new file mode 100755 index 000000000..ef2d3aac0 --- /dev/null +++ b/Frameworks/GME/gme/higan/smp/memory.cpp @@ -0,0 +1,200 @@ +#ifdef SMP_CPP + +inline uint8_t SMP::ram_read(uint16_t addr) { + if(addr >= 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f]; + if(status.ram_disable) return 0x5a; //0xff on mini-SNES + return apuram[addr]; +} + +inline void SMP::ram_write(uint16_t addr, uint8_t data) { + //writes to $ffc0-$ffff always go to apuram, even if the iplrom is enabled + if(status.ram_writable && !status.ram_disable) apuram[addr] = data; +} + +uint8_t SMP::port_read(uint8_t port) const { + return apuram[0xf4 + (port & 3)]; +} + +void SMP::port_write(uint8_t port, uint8_t data) { + apuram[0xf4 + (port & 3)] = data; +} + +uint8_t SMP::op_busread(uint16_t addr) { + unsigned result; + + switch(addr) { + case 0xf0: //TEST -- write-only register + return 0x00; + + case 0xf1: //CONTROL -- write-only register + return 0x00; + + case 0xf2: //DSPADDR + return status.dsp_addr; + + case 0xf3: //DSPDATA + //0x80-0xff are read-only mirrors of 0x00-0x7f + return dsp.read(status.dsp_addr & 0x7f); + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: //CPUIO3 + if (sfm_queue && sfm_queue < sfm_queue_end) { + sfm_last[addr - 0xf4] = *sfm_queue; + return *sfm_queue++; + } + return sfm_last[addr - 0xf4]; + + case 0xf8: //RAM0 + return status.ram00f8; + + case 0xf9: //RAM1 + return status.ram00f9; + + case 0xfa: //T0TARGET + case 0xfb: //T1TARGET + case 0xfc: //T2TARGET -- write-only registers + return 0x00; + + case 0xfd: //T0OUT -- 4-bit counter value + result = timer0.stage3_ticks; + timer0.stage3_ticks = 0; + return result; + + case 0xfe: //T1OUT -- 4-bit counter value + result = timer1.stage3_ticks; + timer1.stage3_ticks = 0; + return result; + + case 0xff: //T2OUT -- 4-bit counter value + result = timer2.stage3_ticks; + timer2.stage3_ticks = 0; + return result; + } + + return ram_read(addr); +} + +void SMP::op_buswrite(uint16_t addr, uint8_t data) { + switch(addr) { + case 0xf0: //TEST + if(regs.p.p) break; //writes only valid when P flag is clear + + status.clock_speed = (data >> 6) & 3; + status.timer_speed = (data >> 4) & 3; + status.timers_enable = data & 0x08; + status.ram_disable = data & 0x04; + status.ram_writable = data & 0x02; + status.timers_disable = data & 0x01; + + status.timer_step = (1 << status.clock_speed) + (2 << status.timer_speed); + + timer0.synchronize_stage1(); + timer1.synchronize_stage1(); + timer2.synchronize_stage1(); + break; + + case 0xf1: //CONTROL + status.iplrom_enable = data & 0x80; + + if ( data & 0x10 ) { + sfm_last [ 0 ] = 0; + sfm_last [ 1 ] = 0; + } + if ( data & 0x20 ) { + sfm_last [ 2 ] = 0; + sfm_last [ 3 ] = 0; + } + + //0->1 transistion resets timers + if(timer2.enable == false && (data & 0x04)) { + timer2.stage2_ticks = 0; + timer2.stage3_ticks = 0; + } + timer2.enable = data & 0x04; + + if(timer1.enable == false && (data & 0x02)) { + timer1.stage2_ticks = 0; + timer1.stage3_ticks = 0; + } + timer1.enable = data & 0x02; + + if(timer0.enable == false && (data & 0x01)) { + timer0.stage2_ticks = 0; + timer0.stage3_ticks = 0; + } + timer0.enable = data & 0x01; + break; + + case 0xf2: //DSPADDR + status.dsp_addr = data; + break; + + case 0xf3: //DSPDATA + if(status.dsp_addr & 0x80) break; //0x80-0xff are read-only mirrors of 0x00-0x7f + dsp.write(status.dsp_addr & 0x7f, data); + break; + + case 0xf4: //CPUIO0 + case 0xf5: //CPUIO1 + case 0xf6: //CPUIO2 + case 0xf7: //CPUIO3 + port_write(addr, data); + break; + + case 0xf8: //RAM0 + status.ram00f8 = data; + break; + + case 0xf9: //RAM1 + status.ram00f9 = data; + break; + + case 0xfa: //T0TARGET + timer0.target = data; + break; + + case 0xfb: //T1TARGET + timer1.target = data; + break; + + case 0xfc: //T2TARGET + timer2.target = data; + break; + + case 0xfd: //T0OUT + case 0xfe: //T1OUT + case 0xff: //T2OUT -- read-only registers + break; + } + + ram_write(addr, data); //all writes, even to MMIO registers, appear on bus +} + +void SMP::op_io() { + add_clocks(24); + cycle_edge(); +} + +uint8_t SMP::op_read(uint16_t addr) { + add_clocks(12); + uint8_t r = op_busread(addr); + add_clocks(12); + cycle_edge(); + return r; +} + +void SMP::op_write(uint16_t addr, uint8_t data) { + add_clocks(24); + op_buswrite(addr, data); + cycle_edge(); +} + +uint8_t SMP::disassembler_read(uint16_t addr) { + if((addr & 0xfff0) == 0x00f0) return 0x00; + if((addr & 0xffc0) == 0xffc0 && status.iplrom_enable) return iplrom[addr & 0x3f]; + return apuram[addr]; +} + +#endif diff --git a/Frameworks/GME/gme/higan/smp/smp.cpp b/Frameworks/GME/gme/higan/smp/smp.cpp new file mode 100755 index 000000000..5abb8c6e1 --- /dev/null +++ b/Frameworks/GME/gme/higan/smp/smp.cpp @@ -0,0 +1,146 @@ +#include "smp.hpp" + +#include + +#define SMP_CPP +namespace SuperFamicom { + +#include "memory.cpp" +#include "timing.cpp" + +void SMP::step(unsigned clocks) { + clock += clocks; + dsp.clock -= clocks; +} + +void SMP::synchronize_dsp() { + while(dsp.clock < 0 && sample_buffer < sample_buffer_end) dsp.enter(); +} + +void SMP::enter() { + while(status.clock_speed != 2 && sample_buffer < sample_buffer_end) op_step(); + if (status.clock_speed == 2) { + synchronize_dsp(); + } +} + +void SMP::render(int16_t * buffer, unsigned count) { + while (count > 4096) { + sample_buffer = buffer; + sample_buffer_end = buffer + 4096; + buffer += 4096; + count -= 4096; + enter(); + } + sample_buffer = buffer; + sample_buffer_end = buffer + count; + enter(); +} + +void SMP::skip(unsigned count) { + while (count > 4096) { + sample_buffer = 0; + sample_buffer_end = (const int16_t *) 4096; + count -= 4096; + enter(); + } + sample_buffer = 0; + sample_buffer_end = (const int16_t *) (intptr_t) count; + enter(); +} + +void SMP::sample(int16_t left, int16_t right) { + if ( sample_buffer >= (const int16_t *) (intptr_t) 4096 ) { + if ( sample_buffer < sample_buffer_end ) *sample_buffer++ = left; + if ( sample_buffer < sample_buffer_end ) *sample_buffer++ = right; + } + else if ( sample_buffer < sample_buffer_end ){ + sample_buffer += 2; + } +} + +void SMP::power() { + //targets not initialized/changed upon reset + timer0.target = 0; + timer1.target = 0; + timer2.target = 0; + + dsp.power(); + + reset(); +} + +void SMP::reset() { + regs.pc = 0xffc0; + regs.a = 0x00; + regs.x = 0x00; + regs.y = 0x00; + regs.s = 0xef; + regs.p = 0x02; + + for(auto& n : apuram) n = rand(); + apuram[0x00f4] = 0x00; + apuram[0x00f5] = 0x00; + apuram[0x00f6] = 0x00; + apuram[0x00f7] = 0x00; + + status.clock_counter = 0; + status.dsp_counter = 0; + status.timer_step = 3; + + //$00f0 + status.clock_speed = 0; + status.timer_speed = 0; + status.timers_enable = true; + status.ram_disable = false; + status.ram_writable = true; + status.timers_disable = false; + + //$00f1 + status.iplrom_enable = true; + + //$00f2 + status.dsp_addr = 0x00; + + //$00f8,$00f9 + status.ram00f8 = 0x00; + status.ram00f9 = 0x00; + + timer0.stage0_ticks = 0; + timer1.stage0_ticks = 0; + timer2.stage0_ticks = 0; + + timer0.stage1_ticks = 0; + timer1.stage1_ticks = 0; + timer2.stage1_ticks = 0; + + timer0.stage2_ticks = 0; + timer1.stage2_ticks = 0; + timer2.stage2_ticks = 0; + + timer0.stage3_ticks = 0; + timer1.stage3_ticks = 0; + timer2.stage3_ticks = 0; + + timer0.current_line = 0; + timer1.current_line = 0; + timer2.current_line = 0; + + timer0.enable = false; + timer1.enable = false; + timer2.enable = false; + + dsp.reset(); +} + +SMP::SMP() : dsp( *this ), timer0( *this ), timer1( *this ), timer2( *this ), clock( 0 ) { + for(auto& byte : iplrom) byte = 0; + set_sfm_queue(0, 0); + f = fopen("/tmp/fucko", "w"); +} + +SMP::~SMP() { + fclose( f ); +} + +} diff --git a/Frameworks/GME/gme/higan/smp/smp.hpp b/Frameworks/GME/gme/higan/smp/smp.hpp new file mode 100755 index 000000000..a321b3566 --- /dev/null +++ b/Frameworks/GME/gme/higan/smp/smp.hpp @@ -0,0 +1,123 @@ +#ifndef _higan_smp_h_ +#define _higan_smp_h_ + +#include "blargg_common.h" + +#include "../processor/spc700/spc700.hpp" + +#include "../dsp/dsp.hpp" + +#include + +namespace SuperFamicom { + +struct SMP : Processor::SPC700 { + long clock; + + uint8_t iplrom[64]; + uint8_t apuram[64 * 1024]; + + SuperFamicom::DSP dsp; + + inline void step(unsigned clocks); + inline void synchronize_dsp(); + + uint8_t port_read(uint8_t port) const; + void port_write(uint8_t port, uint8_t data); + + void enter(); + void power(); + void reset(); + + void render(int16_t * buffer, unsigned count); + void skip(unsigned count); + + FILE *f; + + uint8_t sfm_last[4]; +private: + uint8_t const* sfm_queue; + uint8_t const* sfm_queue_end; +public: + void set_sfm_queue(const uint8_t* queue, const uint8_t* queue_end); + +private: + int16_t * sample_buffer; + int16_t const* sample_buffer_end; +public: + void sample( int16_t, int16_t ); + + SMP(); + ~SMP(); + + struct { + //timing + unsigned clock_counter; + unsigned dsp_counter; + unsigned timer_step; + + //$00f0 + uint8_t clock_speed; + uint8_t timer_speed; + bool timers_enable; + bool ram_disable; + bool ram_writable; + bool timers_disable; + + //$00f1 + bool iplrom_enable; + + //$00f2 + uint8_t dsp_addr; + + //$00f8,$00f9 + uint8_t ram00f8; + uint8_t ram00f9; + } status; + + friend class SMPcore; + + //memory.cpp + uint8_t ram_read(uint16_t addr); + void ram_write(uint16_t addr, uint8_t data); + + uint8_t op_busread(uint16_t addr); + void op_buswrite(uint16_t addr, uint8_t data); + + void op_io(); + uint8_t op_read(uint16_t addr); + void op_write(uint16_t addr, uint8_t data); + + uint8_t disassembler_read(uint16_t addr); + + //timing.cpp + template + struct Timer { + SMP &smp; + uint8_t stage0_ticks; + uint8_t stage1_ticks; + uint8_t stage2_ticks; + uint8_t stage3_ticks; + bool current_line; + bool enable; + uint8_t target; + + Timer(SMP &p_smp) : smp( p_smp ) { } + + void tick(); + void synchronize_stage1(); + }; + + Timer<192> timer0; + Timer<192> timer1; + Timer< 24> timer2; + + inline void add_clocks(unsigned clocks); + inline void cycle_edge(); +}; + +inline void SMP::set_sfm_queue(const uint8_t *queue, const uint8_t *queue_end) { sfm_queue = queue; sfm_queue_end = queue_end; sfm_last[0] = 0; sfm_last[1] = 0; sfm_last[2] = 0; sfm_last[3] = 0; } + +}; + +#endif diff --git a/Frameworks/GME/gme/higan/smp/timing.cpp b/Frameworks/GME/gme/higan/smp/timing.cpp new file mode 100755 index 000000000..c65de6c7f --- /dev/null +++ b/Frameworks/GME/gme/higan/smp/timing.cpp @@ -0,0 +1,54 @@ +#ifdef SMP_CPP + +void SMP::add_clocks(unsigned clocks) { + step(clocks); + synchronize_dsp(); +} + +void SMP::cycle_edge() { + timer0.tick(); + timer1.tick(); + timer2.tick(); + + //TEST register S-SMP speed control + //24 clocks have already been added for this cycle at this point + switch(status.clock_speed) { + case 0: break; //100% speed + case 1: add_clocks(24); break; // 50% speed + case 2: clock = 0; break; // 0% speed -- locks S-SMP + case 3: add_clocks(24 * 9); break; // 10% speed + } +} + +template +void SMP::Timer::tick() { + //stage 0 increment + stage0_ticks += smp.status.timer_step; + if(stage0_ticks < timer_frequency) return; + stage0_ticks -= timer_frequency; + + //stage 1 increment + stage1_ticks ^= 1; + synchronize_stage1(); +} + +template +void SMP::Timer::synchronize_stage1() { + bool new_line = stage1_ticks; + if(smp.status.timers_enable == false) new_line = false; + if(smp.status.timers_disable == true) new_line = false; + + bool old_line = current_line; + current_line = new_line; + if(old_line != 1 || new_line != 0) return; //only pulse on 1->0 transition + + //stage 2 increment + if(enable == false) return; + if(++stage2_ticks != target) return; + + //stage 3 increment + stage2_ticks = 0; + stage3_ticks = (stage3_ticks + 1) & 15; +} + +#endif diff --git a/Plugins/GME/GameDecoder.m b/Plugins/GME/GameDecoder.m index 8a374e866..774a50a37 100755 --- a/Plugins/GME/GameDecoder.m +++ b/Plugins/GME/GameDecoder.m @@ -167,7 +167,7 @@ gme_err_t readCallback( void* data, void* out, long count ) + (NSArray *)fileTypes { - return [NSArray arrayWithObjects:@"ay", @"gbs", @"hes", @"kss", @"nsf", @"nsfe", @"sap", @"sgc", @"spc", @"vgm", @"vgz", nil]; + return [NSArray arrayWithObjects:@"ay", @"gbs", @"hes", @"kss", @"nsf", @"nsfe", @"sap", @"sfm", @"sgc", @"spc", @"vgm", @"vgz", nil]; } + (NSArray *)mimeTypes