Replaced snes_spc with higan accurate core, and enabled SFM support

CQTexperiment
Chris Moeller 2013-10-26 01:54:06 -07:00
parent d9971ee32f
commit cae86b582f
27 changed files with 3617 additions and 4385 deletions

View File

@ -2505,6 +2505,7 @@
C01FCF4B08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = (

View File

@ -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 */,

View File

@ -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 );
}

View File

@ -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

View File

@ -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, &REGS [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 &REGS [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

View File

@ -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();
}

View File

@ -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

View File

@ -25,6 +25,8 @@ Sfm_Emu::Sfm_Emu()
{
set_type( gme_sfm_type );
set_gain( 1.4 );
set_max_initial_silence( 30 );
set_silence_lookahead( 30 ); // Some SFMs may have a lot of initialization code
}
Sfm_Emu::~Sfm_Emu() { }
@ -67,6 +69,8 @@ struct Sfm_File : Gme_Info_
RETURN_ERR( data.resize( file_size ) );
RETURN_ERR( in.read( data.begin(), data.end() - data.begin() ) );
RETURN_ERR( check_sfm_header( data.begin() ) );
if ( file_size < 8 )
return "SFM file too small";
int metadata_size = get_le32( data.begin() + 4 );
byte temp = data[ 8 + metadata_size ];
data[ 8 + metadata_size ] = '\0';
@ -100,7 +104,7 @@ gme_type_t_ const gme_sfm_type [1] = {{ "Super Nintendo with log", 1, &new_sfm_e
blargg_err_t Sfm_Emu::set_sample_rate_( int sample_rate )
{
RETURN_ERR( apu.init() );
smp.power();
if ( sample_rate != native_sample_rate )
{
RETURN_ERR( resampler.resize_buffer( native_sample_rate / 20 * 2 ) );
@ -112,16 +116,17 @@ blargg_err_t Sfm_Emu::set_sample_rate_( int sample_rate )
void Sfm_Emu::mute_voices_( int m )
{
Music_Emu::mute_voices_( m );
apu.mute_voices( m );
for ( int i = 0, j = 1; i < 8; ++i, j <<= 1 )
smp.dsp.channel_enable( i, !( m & j ) );
}
blargg_err_t Sfm_Emu::load_mem_( byte const in [], int size )
{
set_voice_count( Spc_Dsp::voice_count );
set_voice_count( 8 );
if ( size < Sfm_Emu::sfm_min_file_size )
return blargg_err_file_type;
static const char* const names [Spc_Dsp::voice_count] = {
static const char* const names [ 8 ] = {
"DSP 1", "DSP 2", "DSP 3", "DSP 4", "DSP 5", "DSP 6", "DSP 7", "DSP 8"
};
set_voice_names( names );
@ -133,7 +138,7 @@ blargg_err_t Sfm_Emu::load_mem_( byte const in [], int size )
void Sfm_Emu::set_tempo_( double t )
{
apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) );
/*apu.set_tempo( (int) (t * Snes_Spc::tempo_unit) );*/
}
// (n ? n : 256)
@ -159,6 +164,8 @@ blargg_err_t Sfm_Emu::start_track_( int track )
resampler.clear();
filter.clear();
const byte * ptr = file_begin();
if ( file_size() < Sfm_Emu::sfm_min_file_size )
return "SFM file too small";
int metadata_size = get_le32(ptr + 4);
if ( file_size() < metadata_size + Sfm_Emu::sfm_min_file_size )
return "SFM file too small";
@ -168,140 +175,127 @@ blargg_err_t Sfm_Emu::start_track_( int track )
metadata.parseDocument(temp);
delete [] temp;
apu.init_rom( ipl_rom );
memcpy( smp.iplrom, ipl_rom, 64 );
apu.reset();
smp.reset();
memcpy( apu.m.ram.ram, ptr + 8 + metadata_size, 65536 );
memcpy( smp.apuram, ptr + 8 + metadata_size, 65536 );
memcpy( apu.dsp.m.regs, ptr + 8 + metadata_size + 65536, 128 );
memcpy( smp.dsp.spc_dsp.m.regs, ptr + 8 + metadata_size + 65536, 128 );
apu.set_sfm_queue( ptr + 8 + metadata_size + 65536 + 128, ptr + file_size() );
byte regs[Snes_Spc::reg_count] = {0};
smp.set_sfm_queue( ptr + 8 + metadata_size + 65536 + 128, ptr + file_size() );
char * end;
const char * value;
regs[Snes_Spc::r_test] = META_ENUM_INT("smp:test");
regs[Snes_Spc::r_control] |= META_ENUM_INT("smp:iplrom") ? 0x80 : 0;
regs[Snes_Spc::r_dspaddr] = META_ENUM_INT("smp:dspaddr");
uint32_t test = META_ENUM_INT("smp:test");
smp.status.clock_speed = (test >> 6) & 3;
smp.status.timer_speed = (test >> 4) & 3;
smp.status.timers_enable = test & 0x08;
smp.status.ram_disable = test & 0x04;
smp.status.ram_writable = test & 0x02;
smp.status.timers_disable = test & 0x01;
smp.status.iplrom_enable = META_ENUM_INT("smp:iplrom");
smp.status.dsp_addr = META_ENUM_INT("smp:dspaddr");
value = metadata.enumValue("smp:ram");
if (value)
{
regs[Snes_Spc::r_f8] = strtoul(value, &end, 10);
smp.status.ram00f8 = strtoul(value, &end, 10);
if (*end)
{
value = end + 1;
regs[Snes_Spc::r_f9] = strtoul(value, &end, 10);
smp.status.ram00f9 = strtoul(value, &end, 10);
}
}
char temp_path[256];
for (int i = 0; i < 3; ++i)
{
SuperFamicom::SMP::Timer<192> &t = (i == 0 ? smp.timer0 : (i == 1 ? smp.timer1 : *(SuperFamicom::SMP::Timer<192>*)&smp.timer2));
sprintf(temp_path, "smp:timer[%u]:", i);
size_t length = strlen(temp_path);
strcpy(temp_path + length, "enable");
value = metadata.enumValue(temp_path);
if (value)
{
regs[Snes_Spc::r_control] |= strtoul(value, &end, 10) ? 1 << i : 0;
t.enable = !!strtoul(value, &end, 10);
}
strcpy(temp_path + length, "target");
value = metadata.enumValue(temp_path);
if (value)
{
regs[Snes_Spc::r_t0target + i] = strtoul(value, &end, 10);
t.target = strtoul(value, &end, 10);
}
strcpy(temp_path + length, "stage");
value = metadata.enumValue(temp_path);
if (value)
{
for (int j = 0; j < 3; ++j)
{
if (value) value = strchr(value, ',');
if (value) ++value;
}
if (value)
{
regs[Snes_Spc::r_t0out + i] = strtoul(value, &end, 10);
}
t.stage0_ticks = strtoul(value, &end, 10);
if (*end != ',') break;
value = end + 1;
t.stage1_ticks = strtoul(value, &end, 10);
if (*end != ',') break;
value = end + 1;
t.stage2_ticks = strtoul(value, &end, 10);
if (*end != ',') break;
value = end + 1;
t.stage3_ticks = strtoul(value, &end, 10);
}
}
apu.load_regs( regs );
apu.m.rom_enabled = 0;
apu.regs_loaded();
for (int i = 0; i < 3; ++i)
{
sprintf(temp_path, "smp:timer[%u]:", i);
size_t length = strlen(temp_path);
strcpy(temp_path + length, "stage");
strcpy(temp_path + length, "line");
value = metadata.enumValue(temp_path);
if (value)
{
const char * stage = value;
apu.m.timers[i].next_time = strtoul(stage, &end, 10) + 1;
for (int j = 0; j < 2; ++j)
{
if (stage) stage = strchr(stage, ',');
if (stage) ++stage;
}
if (stage)
{
apu.m.timers[i].divider = strtoul(value, &end, 10);
}
t.current_line = !!strtoul(value, &end, 10);
}
}
apu.dsp.m.echo_hist_pos = &apu.dsp.m.echo_hist[META_ENUM_INT("dsp:echohistaddr")];
smp.dsp.spc_dsp.m.echo_hist_pos = &smp.dsp.spc_dsp.m.echo_hist[META_ENUM_INT("dsp:echohistaddr")];
value = metadata.enumValue("dsp:echohistdata");
if (value)
{
for (int i = 0; i < 8; ++i)
{
apu.dsp.m.echo_hist[i][0] = strtoul(value, &end, 10);
smp.dsp.spc_dsp.m.echo_hist[i][0] = strtoul(value, &end, 10);
value = strchr(value, ',');
if (!value) break;
++value;
apu.dsp.m.echo_hist[i][1] = strtoul(value, &end, 10);
smp.dsp.spc_dsp.m.echo_hist[i][1] = strtoul(value, &end, 10);
value = strchr(value, ',');
if (!value) break;
++value;
}
}
apu.dsp.m.phase = META_ENUM_INT("dsp:sample");
apu.dsp.m.kon = META_ENUM_INT("dsp:kon");
apu.dsp.m.noise = META_ENUM_INT("dsp:noise");
apu.dsp.m.counter = META_ENUM_INT("dsp:counter");
apu.dsp.m.echo_offset = META_ENUM_INT("dsp:echooffset");
apu.dsp.m.echo_length = META_ENUM_INT("dsp:echolength");
apu.dsp.m.new_kon = META_ENUM_INT("dsp:koncache");
apu.dsp.m.endx_buf = META_ENUM_INT("dsp:endx");
apu.dsp.m.envx_buf = META_ENUM_INT("dsp:envx");
apu.dsp.m.outx_buf = META_ENUM_INT("dsp:outx");
apu.dsp.m.t_pmon = META_ENUM_INT("dsp:pmon");
apu.dsp.m.t_non = META_ENUM_INT("dsp:non");
apu.dsp.m.t_eon = META_ENUM_INT("dsp:eon");
apu.dsp.m.t_dir = META_ENUM_INT("dsp:dir");
apu.dsp.m.t_koff = META_ENUM_INT("dsp:koff");
apu.dsp.m.t_brr_next_addr = META_ENUM_INT("dsp:brrnext");
apu.dsp.m.t_adsr0 = META_ENUM_INT("dsp:adsr0");
apu.dsp.m.t_brr_header = META_ENUM_INT("dsp:brrheader");
apu.dsp.m.t_brr_byte = META_ENUM_INT("dsp:brrdata");
apu.dsp.m.t_srcn = META_ENUM_INT("dsp:srcn");
apu.dsp.m.t_esa = META_ENUM_INT("dsp:esa");
apu.dsp.m.t_echo_enabled = !META_ENUM_INT("dsp:echodisable");
apu.dsp.m.t_dir_addr = META_ENUM_INT("dsp:diraddr");
apu.dsp.m.t_pitch = META_ENUM_INT("dsp:pitch");
apu.dsp.m.t_output = META_ENUM_INT("dsp:output");
apu.dsp.m.t_looped = META_ENUM_INT("dsp:looped");
apu.dsp.m.t_echo_ptr = META_ENUM_INT("dsp:echoaddr");
smp.dsp.spc_dsp.m.phase = META_ENUM_INT("dsp:sample");
smp.dsp.spc_dsp.m.kon = META_ENUM_INT("dsp:kon");
smp.dsp.spc_dsp.m.noise = META_ENUM_INT("dsp:noise");
smp.dsp.spc_dsp.m.counter = META_ENUM_INT("dsp:counter");
smp.dsp.spc_dsp.m.echo_offset = META_ENUM_INT("dsp:echooffset");
smp.dsp.spc_dsp.m.echo_length = META_ENUM_INT("dsp:echolength");
smp.dsp.spc_dsp.m.new_kon = META_ENUM_INT("dsp:koncache");
smp.dsp.spc_dsp.m.endx_buf = META_ENUM_INT("dsp:endx");
smp.dsp.spc_dsp.m.envx_buf = META_ENUM_INT("dsp:envx");
smp.dsp.spc_dsp.m.outx_buf = META_ENUM_INT("dsp:outx");
smp.dsp.spc_dsp.m.t_pmon = META_ENUM_INT("dsp:pmon");
smp.dsp.spc_dsp.m.t_non = META_ENUM_INT("dsp:non");
smp.dsp.spc_dsp.m.t_eon = META_ENUM_INT("dsp:eon");
smp.dsp.spc_dsp.m.t_dir = META_ENUM_INT("dsp:dir");
smp.dsp.spc_dsp.m.t_koff = META_ENUM_INT("dsp:koff");
smp.dsp.spc_dsp.m.t_brr_next_addr = META_ENUM_INT("dsp:brrnext");
smp.dsp.spc_dsp.m.t_adsr0 = META_ENUM_INT("dsp:adsr0");
smp.dsp.spc_dsp.m.t_brr_header = META_ENUM_INT("dsp:brrheader");
smp.dsp.spc_dsp.m.t_brr_byte = META_ENUM_INT("dsp:brrdata");
smp.dsp.spc_dsp.m.t_srcn = META_ENUM_INT("dsp:srcn");
smp.dsp.spc_dsp.m.t_esa = META_ENUM_INT("dsp:esa");
smp.dsp.spc_dsp.m.t_echo_enabled = !META_ENUM_INT("dsp:echodisable");
smp.dsp.spc_dsp.m.t_dir_addr = META_ENUM_INT("dsp:diraddr");
smp.dsp.spc_dsp.m.t_pitch = META_ENUM_INT("dsp:pitch");
smp.dsp.spc_dsp.m.t_output = META_ENUM_INT("dsp:output");
smp.dsp.spc_dsp.m.t_looped = META_ENUM_INT("dsp:looped");
smp.dsp.spc_dsp.m.t_echo_ptr = META_ENUM_INT("dsp:echoaddr");
#define META_ENUM_LEVELS(n, o) \
@ -316,9 +310,9 @@ blargg_err_t Sfm_Emu::start_track_( int track )
} \
}
META_ENUM_LEVELS("dsp:mainout", apu.dsp.m.t_main_out);
META_ENUM_LEVELS("dsp:echoout", apu.dsp.m.t_echo_out);
META_ENUM_LEVELS("dsp:echoin", apu.dsp.m.t_echo_in);
META_ENUM_LEVELS("dsp:mainout", smp.dsp.spc_dsp.m.t_main_out);
META_ENUM_LEVELS("dsp:echoout", smp.dsp.spc_dsp.m.t_echo_out);
META_ENUM_LEVELS("dsp:echoin", smp.dsp.spc_dsp.m.t_echo_in);
#undef META_ENUM_LEVELS
@ -326,7 +320,7 @@ blargg_err_t Sfm_Emu::start_track_( int track )
{
sprintf(temp_path, "dsp:voice[%u]:", i);
size_t length = strlen(temp_path);
Spc_Dsp::voice_t & voice = apu.dsp.m.voices[i];
SuperFamicom::SPC_DSP::voice_t & voice = smp.dsp.spc_dsp.m.voices[i];
strcpy(temp_path + length, "brrhistaddr");
value = metadata.enumValue(temp_path);
if (value)
@ -337,9 +331,9 @@ blargg_err_t Sfm_Emu::start_track_( int track )
value = metadata.enumValue(temp_path);
if (value)
{
for (int j = 0; j < Spc_Dsp::brr_buf_size; ++j)
for (int j = 0; j < SuperFamicom::SPC_DSP::brr_buf_size; ++j)
{
voice.buf[j] = voice.buf[j + Spc_Dsp::brr_buf_size] = strtoul(value, &end, 10);
voice.buf[j] = voice.buf[j + SuperFamicom::SPC_DSP::brr_buf_size] = strtoul(value, &end, 10);
if (!*end) break;
value = end + 1;
}
@ -353,11 +347,11 @@ blargg_err_t Sfm_Emu::start_track_( int track )
strcpy(temp_path + length, "vbit");
voice.vbit = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "vidx");
voice.regs = &apu.dsp.m.regs[META_ENUM_INT(temp_path)];
voice.regs = &smp.dsp.spc_dsp.m.regs[META_ENUM_INT(temp_path)];
strcpy(temp_path + length, "kondelay");
voice.kon_delay = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "envmode");
voice.env_mode = (Spc_Dsp::env_mode_t) META_ENUM_INT(temp_path);
voice.env_mode = (SuperFamicom::SPC_DSP::env_mode_t) META_ENUM_INT(temp_path);
strcpy(temp_path + length, "env");
voice.env = META_ENUM_INT(temp_path);
strcpy(temp_path + length, "envxout");
@ -367,7 +361,6 @@ blargg_err_t Sfm_Emu::start_track_( int track )
}
filter.set_gain( (int) (gain() * Spc_Filter::gain_unit) );
apu.clear_echo( true );
return blargg_ok;
}
@ -375,7 +368,7 @@ blargg_err_t Sfm_Emu::start_track_( int track )
blargg_err_t Sfm_Emu::play_and_filter( int count, sample_t out [] )
{
RETURN_ERR( apu.play( count, out ) );
smp.render( out, count );
filter.run( out, count );
return blargg_ok;
}
@ -392,7 +385,7 @@ blargg_err_t Sfm_Emu::skip_( int count )
if ( count > 0 )
{
RETURN_ERR( apu.skip( count ) );
smp.skip( count );
filter.clear();
}

View File

@ -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;

File diff suppressed because it is too large Load Diff

View File

@ -1,341 +1,316 @@
// Highly accurate SNES SPC-700 DSP emulator
// snes_spc 0.9.0
#ifndef SPC_DSP_H
#define SPC_DSP_H
#include "blargg_common.h"
extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
class Sfm_Emu;
class Spc_Dsp {
friend class Sfm_Emu;
public:
typedef BOOST::uint8_t uint8_t;
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
// Sets destination for output samples. If out is NULL or out_size is 0,
// doesn't generate any.
typedef short sample_t;
void set_output( sample_t* out, int out_size );
// Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than
// output buffer could hold.
int sample_count() const;
// Emulation
// Resets DSP to power-on state
void reset();
// Emulates pressing reset switch on SNES
void soft_reset();
// Reads/writes DSP registers. For accuracy, you must first call run()
// to catch the DSP up to present.
int read ( int addr ) const;
void write( int addr, int data );
// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
// a pair of samples is be generated.
void run( int clock_count );
// Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy.
enum { voice_count = 8 };
void mute_voices( int mask );
// State
// Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 };
void load( uint8_t const regs [register_count] );
// Saves/loads exact emulator state
enum { state_size = 640 }; // maximum space needed when saving
typedef dsp_copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t );
// Returns non-zero if new key-on events occurred since last call
bool check_kon();
// DSP register addresses
// Global registers
enum {
r_mvoll = 0x0C, r_mvolr = 0x1C,
r_evoll = 0x2C, r_evolr = 0x3C,
r_kon = 0x4C, r_koff = 0x5C,
r_flg = 0x6C, r_endx = 0x7C,
r_efb = 0x0D, r_pmon = 0x2D,
r_non = 0x3D, r_eon = 0x4D,
r_dir = 0x5D, r_esa = 0x6D,
r_edl = 0x7D,
r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
};
// Voice registers
enum {
v_voll = 0x00, v_volr = 0x01,
v_pitchl = 0x02, v_pitchh = 0x03,
v_srcn = 0x04, v_adsr0 = 0x05,
v_adsr1 = 0x06, v_gain = 0x07,
v_envx = 0x08, v_outx = 0x09
};
public:
enum { extra_size = 16 };
sample_t* extra() { return m.extra; }
sample_t const* out_pos() const { return m.out; }
void disable_surround( bool disable = true );
void interpolation_level( int level = 0 ) { m.interpolation_level = level; }
public:
BLARGG_DISABLE_NOTHROW
typedef BOOST::int8_t int8_t;
typedef BOOST::int16_t int16_t;
enum { echo_hist_size = 8 };
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
enum { brr_buf_size = 12 };
struct voice_t
{
int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
int buf_pos; // place in buffer where next samples will be decoded
int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
int brr_addr; // address of current BRR block
int brr_offset; // current decoding offset in BRR block
uint8_t* regs; // pointer to voice's DSP registers
int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
int kon_delay; // KON delay/current setup phase
env_mode_t env_mode;
int env; // current envelope level
int hidden_env; // used by GAIN mode 7, very obscure quirk
uint8_t t_envx_out;
};
// kill me now
const voice_t * get_voice( int ch ) const;
int get_max_level( int v, int ch ) const;
private:
enum { brr_block_size = 9 };
struct state_t
{
uint8_t regs [register_count];
// Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
int echo_hist [echo_hist_size * 2] [2];
int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
int every_other_sample; // toggles every sample
int kon; // KON value when last checked
int noise;
int counter;
int echo_offset; // offset from ESA in echo buffer
int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31)
bool kon_check; // set when a new KON occurs
// Hidden registers also written to when main register is written to
int new_kon;
uint8_t endx_buf;
uint8_t envx_buf;
uint8_t outx_buf;
// Temporary state between clocks
// read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
// read a few clocks ahead then used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_enabled;
// internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
// left/right sums
int t_main_out [2];
int t_echo_out [2];
int t_echo_in [2];
voice_t voices [voice_count];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
int surround_threshold;
int interpolation_level;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;
sample_t extra [extra_size];
int max_level[voice_count][2];
};
state_t m;
void init_counter();
void run_counters();
unsigned read_counter( int rate );
int interpolate( voice_t const* v );
int interpolate_cubic( voice_t const* v );
int interpolate_sinc( voice_t const* v );
int interpolate_linear( voice_t const* v );
int interpolate_nearest( voice_t const* v );
void run_envelope( voice_t* const v );
void decode_brr( voice_t* v );
void misc_27();
void misc_28();
void misc_29();
void misc_30();
void voice_output( voice_t const* v, int ch );
void voice_V1( voice_t* const );
void voice_V2( voice_t* const );
void voice_V3( voice_t* const );
void voice_V3a( voice_t* const );
void voice_V3b( voice_t* const );
void voice_V3c( voice_t* const );
void voice_V4( voice_t* const );
void voice_V5( voice_t* const );
void voice_V6( voice_t* const );
void voice_V7( voice_t* const );
void voice_V8( voice_t* const );
void voice_V9( voice_t* const );
void voice_V7_V4_V1( voice_t* const );
void voice_V8_V5_V2( voice_t* const );
void voice_V9_V6_V3( voice_t* const );
void echo_read( int ch );
int echo_output( int ch );
void echo_write( int ch );
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
void soft_reset_common();
};
#include <assert.h>
inline int Spc_Dsp::sample_count() const { return m.out - m.out_begin; }
inline int Spc_Dsp::read( int addr ) const
{
assert( (unsigned) addr < register_count );
return m.regs [addr];
}
inline void Spc_Dsp::write( int addr, int data )
{
assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data;
switch ( addr & 0x0F )
{
case v_envx:
m.envx_buf = (uint8_t) data;
break;
case v_outx:
m.outx_buf = (uint8_t) data;
break;
case 0x0C:
if ( addr == r_kon )
m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written
{
m.endx_buf = 0;
m.regs [r_endx] = 0;
}
break;
}
}
inline void Spc_Dsp::mute_voices( int mask ) { m.mute_mask = mask; }
inline void Spc_Dsp::disable_surround( bool disable )
{
m.surround_threshold = disable ? 0 : -0x4000;
}
inline const Spc_Dsp::voice_t * Spc_Dsp::get_voice( int ch ) const
{
assert( (unsigned) ch < voice_count );
return &m.voices[ ch ];
}
inline int Spc_Dsp::get_max_level( int v, int ch ) const
{
assert( (unsigned) v < voice_count );
assert( (unsigned) ch < 2 );
int ret = m.max_level[ v ][ ch ];
(( Spc_Dsp * )this)->m.max_level[ v ][ ch ] = 0;
return ret;
}
inline bool Spc_Dsp::check_kon()
{
bool old = m.kon_check;
m.kon_check = 0;
return old;
}
#if !SPC_NO_COPY_STATE_FUNCS
class SPC_State_Copier {
Spc_Dsp::copy_func_t func;
unsigned char** buf;
public:
SPC_State_Copier( unsigned char** p, Spc_Dsp::copy_func_t f ) { func = f; buf = p; }
void copy( void* state, size_t size );
int copy_int( int state, int size );
void skip( int count );
void extra();
};
#define SPC_COPY( type, state )\
{\
state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
assert( (BOOST::type) state == state );\
}
#endif
#endif
// Highly accurate SNES SPC-700 DSP emulator
// snes_spc 0.9.0
#ifndef SPC_DSP_H
#define SPC_DSP_H
#include "blargg_common.h"
extern "C" { typedef void (*dsp_copy_func_t)( unsigned char** io, void* state, size_t ); }
namespace SuperFamicom {
class SPC_DSP {
public:
typedef BOOST::uint8_t uint8_t;
// Setup
// Initializes DSP and has it use the 64K RAM provided
void init( void* ram_64k );
// Sets destination for output samples. If out is NULL or out_size is 0,
// doesn't generate any.
typedef short sample_t;
void set_output( sample_t* out, int out_size );
// Number of samples written to output since it was last set, always
// a multiple of 2. Undefined if more samples were generated than
// output buffer could hold.
int sample_count() const;
// Emulation
// Resets DSP to power-on state
void reset();
// Emulates pressing reset switch on SNES
void soft_reset();
// Reads/writes DSP registers. For accuracy, you must first call run()
// to catch the DSP up to present.
int read ( int addr ) const;
void write( int addr, int data );
// Runs DSP for specified number of clocks (~1024000 per second). Every 32 clocks
// a pair of samples is be generated.
void run( int clock_count );
// Sound control
// Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events).
// Reduces emulation accuracy.
enum { voice_count = 8 };
void mute_voices( int mask );
// State
// Resets DSP and uses supplied values to initialize registers
enum { register_count = 128 };
void load( uint8_t const regs [register_count] );
// Saves/loads exact emulator state
enum { state_size = 640 }; // maximum space needed when saving
typedef dsp_copy_func_t copy_func_t;
void copy_state( unsigned char** io, copy_func_t );
// Returns non-zero if new key-on events occurred since last call
bool check_kon();
// DSP register addresses
// Global registers
enum {
r_mvoll = 0x0C, r_mvolr = 0x1C,
r_evoll = 0x2C, r_evolr = 0x3C,
r_kon = 0x4C, r_koff = 0x5C,
r_flg = 0x6C, r_endx = 0x7C,
r_efb = 0x0D, r_pmon = 0x2D,
r_non = 0x3D, r_eon = 0x4D,
r_dir = 0x5D, r_esa = 0x6D,
r_edl = 0x7D,
r_fir = 0x0F // 8 coefficients at 0x0F, 0x1F ... 0x7F
};
// Voice registers
enum {
v_voll = 0x00, v_volr = 0x01,
v_pitchl = 0x02, v_pitchh = 0x03,
v_srcn = 0x04, v_adsr0 = 0x05,
v_adsr1 = 0x06, v_gain = 0x07,
v_envx = 0x08, v_outx = 0x09
};
public:
enum { extra_size = 16 };
sample_t* extra() { return m.extra; }
sample_t const* out_pos() const { return m.out; }
void disable_surround( bool disable = true );
public:
BLARGG_DISABLE_NOTHROW
typedef BOOST::int8_t int8_t;
typedef BOOST::int16_t int16_t;
enum { echo_hist_size = 8 };
enum env_mode_t { env_release, env_attack, env_decay, env_sustain };
enum { brr_buf_size = 12 };
struct voice_t
{
int buf [brr_buf_size*2];// decoded samples (twice the size to simplify wrap handling)
int buf_pos; // place in buffer where next samples will be decoded
int interp_pos; // relative fractional position in sample (0x1000 = 1.0)
int brr_addr; // address of current BRR block
int brr_offset; // current decoding offset in BRR block
uint8_t* regs; // pointer to voice's DSP registers
int vbit; // bitmask for voice: 0x01 for voice 0, 0x02 for voice 1, etc.
int kon_delay; // KON delay/current setup phase
env_mode_t env_mode;
int env; // current envelope level
int hidden_env; // used by GAIN mode 7, very obscure quirk
uint8_t t_envx_out;
};
//private:
enum { brr_block_size = 9 };
struct state_t
{
uint8_t regs [register_count];
// Echo history keeps most recent 8 samples (twice the size to simplify wrap handling)
int echo_hist [echo_hist_size * 2] [2];
int (*echo_hist_pos) [2]; // &echo_hist [0 to 7]
int every_other_sample; // toggles every sample
int kon; // KON value when last checked
int noise;
int counter;
int echo_offset; // offset from ESA in echo buffer
int echo_length; // number of bytes that echo_offset will stop at
int phase; // next clock cycle to run (0-31)
bool kon_check; // set when a new KON occurs
// Hidden registers also written to when main register is written to
int new_kon;
uint8_t endx_buf;
uint8_t envx_buf;
uint8_t outx_buf;
// Temporary state between clocks
// read once per sample
int t_pmon;
int t_non;
int t_eon;
int t_dir;
int t_koff;
// read a few clocks ahead then used
int t_brr_next_addr;
int t_adsr0;
int t_brr_header;
int t_brr_byte;
int t_srcn;
int t_esa;
int t_echo_enabled;
// internal state that is recalculated every sample
int t_dir_addr;
int t_pitch;
int t_output;
int t_looped;
int t_echo_ptr;
// left/right sums
int t_main_out [2];
int t_echo_out [2];
int t_echo_in [2];
voice_t voices [voice_count];
// non-emulation state
uint8_t* ram; // 64K shared RAM between DSP and SMP
int mute_mask;
int surround_threshold;
sample_t* out;
sample_t* out_end;
sample_t* out_begin;
sample_t extra [extra_size];
};
state_t m;
void init_counter();
void run_counters();
unsigned read_counter( int rate );
int interpolate( voice_t const* v );
void run_envelope( voice_t* const v );
void decode_brr( voice_t* v );
void misc_27();
void misc_28();
void misc_29();
void misc_30();
void voice_output( voice_t const* v, int ch );
void voice_V1( voice_t* const );
void voice_V2( voice_t* const );
void voice_V3( voice_t* const );
void voice_V3a( voice_t* const );
void voice_V3b( voice_t* const );
void voice_V3c( voice_t* const );
void voice_V4( voice_t* const );
void voice_V5( voice_t* const );
void voice_V6( voice_t* const );
void voice_V7( voice_t* const );
void voice_V8( voice_t* const );
void voice_V9( voice_t* const );
void voice_V7_V4_V1( voice_t* const );
void voice_V8_V5_V2( voice_t* const );
void voice_V9_V6_V3( voice_t* const );
void echo_read( int ch );
int echo_output( int ch );
void echo_write( int ch );
void echo_22();
void echo_23();
void echo_24();
void echo_25();
void echo_26();
void echo_27();
void echo_28();
void echo_29();
void echo_30();
void soft_reset_common();
public:
bool mute() { return m.regs[r_flg] & 0x40; }
};
#include <assert.h>
inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; }
inline int SPC_DSP::read( int addr ) const
{
assert( (unsigned) addr < register_count );
return m.regs [addr];
}
inline void SPC_DSP::write( int addr, int data )
{
assert( (unsigned) addr < register_count );
m.regs [addr] = (uint8_t) data;
switch ( addr & 0x0F )
{
case v_envx:
m.envx_buf = (uint8_t) data;
break;
case v_outx:
m.outx_buf = (uint8_t) data;
break;
case 0x0C:
if ( addr == r_kon )
m.new_kon = (uint8_t) data;
if ( addr == r_endx ) // always cleared, regardless of data written
{
m.endx_buf = 0;
m.regs [r_endx] = 0;
}
break;
}
}
inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; }
inline void SPC_DSP::disable_surround( bool disable )
{
m.surround_threshold = disable ? 0 : -0x4000;
}
inline bool SPC_DSP::check_kon()
{
bool old = m.kon_check;
m.kon_check = 0;
return old;
}
#if !SPC_NO_COPY_STATE_FUNCS
class SPC_State_Copier {
SPC_DSP::copy_func_t func;
unsigned char** buf;
public:
SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; }
void copy( void* state, size_t size );
int copy_int( int state, int size );
void skip( int count );
void extra();
};
#define SPC_COPY( type, state )\
{\
state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\
assert( (BOOST::type) state == state );\
}
#endif
};
#endif

View File

@ -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;
}
}

View File

@ -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

View File

@ -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;
}

View File

@ -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 == &regs.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 == &regs.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

View File

@ -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);
}

View File

@ -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;
};

View File

@ -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();
}
}
}

View File

@ -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

113
Frameworks/GME/gme/higan/smp.cpp Executable file
View File

@ -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;
}
}

144
Frameworks/GME/gme/higan/smp.hpp Executable file
View File

@ -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

View File

@ -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

View File

@ -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 );
}
}

View File

@ -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

View File

@ -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

View File

@ -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