From 0370229444126f86ae240d1536fd433bd8c918ce Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Sun, 12 Feb 2012 20:58:04 +1100 Subject: [PATCH] Update to v085r09 release. byuu says: Added VRAM viewer (mouse over to get tile# and VRAM address), CPU+SMP register editors, settings.cfg to cache path+sync audio+mute audio settings (Windows Vista+ ignore my request for the default folder because they are fucking stupid, so they always default to your home folder. I'm going to have to recommend using a batch file to start laevateinn there. Sorry, blame Microsoft for being fuck-ups), geometry.cfg to remember where you placed windows and what size you made them (a bug in Qt prevents me from making some windows fixed-size for now, but that'll change when I can work around the Qt issue), usage map invalidation if the ROM was modified after the usage files, that empty line insertion thing creaothceann wanted on emulation resume, all chips now synchronize immediately rather than just-in-time, which is important for a debugger. Going to postpone the properties viewer until after v086. So this is pretty much ready for release. Please bug-test. I don't care so much about little frills like "oh the memory editor window should default to a little bigger", you can work around that by resizing it. I care about things like, "VRAM write breakpoints don't work at all." If we miss any bugs and it gets released, not the end of the world, but you'll be waiting a while for the next release to address any missed bugs now. --- bsnes/base/base.hpp | 2 +- bsnes/phoenix/core/core.hpp | 1 + bsnes/snes/cpu/timing/timing.cpp | 8 +- bsnes/snes/smp/timing/timing.cpp | 4 + bsnes/ui-debugger/Makefile | 7 +- bsnes/ui-debugger/base.hpp | 3 + bsnes/ui-debugger/breakpoint/breakpoint.cpp | 4 +- bsnes/ui-debugger/console/about.cpp | 3 +- bsnes/ui-debugger/console/console.cpp | 30 ++++- bsnes/ui-debugger/console/console.hpp | 1 + bsnes/ui-debugger/cpu/cpu.cpp | 11 +- bsnes/ui-debugger/cpu/cpu.hpp | 37 +++++- bsnes/ui-debugger/cpu/registers.cpp | 105 ++++++++++++++++ bsnes/ui-debugger/debugger/debugger.cpp | 1 + bsnes/ui-debugger/debugger/hook.cpp | 18 +-- bsnes/ui-debugger/debugger/usage.cpp | 30 ++++- bsnes/ui-debugger/main.cpp | 32 ++++- bsnes/ui-debugger/memory/memory.cpp | 4 +- bsnes/ui-debugger/settings/settings.cpp | 15 +++ bsnes/ui-debugger/settings/settings.hpp | 11 ++ bsnes/ui-debugger/smp/registers.cpp | 88 ++++++++++++++ bsnes/ui-debugger/smp/smp.cpp | 11 +- bsnes/ui-debugger/smp/smp.hpp | 32 ++++- bsnes/ui-debugger/video/video.cpp | 2 + bsnes/ui-debugger/vram/vram.cpp | 126 ++++++++++++++++++++ bsnes/ui-debugger/vram/vram.hpp | 15 +++ bsnes/ui-debugger/window/window.cpp | 25 ++++ bsnes/ui-debugger/window/window.hpp | 15 +++ 28 files changed, 598 insertions(+), 43 deletions(-) create mode 100755 bsnes/ui-debugger/cpu/registers.cpp create mode 100755 bsnes/ui-debugger/settings/settings.cpp create mode 100755 bsnes/ui-debugger/settings/settings.hpp create mode 100755 bsnes/ui-debugger/smp/registers.cpp create mode 100755 bsnes/ui-debugger/vram/vram.cpp create mode 100755 bsnes/ui-debugger/vram/vram.hpp create mode 100755 bsnes/ui-debugger/window/window.cpp create mode 100755 bsnes/ui-debugger/window/window.hpp diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index 7fd709998..49bcbc686 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.08"; +const char Version[] = "085.09"; #include #include diff --git a/bsnes/phoenix/core/core.hpp b/bsnes/phoenix/core/core.hpp index 9c8d3d22f..d44e05966 100755 --- a/bsnes/phoenix/core/core.hpp +++ b/bsnes/phoenix/core/core.hpp @@ -68,6 +68,7 @@ struct Geometry { Size size() const; nall::string text() const; inline Geometry() : x(0), y(0), width(0), height(0) {} + inline Geometry(const Position& position, const Size& size) : x(position.x), y(position.y), width(size.width), height(size.height) {} inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {} Geometry(const nall::string &text); }; diff --git a/bsnes/snes/cpu/timing/timing.cpp b/bsnes/snes/cpu/timing/timing.cpp index f1378f0c9..8189c01b2 100755 --- a/bsnes/snes/cpu/timing/timing.cpp +++ b/bsnes/snes/cpu/timing/timing.cpp @@ -27,6 +27,12 @@ void CPU::add_clocks(unsigned clocks) { status.dram_refreshed = true; add_clocks(40); } + + #if defined(DEBUGGER) + synchronize_smp(); + synchronize_ppu(); + synchronize_coprocessors(); + #endif } //called by ppu.tick() when Hcounter=0 @@ -35,8 +41,8 @@ void CPU::scanline() { status.line_clocks = lineclocks(); //forcefully sync S-CPU to other processors, in case chips are not communicating - synchronize_ppu(); synchronize_smp(); + synchronize_ppu(); synchronize_coprocessors(); system.scanline(); diff --git a/bsnes/snes/smp/timing/timing.cpp b/bsnes/snes/smp/timing/timing.cpp index 40374d1ce..3709ba988 100755 --- a/bsnes/snes/smp/timing/timing.cpp +++ b/bsnes/snes/smp/timing/timing.cpp @@ -4,9 +4,13 @@ void SMP::add_clocks(unsigned clocks) { step(clocks); synchronize_dsp(); + #if defined(DEBUGGER) + synchronize_cpu(); + #else //forcefully sync S-SMP to S-CPU in case chips are not communicating //sync if S-SMP is more than 24 samples ahead of S-CPU if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu(); + #endif } void SMP::cycle_edge() { diff --git a/bsnes/ui-debugger/Makefile b/bsnes/ui-debugger/Makefile index 22439079d..333f34a4d 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-tracer ui-console -ui_objects += ui-video ui-cpu ui-smp ui-memory ui-breakpoint +ui_objects := ui-main ui-settings ui-interface ui-debugger ui-tracer ui-window +ui_objects += ui-console ui-video ui-cpu ui-smp ui-memory ui-breakpoint ui-vram ui_objects += phoenix ruby ui_objects += $(if $(call streq,$(platform),win),resource) @@ -30,15 +30,18 @@ objects := $(ui_objects) $(objects) objects := $(patsubst %,obj/%.o,$(objects)) obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/) +obj/ui-settings.o: $(ui)/settings/settings.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-window.o: $(ui)/window/window.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)/*) +obj/ui-vram.o: $(ui)/vram/vram.cpp $(call rwildcard,$(ui)/*) obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*) $(call compile,$(rubyflags)) diff --git a/bsnes/ui-debugger/base.hpp b/bsnes/ui-debugger/base.hpp index 436abef18..26979af82 100755 --- a/bsnes/ui-debugger/base.hpp +++ b/bsnes/ui-debugger/base.hpp @@ -18,15 +18,18 @@ using namespace phoenix; #include using namespace ruby; +#include "settings/settings.hpp" #include "interface/interface.hpp" #include "debugger/debugger.hpp" #include "tracer/tracer.hpp" +#include "window/window.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" +#include "vram/vram.hpp" extern uint8_t laevateinnLogo[121905]; struct Application { diff --git a/bsnes/ui-debugger/breakpoint/breakpoint.cpp b/bsnes/ui-debugger/breakpoint/breakpoint.cpp index 62bbc4b51..cef0e8e92 100755 --- a/bsnes/ui-debugger/breakpoint/breakpoint.cpp +++ b/bsnes/ui-debugger/breakpoint/breakpoint.cpp @@ -32,8 +32,10 @@ BreakpointEditor::BreakpointEditor() { for(auto &bp : breakpointEntry) layout.append(bp, {0, 0}, 5); append(layout); - setGeometry({800, 600, layout.minimumGeometry().width, layout.minimumGeometry().height - 5}); + setGeometry({128, 128, layout.minimumGeometry().width, layout.minimumGeometry().height - 5}); synchronize(); + + windowManager->append(this, "BreakpointEditor"); } //enable checkbox toggled on one of the five BreakpointEntry items: diff --git a/bsnes/ui-debugger/console/about.cpp b/bsnes/ui-debugger/console/about.cpp index 8373b1218..a03efdbfc 100755 --- a/bsnes/ui-debugger/console/about.cpp +++ b/bsnes/ui-debugger/console/about.cpp @@ -27,5 +27,6 @@ AboutWindow::AboutWindow() { canvas.setImage(logo); canvas.update(); - setGeometry({800, 64, layout.minimumGeometry().width, layout.minimumGeometry().height}); + setGeometry({128, 128, layout.minimumGeometry().width, layout.minimumGeometry().height}); + windowManager->append(this, "AboutWindow"); } diff --git a/bsnes/ui-debugger/console/console.cpp b/bsnes/ui-debugger/console/console.cpp index 2d0413246..7627746d0 100755 --- a/bsnes/ui-debugger/console/console.cpp +++ b/bsnes/ui-debugger/console/console.cpp @@ -5,7 +5,7 @@ ConsoleWindow *consoleWindow = nullptr; ConsoleWindow::ConsoleWindow() { setTitle({"Console - Laevateinn v", Version}); - setGeometry({64, 650, 640, 400}); + setGeometry({64, 640, 640, 400}); setMenuVisible(); menuEmulation.setText("&Emulation"); @@ -13,8 +13,9 @@ ConsoleWindow::ConsoleWindow() { menuEmulationPowerCycle.setText("Power Cycle"); menuEmulationReset.setText("Reset"); menuEmulationSynchronizeAudio.setText("Synchronize Audio"); - menuEmulationSynchronizeAudio.setChecked(); + menuEmulationSynchronizeAudio.setChecked(settings->synchronizeAudio); menuEmulationMuteAudio.setText("Mute Audio"); + menuEmulationMuteAudio.setChecked(settings->muteAudio); menuEmulation.append(menuEmulationReloadCartridge, menuEmulationPowerCycle, menuEmulationReset, menuEmulationSeparator, menuEmulationSynchronizeAudio, menuEmulationMuteAudio); append(menuEmulation); @@ -41,8 +42,10 @@ ConsoleWindow::ConsoleWindow() { menuWindowsSMPDebugger.setText("SMP Debugger"); menuWindowsMemoryEditor.setText("Memory Editor"); menuWindowsBreakpointEditor.setText("Breakpoint Editor"); + menuWindowsVRAMViewer.setText("VRAM Viewer"); menuWindows.append(menuWindowsVideoWindow, menuWindowsSeparator1, menuWindowsCPUDebugger, - menuWindowsSMPDebugger, menuWindowsSeparator2, menuWindowsMemoryEditor, menuWindowsBreakpointEditor); + menuWindowsSMPDebugger, menuWindowsSeparator2, menuWindowsMemoryEditor, menuWindowsBreakpointEditor, + menuWindowsVRAMViewer); append(menuWindows); menuState.setText("&State"); @@ -96,7 +99,11 @@ ConsoleWindow::ConsoleWindow() { }; menuEmulationSynchronizeAudio.onToggle = [&] { - audio.set(Audio::Synchronize, menuEmulationSynchronizeAudio.checked()); + audio.set(Audio::Synchronize, settings->synchronizeAudio = menuEmulationSynchronizeAudio.checked()); + }; + + menuEmulationMuteAudio.onToggle = [&] { + settings->muteAudio = menuEmulationMuteAudio.checked(); }; menuDebugCPU.onToggle = [&] { debugger->debug.cpu = menuDebugCPU.checked(); }; @@ -135,6 +142,11 @@ ConsoleWindow::ConsoleWindow() { breakpointEditor->setFocused(); }; + menuWindowsVRAMViewer.onActivate = [&] { + vramViewer->setVisible(); + vramViewer->setFocused(); + }; + menuStateSave1.onActivate = [&] { interface->saveState(1); }; menuStateSave2.onActivate = [&] { interface->saveState(2); }; menuStateSave3.onActivate = [&] { interface->saveState(3); }; @@ -150,8 +162,12 @@ ConsoleWindow::ConsoleWindow() { menuHelpAbout.onActivate = [&] { aboutWindow->setVisible(); }; runButton.onActivate = [&] { - if(debugger->paused) debugger->resume(); - else debugger->suspend(); + if(debugger->paused) { + print("\n"); + debugger->resume(); + } else { + debugger->suspend(); + } }; stepButton.onActivate = [&] { @@ -162,6 +178,8 @@ ConsoleWindow::ConsoleWindow() { clearButton.onActivate = [&] { console.setText(""); }; + + windowManager->append(this, "ConsoleWindow"); } void ConsoleWindow::print(const string &text) { diff --git a/bsnes/ui-debugger/console/console.hpp b/bsnes/ui-debugger/console/console.hpp index a37ef6b78..03bcc4fbe 100755 --- a/bsnes/ui-debugger/console/console.hpp +++ b/bsnes/ui-debugger/console/console.hpp @@ -24,6 +24,7 @@ struct ConsoleWindow : Window { Separator menuWindowsSeparator2; Item menuWindowsMemoryEditor; Item menuWindowsBreakpointEditor; + Item menuWindowsVRAMViewer; Menu menuState; Item menuStateSave1; diff --git a/bsnes/ui-debugger/cpu/cpu.cpp b/bsnes/ui-debugger/cpu/cpu.cpp index 23392aa0b..af798259a 100755 --- a/bsnes/ui-debugger/cpu/cpu.cpp +++ b/bsnes/ui-debugger/cpu/cpu.cpp @@ -1,6 +1,8 @@ #include "../base.hpp" CPUDebugger *cpuDebugger = nullptr; +#include "registers.cpp" + uint24 CPUDebugger::mirror(uint24 addr) { if((addr & 0x40e000) == 0x0000) addr = 0x7e0000 | (addr & 0x1fff); //$00-3f:80-bf:0000-1fff WRAM return addr; @@ -109,7 +111,7 @@ CPUDebugger::CPUDebugger() { opcodePC = 0x008000; setTitle("CPU Debugger"); - setGeometry({800, 64, 350, 255}); + setGeometry({128, 128, 350, 255}); layout.setMargin(5); stepInto.setText("Step Into"); @@ -148,4 +150,11 @@ CPUDebugger::CPUDebugger() { }; update.onActivate = { &CPUDebugger::updateDisassembly, this }; + + registers.onActivate = [&] { + cpuRegisterEditor->loadRegisters(); + cpuRegisterEditor->setVisible(); + }; + + windowManager->append(this, "CPUDebugger"); } diff --git a/bsnes/ui-debugger/cpu/cpu.hpp b/bsnes/ui-debugger/cpu/cpu.hpp index 8f89e2e3e..a03b5b64e 100755 --- a/bsnes/ui-debugger/cpu/cpu.hpp +++ b/bsnes/ui-debugger/cpu/cpu.hpp @@ -10,7 +10,7 @@ struct CPUDebugger : Window { CheckBox autoUpdate; Button update; TextEdit disassembly; - Label registers; + Button registers; uint24 mirror(uint24 addr); uint8_t read(uint24 addr); @@ -21,4 +21,39 @@ struct CPUDebugger : Window { CPUDebugger(); }; +struct CPURegisterEditor : Window { + VerticalLayout layout; + HorizontalLayout primaryLayout; + Label regALabel; + LineEdit regAValue; + Label regXLabel; + LineEdit regXValue; + Label regYLabel; + LineEdit regYValue; + Label regSLabel; + LineEdit regSValue; + Label regDLabel; + LineEdit regDValue; + Label regDBLabel; + LineEdit regDBValue; + HorizontalLayout secondaryLayout; + CheckBox flagN; + CheckBox flagV; + CheckBox flagM; + CheckBox flagX; + CheckBox flagD; + CheckBox flagI; + CheckBox flagZ; + CheckBox flagC; + HorizontalLayout tertiaryLayout; + CheckBox flagE; + Widget spacer; + Button update; + + void loadRegisters(); + void saveRegisters(); + CPURegisterEditor(); +}; + extern CPUDebugger *cpuDebugger; +extern CPURegisterEditor *cpuRegisterEditor; diff --git a/bsnes/ui-debugger/cpu/registers.cpp b/bsnes/ui-debugger/cpu/registers.cpp new file mode 100755 index 000000000..496cd10fa --- /dev/null +++ b/bsnes/ui-debugger/cpu/registers.cpp @@ -0,0 +1,105 @@ +CPURegisterEditor *cpuRegisterEditor = nullptr; + +CPURegisterEditor::CPURegisterEditor() { + setTitle("CPU Register Editor"); + + layout.setMargin(5); + regALabel.setText("A:"); + regAValue.setFont(application->monospaceFont); + regXLabel.setText("X:"); + regXValue.setFont(application->monospaceFont); + regYLabel.setText("Y:"); + regYValue.setFont(application->monospaceFont); + regSLabel.setText("S:"); + regSValue.setFont(application->monospaceFont); + regDLabel.setText("D:"); + regDValue.setFont(application->monospaceFont); + regDBLabel.setText("DB:"); + regDBValue.setFont(application->monospaceFont); + flagN.setText("N"); + flagV.setText("V"); + flagM.setText("M"); + flagX.setText("X"); + flagD.setText("D"); + flagI.setText("I"); + flagZ.setText("Z"); + flagC.setText("C"); + flagE.setText("E"); + update.setText("Update"); + + loadRegisters(); + + layout.append(primaryLayout, {~0, 0}, 5); + primaryLayout.append(regALabel, {0, 0}, 5); + primaryLayout.append(regAValue, {0, 0}, 5); + primaryLayout.append(regXLabel, {0, 0}, 5); + primaryLayout.append(regXValue, {0, 0}, 5); + primaryLayout.append(regYLabel, {0, 0}, 5); + primaryLayout.append(regYValue, {0, 0}, 5); + primaryLayout.append(regSLabel, {0, 0}, 5); + primaryLayout.append(regSValue, {0, 0}, 5); + primaryLayout.append(regDLabel, {0, 0}, 5); + primaryLayout.append(regDValue, {0, 0}, 5); + primaryLayout.append(regDBLabel, {0, 0}, 5); + primaryLayout.append(regDBValue, {0, 0}); + layout.append(secondaryLayout, {~0, 0}, 5); + secondaryLayout.append(flagN, {0, 0}, 5); + secondaryLayout.append(flagV, {0, 0}, 5); + secondaryLayout.append(flagM, {0, 0}, 5); + secondaryLayout.append(flagX, {0, 0}, 5); + secondaryLayout.append(flagD, {0, 0}, 5); + secondaryLayout.append(flagI, {0, 0}, 5); + secondaryLayout.append(flagZ, {0, 0}, 5); + secondaryLayout.append(flagC, {0, 0}); + layout.append(tertiaryLayout, {~0, 0}); + tertiaryLayout.append(flagE, {0, 0}, 5); + tertiaryLayout.append(spacer, {~0, 0}); + tertiaryLayout.append(update, {80, 0}); + append(layout); + + update.onActivate = [&] { + saveRegisters(); + cpuDebugger->updateDisassembly(); + setVisible(false); + }; + + setGeometry({{128, 128}, layout.minimumGeometry().size()}); + windowManager->append(this, "CPURegisterEditor"); +} + +void CPURegisterEditor::loadRegisters() { + regAValue.setText(hex<4>(SNES::cpu.regs.a)); + regXValue.setText(hex<4>(SNES::cpu.regs.x)); + regYValue.setText(hex<4>(SNES::cpu.regs.y)); + regSValue.setText(hex<4>(SNES::cpu.regs.s)); + regDValue.setText(hex<4>(SNES::cpu.regs.d)); + regDBValue.setText(hex<2>(SNES::cpu.regs.db)); + flagN.setChecked(SNES::cpu.regs.p.n); + flagV.setChecked(SNES::cpu.regs.p.v); + flagM.setChecked(SNES::cpu.regs.p.m); + flagX.setChecked(SNES::cpu.regs.p.x); + flagD.setChecked(SNES::cpu.regs.p.d); + flagI.setChecked(SNES::cpu.regs.p.i); + flagZ.setChecked(SNES::cpu.regs.p.z); + flagC.setChecked(SNES::cpu.regs.p.c); + flagE.setChecked(SNES::cpu.regs.e); +} + +void CPURegisterEditor::saveRegisters() { + SNES::cpu.regs.a = hex(regAValue.text()); + SNES::cpu.regs.x = hex(regXValue.text()); + SNES::cpu.regs.y = hex(regYValue.text()); + SNES::cpu.regs.s = hex(regSValue.text()); + SNES::cpu.regs.d = hex(regDValue.text()); + SNES::cpu.regs.db = hex(regDBValue.text()); + SNES::cpu.regs.p.n = flagN.checked(); + SNES::cpu.regs.p.v = flagV.checked(); + SNES::cpu.regs.p.m = flagM.checked(); + SNES::cpu.regs.p.x = flagX.checked(); + SNES::cpu.regs.p.d = flagD.checked(); + SNES::cpu.regs.p.i = flagI.checked(); + SNES::cpu.regs.p.z = flagZ.checked(); + SNES::cpu.regs.p.c = flagC.checked(); + SNES::cpu.regs.e = flagE.checked(); + SNES::cpu.update_table(); //cache E/M/X flags +} diff --git a/bsnes/ui-debugger/debugger/debugger.cpp b/bsnes/ui-debugger/debugger/debugger.cpp index 2eab4e03d..f341a4a8e 100755 --- a/bsnes/ui-debugger/debugger/debugger.cpp +++ b/bsnes/ui-debugger/debugger/debugger.cpp @@ -14,6 +14,7 @@ void Debugger::run() { if(cpuDebugger->autoUpdate.checked()) cpuDebugger->updateDisassembly(); if(smpDebugger->autoUpdate.checked()) smpDebugger->updateDisassembly(); if(memoryEditor->autoUpdate.checked()) memoryEditor->updateView(); + if(vramViewer->autoUpdate.checked()) vramViewer->updateTiles(); } void Debugger::echo(const string &text) { diff --git a/bsnes/ui-debugger/debugger/hook.cpp b/bsnes/ui-debugger/debugger/hook.cpp index 6fff74e13..391e62142 100755 --- a/bsnes/ui-debugger/debugger/hook.cpp +++ b/bsnes/ui-debugger/debugger/hook.cpp @@ -60,25 +60,15 @@ 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(); + print("CPU NMI\n"); //, text, "\n"); + flags.cpu.stepInto = true; } } 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(); + print("CPU IRQ\n"); + flags.cpu.stepInto = true; } } diff --git a/bsnes/ui-debugger/debugger/usage.cpp b/bsnes/ui-debugger/debugger/usage.cpp index a19fa7714..bd2a2aa85 100755 --- a/bsnes/ui-debugger/debugger/usage.cpp +++ b/bsnes/ui-debugger/debugger/usage.cpp @@ -15,13 +15,31 @@ Usage::~Usage() { void Debugger::loadUsage() { file fp; - if(fp.open({ interface->pathName, "debug/usage.cpu" }, file::mode::read)) { - fp.read(cpuUsage.data, min(cpuUsage.size, fp.size())); - fp.close(); + + //if cartridge image was modified after the usage files, + //then it is possible that the memory map has changed. + //will print invalidation message when files do not exist as well. + + if(file::timestamp(interface->fileName, file::time::modify) >= + file::timestamp({ interface->pathName, "debug/usage.cpu" }, file::time::modify) + ) { + print("CPU usage invalidated\n"); + } else { + if(fp.open({ interface->pathName, "debug/usage.cpu" }, file::mode::read)) { + fp.read(cpuUsage.data, min(cpuUsage.size, fp.size())); + fp.close(); + } } - if(fp.open({ interface->pathName, "debug/usage.apu" }, file::mode::read)) { - fp.read(apuUsage.data, min(apuUsage.size, fp.size())); - fp.close(); + + if(file::timestamp(interface->fileName, file::time::modify) >= + file::timestamp({ interface->pathName, "debug/usage.apu" }, file::time::modify) + ) { + print("APU usage invalidated\n"); + } else { + if(fp.open({ interface->pathName, "debug/usage.apu" }, file::mode::read)) { + fp.read(apuUsage.data, min(apuUsage.size, fp.size())); + fp.close(); + } } } diff --git a/bsnes/ui-debugger/main.cpp b/bsnes/ui-debugger/main.cpp index 2bb7f1868..8d5d22e1a 100755 --- a/bsnes/ui-debugger/main.cpp +++ b/bsnes/ui-debugger/main.cpp @@ -13,9 +13,9 @@ Application::Application(int argc, char **argv) { unused = ::userpath(path); userpath = path; if(Intrinsics::platform() == Intrinsics::Platform::Windows) { - userpath.append("bsnes/"); + userpath.append("laevateinn/"); } else { - userpath.append(".config/bsnes/"); + userpath.append(".config/laevateinn/"); } mkdir(userpath, 0755); } @@ -30,27 +30,40 @@ Application::Application(int argc, char **argv) { monospaceFont = "Liberation Mono, 8"; } + settings = new Settings; + settings->load(); + 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(!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, settings->folderpath); 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; + //save path for later; remove cartridge name from path + settings->folderpath = foldername; + settings->folderpath.rtrim<1>("/"); + settings->folderpath = dir(settings->folderpath); + interface = new Interface; debugger = new Debugger; tracer = new Tracer; + windowManager = new WindowManager; consoleWindow = new ConsoleWindow; aboutWindow = new AboutWindow; videoWindow = new VideoWindow; cpuDebugger = new CPUDebugger; + cpuRegisterEditor = new CPURegisterEditor; smpDebugger = new SMPDebugger; + smpRegisterEditor = new SMPRegisterEditor; memoryEditor = new MemoryEditor; breakpointEditor = new BreakpointEditor; + vramViewer = new VRAMViewer; + windowManager->loadGeometry(); consoleWindow->setVisible(); videoWindow->setVisible(); consoleWindow->setFocused(); @@ -59,13 +72,14 @@ Application::Application(int argc, char **argv) { audio.driver("None"); audio.init(); } - audio.set(Audio::Synchronize, true); + audio.set(Audio::Synchronize, settings->synchronizeAudio); audio.set(Audio::Frequency, 32000u); if(interface->loadCartridge(filename) == false) return; cpuDebugger->updateDisassembly(); smpDebugger->updateDisassembly(); memoryEditor->selectSource(); + vramViewer->updateTiles(); while(quit == false) { OS::processEvents(); @@ -73,20 +87,26 @@ Application::Application(int argc, char **argv) { } interface->saveMemory(); + windowManager->saveGeometry(); + settings->save(); } Application::~Application() { - exit(0); + delete vramViewer; delete breakpointEditor; delete memoryEditor; + delete smpRegisterEditor; delete smpDebugger; + delete cpuRegisterEditor; delete cpuDebugger; delete videoWindow; delete aboutWindow; delete consoleWindow; + delete windowManager; delete tracer; delete debugger; delete interface; + delete settings; } int main(int argc, char **argv) { diff --git a/bsnes/ui-debugger/memory/memory.cpp b/bsnes/ui-debugger/memory/memory.cpp index 5633b90a7..9b1515996 100755 --- a/bsnes/ui-debugger/memory/memory.cpp +++ b/bsnes/ui-debugger/memory/memory.cpp @@ -3,7 +3,7 @@ MemoryEditor *memoryEditor = nullptr; MemoryEditor::MemoryEditor() { setTitle("Memory Editor"); - setGeometry({640, 64, 485, 255}); + setGeometry({128, 128, 485, 255}); gotoLabel.setText("Goto:"); gotoAddress.setFont(application->monospaceFont); @@ -38,6 +38,8 @@ MemoryEditor::MemoryEditor() { exportMemory.onActivate = { &MemoryEditor::exportMemoryToDisk, this }; editor.onRead = { &MemoryEditor::read, this }; editor.onWrite = { &MemoryEditor::write, this }; + + windowManager->append(this, "MemoryEditor"); } uint8_t MemoryEditor::read(unsigned addr) { diff --git a/bsnes/ui-debugger/settings/settings.cpp b/bsnes/ui-debugger/settings/settings.cpp new file mode 100755 index 000000000..3caf85412 --- /dev/null +++ b/bsnes/ui-debugger/settings/settings.cpp @@ -0,0 +1,15 @@ +#include "../base.hpp" +Settings *settings = nullptr; + +void Settings::load() { + config.append(folderpath, "folderpath"); + config.append(synchronizeAudio = true, "synchronizeAudio"); + config.append(muteAudio = false, "muteAudio"); + + config.load({ application->userpath, "settings.cfg" }); + config.save({ application->userpath, "settings.cfg" }); +} + +void Settings::save() { + config.save({ application->userpath, "settings.cfg" }); +} diff --git a/bsnes/ui-debugger/settings/settings.hpp b/bsnes/ui-debugger/settings/settings.hpp new file mode 100755 index 000000000..6bba26c8a --- /dev/null +++ b/bsnes/ui-debugger/settings/settings.hpp @@ -0,0 +1,11 @@ +struct Settings { + configuration config; + string folderpath; + bool synchronizeAudio; + bool muteAudio; + + void load(); + void save(); +}; + +extern Settings *settings; diff --git a/bsnes/ui-debugger/smp/registers.cpp b/bsnes/ui-debugger/smp/registers.cpp new file mode 100755 index 000000000..9a5c95b9c --- /dev/null +++ b/bsnes/ui-debugger/smp/registers.cpp @@ -0,0 +1,88 @@ +SMPRegisterEditor *smpRegisterEditor = nullptr; + +SMPRegisterEditor::SMPRegisterEditor() { + setTitle("SMP Register Editor"); + + layout.setMargin(5); + regALabel.setText("A:"); + regAValue.setFont(application->monospaceFont); + regXLabel.setText("X:"); + regXValue.setFont(application->monospaceFont); + regYLabel.setText("Y:"); + regYValue.setFont(application->monospaceFont); + regSLabel.setText("S:01"); + regSValue.setFont(application->monospaceFont); + flagN.setText("N"); + flagV.setText("V"); + flagP.setText("P"); + flagB.setText("B"); + flagH.setText("H"); + flagI.setText("I"); + flagZ.setText("Z"); + flagC.setText("C"); + update.setText("Update"); + + loadRegisters(); + + layout.append(primaryLayout, {~0, 0}, 5); + primaryLayout.append(regALabel, {0, 0}, 5); + primaryLayout.append(regAValue, {0, 0}, 5); + primaryLayout.append(regXLabel, {0, 0}, 5); + primaryLayout.append(regXValue, {0, 0}, 5); + primaryLayout.append(regYLabel, {0, 0}, 5); + primaryLayout.append(regYValue, {0, 0}, 5); + primaryLayout.append(regSLabel, {0, 0}, 5); + primaryLayout.append(regSValue, {0, 0}); + layout.append(secondaryLayout, {~0, 0}, 5); + secondaryLayout.append(flagN, {0, 0}, 5); + secondaryLayout.append(flagV, {0, 0}, 5); + secondaryLayout.append(flagP, {0, 0}, 5); + secondaryLayout.append(flagB, {0, 0}, 5); + secondaryLayout.append(flagH, {0, 0}, 5); + secondaryLayout.append(flagI, {0, 0}, 5); + secondaryLayout.append(flagZ, {0, 0}, 5); + secondaryLayout.append(flagC, {0, 0}); + layout.append(tertiaryLayout, {~0, 0}); + tertiaryLayout.append(spacer, {~0, 0}); + tertiaryLayout.append(update, {80, 0}); + append(layout); + + update.onActivate = [&] { + saveRegisters(); + smpDebugger->updateDisassembly(); + setVisible(false); + }; + + setGeometry({{128, 128}, layout.minimumGeometry().size()}); + windowManager->append(this, "SMPRegisterEditor"); +} + +void SMPRegisterEditor::loadRegisters() { + regAValue.setText(hex<2>(SNES::smp.regs.a)); + regXValue.setText(hex<2>(SNES::smp.regs.x)); + regYValue.setText(hex<2>(SNES::smp.regs.y)); + regSValue.setText(hex<2>(SNES::smp.regs.s)); + flagN.setChecked(SNES::smp.regs.p.n); + flagV.setChecked(SNES::smp.regs.p.v); + flagP.setChecked(SNES::smp.regs.p.p); + flagB.setChecked(SNES::smp.regs.p.b); + flagH.setChecked(SNES::smp.regs.p.h); + flagI.setChecked(SNES::smp.regs.p.i); + flagZ.setChecked(SNES::smp.regs.p.z); + flagC.setChecked(SNES::smp.regs.p.c); +} + +void SMPRegisterEditor::saveRegisters() { + SNES::smp.regs.a = hex(regAValue.text()); + SNES::smp.regs.x = hex(regXValue.text()); + SNES::smp.regs.y = hex(regYValue.text()); + SNES::smp.regs.s = hex(regSValue.text()); + SNES::smp.regs.p.n = flagN.checked(); + SNES::smp.regs.p.v = flagV.checked(); + SNES::smp.regs.p.p = flagP.checked(); + SNES::smp.regs.p.b = flagB.checked(); + SNES::smp.regs.p.h = flagH.checked(); + SNES::smp.regs.p.i = flagI.checked(); + SNES::smp.regs.p.z = flagZ.checked(); + SNES::smp.regs.p.c = flagC.checked(); +} diff --git a/bsnes/ui-debugger/smp/smp.cpp b/bsnes/ui-debugger/smp/smp.cpp index 627aff963..70f1efb81 100755 --- a/bsnes/ui-debugger/smp/smp.cpp +++ b/bsnes/ui-debugger/smp/smp.cpp @@ -1,6 +1,8 @@ #include "../base.hpp" SMPDebugger *smpDebugger = nullptr; +#include "registers.cpp" + uint8 SMPDebugger::read(uint16 addr) { if((addr & 0xfff0) == 0x00f0) return ~0; //$00f0-00ff MMIO return SNES::smp.op_busread(addr); @@ -71,7 +73,7 @@ SMPDebugger::SMPDebugger() { opcodePC = 0xffc0; setTitle("SMP Debugger"); - setGeometry({800, 800, 350, 255}); + setGeometry({128, 128, 350, 255}); layout.setMargin(5); stepInto.setText("Step Into"); @@ -96,4 +98,11 @@ SMPDebugger::SMPDebugger() { }; update.onActivate = { &SMPDebugger::updateDisassembly, this }; + + registers.onActivate = [&] { + smpRegisterEditor->loadRegisters(); + smpRegisterEditor->setVisible(); + }; + + windowManager->append(this, "SMPDebugger"); } diff --git a/bsnes/ui-debugger/smp/smp.hpp b/bsnes/ui-debugger/smp/smp.hpp index 653ed2e1f..146cd2e16 100755 --- a/bsnes/ui-debugger/smp/smp.hpp +++ b/bsnes/ui-debugger/smp/smp.hpp @@ -8,7 +8,7 @@ struct SMPDebugger : Window { CheckBox autoUpdate; Button update; TextEdit disassembly; - Label registers; + Button registers; uint8 read(uint16 addr); void write(uint16 addr, uint8 data); @@ -18,4 +18,34 @@ struct SMPDebugger : Window { SMPDebugger(); }; +struct SMPRegisterEditor : Window { + VerticalLayout layout; + HorizontalLayout primaryLayout; + Label regALabel; + LineEdit regAValue; + Label regXLabel; + LineEdit regXValue; + Label regYLabel; + LineEdit regYValue; + Label regSLabel; + LineEdit regSValue; + HorizontalLayout secondaryLayout; + CheckBox flagN; + CheckBox flagV; + CheckBox flagP; + CheckBox flagB; + CheckBox flagH; + CheckBox flagI; + CheckBox flagZ; + CheckBox flagC; + HorizontalLayout tertiaryLayout; + Widget spacer; + Button update; + + void loadRegisters(); + void saveRegisters(); + SMPRegisterEditor(); +}; + extern SMPDebugger *smpDebugger; +extern SMPRegisterEditor *smpRegisterEditor; diff --git a/bsnes/ui-debugger/video/video.cpp b/bsnes/ui-debugger/video/video.cpp index c74f33273..4f6dfd6e3 100755 --- a/bsnes/ui-debugger/video/video.cpp +++ b/bsnes/ui-debugger/video/video.cpp @@ -41,4 +41,6 @@ VideoWindow::VideoWindow() { "0x", hex<4>((b << 10) + (g << 5) + (r << 0)) }); }; + + windowManager->append(this, "VideoWindow"); } diff --git a/bsnes/ui-debugger/vram/vram.cpp b/bsnes/ui-debugger/vram/vram.cpp new file mode 100755 index 000000000..811f2f876 --- /dev/null +++ b/bsnes/ui-debugger/vram/vram.cpp @@ -0,0 +1,126 @@ +#include "../base.hpp" +VRAMViewer *vramViewer = nullptr; + +VRAMViewer::VRAMViewer() { + setTitle("VRAM Viewer"); + setStatusFont(application->proportionalFontBold); + setStatusVisible(); + + layout.setMargin(5); + modeLabel.setText("Mode:"); + modeSelection.append("2bpp", "4bpp", "8bpp"); + autoUpdate.setText("Auto"); + update.setText("Update"); + canvas.setSize({512, 512}); + + layout.append(controlLayout, {~0, 0}, 5); + controlLayout.append(modeLabel, {0, 0}, 5); + controlLayout.append(modeSelection, {0, 0}, 5); + controlLayout.append(spacer, {~0, 0}); + controlLayout.append(autoUpdate, {0, 0}, 5); + controlLayout.append(update, {80, 0}); + layout.append(canvas, {512, 512}); + append(layout); + + modeSelection.onChange = update.onActivate = { &VRAMViewer::updateTiles, this }; + + canvas.onMouseLeave = [&] { setStatusText(""); }; + canvas.onMouseMove = [&](Position position) { + unsigned x = position.x, y = position.y, mode = modeSelection.selection(); + if(x >= 256 && mode >= 2) return setStatusText(""); + if(y >= 256 && mode >= 1) return setStatusText(""); + string output = { x, ", ", y, ", " }; + x /= 8, y /= 8; + unsigned tile = 0; + if(mode == 0) tile = y * 64 + x; + if(mode == 1) tile = y * 64 + x; + if(mode == 2) tile = y * 32 + x; + output.append("Tile: 0x", hex<4>(tile), ", Address: 0x", hex<4>(tile * (16 << mode))); + setStatusText(output); + }; + + setGeometry({{128, 128}, layout.minimumGeometry().size()}); + windowManager->append(this, "VRAMViewer"); +} + +void VRAMViewer::updateTiles() { + uint32_t *dp = canvas.data(); + for(unsigned y = 0; y < 512; y++) { + for(unsigned x = 0; x < 512; x++) { + *dp++ = 0xff800000; + } + } + dp = canvas.data(); + const uint8_t *sp = SNES::ppu.vram; + + if(modeSelection.selection() == 0) { + for(unsigned tileY = 0; tileY < 64; tileY++) { + for(unsigned tileX = 0; tileX < 64; tileX++) { + for(unsigned y = 0; y < 8; y++) { + uint8_t d[] = { sp[0], sp[1] }; + for(unsigned x = 0; x < 8; x++) { + unsigned color = 0; + color += d[0] & 0x80 ? 1 : 0; + color += d[1] & 0x80 ? 2 : 0; + for(auto &b : d) b <<= 1; + color *= 0x55; + color = (255u << 24) + (color << 16) + (color << 8) + (color << 0); + dp[(tileY * 8 + y) * 512 + (tileX * 8 + x)] = color; + } + sp += 2; + } + } + } + } + + if(modeSelection.selection() == 1) { + for(unsigned tileY = 0; tileY < 32; tileY++) { + for(unsigned tileX = 0; tileX < 64; tileX++) { + for(unsigned y = 0; y < 8; y++) { + uint8_t d[] = { sp[0], sp[1], sp[16], sp[17] }; + for(unsigned x = 0; x < 8; x++) { + unsigned color = 0; + color += d[0] & 0x80 ? 1 : 0; + color += d[1] & 0x80 ? 2 : 0; + color += d[2] & 0x80 ? 4 : 0; + color += d[3] & 0x80 ? 8 : 0; + for(auto &b : d) b <<= 1; + color *= 0x11; + color = (255u << 24) + (color << 16) + (color << 8) + (color << 0); + dp[(tileY * 8 + y) * 512 + (tileX * 8 + x)] = color; + } + sp += 2; + } + sp += 16; + } + } + } + + if(modeSelection.selection() == 2) { + for(unsigned tileY = 0; tileY < 32; tileY++) { + for(unsigned tileX = 0; tileX < 32; tileX++) { + for(unsigned y = 0; y < 8; y++) { + uint8_t d[] = { sp[0], sp[1], sp[16], sp[17], sp[32], sp[33], sp[48], sp[49] }; + for(unsigned x = 0; x < 8; x++) { + unsigned color = 0; + color += d[0] & 0x80 ? 1 : 0; + color += d[1] & 0x80 ? 2 : 0; + color += d[2] & 0x80 ? 4 : 0; + color += d[3] & 0x80 ? 8 : 0; + color += d[4] & 0x80 ? 16 : 0; + color += d[5] & 0x80 ? 32 : 0; + color += d[6] & 0x80 ? 64 : 0; + color += d[7] & 0x80 ? 128 : 0; + for(auto &b : d) b <<= 1; + color = (255u << 24) + (color << 16) + (color << 8) + (color << 0); + dp[(tileY * 8 + y) * 512 + (tileX * 8 + x)] = color; + } + sp += 2; + } + sp += 48; + } + } + } + + canvas.update(); +} diff --git a/bsnes/ui-debugger/vram/vram.hpp b/bsnes/ui-debugger/vram/vram.hpp new file mode 100755 index 000000000..eb21af6ea --- /dev/null +++ b/bsnes/ui-debugger/vram/vram.hpp @@ -0,0 +1,15 @@ +struct VRAMViewer : Window { + VerticalLayout layout; + HorizontalLayout controlLayout; + Label modeLabel; + ComboBox modeSelection; + Widget spacer; + CheckBox autoUpdate; + Button update; + Canvas canvas; + + void updateTiles(); + VRAMViewer(); +}; + +extern VRAMViewer *vramViewer; diff --git a/bsnes/ui-debugger/window/window.cpp b/bsnes/ui-debugger/window/window.cpp new file mode 100755 index 000000000..81ad9f76b --- /dev/null +++ b/bsnes/ui-debugger/window/window.cpp @@ -0,0 +1,25 @@ +#include "../base.hpp" +WindowManager *windowManager = nullptr; + +void WindowManager::append(Window *window, const string &name) { + windowList.append({ window, name, window->geometry().text() }); +} + +void WindowManager::loadGeometry() { + for(auto &window : windowList) { + config.append(window.geometry, window.name); + } + config.load({ application->userpath, "geometry.cfg" }); + config.save({ application->userpath, "geometry.cfg" }); + for(auto &window : windowList) { + window.window->setGeometry(window.geometry); + } +} + +void WindowManager::saveGeometry() { + for(auto &window : windowList) { + window.geometry = window.window->geometry().text(); + window.window->setVisible(false); + } + config.save({ application->userpath, "geometry.cfg" }); +} diff --git a/bsnes/ui-debugger/window/window.hpp b/bsnes/ui-debugger/window/window.hpp new file mode 100755 index 000000000..590beeb00 --- /dev/null +++ b/bsnes/ui-debugger/window/window.hpp @@ -0,0 +1,15 @@ +struct WindowManager { + struct WindowItem { + Window *window; + string name; + string geometry; + }; + vector windowList; + configuration config; + + void append(Window *window, const string &name); + void loadGeometry(); + void saveGeometry(); +}; + +extern WindowManager *windowManager;