From 9a87a0f586e802cec1ffd80540689868e0290b9f Mon Sep 17 00:00:00 2001 From: CasualPokePlayer <50538166+CasualPokePlayer@users.noreply.github.com> Date: Mon, 18 Oct 2021 18:38:23 -0700 Subject: [PATCH] nuke sameboy (#2934) --- Assets/dll/sameboy.wbx.gz | Bin 76773 -> 0 bytes README.md | 2 +- src/BizHawk.Client.Common/config/Config.cs | 4 +- src/BizHawk.Client.EmuHawk/MainForm.Events.cs | 4 - src/BizHawk.Client.EmuHawk/MainForm.cs | 1 - .../Consoles/Nintendo/Gameboy/LibSameboy.cs | 55 - .../Consoles/Nintendo/Gameboy/Sameboy.cs | 410 ----- src/BizHawk.Emulation.Cores/CoreNames.cs | 1 - waterbox/make-all-cores.sh | 1 - waterbox/readme.txt | 1 - waterbox/sameboy/.vscode/settings.json | 6 - waterbox/sameboy/CHANGES.md | 1 - waterbox/sameboy/LICENSE | 21 - waterbox/sameboy/Makefile | 17 - waterbox/sameboy/README.md | 51 - waterbox/sameboy/apu.c | 402 ----- waterbox/sameboy/apu.h | 65 - waterbox/sameboy/bizhawk.cpp | 285 ---- waterbox/sameboy/blip_buf/blip_buf.c | 344 ----- waterbox/sameboy/blip_buf/blip_buf.h | 72 - waterbox/sameboy/camera.c | 149 -- waterbox/sameboy/camera.h | 29 - waterbox/sameboy/display.c | 796 ---------- waterbox/sameboy/display.h | 40 - waterbox/sameboy/gb.c | 473 ------ waterbox/sameboy/gb.h | 523 ------- waterbox/sameboy/gb_struct_def.h | 5 - waterbox/sameboy/joypad.c | 48 - waterbox/sameboy/joypad.h | 10 - waterbox/sameboy/mbc.c | 154 -- waterbox/sameboy/mbc.h | 31 - waterbox/sameboy/memory.c | 733 --------- waterbox/sameboy/memory.h | 12 - waterbox/sameboy/printer.c | 205 --- waterbox/sameboy/printer.h | 59 - waterbox/sameboy/sgb.c | 1043 ------------- waterbox/sameboy/sgb.h | 36 - waterbox/sameboy/snes_spc/SNES_SPC.cpp | 564 ------- waterbox/sameboy/snes_spc/SNES_SPC.h | 284 ---- waterbox/sameboy/snes_spc/SNES_SPC_misc.cpp | 380 ----- waterbox/sameboy/snes_spc/SNES_SPC_state.cpp | 129 -- waterbox/sameboy/snes_spc/SPC_CPU.h | 1220 --------------- waterbox/sameboy/snes_spc/SPC_DSP.cpp | 1018 ------------ waterbox/sameboy/snes_spc/SPC_DSP.h | 304 ---- waterbox/sameboy/snes_spc/SPC_Filter.cpp | 68 - waterbox/sameboy/snes_spc/SPC_Filter.h | 47 - waterbox/sameboy/snes_spc/blargg_common.h | 186 --- waterbox/sameboy/snes_spc/blargg_config.h | 24 - waterbox/sameboy/snes_spc/blargg_endian.h | 185 --- waterbox/sameboy/snes_spc/blargg_source.h | 100 -- waterbox/sameboy/snes_spc/changes.txt | 107 -- waterbox/sameboy/snes_spc/dsp.cpp | 48 - waterbox/sameboy/snes_spc/dsp.h | 83 - waterbox/sameboy/snes_spc/license.txt | 504 ------ waterbox/sameboy/snes_spc/readme.txt | 86 -- waterbox/sameboy/snes_spc/snes_spc.txt | 318 ---- waterbox/sameboy/snes_spc/spc.cpp | 74 - waterbox/sameboy/snes_spc/spc.h | 149 -- waterbox/sameboy/timing.c | 161 -- waterbox/sameboy/timing.h | 18 - waterbox/sameboy/z80_cpu.c | 1375 ----------------- waterbox/sameboy/z80_cpu.h | 10 - 62 files changed, 3 insertions(+), 13528 deletions(-) delete mode 100644 Assets/dll/sameboy.wbx.gz delete mode 100644 src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs delete mode 100644 src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs delete mode 100644 waterbox/sameboy/.vscode/settings.json delete mode 100644 waterbox/sameboy/CHANGES.md delete mode 100644 waterbox/sameboy/LICENSE delete mode 100644 waterbox/sameboy/Makefile delete mode 100644 waterbox/sameboy/README.md delete mode 100644 waterbox/sameboy/apu.c delete mode 100644 waterbox/sameboy/apu.h delete mode 100644 waterbox/sameboy/bizhawk.cpp delete mode 100644 waterbox/sameboy/blip_buf/blip_buf.c delete mode 100644 waterbox/sameboy/blip_buf/blip_buf.h delete mode 100644 waterbox/sameboy/camera.c delete mode 100644 waterbox/sameboy/camera.h delete mode 100644 waterbox/sameboy/display.c delete mode 100644 waterbox/sameboy/display.h delete mode 100644 waterbox/sameboy/gb.c delete mode 100644 waterbox/sameboy/gb.h delete mode 100644 waterbox/sameboy/gb_struct_def.h delete mode 100644 waterbox/sameboy/joypad.c delete mode 100644 waterbox/sameboy/joypad.h delete mode 100644 waterbox/sameboy/mbc.c delete mode 100644 waterbox/sameboy/mbc.h delete mode 100644 waterbox/sameboy/memory.c delete mode 100644 waterbox/sameboy/memory.h delete mode 100644 waterbox/sameboy/printer.c delete mode 100644 waterbox/sameboy/printer.h delete mode 100644 waterbox/sameboy/sgb.c delete mode 100644 waterbox/sameboy/sgb.h delete mode 100644 waterbox/sameboy/snes_spc/SNES_SPC.cpp delete mode 100644 waterbox/sameboy/snes_spc/SNES_SPC.h delete mode 100644 waterbox/sameboy/snes_spc/SNES_SPC_misc.cpp delete mode 100644 waterbox/sameboy/snes_spc/SNES_SPC_state.cpp delete mode 100644 waterbox/sameboy/snes_spc/SPC_CPU.h delete mode 100644 waterbox/sameboy/snes_spc/SPC_DSP.cpp delete mode 100644 waterbox/sameboy/snes_spc/SPC_DSP.h delete mode 100644 waterbox/sameboy/snes_spc/SPC_Filter.cpp delete mode 100644 waterbox/sameboy/snes_spc/SPC_Filter.h delete mode 100644 waterbox/sameboy/snes_spc/blargg_common.h delete mode 100644 waterbox/sameboy/snes_spc/blargg_config.h delete mode 100644 waterbox/sameboy/snes_spc/blargg_endian.h delete mode 100644 waterbox/sameboy/snes_spc/blargg_source.h delete mode 100644 waterbox/sameboy/snes_spc/changes.txt delete mode 100644 waterbox/sameboy/snes_spc/dsp.cpp delete mode 100644 waterbox/sameboy/snes_spc/dsp.h delete mode 100644 waterbox/sameboy/snes_spc/license.txt delete mode 100644 waterbox/sameboy/snes_spc/readme.txt delete mode 100644 waterbox/sameboy/snes_spc/snes_spc.txt delete mode 100644 waterbox/sameboy/snes_spc/spc.cpp delete mode 100644 waterbox/sameboy/snes_spc/spc.h delete mode 100644 waterbox/sameboy/timing.c delete mode 100644 waterbox/sameboy/timing.h delete mode 100644 waterbox/sameboy/z80_cpu.c delete mode 100644 waterbox/sameboy/z80_cpu.h diff --git a/Assets/dll/sameboy.wbx.gz b/Assets/dll/sameboy.wbx.gz deleted file mode 100644 index 89dd8ebd6e4cad6d27be2293fd61f9fb35b0055c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 76773 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 A1^@s6 diff --git a/README.md b/README.md index 0fe9f2b636..dba02c4c7a 100644 --- a/README.md +++ b/README.md @@ -313,7 +313,7 @@ Sega Genesis | **Genplus-gx** Sega Master System | **SMSHawk** Sega Saturn | **Saturnus** SNES | **BSNES**, Faust, Snes9x -Super Game Boy | **BSNES**, **Gambatte**, **SameBoy** +Super Game Boy | **BSNES**, **Gambatte** TI-83 | **TI83Hawk** TurboGrafx | HyperNyma, **PCEHawk**, **TurboNyma** Uzebox | **Uzem** diff --git a/src/BizHawk.Client.Common/config/Config.cs b/src/BizHawk.Client.Common/config/Config.cs index 1c0830e0a6..b476de52c9 100644 --- a/src/BizHawk.Client.Common/config/Config.cs +++ b/src/BizHawk.Client.Common/config/Config.cs @@ -24,7 +24,7 @@ namespace BizHawk.Client.Common (new[] { "SNES" }, new[] { CoreNames.Faust, CoreNames.Snes9X, CoreNames.Bsnes, CoreNames.Bsnes115 }), (new[] { "SGB" }, - new[] { CoreNames.Gambatte, CoreNames.SameBoy, CoreNames.Bsnes, CoreNames.Bsnes115}), + new[] { CoreNames.Gambatte, CoreNames.Bsnes, CoreNames.Bsnes115}), (new[] { "GB", "GBC" }, new[] { CoreNames.Gambatte, CoreNames.GbHawk, CoreNames.SubGbHawk }), (new[] { "DGB" }, @@ -322,7 +322,7 @@ namespace BizHawk.Client.Common ["GB"] = CoreNames.Gambatte, ["GBC"] = CoreNames.Gambatte, ["DGB"] = CoreNames.DualGambatte, - ["SGB"] = CoreNames.SameBoy, + ["SGB"] = CoreNames.Gambatte, ["PCE"] = CoreNames.TurboNyma, ["PCECD"] = CoreNames.TurboNyma, ["SGX"] = CoreNames.TurboNyma diff --git a/src/BizHawk.Client.EmuHawk/MainForm.Events.cs b/src/BizHawk.Client.EmuHawk/MainForm.Events.cs index ff63e76faf..4845ebf46f 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.Events.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.Events.cs @@ -1574,10 +1574,6 @@ namespace BizHawk.Client.EmuHawk { GBPrefs.DoGBPrefsDialog(this, Config, Game, MovieSession, gb); } - else // SameBoy - { - GenericCoreConfig.DoDialog(this, "Gameboy Settings"); - } } private void GbGpuViewerMenuItem_Click(object sender, EventArgs e) diff --git a/src/BizHawk.Client.EmuHawk/MainForm.cs b/src/BizHawk.Client.EmuHawk/MainForm.cs index e7548c2aac..a8a2636145 100644 --- a/src/BizHawk.Client.EmuHawk/MainForm.cs +++ b/src/BizHawk.Client.EmuHawk/MainForm.cs @@ -1997,7 +1997,6 @@ namespace BizHawk.Client.EmuHawk break; case "GB": case "GBC": - case "SGB" when Emulator is Sameboy: case "SGB" when Emulator is Gameboy: GBSubMenu.Visible = true; break; diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs deleted file mode 100644 index 40b5ba4adf..0000000000 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs +++ /dev/null @@ -1,55 +0,0 @@ -using BizHawk.BizInvoke; -using BizHawk.Emulation.Cores.Waterbox; -using System; - -using System.Runtime.InteropServices; - -namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy -{ - public abstract class LibSameboy : LibWaterboxCore - { - [Flags] - public enum Buttons : uint - { - A = 0x01, - B = 0x02, - SELECT = 0x04, - START = 0x08, - RIGHT = 0x10, - LEFT = 0x20, - UP = 0x40, - DOWN = 0x80 - } - - [StructLayout(LayoutKind.Sequential)] - public new class FrameInfo : LibWaterboxCore.FrameInfo - { - public long Time; - public Buttons Keys; - } - - [BizImport(CC)] - public abstract bool Init(bool cgb, byte[] spc, int spclen); - - [BizImport(CC)] - public abstract void GetGpuMemory(IntPtr[] ptrs); - - [BizImport(CC)] - public abstract void SetScanlineCallback(ScanlineCallback callback, int ly); - - [BizImport(CC)] - public abstract byte GetIoReg(byte port); - - [BizImport(CC)] - public abstract void PutSaveRam(); - - [BizImport(CC)] - public abstract void GetSaveRam(); - - [BizImport(CC)] - public abstract bool HasSaveRam(); - - [BizImport(CC)] - public abstract void SetPrinterCallback(PrinterCallback callback); - } -} diff --git a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs b/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs deleted file mode 100644 index 65d2be72a6..0000000000 --- a/src/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs +++ /dev/null @@ -1,410 +0,0 @@ -using BizHawk.Common; -using BizHawk.Emulation.Common; -using BizHawk.Emulation.Cores.Properties; -using BizHawk.Emulation.Cores.Waterbox; -using System; - -using System.ComponentModel; -using System.IO; -using System.Linq; - -namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy -{ - [PortedCore(CoreNames.SameBoy, "LIJI32", "efc11783c7fb6da66e1dd084e41ba6a85c0bd17e", "https://sameboy.github.io/")] - public class Sameboy : WaterboxCore, - IGameboyCommon, ISaveRam, - ISettable - { - /// - /// the nominal length of one frame - /// - private const int TICKSPERFRAME = 35112; - - /// - /// number of ticks per second (GB, CGB) - /// - private const int TICKSPERSECOND = 2097152; - - /// - /// number of ticks per second (SGB) - /// - private const int TICKSPERSECOND_SGB = 2147727; - - private readonly LibSameboy _core; - private readonly bool _cgb; - private readonly bool _sgb; - - private readonly IntPtr[] _cachedGpuPointers = new IntPtr[4]; - - [CoreConstructor("SGB")] - public Sameboy(byte[] rom, CoreComm comm, Settings settings, SyncSettings syncSettings, bool deterministic) - : this(rom, comm, true, settings, syncSettings, deterministic) - { } - - [CoreConstructor("GB")] - [CoreConstructor("GBC")] - public Sameboy(CoreComm comm, byte[] rom, Settings settings, SyncSettings syncSettings, bool deterministic) - : this(rom, comm, false, settings, syncSettings, deterministic) - { } - - public Sameboy(byte[] rom, CoreComm comm, bool sgb, Settings settings, SyncSettings syncSettings, bool deterministic) - : base(comm, new Configuration - { - DefaultWidth = sgb && (settings == null || settings.ShowSgbBorder) ? 256 : 160, - DefaultHeight = sgb && (settings == null || settings.ShowSgbBorder) ? 224 : 144, - MaxWidth = sgb ? 256 : 160, - MaxHeight = sgb ? 224 : 144, - MaxSamples = 1024, - DefaultFpsNumerator = sgb ? TICKSPERSECOND_SGB : TICKSPERSECOND, - DefaultFpsDenominator = TICKSPERFRAME, - SystemId = sgb ? "SGB" : "GB" - }) - { - _corePrinterCallback = PrinterCallbackRelay; - _coreScanlineCallback = ScanlineCallbackRelay; - - _core = PreInit(new WaterboxOptions - { - Filename = "sameboy.wbx", - SbrkHeapSizeKB = 192, - InvisibleHeapSizeKB = 12, - SealedHeapSizeKB = 9 * 1024, - PlainHeapSizeKB = 4, - MmapHeapSizeKB = 1024, - SkipCoreConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxCoreConsistencyCheck), - SkipMemoryConsistencyCheck = comm.CorePreferences.HasFlag(CoreComm.CorePreferencesFlags.WaterboxMemoryConsistencyCheck), - }, new Delegate[] { _corePrinterCallback, _coreScanlineCallback }); - - _cgb = (rom[0x143] & 0xc0) == 0xc0 && !sgb; - _sgb = sgb; - Console.WriteLine("Automaticly detected CGB to " + _cgb); - _syncSettings = syncSettings ?? new SyncSettings(); - _settings = settings ?? new Settings(); - - var bios = _syncSettings.UseRealBIOS && !sgb - ? comm.CoreFileProvider.GetFirmwareOrThrow(new(_cgb ? "GBC" : "GB", "World")) - : Util.DecompressGzipFile(new MemoryStream(_cgb ? Resources.SameboyCgbBoot.Value : Resources.SameboyDmgBoot.Value)); - - var spc = sgb - ? Util.DecompressGzipFile(new MemoryStream(Resources.SgbCartPresent_SPC.Value)) - : null; - - _exe.AddReadonlyFile(rom, "game.rom"); - _exe.AddReadonlyFile(bios, "boot.rom"); - - if (!_core.Init(_cgb, spc, spc?.Length ?? 0)) - { - throw new InvalidOperationException("Core rejected the rom!"); - } - - _exe.RemoveReadonlyFile("game.rom"); - _exe.RemoveReadonlyFile("boot.rom"); - - _core.GetGpuMemory(_cachedGpuPointers); - - PostInit(); - - DeterministicEmulation = deterministic || !_syncSettings.UseRealTime; - InitializeRtc(_syncSettings.InitialTime); - } - - private static readonly ControllerDefinition _gbDefinition; - private static readonly ControllerDefinition _sgbDefinition; - public override ControllerDefinition ControllerDefinition => _sgb ? _sgbDefinition : _gbDefinition; - - private static ControllerDefinition CreateControllerDefinition(int p) - { - var ret = new ControllerDefinition { Name = "Gameboy Controller" }; - for (int i = 0; i < p; i++) - { - ret.BoolButtons.AddRange( - new[] { "Up", "Down", "Left", "Right", "A", "B", "Select", "Start" } - .Select(s => $"P{i + 1} {s}")); - } - return ret; - } - - static Sameboy() - { - _gbDefinition = CreateControllerDefinition(1); - _sgbDefinition = CreateControllerDefinition(4); - } - - private LibSameboy.Buttons GetButtons(IController c) - { - LibSameboy.Buttons b = 0; - for (int i = _sgb ? 4 : 1; i > 0; i--) - { - if (c.IsPressed($"P{i} Up")) - b |= LibSameboy.Buttons.UP; - if (c.IsPressed($"P{i} Down")) - b |= LibSameboy.Buttons.DOWN; - if (c.IsPressed($"P{i} Left")) - b |= LibSameboy.Buttons.LEFT; - if (c.IsPressed($"P{i} Right")) - b |= LibSameboy.Buttons.RIGHT; - if (c.IsPressed($"P{i} A")) - b |= LibSameboy.Buttons.A; - if (c.IsPressed($"P{i} B")) - b |= LibSameboy.Buttons.B; - if (c.IsPressed($"P{i} Select")) - b |= LibSameboy.Buttons.SELECT; - if (c.IsPressed($"P{i} Start")) - b |= LibSameboy.Buttons.START; - if (_sgb) - { - // The SGB SNES side code enforces U+D/L+R prohibitions - if (((uint)b & 0x30) == 0x30) - b &= unchecked((LibSameboy.Buttons)~0x30); - if (((uint)b & 0xc0) == 0xc0) - b &= unchecked((LibSameboy.Buttons)~0xc0); - } - if (i != 1) - { - b = (LibSameboy.Buttons)((uint)b << 8); - } - } - return b; - } - - public new bool SaveRamModified => _core.HasSaveRam(); - - public new byte[] CloneSaveRam() - { - _exe.AddTransientFile(new byte[0], "save.ram"); - _core.GetSaveRam(); - return _exe.RemoveTransientFile("save.ram"); - } - - public new void StoreSaveRam(byte[] data) - { - _exe.AddReadonlyFile(data, "save.ram"); - _core.PutSaveRam(); - _exe.RemoveReadonlyFile("save.ram"); - } - - private Settings _settings; - private SyncSettings _syncSettings; - - public class Settings - { - [DisplayName("Show SGB Border")] - [DefaultValue(true)] - public bool ShowSgbBorder { get; set; } - - public Settings Clone() - { - return (Settings)MemberwiseClone(); - } - - public static bool NeedsReboot(Settings x, Settings y) - { - return false; - } - - public Settings() - { - SettingsUtil.SetDefaultValues(this); - } - } - - public class SyncSettings - { - [DisplayName("Initial Time")] - [Description("Initial time of emulation. Only relevant when UseRealTime is false.")] - [DefaultValue(typeof(DateTime), "2010-01-01")] - public DateTime InitialTime { get; set; } - - [DisplayName("Use RealTime")] - [Description("If true, RTC clock will be based off of real time instead of emulated time. Ignored (set to false) when recording a movie.")] - [DefaultValue(false)] - public bool UseRealTime { get; set; } - - [Description("If true, real BIOS files will be used. Ignored in SGB mode.")] - [DefaultValue(false)] - public bool UseRealBIOS { get; set; } - - public SyncSettings Clone() - { - return (SyncSettings)MemberwiseClone(); - } - - public static bool NeedsReboot(SyncSettings x, SyncSettings y) - { - return !DeepEquality.DeepEquals(x, y); - } - - public SyncSettings() - { - SettingsUtil.SetDefaultValues(this); - } - } - - public Settings GetSettings() - { - return _settings.Clone(); - } - - public SyncSettings GetSyncSettings() - { - return _syncSettings.Clone(); - } - - public PutSettingsDirtyBits PutSettings(Settings o) - { - var ret = Settings.NeedsReboot(_settings, o); - _settings = o; - return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None; - } - - public PutSettingsDirtyBits PutSyncSettings(SyncSettings o) - { - var ret = SyncSettings.NeedsReboot(_syncSettings, o); - _syncSettings = o; - return ret ? PutSettingsDirtyBits.RebootCore : PutSettingsDirtyBits.None; - } - - protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) - { - return new LibSameboy.FrameInfo - { - Time = GetRtcTime(!DeterministicEmulation), - Keys = GetButtons(controller) - }; - } - - protected override unsafe void FrameAdvancePost() - { - if (_frontendScanlineCallback != null && _scanlineCallbackLine == -1) - _frontendScanlineCallback(_core.GetIoReg(0x40)); - - if (_sgb && !_settings.ShowSgbBorder) - { - fixed(int *buff = _videoBuffer) - { - int* dst = buff; - int* src = buff + (224 - 144) / 2 * 256 + (256 - 160) / 2; - for (int j = 0; j < 144; j++) - { - for (int i = 0; i < 160; i++) - { - *dst++ = *src++; - } - src += 256 - 160; - } - } - BufferWidth = 160; - BufferHeight = 144; - } - } - - protected override void LoadStateBinaryInternal(BinaryReader reader) - { - UpdateCoreScanlineCallback(false); - UpdateCorePrinterCallback(); - } - - public bool IsCGBMode() => _cgb; - - public IGPUMemoryAreas LockGPU() - { - _exe.Enter(); - try - { - return new GPUMemoryAreas(_exe) - { - Vram = _cachedGpuPointers[0], - Oam = _cachedGpuPointers[1], - Sppal = _cachedGpuPointers[3], - Bgpal = _cachedGpuPointers[2] - }; - } - catch - { - _exe.Exit(); - throw; - } - } - - private class GPUMemoryAreas : IGPUMemoryAreas - { - private IMonitor _monitor; - public IntPtr Vram { get; init; } - - public IntPtr Oam { get; init; } - - public IntPtr Sppal { get; init; } - - public IntPtr Bgpal { get; init; } - - public GPUMemoryAreas(IMonitor monitor) - { - _monitor = monitor; - } - - public void Dispose() - { - _monitor?.Exit(); - _monitor = null; - } - } - - private readonly ScanlineCallback _coreScanlineCallback; - private ScanlineCallback _frontendScanlineCallback; - private int _scanlineCallbackLine; - - private void ScanlineCallbackRelay(byte lcdc) - { - _frontendScanlineCallback?.Invoke(lcdc); - } - - public void SetScanlineCallback(ScanlineCallback callback, int line) - { - _frontendScanlineCallback = callback; - _scanlineCallbackLine = line; - UpdateCoreScanlineCallback(true); - } - - private void UpdateCoreScanlineCallback(bool now) - { - if (_frontendScanlineCallback == null) - { - _core.SetScanlineCallback(null, -1); - } - else - { - if (_scanlineCallbackLine >= 0 && _scanlineCallbackLine <= 153) - { - _core.SetScanlineCallback(_coreScanlineCallback, _scanlineCallbackLine); - } - else - { - _core.SetScanlineCallback(null, -1); - if (_scanlineCallbackLine == -2 && now) - { - _frontendScanlineCallback(_core.GetIoReg(0x40)); - } - } - } - } - - private readonly PrinterCallback _corePrinterCallback; - private PrinterCallback _frontendPrinterCallback; - - private void PrinterCallbackRelay(IntPtr image, byte height, byte top_margin, byte bottom_margin, byte exposure) - { - _frontendPrinterCallback?.Invoke(image, height, top_margin, bottom_margin, exposure); - } - - public void SetPrinterCallback(PrinterCallback callback) - { - _frontendPrinterCallback = callback; - UpdateCorePrinterCallback(); - } - - private void UpdateCorePrinterCallback() - { - _core.SetPrinterCallback(_frontendPrinterCallback != null ? _corePrinterCallback : null); - } - } -} diff --git a/src/BizHawk.Emulation.Cores/CoreNames.cs b/src/BizHawk.Emulation.Cores/CoreNames.cs index 251c546ffd..a70a79ae75 100644 --- a/src/BizHawk.Emulation.Cores/CoreNames.cs +++ b/src/BizHawk.Emulation.Cores/CoreNames.cs @@ -44,7 +44,6 @@ namespace BizHawk.Emulation.Cores public const string PceHawk = "PCEHawk"; public const string PicoDrive = "PicoDrive"; public const string QuickNes = "QuickNes"; - public const string SameBoy = "SameBoy"; public const string Saturnus = "Saturnus"; public const string SMSHawk = "SMSHawk"; public const string Snes9X = "Snes9x"; diff --git a/waterbox/make-all-cores.sh b/waterbox/make-all-cores.sh index e43ea80408..385f6ba699 100644 --- a/waterbox/make-all-cores.sh +++ b/waterbox/make-all-cores.sh @@ -6,7 +6,6 @@ cd libco && make -f Makefile $1 -j && cd - cd gpgx && make -f Makefile $1 -j && cd - cd libsnes && make -f Makefile $1 -j && cd - cd picodrive && make -f Makefile $1 -j && cd - -cd sameboy && make -f Makefile $1 -j && cd - cd snes9x && make -f Makefile $1 -j && cd - cd uzem && make -f Makefile $1 -j && cd - cd vb && make -f Makefile $1 -j && cd - diff --git a/waterbox/readme.txt b/waterbox/readme.txt index 1a3749e4eb..ac7995c10d 100644 --- a/waterbox/readme.txt +++ b/waterbox/readme.txt @@ -56,7 +56,6 @@ It consists of a modified musl libc, and build scripts to tie it all together. cd nyma && make -f pcfx.mak install cd nyma && make -f ss.mak install cd picodrive && make install - cd sameboy && make install cd snes9x && make install cd uzem && make install cd vb && make install diff --git a/waterbox/sameboy/.vscode/settings.json b/waterbox/sameboy/.vscode/settings.json deleted file mode 100644 index a5401e1c1f..0000000000 --- a/waterbox/sameboy/.vscode/settings.json +++ /dev/null @@ -1,6 +0,0 @@ -// Place your settings in this file to overwrite default and user settings. -{ - "editor.tabSize": 4, - "editor.insertSpaces": false, - "editor.detectIndentation": false -} \ No newline at end of file diff --git a/waterbox/sameboy/CHANGES.md b/waterbox/sameboy/CHANGES.md deleted file mode 100644 index b94aec4c71..0000000000 --- a/waterbox/sameboy/CHANGES.md +++ /dev/null @@ -1 +0,0 @@ -See https://sameboy.github.io/changelog/ \ No newline at end of file diff --git a/waterbox/sameboy/LICENSE b/waterbox/sameboy/LICENSE deleted file mode 100644 index 008851f512..0000000000 --- a/waterbox/sameboy/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2015-2017 Lior Halphon - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/waterbox/sameboy/Makefile b/waterbox/sameboy/Makefile deleted file mode 100644 index 6634f26730..0000000000 --- a/waterbox/sameboy/Makefile +++ /dev/null @@ -1,17 +0,0 @@ -FLAGS := -Wall -Werror=int-to-pointer-cast \ - -Wno-multichar \ - -fomit-frame-pointer -DGB_INTERNAL -D_GNU_SOURCE - -CCFLAGS := $(FLAGS) \ - -std=gnu99 \ - -DLSB_FIRST -Werror=pointer-to-int-cast -Werror=implicit-function-declaration - -CXXFLAGS := $(FLAGS) -DSPC_NO_COPY_STATE_FUNCS -std=c++0x -fno-exceptions -fno-rtti - -TARGET := sameboy.wbx - -C_SRCS = $(shell find $(ROOT_DIR) -type f -name '*.c') -CPP_SRCS = $(shell find $(ROOT_DIR) -type f -name '*.cpp') -SRCS = $(C_SRCS) $(CPP_SRCS) - -include ../common.mak diff --git a/waterbox/sameboy/README.md b/waterbox/sameboy/README.md deleted file mode 100644 index 9aa5201b63..0000000000 --- a/waterbox/sameboy/README.md +++ /dev/null @@ -1,51 +0,0 @@ -# SameBoy - -SameBoy is an open source Gameboy (DMG) and Gameboy Color (CGB) emulator, written in portable C. It has a native Cocoa frontend for OS X, and an incomplete experimental SDL frontend for other operating systems. It also includes a text-based debugger with an expression evaluator. Visit [the website](https://sameboy.github.io/). - -## Features -Features common to both Cocoa and SDL versions: - * Supports Gameboy (DMG) and Gameboy Color (CGB) emulation - * Lets you choose the model you want to emulate regardless of ROM - * High quality 96KHz audio - * Battery save support - * Save states - * Includes open source DMG and CGB boot ROMs: - * Complete support for (and documentation of) *all* game-specific palettes in the CGB boot ROM, for accurate emulation of Gameboy games on a Gameboy Color - * Supports manual palette selection with key combinations, with 4 additional new palettes (A + B + direction) - * Supports palette selection in a CGB game, forcing it to run in 'paletted' DMG mode, if ROM allows doing so. - * Support for games with a non-Nintendo logo in the header - * No long animation in the DMG boot - * Advanced text-based debugger with an expression evaluator, disassembler, conditional breakpoints, conditional watchpoints, backtracing and other features - * Emulates [PCM_12 and PCM_34 registers](https://github.com/LIJI32/GBVisualizer) - * Emulates LCD timing effects, supporting the Demotronic trick, [GBVideoPlayer](https://github.com/LIJI32/GBVideoPlayer) and other tech demos - * Extermely high accuracy - * Real time clock emulation - -Features currently supported only with the Cocoa version: - * Native Cocoa interface, with support for all system-wide features, such as drag-and-drop and smart titlebars - * Retina display support, allowing a wider range of scaling factors without artifacts - * Optional frame blending - * Several [scaling algorithms](https://sameboy.github.io/scaling/) (Including exclusive algorithms like OmniScale and Anti-aliased Scale2x) - * GameBoy Camera support - -[Read more](https://sameboy.github.io/features/). - -## Compatibility -SameBoy passes many of [blargg's test ROMs](http://gbdev.gg8.se/wiki/articles/Test_ROMs#Blargg.27s_tests), as well as 77 out of 78 of [mooneye-gb's](https://github.com/Gekkio/mooneye-gb) tests (Some tests require the original boot ROMs). SameBoy should work with most games and demos, please [report](https://github.com/LIJI32/SameBoy/issues/new) any broken ROM. The latest results for SameBoy's automatic tester are available [here](https://sameboy.github.io/automation/). - -## Compilation -SameBoy requires the following tools and libraries to build: - * clang - * make - * Cocoa port: OS X SDK and Xcode command line tools - * SDL port: SDL2.framework (OS X) or libsdl2 (Other platforms) - * [rgbds](https://github.com/bentley/rgbds/releases/), for boot ROM compilation - -On Windows, SameBoy also requires: - * Visual Studio (For headers, etc.) - * [GnuWin](http://gnuwin32.sourceforge.net/) - * Running vcvars32 before running make. Make sure all required tools and libraries are in %PATH% and %lib%, repsectively. - -To compile, simply run `make`. The targets are cocoa (Default for OS X), sdl (Default for everything else), bootroms and tester. You may also specify CONF=debug (default), CONF=release or CONF=native_release to control optimization and symbols. native_release is faster than release, but is optimized to the host's CPU and therefore is not portable. You may set BOOTROMS_DIR=... to a directory containing precompiled dmg_boot.bin and cgb_boot.bin files, otherwise the build system will compile and use SameBoy's own boot ROMs. - -SameBoy was compiled and tested on macOS, Ubuntu and 32-bit Windows 7. diff --git a/waterbox/sameboy/apu.c b/waterbox/sameboy/apu.c deleted file mode 100644 index 10b5bf9bea..0000000000 --- a/waterbox/sameboy/apu.c +++ /dev/null @@ -1,402 +0,0 @@ -#include -#include -#include -#include "gb.h" - -#undef max -#define max(a,b) \ -({ __typeof__ (a) _a = (a); \ -__typeof__ (b) _b = (b); \ -_a > _b ? _a : _b; }) - -#undef min -#define min(a,b) \ -({ __typeof__ (a) _a = (a); \ -__typeof__ (b) _b = (b); \ -_a < _b ? _a : _b; }) - -#define APU_FREQUENCY 0x80000 - -static int16_t generate_square(uint64_t phase, uint32_t wave_length, int16_t amplitude, uint8_t duty) -{ - if (!wave_length) return 0; - if (phase % wave_length > wave_length * duty / 8) { - return amplitude; - } - return 0; -} - -static int16_t generate_wave(uint64_t phase, uint32_t wave_length, int16_t amplitude, int8_t *wave, uint8_t shift) -{ - if (!wave_length) wave_length = 1; - phase = phase % wave_length; - return ((wave[(int)(phase * 32 / wave_length)]) >> shift) * (int)amplitude / 0xF; -} - -static int16_t generate_noise(int16_t amplitude, uint16_t lfsr) -{ - if (lfsr & 1) { - return amplitude; - } - return 0; -} - -static int16_t step_lfsr(uint16_t lfsr, bool uses_7_bit) -{ - bool xor = (lfsr & 1) ^ ((lfsr & 2) >> 1); - lfsr >>= 1; - if (xor) { - lfsr |= 0x4000; - } - if (uses_7_bit) { - lfsr &= ~0x40; - if (xor) { - lfsr |= 0x40; - } - } - return lfsr; -} - -/* General Todo: The APU emulation seems to fail many accuracy tests. It might require a rewrite with - these tests in mind. */ - -static void GB_apu_run_internal(GB_gameboy_t *gb) -{ - uint32_t steps = gb->apu.apu_cycles / (CPU_FREQUENCY/APU_FREQUENCY); - if (!steps) - return; - - gb->apu.apu_cycles %= (CPU_FREQUENCY/APU_FREQUENCY); - for (uint8_t i = 0; i < 4; i++) { - /* Phase */ - gb->apu.wave_channels[i].phase += steps; - while (gb->apu.wave_channels[i].wave_length && gb->apu.wave_channels[i].phase >= gb->apu.wave_channels[i].wave_length) { - if (i == 3) { - gb->apu.lfsr = step_lfsr(gb->apu.lfsr, gb->apu.lfsr_7_bit); - } - - gb->apu.wave_channels[i].phase -= gb->apu.wave_channels[i].wave_length; - } - /* Stop on Length */ - if (gb->apu.wave_channels[i].stop_on_length) { - if (gb->apu.wave_channels[i].sound_length > 0) { - gb->apu.wave_channels[i].sound_length -= steps; - } - if (gb->apu.wave_channels[i].sound_length <= 0) { - gb->apu.wave_channels[i].amplitude = 0; - gb->apu.wave_channels[i].is_playing = false; - gb->apu.wave_channels[i].sound_length = i == 2? APU_FREQUENCY : APU_FREQUENCY / 4; - } - } - } - - gb->apu.envelope_step_timer += steps; - while (gb->apu.envelope_step_timer >= APU_FREQUENCY / 64) { - gb->apu.envelope_step_timer -= APU_FREQUENCY / 64; - for (uint8_t i = 0; i < 4; i++) { - if (gb->apu.wave_channels[i].envelope_steps && !--gb->apu.wave_channels[i].cur_envelope_steps) { - gb->apu.wave_channels[i].amplitude = min(max(gb->apu.wave_channels[i].amplitude + gb->apu.wave_channels[i].envelope_direction * CH_STEP, 0), MAX_CH_AMP); - gb->apu.wave_channels[i].cur_envelope_steps = gb->apu.wave_channels[i].envelope_steps; - } - } - } - - gb->apu.sweep_step_timer += steps; - while (gb->apu.sweep_step_timer >= APU_FREQUENCY / 128) { - gb->apu.sweep_step_timer -= APU_FREQUENCY / 128; - if (gb->apu.wave_channels[0].sweep_steps && !--gb->apu.wave_channels[0].cur_sweep_steps) { - - // Convert back to GB format - uint16_t temp = 2048 - gb->apu.wave_channels[0].wave_length / (APU_FREQUENCY / 131072); - - // Apply sweep - temp = temp + gb->apu.wave_channels[0].sweep_direction * - (temp / (1 << gb->apu.wave_channels[0].sweep_shift)); - if (temp > 2047) { - temp = 0; - } - - // Back to frequency - gb->apu.wave_channels[0].wave_length = (2048 - temp) * (APU_FREQUENCY / 131072); - gb->apu.wave_channels[0].cur_sweep_steps = gb->apu.wave_channels[0].sweep_steps; - } - } -} - -void GB_apu_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *samples) -{ - GB_apu_run_internal(gb); - - samples->left = samples->right = 0; - if (!gb->apu.global_enable) { - return; - } - - gb->io_registers[GB_IO_PCM_12] = 0; - gb->io_registers[GB_IO_PCM_34] = 0; - - { - int16_t sample = generate_square(gb->apu.wave_channels[0].phase, - gb->apu.wave_channels[0].wave_length, - gb->apu.wave_channels[0].amplitude, - gb->apu.wave_channels[0].duty); - if (gb->apu.wave_channels[0].left_on ) samples->left += sample; - if (gb->apu.wave_channels[0].right_on) samples->right += sample; - gb->io_registers[GB_IO_PCM_12] = ((int)sample) * 0xF / MAX_CH_AMP; - } - - { - int16_t sample = generate_square(gb->apu.wave_channels[1].phase, - gb->apu.wave_channels[1].wave_length, - gb->apu.wave_channels[1].amplitude, - gb->apu.wave_channels[1].duty); - if (gb->apu.wave_channels[1].left_on ) samples->left += sample; - if (gb->apu.wave_channels[1].right_on) samples->right += sample; - gb->io_registers[GB_IO_PCM_12] |= (((int)sample) * 0xF / MAX_CH_AMP) << 4; - } - - if (gb->apu.wave_channels[2].is_playing) - { - int16_t sample = generate_wave(gb->apu.wave_channels[2].phase, - gb->apu.wave_channels[2].wave_length, - MAX_CH_AMP, - gb->apu.wave_form, - gb->apu.wave_shift); - if (gb->apu.wave_channels[2].left_on ) samples->left += sample; - if (gb->apu.wave_channels[2].right_on) samples->right += sample; - gb->io_registers[GB_IO_PCM_34] = ((int)sample) * 0xF / MAX_CH_AMP; - } - - { - int16_t sample = generate_noise(gb->apu.wave_channels[3].amplitude, - gb->apu.lfsr); - if (gb->apu.wave_channels[3].left_on ) samples->left += sample; - if (gb->apu.wave_channels[3].right_on) samples->right += sample; - gb->io_registers[GB_IO_PCM_34] |= (((int)sample) * 0xF / MAX_CH_AMP) << 4; - } - - samples->left = (int) samples->left * gb->apu.left_volume / 7; - samples->right = (int) samples->right * gb->apu.right_volume / 7; -} - -void GB_apu_run(GB_gameboy_t *gb) -{ - GB_sample_t sample; - GB_apu_get_samples_and_update_pcm_regs(gb, &sample); - gb->sample_callback(gb, sample, gb->cycles_since_epoch); -} - -void GB_apu_init(GB_gameboy_t *gb) -{ - memset(&gb->apu, 0, sizeof(gb->apu)); - gb->apu.wave_channels[0].duty = gb->apu.wave_channels[1].duty = 4; - gb->apu.lfsr = 0x7FFF; - gb->apu.left_volume = 7; - gb->apu.right_volume = 7; - for (int i = 0; i < 4; i++) { - gb->apu.wave_channels[i].left_on = gb->apu.wave_channels[i].right_on = 1; - } -} - -uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg) -{ - GB_apu_run_internal(gb); - - if (reg == GB_IO_NR52) { - uint8_t value = 0; - for (int i = 0; i < 4; i++) { - value >>= 1; - if (gb->apu.wave_channels[i].is_playing) { - value |= 0x8; - } - } - if (gb->apu.global_enable) { - value |= 0x80; - } - value |= 0x70; - return value; - } - - static const char read_mask[GB_IO_WAV_END - GB_IO_NR10 + 1] = { - /* NRX0 NRX1 NRX2 NRX3 NRX4 */ - 0x80, 0x3F, 0x00, 0xFF, 0xBF, // NR1X - 0xFF, 0x3F, 0x00, 0xFF, 0xBF, // NR2X - 0x7F, 0xFF, 0x9F, 0xFF, 0xBF, // NR3X - 0xFF, 0xFF, 0x00, 0x00, 0xBF, // NR4X - 0x00, 0x00, 0x70, 0xFF, 0xFF, // NR5X - - 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, // Unused - // Wave RAM - 0, /* ... */ - }; - - if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END && gb->apu.wave_channels[2].is_playing) { - if (gb->apu.wave_channels[2].wave_length == 0) { - return gb->apu.wave_form[0]; - } - gb->apu.wave_channels[2].phase %= gb->apu.wave_channels[2].wave_length; - return gb->apu.wave_form[(int)(gb->apu.wave_channels[2].phase * 32 / gb->apu.wave_channels[2].wave_length)]; - } - - return gb->io_registers[reg] | read_mask[reg - GB_IO_NR10]; -} - -void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value) -{ - GB_apu_run_internal(gb); - - static const uint8_t duties[] = {1, 2, 4, 6}; /* Values are in 1/8 */ - uint8_t channel = 0; - - if (!gb->apu.global_enable && reg != GB_IO_NR52) { - return; - } - - gb->io_registers[reg] = value; - - switch (reg) { - case GB_IO_NR10: - case GB_IO_NR11: - case GB_IO_NR12: - case GB_IO_NR13: - case GB_IO_NR14: - channel = 0; - break; - case GB_IO_NR21: - case GB_IO_NR22: - case GB_IO_NR23: - case GB_IO_NR24: - channel = 1; - break; - case GB_IO_NR33: - case GB_IO_NR34: - channel = 2; - break; - case GB_IO_NR41: - case GB_IO_NR42: - channel = 3; - default: - break; - } - - switch (reg) { - case GB_IO_NR10: - gb->apu.wave_channels[channel].sweep_direction = value & 8? -1 : 1; - gb->apu.wave_channels[channel].cur_sweep_steps = - gb->apu.wave_channels[channel].sweep_steps = (value & 0x70) >> 4; - gb->apu.wave_channels[channel].sweep_shift = value & 7; - break; - case GB_IO_NR11: - case GB_IO_NR21: - case GB_IO_NR41: - gb->apu.wave_channels[channel].duty = duties[value >> 6]; - gb->apu.wave_channels[channel].sound_length = (64 - (value & 0x3F)) * (APU_FREQUENCY / 256); - if (gb->apu.wave_channels[channel].sound_length == 0) { - gb->apu.wave_channels[channel].is_playing = false; - } - break; - case GB_IO_NR12: - case GB_IO_NR22: - case GB_IO_NR42: - gb->apu.wave_channels[channel].start_amplitude = - gb->apu.wave_channels[channel].amplitude = CH_STEP * (value >> 4); - if (value >> 4 == 0) { - gb->apu.wave_channels[channel].is_playing = false; - } - gb->apu.wave_channels[channel].envelope_direction = value & 8? 1 : -1; - gb->apu.wave_channels[channel].cur_envelope_steps = - gb->apu.wave_channels[channel].envelope_steps = value & 7; - break; - case GB_IO_NR13: - case GB_IO_NR23: - case GB_IO_NR33: - gb->apu.wave_channels[channel].NRX3_X4_temp = (gb->apu.wave_channels[channel].NRX3_X4_temp & 0xFF00) | value; - gb->apu.wave_channels[channel].wave_length = (2048 - gb->apu.wave_channels[channel].NRX3_X4_temp) * (APU_FREQUENCY / 131072); - if (channel == 2) { - gb->apu.wave_channels[channel].wave_length *= 2; - } - break; - case GB_IO_NR14: - case GB_IO_NR24: - case GB_IO_NR34: - gb->apu.wave_channels[channel].stop_on_length = value & 0x40; - if ((value & 0x80) && (channel != 2 || gb->apu.wave_enable)) { - gb->apu.wave_channels[channel].is_playing = true; - gb->apu.wave_channels[channel].phase = 0; - gb->apu.wave_channels[channel].amplitude = gb->apu.wave_channels[channel].start_amplitude; - gb->apu.wave_channels[channel].cur_envelope_steps = gb->apu.wave_channels[channel].envelope_steps; - } - - gb->apu.wave_channels[channel].NRX3_X4_temp = (gb->apu.wave_channels[channel].NRX3_X4_temp & 0xFF) | ((value & 0x7) << 8); - gb->apu.wave_channels[channel].wave_length = (2048 - gb->apu.wave_channels[channel].NRX3_X4_temp) * (APU_FREQUENCY / 131072); - if (channel == 2) { - gb->apu.wave_channels[channel].wave_length *= 2; - } - break; - case GB_IO_NR30: - gb->apu.wave_enable = value & 0x80; - gb->apu.wave_channels[2].is_playing &= gb->apu.wave_enable; - break; - case GB_IO_NR31: - gb->apu.wave_channels[2].sound_length = (256 - value) * (APU_FREQUENCY / 256); - if (gb->apu.wave_channels[2].sound_length == 0) { - gb->apu.wave_channels[2].is_playing = false; - } - break; - case GB_IO_NR32: - gb->apu.wave_shift = ((value >> 5) + 3) & 3; - if (gb->apu.wave_shift == 3) { - gb->apu.wave_shift = 4; - } - break; - case GB_IO_NR43: - { - double r = value & 0x7; - if (r == 0) r = 0.5; - uint8_t s = value >> 4; - gb->apu.wave_channels[3].wave_length = r * (1 << s) * (APU_FREQUENCY / 262144) ; - gb->apu.lfsr_7_bit = value & 0x8; - break; - } - case GB_IO_NR44: - gb->apu.wave_channels[3].stop_on_length = value & 0x40; - if (value & 0x80) { - gb->apu.wave_channels[3].is_playing = true; - gb->apu.lfsr = 0x7FFF; - gb->apu.wave_channels[3].amplitude = gb->apu.wave_channels[3].start_amplitude; - gb->apu.wave_channels[3].cur_envelope_steps = gb->apu.wave_channels[3].envelope_steps; - } - break; - - case GB_IO_NR50: - gb->apu.left_volume = (value & 7); - gb->apu.right_volume = ((value >> 4) & 7); - break; - - case GB_IO_NR51: - for (int i = 0; i < 4; i++) { - gb->apu.wave_channels[i].left_on = value & 1; - gb->apu.wave_channels[i].right_on = value & 0x10; - value >>= 1; - } - break; - case GB_IO_NR52: - - if ((value & 0x80) && !gb->apu.global_enable) { - GB_apu_init(gb); - gb->apu.global_enable = true; - } - else if (!(value & 0x80) && gb->apu.global_enable) { - memset(&gb->apu, 0, sizeof(gb->apu)); - memset(gb->io_registers + GB_IO_NR10, 0, GB_IO_WAV_START - GB_IO_NR10); - } - break; - - default: - if (reg >= GB_IO_WAV_START && reg <= GB_IO_WAV_END) { - gb->apu.wave_form[(reg - GB_IO_WAV_START) * 2] = value >> 4; - gb->apu.wave_form[(reg - GB_IO_WAV_START) * 2 + 1] = value & 0xF; - } - break; - } -} diff --git a/waterbox/sameboy/apu.h b/waterbox/sameboy/apu.h deleted file mode 100644 index a8eab55fd6..0000000000 --- a/waterbox/sameboy/apu.h +++ /dev/null @@ -1,65 +0,0 @@ -#ifndef apu_h -#define apu_h -#include -#include -#include "gb_struct_def.h" -/* Divides nicely and never overflows with 4 channels */ -#define MAX_CH_AMP 0x1E00 -#define CH_STEP (0x1E00/0xF) - - -typedef struct -{ - int16_t left; - int16_t right; -} GB_sample_t; - -/* Not all used on all channels */ -/* All lengths are in APU ticks */ -typedef struct -{ - uint32_t phase; - uint32_t wave_length; - int32_t sound_length; - bool stop_on_length; - uint8_t duty; - int16_t amplitude; - int16_t start_amplitude; - uint8_t envelope_steps; - uint8_t cur_envelope_steps; - int8_t envelope_direction; - uint8_t sweep_steps; - uint8_t cur_sweep_steps; - int8_t sweep_direction; - uint8_t sweep_shift; - bool is_playing; - uint16_t NRX3_X4_temp; - bool left_on; - bool right_on; -} GB_apu_channel_t; - -typedef struct -{ - uint16_t apu_cycles; - bool global_enable; - uint32_t envelope_step_timer; - uint32_t sweep_step_timer; - int8_t wave_form[32]; - uint8_t wave_shift; - bool wave_enable; - uint16_t lfsr; - bool lfsr_7_bit; - uint8_t left_volume; - uint8_t right_volume; - GB_apu_channel_t wave_channels[4]; -} GB_apu_t; - -#ifdef GB_INTERNAL -void GB_apu_write(GB_gameboy_t *gb, uint8_t reg, uint8_t value); -uint8_t GB_apu_read(GB_gameboy_t *gb, uint8_t reg); -void GB_apu_get_samples_and_update_pcm_regs(GB_gameboy_t *gb, GB_sample_t *samples); -void GB_apu_init(GB_gameboy_t *gb); -void GB_apu_run(GB_gameboy_t *gb); -#endif - -#endif /* apu_h */ diff --git a/waterbox/sameboy/bizhawk.cpp b/waterbox/sameboy/bizhawk.cpp deleted file mode 100644 index 94ba62a39f..0000000000 --- a/waterbox/sameboy/bizhawk.cpp +++ /dev/null @@ -1,285 +0,0 @@ -#include -#include "../emulibc/emulibc.h" -#include "../emulibc/waterboxcore.h" -#include "blip_buf/blip_buf.h" - -#define _Static_assert static_assert - -extern "C" { -#include "gb.h" -#include "joypad.h" -#include "apu.h" -#include "sgb.h" -} - -static GB_gameboy_t GB; - -static uint32_t GBPixels[160 * 144]; -static uint32_t *CurrentFramebuffer; -static bool sgb; -static void VBlankCallback(GB_gameboy_t *gb) -{ - if (sgb) - { - sgb_take_frame(GBPixels); - sgb_render_frame(CurrentFramebuffer); - } - else - { - memcpy(CurrentFramebuffer, GBPixels, sizeof(GBPixels)); - } -} - -static void LogCallback(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes) -{ - fputs(string, stdout); -} - -static uint32_t RgbEncodeCallback(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b) -{ - return b | g << 8 | r << 16 | 0xff000000; -} - -static void InfraredCallback(GB_gameboy_t *gb, bool on, long cycles_since_last_update) -{ -} - -static void RumbleCallback(GB_gameboy_t *gb, bool rumble_on) -{ -} - -static void SerialStartCallback(GB_gameboy_t *gb, uint8_t byte_to_send) -{ -} - -static uint8_t SerialEndCallback(GB_gameboy_t *gb) -{ - return 0; -} - -static void (*FrontendInputCallback)(); - -static void InputCallback(GB_gameboy_t *gb) -{ - FrontendInputCallback(); -} - -typedef void (*FrontendPrinterCallback_t)(uint32_t *image, - uint8_t height, - uint8_t top_margin, - uint8_t bottom_margin, - uint8_t exposure); - -static FrontendPrinterCallback_t FrontendPrinterCallback; - -static void PrinterCallback(GB_gameboy_t *gb, - uint32_t *image, - uint8_t height, - uint8_t top_margin, - uint8_t bottom_margin, - uint8_t exposure) -{ - FrontendPrinterCallback(image, height, top_margin, bottom_margin, exposure); -} - -static blip_t *leftblip; -static blip_t *rightblip; -const int SOUND_RATE_GB = 2097152; -const int SOUND_RATE_SGB = 2147727; -static uint64_t sound_start_clock; -static GB_sample_t sample_gb; -static GB_sample_t sample_sgb; - -static void SampleCallback(GB_gameboy_t *gb, GB_sample_t sample, uint64_t clock) -{ - int l = sample.left - sample_gb.left; - int r = sample.right - sample_gb.right; - if (l) - blip_add_delta(leftblip, clock - sound_start_clock, l); - if (r) - blip_add_delta(rightblip, clock - sound_start_clock, r); - sample_gb = sample; -} -static void SgbSampleCallback(int16_t sl, int16_t sr, uint64_t clock) -{ - int l = sl - sample_sgb.left; - int r = sr - sample_sgb.right; - if (l) - blip_add_delta(leftblip, clock - sound_start_clock, l); - if (r) - blip_add_delta(rightblip, clock - sound_start_clock, r); - sample_sgb.left = sl; - sample_sgb.right = sr; -} - -ECL_EXPORT bool Init(bool cgb, const uint8_t *spc, int spclen) -{ - if (spc) - { - GB_init_sgb(&GB); - if (!sgb_init(spc, spclen)) - return false; - sgb = true; - } - else if (cgb) - { - GB_init_cgb(&GB); - } - else - { - GB_init(&GB); - } - - if (GB_load_boot_rom(&GB, "boot.rom") != 0) - return false; - if (GB_load_rom(&GB, "game.rom") != 0) - return false; - - GB_set_pixels_output(&GB, GBPixels); - GB_set_vblank_callback(&GB, VBlankCallback); - GB_set_log_callback(&GB, LogCallback); - GB_set_rgb_encode_callback(&GB, RgbEncodeCallback); - GB_set_infrared_callback(&GB, InfraredCallback); - GB_set_rumble_callback(&GB, RumbleCallback); - GB_set_sample_callback(&GB, SampleCallback); - - leftblip = blip_new(1024); - rightblip = blip_new(1024); - blip_set_rates(leftblip, sgb ? SOUND_RATE_SGB : SOUND_RATE_GB, 44100); - blip_set_rates(rightblip, sgb ? SOUND_RATE_SGB : SOUND_RATE_GB, 44100); - - return true; -} - -struct MyFrameInfo : public FrameInfo -{ - int64_t Time; - uint32_t Keys; -}; - -static int FrameOverflow; - -ECL_EXPORT void FrameAdvance(MyFrameInfo &f) -{ - if (sgb) - { - sgb_set_controller_data((uint8_t *)&f.Keys); - } - else - { - GB_set_key_state(&GB, f.Keys & 0xff); - } - sound_start_clock = GB_epoch(&GB); - CurrentFramebuffer = f.VideoBuffer; - GB_set_lagged(&GB, true); - GB.frontend_rtc_time = f.Time; - - uint32_t target = 35112 - FrameOverflow; - f.Cycles = GB_run_cycles(&GB, target); - FrameOverflow = f.Cycles - target; - if (sgb) - { - f.Width = 256; - f.Height = 224; - sgb_render_audio(GB_epoch(&GB), SgbSampleCallback); - } - else - { - f.Width = 160; - f.Height = 144; - } - blip_end_frame(leftblip, f.Cycles); - blip_end_frame(rightblip, f.Cycles); - f.Samples = blip_read_samples(leftblip, f.SoundBuffer, 2048, 1); - blip_read_samples(rightblip, f.SoundBuffer + 1, 2048, 1); - CurrentFramebuffer = NULL; - f.Lagged = GB_get_lagged(&GB); -} - -static void SetMemoryArea(MemoryArea *m, GB_direct_access_t access, const char *name, int32_t flags) -{ - size_t size; - m->Name = name; - m->Data = GB_get_direct_access(&GB, access, &size, nullptr); - m->Size = size; - m->Flags = flags; -} - -ECL_EXPORT void GetMemoryAreas(MemoryArea *m) -{ - // TODO: "System Bus" - SetMemoryArea(m + 0, GB_DIRECT_ACCESS_RAM, "WRAM", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE | MEMORYAREA_FLAGS_PRIMARY); - SetMemoryArea(m + 1, GB_DIRECT_ACCESS_ROM, "ROM", MEMORYAREA_FLAGS_WORDSIZE1); - SetMemoryArea(m + 2, GB_DIRECT_ACCESS_VRAM, "VRAM", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE); - SetMemoryArea(m + 3, GB_DIRECT_ACCESS_CART_RAM, "CartRAM", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE); - SetMemoryArea(m + 4, GB_DIRECT_ACCESS_OAM, "OAM", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE); - SetMemoryArea(m + 5, GB_DIRECT_ACCESS_HRAM, "HRAM", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE); - SetMemoryArea(m + 6, GB_DIRECT_ACCESS_IO, "IO", MEMORYAREA_FLAGS_WORDSIZE1); - SetMemoryArea(m + 7, GB_DIRECT_ACCESS_BOOTROM, "BOOTROM", MEMORYAREA_FLAGS_WORDSIZE1); - SetMemoryArea(m + 8, GB_DIRECT_ACCESS_BGP, "BGP", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE); - SetMemoryArea(m + 9, GB_DIRECT_ACCESS_OBP, "OBP", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE); -} - -ECL_EXPORT void SetInputCallback(void (*callback)()) -{ - FrontendInputCallback = callback; - GB_set_input_callback(&GB, callback ? InputCallback : nullptr); -} - -ECL_EXPORT void GetGpuMemory(void **p) -{ - p[0] = GB_get_direct_access(&GB, GB_DIRECT_ACCESS_VRAM, nullptr, nullptr); - p[1] = GB_get_direct_access(&GB, GB_DIRECT_ACCESS_OAM, nullptr, nullptr); - p[2] = GB.background_palettes_rgb; - p[3] = GB.sprite_palettes_rgb; -} - -ECL_EXPORT void SetScanlineCallback(void (*callback)(uint8_t), int ly) -{ - GB.scanline_callback = callback; - GB.scanline_callback_ly = ly; -} -ECL_EXPORT uint8_t GetIoReg(uint8_t port) -{ - return GB.io_registers[port]; -} - -ECL_EXPORT void PutSaveRam() -{ - GB_load_battery(&GB, "save.ram"); -} - -ECL_EXPORT void GetSaveRam() -{ - GB_save_battery(&GB, "save.ram"); -} - -ECL_EXPORT bool HasSaveRam() -{ - if (!GB.cartridge_type->has_battery) - return false; // Nothing to save. - if (GB.mbc_ram_size == 0 && !GB.cartridge_type->has_rtc) - return false; /* Claims to have battery, but has no RAM or RTC */ - return true; -} - -ECL_EXPORT void SetPrinterCallback(FrontendPrinterCallback_t callback) -{ - FrontendPrinterCallback = callback; - - if (callback) - { - GB_connect_printer(&GB, PrinterCallback); - } - else - { - GB_set_serial_transfer_start_callback(&GB, NULL); - GB_set_serial_transfer_end_callback(&GB, NULL); - GB.printer.callback = NULL; - } -} - -int main() -{ - return 0; -} diff --git a/waterbox/sameboy/blip_buf/blip_buf.c b/waterbox/sameboy/blip_buf/blip_buf.c deleted file mode 100644 index 1bd3377e92..0000000000 --- a/waterbox/sameboy/blip_buf/blip_buf.c +++ /dev/null @@ -1,344 +0,0 @@ -/* blip_buf 1.1.0. http://www.slack.net/~ant/ */ - -#include "blip_buf.h" - -#include -#include -#include -#include - -/* Library Copyright (C) 2003-2009 Shay Green. This library 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 -library 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 */ - -#if defined (BLARGG_TEST) && BLARGG_TEST - #include "blargg_test.h" -#endif - -/* Equivalent to ULONG_MAX >= 0xFFFFFFFF00000000. -Avoids constants that don't fit in 32 bits. */ -#if ULONG_MAX/0xFFFFFFFF > 0xFFFFFFFF - typedef unsigned long fixed_t; - enum { pre_shift = 32 }; - -#elif defined(ULLONG_MAX) - typedef unsigned long long fixed_t; - enum { pre_shift = 32 }; - -#else - typedef unsigned fixed_t; - enum { pre_shift = 0 }; - -#endif - -enum { time_bits = pre_shift + 20 }; - -static fixed_t const time_unit = (fixed_t) 1 << time_bits; - -enum { bass_shift = 9 }; /* affects high-pass filter breakpoint frequency */ -enum { end_frame_extra = 2 }; /* allows deltas slightly after frame length */ - -enum { half_width = 8 }; -enum { buf_extra = half_width*2 + end_frame_extra }; -enum { phase_bits = 5 }; -enum { phase_count = 1 << phase_bits }; -enum { delta_bits = 15 }; -enum { delta_unit = 1 << delta_bits }; -enum { frac_bits = time_bits - pre_shift }; - -/* We could eliminate avail and encode whole samples in offset, but that would -limit the total buffered samples to blip_max_frame. That could only be -increased by decreasing time_bits, which would reduce resample ratio accuracy. -*/ - -/** Sample buffer that resamples to output rate and accumulates samples -until they're read out */ -struct blip_t -{ - fixed_t factor; - fixed_t offset; - int avail; - int size; - int integrator; -}; - -typedef int buf_t; - -/* probably not totally portable */ -#define SAMPLES( buf ) ((buf_t*) ((buf) + 1)) - -/* Arithmetic (sign-preserving) right shift */ -#define ARITH_SHIFT( n, shift ) \ - ((n) >> (shift)) - -enum { max_sample = +32767 }; -enum { min_sample = -32768 }; - -#define CLAMP( n ) \ - {\ - if ( (short) n != n )\ - n = ARITH_SHIFT( n, 16 ) ^ max_sample;\ - } - -static void check_assumptions( void ) -{ - int n; - - #if INT_MAX < 0x7FFFFFFF || UINT_MAX < 0xFFFFFFFF - #error "int must be at least 32 bits" - #endif - - assert( (-3 >> 1) == -2 ); /* right shift must preserve sign */ - - n = max_sample * 2; - CLAMP( n ); - assert( n == max_sample ); - - n = min_sample * 2; - CLAMP( n ); - assert( n == min_sample ); - - assert( blip_max_ratio <= time_unit ); - assert( blip_max_frame <= (fixed_t) -1 >> time_bits ); -} - -blip_t* blip_new( int size ) -{ - blip_t* m; - assert( size >= 0 ); - - m = (blip_t*) malloc( sizeof *m + (size + buf_extra) * sizeof (buf_t) ); - if ( m ) - { - m->factor = time_unit / blip_max_ratio; - m->size = size; - blip_clear( m ); - check_assumptions(); - } - return m; -} - -void blip_delete( blip_t* m ) -{ - if ( m != NULL ) - { - /* Clear fields in case user tries to use after freeing */ - memset( m, 0, sizeof *m ); - free( m ); - } -} - -void blip_set_rates( blip_t* m, double clock_rate, double sample_rate ) -{ - double factor = time_unit * sample_rate / clock_rate; - m->factor = (fixed_t) factor; - - /* Fails if clock_rate exceeds maximum, relative to sample_rate */ - assert( 0 <= factor - m->factor && factor - m->factor < 1 ); - - /* Avoid requiring math.h. Equivalent to - m->factor = (int) ceil( factor ) */ - if ( m->factor < factor ) - m->factor++; - - /* At this point, factor is most likely rounded up, but could still - have been rounded down in the floating-point calculation. */ -} - -void blip_clear( blip_t* m ) -{ - /* We could set offset to 0, factor/2, or factor-1. 0 is suitable if - factor is rounded up. factor-1 is suitable if factor is rounded down. - Since we don't know rounding direction, factor/2 accommodates either, - with the slight loss of showing an error in half the time. Since for - a 64-bit factor this is years, the halving isn't a problem. */ - - m->offset = m->factor / 2; - m->avail = 0; - m->integrator = 0; - memset( SAMPLES( m ), 0, (m->size + buf_extra) * sizeof (buf_t) ); -} - -int blip_clocks_needed( const blip_t* m, int samples ) -{ - fixed_t needed; - - /* Fails if buffer can't hold that many more samples */ - assert( samples >= 0 && m->avail + samples <= m->size ); - - needed = (fixed_t) samples * time_unit; - if ( needed < m->offset ) - return 0; - - return (needed - m->offset + m->factor - 1) / m->factor; -} - -void blip_end_frame( blip_t* m, unsigned t ) -{ - fixed_t off = t * m->factor + m->offset; - m->avail += off >> time_bits; - m->offset = off & (time_unit - 1); - - /* Fails if buffer size was exceeded */ - assert( m->avail <= m->size ); -} - -int blip_samples_avail( const blip_t* m ) -{ - return m->avail; -} - -static void remove_samples( blip_t* m, int count ) -{ - buf_t* buf = SAMPLES( m ); - int remain = m->avail + buf_extra - count; - m->avail -= count; - - memmove( &buf [0], &buf [count], remain * sizeof buf [0] ); - memset( &buf [remain], 0, count * sizeof buf [0] ); -} - -int blip_read_samples( blip_t* m, short out [], int count, int stereo ) -{ - assert( count >= 0 ); - - if ( count > m->avail ) - count = m->avail; - - if ( count ) - { - int const step = stereo ? 2 : 1; - buf_t const* in = SAMPLES( m ); - buf_t const* end = in + count; - int sum = m->integrator; - do - { - /* Eliminate fraction */ - int s = ARITH_SHIFT( sum, delta_bits ); - - sum += *in++; - - CLAMP( s ); - - *out = s; - out += step; - - /* High-pass filter */ - sum -= s << (delta_bits - bass_shift); - } - while ( in != end ); - m->integrator = sum; - - remove_samples( m, count ); - } - - return count; -} - -/* Things that didn't help performance on x86: - __attribute__((aligned(128))) - #define short int - restrict -*/ - -/* Sinc_Generator( 0.9, 0.55, 4.5 ) */ -static short const bl_step [phase_count + 1] [half_width] = -{ -{ 43, -115, 350, -488, 1136, -914, 5861,21022}, -{ 44, -118, 348, -473, 1076, -799, 5274,21001}, -{ 45, -121, 344, -454, 1011, -677, 4706,20936}, -{ 46, -122, 336, -431, 942, -549, 4156,20829}, -{ 47, -123, 327, -404, 868, -418, 3629,20679}, -{ 47, -122, 316, -375, 792, -285, 3124,20488}, -{ 47, -120, 303, -344, 714, -151, 2644,20256}, -{ 46, -117, 289, -310, 634, -17, 2188,19985}, -{ 46, -114, 273, -275, 553, 117, 1758,19675}, -{ 44, -108, 255, -237, 471, 247, 1356,19327}, -{ 43, -103, 237, -199, 390, 373, 981,18944}, -{ 42, -98, 218, -160, 310, 495, 633,18527}, -{ 40, -91, 198, -121, 231, 611, 314,18078}, -{ 38, -84, 178, -81, 153, 722, 22,17599}, -{ 36, -76, 157, -43, 80, 824, -241,17092}, -{ 34, -68, 135, -3, 8, 919, -476,16558}, -{ 32, -61, 115, 34, -60, 1006, -683,16001}, -{ 29, -52, 94, 70, -123, 1083, -862,15422}, -{ 27, -44, 73, 106, -184, 1152,-1015,14824}, -{ 25, -36, 53, 139, -239, 1211,-1142,14210}, -{ 22, -27, 34, 170, -290, 1261,-1244,13582}, -{ 20, -20, 16, 199, -335, 1301,-1322,12942}, -{ 18, -12, -3, 226, -375, 1331,-1376,12293}, -{ 15, -4, -19, 250, -410, 1351,-1408,11638}, -{ 13, 3, -35, 272, -439, 1361,-1419,10979}, -{ 11, 9, -49, 292, -464, 1362,-1410,10319}, -{ 9, 16, -63, 309, -483, 1354,-1383, 9660}, -{ 7, 22, -75, 322, -496, 1337,-1339, 9005}, -{ 6, 26, -85, 333, -504, 1312,-1280, 8355}, -{ 4, 31, -94, 341, -507, 1278,-1205, 7713}, -{ 3, 35, -102, 347, -506, 1238,-1119, 7082}, -{ 1, 40, -110, 350, -499, 1190,-1021, 6464}, -{ 0, 43, -115, 350, -488, 1136, -914, 5861} -}; - -/* Shifting by pre_shift allows calculation using unsigned int rather than -possibly-wider fixed_t. On 32-bit platforms, this is likely more efficient. -And by having pre_shift 32, a 32-bit platform can easily do the shift by -simply ignoring the low half. */ - -void blip_add_delta( blip_t* m, unsigned time, int delta ) -{ - unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); - buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); - - int const phase_shift = frac_bits - phase_bits; - int phase = fixed >> phase_shift & (phase_count - 1); - short const* in = bl_step [phase]; - short const* rev = bl_step [phase_count - phase]; - - int interp = fixed >> (phase_shift - delta_bits) & (delta_unit - 1); - int delta2 = (delta * interp) >> delta_bits; - delta -= delta2; - - /* Fails if buffer size was exceeded */ - assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); - - out [0] += in[0]*delta + in[half_width+0]*delta2; - out [1] += in[1]*delta + in[half_width+1]*delta2; - out [2] += in[2]*delta + in[half_width+2]*delta2; - out [3] += in[3]*delta + in[half_width+3]*delta2; - out [4] += in[4]*delta + in[half_width+4]*delta2; - out [5] += in[5]*delta + in[half_width+5]*delta2; - out [6] += in[6]*delta + in[half_width+6]*delta2; - out [7] += in[7]*delta + in[half_width+7]*delta2; - - in = rev; - out [ 8] += in[7]*delta + in[7-half_width]*delta2; - out [ 9] += in[6]*delta + in[6-half_width]*delta2; - out [10] += in[5]*delta + in[5-half_width]*delta2; - out [11] += in[4]*delta + in[4-half_width]*delta2; - out [12] += in[3]*delta + in[3-half_width]*delta2; - out [13] += in[2]*delta + in[2-half_width]*delta2; - out [14] += in[1]*delta + in[1-half_width]*delta2; - out [15] += in[0]*delta + in[0-half_width]*delta2; -} - -void blip_add_delta_fast( blip_t* m, unsigned time, int delta ) -{ - unsigned fixed = (unsigned) ((time * m->factor + m->offset) >> pre_shift); - buf_t* out = SAMPLES( m ) + m->avail + (fixed >> frac_bits); - - int interp = fixed >> (frac_bits - delta_bits) & (delta_unit - 1); - int delta2 = delta * interp; - - /* Fails if buffer size was exceeded */ - assert( out <= &SAMPLES( m ) [m->size + end_frame_extra] ); - - out [7] += delta * delta_unit - delta2; - out [8] += delta2; -} diff --git a/waterbox/sameboy/blip_buf/blip_buf.h b/waterbox/sameboy/blip_buf/blip_buf.h deleted file mode 100644 index e9a5d4cc3b..0000000000 --- a/waterbox/sameboy/blip_buf/blip_buf.h +++ /dev/null @@ -1,72 +0,0 @@ -/** \file -Sample buffer that resamples from input clock rate to output sample rate */ - -/* blip_buf 1.1.0 */ -#ifndef BLIP_BUF_H -#define BLIP_BUF_H - -#ifdef __cplusplus - extern "C" { -#endif - -/** First parameter of most functions is blip_t*, or const blip_t* if nothing -is changed. */ -typedef struct blip_t blip_t; - -/** Creates new buffer that can hold at most sample_count samples. Sets rates -so that there are blip_max_ratio clocks per sample. Returns pointer to new -buffer, or NULL if insufficient memory. */ -blip_t* blip_new( int sample_count ); - -/** Sets approximate input clock rate and output sample rate. For every -clock_rate input clocks, approximately sample_rate samples are generated. */ -void blip_set_rates( blip_t*, double clock_rate, double sample_rate ); - -enum { /** Maximum clock_rate/sample_rate ratio. For a given sample_rate, -clock_rate must not be greater than sample_rate*blip_max_ratio. */ -blip_max_ratio = 1 << 20 }; - -/** Clears entire buffer. Afterwards, blip_samples_avail() == 0. */ -void blip_clear( blip_t* ); - -/** Adds positive/negative delta into buffer at specified clock time. */ -void blip_add_delta( blip_t*, unsigned int clock_time, int delta ); - -/** Same as blip_add_delta(), but uses faster, lower-quality synthesis. */ -void blip_add_delta_fast( blip_t*, unsigned int clock_time, int delta ); - -/** Length of time frame, in clocks, needed to make sample_count additional -samples available. */ -int blip_clocks_needed( const blip_t*, int sample_count ); - -enum { /** Maximum number of samples that can be generated from one time frame. */ -blip_max_frame = 4000 }; - -/** Makes input clocks before clock_duration available for reading as output -samples. Also begins new time frame at clock_duration, so that clock time 0 in -the new time frame specifies the same clock as clock_duration in the old time -frame specified. Deltas can have been added slightly past clock_duration (up to -however many clocks there are in two output samples). */ -void blip_end_frame( blip_t*, unsigned int clock_duration ); - -/** Number of buffered samples available for reading. */ -int blip_samples_avail( const blip_t* ); - -/** Reads and removes at most 'count' samples and writes them to 'out'. If -'stereo' is true, writes output to every other element of 'out', allowing easy -interleaving of two buffers into a stereo sample stream. Outputs 16-bit signed -samples. Returns number of samples actually read. */ -int blip_read_samples( blip_t*, short out [], int count, int stereo ); - -/** Frees buffer. No effect if NULL is passed. */ -void blip_delete( blip_t* ); - - -/* Deprecated */ -typedef blip_t blip_buffer_t; - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/waterbox/sameboy/camera.c b/waterbox/sameboy/camera.c deleted file mode 100644 index 6ce2c0d89e..0000000000 --- a/waterbox/sameboy/camera.c +++ /dev/null @@ -1,149 +0,0 @@ -#include "gb.h" - -static int noise_seed = 0; - -/* This is not a complete emulation of the camera chip. Only the features used by the GameBoy Camera ROMs are supported. - We also do not emulate the timing of the real cart, as it might be actually faster than the webcam. */ - -static uint8_t generate_noise(uint8_t x, uint8_t y) -{ - int value = (x + y * 128 + noise_seed); - uint8_t *data = (uint8_t *) &value; - unsigned hash = 0; - - while ((int *) data != &value + 1) { - hash ^= (*data << 8); - if (hash & 0x8000) { - hash ^= 0x8a00; - hash ^= *data; - } - data++; - hash <<= 1; - } - return (hash >> 8); -} - -static long get_processed_color(GB_gameboy_t *gb, uint8_t x, uint8_t y) -{ - if (x >= 128) { - x = 0; - } - if (y >= 112) { - y = 0; - } - - long color = gb->camera_get_pixel_callback? gb->camera_get_pixel_callback(gb, x, y) : (generate_noise(x, y)); - - static const double gain_values[] = - {0.8809390, 0.9149149, 0.9457498, 0.9739758, - 1.0000000, 1.0241412, 1.0466537, 1.0677433, - 1.0875793, 1.1240310, 1.1568911, 1.1868043, - 1.2142561, 1.2396208, 1.2743837, 1.3157323, - 1.3525190, 1.3856512, 1.4157897, 1.4434309, - 1.4689574, 1.4926697, 1.5148087, 1.5355703, - 1.5551159, 1.5735801, 1.5910762, 1.6077008, - 1.6235366, 1.6386550, 1.6531183, 1.6669808}; - /* Multiply color by gain value */ - color *= gain_values[gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0x1F]; - - - /* Color is multiplied by the exposure register to simulate exposure. */ - color = color * ((gb->camera_registers[GB_CAMERA_EXPOSURE_HIGH] << 8) + gb->camera_registers[GB_CAMERA_EXPOSURE_LOW]) / 0x1000; - - return color; -} - -uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) { - /* Forbid reading the image while the camera is busy. */ - return 0xFF; - } - uint8_t tile_x = addr / 0x10 % 0x10; - uint8_t tile_y = addr / 0x10 / 0x10; - - uint8_t y = ((addr >> 1) & 0x7) + tile_y * 8; - uint8_t bit = addr & 1; - - uint8_t ret = 0; - - for (uint8_t x = tile_x * 8; x < tile_x * 8 + 8; x++) { - - long color = get_processed_color(gb, x, y); - - static const double edge_enhancement_ratios[] = {0.5, 0.75, 1, 1.25, 2, 3, 4, 5}; - double edge_enhancement_ratio = edge_enhancement_ratios[(gb->camera_registers[GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE] >> 4) & 0x7]; - if ((gb->camera_registers[GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS] & 0xE0) == 0xE0) { - color += (color * 4) * edge_enhancement_ratio; - color -= get_processed_color(gb, x - 1, y) * edge_enhancement_ratio; - color -= get_processed_color(gb, x + 1, y) * edge_enhancement_ratio; - color -= get_processed_color(gb, x, y - 1) * edge_enhancement_ratio; - color -= get_processed_color(gb, x, y + 1) * edge_enhancement_ratio; - } - - - /* The camera's registers are used as a threshold pattern, which defines the dithering */ - uint8_t pattern_base = ((x & 3) + (y & 3) * 4) * 3 + GB_CAMERA_DITHERING_PATTERN_START; - - if (color < gb->camera_registers[pattern_base]) { - color = 3; - } - else if (color < gb->camera_registers[pattern_base + 1]) { - color = 2; - } - else if (color < gb->camera_registers[pattern_base + 2]) { - color = 1; - } - else { - color = 0; - } - - ret <<= 1; - ret |= (color >> bit) & 1; - } - - return ret; -} - -void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback) -{ - gb->camera_get_pixel_callback = callback; -} - -void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback) -{ - gb->camera_update_request_callback = callback; -} - -void GB_camera_updated(GB_gameboy_t *gb) -{ - gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] &= ~1; -} - -void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - addr &= 0x7F; - if (addr == GB_CAMERA_SHOOT_AND_1D_FLAGS) { - value &= 0x7; - noise_seed = 42; // rand(); - if ((value & 1) && !(gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] & 1) && gb->camera_update_request_callback) { - /* If no callback is set, ignore the write as if the camera is instantly done */ - gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS] |= 1; - gb->camera_update_request_callback(gb); - } - } - else { - if (addr >= 0x36) { - GB_log(gb, "Wrote invalid camera register %02x: %2x\n", addr, value); - return; - } - gb->camera_registers[addr] = value; - } -} -uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr) -{ - if ((addr & 0x7F) == 0) { - return gb->camera_registers[GB_CAMERA_SHOOT_AND_1D_FLAGS]; - } - return 0; -} diff --git a/waterbox/sameboy/camera.h b/waterbox/sameboy/camera.h deleted file mode 100644 index 21c69b68e6..0000000000 --- a/waterbox/sameboy/camera.h +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef camera_h -#define camera_h -#include -#include "gb_struct_def.h" - -typedef uint8_t (*GB_camera_get_pixel_callback_t)(GB_gameboy_t *gb, uint8_t x, uint8_t y); -typedef void (*GB_camera_update_request_callback_t)(GB_gameboy_t *gb); - -enum { - GB_CAMERA_SHOOT_AND_1D_FLAGS = 0, - GB_CAMERA_GAIN_AND_EDGE_ENHACEMENT_FLAGS = 1, - GB_CAMERA_EXPOSURE_HIGH = 2, - GB_CAMERA_EXPOSURE_LOW = 3, - GB_CAMERA_EDGE_ENHANCEMENT_INVERT_AND_VOLTAGE = 4, - GB_CAMERA_DITHERING_PATTERN_START = 6, - GB_CAMERA_DITHERING_PATTERN_END = 0x35, -}; - -uint8_t GB_camera_read_image(GB_gameboy_t *gb, uint16_t addr); - -void GB_set_camera_get_pixel_callback(GB_gameboy_t *gb, GB_camera_get_pixel_callback_t callback); -void GB_set_camera_update_request_callback(GB_gameboy_t *gb, GB_camera_update_request_callback_t callback); - -void GB_camera_updated(GB_gameboy_t *gb); - -void GB_camera_write_register(GB_gameboy_t *gb, uint16_t addr, uint8_t value); -uint8_t GB_camera_read_register(GB_gameboy_t *gb, uint16_t addr); - -#endif diff --git a/waterbox/sameboy/display.c b/waterbox/sameboy/display.c deleted file mode 100644 index 28dd5f07ec..0000000000 --- a/waterbox/sameboy/display.c +++ /dev/null @@ -1,796 +0,0 @@ -#include -#include -#include -#include -#include "gb.h" - -/* - Each line is 456 cycles, approximately: - Mode 2 - 80 cycles / OAM Transfer - Mode 3 - 172 cycles / Rendering - Mode 0 - 204 cycles / HBlank - - Mode 1 is VBlank - - Todo: Mode lengths are not constants, see http://blog.kevtris.org/blogfiles/Nitty%20Gritty%20Gameboy%20VRAM%20Timing.txt - */ - -#define MODE2_LENGTH (80) -#define MODE3_LENGTH (172) -#define MODE0_LENGTH (204) -#define LINE_LENGTH (MODE2_LENGTH + MODE3_LENGTH + MODE0_LENGTH) // = 456 -#define LINES (144) -#define WIDTH (160) -#define VIRTUAL_LINES (LCDC_PERIOD / LINE_LENGTH) // = 154 - -typedef struct __attribute__((packed)) { - uint8_t y; - uint8_t x; - uint8_t tile; - uint8_t flags; -} GB_sprite_t; - -static bool window_enabled(GB_gameboy_t *gb) -{ - if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) { - if (!gb->cgb_mode && gb->is_cgb) { - return false; - } - } - return (gb->io_registers[GB_IO_LCDC] & 0x20) && gb->io_registers[GB_IO_WX] < 167; -} - -static uint32_t get_pixel(GB_gameboy_t *gb, uint8_t x, uint8_t y) -{ - /* - Bit 7 - LCD Display Enable (0=Off, 1=On) - Bit 6 - Window Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF) - Bit 5 - Window Display Enable (0=Off, 1=On) - Bit 4 - BG & Window Tile Data Select (0=8800-97FF, 1=8000-8FFF) - Bit 3 - BG Tile Map Display Select (0=9800-9BFF, 1=9C00-9FFF) - Bit 2 - OBJ (Sprite) Size (0=8x8, 1=8x16) - Bit 1 - OBJ (Sprite) Display Enable (0=Off, 1=On) - Bit 0 - BG Display (for CGB see below) (0=Off, 1=On) - */ - uint16_t map = 0x1800; - uint8_t tile = 0; - uint8_t attributes = 0; - uint8_t sprite_palette = 0; - uint16_t tile_address = 0; - uint8_t background_pixel = 0, sprite_pixel = 0; - GB_sprite_t *sprite = (GB_sprite_t *) &gb->oam; - uint8_t sprites_in_line = 0; - bool lcd_8_16_mode = (gb->io_registers[GB_IO_LCDC] & 4) != 0; - bool sprites_enabled = (gb->io_registers[GB_IO_LCDC] & 2) != 0; - uint8_t lowest_sprite_x = 0xFF; - bool use_obp1 = false, priority = false; - bool in_window = false; - bool bg_enabled = true; - bool bg_behind = false; - if ((gb->io_registers[GB_IO_LCDC] & 0x1) == 0) { - if (gb->cgb_mode) { - bg_behind = true; - } - else { - bg_enabled = false; - } - } - if (window_enabled(gb) && y >= gb->io_registers[GB_IO_WY] && x + 7 >= gb->io_registers[GB_IO_WX] && gb->current_window_line != 0xFF) { - in_window = true; - } - - if (sprites_enabled) { - // Loop all sprites - for (uint8_t i = 40; i--; sprite++) { - int sprite_y = sprite->y - 16; - int sprite_x = sprite->x - 8; - // Is sprite in our line? - if (sprite_y <= y && sprite_y + (lcd_8_16_mode? 16:8) > y) { - uint8_t tile_x, tile_y, current_sprite_pixel; - uint16_t line_address; - // Limit to 10 sprites in one scan line. - if (++sprites_in_line == 11) break; - // Does not overlap our pixel. - if (sprite_x > x || sprite_x + 8 <= x) continue; - tile_x = x - sprite_x; - tile_y = y - sprite_y; - if (sprite->flags & 0x20) tile_x = 7 - tile_x; - if (sprite->flags & 0x40) tile_y = (lcd_8_16_mode? 15:7) - tile_y; - line_address = (lcd_8_16_mode? sprite->tile & 0xFE : sprite->tile) * 0x10 + tile_y * 2; - if (gb->cgb_mode && (sprite->flags & 0x8)) { - line_address += 0x2000; - } - current_sprite_pixel = (((gb->vram[line_address ] >> ((~tile_x)&7)) & 1 ) | - ((gb->vram[line_address + 1] >> ((~tile_x)&7)) & 1) << 1 ); - /* From Pandocs: - When sprites with different x coordinate values overlap, the one with the smaller x coordinate - (closer to the left) will have priority and appear above any others. This applies in Non CGB Mode - only. When sprites with the same x coordinate values overlap, they have priority according to table - ordering. (i.e. $FE00 - highest, $FE04 - next highest, etc.) In CGB Mode priorities are always - assigned like this. - */ - if (current_sprite_pixel != 0) { - if (!gb->cgb_mode && sprite->x >= lowest_sprite_x) { - break; - } - sprite_pixel = current_sprite_pixel; - lowest_sprite_x = sprite->x; - use_obp1 = (sprite->flags & 0x10) != 0; - sprite_palette = sprite->flags & 7; - priority = (sprite->flags & 0x80) != 0; - if (gb->cgb_mode) { - break; - } - } - } - } - } - - if (in_window) { - x -= gb->io_registers[GB_IO_WX] - 7; // Todo: This value is probably latched - y = gb->current_window_line; - } - else { - x += gb->effective_scx; - y += gb->io_registers[GB_IO_SCY]; - } - if (gb->io_registers[GB_IO_LCDC] & 0x08 && !in_window) { - map = 0x1C00; - } - else if (gb->io_registers[GB_IO_LCDC] & 0x40 && in_window) { - map = 0x1C00; - } - tile = gb->vram[map + x/8 + y/8 * 32]; - if (gb->cgb_mode) { - attributes = gb->vram[map + x/8 + y/8 * 32 + 0x2000]; - } - - if (attributes & 0x80) { - priority = !bg_behind && bg_enabled; - } - - if (!priority && sprite_pixel) { - if (!gb->cgb_mode) { - sprite_pixel = (gb->io_registers[use_obp1? GB_IO_OBP1:GB_IO_OBP0] >> (sprite_pixel << 1)) & 3; - sprite_palette = use_obp1; - } - return gb->sprite_palettes_rgb[sprite_palette * 4 + sprite_pixel]; - } - - if (bg_enabled) { - if (gb->io_registers[GB_IO_LCDC] & 0x10) { - tile_address = tile * 0x10; - } - else { - tile_address = (int8_t) tile * 0x10 + 0x1000; - } - if (attributes & 0x8) { - tile_address += 0x2000; - } - - if (attributes & 0x20) { - x = ~x; - } - - if (attributes & 0x40) { - y = ~y; - } - - background_pixel = (((gb->vram[tile_address + (y & 7) * 2 ] >> ((~x)&7)) & 1 ) | - ((gb->vram[tile_address + (y & 7) * 2 + 1] >> ((~x)&7)) & 1) << 1 ); - } - - if (priority && sprite_pixel && !background_pixel) { - if (!gb->cgb_mode) { - sprite_pixel = (gb->io_registers[use_obp1? GB_IO_OBP1:GB_IO_OBP0] >> (sprite_pixel << 1)) & 3; - sprite_palette = use_obp1; - } - return gb->sprite_palettes_rgb[sprite_palette * 4 + sprite_pixel]; - } - - if (!gb->cgb_mode) { - background_pixel = ((gb->io_registers[GB_IO_BGP] >> (background_pixel << 1)) & 3); - } - - return gb->background_palettes_rgb[(attributes & 7) * 4 + background_pixel]; -} - -static void display_vblank(GB_gameboy_t *gb) -{ - if (!gb->disable_rendering && ((!(gb->io_registers[GB_IO_LCDC] & 0x80) || gb->stopped) || gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON)) { - /* LCD is off, set screen to white */ - uint32_t white = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - for (unsigned i = 0; i < WIDTH * LINES; i++) { - gb ->screen[i] = white; - } - } - - gb->vblank_callback(gb); - - gb->vblank_just_occured = true; -} - -static inline uint8_t scale_channel(uint8_t x) -{ - x &= 0x1f; - return (x << 3) | (x >> 2); -} - -void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index) -{ - uint8_t *palette_data = background_palette? gb->background_palettes_data : gb->sprite_palettes_data; - uint16_t color = palette_data[index & ~1] | (palette_data[index | 1] << 8); - - // No need to &, scale channel does that. - uint8_t r = scale_channel(color); - uint8_t g = scale_channel(color >> 5); - uint8_t b = scale_channel(color >> 10); - assert (gb->rgb_encode_callback); - (background_palette? gb->background_palettes_rgb : gb->sprite_palettes_rgb)[index / 2] = gb->rgb_encode_callback(gb, r, g, b); -} - -/* - STAT interrupt is implemented based on this finding: - http://board.byuu.org/phpbb3/viewtopic.php?p=25527#p25531 - - General timing is based on GiiBiiAdvance's documents: - https://github.com/AntonioND/giibiiadvance - - */ - -static void update_display_state(GB_gameboy_t *gb, uint8_t cycles) -{ - uint8_t previous_stat_interrupt_line = gb->stat_interrupt_line; - gb->stat_interrupt_line = false; - - if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { - /* LCD is disabled, state is constant */ - - /* When the LCD is off, LY is 0 and STAT mode is 0. - Todo: Verify the LY=LYC flag should be on. */ - gb->io_registers[GB_IO_LY] = 0; - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 4; - if (gb->hdma_on_hblank) { - gb->hdma_on_hblank = false; - gb->hdma_on = false; - - /* Todo: is this correct? */ - gb->hdma_steps_left = 0xff; - } - - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - - /* Keep sending vblanks to user even if the screen is off */ - gb->display_cycles += cycles; - if (gb->display_cycles >= LCDC_PERIOD) { - /* VBlank! */ - gb->display_cycles -= LCDC_PERIOD; - display_vblank(gb); - } - - /* Reset window rendering state */ - gb->current_window_line = 0xFF; - return; - } - - uint8_t atomic_increase = gb->cgb_double_speed? 2 : 4; - uint8_t stat_delay = gb->cgb_double_speed? 2 : (gb->cgb_mode? 0 : 4); - /* Todo: This is correct for DMG. Is it correct for the 3 CGB modes (DMG/single/double)?*/ - uint8_t scx_delay = ((gb->effective_scx & 7) + atomic_increase - 1) & ~(atomic_increase - 1); - /* Todo: These are correct for DMG, DMG-mode CGB, and single speed CGB. Is is correct for double speed CGB? */ - uint8_t oam_blocking_rush = gb->cgb_double_speed? 2 : 4; - uint8_t vram_blocking_rush = gb->is_cgb? 0 : 4; - - for (; cycles; cycles -= atomic_increase) { - - gb->display_cycles += atomic_increase; - /* The very first line is 2 (4 from the CPU's perseptive) clocks shorter when the LCD turns on. - Todo: Verify on the 3 CGB modes, especially double speed mode. */ - if (gb->first_scanline && gb->display_cycles >= LINE_LENGTH - atomic_increase) { - gb->first_scanline = false; - gb->display_cycles += atomic_increase; - } - bool should_compare_ly = true; - uint8_t ly_for_comparison = gb->io_registers[GB_IO_LY] = gb->display_cycles / LINE_LENGTH; - - if (gb->scanline_callback && gb->display_cycles % LINE_LENGTH == 0 && gb->scanline_callback_ly == ly_for_comparison) { - gb->scanline_callback(gb->io_registers[GB_IO_LCDC]); - } - - /* Handle cycle completion. STAT's initial value depends on model and mode */ - if (gb->display_cycles == LCDC_PERIOD) { - /* VBlank! */ - gb->display_cycles = 0; - gb->io_registers[GB_IO_STAT] &= ~3; - if (gb->is_cgb) { - if (stat_delay) { - gb->io_registers[GB_IO_STAT] |= 1; - } - else { - gb->io_registers[GB_IO_STAT] |= 2; - } - } - ly_for_comparison = gb->io_registers[GB_IO_LY] = 0; - - /* Todo: verify timing */ - gb->oam_read_blocked = true; - gb->vram_read_blocked = false; - gb->oam_write_blocked = true; - gb->vram_write_blocked = false; - - - /* Reset window rendering state */ - gb->current_window_line = 0xFF; - } - - /* Entered VBlank state, update STAT and IF */ - else if (gb->display_cycles == LINES * LINE_LENGTH + stat_delay) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 1; - gb->io_registers[GB_IO_IF] |= 1; - - /* Entering VBlank state triggers the OAM interrupt. In CGB, it happens 4 cycles earlier */ - if (gb->io_registers[GB_IO_STAT] & 0x20 && !gb->is_cgb) { - gb->stat_interrupt_line = true; - } - if (gb->frame_skip_state == GB_FRAMESKIP_LCD_TURNED_ON) { - if (!gb->is_cgb) { - display_vblank(gb); - gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED; - } - else { - gb->frame_skip_state = GB_FRAMESKIP_FIRST_FRAME_SKIPPED; - } - } - else { - gb->frame_skip_state = GB_FRAMESKIP_SECOND_FRAME_RENDERED; - display_vblank(gb); - } - } - - - /* Handle line 0 right after turning the LCD on */ - else if (gb->first_scanline) { - /* OAM and VRAM blocking is not rushed in the very first scanline */ - if (gb->display_cycles == atomic_increase) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - } - else if (gb->display_cycles == MODE2_LENGTH) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 3; - gb->oam_read_blocked = true; - gb->vram_read_blocked = true; - gb->oam_write_blocked = true; - gb->vram_write_blocked = true; - } - else if (gb->display_cycles == MODE2_LENGTH + MODE3_LENGTH) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - } - } - - /* Handle STAT changes for lines 0-143 */ - else if (gb->display_cycles < LINES * LINE_LENGTH) { - unsigned position_in_line = gb->display_cycles % LINE_LENGTH; - - /* Handle OAM and VRAM blocking */ - /* Todo: verify CGB timing for write blocking */ - if (position_in_line == stat_delay - oam_blocking_rush || - // In case stat_delay is 0 - (position_in_line == LINE_LENGTH + stat_delay - oam_blocking_rush && gb->io_registers[GB_IO_LY] != 143)) { - gb->oam_read_blocked = true; - gb->oam_write_blocked = gb->is_cgb; - } - else if (position_in_line == MODE2_LENGTH + stat_delay - vram_blocking_rush) { - gb->vram_read_blocked = true; - gb->vram_write_blocked = gb->is_cgb; - } - - if (position_in_line == stat_delay) { - gb->oam_write_blocked = true; - } - else if (!gb->is_cgb && position_in_line == MODE2_LENGTH + stat_delay - oam_blocking_rush) { - gb->oam_write_blocked = false; - } - else if (position_in_line == MODE2_LENGTH + stat_delay) { - gb->vram_write_blocked = true; - gb->oam_write_blocked = true; - } - - /* Handle everything else */ - if (position_in_line == stat_delay) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 2; - if (window_enabled(gb) && gb->display_cycles / LINE_LENGTH >= gb->io_registers[GB_IO_WY]) { - gb->current_window_line++; - } - } - else if (position_in_line == 0 && gb->display_cycles != 0) { - should_compare_ly = gb->is_cgb; - ly_for_comparison--; - } - else if (position_in_line == MODE2_LENGTH + stat_delay) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->io_registers[GB_IO_STAT] |= 3; - gb->effective_scx = gb->io_registers[GB_IO_SCX]; - gb->previous_lcdc_x = - (gb->effective_scx & 0x7); - - /* Todo: This works on both 007 - The World Is Not Enough and Donkey Kong 94, but should be verified better */ - if (window_enabled(gb) && gb->display_cycles / LINE_LENGTH == gb->io_registers[GB_IO_WY] && gb->current_window_line == 0xFF) { - gb->current_window_line = 0; - } - } - else if (position_in_line == MODE2_LENGTH + MODE3_LENGTH + stat_delay + scx_delay) { - gb->io_registers[GB_IO_STAT] &= ~3; - gb->oam_read_blocked = false; - gb->vram_read_blocked = false; - gb->oam_write_blocked = false; - gb->vram_write_blocked = false; - if (gb->hdma_on_hblank) { - gb->hdma_on = true; - gb->hdma_cycles = 0; - } - } - } - - /* Line 153 is special */ - else if (gb->display_cycles >= (VIRTUAL_LINES - 1) * LINE_LENGTH) { - /* DMG */ - if (!gb->is_cgb) { - switch (gb->display_cycles - (VIRTUAL_LINES - 1) * LINE_LENGTH) { - case 0: - should_compare_ly = false; - break; - case 4: - gb->io_registers[GB_IO_LY] = 0; - ly_for_comparison = VIRTUAL_LINES - 1; - break; - case 8: - gb->io_registers[GB_IO_LY] = 0; - should_compare_ly = false; - break; - default: - gb->io_registers[GB_IO_LY] = 0; - ly_for_comparison = 0; - } - } - /* CGB in DMG mode */ - else if (!gb->cgb_mode) { - switch (gb->display_cycles - (VIRTUAL_LINES - 1) * LINE_LENGTH) { - case 0: - ly_for_comparison = VIRTUAL_LINES - 2; - break; - case 4: - break; - case 8: - gb->io_registers[GB_IO_LY] = 0; - break; - default: - gb->io_registers[GB_IO_LY] = 0; - ly_for_comparison = 0; - } - } - /* Single speed CGB */ - else if (!gb->cgb_double_speed) { - switch (gb->display_cycles - (VIRTUAL_LINES - 1) * LINE_LENGTH) { - case 0: - break; - case 4: - gb->io_registers[GB_IO_LY] = 0; - break; - default: - gb->io_registers[GB_IO_LY] = 0; - ly_for_comparison = 0; - } - } - - /* Double speed CGB */ - else { - switch (gb->display_cycles - (VIRTUAL_LINES - 1) * LINE_LENGTH) { - case 0: - ly_for_comparison = VIRTUAL_LINES - 2; - break; - case 2: - case 4: - break; - case 6: - case 8: - gb->io_registers[GB_IO_LY] = 0; - break; - default: - gb->io_registers[GB_IO_LY] = 0; - ly_for_comparison = 0; - } - } - } - - /* Lines 144 - 152 */ - else { - if (stat_delay && gb->display_cycles % LINE_LENGTH == 0) { - should_compare_ly = gb->is_cgb; - ly_for_comparison--; - } - } - - /* Set LY=LYC bit */ - if (should_compare_ly && (ly_for_comparison == gb->io_registers[GB_IO_LYC])) { - gb->io_registers[GB_IO_STAT] |= 4; - } - else { - gb->io_registers[GB_IO_STAT] &= ~4; - } - - if (!gb->stat_interrupt_line) { - switch (gb->io_registers[GB_IO_STAT] & 3) { - case 0: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 8; break; - case 1: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x10; break; - case 2: gb->stat_interrupt_line = gb->io_registers[GB_IO_STAT] & 0x20; break; - } - - /* Use requested a LY=LYC interrupt and the LY=LYC bit is on */ - if ((gb->io_registers[GB_IO_STAT] & 0x44) == 0x44) { - gb->stat_interrupt_line = true; - } - } - } - - /* On the CGB, the last cycle of line 144 triggers an OAM interrupt - Todo: Verify timing for CGB in CGB mode and double speed CGB */ - if (gb->is_cgb && - gb->display_cycles == LINES * LINE_LENGTH + stat_delay - atomic_increase && - (gb->io_registers[GB_IO_STAT] & 0x20)) { - gb->stat_interrupt_line = true; - } - - if (gb->stat_interrupt_line && !previous_stat_interrupt_line) { - gb->io_registers[GB_IO_IF] |= 2; - } - - /* The value of LY is glitched in the last cycle of every line in CGB mode CGB in single speed - This is based on GiiBiiAdvance's docs */ - if (gb->cgb_mode && !gb->cgb_double_speed && - gb->display_cycles % LINE_LENGTH == LINE_LENGTH - 4) { - uint8_t glitch_pattern[] = {0, 0, 2, 0, 4, 4, 6, 0, 8}; - if ((gb->io_registers[GB_IO_LY] & 0xF) == 0xF) { - gb->io_registers[GB_IO_LY] = glitch_pattern[gb->io_registers[GB_IO_LY] >> 4] << 4; - } - else { - gb->io_registers[GB_IO_LY] = glitch_pattern[gb->io_registers[GB_IO_LY] & 7] | (gb->io_registers[GB_IO_LY] & 0xF8); - } - } -} - -void GB_display_run(GB_gameboy_t *gb, uint8_t cycles) -{ - update_display_state(gb, cycles); - if (gb->disable_rendering) { - return; - } - - /* - Display controller bug: For some reason, the OAM STAT interrupt is called, as expected, for LY = 0..143. - However, it is also called from LY = 144. - - See http://forums.nesdev.com/viewtopic.php?f=20&t=13727 - */ - - if (!(gb->io_registers[GB_IO_LCDC] & 0x80)) { - /* LCD is disabled, do nothing */ - return; - } - if (gb->display_cycles >= LINE_LENGTH * 144) { /* VBlank */ - return; - } - - uint8_t effective_ly = gb->display_cycles / LINE_LENGTH; - - - if (gb->display_cycles % LINE_LENGTH < MODE2_LENGTH) { /* Mode 2 */ - return; - } - - - /* Render */ - /* Todo: it appears that the actual rendering starts 4 cycles after mode 3 starts. Is this correct? */ - int16_t current_lcdc_x = gb->display_cycles % LINE_LENGTH - MODE2_LENGTH - (gb->effective_scx & 0x7) - 4; - - for (;gb->previous_lcdc_x < current_lcdc_x; gb->previous_lcdc_x++) { - if (gb->previous_lcdc_x >= WIDTH) { - continue; - } - if (gb->previous_lcdc_x < 0) { - continue; - } - gb->screen[effective_ly * WIDTH + gb->previous_lcdc_x] = - get_pixel(gb, gb->previous_lcdc_x, effective_ly); - } -} - -void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index) -{ - uint32_t none_palette[4]; - uint32_t *palette = NULL; - - switch (gb->is_cgb? palette_type : GB_PALETTE_NONE) { - default: - case GB_PALETTE_NONE: - none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA); - none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55); - none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 ); - palette = none_palette; - break; - case GB_PALETTE_BACKGROUND: - palette = gb->background_palettes_rgb + (4 * (palette_index & 7)); - break; - case GB_PALETTE_OAM: - palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7)); - break; - } - - for (unsigned y = 0; y < 192; y++) { - for (unsigned x = 0; x < 256; x++) { - if (x >= 128 && !gb->is_cgb) { - *(dest++) = gb->background_palettes_rgb[0]; - continue; - } - uint16_t tile = (x % 128) / 8 + y / 8 * 16; - uint16_t tile_address = tile * 0x10 + (x >= 128? 0x2000 : 0); - uint8_t pixel = (((gb->vram[tile_address + (y & 7) * 2 ] >> ((~x)&7)) & 1 ) | - ((gb->vram[tile_address + (y & 7) * 2 + 1] >> ((~x)&7)) & 1) << 1); - - if (!gb->cgb_mode) { - if (palette_type == GB_PALETTE_BACKGROUND) { - pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3); - } - else if (!gb->cgb_mode) { - if (palette_type == GB_PALETTE_OAM) { - pixel = ((gb->io_registers[palette_index == 0? GB_IO_OBP0 : GB_IO_OBP1] >> (pixel << 1)) & 3); - } - } - } - - - *(dest++) = palette[pixel]; - } - } -} - -void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type) -{ - uint32_t none_palette[4]; - uint32_t *palette = NULL; - uint16_t map = 0x1800; - - switch (gb->is_cgb? palette_type : GB_PALETTE_NONE) { - case GB_PALETTE_NONE: - none_palette[0] = gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - none_palette[1] = gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA); - none_palette[2] = gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55); - none_palette[3] = gb->rgb_encode_callback(gb, 0, 0, 0 ); - palette = none_palette; - break; - case GB_PALETTE_BACKGROUND: - palette = gb->background_palettes_rgb + (4 * (palette_index & 7)); - break; - case GB_PALETTE_OAM: - palette = gb->sprite_palettes_rgb + (4 * (palette_index & 7)); - break; - case GB_PALETTE_AUTO: - break; - } - - if (map_type == GB_MAP_9C00 || (map_type == GB_MAP_AUTO && gb->io_registers[GB_IO_LCDC] & 0x08)) { - map = 0x1c00; - } - - if (tileset_type == GB_TILESET_AUTO) { - tileset_type = (gb->io_registers[GB_IO_LCDC] & 0x10)? GB_TILESET_8800 : GB_TILESET_8000; - } - - for (unsigned y = 0; y < 256; y++) { - for (unsigned x = 0; x < 256; x++) { - uint8_t tile = gb->vram[map + x/8 + y/8 * 32]; - uint16_t tile_address; - uint8_t attributes = 0; - - if (tileset_type == GB_TILESET_8800) { - tile_address = tile * 0x10; - } - else { - tile_address = (int8_t) tile * 0x10 + 0x1000; - } - - if (gb->cgb_mode) { - attributes = gb->vram[map + x/8 + y/8 * 32 + 0x2000]; - } - - if (attributes & 0x8) { - tile_address += 0x2000; - } - - uint8_t pixel = (((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 ] >> (((attributes & 0x20)? x : ~x)&7)) & 1 ) | - ((gb->vram[tile_address + (((attributes & 0x40)? ~y : y) & 7) * 2 + 1] >> (((attributes & 0x20)? x : ~x)&7)) & 1) << 1); - - if (!gb->cgb_mode && (palette_type == GB_PALETTE_BACKGROUND || palette_type == GB_PALETTE_AUTO)) { - pixel = ((gb->io_registers[GB_IO_BGP] >> (pixel << 1)) & 3); - } - - if (palette) { - *(dest++) = palette[pixel]; - } - else { - *(dest++) = gb->background_palettes_rgb[(attributes & 7) * 4 + pixel]; - } - } - } -} - -uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height) -{ - uint8_t count = 0; - *sprite_height = (gb->io_registers[GB_IO_LCDC] & 4) ? 16:8; - uint8_t oam_to_dest_index[40] = {0,}; - for (unsigned y = 0; y < LINES; y++) { - GB_sprite_t *sprite = (GB_sprite_t *) &gb->oam; - uint8_t sprites_in_line = 0; - for (uint8_t i = 0; i < 40; i++, sprite++) { - int sprite_y = sprite->y - 16; - bool obscured = false; - // Is sprite not in this line? - if (sprite_y > y || sprite_y + *sprite_height <= y) continue; - if (++sprites_in_line == 11) obscured = true; - - GB_oam_info_t *info = NULL; - if (!oam_to_dest_index[i]) { - info = dest + count; - oam_to_dest_index[i] = ++count; - info->x = sprite->x; - info->y = sprite->y; - info->tile = *sprite_height == 16? sprite->tile & 0xFE : sprite->tile; - info->flags = sprite->flags; - info->obscured_by_line_limit = false; - info->oam_addr = 0xFE00 + i * sizeof(*sprite); - } - else { - info = dest + oam_to_dest_index[i] - 1; - } - info->obscured_by_line_limit |= obscured; - } - } - - - for (unsigned i = 0; i < count; i++) { - uint16_t vram_address = dest[i].tile * 0x10; - uint8_t flags = dest[i].flags; - uint8_t palette = gb->cgb_mode? (flags & 7) : ((flags & 0x10)? 1 : 0); - if (gb->is_cgb && (flags & 0x8)) { - vram_address += 0x2000; - } - - for (unsigned y = 0; y < *sprite_height; y++) { - for (unsigned x = 0; x < 8; x++) { - uint8_t color = (((gb->vram[vram_address ] >> ((~x)&7)) & 1 ) | - ((gb->vram[vram_address + 1] >> ((~x)&7)) & 1) << 1 ); - - if (!gb->cgb_mode) { - color = (gb->io_registers[palette? GB_IO_OBP1:GB_IO_OBP0] >> (color << 1)) & 3; - } - dest[i].image[((flags & 0x20)?7-x:x) + ((flags & 0x40)?*sprite_height - 1 -y:y) * 8] = gb->sprite_palettes_rgb[palette * 4 + color]; - } - vram_address += 2; - } - } - return count; -} diff --git a/waterbox/sameboy/display.h b/waterbox/sameboy/display.h deleted file mode 100644 index 3967787237..0000000000 --- a/waterbox/sameboy/display.h +++ /dev/null @@ -1,40 +0,0 @@ -#ifndef display_h -#define display_h - -#include "gb.h" -#ifdef GB_INTERNAL -void GB_display_run(GB_gameboy_t *gb, uint8_t cycles); -void GB_palette_changed(GB_gameboy_t *gb, bool background_palette, uint8_t index); -#endif - -typedef enum { - GB_PALETTE_NONE, - GB_PALETTE_BACKGROUND, - GB_PALETTE_OAM, - GB_PALETTE_AUTO, -} GB_palette_type_t; - -typedef enum { - GB_MAP_AUTO, - GB_MAP_9800, - GB_MAP_9C00, -} GB_map_type_t; - -typedef enum { - GB_TILESET_AUTO, - GB_TILESET_8800, - GB_TILESET_8000, -} GB_tileset_type_t; - -typedef struct { - uint32_t image[128]; - uint8_t x, y, tile, flags; - uint16_t oam_addr; - bool obscured_by_line_limit; -} GB_oam_info_t; - -void GB_draw_tileset(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index); -void GB_draw_tilemap(GB_gameboy_t *gb, uint32_t *dest, GB_palette_type_t palette_type, uint8_t palette_index, GB_map_type_t map_type, GB_tileset_type_t tileset_type); -uint8_t GB_get_oam_info(GB_gameboy_t *gb, GB_oam_info_t *dest, uint8_t *sprite_height); - -#endif /* display_h */ diff --git a/waterbox/sameboy/gb.c b/waterbox/sameboy/gb.c deleted file mode 100644 index ce14dc647c..0000000000 --- a/waterbox/sameboy/gb.c +++ /dev/null @@ -1,473 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include "gb.h" -#include "../emulibc/emulibc.h" - -void GB_attributed_logv(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, va_list args) -{ - char *string = NULL; - vasprintf(&string, fmt, args); - if (string) { - if (gb->log_callback) { - gb->log_callback(gb, string, attributes); - } - else { - /* Todo: Add ANSI escape sequences for attributed text */ - printf("%s", string); - } - } - free(string); -} - -void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - GB_attributed_logv(gb, attributes, fmt, args); - va_end(args); -} - -void GB_log(GB_gameboy_t *gb, const char *fmt, ...) -{ - va_list args; - va_start(args, fmt); - GB_attributed_logv(gb, 0, fmt, args); - va_end(args); -} - -void GB_init(GB_gameboy_t *gb) -{ - memset(gb, 0, sizeof(*gb)); - gb->ram = malloc(gb->ram_size = 0x2000); - gb->vram = malloc(gb->vram_size = 0x2000); - gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type - - GB_reset(gb); -} - -void GB_init_sgb(GB_gameboy_t *gb) -{ - memset(gb, 0, sizeof(*gb)); - gb->ram = malloc(gb->ram_size = 0x2000); - gb->vram = malloc(gb->vram_size = 0x2000); - gb->is_sgb = true; - gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type - - GB_reset(gb); -} - -void GB_init_cgb(GB_gameboy_t *gb) -{ - memset(gb, 0, sizeof(*gb)); - gb->ram = malloc(gb->ram_size = 0x2000 * 8); - gb->vram = malloc(gb->vram_size = 0x2000 * 2); - gb->is_cgb = true; - gb->cartridge_type = &GB_cart_defs[0]; // Default cartridge type - - GB_reset(gb); -} - -int GB_load_boot_rom(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - GB_log(gb, "Could not open boot ROM: %s.\n", strerror(errno)); - return errno; - } - fread(gb->boot_rom, sizeof(gb->boot_rom), 1, f); - fclose(f); - return 0; -} - -int GB_load_rom(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - GB_log(gb, "Could not open ROM: %s.\n", strerror(errno)); - return errno; - } - fseek(f, 0, SEEK_END); - gb->rom_size = (ftell(f) + 0x3FFF) & ~0x3FFF; /* Round to bank */ - /* And then round to a power of two */ - while (gb->rom_size & (gb->rom_size - 1)) { - /* I promise this works. */ - gb->rom_size |= gb->rom_size >> 1; - gb->rom_size++; - } - fseek(f, 0, SEEK_SET); - if (gb->rom) { - free(gb->rom); - } - gb->rom = alloc_sealed(gb->rom_size); - memset(gb->rom, 0xFF, gb->rom_size); /* Pad with 0xFFs */ - fread(gb->rom, gb->rom_size, 1, f); - fclose(f); - GB_configure_cart(gb); - - return 0; -} - -int GB_save_battery(GB_gameboy_t *gb, const char *path) -{ - if (!gb->cartridge_type->has_battery) return 0; // Nothing to save. - if (gb->mbc_ram_size == 0 && !gb->cartridge_type->has_rtc) return 0; /* Claims to have battery, but has no RAM or RTC */ - FILE *f = fopen(path, "wb"); - if (!f) { - GB_log(gb, "Could not open battery save: %s.\n", strerror(errno)); - return errno; - } - - if (fwrite(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) { - fclose(f); - return EIO; - } - if (gb->cartridge_type->has_rtc) { - if (fwrite(&gb->rtc_real, 1, sizeof(gb->rtc_real), f) != sizeof(gb->rtc_real)) { - fclose(f); - return EIO; - } - - if (fwrite(&gb->last_rtc_second, 1, sizeof(gb->last_rtc_second), f) != sizeof(gb->last_rtc_second)) { - fclose(f); - return EIO; - } - } - - errno = 0; - fclose(f); - return errno; -} - -/* Loading will silently stop if the format is incomplete */ -void GB_load_battery(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "rb"); - if (!f) { - return; - } - - if (fread(gb->mbc_ram, 1, gb->mbc_ram_size, f) != gb->mbc_ram_size) { - goto reset_rtc; - } - - if (fread(&gb->rtc_real, 1, sizeof(gb->rtc_real), f) != sizeof(gb->rtc_real)) { - goto reset_rtc; - } - - if (fread(&gb->last_rtc_second, 1, sizeof(gb->last_rtc_second), f) != sizeof(gb->last_rtc_second)) { - goto reset_rtc; - } - - if (gb->last_rtc_second > time(NULL)) { - /* We must reset RTC here, or it will not advance. */ - goto reset_rtc; - } - - if (gb->last_rtc_second < 852076800) { /* 1/1/97. There weren't any RTC games that time, - so if the value we read is lower it means it wasn't - really RTC data. */ - goto reset_rtc; - } - goto exit; -reset_rtc: - gb->last_rtc_second = time(NULL); - gb->rtc_real.high |= 0x80; /* This gives the game a hint that the clock should be reset. */ -exit: - fclose(f); - return; -} - -void GB_run(GB_gameboy_t *gb) -{ - GB_cpu_run(gb); - if (gb->vblank_just_occured) { - GB_update_joyp(gb); - GB_rtc_run(gb); - } -} - -uint64_t GB_run_cycles(GB_gameboy_t *gb, uint32_t cycles) -{ - GB_update_joyp(gb); - GB_rtc_run(gb); - uint64_t start = gb->cycles_since_epoch; - uint64_t target = start + cycles; - while (gb->cycles_since_epoch < target) { - GB_run(gb); - } - return gb->cycles_since_epoch - start; -} - -uint64_t GB_epoch(GB_gameboy_t *gb) -{ - return gb->cycles_since_epoch; -} - -void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output) -{ - gb->screen = output; -} - -void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback) -{ - gb->vblank_callback = callback; -} - -void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback) -{ - gb->log_callback = callback; -} - -void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback) -{ - gb->input_callback = callback; -} - -void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback) -{ - if (!gb->rgb_encode_callback && !gb->is_cgb) { - gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] = - callback(gb, 0xFF, 0xFF, 0xFF); - gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] = - callback(gb, 0xAA, 0xAA, 0xAA); - gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] = - callback(gb, 0x55, 0x55, 0x55); - gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] = - callback(gb, 0, 0, 0); - } - gb->rgb_encode_callback = callback; -} - -void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback) -{ - gb->infrared_callback = callback; -} - -void GB_set_infrared_input(GB_gameboy_t *gb, bool state) -{ - gb->infrared_input = state; - gb->cycles_since_input_ir_change = 0; - gb->ir_queue_length = 0; -} - -void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change) -{ - if (gb->ir_queue_length == GB_MAX_IR_QUEUE) { - GB_log(gb, "IR Queue is full\n"); - return; - } - gb->ir_queue[gb->ir_queue_length++] = (GB_ir_queue_item_t){state, cycles_after_previous_change}; -} - -void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback) -{ - gb->rumble_callback = callback; -} - -void GB_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback) -{ - gb->sample_callback = callback; -} - -void GB_set_serial_transfer_start_callback(GB_gameboy_t *gb, GB_serial_transfer_start_callback_t callback) -{ - gb->serial_transfer_start_callback = callback; -} - -void GB_set_serial_transfer_end_callback(GB_gameboy_t *gb, GB_serial_transfer_end_callback_t callback) -{ - gb->serial_transfer_end_callback = callback; -} - -uint8_t GB_serial_get_data(GB_gameboy_t *gb) -{ - if (gb->io_registers[GB_IO_SC] & 1) { - /* Internal Clock */ - GB_log(gb, "Serial read request while using internal clock. \n"); - return 0xFF; - } - return gb->io_registers[GB_IO_SB]; -} -void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data) -{ - if (gb->io_registers[GB_IO_SC] & 1) { - /* Internal Clock */ - GB_log(gb, "Serial write request while using internal clock. \n"); - return; - } - gb->io_registers[GB_IO_SB] = data; - gb->io_registers[GB_IO_IF] |= 8; -} - -void GB_disconnect_serial(GB_gameboy_t *gb) -{ - gb->serial_transfer_start_callback = NULL; - gb->serial_transfer_end_callback = NULL; - - /* Reset any internally-emulated device. Currently, only the printer. */ - memset(&gb->printer, 0, sizeof(gb->printer)); -} - -bool GB_is_inited(GB_gameboy_t *gb) -{ - return gb->magic == 'SAME'; -} - -bool GB_is_cgb(GB_gameboy_t *gb) -{ - return gb->is_cgb; -} - -void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled) -{ - gb->disable_rendering = disabled; -} - -void *GB_get_user_data(GB_gameboy_t *gb) -{ - return gb->user_data; -} - -void GB_set_user_data(GB_gameboy_t *gb, void *data) -{ - gb->user_data = data; -} - -void GB_reset(GB_gameboy_t *gb) -{ - uint32_t mbc_ram_size = gb->mbc_ram_size; - bool cgb = gb->is_cgb; - // memset(gb, 0, (size_t)GB_GET_SECTION((GB_gameboy_t *) 0, unsaved)); - gb->version = GB_STRUCT_VERSION; - - gb->mbc_rom_bank = 1; - gb->last_rtc_second = time(NULL); - gb->cgb_ram_bank = 1; - gb->io_registers[GB_IO_JOYP] = 0xF; - gb->mbc_ram_size = mbc_ram_size; - if (cgb) { - gb->ram_size = 0x2000 * 8; - memset(gb->ram, 0, gb->ram_size); - gb->vram_size = 0x2000 * 2; - memset(gb->vram, 0, gb->vram_size); - - gb->is_cgb = true; - gb->cgb_mode = true; - gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0x00; - } - else { - gb->ram_size = 0x2000; - memset(gb->ram, 0, gb->ram_size); - gb->vram_size = 0x2000; - memset(gb->vram, 0, gb->vram_size); - - if (gb->rgb_encode_callback) { - gb->sprite_palettes_rgb[4] = gb->sprite_palettes_rgb[0] = gb->background_palettes_rgb[0] = - gb->rgb_encode_callback(gb, 0xFF, 0xFF, 0xFF); - gb->sprite_palettes_rgb[5] = gb->sprite_palettes_rgb[1] = gb->background_palettes_rgb[1] = - gb->rgb_encode_callback(gb, 0xAA, 0xAA, 0xAA); - gb->sprite_palettes_rgb[6] = gb->sprite_palettes_rgb[2] = gb->background_palettes_rgb[2] = - gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55); - gb->sprite_palettes_rgb[7] = gb->sprite_palettes_rgb[3] = gb->background_palettes_rgb[3] = - gb->rgb_encode_callback(gb, 0, 0, 0); - } - gb->io_registers[GB_IO_OBP0] = gb->io_registers[GB_IO_OBP1] = 0xFF; - } - /* The serial interrupt always occur on the 0xF8th cycle of every 0x100 cycle since boot. */ - gb->serial_cycles = 0x100 - 0xF8; - gb->io_registers[GB_IO_SC] = 0x7E; - gb->magic = (uintptr_t)'SAME'; -} - -void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb) -{ - if (is_cgb) { - gb->ram = realloc(gb->ram, gb->ram_size = 0x2000 * 8); - gb->vram = realloc(gb->vram, gb->vram_size = 0x2000 * 2); - } - else { - gb->ram = realloc(gb->ram, gb->ram_size = 0x2000); - gb->vram = realloc(gb->vram, gb->vram_size = 0x2000); - } - gb->is_cgb = is_cgb; - GB_reset(gb); - -} - -void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank) -{ - /* Set size and bank to dummy pointers if not set */ - size_t dummy_size; - uint16_t dummy_bank; - if (!size) { - size = &dummy_size; - } - - if (!bank) { - bank = &dummy_bank; - } - - - switch (access) { - case GB_DIRECT_ACCESS_ROM: - *size = gb->rom_size; - *bank = gb->mbc_rom_bank; - return gb->rom; - case GB_DIRECT_ACCESS_RAM: - *size = gb->ram_size; - *bank = gb->cgb_ram_bank; - return gb->ram; - case GB_DIRECT_ACCESS_CART_RAM: - *size = gb->mbc_ram_size; - *bank = gb->mbc_ram_bank; - return gb->mbc_ram; - case GB_DIRECT_ACCESS_VRAM: - *size = gb->vram_size; - *bank = gb->cgb_vram_bank; - return gb->vram; - case GB_DIRECT_ACCESS_HRAM: - *size = sizeof(gb->hram); - *bank = 0; - return &gb->hram; - case GB_DIRECT_ACCESS_IO: - *size = sizeof(gb->io_registers); - *bank = 0; - return &gb->io_registers; - case GB_DIRECT_ACCESS_BOOTROM: - *size = gb->is_cgb? sizeof(gb->boot_rom) : 0x100; - *bank = 0; - return &gb->boot_rom; - case GB_DIRECT_ACCESS_OAM: - *size = sizeof(gb->oam); - *bank = 0; - return &gb->oam; - case GB_DIRECT_ACCESS_BGP: - *size = sizeof(gb->background_palettes_data); - *bank = 0; - return &gb->background_palettes_data; - case GB_DIRECT_ACCESS_OBP: - *size = sizeof(gb->sprite_palettes_data); - *bank = 0; - return &gb->sprite_palettes_data; - default: - *size = 0; - *bank = 0; - return NULL; - } -} - -void GB_set_lagged(GB_gameboy_t *gb, bool lagged) -{ - gb->lagged = lagged; -} - -bool GB_get_lagged(GB_gameboy_t *gb) -{ - return gb->lagged; -} - diff --git a/waterbox/sameboy/gb.h b/waterbox/sameboy/gb.h deleted file mode 100644 index 098a3d2ea3..0000000000 --- a/waterbox/sameboy/gb.h +++ /dev/null @@ -1,523 +0,0 @@ -#ifndef GB_h -#define GB_h -#include -#include -#include -#include -#include - -#include "gb_struct_def.h" - -#include "apu.h" -#include "camera.h" -#include "display.h" -#include "joypad.h" -#include "mbc.h" -#include "memory.h" -#include "printer.h" -#include "timing.h" -#include "z80_cpu.h" - -#define GB_STRUCT_VERSION 11 - -enum { - GB_REGISTER_AF, - GB_REGISTER_BC, - GB_REGISTER_DE, - GB_REGISTER_HL, - GB_REGISTER_SP, - GB_REGISTERS_16_BIT /* Count */ -}; - -/* Todo: Actually use these! */ -enum { - GB_CARRY_FLAG = 16, - GB_HALF_CARRY_FLAG = 32, - GB_SUBSTRACT_FLAG = 64, - GB_ZERO_FLAG = 128, -}; - -#define GB_MAX_IR_QUEUE 256 - -enum { - /* Joypad and Serial */ - GB_IO_JOYP = 0x00, // Joypad (R/W) - GB_IO_SB = 0x01, // Serial transfer data (R/W) - GB_IO_SC = 0x02, // Serial Transfer Control (R/W) - - /* Missing */ - - /* Timers */ - GB_IO_DIV = 0x04, // Divider Register (R/W) - GB_IO_TIMA = 0x05, // Timer counter (R/W) - GB_IO_TMA = 0x06, // Timer Modulo (R/W) - GB_IO_TAC = 0x07, // Timer Control (R/W) - - /* Missing */ - - GB_IO_IF = 0x0f, // Interrupt Flag (R/W) - - /* Sound */ - GB_IO_NR10 = 0x10, // Channel 1 Sweep register (R/W) - GB_IO_NR11 = 0x11, // Channel 1 Sound length/Wave pattern duty (R/W) - GB_IO_NR12 = 0x12, // Channel 1 Volume Envelope (R/W) - GB_IO_NR13 = 0x13, // Channel 1 Frequency lo (Write Only) - GB_IO_NR14 = 0x14, // Channel 1 Frequency hi (R/W) - GB_IO_NR21 = 0x16, // Channel 2 Sound Length/Wave Pattern Duty (R/W) - GB_IO_NR22 = 0x17, // Channel 2 Volume Envelope (R/W) - GB_IO_NR23 = 0x18, // Channel 2 Frequency lo data (W) - GB_IO_NR24 = 0x19, // Channel 2 Frequency hi data (R/W) - GB_IO_NR30 = 0x1a, // Channel 3 Sound on/off (R/W) - GB_IO_NR31 = 0x1b, // Channel 3 Sound Length - GB_IO_NR32 = 0x1c, // Channel 3 Select output level (R/W) - GB_IO_NR33 = 0x1d, // Channel 3 Frequency's lower data (W) - GB_IO_NR34 = 0x1e, // Channel 3 Frequency's higher data (R/W) - - /* Missing */ - - GB_IO_NR41 = 0x20, // Channel 4 Sound Length (R/W) - GB_IO_NR42 = 0x21, // Channel 4 Volume Envelope (R/W) - GB_IO_NR43 = 0x22, // Channel 4 Polynomial Counter (R/W) - GB_IO_NR44 = 0x23, // Channel 4 Counter/consecutive, Inital (R/W) - GB_IO_NR50 = 0x24, // Channel control / ON-OFF / Volume (R/W) - GB_IO_NR51 = 0x25, // Selection of Sound output terminal (R/W) - GB_IO_NR52 = 0x26, // Sound on/off - - /* Missing */ - - GB_IO_WAV_START = 0x30, // Wave pattern start - GB_IO_WAV_END = 0x3f, // Wave pattern end - - /* Graphics */ - GB_IO_LCDC = 0x40, // LCD Control (R/W) - GB_IO_STAT = 0x41, // LCDC Status (R/W) - GB_IO_SCY = 0x42, // Scroll Y (R/W) - GB_IO_SCX = 0x43, // Scroll X (R/W) - GB_IO_LY = 0x44, // LCDC Y-Coordinate (R) - GB_IO_LYC = 0x45, // LY Compare (R/W) - GB_IO_DMA = 0x46, // DMA Transfer and Start Address (W) - GB_IO_BGP = 0x47, // BG Palette Data (R/W) - Non CGB Mode Only - GB_IO_OBP0 = 0x48, // Object Palette 0 Data (R/W) - Non CGB Mode Only - GB_IO_OBP1 = 0x49, // Object Palette 1 Data (R/W) - Non CGB Mode Only - GB_IO_WY = 0x4a, // Window Y Position (R/W) - GB_IO_WX = 0x4b, // Window X Position minus 7 (R/W) - // Has some undocumented compatibility flags written at boot. - // Unfortunately it is not readable or writable after boot has finished, so research of this - // register is quite limited. The value written to this register, however, can be controlled - // in some cases. - GB_IO_DMG_EMULATION = 0x4c, - - /* General CGB features */ - GB_IO_KEY1 = 0x4d, // CGB Mode Only - Prepare Speed Switch - - /* Missing */ - - GB_IO_VBK = 0x4f, // CGB Mode Only - VRAM Bank - GB_IO_BIOS = 0x50, // Write to disable the BIOS mapping - - /* CGB DMA */ - GB_IO_HDMA1 = 0x51, // CGB Mode Only - New DMA Source, High - GB_IO_HDMA2 = 0x52, // CGB Mode Only - New DMA Source, Low - GB_IO_HDMA3 = 0x53, // CGB Mode Only - New DMA Destination, High - GB_IO_HDMA4 = 0x54, // CGB Mode Only - New DMA Destination, Low - GB_IO_HDMA5 = 0x55, // CGB Mode Only - New DMA Length/Mode/Start - - /* IR */ - GB_IO_RP = 0x56, // CGB Mode Only - Infrared Communications Port - - /* Missing */ - - /* CGB Paletts */ - GB_IO_BGPI = 0x68, // CGB Mode Only - Background Palette Index - GB_IO_BGPD = 0x69, // CGB Mode Only - Background Palette Data - GB_IO_OBPI = 0x6a, // CGB Mode Only - Sprite Palette Index - GB_IO_OBPD = 0x6b, // CGB Mode Only - Sprite Palette Data - - // 1 is written for DMG ROMs on a CGB. Does not appear to have an effect. - GB_IO_DMG_EMULATION_INDICATION = 0x6c, // (FEh) Bit 0 (Read/Write) - - /* Missing */ - - GB_IO_SVBK = 0x70, // CGB Mode Only - WRAM Bank - GB_IO_UNKNOWN2 = 0x72, // (00h) - Bit 0-7 (Read/Write) - GB_IO_UNKNOWN3 = 0x73, // (00h) - Bit 0-7 (Read/Write) - GB_IO_UNKNOWN4 = 0x74, // (00h) - Bit 0-7 (Read/Write) - CGB Mode Only - GB_IO_UNKNOWN5 = 0x75, // (8Fh) - Bit 4-6 (Read/Write) - GB_IO_PCM_12 = 0x76, // Channels 1 and 2 amplitudes - GB_IO_PCM_34 = 0x77, // Channels 3 and 4 amplitudes - GB_IO_UNKNOWN8 = 0x7F, // Unknown, write only -}; - -typedef enum { - GB_LOG_BOLD = 1, - GB_LOG_DASHED_UNDERLINE = 2, - GB_LOG_UNDERLINE = 4, - GB_LOG_UNDERLINE_MASK = GB_LOG_DASHED_UNDERLINE | GB_LOG_UNDERLINE -} GB_log_attributes; - -#ifdef GB_INTERNAL -#define LCDC_PERIOD 70224 -#define CPU_FREQUENCY 0x400000 -#define DIV_CYCLES (0x100) -#define INTERNAL_DIV_CYCLES (0x40000) -#define FRAME_LENGTH 16742706 // in nanoseconds -#endif - -typedef void (*GB_vblank_callback_t)(GB_gameboy_t *gb); -typedef void (*GB_log_callback_t)(GB_gameboy_t *gb, const char *string, GB_log_attributes attributes); -typedef void (*GB_input_callback_t)(GB_gameboy_t *gb); -typedef uint32_t (*GB_rgb_encode_callback_t)(GB_gameboy_t *gb, uint8_t r, uint8_t g, uint8_t b); -typedef void (*GB_infrared_callback_t)(GB_gameboy_t *gb, bool on, long cycles_since_last_update); -typedef void (*GB_rumble_callback_t)(GB_gameboy_t *gb, bool rumble_on); -typedef void (*GB_serial_transfer_start_callback_t)(GB_gameboy_t *gb, uint8_t byte_to_send); -typedef uint8_t (*GB_serial_transfer_end_callback_t)(GB_gameboy_t *gb); -typedef void (*GB_sample_callback_t)(GB_gameboy_t *gb, GB_sample_t sample, uint64_t clock); -typedef void (*GB_scanline_callback_t)(uint8_t lcdc); - -typedef struct { - bool state; - long delay; -} GB_ir_queue_item_t; - -/* When state saving, each section is dumped independently of other sections. - This allows adding data to the end of the section without worrying about future compatibility. - Some other changes might be "safe" as well. - This struct is not packed, but dumped sections exclusively use types that have the same alignment in both 32 and 64 - bit platforms. */ - -/* We make sure bool is 1 for cross-platform save state compatibility. */ -/* Todo: We might want to typedef our own bool if this prevents SameBoy from working on specific platforms. */ -_Static_assert(sizeof(bool) == 1, "sizeof(bool) != 1"); - -#ifdef GB_INTERNAL -struct GB_gameboy_s { -#else -struct GB_gameboy_internal_s { -#endif - /* The magic makes sure a state file is: - - Indeed a SameBoy state file. - - Has the same endianess has the current platform. */ - volatile uint32_t magic; - /* The version field makes sure we don't load save state files with a completely different structure. - This happens when struct fields are removed/resized in an backward incompatible manner. */ - uint32_t version; - - /* Registers */ - uint16_t pc; - union { - uint16_t registers[GB_REGISTERS_16_BIT]; - struct { - uint16_t af, - bc, - de, - hl, - sp; - }; - struct { -#if __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ - uint8_t a, f, - b, c, - d, e, - h, l; -#elif __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ - uint8_t f, a, - c, b, - e, d, - l, h; -#else -#error Unable to detect endianess -#endif - }; - - }; - uint8_t ime; - uint8_t interrupt_enable; - uint8_t cgb_ram_bank; - - /* CPU and General Hardware Flags*/ - bool is_sgb; - bool cgb_mode; - bool is_cgb; - bool cgb_double_speed; - bool halted; - bool stopped; - bool boot_rom_finished; - bool ime_toggle; /* ei (and di in CGB) have delayed effects.*/ - bool halt_bug; - - /* Misc state */ - bool infrared_input; - GB_printer_t printer; - - /* DMA and HDMA */ - bool hdma_on; - bool hdma_on_hblank; - uint8_t hdma_steps_left; - uint16_t hdma_cycles; - uint16_t hdma_current_src, hdma_current_dest; - - uint8_t dma_steps_left; - uint8_t dma_current_dest; - uint16_t dma_current_src; - int16_t dma_cycles; - bool is_dma_restarting; - - /* MBC */ - uint16_t mbc_rom_bank; - uint8_t mbc_ram_bank; - uint32_t mbc_ram_size; - bool mbc_ram_enable; - union { - struct { - uint8_t bank_low:5; - uint8_t bank_high:2; - uint8_t padding:1; // Save state compatibility with 0.9 - uint8_t mode:1; - } mbc1; - - struct { - uint8_t rom_bank:4; - } mbc2; - - struct { - uint8_t rom_bank:7; - uint8_t padding:1; - uint8_t ram_bank:4; - } mbc3; - - struct { - uint8_t rom_bank_low; - uint8_t rom_bank_high:1; - uint8_t ram_bank:4; - } mbc5; - - struct { - uint8_t bank_low:6; - uint8_t bank_high:3; - uint8_t mode:1; - } huc1; - - struct { - uint8_t rom_bank; - uint8_t ram_bank; - } huc3; - }; - uint16_t mbc_rom0_bank; /* For some MBC1 wirings. */ - bool camera_registers_mapped; - uint8_t camera_registers[0x36]; - bool rumble_state; - - - /* HRAM and HW Registers */ - uint8_t hram[0xFFFF - 0xFF80]; - uint8_t io_registers[0x80]; - - /* Timing */ - uint32_t display_cycles; - uint32_t div_cycles; - uint8_t tima_reload_state; /* After TIMA overflows, it becomes 0 for 4 cycles before actually reloading. */ - uint16_t serial_cycles; /* This field changed its meaning in v0.10 */ - uint16_t serial_length; - - /* APU */ - GB_apu_t apu; - - /* RTC */ - union { - struct { - uint8_t seconds; - uint8_t minutes; - uint8_t hours; - uint8_t days; - uint8_t high; - }; - uint8_t data[5]; - } rtc_real, rtc_latched; - time_t last_rtc_second; - bool rtc_latch; - - /* Video Display */ - uint32_t vram_size; // Different between CGB and DMG - uint8_t cgb_vram_bank; - uint8_t oam[0xA0]; - uint8_t background_palettes_data[0x40]; - uint8_t sprite_palettes_data[0x40]; - uint32_t background_palettes_rgb[0x20]; - uint32_t sprite_palettes_rgb[0x20]; - int16_t previous_lcdc_x; - bool stat_interrupt_line; - uint8_t effective_scx; - uint8_t current_window_line; - /* The LCDC will skip the first frame it renders after turning it on. - On the CGB, a frame is not skipped if the previous frame was skipped as well. - See https://www.reddit.com/r/EmuDev/comments/6exyxu/ */ - enum { - GB_FRAMESKIP_LCD_TURNED_ON, // On a DMG, the LCD renders a blank screen during this state, - // on a CGB, the previous frame is repeated (which might be - // blank if the LCD was off for more than a few cycles) - GB_FRAMESKIP_FIRST_FRAME_SKIPPED, // This state is 'skipped' when emulating a DMG - GB_FRAMESKIP_SECOND_FRAME_RENDERED, - } frame_skip_state; - bool first_scanline; // The very first scan line after turning the LCD behaves differently. - bool oam_read_blocked; - bool vram_read_blocked; - bool oam_write_blocked; - bool vram_write_blocked; - - /* Unsaved data. This includes all pointers, as well as everything that shouldn't be on a save state */ - /* This data is reserved on reset and must come last in the struct */ - /* ROM */ - uint8_t *rom; - uint32_t rom_size; - const GB_cartridge_t *cartridge_type; - enum { - GB_STANDARD_MBC1_WIRING, - GB_MBC1M_WIRING, - } mbc1_wiring; - - /* Various RAMs */ - uint8_t *ram; - uint8_t *vram; - uint8_t *mbc_ram; - - /* I/O */ - uint32_t *screen; - int keys; - bool lagged; - - /* Timing */ - uint64_t cycles_since_epoch; - - /* Callbacks */ - void *user_data; - GB_log_callback_t log_callback; - GB_input_callback_t input_callback; - GB_rgb_encode_callback_t rgb_encode_callback; - GB_vblank_callback_t vblank_callback; - GB_infrared_callback_t infrared_callback; - GB_camera_get_pixel_callback_t camera_get_pixel_callback; - GB_camera_update_request_callback_t camera_update_request_callback; - GB_rumble_callback_t rumble_callback; - GB_serial_transfer_start_callback_t serial_transfer_start_callback; - GB_serial_transfer_end_callback_t serial_transfer_end_callback; - GB_sample_callback_t sample_callback; - - GB_scanline_callback_t scanline_callback; - int scanline_callback_ly; - - /* IR */ - long cycles_since_ir_change; - long cycles_since_input_ir_change; - GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE]; - size_t ir_queue_length; - - /* Breakpoints */ - uint16_t n_breakpoints; - struct GB_breakpoint_s *breakpoints; - - /* SLD (Todo: merge with backtrace) */ - bool stack_leak_detection; - int debug_call_depth; - uint16_t sp_for_call_depth[0x200]; /* Should be much more than enough */ - uint16_t addr_for_call_depth[0x200]; - - /* Backtrace */ - unsigned int backtrace_size; - uint16_t backtrace_sps[0x200]; - struct { - uint16_t bank; - uint16_t addr; - } backtrace_returns[0x200]; - - /* Misc */ - bool disable_rendering; - uint32_t ram_size; // Different between CGB and DMG - uint8_t boot_rom[0x900]; - bool vblank_just_occured; // For slow operations involving syscalls; these should only run once per vblank - - int64_t frontend_rtc_time; -}; - -#ifndef GB_INTERNAL -struct GB_gameboy_s { - char __internal[sizeof(struct GB_gameboy_internal_s)]; -}; -#endif - - -#ifndef __printflike -/* Missing from Linux headers. */ -#define __printflike(fmtarg, firstvararg) \ -__attribute__((__format__ (__printf__, fmtarg, firstvararg))) -#endif - -void GB_init(GB_gameboy_t *gb); -void GB_init_sgb(GB_gameboy_t *gb); -void GB_init_cgb(GB_gameboy_t *gb); -bool GB_is_inited(GB_gameboy_t *gb); -bool GB_is_cgb(GB_gameboy_t *gb); -void GB_free(GB_gameboy_t *gb); -void GB_reset(GB_gameboy_t *gb); -void GB_switch_model_and_reset(GB_gameboy_t *gb, bool is_cgb); -void GB_run(GB_gameboy_t *gb); -uint64_t GB_run_cycles(GB_gameboy_t *gb, uint32_t cycles); -uint64_t GB_epoch(GB_gameboy_t *gb); - -typedef enum { - GB_DIRECT_ACCESS_ROM, - GB_DIRECT_ACCESS_RAM, - GB_DIRECT_ACCESS_CART_RAM, - GB_DIRECT_ACCESS_VRAM, - GB_DIRECT_ACCESS_HRAM, - GB_DIRECT_ACCESS_IO, /* Warning: Some registers can only be read/written correctly via GB_memory_read/write. */ - GB_DIRECT_ACCESS_BOOTROM, - GB_DIRECT_ACCESS_OAM, - GB_DIRECT_ACCESS_BGP, - GB_DIRECT_ACCESS_OBP, -} GB_direct_access_t; - -/* Returns a mutable pointer to various hardware memories. If that memory is banked, the current bank - is returned at *bank, even if only a portion of the memory is banked. */ -void *GB_get_direct_access(GB_gameboy_t *gb, GB_direct_access_t access, size_t *size, uint16_t *bank); - -void *GB_get_user_data(GB_gameboy_t *gb); -void GB_set_user_data(GB_gameboy_t *gb, void *data); - -int GB_load_boot_rom(GB_gameboy_t *gb, const char *path); -int GB_load_rom(GB_gameboy_t *gb, const char *path); - -int GB_save_battery(GB_gameboy_t *gb, const char *path); -void GB_load_battery(GB_gameboy_t *gb, const char *path); - -void GB_set_rendering_disabled(GB_gameboy_t *gb, bool disabled); - -void GB_log(GB_gameboy_t *gb, const char *fmt, ...) __printflike(2, 3); -void GB_attributed_log(GB_gameboy_t *gb, GB_log_attributes attributes, const char *fmt, ...) __printflike(3, 4); - -void GB_set_pixels_output(GB_gameboy_t *gb, uint32_t *output); - -void GB_set_infrared_input(GB_gameboy_t *gb, bool state); -void GB_queue_infrared_input(GB_gameboy_t *gb, bool state, long cycles_after_previous_change); - -void GB_set_vblank_callback(GB_gameboy_t *gb, GB_vblank_callback_t callback); -void GB_set_log_callback(GB_gameboy_t *gb, GB_log_callback_t callback); -void GB_set_input_callback(GB_gameboy_t *gb, GB_input_callback_t callback); -void GB_set_rgb_encode_callback(GB_gameboy_t *gb, GB_rgb_encode_callback_t callback); -void GB_set_infrared_callback(GB_gameboy_t *gb, GB_infrared_callback_t callback); -void GB_set_rumble_callback(GB_gameboy_t *gb, GB_rumble_callback_t callback); -void GB_set_sample_callback(GB_gameboy_t *gb, GB_sample_callback_t callback); - -/* These APIs are used when using internal clock */ -void GB_set_serial_transfer_start_callback(GB_gameboy_t *gb, GB_serial_transfer_start_callback_t callback); -void GB_set_serial_transfer_end_callback(GB_gameboy_t *gb, GB_serial_transfer_end_callback_t callback); - -/* These APIs are used when using external clock */ -uint8_t GB_serial_get_data(GB_gameboy_t *gb); -void GB_serial_set_data(GB_gameboy_t *gb, uint8_t data); - -void GB_disconnect_serial(GB_gameboy_t *gb); - -void GB_set_lagged(GB_gameboy_t *gb, bool lagged); -bool GB_get_lagged(GB_gameboy_t *gb); - -#endif /* GB_h */ diff --git a/waterbox/sameboy/gb_struct_def.h b/waterbox/sameboy/gb_struct_def.h deleted file mode 100644 index 0e0ebd12ee..0000000000 --- a/waterbox/sameboy/gb_struct_def.h +++ /dev/null @@ -1,5 +0,0 @@ -#ifndef gb_struct_def_h -#define gb_struct_def_h -struct GB_gameboy_s; -typedef struct GB_gameboy_s GB_gameboy_t; -#endif diff --git a/waterbox/sameboy/joypad.c b/waterbox/sameboy/joypad.c deleted file mode 100644 index 0cad7ab084..0000000000 --- a/waterbox/sameboy/joypad.c +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include "gb.h" -#include - -void GB_update_joyp(GB_gameboy_t *gb) -{ - uint8_t key_selection = 0; - uint8_t previous_state = 0; - - /* Todo: add delay to key selection */ - previous_state = gb->io_registers[GB_IO_JOYP] & 0xF; - key_selection = gb->io_registers[GB_IO_JOYP] >> 4 & 3; - gb->io_registers[GB_IO_JOYP] &= 0xF0; - switch (key_selection) { - case 3: - /* Nothing is wired, all up */ - gb->io_registers[GB_IO_JOYP] |= 0x0F; - break; - - case 2: - /* Direction keys */ - gb->io_registers[GB_IO_JOYP] |= ~gb->keys >> 4 & 0xf; - break; - - case 1: - /* Other keys */ - gb->io_registers[GB_IO_JOYP] |= ~gb->keys & 0xf; - break; - - case 0: - /* Todo: verifiy this is correct */ - gb->io_registers[GB_IO_JOYP] |= ~(gb->keys >> 4 & gb->keys) & 0xf; - break; - - default: - break; - } - if (previous_state != (gb->io_registers[GB_IO_JOYP] & 0xF)) { - /* Todo: disable when emulating CGB */ - gb->io_registers[GB_IO_IF] |= 0x10; - } - gb->io_registers[GB_IO_JOYP] |= 0xC0; // No SGB support -} - -void GB_set_key_state(GB_gameboy_t *gb, int keys) -{ - gb->keys = keys; -} diff --git a/waterbox/sameboy/joypad.h b/waterbox/sameboy/joypad.h deleted file mode 100644 index f7ffdfdec4..0000000000 --- a/waterbox/sameboy/joypad.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef joypad_h -#define joypad_h -#include "gb_struct_def.h" - -void GB_set_key_state(GB_gameboy_t *gb, int keys); - -#ifdef GB_INTERNAL -void GB_update_joyp(GB_gameboy_t *gb); -#endif -#endif /* joypad_h */ diff --git a/waterbox/sameboy/mbc.c b/waterbox/sameboy/mbc.c deleted file mode 100644 index d3791a180f..0000000000 --- a/waterbox/sameboy/mbc.c +++ /dev/null @@ -1,154 +0,0 @@ -#include -#include -#include -#include "gb.h" - -const GB_cartridge_t GB_cart_defs[256] = { - // From http://gbdev.gg8.se/wiki/articles/The_Cartridge_Header#0147_-_Cartridge_Type - /* MBC SUBTYPE RAM BAT. RTC RUMB. */ - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 00h ROM ONLY - { GB_MBC1 , GB_STANDARD_MBC, false, false, false, false}, // 01h MBC1 - { GB_MBC1 , GB_STANDARD_MBC, true , false, false, false}, // 02h MBC1+RAM - { GB_MBC1 , GB_STANDARD_MBC, true , true , false, false}, // 03h MBC1+RAM+BATTERY - [5] = - { GB_MBC2 , GB_STANDARD_MBC, true , false, false, false}, // 05h MBC2 - { GB_MBC2 , GB_STANDARD_MBC, true , true , false, false}, // 06h MBC2+BATTERY - [8] = - { GB_NO_MBC, GB_STANDARD_MBC, true , false, false, false}, // 08h ROM+RAM - { GB_NO_MBC, GB_STANDARD_MBC, true , true , false, false}, // 09h ROM+RAM+BATTERY - [0xB] = - /* Todo: Not supported yet */ - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Bh MMM01 - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Ch MMM01+RAM - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // 0Dh MMM01+RAM+BATTERY - [0xF] = - { GB_MBC3 , GB_STANDARD_MBC, false, true, true , false}, // 0Fh MBC3+TIMER+BATTERY - { GB_MBC3 , GB_STANDARD_MBC, true , true, true , false}, // 10h MBC3+TIMER+RAM+BATTERY - { GB_MBC3 , GB_STANDARD_MBC, false, false, false, false}, // 11h MBC3 - { GB_MBC3 , GB_STANDARD_MBC, true , false, false, false}, // 12h MBC3+RAM - { GB_MBC3 , GB_STANDARD_MBC, true , true , false, false}, // 13h MBC3+RAM+BATTERY - [0x19] = - { GB_MBC5 , GB_STANDARD_MBC, false, false, false, false}, // 19h MBC5 - { GB_MBC5 , GB_STANDARD_MBC, true , false, false, false}, // 1Ah MBC5+RAM - { GB_MBC5 , GB_STANDARD_MBC, true , true , false, false}, // 1Bh MBC5+RAM+BATTERY - { GB_MBC5 , GB_STANDARD_MBC, false, false, false, true }, // 1Ch MBC5+RUMBLE - { GB_MBC5 , GB_STANDARD_MBC, true , false, false, true }, // 1Dh MBC5+RUMBLE+RAM - { GB_MBC5 , GB_STANDARD_MBC, true , true , false, true }, // 1Eh MBC5+RUMBLE+RAM+BATTERY - [0xFC] = - { GB_MBC5 , GB_CAMERA , true , true , false, false}, // FCh POCKET CAMERA - { GB_NO_MBC, GB_STANDARD_MBC, false, false, false, false}, // FDh BANDAI TAMA5 (Todo: Not supported) - { GB_HUC3 , GB_STANDARD_MBC, true , true , false, false}, // FEh HuC3 (Todo: Mapper support only) - { GB_HUC1 , GB_STANDARD_MBC, true , true , false, false}, // FFh HuC1+RAM+BATTERY (Todo: No IR bindings) -}; - -void GB_update_mbc_mappings(GB_gameboy_t *gb) -{ - switch (gb->cartridge_type->mbc_type) { - case GB_NO_MBC: return; - case GB_MBC1: - switch (gb->mbc1_wiring) { - case GB_STANDARD_MBC1_WIRING: - gb->mbc_rom_bank = gb->mbc1.bank_low | (gb->mbc1.bank_high << 5); - if (gb->mbc1.mode == 0) { - gb->mbc_ram_bank = 0; - gb->mbc_rom0_bank = 0; - } - else { - gb->mbc_ram_bank = gb->mbc1.bank_high; - gb->mbc_rom0_bank = gb->mbc1.bank_high << 5; - } - if ((gb->mbc_rom_bank & 0x1F) == 0) { - gb->mbc_rom_bank++; - } - break; - case GB_MBC1M_WIRING: - gb->mbc_rom_bank = (gb->mbc1.bank_low & 0xF) | (gb->mbc1.bank_high << 4); - if (gb->mbc1.mode == 0) { - gb->mbc_ram_bank = 0; - gb->mbc_rom0_bank = 0; - } - else { - gb->mbc_rom0_bank = gb->mbc1.bank_high << 4; - gb->mbc_ram_bank = 0; - } - if ((gb->mbc1.bank_low & 0x1F) == 0) { - gb->mbc_rom_bank++; - } - break; - } - break; - case GB_MBC2: - gb->mbc_rom_bank = gb->mbc2.rom_bank; - if ((gb->mbc_rom_bank & 0xF) == 0) { - gb->mbc_rom_bank = 1; - } - break; - case GB_MBC3: - gb->mbc_rom_bank = gb->mbc3.rom_bank; - gb->mbc_ram_bank = gb->mbc3.ram_bank; - if (gb->mbc_rom_bank == 0) { - gb->mbc_rom_bank = 1; - } - break; - case GB_MBC5: - gb->mbc_rom_bank = gb->mbc5.rom_bank_low | (gb->mbc5.rom_bank_high << 8); - gb->mbc_ram_bank = gb->mbc5.ram_bank; - break; - case GB_HUC1: - if (gb->huc1.mode == 0) { - gb->mbc_rom_bank = gb->huc1.bank_low | (gb->mbc1.bank_high << 6); - gb->mbc_ram_bank = 0; - } - else { - gb->mbc_rom_bank = gb->huc1.bank_low; - gb->mbc_ram_bank = gb->huc1.bank_high; - } - break; - case GB_HUC3: - gb->mbc_rom_bank = gb->huc3.rom_bank; - gb->mbc_ram_bank = gb->huc3.ram_bank; - break; - } -} - -void GB_configure_cart(GB_gameboy_t *gb) -{ - gb->cartridge_type = &GB_cart_defs[gb->rom[0x147]]; - - if (gb->rom[0x147] == 0 && gb->rom_size > 0x8000) { - GB_log(gb, "ROM header reports no MBC, but file size is over 32Kb. Assuming cartridge uses MBC3.\n"); - gb->cartridge_type = &GB_cart_defs[0x11]; - } - else if (gb->rom[0x147] != 0 && memcmp(gb->cartridge_type, &GB_cart_defs[0], sizeof(GB_cart_defs[0])) == 0) { - GB_log(gb, "Cartridge type %02x is not yet supported.\n", gb->rom[0x147]); - } - - if (gb->cartridge_type->has_ram) { - if (gb->cartridge_type->mbc_type == GB_MBC2) { - gb->mbc_ram_size = 0x200; - } - else { - static const int ram_sizes[256] = {0, 0x800, 0x2000, 0x8000, 0x20000, 0x10000}; - gb->mbc_ram_size = ram_sizes[gb->rom[0x149]]; - } - gb->mbc_ram = malloc(gb->mbc_ram_size); - - /* Todo: Some games assume unintialized MBC RAM is 0xFF. It this true for all cartridges types? */ - memset(gb->mbc_ram, 0xFF, gb->mbc_ram_size); - } - - /* MBC1 has at least 3 types of wiring (We currently support two (Standard and 4bit-MBC1M) of these). - See http://forums.nesdev.com/viewtopic.php?f=20&t=14099 */ - - /* Attempt to "guess" wiring */ - if (gb->cartridge_type->mbc_type == GB_MBC1) { - if (gb->rom_size >= 0x44000 && memcmp(gb->rom + 0x104, gb->rom + 0x40104, 0x30) == 0) { - gb->mbc1_wiring = GB_MBC1M_WIRING; - } - } - - /* Set MBC5's bank to 1 correctly */ - if (gb->cartridge_type->mbc_type == GB_MBC5) { - gb->mbc5.rom_bank_low = 1; - } -} diff --git a/waterbox/sameboy/mbc.h b/waterbox/sameboy/mbc.h deleted file mode 100644 index e7260f5643..0000000000 --- a/waterbox/sameboy/mbc.h +++ /dev/null @@ -1,31 +0,0 @@ -#ifndef MBC_h -#define MBC_h -#include "gb_struct_def.h" - -typedef struct { - enum { - GB_NO_MBC, - GB_MBC1, - GB_MBC2, - GB_MBC3, - GB_MBC5, - GB_HUC1, /* Todo: HUC1 features are not emulated. Should be unified with the CGB IR sensor API. */ - GB_HUC3, - } mbc_type; - enum { - GB_STANDARD_MBC, - GB_CAMERA, - } mbc_subtype; - bool has_ram; - bool has_battery; - bool has_rtc; - bool has_rumble; -} GB_cartridge_t; - -#ifdef GB_INTERNAL -extern const GB_cartridge_t GB_cart_defs[256]; -void GB_update_mbc_mappings(GB_gameboy_t *gb); -void GB_configure_cart(GB_gameboy_t *gb); -#endif - -#endif /* MBC_h */ diff --git a/waterbox/sameboy/memory.c b/waterbox/sameboy/memory.c deleted file mode 100644 index 6988beb4cd..0000000000 --- a/waterbox/sameboy/memory.c +++ /dev/null @@ -1,733 +0,0 @@ -#include -#include -#include "gb.h" -#include "sgb.h" - -typedef uint8_t GB_read_function_t(GB_gameboy_t *gb, uint16_t addr); -typedef void GB_write_function_t(GB_gameboy_t *gb, uint16_t addr, uint8_t value); - -typedef enum { - GB_BUS_MAIN, /* In DMG: Cart and RAM. In CGB: Cart only */ - GB_BUS_RAM, /* In CGB only. */ - GB_BUS_VRAM, - GB_BUS_INTERNAL, /* Anything in highram. Might not be the most correct name. */ -} GB_bus_t; - -static GB_bus_t bus_for_addr(GB_gameboy_t *gb, uint16_t addr) -{ - if (addr < 0x8000) { - return GB_BUS_MAIN; - } - if (addr < 0xA000) { - return GB_BUS_VRAM; - } - if (addr < 0xC000) { - return GB_BUS_MAIN; - } - if (addr < 0xFE00) { - return gb->is_cgb? GB_BUS_RAM : GB_BUS_MAIN; - } - return GB_BUS_INTERNAL; -} - -static bool is_addr_in_dma_use(GB_gameboy_t *gb, uint16_t addr) -{ - if (!gb->dma_steps_left || (gb->dma_cycles < 0 && !gb->is_dma_restarting)) return false; - return bus_for_addr(gb, addr) == bus_for_addr(gb, gb->dma_current_src); -} - -static uint8_t read_rom(GB_gameboy_t *gb, uint16_t addr) -{ - if (addr < 0x100 && !gb->boot_rom_finished) { - return gb->boot_rom[addr]; - } - - if (addr >= 0x200 && addr < 0x900 && gb->is_cgb && !gb->boot_rom_finished) { - return gb->boot_rom[addr]; - } - - if (!gb->rom_size) { - return 0xFF; - } - unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom0_bank * 0x4000; - return gb->rom[effective_address & (gb->rom_size - 1)]; -} - -static uint8_t read_mbc_rom(GB_gameboy_t *gb, uint16_t addr) -{ - unsigned int effective_address = (addr & 0x3FFF) + gb->mbc_rom_bank * 0x4000; - return gb->rom[effective_address & (gb->rom_size - 1)]; -} - -static uint8_t read_vram(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->vram_read_blocked) { - return 0xFF; - } - return gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000]; -} - -static uint8_t read_mbc_ram(GB_gameboy_t *gb, uint16_t addr) -{ - if ((!gb->mbc_ram_enable || !gb->mbc_ram_size) && - gb->cartridge_type->mbc_subtype != GB_CAMERA && - gb->cartridge_type->mbc_type != GB_HUC1) return 0xFF; - - if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) { - /* RTC read */ - gb->rtc_latched.high |= ~0xC1; /* Not all bytes in RTC high are used. */ - return gb->rtc_latched.data[gb->mbc_ram_bank - 8]; - } - - if (gb->camera_registers_mapped) { - return GB_camera_read_register(gb, addr); - } - - if (!gb->mbc_ram) { - return 0xFF; - } - - if (gb->cartridge_type->mbc_subtype == GB_CAMERA && gb->mbc_ram_bank == 0 && addr >= 0xa100 && addr < 0xaf00) { - return GB_camera_read_image(gb, addr - 0xa100); - } - - uint8_t ret = gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)]; - if (gb->cartridge_type->mbc_type == GB_MBC2) { - ret |= 0xF0; - } - return ret; -} - -static uint8_t read_ram(GB_gameboy_t *gb, uint16_t addr) -{ - return gb->ram[addr & 0x0FFF]; -} - -static uint8_t read_banked_ram(GB_gameboy_t *gb, uint16_t addr) -{ - return gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000]; -} - -static uint8_t read_high_memory(GB_gameboy_t *gb, uint16_t addr) -{ - - if (addr < 0xFE00) { - return gb->ram[addr & 0x0FFF]; - } - - if (addr < 0xFEA0) { - if (gb->oam_read_blocked || (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { - return 0xFF; - } - return gb->oam[addr & 0xFF]; - } - - if (addr < 0xFF00) { - /* Unusable. CGB results are verified, but DMG results were tested on a SGB2 */ - if ((gb->io_registers[GB_IO_STAT] & 0x3) >= 2) { /* Seems to be disabled in Modes 2 and 3 */ - return 0xFF; - } - if (gb->is_cgb) { - return (addr & 0xF0) | ((addr >> 4) & 0xF); - } - return 0; - - } - - if (addr < 0xFF80) { - if (addr == 0xff00) - { - if (gb->input_callback) - gb->input_callback(gb); - gb->lagged = false; - if (gb->is_sgb) { - return sgb_read_ff00(gb->cycles_since_epoch); - } - } - - switch (addr & 0xFF) { - case GB_IO_IF: - return gb->io_registers[GB_IO_IF] | 0xE0; - case GB_IO_TAC: - return gb->io_registers[GB_IO_TAC] | 0xF8; - case GB_IO_STAT: - return gb->io_registers[GB_IO_STAT] | 0x80; - case GB_IO_DMG_EMULATION_INDICATION: - if (!gb->cgb_mode) { - return 0xFF; - } - return gb->io_registers[GB_IO_DMG_EMULATION_INDICATION] | 0xFE; - - case GB_IO_PCM_12: - case GB_IO_PCM_34: - { - if (!gb->is_cgb) return 0xFF; - GB_sample_t dummy; - GB_apu_get_samples_and_update_pcm_regs(gb, &dummy); - } - /* Fall through */ - case GB_IO_JOYP: - case GB_IO_TMA: - case GB_IO_LCDC: - case GB_IO_SCY: - case GB_IO_SCX: - case GB_IO_LY: - case GB_IO_LYC: - case GB_IO_BGP: - case GB_IO_OBP0: - case GB_IO_OBP1: - case GB_IO_WY: - case GB_IO_WX: - case GB_IO_SC: - case GB_IO_SB: - return gb->io_registers[addr & 0xFF]; - case GB_IO_TIMA: - if (gb->tima_reload_state == GB_TIMA_RELOADING) { - return 0; - } - return gb->io_registers[GB_IO_TIMA]; - case GB_IO_DIV: - return gb->div_cycles >> 8; - case GB_IO_HDMA5: - if (!gb->cgb_mode) return 0xFF; - return ((gb->hdma_on || gb->hdma_on_hblank)? 0 : 0x80) | ((gb->hdma_steps_left - 1) & 0x7F); - case GB_IO_SVBK: - if (!gb->cgb_mode) { - return 0xFF; - } - return gb->cgb_ram_bank | ~0x7; - case GB_IO_VBK: - if (!gb->is_cgb) { - return 0xFF; - } - return gb->cgb_vram_bank | ~0x1; - - /* Todo: It seems that a CGB in DMG mode can access BGPI and OBPI, but not BGPD and OBPD? */ - case GB_IO_BGPI: - case GB_IO_OBPI: - if (!gb->is_cgb) { - return 0xFF; - } - return gb->io_registers[addr & 0xFF] | 0x40; - - case GB_IO_BGPD: - case GB_IO_OBPD: - { - if (!gb->cgb_mode && gb->boot_rom_finished) { - return 0xFF; - } - uint8_t index_reg = (addr & 0xFF) - 1; - return ((addr & 0xFF) == GB_IO_BGPD? - gb->background_palettes_data : - gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F]; - } - - case GB_IO_KEY1: - if (!gb->cgb_mode) { - return 0xFF; - } - return (gb->io_registers[GB_IO_KEY1] & 0x7F) | (gb->cgb_double_speed? 0xFE : 0x7E); - - case GB_IO_RP: { - if (!gb->cgb_mode) return 0xFF; - /* You will read your own IR LED if it's on. */ - bool read_value = gb->infrared_input || (gb->io_registers[GB_IO_RP] & 1); - uint8_t ret = (gb->io_registers[GB_IO_RP] & 0xC1) | 0x3C; - if ((gb->io_registers[GB_IO_RP] & 0xC0) == 0xC0 && read_value) { - ret |= 2; - } - return ret; - } - case GB_IO_DMA: - /* Todo: is this documented? */ - return gb->is_cgb? 0x00 : 0xFF; - case GB_IO_UNKNOWN2: - case GB_IO_UNKNOWN3: - return gb->is_cgb? gb->io_registers[addr & 0xFF] : 0xFF; - case GB_IO_UNKNOWN4: - return gb->cgb_mode? gb->io_registers[addr & 0xFF] : 0xFF; - case GB_IO_UNKNOWN5: - return gb->is_cgb? gb->io_registers[addr & 0xFF] | 0x8F : 0xFF; - default: - if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) { - return GB_apu_read(gb, addr & 0xFF); - } - return 0xFF; - } - /* Hardware registers */ - return 0; - } - - if (addr == 0xFFFF) { - /* Interrupt Mask */ - return gb->interrupt_enable; - } - - /* HRAM */ - return gb->hram[addr - 0xFF80]; -} - -static GB_read_function_t * const read_map[] = -{ - read_rom, read_rom, read_rom, read_rom, /* 0XXX, 1XXX, 2XXX, 3XXX */ - read_mbc_rom, read_mbc_rom, read_mbc_rom, read_mbc_rom, /* 4XXX, 5XXX, 6XXX, 7XXX */ - read_vram, read_vram, /* 8XXX, 9XXX */ - read_mbc_ram, read_mbc_ram, /* AXXX, BXXX */ - read_ram, read_banked_ram, /* CXXX, DXXX */ - read_high_memory, read_high_memory, /* EXXX FXXX */ -}; - -uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr) -{ - if (is_addr_in_dma_use(gb, addr)) { - addr = gb->dma_current_src; - } - return read_map[addr >> 12](gb, addr); -} - -static void write_mbc(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - switch (gb->cartridge_type->mbc_type) { - case GB_NO_MBC: return; - case GB_MBC1: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->mbc1.bank_low = value; break; - case 0x4000: case 0x5000: gb->mbc1.bank_high = value; break; - case 0x6000: case 0x7000: gb->mbc1.mode = value; break; - } - break; - case GB_MBC2: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: if (!(addr & 0x100)) gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: if ( addr & 0x100) gb->mbc2.rom_bank = value; break; - } - break; - case GB_MBC3: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->mbc3.rom_bank = value; break; - case 0x4000: case 0x5000: gb->mbc3.ram_bank = value; break; - case 0x6000: case 0x7000: - if (!gb->rtc_latch && (value & 1)) { /* Todo: verify condition is correct */ - memcpy(&gb->rtc_latched, &gb->rtc_real, sizeof(gb->rtc_real)); - } - gb->rtc_latch = value & 1; - break; - } - break; - case GB_MBC5: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: gb->mbc5.rom_bank_low = value; break; - case 0x3000: gb->mbc5.rom_bank_high = value; break; - case 0x4000: case 0x5000: - if (gb->cartridge_type->has_rumble) { - if (!!(value & 8) != gb->rumble_state) { - gb->rumble_state = !gb->rumble_state; - if (gb->rumble_callback) { - gb->rumble_callback(gb, gb->rumble_state); - } - } - value &= 7; - } - gb->mbc5.ram_bank = value; - gb->camera_registers_mapped = (value & 0x10) && gb->cartridge_type->mbc_subtype == GB_CAMERA; - break; - } - break; - case GB_HUC1: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->huc1.bank_low = value; break; - case 0x4000: case 0x5000: gb->huc1.bank_high = value; break; - case 0x6000: case 0x7000: gb->huc1.mode = value; break; - } - break; - case GB_HUC3: - switch (addr & 0xF000) { - case 0x0000: case 0x1000: gb->mbc_ram_enable = (value & 0xF) == 0xA; break; - case 0x2000: case 0x3000: gb->huc3.rom_bank = value; break; - case 0x4000: case 0x5000: gb->huc3.ram_bank = value; break; - } - break; - } - GB_update_mbc_mappings(gb); -} - -static void write_vram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (gb->vram_write_blocked) { - //GB_log(gb, "Wrote %02x to %04x (VRAM) during mode 3\n", value, addr); - return; - } - gb->vram[(addr & 0x1FFF) + (uint16_t) gb->cgb_vram_bank * 0x2000] = value; -} - -static void write_mbc_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (gb->camera_registers_mapped) { - GB_camera_write_register(gb, addr, value); - return; - } - - if (!gb->mbc_ram_enable || !gb->mbc_ram_size) return; - - if (gb->cartridge_type->has_rtc && gb->mbc_ram_bank >= 8 && gb->mbc_ram_bank <= 0xC) { - /* RTC read */ - gb->rtc_latched.data[gb->mbc_ram_bank - 8] = gb->rtc_real.data[gb->mbc_ram_bank - 8] = value; /* Todo: does it really write both? */ - } - - if (!gb->mbc_ram) { - return; - } - - gb->mbc_ram[((addr & 0x1FFF) + gb->mbc_ram_bank * 0x2000) & (gb->mbc_ram_size - 1)] = value; -} - -static void write_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - gb->ram[addr & 0x0FFF] = value; -} - -static void write_banked_ram(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - gb->ram[(addr & 0x0FFF) + gb->cgb_ram_bank * 0x1000] = value; -} - -static void write_high_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (addr < 0xFE00) { - GB_log(gb, "Wrote %02x to %04x (RAM Mirror)\n", value, addr); - gb->ram[addr & 0x0FFF] = value; - return; - } - - if (addr < 0xFEA0) { - if (gb->oam_write_blocked|| (gb->dma_steps_left && (gb->dma_cycles > 0 || gb->is_dma_restarting))) { - return; - } - gb->oam[addr & 0xFF] = value; - return; - } - - if (addr < 0xFF00) { - GB_log(gb, "Wrote %02x to %04x (Unused)\n", value, addr); - return; - } - - if (addr < 0xFF80) { - if (addr == 0xff00 && gb->is_sgb) - { - sgb_write_ff00(value, gb->cycles_since_epoch); - return; - } - - /* Hardware registers */ - switch (addr & 0xFF) { - - case GB_IO_SCX: - case GB_IO_IF: - case GB_IO_SCY: - case GB_IO_LYC: - case GB_IO_BGP: - case GB_IO_OBP0: - case GB_IO_OBP1: - case GB_IO_WY: - case GB_IO_WX: - case GB_IO_SB: - case GB_IO_DMG_EMULATION_INDICATION: - case GB_IO_UNKNOWN2: - case GB_IO_UNKNOWN3: - case GB_IO_UNKNOWN4: - case GB_IO_UNKNOWN5: - gb->io_registers[addr & 0xFF] = value; - return; - - case GB_IO_TIMA: - if (gb->tima_reload_state != GB_TIMA_RELOADED) { - gb->io_registers[GB_IO_TIMA] = value; - } - return; - - case GB_IO_TMA: - gb->io_registers[GB_IO_TMA] = value; - if (gb->tima_reload_state != GB_TIMA_RUNNING) { - gb->io_registers[GB_IO_TIMA] = value; - } - return; - - case GB_IO_TAC: - GB_emulate_timer_glitch(gb, gb->io_registers[GB_IO_TAC], value); - gb->io_registers[GB_IO_TAC] = value; - return; - - - case GB_IO_LCDC: - if ((value & 0x80) && !(gb->io_registers[GB_IO_LCDC] & 0x80)) { - /* It appears that there's a slight delay after enabling the screen? */ - /* Todo: verify this. */ - gb->display_cycles = 0; - gb->first_scanline = true; - if (gb->frame_skip_state == GB_FRAMESKIP_SECOND_FRAME_RENDERED) { - gb->frame_skip_state = GB_FRAMESKIP_LCD_TURNED_ON; - } - } - gb->io_registers[GB_IO_LCDC] = value; - return; - - case GB_IO_STAT: - /* A DMG bug: http://www.devrs.com/gb/files/faqs.html#GBBugs */ - if (!gb->is_cgb && !gb->stat_interrupt_line && - (gb->io_registers[GB_IO_STAT] & 0x3) < 2 && (gb->io_registers[GB_IO_LCDC] & 0x80)) { - gb->io_registers[GB_IO_IF] |= 2; - } - /* Delete previous R/W bits */ - gb->io_registers[GB_IO_STAT] &= 7; - /* Set them by value */ - gb->io_registers[GB_IO_STAT] |= value & ~7; - /* Set unused bit to 1 */ - gb->io_registers[GB_IO_STAT] |= 0x80; - return; - - case GB_IO_DIV: - GB_set_internal_div_counter(gb, 0); - return; - - case GB_IO_JOYP: - gb->io_registers[GB_IO_JOYP] &= 0x0F; - gb->io_registers[GB_IO_JOYP] |= value & 0xF0; - GB_update_joyp(gb); - return; - - case GB_IO_BIOS: - gb->boot_rom_finished = true; - return; - - case GB_IO_DMG_EMULATION: - if (gb->is_cgb && !gb->boot_rom_finished) { - gb->cgb_mode = value != 4; /* The real "contents" of this register aren't quite known yet. */ - } - return; - - case GB_IO_DMA: - if (value <= 0xE0) { - if (gb->dma_steps_left) { - /* This is not correct emulation, since we're not really delaying the second DMA. - One write that should have happened in the first DMA will not happen. However, - since that byte will be overwritten by the second DMA before it can actually be - read, it doesn't actually matter. */ - gb->is_dma_restarting = true; - } - gb->dma_cycles = -7; - gb->dma_current_dest = 0; - gb->dma_current_src = value << 8; - gb->dma_steps_left = 0xa0; - } - /* else { what? } */ - - return; - case GB_IO_SVBK: - if (!gb->cgb_mode) { - return; - } - gb->cgb_ram_bank = value & 0x7; - if (!gb->cgb_ram_bank) { - gb->cgb_ram_bank++; - } - return; - case GB_IO_VBK: - if (!gb->cgb_mode) { - return; - } - gb->cgb_vram_bank = value & 0x1; - return; - - case GB_IO_BGPI: - case GB_IO_OBPI: - if (!gb->is_cgb) { - return; - } - gb->io_registers[addr & 0xFF] = value; - return; - case GB_IO_BGPD: - case GB_IO_OBPD: - if (!gb->cgb_mode && gb->boot_rom_finished) { - /* Todo: Due to the behavior of a broken Game & Watch Gallery 2 ROM on a real CGB. A proper test ROM - is required. */ - return; - } - uint8_t index_reg = (addr & 0xFF) - 1; - ((addr & 0xFF) == GB_IO_BGPD? - gb->background_palettes_data : - gb->sprite_palettes_data)[gb->io_registers[index_reg] & 0x3F] = value; - GB_palette_changed(gb, (addr & 0xFF) == GB_IO_BGPD, gb->io_registers[index_reg] & 0x3F); - if (gb->io_registers[index_reg] & 0x80) { - gb->io_registers[index_reg]++; - gb->io_registers[index_reg] |= 0x80; - } - return; - case GB_IO_KEY1: - if (!gb->is_cgb) { - return; - } - gb->io_registers[GB_IO_KEY1] = value; - return; - case GB_IO_HDMA1: - if (gb->cgb_mode) { - gb->hdma_current_src &= 0xF0; - gb->hdma_current_src |= value << 8; - } - return; - case GB_IO_HDMA2: - if (gb->cgb_mode) { - gb->hdma_current_src &= 0xFF00; - gb->hdma_current_src |= value & 0xF0; - } - return; - case GB_IO_HDMA3: - if (gb->cgb_mode) { - gb->hdma_current_dest &= 0xF0; - gb->hdma_current_dest |= value << 8; - } - return; - case GB_IO_HDMA4: - if (gb->cgb_mode) { - gb->hdma_current_dest &= 0x1F00; - gb->hdma_current_dest |= value & 0xF0; - } - return; - case GB_IO_HDMA5: - if (!gb->cgb_mode) return; - if ((value & 0x80) == 0 && gb->hdma_on_hblank) { - gb->hdma_on_hblank = false; - return; - } - gb->hdma_on = (value & 0x80) == 0; - gb->hdma_on_hblank = (value & 0x80) != 0; - if (gb->hdma_on_hblank && (gb->io_registers[GB_IO_STAT] & 3) == 0) { - gb->hdma_on = true; - gb->hdma_cycles = 0; - } - gb->io_registers[GB_IO_HDMA5] = value; - gb->hdma_steps_left = (gb->io_registers[GB_IO_HDMA5] & 0x7F) + 1; - /* Todo: Verify this. Gambatte's DMA tests require this. */ - if (gb->hdma_current_dest + (gb->hdma_steps_left << 4) > 0xFFFF) { - gb->hdma_steps_left = (0x10000 - gb->hdma_current_dest) >> 4; - } - gb->hdma_cycles = 0; - return; - - /* Todo: what happens when starting a transfer during a transfer? - What happens when starting a transfer during external clock? - */ - case GB_IO_SC: - if (!gb->cgb_mode) { - value |= 2; - } - gb->io_registers[GB_IO_SC] = value | (~0x83); - if ((value & 0x80) && (value & 0x1) ) { - gb->serial_length = gb->cgb_mode && (value & 2)? 128 : 4096; - /* Todo: This is probably incorrect for CGB's faster clock mode. */ - gb->serial_cycles &= 0xFF; - if (gb->serial_transfer_start_callback) { - gb->serial_transfer_start_callback(gb, gb->io_registers[GB_IO_SB]); - } - } - else { - gb->serial_length = 0; - } - return; - - case GB_IO_RP: { - if (!gb->is_cgb) { - return; - } - if ((value & 1) != (gb->io_registers[GB_IO_RP] & 1)) { - if (gb->infrared_callback) { - gb->infrared_callback(gb, value & 1, gb->cycles_since_ir_change); - gb->cycles_since_ir_change = 0; - } - } - gb->io_registers[GB_IO_RP] = value; - return; - } - - default: - if ((addr & 0xFF) >= GB_IO_NR10 && (addr & 0xFF) <= GB_IO_WAV_END) { - GB_apu_write(gb, addr & 0xFF, value); - return; - } - GB_log(gb, "Wrote %02x to %04x (HW Register)\n", value, addr); - return; - } - } - - if (addr == 0xFFFF) { - /* Interrupt mask */ - gb->interrupt_enable = value; - return; - } - - /* HRAM */ - gb->hram[addr - 0xFF80] = value; -} - - - -static GB_write_function_t * const write_map[] = -{ - write_mbc, write_mbc, write_mbc, write_mbc, /* 0XXX, 1XXX, 2XXX, 3XXX */ - write_mbc, write_mbc, write_mbc, write_mbc, /* 4XXX, 5XXX, 6XXX, 7XXX */ - write_vram, write_vram, /* 8XXX, 9XXX */ - write_mbc_ram, write_mbc_ram, /* AXXX, BXXX */ - write_ram, write_banked_ram, /* CXXX, DXXX */ - write_high_memory, write_high_memory, /* EXXX FXXX */ -}; - -void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (is_addr_in_dma_use(gb, addr)) { - /* Todo: What should happen? Will this affect DMA? Will data be written? What and where? */ - return; - } - write_map[addr >> 12](gb, addr, value); -} - -void GB_dma_run(GB_gameboy_t *gb) -{ - while (gb->dma_cycles >= 4 && gb->dma_steps_left) { - /* Todo: measure this value */ - gb->dma_cycles -= 4; - gb->dma_steps_left--; - gb->oam[gb->dma_current_dest++] = GB_read_memory(gb, gb->dma_current_src); - /* dma_current_src must be the correct value during GB_read_memory */ - gb->dma_current_src++; - if (!gb->dma_steps_left) { - gb->is_dma_restarting = false; - } - } -} - -void GB_hdma_run(GB_gameboy_t *gb) -{ - if (!gb->hdma_on) return; - while (gb->hdma_cycles >= 8) { - gb->hdma_cycles -= 8; - - for (uint8_t i = 0; i < 0x10; i++) { - GB_write_memory(gb, 0x8000 | (gb->hdma_current_dest++ & 0x1FFF), GB_read_memory(gb, (gb->hdma_current_src++))); - } - - if(--gb->hdma_steps_left == 0){ - gb->hdma_on = false; - gb->hdma_on_hblank = false; - gb->io_registers[GB_IO_HDMA5] &= 0x7F; - break; - } - if (gb->hdma_on_hblank) { - gb->hdma_on = false; - break; - } - } -} diff --git a/waterbox/sameboy/memory.h b/waterbox/sameboy/memory.h deleted file mode 100644 index 16b3b9220f..0000000000 --- a/waterbox/sameboy/memory.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef memory_h -#define memory_h -#include "gb.h" - -uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr); -void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value); -#ifdef GB_INTERNAL -void GB_dma_run(GB_gameboy_t *gb); -void GB_hdma_run(GB_gameboy_t *gb); -#endif - -#endif /* memory_h */ diff --git a/waterbox/sameboy/printer.c b/waterbox/sameboy/printer.c deleted file mode 100644 index e27d7d544d..0000000000 --- a/waterbox/sameboy/printer.c +++ /dev/null @@ -1,205 +0,0 @@ -#include "gb.h" -#include "../emulibc/emulibc.h" - -/* TODO: Emulation is VERY basic and assumes the ROM correctly uses the printer's interface. - Incorrect usage is not correctly emulated, as it's not well documented, nor do I - have my own GB Printer to figure it out myself. - - It also does not currently emulate communication timeout, which means that a bug - might prevent the printer operation until the GameBoy is restarted. - - Also, field mask values are assumed. */ - -// hackadoodle! we must not overflow the stack -static ECL_INVISIBLE uint32_t tmp_image[160 * 200]; - -static void handle_command(GB_gameboy_t *gb) -{ - - switch (gb->printer.command_id) { - case GB_PRINTER_INIT_COMMAND: - gb->printer.status = 0; - gb->printer.image_offset = 0; - break; - - case GB_PRINTER_START_COMMAND: - if (gb->printer.command_length == 4) { - gb->printer.status = 6; /* Printing */ - uint32_t *const image = tmp_image; - uint8_t palette = gb->printer.command_data[2]; - uint32_t colors[4] = {gb->rgb_encode_callback(gb, 0xff, 0xff, 0xff), - gb->rgb_encode_callback(gb, 0xaa, 0xaa, 0xaa), - gb->rgb_encode_callback(gb, 0x55, 0x55, 0x55), - gb->rgb_encode_callback(gb, 0x00, 0x00, 0x00)}; - for (unsigned i = 0; i < gb->printer.image_offset; i++) { - image[i] = colors[(palette >> (gb->printer.image[i] * 2)) & 3]; - } - - if (gb->printer.callback) { - gb->printer.callback(gb, image, gb->printer.image_offset / 160, - gb->printer.command_data[1] >> 4, gb->printer.command_data[1] & 7, - gb->printer.command_data[3] & 0x7F); - } - - gb->printer.image_offset = 0; - } - break; - - case GB_PRINTER_DATA_COMMAND: - if (gb->printer.command_length == GB_PRINTER_DATA_SIZE) { - gb->printer.image_offset %= sizeof(gb->printer.image); - gb->printer.status = 8; /* Received 0x280 bytes */ - - uint8_t *byte = gb->printer.command_data; - - for (unsigned row = 2; row--; ) { - for (unsigned tile_x = 0; tile_x < 160 / 8; tile_x++) { - for (unsigned y = 0; y < 8; y++, byte += 2) { - for (unsigned x_pixel = 0; x_pixel < 8; x_pixel++) { - gb->printer.image[gb->printer.image_offset + tile_x * 8 + x_pixel + y * 160] = - ((*byte) >> 7) | (((*(byte + 1)) >> 7) << 1); - (*byte) <<= 1; - (*(byte + 1)) <<= 1; - } - } - } - - gb->printer.image_offset += 8 * 160; - } - } - - case GB_PRINTER_NOP_COMMAND: - default: - break; - } -} - -static void serial_start(GB_gameboy_t *gb, uint8_t byte_received) -{ - gb->printer.byte_to_send = 0; - switch (gb->printer.command_state) { - case GB_PRINTER_COMMAND_MAGIC1: - if (byte_received != 0x88) { - return; - } - gb->printer.status &= ~1; - gb->printer.command_length = 0; - gb->printer.checksum = 0; - break; - - case GB_PRINTER_COMMAND_MAGIC2: - if (byte_received != 0x33) { - if (byte_received != 0x88) { - gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1; - } - return; - } - break; - - case GB_PRINTER_COMMAND_ID: - gb->printer.command_id = byte_received & 0xF; - break; - - case GB_PRINTER_COMMAND_COMPRESSION: - gb->printer.compression = byte_received & 1; - break; - - case GB_PRINTER_COMMAND_LENGTH_LOW: - gb->printer.length_left = byte_received; - break; - - case GB_PRINTER_COMMAND_LENGTH_HIGH: - gb->printer.length_left |= (byte_received & 3) << 8; - break; - - case GB_PRINTER_COMMAND_DATA: - if (gb->printer.command_length != GB_PRINTER_MAX_COMMAND_LENGTH) { - if (gb->printer.compression) { - if (!gb->printer.compression_run_lenth) { - gb->printer.compression_run_is_compressed = byte_received & 0x80; - gb->printer.compression_run_lenth = (byte_received & 0x7F) + 1 + gb->printer.compression_run_is_compressed; - } - else if (gb->printer.compression_run_is_compressed) { - while (gb->printer.compression_run_lenth) { - gb->printer.command_data[gb->printer.command_length++] = byte_received; - gb->printer.compression_run_lenth--; - if (gb->printer.command_length == GB_PRINTER_MAX_COMMAND_LENGTH) { - gb->printer.compression_run_lenth = 0; - } - } - } - else { - gb->printer.command_data[gb->printer.command_length++] = byte_received; - gb->printer.compression_run_lenth--; - } - } - else { - gb->printer.command_data[gb->printer.command_length++] = byte_received; - } - } - gb->printer.length_left--; - break; - - case GB_PRINTER_COMMAND_CHECKSUM_LOW: - gb->printer.checksum ^= byte_received; - break; - - case GB_PRINTER_COMMAND_CHECKSUM_HIGH: - gb->printer.checksum ^= byte_received << 8; - if (gb->printer.checksum) { - gb->printer.status |= 1; /* Checksum error*/ - gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1; - return; - } - break; - case GB_PRINTER_COMMAND_ACTIVE: - gb->printer.byte_to_send = 0x81; - break; - case GB_PRINTER_COMMAND_STATUS: - - if ((gb->printer.command_id & 0xF) == GB_PRINTER_INIT_COMMAND) { - /* Games expect INIT commands to return 0? */ - gb->printer.byte_to_send = 0; - } - else { - gb->printer.byte_to_send = gb->printer.status; - } - - /* Printing is done instantly, but let the game recieve a 6 (Printing) status at least once, for compatibility */ - if (gb->printer.status == 6) { - gb->printer.status = 4; /* Done */ - } - - gb->printer.command_state = GB_PRINTER_COMMAND_MAGIC1; - handle_command(gb); - return; - } - - if (gb->printer.command_state >= GB_PRINTER_COMMAND_ID && gb->printer.command_state < GB_PRINTER_COMMAND_CHECKSUM_LOW) { - gb->printer.checksum += byte_received; - } - - if (gb->printer.command_state != GB_PRINTER_COMMAND_DATA) { - gb->printer.command_state++; - } - - if (gb->printer.command_state == GB_PRINTER_COMMAND_DATA) { - if (gb->printer.length_left == 0) { - gb->printer.command_state++; - } - } - -} - -static uint8_t serial_end(GB_gameboy_t *gb) -{ - return gb->printer.byte_to_send; -} - -void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback) -{ - memset(&gb->printer, 0, sizeof(gb->printer)); - GB_set_serial_transfer_start_callback(gb, serial_start); - GB_set_serial_transfer_end_callback(gb, serial_end); - gb->printer.callback = callback; -} \ No newline at end of file diff --git a/waterbox/sameboy/printer.h b/waterbox/sameboy/printer.h deleted file mode 100644 index e5d9036a08..0000000000 --- a/waterbox/sameboy/printer.h +++ /dev/null @@ -1,59 +0,0 @@ -#ifndef printer_h -#define printer_h -#include -#include -#include "gb_struct_def.h" -#define GB_PRINTER_MAX_COMMAND_LENGTH 0x280 -#define GB_PRINTER_DATA_SIZE 0x280 - -typedef void (*GB_print_image_callback_t)(GB_gameboy_t *gb, - uint32_t *image, - uint8_t height, - uint8_t top_margin, - uint8_t bottom_margin, - uint8_t exposure); - - -typedef struct -{ - /* Communication state machine */ - - enum { - GB_PRINTER_COMMAND_MAGIC1, - GB_PRINTER_COMMAND_MAGIC2, - GB_PRINTER_COMMAND_ID, - GB_PRINTER_COMMAND_COMPRESSION, - GB_PRINTER_COMMAND_LENGTH_LOW, - GB_PRINTER_COMMAND_LENGTH_HIGH, - GB_PRINTER_COMMAND_DATA, - GB_PRINTER_COMMAND_CHECKSUM_LOW, - GB_PRINTER_COMMAND_CHECKSUM_HIGH, - GB_PRINTER_COMMAND_ACTIVE, - GB_PRINTER_COMMAND_STATUS, - } command_state : 8; - enum { - GB_PRINTER_INIT_COMMAND = 1, - GB_PRINTER_START_COMMAND = 2, - GB_PRINTER_DATA_COMMAND = 4, - GB_PRINTER_NOP_COMMAND = 0xF, - } command_id : 8; - bool compression; - uint16_t length_left; - uint8_t command_data[GB_PRINTER_MAX_COMMAND_LENGTH]; - uint16_t command_length; - uint16_t checksum; - uint8_t status; - uint8_t byte_to_send; - - uint8_t image[160 * 200]; - uint16_t image_offset; - - GB_print_image_callback_t callback; - - uint8_t compression_run_lenth; - bool compression_run_is_compressed; -} GB_printer_t; - - -void GB_connect_printer(GB_gameboy_t *gb, GB_print_image_callback_t callback); -#endif diff --git a/waterbox/sameboy/sgb.c b/waterbox/sameboy/sgb.c deleted file mode 100644 index 3917595931..0000000000 --- a/waterbox/sameboy/sgb.c +++ /dev/null @@ -1,1043 +0,0 @@ -#include "sgb.h" -#include -#include -#include -#include "snes_spc/spc.h" -#include "../emulibc/emulibc.h" - -#define utils_log printf - -const uint8_t iplrom[64] = { - /*ffc0*/ 0xcd, 0xef, //mov x,#$ef - /*ffc2*/ 0xbd, //mov sp,x - /*ffc3*/ 0xe8, 0x00, //mov a,#$00 - /*ffc5*/ 0xc6, //mov (x),a - /*ffc6*/ 0x1d, //dec x - /*ffc7*/ 0xd0, 0xfc, //bne $ffc5 - /*ffc9*/ 0x8f, 0xaa, 0xf4, //mov $f4,#$aa - /*ffcc*/ 0x8f, 0xbb, 0xf5, //mov $f5,#$bb - /*ffcf*/ 0x78, 0xcc, 0xf4, //cmp $f4,#$cc - /*ffd2*/ 0xd0, 0xfb, //bne $ffcf - /*ffd4*/ 0x2f, 0x19, //bra $ffef - /*ffd6*/ 0xeb, 0xf4, //mov y,$f4 - /*ffd8*/ 0xd0, 0xfc, //bne $ffd6 - /*ffda*/ 0x7e, 0xf4, //cmp y,$f4 - /*ffdc*/ 0xd0, 0x0b, //bne $ffe9 - /*ffde*/ 0xe4, 0xf5, //mov a,$f5 - /*ffe0*/ 0xcb, 0xf4, //mov $f4,y - /*ffe2*/ 0xd7, 0x00, //mov ($00)+y,a - /*ffe4*/ 0xfc, //inc y - /*ffe5*/ 0xd0, 0xf3, //bne $ffda - /*ffe7*/ 0xab, 0x01, //inc $01 - /*ffe9*/ 0x10, 0xef, //bpl $ffda - /*ffeb*/ 0x7e, 0xf4, //cmp y,$f4 - /*ffed*/ 0x10, 0xeb, //bpl $ffda - /*ffef*/ 0xba, 0xf6, //movw ya,$f6 - /*fff1*/ 0xda, 0x00, //movw $00,ya - /*fff3*/ 0xba, 0xf4, //movw ya,$f4 - /*fff5*/ 0xc4, 0xf4, //mov $f4,a - /*fff7*/ 0xdd, //mov a,y - /*fff8*/ 0x5d, //mov x,a - /*fff9*/ 0xd0, 0xdb, //bne $ffd6 - /*fffb*/ 0x1f, 0x00, 0x00, //jmp ($0000+x) - /*fffe*/ 0xc0, 0xff //reset vector location ($ffc0) -}; - -// the "reference clock" is tied to the GB cpu. 35112 of these should equal one GB LCD frame. -// it is always increasing and never resets/rebases - -const int refclocks_per_spc_sample = 67; // ~32055hz - -typedef struct -{ - // writes to FF00 - uint64_t last_write_time; // last write time relative to reference clock - uint8_t last_write_value; - - // recv packets - uint8_t read_index; // 0-127, index of the next bit read. if 255, not currently reading - uint8_t packet[16]; // a packet in the process of being formed - - uint8_t command[16 * 7]; // a command in the process of being formed - uint8_t expected_packets; // total number of packets expected for a command - uint8_t next_packet; // index of the next packet to be read - - // joypad reading - uint8_t joypad_index; // index of currently reading joypad - uint8_t num_joypads; // number of currently selected joypads (MLT_REQ) - uint8_t joypad_data[4]; // data for each joypad - uint8_t joypad_has_been_read; // state for advancing joypad_index. extermely weird; logic lifted from VBA and probably wrong - - // palettes - uint32_t palette[8][16]; - uint32_t auxpalette[512][4]; - - // border - uint8_t tiles[256][64]; // tiles stored in packed form - uint16_t tilemap[32 * 32]; - - // frame data - uint8_t frame[160 * 144]; // the most recent obtained full frame - uint32_t frozenframe[256 * 224]; // the most recent saved full frame (MASK_EN) - uint8_t attr[20 * 18]; // current attr map for the GB screen - uint8_t auxattr[45][20 * 18]; // 45 attr files - - // MASK_EN - uint8_t active_mask; // true if mask is currently being used - - // audio - SNES_SPC *spc; - uint64_t frame_start; // when the current audio frame started relative to reference clock - uint32_t clock_remainder; // number of reference clocks not sent to the SPC last frame - uint8_t sound_control[4]; // TODO... - - // transfers - uint32_t waiting_transfer; -#define TRN_NONE 0 -#define TRN_SOUND 1 -#define TRN_PAL 2 -#define TRN_CHR_LOW 3 -#define TRN_CHR_HI 4 -#define TRN_PCT 5 -#define TRN_ATTR 6 - int32_t transfer_countdown; // number of frames until transfer. not entirely accurate -} sgb_t; - -static sgb_t sgb; - -static uint32_t makecol(uint16_t c) -{ - return c >> 7 & 0xf8 | c >> 12 & 0x07 | c << 6 & 0xf800 | c << 1 & 0x0700 | c << 19 & 0xf80000 | c << 14 & 0x070000 | 0xff000000; -} - -static void cmd_trn(uint32_t which) -{ - if ((sgb.command[0] & 7) == 1) - { - if (sgb.waiting_transfer == TRN_NONE) - { - sgb.waiting_transfer = which; - sgb.transfer_countdown = 4; - } - else - { - utils_log("SGB: TRN already queued!\n"); - } - } - else - { - utils_log("SGB: cmd_trn bad length\n"); - } -} - -static void cmd_pal(int a, int b) -{ - if ((sgb.command[0] & 7) == 1) - { - uint32_t c[7]; - for (int i = 0; i < 7; i++) - c[i] = makecol(sgb.command[i * 2 + 1] | sgb.command[i * 2 + 2] << 8); - sgb.palette[0][0] = c[0]; - sgb.palette[1][0] = c[0]; - sgb.palette[2][0] = c[0]; - sgb.palette[3][0] = c[0]; - sgb.palette[a][1] = c[1]; - sgb.palette[a][2] = c[2]; - sgb.palette[a][3] = c[3]; - sgb.palette[b][1] = c[4]; - sgb.palette[b][2] = c[5]; - sgb.palette[b][3] = c[6]; - } - else - { - utils_log("SGB: cmd_pal bad length\n"); - } -} - -static void cmd_pal_set(void) -{ - if ((sgb.command[0] & 7) == 1) - { - int p0 = sgb.command[1] | sgb.command[2] << 8 & 0x100; - for (int i = 0; i < 4; i++) - { - int p = sgb.command[i * 2 + 1] | sgb.command[i * 2 + 2] << 8 & 0x100; - sgb.palette[i][0] = sgb.auxpalette[p0][0]; - sgb.palette[i][1] = sgb.auxpalette[p][1]; - sgb.palette[i][2] = sgb.auxpalette[p][2]; - sgb.palette[i][3] = sgb.auxpalette[p][3]; - } - if (sgb.command[9] & 0x80) // change attribute - { - int attr = sgb.command[9] & 0x3f; - if (attr >= 45) - attr = 44; - memcpy(sgb.attr, sgb.auxattr[attr], sizeof(sgb.attr)); - } - if (sgb.command[9] & 0x40) // cancel mask - { - sgb.active_mask = 0; - } - } - else - { - utils_log("SGB: cmd_pal bad length\n"); - } -} - -static void cmd_attr_blk() -{ - int nset = sgb.command[1]; - if (nset <= 0 || nset >= 19) - { - utils_log("SGB: cmd_attr_blk bad nset\n"); - return; - } - int npacket = (nset * 6 + 16) / 16; - if ((sgb.command[0] & 7) != npacket) - { - utils_log("SGB: cmd_attr_blk bad length\n"); - return; - } - for (int i = 0; i < nset; i++) - { - int ctrl = sgb.command[i * 6 + 2] & 7; - int pals = sgb.command[i * 6 + 3]; - int x1 = sgb.command[i * 6 + 4]; - int y1 = sgb.command[i * 6 + 5]; - int x2 = sgb.command[i * 6 + 6]; - int y2 = sgb.command[i * 6 + 7]; - int inside = ctrl & 1; - int line = ctrl & 2; - int outside = ctrl & 4; - int insidepal = pals & 3; - int linepal = pals >> 2 & 3; - int outsidepal = pals >> 4 & 3; - if (ctrl == 1) - { - ctrl = 3; - linepal = insidepal; - } - else if (ctrl == 4) - { - ctrl = 6; - linepal = outsidepal; - } - uint8_t *dst = sgb.attr; - for (int y = 0; y < 18; y++) - { - for (int x = 0; x < 20; x++) - { - if (outside && (x < x1 || x > x2 || y < y1 || y > y2)) - *dst = outsidepal; - else if (inside && x > x1 && x < x2 && y > y1 && y < y2) - *dst = insidepal; - else if (line) - *dst = linepal; - dst++; - } - } - } -} - -static void cmd_attr_lin() -{ - int nset = sgb.command[1]; - if (nset <= 0 || nset >= 111) - { - utils_log("SGB: cmd_attr_lin bad nset\n"); - return; - } - int npacket = (nset + 17) / 16; - if ((sgb.command[0] & 7) != npacket) - { - utils_log("SGB: cmd_attr_lin bad length\n"); - return; - } - for (int i = 0; i < nset; i++) - { - uint8_t v = sgb.command[i + 2]; - int line = v & 31; - int a = v >> 5 & 3; - if (v & 0x80) // horizontal - { - if (line > 17) - line = 17; - memset(sgb.attr + line * 20, a, 20); - } - else // vertical - { - if (line > 19) - line = 19; - uint8_t *dst = sgb.attr + line; - for (int i = 0; i < 18; i++, dst += 20) - dst[0] = a; - } - } -} - -static void cmd_attr_div() -{ - if ((sgb.command[0] & 7) == 1) - { - uint8_t v = sgb.command[1]; - - int c = v & 3; - int a = v >> 2 & 3; - int b = v >> 4 & 3; - - int pos = sgb.command[2]; - uint8_t *dst = sgb.attr; - if (v & 0x40) // horizontal - { - if (pos > 17) - pos = 17; - int i; - for (i = 0; i < pos; i++, dst += 20) - memset(dst, a, 20); - memset(dst, b, 20); - i++, dst += 20; - for (; i < 18; i++, dst += 20) - memset(dst, c, 20); - } - else // vertical - { - if (pos > 19) - pos = 19; - for (int j = 0; j < 18; j++) - { - int i; - for (i = 0; i < pos; i++) - *dst++ = a; - *dst++ = b; - i++; - for (; i < 20; i++) - *dst++ = c; - } - } - } - else - { - utils_log("SGB: cmd_attr_div bad length\n"); - } -} - -static void cmd_attr_chr() -{ - int x = sgb.command[1]; - int y = sgb.command[2]; - int n = sgb.command[3] | sgb.command[4] << 8; - if (n > 360) - { - utils_log("SGB: cmd_attr_chr bad n\n"); - return; - } - int npacket = (n + 87) / 64; - if ((sgb.command[0] & 7) < npacket) - { - utils_log("SGB: cmd_attr_chr bad length\n"); - return; - } - uint8_t *dst = sgb.attr; - if (x > 19) - x = 19; - if (y > 17) - y = 17; - int vertical = sgb.command[5]; - for (int i = 0; i < n; i++) - { - uint8_t v = sgb.command[i / 4 + 6]; - int a = v >> (2 * (3 - (i & 3))) & 3; - dst[y * 20 + x] = a; - if (vertical) - { - y++; - if (y == 18) - { - y = 0; - x++; - if (x == 20) - return; - } - } - else - { - x++; - if (x == 20) - { - x = 0; - y++; - if (y == 18) - return; - } - } - } -} - -static void cmd_attr_set() -{ - if ((sgb.command[0] & 7) == 1) - { - int attr = sgb.command[1] & 0x3f; - if (attr >= 45) - attr = 44; - memcpy(sgb.attr, sgb.auxattr[attr], sizeof(sgb.attr)); - if (sgb.command[1] & 0x40) - { - sgb.active_mask = 0; - } - } - else - { - utils_log("SGB: cmd_attr_set bad length\n"); - } -} - -static void cmd_mlt_req(void) -{ - if ((sgb.command[0] & 7) == 1) - { - switch (sgb.command[1] & 3) - { - case 0: - case 2: - sgb.num_joypads = 1; - sgb.joypad_index = 0; - break; - case 1: - sgb.num_joypads = 2; - sgb.joypad_index = 1; - break; - case 3: - sgb.num_joypads = 4; - sgb.joypad_index = 1; - break; - } - utils_log("SGB: %u joypads\n", sgb.num_joypads); - } - else - { - utils_log("SGB: cmd_mlt_req bad length\n"); - } -} - -static void cmd_mask(void) -{ - if ((sgb.command[0] & 7) == 1) - { - switch (sgb.command[1] & 3) - { - case 0: - sgb.active_mask = 0; - break; - case 1: - sgb.active_mask = 1; - break; - case 2: - case 3: - sgb.active_mask = 1; - for (int i = 0; i < 256 * 224; i++) - sgb.frozenframe[i] = sgb.palette[0][0]; - break; - } - } - else - { - utils_log("SGB: cmd_mask bad length\n"); - } -} - -static void cmd_sound(void) -{ - if ((sgb.command[0] & 7) == 1) - { - sgb.sound_control[1] = sgb.command[1]; - sgb.sound_control[2] = sgb.command[2]; - sgb.sound_control[3] = sgb.command[3]; - sgb.sound_control[0] = sgb.command[4]; - } - else - { - utils_log("SGB: cmd_sound bad length\n"); - } -} - -static void do_command(void) -{ - const int command = sgb.command[0] >> 3; - switch (command) - { - default: - utils_log("SGB: Unknown or unimplemented command %02xh\n", command); - break; - - case 0x00: // PAL01 - utils_log("SGB: PAL01\n"); - cmd_pal(0, 1); - break; - case 0x01: // PAL23 - utils_log("SGB: PAL23\n"); - cmd_pal(2, 3); - break; - case 0x02: // PAL03 - utils_log("SGB: PAL03\n"); - cmd_pal(0, 3); - break; - case 0x03: // PAL12 - utils_log("SGB: PAL12\n"); - cmd_pal(1, 2); - break; - case 0x0a: // PAL_SET - utils_log("SGB: PAL_SET\n"); - cmd_pal_set(); - break; - - case 0x04: // ATTR_BLK - utils_log("SGB: ATTR_BLK\n"); - cmd_attr_blk(); - break; - case 0x05: // ATTR_LIN - utils_log("SGB: ATTR_LIN\n"); - cmd_attr_lin(); - break; - case 0x06: // ATTR_DIV - utils_log("SGB: ATTR_DIV\n"); - cmd_attr_div(); - break; - case 0x07: // ATTR_CHR - utils_log("SGB: ATTR_CHR\n"); - cmd_attr_chr(); - break; - case 0x16: // ATTR_SET - utils_log("SGB: ATTR_SET\n"); - cmd_attr_set(); - break; - - case 0x17: // MASK_EN - utils_log("SGB: MASK_EN\n"); - cmd_mask(); - break; - - // unknown functions - case 0x0c: // ATRC_EN - utils_log("SGB: ATRC_EN??\n"); - break; - case 0x0d: // TEST_EN - utils_log("SGB: TEST_EN??\n"); - break; - case 0x0e: // ICON_EN - utils_log("SGB: ICON_EN??\n"); - break; - case 0x18: // OBJ_TRN - // no game used this - utils_log("SGB: OBJ_TRN??\n"); - break; - - // unimplementable functions - case 0x0f: // DATA_SND - // TODO: Is it possible for this (and DATA_TRN) to write data to - // memory areas used for the attribute file, etc? - // If so, do games do this? - utils_log("SGB: DATA_SND!! %02x:%02x%02x [%02x]\n", sgb.command[3], sgb.command[2], sgb.command[1], sgb.command[4]); - break; - case 0x10: // DATA_TRN - utils_log("SGB: DATA_TRN!!\n"); - break; - case 0x12: // JUMP - utils_log("SGB: JUMP!!\n"); - break; - - // joypad - case 0x11: // MLT_REQ - utils_log("SGB: MLT_REQ\n"); - cmd_mlt_req(); - break; - - // sound - case 0x08: // SOUND - utils_log("SGB: SOUND %02x %02x %02x %02x\n", sgb.command[1], sgb.command[2], sgb.command[3], sgb.command[4]); - cmd_sound(); - break; - - // all vram transfers - case 0x09: // SOU_TRN - utils_log("SGB: SOU_TRN\n"); - cmd_trn(TRN_SOUND); - break; - case 0x0b: // PAL_TRN - utils_log("SGB: PAL_TRN\n"); - cmd_trn(TRN_PAL); - break; - case 0x13: // CHR_TRN - utils_log("SGB: CHR_TRN\n"); - cmd_trn(sgb.command[1] & 1 ? TRN_CHR_HI : TRN_CHR_LOW); - break; - case 0x14: // PCT_TRN - utils_log("SGB: PCT_TRN\n"); - cmd_trn(TRN_PCT); - break; - case 0x15: // ATTR_TRN - utils_log("SGB: ATTR_TRN\n"); - cmd_trn(TRN_ATTR); - break; - } -} - -static void do_packet(void) -{ - memcpy(sgb.command + sgb.next_packet * 16, sgb.packet, sizeof(sgb.packet)); - sgb.next_packet++; - - if (sgb.expected_packets == 0) // not in the middle of a command - sgb.expected_packets = sgb.command[0] & 7; - - if (sgb.expected_packets == 0) // huh? - { - utils_log("SGB: zero packet command\n"); - sgb.expected_packets = 0; - sgb.next_packet = 0; - } - else if (sgb.next_packet == sgb.expected_packets) - { - do_command(); - sgb.expected_packets = 0; - sgb.next_packet = 0; - } -} - -int sgb_init(const uint8_t *spc, int length) -{ - memset(&sgb, 0, sizeof(sgb)); - sgb.read_index = 255; - sgb.num_joypads = 1; - sgb.palette[0][0] = 0xffffffff; - sgb.palette[0][1] = 0xffaaaaaa; - sgb.palette[0][2] = 0xff555555; - sgb.palette[0][3] = 0xff000000; - - sgb.spc = spc_new(); - spc_init_rom(sgb.spc, iplrom); - spc_reset(sgb.spc); - if (spc_load_spc(sgb.spc, spc, length) != NULL) - { - utils_log("SGB: Failed to load SPC\n"); - return 0; - } - - // make a scratch buffer in a predictable (not stack) place because spc stores multiple pointers to it - // which is kind of nasty... - int16_t *sound_buffer = alloc_invisible(4096 * sizeof(int16_t)); - - // the combination of the sameboy bootrom plus the built in SPC file we use means - // that the SPC doesn't finish its init fast enough for donkey kong, which starts poking - // data too early. it's just a combination of various HLE concerns not meshing... - spc_set_output(sgb.spc, sound_buffer, 4096); - for (int i = 0; i < 240; i++) - { - spc_end_frame(sgb.spc, 35104); - } - - return 1; -} - -void sgb_write_ff00(uint8_t val, uint64_t time) -{ - val &= 0x30; - - //utils_log("ZZ: %02x, %llu", val, time); - const int p14_fell = (val & 0x10) < (sgb.last_write_value & 0x10); - const int p15_fell = (val & 0x20) < (sgb.last_write_value & 0x20); - const int p14_rose = (val & 0x10) > (sgb.last_write_value & 0x10); - const int p15_rose = (val & 0x20) > (sgb.last_write_value & 0x20); - - if (val == 0) // reset command processing - { - sgb.read_index = 0; - memset(sgb.packet, 0, sizeof(sgb.packet)); - } - else if (sgb.read_index != 255) // currently reading a packet - { - if (p14_fell || p15_fell) - { - if (sgb.read_index == 128) // end of packet - { - if (p14_fell) - do_packet(); - else - utils_log("SGB: Stop bit not present\n"); - sgb.read_index = 255; - } - else - { - if (p15_fell) - { - int byte = sgb.read_index >> 3; - int bit = sgb.read_index & 7; - sgb.packet[byte] |= 1 << bit; - } - sgb.read_index++; - } - } - } - else // joypad processing - { - if (val == 0x10) - sgb.joypad_has_been_read |= 2; // reading P15 - if (val == 0x20) - sgb.joypad_has_been_read |= 1; // reading P14 - if (val == 0x30 && (p14_rose || p15_rose)) - { - if (sgb.joypad_has_been_read == 7) - { - sgb.joypad_has_been_read = 0; - sgb.joypad_index++; - sgb.joypad_index &= sgb.num_joypads - 1; - //utils_log("SGB: joypad index to %u", sgb.joypad_index); - } - else - { - sgb.joypad_has_been_read &= 3; // the other line must be asserted and a read must happen before joypad_index inc?? - } - } - } - - sgb.last_write_value = val; - sgb.last_write_time = time; -} - -uint8_t sgb_read_ff00(uint64_t time) -{ - uint8_t ret = sgb.last_write_value & 0xf0 | 0xc0; - const int p14 = !(ret & 0x10); - const int p15 = !(ret & 0x20); - const int ji = sgb.joypad_index; - - // TODO: is this "reset" correct? - sgb.joypad_has_been_read |= 4; // read occured - sgb.read_index = 255; - sgb.next_packet = 0; - sgb.expected_packets = 0; - - if (!p14 && !p15) - { - //utils_log("SGB: SCAN%u", ji); - // scan id - return ret | (15 - ji); - } - else - { - // get data - const uint8_t j = sgb.joypad_data[ji]; - if (p14) - ret |= j >> 4; - if (p15) - ret |= j & 0x0f; - //utils_log("SGB: READ%u %02x", ji, ret ^ 0x0f); - return ret ^ 0x0f; - } -} - -// for each of 4 joypads: -// 7......0 -// DULRSsBA -void sgb_set_controller_data(const uint8_t *buttons) -{ - memcpy(sgb.joypad_data, buttons, sizeof(sgb.joypad_data)); -} - -static void trn_sound(const uint8_t *data) -{ - const uint8_t *const dataend = data + 0x10000; - uint8_t *const dst = spc_get_ram(sgb.spc); - - while (1) - { - if (data + 4 > dataend) - { - utils_log("TRN_SOUND header overflow\n"); - break; - } - int len = data[0] | data[1] << 8; - int addr = data[2] | data[3] << 8; - if (!len) - { - utils_log("TRN_SOUND END %04x\n", addr); - break; - } - data += 4; - if (data + len > dataend) - { - utils_log("TRN_SOUND src overflow\n"); - break; - } - if (addr + len >= 0x10000) - { - utils_log("TRN_SOUND dst overflow\n"); - return; - } - utils_log("TRN_SOUND addr %04x len %04x\n", addr, len); - memcpy(dst + addr, data, len); - data += len; - } -} - -static void trn_pal(const uint8_t *data) -{ - const uint16_t *src = (const uint16_t *)data; - uint32_t *dst = sgb.auxpalette[0]; - for (int i = 0; i < 2048; i++) - dst[i] = makecol(src[i]); -} - -static void trn_attr(const uint8_t *data) -{ - uint8_t *dst = sgb.auxattr[0]; - for (int n = 0; n < 45 * 90; n++) - { - uint8_t s = *data++; - *dst++ = s >> 6 & 3; - *dst++ = s >> 4 & 3; - *dst++ = s >> 2 & 3; - *dst++ = s >> 0 & 3; - } -} - -static void trn_pct(const uint8_t *data) -{ - memcpy(sgb.tilemap, data, sizeof(sgb.tilemap)); - const uint16_t *palettes = (const uint16_t *)(data + sizeof(sgb.tilemap)); - uint32_t *dst = sgb.palette[4]; - for (int i = 0; i < 64; i++) - dst[i] = makecol(palettes[i]); -} - -static void trn_chr(const uint8_t *data, int bank) -{ - uint8_t *dst = sgb.tiles[128 * bank]; - for (int n = 0; n < 128; n++) - { - for (int y = 0; y < 8; y++) - { - int a = data[0]; - int b = data[1] << 1; - int c = data[16] << 2; - int d = data[17] << 3; - for (int x = 7; x >= 0; x--) - { - dst[x] = a & 1 | b & 2 | c & 4 | d & 8; - a >>= 1; - b >>= 1; - c >>= 1; - d >>= 1; - } - dst += 8; - data += 2; - } - data += 16; - } -} - -static void do_vram_transfer(void) -{ - uint8_t vram[4096]; - for (int tilenum = 0; tilenum < 256; tilenum++) - { - const int ty = tilenum / 20; - const int tx = tilenum % 20; - const uint8_t *src = sgb.frame + ty * 8 * 160 + tx * 8; - uint8_t *dst = vram + 16 * tilenum; - for (int j = 0; j < 8; j++) - { - uint32_t a = 0, b = 0; - a |= (src[7] & 1) << 0; - a |= (src[6] & 1) << 1; - a |= (src[5] & 1) << 2; - a |= (src[4] & 1) << 3; - a |= (src[3] & 1) << 4; - a |= (src[2] & 1) << 5; - a |= (src[1] & 1) << 6; - a |= (src[0] & 1) << 7; - - b |= (src[7] & 2) >> 1; - b |= (src[6] & 2) << 0; - b |= (src[5] & 2) << 1; - b |= (src[4] & 2) << 2; - b |= (src[3] & 2) << 3; - b |= (src[2] & 2) << 4; - b |= (src[1] & 2) << 5; - b |= (src[0] & 2) << 6; - *dst++ = a; - *dst++ = b; - src += 160; - } - } - - switch (sgb.waiting_transfer) - { - case TRN_SOUND: - trn_sound(vram); - break; - case TRN_PAL: - trn_pal(vram); - break; - case TRN_CHR_LOW: - trn_chr(vram, 0); - break; - case TRN_CHR_HI: - trn_chr(vram, 1); - break; - case TRN_PCT: - trn_pct(vram); - break; - case TRN_ATTR: - trn_attr(vram); - break; - } -} - -static void sgb_render_frame_gb(uint32_t *vbuff) -{ - const uint8_t *attr = sgb.attr; - const uint8_t *src = sgb.frame; - uint32_t *dst = vbuff + ((224 - 144) / 2 * 256 + (256 - 160) / 2); - - for (int j = 0; j < 144; j++) - { - const uint8_t *attr_line = attr + j / 8 * 20; - for (int i = 0; i < 160; i++) - { - const int attr_index = i / 8; - *dst++ = sgb.palette[attr_line[attr_index]][*src++]; - } - dst += 256 - 160; - } -} - -static void draw_tile(uint16_t entry, uint32_t *dest) -{ - const uint8_t *tile = sgb.tiles[entry & 0xff]; - const uint32_t *palette = sgb.palette[entry >> 10 & 7]; - int hflip = entry & 0x4000; - int vflip = entry & 0x8000; - int hinc, vinc; - if (hflip) - { - hinc = -1; - dest += 7; - } - else - { - hinc = 1; - } - if (vflip) - { - vinc = -256; - dest += 7 * 256; - } - else - { - vinc = 256; - } - vinc -= 8 * hinc; - for (int y = 0; y < 8; y++, dest += vinc) - { - for (int x = 0; x < 8; x++, dest += hinc) - { - int c = *tile++; - if (c) - *dest = palette[c]; - } - } -} - -static void sgb_render_border(uint32_t *vbuff) -{ - const uint16_t *tilemap = sgb.tilemap; - for (int n = 0; n < 32 * 28; n++) - { - draw_tile(*tilemap++, vbuff); - vbuff += 8; - if ((n & 31) == 31) - vbuff += 256 * 7; - } -} - -// 160x144 32bpp pixel data -// assumed to contain exact pixel values 00, 55, aa, ff -void sgb_take_frame(uint32_t *vbuff) -{ - for (int i = 0; i < 160 * 144; i++) - { - sgb.frame[i] = 3 - (vbuff[i] >> 6 & 3); // 0, 1, 2, or 3 for each pixel - } - if (sgb.waiting_transfer != TRN_NONE) - { - if (!--sgb.transfer_countdown) - { - do_vram_transfer(); - sgb.waiting_transfer = TRN_NONE; - } - } - if (!sgb.active_mask) - { - // render the frame now - for (int i = 0; i < 256 * 224; i++) - sgb.frozenframe[i] = sgb.palette[0][0]; - sgb_render_frame_gb(sgb.frozenframe); - sgb_render_border(sgb.frozenframe); - } -} - -void sgb_render_frame(uint32_t *vbuff) -{ - memcpy(vbuff, sgb.frozenframe, sizeof(sgb.frozenframe)); -} - -void sgb_render_audio(uint64_t time, void (*callback)(int16_t l, int16_t r, uint64_t time)) -{ - int16_t sound_buffer[4096]; - uint32_t diff = time - sgb.frame_start + sgb.clock_remainder; - //utils_log("%ul", diff); - - uint32_t samples = diff / refclocks_per_spc_sample; - uint32_t new_remainder = diff % refclocks_per_spc_sample; - - spc_set_output(sgb.spc, sound_buffer, sizeof(sound_buffer) / sizeof(sound_buffer[0])); - int matched = 1; - for (int p = 0; p < 4; p++) - { - if (spc_read_port(sgb.spc, 0, p) != sgb.sound_control[p]) - matched = 0; - } - if (matched) // recived - { - sgb.sound_control[0] = 0; - sgb.sound_control[1] = 0; - sgb.sound_control[2] = 0; - } - else - { - utils_log("SPC: %02x %02x %02x %02x => %02x %02x %02x %02x\n", - spc_read_port(sgb.spc, 0, 0), - spc_read_port(sgb.spc, 0, 1), - spc_read_port(sgb.spc, 0, 2), - spc_read_port(sgb.spc, 0, 3), - sgb.sound_control[0], - sgb.sound_control[1], - sgb.sound_control[2], - sgb.sound_control[3]); - } - for (int p = 0; p < 4; p++) - { - spc_write_port(sgb.spc, 0, p, sgb.sound_control[p]); - } - - spc_end_frame(sgb.spc, samples * 32); - - uint64_t t = sgb.frame_start + refclocks_per_spc_sample - sgb.clock_remainder; - for (int i = 0; i < samples; i++, t += refclocks_per_spc_sample) - callback(sound_buffer[i * 2], sound_buffer[i * 2 + 1], t); - - sgb.frame_start = time; - sgb.clock_remainder = new_remainder; -} diff --git a/waterbox/sameboy/sgb.h b/waterbox/sameboy/sgb.h deleted file mode 100644 index 35e76dcad9..0000000000 --- a/waterbox/sameboy/sgb.h +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once -#include - -// whenever a time is asked for, it is relative to a clock that ticks 35112 times -// per nominal frame on the GB lcd, starts at 0 when emulation begins, and never resets/rebases - -// write to MMIO ff00. only bits 4 and 5 are used -void sgb_write_ff00(uint8_t val, uint64_t time); - -// read from MMIO ff00. supplies data for all 8 bits -uint8_t sgb_read_ff00(uint64_t time); - -// set controller data to be used by subsequent controller reads -// buttons[0] = controller 1, buttons[3] = controller 4 -// 7......0 -// DULRSsBA -void sgb_set_controller_data(const uint8_t* buttons); - -// initialize the SGB module. pass an SPC file that results from the real S-CPU initialization, -// and the length of that file -int sgb_init(const uint8_t* spc, int length); - -// call whenever the gameboy has finished producing a video frame -// data is 32bpp 160x144 screen data. for each pixel: -//31 7 0 -// xxxxxxxx xxxxxxxx xxxxxxxx DDxxxxxx -- DD = 0, 1, 2, or 3. x = don't care -void sgb_take_frame(uint32_t* vbuff); - -// copy the finished video frame to an output buffer. pixel format is 32bpp xrgb -// can be called at any time, including right after sgb_take_frame -void sgb_render_frame(uint32_t* vbuff); - -// call to finish a frame's worth of audio. should be called once every 35112 time units (some jitter is OK) -// callback will be called with L and R sample values for various time points -// between the last time sgb_render_audio was called and now -void sgb_render_audio(uint64_t time, void(*callback)(int16_t l, int16_t r, uint64_t time)); diff --git a/waterbox/sameboy/snes_spc/SNES_SPC.cpp b/waterbox/sameboy/snes_spc/SNES_SPC.cpp deleted file mode 100644 index fb3b147ae8..0000000000 --- a/waterbox/sameboy/snes_spc/SNES_SPC.cpp +++ /dev/null @@ -1,564 +0,0 @@ -// Core SPC emulation: CPU, timers, SMP registers, memory - -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "SNES_SPC.h" - -#include - -/* 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 - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - - -//// Timers - -#if SPC_DISABLE_TEMPO - #define TIMER_DIV( t, n ) ((n) >> t->prescaler) - #define TIMER_MUL( t, n ) ((n) << t->prescaler) -#else - #define TIMER_DIV( t, n ) ((n) / t->prescaler) - #define TIMER_MUL( t, n ) ((n) * t->prescaler) -#endif - -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; - - 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 ) - dsp.write( REGS [r_dspaddr], data ); - 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 - -// divided into multiple functions to keep rarely-used functionality separate -// so often-used functionality can be optimized better by compiler - -// If write isn't preceded by read, data has this added to it -int const no_read_before_write = 0x2000; - -void SNES_SPC::cpu_write_smp_reg_( int data, rel_time_t time, int addr ) -{ - switch ( addr ) - { - case r_t0target: - case r_t1target: - case r_t2target: { - Timer* t = &m.timers [addr - r_t0target]; - int period = IF_0_THEN_256( data ); - if ( t->period != period ) - { - t = run_timer( t, time ); - #if SPC_MORE_ACCURACY - // Insane behavior when target is written just after counter is - // clocked and counter matches new period and new period isn't 1, 2, 4, or 8 - if ( t->divider == (period & 0xFF) && - t->next_time == time + TIMER_MUL( t, 1 ) && - ((period - 1) | ~0x0F) & period ) - { - //dprintf( "SPC pathological timer target write\n" ); - - // If the period is 3, 5, or 9, there's a probability this behavior won't occur, - // based on the previous period - int prob = 0xFF; - int old_period = t->period & 0xFF; - if ( period == 3 ) prob = glitch_probs [0] [old_period]; - if ( period == 5 ) prob = glitch_probs [1] [old_period]; - if ( period == 9 ) prob = glitch_probs [2] [old_period]; - - // The glitch suppresses incrementing of one of the counter bits, based on - // the lowest set bit in the new period - int b = 1; - while ( !(period & b) ) - b <<= 1; - - if ( (rand() >> 4 & 0xFF) <= prob ) - t->divider = (t->divider - b) & 0xFF; - } - #endif - t->period = period; - } - break; - } - - case r_t0out: - case r_t1out: - case r_t2out: - if ( !SPC_MORE_ACCURACY ) - dprintf( "SPC wrote to counter %d\n", (int) addr - r_t0out ); - - if ( data < no_read_before_write / 2 ) - run_timer( &m.timers [addr - r_t0out], time - 1 )->counter = 0; - break; - - // Registers that act like RAM - case 0x8: - case 0x9: - REGS_IN [addr] = (uint8_t) data; - break; - - case r_test: - if ( (uint8_t) data != 0x0A ) - dprintf( "SPC wrote to test register\n" ); - break; - - case r_control: - // port clears - if ( data & 0x10 ) - { - REGS_IN [r_cpuio0] = 0; - REGS_IN [r_cpuio1] = 0; - } - if ( data & 0x20 ) - { - REGS_IN [r_cpuio2] = 0; - REGS_IN [r_cpuio3] = 0; - } - - // timers - { - for ( int i = 0; i < timer_count; i++ ) - { - Timer* t = &m.timers [i]; - int enabled = data >> i & 1; - if ( t->enabled != enabled ) - { - t = run_timer( t, time ); - t->enabled = enabled; - if ( enabled ) - { - t->divider = 0; - t->counter = 0; - } - } - } - } - enable_rom( data & 0x80 ); - break; - } -} - -void SNES_SPC::cpu_write_smp_reg( int data, rel_time_t time, int addr ) -{ - if ( addr == r_dspdata ) // 99% - dsp_write( data, time ); - else - cpu_write_smp_reg_( data, time, addr ); -} - -void SNES_SPC::cpu_write_high( int data, int i, rel_time_t time ) -{ - if ( i < rom_size ) - { - m.hi_ram [i] = (uint8_t) data; - if ( m.rom_enabled ) - RAM [i + rom_addr] = m.rom [i]; // restore overwritten ROM - } - else - { - assert( RAM [i + rom_addr] == (uint8_t) data ); - RAM [i + rom_addr] = cpu_pad_fill; // restore overwritten padding - cpu_write( data, i + rom_addr - 0x10000, time ); - } -} - -int const bits_in_int = CHAR_BIT * sizeof (int); - -void SNES_SPC::cpu_write( int data, int addr, rel_time_t time ) -{ - MEM_ACCESS( time, addr ) - - // RAM - RAM [addr] = (uint8_t) data; - int reg = addr - 0xF0; - if ( reg >= 0 ) // 64% - { - // $F0-$FF - if ( reg < reg_count ) // 87% - { - REGS [reg] = (uint8_t) data; - - // Ports - #ifdef SPC_PORT_WRITE_HOOK - if ( (unsigned) (reg - r_cpuio0) < port_count ) - SPC_PORT_WRITE_HOOK( m.spc_time + time, (reg - r_cpuio0), - (uint8_t) data, ®S [r_cpuio0] ); - #endif - - // Registers other than $F2 and $F4-$F7 - //if ( reg != 2 && reg != 4 && reg != 5 && reg != 6 && reg != 7 ) - // TODO: this is a bit on the fragile side - if ( ((~0x2F00 << (bits_in_int - 16)) << reg) < 0 ) // 36% - cpu_write_smp_reg( data, time, reg ); - } - // High mem/address wrap-around - else - { - reg -= rom_addr - 0xF0; - if ( reg >= 0 ) // 1% in IPL ROM area or address wrapped around - cpu_write_high( data, reg, time ); - } - } -} - - -//// CPU read - -inline int SNES_SPC::cpu_read_smp_reg( int reg, rel_time_t time ) -{ - int result = REGS_IN [reg]; - reg -= r_dspaddr; - // DSP addr and data - if ( (unsigned) reg <= 1 ) // 4% 0xF2 and 0xF3 - { - result = REGS [r_dspaddr]; - if ( (unsigned) reg == 1 ) - result = dsp_read( time ); // 0xF3 - } - return result; -} - -int SNES_SPC::cpu_read( int addr, rel_time_t time ) -{ - MEM_ACCESS( time, addr ) - - // RAM - int result = RAM [addr]; - int reg = addr - 0xF0; - if ( reg >= 0 ) // 40% - { - reg -= 0x10; - if ( (unsigned) reg >= 0xFF00 ) // 21% - { - reg += 0x10 - r_t0out; - - // Timers - if ( (unsigned) reg < timer_count ) // 90% - { - Timer* t = &m.timers [reg]; - if ( time >= t->next_time ) - t = run_timer_( t, time ); - result = t->counter; - t->counter = 0; - } - // Other registers - else if ( reg < 0 ) // 10% - { - result = cpu_read_smp_reg( reg + r_t0out, time ); - } - else // 1% - { - assert( reg + (r_t0out + 0xF0 - 0x10000) < 0x100 ); - result = cpu_read( reg + (r_t0out + 0xF0 - 0x10000), time ); - } - } - } - - return result; -} - - -//// Run - -// Prefix and suffix for CPU emulator function -#define SPC_CPU_RUN_FUNC \ -BOOST::uint8_t* SNES_SPC::run_until_( time_t end_time )\ -{\ - rel_time_t rel_time = m.spc_time - end_time;\ - assert( rel_time <= 0 );\ - m.spc_time = end_time;\ - m.dsp_time += rel_time;\ - m.timers [0].next_time += rel_time;\ - m.timers [1].next_time += rel_time;\ - m.timers [2].next_time += rel_time; - -#define SPC_CPU_RUN_FUNC_END \ - m.spc_time += rel_time;\ - m.dsp_time -= rel_time;\ - m.timers [0].next_time -= rel_time;\ - m.timers [1].next_time -= rel_time;\ - m.timers [2].next_time -= rel_time;\ - assert( m.spc_time <= end_time );\ - return ®S [r_cpuio0];\ -} - -int const cpu_lag_max = 12 - 1; // DIV YA,X takes 12 clocks - -void SNES_SPC::end_frame( time_t end_time ) -{ - // Catch CPU up to as close to end as possible. If final instruction - // would exceed end, does NOT execute it and leaves m.spc_time < end. - if ( end_time > m.spc_time ) - run_until_( end_time ); - - m.spc_time -= end_time; - m.extra_clocks += end_time; - - // Greatest number of clocks early that emulation can stop early due to - // not being able to execute current instruction without going over - // allowed time. - assert( -cpu_lag_max <= m.spc_time && m.spc_time <= 0 ); - - // Catch timers up to CPU - for ( int i = 0; i < timer_count; i++ ) - run_timer( &m.timers [i], 0 ); - - // Catch DSP up to CPU - if ( m.dsp_time < 0 ) - { - RUN_DSP( 0, max_reg_time ); - } - - // Save any extra samples beyond what should be generated - if ( m.buf_begin ) - save_extra(); -} - -// Inclusion here allows static memory access functions and better optimization -#include "SPC_CPU.h" diff --git a/waterbox/sameboy/snes_spc/SNES_SPC.h b/waterbox/sameboy/snes_spc/SNES_SPC.h deleted file mode 100644 index fb1ad18a45..0000000000 --- a/waterbox/sameboy/snes_spc/SNES_SPC.h +++ /dev/null @@ -1,284 +0,0 @@ -// SNES SPC-700 APU emulator - -// snes_spc 0.9.0 -#ifndef SNES_SPC_H -#define SNES_SPC_H - -#include "SPC_DSP.h" -#include "blargg_endian.h" -#include - -struct SNES_SPC { -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 ); - - uint8_t* get_ram(); - -// Sound control - - // Mutes voices corresponding to non-zero bits in mask (issues repeated KOFF events). - // Reduces emulation accuracy. - enum { voice_count = 8 }; - void mute_voices( int mask ); - - // If true, prevents channels and global volumes from being phase-negated. - // Only supported by fast DSP. - void disable_surround( bool disable = true ); - - // 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(); - - // Plays for count samples and write samples to out. Discards samples if out - // is NULL. Count must be a multiple of 2 since output is stereo. - blargg_err_t play( int count, sample_t* out ); - - // Skips count samples. Several times faster than play() when using fast DSP. - blargg_err_t skip( int count ); - -// State save/load (only available with accurate DSP) - -#if !SPC_NO_COPY_STATE_FUNCS - // Saves/loads state - enum { state_size = 67 * 1024L }; // maximum space needed when saving - typedef SPC_DSP::copy_func_t copy_func_t; - void copy_state( unsigned char** io, copy_func_t ); - - // Writes minimal header to spc_out - static void init_header( void* spc_out ); - - // 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: - 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]; - - struct - { - int pc; - int a; - int x; - int y; - int psw; - int sp; - } cpu_regs; - - rel_time_t dsp_time; - time_t spc_time; - bool echo_accessed; - - int tempo; - int skipped_kon; - int skipped_koff; - const char* cpu_error; - - int extra_clocks; - sample_t* buf_begin; - sample_t const* buf_end; - sample_t* extra_pos; - sample_t extra_buf [extra_size]; - - int rom_enabled; - uint8_t rom [rom_size]; - uint8_t hi_ram [rom_size]; - - unsigned char cycle_table [256]; - - struct - { - // padding to neutralize address overflow - union { - uint8_t padding1 [0x100]; - uint16_t align; // makes compiler align data for 16-bit access - } padding1 [1]; - uint8_t ram [0x10000]; - uint8_t padding2 [0x100]; - } ram; - }; - state_t m; - - enum { rom_addr = 0xFFC0 }; - - enum { skipping_time = 127 }; - - // Value that padding should be filled with - enum { cpu_pad_fill = 0xFF }; - - enum { - r_test = 0x0, r_control = 0x1, - r_dspaddr = 0x2, r_dspdata = 0x3, - r_cpuio0 = 0x4, r_cpuio1 = 0x5, - r_cpuio2 = 0x6, r_cpuio3 = 0x7, - r_f8 = 0x8, r_f9 = 0x9, - r_t0target = 0xA, r_t1target = 0xB, r_t2target = 0xC, - r_t0out = 0xD, r_t1out = 0xE, r_t2out = 0xF - }; - - void timers_loaded(); - void enable_rom( int enable ); - void reset_buf(); - void save_extra(); - void load_regs( uint8_t const in [reg_count] ); - void ram_loaded(); - void regs_loaded(); - void reset_time_regs(); - void reset_common( int timer_counter_init ); - - Timer* run_timer_ ( Timer* t, rel_time_t ); - Timer* run_timer ( Timer* t, rel_time_t ); - int dsp_read ( rel_time_t ); - void dsp_write ( int data, rel_time_t ); - void cpu_write_smp_reg_( int data, rel_time_t, int addr ); - void cpu_write_smp_reg ( int data, rel_time_t, int addr ); - void cpu_write_high ( int data, int i, rel_time_t ); - void cpu_write ( int data, int addr, rel_time_t ); - int cpu_read_smp_reg ( int i, rel_time_t ); - int cpu_read ( int addr, rel_time_t ); - unsigned CPU_mem_bit ( uint8_t const* pc, rel_time_t ); - - bool check_echo_access ( int addr ); - uint8_t* run_until_( time_t end_time ); - - struct spc_file_t - { - char signature [signature_size]; - uint8_t has_id666; - uint8_t version; - uint8_t pcl, pch; - uint8_t a; - uint8_t x; - uint8_t y; - uint8_t psw; - uint8_t sp; - char text [212]; - uint8_t ram [0x10000]; - uint8_t dsp [128]; - uint8_t unused [0x40]; - uint8_t ipl_rom [0x40]; - }; - - static char const signature [signature_size + 1]; - - void save_regs( uint8_t out [reg_count] ); -}; - -#include - -inline uint8_t* SNES_SPC::get_ram() { return m.ram.ram; } - -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 ); } - -#if !SPC_NO_COPY_STATE_FUNCS -inline bool SNES_SPC::check_kon() { return dsp.check_kon(); } -#endif - -#endif diff --git a/waterbox/sameboy/snes_spc/SNES_SPC_misc.cpp b/waterbox/sameboy/snes_spc/SNES_SPC_misc.cpp deleted file mode 100644 index 87288ab8ee..0000000000 --- a/waterbox/sameboy/snes_spc/SNES_SPC_misc.cpp +++ /dev/null @@ -1,380 +0,0 @@ -// SPC emulation support: init, sample buffering, reset, SPC loading - -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "SNES_SPC.h" - -#include - -/* 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 ); - - 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 0; -} - -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 SPC_DISABLE_TEMPO - m.timers [2].prescaler = timer2_shift; - m.timers [1].prescaler = timer2_shift + other_shift; - m.timers [0].prescaler = timer2_shift + other_shift; - #else - 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; - #endif -} - -// 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 = 0; - 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 0; -} - -void SNES_SPC::clear_echo() -{ - if ( !(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 ); - } -} - - -//// 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 = 0; - - dsp.set_output( 0, 0 ); -} - -void SNES_SPC::set_output( sample_t* out, int size ) -{ - require( (size & 1) == 0 ); // size must be even - - m.extra_clocks &= clocks_per_sample - 1; - if ( out ) - { - sample_t const* out_end = out + size; - m.buf_begin = out; - m.buf_end = out_end; - - // Copy extra to output - sample_t const* in = m.extra_buf; - while ( in < m.extra_pos && out < out_end ) - *out++ = *in++; - - // Handle output being full already - if ( out >= out_end ) - { - // Have DSP write to remaining extra space - out = dsp.extra(); - out_end = &dsp.extra() [extra_size]; - - // Copy any remaining extra samples as if DSP wrote them - while ( in < m.extra_pos ) - *out++ = *in++; - assert( out <= out_end ); - } - - dsp.set_output( out, out_end - out ); - } - else - { - 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 = 0; - return err; -} - -blargg_err_t SNES_SPC::skip( int count ) -{ - #if SPC_LESS_ACCURATE - if ( count > 2 * sample_rate * 2 ) - { - set_output( 0, 0 ); - - // Skip a multiple of 4 samples - time_t end = count; - count = (count & 3) + 1 * sample_rate * 2; - end = (end - count) * (clocks_per_sample / 2); - - m.skipped_kon = 0; - m.skipped_koff = 0; - - // Preserve DSP and timer synchronization - // TODO: verify that this really preserves it - int old_dsp_time = m.dsp_time + m.spc_time; - m.dsp_time = end - m.spc_time + skipping_time; - end_frame( end ); - m.dsp_time = m.dsp_time - skipping_time + old_dsp_time; - - dsp.write( SPC_DSP::r_koff, m.skipped_koff & ~m.skipped_kon ); - dsp.write( SPC_DSP::r_kon , m.skipped_kon ); - clear_echo(); - } - #endif - - return play( count, 0 ); -} diff --git a/waterbox/sameboy/snes_spc/SNES_SPC_state.cpp b/waterbox/sameboy/snes_spc/SNES_SPC_state.cpp deleted file mode 100644 index a8052b6587..0000000000 --- a/waterbox/sameboy/snes_spc/SNES_SPC_state.cpp +++ /dev/null @@ -1,129 +0,0 @@ -// SPC emulation state save/load: copy_state(), save_spc() -// Separate file to avoid linking in unless needed - -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "SNES_SPC.h" - -#if !SPC_NO_COPY_STATE_FUNCS - -#include - -/* 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]) - -void SNES_SPC::save_regs( uint8_t out [reg_count] ) -{ - // Use current timer counter values - for ( int i = 0; i < timer_count; i++ ) - out [r_t0out + i] = m.timers [i].counter; - - // Last written values - memcpy( out, REGS, r_t0out ); -} - -void SNES_SPC::init_header( void* spc_out ) -{ - spc_file_t* const spc = (spc_file_t*) spc_out; - - spc->has_id666 = 26; // has none - spc->version = 30; - memcpy( spc, signature, sizeof spc->signature ); - memset( spc->text, 0, sizeof spc->text ); -} - -void SNES_SPC::save_spc( void* spc_out ) -{ - spc_file_t* const spc = (spc_file_t*) spc_out; - - // CPU - spc->pcl = (uint8_t) (m.cpu_regs.pc >> 0); - spc->pch = (uint8_t) (m.cpu_regs.pc >> 8); - spc->a = m.cpu_regs.a; - spc->x = m.cpu_regs.x; - spc->y = m.cpu_regs.y; - spc->psw = m.cpu_regs.psw; - spc->sp = m.cpu_regs.sp; - - // RAM, ROM - memcpy( spc->ram, RAM, sizeof spc->ram ); - if ( m.rom_enabled ) - memcpy( spc->ram + rom_addr, m.hi_ram, sizeof m.hi_ram ); - memset( spc->unused, 0, sizeof spc->unused ); - memcpy( spc->ipl_rom, m.rom, sizeof spc->ipl_rom ); - - // SMP registers - save_regs( &spc->ram [0xF0] ); - int i; - for ( i = 0; i < port_count; i++ ) - spc->ram [0xF0 + r_cpuio0 + i] = REGS_IN [r_cpuio0 + i]; - - // DSP registers - for ( i = 0; i < SPC_DSP::register_count; i++ ) - spc->dsp [i] = dsp.read( i ); -} - -void SNES_SPC::copy_state( unsigned char** io, copy_func_t copy ) -{ - SPC_State_Copier copier( io, copy ); - - // Make state data more readable by putting 64K RAM, 16 SMP registers, - // then DSP (with its 128 registers) first - - // RAM - enable_rom( 0 ); // will get re-enabled if necessary in regs_loaded() below - copier.copy( RAM, 0x10000 ); - - { - // SMP registers - uint8_t out_ports [port_count]; - uint8_t regs [reg_count]; - memcpy( out_ports, ®S [r_cpuio0], sizeof out_ports ); - save_regs( regs ); - copier.copy( regs, sizeof regs ); - copier.copy( out_ports, sizeof out_ports ); - load_regs( regs ); - regs_loaded(); - memcpy( ®S [r_cpuio0], out_ports, sizeof out_ports ); - } - - // CPU registers - SPC_COPY( uint16_t, m.cpu_regs.pc ); - SPC_COPY( uint8_t, m.cpu_regs.a ); - SPC_COPY( uint8_t, m.cpu_regs.x ); - SPC_COPY( uint8_t, m.cpu_regs.y ); - SPC_COPY( uint8_t, m.cpu_regs.psw ); - SPC_COPY( uint8_t, m.cpu_regs.sp ); - copier.extra(); - - SPC_COPY( int16_t, m.spc_time ); - SPC_COPY( int16_t, m.dsp_time ); - - // DSP - dsp.copy_state( io, copy ); - - // Timers - for ( int i = 0; i < timer_count; i++ ) - { - Timer* t = &m.timers [i]; - SPC_COPY( int16_t, t->next_time ); - SPC_COPY( uint8_t, t->divider ); - copier.extra(); - } - copier.extra(); -} -#endif diff --git a/waterbox/sameboy/snes_spc/SPC_CPU.h b/waterbox/sameboy/snes_spc/SPC_CPU.h deleted file mode 100644 index 664fc4886e..0000000000 --- a/waterbox/sameboy/snes_spc/SPC_CPU.h +++ /dev/null @@ -1,1220 +0,0 @@ -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -/* Copyright (C) 2004-2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -//// Memory access - -#if SPC_MORE_ACCURACY - #define SUSPICIOUS_OPCODE( name ) ((void) 0) -#else - #define SUSPICIOUS_OPCODE( name ) dprintf( "SPC: suspicious opcode: " name "\n" ) -#endif - -#define CPU_READ( time, offset, addr )\ - cpu_read( addr, time + offset ) - -#define CPU_WRITE( time, offset, addr, data )\ - cpu_write( data, addr, time + offset ) - -#if SPC_MORE_ACCURACY - #define CPU_READ_TIMER( time, offset, addr, out )\ - { out = CPU_READ( time, offset, addr ); } - -#else - // timers are by far the most common thing read from dp - #define CPU_READ_TIMER( time, offset, addr_, out )\ - {\ - rel_time_t adj_time = time + offset;\ - int dp_addr = addr_;\ - int ti = dp_addr - (r_t0out + 0xF0);\ - if ( (unsigned) ti < timer_count )\ - {\ - Timer* t = &m.timers [ti];\ - if ( adj_time >= t->next_time )\ - t = run_timer_( t, adj_time );\ - out = t->counter;\ - t->counter = 0;\ - }\ - else\ - {\ - out = ram [dp_addr];\ - int i = dp_addr - 0xF0;\ - if ( (unsigned) i < 0x10 )\ - out = cpu_read_smp_reg( i, adj_time );\ - }\ - } -#endif - -#define TIME_ADJ( n ) (n) - -#define READ_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), (addr), out ) -#define READ( time, addr ) CPU_READ ( rel_time, TIME_ADJ(time), (addr) ) -#define WRITE( time, addr, data ) CPU_WRITE( rel_time, TIME_ADJ(time), (addr), (data) ) - -#define DP_ADDR( addr ) (dp + (addr)) - -#define READ_DP_TIMER( time, addr, out ) CPU_READ_TIMER( rel_time, TIME_ADJ(time), DP_ADDR( addr ), out ) -#define READ_DP( time, addr ) READ ( time, DP_ADDR( addr ) ) -#define WRITE_DP( time, addr, data ) WRITE( time, DP_ADDR( addr ), data ) - -#define READ_PROG16( addr ) GET_LE16( ram + (addr) ) - -#define SET_PC( n ) (pc = ram + (n)) -#define GET_PC() (pc - ram) -#define READ_PC( pc ) (*(pc)) -#define READ_PC16( pc ) GET_LE16( pc ) - -// TODO: remove non-wrapping versions? -#define SPC_NO_SP_WRAPAROUND 0 - -#define SET_SP( v ) (sp = ram + 0x101 + (v)) -#define GET_SP() (sp - 0x101 - ram) - -#if SPC_NO_SP_WRAPAROUND -#define PUSH16( v ) (sp -= 2, SET_LE16( sp, v )) -#define PUSH( v ) (void) (*--sp = (uint8_t) (v)) -#define POP( out ) (void) ((out) = *sp++) - -#else -#define PUSH16( data )\ -{\ - int addr = (sp -= 2) - ram;\ - if ( addr > 0x100 )\ - {\ - SET_LE16( sp, data );\ - }\ - else\ - {\ - ram [(uint8_t) addr + 0x100] = (uint8_t) data;\ - sp [1] = (uint8_t) (data >> 8);\ - sp += 0x100;\ - }\ -} - -#define PUSH( data )\ -{\ - *--sp = (uint8_t) (data);\ - if ( sp - ram == 0x100 )\ - sp += 0x100;\ -} - -#define POP( out )\ -{\ - out = *sp++;\ - if ( sp - ram == 0x201 )\ - {\ - out = sp [-0x101];\ - sp -= 0x100;\ - }\ -} - -#endif - -#define MEM_BIT( rel ) CPU_mem_bit( pc, rel_time + rel ) - -unsigned SNES_SPC::CPU_mem_bit( uint8_t const* pc, rel_time_t rel_time ) -{ - unsigned addr = READ_PC16( pc ); - unsigned t = READ( 0, addr & 0x1FFF ) >> (addr >> 13); - return t << 8 & 0x100; -} - -//// Status flag handling - -// Hex value in name to clarify code and bit shifting. -// Flag stored in indicated variable during emulation -int const n80 = 0x80; // nz -int const v40 = 0x40; // psw -int const p20 = 0x20; // dp -int const b10 = 0x10; // psw -int const h08 = 0x08; // psw -int const i04 = 0x04; // psw -int const z02 = 0x02; // nz -int const c01 = 0x01; // c - -int const nz_neg_mask = 0x880; // either bit set indicates N flag set - -#define GET_PSW( out )\ -{\ - out = psw & ~(n80 | p20 | z02 | c01);\ - out |= c >> 8 & c01;\ - out |= dp >> 3 & p20;\ - out |= ((nz >> 4) | nz) & n80;\ - if ( !(uint8_t) nz ) out |= z02;\ -} - -#define SET_PSW( in )\ -{\ - psw = in;\ - c = in << 8;\ - dp = in << 3 & 0x100;\ - nz = (in << 4 & 0x800) | (~in & z02);\ -} - -SPC_CPU_RUN_FUNC -{ - uint8_t* const ram = RAM; - int a = m.cpu_regs.a; - int x = m.cpu_regs.x; - int y = m.cpu_regs.y; - uint8_t const* pc; - uint8_t* sp; - int psw; - int c; - int nz; - int dp; - - SET_PC( m.cpu_regs.pc ); - SET_SP( m.cpu_regs.sp ); - SET_PSW( m.cpu_regs.psw ); - - goto loop; - - - // Main loop - -cbranch_taken_loop: - pc += *(BOOST::int8_t const*) pc; -inc_pc_loop: - pc++; -loop: -{ - unsigned opcode; - unsigned data; - - check( (unsigned) a < 0x100 ); - check( (unsigned) x < 0x100 ); - check( (unsigned) y < 0x100 ); - - opcode = *pc; - if ( (rel_time += m.cycle_table [opcode]) > 0 ) - goto out_of_time; - - #ifdef SPC_CPU_OPCODE_HOOK - SPC_CPU_OPCODE_HOOK( GET_PC(), opcode ); - #endif - /* - //SUB_CASE_COUNTER( 1 ); - #define PROFILE_TIMER_LOOP( op, addr, len )\ - if ( opcode == op )\ - {\ - int cond = (unsigned) ((addr) - 0xFD) < 3 &&\ - pc [len] == 0xF0 && pc [len+1] == 0xFE - len;\ - SUB_CASE_COUNTER( op && cond );\ - } - - PROFILE_TIMER_LOOP( 0xEC, GET_LE16( pc + 1 ), 3 ); - PROFILE_TIMER_LOOP( 0xEB, pc [1], 2 ); - PROFILE_TIMER_LOOP( 0xE4, pc [1], 2 ); - */ - - // TODO: if PC is at end of memory, this will get wrong operand (very obscure) - data = *++pc; - switch ( opcode ) - { - -// Common instructions - -#define BRANCH( cond )\ -{\ - pc++;\ - pc += (BOOST::int8_t) data;\ - if ( cond )\ - goto loop;\ - pc -= (BOOST::int8_t) data;\ - rel_time -= 2;\ - goto loop;\ -} - - case 0xF0: // BEQ - BRANCH( !(uint8_t) nz ) // 89% taken - - case 0xD0: // BNE - BRANCH( (uint8_t) nz ) - - case 0x3F:{// CALL - int old_addr = GET_PC() + 2; - SET_PC( READ_PC16( pc ) ); - PUSH16( old_addr ); - goto loop; - } - - case 0x6F:// RET - #if SPC_NO_SP_WRAPAROUND - { - SET_PC( GET_LE16( sp ) ); - sp += 2; - } - #else - { - int addr = sp - ram; - SET_PC( GET_LE16( sp ) ); - sp += 2; - if ( addr < 0x1FF ) - goto loop; - - SET_PC( sp [-0x101] * 0x100 + ram [(uint8_t) addr + 0x100] ); - sp -= 0x100; - } - #endif - goto loop; - - case 0xE4: // MOV a,dp - ++pc; - // 80% from timer - READ_DP_TIMER( 0, data, a = nz ); - goto loop; - - case 0xFA:{// MOV dp,dp - int temp; - READ_DP_TIMER( -2, data, temp ); - data = temp + no_read_before_write ; - } - // fall through - case 0x8F:{// MOV dp,#imm - int temp = READ_PC( pc + 1 ); - pc += 2; - - #if !SPC_MORE_ACCURACY - { - int i = dp + temp; - ram [i] = (uint8_t) data; - i -= 0xF0; - if ( (unsigned) i < 0x10 ) // 76% - { - REGS [i] = (uint8_t) data; - - // Registers other than $F2 and $F4-$F7 - //if ( i != 2 && i != 4 && i != 5 && i != 6 && i != 7 ) - if ( ((~0x2F00 << (bits_in_int - 16)) << i) < 0 ) // 12% - cpu_write_smp_reg( data, rel_time, i ); - } - } - #else - WRITE_DP( 0, temp, data ); - #endif - goto loop; - } - - case 0xC4: // MOV dp,a - ++pc; - #if !SPC_MORE_ACCURACY - { - int i = dp + data; - ram [i] = (uint8_t) a; - i -= 0xF0; - if ( (unsigned) i < 0x10 ) // 39% - { - unsigned sel = i - 2; - REGS [i] = (uint8_t) a; - - if ( sel == 1 ) // 51% $F3 - dsp_write( a, rel_time ); - else if ( sel > 1 ) // 1% not $F2 or $F3 - cpu_write_smp_reg_( a, rel_time, i ); - } - } - #else - WRITE_DP( 0, data, a ); - #endif - goto loop; - -#define CASE( n ) case n: - -// Define common address modes based on opcode for immediate mode. Execution -// ends with data set to the address of the operand. -#define ADDR_MODES_( op )\ - CASE( op - 0x02 ) /* (X) */\ - data = x + dp;\ - pc--;\ - goto end_##op;\ - CASE( op + 0x0F ) /* (dp)+Y */\ - data = READ_PROG16( data + dp ) + y;\ - goto end_##op;\ - CASE( op - 0x01 ) /* (dp+X) */\ - data = READ_PROG16( ((uint8_t) (data + x)) + dp );\ - goto end_##op;\ - CASE( op + 0x0E ) /* abs+Y */\ - data += y;\ - goto abs_##op;\ - CASE( op + 0x0D ) /* abs+X */\ - data += x;\ - CASE( op - 0x03 ) /* abs */\ - abs_##op:\ - data += 0x100 * READ_PC( ++pc );\ - goto end_##op;\ - CASE( op + 0x0C ) /* dp+X */\ - data = (uint8_t) (data + x); - -#define ADDR_MODES_NO_DP( op )\ - ADDR_MODES_( op )\ - data += dp;\ - end_##op: - -#define ADDR_MODES( op )\ - ADDR_MODES_( op )\ - CASE( op - 0x04 ) /* dp */\ - data += dp;\ - end_##op: - -// 1. 8-bit Data Transmission Commands. Group I - - ADDR_MODES_NO_DP( 0xE8 ) // MOV A,addr - a = nz = READ( 0, data ); - goto inc_pc_loop; - - case 0xBF:{// MOV A,(X)+ - int temp = x + dp; - x = (uint8_t) (x + 1); - a = nz = READ( -1, temp ); - goto loop; - } - - case 0xE8: // MOV A,imm - a = data; - nz = data; - goto inc_pc_loop; - - case 0xF9: // MOV X,dp+Y - data = (uint8_t) (data + y); - case 0xF8: // MOV X,dp - READ_DP_TIMER( 0, data, x = nz ); - goto inc_pc_loop; - - case 0xE9: // MOV X,abs - data = READ_PC16( pc ); - ++pc; - data = READ( 0, data ); - case 0xCD: // MOV X,imm - x = data; - nz = data; - goto inc_pc_loop; - - case 0xFB: // MOV Y,dp+X - data = (uint8_t) (data + x); - case 0xEB: // MOV Y,dp - // 70% from timer - pc++; - READ_DP_TIMER( 0, data, y = nz ); - goto loop; - - case 0xEC:{// MOV Y,abs - int temp = READ_PC16( pc ); - pc += 2; - READ_TIMER( 0, temp, y = nz ); - //y = nz = READ( 0, temp ); - goto loop; - } - - case 0x8D: // MOV Y,imm - y = data; - nz = data; - goto inc_pc_loop; - -// 2. 8-BIT DATA TRANSMISSION COMMANDS, GROUP 2 - - ADDR_MODES_NO_DP( 0xC8 ) // MOV addr,A - WRITE( 0, data, a ); - goto inc_pc_loop; - - { - int temp; - case 0xCC: // MOV abs,Y - temp = y; - goto mov_abs_temp; - case 0xC9: // MOV abs,X - temp = x; - mov_abs_temp: - WRITE( 0, READ_PC16( pc ), temp ); - pc += 2; - goto loop; - } - - case 0xD9: // MOV dp+Y,X - data = (uint8_t) (data + y); - case 0xD8: // MOV dp,X - WRITE( 0, data + dp, x ); - goto inc_pc_loop; - - case 0xDB: // MOV dp+X,Y - data = (uint8_t) (data + x); - case 0xCB: // MOV dp,Y - WRITE( 0, data + dp, y ); - goto inc_pc_loop; - -// 3. 8-BIT DATA TRANSMISSIN COMMANDS, GROUP 3. - - case 0x7D: // MOV A,X - a = x; - nz = x; - goto loop; - - case 0xDD: // MOV A,Y - a = y; - nz = y; - goto loop; - - case 0x5D: // MOV X,A - x = a; - nz = a; - goto loop; - - case 0xFD: // MOV Y,A - y = a; - nz = a; - goto loop; - - case 0x9D: // MOV X,SP - x = nz = GET_SP(); - goto loop; - - case 0xBD: // MOV SP,X - SET_SP( x ); - goto loop; - - //case 0xC6: // MOV (X),A (handled by MOV addr,A in group 2) - - case 0xAF: // MOV (X)+,A - WRITE_DP( 0, x, a + no_read_before_write ); - x++; - goto loop; - -// 5. 8-BIT LOGIC OPERATION COMMANDS - -#define LOGICAL_OP( op, func )\ - ADDR_MODES( op ) /* addr */\ - data = READ( 0, data );\ - case op: /* imm */\ - nz = a func##= data;\ - goto inc_pc_loop;\ - { unsigned addr;\ - case op + 0x11: /* X,Y */\ - data = READ_DP( -2, y );\ - addr = x + dp;\ - goto addr_##op;\ - case op + 0x01: /* dp,dp */\ - data = READ_DP( -3, data );\ - case op + 0x10:{/*dp,imm*/\ - uint8_t const* addr2 = pc + 1;\ - pc += 2;\ - addr = READ_PC( addr2 ) + dp;\ - }\ - addr_##op:\ - nz = data func READ( -1, addr );\ - WRITE( 0, addr, nz );\ - goto loop;\ - } - - LOGICAL_OP( 0x28, & ); // AND - - LOGICAL_OP( 0x08, | ); // OR - - LOGICAL_OP( 0x48, ^ ); // EOR - -// 4. 8-BIT ARITHMETIC OPERATION COMMANDS - - ADDR_MODES( 0x68 ) // CMP addr - data = READ( 0, data ); - case 0x68: // CMP imm - nz = a - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x79: // CMP (X),(Y) - data = READ_DP( -2, y ); - nz = READ_DP( -1, x ) - data; - c = ~nz; - nz &= 0xFF; - goto loop; - - case 0x69: // CMP dp,dp - data = READ_DP( -3, data ); - case 0x78: // CMP dp,imm - nz = READ_DP( -1, READ_PC( ++pc ) ) - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x3E: // CMP X,dp - data += dp; - goto cmp_x_addr; - case 0x1E: // CMP X,abs - data = READ_PC16( pc ); - pc++; - cmp_x_addr: - data = READ( 0, data ); - case 0xC8: // CMP X,imm - nz = x - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - case 0x7E: // CMP Y,dp - data += dp; - goto cmp_y_addr; - case 0x5E: // CMP Y,abs - data = READ_PC16( pc ); - pc++; - cmp_y_addr: - data = READ( 0, data ); - case 0xAD: // CMP Y,imm - nz = y - data; - c = ~nz; - nz &= 0xFF; - goto inc_pc_loop; - - { - int addr; - case 0xB9: // SBC (x),(y) - case 0x99: // ADC (x),(y) - pc--; // compensate for inc later - data = READ_DP( -2, y ); - addr = x + dp; - goto adc_addr; - case 0xA9: // SBC dp,dp - case 0x89: // ADC dp,dp - data = READ_DP( -3, data ); - case 0xB8: // SBC dp,imm - case 0x98: // ADC dp,imm - addr = READ_PC( ++pc ) + dp; - adc_addr: - nz = READ( -1, addr ); - goto adc_data; - -// catch ADC and SBC together, then decode later based on operand -#undef CASE -#define CASE( n ) case n: case (n) + 0x20: - ADDR_MODES( 0x88 ) // ADC/SBC addr - data = READ( 0, data ); - case 0xA8: // SBC imm - case 0x88: // ADC imm - addr = -1; // A - nz = a; - adc_data: { - int flags; - if ( opcode >= 0xA0 ) // SBC - data ^= 0xFF; - - flags = data ^ nz; - nz += data + (c >> 8 & 1); - flags ^= nz; - - psw = (psw & ~(v40 | h08)) | - (flags >> 1 & h08) | - ((flags + 0x80) >> 2 & v40); - c = nz; - if ( addr < 0 ) - { - a = (uint8_t) nz; - goto inc_pc_loop; - } - WRITE( 0, addr, /*(uint8_t)*/ nz ); - goto inc_pc_loop; - } - - } - -// 6. ADDITION & SUBTRACTION COMMANDS - -#define INC_DEC_REG( reg, op )\ - nz = reg op;\ - reg = (uint8_t) nz;\ - goto loop; - - case 0xBC: INC_DEC_REG( a, + 1 ) // INC A - case 0x3D: INC_DEC_REG( x, + 1 ) // INC X - case 0xFC: INC_DEC_REG( y, + 1 ) // INC Y - - case 0x9C: INC_DEC_REG( a, - 1 ) // DEC A - case 0x1D: INC_DEC_REG( x, - 1 ) // DEC X - case 0xDC: INC_DEC_REG( y, - 1 ) // DEC Y - - case 0x9B: // DEC dp+X - case 0xBB: // INC dp+X - data = (uint8_t) (data + x); - case 0x8B: // DEC dp - case 0xAB: // INC dp - data += dp; - goto inc_abs; - case 0x8C: // DEC abs - case 0xAC: // INC abs - data = READ_PC16( pc ); - pc++; - inc_abs: - nz = (opcode >> 4 & 2) - 1; - nz += READ( -1, data ); - WRITE( 0, data, /*(uint8_t)*/ nz ); - goto inc_pc_loop; - -// 7. SHIFT, ROTATION COMMANDS - - case 0x5C: // LSR A - c = 0; - case 0x7C:{// ROR A - nz = (c >> 1 & 0x80) | (a >> 1); - c = a << 8; - a = nz; - goto loop; - } - - case 0x1C: // ASL A - c = 0; - case 0x3C:{// ROL A - int temp = c >> 8 & 1; - c = a << 1; - nz = c | temp; - a = (uint8_t) nz; - goto loop; - } - - case 0x0B: // ASL dp - c = 0; - data += dp; - goto rol_mem; - case 0x1B: // ASL dp+X - c = 0; - case 0x3B: // ROL dp+X - data = (uint8_t) (data + x); - case 0x2B: // ROL dp - data += dp; - goto rol_mem; - case 0x0C: // ASL abs - c = 0; - case 0x2C: // ROL abs - data = READ_PC16( pc ); - pc++; - rol_mem: - nz = c >> 8 & 1; - nz |= (c = READ( -1, data ) << 1); - WRITE( 0, data, /*(uint8_t)*/ nz ); - goto inc_pc_loop; - - case 0x4B: // LSR dp - c = 0; - data += dp; - goto ror_mem; - case 0x5B: // LSR dp+X - c = 0; - case 0x7B: // ROR dp+X - data = (uint8_t) (data + x); - case 0x6B: // ROR dp - data += dp; - goto ror_mem; - case 0x4C: // LSR abs - c = 0; - case 0x6C: // ROR abs - data = READ_PC16( pc ); - pc++; - ror_mem: { - int temp = READ( -1, data ); - nz = (c >> 1 & 0x80) | (temp >> 1); - c = temp << 8; - WRITE( 0, data, nz ); - goto inc_pc_loop; - } - - case 0x9F: // XCN - nz = a = (a >> 4) | (uint8_t) (a << 4); - goto loop; - -// 8. 16-BIT TRANSMISION COMMANDS - - case 0xBA: // MOVW YA,dp - a = READ_DP( -2, data ); - nz = (a & 0x7F) | (a >> 1); - y = READ_DP( 0, (uint8_t) (data + 1) ); - nz |= y; - goto inc_pc_loop; - - case 0xDA: // MOVW dp,YA - WRITE_DP( -1, data, a ); - WRITE_DP( 0, (uint8_t) (data + 1), y + no_read_before_write ); - goto inc_pc_loop; - -// 9. 16-BIT OPERATION COMMANDS - - case 0x3A: // INCW dp - case 0x1A:{// DECW dp - int temp; - // low byte - data += dp; - temp = READ( -3, data ); - temp += (opcode >> 4 & 2) - 1; // +1 for INCW, -1 for DECW - nz = ((temp >> 1) | temp) & 0x7F; - WRITE( -2, data, /*(uint8_t)*/ temp ); - - // high byte - data = (uint8_t) (data + 1) + dp; - temp = (uint8_t) ((temp >> 8) + READ( -1, data )); - nz |= temp; - WRITE( 0, data, temp ); - - goto inc_pc_loop; - } - - case 0x7A: // ADDW YA,dp - case 0x9A:{// SUBW YA,dp - int lo = READ_DP( -2, data ); - int hi = READ_DP( 0, (uint8_t) (data + 1) ); - int result; - int flags; - - if ( opcode == 0x9A ) // SUBW - { - lo = (lo ^ 0xFF) + 1; - hi ^= 0xFF; - } - - lo += a; - result = y + hi + (lo >> 8); - flags = hi ^ y ^ result; - - psw = (psw & ~(v40 | h08)) | - (flags >> 1 & h08) | - ((flags + 0x80) >> 2 & v40); - c = result; - a = (uint8_t) lo; - result = (uint8_t) result; - y = result; - nz = (((lo >> 1) | lo) & 0x7F) | result; - - goto inc_pc_loop; - } - - case 0x5A: { // CMPW YA,dp - int temp = a - READ_DP( -1, data ); - nz = ((temp >> 1) | temp) & 0x7F; - temp = y + (temp >> 8); - temp -= READ_DP( 0, (uint8_t) (data + 1) ); - nz |= temp; - c = ~temp; - nz &= 0xFF; - goto inc_pc_loop; - } - -// 10. MULTIPLICATION & DIVISON COMMANDS - - case 0xCF: { // MUL YA - unsigned temp = y * a; - a = (uint8_t) temp; - nz = ((temp >> 1) | temp) & 0x7F; - y = temp >> 8; - nz |= y; - goto loop; - } - - case 0x9E: // DIV YA,X - { - unsigned ya = y * 0x100 + a; - - psw &= ~(h08 | v40); - - if ( y >= x ) - psw |= v40; - - if ( (y & 15) >= (x & 15) ) - psw |= h08; - - if ( y < x * 2 ) - { - a = ya / x; - y = ya - a * x; - } - else - { - a = 255 - (ya - x * 0x200) / (256 - x); - y = x + (ya - x * 0x200) % (256 - x); - } - - nz = (uint8_t) a; - a = (uint8_t) a; - - goto loop; - } - -// 11. DECIMAL COMPENSATION COMMANDS - - case 0xDF: // DAA - SUSPICIOUS_OPCODE( "DAA" ); - if ( a > 0x99 || c & 0x100 ) - { - a += 0x60; - c = 0x100; - } - - if ( (a & 0x0F) > 9 || psw & h08 ) - a += 0x06; - - nz = a; - a = (uint8_t) a; - goto loop; - - case 0xBE: // DAS - SUSPICIOUS_OPCODE( "DAS" ); - if ( a > 0x99 || !(c & 0x100) ) - { - a -= 0x60; - c = 0; - } - - if ( (a & 0x0F) > 9 || !(psw & h08) ) - a -= 0x06; - - nz = a; - a = (uint8_t) a; - goto loop; - -// 12. BRANCHING COMMANDS - - case 0x2F: // BRA rel - pc += (BOOST::int8_t) data; - goto inc_pc_loop; - - case 0x30: // BMI - BRANCH( (nz & nz_neg_mask) ) - - case 0x10: // BPL - BRANCH( !(nz & nz_neg_mask) ) - - case 0xB0: // BCS - BRANCH( c & 0x100 ) - - case 0x90: // BCC - BRANCH( !(c & 0x100) ) - - case 0x70: // BVS - BRANCH( psw & v40 ) - - case 0x50: // BVC - BRANCH( !(psw & v40) ) - - #define CBRANCH( cond )\ - {\ - pc++;\ - if ( cond )\ - goto cbranch_taken_loop;\ - rel_time -= 2;\ - goto inc_pc_loop;\ - } - - case 0x03: // BBS dp.bit,rel - case 0x23: - case 0x43: - case 0x63: - case 0x83: - case 0xA3: - case 0xC3: - case 0xE3: - CBRANCH( READ_DP( -4, data ) >> (opcode >> 5) & 1 ) - - case 0x13: // BBC dp.bit,rel - case 0x33: - case 0x53: - case 0x73: - case 0x93: - case 0xB3: - case 0xD3: - case 0xF3: - CBRANCH( !(READ_DP( -4, data ) >> (opcode >> 5) & 1) ) - - case 0xDE: // CBNE dp+X,rel - data = (uint8_t) (data + x); - // fall through - case 0x2E:{// CBNE dp,rel - int temp; - // 61% from timer - READ_DP_TIMER( -4, data, temp ); - CBRANCH( temp != a ) - } - - case 0x6E: { // DBNZ dp,rel - unsigned temp = READ_DP( -4, data ) - 1; - WRITE_DP( -3, (uint8_t) data, /*(uint8_t)*/ temp + no_read_before_write ); - CBRANCH( temp ) - } - - case 0xFE: // DBNZ Y,rel - y = (uint8_t) (y - 1); - BRANCH( y ) - - case 0x1F: // JMP [abs+X] - SET_PC( READ_PC16( pc ) + x ); - // fall through - case 0x5F: // JMP abs - SET_PC( READ_PC16( pc ) ); - goto loop; - -// 13. SUB-ROUTINE CALL RETURN COMMANDS - - case 0x0F:{// BRK - int temp; - int ret_addr = GET_PC(); - SUSPICIOUS_OPCODE( "BRK" ); - SET_PC( READ_PROG16( 0xFFDE ) ); // vector address verified - PUSH16( ret_addr ); - GET_PSW( temp ); - psw = (psw | b10) & ~i04; - PUSH( temp ); - goto loop; - } - - case 0x4F:{// PCALL offset - int ret_addr = GET_PC() + 1; - SET_PC( 0xFF00 | data ); - PUSH16( ret_addr ); - goto loop; - } - - case 0x01: // TCALL n - case 0x11: - case 0x21: - case 0x31: - case 0x41: - case 0x51: - case 0x61: - case 0x71: - case 0x81: - case 0x91: - case 0xA1: - case 0xB1: - case 0xC1: - case 0xD1: - case 0xE1: - case 0xF1: { - int ret_addr = GET_PC(); - SET_PC( READ_PROG16( 0xFFDE - (opcode >> 3) ) ); - PUSH16( ret_addr ); - goto loop; - } - -// 14. STACK OPERATION COMMANDS - - { - int temp; - case 0x7F: // RET1 - temp = *sp; - SET_PC( GET_LE16( sp + 1 ) ); - sp += 3; - goto set_psw; - case 0x8E: // POP PSW - POP( temp ); - set_psw: - SET_PSW( temp ); - goto loop; - } - - case 0x0D: { // PUSH PSW - int temp; - GET_PSW( temp ); - PUSH( temp ); - goto loop; - } - - case 0x2D: // PUSH A - PUSH( a ); - goto loop; - - case 0x4D: // PUSH X - PUSH( x ); - goto loop; - - case 0x6D: // PUSH Y - PUSH( y ); - goto loop; - - case 0xAE: // POP A - POP( a ); - goto loop; - - case 0xCE: // POP X - POP( x ); - goto loop; - - case 0xEE: // POP Y - POP( y ); - goto loop; - -// 15. BIT OPERATION COMMANDS - - case 0x02: // SET1 - case 0x22: - case 0x42: - case 0x62: - case 0x82: - case 0xA2: - case 0xC2: - case 0xE2: - case 0x12: // CLR1 - case 0x32: - case 0x52: - case 0x72: - case 0x92: - case 0xB2: - case 0xD2: - case 0xF2: { - int bit = 1 << (opcode >> 5); - int mask = ~bit; - if ( opcode & 0x10 ) - bit = 0; - data += dp; - WRITE( 0, data, (READ( -1, data ) & mask) | bit ); - goto inc_pc_loop; - } - - case 0x0E: // TSET1 abs - case 0x4E: // TCLR1 abs - data = READ_PC16( pc ); - pc += 2; - { - unsigned temp = READ( -2, data ); - nz = (uint8_t) (a - temp); - temp &= ~a; - if ( opcode == 0x0E ) - temp |= a; - WRITE( 0, data, temp ); - } - goto loop; - - case 0x4A: // AND1 C,mem.bit - c &= MEM_BIT( 0 ); - pc += 2; - goto loop; - - case 0x6A: // AND1 C,/mem.bit - c &= ~MEM_BIT( 0 ); - pc += 2; - goto loop; - - case 0x0A: // OR1 C,mem.bit - c |= MEM_BIT( -1 ); - pc += 2; - goto loop; - - case 0x2A: // OR1 C,/mem.bit - c |= ~MEM_BIT( -1 ); - pc += 2; - goto loop; - - case 0x8A: // EOR1 C,mem.bit - c ^= MEM_BIT( -1 ); - pc += 2; - goto loop; - - case 0xEA: // NOT1 mem.bit - data = READ_PC16( pc ); - pc += 2; - { - unsigned temp = READ( -1, data & 0x1FFF ); - temp ^= 1 << (data >> 13); - WRITE( 0, data & 0x1FFF, temp ); - } - goto loop; - - case 0xCA: // MOV1 mem.bit,C - data = READ_PC16( pc ); - pc += 2; - { - unsigned temp = READ( -2, data & 0x1FFF ); - unsigned bit = data >> 13; - temp = (temp & ~(1 << bit)) | ((c >> 8 & 1) << bit); - WRITE( 0, data & 0x1FFF, temp + no_read_before_write ); - } - goto loop; - - case 0xAA: // MOV1 C,mem.bit - c = MEM_BIT( 0 ); - pc += 2; - goto loop; - -// 16. PROGRAM PSW FLAG OPERATION COMMANDS - - case 0x60: // CLRC - c = 0; - goto loop; - - case 0x80: // SETC - c = ~0; - goto loop; - - case 0xED: // NOTC - c ^= 0x100; - goto loop; - - case 0xE0: // CLRV - psw &= ~(v40 | h08); - goto loop; - - case 0x20: // CLRP - dp = 0; - goto loop; - - case 0x40: // SETP - dp = 0x100; - goto loop; - - case 0xA0: // EI - SUSPICIOUS_OPCODE( "EI" ); - psw |= i04; - goto loop; - - case 0xC0: // DI - SUSPICIOUS_OPCODE( "DI" ); - psw &= ~i04; - goto loop; - -// 17. OTHER COMMANDS - - case 0x00: // NOP - goto loop; - - case 0xFF:{// STOP - // handle PC wrap-around - unsigned addr = GET_PC() - 1; - if ( addr >= 0x10000 ) - { - addr &= 0xFFFF; - SET_PC( addr ); - dprintf( "SPC: PC wrapped around\n" ); - goto loop; - } - } - // fall through - case 0xEF: // SLEEP - SUSPICIOUS_OPCODE( "STOP/SLEEP" ); - --pc; - rel_time = 0; - m.cpu_error = "SPC emulation error"; - goto stop; - } // switch - - assert( 0 ); // catch any unhandled instructions -} -out_of_time: - rel_time -= m.cycle_table [*pc]; // undo partial execution of opcode -stop: - - // Uncache registers - if ( GET_PC() >= 0x10000 ) - dprintf( "SPC: PC wrapped around\n" ); - m.cpu_regs.pc = (uint16_t) GET_PC(); - m.cpu_regs.sp = ( uint8_t) GET_SP(); - m.cpu_regs.a = ( uint8_t) a; - m.cpu_regs.x = ( uint8_t) x; - m.cpu_regs.y = ( uint8_t) y; - { - int temp; - GET_PSW( temp ); - m.cpu_regs.psw = (uint8_t) temp; - } -} -SPC_CPU_RUN_FUNC_END diff --git a/waterbox/sameboy/snes_spc/SPC_DSP.cpp b/waterbox/sameboy/snes_spc/SPC_DSP.cpp deleted file mode 100644 index dd180506fc..0000000000 --- a/waterbox/sameboy/snes_spc/SPC_DSP.cpp +++ /dev/null @@ -1,1018 +0,0 @@ -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "SPC_DSP.h" - -#include "blargg_endian.h" -#include - -/* Copyright (C) 2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -#ifdef BLARGG_ENABLE_OPTIMIZER - #include BLARGG_ENABLE_OPTIMIZER -#endif - -#if INT_MAX < 0x7FFFFFFF - #error "Requires that int type have at least 32 bits" -#endif - -// TODO: add to blargg_endian.h -#define GET_LE16SA( addr ) ((BOOST::int16_t) GET_LE16( addr )) -#define GET_LE16A( addr ) GET_LE16( addr ) -#define SET_LE16A( addr, data ) SET_LE16( addr, data ) - -static BOOST::uint8_t const initial_regs [SPC_DSP::register_count] = -{ - 0x45,0x8B,0x5A,0x9A,0xE4,0x82,0x1B,0x78,0x00,0x00,0xAA,0x96,0x89,0x0E,0xE0,0x80, - 0x2A,0x49,0x3D,0xBA,0x14,0xA0,0xAC,0xC5,0x00,0x00,0x51,0xBB,0x9C,0x4E,0x7B,0xFF, - 0xF4,0xFD,0x57,0x32,0x37,0xD9,0x42,0x22,0x00,0x00,0x5B,0x3C,0x9F,0x1B,0x87,0x9A, - 0x6F,0x27,0xAF,0x7B,0xE5,0x68,0x0A,0xD9,0x00,0x00,0x9A,0xC5,0x9C,0x4E,0x7B,0xFF, - 0xEA,0x21,0x78,0x4F,0xDD,0xED,0x24,0x14,0x00,0x00,0x77,0xB1,0xD1,0x36,0xC1,0x67, - 0x52,0x57,0x46,0x3D,0x59,0xF4,0x87,0xA4,0x00,0x00,0x7E,0x44,0x9C,0x4E,0x7B,0xFF, - 0x75,0xF5,0x06,0x97,0x10,0xC3,0x24,0xBB,0x00,0x00,0x7B,0x7A,0xE0,0x60,0x12,0x0F, - 0xF7,0x74,0x1C,0xE5,0x39,0x3D,0x73,0xC1,0x00,0x00,0x7A,0xB3,0xFF,0x4E,0x7B,0xFF -}; - -// if ( io < -32768 ) io = -32768; -// if ( io > 32767 ) io = 32767; -#define CLAMP16( io )\ -{\ - if ( (int16_t) io != io )\ - io = (io >> 31) ^ 0x7FFF;\ -} - -// Access global DSP register -#define REG(n) m.regs [r_##n] - -// Access voice DSP register -#define VREG(r,n) r [v_##n] - -#define WRITE_SAMPLES( l, r, out ) \ -{\ - out [0] = l;\ - out [1] = r;\ - out += 2;\ - if ( out >= m.out_end )\ - {\ - check( out == m.out_end );\ - check( m.out_end != &m.extra [extra_size] || \ - (m.extra <= m.out_begin && m.extra < &m.extra [extra_size]) );\ - out = m.extra;\ - m.out_end = &m.extra [extra_size];\ - }\ -}\ - -void SPC_DSP::set_output( sample_t* out, int size ) -{ - require( (size & 1) == 0 ); // must be even - if ( !out ) - { - out = m.extra; - size = extra_size; - } - m.out_begin = out; - m.out = out; - m.out_end = out + size; -} - -// Volume registers and efb are signed! Easy to forget int8_t cast. -// Prefixes are to avoid accidental use of locals with same names. - -// Gaussian interpolation - -static short const gauss [512] = -{ - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, - 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4, 5, 5, 5, 5, - 6, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10, 10, - 11, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15, 15, 16, 16, 17, 17, - 18, 19, 19, 20, 20, 21, 21, 22, 23, 23, 24, 24, 25, 26, 27, 27, - 28, 29, 29, 30, 31, 32, 32, 33, 34, 35, 36, 36, 37, 38, 39, 40, - 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, - 58, 59, 60, 61, 62, 64, 65, 66, 67, 69, 70, 71, 73, 74, 76, 77, - 78, 80, 81, 83, 84, 86, 87, 89, 90, 92, 94, 95, 97, 99, 100, 102, - 104, 106, 107, 109, 111, 113, 115, 117, 118, 120, 122, 124, 126, 128, 130, 132, - 134, 137, 139, 141, 143, 145, 147, 150, 152, 154, 156, 159, 161, 163, 166, 168, - 171, 173, 175, 178, 180, 183, 186, 188, 191, 193, 196, 199, 201, 204, 207, 210, - 212, 215, 218, 221, 224, 227, 230, 233, 236, 239, 242, 245, 248, 251, 254, 257, - 260, 263, 267, 270, 273, 276, 280, 283, 286, 290, 293, 297, 300, 304, 307, 311, - 314, 318, 321, 325, 328, 332, 336, 339, 343, 347, 351, 354, 358, 362, 366, 370, - 374, 378, 381, 385, 389, 393, 397, 401, 405, 410, 414, 418, 422, 426, 430, 434, - 439, 443, 447, 451, 456, 460, 464, 469, 473, 477, 482, 486, 491, 495, 499, 504, - 508, 513, 517, 522, 527, 531, 536, 540, 545, 550, 554, 559, 563, 568, 573, 577, - 582, 587, 592, 596, 601, 606, 611, 615, 620, 625, 630, 635, 640, 644, 649, 654, - 659, 664, 669, 674, 678, 683, 688, 693, 698, 703, 708, 713, 718, 723, 728, 732, - 737, 742, 747, 752, 757, 762, 767, 772, 777, 782, 787, 792, 797, 802, 806, 811, - 816, 821, 826, 831, 836, 841, 846, 851, 855, 860, 865, 870, 875, 880, 884, 889, - 894, 899, 904, 908, 913, 918, 923, 927, 932, 937, 941, 946, 951, 955, 960, 965, - 969, 974, 978, 983, 988, 992, 997,1001,1005,1010,1014,1019,1023,1027,1032,1036, -1040,1045,1049,1053,1057,1061,1066,1070,1074,1078,1082,1086,1090,1094,1098,1102, -1106,1109,1113,1117,1121,1125,1128,1132,1136,1139,1143,1146,1150,1153,1157,1160, -1164,1167,1170,1174,1177,1180,1183,1186,1190,1193,1196,1199,1202,1205,1207,1210, -1213,1216,1219,1221,1224,1227,1229,1232,1234,1237,1239,1241,1244,1246,1248,1251, -1253,1255,1257,1259,1261,1263,1265,1267,1269,1270,1272,1274,1275,1277,1279,1280, -1282,1283,1284,1286,1287,1288,1290,1291,1292,1293,1294,1295,1296,1297,1297,1298, -1299,1300,1300,1301,1302,1302,1303,1303,1303,1304,1304,1304,1304,1304,1305,1305, -}; - -inline int SPC_DSP::interpolate( voice_t const* v ) -{ - // Make pointers into gaussian based on fractional position between samples - int offset = v->interp_pos >> 4 & 0xFF; - short const* fwd = gauss + 255 - offset; - short const* rev = gauss + offset; // mirror left half of gaussian - - int const* in = &v->buf [(v->interp_pos >> 12) + v->buf_pos]; - int out; - out = (fwd [ 0] * in [0]) >> 11; - out += (fwd [256] * in [1]) >> 11; - out += (rev [256] * in [2]) >> 11; - out = (int16_t) out; - out += (rev [ 0] * in [3]) >> 11; - - CLAMP16( out ); - out &= ~1; - return out; -} - - -//// Counters - -int const simple_counter_range = 2048 * 5 * 3; // 30720 - -static unsigned const counter_rates [32] = -{ - simple_counter_range + 1, // never fires - 2048, 1536, - 1280, 1024, 768, - 640, 512, 384, - 320, 256, 192, - 160, 128, 96, - 80, 64, 48, - 40, 32, 24, - 20, 16, 12, - 10, 8, 6, - 5, 4, 3, - 2, - 1 -}; - -static unsigned const counter_offsets [32] = -{ - 1, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 536, 0, 1040, - 0, - 0 -}; - -inline void SPC_DSP::init_counter() -{ - m.counter = 0; -} - -inline void SPC_DSP::run_counters() -{ - if ( --m.counter < 0 ) - m.counter = simple_counter_range - 1; -} - -inline unsigned SPC_DSP::read_counter( int rate ) -{ - return ((unsigned) m.counter + counter_offsets [rate]) % counter_rates [rate]; -} - - -//// Envelope - -inline void SPC_DSP::run_envelope( voice_t* const v ) -{ - int env = v->env; - if ( v->env_mode == env_release ) // 60% - { - if ( (env -= 0x8) < 0 ) - env = 0; - v->env = env; - } - else - { - int rate; - int env_data = VREG(v->regs,adsr1); - if ( m.t_adsr0 & 0x80 ) // 99% ADSR - { - if ( v->env_mode >= env_decay ) // 99% - { - env--; - env -= env >> 8; - rate = env_data & 0x1F; - if ( v->env_mode == env_decay ) // 1% - rate = (m.t_adsr0 >> 3 & 0x0E) + 0x10; - } - else // env_attack - { - rate = (m.t_adsr0 & 0x0F) * 2 + 1; - env += rate < 31 ? 0x20 : 0x400; - } - } - else // GAIN - { - int mode; - env_data = VREG(v->regs,gain); - mode = env_data >> 5; - if ( mode < 4 ) // direct - { - env = env_data * 0x10; - rate = 31; - } - else - { - rate = env_data & 0x1F; - if ( mode == 4 ) // 4: linear decrease - { - env -= 0x20; - } - else if ( mode < 6 ) // 5: exponential decrease - { - env--; - env -= env >> 8; - } - else // 6,7: linear increase - { - env += 0x20; - if ( mode > 6 && (unsigned) v->hidden_env >= 0x600 ) - env += 0x8 - 0x20; // 7: two-slope linear increase - } - } - } - - // Sustain level - if ( (env >> 8) == (env_data >> 5) && v->env_mode == env_decay ) - v->env_mode = env_sustain; - - v->hidden_env = env; - - // unsigned cast because linear decrease going negative also triggers this - if ( (unsigned) env > 0x7FF ) - { - env = (env < 0 ? 0 : 0x7FF); - if ( v->env_mode == env_attack ) - v->env_mode = env_decay; - } - - if ( !read_counter( rate ) ) - v->env = env; // nothing else is controlled by the counter - } -} - - -//// BRR Decoding - -inline void SPC_DSP::decode_brr( voice_t* v ) -{ - // Arrange the four input nybbles in 0xABCD order for easy decoding - int nybbles = m.t_brr_byte * 0x100 + m.ram [(v->brr_addr + v->brr_offset + 1) & 0xFFFF]; - - int const header = m.t_brr_header; - - // Write to next four samples in circular buffer - int* pos = &v->buf [v->buf_pos]; - int* end; - if ( (v->buf_pos += 4) >= brr_buf_size ) - v->buf_pos = 0; - - // Decode four samples - for ( end = pos + 4; pos < end; pos++, nybbles <<= 4 ) - { - // Extract nybble and sign-extend - int s = (int16_t) nybbles >> 12; - - // Shift sample based on header - int const shift = header >> 4; - s = (s << shift) >> 1; - if ( shift >= 0xD ) // handle invalid range - s = (s >> 25) << 11; // same as: s = (s < 0 ? -0x800 : 0) - - // Apply IIR filter (8 is the most commonly used) - int const filter = header & 0x0C; - int const p1 = pos [brr_buf_size - 1]; - int const p2 = pos [brr_buf_size - 2] >> 1; - if ( filter >= 8 ) - { - s += p1; - s -= p2; - if ( filter == 8 ) // s += p1 * 0.953125 - p2 * 0.46875 - { - s += p2 >> 4; - s += (p1 * -3) >> 6; - } - else // s += p1 * 0.8984375 - p2 * 0.40625 - { - s += (p1 * -13) >> 7; - s += (p2 * 3) >> 4; - } - } - else if ( filter ) // s += p1 * 0.46875 - { - s += p1 >> 1; - s += (-p1) >> 5; - } - - // Adjust and write sample - CLAMP16( s ); - s = (int16_t) (s * 2); - pos [brr_buf_size] = pos [0] = s; // second copy simplifies wrap-around - } -} - - -//// Misc - -#define MISC_CLOCK( n ) inline void SPC_DSP::misc_##n() - -MISC_CLOCK( 27 ) -{ - m.t_pmon = REG(pmon) & 0xFE; // voice 0 doesn't support PMON -} -MISC_CLOCK( 28 ) -{ - m.t_non = REG(non); - m.t_eon = REG(eon); - m.t_dir = REG(dir); -} -MISC_CLOCK( 29 ) -{ - if ( (m.every_other_sample ^= 1) != 0 ) - m.new_kon &= ~m.kon; // clears KON 63 clocks after it was last read -} -MISC_CLOCK( 30 ) -{ - if ( m.every_other_sample ) - { - m.kon = m.new_kon; - m.t_koff = REG(koff) | m.mute_mask; - } - - run_counters(); - - // Noise - if ( !read_counter( REG(flg) & 0x1F ) ) - { - int feedback = (m.noise << 13) ^ (m.noise << 14); - m.noise = (feedback & 0x4000) ^ (m.noise >> 1); - } -} - - -//// Voices - -#define VOICE_CLOCK( n ) void SPC_DSP::voice_##n( voice_t* const v ) - -inline VOICE_CLOCK( V1 ) -{ - m.t_dir_addr = m.t_dir * 0x100 + m.t_srcn * 4; - m.t_srcn = VREG(v->regs,srcn); -} -inline VOICE_CLOCK( V2 ) -{ - // Read sample pointer (ignored if not needed) - uint8_t const* entry = &m.ram [m.t_dir_addr]; - if ( !v->kon_delay ) - entry += 2; - m.t_brr_next_addr = GET_LE16A( entry ); - - m.t_adsr0 = VREG(v->regs,adsr0); - - // Read pitch, spread over two clocks - m.t_pitch = VREG(v->regs,pitchl); -} -inline VOICE_CLOCK( V3a ) -{ - m.t_pitch += (VREG(v->regs,pitchh) & 0x3F) << 8; -} -inline VOICE_CLOCK( V3b ) -{ - // Read BRR header and byte - m.t_brr_byte = m.ram [(v->brr_addr + v->brr_offset) & 0xFFFF]; - m.t_brr_header = m.ram [v->brr_addr]; // brr_addr doesn't need masking -} -VOICE_CLOCK( V3c ) -{ - // Pitch modulation using previous voice's output - if ( m.t_pmon & v->vbit ) - m.t_pitch += ((m.t_output >> 5) * m.t_pitch) >> 10; - - if ( v->kon_delay ) - { - // Get ready to start BRR decoding on next sample - if ( v->kon_delay == 5 ) - { - v->brr_addr = m.t_brr_next_addr; - v->brr_offset = 1; - v->buf_pos = 0; - m.t_brr_header = 0; // header is ignored on this sample - m.kon_check = true; - } - - // Envelope is never run during KON - v->env = 0; - v->hidden_env = 0; - - // Disable BRR decoding until last three samples - v->interp_pos = 0; - if ( --v->kon_delay & 3 ) - v->interp_pos = 0x4000; - - // Pitch is never added during KON - m.t_pitch = 0; - } - - // Gaussian interpolation - { - int output = interpolate( v ); - - // Noise - if ( m.t_non & v->vbit ) - output = (int16_t) (m.noise * 2); - - // Apply envelope - m.t_output = (output * v->env) >> 11 & ~1; - v->t_envx_out = (uint8_t) (v->env >> 4); - } - - // Immediate silence due to end of sample or soft reset - if ( REG(flg) & 0x80 || (m.t_brr_header & 3) == 1 ) - { - v->env_mode = env_release; - v->env = 0; - } - - if ( m.every_other_sample ) - { - // KOFF - if ( m.t_koff & v->vbit ) - v->env_mode = env_release; - - // KON - if ( m.kon & v->vbit ) - { - v->kon_delay = 5; - v->env_mode = env_attack; - } - } - - // Run envelope for next sample - if ( !v->kon_delay ) - run_envelope( v ); -} -inline void SPC_DSP::voice_output( voice_t const* v, int ch ) -{ - // Apply left/right volume - int amp = (m.t_output * (int8_t) VREG(v->regs,voll + ch)) >> 7; - - // Add to output total - m.t_main_out [ch] += amp; - CLAMP16( m.t_main_out [ch] ); - - // Optionally add to echo total - if ( m.t_eon & v->vbit ) - { - m.t_echo_out [ch] += amp; - CLAMP16( m.t_echo_out [ch] ); - } -} -VOICE_CLOCK( V4 ) -{ - // Decode BRR - m.t_looped = 0; - if ( v->interp_pos >= 0x4000 ) - { - decode_brr( v ); - - if ( (v->brr_offset += 2) >= brr_block_size ) - { - // Start decoding next BRR block - assert( v->brr_offset == brr_block_size ); - v->brr_addr = (v->brr_addr + brr_block_size) & 0xFFFF; - if ( m.t_brr_header & 1 ) - { - v->brr_addr = m.t_brr_next_addr; - m.t_looped = v->vbit; - } - v->brr_offset = 1; - } - } - - // Apply pitch - v->interp_pos = (v->interp_pos & 0x3FFF) + m.t_pitch; - - // Keep from getting too far ahead (when using pitch modulation) - if ( v->interp_pos > 0x7FFF ) - v->interp_pos = 0x7FFF; - - // Output left - voice_output( v, 0 ); -} -inline VOICE_CLOCK( V5 ) -{ - // Output right - voice_output( v, 1 ); - - // ENDX, OUTX, and ENVX won't update if you wrote to them 1-2 clocks earlier - int endx_buf = REG(endx) | m.t_looped; - - // Clear bit in ENDX if KON just began - if ( v->kon_delay == 5 ) - endx_buf &= ~v->vbit; - m.endx_buf = (uint8_t) endx_buf; -} -inline VOICE_CLOCK( V6 ) -{ - (void) v; // avoid compiler warning about unused v - m.outx_buf = (uint8_t) (m.t_output >> 8); -} -inline VOICE_CLOCK( V7 ) -{ - // Update ENDX - REG(endx) = m.endx_buf; - - m.envx_buf = v->t_envx_out; -} -inline VOICE_CLOCK( V8 ) -{ - // Update OUTX - VREG(v->regs,outx) = m.outx_buf; -} -inline VOICE_CLOCK( V9 ) -{ - // Update ENVX - VREG(v->regs,envx) = m.envx_buf; -} - -// Most voices do all these in one clock, so make a handy composite -inline VOICE_CLOCK( V3 ) -{ - voice_V3a( v ); - voice_V3b( v ); - voice_V3c( v ); -} - -// Common combinations of voice steps on different voices. This greatly reduces -// code size and allows everything to be inlined in these functions. -VOICE_CLOCK(V7_V4_V1) { voice_V7(v); voice_V1(v+3); voice_V4(v+1); } -VOICE_CLOCK(V8_V5_V2) { voice_V8(v); voice_V5(v+1); voice_V2(v+2); } -VOICE_CLOCK(V9_V6_V3) { voice_V9(v); voice_V6(v+1); voice_V3(v+2); } - - -//// Echo - -// Current echo buffer pointer for left/right channel -#define ECHO_PTR( ch ) (&m.ram [m.t_echo_ptr + ch * 2]) - -// Sample in echo history buffer, where 0 is the oldest -#define ECHO_FIR( i ) (m.echo_hist_pos [i]) - -// Calculate FIR point for left/right channel -#define CALC_FIR( i, ch ) ((ECHO_FIR( i + 1 ) [ch] * (int8_t) REG(fir + i * 0x10)) >> 6) - -#define ECHO_CLOCK( n ) inline void SPC_DSP::echo_##n() - -inline void SPC_DSP::echo_read( int ch ) -{ - int s = GET_LE16SA( ECHO_PTR( ch ) ); - // second copy simplifies wrap-around handling - ECHO_FIR( 0 ) [ch] = ECHO_FIR( 8 ) [ch] = s >> 1; -} - -ECHO_CLOCK( 22 ) -{ - // History - if ( ++m.echo_hist_pos >= &m.echo_hist [echo_hist_size] ) - m.echo_hist_pos = m.echo_hist; - - m.t_echo_ptr = (m.t_esa * 0x100 + m.echo_offset) & 0xFFFF; - echo_read( 0 ); - - // FIR (using l and r temporaries below helps compiler optimize) - int l = CALC_FIR( 0, 0 ); - int r = CALC_FIR( 0, 1 ); - - m.t_echo_in [0] = l; - m.t_echo_in [1] = r; -} -ECHO_CLOCK( 23 ) -{ - int l = CALC_FIR( 1, 0 ) + CALC_FIR( 2, 0 ); - int r = CALC_FIR( 1, 1 ) + CALC_FIR( 2, 1 ); - - m.t_echo_in [0] += l; - m.t_echo_in [1] += r; - - echo_read( 1 ); -} -ECHO_CLOCK( 24 ) -{ - int l = CALC_FIR( 3, 0 ) + CALC_FIR( 4, 0 ) + CALC_FIR( 5, 0 ); - int r = CALC_FIR( 3, 1 ) + CALC_FIR( 4, 1 ) + CALC_FIR( 5, 1 ); - - m.t_echo_in [0] += l; - m.t_echo_in [1] += r; -} -ECHO_CLOCK( 25 ) -{ - int l = m.t_echo_in [0] + CALC_FIR( 6, 0 ); - int r = m.t_echo_in [1] + CALC_FIR( 6, 1 ); - - l = (int16_t) l; - r = (int16_t) r; - - l += (int16_t) CALC_FIR( 7, 0 ); - r += (int16_t) CALC_FIR( 7, 1 ); - - CLAMP16( l ); - CLAMP16( r ); - - m.t_echo_in [0] = l & ~1; - m.t_echo_in [1] = r & ~1; -} -inline int SPC_DSP::echo_output( int ch ) -{ - int out = (int16_t) ((m.t_main_out [ch] * (int8_t) REG(mvoll + ch * 0x10)) >> 7) + - (int16_t) ((m.t_echo_in [ch] * (int8_t) REG(evoll + ch * 0x10)) >> 7); - CLAMP16( out ); - return out; -} -ECHO_CLOCK( 26 ) -{ - // Left output volumes - // (save sample for next clock so we can output both together) - m.t_main_out [0] = echo_output( 0 ); - - // Echo feedback - int l = m.t_echo_out [0] + (int16_t) ((m.t_echo_in [0] * (int8_t) REG(efb)) >> 7); - int r = m.t_echo_out [1] + (int16_t) ((m.t_echo_in [1] * (int8_t) REG(efb)) >> 7); - - CLAMP16( l ); - CLAMP16( r ); - - m.t_echo_out [0] = l & ~1; - m.t_echo_out [1] = r & ~1; -} -ECHO_CLOCK( 27 ) -{ - // Output - int l = m.t_main_out [0]; - int r = echo_output( 1 ); - m.t_main_out [0] = 0; - m.t_main_out [1] = 0; - - // TODO: global muting isn't this simple (turns DAC on and off - // or something, causing small ~37-sample pulse when first muted) - if ( REG(flg) & 0x40 ) - { - l = 0; - r = 0; - } - - // Output sample to DAC - #ifdef SPC_DSP_OUT_HOOK - SPC_DSP_OUT_HOOK( l, r ); - #else - sample_t* out = m.out; - WRITE_SAMPLES( l, r, out ); - m.out = out; - #endif -} -ECHO_CLOCK( 28 ) -{ - m.t_echo_enabled = REG(flg); -} -inline void SPC_DSP::echo_write( int ch ) -{ - if ( !(m.t_echo_enabled & 0x20) ) - SET_LE16A( ECHO_PTR( ch ), m.t_echo_out [ch] ); - m.t_echo_out [ch] = 0; -} -ECHO_CLOCK( 29 ) -{ - m.t_esa = REG(esa); - - if ( !m.echo_offset ) - m.echo_length = (REG(edl) & 0x0F) * 0x800; - - m.echo_offset += 4; - if ( m.echo_offset >= m.echo_length ) - m.echo_offset = 0; - - // Write left echo - echo_write( 0 ); - - m.t_echo_enabled = REG(flg); -} -ECHO_CLOCK( 30 ) -{ - // Write right echo - echo_write( 1 ); -} - - -//// Timing - -// Execute clock for a particular voice -#define V( clock, voice ) voice_##clock( &m.voices [voice] ); - -/* The most common sequence of clocks uses composite operations -for efficiency. For example, the following are equivalent to the -individual steps on the right: - -V(V7_V4_V1,2) -> V(V7,2) V(V4,3) V(V1,5) -V(V8_V5_V2,2) -> V(V8,2) V(V5,3) V(V2,4) -V(V9_V6_V3,2) -> V(V9,2) V(V6,3) V(V3,4) */ - -// Voice 0 1 2 3 4 5 6 7 -#define GEN_DSP_TIMING \ -PHASE( 0) V(V5,0)V(V2,1)\ -PHASE( 1) V(V6,0)V(V3,1)\ -PHASE( 2) V(V7_V4_V1,0)\ -PHASE( 3) V(V8_V5_V2,0)\ -PHASE( 4) V(V9_V6_V3,0)\ -PHASE( 5) V(V7_V4_V1,1)\ -PHASE( 6) V(V8_V5_V2,1)\ -PHASE( 7) V(V9_V6_V3,1)\ -PHASE( 8) V(V7_V4_V1,2)\ -PHASE( 9) V(V8_V5_V2,2)\ -PHASE(10) V(V9_V6_V3,2)\ -PHASE(11) V(V7_V4_V1,3)\ -PHASE(12) V(V8_V5_V2,3)\ -PHASE(13) V(V9_V6_V3,3)\ -PHASE(14) V(V7_V4_V1,4)\ -PHASE(15) V(V8_V5_V2,4)\ -PHASE(16) V(V9_V6_V3,4)\ -PHASE(17) V(V1,0) V(V7,5)V(V4,6)\ -PHASE(18) V(V8_V5_V2,5)\ -PHASE(19) V(V9_V6_V3,5)\ -PHASE(20) V(V1,1) V(V7,6)V(V4,7)\ -PHASE(21) V(V8,6)V(V5,7) V(V2,0) /* t_brr_next_addr order dependency */\ -PHASE(22) V(V3a,0) V(V9,6)V(V6,7) echo_22();\ -PHASE(23) V(V7,7) echo_23();\ -PHASE(24) V(V8,7) echo_24();\ -PHASE(25) V(V3b,0) V(V9,7) echo_25();\ -PHASE(26) echo_26();\ -PHASE(27) misc_27(); echo_27();\ -PHASE(28) misc_28(); echo_28();\ -PHASE(29) misc_29(); echo_29();\ -PHASE(30) misc_30();V(V3c,0) echo_30();\ -PHASE(31) V(V4,0) V(V1,2)\ - -#if !SPC_DSP_CUSTOM_RUN - -void SPC_DSP::run( int clocks_remain ) -{ - require( clocks_remain > 0 ); - - int const phase = m.phase; - m.phase = (phase + clocks_remain) & 31; - switch ( phase ) - { - loop: - - #define PHASE( n ) if ( n && !--clocks_remain ) break; case n: - GEN_DSP_TIMING - #undef PHASE - - if ( --clocks_remain ) - goto loop; - } -} - -#endif - - -//// Setup - -void SPC_DSP::init( void* ram_64k ) -{ - m.ram = (uint8_t*) ram_64k; - mute_voices( 0 ); - disable_surround( false ); - set_output( 0, 0 ); - reset(); - - #ifndef NDEBUG - // be sure this sign-extends - assert( (int16_t) 0x8000 == -0x8000 ); - - // be sure right shift preserves sign - assert( (-1 >> 1) == -1 ); - - // check clamp macro - int i; - i = +0x8000; CLAMP16( i ); assert( i == +0x7FFF ); - i = -0x8001; CLAMP16( i ); assert( i == -0x8000 ); - - blargg_verify_byte_order(); - #endif -} - -void SPC_DSP::soft_reset_common() -{ - require( m.ram ); // init() must have been called already - - m.noise = 0x4000; - m.echo_hist_pos = m.echo_hist; - m.every_other_sample = 1; - m.echo_offset = 0; - m.phase = 0; - - init_counter(); -} - -void SPC_DSP::soft_reset() -{ - REG(flg) = 0xE0; - soft_reset_common(); -} - -void SPC_DSP::load( uint8_t const regs [register_count] ) -{ - memcpy( m.regs, regs, sizeof m.regs ); - memset( &m.regs [register_count], 0, offsetof (state_t,ram) - register_count ); - - // Internal state - for ( int i = voice_count; --i >= 0; ) - { - voice_t* v = &m.voices [i]; - v->brr_offset = 1; - v->vbit = 1 << i; - v->regs = &m.regs [i * 0x10]; - } - m.new_kon = REG(kon); - m.t_dir = REG(dir); - m.t_esa = REG(esa); - - soft_reset_common(); -} - -void SPC_DSP::reset() { load( initial_regs ); } - - -//// State save/load - -#if !SPC_NO_COPY_STATE_FUNCS - -void SPC_State_Copier::copy( void* state, size_t size ) -{ - func( buf, state, size ); -} - -int SPC_State_Copier::copy_int( int state, int size ) -{ - BOOST::uint8_t s [2]; - SET_LE16( s, state ); - func( buf, &s, size ); - return GET_LE16( s ); -} - -void SPC_State_Copier::skip( int count ) -{ - if ( count > 0 ) - { - char temp [64]; - memset( temp, 0, sizeof temp ); - do - { - int n = sizeof temp; - if ( n > count ) - n = count; - count -= n; - func( buf, temp, n ); - } - while ( count ); - } -} - -void SPC_State_Copier::extra() -{ - int n = 0; - SPC_State_Copier& copier = *this; - SPC_COPY( uint8_t, n ); - skip( n ); -} - -void SPC_DSP::copy_state( unsigned char** io, copy_func_t copy ) -{ - SPC_State_Copier copier( io, copy ); - - // DSP registers - copier.copy( m.regs, register_count ); - - // Internal state - - // Voices - int i; - for ( i = 0; i < voice_count; i++ ) - { - voice_t* v = &m.voices [i]; - - // BRR buffer - int i; - for ( i = 0; i < brr_buf_size; i++ ) - { - int s = v->buf [i]; - SPC_COPY( int16_t, s ); - v->buf [i] = v->buf [i + brr_buf_size] = s; - } - - SPC_COPY( uint16_t, v->interp_pos ); - SPC_COPY( uint16_t, v->brr_addr ); - SPC_COPY( uint16_t, v->env ); - SPC_COPY( int16_t, v->hidden_env ); - SPC_COPY( uint8_t, v->buf_pos ); - SPC_COPY( uint8_t, v->brr_offset ); - SPC_COPY( uint8_t, v->kon_delay ); - { - int m = v->env_mode; - SPC_COPY( uint8_t, m ); - v->env_mode = (enum env_mode_t) m; - } - SPC_COPY( uint8_t, v->t_envx_out ); - - copier.extra(); - } - - // Echo history - for ( i = 0; i < echo_hist_size; i++ ) - { - int j; - for ( j = 0; j < 2; j++ ) - { - int s = m.echo_hist_pos [i] [j]; - SPC_COPY( int16_t, s ); - m.echo_hist [i] [j] = s; // write back at offset 0 - } - } - m.echo_hist_pos = m.echo_hist; - memcpy( &m.echo_hist [echo_hist_size], m.echo_hist, echo_hist_size * sizeof m.echo_hist [0] ); - - // Misc - SPC_COPY( uint8_t, m.every_other_sample ); - SPC_COPY( uint8_t, m.kon ); - - SPC_COPY( uint16_t, m.noise ); - SPC_COPY( uint16_t, m.counter ); - SPC_COPY( uint16_t, m.echo_offset ); - SPC_COPY( uint16_t, m.echo_length ); - SPC_COPY( uint8_t, m.phase ); - - SPC_COPY( uint8_t, m.new_kon ); - SPC_COPY( uint8_t, m.endx_buf ); - SPC_COPY( uint8_t, m.envx_buf ); - SPC_COPY( uint8_t, m.outx_buf ); - - SPC_COPY( uint8_t, m.t_pmon ); - SPC_COPY( uint8_t, m.t_non ); - SPC_COPY( uint8_t, m.t_eon ); - SPC_COPY( uint8_t, m.t_dir ); - SPC_COPY( uint8_t, m.t_koff ); - - SPC_COPY( uint16_t, m.t_brr_next_addr ); - SPC_COPY( uint8_t, m.t_adsr0 ); - SPC_COPY( uint8_t, m.t_brr_header ); - SPC_COPY( uint8_t, m.t_brr_byte ); - SPC_COPY( uint8_t, m.t_srcn ); - SPC_COPY( uint8_t, m.t_esa ); - SPC_COPY( uint8_t, m.t_echo_enabled ); - - SPC_COPY( int16_t, m.t_main_out [0] ); - SPC_COPY( int16_t, m.t_main_out [1] ); - SPC_COPY( int16_t, m.t_echo_out [0] ); - SPC_COPY( int16_t, m.t_echo_out [1] ); - SPC_COPY( int16_t, m.t_echo_in [0] ); - SPC_COPY( int16_t, m.t_echo_in [1] ); - - SPC_COPY( uint16_t, m.t_dir_addr ); - SPC_COPY( uint16_t, m.t_pitch ); - SPC_COPY( int16_t, m.t_output ); - SPC_COPY( uint16_t, m.t_echo_ptr ); - SPC_COPY( uint8_t, m.t_looped ); - - copier.extra(); -} -#endif diff --git a/waterbox/sameboy/snes_spc/SPC_DSP.h b/waterbox/sameboy/snes_spc/SPC_DSP.h deleted file mode 100644 index 4522ace915..0000000000 --- a/waterbox/sameboy/snes_spc/SPC_DSP.h +++ /dev/null @@ -1,304 +0,0 @@ -// 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 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 ) { } // not supported -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; - 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(); -}; - -#include - -inline int SPC_DSP::sample_count() const { return m.out - m.out_begin; } - -inline int SPC_DSP::read( int addr ) const -{ - assert( (unsigned) addr < register_count ); - return m.regs [addr]; -} - -inline void SPC_DSP::write( int addr, int data ) -{ - assert( (unsigned) addr < register_count ); - - m.regs [addr] = (uint8_t) data; - switch ( addr & 0x0F ) - { - case v_envx: - m.envx_buf = (uint8_t) data; - break; - - case v_outx: - m.outx_buf = (uint8_t) data; - break; - - case 0x0C: - if ( addr == r_kon ) - m.new_kon = (uint8_t) data; - - if ( addr == r_endx ) // always cleared, regardless of data written - { - m.endx_buf = 0; - m.regs [r_endx] = 0; - } - break; - } -} - -inline void SPC_DSP::mute_voices( int mask ) { m.mute_mask = mask; } - -inline bool SPC_DSP::check_kon() -{ - bool old = m.kon_check; - m.kon_check = 0; - return old; -} - -#if !SPC_NO_COPY_STATE_FUNCS - -class SPC_State_Copier { - SPC_DSP::copy_func_t func; - unsigned char** buf; -public: - SPC_State_Copier( unsigned char** p, SPC_DSP::copy_func_t f ) { func = f; buf = p; } - void copy( void* state, size_t size ); - int copy_int( int state, int size ); - void skip( int count ); - void extra(); -}; - -#define SPC_COPY( type, state )\ -{\ - state = (BOOST::type) copier.copy_int( state, sizeof (BOOST::type) );\ - assert( (BOOST::type) state == state );\ -} - -#endif - -#endif diff --git a/waterbox/sameboy/snes_spc/SPC_Filter.cpp b/waterbox/sameboy/snes_spc/SPC_Filter.cpp deleted file mode 100644 index b3d5770822..0000000000 --- a/waterbox/sameboy/snes_spc/SPC_Filter.cpp +++ /dev/null @@ -1,68 +0,0 @@ -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "SPC_Filter.h" - -#include - -/* Copyright (C) 2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -void SPC_Filter::clear() { memset( ch, 0, sizeof ch ); } - -SPC_Filter::SPC_Filter() -{ - gain = gain_unit; - bass = bass_norm; - clear(); -} - -void SPC_Filter::run( short* io, int count ) -{ - require( (count & 1) == 0 ); // must be even - - int const gain = this->gain; - int const bass = this->bass; - chan_t* c = &ch [2]; - do - { - // cache in registers - int sum = (--c)->sum; - int pp1 = c->pp1; - int p1 = c->p1; - - for ( int i = 0; i < count; i += 2 ) - { - // Low-pass filter (two point FIR with coeffs 0.25, 0.75) - int f = io [i] + p1; - p1 = io [i] * 3; - - // High-pass filter ("leaky integrator") - int delta = f - pp1; - pp1 = f; - int s = sum >> (gain_bits + 2); - sum += (delta * gain) - (sum >> bass); - - // Clamp to 16 bits - if ( (short) s != s ) - s = (s >> 31) ^ 0x7FFF; - - io [i] = (short) s; - } - - c->p1 = p1; - c->pp1 = pp1; - c->sum = sum; - ++io; - } - while ( c != ch ); -} diff --git a/waterbox/sameboy/snes_spc/SPC_Filter.h b/waterbox/sameboy/snes_spc/SPC_Filter.h deleted file mode 100644 index d5c83cb8f1..0000000000 --- a/waterbox/sameboy/snes_spc/SPC_Filter.h +++ /dev/null @@ -1,47 +0,0 @@ -// Simple low-pass and high-pass filter to better match sound output of a SNES - -// snes_spc 0.9.0 -#ifndef SPC_FILTER_H -#define SPC_FILTER_H - -#include "blargg_common.h" - -struct SPC_Filter { -public: - - // Filters count samples of stereo sound in place. Count must be a multiple of 2. - typedef short sample_t; - void run( sample_t* io, int count ); - -// Optional features - - // Clears filter to silence - void clear(); - - // Sets gain (volume), where gain_unit is normal. Gains greater than gain_unit - // are fine, since output is clamped to 16-bit sample range. - enum { gain_unit = 0x100 }; - void set_gain( int gain ); - - // Sets amount of bass (logarithmic scale) - enum { bass_none = 0 }; - enum { bass_norm = 8 }; // normal amount - enum { bass_max = 31 }; - void set_bass( int bass ); - -public: - SPC_Filter(); - BLARGG_DISABLE_NOTHROW -private: - enum { gain_bits = 8 }; - int gain; - int bass; - struct chan_t { int p1, pp1, sum; }; - chan_t ch [2]; -}; - -inline void SPC_Filter::set_gain( int g ) { gain = g; } - -inline void SPC_Filter::set_bass( int b ) { bass = b; } - -#endif diff --git a/waterbox/sameboy/snes_spc/blargg_common.h b/waterbox/sameboy/snes_spc/blargg_common.h deleted file mode 100644 index 75edff3914..0000000000 --- a/waterbox/sameboy/snes_spc/blargg_common.h +++ /dev/null @@ -1,186 +0,0 @@ -// Sets up common environment for Shay Green's libraries. -// To change configuration options, modify blargg_config.h, not this file. - -// snes_spc 0.9.0 -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -#include -#include -#include -#include - -#undef BLARGG_COMMON_H -// allow blargg_config.h to #include blargg_common.h -#include "blargg_config.h" -#ifndef BLARGG_COMMON_H -#define BLARGG_COMMON_H - -// BLARGG_RESTRICT: equivalent to restrict, where supported -#if defined (__GNUC__) || _MSC_VER >= 1100 - #define BLARGG_RESTRICT __restrict -#else - #define BLARGG_RESTRICT -#endif - -// STATIC_CAST(T,expr): Used in place of static_cast (expr) -#ifndef STATIC_CAST - #define STATIC_CAST(T,expr) ((T) (expr)) -#endif - -// blargg_err_t (0 on success, otherwise error string) -#ifndef blargg_err_t - typedef const char* blargg_err_t; -#endif - -// blargg_vector - very lightweight vector of POD types (no constructor/destructor) -template -class blargg_vector { - T* begin_; - size_t size_; -public: - blargg_vector() : begin_( 0 ), size_( 0 ) { } - ~blargg_vector() { free( begin_ ); } - size_t size() const { return size_; } - T* begin() const { return begin_; } - T* end() const { return begin_ + size_; } - blargg_err_t resize( size_t n ) - { - // TODO: blargg_common.cpp to hold this as an outline function, ugh - void* p = realloc( begin_, n * sizeof (T) ); - if ( p ) - begin_ = (T*) p; - else if ( n > size_ ) // realloc failure only a problem if expanding - return "Out of memory"; - size_ = n; - return 0; - } - void clear() { void* p = begin_; begin_ = 0; size_ = 0; free( p ); } - T& operator [] ( size_t n ) const - { - assert( n <= size_ ); // <= to allow past-the-end value - return begin_ [n]; - } -}; - -#ifndef BLARGG_DISABLE_NOTHROW - // throw spec mandatory in ISO C++ if operator new can return NULL - #if __cplusplus >= 199711 || defined (__GNUC__) - #define BLARGG_THROWS( spec ) throw spec - #else - #define BLARGG_THROWS( spec ) - #endif - #define BLARGG_DISABLE_NOTHROW \ - void* operator new ( size_t s ) BLARGG_THROWS(()) { return malloc( s ); }\ - void operator delete ( void* p ) { free( p ); } - #define BLARGG_NEW new -#else - #include - #define BLARGG_NEW new (std::nothrow) -#endif - -// BLARGG_4CHAR('a','b','c','d') = 'abcd' (four character integer constant) -#define BLARGG_4CHAR( a, b, c, d ) \ - ((a&0xFF)*0x1000000L + (b&0xFF)*0x10000L + (c&0xFF)*0x100L + (d&0xFF)) - -// BOOST_STATIC_ASSERT( expr ): Generates compile error if expr is 0. -#ifndef BOOST_STATIC_ASSERT - #ifdef _MSC_VER - // MSVC6 (_MSC_VER < 1300) fails for use of __LINE__ when /Zl is specified - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / (int) !!(expr) - 1] ) - #else - // Some other compilers fail when declaring same function multiple times in class, - // so differentiate them by line - #define BOOST_STATIC_ASSERT( expr ) \ - void blargg_failed_( int (*arg) [2 / !!(expr) - 1] [__LINE__] ) - #endif -#endif - -// BLARGG_COMPILER_HAS_BOOL: If 0, provides bool support for old compiler. If 1, -// compiler is assumed to support bool. If undefined, availability is determined. -#ifndef BLARGG_COMPILER_HAS_BOOL - #if defined (__MWERKS__) - #if !__option(bool) - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (_MSC_VER) - #if _MSC_VER < 1100 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif - #elif defined (__GNUC__) - // supports bool - #elif __cplusplus < 199711 - #define BLARGG_COMPILER_HAS_BOOL 0 - #endif -#endif -#if defined (BLARGG_COMPILER_HAS_BOOL) && !BLARGG_COMPILER_HAS_BOOL - // If you get errors here, modify your blargg_config.h file - typedef int bool; - const bool true = 1; - const bool false = 0; -#endif - -// blargg_long/blargg_ulong = at least 32 bits, int if it's big enough - -#if INT_MAX < 0x7FFFFFFF || LONG_MAX == 0x7FFFFFFF - typedef long blargg_long; -#else - typedef int blargg_long; -#endif - -#if UINT_MAX < 0xFFFFFFFF || ULONG_MAX == 0xFFFFFFFF - typedef unsigned long blargg_ulong; -#else - typedef unsigned blargg_ulong; -#endif - -// BOOST::int8_t etc. - -// HAVE_STDINT_H: If defined, use for int8_t etc. -#if defined (HAVE_STDINT_H) - #include - #define BOOST - -// HAVE_INTTYPES_H: If defined, use for int8_t etc. -#elif defined (HAVE_INTTYPES_H) - #include - #define BOOST - -#else - struct BOOST - { - #if UCHAR_MAX == 0xFF && SCHAR_MAX == 0x7F - typedef signed char int8_t; - typedef unsigned char uint8_t; - #else - // No suitable 8-bit type available - typedef struct see_blargg_common_h int8_t; - typedef struct see_blargg_common_h uint8_t; - #endif - - #if USHRT_MAX == 0xFFFF - typedef short int16_t; - typedef unsigned short uint16_t; - #else - // No suitable 16-bit type available - typedef struct see_blargg_common_h int16_t; - typedef struct see_blargg_common_h uint16_t; - #endif - - #if ULONG_MAX == 0xFFFFFFFF - typedef long int32_t; - typedef unsigned long uint32_t; - #elif UINT_MAX == 0xFFFFFFFF - typedef int int32_t; - typedef unsigned int uint32_t; - #else - // No suitable 32-bit type available - typedef struct see_blargg_common_h int32_t; - typedef struct see_blargg_common_h uint32_t; - #endif - }; -#endif - -#endif -#endif diff --git a/waterbox/sameboy/snes_spc/blargg_config.h b/waterbox/sameboy/snes_spc/blargg_config.h deleted file mode 100644 index 9dc69db836..0000000000 --- a/waterbox/sameboy/snes_spc/blargg_config.h +++ /dev/null @@ -1,24 +0,0 @@ -// snes_spc 0.9.0 user configuration file. Don't replace when updating library. - -// snes_spc 0.9.0 -#ifndef BLARGG_CONFIG_H -#define BLARGG_CONFIG_H - -// Uncomment to disable debugging checks -//#define NDEBUG 1 - -// Uncomment to enable platform-specific (and possibly non-portable) optimizations -//#define BLARGG_NONPORTABLE 1 - -// Uncomment if automatic byte-order determination doesn't work -//#define BLARGG_BIG_ENDIAN 1 - -// Uncomment if you get errors in the bool section of blargg_common.h -//#define BLARGG_COMPILER_HAS_BOOL 1 - -// Use standard config.h if present -#ifdef HAVE_CONFIG_H - #include "config.h" -#endif - -#endif diff --git a/waterbox/sameboy/snes_spc/blargg_endian.h b/waterbox/sameboy/snes_spc/blargg_endian.h deleted file mode 100644 index f2daca6416..0000000000 --- a/waterbox/sameboy/snes_spc/blargg_endian.h +++ /dev/null @@ -1,185 +0,0 @@ -// CPU Byte Order Utilities - -// snes_spc 0.9.0 -#ifndef BLARGG_ENDIAN -#define BLARGG_ENDIAN - -#include "blargg_common.h" - -// BLARGG_CPU_CISC: Defined if CPU has very few general-purpose registers (< 16) -#if defined (_M_IX86) || defined (_M_IA64) || defined (__i486__) || \ - defined (__x86_64__) || defined (__ia64__) || defined (__i386__) - #define BLARGG_CPU_X86 1 - #define BLARGG_CPU_CISC 1 -#endif - -#if defined (__powerpc__) || defined (__ppc__) || defined (__POWERPC__) || defined (__powerc) - #define BLARGG_CPU_POWERPC 1 - #define BLARGG_CPU_RISC 1 -#endif - -// BLARGG_BIG_ENDIAN, BLARGG_LITTLE_ENDIAN: Determined automatically, otherwise only -// one may be #defined to 1. Only needed if something actually depends on byte order. -#if !defined (BLARGG_BIG_ENDIAN) && !defined (BLARGG_LITTLE_ENDIAN) -#ifdef __GLIBC__ - // GCC handles this for us - #include - #if __BYTE_ORDER == __LITTLE_ENDIAN - #define BLARGG_LITTLE_ENDIAN 1 - #elif __BYTE_ORDER == __BIG_ENDIAN - #define BLARGG_BIG_ENDIAN 1 - #endif -#else - -#if defined (LSB_FIRST) || defined (__LITTLE_ENDIAN__) || BLARGG_CPU_X86 || \ - (defined (LITTLE_ENDIAN) && LITTLE_ENDIAN+0 != 1234) - #define BLARGG_LITTLE_ENDIAN 1 -#endif - -#if defined (MSB_FIRST) || defined (__BIG_ENDIAN__) || defined (WORDS_BIGENDIAN) || \ - defined (__sparc__) || BLARGG_CPU_POWERPC || \ - (defined (BIG_ENDIAN) && BIG_ENDIAN+0 != 4321) - #define BLARGG_BIG_ENDIAN 1 -#elif !defined (__mips__) - // No endian specified; assume little-endian, since it's most common - #define BLARGG_LITTLE_ENDIAN 1 -#endif -#endif -#endif - -#if BLARGG_LITTLE_ENDIAN && BLARGG_BIG_ENDIAN - #undef BLARGG_LITTLE_ENDIAN - #undef BLARGG_BIG_ENDIAN -#endif - -inline void blargg_verify_byte_order() -{ - #ifndef NDEBUG - #if BLARGG_BIG_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i == 0 ); - #elif BLARGG_LITTLE_ENDIAN - volatile int i = 1; - assert( *(volatile char*) &i != 0 ); - #endif - #endif -} - -inline unsigned get_le16( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [1] << 8 | - (unsigned) ((unsigned char const*) p) [0]; -} - -inline unsigned get_be16( void const* p ) -{ - return (unsigned) ((unsigned char const*) p) [0] << 8 | - (unsigned) ((unsigned char const*) p) [1]; -} - -inline blargg_ulong get_le32( void const* p ) -{ - return (blargg_ulong) ((unsigned char const*) p) [3] << 24 | - (blargg_ulong) ((unsigned char const*) p) [2] << 16 | - (blargg_ulong) ((unsigned char const*) p) [1] << 8 | - (blargg_ulong) ((unsigned char const*) p) [0]; -} - -inline blargg_ulong get_be32( void const* p ) -{ - return (blargg_ulong) ((unsigned char const*) p) [0] << 24 | - (blargg_ulong) ((unsigned char const*) p) [1] << 16 | - (blargg_ulong) ((unsigned char const*) p) [2] << 8 | - (blargg_ulong) ((unsigned char const*) p) [3]; -} - -inline void set_le16( void* p, unsigned n ) -{ - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [0] = (unsigned char) n; -} - -inline void set_be16( void* p, unsigned n ) -{ - ((unsigned char*) p) [0] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) n; -} - -inline void set_le32( void* p, blargg_ulong n ) -{ - ((unsigned char*) p) [0] = (unsigned char) n; - ((unsigned char*) p) [1] = (unsigned char) (n >> 8); - ((unsigned char*) p) [2] = (unsigned char) (n >> 16); - ((unsigned char*) p) [3] = (unsigned char) (n >> 24); -} - -inline void set_be32( void* p, blargg_ulong n ) -{ - ((unsigned char*) p) [3] = (unsigned char) n; - ((unsigned char*) p) [2] = (unsigned char) (n >> 8); - ((unsigned char*) p) [1] = (unsigned char) (n >> 16); - ((unsigned char*) p) [0] = (unsigned char) (n >> 24); -} - -#if BLARGG_NONPORTABLE - // Optimized implementation if byte order is known - #if BLARGG_LITTLE_ENDIAN - #define GET_LE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_LE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_LE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_LE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - #elif BLARGG_BIG_ENDIAN - #define GET_BE16( addr ) (*(BOOST::uint16_t*) (addr)) - #define GET_BE32( addr ) (*(BOOST::uint32_t*) (addr)) - #define SET_BE16( addr, data ) (void) (*(BOOST::uint16_t*) (addr) = (data)) - #define SET_BE32( addr, data ) (void) (*(BOOST::uint32_t*) (addr) = (data)) - - #if BLARGG_CPU_POWERPC - // PowerPC has special byte-reversed instructions - #if defined (__MWERKS__) - #define GET_LE16( addr ) (__lhbrx( addr, 0 )) - #define GET_LE32( addr ) (__lwbrx( addr, 0 )) - #define SET_LE16( addr, in ) (__sthbrx( in, addr, 0 )) - #define SET_LE32( addr, in ) (__stwbrx( in, addr, 0 )) - #elif defined (__GNUC__) - #define GET_LE16( addr ) ({unsigned ppc_lhbrx_; asm( "lhbrx %0,0,%1" : "=r" (ppc_lhbrx_) : "r" (addr), "0" (ppc_lhbrx_) ); ppc_lhbrx_;}) - #define GET_LE32( addr ) ({unsigned ppc_lwbrx_; asm( "lwbrx %0,0,%1" : "=r" (ppc_lwbrx_) : "r" (addr), "0" (ppc_lwbrx_) ); ppc_lwbrx_;}) - #define SET_LE16( addr, in ) ({asm( "sthbrx %0,0,%1" : : "r" (in), "r" (addr) );}) - #define SET_LE32( addr, in ) ({asm( "stwbrx %0,0,%1" : : "r" (in), "r" (addr) );}) - #endif - #endif - #endif -#endif - -#ifndef GET_LE16 - #define GET_LE16( addr ) get_le16( addr ) - #define SET_LE16( addr, data ) set_le16( addr, data ) -#endif - -#ifndef GET_LE32 - #define GET_LE32( addr ) get_le32( addr ) - #define SET_LE32( addr, data ) set_le32( addr, data ) -#endif - -#ifndef GET_BE16 - #define GET_BE16( addr ) get_be16( addr ) - #define SET_BE16( addr, data ) set_be16( addr, data ) -#endif - -#ifndef GET_BE32 - #define GET_BE32( addr ) get_be32( addr ) - #define SET_BE32( addr, data ) set_be32( addr, data ) -#endif - -// auto-selecting versions - -inline void set_le( BOOST::uint16_t* p, unsigned n ) { SET_LE16( p, n ); } -inline void set_le( BOOST::uint32_t* p, blargg_ulong n ) { SET_LE32( p, n ); } -inline void set_be( BOOST::uint16_t* p, unsigned n ) { SET_BE16( p, n ); } -inline void set_be( BOOST::uint32_t* p, blargg_ulong n ) { SET_BE32( p, n ); } -inline unsigned get_le( BOOST::uint16_t* p ) { return GET_LE16( p ); } -inline blargg_ulong get_le( BOOST::uint32_t* p ) { return GET_LE32( p ); } -inline unsigned get_be( BOOST::uint16_t* p ) { return GET_BE16( p ); } -inline blargg_ulong get_be( BOOST::uint32_t* p ) { return GET_BE32( p ); } - -#endif diff --git a/waterbox/sameboy/snes_spc/blargg_source.h b/waterbox/sameboy/snes_spc/blargg_source.h deleted file mode 100644 index 5e45c4fb42..0000000000 --- a/waterbox/sameboy/snes_spc/blargg_source.h +++ /dev/null @@ -1,100 +0,0 @@ -/* Included at the beginning of library source files, after all other #include lines. -Sets up helpful macros and services used in my source code. They don't need -module an annoying module prefix on their names since they are defined after -all other #include lines. */ - -// snes_spc 0.9.0 -#ifndef BLARGG_SOURCE_H -#define BLARGG_SOURCE_H - -// If debugging is enabled, abort program if expr is false. Meant for checking -// internal state and consistency. A failed assertion indicates a bug in the module. -// void assert( bool expr ); -#include - -// If debugging is enabled and expr is false, abort program. Meant for checking -// caller-supplied parameters and operations that are outside the control of the -// module. A failed requirement indicates a bug outside the module. -// void require( bool expr ); -#undef require -#define require( expr ) assert( expr ) - -// Like printf() except output goes to debug log file. Might be defined to do -// nothing (not even evaluate its arguments). -// void dprintf( const char* format, ... ); -static inline void blargg_dprintf_( const char*, ... ) { } -#undef dprintf -#define dprintf (1) ? (void) 0 : blargg_dprintf_ - -// If enabled, evaluate expr and if false, make debug log entry with source file -// and line. Meant for finding situations that should be examined further, but that -// don't indicate a problem. In all cases, execution continues normally. -#undef check -#define check( expr ) ((void) 0) - -// If expr yields error string, return it from current function, otherwise continue. -#undef RETURN_ERR -#define RETURN_ERR( expr ) do { \ - blargg_err_t blargg_return_err_ = (expr); \ - if ( blargg_return_err_ ) return blargg_return_err_; \ - } while ( 0 ) - -// If ptr is 0, return out of memory error string. -#undef CHECK_ALLOC -#define CHECK_ALLOC( ptr ) do { if ( (ptr) == 0 ) return "Out of memory"; } while ( 0 ) - -// Avoid any macros which evaluate their arguments multiple times -#undef min -#undef max - -#define DEF_MIN_MAX( type ) \ - static inline type min( type x, type y ) { if ( x < y ) return x; return y; }\ - static inline type max( type x, type y ) { if ( y < x ) return x; return y; } - -DEF_MIN_MAX( int ) -DEF_MIN_MAX( unsigned ) -DEF_MIN_MAX( long ) -DEF_MIN_MAX( unsigned long ) -DEF_MIN_MAX( float ) -DEF_MIN_MAX( double ) - -#undef DEF_MIN_MAX - -/* -// using const references generates crappy code, and I am currenly only using these -// for built-in types, so they take arguments by value - -// TODO: remove -inline int min( int x, int y ) -template -inline T min( T x, T y ) -{ - if ( x < y ) - return x; - return y; -} - -template -inline T max( T x, T y ) -{ - if ( x < y ) - return y; - return x; -} -*/ - -// TODO: good idea? bad idea? -#undef byte -#define byte byte_ -typedef unsigned char byte; - -// deprecated -#define BLARGG_CHECK_ALLOC CHECK_ALLOC -#define BLARGG_RETURN_ERR RETURN_ERR - -// BLARGG_SOURCE_BEGIN: If defined, #included, allowing redefition of dprintf and check -#ifdef BLARGG_SOURCE_BEGIN - #include BLARGG_SOURCE_BEGIN -#endif - -#endif diff --git a/waterbox/sameboy/snes_spc/changes.txt b/waterbox/sameboy/snes_spc/changes.txt deleted file mode 100644 index 33661832b4..0000000000 --- a/waterbox/sameboy/snes_spc/changes.txt +++ /dev/null @@ -1,107 +0,0 @@ -snes_spc Change Log -------------------- - -snes_spc 0.9.0 --------------- -- Improved documentation - -- SPC: Added spc_skip() function for quickly seeking in an SPC music -file. Runs 3-4x faster than normal playback using the fast DSP (or about -43-60X real-time on my 400 MHz Mac). - -- SPC: Added spc_set_tempo() to change tempo of SPC music playback. - -- SPC: Sample generation is now corrected to generate exactly one pair -of samples every 32 clocks without exception. Before it could generate a -few samples more or less depending on how far ahead or behind DSP was at -the moment. - -- SPC: Changed spc_reset() and spc_soft_reset() to also reset output -buffer (see spc.h). - -- SPC: Fixed minor timer counting bug. - -- SPC: Stack pointer wrap-around is now emulated (and without any -noticeable performance hit). - -- SPC: Runs about 5% faster due to various optimizations. - -- SPC: Found way to make fast DSP register accesses cycle-accurate in -most cases, without reducing performance. Allows fast DSP to pass most -of my validation tests. - -- DSP: Added surround disable support to fast DSP again. - -- DSP: Improved voice un-muting to take effect immediately on fast DSP. - -- DSP: Noise shift register now starts at 0x4000 instead of 0x4001 as it -incorrectly did before. - -- Converted library to C++ code internally. A C interface is still -included in spc.h and dsp.h. Note that these are different than the -previous interface, so your code will require minor changes: - - Old SPC code New SPC code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #include "spc/spc.h" #include "snes_spc/spc.h" - - snes_spc_t* spc; SNES_SPC* spc; - spc = malloc( sizeof (snes_spc_t) ); spc = spc_new(); - spc_init( spc ); - - spc_end_frame( time ); spc_end_frame( spc, time ); - /* etc. */ - - /* done using SPC */ spc_delete( spc ); - - - Old DSP code New DSP code - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - #include "spc/spc_dsp.h" #include "snes_spc/dsp.h" - - spc_dsp_init( ram ); SPC_DSP* dsp; - dsp = spc_dsp_new(); - spc_dsp_init( dsp, ram ); - - spc_dsp_run( count ); spc_dsp_run( dsp, count ); - /* etc. */ - - /* done using DSP */ spc_dsp_delete( dsp ); - - -snes_spc 0.8.0 --------------- -- Added several demos - -- Added high-pass/low-pass filter to better match SNES sound - -- Added save state functionality for SPC and accurate DSP (but not fast -DSP) - -- Added emulation of reset switch on NES (soft reset) - -- Made source more compatible with pre-C99 compilers by eliminating -mid-block declarations - -- SPC: Many S-SMP accuracy improvements, mostly in memory access times - -- SPC: S-SMP speed improvements - -- SPC: Added SPC load/save functions and KON checking to help trim -silence from beginning - -- SPC: Changed spc_init() to have you allocate most of the memory used -by the library so you have more control over it - -- DSP: New highly accurate DSP and faster version derived from same code - -- DSP: Changed prefix from dsp_ to spc_dsp_. Your DSP code will require -changes. - -- DSP: Removed surround disable and gain. Gain can now be done with the -dsp_filter module, and surround disable will probably only be -implemented in the fast DSP at some point. - -- DSP: Changed interface to work in clocks rather than samples, -necessary for the new accurate DSP. Sample output is now done with -separate functions. Your DSP code will require changes. diff --git a/waterbox/sameboy/snes_spc/dsp.cpp b/waterbox/sameboy/snes_spc/dsp.cpp deleted file mode 100644 index 99d0cf5c5e..0000000000 --- a/waterbox/sameboy/snes_spc/dsp.cpp +++ /dev/null @@ -1,48 +0,0 @@ -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "dsp.h" - -#include "SPC_DSP.h" - -/* Copyright (C) 2007 Shay Green. This module is free software; you -can redistribute it and/or modify it under the terms of the GNU Lesser -General Public License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. This -module is distributed in the hope that it will be useful, but WITHOUT ANY -WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS -FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more -details. You should have received a copy of the GNU Lesser General Public -License along with this module; if not, write to the Free Software Foundation, -Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ - -#include "blargg_source.h" - -SPC_DSP* spc_dsp_new( void ) -{ - // be sure constants match - assert( spc_dsp_voice_count == (int) SPC_DSP::voice_count ); - assert( spc_dsp_register_count == (int) SPC_DSP::register_count ); - #if !SPC_NO_COPY_STATE_FUNCS - assert( spc_dsp_state_size == (int) SPC_DSP::state_size ); - #endif - - return new SPC_DSP; -} - -void spc_dsp_delete ( SPC_DSP* s ) { delete s; } -void spc_dsp_init ( SPC_DSP* s, void* ram_64k ) { s->init( ram_64k ); } -void spc_dsp_set_output ( SPC_DSP* s, spc_dsp_sample_t* p, int n ) { s->set_output( p, n ); } -int spc_dsp_sample_count( SPC_DSP const* s ) { return s->sample_count(); } -void spc_dsp_reset ( SPC_DSP* s ) { s->reset(); } -void spc_dsp_soft_reset ( SPC_DSP* s ) { s->soft_reset(); } -int spc_dsp_read ( SPC_DSP const* s, int addr ) { return s->read( addr ); } -void spc_dsp_write ( SPC_DSP* s, int addr, int data ) { s->write( addr, data ); } -void spc_dsp_run ( SPC_DSP* s, int clock_count ) { s->run( clock_count ); } -void spc_dsp_mute_voices ( SPC_DSP* s, int mask ) { s->mute_voices( mask ); } -void spc_dsp_disable_surround( SPC_DSP* s, int disable ) { s->disable_surround( disable ); } -void spc_dsp_load ( SPC_DSP* s, unsigned char const regs [spc_dsp_register_count] ) { s->load( regs ); } - -#if !SPC_NO_COPY_STATE_FUNCS -void spc_dsp_copy_state ( SPC_DSP* s, unsigned char** p, spc_dsp_copy_func_t f ) { s->copy_state( p, f ); } -int spc_dsp_check_kon ( SPC_DSP* s ) { return s->check_kon(); } -#endif diff --git a/waterbox/sameboy/snes_spc/dsp.h b/waterbox/sameboy/snes_spc/dsp.h deleted file mode 100644 index 59867d92f9..0000000000 --- a/waterbox/sameboy/snes_spc/dsp.h +++ /dev/null @@ -1,83 +0,0 @@ -/* SNES SPC-700 DSP emulator C interface (also usable from C++) */ - -/* snes_spc 0.9.0 */ -#ifndef DSP_H -#define DSP_H - -#include - -#ifdef __cplusplus - extern "C" { -#endif - -typedef struct SPC_DSP SPC_DSP; - -/* Creates new DSP emulator. NULL if out of memory. */ -SPC_DSP* spc_dsp_new( void ); - -/* Frees DSP emulator */ -void spc_dsp_delete( SPC_DSP* ); - -/* Initializes DSP and has it use the 64K RAM provided */ -void spc_dsp_init( SPC_DSP*, void* ram_64k ); - -/* Sets destination for output samples. If out is NULL or out_size is 0, -doesn't generate any. */ -typedef short spc_dsp_sample_t; -void spc_dsp_set_output( SPC_DSP*, spc_dsp_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 spc_dsp_sample_count( SPC_DSP const* ); - - -/**** Emulation *****/ - -/* Resets DSP to power-on state */ -void spc_dsp_reset( SPC_DSP* ); - -/* Emulates pressing reset switch on SNES */ -void spc_dsp_soft_reset( SPC_DSP* ); - -/* Reads/writes DSP registers. For accuracy, you must first call spc_dsp_run() */ -/* to catch the DSP up to present. */ -int spc_dsp_read ( SPC_DSP const*, int addr ); -void spc_dsp_write( SPC_DSP*, 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 spc_dsp_run( SPC_DSP*, int clock_count ); - - -/**** Sound control *****/ - -/* Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */ -enum { spc_dsp_voice_count = 8 }; -void spc_dsp_mute_voices( SPC_DSP*, int mask ); - -/* If true, prevents channels and global volumes from being phase-negated. -Only supported by fast DSP; has no effect on accurate DSP. */ -void spc_dsp_disable_surround( SPC_DSP*, int disable ); - - -/**** State save/load *****/ - -/* Resets DSP and uses supplied values to initialize registers */ -enum { spc_dsp_register_count = 128 }; -void spc_dsp_load( SPC_DSP*, unsigned char const regs [spc_dsp_register_count] ); - -/* Saves/loads exact emulator state (accurate DSP only) */ -enum { spc_dsp_state_size = 640 }; /* maximum space needed when saving */ -typedef void (*spc_dsp_copy_func_t)( unsigned char** io, void* state, size_t ); -void spc_dsp_copy_state( SPC_DSP*, unsigned char** io, spc_dsp_copy_func_t ); - -/* Returns non-zero if new key-on events occurred since last call (accurate DSP only) */ -int spc_dsp_check_kon( SPC_DSP* ); - - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/waterbox/sameboy/snes_spc/license.txt b/waterbox/sameboy/snes_spc/license.txt deleted file mode 100644 index 5faba9d48c..0000000000 --- a/waterbox/sameboy/snes_spc/license.txt +++ /dev/null @@ -1,504 +0,0 @@ - GNU LESSER GENERAL PUBLIC LICENSE - Version 2.1, February 1999 - - Copyright (C) 1991, 1999 Free Software Foundation, Inc. - 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - Everyone is permitted to copy and distribute verbatim copies - of this license document, but changing it is not allowed. - -[This is the first released version of the Lesser GPL. It also counts - as the successor of the GNU Library Public License, version 2, hence - the version number 2.1.] - - Preamble - - The licenses for most software are designed to take away your -freedom to share and change it. By contrast, the GNU General Public -Licenses are intended to guarantee your freedom to share and change -free software--to make sure the software is free for all its users. - - This license, the Lesser General Public License, applies to some -specially designated software packages--typically libraries--of the -Free Software Foundation and other authors who decide to use it. You -can use it too, but we suggest you first think carefully about whether -this license or the ordinary General Public License is the better -strategy to use in any particular case, based on the explanations below. - - When we speak of free software, we are referring to freedom of use, -not price. Our General Public Licenses are designed to make sure that -you have the freedom to distribute copies of free software (and charge -for this service if you wish); that you receive source code or can get -it if you want it; that you can change the software and use pieces of -it in new free programs; and that you are informed that you can do -these things. - - To protect your rights, we need to make restrictions that forbid -distributors to deny you these rights or to ask you to surrender these -rights. These restrictions translate to certain responsibilities for -you if you distribute copies of the library or if you modify it. - - For example, if you distribute copies of the library, whether gratis -or for a fee, you must give the recipients all the rights that we gave -you. You must make sure that they, too, receive or can get the source -code. If you link other code with the library, you must provide -complete object files to the recipients, so that they can relink them -with the library after making changes to the library and recompiling -it. And you must show them these terms so they know their rights. - - We protect your rights with a two-step method: (1) we copyright the -library, and (2) we offer you this license, which gives you legal -permission to copy, distribute and/or modify the library. - - To protect each distributor, we want to make it very clear that -there is no warranty for the free library. Also, if the library is -modified by someone else and passed on, the recipients should know -that what they have is not the original version, so that the original -author's reputation will not be affected by problems that might be -introduced by others. - - Finally, software patents pose a constant threat to the existence of -any free program. We wish to make sure that a company cannot -effectively restrict the users of a free program by obtaining a -restrictive license from a patent holder. Therefore, we insist that -any patent license obtained for a version of the library must be -consistent with the full freedom of use specified in this license. - - Most GNU software, including some libraries, is covered by the -ordinary GNU General Public License. This license, the GNU Lesser -General Public License, applies to certain designated libraries, and -is quite different from the ordinary General Public License. We use -this license for certain libraries in order to permit linking those -libraries into non-free programs. - - When a program is linked with a library, whether statically or using -a shared library, the combination of the two is legally speaking a -combined work, a derivative of the original library. The ordinary -General Public License therefore permits such linking only if the -entire combination fits its criteria of freedom. The Lesser General -Public License permits more lax criteria for linking other code with -the library. - - We call this license the "Lesser" General Public License because it -does Less to protect the user's freedom than the ordinary General -Public License. It also provides other free software developers Less -of an advantage over competing non-free programs. These disadvantages -are the reason we use the ordinary General Public License for many -libraries. However, the Lesser license provides advantages in certain -special circumstances. - - For example, on rare occasions, there may be a special need to -encourage the widest possible use of a certain library, so that it becomes -a de-facto standard. To achieve this, non-free programs must be -allowed to use the library. A more frequent case is that a free -library does the same job as widely used non-free libraries. In this -case, there is little to gain by limiting the free library to free -software only, so we use the Lesser General Public License. - - In other cases, permission to use a particular library in non-free -programs enables a greater number of people to use a large body of -free software. For example, permission to use the GNU C Library in -non-free programs enables many more people to use the whole GNU -operating system, as well as its variant, the GNU/Linux operating -system. - - Although the Lesser General Public License is Less protective of the -users' freedom, it does ensure that the user of a program that is -linked with the Library has the freedom and the wherewithal to run -that program using a modified version of the Library. - - The precise terms and conditions for copying, distribution and -modification follow. Pay close attention to the difference between a -"work based on the library" and a "work that uses the library". The -former contains code derived from the library, whereas the latter must -be combined with the library in order to run. - - GNU LESSER GENERAL PUBLIC LICENSE - TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION - - 0. This License Agreement applies to any software library or other -program which contains a notice placed by the copyright holder or -other authorized party saying it may be distributed under the terms of -this Lesser General Public License (also called "this License"). -Each licensee is addressed as "you". - - A "library" means a collection of software functions and/or data -prepared so as to be conveniently linked with application programs -(which use some of those functions and data) to form executables. - - The "Library", below, refers to any such software library or work -which has been distributed under these terms. A "work based on the -Library" means either the Library or any derivative work under -copyright law: that is to say, a work containing the Library or a -portion of it, either verbatim or with modifications and/or translated -straightforwardly into another language. (Hereinafter, translation is -included without limitation in the term "modification".) - - "Source code" for a work means the preferred form of the work for -making modifications to it. For a library, complete source code means -all the source code for all modules it contains, plus any associated -interface definition files, plus the scripts used to control compilation -and installation of the library. - - Activities other than copying, distribution and modification are not -covered by this License; they are outside its scope. The act of -running a program using the Library is not restricted, and output from -such a program is covered only if its contents constitute a work based -on the Library (independent of the use of the Library in a tool for -writing it). Whether that is true depends on what the Library does -and what the program that uses the Library does. - - 1. You may copy and distribute verbatim copies of the Library's -complete source code as you receive it, in any medium, provided that -you conspicuously and appropriately publish on each copy an -appropriate copyright notice and disclaimer of warranty; keep intact -all the notices that refer to this License and to the absence of any -warranty; and distribute a copy of this License along with the -Library. - - You may charge a fee for the physical act of transferring a copy, -and you may at your option offer warranty protection in exchange for a -fee. - - 2. You may modify your copy or copies of the Library or any portion -of it, thus forming a work based on the Library, and copy and -distribute such modifications or work under the terms of Section 1 -above, provided that you also meet all of these conditions: - - a) The modified work must itself be a software library. - - b) You must cause the files modified to carry prominent notices - stating that you changed the files and the date of any change. - - c) You must cause the whole of the work to be licensed at no - charge to all third parties under the terms of this License. - - d) If a facility in the modified Library refers to a function or a - table of data to be supplied by an application program that uses - the facility, other than as an argument passed when the facility - is invoked, then you must make a good faith effort to ensure that, - in the event an application does not supply such function or - table, the facility still operates, and performs whatever part of - its purpose remains meaningful. - - (For example, a function in a library to compute square roots has - a purpose that is entirely well-defined independent of the - application. Therefore, Subsection 2d requires that any - application-supplied function or table used by this function must - be optional: if the application does not supply it, the square - root function must still compute square roots.) - -These requirements apply to the modified work as a whole. If -identifiable sections of that work are not derived from the Library, -and can be reasonably considered independent and separate works in -themselves, then this License, and its terms, do not apply to those -sections when you distribute them as separate works. But when you -distribute the same sections as part of a whole which is a work based -on the Library, the distribution of the whole must be on the terms of -this License, whose permissions for other licensees extend to the -entire whole, and thus to each and every part regardless of who wrote -it. - -Thus, it is not the intent of this section to claim rights or contest -your rights to work written entirely by you; rather, the intent is to -exercise the right to control the distribution of derivative or -collective works based on the Library. - -In addition, mere aggregation of another work not based on the Library -with the Library (or with a work based on the Library) on a volume of -a storage or distribution medium does not bring the other work under -the scope of this License. - - 3. You may opt to apply the terms of the ordinary GNU General Public -License instead of this License to a given copy of the Library. To do -this, you must alter all the notices that refer to this License, so -that they refer to the ordinary GNU General Public License, version 2, -instead of to this License. (If a newer version than version 2 of the -ordinary GNU General Public License has appeared, then you can specify -that version instead if you wish.) Do not make any other change in -these notices. - - Once this change is made in a given copy, it is irreversible for -that copy, so the ordinary GNU General Public License applies to all -subsequent copies and derivative works made from that copy. - - This option is useful when you wish to copy part of the code of -the Library into a program that is not a library. - - 4. You may copy and distribute the Library (or a portion or -derivative of it, under Section 2) in object code or executable form -under the terms of Sections 1 and 2 above provided that you accompany -it with the complete corresponding machine-readable source code, which -must be distributed under the terms of Sections 1 and 2 above on a -medium customarily used for software interchange. - - If distribution of object code is made by offering access to copy -from a designated place, then offering equivalent access to copy the -source code from the same place satisfies the requirement to -distribute the source code, even though third parties are not -compelled to copy the source along with the object code. - - 5. A program that contains no derivative of any portion of the -Library, but is designed to work with the Library by being compiled or -linked with it, is called a "work that uses the Library". Such a -work, in isolation, is not a derivative work of the Library, and -therefore falls outside the scope of this License. - - However, linking a "work that uses the Library" with the Library -creates an executable that is a derivative of the Library (because it -contains portions of the Library), rather than a "work that uses the -library". The executable is therefore covered by this License. -Section 6 states terms for distribution of such executables. - - When a "work that uses the Library" uses material from a header file -that is part of the Library, the object code for the work may be a -derivative work of the Library even though the source code is not. -Whether this is true is especially significant if the work can be -linked without the Library, or if the work is itself a library. The -threshold for this to be true is not precisely defined by law. - - If such an object file uses only numerical parameters, data -structure layouts and accessors, and small macros and small inline -functions (ten lines or less in length), then the use of the object -file is unrestricted, regardless of whether it is legally a derivative -work. (Executables containing this object code plus portions of the -Library will still fall under Section 6.) - - Otherwise, if the work is a derivative of the Library, you may -distribute the object code for the work under the terms of Section 6. -Any executables containing that work also fall under Section 6, -whether or not they are linked directly with the Library itself. - - 6. As an exception to the Sections above, you may also combine or -link a "work that uses the Library" with the Library to produce a -work containing portions of the Library, and distribute that work -under terms of your choice, provided that the terms permit -modification of the work for the customer's own use and reverse -engineering for debugging such modifications. - - You must give prominent notice with each copy of the work that the -Library is used in it and that the Library and its use are covered by -this License. You must supply a copy of this License. If the work -during execution displays copyright notices, you must include the -copyright notice for the Library among them, as well as a reference -directing the user to the copy of this License. Also, you must do one -of these things: - - a) Accompany the work with the complete corresponding - machine-readable source code for the Library including whatever - changes were used in the work (which must be distributed under - Sections 1 and 2 above); and, if the work is an executable linked - with the Library, with the complete machine-readable "work that - uses the Library", as object code and/or source code, so that the - user can modify the Library and then relink to produce a modified - executable containing the modified Library. (It is understood - that the user who changes the contents of definitions files in the - Library will not necessarily be able to recompile the application - to use the modified definitions.) - - b) Use a suitable shared library mechanism for linking with the - Library. A suitable mechanism is one that (1) uses at run time a - copy of the library already present on the user's computer system, - rather than copying library functions into the executable, and (2) - will operate properly with a modified version of the library, if - the user installs one, as long as the modified version is - interface-compatible with the version that the work was made with. - - c) Accompany the work with a written offer, valid for at - least three years, to give the same user the materials - specified in Subsection 6a, above, for a charge no more - than the cost of performing this distribution. - - d) If distribution of the work is made by offering access to copy - from a designated place, offer equivalent access to copy the above - specified materials from the same place. - - e) Verify that the user has already received a copy of these - materials or that you have already sent this user a copy. - - For an executable, the required form of the "work that uses the -Library" must include any data and utility programs needed for -reproducing the executable from it. However, as a special exception, -the materials to be distributed need not include anything that is -normally distributed (in either source or binary form) with the major -components (compiler, kernel, and so on) of the operating system on -which the executable runs, unless that component itself accompanies -the executable. - - It may happen that this requirement contradicts the license -restrictions of other proprietary libraries that do not normally -accompany the operating system. Such a contradiction means you cannot -use both them and the Library together in an executable that you -distribute. - - 7. You may place library facilities that are a work based on the -Library side-by-side in a single library together with other library -facilities not covered by this License, and distribute such a combined -library, provided that the separate distribution of the work based on -the Library and of the other library facilities is otherwise -permitted, and provided that you do these two things: - - a) Accompany the combined library with a copy of the same work - based on the Library, uncombined with any other library - facilities. This must be distributed under the terms of the - Sections above. - - b) Give prominent notice with the combined library of the fact - that part of it is a work based on the Library, and explaining - where to find the accompanying uncombined form of the same work. - - 8. You may not copy, modify, sublicense, link with, or distribute -the Library except as expressly provided under this License. Any -attempt otherwise to copy, modify, sublicense, link with, or -distribute the Library is void, and will automatically terminate your -rights under this License. However, parties who have received copies, -or rights, from you under this License will not have their licenses -terminated so long as such parties remain in full compliance. - - 9. You are not required to accept this License, since you have not -signed it. However, nothing else grants you permission to modify or -distribute the Library or its derivative works. These actions are -prohibited by law if you do not accept this License. Therefore, by -modifying or distributing the Library (or any work based on the -Library), you indicate your acceptance of this License to do so, and -all its terms and conditions for copying, distributing or modifying -the Library or works based on it. - - 10. Each time you redistribute the Library (or any work based on the -Library), the recipient automatically receives a license from the -original licensor to copy, distribute, link with or modify the Library -subject to these terms and conditions. You may not impose any further -restrictions on the recipients' exercise of the rights granted herein. -You are not responsible for enforcing compliance by third parties with -this License. - - 11. If, as a consequence of a court judgment or allegation of patent -infringement or for any other reason (not limited to patent issues), -conditions are imposed on you (whether by court order, agreement or -otherwise) that contradict the conditions of this License, they do not -excuse you from the conditions of this License. If you cannot -distribute so as to satisfy simultaneously your obligations under this -License and any other pertinent obligations, then as a consequence you -may not distribute the Library at all. For example, if a patent -license would not permit royalty-free redistribution of the Library by -all those who receive copies directly or indirectly through you, then -the only way you could satisfy both it and this License would be to -refrain entirely from distribution of the Library. - -If any portion of this section is held invalid or unenforceable under any -particular circumstance, the balance of the section is intended to apply, -and the section as a whole is intended to apply in other circumstances. - -It is not the purpose of this section to induce you to infringe any -patents or other property right claims or to contest validity of any -such claims; this section has the sole purpose of protecting the -integrity of the free software distribution system which is -implemented by public license practices. Many people have made -generous contributions to the wide range of software distributed -through that system in reliance on consistent application of that -system; it is up to the author/donor to decide if he or she is willing -to distribute software through any other system and a licensee cannot -impose that choice. - -This section is intended to make thoroughly clear what is believed to -be a consequence of the rest of this License. - - 12. If the distribution and/or use of the Library is restricted in -certain countries either by patents or by copyrighted interfaces, the -original copyright holder who places the Library under this License may add -an explicit geographical distribution limitation excluding those countries, -so that distribution is permitted only in or among countries not thus -excluded. In such case, this License incorporates the limitation as if -written in the body of this License. - - 13. The Free Software Foundation may publish revised and/or new -versions of the Lesser General Public License from time to time. -Such new versions will be similar in spirit to the present version, -but may differ in detail to address new problems or concerns. - -Each version is given a distinguishing version number. If the Library -specifies a version number of this License which applies to it and -"any later version", you have the option of following the terms and -conditions either of that version or of any later version published by -the Free Software Foundation. If the Library does not specify a -license version number, you may choose any version ever published by -the Free Software Foundation. - - 14. If you wish to incorporate parts of the Library into other free -programs whose distribution conditions are incompatible with these, -write to the author to ask for permission. For software which is -copyrighted by the Free Software Foundation, write to the Free -Software Foundation; we sometimes make exceptions for this. Our -decision will be guided by the two goals of preserving the free status -of all derivatives of our free software and of promoting the sharing -and reuse of software generally. - - NO WARRANTY - - 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO -WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. -EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR -OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY -KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE -IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR -PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE -LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME -THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. - - 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN -WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY -AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU -FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR -CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE -LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING -RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A -FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF -SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH -DAMAGES. - - END OF TERMS AND CONDITIONS - - How to Apply These Terms to Your New Libraries - - If you develop a new library, and you want it to be of the greatest -possible use to the public, we recommend making it free software that -everyone can redistribute and change. You can do so by permitting -redistribution under these terms (or, alternatively, under the terms of the -ordinary General Public License). - - To apply these terms, attach the following notices to the library. It is -safest to attach them to the start of each source file to most effectively -convey the exclusion of warranty; and each file should have at least the -"copyright" line and a pointer to where the full notice is found. - - - Copyright (C) - - This library 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 library 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 library; if not, write to the Free Software - Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA - -Also add information on how to contact you by electronic and paper mail. - -You should also get your employer (if you work as a programmer) or your -school, if any, to sign a "copyright disclaimer" for the library, if -necessary. Here is a sample; alter the names: - - Yoyodyne, Inc., hereby disclaims all copyright interest in the - library `Frob' (a library for tweaking knobs) written by James Random Hacker. - - , 1 April 1990 - Ty Coon, President of Vice - -That's all there is to it! - - diff --git a/waterbox/sameboy/snes_spc/readme.txt b/waterbox/sameboy/snes_spc/readme.txt deleted file mode 100644 index 979913f0ac..0000000000 --- a/waterbox/sameboy/snes_spc/readme.txt +++ /dev/null @@ -1,86 +0,0 @@ -snes_spc 0.9.0: SNES SPC-700 APU Emulator ------------------------------------------ -This library includes a full SPC emulator and an S-DSP emulator that can -be used on its own. Two S-DSP emulators are available: a highly accurate -one for use in a SNES emulator, and a 3x faster one for use in an SPC -music player or a resource-limited SNES emulator. - -* Can be used from C and C++ code -* Full SPC-700 APU emulator with cycle accuracy in most cases -* Loads, plays, and saves SPC music files -* Can save and load exact full emulator state -* DSP voice muting, surround sound disable, and song tempo adjustment -* Uses 7% CPU average on 400 MHz Mac to play an SPC using fast DSP - -The accurate DSP emulator is based on past research by others and -hundreds of hours of recent research by me. It passes over a hundred -strenuous timing and behavior validation tests that were also run on the -SNES. As far as I know, it's the first DSP emulator with cycle accuracy, -properly emulating every DSP register and memory access at the exact SPC -cycle it occurs at, whereas previous DSP emulators emulated these only -to the nearest sample (which occurs every 32 clocks). - -Author : Shay Green -Website: http://www.slack.net/~ant/ -Forum : http://groups.google.com/group/blargg-sound-libs -License: GNU Lesser General Public License (LGPL) - - -Getting Started ---------------- -Build a program consisting of demo/play_spc.c, demo/demo_util.c, -demo/wave_writer.c, and all source files in snes_spc/. Put an SPC music -file in the same directory and name it "test.spc". Running the program -should generate the recording "out.wav". - -Read snes_spc.txt for more information. Post to the discussion forum for -assistance. - - -Files ------ -snes_spc.txt Documentation -changes.txt Change log -license.txt GNU LGPL license - -demo/ - play_spc.c Records SPC file to wave sound file - benchmark.c Finds how fast emulator runs on your computer - trim_spc.c Trims silence off beginning of an SPC file - save_state.c Saves/loads exact emulator state to/from file - comm.c Communicates with SPC how SNES would - demo_util.h General utility functions used by demos - demo_util.c - wave_writer.h WAVE sound file writer used for demo output - wave_writer.c - -fast_dsp/ Optional standalone fast DSP emulator - SPC_DSP.h To use with full SPC emulator, move into - SPC_DSP.cpp snes_spc/ and replace original files - -snes_spc/ Library sources - blargg_config.h Configuration (modify as necessary) - - spc.h C interface to SPC emulator and sound filter - spc.cpp - - SPC_Filter.h Optional filter to make sound more authentic - SPC_Filter.cpp - - SNES_SPC.h Full SPC emulator - SNES_SPC.cpp - SNES_SPC_misc.cpp - SNES_SPC_state.cpp - SPC_CPU.h - - dsp.h C interface to DSP emulator - dsp.cpp - - SPC_DSP.h Standalone accurate DSP emulator - SPC_DSP.cpp - blargg_common.h - blargg_endian.h - blargg_source.h - --- -Shay Green diff --git a/waterbox/sameboy/snes_spc/snes_spc.txt b/waterbox/sameboy/snes_spc/snes_spc.txt deleted file mode 100644 index d37b34347f..0000000000 --- a/waterbox/sameboy/snes_spc/snes_spc.txt +++ /dev/null @@ -1,318 +0,0 @@ -snes_spc 0.9.0: SNES SPC-700 APU Emulator ------------------------------------------ -Author : Shay Green -Website: http://www.slack.net/~ant/ -Forum : http://groups.google.com/group/blargg-sound-libs -License: GNU Lesser General Public License (LGPL) - - -Contents --------- -* C and C++ Interfaces -* Overview -* Full SPC Emulation -* DSP Emulation -* SPC Music Playback -* State Copying -* Library Compilation -* Error handling -* Solving Problems -* Accurate S-DSP Limitations -* Fast S-DSP Limitations -* S-SMP Limitations -* To Do -* Thanks - - -C and C++ Interfaces --------------------- -The library includes a C interface in spc.h and dsp.h, which can be used -from C and C++. This C interface is referred to in the following -documentation. If you're building this as a shared library (rather than -linking statically), you should use the C interface since it will change -less in future versions. - -The native C++ interface is in the header files SNES_SPC.h, SPC_DSP.h, -and SPC_Filter.h, and the two interfaces can be freely mixed in C++ -code. Conversion between the two interfaces generally follows a pattern: - - C interface C++ interface - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - SNES_SPC* spc; SNES_SPC* spc; - - spc = spc_new(); spc = new SNES_SPC; - - spc_play( spc, count, buf ); spc->play( count, buf ); - - spc_sample_rate SNES_SPC::sample_rate - - spc_delete( spc ); delete spc; - - -Overview --------- -There are three main roles for this library: -* Full SPC emulation in a SNES emulator -* DSP emulation in a SNES emulator (where you emulate the SMP CPU) -* SPC playback in an SPC music file player - -Each of these uses are described separately below. - - -Full SPC Emulation ------------------- -See spc.h for full function reference (SNES_SPC.h if using C++). - -* Create SPC emulator with spc_new() and check for NULL. - -* Call spc_init_rom() with a pointer to the 64-byte IPL ROM dump (not -included with library). - -* When your emulated SNES is powered on, call spc_reset(). When the -reset switch is pressed, call spc_soft_reset(). - -* Call spc_set_output() with your output buffer, then do emulation. - -* When the SNES CPU accesses APU ports, call spc_read_port() and -spc_write_port(). - -* When your emulator's timebase is going back to 0, call -spc_end_frame(), usually at the end of a video frame or scanline. - -* Periodically play samples from your buffer. Use spc_sample_count() to -find out how many samples have been written, then spc_set_output() after -you've made more space in your buffer. - -* Save/load full emulator state with spc_copy_state(). - -* You can save as an SPC music file with spc_save_spc(). - -* When done, use spc_delete() to free memory. - - -DSP Emulation -------------- -See dsp.h for full function reference (SPC_DSP.h if using C++). - -* Create DSP emulator with spc_dsp_new() and check for NULL. - -* Let the DSP know where your 64K RAM is with spc_dsp_init(). - -* When your emulated SNES is powered on, call spc_dsp_reset(). When the -reset switch is pressed, call spc_dsp_soft_reset(). - -* Call spc_dsp_set_output() with your output buffer, then do emulation. - -* Use spc_dsp_run() to run DSP for specified number of clocks (1024000 -per second). Every 32 clocks a pair of samples is added to your output -buffer. - -* Use spc_dsp_read() and spc_dsp_write() to handle DSP reads/writes from -the S-SMP. Before calling these always catch the DSP up to present time -with spc_dsp_run(). - -* Periodically play samples from your buffer. Use spc_dsp_sample_count() -to find out how many samples have been written, then -spc_dsp_set_output() after you've made more space in your buffer. - -* Use spc_dsp_copy_state() to save/load full DSP state. - -* When done, use spc_delete() to free memory. - - -SPC Music Playback ------------------- -See spc.h for full function reference (SNES_SPC.h if using C++). - -* Create SPC emulator with spc_new() and check for NULL. - -* Load SPC with spc_load_spc() and check for error. - -* Optionally cear echo buffer with spc_clear_echo(). Many SPCs have -garbage in echo buffer, which causes noise at the beginning. - -* Generate samples as needed with spc_play(). - -* When done, use spc_delete() to free memory. - -* For a more complete game music playback library, use Game_Music_Emu - - -State Copying -------------- -The SPC and DSP modules include state save/load functions. They take a -pointer to a pointer to a buffer to store state, and a copy function. -This copy function can either copy data to the buffer or from it, -allowing state save and restore with the same library function. The -internal save state format allows for future expansion without making -previous save states unusable. - -The SPC save state format puts the most important parts first to make it -easier to manually examine. It's organized as follows: - -Offset Size Data -- - - - - - - - - - - - - - - - - - - 0 $10000 SPC RAM -$10000 $10 SMP $F0-$FF registers -$10010 4 SMP $F4-$F8 output registers -$10014 2 PC -$10016 1 A -$10017 1 X -$10018 1 Y -$10019 1 PSW -$1001A 1 SP -$1001B 5 internal -$10020 $80 DSP registers -$100A0 ... internal - - -Library Compilation -------------------- -While this library is in C++, it has been written to easily link in a C -program *without* needing the standard C++ library. It doesn't use -exception handling or run-time type information (RTTI), so you can -disable these in your C++ compiler to increase efficiency. - -If you're building a shared library (DLL), I recommend only exporting -the C interfaces in spc.h and dsp.h, as the C++ interfaces expose -implementation details that will break link compatibility across -versions. - -If you're using C and compiling with GCC, I recommend the following -command-line options when compiling the library source, otherwise GCC -will insert calls to the standard C++ library and require that it be -linked in: - - -fno-rtti -fno-exceptions - -For maximum optimization, see the NDEBUG and BLARGG_NONPORTABLE options -in blargg_config. If using GCC, you can enable these by adding the -following command-line options when you invoke gcc. If you encounter -problems, try without -DBLARGG_NONPORTABLE; if that works, contact me so -I can figure out why BLARGG_NONPORTABLE was causing it to fail. - - -O3 -DNDEBUG -DBLARGG_NONPORTABLE -fno-rtti -fno-exceptions - - - -Error handling --------------- -Functions which can fail have a return type of spc_err_t (blargg_err_t -in the C++ interfaces), which is a pointer to an error string (const -char*). If a function is successful it returns NULL. Errors that you can -easily avoid are checked with debug assertions; spc_err_t return values -are only used for genuine run-time errors that can't be easily predicted -in advance (out of memory, I/O errors, incompatible file data). Your -code should check all error values. - -To improve usability for C programmers, C++ programmers unfamiliar with -exceptions, and compatibility with older C++ compilers, the library does -*not* throw any C++ exceptions and uses malloc() instead of the standard -operator new. This means that you *must* check for NULL when creating a -library object with the new operator. - - -Solving Problems ----------------- -If you're having problems, try the following: - -* If you're getting garbled sound, try this simple siren generator in -place of your call to play(). This will quickly tell whether the problem -is in the library or in your code. - - static void play_siren( long count, short* out ) - { - static double a, a2; - while ( count-- ) - *out++ = 0x2000 * sin( a += .1 + .05*sin( a2+=.00005 ) ); - } - -* Enable debugging support in your environment. This enables assertions -and other run-time checks. - -* Turn the compiler's optimizer is off. Sometimes an optimizer generates -bad code. - -* If multiple threads are being used, ensure that only one at a time is -accessing a given set of objects from the library. This library is not -in general thread-safe, though independent objects can be used in -separate threads. - -* If all else fails, see if the demos work. - - -Accurate S-DSP Limitations --------------------------- -* Power-up and soft reset behavior might have slight inaccuracies. - -* Muting (FLG bit 6) behavior when toggling bit very rapidly is not -emulated properly. - -* No other known inaccuracies. Has passed 100+ strenuous tests. - - -Fast S-DSP Limitations ----------------------- -* Uses faster sample calculations except in cases where exact value is -actually important (BRR decoding, and gaussian interpolation combined -with pitch modulation). - -* Stops decoding BRR data when a voice's envelope has released to -silence. - -* Emulates 32 clocks at a time, so DSP register and memory accesses are -all done in a bunch rather than spread out. Even though, some clever -code makes register accesses separated by 40 or so clocks occur with -cycle-accurate timing. - - -S-SMP Limitations ------------------ -* Opcode fetches and indirect pointers are always read directly from -memory, even for the $F0-$FF region, and the DSP is not caught up for -these fetches. - -* Attempts to perversely execute data in registers or an area being -modified by echo will not be emulated properly. - -* Has not been thoroughly tested. - -* Test register ($F0) is not implemented. - -* Echo buffer can overwrite IPL ROM area, and does not correctly update -extra RAM there. - - -To Do ------ -* I'd like feedback on the interface and any ways to improve it. In -particular, the differing features between the accurate and fast DSP -emulators might make it harder to cleanly switch between them without -modifying source code. - -* Finish thorough tests on SMP memory access times. - -* Finish thorough tests on SMP instruction behavior (flags, registers). - -* Finish thorough tests on SMP timers. - -* Finish power-up and reset behavior testing. - -* Come up with best starting conditions to play an SPC and implement in -hardware SNES SPC player for verification. - - -Thanks ------- -Thanks to Anti-Resonance's SPC2ROM and help getting SPCs playing on my -SNES in the first place, then Brad Martin's openspc and Chris Moeller's -openspc++ C++ adaptation, giving me a good SPC emulator to start with -several years ago. Thanks to Richard Bannister, Mahendra Tallur, Shazz, -nenolod, theHobbit, Johan Samuelsson, nes6502, and Micket for helping -test my Game_Music_Emu library. Thanks to hcs for help in converting to -C for the Rockbox port. Thanks to byuu (bsnes author) and pagefault and -Nach (zsnes team) for testing and using my new rewritten DSP in their -emulators. Thanks to anomie for his good SNES documentation and -discussions with me to keep it up to date with my latest findings. --- -Shay Green diff --git a/waterbox/sameboy/snes_spc/spc.cpp b/waterbox/sameboy/snes_spc/spc.cpp deleted file mode 100644 index 4e5a77ef15..0000000000 --- a/waterbox/sameboy/snes_spc/spc.cpp +++ /dev/null @@ -1,74 +0,0 @@ -// snes_spc 0.9.0. http://www.slack.net/~ant/ - -#include "spc.h" - -#include "SNES_SPC.h" -#include "SPC_Filter.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" - -SNES_SPC* spc_new( void ) -{ - // be sure constants match - assert( spc_sample_rate == (int) SNES_SPC::sample_rate ); - assert( spc_rom_size == (int) SNES_SPC::rom_size ); - assert( spc_clock_rate == (int) SNES_SPC::clock_rate ); - assert( spc_clocks_per_sample == (int) SNES_SPC::clocks_per_sample ); - assert( spc_port_count == (int) SNES_SPC::port_count ); - assert( spc_voice_count == (int) SNES_SPC::voice_count ); - assert( spc_tempo_unit == (int) SNES_SPC::tempo_unit ); - assert( spc_file_size == (int) SNES_SPC::spc_file_size ); - #if !SPC_NO_COPY_STATE_FUNCS - assert( spc_state_size == (int) SNES_SPC::state_size ); - #endif - - SNES_SPC* s = new SNES_SPC; - if ( s && s->init() ) - { - delete s; - s = 0; - } - return s; -} - -void spc_delete ( SNES_SPC* s ) { delete s; } -void spc_init_rom ( SNES_SPC* s, unsigned char const r [64] ) { s->init_rom( r ); } -void spc_set_output ( SNES_SPC* s, spc_sample_t* p, int n ) { s->set_output( p, n ); } -int spc_sample_count ( SNES_SPC const* s ) { return s->sample_count(); } -void spc_reset ( SNES_SPC* s ) { s->reset(); } -void spc_soft_reset ( SNES_SPC* s ) { s->soft_reset(); } -int spc_read_port ( SNES_SPC* s, spc_time_t t, int p ) { return s->read_port( t, p ); } -void spc_write_port ( SNES_SPC* s, spc_time_t t, int p, int d ) { s->write_port( t, p, d ); } -void spc_end_frame ( SNES_SPC* s, spc_time_t t ) { s->end_frame( t ); } -void spc_mute_voices ( SNES_SPC* s, int mask ) { s->mute_voices( mask ); } -void spc_disable_surround( SNES_SPC* s, int disable ) { s->disable_surround( disable ); } -void spc_set_tempo ( SNES_SPC* s, int tempo ) { s->set_tempo( tempo ); } -uint8_t* spc_get_ram(SNES_SPC* s) { return s->get_ram(); } -spc_err_t spc_load_spc ( SNES_SPC* s, void const* p, long n ) { return s->load_spc( p, n ); } -void spc_clear_echo ( SNES_SPC* s ) { s->clear_echo(); } -spc_err_t spc_play ( SNES_SPC* s, int count, short* out ) { return s->play( count, out ); } -spc_err_t spc_skip ( SNES_SPC* s, int count ) { return s->skip( count ); } -#if !SPC_NO_COPY_STATE_FUNCS -void spc_copy_state ( SNES_SPC* s, unsigned char** p, spc_copy_func_t f ) { s->copy_state( p, f ); } -void spc_init_header ( void* spc_out ) { SNES_SPC::init_header( spc_out ); } -void spc_save_spc ( SNES_SPC* s, void* spc_out ) { s->save_spc( spc_out ); } -int spc_check_kon ( SNES_SPC* s ) { return s->check_kon(); } -#endif - -SPC_Filter* spc_filter_new( void ) { return new SPC_Filter; } -void spc_filter_delete( SPC_Filter* f ) { delete f; } -void spc_filter_run( SPC_Filter* f, spc_sample_t* p, int s ) { f->run( p, s ); } -void spc_filter_clear( SPC_Filter* f ) { f->clear(); } -void spc_filter_set_gain( SPC_Filter* f, int gain ) { f->set_gain( gain ); } -void spc_filter_set_bass( SPC_Filter* f, int bass ) { f->set_bass( bass ); } diff --git a/waterbox/sameboy/snes_spc/spc.h b/waterbox/sameboy/snes_spc/spc.h deleted file mode 100644 index cf23f3c945..0000000000 --- a/waterbox/sameboy/snes_spc/spc.h +++ /dev/null @@ -1,149 +0,0 @@ -/* SNES SPC-700 APU emulator C interface (also usable from C++) */ - -/* snes_spc 0.9.0 */ -#ifndef SPC_H -#define SPC_H - -#include -#include - -#ifdef __cplusplus - extern "C" { -#endif - -/* Error string return. NULL if success, otherwise error message. */ -typedef const char* spc_err_t; - -typedef struct SNES_SPC SNES_SPC; - -/* Creates new SPC emulator. NULL if out of memory. */ -SNES_SPC* spc_new( void ); - -/* Frees SPC emulator */ -void spc_delete( SNES_SPC* ); - -/* Sample pairs generated per second */ -enum { spc_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 { spc_rom_size = 0x40 }; -void spc_init_rom( SNES_SPC*, unsigned char const rom [spc_rom_size] ); - -/* Sets destination for output samples */ -typedef short spc_sample_t; -void spc_set_output( SNES_SPC*, spc_sample_t* out, int out_size ); - -/* Number of samples written to output since last set */ -int spc_sample_count( SNES_SPC const* ); - -/* Resets SPC to power-on state. This resets your output buffer, so you must -call spc_set_output() after this. */ -void spc_reset( SNES_SPC* ); - -/* Emulates pressing reset switch on SNES. This resets your output buffer, so -you must call spc_set_output() after this. */ -void spc_soft_reset( SNES_SPC* ); - -/* 1024000 SPC clocks per second, sample pair every 32 clocks */ -typedef int spc_time_t; -enum { spc_clock_rate = 1024000 }; -enum { spc_clocks_per_sample = 32 }; - -/* Reads/writes port at specified time */ -enum { spc_port_count = 4 }; -int spc_read_port ( SNES_SPC*, spc_time_t, int port ); -void spc_write_port( SNES_SPC*, spc_time_t, int port, int data ); - -/* Runs SPC to end_time and starts a new time frame at 0 */ -void spc_end_frame( SNES_SPC*, spc_time_t end_time ); - -uint8_t* spc_get_ram(SNES_SPC*); - -/**** Sound control ****/ - -/*Mutes voices corresponding to non-zero bits in mask. Reduces emulation accuracy. */ -enum { spc_voice_count = 8 }; -void spc_mute_voices( SNES_SPC*, int mask ); - -/* If true, prevents channels and global volumes from being phase-negated. -Only supported by fast DSP; has no effect on accurate DSP. */ -void spc_disable_surround( SNES_SPC*, int disable ); - -/* Sets tempo, where spc_tempo_unit = normal, spc_tempo_unit / 2 = half speed, etc. */ -enum { spc_tempo_unit = 0x100 }; -void spc_set_tempo( SNES_SPC*, int ); - - -/**** SPC music playback *****/ - -/* Loads SPC data into emulator. Returns NULL on success, otherwise error string. */ -spc_err_t spc_load_spc( SNES_SPC*, void const* spc_in, long size ); - -/* Clears echo region. Useful after loading an SPC as many have garbage in echo. */ -void spc_clear_echo( SNES_SPC* ); - -/* 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. */ -spc_err_t spc_play( SNES_SPC*, int count, short* out ); - -/* Skips count samples. Several times faster than spc_play(). */ -spc_err_t spc_skip( SNES_SPC*, int count ); - - -/**** State save/load (only available with accurate DSP) ****/ - -/* Saves/loads exact emulator state */ -enum { spc_state_size = 67 * 1024L }; /* maximum space needed when saving */ -typedef void (*spc_copy_func_t)( unsigned char** io, void* state, size_t ); -void spc_copy_state( SNES_SPC*, unsigned char** io, spc_copy_func_t ); - -/* Writes minimal SPC file header to spc_out */ -void spc_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 spc_init_header() for that. */ -enum { spc_file_size = 0x10200 }; /* spc_out must have this many bytes allocated */ -void spc_save_spc( SNES_SPC*, void* spc_out ); - -/* Returns non-zero if new key-on events occurred since last check. Useful for -trimming silence while saving an SPC. */ -int spc_check_kon( SNES_SPC* ); - - -/**** SPC_Filter ****/ - -typedef struct SPC_Filter SPC_Filter; - -/* Creates new filter. NULL if out of memory. */ -SPC_Filter* spc_filter_new( void ); - -/* Frees filter */ -void spc_filter_delete( SPC_Filter* ); - -/* Filters count samples of stereo sound in place. Count must be a multiple of 2. */ -void spc_filter_run( SPC_Filter*, spc_sample_t* io, int count ); - -/* Clears filter to silence */ -void spc_filter_clear( SPC_Filter* ); - -/* Sets gain (volume), where spc_filter_gain_unit is normal. Gains greater than -spc_filter_gain_unit are fine, since output is clamped to 16-bit sample range. */ -enum { spc_filter_gain_unit = 0x100 }; -void spc_filter_set_gain( SPC_Filter*, int gain ); - -/* Sets amount of bass (logarithmic scale) */ -enum { spc_filter_bass_none = 0 }; -enum { spc_filter_bass_norm = 8 }; /* normal amount */ -enum { spc_filter_bass_max = 31 }; -void spc_filter_set_bass( SPC_Filter*, int bass ); - - -#ifdef __cplusplus - } -#endif - -#endif diff --git a/waterbox/sameboy/timing.c b/waterbox/sameboy/timing.c deleted file mode 100644 index bb2501b31a..0000000000 --- a/waterbox/sameboy/timing.c +++ /dev/null @@ -1,161 +0,0 @@ -#include "gb.h" -#ifdef _WIN32 -#define _WIN32_WINNT 0x0500 -#include -#else -#include -#endif - -static void GB_ir_run(GB_gameboy_t *gb) -{ - if (gb->ir_queue_length == 0) return; - if (gb->cycles_since_input_ir_change >= gb->ir_queue[0].delay) { - gb->cycles_since_input_ir_change -= gb->ir_queue[0].delay; - gb->infrared_input = gb->ir_queue[0].state; - gb->ir_queue_length--; - memmove(&gb->ir_queue[0], &gb->ir_queue[1], sizeof(gb->ir_queue[0]) * (gb->ir_queue_length)); - } -} - -static void advance_tima_state_machine(GB_gameboy_t *gb) -{ - if (gb->tima_reload_state == GB_TIMA_RELOADED) { - gb->tima_reload_state = GB_TIMA_RUNNING; - } - else if (gb->tima_reload_state == GB_TIMA_RELOADING) { - gb->tima_reload_state = GB_TIMA_RELOADED; - } -} - -void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) -{ - // Affected by speed boost - gb->dma_cycles += cycles; - - advance_tima_state_machine(gb); - for (int i = 0; i < cycles; i += 4) { - GB_set_internal_div_counter(gb, gb->div_cycles + 4); - } - - if (cycles > 4) { - advance_tima_state_machine(gb); - if (cycles > 8) { - advance_tima_state_machine(gb); - } - } - - uint16_t previous_serial_cycles = gb->serial_cycles; - gb->serial_cycles += cycles; - if (gb->serial_length) { - if ((gb->serial_cycles & gb->serial_length) != (previous_serial_cycles & gb->serial_length)) { - gb->serial_length = 0; - gb->io_registers[GB_IO_SC] &= ~0x80; - /* TODO: Does SB "update" bit by bit? */ - if (gb->serial_transfer_end_callback) { - gb->io_registers[GB_IO_SB] = gb->serial_transfer_end_callback(gb); - } - else { - gb->io_registers[GB_IO_SB] = 0xFF; - } - - gb->io_registers[GB_IO_IF] |= 8; - } - } - - if (gb->cgb_double_speed) { - cycles >>=1; - } - - // Not affected by speed boost - gb->hdma_cycles += cycles; - gb->apu.apu_cycles += cycles; - gb->cycles_since_ir_change += cycles; - gb->cycles_since_input_ir_change += cycles; - gb->cycles_since_epoch += cycles >> 1; - GB_dma_run(gb); - GB_hdma_run(gb); - GB_apu_run(gb); - GB_display_run(gb, cycles); - GB_ir_run(gb); -} - -/* Standard Timers */ -static const unsigned int GB_TAC_RATIOS[] = {1024, 16, 64, 256}; - -static void increase_tima(GB_gameboy_t *gb) -{ - gb->io_registers[GB_IO_TIMA]++; - if (gb->io_registers[GB_IO_TIMA] == 0) { - gb->io_registers[GB_IO_TIMA] = gb->io_registers[GB_IO_TMA]; - gb->io_registers[GB_IO_IF] |= 4; - gb->tima_reload_state = GB_TIMA_RELOADING; - } -} - -static bool counter_overflow_check(uint32_t old, uint32_t new, uint32_t max) -{ - return (old & (max >> 1)) && !(new & (max >> 1)); -} - -void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value) -{ - /* TIMA increases when a specific high-bit becomes a low-bit. */ - value &= INTERNAL_DIV_CYCLES - 1; - if ((gb->io_registers[GB_IO_TAC] & 4) && - counter_overflow_check(gb->div_cycles, value, GB_TAC_RATIOS[gb->io_registers[GB_IO_TAC] & 3])) { - increase_tima(gb); - } - gb->div_cycles = value; -} - -/* - This glitch is based on the expected results of mooneye-gb rapid_toggle test. - This glitch happens because how TIMA is increased, see GB_set_internal_div_counter. - According to GiiBiiAdvance, GBC's behavior is different, but this was not tested or implemented. -*/ -void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac) -{ - /* Glitch only happens when old_tac is enabled. */ - if (!(old_tac & 4)) return; - - unsigned int old_clocks = GB_TAC_RATIOS[old_tac & 3]; - unsigned int new_clocks = GB_TAC_RATIOS[new_tac & 3]; - - /* The bit used for overflow testing must have been 1 */ - if (gb->div_cycles & (old_clocks >> 1)) { - /* And now either the timer must be disabled, or the new bit used for overflow testing be 0. */ - if (!(new_tac & 4) || gb->div_cycles & (new_clocks >> 1)) { - increase_tima(gb); - } - } -} - -void GB_rtc_run(GB_gameboy_t *gb) -{ - if ((gb->rtc_real.high & 0x40) == 0) { /* is timer running? */ - time_t current_time = gb->frontend_rtc_time; - while (gb->last_rtc_second < current_time) { - gb->last_rtc_second++; - if (++gb->rtc_real.seconds == 60) - { - gb->rtc_real.seconds = 0; - if (++gb->rtc_real.minutes == 60) - { - gb->rtc_real.minutes = 0; - if (++gb->rtc_real.hours == 24) - { - gb->rtc_real.hours = 0; - if (++gb->rtc_real.days == 0) - { - if (gb->rtc_real.high & 1) /* Bit 8 of days*/ - { - gb->rtc_real.high |= 0x80; /* Overflow bit */ - } - gb->rtc_real.high ^= 1; - } - } - } - } - } - } -} diff --git a/waterbox/sameboy/timing.h b/waterbox/sameboy/timing.h deleted file mode 100644 index bd3b2f2732..0000000000 --- a/waterbox/sameboy/timing.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef timing_h -#define timing_h -#include "gb.h" - -#ifdef GB_INTERNAL -void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles); -void GB_set_internal_div_counter(GB_gameboy_t *gb, uint32_t value); -void GB_rtc_run(GB_gameboy_t *gb); -void GB_emulate_timer_glitch(GB_gameboy_t *gb, uint8_t old_tac, uint8_t new_tac); - -enum { - GB_TIMA_RUNNING = 0, - GB_TIMA_RELOADING = 1, - GB_TIMA_RELOADED = 2 -}; -#endif - -#endif /* timing_h */ diff --git a/waterbox/sameboy/z80_cpu.c b/waterbox/sameboy/z80_cpu.c deleted file mode 100644 index 9558765263..0000000000 --- a/waterbox/sameboy/z80_cpu.c +++ /dev/null @@ -1,1375 +0,0 @@ -#include -#include -#include "gb.h" - - -typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode); - -static void ill(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_log(gb, "Illegal Opcode. Halting.\n"); - gb->interrupt_enable = 0; - gb->halted = true; -} - -static void nop(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); -} - -static void stop(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - if (gb->io_registers[GB_IO_KEY1] & 0x1) { - /* Make sure we don't leave display_cycles not divisble by 4 in single speed mode */ - if (gb->display_cycles % 4 == 2) { - GB_advance_cycles(gb, 4); - } - - /* Todo: the switch is not instant. We should emulate this. */ - gb->cgb_double_speed ^= true; - gb->io_registers[GB_IO_KEY1] = 0; - } - else { - gb->stopped = true; - } - gb->pc++; -} - -/* Operand naming conventions for functions: - r = 8-bit register - lr = low 8-bit register - hr = high 8-bit register - rr = 16-bit register - d8 = 8-bit imm - d16 = 16-bit imm - d.. = [..] - cc = condition code (z, nz, c, nc) - */ - -static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - uint16_t value; - GB_advance_cycles(gb, 4); - register_id = (opcode >> 4) + 1; - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - value |= GB_read_memory(gb, gb->pc++) << 8; - GB_advance_cycles(gb, 4); - gb->registers[register_id] = value; -} - -static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 4); - register_id = (opcode >> 4) + 1; - GB_write_memory(gb, gb->registers[register_id], gb->registers[GB_REGISTER_AF] >> 8); - GB_advance_cycles(gb, 4); -} - -static void inc_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 8); - register_id = (opcode >> 4) + 1; - gb->registers[register_id]++; -} - -static void inc_hr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 4); - register_id = ((opcode >> 4) + 1) & 0x03; - gb->registers[register_id] += 0x100; - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - - if ((gb->registers[register_id] & 0x0F00) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF00) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} -static void dec_hr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 4); - register_id = ((opcode >> 4) + 1) & 0x03; - gb->registers[register_id] -= 0x100; - gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - - if ((gb->registers[register_id] & 0x0F00) == 0xF00) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF00) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void ld_hr_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 4); - register_id = ((opcode >> 4) + 1) & 0x03; - gb->registers[register_id] &= 0xFF; - gb->registers[register_id] |= GB_read_memory(gb, gb->pc++) << 8; - GB_advance_cycles(gb, 4); -} - -static void rlca(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry = (gb->registers[GB_REGISTER_AF] & 0x8000) != 0; - - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] & 0xFF00) << 1; - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG | 0x0100; - } -} - -static void rla(GB_gameboy_t *gb, uint8_t opcode) -{ - bool bit7 = (gb->registers[GB_REGISTER_AF] & 0x8000) != 0; - bool carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] & 0xFF00) << 1; - if (carry) { - gb->registers[GB_REGISTER_AF] |= 0x0100; - } - if (bit7) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode) -{ - /* Todo: Verify order is correct */ - uint16_t addr; - GB_advance_cycles(gb, 4); - addr = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - addr |= GB_read_memory(gb, gb->pc++) << 8; - GB_advance_cycles(gb, 4); - GB_write_memory(gb, addr, gb->registers[GB_REGISTER_SP] & 0xFF); - GB_advance_cycles(gb, 4); - GB_write_memory(gb, addr+1, gb->registers[GB_REGISTER_SP] >> 8); - GB_advance_cycles(gb, 4); -} - -static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t hl = gb->registers[GB_REGISTER_HL]; - uint16_t rr; - uint8_t register_id; - GB_advance_cycles(gb, 8); - register_id = (opcode >> 4) + 1; - rr = gb->registers[register_id]; - gb->registers[GB_REGISTER_HL] = hl + rr; - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_CARRY_FLAG | GB_HALF_CARRY_FLAG); - - /* The meaning of the Half Carry flag is really hard to track -_- */ - if (((hl & 0xFFF) + (rr & 0xFFF)) & 0x1000) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ( ((unsigned long) hl) + ((unsigned long) rr) & 0x10000) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void ld_a_drr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - register_id = (opcode >> 4) + 1; - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[register_id]) << 8; - GB_advance_cycles(gb, 4); -} - -static void dec_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 8); - register_id = (opcode >> 4) + 1; - gb->registers[register_id]--; -} - -static void inc_lr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - uint8_t value; - GB_advance_cycles(gb, 4); - register_id = (opcode >> 4) + 1; - - value = (gb->registers[register_id] & 0xFF) + 1; - gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value; - - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - - if ((gb->registers[register_id] & 0x0F) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} -static void dec_lr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - uint8_t value; - GB_advance_cycles(gb, 4); - register_id = (opcode >> 4) + 1; - - value = (gb->registers[register_id] & 0xFF) - 1; - gb->registers[register_id] = (gb->registers[register_id] & 0xFF00) | value; - - gb->registers[GB_REGISTER_AF] &= ~(GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - - if ((gb->registers[register_id] & 0x0F) == 0xF) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[register_id] & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void ld_lr_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 4); - register_id = (opcode >> 4) + 1; - gb->registers[register_id] &= 0xFF00; - gb->registers[register_id] |= GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); -} - -static void rrca(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry = (gb->registers[GB_REGISTER_AF] & 0x100) != 0; - - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] >> 1) & 0xFF00; - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG | 0x8000; - } -} - -static void rra(GB_gameboy_t *gb, uint8_t opcode) -{ - bool bit1 = (gb->registers[GB_REGISTER_AF] & 0x0100) != 0; - bool carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] = (gb->registers[GB_REGISTER_AF] >> 1) & 0xFF00; - if (carry) { - gb->registers[GB_REGISTER_AF] |= 0x8000; - } - if (bit1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void jr_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - /* Todo: Verify cycles are not 8 and 4 instead */ - GB_advance_cycles(gb, 4); - gb->pc += (int8_t)GB_read_memory(gb, gb->pc) + 1; - GB_advance_cycles(gb, 8); -} - -static bool condition_code(GB_gameboy_t *gb, uint8_t opcode) -{ - switch ((opcode >> 3) & 0x3) { - case 0: - return !(gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG); - case 1: - return (gb->registers[GB_REGISTER_AF] & GB_ZERO_FLAG); - case 2: - return !(gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG); - case 3: - return (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG); - } - - return false; -} - -static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - if (condition_code(gb, opcode)) { - GB_advance_cycles(gb, 4); - gb->pc += (int8_t)GB_read_memory(gb, gb->pc) + 1; - GB_advance_cycles(gb, 8); - } - else { - GB_advance_cycles(gb, 8); - gb->pc += 1; - } -} - -static void daa(GB_gameboy_t *gb, uint8_t opcode) -{ - /* This function is UGLY and UNREADABLE! But it passes Blargg's daa test! */ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= ~GB_ZERO_FLAG; - if (gb->registers[GB_REGISTER_AF] & GB_SUBSTRACT_FLAG) { - if (gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) { - gb->registers[GB_REGISTER_AF] &= ~GB_HALF_CARRY_FLAG; - if (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) { - gb->registers[GB_REGISTER_AF] += 0x9A00; - } - else { - gb->registers[GB_REGISTER_AF] += 0xFA00; - } - } - else if(gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) { - gb->registers[GB_REGISTER_AF] += 0xA000; - } - } - else { - if (gb->registers[GB_REGISTER_AF] & GB_HALF_CARRY_FLAG) { - uint16_t number = gb->registers[GB_REGISTER_AF] >> 8; - if (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) { - number += 0x100; - } - gb->registers[GB_REGISTER_AF] = 0; - number += 0x06; - if (number >= 0xa0) { - number -= 0xa0; - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - gb->registers[GB_REGISTER_AF] |= number << 8; - } - else { - uint16_t number = gb->registers[GB_REGISTER_AF] >> 8; - if (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) { - number += 0x100; - } - if (number > 0x99) { - number += 0x60; - } - number = (number & 0x0F) + ((number & 0x0F) > 9 ? 6 : 0) + (number & 0xFF0); - gb->registers[GB_REGISTER_AF] = number << 8; - if (number & 0xFF00) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - } - } - if ((gb->registers[GB_REGISTER_AF] & 0xFF00) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void cpl(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] ^= 0xFF00; - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG; -} - -static void scf(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG); -} - -static void ccf(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] ^= GB_CARRY_FLAG; - gb->registers[GB_REGISTER_AF] &= ~(GB_HALF_CARRY_FLAG | GB_SUBSTRACT_FLAG); -} - -static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_HL]++, gb->registers[GB_REGISTER_AF] >> 8); - GB_advance_cycles(gb, 4); -} - -static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_HL]--, gb->registers[GB_REGISTER_AF] >> 8); - GB_advance_cycles(gb, 4); -} - -static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]++) << 8; - GB_advance_cycles(gb, 4); -} - -static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, gb->registers[GB_REGISTER_HL]--) << 8; - GB_advance_cycles(gb, 4); -} - -static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]) + 1; - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value); - GB_advance_cycles(gb, 4); - - gb->registers[GB_REGISTER_AF] &= ~(GB_SUBSTRACT_FLAG | GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - if ((value & 0x0F) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((value & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]) - 1; - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value); - GB_advance_cycles(gb, 4); - - gb->registers[GB_REGISTER_AF] &= ~( GB_ZERO_FLAG | GB_HALF_CARRY_FLAG); - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - if ((value & 0x0F) == 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((value & 0xFF) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void ld_dhl_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - uint8_t data = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_HL], data); - GB_advance_cycles(gb, 4); -} - -uint8_t get_src_value(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t src_register_id; - uint8_t src_low; - src_register_id = ((opcode >> 1) + 1) & 3; - src_low = opcode & 1; - if (src_register_id == GB_REGISTER_AF) { - if (src_low) { - return gb->registers[GB_REGISTER_AF] >> 8; - } - uint8_t ret = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]); - GB_advance_cycles(gb, 4); - return ret; - } - if (src_low) { - return gb->registers[src_register_id] & 0xFF; - } - return gb->registers[src_register_id] >> 8; -} - -static void set_src_value(GB_gameboy_t *gb, uint8_t opcode, uint8_t value) -{ - uint8_t src_register_id; - uint8_t src_low; - src_register_id = ((opcode >> 1) + 1) & 3; - src_low = opcode & 1; - - if (src_register_id == GB_REGISTER_AF) { - if (src_low) { - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= value << 8; - } - else { - GB_write_memory(gb, gb->registers[GB_REGISTER_HL], value); - GB_advance_cycles(gb, 4); - } - } - else { - if (src_low) { - gb->registers[src_register_id] &= 0xFF00; - gb->registers[src_register_id] |= value; - } - else { - gb->registers[src_register_id] &= 0xFF; - gb->registers[src_register_id] |= value << 8; - } - } -} - -/* The LD r,r instruction is extremely common and extremely simple. Decoding this opcode at runtime is a significent - performance hit, so we generate functions for every ld x,y couple (including [hl]) at compile time using macros. */ - -/* Todo: It's probably wise to do the same to all opcodes. */ - -#define LD_X_Y(x, y) \ -static void ld_##x##_##y(GB_gameboy_t *gb, uint8_t opcode) \ -{ \ - GB_advance_cycles(gb, 4); \ - gb->x = gb->y;\ -} - -#define LD_X_DHL(x) \ -static void ld_##x##_##dhl(GB_gameboy_t *gb, uint8_t opcode) \ -{ \ -GB_advance_cycles(gb, 4); \ -gb->x = GB_read_memory(gb, gb->registers[GB_REGISTER_HL]); \ -GB_advance_cycles(gb, 4);\ -} - -#define LD_DHL_Y(y) \ -static void ld_##dhl##_##y(GB_gameboy_t *gb, uint8_t opcode) \ -{ \ -GB_advance_cycles(gb, 4); \ -GB_write_memory(gb, gb->registers[GB_REGISTER_HL], gb->y); \ -GB_advance_cycles(gb, 4);\ -} - -LD_X_Y(b,c) LD_X_Y(b,d) LD_X_Y(b,e) LD_X_Y(b,h) LD_X_Y(b,l) LD_X_DHL(b) LD_X_Y(b,a) -LD_X_Y(c,b) LD_X_Y(c,d) LD_X_Y(c,e) LD_X_Y(c,h) LD_X_Y(c,l) LD_X_DHL(c) LD_X_Y(c,a) -LD_X_Y(d,b) LD_X_Y(d,c) LD_X_Y(d,e) LD_X_Y(d,h) LD_X_Y(d,l) LD_X_DHL(d) LD_X_Y(d,a) -LD_X_Y(e,b) LD_X_Y(e,c) LD_X_Y(e,d) LD_X_Y(e,h) LD_X_Y(e,l) LD_X_DHL(e) LD_X_Y(e,a) -LD_X_Y(h,b) LD_X_Y(h,c) LD_X_Y(h,d) LD_X_Y(h,e) LD_X_Y(h,l) LD_X_DHL(h) LD_X_Y(h,a) -LD_X_Y(l,b) LD_X_Y(l,c) LD_X_Y(l,d) LD_X_Y(l,e) LD_X_Y(l,h) LD_X_DHL(l) LD_X_Y(l,a) -LD_DHL_Y(b) LD_DHL_Y(c) LD_DHL_Y(d) LD_DHL_Y(e) LD_DHL_Y(h) LD_DHL_Y(l) LD_DHL_Y(a) -LD_X_Y(a,b) LD_X_Y(a,c) LD_X_Y(a,d) LD_X_Y(a,e) LD_X_Y(a,h) LD_X_Y(a,l) LD_X_DHL(a) - - -static void add_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a + value) << 8; - if ((uint8_t)(a + value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void adc_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = (a + value + carry) << 8; - - if ((uint8_t)(a + value + carry) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) + carry > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) + carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG; - - if ((uint8_t) (a - value - carry) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF) + carry) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) - ((unsigned long) value) - carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void and_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a & value) << 8) | GB_HALF_CARRY_FLAG; - if ((a & value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void xor_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a ^ value) << 8; - if ((a ^ value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void or_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a | value) << 8; - if ((a | value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void halt(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->halted = true; - /* Despite what some online documentations say, the HALT bug also happens on a CGB, in both CGB and DMG modes. */ - if (!gb->ime && (gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F) != 0) { - gb->halted = false; - gb->halt_bug = true; - } -} - -static void ret_cc(GB_gameboy_t *gb, uint8_t opcode) -{ - /* Todo: Verify timing */ - if (condition_code(gb, opcode)) { - GB_advance_cycles(gb, 8); - gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]); - GB_advance_cycles(gb, 4); - gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8; - GB_advance_cycles(gb, 8); - gb->registers[GB_REGISTER_SP] += 2; - } - else { - GB_advance_cycles(gb, 8); - } -} - -static void pop_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 4); - register_id = ((opcode >> 4) + 1) & 3; - gb->registers[register_id] = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]); - GB_advance_cycles(gb, 4); - gb->registers[register_id] |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8; - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFFF0; // Make sure we don't set impossible flags on F! See Blargg's PUSH AF test. - gb->registers[GB_REGISTER_SP] += 2; -} - -static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - if (condition_code(gb, opcode)) { - GB_advance_cycles(gb, 4); - uint16_t addr = GB_read_memory(gb, gb->pc); - GB_advance_cycles(gb, 4); - addr |= (GB_read_memory(gb, gb->pc + 1) << 8); - GB_advance_cycles(gb, 8); - gb->pc = addr; - - } - else { - GB_advance_cycles(gb, 12); - gb->pc += 2; - } -} - -static void jp_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - uint16_t addr = GB_read_memory(gb, gb->pc); - GB_advance_cycles(gb, 4); - addr |= (GB_read_memory(gb, gb->pc + 1) << 8); - GB_advance_cycles(gb, 8); - gb->pc = addr;} - -static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t call_addr = gb->pc - 1; - if (condition_code(gb, opcode)) { - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_SP] -= 2; - uint16_t addr = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - addr |= (GB_read_memory(gb, gb->pc++) << 8); - GB_advance_cycles(gb, 8); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); - GB_advance_cycles(gb, 4); - gb->pc = addr; - } - else { - GB_advance_cycles(gb, 12); - gb->pc += 2; - } -} - -static void push_rr(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t register_id; - GB_advance_cycles(gb, 8); - register_id = ((opcode >> 4) + 1) & 3; - gb->registers[GB_REGISTER_SP] -= 2; - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->registers[register_id]) >> 8); - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->registers[register_id]) & 0xFF); - GB_advance_cycles(gb, 4); -} - -static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a + value) << 8; - if ((uint8_t) (a + value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void adc_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = (a + value + carry) << 8; - - if (gb->registers[GB_REGISTER_AF] == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) + (value & 0xF) + carry > 0x0F) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) + ((unsigned long) value) + carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a - value) << 8) | GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a, carry; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - gb->registers[GB_REGISTER_AF] = ((a - value - carry) << 8) | GB_SUBSTRACT_FLAG; - - if ((uint8_t) (a - value - carry) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF) + carry) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (((unsigned long) a) - ((unsigned long) value) - carry > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void and_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = ((a & value) << 8) | GB_HALF_CARRY_FLAG; - if ((a & value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void xor_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a ^ value) << 8; - if ((a ^ value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void or_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] = (a | value) << 8; - if ((a | value) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value, a; - GB_advance_cycles(gb, 4); - value = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - a = gb->registers[GB_REGISTER_AF] >> 8; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - gb->registers[GB_REGISTER_AF] |= GB_SUBSTRACT_FLAG; - if (a == value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - if ((a & 0xF) < (value & 0xF)) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - if (a < value) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void rst(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t call_addr = gb->pc - 1; - GB_advance_cycles(gb, 8); - gb->registers[GB_REGISTER_SP] -= 2; - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); - GB_advance_cycles(gb, 4); - gb->pc = opcode ^ 0xC7; -} - -static void ret(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]); - GB_advance_cycles(gb, 4); - gb->pc |= GB_read_memory(gb, gb->registers[GB_REGISTER_SP] + 1) << 8; - GB_advance_cycles(gb, 8); - gb->registers[GB_REGISTER_SP] += 2; -} - -static void reti(GB_gameboy_t *gb, uint8_t opcode) -{ - ret(gb, opcode); - gb->ime = true; -} - -static void call_a16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t call_addr = gb->pc - 1; - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_SP] -= 2; - uint16_t addr = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - addr |= (GB_read_memory(gb, gb->pc++) << 8); - GB_advance_cycles(gb, 8); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP] + 1, (gb->pc) >> 8); - GB_advance_cycles(gb, 4); - GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); - GB_advance_cycles(gb, 4); - gb->pc = addr; -} - -static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - uint8_t temp = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - GB_write_memory(gb, 0xFF00 + temp, gb->registers[GB_REGISTER_AF] >> 8); - GB_advance_cycles(gb, 4); -} - -static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFF; - uint8_t temp = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, 0xFF00 + temp) << 8; - GB_advance_cycles(gb, 4); -} - -static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - GB_write_memory(gb, 0xFF00 + (gb->registers[GB_REGISTER_BC] & 0xFF), gb->registers[GB_REGISTER_AF] >> 8); - GB_advance_cycles(gb, 4); -} - -static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFF; - gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, 0xFF00 + (gb->registers[GB_REGISTER_BC] & 0xFF)) << 8; - GB_advance_cycles(gb, 4); -} - -static void add_sp_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - int16_t offset; - uint16_t sp = gb->registers[GB_REGISTER_SP]; - GB_advance_cycles(gb, 4); - offset = (int8_t) GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 12); - gb->registers[GB_REGISTER_SP] += offset; - - gb->registers[GB_REGISTER_AF] &= 0xFF00; - - /* A new instruction, a new meaning for Half Carry! */ - if ((sp & 0xF) + (offset & 0xF) > 0xF) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((sp & 0xFF) + (offset & 0xFF) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void jp_hl(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - gb->pc = gb->registers[GB_REGISTER_HL]; -} - -static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t addr; - GB_advance_cycles(gb, 4); - addr = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - addr |= GB_read_memory(gb, gb->pc++) << 8; - GB_advance_cycles(gb, 4); - GB_write_memory(gb, addr, gb->registers[GB_REGISTER_AF] >> 8); - GB_advance_cycles(gb, 4); -} - -static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode) -{ - uint16_t addr; - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFF; - addr = GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 4); - addr |= GB_read_memory(gb, gb->pc++) << 8 ; - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] |= GB_read_memory(gb, addr) << 8; - GB_advance_cycles(gb, 4); -} - -static void di(GB_gameboy_t *gb, uint8_t opcode) -{ - /* DI is NOT delayed, not even on a CGB. Mooneye's di_timing-GS test fails on a CGB - for different reasons.*/ - GB_advance_cycles(gb, 4); - gb->ime = false; -} - -static void ei(GB_gameboy_t *gb, uint8_t opcode) -{ - /* ei is actually "disable interrupts for one instruction, then enable them". */ - GB_advance_cycles(gb, 4); - gb->ime = false; - gb->ime_toggle = true; -} - -static void ld_hl_sp_r8(GB_gameboy_t *gb, uint8_t opcode) -{ - int16_t offset; - GB_advance_cycles(gb, 4); - gb->registers[GB_REGISTER_AF] &= 0xFF00; - offset = (int8_t) GB_read_memory(gb, gb->pc++); - GB_advance_cycles(gb, 8); - gb->registers[GB_REGISTER_HL] = gb->registers[GB_REGISTER_SP] + offset; - - if ((gb->registers[GB_REGISTER_SP] & 0xF) + (offset & 0xF) > 0xF) { - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - } - - if ((gb->registers[GB_REGISTER_SP] & 0xFF) + (offset & 0xFF) > 0xFF) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } -} - -static void ld_sp_hl(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 8); - gb->registers[GB_REGISTER_SP] = gb->registers[GB_REGISTER_HL]; -} - -static void rlc_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - carry = (value & 0x80) != 0; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value << 1) | carry); - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (!(value << 1)) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void rrc_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - carry = (value & 0x01) != 0; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - value = (value >> 1) | (carry << 7); - set_src_value(gb, opcode, value); - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void rl_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - bool bit7; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - bit7 = (value & 0x80) != 0; - - gb->registers[GB_REGISTER_AF] &= 0xFF00; - value = (value << 1) | carry; - set_src_value(gb, opcode, value); - if (bit7) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void rr_r(GB_gameboy_t *gb, uint8_t opcode) -{ - bool carry; - uint8_t value; - bool bit1; - - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - carry = (gb->registers[GB_REGISTER_AF] & GB_CARRY_FLAG) != 0; - bit1 = (value & 0x1) != 0; - - gb->registers[GB_REGISTER_AF] &= 0xFF00; - value = (value >> 1) | (carry << 7); - set_src_value(gb, opcode, value); - if (bit1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void sla_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - bool carry; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - carry = (value & 0x80) != 0; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value << 1)); - if (carry) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if ((value & 0x7F) == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void sra_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t bit7; - uint8_t value; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - bit7 = value & 0x80; - gb->registers[GB_REGISTER_AF] &= 0xFF00; - if (value & 1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - value = (value >> 1) | bit7; - set_src_value(gb, opcode, value); - if (value == 0) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void srl_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value >> 1)); - if (value & 1) { - gb->registers[GB_REGISTER_AF] |= GB_CARRY_FLAG; - } - if (!(value >> 1)) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void swap_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - gb->registers[GB_REGISTER_AF] &= 0xFF00; - set_src_value(gb, opcode, (value >> 4) | (value << 4)); - if (!value) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } -} - -static void bit_r(GB_gameboy_t *gb, uint8_t opcode) -{ - uint8_t value; - uint8_t bit; - GB_advance_cycles(gb, 4); - value = get_src_value(gb, opcode); - bit = 1 << ((opcode >> 3) & 7); - if ((opcode & 0xC0) == 0x40) { /* Bit */ - gb->registers[GB_REGISTER_AF] &= 0xFF00 | GB_CARRY_FLAG; - gb->registers[GB_REGISTER_AF] |= GB_HALF_CARRY_FLAG; - if (!(bit & value)) { - gb->registers[GB_REGISTER_AF] |= GB_ZERO_FLAG; - } - } - else if ((opcode & 0xC0) == 0x80) { /* res */ - set_src_value(gb, opcode, value & ~bit) ; - } - else if ((opcode & 0xC0) == 0xC0) { /* set */ - set_src_value(gb, opcode, value | bit) ; - } -} - -static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode) -{ - GB_advance_cycles(gb, 4); - opcode = GB_read_memory(gb, gb->pc++); - switch (opcode >> 3) { - case 0: - rlc_r(gb, opcode); - break; - case 1: - rrc_r(gb, opcode); - break; - case 2: - rl_r(gb, opcode); - break; - case 3: - rr_r(gb, opcode); - break; - case 4: - sla_r(gb, opcode); - break; - case 5: - sra_r(gb, opcode); - break; - case 6: - swap_r(gb, opcode); - break; - case 7: - srl_r(gb, opcode); - break; - default: - bit_r(gb, opcode); - break; - } -} - -static GB_opcode_t *opcodes[256] = { - /* X0 X1 X2 X3 X4 X5 X6 X7 */ - /* X8 X9 Xa Xb Xc Xd Xe Xf */ - nop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rlca, /* 0X */ - ld_da16_sp, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rrca, - stop, ld_rr_d16, ld_drr_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, rla, /* 1X */ - jr_r8, add_hl_rr, ld_a_drr, dec_rr, inc_lr, dec_lr, ld_lr_d8, rra, - jr_cc_r8, ld_rr_d16, ld_dhli_a, inc_rr, inc_hr, dec_hr, ld_hr_d8, daa, /* 2X */ - jr_cc_r8, add_hl_rr, ld_a_dhli, dec_rr, inc_lr, dec_lr, ld_lr_d8, cpl, - jr_cc_r8, ld_rr_d16, ld_dhld_a, inc_rr, inc_dhl, dec_dhl, ld_dhl_d8, scf, /* 3X */ - jr_cc_r8, add_hl_rr, ld_a_dhld, dec_rr, inc_hr, dec_hr, ld_hr_d8, ccf, - nop, ld_b_c, ld_b_d, ld_b_e, ld_b_h, ld_b_l, ld_b_dhl, ld_b_a, /* 4X */ - ld_c_b, nop, ld_c_d, ld_c_e, ld_c_h, ld_c_l, ld_c_dhl, ld_c_a, - ld_d_b, ld_d_c, nop, ld_d_e, ld_d_h, ld_d_l, ld_d_dhl, ld_d_a, /* 5X */ - ld_e_b, ld_e_c, ld_e_d, nop, ld_e_h, ld_e_l, ld_e_dhl, ld_e_a, - ld_h_b, ld_h_c, ld_h_d, ld_h_e, nop, ld_h_l, ld_h_dhl, ld_h_a, /* 6X */ - ld_l_b, ld_l_c, ld_l_d, ld_l_e, ld_l_h, nop, ld_l_dhl, ld_l_a, - ld_dhl_b, ld_dhl_c, ld_dhl_d, ld_dhl_e, ld_dhl_h, ld_dhl_l, halt, ld_dhl_a, /* 7X */ - ld_a_b, ld_a_c, ld_a_d, ld_a_e, ld_a_h, ld_a_l, ld_a_dhl, nop, - add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, add_a_r, /* 8X */ - adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, adc_a_r, - sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, sub_a_r, /* 9X */ - sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, sbc_a_r, - and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, and_a_r, /* aX */ - xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, xor_a_r, - or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, or_a_r, /* bX */ - cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, cp_a_r, - ret_cc, pop_rr, jp_cc_a16, jp_a16, call_cc_a16,push_rr, add_a_d8, rst, /* cX */ - ret_cc, ret, jp_cc_a16, cb_prefix, call_cc_a16,call_a16, adc_a_d8, rst, - ret_cc, pop_rr, jp_cc_a16, ill, call_cc_a16,push_rr, sub_a_d8, rst, /* dX */ - ret_cc, reti, jp_cc_a16, ill, call_cc_a16,ill, sbc_a_d8, rst, - ld_da8_a, pop_rr, ld_dc_a, ill, ill, push_rr, and_a_d8, rst, /* eX */ - add_sp_r8, jp_hl, ld_da16_a, ill, ill, ill, xor_a_d8, rst, - ld_a_da8, pop_rr, ld_a_dc, di, ill, push_rr, or_a_d8, rst, /* fX */ - ld_hl_sp_r8,ld_sp_hl, ld_a_da16, ei, ill, ill, cp_a_d8, rst, -}; -void GB_cpu_run(GB_gameboy_t *gb) -{ - gb->vblank_just_occured = false; - bool interrupt = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F; - - if (interrupt) { - gb->halted = false; - } - - if (gb->hdma_on) { - GB_advance_cycles(gb, 4); - return; - } - - bool effecitve_ime = gb->ime; - if (gb->ime_toggle) { - gb->ime = !gb->ime; - gb->ime_toggle = false; - } - - if (effecitve_ime && interrupt) { - uint8_t interrupt_bit = 0; - uint8_t interrupt_queue = gb->interrupt_enable & gb->io_registers[GB_IO_IF] & 0x1F; - while (!(interrupt_queue & 1)) { - interrupt_queue >>= 1; - interrupt_bit++; - } - gb->io_registers[GB_IO_IF] &= ~(1 << interrupt_bit); - gb->ime = false; - nop(gb, 0); - /* Run pseudo instructions rst 40-60*/ - rst(gb, 0x87 + interrupt_bit * 8); - } - else if(!gb->halted && !gb->stopped) { - uint8_t opcode = GB_read_memory(gb, gb->pc++); - if (gb->halt_bug) { - gb->pc--; - gb->halt_bug = false; - } - opcodes[opcode](gb, opcode); - } - else { - GB_advance_cycles(gb, 4); - } -} diff --git a/waterbox/sameboy/z80_cpu.h b/waterbox/sameboy/z80_cpu.h deleted file mode 100644 index 1434ed7b96..0000000000 --- a/waterbox/sameboy/z80_cpu.h +++ /dev/null @@ -1,10 +0,0 @@ -#ifndef z80_cpu_h -#define z80_cpu_h -#include "gb.h" - -void GB_cpu_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count); -#ifdef GB_INTERNAL -void GB_cpu_run(GB_gameboy_t *gb); -#endif - -#endif /* z80_cpu_h */