From 34e68c589fd602333be43b94c06aa641052ab39d Mon Sep 17 00:00:00 2001 From: nattthebear Date: Sun, 16 Jul 2017 14:53:03 -0400 Subject: [PATCH] Sameboy progress --- BizHawk.Client.Common/RomLoader.cs | 3 +- .../BizHawk.Emulation.Cores.csproj | 4 + .../Consoles/Nintendo/Gameboy/LibSameboy.cs | 16 + .../Consoles/Nintendo/Gameboy/Sameboy.cs | 88 + .../Properties/Resources.Designer.cs | 20 + .../Properties/Resources.resx | 6 + .../Resources/cgb_boot.bin.gz | Bin 0 -> 1682 bytes .../Resources/dmg_boot.bin.gz | Bin 0 -> 215 bytes waterbox/emulibc/emulibc.h | 4 + waterbox/sameboy/.vscode/settings.json | 6 + waterbox/sameboy/Makefile | 2 +- waterbox/sameboy/bizhawk.cpp | 118 ++ waterbox/sameboy/debugger.c | 1850 ----------------- waterbox/sameboy/debugger.h | 32 - waterbox/sameboy/gb.c | 4 +- waterbox/sameboy/gb.h | 12 - waterbox/sameboy/memory.c | 6 - waterbox/sameboy/timing.c | 2 - waterbox/sameboy/z80_cpu.c | 6 - waterbox/sameboy/z80_disassembler.c | 788 ------- 20 files changed, 266 insertions(+), 2701 deletions(-) create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs create mode 100644 BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs create mode 100644 BizHawk.Emulation.Cores/Resources/cgb_boot.bin.gz create mode 100644 BizHawk.Emulation.Cores/Resources/dmg_boot.bin.gz create mode 100644 waterbox/sameboy/.vscode/settings.json create mode 100644 waterbox/sameboy/bizhawk.cpp delete mode 100644 waterbox/sameboy/debugger.c delete mode 100644 waterbox/sameboy/debugger.h delete mode 100644 waterbox/sameboy/z80_disassembler.c diff --git a/BizHawk.Client.Common/RomLoader.cs b/BizHawk.Client.Common/RomLoader.cs index 9a9e0338ab..4af0355a29 100644 --- a/BizHawk.Client.Common/RomLoader.cs +++ b/BizHawk.Client.Common/RomLoader.cs @@ -921,7 +921,8 @@ namespace BizHawk.Client.Common if (!Global.Config.GB_AsSGB) { //core = CoreInventory.Instance["GB", "Pizza Boy"]; - core = CoreInventory.Instance["GB", "Gambatte"]; + //core = CoreInventory.Instance["GB", "Gambatte"]; + core = CoreInventory.Instance["GB", "SameBoy"]; } else { diff --git a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj index 07fb3f1ede..f56e83a6ab 100644 --- a/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj +++ b/BizHawk.Emulation.Cores/BizHawk.Emulation.Cores.csproj @@ -538,7 +538,9 @@ + + @@ -1309,6 +1311,8 @@ + + diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs new file mode 100644 index 0000000000..5c13e48d23 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/LibSameboy.cs @@ -0,0 +1,16 @@ +using BizHawk.Common.BizInvoke; +using BizHawk.Emulation.Cores.Waterbox; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy +{ + public abstract class LibSameboy : LibWaterboxCore + { + [BizImport(CC)] + public abstract bool Init(bool cgb); + } +} diff --git a/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs new file mode 100644 index 0000000000..453aaf65a0 --- /dev/null +++ b/BizHawk.Emulation.Cores/Consoles/Nintendo/Gameboy/Sameboy.cs @@ -0,0 +1,88 @@ +using BizHawk.Common; +using BizHawk.Emulation.Common; +using BizHawk.Emulation.Cores.Properties; +using BizHawk.Emulation.Cores.Waterbox; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace BizHawk.Emulation.Cores.Consoles.Nintendo.Gameboy +{ + [Core("SameBoy", "LIJI32", true, false, "efc11783c7fb6da66e1dd084e41ba6a85c0bd17e", + "https://sameboy.github.io/", false)] + public class Sameboy : WaterboxCore, IGameboyCommon + { + /// + /// 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 LibSameboy _core; + private bool _cgb; + + [CoreConstructor("GB")] + public Sameboy(CoreComm comm, byte[] rom) + : base(comm, new Configuration + { + DefaultWidth = 160, + DefaultHeight = 144, + MaxWidth = 256, + MaxHeight = 224, + MaxSamples = 1024, + DefaultFpsNumerator = TICKSPERSECOND, + DefaultFpsDenominator = TICKSPERFRAME, + SystemId = "GB" + }) + { + _core = PreInit(new PeRunnerOptions + { + Filename = "sameboy.wbx", + SbrkHeapSizeKB = 128, + InvisibleHeapSizeKB = 16 * 1024, + SealedHeapSizeKB = 5 * 1024, + PlainHeapSizeKB = 4096, + MmapHeapSizeKB = 34 * 1024 + }); + + _cgb = (rom[0x143] & 0xc0) == 0xc0; + Console.WriteLine("Automaticly detected CGB to " + _cgb); + var bios = Util.DecompressGzipFile(new MemoryStream( + _cgb ? Resources.SameboyCgbBoot : Resources.SameboyDmgBoot)); + + _exe.AddReadonlyFile(rom, "game.rom"); + _exe.AddReadonlyFile(bios, "boot.rom"); + + if (!_core.Init(_cgb)) + { + throw new InvalidOperationException("Core rejected the rom!"); + } + + _exe.RemoveReadonlyFile("game.rom"); + _exe.RemoveReadonlyFile("boot.rom"); + + PostInit(); + } + + protected override LibWaterboxCore.FrameInfo FrameAdvancePrep(IController controller, bool render, bool rendersound) + { + return new LibSameboy.FrameInfo + { + }; + } + + public bool IsCGBMode() => _cgb; + } +} diff --git a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs index 59d6602caf..5a3bbcce9a 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs +++ b/BizHawk.Emulation.Cores/Properties/Resources.Designer.cs @@ -60,6 +60,26 @@ namespace BizHawk.Emulation.Cores.Properties { } } + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] SameboyCgbBoot { + get { + object obj = ResourceManager.GetObject("SameboyCgbBoot", resourceCulture); + return ((byte[])(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Byte[]. + /// + internal static byte[] SameboyDmgBoot { + get { + object obj = ResourceManager.GetObject("SameboyDmgBoot", resourceCulture); + return ((byte[])(obj)); + } + } + /// /// Looks up a localized resource of type System.Byte[]. /// diff --git a/BizHawk.Emulation.Cores/Properties/Resources.resx b/BizHawk.Emulation.Cores/Properties/Resources.resx index 1378c9e6e9..bdb07b9a9f 100644 --- a/BizHawk.Emulation.Cores/Properties/Resources.resx +++ b/BizHawk.Emulation.Cores/Properties/Resources.resx @@ -118,6 +118,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + ..\Resources\cgb_boot.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + ..\Resources\dmg_boot.bin.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + ..\Resources\sgb-cart-present.spc.gz;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 diff --git a/BizHawk.Emulation.Cores/Resources/cgb_boot.bin.gz b/BizHawk.Emulation.Cores/Resources/cgb_boot.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..ab0eb54bfdbeeb29f773031bd817112cd4bd807d GIT binary patch literal 1682 zcmV;D25tEtiwFqP+h$n;0ApujUt(`>bS`3PZUE(0_Y2cN6uvZVT1V5=8LKvz)KTl+ zE2i#^TU6W%?g@&kHW5Ju#l3NZI1m*@R9tZzN}MPT)N?P?I;wRgzSQjx{sVs8-Fxre zy^nj3BQI4gd&kp;x`;HhA5-Q$EF!YlbvqnLtX3RqP!sCsp?2 zPs(S~BIu?>P7Ad#D={z%PI*xSzc$x13PsxQ_{JAp>J~MP)k9LGpfmkA13@fv(p<f1s*MmhE)+Zu; zM`cM06?Dc93E;;sVY5iXFfw;B$^z2RyvZ;X&Gw8k-R!|I9!ERsXkpOImkjy_(gFSN zm{p|Kg&VKsISU)~>TW1wO7<99tmfuITgNY1QDbyrds&r=+Jq9@-TuR;*ksR54buRGCmLTuzcaF~%bni^ZZY4pt8;TdGJg2Inu8uWW&` zyihGzi7AdMmn(-*L8)kgl8u`+Z`7z$<7RD|wZWY#cJ0)t6K>V6QLDDCnl!_mFn&CL z{`__8*6F(5vSmxi^y<~C(P*?^zkcV>pVt~T#Drju<1rk3OfcbQahlc;<2o46$9UcH z`>a_U#xQt75YGdLZ~o5t>vpcwck0^ChO_XHt7yzsJaPHXmKy1R_mNpUIgB_= zc)}x|;LQXg(HWSep~Jyf!Q^o|oz7;n1p5%;Z1qUB^FX_H;H1Vo3#Z(+T6I+GW(e8p z%i_r8NTKoT5{_KlsdYI=+H_?q=clja-Y0>0$s98&Hyno~Vjg4hQc>{u=$S7;sRtW^4D4Oq%$SK2Crzp!pYZv`!2b_9$vFzz56E&*xgHf2CuRaFGA0AMCm0F+9wtXfSsC=Y99T3$1p4U%qENnzc)xhWhN1zb%x>+B5<|BUm!C;5+E_R5b?Y^5ocDFG8 zWQh85mQGrMs862o&}&gqI>M)?z7s}A{Ac(_PD3dYVqDHQ{|HkUXACpzHH-V8!@&Bn z36)YYZSr2y_G3eR6Q-4*k+T%afY1?UBX3GQamx2lZQ=N%dBv&j_6*9C8?*0)|aN_01N|Fc0ZEBGNB{`{0A3e5XaE2J literal 0 HcmV?d00001 diff --git a/BizHawk.Emulation.Cores/Resources/dmg_boot.bin.gz b/BizHawk.Emulation.Cores/Resources/dmg_boot.bin.gz new file mode 100644 index 0000000000000000000000000000000000000000..85f1e8276a20c915a780bf8d228032451b736ab4 GIT binary patch literal 215 zcmV;|04V<-iwFqP+h$n;0Ay`vUt(`>bS`3PZU8g<_g|5rLFsgk#&5fZ2Wk%l?LI#c zdZ21o{y@d<&jWWs7Dh#Z1}XQmO$Z{O;gjGR20jicVI^f=g`akkuLNc)>d)lk zvDM%(0t)EyOMJDP_`rcp_v}0dyXLb?8QAzitb-udG$2cH1LM<=ioT4D!VH2p7!Lnq zuw&t4IIZyYwBX0ng0>1@E0vU$luv$e`8Pq~*U7Wf8CZeT17W-N2O=kJoOUkVxzx#q RfokAE002P!A@=|Q005=QWzzrv literal 0 HcmV?d00001 diff --git a/waterbox/emulibc/emulibc.h b/waterbox/emulibc/emulibc.h index da0eccc7c8..29d0ff7baa 100644 --- a/waterbox/emulibc/emulibc.h +++ b/waterbox/emulibc/emulibc.h @@ -10,7 +10,11 @@ extern "C" { // mark an entry point or callback pointer #define ECL_ENTRY // mark a visible symbol +#ifdef __cplusplus +#define ECL_EXPORT extern "C" __attribute__((visibility("default"))) +#else #define ECL_EXPORT __attribute__((visibility("default"))) +#endif // allocate memory from the "sealed" pool. this memory can never be freed, // and can only be allocated or written to during the init phase. after that, the host diff --git a/waterbox/sameboy/.vscode/settings.json b/waterbox/sameboy/.vscode/settings.json new file mode 100644 index 0000000000..a5401e1c1f --- /dev/null +++ b/waterbox/sameboy/.vscode/settings.json @@ -0,0 +1,6 @@ +// 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/Makefile b/waterbox/sameboy/Makefile index 0fdbe63b73..f3824c30ee 100644 --- a/waterbox/sameboy/Makefile +++ b/waterbox/sameboy/Makefile @@ -11,7 +11,7 @@ CCFLAGS:=$(FLAGS) -Ilib \ -std=gnu99 \ -DLSB_FIRST -D_GNU_SOURCE -DGB_INTERNAL -CPPFLAGS:=$(FLAGS) -DSPC_NO_COPY_STATE_FUNCS +CPPFLAGS:=$(FLAGS) -DSPC_NO_COPY_STATE_FUNCS -std=c++0x TARGET = sameboy.wbx diff --git a/waterbox/sameboy/bizhawk.cpp b/waterbox/sameboy/bizhawk.cpp new file mode 100644 index 0000000000..81b31201d0 --- /dev/null +++ b/waterbox/sameboy/bizhawk.cpp @@ -0,0 +1,118 @@ +#include +#include "../emulibc/emulibc.h" +#include "../emulibc/waterboxcore.h" + +#define _Static_assert static_assert + +extern "C" { +#include "gb.h" +#include "joypad.h" +#include "apu.h" +} + +static GB_gameboy_t GB; + +static void VBlankCallback(GB_gameboy_t *gb) +{ + +} + +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; +} + +ECL_EXPORT bool Init(bool cgb) +{ + 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_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); + + return true; +} + +struct MyFrameInfo : public FrameInfo +{ + +}; + +ECL_EXPORT void FrameAdvance(MyFrameInfo &f) +{ + GB_set_pixels_output(&GB, f.VideoBuffer); + // void GB_set_key_state(GB_gameboy_t *gb, GB_key_t index, bool pressed); + GB_run_frame(&GB); + f.Samples = 735; + f.Width = 160; + f.Height = 144; +} + +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 + 8, GB_DIRECT_ACCESS_OBP, "OBP", MEMORYAREA_FLAGS_WORDSIZE1 | MEMORYAREA_FLAGS_WRITABLE); +} + +ECL_EXPORT void SetInputCallback(void (*callback)()) +{ + // TODO +} + +int main() +{ + return 0; +} diff --git a/waterbox/sameboy/debugger.c b/waterbox/sameboy/debugger.c deleted file mode 100644 index 7fc7b2a9a6..0000000000 --- a/waterbox/sameboy/debugger.c +++ /dev/null @@ -1,1850 +0,0 @@ -#include -#include -#include -#include "gb.h" - -typedef struct { - bool has_bank; - uint16_t bank:9; - uint16_t value; -} value_t; - -typedef struct { - enum { - LVALUE_MEMORY, - LVALUE_REG16, - LVALUE_REG_H, - LVALUE_REG_L, - } kind; - union { - uint16_t *register_address; - value_t memory_address; - }; -} lvalue_t; - -#define VALUE_16(x) ((value_t){false, 0, (x)}) - -struct GB_breakpoint_s { - union { - struct { - uint16_t addr; - uint16_t bank; /* -1 = any bank*/ - }; - uint32_t key; /* For sorting and comparing */ - }; - char *condition; -}; - -#define BP_KEY(x) (((struct GB_breakpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key) - -#define GB_WATCHPOINT_R (1) -#define GB_WATCHPOINT_W (2) - -struct GB_watchpoint_s { - union { - struct { - uint16_t addr; - uint16_t bank; /* -1 = any bank*/ - }; - uint32_t key; /* For sorting and comparing */ - }; - char *condition; - uint8_t flags; -}; - -#define WP_KEY(x) (((struct GB_watchpoint_s){.addr = ((x).value), .bank = (x).has_bank? (x).bank : -1 }).key) - -static uint16_t bank_for_addr(GB_gameboy_t *gb, uint16_t addr) -{ - if (addr < 0x4000) { - return gb->mbc_rom0_bank; - } - - if (addr < 0x8000) { - return gb->mbc_rom_bank; - } - - if (addr < 0xD000) { - return 0; - } - - if (addr < 0xE000) { - return gb->cgb_ram_bank; - } - - return 0; -} - -typedef struct { - uint16_t rom0_bank; - uint16_t rom_bank; - uint8_t mbc_ram_bank; - bool mbc_ram_enable; - uint8_t ram_bank; - uint8_t vram_bank; -} banking_state_t; - -static inline void save_banking_state(GB_gameboy_t *gb, banking_state_t *state) -{ - state->rom0_bank = gb->mbc_rom0_bank; - state->rom_bank = gb->mbc_rom_bank; - state->mbc_ram_bank = gb->mbc_ram_bank; - state->mbc_ram_enable = gb->mbc_ram_enable; - state->ram_bank = gb->cgb_ram_bank; - state->vram_bank = gb->cgb_vram_bank; -} - -static inline void restore_banking_state(GB_gameboy_t *gb, banking_state_t *state) -{ - - gb->mbc_rom0_bank = state->rom0_bank; - gb->mbc_rom_bank = state->rom_bank; - gb->mbc_ram_bank = state->mbc_ram_bank; - gb->mbc_ram_enable = state->mbc_ram_enable; - gb->cgb_ram_bank = state->ram_bank; - gb->cgb_vram_bank = state->vram_bank; -} - -static inline void switch_banking_state(GB_gameboy_t *gb, uint16_t bank) -{ - gb->mbc_rom0_bank = bank; - gb->mbc_rom_bank = bank; - gb->mbc_ram_bank = bank; - gb->mbc_ram_enable = true; - if (gb->is_cgb) { - gb->cgb_ram_bank = bank & 7; - gb->cgb_vram_bank = bank & 1; - } -} - -static const char *value_to_string(GB_gameboy_t *gb, uint16_t value, bool prefer_name) -{ - static __thread char output[256]; - const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, value); - - if (symbol && (value - symbol->addr > 0x1000 || symbol->addr == 0) ) { - symbol = NULL; - } - - /* Avoid overflow */ - if (symbol && strlen(symbol->name) > 240) { - symbol = NULL; - } - - if (!symbol) { - sprintf(output, "$%04x", value); - } - - else if (symbol->addr == value) { - if (prefer_name) { - sprintf(output, "%s ($%04x)", symbol->name, value); - } - else { - sprintf(output, "$%04x (%s)", value, symbol->name); - } - } - - else { - if (prefer_name) { - sprintf(output, "%s+$%03x ($%04x)", symbol->name, value - symbol->addr, value); - } - else { - sprintf(output, "$%04x (%s+$%03x)", value, symbol->name, value - symbol->addr); - } - } - return output; -} - -static const char *debugger_value_to_string(GB_gameboy_t *gb, value_t value, bool prefer_name) -{ - if (!value.has_bank) return value_to_string(gb, value.value, prefer_name); - - static __thread char output[256]; - const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[value.bank], value.value); - - if (symbol && (value.value - symbol->addr > 0x1000 || symbol->addr == 0) ) { - symbol = NULL; - } - - /* Avoid overflow */ - if (symbol && strlen(symbol->name) > 240) { - symbol = NULL; - } - - if (!symbol) { - sprintf(output, "$%02x:$%04x", value.bank, value.value); - } - - else if (symbol->addr == value.value) { - if (prefer_name) { - sprintf(output, "%s ($%02x:$%04x)", symbol->name, value.bank, value.value); - } - else { - sprintf(output, "$%02x:$%04x (%s)", value.bank, value.value, symbol->name); - } - } - - else { - if (prefer_name) { - sprintf(output, "%s+$%03x ($%02x:$%04x)", symbol->name, value.value - symbol->addr, value.bank, value.value); - } - else { - sprintf(output, "$%02x:$%04x (%s+$%03x)", value.bank, value.value, symbol->name, value.value - symbol->addr); - } - } - return output; -} - -static value_t read_lvalue(GB_gameboy_t *gb, lvalue_t lvalue) -{ - /* Not used until we add support for operators like += */ - switch (lvalue.kind) { - case LVALUE_MEMORY: - if (lvalue.memory_address.has_bank) { - banking_state_t state; - save_banking_state(gb, &state); - switch_banking_state(gb, lvalue.memory_address.bank); - value_t r = VALUE_16(GB_read_memory(gb, lvalue.memory_address.value)); - restore_banking_state(gb, &state); - return r; - } - return VALUE_16(GB_read_memory(gb, lvalue.memory_address.value)); - - case LVALUE_REG16: - return VALUE_16(*lvalue.register_address); - - case LVALUE_REG_L: - return VALUE_16(*lvalue.register_address & 0x00FF); - - case LVALUE_REG_H: - return VALUE_16(*lvalue.register_address >> 8); - } -} - -static void write_lvalue(GB_gameboy_t *gb, lvalue_t lvalue, uint16_t value) -{ - switch (lvalue.kind) { - case LVALUE_MEMORY: - if (lvalue.memory_address.has_bank) { - banking_state_t state; - save_banking_state(gb, &state); - switch_banking_state(gb, lvalue.memory_address.bank); - GB_write_memory(gb, lvalue.memory_address.value, value); - restore_banking_state(gb, &state); - return; - } - GB_write_memory(gb, lvalue.memory_address.value, value); - return; - - case LVALUE_REG16: - *lvalue.register_address = value; - return; - - case LVALUE_REG_L: - *lvalue.register_address &= 0xFF00; - *lvalue.register_address |= value & 0xFF; - return; - - case LVALUE_REG_H: - *lvalue.register_address &= 0x00FF; - *lvalue.register_address |= value << 8; - return; - } -} - -/* 16 bit value 16 bit value = 16 bit value - 25 bit address 16 bit value = 25 bit address - 16 bit value 25 bit address = 25 bit address - 25 bit address 25 bit address = 16 bit value (since adding pointers, for examples, makes no sense) - - Boolean operators always return a 16-bit value - */ -#define FIX_BANK(x) ((value_t){a.has_bank ^ b.has_bank, a.has_bank? a.bank : b.bank, (x)}) - -static value_t add(value_t a, value_t b) {return FIX_BANK(a.value + b.value);} -static value_t sub(value_t a, value_t b) {return FIX_BANK(a.value - b.value);} -static value_t mul(value_t a, value_t b) {return FIX_BANK(a.value * b.value);} -static value_t _div(value_t a, value_t b) { - if (b.value == 0) { - return FIX_BANK(0); - } - return FIX_BANK(a.value / b.value); -}; -static value_t mod(value_t a, value_t b) { - if (b.value == 0) { - return FIX_BANK(0); - } - return FIX_BANK(a.value % b.value); -}; -static value_t and(value_t a, value_t b) {return FIX_BANK(a.value & b.value);} -static value_t or(value_t a, value_t b) {return FIX_BANK(a.value | b.value);} -static value_t xor(value_t a, value_t b) {return FIX_BANK(a.value ^ b.value);} -static value_t shleft(value_t a, value_t b) {return FIX_BANK(a.value << b.value);} -static value_t shright(value_t a, value_t b) {return FIX_BANK(a.value >> b.value);} -static value_t assign(GB_gameboy_t *gb, lvalue_t a, uint16_t b) -{ - write_lvalue(gb, a, b); - return read_lvalue(gb, a); -} - -static value_t bool_and(value_t a, value_t b) {return VALUE_16(a.value && b.value);} -static value_t bool_or(value_t a, value_t b) {return VALUE_16(a.value || b.value);} -static value_t equals(value_t a, value_t b) {return VALUE_16(a.value == b.value);} -static value_t different(value_t a, value_t b) {return VALUE_16(a.value != b.value);} -static value_t lower(value_t a, value_t b) {return VALUE_16(a.value < b.value);} -static value_t greater(value_t a, value_t b) {return VALUE_16(a.value > b.value);} -static value_t lower_equals(value_t a, value_t b) {return VALUE_16(a.value <= b.value);} -static value_t greater_equals(value_t a, value_t b) {return VALUE_16(a.value >= b.value);} -static value_t bank(value_t a, value_t b) {return (value_t) {true, a.value, b.value};} - - -static struct { - const char *string; - char priority; - value_t (*operator)(value_t, value_t); - value_t (*lvalue_operator)(GB_gameboy_t *, lvalue_t, uint16_t); -} operators[] = -{ - // Yes. This is not C-like. But it makes much more sense. - // Deal with it. - {"+", 0, add}, - {"-", 0, sub}, - {"||", 0, bool_or}, - {"|", 0, or}, - {"*", 1, mul}, - {"/", 1, _div}, - {"%", 1, mod}, - {"&&", 1, bool_and}, - {"&", 1, and}, - {"^", 1, xor}, - {"<<", 2, shleft}, - {"<=", 3, lower_equals}, - {"<", 3, lower}, - {">>", 2, shright}, - {">=", 3, greater_equals}, - {">", 3, greater}, - {"==", 3, equals}, - {"=", -1, NULL, assign}, - {"!=", 3, different}, - {":", 4, bank}, -}; - -value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, - size_t length, bool *error, - uint16_t *watchpoint_address, uint8_t *watchpoint_new_value); - -static lvalue_t debugger_evaluate_lvalue(GB_gameboy_t *gb, const char *string, - size_t length, bool *error, - uint16_t *watchpoint_address, uint8_t *watchpoint_new_value) -{ - *error = false; - // Strip whitespace - while (length && (string[0] == ' ' || string[0] == '\n' || string[0] == '\r' || string[0] == '\t')) { - string++; - length--; - } - while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) { - length--; - } - if (length == 0) - { - GB_log(gb, "Expected expression.\n"); - *error = true; - return (lvalue_t){0,}; - } - if (string[0] == '(' && string[length - 1] == ')') { - // Attempt to strip parentheses - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '(') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ')') depth--; - } - if (depth == 0) return debugger_evaluate_lvalue(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value); - } - else if (string[0] == '[' && string[length - 1] == ']') { - // Attempt to strip square parentheses (memory dereference) - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '[') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ']') depth--; - } - if (depth == 0) { - return (lvalue_t){LVALUE_MEMORY, .memory_address = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value)}; - } - } - - // Registers - if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) { - if (length == 1) { - switch (string[0]) { - case 'a': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_AF]}; - case 'f': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_AF]}; - case 'b': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_BC]}; - case 'c': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_BC]}; - case 'd': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_DE]}; - case 'e': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_DE]}; - case 'h': return (lvalue_t){LVALUE_REG_H, .register_address = &gb->registers[GB_REGISTER_HL]}; - case 'l': return (lvalue_t){LVALUE_REG_L, .register_address = &gb->registers[GB_REGISTER_HL]}; - } - } - else if (length == 2) { - switch (string[0]) { - case 'a': if (string[1] == 'f') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_AF]}; - case 'b': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_BC]}; - case 'd': if (string[1] == 'e') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_DE]}; - case 'h': if (string[1] == 'l') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_HL]}; - case 's': if (string[1] == 'p') return (lvalue_t){LVALUE_REG16, .register_address = &gb->registers[GB_REGISTER_SP]}; - case 'p': if (string[1] == 'c') return (lvalue_t){LVALUE_REG16, .register_address = &gb->pc}; - } - } - GB_log(gb, "Unknown register: %.*s\n", (unsigned int) length, string); - *error = true; - return (lvalue_t){0,}; - } - - GB_log(gb, "Expression is not an lvalue: %.*s\n", (unsigned int) length, string); - *error = true; - return (lvalue_t){0,}; -} - -#define ERROR ((value_t){0,}) -value_t debugger_evaluate(GB_gameboy_t *gb, const char *string, - size_t length, bool *error, - uint16_t *watchpoint_address, uint8_t *watchpoint_new_value) -{ - /* Disable watchpoints while evaulating expressions */ - uint16_t n_watchpoints = gb->n_watchpoints; - gb->n_watchpoints = 0; - - value_t ret = ERROR; - - *error = false; - // Strip whitespace - while (length && (string[0] == ' ' || string[0] == '\n' || string[0] == '\r' || string[0] == '\t')) { - string++; - length--; - } - while (length && (string[length-1] == ' ' || string[length-1] == '\n' || string[length-1] == '\r' || string[length-1] == '\t')) { - length--; - } - if (length == 0) - { - GB_log(gb, "Expected expression.\n"); - *error = true; - goto exit; - } - if (string[0] == '(' && string[length - 1] == ')') { - // Attempt to strip parentheses - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '(') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ')') depth--; - } - if (depth == 0) { - ret = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value); - goto exit; - } - } - else if (string[0] == '[' && string[length - 1] == ']') { - // Attempt to strip square parentheses (memory dereference) - signed int depth = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '[') depth++; - if (depth == 0) { - // First and last are not matching - depth = 1; - break; - } - if (string[i] == ']') depth--; - } - - if (depth == 0) { - value_t addr = debugger_evaluate(gb, string + 1, length - 2, error, watchpoint_address, watchpoint_new_value); - banking_state_t state; - if (addr.bank) { - save_banking_state(gb, &state); - switch_banking_state(gb, addr.bank); - } - ret = VALUE_16(GB_read_memory(gb, addr.value)); - if (addr.bank) { - restore_banking_state(gb, &state); - } - goto exit; - } - - } - // Search for lowest priority operator - signed int depth = 0; - unsigned int operator_index = -1; - unsigned int operator_pos = 0; - for (int i = 0; i < length; i++) { - if (string[i] == '(') depth++; - else if (string[i] == ')') depth--; - else if (string[i] == '[') depth++; - else if (string[i] == ']') depth--; - else if (depth == 0) { - for (int j = 0; j < sizeof(operators) / sizeof(operators[0]); j++) { - if (strlen(operators[j].string) > length - i) continue; // Operator too big. - // Priority higher than what we already have. - if (operator_index != -1 && operators[operator_index].priority < operators[j].priority) continue; - unsigned long operator_length = strlen(operators[j].string); - if (memcmp(string + i, operators[j].string, operator_length) == 0) { - // Found an operator! - operator_pos = i; - operator_index = j; - /* for supporting = vs ==, etc*/ - i += operator_length - 1; - break; - } - } - } - } - if (operator_index != -1) { - unsigned int right_start = (unsigned int)(operator_pos + strlen(operators[operator_index].string)); - value_t right = debugger_evaluate(gb, string + right_start, length - right_start, error, watchpoint_address, watchpoint_new_value); - if (*error) goto exit; - if (operators[operator_index].lvalue_operator) { - lvalue_t left = debugger_evaluate_lvalue(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value); - if (*error) goto exit; - ret = operators[operator_index].lvalue_operator(gb, left, right.value); - goto exit; - } - value_t left = debugger_evaluate(gb, string, operator_pos, error, watchpoint_address, watchpoint_new_value); - if (*error) goto exit; - ret = operators[operator_index].operator(left, right); - goto exit; - } - - // Not an expression - must be a register or a literal - - // Registers - if (string[0] != '$' && (string[0] < '0' || string[0] > '9')) { - if (length == 1) { - switch (string[0]) { - case 'a': ret = VALUE_16(gb->registers[GB_REGISTER_AF] >> 8); goto exit; - case 'f': ret = VALUE_16(gb->registers[GB_REGISTER_AF] & 0xFF); goto exit; - case 'b': ret = VALUE_16(gb->registers[GB_REGISTER_BC] >> 8); goto exit; - case 'c': ret = VALUE_16(gb->registers[GB_REGISTER_BC] & 0xFF); goto exit; - case 'd': ret = VALUE_16(gb->registers[GB_REGISTER_DE] >> 8); goto exit; - case 'e': ret = VALUE_16(gb->registers[GB_REGISTER_DE] & 0xFF); goto exit; - case 'h': ret = VALUE_16(gb->registers[GB_REGISTER_HL] >> 8); goto exit; - case 'l': ret = VALUE_16(gb->registers[GB_REGISTER_HL] & 0xFF); goto exit; - } - } - else if (length == 2) { - switch (string[0]) { - case 'a': if (string[1] == 'f') {ret = VALUE_16(gb->registers[GB_REGISTER_AF]); goto exit;} - case 'b': if (string[1] == 'c') {ret = VALUE_16(gb->registers[GB_REGISTER_BC]); goto exit;} - case 'd': if (string[1] == 'e') {ret = VALUE_16(gb->registers[GB_REGISTER_DE]); goto exit;} - case 'h': if (string[1] == 'l') {ret = VALUE_16(gb->registers[GB_REGISTER_HL]); goto exit;} - case 's': if (string[1] == 'p') {ret = VALUE_16(gb->registers[GB_REGISTER_SP]); goto exit;} - case 'p': if (string[1] == 'c') {ret = (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}; goto exit;} - } - } - else if (length == 3) { - if (watchpoint_address && memcmp(string, "old", 3) == 0) { - ret = VALUE_16(GB_read_memory(gb, *watchpoint_address)); - goto exit; - } - - if (watchpoint_new_value && memcmp(string, "new", 3) == 0) { - ret = VALUE_16(*watchpoint_new_value); - goto exit; - } - - /* $new is identical to $old in read conditions */ - if (watchpoint_address && memcmp(string, "new", 3) == 0) { - ret = VALUE_16(GB_read_memory(gb, *watchpoint_address)); - goto exit; - } - } - - char symbol_name[length + 1]; - memcpy(symbol_name, string, length); - symbol_name[length] = 0; - const GB_symbol_t *symbol = GB_reversed_map_find_symbol(&gb->reversed_symbol_map, symbol_name); - if (symbol) { - ret = (value_t){true, symbol->bank, symbol->addr}; - goto exit; - } - - GB_log(gb, "Unknown register or symbol: %.*s\n", (unsigned int) length, string); - *error = true; - goto exit; - } - - char *end; - int base = 10; - if (string[0] == '$') { - string++; - base = 16; - length--; - } - uint16_t literal = (uint16_t) (strtol(string, &end, base)); - if (end != string + length) { - GB_log(gb, "Failed to parse: %.*s\n", (unsigned int) length, string); - *error = true; - goto exit; - } - ret = VALUE_16(literal); -exit: - gb->n_watchpoints = n_watchpoints; - return ret; -} - -struct debugger_command_s; -typedef bool debugger_command_imp_t(GB_gameboy_t *gb, char *arguments, char *modifiers, const struct debugger_command_s *command); - -typedef struct debugger_command_s { - const char *command; - uint8_t min_length; - debugger_command_imp_t *implementation; - const char *help_string; // Null if should not appear in help - const char *arguments_format; // For usage message - const char *modifiers_format; // For usage message -} debugger_command_t; - -static const char *lstrip(const char *str) -{ - while (*str == ' ' || *str == '\t') { - str++; - } - return str; -} - -#define STOPPED_ONLY \ -if (!gb->debug_stopped) { \ -GB_log(gb, "Program is running. \n"); \ -return false; \ -} - -#define NO_MODIFIERS \ -if (modifiers) { \ -print_usage(gb, command); \ -return true; \ -} - -static void print_usage(GB_gameboy_t *gb, const debugger_command_t *command) -{ - GB_log(gb, "Usage: %s", command->command); - - if (command->modifiers_format) { - GB_log(gb, "[/%s]", command->modifiers_format); - } - - if (command->arguments_format) { - GB_log(gb, " %s", command->arguments_format); - } - - GB_log(gb, "\n"); -} - -static bool cont(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - return false; -} - -static bool next(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - gb->debug_next_command = true; - gb->debug_call_depth = 0; - return false; -} - -static bool step(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - return false; -} - -static bool finish(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - gb->debug_fin_command = true; - gb->debug_call_depth = 0; - return false; -} - -static bool stack_leak_detection(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - gb->debug_stopped = false; - gb->stack_leak_detection = true; - gb->debug_call_depth = 0; - return false; -} - -static bool registers(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - GB_log(gb, "AF = $%04x\n", gb->registers[GB_REGISTER_AF]); /* AF can't really be an address */ - GB_log(gb, "BC = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_BC], false)); - GB_log(gb, "DE = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_DE], false)); - GB_log(gb, "HL = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_HL], false)); - GB_log(gb, "SP = %s\n", value_to_string(gb, gb->registers[GB_REGISTER_SP], false)); - GB_log(gb, "PC = %s\n", value_to_string(gb, gb->pc, false)); - return true; -} - -/* Find the index of the closest breakpoint equal or greater to addr */ -static uint16_t find_breakpoint(GB_gameboy_t *gb, value_t addr) -{ - if (!gb->breakpoints) { - return 0; - } - - uint32_t key = BP_KEY(addr); - - int min = 0; - int max = gb->n_breakpoints; - while (min < max) { - uint16_t pivot = (min + max) / 2; - if (gb->breakpoints[pivot].key == key) return pivot; - if (gb->breakpoints[pivot].key > key) { - max = pivot; - } - else { - min = pivot + 1; - } - } - return (uint16_t) min; -} - -static bool breakpoint(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments)) == 0) { - print_usage(gb, command); - return true; - } - - if (gb->n_breakpoints == (typeof(gb->n_breakpoints)) -1) { - GB_log(gb, "Too many breakpoints set\n"); - return true; - } - - char *condition = NULL; - if ((condition = strstr(arguments, " if "))) { - *condition = 0; - condition += strlen(" if "); - /* Verify condition is sane (Todo: This might have side effects!) */ - bool error; - debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, NULL, NULL); - if (error) return true; - - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); - uint32_t key = BP_KEY(result); - - if (error) return true; - - uint16_t index = find_breakpoint(gb, result); - if (index < gb->n_breakpoints && gb->breakpoints[index].key == key) { - GB_log(gb, "Breakpoint already set at %s\n", debugger_value_to_string(gb, result, true)); - if (!gb->breakpoints[index].condition && condition) { - GB_log(gb, "Added condition to breakpoint\n"); - gb->breakpoints[index].condition = strdup(condition); - } - else if (gb->breakpoints[index].condition && condition) { - GB_log(gb, "Replaced breakpoint condition\n"); - free(gb->breakpoints[index].condition); - gb->breakpoints[index].condition = strdup(condition); - } - else if (gb->breakpoints[index].condition && !condition) { - GB_log(gb, "Removed breakpoint condition\n"); - free(gb->breakpoints[index].condition); - gb->breakpoints[index].condition = NULL; - } - return true; - } - - gb->breakpoints = realloc(gb->breakpoints, (gb->n_breakpoints + 1) * sizeof(gb->breakpoints[0])); - memmove(&gb->breakpoints[index + 1], &gb->breakpoints[index], (gb->n_breakpoints - index) * sizeof(gb->breakpoints[0])); - gb->breakpoints[index].key = key; - - if (condition) { - gb->breakpoints[index].condition = strdup(condition); - } - else { - gb->breakpoints[index].condition = NULL; - } - gb->n_breakpoints++; - - GB_log(gb, "Breakpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -static bool delete(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments)) == 0) { - for (unsigned i = gb->n_breakpoints; i--;) { - if (gb->breakpoints[i].condition) { - free(gb->breakpoints[i].condition); - } - } - free(gb->breakpoints); - gb->breakpoints = NULL; - gb->n_breakpoints = 0; - return true; - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); - uint32_t key = BP_KEY(result); - - if (error) return true; - - uint16_t index = find_breakpoint(gb, result); - if (index >= gb->n_breakpoints || gb->breakpoints[index].key != key) { - GB_log(gb, "No breakpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; - } - - if (gb->breakpoints[index].condition) { - free(gb->breakpoints[index].condition); - } - - memmove(&gb->breakpoints[index], &gb->breakpoints[index + 1], (gb->n_breakpoints - index - 1) * sizeof(gb->breakpoints[0])); - gb->n_breakpoints--; - gb->breakpoints = realloc(gb->breakpoints, gb->n_breakpoints * sizeof(gb->breakpoints[0])); - - GB_log(gb, "Breakpoint removed from %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -/* Find the index of the closest watchpoint equal or greater to addr */ -static uint16_t find_watchpoint(GB_gameboy_t *gb, value_t addr) -{ - if (!gb->watchpoints) { - return 0; - } - uint32_t key = WP_KEY(addr); - int min = 0; - int max = gb->n_watchpoints; - while (min < max) { - uint16_t pivot = (min + max) / 2; - if (gb->watchpoints[pivot].key == key) return pivot; - if (gb->watchpoints[pivot].key > key) { - max = pivot; - } - else { - min = pivot + 1; - } - } - return (uint16_t) min; -} - -static bool watch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { -print_usage: - print_usage(gb, command); - return true; - } - - if (gb->n_watchpoints == (typeof(gb->n_watchpoints)) -1) { - GB_log(gb, "Too many watchpoints set\n"); - return true; - } - - if (!modifiers) { - modifiers = "w"; - } - - uint8_t flags = 0; - while (*modifiers) { - switch (*modifiers) { - case 'r': - flags |= GB_WATCHPOINT_R; - break; - case 'w': - flags |= GB_WATCHPOINT_W; - break; - default: - goto print_usage; - } - modifiers++; - } - - if (!flags) { - goto print_usage; - } - - char *condition = NULL; - if ((condition = strstr(arguments, " if "))) { - *condition = 0; - condition += strlen(" if "); - /* Verify condition is sane (Todo: This might have side effects!) */ - bool error; - /* To make $new and $old legal */ - uint16_t dummy = 0; - uint8_t dummy2 = 0; - debugger_evaluate(gb, condition, (unsigned int)strlen(condition), &error, &dummy, &dummy2); - if (error) return true; - - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); - uint32_t key = WP_KEY(result); - - if (error) return true; - - uint16_t index = find_watchpoint(gb, result); - if (index < gb->n_watchpoints && gb->watchpoints[index].key == key) { - GB_log(gb, "Watchpoint already set at %s\n", debugger_value_to_string(gb, result, true)); - if (gb->watchpoints[index].flags != flags) { - GB_log(gb, "Modified watchpoint type\n"); - gb->watchpoints[index].flags = flags; - } - if (!gb->watchpoints[index].condition && condition) { - GB_log(gb, "Added condition to watchpoint\n"); - gb->watchpoints[index].condition = strdup(condition); - } - else if (gb->watchpoints[index].condition && condition) { - GB_log(gb, "Replaced watchpoint condition\n"); - free(gb->watchpoints[index].condition); - gb->watchpoints[index].condition = strdup(condition); - } - else if (gb->watchpoints[index].condition && !condition) { - GB_log(gb, "Removed watchpoint condition\n"); - free(gb->watchpoints[index].condition); - gb->watchpoints[index].condition = NULL; - } - return true; - } - - gb->watchpoints = realloc(gb->watchpoints, (gb->n_watchpoints + 1) * sizeof(gb->watchpoints[0])); - memmove(&gb->watchpoints[index + 1], &gb->watchpoints[index], (gb->n_watchpoints - index) * sizeof(gb->watchpoints[0])); - gb->watchpoints[index].key = key; - gb->watchpoints[index].flags = flags; - if (condition) { - gb->watchpoints[index].condition = strdup(condition); - } - else { - gb->watchpoints[index].condition = NULL; - } - gb->n_watchpoints++; - - GB_log(gb, "Watchpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -static bool unwatch(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments)) == 0) { - for (unsigned i = gb->n_watchpoints; i--;) { - if (gb->watchpoints[i].condition) { - free(gb->watchpoints[i].condition); - } - } - free(gb->watchpoints); - gb->watchpoints = NULL; - gb->n_watchpoints = 0; - return true; - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); - uint32_t key = WP_KEY(result); - - if (error) return true; - - uint16_t index = find_watchpoint(gb, result); - if (index >= gb->n_watchpoints || gb->watchpoints[index].key != key) { - GB_log(gb, "No watchpoint set at %s\n", debugger_value_to_string(gb, result, true)); - return true; - } - - if (gb->watchpoints[index].condition) { - free(gb->watchpoints[index].condition); - } - - memmove(&gb->watchpoints[index], &gb->watchpoints[index + 1], (gb->n_watchpoints - index - 1) * sizeof(gb->watchpoints[0])); - gb->n_watchpoints--; - gb->watchpoints = realloc(gb->watchpoints, gb->n_watchpoints* sizeof(gb->watchpoints[0])); - - GB_log(gb, "Watchpoint removed from %s\n", debugger_value_to_string(gb, result, true)); - return true; -} - -static bool list(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - if (gb->n_breakpoints == 0) { - GB_log(gb, "No breakpoints set.\n"); - } - else { - GB_log(gb, "%d breakpoint(s) set:\n", gb->n_breakpoints); - for (uint16_t i = 0; i < gb->n_breakpoints; i++) { - value_t addr = (value_t){gb->breakpoints[i].bank != (uint16_t)-1, gb->breakpoints[i].bank, gb->breakpoints[i].addr}; - if (gb->breakpoints[i].condition) { - GB_log(gb, " %d. %s (Condition: %s)\n", i + 1, - debugger_value_to_string(gb, addr, addr.has_bank), - gb->breakpoints[i].condition); - } - else { - GB_log(gb, " %d. %s\n", i + 1, debugger_value_to_string(gb, addr, addr.has_bank)); - } - } - } - - if (gb->n_watchpoints == 0) { - GB_log(gb, "No watchpoints set.\n"); - } - else { - GB_log(gb, "%d watchpoint(s) set:\n", gb->n_watchpoints); - for (uint16_t i = 0; i < gb->n_watchpoints; i++) { - value_t addr = (value_t){gb->watchpoints[i].bank != (uint16_t)-1, gb->watchpoints[i].bank, gb->watchpoints[i].addr}; - if (gb->watchpoints[i].condition) { - GB_log(gb, " %d. %s (%c%c, Condition: %s)\n", i + 1, debugger_value_to_string(gb, addr, addr.has_bank), - (gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-', - (gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-', - gb->watchpoints[i].condition); - } - else { - GB_log(gb, " %d. %s (%c%c)\n", i + 1, debugger_value_to_string(gb,addr, addr.has_bank), - (gb->watchpoints[i].flags & GB_WATCHPOINT_R)? 'r' : '-', - (gb->watchpoints[i].flags & GB_WATCHPOINT_W)? 'w' : '-'); - } - } - } - - return true; -} - -static bool _should_break(GB_gameboy_t *gb, value_t addr) -{ - uint16_t index = find_breakpoint(gb, addr); - uint32_t key = BP_KEY(addr); - - if (index < gb->n_breakpoints && gb->breakpoints[index].key == key) { - if (!gb->breakpoints[index].condition) { - return true; - } - bool error; - bool condition = debugger_evaluate(gb, gb->breakpoints[index].condition, - (unsigned int)strlen(gb->breakpoints[index].condition), &error, NULL, NULL).value; - if (error) { - /* Should never happen */ - GB_log(gb, "An internal error has occured\n"); - return true; - } - return condition; - } - return false; -} - -static bool should_break(GB_gameboy_t *gb, uint16_t addr) -{ - /* Try any-bank breakpoint */ - value_t full_addr = (VALUE_16(addr)); - if (_should_break(gb, full_addr)) return true; - - /* Try bank-specific breakpoint */ - full_addr.has_bank = true; - full_addr.bank = bank_for_addr(gb, addr); - return _should_break(gb, full_addr); -} - -static bool print(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { - print_usage(gb, command); - return true; - } - - if (!modifiers || !modifiers[0]) { - modifiers = "a"; - } - else if (modifiers[1]) { - print_usage(gb, command); - return true; - } - - bool error; - value_t result = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); - if (!error) { - switch (modifiers[0]) { - case 'a': - GB_log(gb, "=%s\n", debugger_value_to_string(gb, result, false)); - break; - case 'd': - GB_log(gb, "=%d\n", result.value); - break; - case 'x': - GB_log(gb, "=$%x\n", result.value); - break; - case 'o': - GB_log(gb, "=0%o\n", result.value); - break; - case 'b': - { - if (!result.value) { - GB_log(gb, "=%%0\n"); - break; - } - char binary[17]; - binary[16] = 0; - char *ptr = &binary[16]; - while (result.value) { - *(--ptr) = (result.value & 1)? '1' : '0'; - result.value >>= 1; - } - GB_log(gb, "=%%%s\n", ptr); - break; - } - default: - break; - } - } - return true; -} - -static bool examine(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { - print_usage(gb, command); - return true; - } - - bool error; - value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); - uint16_t count = 32; - - if (modifiers) { - char *end; - count = (uint16_t) (strtol(modifiers, &end, 10)); - if (*end) { - print_usage(gb, command); - return true; - } - } - - if (!error) { - if (addr.has_bank) { - banking_state_t old_state; - save_banking_state(gb, &old_state); - switch_banking_state(gb, addr.bank); - - while (count) { - GB_log(gb, "%02x:%04x: ", addr.bank, addr.value); - for (int i = 0; i < 16 && count; i++) { - GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i)); - count--; - } - addr.value += 16; - GB_log(gb, "\n"); - } - - restore_banking_state(gb, &old_state); - } - else { - while (count) { - GB_log(gb, "%04x: ", addr.value); - for (int i = 0; i < 16 && count; i++) { - GB_log(gb, "%02x ", GB_read_memory(gb, addr.value + i)); - count--; - } - addr.value += 16; - GB_log(gb, "\n"); - } - } - } - return true; -} - -static bool disassemble(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - if (strlen(lstrip(arguments)) == 0) { - arguments = "pc"; - } - - bool error; - value_t addr = debugger_evaluate(gb, arguments, (unsigned int)strlen(arguments), &error, NULL, NULL); - uint16_t count = 5; - - if (modifiers) { - char *end; - count = (uint16_t) (strtol(modifiers, &end, 10)); - if (*end) { - print_usage(gb, command); - return true; - } - } - - if (!error) { - if (addr.has_bank) { - banking_state_t old_state; - save_banking_state(gb, &old_state); - switch_banking_state(gb, addr.bank); - - GB_cpu_disassemble(gb, addr.value, count); - - restore_banking_state(gb, &old_state); - } - else { - GB_cpu_disassemble(gb, addr.value, count); - } - } - return true; -} - -static bool mbc(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - const GB_cartridge_t *cartridge = gb->cartridge_type; - - if (cartridge->has_ram) { - GB_log(gb, "Cartrdige includes%s RAM: $%x bytes\n", cartridge->has_battery? " battery-backed": "", gb->mbc_ram_size); - } - else { - GB_log(gb, "No cartridge RAM\n"); - } - - if (cartridge->mbc_type) { - static const char * const mapper_names[] = { - [GB_MBC1] = "MBC1", - [GB_MBC2] = "MBC2", - [GB_MBC3] = "MBC3", - [GB_MBC5] = "MBC5", - [GB_HUC1] = "HUC1", - [GB_HUC3] = "HUC3", - }; - GB_log(gb, "%s\n", mapper_names[cartridge->mbc_type]); - GB_log(gb, "Current mapped ROM bank: %x\n", gb->mbc_rom_bank); - if (cartridge->has_ram) { - GB_log(gb, "Current mapped RAM bank: %x\n", gb->mbc_ram_bank); - GB_log(gb, "RAM is curently %s\n", gb->mbc_ram_enable? "enabled" : "disabled"); - } - if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_STANDARD_MBC1_WIRING) { - GB_log(gb, "MBC1 banking mode is %s\n", gb->mbc1.mode == 1 ? "RAM" : "ROM"); - } - if (cartridge->mbc_type == GB_MBC1 && gb->mbc1_wiring == GB_MBC1M_WIRING) { - GB_log(gb, "MBC1 uses MBC1M wiring. \n"); - GB_log(gb, "Current mapped ROM0 bank: %x\n", gb->mbc_rom0_bank); - GB_log(gb, "MBC1 multicart banking mode is %s\n", gb->mbc1.mode == 1 ? "enabled" : "disabled"); - } - - } - else { - GB_log(gb, "No MBC\n"); - } - - if (cartridge->has_rumble) { - GB_log(gb, "Cart contains a rumble pak\n"); - } - - if (cartridge->has_rtc) { - GB_log(gb, "Cart contains a real time clock\n"); - } - - return true; -} - -static bool backtrace(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - GB_log(gb, " 1. %s\n", debugger_value_to_string(gb, (value_t){true, bank_for_addr(gb, gb->pc), gb->pc}, true)); - for (unsigned int i = gb->backtrace_size; i--;) { - GB_log(gb, "%3d. %s\n", gb->backtrace_size - i + 1, debugger_value_to_string(gb, (value_t){true, gb->backtrace_returns[i].bank, gb->backtrace_returns[i].addr}, true)); - } - - return true; -} - -static bool ticks(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - STOPPED_ONLY - - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - GB_log(gb, "Ticks: %lu. (Resetting)\n", gb->debugger_ticks); - gb->debugger_ticks = 0; - - return true; -} - - -static bool palettes(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - - if (!gb->is_cgb) { - GB_log(gb, "Not available on a DMG.\n"); - return true; - } - - GB_log(gb, "Background palettes: \n"); - for (unsigned i = 0; i < 32; i++) { - GB_log(gb, "%04x ", ((uint16_t *)&gb->background_palettes_data)[i]); - if (i % 4 == 3) { - GB_log(gb, "\n"); - } - } - - GB_log(gb, "Sprites palettes: \n"); - for (unsigned i = 0; i < 32; i++) { - GB_log(gb, "%04x ", ((uint16_t *)&gb->sprite_palettes_data)[i]); - if (i % 4 == 3) { - GB_log(gb, "\n"); - } - } - - return true; -} - -static bool lcd(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command) -{ - NO_MODIFIERS - if (strlen(lstrip(arguments))) { - print_usage(gb, command); - return true; - } - GB_log(gb, "LCDC:\n"); - GB_log(gb, " LCD enabled: %s\n",(gb->io_registers[GB_IO_LCDC] & 128)? "Enabled" : "Disabled"); - GB_log(gb, " %s: %s\n", gb->is_cgb? (gb->cgb_mode? "Sprite priority flags" : "Background and Window") : "Background", - (gb->io_registers[GB_IO_LCDC] & 1)? "Enabled" : "Disabled"); - GB_log(gb, " Objects: %s\n", (gb->io_registers[GB_IO_LCDC] & 2)? "Enabled" : "Disabled"); - GB_log(gb, " Object size: %s\n", (gb->io_registers[GB_IO_LCDC] & 4)? "8x16" : "8x8"); - GB_log(gb, " Background tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 8)? "$9C00" : "$9800"); - GB_log(gb, " Background and Window Tileset: %s\n", (gb->io_registers[GB_IO_LCDC] & 16)? "$8000" : "$8800"); - GB_log(gb, " Window: %s\n", (gb->io_registers[GB_IO_LCDC] & 32)? "Enabled" : "Disabled"); - GB_log(gb, " Window tilemap: %s\n", (gb->io_registers[GB_IO_LCDC] & 64)? "$9C00" : "$9800"); - - GB_log(gb, "\nSTAT:\n"); - static const char *modes[] = {"Mode 0, H-Blank", "Mode 1, V-Blank", "Mode 2, OAM", "Mode 3, Rendering"}; - GB_log(gb, " Current mode: %s\n", modes[gb->io_registers[GB_IO_STAT] & 3]); - GB_log(gb, " LYC flag: %s\n", (gb->io_registers[GB_IO_STAT] & 4)? "On" : "Off"); - GB_log(gb, " H-Blank interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 8)? "Enabled" : "Disabled"); - GB_log(gb, " V-Blank interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 16)? "Enabled" : "Disabled"); - GB_log(gb, " OAM interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 32)? "Enabled" : "Disabled"); - GB_log(gb, " LYC interrupt: %s\n", (gb->io_registers[GB_IO_STAT] & 64)? "Enabled" : "Disabled"); - - - GB_log(gb, "\nCycles since frame start: %d\n", gb->display_cycles); - GB_log(gb, "Current line: %d\n", gb->display_cycles / 456); - GB_log(gb, "LY: %d\n", gb->io_registers[GB_IO_LY]); - GB_log(gb, "LYC: %d\n", gb->io_registers[GB_IO_LYC]); - GB_log(gb, "Window position: %d, %d\n", (signed) gb->io_registers[GB_IO_WX] - 7 , gb->io_registers[GB_IO_WY]); - GB_log(gb, "Interrupt line: %s\n", gb->stat_interrupt_line? "On" : "Off"); - - return true; -} - -static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *command); - -#define HELP_NEWLINE "\n " - -/* Commands without implementations are aliases of the previous non-alias commands */ -static const debugger_command_t commands[] = { - {"continue", 1, cont, "Continue running until next stop"}, - {"next", 1, next, "Run the next instruction, skipping over function calls"}, - {"step", 1, step, "Run the next instruction, stepping into function calls"}, - {"finish", 1, finish, "Run until the current function returns"}, - {"backtrace", 2, backtrace, "Display the current call stack"}, - {"bt", 2, }, /* Alias */ - {"sld", 3, stack_leak_detection, "Like finish, but stops if a stack leak is detected (Experimental)"}, - {"ticks", 2, ticks, "Display the number of CPU ticks since the last time 'ticks' was used"}, - {"registers", 1, registers, "Print values of processor registers and other important registers"}, - {"cartridge", 2, mbc, "Displays information about the MBC and cartridge"}, - {"mbc", 3, }, /* Alias */ - {"lcd", 3, lcd, "Displays information about the current state of the LCD controller"}, - {"palettes", 3, palettes, "Displays the current CGB palettes"}, - {"breakpoint", 1, breakpoint, "Add a new breakpoint at the specified address/expression" HELP_NEWLINE - "Can also modify the condition of existing breakpoints.", - "[ if ]"}, - {"delete", 2, delete, "Delete a breakpoint by its address, or all breakpoints", "[]"}, - {"watch", 1, watch, "Add a new watchpoint at the specified address/expression." HELP_NEWLINE - "Can also modify the condition and type of existing watchpoints." HELP_NEWLINE - "Default watchpoint type is write-only.", - "[ if ]", "(r|w|rw)"}, - {"unwatch", 3, unwatch, "Delete a watchpoint by its address, or all watchpoints", "[]"}, - {"list", 1, list, "List all set breakpoints and watchpoints"}, - {"print", 1, print, "Evaluate and print an expression" HELP_NEWLINE - "Use modifier to format as an address (a, default) or as a number in" HELP_NEWLINE - "decimal (d), hexadecimal (x), octal (o) or binary (b).", - "", "format"}, - {"eval", 2, }, /* Alias */ - {"examine", 2, examine, "Examine values at address", "", "count"}, - {"x", 1, }, /* Alias */ - {"disassemble", 1, disassemble, "Disassemble instructions at address", "", "count"}, - - - {"help", 1, help, "List available commands or show help for the specified command", "[]"}, - {NULL,}, /* Null terminator */ -}; - -static const debugger_command_t *find_command(const char *string) -{ - size_t length = strlen(string); - for (const debugger_command_t *command = commands; command->command; command++) { - if (command->min_length > length) continue; - if (memcmp(command->command, string, length) == 0) { /* Is a substring? */ - /* Aliases */ - while (!command->implementation) { - command--; - } - return command; - } - } - - return NULL; -} - -static void print_command_shortcut(GB_gameboy_t *gb, const debugger_command_t *command) -{ - GB_attributed_log(gb, GB_LOG_BOLD | GB_LOG_UNDERLINE, "%.*s", command->min_length, command->command); - GB_attributed_log(gb, GB_LOG_BOLD , "%s", command->command + command->min_length); -} - -static void print_command_description(GB_gameboy_t *gb, const debugger_command_t *command) -{ - print_command_shortcut(gb, command); - GB_log(gb, ": "); - GB_log(gb, (const char *)&" %s\n" + strlen(command->command), command->help_string); -} - -static bool help(GB_gameboy_t *gb, char *arguments, char *modifiers, const debugger_command_t *ignored) -{ - const debugger_command_t *command = find_command(arguments); - if (command) { - print_command_description(gb, command); - GB_log(gb, "\n"); - print_usage(gb, command); - - command++; - if (command->command && !command->implementation) { /* Command has aliases*/ - GB_log(gb, "\nAliases: "); - do { - print_command_shortcut(gb, command); - GB_log(gb, " "); - command++; - } while (command->command && !command->implementation); - GB_log(gb, "\n"); - } - return true; - } - for (command = commands; command->command; command++) { - if (command->help_string) { - print_command_description(gb, command); - } - } - return true; -} - -void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr) -{ - /* Called just after the CPU calls a function/enters an interrupt/etc... */ - - if (gb->stack_leak_detection) { - if (gb->debug_call_depth >= sizeof(gb->sp_for_call_depth) / sizeof(gb->sp_for_call_depth[0])) { - GB_log(gb, "Potential stack overflow detected (Functions nest too much). \n"); - gb->debug_stopped = true; - } - else { - gb->sp_for_call_depth[gb->debug_call_depth] = gb->registers[GB_REGISTER_SP]; - gb->addr_for_call_depth[gb->debug_call_depth] = gb->pc; - } - } - - if (gb->backtrace_size < sizeof(gb->backtrace_sps) / sizeof(gb->backtrace_sps[0])) { - - while (gb->backtrace_size) { - if (gb->backtrace_sps[gb->backtrace_size - 1] < gb->registers[GB_REGISTER_SP]) { - gb->backtrace_size--; - } - else { - break; - } - } - - gb->backtrace_sps[gb->backtrace_size] = gb->registers[GB_REGISTER_SP]; - gb->backtrace_returns[gb->backtrace_size].bank = bank_for_addr(gb, call_addr); - gb->backtrace_returns[gb->backtrace_size].addr = call_addr; - gb->backtrace_size++; - } - - gb->debug_call_depth++; -} - -void GB_debugger_ret_hook(GB_gameboy_t *gb) -{ - /* Called just before the CPU runs ret/reti */ - - gb->debug_call_depth--; - - if (gb->stack_leak_detection) { - if (gb->debug_call_depth < 0) { - GB_log(gb, "Function finished without a stack leak.\n"); - gb->debug_stopped = true; - } - else { - if (gb->registers[GB_REGISTER_SP] != gb->sp_for_call_depth[gb->debug_call_depth]) { - GB_log(gb, "Stack leak detected for function %s!\n", value_to_string(gb, gb->addr_for_call_depth[gb->debug_call_depth], true)); - GB_log(gb, "SP is $%04x, should be $%04x.\n", gb->registers[GB_REGISTER_SP], - gb->sp_for_call_depth[gb->debug_call_depth]); - gb->debug_stopped = true; - } - } - } - - while (gb->backtrace_size) { - if (gb->backtrace_sps[gb->backtrace_size - 1] <= gb->registers[GB_REGISTER_SP]) { - gb->backtrace_size--; - } - else { - break; - } - } -} - -static bool _GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, value_t addr, uint8_t value) -{ - uint16_t index = find_watchpoint(gb, addr); - uint32_t key = WP_KEY(addr); - - if (index < gb->n_watchpoints && gb->watchpoints[index].key == key) { - if (!(gb->watchpoints[index].flags & GB_WATCHPOINT_W)) { - return false; - } - if (!gb->watchpoints[index].condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s] = $%02x\n", debugger_value_to_string(gb, addr, true), value); - return true; - } - bool error; - bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, - (unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, &value).value; - if (error) { - /* Should never happen */ - GB_log(gb, "An internal error has occured\n"); - return false; - } - if (condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s] = $%02x\n", debugger_value_to_string(gb, addr, true), value); - return true; - } - } - return false; -} - -void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value) -{ - if (gb->debug_stopped) return; - - /* Try any-bank breakpoint */ - value_t full_addr = (VALUE_16(addr)); - if (_GB_debugger_test_write_watchpoint(gb, full_addr, value)) return; - - /* Try bank-specific breakpoint */ - full_addr.has_bank = true; - full_addr.bank = bank_for_addr(gb, addr); - _GB_debugger_test_write_watchpoint(gb, full_addr, value); -} - -static bool _GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, value_t addr) -{ - uint16_t index = find_watchpoint(gb, addr); - uint32_t key = WP_KEY(addr); - - if (index < gb->n_watchpoints && gb->watchpoints[index].key == key) { - if (!(gb->watchpoints[index].flags & GB_WATCHPOINT_R)) { - return false; - } - if (!gb->watchpoints[index].condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s]\n", debugger_value_to_string(gb, addr, true)); - return true; - } - bool error; - bool condition = debugger_evaluate(gb, gb->watchpoints[index].condition, - (unsigned int)strlen(gb->watchpoints[index].condition), &error, &addr.value, NULL).value; - if (error) { - /* Should never happen */ - GB_log(gb, "An internal error has occured\n"); - return false; - } - if (condition) { - gb->debug_stopped = true; - GB_log(gb, "Watchpoint: [%s]\n", debugger_value_to_string(gb, addr, true)); - return true; - } - } - return false; -} - -void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr) -{ - if (gb->debug_stopped) return; - - /* Try any-bank breakpoint */ - value_t full_addr = (VALUE_16(addr)); - if (_GB_debugger_test_read_watchpoint(gb, full_addr)) return; - - /* Try bank-specific breakpoint */ - full_addr.has_bank = true; - full_addr.bank = bank_for_addr(gb, addr); - _GB_debugger_test_read_watchpoint(gb, full_addr); -} - -/* Returns true if debugger waits for more commands */ -bool GB_debugger_execute_command(GB_gameboy_t *gb, char *input) -{ - if (!input[0]) { - return true; - } - - char *command_string = input; - char *arguments = strchr(input, ' '); - if (arguments) { - /* Actually "split" the string. */ - arguments[0] = 0; - arguments++; - } - else { - arguments = ""; - } - - char *modifiers = strchr(command_string, '/'); - if (modifiers) { - /* Actually "split" the string. */ - modifiers[0] = 0; - modifiers++; - } - - const debugger_command_t *command = find_command(command_string); - if (command) { - return command->implementation(gb, arguments, modifiers, command); - } - else { - GB_log(gb, "%s: no such command.\n", command_string); - return true; - } -} - -void GB_debugger_run(GB_gameboy_t *gb) -{ - if (gb->debug_disable) return; - - char *input = NULL; - if (gb->debug_next_command && gb->debug_call_depth <= 0) { - gb->debug_stopped = true; - } - if (gb->debug_fin_command && gb->debug_call_depth == -1) { - gb->debug_stopped = true; - } - if (gb->debug_stopped) { - GB_cpu_disassemble(gb, gb->pc, 5); - } -next_command: - if (input) { - free(input); - } - if (gb->breakpoints && !gb->debug_stopped && should_break(gb, gb->pc)) { - gb->debug_stopped = true; - GB_log(gb, "Breakpoint: PC = %s\n", value_to_string(gb, gb->pc, true)); - GB_cpu_disassemble(gb, gb->pc, 5); - } - if (gb->debug_stopped && !gb->debug_disable) { - gb->debug_next_command = false; - gb->debug_fin_command = false; - gb->stack_leak_detection = false; - input = gb->input_callback(gb); - - if (input == NULL) { - /* Debugging is no currently available, continue running */ - gb->debug_stopped = false; - return; - } - - if (GB_debugger_execute_command(gb, input)) { - goto next_command; - } - - free(input); - } -} - -void GB_debugger_handle_async_commands(GB_gameboy_t *gb) -{ - char *input = NULL; - - while (gb->async_input_callback && (input = gb->async_input_callback(gb))) { - GB_debugger_execute_command(gb, input); - free(input); - } -} - -void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path) -{ - FILE *f = fopen(path, "r"); - if (!f) return; - - char *line = NULL; - size_t size = 0; - size_t length = 0; - while ((length = getline(&line, &size, f)) != -1) { - for (unsigned i = 0; i < length; i++) { - if (line[i] == ';' || line[i] == '\n' || line[i] == '\r') { - line[i] = 0; - length = i; - break; - } - } - if (length == 0) continue; - - unsigned int bank, address; - char symbol[length]; - - if (sscanf(line, "%02x:%04x %s", &bank, &address, symbol) == 3) { - bank &= 0x1FF; - if (!gb->bank_symbols[bank]) { - gb->bank_symbols[bank] = GB_map_alloc(); - } - GB_bank_symbol_t *allocated_symbol = GB_map_add_symbol(gb->bank_symbols[bank], address, symbol); - if (allocated_symbol) { - GB_reversed_map_add_symbol(&gb->reversed_symbol_map, bank, allocated_symbol); - } - } - } - free(line); - fclose(f); -} - -const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr) -{ - uint16_t bank = bank_for_addr(gb, addr); - - const GB_bank_symbol_t *symbol = GB_map_find_symbol(gb->bank_symbols[bank], addr); - if (symbol) return symbol; - if (bank != 0) return GB_map_find_symbol(gb->bank_symbols[0], addr); /* Maybe the symbol incorrectly uses bank 0? */ - - return NULL; -} - -const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr) -{ - const GB_bank_symbol_t *symbol = GB_debugger_find_symbol(gb, addr); - if (symbol && symbol->addr == addr) return symbol->name; - return NULL; -} - -/* The public version of debugger_evaluate */ -bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank) -{ - bool error = false; - value_t value = debugger_evaluate(gb, string, strlen(string), &error, NULL, NULL); - if (result) { - *result = value.value; - } - if (result_bank) { - *result_bank = value.has_bank? value.value : -1; - } - return error; -} - -void GB_debugger_break(GB_gameboy_t *gb) -{ - gb->debug_stopped = true; -} - -bool GB_debugger_is_stopped(GB_gameboy_t *gb) -{ - return gb->debug_stopped; -} - -void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled) -{ - gb->debug_disable = disabled; -} diff --git a/waterbox/sameboy/debugger.h b/waterbox/sameboy/debugger.h deleted file mode 100644 index 5d6491972c..0000000000 --- a/waterbox/sameboy/debugger.h +++ /dev/null @@ -1,32 +0,0 @@ -#ifndef debugger_h -#define debugger_h -#include -#include -#include "gb_struct_def.h" -#include "symbol_hash.h" - -#ifdef GB_INTERNAL -void GB_debugger_run(GB_gameboy_t *gb); -void GB_debugger_handle_async_commands(GB_gameboy_t *gb); -void GB_debugger_call_hook(GB_gameboy_t *gb, uint16_t call_addr); -void GB_debugger_ret_hook(GB_gameboy_t *gb); -void GB_debugger_test_write_watchpoint(GB_gameboy_t *gb, uint16_t addr, uint8_t value); -void GB_debugger_test_read_watchpoint(GB_gameboy_t *gb, uint16_t addr); -const GB_bank_symbol_t *GB_debugger_find_symbol(GB_gameboy_t *gb, uint16_t addr); -#endif - -#ifdef GB_INTERNAL -bool /* Returns true if debugger waits for more commands. Not relevant for non-GB_INTERNAL */ -#else -void -#endif -GB_debugger_execute_command(GB_gameboy_t *gb, char *input); /* Destroys input. */ - - -void GB_debugger_load_symbol_file(GB_gameboy_t *gb, const char *path); -const char *GB_debugger_name_for_address(GB_gameboy_t *gb, uint16_t addr); -bool GB_debugger_evaluate(GB_gameboy_t *gb, const char *string, uint16_t *result, uint16_t *result_bank); /* result_bank is -1 if unused. */ -void GB_debugger_break(GB_gameboy_t *gb); -bool GB_debugger_is_stopped(GB_gameboy_t *gb); -void GB_debugger_set_disabled(GB_gameboy_t *gb, bool disabled); -#endif /* debugger_h */ diff --git a/waterbox/sameboy/gb.c b/waterbox/sameboy/gb.c index 56697a1e83..d464734c4e 100644 --- a/waterbox/sameboy/gb.c +++ b/waterbox/sameboy/gb.c @@ -67,7 +67,7 @@ static char *default_input_callback(GB_gameboy_t *gb) static char *default_async_input_callback(GB_gameboy_t *gb) { -#ifndef _WIN32 +#if 0 fd_set set; FD_ZERO(&set); FD_SET(STDIN_FILENO, &set); @@ -260,12 +260,10 @@ exit: void GB_run(GB_gameboy_t *gb) { - GB_debugger_run(gb); GB_cpu_run(gb); if (gb->vblank_just_occured) { GB_update_joyp(gb); GB_rtc_run(gb); - GB_debugger_handle_async_commands(gb); } } diff --git a/waterbox/sameboy/gb.h b/waterbox/sameboy/gb.h index 82608821fd..4d7588b644 100644 --- a/waterbox/sameboy/gb.h +++ b/waterbox/sameboy/gb.h @@ -10,7 +10,6 @@ #include "apu.h" #include "camera.h" -#include "debugger.h" #include "display.h" #include "joypad.h" #include "mbc.h" @@ -179,9 +178,6 @@ typedef struct { long delay; } GB_ir_queue_item_t; -struct GB_breakpoint_s; -struct GB_watchpoint_s; - /* 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. @@ -445,10 +441,6 @@ struct GB_gameboy_internal_s { GB_ir_queue_item_t ir_queue[GB_MAX_IR_QUEUE]; size_t ir_queue_length; - /*** Debugger ***/ - volatile bool debug_stopped, debug_disable; - bool debug_fin_command, debug_next_command; - /* Breakpoints */ uint16_t n_breakpoints; struct GB_breakpoint_s *breakpoints; @@ -467,10 +459,6 @@ struct GB_gameboy_internal_s { uint16_t addr; } backtrace_returns[0x200]; - /* Watchpoints */ - uint16_t n_watchpoints; - struct GB_watchpoint_s *watchpoints; - /* Symbol tables */ GB_symbol_map_t *bank_symbols[0x200]; GB_reversed_symbol_map_t reversed_symbol_map; diff --git a/waterbox/sameboy/memory.c b/waterbox/sameboy/memory.c index c914e55965..f694d210a4 100644 --- a/waterbox/sameboy/memory.c +++ b/waterbox/sameboy/memory.c @@ -268,9 +268,6 @@ static GB_read_function_t * const read_map[] = uint8_t GB_read_memory(GB_gameboy_t *gb, uint16_t addr) { - if (gb->n_watchpoints) { - GB_debugger_test_read_watchpoint(gb, addr); - } if (is_addr_in_dma_use(gb, addr)) { addr = gb->dma_current_src; } @@ -677,9 +674,6 @@ static GB_write_function_t * const write_map[] = void GB_write_memory(GB_gameboy_t *gb, uint16_t addr, uint8_t value) { - if (gb->n_watchpoints) { - GB_debugger_test_write_watchpoint(gb, addr, 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; diff --git a/waterbox/sameboy/timing.c b/waterbox/sameboy/timing.c index 77348fe232..d53c85a014 100644 --- a/waterbox/sameboy/timing.c +++ b/waterbox/sameboy/timing.c @@ -125,8 +125,6 @@ void GB_advance_cycles(GB_gameboy_t *gb, uint8_t cycles) } } - gb->debugger_ticks += cycles; - if (gb->cgb_double_speed) { cycles >>=1; } diff --git a/waterbox/sameboy/z80_cpu.c b/waterbox/sameboy/z80_cpu.c index 0a1bde5dd7..19adc82f47 100644 --- a/waterbox/sameboy/z80_cpu.c +++ b/waterbox/sameboy/z80_cpu.c @@ -701,7 +701,6 @@ static void ret_cc(GB_gameboy_t *gb, uint8_t opcode) { /* Todo: Verify timing */ if (condition_code(gb, opcode)) { - GB_debugger_ret_hook(gb); GB_advance_cycles(gb, 8); gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]); GB_advance_cycles(gb, 4); @@ -768,8 +767,6 @@ static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode) GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_advance_cycles(gb, 4); gb->pc = addr; - - GB_debugger_call_hook(gb, call_addr); } else { GB_advance_cycles(gb, 12); @@ -938,12 +935,10 @@ static void rst(GB_gameboy_t *gb, uint8_t opcode) GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_advance_cycles(gb, 4); gb->pc = opcode ^ 0xC7; - GB_debugger_call_hook(gb, call_addr); } static void ret(GB_gameboy_t *gb, uint8_t opcode) { - GB_debugger_ret_hook(gb); GB_advance_cycles(gb, 4); gb->pc = GB_read_memory(gb, gb->registers[GB_REGISTER_SP]); GB_advance_cycles(gb, 4); @@ -972,7 +967,6 @@ static void call_a16(GB_gameboy_t *gb, uint8_t opcode) GB_write_memory(gb, gb->registers[GB_REGISTER_SP], (gb->pc) & 0xFF); GB_advance_cycles(gb, 4); gb->pc = addr; - GB_debugger_call_hook(gb, call_addr); } static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode) diff --git a/waterbox/sameboy/z80_disassembler.c b/waterbox/sameboy/z80_disassembler.c deleted file mode 100644 index 08fb62f039..0000000000 --- a/waterbox/sameboy/z80_disassembler.c +++ /dev/null @@ -1,788 +0,0 @@ -#include -#include -#include "gb.h" - - -typedef void GB_opcode_t(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc); - -static void ill(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, ".BYTE $%02x\n", opcode); - (*pc)++; -} - -static void nop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "NOP\n"); - (*pc)++; -} - -static void stop(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint8_t next = GB_read_memory(gb, (*pc)++); - if (next) { - GB_log(gb, "CORRUPTED STOP (%02x)\n", next); - } - else { - GB_log(gb, "STOP\n"); - } -} - -static char *register_names[] = {"af", "bc", "de", "hl", "sp"}; - -static void ld_rr_d16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - uint16_t value; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - value = GB_read_memory(gb, (*pc)++); - value |= GB_read_memory(gb, (*pc)++) << 8; - const char *symbol = GB_debugger_name_for_address(gb, value); - if (symbol) { - GB_log(gb, "LD %s, %s ; =$%04x\n", register_names[register_id], symbol, value); - } - else { - GB_log(gb, "LD %s, $%04x\n", register_names[register_id], value); - } -} - -static void ld_drr_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "LD [%s], a\n", register_names[register_id]); -} - -static void inc_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "INC %s\n", register_names[register_id]); -} - -static void inc_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = ((opcode >> 4) + 1) & 0x03; - GB_log(gb, "INC %c\n", register_names[register_id][0]); - -} -static void dec_hr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = ((opcode >> 4) + 1) & 0x03; - GB_log(gb, "DEC %c\n", register_names[register_id][0]); -} - -static void ld_hr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = ((opcode >> 4) + 1) & 0x03; - GB_log(gb, "LD %c, $%02x\n", register_names[register_id][0], GB_read_memory(gb, (*pc)++)); -} - -static void rlca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RLCA\n"); -} - -static void rla(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RLA\n"); -} - -static void ld_da16_sp(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc){ - uint16_t addr; - (*pc)++; - addr = GB_read_memory(gb, (*pc)++); - addr |= GB_read_memory(gb, (*pc)++) << 8; - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "LD [%s], sp ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "LD [$%04x], sp\n", addr); - } -} - -static void add_hl_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - (*pc)++; - register_id = (opcode >> 4) + 1; - GB_log(gb, "ADD hl, %s\n", register_names[register_id]); -} - -static void ld_a_drr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "LD a, [%s]\n", register_names[register_id]); -} - -static void dec_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - GB_log(gb, "DEC %s\n", register_names[register_id]); -} - -static void inc_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - - GB_log(gb, "INC %c\n", register_names[register_id][1]); -} -static void dec_lr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - - GB_log(gb, "DEC %c\n", register_names[register_id][1]); -} - -static void ld_lr_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = (GB_read_memory(gb, (*pc)++) >> 4) + 1; - - GB_log(gb, "LD %c, $%02x\n", register_names[register_id][1], GB_read_memory(gb, (*pc)++)); -} - -static void rrca(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "RRCA\n"); - (*pc)++; -} - -static void rra(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "RRA\n"); - (*pc)++; -} - -static void jr_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1; - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR %s ; =$%04x\n", symbol, addr); - } - else { - GB_attributed_log(gb, GB_LOG_UNDERLINE, "JR $%04x\n", addr); - } - (*pc)++; -} - -static const char *condition_code(uint8_t opcode) -{ - switch ((opcode >> 3) & 0x3) { - case 0: - return "nz"; - case 1: - return "z"; - case 2: - return "nc"; - case 3: - return "c"; - } - - return NULL; -} - -static void jr_cc_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = *pc + (int8_t) GB_read_memory(gb, (*pc)) + 1; - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr); - } - else { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JR %s, $%04x\n", condition_code(opcode), addr); - } - (*pc)++; -} - -static void daa(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "DAA\n"); - (*pc)++; -} - -static void cpl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "CPL\n"); - (*pc)++; -} - -static void scf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "SCF\n"); - (*pc)++; -} - -static void ccf(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "CCF\n"); - (*pc)++; -} - -static void ld_dhli_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD [hli], a\n"); - (*pc)++; -} - -static void ld_dhld_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD [hld], a\n"); - (*pc)++; -} - -static void ld_a_dhli(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD a, [hli]\n"); - (*pc)++; -} - -static void ld_a_dhld(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "LD a, [hld]\n"); - (*pc)++; -} - -static void inc_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "INC [hl]\n"); - (*pc)++; -} - -static void dec_dhl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - GB_log(gb, "DEC [hl]\n"); - (*pc)++; -} - -static void ld_dhl_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LD [hl], $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static const char *get_src_name(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) { - return src_low? "a": "[hl]"; - } - if (src_low) { - return register_names[src_register_id] + 1; - } - static const char *high_register_names[] = {"a", "b", "d", "h"}; - return high_register_names[src_register_id]; -} - -static const char *get_dst_name(uint8_t opcode) -{ - uint8_t dst_register_id; - uint8_t dst_low; - dst_register_id = ((opcode >> 4) + 1) & 3; - dst_low = opcode & 8; - if (dst_register_id == GB_REGISTER_AF) { - return dst_low? "a": "[hl]"; - } - if (dst_low) { - return register_names[dst_register_id] + 1; - } - static const char *high_register_names[] = {"a", "b", "d", "h"}; - return high_register_names[dst_register_id]; -} - -static void ld_r_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LD %s, %s\n", get_dst_name(opcode), get_src_name(opcode)); -} - -static void add_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADD %s\n", get_src_name(opcode)); -} - -static void adc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADC %s\n", get_src_name(opcode)); -} - -static void sub_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SUB %s\n", get_src_name(opcode)); -} - -static void sbc_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SBC %s\n", get_src_name(opcode)); -} - -static void and_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "AND %s\n", get_src_name(opcode)); -} - -static void xor_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "XOR %s\n", get_src_name(opcode)); -} - -static void or_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "OR %s\n", get_src_name(opcode)); -} - -static void cp_a_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "CP %s\n", get_src_name(opcode)); -} - -static void halt(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "HALT\n"); -} - -static void ret_cc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "RET %s\n", condition_code(opcode)); -} - -static void pop_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3; - GB_log(gb, "POP %s\n", register_names[register_id]); -} - -static void jp_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr); - } - else { - GB_attributed_log(gb, GB_LOG_DASHED_UNDERLINE, "JP %s, $%04x\n", condition_code(opcode), addr); - } - (*pc) += 2; -} - -static void jp_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "JP %s ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "JP $%04x\n", addr); - } - (*pc) += 2; -} - -static void call_cc_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "CALL %s, %s ; =$%04x\n", condition_code(opcode), symbol, addr); - } - else { - GB_log(gb, "CALL %s, $%04x\n", condition_code(opcode), addr); - } - (*pc) += 2; -} - -static void push_rr(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t register_id; - register_id = ((GB_read_memory(gb, (*pc)++) >> 4) + 1) & 3; - GB_log(gb, "PUSH %s\n", register_names[register_id]); -} - -static void add_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADD $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void adc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "ADC $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void sub_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SUB $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void sbc_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SBC $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void and_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "AND $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void xor_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "XOR $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void or_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "OR $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void cp_a_d8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "CP $%02x\n", GB_read_memory(gb, (*pc)++)); -} - -static void rst(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RST $%02x\n", opcode ^ 0xC7); - -} - -static void ret(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_attributed_log(gb, GB_LOG_UNDERLINE, "RET\n"); -} - -static void reti(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_attributed_log(gb, GB_LOG_UNDERLINE, "RETI\n"); -} - -static void call_a16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr); - if (symbol) { - GB_log(gb, "CALL %s ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "CALL $%04x\n", addr); - } - (*pc) += 2; -} - -static void ld_da8_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint8_t addr = GB_read_memory(gb, (*pc)++); - const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr); - if (symbol) { - GB_log(gb, "LDH [%s & $FF], a ; =$%02x\n", symbol, addr); - } - else { - GB_log(gb, "LDH [$%02x], a\n", addr); - } -} - -static void ld_a_da8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint8_t addr = GB_read_memory(gb, (*pc)++); - const char *symbol = GB_debugger_name_for_address(gb, 0xff00 + addr); - if (symbol) { - GB_log(gb, "LDH a, [%s & $FF] ; =$%02x\n", symbol, addr); - } - else { - GB_log(gb, "LDH a, [$%02x]\n", addr); - } -} - -static void ld_dc_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LDH [c], a\n"); -} - -static void ld_a_dc(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LDH a, [c]\n"); -} - -static void add_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - int8_t temp = GB_read_memory(gb, (*pc)++); - GB_log(gb, "ADD SP, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp); -} - -static void jp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "JP hl\n"); -} - -static void ld_da16_a(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "LD [%s], a ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "LD [$%04x], a\n", addr); - } - (*pc) += 2; -} - -static void ld_a_da16(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - uint16_t addr = GB_read_memory(gb, *pc) | (GB_read_memory(gb, *pc + 1) << 8); - const char *symbol = GB_debugger_name_for_address(gb, addr); - if (symbol) { - GB_log(gb, "LD a, [%s] ; =$%04x\n", symbol, addr); - } - else { - GB_log(gb, "LD a, [$%04x]\n", addr); - } - (*pc) += 2; -} - -static void di(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "DI\n"); -} - -static void ei(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "EI\n"); -} - -static void ld_hl_sp_r8(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - int8_t temp = GB_read_memory(gb, (*pc)++); - GB_log(gb, "LD hl, sp, %s$%02x\n", temp < 0? "-" : "", temp < 0? -temp : temp); -} - -static void ld_sp_hl(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "LD sp, hl\n"); -} - -static void rlc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RLC %s\n", get_src_name(opcode)); -} - -static void rrc_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RRC %s\n", get_src_name(opcode)); -} - -static void rl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RL %s\n", get_src_name(opcode)); -} - -static void rr_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RR %s\n", get_src_name(opcode)); -} - -static void sla_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SLA %s\n", get_src_name(opcode)); -} - -static void sra_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SRA %s\n", get_src_name(opcode)); -} - -static void srl_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "SRL %s\n", get_src_name(opcode)); -} - -static void swap_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - (*pc)++; - GB_log(gb, "RLC %s\n", get_src_name(opcode)); -} - -static void bit_r(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - uint8_t bit; - (*pc)++; - bit = ((opcode >> 3) & 7); - if ((opcode & 0xC0) == 0x40) { /* Bit */ - GB_log(gb, "BIT %s, %d\n", get_src_name(opcode), bit); - } - else if ((opcode & 0xC0) == 0x80) { /* res */ - GB_log(gb, "RES %s, %d\n", get_src_name(opcode), bit); - } - else if ((opcode & 0xC0) == 0xC0) { /* set */ - GB_log(gb, "SET %s, %d\n", get_src_name(opcode), bit); - } -} - -static void cb_prefix(GB_gameboy_t *gb, uint8_t opcode, uint16_t *pc) -{ - opcode = GB_read_memory(gb, ++*pc); - switch (opcode >> 3) { - case 0: - rlc_r(gb, opcode, pc); - break; - case 1: - rrc_r(gb, opcode, pc); - break; - case 2: - rl_r(gb, opcode, pc); - break; - case 3: - rr_r(gb, opcode, pc); - break; - case 4: - sla_r(gb, opcode, pc); - break; - case 5: - sra_r(gb, opcode, pc); - break; - case 6: - swap_r(gb, opcode, pc); - break; - case 7: - srl_r(gb, opcode, pc); - break; - default: - bit_r(gb, opcode, pc); - 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, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 4X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 5X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, /* 6X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, halt, ld_r_r, /* 7X */ - ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, ld_r_r, - 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_disassemble(GB_gameboy_t *gb, uint16_t pc, uint16_t count) -{ - const GB_bank_symbol_t *function_symbol = GB_debugger_find_symbol(gb, pc); - - if (function_symbol && pc - function_symbol->addr > 0x1000) { - function_symbol = NULL; - } - - if (function_symbol && pc != function_symbol->addr) { - GB_log(gb, "%s:\n", function_symbol->name); - } - - uint16_t current_function = function_symbol? function_symbol->addr : 0; - - while (count--) { - function_symbol = GB_debugger_find_symbol(gb, pc); - if (function_symbol && function_symbol->addr == pc) { - if (current_function != function_symbol->addr) { - GB_log(gb, "\n"); - } - GB_log(gb, "%s:\n", function_symbol->name); - } - if (function_symbol) { - GB_log(gb, "%s%04x <+%03x>: ", pc == gb->pc? " ->": " ", pc, pc - function_symbol->addr); - } - else { - GB_log(gb, "%s%04x: ", pc == gb->pc? " ->": " ", pc); - } - uint8_t opcode = GB_read_memory(gb, pc); - opcodes[opcode](gb, opcode, &pc); - } -}