Replaced snes_spc with higan accurate core, and enabled SFM support
parent
d9971ee32f
commit
cae86b582f
|
@ -2505,6 +2505,7 @@
|
|||
C01FCF4B08A954540054247B /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
CODE_SIGN_IDENTITY = "";
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
COPY_PHASE_STRIP = NO;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
|
|
|
@ -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 = "<group>"; };
|
||||
17C8F1DD0CBED286008D969D /* Sms_Apu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Sms_Apu.cpp; path = gme/Sms_Apu.cpp; sourceTree = "<group>"; };
|
||||
17C8F1DE0CBED286008D969D /* Sms_Apu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Sms_Apu.h; path = gme/Sms_Apu.h; sourceTree = "<group>"; };
|
||||
17C8F1E00CBED286008D969D /* Snes_Spc.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Snes_Spc.cpp; path = gme/Snes_Spc.cpp; sourceTree = "<group>"; };
|
||||
17C8F1E10CBED286008D969D /* Snes_Spc.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Snes_Spc.h; path = gme/Snes_Spc.h; sourceTree = "<group>"; };
|
||||
17C8F1E20CBED286008D969D /* Spc_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Cpu.cpp; path = gme/Spc_Cpu.cpp; sourceTree = "<group>"; };
|
||||
17C8F1E30CBED286008D969D /* Spc_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Cpu.h; path = gme/Spc_Cpu.h; sourceTree = "<group>"; };
|
||||
17C8F1E40CBED286008D969D /* Spc_Dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Dsp.cpp; path = gme/Spc_Dsp.cpp; sourceTree = "<group>"; };
|
||||
17C8F1E50CBED286008D969D /* Spc_Dsp.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Dsp.h; path = gme/Spc_Dsp.h; sourceTree = "<group>"; };
|
||||
17C8F1E60CBED286008D969D /* Spc_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Spc_Emu.cpp; path = gme/Spc_Emu.cpp; sourceTree = "<group>"; };
|
||||
17C8F1E70CBED286008D969D /* Spc_Emu.h */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.c.h; name = Spc_Emu.h; path = gme/Spc_Emu.h; sourceTree = "<group>"; };
|
||||
17C8F1EA0CBED286008D969D /* Vgm_Emu.cpp */ = {isa = PBXFileReference; fileEncoding = 30; lastKnownFileType = sourcecode.cpp.cpp; name = Vgm_Emu.cpp; path = gme/Vgm_Emu.cpp; sourceTree = "<group>"; };
|
||||
|
@ -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 = "<group>"; };
|
||||
8370B72D17F615FE001A4D7A /* Z80_Cpu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Z80_Cpu.cpp; path = gme/Z80_Cpu.cpp; sourceTree = "<group>"; };
|
||||
8370B72E17F615FE001A4D7A /* Z80_Cpu.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = Z80_Cpu.h; path = gme/Z80_Cpu.h; sourceTree = "<group>"; };
|
||||
83FC5D3B181B47FB00B917E5 /* dsp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = dsp.cpp; sourceTree = "<group>"; };
|
||||
83FC5D3C181B47FB00B917E5 /* dsp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = dsp.hpp; sourceTree = "<group>"; };
|
||||
83FC5D57181B47FB00B917E5 /* smp.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = smp.cpp; sourceTree = "<group>"; };
|
||||
83FC5D58181B47FB00B917E5 /* smp.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = smp.hpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
||||
83FC5D95181B638C00B917E5 /* memory.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; lineEnding = 0; path = memory.cpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
||||
83FC5D96181B638C00B917E5 /* timing.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = timing.cpp; sourceTree = "<group>"; };
|
||||
83FC5D97181B675900B917E5 /* SPC_DSP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = SPC_DSP.cpp; sourceTree = "<group>"; };
|
||||
83FC5D98181B675900B917E5 /* SPC_DSP.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = SPC_DSP.h; sourceTree = "<group>"; };
|
||||
83FC5DA3181B8B1900B917E5 /* registers.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; lineEnding = 0; path = registers.hpp; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.cpp; };
|
||||
83FC5DA5181B8B1900B917E5 /* spc700.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = spc700.cpp; sourceTree = "<group>"; };
|
||||
83FC5DA6181B8B1900B917E5 /* spc700.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = spc700.hpp; sourceTree = "<group>"; };
|
||||
83FC5DB1181B93DD00B917E5 /* algorithms.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = algorithms.cpp; sourceTree = "<group>"; };
|
||||
83FC5DB3181B93DD00B917E5 /* instructions.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = instructions.cpp; sourceTree = "<group>"; };
|
||||
83FC5DB4181B93DD00B917E5 /* memory.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = memory.hpp; sourceTree = "<group>"; };
|
||||
8DC2EF5A0486A6940098B216 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
|
||||
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 = "<absolute>"; };
|
||||
|
@ -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 = "<group>";
|
||||
};
|
||||
83FC5D35181B47FB00B917E5 /* higan */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83FC5D9D181B8B1900B917E5 /* processor */,
|
||||
83FC5D36181B47FB00B917E5 /* dsp */,
|
||||
83FC5D40181B47FB00B917E5 /* smp */,
|
||||
);
|
||||
name = higan;
|
||||
path = gme/higan;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83FC5D36181B47FB00B917E5 /* dsp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83FC5D97181B675900B917E5 /* SPC_DSP.cpp */,
|
||||
83FC5D98181B675900B917E5 /* SPC_DSP.h */,
|
||||
83FC5D3B181B47FB00B917E5 /* dsp.cpp */,
|
||||
83FC5D3C181B47FB00B917E5 /* dsp.hpp */,
|
||||
);
|
||||
path = dsp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83FC5D40181B47FB00B917E5 /* smp */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83FC5D95181B638C00B917E5 /* memory.cpp */,
|
||||
83FC5D96181B638C00B917E5 /* timing.cpp */,
|
||||
83FC5D57181B47FB00B917E5 /* smp.cpp */,
|
||||
83FC5D58181B47FB00B917E5 /* smp.hpp */,
|
||||
);
|
||||
path = smp;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
83FC5D9D181B8B1900B917E5 /* processor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
83FC5D9E181B8B1900B917E5 /* spc700 */,
|
||||
);
|
||||
path = processor;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
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 = "<group>";
|
||||
};
|
||||
/* 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 */,
|
||||
|
|
|
@ -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 );
|
||||
}
|
|
@ -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 <assert.h>
|
||||
|
||||
inline int Snes_Spc::sample_count() const { return (m.extra_clocks >> 5) * 2; }
|
||||
|
||||
inline int Snes_Spc::read_port( time_t t, int port )
|
||||
{
|
||||
assert( (unsigned) port < port_count );
|
||||
return run_until_( t ) [port];
|
||||
}
|
||||
|
||||
inline void Snes_Spc::write_port( time_t t, int port, int data )
|
||||
{
|
||||
assert( (unsigned) port < port_count );
|
||||
run_until_( t ) [0x10 + port] = data;
|
||||
}
|
||||
|
||||
inline void Snes_Spc::mute_voices( int mask ) { dsp.mute_voices( mask ); }
|
||||
|
||||
inline void Snes_Spc::disable_surround( bool disable ) { dsp.disable_surround( disable ); }
|
||||
|
||||
inline void Snes_Spc::interpolation_level( int level ) { dsp.interpolation_level( level ); }
|
||||
|
||||
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
|
|
@ -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"
|
File diff suppressed because it is too large
Load Diff
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
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);
|
||||
}
|
||||
if (value)
|
||||
{
|
||||
regs[Snes_Spc::r_t0out + i] = 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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
467
Frameworks/GME/gme/Spc_Dsp.cpp → Frameworks/GME/gme/higan/dsp/SPC_DSP.cpp
Normal file → Executable file
467
Frameworks/GME/gme/Spc_Dsp.cpp → Frameworks/GME/gme/higan/dsp/SPC_DSP.cpp
Normal file → Executable file
|
@ -1,6 +1,6 @@
|
|||
// snes_spc 0.9.0. http://www.slack.net/~ant/
|
||||
|
||||
#include "Spc_Dsp.h"
|
||||
#include "SPC_DSP.h"
|
||||
|
||||
#include "blargg_endian.h"
|
||||
#include <string.h>
|
||||
|
@ -18,6 +18,8 @@ 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
|
||||
|
@ -31,16 +33,25 @@ Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
|
|||
#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] =
|
||||
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
|
||||
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;
|
||||
|
@ -72,7 +83,7 @@ static BOOST::uint8_t const initial_regs [Spc_Dsp::register_count] =
|
|||
}\
|
||||
}\
|
||||
|
||||
void Spc_Dsp::set_output( sample_t* out, int size )
|
||||
void SPC_DSP::set_output( sample_t* out, int size )
|
||||
{
|
||||
require( (size & 1) == 0 ); // must be even
|
||||
if ( !out )
|
||||
|
@ -126,305 +137,7 @@ static short const gauss [512] =
|
|||
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 )
|
||||
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;
|
||||
|
@ -444,68 +157,6 @@ inline int Spc_Dsp::interpolate( voice_t const* v )
|
|||
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
|
||||
|
||||
|
@ -544,18 +195,18 @@ static unsigned const counter_offsets [32] =
|
|||
0
|
||||
};
|
||||
|
||||
inline void Spc_Dsp::init_counter()
|
||||
inline void SPC_DSP::init_counter()
|
||||
{
|
||||
m.counter = 0;
|
||||
}
|
||||
|
||||
inline void Spc_Dsp::run_counters()
|
||||
inline void SPC_DSP::run_counters()
|
||||
{
|
||||
if ( --m.counter < 0 )
|
||||
m.counter = simple_counter_range - 1;
|
||||
}
|
||||
|
||||
inline unsigned Spc_Dsp::read_counter( int rate )
|
||||
inline unsigned SPC_DSP::read_counter( int rate )
|
||||
{
|
||||
return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate];
|
||||
}
|
||||
|
@ -563,7 +214,7 @@ inline unsigned Spc_Dsp::read_counter( int rate )
|
|||
|
||||
//// Envelope
|
||||
|
||||
inline void Spc_Dsp::run_envelope( voice_t* const v )
|
||||
inline void SPC_DSP::run_envelope( voice_t* const v )
|
||||
{
|
||||
int env = v->env;
|
||||
if ( v->env_mode == env_release ) // 60%
|
||||
|
@ -645,7 +296,7 @@ inline void Spc_Dsp::run_envelope( voice_t* const v )
|
|||
|
||||
//// BRR Decoding
|
||||
|
||||
inline void Spc_Dsp::decode_brr( voice_t* v )
|
||||
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];
|
||||
|
@ -705,7 +356,7 @@ inline void Spc_Dsp::decode_brr( voice_t* v )
|
|||
|
||||
//// Misc
|
||||
|
||||
#define MISC_CLOCK( n ) inline void Spc_Dsp::misc_##n()
|
||||
#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n()
|
||||
|
||||
MISC_CLOCK( 27 )
|
||||
{
|
||||
|
@ -743,7 +394,7 @@ MISC_CLOCK( 30 )
|
|||
|
||||
//// Voices
|
||||
|
||||
#define VOICE_CLOCK( n ) void Spc_Dsp::voice_##n( voice_t* const v )
|
||||
#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v )
|
||||
|
||||
inline VOICE_CLOCK( V1 )
|
||||
{
|
||||
|
@ -806,31 +457,7 @@ VOICE_CLOCK( V3c )
|
|||
|
||||
// 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;
|
||||
}
|
||||
int output = interpolate( v );
|
||||
|
||||
// Noise
|
||||
if ( m.t_non & v->vbit )
|
||||
|
@ -866,7 +493,7 @@ VOICE_CLOCK( V3c )
|
|||
if ( !v->kon_delay )
|
||||
run_envelope( v );
|
||||
}
|
||||
inline void Spc_Dsp::voice_output( voice_t const* v, int ch )
|
||||
inline void SPC_DSP::voice_output( voice_t const* v, int ch )
|
||||
{
|
||||
// Check surround removal
|
||||
int vol = (int8_t) VREG(v->regs,voll + ch);
|
||||
|
@ -877,10 +504,6 @@ inline void Spc_Dsp::voice_output( voice_t const* v, int ch )
|
|||
// 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] );
|
||||
|
@ -986,9 +609,9 @@ VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); }
|
|||
// 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()
|
||||
#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n()
|
||||
|
||||
inline void Spc_Dsp::echo_read( int ch )
|
||||
inline void SPC_DSP::echo_read( int ch )
|
||||
{
|
||||
int s = GET_LE16SA( ECHO_PTR( ch ) );
|
||||
// second copy simplifies wrap-around handling
|
||||
|
@ -1046,7 +669,7 @@ ECHO_CLOCK( 25 )
|
|||
m.t_echo_in [0] = l & ~1;
|
||||
m.t_echo_in [1] = r & ~1;
|
||||
}
|
||||
inline int Spc_Dsp::echo_output( int ch )
|
||||
inline int SPC_DSP::echo_output( int ch )
|
||||
{
|
||||
// Check surround removal
|
||||
int vol = (int8_t) REG(mvoll + ch * 0x10);
|
||||
|
@ -1104,7 +727,7 @@ ECHO_CLOCK( 28 )
|
|||
{
|
||||
m.t_echo_enabled = REG(flg);
|
||||
}
|
||||
inline void Spc_Dsp::echo_write( int ch )
|
||||
inline void SPC_DSP::echo_write( int ch )
|
||||
{
|
||||
if ( !(m.t_echo_enabled & 0x20) )
|
||||
SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] );
|
||||
|
@ -1183,7 +806,7 @@ PHASE(31) V(V4,0) V(V1,2)\
|
|||
|
||||
#if !SPC_DSP_CUSTOM_RUN
|
||||
|
||||
void Spc_Dsp::run( int clocks_remain )
|
||||
void SPC_DSP::run( int clocks_remain )
|
||||
{
|
||||
require( clocks_remain > 0 );
|
||||
|
||||
|
@ -1207,12 +830,11 @@ void Spc_Dsp::run( int clocks_remain )
|
|||
|
||||
//// Setup
|
||||
|
||||
void Spc_Dsp::init( void* ram_64k )
|
||||
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();
|
||||
|
||||
|
@ -1232,7 +854,7 @@ void Spc_Dsp::init( void* ram_64k )
|
|||
#endif
|
||||
}
|
||||
|
||||
void Spc_Dsp::soft_reset_common()
|
||||
void SPC_DSP::soft_reset_common()
|
||||
{
|
||||
require( m.ram ); // init() must have been called already
|
||||
|
||||
|
@ -1245,13 +867,13 @@ void Spc_Dsp::soft_reset_common()
|
|||
init_counter();
|
||||
}
|
||||
|
||||
void Spc_Dsp::soft_reset()
|
||||
void SPC_DSP::soft_reset()
|
||||
{
|
||||
REG(flg) = 0xE0;
|
||||
soft_reset_common();
|
||||
}
|
||||
|
||||
void Spc_Dsp::load( uint8_t const regs [register_count] )
|
||||
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 );
|
||||
|
@ -1271,7 +893,7 @@ void Spc_Dsp::load( uint8_t const regs [register_count] )
|
|||
soft_reset_common();
|
||||
}
|
||||
|
||||
void Spc_Dsp::reset() { load( initial_regs ); }
|
||||
void SPC_DSP::reset() { load( initial_regs ); }
|
||||
|
||||
|
||||
//// State save/load
|
||||
|
@ -1317,7 +939,7 @@ void SPC_State_Copier::extra()
|
|||
skip( n );
|
||||
}
|
||||
|
||||
void Spc_Dsp::copy_state( unsigned char** io, copy_func_t copy )
|
||||
void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy )
|
||||
{
|
||||
SPC_State_Copier copier( io, copy );
|
||||
|
||||
|
@ -1416,4 +1038,7 @@ void Spc_Dsp::copy_state( unsigned char** io, copy_func_t copy )
|
|||
|
||||
copier.extra();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
#endif
|
57
Frameworks/GME/gme/Spc_Dsp.h → Frameworks/GME/gme/higan/dsp/SPC_DSP.h
Normal file → Executable file
57
Frameworks/GME/gme/Spc_Dsp.h → Frameworks/GME/gme/higan/dsp/SPC_DSP.h
Normal file → Executable file
|
@ -8,11 +8,8 @@
|
|||
|
||||
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;
|
||||
|
||||
namespace SuperFamicom {
|
||||
class SPC_DSP {
|
||||
public:
|
||||
typedef BOOST::uint8_t uint8_t;
|
||||
|
||||
|
@ -98,7 +95,6 @@ public:
|
|||
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
|
||||
|
||||
|
@ -124,11 +120,7 @@ public:
|
|||
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:
|
||||
//private:
|
||||
enum { brr_block_size = 9 };
|
||||
|
||||
struct state_t
|
||||
|
@ -190,13 +182,10 @@ private:
|
|||
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;
|
||||
|
||||
|
@ -205,10 +194,6 @@ private:
|
|||
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 );
|
||||
|
||||
|
@ -248,19 +233,22 @@ private:
|
|||
void echo_30();
|
||||
|
||||
void soft_reset_common();
|
||||
|
||||
public:
|
||||
bool mute() { return m.regs[r_flg] & 0x40; }
|
||||
};
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; }
|
||||
inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; }
|
||||
|
||||
inline int Spc_Dsp::read( int addr ) const
|
||||
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 )
|
||||
inline void SPC_DSP::write( int addr, int data )
|
||||
{
|
||||
assert( (unsigned) addr < register_count );
|
||||
|
||||
|
@ -288,29 +276,14 @@ inline void Spc_Dsp::write( int addr, int data )
|
|||
}
|
||||
}
|
||||
|
||||
inline void Spc_Dsp::mute_voices( int mask ) { m.mute_mask = mask; }
|
||||
inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
|
||||
|
||||
inline void Spc_Dsp::disable_surround( bool disable )
|
||||
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()
|
||||
inline bool SPC_DSP::check_kon()
|
||||
{
|
||||
bool old = m.kon_check;
|
||||
m.kon_check = 0;
|
||||
|
@ -320,10 +293,10 @@ inline bool Spc_Dsp::check_kon()
|
|||
#if !SPC_NO_COPY_STATE_FUNCS
|
||||
|
||||
class SPC_State_Copier {
|
||||
Spc_Dsp::copy_func_t func;
|
||||
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; }
|
||||
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 );
|
||||
|
@ -338,4 +311,6 @@ public:
|
|||
|
||||
#endif
|
||||
|
||||
};
|
||||
|
||||
#endif
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -0,0 +1,556 @@
|
|||
#define call (this->*op)
|
||||
|
||||
template<uint8_t (SPC700::*op)(uint8_t)>
|
||||
void SPC700::op_adjust(uint8_t& r) {
|
||||
op_io();
|
||||
r = call(r);
|
||||
}
|
||||
|
||||
template<uint8_t (SPC700::*op)(uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
void SPC700::op_read_const(uint8_t& r) {
|
||||
rd = op_readpc();
|
||||
r = call(r, rd);
|
||||
}
|
||||
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
void SPC700::op_read_dp(uint8_t& r) {
|
||||
dp = op_readpc();
|
||||
rd = op_readdp(dp);
|
||||
r = call(r, rd);
|
||||
}
|
||||
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint16_t (SPC700::*op)(uint16_t, uint16_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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<uint8_t (SPC700::*op)(uint8_t, uint8_t)>
|
||||
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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
};
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,106 @@
|
|||
#ifndef PROCESSOR_SPC700_HPP
|
||||
#define PROCESSOR_SPC700_HPP
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
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<uint8_t (SPC700::*op)(uint8_t)> void op_adjust(uint8_t&);
|
||||
template<uint8_t (SPC700::*op)(uint8_t)> void op_adjust_addr();
|
||||
template<uint8_t (SPC700::*op)(uint8_t)> void op_adjust_dp();
|
||||
void op_adjust_dpw(signed);
|
||||
template<uint8_t (SPC700::*op)(uint8_t)> void op_adjust_dpx();
|
||||
void op_branch(bool);
|
||||
void op_branch_bit();
|
||||
void op_pull(uint8_t&);
|
||||
void op_push(uint8_t);
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_read_addr(uint8_t&);
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_read_addri(uint8_t&);
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_read_const(uint8_t&);
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_read_dp(uint8_t&);
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_read_dpi(uint8_t&, uint8_t&);
|
||||
template<uint16_t (SPC700::*op)(uint16_t, uint16_t)> void op_read_dpw();
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_read_idpx();
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_read_idpy();
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> 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<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_write_dp_const();
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> void op_write_dp_dp();
|
||||
template<uint8_t (SPC700::*op)(uint8_t, uint8_t)> 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
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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<unsigned frequency>
|
||||
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
|
|
@ -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
|
|
@ -0,0 +1,146 @@
|
|||
#include "smp.hpp"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
#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 );
|
||||
}
|
||||
|
||||
}
|
|
@ -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 <stdio.h>
|
||||
|
||||
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<unsigned frequency>
|
||||
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
|
|
@ -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<unsigned timer_frequency>
|
||||
void SMP::Timer<timer_frequency>::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<unsigned timer_frequency>
|
||||
void SMP::Timer<timer_frequency>::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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue