diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index f6be8cdc..7fd70999 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 24e4de7c..be954c62 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 1a4de4c0..e64881c9 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 2c82394b..037dd5a9 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 aedc20be..aeb28bba 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 a1803bbf..08812fb3 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 b3e397d7..8248104a 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 670b9920..bd9cf54b 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 ed168866..8cdbe573 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 c341156c..8e1df517 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 26f39558..05e066d4 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 3cd9a8f4..5eb963e3 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 b1312339..00000000 --- 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 8a4c1ba2..00000000 --- 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 26553af4..c279a806 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 5995c57a..39b25c20 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 0c105624..7a30c3eb 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 15502335..6c674841 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 dda35883..ae684a7d 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 641ac13c..7735f358 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 a4bad10e..e1fcd4a4 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 b03f0be8..ecbab1be 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 2fdc3409..284e389d 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 f5046e35..56eb37a1 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 7ea7fed7..22439079 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 990d30ca..436abef1 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 0c001dcc..62bbc4b5 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 229211fe..57f6f12b 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 0cc87d2e..8373b121 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 4c05087d..2d041324 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 26a5c9b8..a37ef6b7 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 9a921a4e..23392aa0 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 b8801ded..8f89e2e3 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 a9811af8..2eab4e03 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 c06a64a9..16721ea7 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 b7f51bd4..6fff74e1 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 a2356221..c9bda043 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 8d4e7bd0..89ff6186 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 b04f35fe..2bb7f186 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 21f49802..5633b90a 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 3725f82f..a4bad266 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 00000000..b18944ff --- /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 00000000..627aff96 --- /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 00000000..653ed2e1 --- /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 00000000..c30dc778 --- /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 00000000..c599b461 --- /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 447c6e20..c74f3327 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();