mirror of https://github.com/bsnes-emu/bsnes.git
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.
This commit is contained in:
parent
82afd511fc
commit
0370229444
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BASE_HPP
|
#ifndef BASE_HPP
|
||||||
#define BASE_HPP
|
#define BASE_HPP
|
||||||
|
|
||||||
const char Version[] = "085.08";
|
const char Version[] = "085.09";
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
|
|
|
@ -68,6 +68,7 @@ struct Geometry {
|
||||||
Size size() const;
|
Size size() const;
|
||||||
nall::string text() const;
|
nall::string text() const;
|
||||||
inline Geometry() : x(0), y(0), width(0), height(0) {}
|
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) {}
|
inline Geometry(signed x, signed y, unsigned width, unsigned height) : x(x), y(y), width(width), height(height) {}
|
||||||
Geometry(const nall::string &text);
|
Geometry(const nall::string &text);
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,12 @@ void CPU::add_clocks(unsigned clocks) {
|
||||||
status.dram_refreshed = true;
|
status.dram_refreshed = true;
|
||||||
add_clocks(40);
|
add_clocks(40);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(DEBUGGER)
|
||||||
|
synchronize_smp();
|
||||||
|
synchronize_ppu();
|
||||||
|
synchronize_coprocessors();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
//called by ppu.tick() when Hcounter=0
|
//called by ppu.tick() when Hcounter=0
|
||||||
|
@ -35,8 +41,8 @@ void CPU::scanline() {
|
||||||
status.line_clocks = lineclocks();
|
status.line_clocks = lineclocks();
|
||||||
|
|
||||||
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
||||||
synchronize_ppu();
|
|
||||||
synchronize_smp();
|
synchronize_smp();
|
||||||
|
synchronize_ppu();
|
||||||
synchronize_coprocessors();
|
synchronize_coprocessors();
|
||||||
system.scanline();
|
system.scanline();
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,13 @@ void SMP::add_clocks(unsigned clocks) {
|
||||||
step(clocks);
|
step(clocks);
|
||||||
synchronize_dsp();
|
synchronize_dsp();
|
||||||
|
|
||||||
|
#if defined(DEBUGGER)
|
||||||
|
synchronize_cpu();
|
||||||
|
#else
|
||||||
//forcefully sync S-SMP to S-CPU in case chips are not communicating
|
//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
|
//sync if S-SMP is more than 24 samples ahead of S-CPU
|
||||||
if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu();
|
if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void SMP::cycle_edge() {
|
void SMP::cycle_edge() {
|
||||||
|
|
|
@ -3,8 +3,8 @@ options += debugger
|
||||||
include $(snes)/Makefile
|
include $(snes)/Makefile
|
||||||
name := laevateinn
|
name := laevateinn
|
||||||
|
|
||||||
ui_objects := ui-main ui-interface ui-debugger ui-tracer ui-console
|
ui_objects := ui-main ui-settings ui-interface ui-debugger ui-tracer ui-window
|
||||||
ui_objects += ui-video ui-cpu ui-smp ui-memory ui-breakpoint
|
ui_objects += ui-console ui-video ui-cpu ui-smp ui-memory ui-breakpoint ui-vram
|
||||||
ui_objects += phoenix ruby
|
ui_objects += phoenix ruby
|
||||||
ui_objects += $(if $(call streq,$(platform),win),resource)
|
ui_objects += $(if $(call streq,$(platform),win),resource)
|
||||||
|
|
||||||
|
@ -30,15 +30,18 @@ objects := $(ui_objects) $(objects)
|
||||||
objects := $(patsubst %,obj/%.o,$(objects))
|
objects := $(patsubst %,obj/%.o,$(objects))
|
||||||
|
|
||||||
obj/ui-main.o: $(ui)/main.cpp $(call rwildcard,$(ui)/)
|
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-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/*)
|
||||||
obj/ui-debugger.o: $(ui)/debugger/debugger.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-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-console.o: $(ui)/console/console.cpp $(call rwildcard,$(ui)/*)
|
||||||
obj/ui-cpu.o: $(ui)/cpu/cpu.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-smp.o: $(ui)/smp/smp.cpp $(call rwildcard,$(ui)/*)
|
||||||
obj/ui-video.o: $(ui)/video/video.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-memory.o: $(ui)/memory/memory.cpp $(call rwildcard,$(ui)/*)
|
||||||
obj/ui-breakpoint.o: $(ui)/breakpoint/breakpoint.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/*)
|
obj/ruby.o: ruby/ruby.cpp $(call rwildcard,ruby/*)
|
||||||
$(call compile,$(rubyflags))
|
$(call compile,$(rubyflags))
|
||||||
|
|
|
@ -18,15 +18,18 @@ using namespace phoenix;
|
||||||
#include <ruby/ruby.hpp>
|
#include <ruby/ruby.hpp>
|
||||||
using namespace ruby;
|
using namespace ruby;
|
||||||
|
|
||||||
|
#include "settings/settings.hpp"
|
||||||
#include "interface/interface.hpp"
|
#include "interface/interface.hpp"
|
||||||
#include "debugger/debugger.hpp"
|
#include "debugger/debugger.hpp"
|
||||||
#include "tracer/tracer.hpp"
|
#include "tracer/tracer.hpp"
|
||||||
|
#include "window/window.hpp"
|
||||||
#include "console/console.hpp"
|
#include "console/console.hpp"
|
||||||
#include "video/video.hpp"
|
#include "video/video.hpp"
|
||||||
#include "cpu/cpu.hpp"
|
#include "cpu/cpu.hpp"
|
||||||
#include "smp/smp.hpp"
|
#include "smp/smp.hpp"
|
||||||
#include "memory/memory.hpp"
|
#include "memory/memory.hpp"
|
||||||
#include "breakpoint/breakpoint.hpp"
|
#include "breakpoint/breakpoint.hpp"
|
||||||
|
#include "vram/vram.hpp"
|
||||||
extern uint8_t laevateinnLogo[121905];
|
extern uint8_t laevateinnLogo[121905];
|
||||||
|
|
||||||
struct Application {
|
struct Application {
|
||||||
|
|
|
@ -32,8 +32,10 @@ BreakpointEditor::BreakpointEditor() {
|
||||||
for(auto &bp : breakpointEntry) layout.append(bp, {0, 0}, 5);
|
for(auto &bp : breakpointEntry) layout.append(bp, {0, 0}, 5);
|
||||||
append(layout);
|
append(layout);
|
||||||
|
|
||||||
setGeometry({800, 600, layout.minimumGeometry().width, layout.minimumGeometry().height - 5});
|
setGeometry({128, 128, layout.minimumGeometry().width, layout.minimumGeometry().height - 5});
|
||||||
synchronize();
|
synchronize();
|
||||||
|
|
||||||
|
windowManager->append(this, "BreakpointEditor");
|
||||||
}
|
}
|
||||||
|
|
||||||
//enable checkbox toggled on one of the five BreakpointEntry items:
|
//enable checkbox toggled on one of the five BreakpointEntry items:
|
||||||
|
|
|
@ -27,5 +27,6 @@ AboutWindow::AboutWindow() {
|
||||||
canvas.setImage(logo);
|
canvas.setImage(logo);
|
||||||
canvas.update();
|
canvas.update();
|
||||||
|
|
||||||
setGeometry({800, 64, layout.minimumGeometry().width, layout.minimumGeometry().height});
|
setGeometry({128, 128, layout.minimumGeometry().width, layout.minimumGeometry().height});
|
||||||
|
windowManager->append(this, "AboutWindow");
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@ ConsoleWindow *consoleWindow = nullptr;
|
||||||
|
|
||||||
ConsoleWindow::ConsoleWindow() {
|
ConsoleWindow::ConsoleWindow() {
|
||||||
setTitle({"Console - Laevateinn v", Version});
|
setTitle({"Console - Laevateinn v", Version});
|
||||||
setGeometry({64, 650, 640, 400});
|
setGeometry({64, 640, 640, 400});
|
||||||
setMenuVisible();
|
setMenuVisible();
|
||||||
|
|
||||||
menuEmulation.setText("&Emulation");
|
menuEmulation.setText("&Emulation");
|
||||||
|
@ -13,8 +13,9 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
menuEmulationPowerCycle.setText("Power Cycle");
|
menuEmulationPowerCycle.setText("Power Cycle");
|
||||||
menuEmulationReset.setText("Reset");
|
menuEmulationReset.setText("Reset");
|
||||||
menuEmulationSynchronizeAudio.setText("Synchronize Audio");
|
menuEmulationSynchronizeAudio.setText("Synchronize Audio");
|
||||||
menuEmulationSynchronizeAudio.setChecked();
|
menuEmulationSynchronizeAudio.setChecked(settings->synchronizeAudio);
|
||||||
menuEmulationMuteAudio.setText("Mute Audio");
|
menuEmulationMuteAudio.setText("Mute Audio");
|
||||||
|
menuEmulationMuteAudio.setChecked(settings->muteAudio);
|
||||||
menuEmulation.append(menuEmulationReloadCartridge, menuEmulationPowerCycle, menuEmulationReset,
|
menuEmulation.append(menuEmulationReloadCartridge, menuEmulationPowerCycle, menuEmulationReset,
|
||||||
menuEmulationSeparator, menuEmulationSynchronizeAudio, menuEmulationMuteAudio);
|
menuEmulationSeparator, menuEmulationSynchronizeAudio, menuEmulationMuteAudio);
|
||||||
append(menuEmulation);
|
append(menuEmulation);
|
||||||
|
@ -41,8 +42,10 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
menuWindowsSMPDebugger.setText("SMP Debugger");
|
menuWindowsSMPDebugger.setText("SMP Debugger");
|
||||||
menuWindowsMemoryEditor.setText("Memory Editor");
|
menuWindowsMemoryEditor.setText("Memory Editor");
|
||||||
menuWindowsBreakpointEditor.setText("Breakpoint Editor");
|
menuWindowsBreakpointEditor.setText("Breakpoint Editor");
|
||||||
|
menuWindowsVRAMViewer.setText("VRAM Viewer");
|
||||||
menuWindows.append(menuWindowsVideoWindow, menuWindowsSeparator1, menuWindowsCPUDebugger,
|
menuWindows.append(menuWindowsVideoWindow, menuWindowsSeparator1, menuWindowsCPUDebugger,
|
||||||
menuWindowsSMPDebugger, menuWindowsSeparator2, menuWindowsMemoryEditor, menuWindowsBreakpointEditor);
|
menuWindowsSMPDebugger, menuWindowsSeparator2, menuWindowsMemoryEditor, menuWindowsBreakpointEditor,
|
||||||
|
menuWindowsVRAMViewer);
|
||||||
append(menuWindows);
|
append(menuWindows);
|
||||||
|
|
||||||
menuState.setText("&State");
|
menuState.setText("&State");
|
||||||
|
@ -96,7 +99,11 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
};
|
};
|
||||||
|
|
||||||
menuEmulationSynchronizeAudio.onToggle = [&] {
|
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(); };
|
menuDebugCPU.onToggle = [&] { debugger->debug.cpu = menuDebugCPU.checked(); };
|
||||||
|
@ -135,6 +142,11 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
breakpointEditor->setFocused();
|
breakpointEditor->setFocused();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
menuWindowsVRAMViewer.onActivate = [&] {
|
||||||
|
vramViewer->setVisible();
|
||||||
|
vramViewer->setFocused();
|
||||||
|
};
|
||||||
|
|
||||||
menuStateSave1.onActivate = [&] { interface->saveState(1); };
|
menuStateSave1.onActivate = [&] { interface->saveState(1); };
|
||||||
menuStateSave2.onActivate = [&] { interface->saveState(2); };
|
menuStateSave2.onActivate = [&] { interface->saveState(2); };
|
||||||
menuStateSave3.onActivate = [&] { interface->saveState(3); };
|
menuStateSave3.onActivate = [&] { interface->saveState(3); };
|
||||||
|
@ -150,8 +162,12 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
menuHelpAbout.onActivate = [&] { aboutWindow->setVisible(); };
|
menuHelpAbout.onActivate = [&] { aboutWindow->setVisible(); };
|
||||||
|
|
||||||
runButton.onActivate = [&] {
|
runButton.onActivate = [&] {
|
||||||
if(debugger->paused) debugger->resume();
|
if(debugger->paused) {
|
||||||
else debugger->suspend();
|
print("\n");
|
||||||
|
debugger->resume();
|
||||||
|
} else {
|
||||||
|
debugger->suspend();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
stepButton.onActivate = [&] {
|
stepButton.onActivate = [&] {
|
||||||
|
@ -162,6 +178,8 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
clearButton.onActivate = [&] {
|
clearButton.onActivate = [&] {
|
||||||
console.setText("");
|
console.setText("");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
windowManager->append(this, "ConsoleWindow");
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleWindow::print(const string &text) {
|
void ConsoleWindow::print(const string &text) {
|
||||||
|
|
|
@ -24,6 +24,7 @@ struct ConsoleWindow : Window {
|
||||||
Separator menuWindowsSeparator2;
|
Separator menuWindowsSeparator2;
|
||||||
Item menuWindowsMemoryEditor;
|
Item menuWindowsMemoryEditor;
|
||||||
Item menuWindowsBreakpointEditor;
|
Item menuWindowsBreakpointEditor;
|
||||||
|
Item menuWindowsVRAMViewer;
|
||||||
|
|
||||||
Menu menuState;
|
Menu menuState;
|
||||||
Item menuStateSave1;
|
Item menuStateSave1;
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
#include "../base.hpp"
|
#include "../base.hpp"
|
||||||
CPUDebugger *cpuDebugger = nullptr;
|
CPUDebugger *cpuDebugger = nullptr;
|
||||||
|
|
||||||
|
#include "registers.cpp"
|
||||||
|
|
||||||
uint24 CPUDebugger::mirror(uint24 addr) {
|
uint24 CPUDebugger::mirror(uint24 addr) {
|
||||||
if((addr & 0x40e000) == 0x0000) addr = 0x7e0000 | (addr & 0x1fff); //$00-3f:80-bf:0000-1fff WRAM
|
if((addr & 0x40e000) == 0x0000) addr = 0x7e0000 | (addr & 0x1fff); //$00-3f:80-bf:0000-1fff WRAM
|
||||||
return addr;
|
return addr;
|
||||||
|
@ -109,7 +111,7 @@ CPUDebugger::CPUDebugger() {
|
||||||
opcodePC = 0x008000;
|
opcodePC = 0x008000;
|
||||||
|
|
||||||
setTitle("CPU Debugger");
|
setTitle("CPU Debugger");
|
||||||
setGeometry({800, 64, 350, 255});
|
setGeometry({128, 128, 350, 255});
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
stepInto.setText("Step Into");
|
stepInto.setText("Step Into");
|
||||||
|
@ -148,4 +150,11 @@ CPUDebugger::CPUDebugger() {
|
||||||
};
|
};
|
||||||
|
|
||||||
update.onActivate = { &CPUDebugger::updateDisassembly, this };
|
update.onActivate = { &CPUDebugger::updateDisassembly, this };
|
||||||
|
|
||||||
|
registers.onActivate = [&] {
|
||||||
|
cpuRegisterEditor->loadRegisters();
|
||||||
|
cpuRegisterEditor->setVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
windowManager->append(this, "CPUDebugger");
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ struct CPUDebugger : Window {
|
||||||
CheckBox autoUpdate;
|
CheckBox autoUpdate;
|
||||||
Button update;
|
Button update;
|
||||||
TextEdit disassembly;
|
TextEdit disassembly;
|
||||||
Label registers;
|
Button registers;
|
||||||
|
|
||||||
uint24 mirror(uint24 addr);
|
uint24 mirror(uint24 addr);
|
||||||
uint8_t read(uint24 addr);
|
uint8_t read(uint24 addr);
|
||||||
|
@ -21,4 +21,39 @@ struct CPUDebugger : Window {
|
||||||
CPUDebugger();
|
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 CPUDebugger *cpuDebugger;
|
||||||
|
extern CPURegisterEditor *cpuRegisterEditor;
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ void Debugger::run() {
|
||||||
if(cpuDebugger->autoUpdate.checked()) cpuDebugger->updateDisassembly();
|
if(cpuDebugger->autoUpdate.checked()) cpuDebugger->updateDisassembly();
|
||||||
if(smpDebugger->autoUpdate.checked()) smpDebugger->updateDisassembly();
|
if(smpDebugger->autoUpdate.checked()) smpDebugger->updateDisassembly();
|
||||||
if(memoryEditor->autoUpdate.checked()) memoryEditor->updateView();
|
if(memoryEditor->autoUpdate.checked()) memoryEditor->updateView();
|
||||||
|
if(vramViewer->autoUpdate.checked()) vramViewer->updateTiles();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::echo(const string &text) {
|
void Debugger::echo(const string &text) {
|
||||||
|
|
|
@ -60,25 +60,15 @@ void Debugger::cpu_op_write(uint24 addr, uint8 data) {
|
||||||
|
|
||||||
void Debugger::cpu_op_nmi() {
|
void Debugger::cpu_op_nmi() {
|
||||||
if(flags.cpu.nmi) {
|
if(flags.cpu.nmi) {
|
||||||
char text[512];
|
print("CPU NMI\n"); //, text, "\n");
|
||||||
SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC = SNES::cpu.regs.pc);
|
flags.cpu.stepInto = true;
|
||||||
print("CPU NMI\n", text, "\n");
|
|
||||||
|
|
||||||
cpuDebugger->updateDisassembly();
|
|
||||||
suspend();
|
|
||||||
SNES::scheduler.debug();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::cpu_op_irq() {
|
void Debugger::cpu_op_irq() {
|
||||||
if(flags.cpu.irq) {
|
if(flags.cpu.irq) {
|
||||||
char text[512];
|
print("CPU IRQ\n");
|
||||||
SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC = SNES::cpu.regs.pc);
|
flags.cpu.stepInto = true;
|
||||||
print("CPU IRQ\n", text, "\n");
|
|
||||||
|
|
||||||
cpuDebugger->updateDisassembly();
|
|
||||||
suspend();
|
|
||||||
SNES::scheduler.debug();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,13 +15,31 @@ Usage::~Usage() {
|
||||||
|
|
||||||
void Debugger::loadUsage() {
|
void Debugger::loadUsage() {
|
||||||
file fp;
|
file fp;
|
||||||
if(fp.open({ interface->pathName, "debug/usage.cpu" }, file::mode::read)) {
|
|
||||||
fp.read(cpuUsage.data, min(cpuUsage.size, fp.size()));
|
//if cartridge image was modified after the usage files,
|
||||||
fp.close();
|
//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()));
|
if(file::timestamp(interface->fileName, file::time::modify) >=
|
||||||
fp.close();
|
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();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ Application::Application(int argc, char **argv) {
|
||||||
unused = ::userpath(path);
|
unused = ::userpath(path);
|
||||||
userpath = path;
|
userpath = path;
|
||||||
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
|
if(Intrinsics::platform() == Intrinsics::Platform::Windows) {
|
||||||
userpath.append("bsnes/");
|
userpath.append("laevateinn/");
|
||||||
} else {
|
} else {
|
||||||
userpath.append(".config/bsnes/");
|
userpath.append(".config/laevateinn/");
|
||||||
}
|
}
|
||||||
mkdir(userpath, 0755);
|
mkdir(userpath, 0755);
|
||||||
}
|
}
|
||||||
|
@ -30,27 +30,40 @@ Application::Application(int argc, char **argv) {
|
||||||
monospaceFont = "Liberation Mono, 8";
|
monospaceFont = "Liberation Mono, 8";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
settings = new Settings;
|
||||||
|
settings->load();
|
||||||
|
|
||||||
string foldername;
|
string foldername;
|
||||||
if(argc >= 2) foldername = argv[1];
|
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 = "/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 = DialogWindow::folderSelect(Window::None, settings->folderpath);
|
||||||
if(!foldername.endswith(".sfc/")) return;
|
if(!foldername.endswith(".sfc/")) return;
|
||||||
lstring contents = directory::files(foldername, "*.sfc");
|
lstring contents = directory::files(foldername, "*.sfc");
|
||||||
if(contents.size() != 1) return;
|
if(contents.size() != 1) return;
|
||||||
string filename = { foldername, contents[0] };
|
string filename = { foldername, contents[0] };
|
||||||
if(!file::exists(filename)) return;
|
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;
|
interface = new Interface;
|
||||||
debugger = new Debugger;
|
debugger = new Debugger;
|
||||||
tracer = new Tracer;
|
tracer = new Tracer;
|
||||||
|
windowManager = new WindowManager;
|
||||||
consoleWindow = new ConsoleWindow;
|
consoleWindow = new ConsoleWindow;
|
||||||
aboutWindow = new AboutWindow;
|
aboutWindow = new AboutWindow;
|
||||||
videoWindow = new VideoWindow;
|
videoWindow = new VideoWindow;
|
||||||
cpuDebugger = new CPUDebugger;
|
cpuDebugger = new CPUDebugger;
|
||||||
|
cpuRegisterEditor = new CPURegisterEditor;
|
||||||
smpDebugger = new SMPDebugger;
|
smpDebugger = new SMPDebugger;
|
||||||
|
smpRegisterEditor = new SMPRegisterEditor;
|
||||||
memoryEditor = new MemoryEditor;
|
memoryEditor = new MemoryEditor;
|
||||||
breakpointEditor = new BreakpointEditor;
|
breakpointEditor = new BreakpointEditor;
|
||||||
|
vramViewer = new VRAMViewer;
|
||||||
|
|
||||||
|
windowManager->loadGeometry();
|
||||||
consoleWindow->setVisible();
|
consoleWindow->setVisible();
|
||||||
videoWindow->setVisible();
|
videoWindow->setVisible();
|
||||||
consoleWindow->setFocused();
|
consoleWindow->setFocused();
|
||||||
|
@ -59,13 +72,14 @@ Application::Application(int argc, char **argv) {
|
||||||
audio.driver("None");
|
audio.driver("None");
|
||||||
audio.init();
|
audio.init();
|
||||||
}
|
}
|
||||||
audio.set(Audio::Synchronize, true);
|
audio.set(Audio::Synchronize, settings->synchronizeAudio);
|
||||||
audio.set(Audio::Frequency, 32000u);
|
audio.set(Audio::Frequency, 32000u);
|
||||||
|
|
||||||
if(interface->loadCartridge(filename) == false) return;
|
if(interface->loadCartridge(filename) == false) return;
|
||||||
cpuDebugger->updateDisassembly();
|
cpuDebugger->updateDisassembly();
|
||||||
smpDebugger->updateDisassembly();
|
smpDebugger->updateDisassembly();
|
||||||
memoryEditor->selectSource();
|
memoryEditor->selectSource();
|
||||||
|
vramViewer->updateTiles();
|
||||||
|
|
||||||
while(quit == false) {
|
while(quit == false) {
|
||||||
OS::processEvents();
|
OS::processEvents();
|
||||||
|
@ -73,20 +87,26 @@ Application::Application(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
interface->saveMemory();
|
interface->saveMemory();
|
||||||
|
windowManager->saveGeometry();
|
||||||
|
settings->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
exit(0);
|
delete vramViewer;
|
||||||
delete breakpointEditor;
|
delete breakpointEditor;
|
||||||
delete memoryEditor;
|
delete memoryEditor;
|
||||||
|
delete smpRegisterEditor;
|
||||||
delete smpDebugger;
|
delete smpDebugger;
|
||||||
|
delete cpuRegisterEditor;
|
||||||
delete cpuDebugger;
|
delete cpuDebugger;
|
||||||
delete videoWindow;
|
delete videoWindow;
|
||||||
delete aboutWindow;
|
delete aboutWindow;
|
||||||
delete consoleWindow;
|
delete consoleWindow;
|
||||||
|
delete windowManager;
|
||||||
delete tracer;
|
delete tracer;
|
||||||
delete debugger;
|
delete debugger;
|
||||||
delete interface;
|
delete interface;
|
||||||
|
delete settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv) {
|
int main(int argc, char **argv) {
|
||||||
|
|
|
@ -3,7 +3,7 @@ MemoryEditor *memoryEditor = nullptr;
|
||||||
|
|
||||||
MemoryEditor::MemoryEditor() {
|
MemoryEditor::MemoryEditor() {
|
||||||
setTitle("Memory Editor");
|
setTitle("Memory Editor");
|
||||||
setGeometry({640, 64, 485, 255});
|
setGeometry({128, 128, 485, 255});
|
||||||
|
|
||||||
gotoLabel.setText("Goto:");
|
gotoLabel.setText("Goto:");
|
||||||
gotoAddress.setFont(application->monospaceFont);
|
gotoAddress.setFont(application->monospaceFont);
|
||||||
|
@ -38,6 +38,8 @@ MemoryEditor::MemoryEditor() {
|
||||||
exportMemory.onActivate = { &MemoryEditor::exportMemoryToDisk, this };
|
exportMemory.onActivate = { &MemoryEditor::exportMemoryToDisk, this };
|
||||||
editor.onRead = { &MemoryEditor::read, this };
|
editor.onRead = { &MemoryEditor::read, this };
|
||||||
editor.onWrite = { &MemoryEditor::write, this };
|
editor.onWrite = { &MemoryEditor::write, this };
|
||||||
|
|
||||||
|
windowManager->append(this, "MemoryEditor");
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MemoryEditor::read(unsigned addr) {
|
uint8_t MemoryEditor::read(unsigned addr) {
|
||||||
|
|
|
@ -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" });
|
||||||
|
}
|
|
@ -0,0 +1,11 @@
|
||||||
|
struct Settings {
|
||||||
|
configuration config;
|
||||||
|
string folderpath;
|
||||||
|
bool synchronizeAudio;
|
||||||
|
bool muteAudio;
|
||||||
|
|
||||||
|
void load();
|
||||||
|
void save();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern Settings *settings;
|
|
@ -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();
|
||||||
|
}
|
|
@ -1,6 +1,8 @@
|
||||||
#include "../base.hpp"
|
#include "../base.hpp"
|
||||||
SMPDebugger *smpDebugger = nullptr;
|
SMPDebugger *smpDebugger = nullptr;
|
||||||
|
|
||||||
|
#include "registers.cpp"
|
||||||
|
|
||||||
uint8 SMPDebugger::read(uint16 addr) {
|
uint8 SMPDebugger::read(uint16 addr) {
|
||||||
if((addr & 0xfff0) == 0x00f0) return ~0; //$00f0-00ff MMIO
|
if((addr & 0xfff0) == 0x00f0) return ~0; //$00f0-00ff MMIO
|
||||||
return SNES::smp.op_busread(addr);
|
return SNES::smp.op_busread(addr);
|
||||||
|
@ -71,7 +73,7 @@ SMPDebugger::SMPDebugger() {
|
||||||
opcodePC = 0xffc0;
|
opcodePC = 0xffc0;
|
||||||
|
|
||||||
setTitle("SMP Debugger");
|
setTitle("SMP Debugger");
|
||||||
setGeometry({800, 800, 350, 255});
|
setGeometry({128, 128, 350, 255});
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
stepInto.setText("Step Into");
|
stepInto.setText("Step Into");
|
||||||
|
@ -96,4 +98,11 @@ SMPDebugger::SMPDebugger() {
|
||||||
};
|
};
|
||||||
|
|
||||||
update.onActivate = { &SMPDebugger::updateDisassembly, this };
|
update.onActivate = { &SMPDebugger::updateDisassembly, this };
|
||||||
|
|
||||||
|
registers.onActivate = [&] {
|
||||||
|
smpRegisterEditor->loadRegisters();
|
||||||
|
smpRegisterEditor->setVisible();
|
||||||
|
};
|
||||||
|
|
||||||
|
windowManager->append(this, "SMPDebugger");
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ struct SMPDebugger : Window {
|
||||||
CheckBox autoUpdate;
|
CheckBox autoUpdate;
|
||||||
Button update;
|
Button update;
|
||||||
TextEdit disassembly;
|
TextEdit disassembly;
|
||||||
Label registers;
|
Button registers;
|
||||||
|
|
||||||
uint8 read(uint16 addr);
|
uint8 read(uint16 addr);
|
||||||
void write(uint16 addr, uint8 data);
|
void write(uint16 addr, uint8 data);
|
||||||
|
@ -18,4 +18,34 @@ struct SMPDebugger : Window {
|
||||||
SMPDebugger();
|
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 SMPDebugger *smpDebugger;
|
||||||
|
extern SMPRegisterEditor *smpRegisterEditor;
|
||||||
|
|
|
@ -41,4 +41,6 @@ VideoWindow::VideoWindow() {
|
||||||
"0x", hex<4>((b << 10) + (g << 5) + (r << 0))
|
"0x", hex<4>((b << 10) + (g << 5) + (r << 0))
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
windowManager->append(this, "VideoWindow");
|
||||||
}
|
}
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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;
|
|
@ -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" });
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
struct WindowManager {
|
||||||
|
struct WindowItem {
|
||||||
|
Window *window;
|
||||||
|
string name;
|
||||||
|
string geometry;
|
||||||
|
};
|
||||||
|
vector<WindowItem> windowList;
|
||||||
|
configuration config;
|
||||||
|
|
||||||
|
void append(Window *window, const string &name);
|
||||||
|
void loadGeometry();
|
||||||
|
void saveGeometry();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern WindowManager *windowManager;
|
Loading…
Reference in New Issue