diff --git a/bsnes/base/base.hpp b/bsnes/base/base.hpp index 7fd70999..49bcbc68 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 9c8d3d22..d44e0596 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 f1378f0c..8189c01b 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 40374d1c..3709ba98 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 22439079..333f34a4 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 436abef1..26979af8 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 62bbc4b5..cef0e8e9 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 8373b121..a03efdbf 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 2d041324..7627746d 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 a37ef6b7..03bcc4fb 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 23392aa0..af798259 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 8f89e2e3..a03b5b64 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 00000000..496cd10f --- /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 2eab4e03..f341a4a8 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 6fff74e1..391e6214 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 a19fa771..bd2a2aa8 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 2bb7f186..8d5d22e1 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 5633b90a..9b151599 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 00000000..3caf8541 --- /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 00000000..6bba26c8 --- /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 00000000..9a5c95b9 --- /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 627aff96..70f1efb8 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 653ed2e1..146cd2e1 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 c74f3327..4f6dfd6e 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 00000000..811f2f87 --- /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 00000000..eb21af6e --- /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 00000000..81ad9f76 --- /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 00000000..590beeb0 --- /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;