From 82afd511fc022f7475b8948af9411d37f92b2a16 Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 12 Feb 2012 16:35:40 +1100 Subject: [PATCH] Update to v085r08 release. byuu says: Changelog: - follow the Laevateinn topic to get most of it - also added NMI, IRQ step buttons to CPU debugger - also added trace masking + trace mask reset - also added memory export - cartridge loading is entirely folder-based now FitzRoy, I'll go ahead and make a second compromise with you for v086: I'll match the following: /path/to/SNES.sfc/*.sfc /path/to/NES.fc/*.prg, *.chr (split format) /path/to/NES.fc/*.fc (merged format) /path/to/GB.gb/*.gb /path/to/GBC.gbc/*.gbc Condition will be that there can only be one of each file. If there's more than one, it'll abort. That lets me name my ROMs as "Game.fc/Game.fc", and you can name yours as "Game.fc/cartridge.prg, cartridge.chr". Or whatever you want. We'll just go with that, see what fares out as the most popular, and then restrict it back to that method. The folder must have the .fc, etc extension though. That will be how we avoid false-positive folder matches. [Editor's note - the Laevateinn topic mentions these changes for v085r08: Added SMP/PPU breakpoints, SMP debugger, SMP stepping / tracing, memory editing on APU-bus / VRAM / OAM / CGRAM, save state menu, WRAM mirroring on breakpoints, protected MMIO memory regions (otherwise, viewing $002100 could crash your game.) Major missing components: - trace mask - trace mask clear / usage map clear - window geometry caching / sizing improvements - VRAM viewer - properties viewer - working memory export button The rest will most likely appear after v086 is released. ] --- bsnes/base/base.hpp | 134 ++++++++--------- bsnes/nall/varint.hpp | 67 +++++++++ bsnes/phoenix/core/state.hpp | 2 +- bsnes/phoenix/gtk/platform.hpp | 1 + bsnes/phoenix/gtk/settings.cpp | 1 + bsnes/phoenix/gtk/widget/text-edit.cpp | 3 + bsnes/phoenix/gtk/window.cpp | 15 +- bsnes/phoenix/qt/platform.moc | 2 +- bsnes/phoenix/qt/widget/text-edit.cpp | 2 + bsnes/phoenix/windows/widget/text-edit.cpp | 4 +- bsnes/snes/cpu/cpu.cpp | 2 + bsnes/snes/cpu/cpu.hpp | 2 + bsnes/snes/debugger/debugger.cpp | 108 -------------- bsnes/snes/debugger/debugger.hpp | 28 ---- bsnes/snes/ppu/background/background.hpp | 2 +- bsnes/snes/ppu/mmio/mmio.cpp | 41 ++++-- bsnes/snes/ppu/mmio/mmio.hpp | 7 +- bsnes/snes/ppu/screen/screen.hpp | 2 +- bsnes/snes/ppu/sprite/list.cpp | 2 - bsnes/snes/ppu/sprite/sprite.hpp | 2 +- bsnes/snes/ppu/window/window.hpp | 2 +- bsnes/snes/smp/memory/memory.cpp | 4 +- bsnes/snes/system/system.cpp | 1 - bsnes/snes/system/system.hpp | 1 - bsnes/ui-debugger/Makefile | 6 +- bsnes/ui-debugger/base.hpp | 2 + bsnes/ui-debugger/breakpoint/breakpoint.cpp | 144 ++++++++++++++++++- bsnes/ui-debugger/breakpoint/breakpoint.hpp | 22 +++ bsnes/ui-debugger/console/about.cpp | 12 +- bsnes/ui-debugger/console/console.cpp | 52 +++++-- bsnes/ui-debugger/console/console.hpp | 18 ++- bsnes/ui-debugger/cpu/cpu.cpp | 53 +++++-- bsnes/ui-debugger/cpu/cpu.hpp | 13 +- bsnes/ui-debugger/debugger/debugger.cpp | 42 +++--- bsnes/ui-debugger/debugger/debugger.hpp | 18 ++- bsnes/ui-debugger/debugger/hook.cpp | 151 +++++++++++++++++++- bsnes/ui-debugger/interface/interface.cpp | 24 +++- bsnes/ui-debugger/interface/interface.hpp | 2 + bsnes/ui-debugger/main.cpp | 17 ++- bsnes/ui-debugger/memory/memory.cpp | 70 +++++++-- bsnes/ui-debugger/memory/memory.hpp | 8 +- bsnes/ui-debugger/resource.rc | 2 + bsnes/ui-debugger/smp/smp.cpp | 99 +++++++++++++ bsnes/ui-debugger/smp/smp.hpp | 21 +++ bsnes/ui-debugger/tracer/tracer.cpp | 55 +++++++ bsnes/ui-debugger/tracer/tracer.hpp | 22 +++ bsnes/ui-debugger/video/video.cpp | 2 +- 47 files changed, 974 insertions(+), 316 deletions(-) delete mode 100755 bsnes/snes/debugger/debugger.cpp delete mode 100755 bsnes/snes/debugger/debugger.hpp create mode 100755 bsnes/ui-debugger/resource.rc create mode 100755 bsnes/ui-debugger/smp/smp.cpp create mode 100755 bsnes/ui-debugger/smp/smp.hpp create mode 100755 bsnes/ui-debugger/tracer/tracer.cpp create mode 100755 bsnes/ui-debugger/tracer/tracer.hpp diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index f6be8cdcb..7fd709998 100755 --- a/bsnes/base/base.hpp +++ b/bsnes/base/base.hpp @@ -1,7 +1,7 @@ #ifndef BASE_HPP #define BASE_HPP -const char Version[] = "085.07"; +const char Version[] = "085.08"; #include #include @@ -55,73 +55,73 @@ template struct hook { #define privileged private #endif -typedef int_t< 1> int1; -typedef int_t< 2> int2; -typedef int_t< 3> int3; -typedef int_t< 4> int4; -typedef int_t< 5> int5; -typedef int_t< 6> int6; -typedef int_t< 7> int7; -typedef int8_t int8; -typedef int_t< 9> int9; -typedef int_t<10> int10; -typedef int_t<11> int11; -typedef int_t<12> int12; -typedef int_t<13> int13; -typedef int_t<14> int14; -typedef int_t<15> int15; -typedef int16_t int16; -typedef int_t<17> int17; -typedef int_t<18> int18; -typedef int_t<19> int19; -typedef int_t<20> int20; -typedef int_t<21> int21; -typedef int_t<22> int22; -typedef int_t<23> int23; -typedef int_t<24> int24; -typedef int_t<25> int25; -typedef int_t<26> int26; -typedef int_t<27> int27; -typedef int_t<28> int28; -typedef int_t<29> int29; -typedef int_t<30> int30; -typedef int_t<31> int31; -typedef int32_t int32; -typedef int64_t int64; +typedef int1_t int1; +typedef int2_t int2; +typedef int3_t int3; +typedef int4_t int4; +typedef int5_t int5; +typedef int6_t int6; +typedef int7_t int7; +typedef int8_t int8; +typedef int9_t int9; +typedef int10_t int10; +typedef int11_t int11; +typedef int12_t int12; +typedef int13_t int13; +typedef int14_t int14; +typedef int15_t int15; +typedef int16_t int16; +typedef int17_t int17; +typedef int18_t int18; +typedef int19_t int19; +typedef int20_t int20; +typedef int21_t int21; +typedef int22_t int22; +typedef int23_t int23; +typedef int24_t int24; +typedef int25_t int25; +typedef int26_t int26; +typedef int27_t int27; +typedef int28_t int28; +typedef int29_t int29; +typedef int30_t int30; +typedef int31_t int31; +typedef int32_t int32; +typedef int64_t int64; -typedef uint_t< 1> uint1; -typedef uint_t< 2> uint2; -typedef uint_t< 3> uint3; -typedef uint_t< 4> uint4; -typedef uint_t< 5> uint5; -typedef uint_t< 6> uint6; -typedef uint_t< 7> uint7; -typedef uint8_t uint8; -typedef uint_t< 9> uint9; -typedef uint_t<10> uint10; -typedef uint_t<11> uint11; -typedef uint_t<12> uint12; -typedef uint_t<13> uint13; -typedef uint_t<14> uint14; -typedef uint_t<15> uint15; -typedef uint16_t uint16; -typedef uint_t<17> uint17; -typedef uint_t<18> uint18; -typedef uint_t<19> uint19; -typedef uint_t<20> uint20; -typedef uint_t<21> uint21; -typedef uint_t<22> uint22; -typedef uint_t<23> uint23; -typedef uint_t<24> uint24; -typedef uint_t<25> uint25; -typedef uint_t<26> uint26; -typedef uint_t<27> uint27; -typedef uint_t<28> uint28; -typedef uint_t<29> uint29; -typedef uint_t<30> uint30; -typedef uint_t<31> uint31; -typedef uint32_t uint32; -typedef uint64_t uint64; +typedef uint1_t uint1; +typedef uint2_t uint2; +typedef uint3_t uint3; +typedef uint4_t uint4; +typedef uint5_t uint5; +typedef uint6_t uint6; +typedef uint7_t uint7; +typedef uint8_t uint8; +typedef uint9_t uint9; +typedef uint10_t uint10; +typedef uint11_t uint11; +typedef uint12_t uint12; +typedef uint13_t uint13; +typedef uint14_t uint14; +typedef uint15_t uint15; +typedef uint16_t uint16; +typedef uint17_t uint17; +typedef uint18_t uint18; +typedef uint19_t uint19; +typedef uint20_t uint20; +typedef uint21_t uint21; +typedef uint22_t uint22; +typedef uint23_t uint23; +typedef uint24_t uint24; +typedef uint25_t uint25; +typedef uint26_t uint26; +typedef uint27_t uint27; +typedef uint28_t uint28; +typedef uint29_t uint29; +typedef uint30_t uint30; +typedef uint31_t uint31; +typedef uint32_t uint32; +typedef uint64_t uint64; typedef varuint_t varuint; diff --git a/bsnes/nall/varint.hpp b/bsnes/nall/varint.hpp index 24e4de7c4..be954c625 100755 --- a/bsnes/nall/varint.hpp +++ b/bsnes/nall/varint.hpp @@ -116,4 +116,71 @@ namespace nall { }; } +//typedefs + typedef nall::uint_t< 1> uint1_t; + typedef nall::uint_t< 2> uint2_t; + typedef nall::uint_t< 3> uint3_t; + typedef nall::uint_t< 4> uint4_t; + typedef nall::uint_t< 5> uint5_t; + typedef nall::uint_t< 6> uint6_t; + typedef nall::uint_t< 7> uint7_t; +//typedef nall::uint_t< 8> uint8_t; + typedef nall::uint_t< 9> uint9_t; + typedef nall::uint_t<10> uint10_t; + typedef nall::uint_t<11> uint11_t; + typedef nall::uint_t<12> uint12_t; + typedef nall::uint_t<13> uint13_t; + typedef nall::uint_t<14> uint14_t; + typedef nall::uint_t<15> uint15_t; +//typedef nall::uint_t<16> uint16_t; + typedef nall::uint_t<17> uint17_t; + typedef nall::uint_t<18> uint18_t; + typedef nall::uint_t<19> uint19_t; + typedef nall::uint_t<20> uint20_t; + typedef nall::uint_t<21> uint21_t; + typedef nall::uint_t<22> uint22_t; + typedef nall::uint_t<23> uint23_t; + typedef nall::uint_t<24> uint24_t; + typedef nall::uint_t<25> uint25_t; + typedef nall::uint_t<26> uint26_t; + typedef nall::uint_t<27> uint27_t; + typedef nall::uint_t<28> uint28_t; + typedef nall::uint_t<29> uint29_t; + typedef nall::uint_t<30> uint30_t; + typedef nall::uint_t<31> uint31_t; +//typedef nall::uint_t<32> uint32_t; + + typedef nall::int_t< 1> int1_t; + typedef nall::int_t< 2> int2_t; + typedef nall::int_t< 3> int3_t; + typedef nall::int_t< 4> int4_t; + typedef nall::int_t< 5> int5_t; + typedef nall::int_t< 6> int6_t; + typedef nall::int_t< 7> int7_t; +//typedef nall::int_t< 8> int8_t; + typedef nall::int_t< 9> int9_t; + typedef nall::int_t<10> int10_t; + typedef nall::int_t<11> int11_t; + typedef nall::int_t<12> int12_t; + typedef nall::int_t<13> int13_t; + typedef nall::int_t<14> int14_t; + typedef nall::int_t<15> int15_t; +//typedef nall::int_t<16> int16_t; + typedef nall::int_t<17> int17_t; + typedef nall::int_t<18> int18_t; + typedef nall::int_t<19> int19_t; + typedef nall::int_t<20> int20_t; + typedef nall::int_t<21> int21_t; + typedef nall::int_t<22> int22_t; + typedef nall::int_t<23> int23_t; + typedef nall::int_t<24> int24_t; + typedef nall::int_t<25> int25_t; + typedef nall::int_t<26> int26_t; + typedef nall::int_t<27> int27_t; + typedef nall::int_t<28> int28_t; + typedef nall::int_t<29> int29_t; + typedef nall::int_t<30> int30_t; + typedef nall::int_t<31> int31_t; +//typedef nall::int_t<32> int32_t; + #endif diff --git a/bsnes/phoenix/core/state.hpp b/bsnes/phoenix/core/state.hpp index 1a4de4c04..e64881c9b 100755 --- a/bsnes/phoenix/core/state.hpp +++ b/bsnes/phoenix/core/state.hpp @@ -250,7 +250,7 @@ struct TextEdit::State { State() { cursorPosition = 0; editable = true; - wordWrap = false; + wordWrap = true; } }; diff --git a/bsnes/phoenix/gtk/platform.hpp b/bsnes/phoenix/gtk/platform.hpp index 2c82394bc..037dd5a95 100755 --- a/bsnes/phoenix/gtk/platform.hpp +++ b/bsnes/phoenix/gtk/platform.hpp @@ -7,6 +7,7 @@ struct Settings : public configuration { unsigned frameGeometryHeight; unsigned menuGeometryHeight; unsigned statusGeometryHeight; + unsigned windowBackgroundColor; void load(); void save(); diff --git a/bsnes/phoenix/gtk/settings.cpp b/bsnes/phoenix/gtk/settings.cpp index aedc20bed..aeb28bbac 100755 --- a/bsnes/phoenix/gtk/settings.cpp +++ b/bsnes/phoenix/gtk/settings.cpp @@ -21,4 +21,5 @@ Settings::Settings() { append(frameGeometryHeight = 28, "frameGeometryHeight"); append(menuGeometryHeight = 20, "menuGeometryHeight"); append(statusGeometryHeight = 20, "statusGeometryHeight"); + append(windowBackgroundColor = 0xedeceb, "windowBackgroundColor"); } diff --git a/bsnes/phoenix/gtk/widget/text-edit.cpp b/bsnes/phoenix/gtk/widget/text-edit.cpp index a1803bbf1..08812fb3f 100755 --- a/bsnes/phoenix/gtk/widget/text-edit.cpp +++ b/bsnes/phoenix/gtk/widget/text-edit.cpp @@ -24,6 +24,9 @@ void pTextEdit::setText(const string &text) { void pTextEdit::setWordWrap(bool wordWrap) { gtk_text_view_set_wrap_mode(GTK_TEXT_VIEW(subWidget), wordWrap ? GTK_WRAP_WORD_CHAR : GTK_WRAP_NONE); + gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(gtkWidget), + wordWrap ? GTK_POLICY_NEVER : GTK_POLICY_ALWAYS, + GTK_POLICY_ALWAYS); } string pTextEdit::text() { diff --git a/bsnes/phoenix/gtk/window.cpp b/bsnes/phoenix/gtk/window.cpp index b3e397d7f..8248104a4 100755 --- a/bsnes/phoenix/gtk/window.cpp +++ b/bsnes/phoenix/gtk/window.cpp @@ -43,6 +43,13 @@ static gboolean Window_configure(GtkWidget *widget, GdkEvent *event, Window *win settings->frameGeometryY = client.y - border.y; settings->frameGeometryWidth = border.width - client.width; settings->frameGeometryHeight = border.height - client.height; + if(window->state.backgroundColorOverride == false) { + GdkColor color = widget->style->bg[GTK_STATE_NORMAL]; + settings->windowBackgroundColor + = ((uint8_t)(color.red >> 8) << 16) + + ((uint8_t)(color.green >> 8) << 8) + + ((uint8_t)(color.blue >> 8) << 0); + } settings->save(); } @@ -115,8 +122,12 @@ void pWindow::append(Widget &widget) { Color pWindow::backgroundColor() { if(window.state.backgroundColorOverride) return window.state.backgroundColor; - GdkColor color = widget->style->bg[GTK_STATE_NORMAL]; - return { (uint8_t)(color.red >> 8), (uint8_t)(color.green >> 8), (uint8_t)(color.blue >> 8), 255 }; + return { + (uint8_t)(settings->windowBackgroundColor >> 16), + (uint8_t)(settings->windowBackgroundColor >> 8), + (uint8_t)(settings->windowBackgroundColor >> 0), + 255 + }; } Geometry pWindow::frameMargin() { diff --git a/bsnes/phoenix/qt/platform.moc b/bsnes/phoenix/qt/platform.moc index 670b9920f..bd9cf54bd 100755 --- a/bsnes/phoenix/qt/platform.moc +++ b/bsnes/phoenix/qt/platform.moc @@ -1,7 +1,7 @@ /**************************************************************************** ** Meta object code from reading C++ file 'platform.moc.hpp' ** -** Created: Thu Feb 2 17:56:05 2012 +** Created: Fri Feb 10 22:23:15 2012 ** by: The Qt Meta Object Compiler version 62 (Qt 4.6.3) ** ** WARNING! All changes made in this file will be lost! diff --git a/bsnes/phoenix/qt/widget/text-edit.cpp b/bsnes/phoenix/qt/widget/text-edit.cpp index ed168866d..8cdbe5738 100755 --- a/bsnes/phoenix/qt/widget/text-edit.cpp +++ b/bsnes/phoenix/qt/widget/text-edit.cpp @@ -15,6 +15,8 @@ void pTextEdit::setText(const string &text) { void pTextEdit::setWordWrap(bool wordWrap) { qtTextEdit->setWordWrapMode(wordWrap ? QTextOption::WordWrap : QTextOption::NoWrap); + qtTextEdit->setHorizontalScrollBarPolicy(wordWrap ? Qt::ScrollBarAlwaysOff : Qt::ScrollBarAlwaysOn); + qtTextEdit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOn); } string pTextEdit::text() { diff --git a/bsnes/phoenix/windows/widget/text-edit.cpp b/bsnes/phoenix/windows/widget/text-edit.cpp index c341156c8..8e1df517f 100755 --- a/bsnes/phoenix/windows/widget/text-edit.cpp +++ b/bsnes/phoenix/windows/widget/text-edit.cpp @@ -1,5 +1,7 @@ void pTextEdit::setCursorPosition(unsigned position) { + if(position == ~0) position >>= 1; //Edit_SetSel takes signed type Edit_SetSel(hwnd, position, position); + Edit_ScrollCaret(hwnd); } void pTextEdit::setEditable(bool editable) { @@ -34,7 +36,7 @@ string pTextEdit::text() { void pTextEdit::constructor() { hwnd = CreateWindowEx( WS_EX_CLIENTEDGE, L"EDIT", L"", - WS_CHILD | WS_TABSTOP | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | (textEdit.state.wordWrap == false ? ES_AUTOHSCROLL : 0), + WS_CHILD | WS_TABSTOP | WS_VSCROLL | ES_AUTOVSCROLL | ES_MULTILINE | ES_WANTRETURN | (textEdit.state.wordWrap == false ? WS_HSCROLL | ES_AUTOHSCROLL : 0), 0, 0, 0, 0, parentWindow->p.hwnd, (HMENU)id, GetModuleHandle(0), 0 ); SetWindowLongPtr(hwnd, GWLP_USERDATA, (LONG_PTR)&textEdit); diff --git a/bsnes/snes/cpu/cpu.cpp b/bsnes/snes/cpu/cpu.cpp index 26f395589..05e066d48 100755 --- a/bsnes/snes/cpu/cpu.cpp +++ b/bsnes/snes/cpu/cpu.cpp @@ -66,10 +66,12 @@ void CPU::enter() { status.nmi_pending = false; regs.vector = (regs.e == false ? 0xffea : 0xfffa); op_irq(); + debugger.op_nmi(); } else if(status.irq_pending) { status.irq_pending = false; regs.vector = (regs.e == false ? 0xffee : 0xfffe); op_irq(); + debugger.op_irq(); } else if(status.reset_pending) { status.reset_pending = false; add_clocks(186); diff --git a/bsnes/snes/cpu/cpu.hpp b/bsnes/snes/cpu/cpu.hpp index 3cd9a8f4a..5eb963e30 100755 --- a/bsnes/snes/cpu/cpu.hpp +++ b/bsnes/snes/cpu/cpu.hpp @@ -138,6 +138,8 @@ privileged: hook op_exec; hook op_read; hook op_write; + hook op_nmi; + hook op_irq; } debugger; }; diff --git a/bsnes/snes/debugger/debugger.cpp b/bsnes/snes/debugger/debugger.cpp deleted file mode 100755 index b1312339e..000000000 --- a/bsnes/snes/debugger/debugger.cpp +++ /dev/null @@ -1,108 +0,0 @@ -#ifdef SYSTEM_CPP - -Debugger debugger; - -void Debugger::breakpoint_test(Debugger::Breakpoint::Source source, Debugger::Breakpoint::Mode mode, unsigned addr, uint8 data) { - for(unsigned i = 0; i < Breakpoints; i++) { - if(breakpoint[i].enabled == false) continue; - - bool source_wram = ((breakpoint[i].addr & 0x40e000) == 0x000000) || ((breakpoint[i].addr & 0xffe000) == 0x7e0000); - bool offset_wram = ((addr & 0x40e000) == 0x000000) || ((addr & 0xffe000) == 0x7e0000); - - if(source == Debugger::Breakpoint::Source::CPUBus && source_wram && offset_wram) { - //shadow S-CPU WRAM addresses ($00-3f|80-bf:0000-1fff mirrors $7e:0000-1fff) - if((breakpoint[i].addr & 0x1fff) != (addr & 0x1fff)) continue; - } else { - if(breakpoint[i].addr != addr) continue; - } - - if(breakpoint[i].data != -1 && breakpoint[i].data != data) continue; - if(breakpoint[i].source != source) continue; - if(breakpoint[i].mode != mode) continue; - - breakpoint[i].counter++; - breakpoint_hit = i; - break_event = BreakEvent::BreakpointHit; - scheduler.exit(Scheduler::ExitReason::DebuggerEvent); - break; - } -} - -uint8 Debugger::read(Debugger::MemorySource source, unsigned addr) { - switch(source) { - case MemorySource::CPUBus: { - //do not read from memory-mapped registers that could affect program behavior - if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO - return bus.read(addr & 0xffffff); - } break; - - case MemorySource::APUBus: { - if((addr & 0xffc0) == 0xffc0) return smp.iplrom[addr & 0x3f]; - return smp.apuram[addr & 0xffff]; - } break; - - case MemorySource::APURAM: { - return smp.apuram[addr & 0xffff]; - } break; - - case MemorySource::VRAM: { - return ppu.vram[addr & 0xffff]; - } break; - - case MemorySource::OAM: { - if(addr & 0x0200) return ppu.oam[0x0200 + (addr & 0x1f)]; - return ppu.oam[addr & 0x01ff]; - } break; - - case MemorySource::CGRAM: { - return ppu.cgram[addr & 0x01ff]; - } break; - } - - return 0x00; -} - -void Debugger::write(Debugger::MemorySource source, unsigned addr, uint8 data) { - switch(source) { - case MemorySource::CPUBus: { - //do not write to memory-mapped registers that could affect program behavior - if(((addr - 0x2000) & 0x40c000) == 0x000000) break; //$00-3f:2000-5fff MMIO - cartridge.rom.write_protect(false); - bus.write(addr & 0xffffff, data); - cartridge.rom.write_protect(true); - } break; - - case MemorySource::APURAM: { - smp.apuram[addr & 0xffff] = data; - } break; - - case MemorySource::VRAM: { - ppu.vram[addr & 0xffff] = data; - } break; - - case MemorySource::OAM: { - if(addr & 0x0200) ppu.oam[0x0200 + (addr & 0x1f)] = data; - else ppu.oam[addr & 0x01ff] = data; - } break; - - case MemorySource::CGRAM: { - ppu.cgram[addr & 0x01ff] = data; - } break; - } -} - -Debugger::Debugger() { - break_event = BreakEvent::None; - - for(unsigned n = 0; n < Breakpoints; n++) { - breakpoint[n].enabled = false; - breakpoint[n].addr = 0; - breakpoint[n].data = -1; - breakpoint[n].mode = Breakpoint::Mode::Exec; - breakpoint[n].source = Breakpoint::Source::CPUBus; - breakpoint[n].counter = 0; - } - breakpoint_hit = 0; -} - -#endif diff --git a/bsnes/snes/debugger/debugger.hpp b/bsnes/snes/debugger/debugger.hpp deleted file mode 100755 index 8a4c1ba29..000000000 --- a/bsnes/snes/debugger/debugger.hpp +++ /dev/null @@ -1,28 +0,0 @@ -struct Debugger { - enum class BreakEvent : unsigned { - None, - BreakpointHit, - CPUStep, - SMPStep, - } break_event; - - enum { Breakpoints = 8 }; - struct Breakpoint { - bool enabled; - unsigned addr; - signed data; //-1 = unused - enum class Mode : unsigned { Exec, Read, Write } mode; - enum class Source : unsigned { CPUBus, APURAM, VRAM, OAM, CGRAM } source; - unsigned counter; //number of times breakpoint has been hit since being set - } breakpoint[Breakpoints]; - unsigned breakpoint_hit; - void breakpoint_test(Breakpoint::Source source, Breakpoint::Mode mode, unsigned addr, uint8 data); - - enum class MemorySource : unsigned { CPUBus, APUBus, APURAM, VRAM, OAM, CGRAM }; - uint8 read(MemorySource, unsigned addr); - void write(MemorySource, unsigned addr, uint8 data); - - Debugger(); -}; - -extern Debugger debugger; diff --git a/bsnes/snes/ppu/background/background.hpp b/bsnes/snes/ppu/background/background.hpp index 26553af4b..c279a8063 100755 --- a/bsnes/snes/ppu/background/background.hpp +++ b/bsnes/snes/ppu/background/background.hpp @@ -1,4 +1,4 @@ -class Background { +struct Background { struct ID { enum { BG1, BG2, BG3, BG4 }; }; unsigned id; diff --git a/bsnes/snes/ppu/mmio/mmio.cpp b/bsnes/snes/ppu/mmio/mmio.cpp index 5995c57a6..39b25c204 100755 --- a/bsnes/snes/ppu/mmio/mmio.cpp +++ b/bsnes/snes/ppu/mmio/mmio.cpp @@ -47,6 +47,31 @@ void PPU::vram_write(unsigned addr, uint8 data) { } } +uint8 PPU::oam_read(unsigned addr) { + debugger.oam_read(addr); + + return oam[addr]; +} + +void PPU::oam_write(unsigned addr, uint8 data) { + debugger.oam_write(addr, data); + + oam[addr] = data; + sprite.update(addr, data); +} + +uint8 PPU::cgram_read(unsigned addr) { + debugger.cgram_read(addr); + + return cgram[addr]; +} + +void PPU::cgram_write(unsigned addr, uint8 data) { + debugger.cgram_write(addr, data); + + cgram[addr] = data; +} + void PPU::mmio_update_video_mode() { switch(regs.bgmode) { case 0: { @@ -181,10 +206,10 @@ void PPU::mmio_w2104(uint8 data) { if(latch == 0) regs.oam_latchdata = data; if(addr & 0x0200) { - sprite.update(addr, data); + oam_write(addr, data); } else if(latch == 1) { - sprite.update((addr & ~1) + 0, regs.oam_latchdata); - sprite.update((addr & ~1) + 1, data); + oam_write((addr & ~1) + 0, regs.oam_latchdata); + oam_write((addr & ~1) + 1, data); } sprite.set_first_sprite(); } @@ -403,8 +428,8 @@ void PPU::mmio_w2122(uint8 data) { if(latch == 0) { regs.cgram_latchdata = data; } else { - cgram[(addr & ~1) + 0] = regs.cgram_latchdata; - cgram[(addr & ~1) + 1] = data & 0x7f; + cgram_write((addr & ~1) + 0, regs.cgram_latchdata); + cgram_write((addr & ~1) + 1, data & 0x7f); } } @@ -584,7 +609,7 @@ uint8 PPU::mmio_r2138() { if(regs.display_disable == false && vcounter() < (!regs.overscan ? 225 : 240)) addr = regs.oam_iaddr; if(addr & 0x0200) addr &= 0x021f; - regs.ppu1_mdr = oam[addr]; + regs.ppu1_mdr = oam_read(addr); sprite.set_first_sprite(); return regs.ppu1_mdr; } @@ -625,10 +650,10 @@ uint8 PPU::mmio_r213b() { ) addr = regs.cgram_iaddr; if(latch == 0) { - regs.ppu2_mdr = cgram[addr]; + regs.ppu2_mdr = cgram_read(addr); } else { regs.ppu2_mdr &= 0x80; - regs.ppu2_mdr |= cgram[addr]; + regs.ppu2_mdr |= cgram_read(addr); } return regs.ppu2_mdr; } diff --git a/bsnes/snes/ppu/mmio/mmio.hpp b/bsnes/snes/ppu/mmio/mmio.hpp index 0c1056245..7a30c3ebe 100755 --- a/bsnes/snes/ppu/mmio/mmio.hpp +++ b/bsnes/snes/ppu/mmio/mmio.hpp @@ -1,8 +1,7 @@ public: uint8 mmio_read(unsigned addr); void mmio_write(unsigned addr, uint8 data); - -private: +privileged: struct { uint8 ppu1_mdr; @@ -91,6 +90,10 @@ struct { uint16 get_vram_address(); uint8 vram_read(unsigned addr); void vram_write(unsigned addr, uint8 data); +uint8 oam_read(unsigned addr); +void oam_write(unsigned addr, uint8 data); +uint8 cgram_read(unsigned addr); +void cgram_write(unsigned addr, uint8 data); void mmio_update_video_mode(); diff --git a/bsnes/snes/ppu/screen/screen.hpp b/bsnes/snes/ppu/screen/screen.hpp index 15502335a..6c6748410 100755 --- a/bsnes/snes/ppu/screen/screen.hpp +++ b/bsnes/snes/ppu/screen/screen.hpp @@ -1,4 +1,4 @@ -class Screen { +struct Screen { uint32 *output; struct Regs { diff --git a/bsnes/snes/ppu/sprite/list.cpp b/bsnes/snes/ppu/sprite/list.cpp index dda358837..ae684a7d3 100755 --- a/bsnes/snes/ppu/sprite/list.cpp +++ b/bsnes/snes/ppu/sprite/list.cpp @@ -1,8 +1,6 @@ #ifdef PPU_CPP void PPU::Sprite::update(unsigned addr, uint8 data) { - ppu.oam[addr] = data; - if(addr < 0x0200) { unsigned n = addr >> 2; addr &= 3; diff --git a/bsnes/snes/ppu/sprite/sprite.hpp b/bsnes/snes/ppu/sprite/sprite.hpp index 641ac13ca..7735f3582 100755 --- a/bsnes/snes/ppu/sprite/sprite.hpp +++ b/bsnes/snes/ppu/sprite/sprite.hpp @@ -1,4 +1,4 @@ -class Sprite { +struct Sprite { struct SpriteItem { uint16 x; uint16 y; diff --git a/bsnes/snes/ppu/window/window.hpp b/bsnes/snes/ppu/window/window.hpp index a4bad10ee..e1fcd4a4f 100755 --- a/bsnes/snes/ppu/window/window.hpp +++ b/bsnes/snes/ppu/window/window.hpp @@ -1,4 +1,4 @@ -class Window { +struct Window { struct { bool bg1_one_enable; bool bg1_one_invert; diff --git a/bsnes/snes/smp/memory/memory.cpp b/bsnes/snes/smp/memory/memory.cpp index b03f0be89..ecbab1beb 100755 --- a/bsnes/snes/smp/memory/memory.cpp +++ b/bsnes/snes/smp/memory/memory.cpp @@ -19,7 +19,7 @@ void SMP::port_write(uint2 port, uint8 data) { apuram[0xf4 + port] = data; } -alwaysinline uint8 SMP::op_busread(uint16 addr) { +uint8 SMP::op_busread(uint16 addr) { unsigned result; switch(addr) { @@ -73,7 +73,7 @@ alwaysinline uint8 SMP::op_busread(uint16 addr) { return ram_read(addr); } -alwaysinline void SMP::op_buswrite(uint16 addr, uint8 data) { +void SMP::op_buswrite(uint16 addr, uint8 data) { switch(addr) { case 0xf0: //TEST if(regs.p.p) break; //writes only valid when P flag is clear diff --git a/bsnes/snes/system/system.cpp b/bsnes/snes/system/system.cpp index 2fdc3409e..284e389d2 100755 --- a/bsnes/snes/system/system.cpp +++ b/bsnes/snes/system/system.cpp @@ -6,7 +6,6 @@ namespace SNES { System system; #include -#include #include #include diff --git a/bsnes/snes/system/system.hpp b/bsnes/snes/system/system.hpp index f5046e35a..56eb37a15 100755 --- a/bsnes/snes/system/system.hpp +++ b/bsnes/snes/system/system.hpp @@ -47,7 +47,6 @@ private: #include "input.hpp" #include -#include #include #include diff --git a/bsnes/ui-debugger/Makefile b/bsnes/ui-debugger/Makefile index 7ea7fed74..22439079d 100755 --- a/bsnes/ui-debugger/Makefile +++ b/bsnes/ui-debugger/Makefile @@ -3,8 +3,8 @@ options += debugger include $(snes)/Makefile name := laevateinn -ui_objects := ui-main ui-interface ui-debugger ui-console -ui_objects += ui-video ui-cpu ui-memory ui-breakpoint +ui_objects := ui-main ui-interface ui-debugger ui-tracer ui-console +ui_objects += ui-video ui-cpu ui-smp ui-memory ui-breakpoint ui_objects += phoenix ruby ui_objects += $(if $(call streq,$(platform),win),resource) @@ -32,8 +32,10 @@ objects := $(patsubst %,obj/%.o,$(objects)) obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/) obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/*) obj/ui-debugger.o: $(ui)/debugger/debugger.cpp $(call rwildcard,$(ui)/*) +obj/ui-tracer.o: $(ui)/tracer/tracer.cpp $(call rwildcard,$(ui)/*) obj/ui-console.o: $(ui)/console/console.cpp $(call rwildcard,$(ui)/*) obj/ui-cpu.o: $(ui)/cpu/cpu.cpp $(call rwildcard,$(ui)/*) +obj/ui-smp.o: $(ui)/smp/smp.cpp $(call rwildcard,$(ui)/*) obj/ui-video.o: $(ui)/video/video.cpp $(call rwildcard,$(ui)/*) obj/ui-memory.o: $(ui)/memory/memory.cpp $(call rwildcard,$(ui)/*) obj/ui-breakpoint.o: $(ui)/breakpoint/breakpoint.cpp $(call rwildcard,$(ui)/*) diff --git a/bsnes/ui-debugger/base.hpp b/bsnes/ui-debugger/base.hpp index 990d30ca1..436abef18 100755 --- a/bsnes/ui-debugger/base.hpp +++ b/bsnes/ui-debugger/base.hpp @@ -20,9 +20,11 @@ using namespace ruby; #include "interface/interface.hpp" #include "debugger/debugger.hpp" +#include "tracer/tracer.hpp" #include "console/console.hpp" #include "video/video.hpp" #include "cpu/cpu.hpp" +#include "smp/smp.hpp" #include "memory/memory.hpp" #include "breakpoint/breakpoint.hpp" extern uint8_t laevateinnLogo[121905]; diff --git a/bsnes/ui-debugger/breakpoint/breakpoint.cpp b/bsnes/ui-debugger/breakpoint/breakpoint.cpp index 0c001dcc8..62bbc4b51 100755 --- a/bsnes/ui-debugger/breakpoint/breakpoint.cpp +++ b/bsnes/ui-debugger/breakpoint/breakpoint.cpp @@ -3,7 +3,7 @@ BreakpointEditor *breakpointEditor = nullptr; BreakpointEntry::BreakpointEntry() { static unsigned id = 1; - enable.setText({ id++, ". Enable" }); + enable.setText({ "#", id++ }); addr.setFont(application->monospaceFont); data.setFont(application->monospaceFont); type.append("Read", "Write", "Exec"); @@ -61,12 +61,46 @@ void BreakpointEditor::synchronize() { for(auto &bp : breakpoint) if(bp.type == Breakpoint::Read && bp.source == Breakpoint::CPU) breakpointReadCPU.append(bp); for(auto &bp : breakpoint) if(bp.type == Breakpoint::Write && bp.source == Breakpoint::CPU) breakpointWriteCPU.append(bp); for(auto &bp : breakpoint) if(bp.type == Breakpoint::Exec && bp.source == Breakpoint::CPU) breakpointExecCPU.append(bp); + + for(auto &bp : breakpointReadCPU) bp.addr = cpuDebugger->mirror(bp.addr); + for(auto &bp : breakpointWriteCPU) bp.addr = cpuDebugger->mirror(bp.addr); + for(auto &bp : breakpointExecCPU) bp.addr = cpuDebugger->mirror(bp.addr); + + breakpointReadAPU.reset(); + breakpointWriteAPU.reset(); + breakpointExecAPU.reset(); + + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Read && bp.source == Breakpoint::APU) breakpointReadAPU.append(bp); + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Write && bp.source == Breakpoint::APU) breakpointWriteAPU.append(bp); + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Exec && bp.source == Breakpoint::APU) breakpointExecAPU.append(bp); + + breakpointReadVRAM.reset(); + breakpointWriteVRAM.reset(); + + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Read && bp.source == Breakpoint::VRAM) breakpointReadVRAM.append(bp); + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Write && bp.source == Breakpoint::VRAM) breakpointWriteVRAM.append(bp); + + breakpointReadOAM.reset(); + breakpointWriteOAM.reset(); + + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Read && bp.source == Breakpoint::OAM) breakpointReadOAM.append(bp); + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Write && bp.source == Breakpoint::OAM) breakpointWriteOAM.append(bp); + + breakpointReadCGRAM.reset(); + breakpointWriteCGRAM.reset(); + + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Read && bp.source == Breakpoint::CGRAM) breakpointReadCGRAM.append(bp); + for(auto &bp : breakpoint) if(bp.type == Breakpoint::Write && bp.source == Breakpoint::CGRAM) breakpointWriteCGRAM.append(bp); } +//S-CPU +//===== + bool BreakpointEditor::testReadCPU(uint24 addr) { + addr = cpuDebugger->mirror(addr); for(auto &bp : breakpointReadCPU) { if(bp.addr == addr) { - if(bp.compare && bp.data != SNES::bus.read(addr)) continue; + if(bp.compare && bp.data != cpuDebugger->read(addr)) continue; debugger->print("Breakpoint #", bp.id, " hit\n"); return true; } @@ -75,6 +109,7 @@ bool BreakpointEditor::testReadCPU(uint24 addr) { } bool BreakpointEditor::testWriteCPU(uint24 addr, uint8 data) { + addr = cpuDebugger->mirror(addr); for(auto &bp : breakpointWriteCPU) { if(bp.addr == addr) { if(bp.compare && bp.data != data) continue; @@ -86,6 +121,7 @@ bool BreakpointEditor::testWriteCPU(uint24 addr, uint8 data) { } bool BreakpointEditor::testExecCPU(uint24 addr) { + addr = cpuDebugger->mirror(addr); for(auto &bp : breakpointExecCPU) { if(bp.addr == addr) { debugger->print("Breapoint #", bp.id, " hit\n"); @@ -94,3 +130,107 @@ bool BreakpointEditor::testExecCPU(uint24 addr) { } return false; } + +//S-SMP +//===== + +bool BreakpointEditor::testReadAPU(uint16 addr) { + for(auto &bp : breakpointReadAPU) { + if(bp.addr == addr) { + if(bp.compare && bp.data != smpDebugger->read(addr)) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +bool BreakpointEditor::testWriteAPU(uint16 addr, uint8 data) { + for(auto &bp : breakpointWriteAPU) { + if(bp.addr == addr) { + if(bp.compare && bp.data != data) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +bool BreakpointEditor::testExecAPU(uint16 addr) { + for(auto &bp : breakpointExecAPU) { + if(bp.addr == addr) { + debugger->print("Breapoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +//S-PPU +//===== + +bool BreakpointEditor::testReadVRAM(uint16 addr) { + for(auto &bp : breakpointReadVRAM) { + if(bp.addr == addr) { + if(bp.compare && bp.data != SNES::ppu.vram[addr]) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +bool BreakpointEditor::testWriteVRAM(uint16 addr, uint8 data) { + for(auto &bp : breakpointWriteVRAM) { + if(bp.addr == addr) { + if(bp.compare && bp.data != data) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +bool BreakpointEditor::testReadOAM(uint16 addr) { + for(auto &bp : breakpointReadOAM) { + if(bp.addr == addr) { + if(bp.compare && bp.data != SNES::ppu.oam[addr]) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +bool BreakpointEditor::testWriteOAM(uint16 addr, uint8 data) { + for(auto &bp : breakpointWriteOAM) { + if(bp.addr == addr) { + if(bp.compare && bp.data != data) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +bool BreakpointEditor::testReadCGRAM(uint16 addr) { + for(auto &bp : breakpointReadCGRAM) { + if(bp.addr == addr) { + if(bp.compare && bp.data != SNES::ppu.cgram[addr]) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} + +bool BreakpointEditor::testWriteCGRAM(uint16 addr, uint8 data) { + for(auto &bp : breakpointWriteCGRAM) { + if(bp.addr == addr) { + if(bp.compare && bp.data != data) continue; + debugger->print("Breakpoint #", bp.id, " hit\n"); + return true; + } + } + return false; +} diff --git a/bsnes/ui-debugger/breakpoint/breakpoint.hpp b/bsnes/ui-debugger/breakpoint/breakpoint.hpp index 229211fe1..57f6f12b1 100755 --- a/bsnes/ui-debugger/breakpoint/breakpoint.hpp +++ b/bsnes/ui-debugger/breakpoint/breakpoint.hpp @@ -26,12 +26,34 @@ struct BreakpointEditor : Window { vector breakpointReadCPU; vector breakpointWriteCPU; vector breakpointExecCPU; + vector breakpointReadAPU; + vector breakpointWriteAPU; + vector breakpointExecAPU; + vector breakpointReadVRAM; + vector breakpointWriteVRAM; + vector breakpointReadOAM; + vector breakpointWriteOAM; + vector breakpointReadCGRAM; + vector breakpointWriteCGRAM; void synchronize(); bool testReadCPU(uint24 addr); bool testWriteCPU(uint24 addr, uint8 data); bool testExecCPU(uint24 addr); + bool testReadAPU(uint16 addr); + bool testWriteAPU(uint16 addr, uint8 data); + bool testExecAPU(uint16 addr); + + bool testReadVRAM(uint16 addr); + bool testWriteVRAM(uint16 addr, uint8 data); + + bool testReadOAM(uint16 addr); + bool testWriteOAM(uint16 addr, uint8 data); + + bool testReadCGRAM(uint16 addr); + bool testWriteCGRAM(uint16 addr, uint8 data); + BreakpointEditor(); }; diff --git a/bsnes/ui-debugger/console/about.cpp b/bsnes/ui-debugger/console/about.cpp index 0cc87d2ee..8373b1218 100755 --- a/bsnes/ui-debugger/console/about.cpp +++ b/bsnes/ui-debugger/console/about.cpp @@ -3,7 +3,7 @@ AboutWindow *aboutWindow = nullptr; AboutWindow::AboutWindow() { setTitle("About Laevateinn"); - setResizable(false); +//setResizable(false); layout.setMargin(10); layout.setAlignment(0.5); @@ -12,20 +12,20 @@ AboutWindow::AboutWindow() { title.setText("Laevateinn"); version.setFont("Sans, 8, Bold"); version.setText({"bsnes/debugger v", Version}); + website.setFont("Sans, 8, Bold"); + website.setText("http://byuu.org/"); layout.append(canvas, {288, 360}); layout.append(title, {0, 0}); layout.append(version, {0, 0}); + layout.append(website, {0, 0}); append(layout); -} - -void AboutWindow::show() { - setVisible(); - setGeometry({800, 64, layout.minimumGeometry().width, layout.minimumGeometry().height}); image logo(0, 32, 255u << 24, 255u << 16, 255u << 8, 255u << 0); logo.loadPNG(laevateinnLogo, sizeof laevateinnLogo); logo.alphaBlend(backgroundColor().rgb()); canvas.setImage(logo); canvas.update(); + + setGeometry({800, 64, layout.minimumGeometry().width, layout.minimumGeometry().height}); } diff --git a/bsnes/ui-debugger/console/console.cpp b/bsnes/ui-debugger/console/console.cpp index 4c05087d4..2d0413246 100755 --- a/bsnes/ui-debugger/console/console.cpp +++ b/bsnes/ui-debugger/console/console.cpp @@ -24,28 +24,42 @@ ConsoleWindow::ConsoleWindow() { menuDebugCPU.setChecked(debugger->debug.cpu); menuDebugSMP.setText("SMP"); menuDebugSMP.setChecked(debugger->debug.smp); - menuDebugSMP.setEnabled(false); menuDebug.append(menuDebugCPU, menuDebugSMP); append(menuDebug); menuTracer.setText("&Tracer"); menuTracerEnable.setText("Enable"); + menuTracerMask.setChecked(tracer->mask); menuTracerMask.setText("Mask"); - menuTracerMask.setEnabled(false); menuTracerMaskReset.setText("Reset Mask"); - menuTracerMaskReset.setEnabled(false); menuTracer.append(menuTracerEnable, menuTracerMask, menuTracerMaskReset); append(menuTracer); menuWindows.setText("&Windows"); menuWindowsVideoWindow.setText("Video"); menuWindowsCPUDebugger.setText("CPU Debugger"); + menuWindowsSMPDebugger.setText("SMP Debugger"); menuWindowsMemoryEditor.setText("Memory Editor"); menuWindowsBreakpointEditor.setText("Breakpoint Editor"); - menuWindows.append(menuWindowsVideoWindow, menuWindowsCPUDebugger, menuWindowsMemoryEditor, - menuWindowsBreakpointEditor); + menuWindows.append(menuWindowsVideoWindow, menuWindowsSeparator1, menuWindowsCPUDebugger, + menuWindowsSMPDebugger, menuWindowsSeparator2, menuWindowsMemoryEditor, menuWindowsBreakpointEditor); append(menuWindows); + menuState.setText("&State"); + menuStateSave1.setText("Save - Slot 1"); + menuStateSave2.setText("Save - Slot 2"); + menuStateSave3.setText("Save - Slot 3"); + menuStateSave4.setText("Save - Slot 4"); + menuStateSave5.setText("Save - Slot 5"); + menuStateLoad1.setText("Load - Slot 1"); + menuStateLoad2.setText("Load - Slot 2"); + menuStateLoad3.setText("Load - Slot 3"); + menuStateLoad4.setText("Load - Slot 4"); + menuStateLoad5.setText("Load - Slot 5"); + menuState.append(menuStateSave1, menuStateSave2, menuStateSave3, menuStateSave4, menuStateSave5, + menuStateSeparator, menuStateLoad1, menuStateLoad2, menuStateLoad3, menuStateLoad4, menuStateLoad5); + append(menuState); + menuHelp.setText("&Help"); menuHelpAbout.setText("About ..."); menuHelp.append(menuHelpAbout); @@ -88,7 +102,12 @@ ConsoleWindow::ConsoleWindow() { menuDebugCPU.onToggle = [&] { debugger->debug.cpu = menuDebugCPU.checked(); }; menuDebugSMP.onToggle = [&] { debugger->debug.smp = menuDebugSMP.checked(); }; - menuTracerEnable.onToggle = [&] { debugger->tracerEnable(menuTracerEnable.checked()); }; + menuTracerEnable.onToggle = [&] { tracer->enable(menuTracerEnable.checked()); }; + menuTracerMask.onToggle = [&] { tracer->mask = menuTracerMask.checked(); }; + menuTracerMaskReset.onActivate = [&] { + tracer->resetMask(); + debugger->print("Tracer mask reset\n"); + }; menuWindowsVideoWindow.onActivate = [&] { videoWindow->setVisible(); @@ -100,8 +119,13 @@ ConsoleWindow::ConsoleWindow() { cpuDebugger->setFocused(); }; + menuWindowsSMPDebugger.onActivate = [&] { + smpDebugger->setVisible(); + smpDebugger->setFocused(); + }; + menuWindowsMemoryEditor.onActivate = [&] { - memoryEditor->update(); + memoryEditor->updateView(); memoryEditor->setVisible(); memoryEditor->setFocused(); }; @@ -111,7 +135,19 @@ ConsoleWindow::ConsoleWindow() { breakpointEditor->setFocused(); }; - menuHelpAbout.onActivate = [&] { aboutWindow->show(); }; + menuStateSave1.onActivate = [&] { interface->saveState(1); }; + menuStateSave2.onActivate = [&] { interface->saveState(2); }; + menuStateSave3.onActivate = [&] { interface->saveState(3); }; + menuStateSave4.onActivate = [&] { interface->saveState(4); }; + menuStateSave5.onActivate = [&] { interface->saveState(5); }; + + menuStateLoad1.onActivate = [&] { interface->loadState(1); }; + menuStateLoad2.onActivate = [&] { interface->loadState(2); }; + menuStateLoad3.onActivate = [&] { interface->loadState(3); }; + menuStateLoad4.onActivate = [&] { interface->loadState(4); }; + menuStateLoad5.onActivate = [&] { interface->loadState(5); }; + + menuHelpAbout.onActivate = [&] { aboutWindow->setVisible(); }; runButton.onActivate = [&] { if(debugger->paused) debugger->resume(); diff --git a/bsnes/ui-debugger/console/console.hpp b/bsnes/ui-debugger/console/console.hpp index 26a5c9b81..a37ef6b78 100755 --- a/bsnes/ui-debugger/console/console.hpp +++ b/bsnes/ui-debugger/console/console.hpp @@ -18,10 +18,26 @@ struct ConsoleWindow : Window { Menu menuWindows; Item menuWindowsVideoWindow; + Separator menuWindowsSeparator1; Item menuWindowsCPUDebugger; + Item menuWindowsSMPDebugger; + Separator menuWindowsSeparator2; Item menuWindowsMemoryEditor; Item menuWindowsBreakpointEditor; + Menu menuState; + Item menuStateSave1; + Item menuStateSave2; + Item menuStateSave3; + Item menuStateSave4; + Item menuStateSave5; + Separator menuStateSeparator; + Item menuStateLoad1; + Item menuStateLoad2; + Item menuStateLoad3; + Item menuStateLoad4; + Item menuStateLoad5; + Menu menuHelp; Item menuHelpAbout; @@ -43,8 +59,8 @@ struct AboutWindow : Window { Canvas canvas; Label title; Label version; + Label website; - void show(); AboutWindow(); }; diff --git a/bsnes/ui-debugger/cpu/cpu.cpp b/bsnes/ui-debugger/cpu/cpu.cpp index 9a921a4e6..23392aa0b 100755 --- a/bsnes/ui-debugger/cpu/cpu.cpp +++ b/bsnes/ui-debugger/cpu/cpu.cpp @@ -1,7 +1,25 @@ #include "../base.hpp" CPUDebugger *cpuDebugger = nullptr; -unsigned CPUDebugger::opcodeLength(uint24 addr) const { +uint24 CPUDebugger::mirror(uint24 addr) { + if((addr & 0x40e000) == 0x0000) addr = 0x7e0000 | (addr & 0x1fff); //$00-3f:80-bf:0000-1fff WRAM + return addr; +} + +uint8 CPUDebugger::read(uint24 addr) { + if((addr & 0x40e000) == 0x2000) return ~0; //$00-3f|80-bf:2000-3fff MMIO + if((addr & 0x40e000) == 0x4000) return ~0; //$00-3f|80-bf:4000-5fff MMIO + return SNES::bus.read(mirror(addr)); +} + +void CPUDebugger::write(uint24 addr, uint8 data) { + if((addr & 0x40e000) == 0x2000) return; //$00-3f|80-bf:2000-3fff MMIO + if((addr & 0x40e000) == 0x4000) return; //$00-3f|80-bf:4000-5fff MMIO + if((addr & 0x40e000) == 0x0000) addr = 0x7e0000 | (addr & 0x1fff); //$00-3f:80-bf:0000-1fff WRAM + return SNES::bus.write(mirror(addr), data); +} + +unsigned CPUDebugger::opcodeLength(uint24 addr) { #define M 5 #define X 6 static unsigned lengthTable[256] = { @@ -79,9 +97,9 @@ void CPUDebugger::updateDisassembly() { registers.setText({ "A:", hex<4>(SNES::cpu.regs.a), " X:", hex<4>(SNES::cpu.regs.x), " Y:", hex<4>(SNES::cpu.regs.y), " S:", hex<4>(SNES::cpu.regs.s), " D:", hex<4>(SNES::cpu.regs.d), " DB:", hex<2>(SNES::cpu.regs.db), " ", - SNES::cpu.regs.e ? "E" : "R", ":", SNES::cpu.regs.p.n ? "N" : "n", SNES::cpu.regs.p.v ? "V" : "v", - SNES::cpu.regs.p.m ? "M" : "m", SNES::cpu.regs.p.x ? "X" : "x", + SNES::cpu.regs.e ? (SNES::cpu.regs.p.m ? "1" : "0") : (SNES::cpu.regs.p.m ? "M" : "m"), + SNES::cpu.regs.e ? (SNES::cpu.regs.p.x ? "B" : "b") : (SNES::cpu.regs.p.x ? "X" : "x"), SNES::cpu.regs.p.d ? "D" : "d", SNES::cpu.regs.p.i ? "I" : "i", SNES::cpu.regs.p.z ? "Z" : "z", SNES::cpu.regs.p.c ? "C" : "c", }); @@ -91,17 +109,13 @@ CPUDebugger::CPUDebugger() { opcodePC = 0x008000; setTitle("CPU Debugger"); - setGeometry({800, 64, 500, 255}); + setGeometry({800, 64, 350, 255}); layout.setMargin(5); stepInto.setText("Step Into"); - stepOver.setText("Step Over"); - stepOver.setEnabled(false); - stepOut.setText("Step Out"); - stepOut.setEnabled(false); - skipOver.setText("Skip Over"); - skipOver.setEnabled(false); - autoRefresh.setText("Auto"); + stepNMI.setText("NMI"); + stepIRQ.setText("IRQ"); + autoUpdate.setText("Auto"); update.setText("Update"); disassembly.setFont(application->monospaceFont); registers.setFont(application->monospaceFont); @@ -109,11 +123,10 @@ CPUDebugger::CPUDebugger() { layout.append(controlLayout, {~0, 0}, 5); controlLayout.append(stepInto, {80, 0}, 5); - controlLayout.append(stepOver, {80, 0}, 5); - controlLayout.append(stepOut, {80, 0}, 5); - controlLayout.append(skipOver, {80, 0}); + controlLayout.append(stepNMI, {40, 0}, 5); + controlLayout.append(stepIRQ, {40, 0}, 5); controlLayout.append(spacer, {~0, 0}); - controlLayout.append(autoRefresh, {0, 0}, 5); + controlLayout.append(autoUpdate, {0, 0}, 5); controlLayout.append(update, {80, 0}); layout.append(disassembly, {~0, ~0}, 5); layout.append(registers, {~0, 0}); @@ -124,5 +137,15 @@ CPUDebugger::CPUDebugger() { debugger->resume(); }; + stepNMI.onActivate = [&] { + debugger->flags.cpu.nmi = true; + debugger->resume(); + }; + + stepIRQ.onActivate = [&] { + debugger->flags.cpu.irq = true; + debugger->resume(); + }; + update.onActivate = { &CPUDebugger::updateDisassembly, this }; } diff --git a/bsnes/ui-debugger/cpu/cpu.hpp b/bsnes/ui-debugger/cpu/cpu.hpp index b8801ded6..8f89e2e3e 100755 --- a/bsnes/ui-debugger/cpu/cpu.hpp +++ b/bsnes/ui-debugger/cpu/cpu.hpp @@ -4,16 +4,19 @@ struct CPUDebugger : Window { VerticalLayout layout; HorizontalLayout controlLayout; Button stepInto; - Button stepOver; - Button stepOut; - Button skipOver; + Button stepNMI; + Button stepIRQ; Widget spacer; - CheckBox autoRefresh; + CheckBox autoUpdate; Button update; TextEdit disassembly; Label registers; - unsigned opcodeLength(uint24 addr) const; + uint24 mirror(uint24 addr); + uint8_t read(uint24 addr); + void write(uint24 addr, uint8 data); + + unsigned opcodeLength(uint24 addr); void updateDisassembly(); CPUDebugger(); }; diff --git a/bsnes/ui-debugger/debugger/debugger.cpp b/bsnes/ui-debugger/debugger/debugger.cpp index a9811af88..2eab4e03d 100755 --- a/bsnes/ui-debugger/debugger/debugger.cpp +++ b/bsnes/ui-debugger/debugger/debugger.cpp @@ -11,8 +11,9 @@ void Debugger::run() { } SNES::system.run(); - if(cpuDebugger->autoRefresh.checked()) cpuDebugger->updateDisassembly(); - if(memoryEditor->autoRefresh.checked()) memoryEditor->update(); + if(cpuDebugger->autoUpdate.checked()) cpuDebugger->updateDisassembly(); + if(smpDebugger->autoUpdate.checked()) smpDebugger->updateDisassembly(); + if(memoryEditor->autoUpdate.checked()) memoryEditor->updateView(); } void Debugger::echo(const string &text) { @@ -30,33 +31,20 @@ void Debugger::suspend() { paused = true; flags.step = false; flags.cpu.stepInto = false; + flags.cpu.nmi = false; + flags.cpu.irq = false; + flags.smp.stepInto = false; consoleWindow->runButton.setText("Run"); } -void Debugger::tracerEnable(bool state) { - if(state == false) { - print("Tracer disabled\n"); - fpTracer.close(); - return; - } - - //try not to overwrite existing traces: scan from 001-999. - //if all files exist, use 000, even if it overwrites another log. - unsigned n = 1; - do { - if(file::exists({ interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" }) == false) break; - } while(++n <= 999); - - string filename = { interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" }; - if(fpTracer.open(filename, file::mode::write) == false) return; - print("Tracing to ", filename, "\n"); -} - Debugger::Debugger() { paused = true; flags.step = false; flags.cpu.stepInto = false; + flags.cpu.nmi = false; + flags.cpu.irq = false; + flags.smp.stepInto = false; debug.cpu = true; debug.smp = false; @@ -68,7 +56,19 @@ Debugger::Debugger() { SNES::cpu.debugger.op_read = { &Debugger::cpu_op_read, this }; SNES::cpu.debugger.op_write = { &Debugger::cpu_op_write, this }; + SNES::cpu.debugger.op_nmi = { &Debugger::cpu_op_nmi, this }; + SNES::cpu.debugger.op_irq = { &Debugger::cpu_op_irq, this }; + SNES::smp.debugger.op_exec = { &Debugger::smp_op_exec, this }; SNES::smp.debugger.op_read = { &Debugger::smp_op_read, this }; SNES::smp.debugger.op_write = { &Debugger::smp_op_write, this }; + + SNES::ppu.debugger.vram_read = { &Debugger::ppu_vram_read, this }; + SNES::ppu.debugger.vram_write = { &Debugger::ppu_vram_write, this }; + + SNES::ppu.debugger.oam_read = { &Debugger::ppu_oam_read, this }; + SNES::ppu.debugger.oam_write = { &Debugger::ppu_oam_write, this }; + + SNES::ppu.debugger.cgram_read = { &Debugger::ppu_cgram_read, this }; + SNES::ppu.debugger.cgram_write = { &Debugger::ppu_cgram_write, this }; } diff --git a/bsnes/ui-debugger/debugger/debugger.hpp b/bsnes/ui-debugger/debugger/debugger.hpp index c06a64a99..16721ea79 100755 --- a/bsnes/ui-debugger/debugger/debugger.hpp +++ b/bsnes/ui-debugger/debugger/debugger.hpp @@ -16,7 +16,12 @@ struct Debugger { bool step; struct CPU { bool stepInto; + bool nmi; + bool irq; } cpu; + struct SMP { + bool stepInto; + } smp; } flags; struct Debug { @@ -34,21 +39,28 @@ struct Debugger { void echo(const string &text); void resume(); //start running until breakpoint is reached void suspend(); //stop running as soon as possible - void tracerEnable(bool); //S-CPU void cpu_op_exec(uint24 addr); void cpu_op_read(uint24 addr); void cpu_op_write(uint24 addr, uint8 data); + void cpu_op_nmi(); + void cpu_op_irq(); //S-SMP void smp_op_exec(uint16 addr); void smp_op_read(uint16 addr); void smp_op_write(uint16 addr, uint8 data); - Debugger(); + //S-PPU + void ppu_vram_read(uint16 addr); + void ppu_oam_read(uint16 addr); + void ppu_cgram_read(uint16 addr); + void ppu_vram_write(uint16 addr, uint8 data); + void ppu_oam_write(uint16 addr, uint8 data); + void ppu_cgram_write(uint16 addr, uint8 data); - file fpTracer; + Debugger(); template void print(Args&&... args) { string text(std::forward(args)...); diff --git a/bsnes/ui-debugger/debugger/hook.cpp b/bsnes/ui-debugger/debugger/hook.cpp index b7f51bd40..6fff74e13 100755 --- a/bsnes/ui-debugger/debugger/hook.cpp +++ b/bsnes/ui-debugger/debugger/hook.cpp @@ -6,11 +6,15 @@ void Debugger::cpu_op_exec(uint24 addr) { cpuDebugger->opcodePC = addr; bool breakpointHit = breakpointEditor->testExecCPU(addr); - if(fpTracer.open() || (debug.cpu && flags.step) || flags.cpu.stepInto || breakpointHit) { + if((debug.cpu && tracer->enabled() && !tracer->maskCPU(addr)) + || (debug.cpu && flags.step) + || flags.cpu.stepInto + || breakpointHit + ) { char text[512]; SNES::cpu.disassemble_opcode(text, addr); - if(fpTracer.open()) fpTracer.print(text, "\n"); + if(debug.cpu && tracer->enabled()) tracer->print(text, "\n"); if((debug.cpu && flags.step) || flags.cpu.stepInto || breakpointHit) { print(text, "\n"); if(debug.cpu && flags.step) { @@ -54,17 +58,160 @@ void Debugger::cpu_op_write(uint24 addr, uint8 data) { } } +void Debugger::cpu_op_nmi() { + if(flags.cpu.nmi) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC = SNES::cpu.regs.pc); + print("CPU NMI\n", text, "\n"); + + cpuDebugger->updateDisassembly(); + suspend(); + SNES::scheduler.debug(); + } +} + +void Debugger::cpu_op_irq() { + if(flags.cpu.irq) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC = SNES::cpu.regs.pc); + print("CPU IRQ\n", text, "\n"); + + cpuDebugger->updateDisassembly(); + suspend(); + SNES::scheduler.debug(); + } +} + //S-SMP //===== void Debugger::smp_op_exec(uint16 addr) { apuUsage.data[addr] |= Usage::Exec; + smpDebugger->opcodePC = addr; + bool breakpointHit = breakpointEditor->testExecAPU(addr); + + if((debug.cpu && tracer->enabled() && !tracer->maskSMP(addr)) + || (debug.smp && flags.step) + || flags.smp.stepInto + || breakpointHit + ) { + string text = SNES::smp.disassemble_opcode(addr); + + if(debug.smp && tracer->enabled()) tracer->print(text, "\n"); + if((debug.smp && flags.step) || flags.smp.stepInto || breakpointHit) { + print(text, "\n"); + if(debug.smp && flags.step) { + consoleWindow->stepButton.setFocused(); + } + if(flags.smp.stepInto) { + smpDebugger->stepInto.setFocused(); + smpDebugger->updateDisassembly(); + } + suspend(); + SNES::scheduler.debug(); + } + } } void Debugger::smp_op_read(uint16 addr) { apuUsage.data[addr] |= Usage::Read; + bool breakpointHit = breakpointEditor->testReadAPU(addr); + + if(breakpointHit) { + print(SNES::smp.disassemble_opcode(smpDebugger->opcodePC), "\n"); + suspend(); + SNES::scheduler.debug(); + } } void Debugger::smp_op_write(uint16 addr, uint8 data) { apuUsage.data[addr] |= Usage::Write; + bool breakpointHit = breakpointEditor->testWriteAPU(addr, data); + + if(breakpointHit) { + print(SNES::smp.disassemble_opcode(smpDebugger->opcodePC), "\n"); + suspend(); + SNES::scheduler.debug(); + } +} + +//S-PPU +//===== + +void Debugger::ppu_vram_read(uint16 addr) { + bool breakpointHit = breakpointEditor->testReadVRAM(addr); + + if(breakpointHit) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC); + print(text, "\n"); + + suspend(); + SNES::scheduler.debug(); + } +} + +void Debugger::ppu_vram_write(uint16 addr, uint8 data) { + bool breakpointHit = breakpointEditor->testWriteVRAM(addr, data); + + if(breakpointHit) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC); + print(text, "\n"); + + suspend(); + SNES::scheduler.debug(); + } +} + +void Debugger::ppu_oam_read(uint16 addr) { + bool breakpointHit = breakpointEditor->testReadOAM(addr); + + if(breakpointHit) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC); + print(text, "\n"); + + suspend(); + SNES::scheduler.debug(); + } +} + +void Debugger::ppu_oam_write(uint16 addr, uint8 data) { + bool breakpointHit = breakpointEditor->testWriteOAM(addr, data); + + if(breakpointHit) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC); + print(text, "\n"); + + suspend(); + SNES::scheduler.debug(); + } +} + +void Debugger::ppu_cgram_read(uint16 addr) { + bool breakpointHit = breakpointEditor->testReadCGRAM(addr); + + if(breakpointHit) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC); + print(text, "\n"); + + suspend(); + SNES::scheduler.debug(); + } +} + +void Debugger::ppu_cgram_write(uint16 addr, uint8 data) { + bool breakpointHit = breakpointEditor->testWriteCGRAM(addr, data); + + if(breakpointHit) { + char text[512]; + SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC); + print(text, "\n"); + + suspend(); + SNES::scheduler.debug(); + } } diff --git a/bsnes/ui-debugger/interface/interface.cpp b/bsnes/ui-debugger/interface/interface.cpp index a23562216..c9bda043b 100755 --- a/bsnes/ui-debugger/interface/interface.cpp +++ b/bsnes/ui-debugger/interface/interface.cpp @@ -15,7 +15,7 @@ bool Interface::loadCartridge(const string &filename) { fileName = filename; baseName = nall::basename(fileName); pathName = dir(baseName); - mkdir(string{pathName, "debug/"}, 0755); + mkdir(string(pathName, "debug/"), 0755); string markup; markup.readfile({ baseName, ".xml" }); @@ -31,6 +31,7 @@ bool Interface::loadCartridge(const string &filename) { debugger->print("Loaded ", fileName, "\n"); loadMemory(); debugger->print(markup, "\n"); + debugger->suspend(); return true; } @@ -62,6 +63,27 @@ void Interface::saveMemory() { debugger->saveUsage(); } +bool Interface::loadState(unsigned slot) { + string filename = { baseName, "-", slot, ".bst" }; + uint8_t *data; + unsigned size; + if(file::read(filename, data, size) == false) return false; + serializer s(data, size); + bool result = SNES::system.unserialize(s); + delete[] data; + if(result) debugger->print("Loaded state from ", filename, "\n"); + return result; +} + +bool Interface::saveState(unsigned slot) { + SNES::system.runtosave(); + serializer s = SNES::system.serialize(); + string filename = { baseName, "-", slot, ".bst" }; + bool result = file::write(filename, s.data(), s.size()); + if(result) debugger->print("Saved state to ", filename, "\n"); + return result; +} + //hires is always true for accuracy core //overscan is ignored for the debugger port void Interface::videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan) { diff --git a/bsnes/ui-debugger/interface/interface.hpp b/bsnes/ui-debugger/interface/interface.hpp index 8d4e7bd0e..89ff61861 100755 --- a/bsnes/ui-debugger/interface/interface.hpp +++ b/bsnes/ui-debugger/interface/interface.hpp @@ -6,6 +6,8 @@ struct Interface : SNES::Interface { bool loadCartridge(const string &filename); void loadMemory(); void saveMemory(); + bool loadState(unsigned slot); + bool saveState(unsigned slot); void videoRefresh(const uint32_t *data, bool hires, bool interlace, bool overscan); void audioSample(int16_t lsample, int16_t rsample); diff --git a/bsnes/ui-debugger/main.cpp b/bsnes/ui-debugger/main.cpp index b04f35fed..2bb7f1868 100755 --- a/bsnes/ui-debugger/main.cpp +++ b/bsnes/ui-debugger/main.cpp @@ -30,18 +30,24 @@ Application::Application(int argc, char **argv) { monospaceFont = "Liberation Mono, 8"; } - string filename; - if(argc >= 2) filename = argv[1]; - if(!file::exists(filename)) filename = "/media/sdb1/root/cartridges/Laevateinn/The Legend of Zelda - A Link to the Past (US).sfc"; - if(!file::exists(filename)) filename = DialogWindow::fileOpen(Window::None, "", "SNES images (*.sfc)"); + string foldername; + if(argc >= 2) foldername = argv[1]; + if(!directory::exists(foldername)) foldername = "/media/sdb1/root/cartridges/The Legend of Zelda - A Link to the Past (US).sfc/"; + if(!directory::exists(foldername)) foldername = DialogWindow::folderSelect(Window::None, ""); + if(!foldername.endswith(".sfc/")) return; + lstring contents = directory::files(foldername, "*.sfc"); + if(contents.size() != 1) return; + string filename = { foldername, contents[0] }; if(!file::exists(filename)) return; interface = new Interface; debugger = new Debugger; + tracer = new Tracer; consoleWindow = new ConsoleWindow; aboutWindow = new AboutWindow; videoWindow = new VideoWindow; cpuDebugger = new CPUDebugger; + smpDebugger = new SMPDebugger; memoryEditor = new MemoryEditor; breakpointEditor = new BreakpointEditor; @@ -58,6 +64,7 @@ Application::Application(int argc, char **argv) { if(interface->loadCartridge(filename) == false) return; cpuDebugger->updateDisassembly(); + smpDebugger->updateDisassembly(); memoryEditor->selectSource(); while(quit == false) { @@ -72,10 +79,12 @@ Application::~Application() { exit(0); delete breakpointEditor; delete memoryEditor; + delete smpDebugger; delete cpuDebugger; delete videoWindow; delete aboutWindow; delete consoleWindow; + delete tracer; delete debugger; delete interface; } diff --git a/bsnes/ui-debugger/memory/memory.cpp b/bsnes/ui-debugger/memory/memory.cpp index 21f498020..5633b90a7 100755 --- a/bsnes/ui-debugger/memory/memory.cpp +++ b/bsnes/ui-debugger/memory/memory.cpp @@ -8,8 +8,9 @@ MemoryEditor::MemoryEditor() { gotoLabel.setText("Goto:"); gotoAddress.setFont(application->monospaceFont); source.append("CPU-Bus", "APU-Bus", "VRAM", "OAM", "CGRAM"); - autoRefresh.setText("Auto"); - updateButton.setText("Update"); + exportMemory.setText("Export"); + autoUpdate.setText("Auto"); + update.setText("Update"); editor.setFont(application->monospaceFont); editor.setColumns(16); editor.setRows(16); @@ -17,11 +18,12 @@ MemoryEditor::MemoryEditor() { layout.setMargin(5); layout.append(controlLayout, {~0, 0}, 5); controlLayout.append(gotoLabel, {0, 0}, 5); - controlLayout.append(gotoAddress, {100, 0}, 5); + controlLayout.append(gotoAddress, {50, 0}, 5); controlLayout.append(source, {0, 0}, 5); + controlLayout.append(exportMemory, {80, 0}, 5); controlLayout.append(spacer, {~0, 0}); - controlLayout.append(autoRefresh, {0, 0}, 5); - controlLayout.append(updateButton, {80, 0}); + controlLayout.append(autoUpdate, {0, 0}, 5); + controlLayout.append(update, {80, 0}); layout.append(editor, {~0, ~0}); append(layout); @@ -30,28 +32,47 @@ MemoryEditor::MemoryEditor() { editor.update(); }; - updateButton.onActivate = { &MemoryEditor::update, this }; + update.onActivate = { &MemoryEditor::updateView, this }; source.onChange = { &MemoryEditor::selectSource, this }; + exportMemory.onActivate = { &MemoryEditor::exportMemoryToDisk, this }; editor.onRead = { &MemoryEditor::read, this }; editor.onWrite = { &MemoryEditor::write, this }; } uint8_t MemoryEditor::read(unsigned addr) { if(SNES::cartridge.loaded() == false) return 0x00; - if(source.selection() == 0) { - return SNES::bus.read(addr & 0xffffff); + switch(source.selection()) { + case 0: return cpuDebugger->read(addr); + case 1: return smpDebugger->read(addr); + case 2: return SNES::ppu.vram[addr & 0xffff]; + case 3: return SNES::ppu.oam[addr % 544]; + case 4: return SNES::ppu.cgram[addr & 0x01ff]; } - return 0x00; + return ~0; } void MemoryEditor::write(unsigned addr, uint8_t data) { if(SNES::cartridge.loaded() == false) return; - if(source.selection() == 0) { + switch(source.selection()) { + case 0: SNES::cartridge.rom.write_protect(false); - SNES::bus.write(addr & 0xffffff, data); + cpuDebugger->write(addr, data); SNES::cartridge.rom.write_protect(true); - return; + break; + case 1: + smpDebugger->write(addr, data); + break; + case 2: + SNES::ppu.vram[addr & 0xffff] = data; + break; + case 3: + SNES::ppu.oam[addr % 544] = data; + SNES::ppu.sprite.synchronize(); //cache OAM changes internally + break; + case 4: + SNES::ppu.cgram[addr & 0x01ff] = data; + break; } } @@ -64,9 +85,30 @@ void MemoryEditor::selectSource() { case 3: editor.setLength(544); break; case 4: editor.setLength(512); break; } - update(); + updateView(); } -void MemoryEditor::update() { +void MemoryEditor::exportMemoryToDisk() { + string filename = { interface->pathName, "debug/memory-" }; + switch(source.selection()) { + case 0: filename.append("cpu.bin"); break; + case 1: filename.append("apu.bin"); break; + case 2: filename.append("vram.bin"); break; + case 3: filename.append("oam.bin"); break; + case 4: filename.append("cgram.bin"); break; + } + file fp; + if(fp.open(filename, file::mode::write) == false) return; + switch(source.selection()) { + case 0: for(unsigned addr = 0; addr <= 0xffffff; addr++) fp.write(cpuDebugger->read(addr)); break; + case 1: for(unsigned addr = 0; addr <= 0xffff; addr++) fp.write(smpDebugger->read(addr)); break; + case 2: for(unsigned addr = 0; addr <= 0xffff; addr++) fp.write(SNES::ppu.vram[addr]); break; + case 3: for(unsigned addr = 0; addr <= 0x021f; addr++) fp.write(SNES::ppu.oam[addr]); break; + case 4: for(unsigned addr = 0; addr <= 0x01ff; addr++) fp.write(SNES::ppu.cgram[addr]); break; + } + debugger->print("Exported memory to ", filename, "\n"); +} + +void MemoryEditor::updateView() { editor.update(); } diff --git a/bsnes/ui-debugger/memory/memory.hpp b/bsnes/ui-debugger/memory/memory.hpp index 3725f82f2..a4bad2668 100755 --- a/bsnes/ui-debugger/memory/memory.hpp +++ b/bsnes/ui-debugger/memory/memory.hpp @@ -4,15 +4,17 @@ struct MemoryEditor : Window { Label gotoLabel; LineEdit gotoAddress; ComboBox source; + Button exportMemory; Widget spacer; - CheckBox autoRefresh; - Button updateButton; + CheckBox autoUpdate; + Button update; HexEdit editor; uint8_t read(unsigned addr); void write(unsigned addr, uint8_t data); void selectSource(); - void update(); + void exportMemoryToDisk(); + void updateView(); MemoryEditor(); }; diff --git a/bsnes/ui-debugger/resource.rc b/bsnes/ui-debugger/resource.rc new file mode 100755 index 000000000..b18944ff9 --- /dev/null +++ b/bsnes/ui-debugger/resource.rc @@ -0,0 +1,2 @@ +1 24 "../data/bsnes.Manifest" +2 ICON DISCARDABLE "../data/bsnes.ico" diff --git a/bsnes/ui-debugger/smp/smp.cpp b/bsnes/ui-debugger/smp/smp.cpp new file mode 100755 index 000000000..627aff963 --- /dev/null +++ b/bsnes/ui-debugger/smp/smp.cpp @@ -0,0 +1,99 @@ +#include "../base.hpp" +SMPDebugger *smpDebugger = nullptr; + +uint8 SMPDebugger::read(uint16 addr) { + if((addr & 0xfff0) == 0x00f0) return ~0; //$00f0-00ff MMIO + return SNES::smp.op_busread(addr); +} + +void SMPDebugger::write(uint16 addr, uint8 data) { + if((addr & 0xfff0) == 0x00f0) return; //$00f0-00ff MMIO + return SNES::smp.op_buswrite(addr, data); +} + +unsigned SMPDebugger::opcodeLength(uint16 addr) { + static unsigned lengthTable[256] = { + 0 + }; + return lengthTable[SNES::smp.op_busread(addr)]; +} + +void SMPDebugger::updateDisassembly() { + string line[15]; + + line[7] = { "> ", SNES::smp.disassemble_opcode(opcodePC) }; + line[7][31] = 0; + + signed addr = opcodePC; + for(signed o = 6; o >= 0; o--) { + for(signed b = 1; b <= 3; b++) { + if(addr - b >= 0 && (debugger->apuUsage.data[addr - b] & Usage::Exec)) { + addr -= b; + line[o] = { " ", SNES::smp.disassemble_opcode(addr) }; + line[o][31] = 0; + break; + } + } + } + + addr = opcodePC; + for(signed o = 8; o <= 14; o++) { + for(signed b = 1; b <= 3; b++) { + if(addr - b <= 0xffff && (debugger->apuUsage.data[addr + b] & Usage::Exec)) { + addr += b; + line[o] = { " ", SNES::smp.disassemble_opcode(addr) }; + line[o][31] = 0; + break; + } + } + } + + string output; + for(auto &n : line) { + if(n.empty()) output.append(" ...\n"); + else output.append(n, "\n"); + } + output.rtrim<1>("\n"); + + disassembly.setText(output); + registers.setText({ + "YA:", hex<2>(SNES::smp.regs.y), hex<2>(SNES::smp.regs.a), + " A:", hex<2>(SNES::smp.regs.a), " X:", hex<2>(SNES::smp.regs.x), + " Y:", hex<2>(SNES::smp.regs.y), " S:01", hex<2>(SNES::smp.regs.s), " ", + SNES::smp.regs.p.n ? "N" : "n", SNES::smp.regs.p.v ? "V" : "v", + SNES::smp.regs.p.p ? "P" : "p", SNES::smp.regs.p.b ? "B" : "b", + SNES::smp.regs.p.h ? "H" : "h", SNES::smp.regs.p.i ? "I" : "i", + SNES::smp.regs.p.z ? "Z" : "z", SNES::smp.regs.p.c ? "C" : "c", + }); +} + +SMPDebugger::SMPDebugger() { + opcodePC = 0xffc0; + + setTitle("SMP Debugger"); + setGeometry({800, 800, 350, 255}); + + layout.setMargin(5); + stepInto.setText("Step Into"); + autoUpdate.setText("Auto"); + update.setText("Update"); + disassembly.setFont(application->monospaceFont); + registers.setFont(application->monospaceFont); + registers.setText(" "); + + layout.append(controlLayout, {~0, 0}, 5); + controlLayout.append(stepInto, {80, 0}, 5); + controlLayout.append(spacer, {~0, 0}); + controlLayout.append(autoUpdate, {0, 0}, 5); + controlLayout.append(update, {80, 0}); + layout.append(disassembly, {~0, ~0}, 5); + layout.append(registers, {~0, 0}); + append(layout); + + stepInto.onActivate = [&] { + debugger->flags.smp.stepInto = true; + debugger->resume(); + }; + + update.onActivate = { &SMPDebugger::updateDisassembly, this }; +} diff --git a/bsnes/ui-debugger/smp/smp.hpp b/bsnes/ui-debugger/smp/smp.hpp new file mode 100755 index 000000000..653ed2e1f --- /dev/null +++ b/bsnes/ui-debugger/smp/smp.hpp @@ -0,0 +1,21 @@ +struct SMPDebugger : Window { + uint16 opcodePC; + + VerticalLayout layout; + HorizontalLayout controlLayout; + Button stepInto; + Widget spacer; + CheckBox autoUpdate; + Button update; + TextEdit disassembly; + Label registers; + + uint8 read(uint16 addr); + void write(uint16 addr, uint8 data); + + unsigned opcodeLength(uint16 addr); + void updateDisassembly(); + SMPDebugger(); +}; + +extern SMPDebugger *smpDebugger; diff --git a/bsnes/ui-debugger/tracer/tracer.cpp b/bsnes/ui-debugger/tracer/tracer.cpp new file mode 100755 index 000000000..c30dc7780 --- /dev/null +++ b/bsnes/ui-debugger/tracer/tracer.cpp @@ -0,0 +1,55 @@ +#include "../base.hpp" +Tracer *tracer = nullptr; + +void Tracer::resetMask() { + memset(cpuMask, 0, 0x200000); + memset(smpMask, 0, 0x2000); +} + +bool Tracer::maskCPU(uint24 addr) { + if(mask == false) return false; + if(cpuMask[addr >> 3] & (1 << (addr & 7))) return true; + cpuMask[addr >> 3] |= 1 << (addr & 7); + return false; +} + +bool Tracer::maskSMP(uint16 addr) { + if(mask == false) return false; + if(smpMask[addr >> 3] & (1 << (addr & 7))) return true; + smpMask[addr >> 3] |= 1 << (addr & 7); + return false; +} + +bool Tracer::enabled() { + return fp.open(); +} + +void Tracer::enable(bool state) { + if(state == false) { + debugger->print("Tracer disabled\n"); + fp.close(); + return; + } + + //try not to overwrite existing traces: scan from 001-999. + //if all files exist, use 000, even if it overwrites another log. + unsigned n = 1; + do { + if(file::exists({ interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" }) == false) break; + } while(++n <= 999); + + string filename = { interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" }; + if(fp.open(filename, file::mode::write) == false) return; + debugger->print("Tracing to ", filename, "\n"); +} + +Tracer::Tracer() { + mask = false; + cpuMask = new uint8_t[0x200000](); + smpMask = new uint8_t[0x2000](); +} + +Tracer::~Tracer() { + delete[] cpuMask; + delete[] smpMask; +} diff --git a/bsnes/ui-debugger/tracer/tracer.hpp b/bsnes/ui-debugger/tracer/tracer.hpp new file mode 100755 index 000000000..c599b461d --- /dev/null +++ b/bsnes/ui-debugger/tracer/tracer.hpp @@ -0,0 +1,22 @@ +struct Tracer { + file fp; + bool mask; + uint8_t *cpuMask; + uint8_t *smpMask; + + void resetMask(); + bool maskCPU(uint24 addr); + bool maskSMP(uint16 addr); + + bool enabled(); + void enable(bool); + + Tracer(); + ~Tracer(); + + template void print(Args&&... args) { + fp.print(std::forward(args)...); + } +}; + +extern Tracer *tracer; diff --git a/bsnes/ui-debugger/video/video.cpp b/bsnes/ui-debugger/video/video.cpp index 447c6e20e..c74f33273 100755 --- a/bsnes/ui-debugger/video/video.cpp +++ b/bsnes/ui-debugger/video/video.cpp @@ -3,8 +3,8 @@ VideoWindow *videoWindow = nullptr; VideoWindow::VideoWindow() { setTitle("Video"); +//setResizable(false); setGeometry({64, 64, 512, 480}); - setResizable(false); setStatusFont(application->proportionalFontBold); setStatusVisible();