mirror of https://github.com/bsnes-emu/bsnes.git
Update to v085r07 release.
byuu says: Changelog: - stuff [Editor's note - pretty much just more debugger implementation]
This commit is contained in:
parent
4bc5f66aa5
commit
ad3eafd735
|
@ -1,7 +1,7 @@
|
||||||
#ifndef BASE_HPP
|
#ifndef BASE_HPP
|
||||||
#define BASE_HPP
|
#define BASE_HPP
|
||||||
|
|
||||||
const char Version[] = "085.06";
|
const char Version[] = "085.07";
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/algorithm.hpp>
|
#include <nall/algorithm.hpp>
|
||||||
|
|
|
@ -433,51 +433,4 @@ void CPUcore::disassemble_opcode(char *output, uint32 addr) {
|
||||||
strcat(s, t);
|
strcat(s, t);
|
||||||
}
|
}
|
||||||
|
|
||||||
//opcode_length() retrieves the length of the next opcode
|
|
||||||
//to be executed. It is used by the debugger to step over,
|
|
||||||
//disable and proceed cpu opcodes.
|
|
||||||
//
|
|
||||||
//5 and 6 are special cases, 5 is used for #consts based on
|
|
||||||
//the A register size, 6 for the X/Y register size. the
|
|
||||||
//rest are literal sizes. There's no need to test for
|
|
||||||
//emulation mode, as regs.p.m/regs.p.x should *always* be
|
|
||||||
//set in emulation mode.
|
|
||||||
|
|
||||||
uint8 CPUcore::opcode_length() {
|
|
||||||
uint8 op, len;
|
|
||||||
static uint8 op_len_tbl[256] = {
|
|
||||||
//0,1,2,3, 4,5,6,7, 8,9,a,b, c,d,e,f
|
|
||||||
|
|
||||||
2,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x0n
|
|
||||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x1n
|
|
||||||
3,2,4,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x2n
|
|
||||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x3n
|
|
||||||
|
|
||||||
1,2,2,2, 3,2,2,2, 1,5,1,1, 3,3,3,4, //0x4n
|
|
||||||
2,2,2,2, 3,2,2,2, 1,3,1,1, 4,3,3,4, //0x5n
|
|
||||||
1,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x6n
|
|
||||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x7n
|
|
||||||
|
|
||||||
2,2,3,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0x8n
|
|
||||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0x9n
|
|
||||||
6,2,6,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xan
|
|
||||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xbn
|
|
||||||
|
|
||||||
6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xcn
|
|
||||||
2,2,2,2, 2,2,2,2, 1,3,1,1, 3,3,3,4, //0xdn
|
|
||||||
6,2,2,2, 2,2,2,2, 1,5,1,1, 3,3,3,4, //0xen
|
|
||||||
2,2,2,2, 3,2,2,2, 1,3,1,1, 3,3,3,4 //0xfn
|
|
||||||
};
|
|
||||||
|
|
||||||
if(false /* in_opcode() == true */) {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
op = dreadb(regs.pc.d);
|
|
||||||
len = op_len_tbl[op];
|
|
||||||
if(len == 5) return (regs.e || regs.p.m) ? 2 : 3;
|
|
||||||
if(len == 6) return (regs.e || regs.p.x) ? 2 : 3;
|
|
||||||
return len;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -27,4 +27,3 @@ uint8 dreadb(uint32 addr);
|
||||||
uint16 dreadw(uint32 addr);
|
uint16 dreadw(uint32 addr);
|
||||||
uint32 dreadl(uint32 addr);
|
uint32 dreadl(uint32 addr);
|
||||||
uint32 decode(uint8 offset_type, uint32 addr);
|
uint32 decode(uint8 offset_type, uint32 addr);
|
||||||
uint8 opcode_length();
|
|
||||||
|
|
|
@ -4,7 +4,7 @@ include $(snes)/Makefile
|
||||||
name := laevateinn
|
name := laevateinn
|
||||||
|
|
||||||
ui_objects := ui-main ui-interface ui-debugger ui-console
|
ui_objects := ui-main ui-interface ui-debugger ui-console
|
||||||
ui_objects += ui-video ui-memory ui-breakpoint
|
ui_objects += ui-video ui-cpu ui-memory ui-breakpoint
|
||||||
ui_objects += phoenix ruby
|
ui_objects += phoenix ruby
|
||||||
ui_objects += $(if $(call streq,$(platform),win),resource)
|
ui_objects += $(if $(call streq,$(platform),win),resource)
|
||||||
|
|
||||||
|
@ -33,6 +33,7 @@ obj/ui-main.o: $(ui)/main.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-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-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)/*)
|
||||||
|
|
|
@ -22,6 +22,7 @@ using namespace ruby;
|
||||||
#include "debugger/debugger.hpp"
|
#include "debugger/debugger.hpp"
|
||||||
#include "console/console.hpp"
|
#include "console/console.hpp"
|
||||||
#include "video/video.hpp"
|
#include "video/video.hpp"
|
||||||
|
#include "cpu/cpu.hpp"
|
||||||
#include "memory/memory.hpp"
|
#include "memory/memory.hpp"
|
||||||
#include "breakpoint/breakpoint.hpp"
|
#include "breakpoint/breakpoint.hpp"
|
||||||
extern uint8_t laevateinnLogo[121905];
|
extern uint8_t laevateinnLogo[121905];
|
||||||
|
|
|
@ -2,7 +2,8 @@
|
||||||
BreakpointEditor *breakpointEditor = nullptr;
|
BreakpointEditor *breakpointEditor = nullptr;
|
||||||
|
|
||||||
BreakpointEntry::BreakpointEntry() {
|
BreakpointEntry::BreakpointEntry() {
|
||||||
enable.setText("Enable");
|
static unsigned id = 1;
|
||||||
|
enable.setText({ id++, ". Enable" });
|
||||||
addr.setFont(application->monospaceFont);
|
addr.setFont(application->monospaceFont);
|
||||||
data.setFont(application->monospaceFont);
|
data.setFont(application->monospaceFont);
|
||||||
type.append("Read", "Write", "Exec");
|
type.append("Read", "Write", "Exec");
|
||||||
|
@ -13,14 +14,83 @@ BreakpointEntry::BreakpointEntry() {
|
||||||
append(data, {25, 0}, 5);
|
append(data, {25, 0}, 5);
|
||||||
append(type, {0, 0}, 5);
|
append(type, {0, 0}, 5);
|
||||||
append(source, {0, 0});
|
append(source, {0, 0});
|
||||||
|
|
||||||
|
enable.onToggle = [&] {
|
||||||
|
bool flag = !enable.checked();
|
||||||
|
addr.setEnabled(flag);
|
||||||
|
data.setEnabled(flag);
|
||||||
|
type.setEnabled(flag);
|
||||||
|
source.setEnabled(flag);
|
||||||
|
breakpointEditor->synchronize();
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
BreakpointEditor::BreakpointEditor() {
|
BreakpointEditor::BreakpointEditor() {
|
||||||
setTitle("Breakpoint Editor");
|
setTitle("Breakpoint Editor");
|
||||||
|
|
||||||
layout.setMargin(5);
|
layout.setMargin(5);
|
||||||
for(auto &bp : breakpoint) 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({800, 600, layout.minimumGeometry().width, layout.minimumGeometry().height - 5});
|
||||||
|
synchronize();
|
||||||
|
}
|
||||||
|
|
||||||
|
//enable checkbox toggled on one of the five BreakpointEntry items:
|
||||||
|
//cache settings to decrease testing overhead whilst debugging
|
||||||
|
void BreakpointEditor::synchronize() {
|
||||||
|
breakpoint.reset();
|
||||||
|
unsigned id = 0;
|
||||||
|
for(auto &entry : breakpointEntry) {
|
||||||
|
id++;
|
||||||
|
if(entry.enable.checked() == false) continue;
|
||||||
|
Breakpoint bp;
|
||||||
|
bp.id = id;
|
||||||
|
bp.compare = !entry.data.text().empty();
|
||||||
|
bp.addr = hex(entry.addr.text());
|
||||||
|
bp.data = hex(entry.data.text());
|
||||||
|
bp.type = entry.type.selection();
|
||||||
|
bp.source = entry.source.selection();
|
||||||
|
breakpoint.append(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
breakpointReadCPU.reset();
|
||||||
|
breakpointWriteCPU.reset();
|
||||||
|
breakpointExecCPU.reset();
|
||||||
|
|
||||||
|
for(auto &bp : breakpoint) if(bp.type == Breakpoint::Read && bp.source == Breakpoint::CPU) breakpointReadCPU.append(bp);
|
||||||
|
for(auto &bp : breakpoint) if(bp.type == Breakpoint::Write && bp.source == Breakpoint::CPU) breakpointWriteCPU.append(bp);
|
||||||
|
for(auto &bp : breakpoint) if(bp.type == Breakpoint::Exec && bp.source == Breakpoint::CPU) breakpointExecCPU.append(bp);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakpointEditor::testReadCPU(uint24 addr) {
|
||||||
|
for(auto &bp : breakpointReadCPU) {
|
||||||
|
if(bp.addr == addr) {
|
||||||
|
if(bp.compare && bp.data != SNES::bus.read(addr)) continue;
|
||||||
|
debugger->print("Breakpoint #", bp.id, " hit\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakpointEditor::testWriteCPU(uint24 addr, uint8 data) {
|
||||||
|
for(auto &bp : breakpointWriteCPU) {
|
||||||
|
if(bp.addr == addr) {
|
||||||
|
if(bp.compare && bp.data != data) continue;
|
||||||
|
debugger->print("Breakpoint #", bp.id, " hit\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool BreakpointEditor::testExecCPU(uint24 addr) {
|
||||||
|
for(auto &bp : breakpointExecCPU) {
|
||||||
|
if(bp.addr == addr) {
|
||||||
|
debugger->print("Breapoint #", bp.id, " hit\n");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,27 @@ struct BreakpointEntry : HorizontalLayout {
|
||||||
|
|
||||||
struct BreakpointEditor : Window {
|
struct BreakpointEditor : Window {
|
||||||
VerticalLayout layout;
|
VerticalLayout layout;
|
||||||
BreakpointEntry breakpoint[5];
|
BreakpointEntry breakpointEntry[8];
|
||||||
|
|
||||||
|
struct Breakpoint {
|
||||||
|
enum : unsigned { Read, Write, Exec };
|
||||||
|
enum : unsigned { CPU, APU, VRAM, OAM, CGRAM };
|
||||||
|
unsigned id;
|
||||||
|
bool compare;
|
||||||
|
unsigned addr;
|
||||||
|
unsigned data;
|
||||||
|
unsigned type;
|
||||||
|
unsigned source;
|
||||||
|
};
|
||||||
|
vector<Breakpoint> breakpoint;
|
||||||
|
vector<Breakpoint> breakpointReadCPU;
|
||||||
|
vector<Breakpoint> breakpointWriteCPU;
|
||||||
|
vector<Breakpoint> breakpointExecCPU;
|
||||||
|
void synchronize();
|
||||||
|
|
||||||
|
bool testReadCPU(uint24 addr);
|
||||||
|
bool testWriteCPU(uint24 addr, uint8 data);
|
||||||
|
bool testExecCPU(uint24 addr);
|
||||||
|
|
||||||
BreakpointEditor();
|
BreakpointEditor();
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,6 +6,7 @@ AboutWindow::AboutWindow() {
|
||||||
setResizable(false);
|
setResizable(false);
|
||||||
|
|
||||||
layout.setMargin(10);
|
layout.setMargin(10);
|
||||||
|
layout.setAlignment(0.5);
|
||||||
canvas.setSize({288, 360});
|
canvas.setSize({288, 360});
|
||||||
title.setFont("Sans, 16, Bold");
|
title.setFont("Sans, 16, Bold");
|
||||||
title.setText("Laevateinn");
|
title.setText("Laevateinn");
|
||||||
|
@ -13,14 +14,8 @@ AboutWindow::AboutWindow() {
|
||||||
version.setText({"bsnes/debugger v", Version});
|
version.setText({"bsnes/debugger v", Version});
|
||||||
|
|
||||||
layout.append(canvas, {288, 360});
|
layout.append(canvas, {288, 360});
|
||||||
layout.append(titleLayout, {~0, 0});
|
layout.append(title, {0, 0});
|
||||||
titleLayout.append(titleL, {~0, 0});
|
layout.append(version, {0, 0});
|
||||||
titleLayout.append(title, {0, 0});
|
|
||||||
titleLayout.append(titleR, {~0, 0});
|
|
||||||
layout.append(versionLayout, {~0, 0});
|
|
||||||
versionLayout.append(versionL, {~0, 0});
|
|
||||||
versionLayout.append(version, {0, 0});
|
|
||||||
versionLayout.append(versionR, {~0, 0});
|
|
||||||
append(layout);
|
append(layout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,9 +21,9 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
|
|
||||||
menuDebug.setText("&Debug");
|
menuDebug.setText("&Debug");
|
||||||
menuDebugCPU.setText("CPU");
|
menuDebugCPU.setText("CPU");
|
||||||
menuDebugCPU.setChecked(debugger->flags.debugCPU);
|
menuDebugCPU.setChecked(debugger->debug.cpu);
|
||||||
menuDebugSMP.setText("SMP");
|
menuDebugSMP.setText("SMP");
|
||||||
menuDebugSMP.setChecked(debugger->flags.debugSMP);
|
menuDebugSMP.setChecked(debugger->debug.smp);
|
||||||
menuDebugSMP.setEnabled(false);
|
menuDebugSMP.setEnabled(false);
|
||||||
menuDebug.append(menuDebugCPU, menuDebugSMP);
|
menuDebug.append(menuDebugCPU, menuDebugSMP);
|
||||||
append(menuDebug);
|
append(menuDebug);
|
||||||
|
@ -39,9 +39,11 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
|
|
||||||
menuWindows.setText("&Windows");
|
menuWindows.setText("&Windows");
|
||||||
menuWindowsVideoWindow.setText("Video");
|
menuWindowsVideoWindow.setText("Video");
|
||||||
|
menuWindowsCPUDebugger.setText("CPU Debugger");
|
||||||
menuWindowsMemoryEditor.setText("Memory Editor");
|
menuWindowsMemoryEditor.setText("Memory Editor");
|
||||||
menuWindowsBreakpointEditor.setText("Breakpoint Editor");
|
menuWindowsBreakpointEditor.setText("Breakpoint Editor");
|
||||||
menuWindows.append(menuWindowsVideoWindow, menuWindowsMemoryEditor, menuWindowsBreakpointEditor);
|
menuWindows.append(menuWindowsVideoWindow, menuWindowsCPUDebugger, menuWindowsMemoryEditor,
|
||||||
|
menuWindowsBreakpointEditor);
|
||||||
append(menuWindows);
|
append(menuWindows);
|
||||||
|
|
||||||
menuHelp.setText("&Help");
|
menuHelp.setText("&Help");
|
||||||
|
@ -83,8 +85,8 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
audio.set(Audio::Synchronize, menuEmulationSynchronizeAudio.checked());
|
audio.set(Audio::Synchronize, menuEmulationSynchronizeAudio.checked());
|
||||||
};
|
};
|
||||||
|
|
||||||
menuDebugCPU.onToggle = [&] { debugger->flags.debugCPU = menuDebugCPU.checked(); };
|
menuDebugCPU.onToggle = [&] { debugger->debug.cpu = menuDebugCPU.checked(); };
|
||||||
menuDebugSMP.onToggle = [&] { debugger->flags.debugSMP = menuDebugSMP.checked(); };
|
menuDebugSMP.onToggle = [&] { debugger->debug.smp = menuDebugSMP.checked(); };
|
||||||
|
|
||||||
menuTracerEnable.onToggle = [&] { debugger->tracerEnable(menuTracerEnable.checked()); };
|
menuTracerEnable.onToggle = [&] { debugger->tracerEnable(menuTracerEnable.checked()); };
|
||||||
|
|
||||||
|
@ -93,6 +95,11 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
videoWindow->setFocused();
|
videoWindow->setFocused();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
menuWindowsCPUDebugger.onActivate = [&] {
|
||||||
|
cpuDebugger->setVisible();
|
||||||
|
cpuDebugger->setFocused();
|
||||||
|
};
|
||||||
|
|
||||||
menuWindowsMemoryEditor.onActivate = [&] {
|
menuWindowsMemoryEditor.onActivate = [&] {
|
||||||
memoryEditor->update();
|
memoryEditor->update();
|
||||||
memoryEditor->setVisible();
|
memoryEditor->setVisible();
|
||||||
|
@ -107,7 +114,7 @@ ConsoleWindow::ConsoleWindow() {
|
||||||
menuHelpAbout.onActivate = [&] { aboutWindow->show(); };
|
menuHelpAbout.onActivate = [&] { aboutWindow->show(); };
|
||||||
|
|
||||||
runButton.onActivate = [&] {
|
runButton.onActivate = [&] {
|
||||||
if(debugger->flags.paused == true) debugger->resume();
|
if(debugger->paused) debugger->resume();
|
||||||
else debugger->suspend();
|
else debugger->suspend();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct ConsoleWindow : Window {
|
||||||
|
|
||||||
Menu menuWindows;
|
Menu menuWindows;
|
||||||
Item menuWindowsVideoWindow;
|
Item menuWindowsVideoWindow;
|
||||||
|
Item menuWindowsCPUDebugger;
|
||||||
Item menuWindowsMemoryEditor;
|
Item menuWindowsMemoryEditor;
|
||||||
Item menuWindowsBreakpointEditor;
|
Item menuWindowsBreakpointEditor;
|
||||||
|
|
||||||
|
@ -40,14 +41,8 @@ struct ConsoleWindow : Window {
|
||||||
struct AboutWindow : Window {
|
struct AboutWindow : Window {
|
||||||
VerticalLayout layout;
|
VerticalLayout layout;
|
||||||
Canvas canvas;
|
Canvas canvas;
|
||||||
HorizontalLayout titleLayout;
|
|
||||||
Widget titleL;
|
|
||||||
Label title;
|
Label title;
|
||||||
Widget titleR;
|
|
||||||
HorizontalLayout versionLayout;
|
|
||||||
Widget versionL;
|
|
||||||
Label version;
|
Label version;
|
||||||
Widget versionR;
|
|
||||||
|
|
||||||
void show();
|
void show();
|
||||||
AboutWindow();
|
AboutWindow();
|
||||||
|
|
|
@ -0,0 +1,128 @@
|
||||||
|
#include "../base.hpp"
|
||||||
|
CPUDebugger *cpuDebugger = nullptr;
|
||||||
|
|
||||||
|
unsigned CPUDebugger::opcodeLength(uint24 addr) const {
|
||||||
|
#define M 5
|
||||||
|
#define X 6
|
||||||
|
static unsigned lengthTable[256] = {
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||||
|
3, 2, 4, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||||
|
|
||||||
|
1, 2, 2, 2, 3, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 3, 2, 2, 2, 1, 3, 1, 1, 4, 3, 3, 4,
|
||||||
|
1, 2, 3, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||||
|
|
||||||
|
2, 2, 3, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||||
|
X, 2, X, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||||
|
|
||||||
|
X, 2, 2, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 2, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||||
|
X, 2, 2, 2, 2, 2, 2, 2, 1, M, 1, 1, 3, 3, 3, 4,
|
||||||
|
2, 2, 2, 2, 3, 2, 2, 2, 1, 3, 1, 1, 3, 3, 3, 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
unsigned length = lengthTable[SNES::bus.read(addr)];
|
||||||
|
if(length == M) return 3 - (SNES::cpu.regs.e | SNES::cpu.regs.p.m);
|
||||||
|
if(length == X) return 3 - (SNES::cpu.regs.e | SNES::cpu.regs.p.x);
|
||||||
|
return length;
|
||||||
|
#undef M
|
||||||
|
#undef X
|
||||||
|
}
|
||||||
|
|
||||||
|
void CPUDebugger::updateDisassembly() {
|
||||||
|
string line[15];
|
||||||
|
char text[512];
|
||||||
|
|
||||||
|
SNES::cpu.disassemble_opcode(text, opcodePC);
|
||||||
|
text[29] = 0;
|
||||||
|
line[7] = { "> ", text };
|
||||||
|
|
||||||
|
signed addr = opcodePC;
|
||||||
|
for(signed o = 6; o >= 0; o--) {
|
||||||
|
for(signed b = 1; b <= 4; b++) {
|
||||||
|
if(addr - b >= 0 && (debugger->cpuUsage.data[addr - b] & Usage::Exec)) {
|
||||||
|
addr -= b;
|
||||||
|
SNES::cpu.disassemble_opcode(text, addr);
|
||||||
|
text[29] = 0;
|
||||||
|
line[o] = { " ", text };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addr = opcodePC;
|
||||||
|
for(signed o = 8; o <= 14; o++) {
|
||||||
|
for(signed b = 1; b <= 4; b++) {
|
||||||
|
if(addr + b <= 0xffffff && (debugger->cpuUsage.data[addr + b] & Usage::Exec)) {
|
||||||
|
addr += b;
|
||||||
|
SNES::cpu.disassemble_opcode(text, addr);
|
||||||
|
text[29] = 0;
|
||||||
|
line[o] = { " ", text };
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string output;
|
||||||
|
for(auto &n : line) {
|
||||||
|
if(n.empty()) output.append(" ...\n");
|
||||||
|
else output.append(n, "\n");
|
||||||
|
}
|
||||||
|
output.rtrim<1>("\n");
|
||||||
|
|
||||||
|
disassembly.setText(output);
|
||||||
|
registers.setText({
|
||||||
|
"A:", hex<4>(SNES::cpu.regs.a), " X:", hex<4>(SNES::cpu.regs.x), " Y:", hex<4>(SNES::cpu.regs.y),
|
||||||
|
" S:", hex<4>(SNES::cpu.regs.s), " D:", hex<4>(SNES::cpu.regs.d), " DB:", hex<2>(SNES::cpu.regs.db), " ",
|
||||||
|
SNES::cpu.regs.e ? "E" : "R", ":",
|
||||||
|
SNES::cpu.regs.p.n ? "N" : "n", SNES::cpu.regs.p.v ? "V" : "v",
|
||||||
|
SNES::cpu.regs.p.m ? "M" : "m", SNES::cpu.regs.p.x ? "X" : "x",
|
||||||
|
SNES::cpu.regs.p.d ? "D" : "d", SNES::cpu.regs.p.i ? "I" : "i",
|
||||||
|
SNES::cpu.regs.p.z ? "Z" : "z", SNES::cpu.regs.p.c ? "C" : "c",
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
CPUDebugger::CPUDebugger() {
|
||||||
|
opcodePC = 0x008000;
|
||||||
|
|
||||||
|
setTitle("CPU Debugger");
|
||||||
|
setGeometry({800, 64, 500, 255});
|
||||||
|
|
||||||
|
layout.setMargin(5);
|
||||||
|
stepInto.setText("Step Into");
|
||||||
|
stepOver.setText("Step Over");
|
||||||
|
stepOver.setEnabled(false);
|
||||||
|
stepOut.setText("Step Out");
|
||||||
|
stepOut.setEnabled(false);
|
||||||
|
skipOver.setText("Skip Over");
|
||||||
|
skipOver.setEnabled(false);
|
||||||
|
autoRefresh.setText("Auto");
|
||||||
|
update.setText("Update");
|
||||||
|
disassembly.setFont(application->monospaceFont);
|
||||||
|
registers.setFont(application->monospaceFont);
|
||||||
|
registers.setText(" ");
|
||||||
|
|
||||||
|
layout.append(controlLayout, {~0, 0}, 5);
|
||||||
|
controlLayout.append(stepInto, {80, 0}, 5);
|
||||||
|
controlLayout.append(stepOver, {80, 0}, 5);
|
||||||
|
controlLayout.append(stepOut, {80, 0}, 5);
|
||||||
|
controlLayout.append(skipOver, {80, 0});
|
||||||
|
controlLayout.append(spacer, {~0, 0});
|
||||||
|
controlLayout.append(autoRefresh, {0, 0}, 5);
|
||||||
|
controlLayout.append(update, {80, 0});
|
||||||
|
layout.append(disassembly, {~0, ~0}, 5);
|
||||||
|
layout.append(registers, {~0, 0});
|
||||||
|
append(layout);
|
||||||
|
|
||||||
|
stepInto.onActivate = [&] {
|
||||||
|
debugger->flags.cpu.stepInto = true;
|
||||||
|
debugger->resume();
|
||||||
|
};
|
||||||
|
|
||||||
|
update.onActivate = { &CPUDebugger::updateDisassembly, this };
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
struct CPUDebugger : Window {
|
||||||
|
uint24 opcodePC;
|
||||||
|
|
||||||
|
VerticalLayout layout;
|
||||||
|
HorizontalLayout controlLayout;
|
||||||
|
Button stepInto;
|
||||||
|
Button stepOver;
|
||||||
|
Button stepOut;
|
||||||
|
Button skipOver;
|
||||||
|
Widget spacer;
|
||||||
|
CheckBox autoRefresh;
|
||||||
|
Button update;
|
||||||
|
TextEdit disassembly;
|
||||||
|
Label registers;
|
||||||
|
|
||||||
|
unsigned opcodeLength(uint24 addr) const;
|
||||||
|
void updateDisassembly();
|
||||||
|
CPUDebugger();
|
||||||
|
};
|
||||||
|
|
||||||
|
extern CPUDebugger *cpuDebugger;
|
|
@ -2,15 +2,17 @@
|
||||||
Debugger *debugger = nullptr;
|
Debugger *debugger = nullptr;
|
||||||
|
|
||||||
#include "hook.cpp"
|
#include "hook.cpp"
|
||||||
|
#include "usage.cpp"
|
||||||
|
|
||||||
void Debugger::run() {
|
void Debugger::run() {
|
||||||
if(flags.paused == true) {
|
if(paused == true) {
|
||||||
usleep(2000);
|
usleep(2000);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(memoryEditor->autoRefresh.checked()) memoryEditor->update();
|
|
||||||
SNES::system.run();
|
SNES::system.run();
|
||||||
|
if(cpuDebugger->autoRefresh.checked()) cpuDebugger->updateDisassembly();
|
||||||
|
if(memoryEditor->autoRefresh.checked()) memoryEditor->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::echo(const string &text) {
|
void Debugger::echo(const string &text) {
|
||||||
|
@ -18,15 +20,17 @@ void Debugger::echo(const string &text) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::resume() {
|
void Debugger::resume() {
|
||||||
flags.paused = false;
|
if(paused == false) return;
|
||||||
|
paused = false;
|
||||||
consoleWindow->runButton.setText("Stop");
|
consoleWindow->runButton.setText("Stop");
|
||||||
consoleWindow->stepButton.setEnabled(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::suspend() {
|
void Debugger::suspend() {
|
||||||
flags.paused = true;
|
if(paused == true) return;
|
||||||
|
paused = true;
|
||||||
|
flags.step = false;
|
||||||
|
flags.cpu.stepInto = false;
|
||||||
consoleWindow->runButton.setText("Run");
|
consoleWindow->runButton.setText("Run");
|
||||||
consoleWindow->stepButton.setEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::tracerEnable(bool state) {
|
void Debugger::tracerEnable(bool state) {
|
||||||
|
@ -40,22 +44,31 @@ void Debugger::tracerEnable(bool state) {
|
||||||
//if all files exist, use 000, even if it overwrites another log.
|
//if all files exist, use 000, even if it overwrites another log.
|
||||||
unsigned n = 1;
|
unsigned n = 1;
|
||||||
do {
|
do {
|
||||||
if(file::exists({ interface->baseName, "-", decimal<3, '0'>(n), ".log" }) == false) break;
|
if(file::exists({ interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" }) == false) break;
|
||||||
} while(++n <= 999);
|
} while(++n <= 999);
|
||||||
|
|
||||||
string filename = { interface->baseName, "-", decimal<3, '0'>(n), ".log" };
|
string filename = { interface->pathName, "debug/trace-", decimal<3, '0'>(n), ".log" };
|
||||||
if(fpTracer.open(filename, file::mode::write) == false) return;
|
if(fpTracer.open(filename, file::mode::write) == false) return;
|
||||||
print("Tracing to ", filename, "\n");
|
print("Tracing to ", filename, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
Debugger::Debugger() {
|
Debugger::Debugger() {
|
||||||
flags.paused = true;
|
paused = true;
|
||||||
flags.step = false;
|
|
||||||
|
|
||||||
flags.debugCPU = true;
|
flags.step = false;
|
||||||
flags.debugSMP = false;
|
flags.cpu.stepInto = false;
|
||||||
|
|
||||||
|
debug.cpu = true;
|
||||||
|
debug.smp = false;
|
||||||
|
|
||||||
|
cpuUsage.allocate(16 * 1024 * 1024);
|
||||||
|
apuUsage.allocate(64 * 1024);
|
||||||
|
|
||||||
SNES::cpu.debugger.op_exec = { &Debugger::cpu_op_exec, this };
|
SNES::cpu.debugger.op_exec = { &Debugger::cpu_op_exec, this };
|
||||||
SNES::cpu.debugger.op_read = { &Debugger::cpu_op_read, this };
|
SNES::cpu.debugger.op_read = { &Debugger::cpu_op_read, this };
|
||||||
SNES::cpu.debugger.op_write = { &Debugger::cpu_op_write, this };
|
SNES::cpu.debugger.op_write = { &Debugger::cpu_op_write, this };
|
||||||
|
|
||||||
|
SNES::smp.debugger.op_exec = { &Debugger::smp_op_exec, this };
|
||||||
|
SNES::smp.debugger.op_read = { &Debugger::smp_op_read, this };
|
||||||
|
SNES::smp.debugger.op_write = { &Debugger::smp_op_write, this };
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,12 +1,35 @@
|
||||||
struct Debugger {
|
struct Usage {
|
||||||
struct Flags {
|
enum : unsigned { Read = 4, Write = 2, Exec = 1 };
|
||||||
bool paused; //do not run emulation when true; set when breakpoint is hit
|
uint8_t *data;
|
||||||
bool step; //break on the next instruction from any processor
|
unsigned size;
|
||||||
|
|
||||||
bool debugCPU;
|
void allocate(unsigned size);
|
||||||
bool debugSMP;
|
void reset();
|
||||||
|
Usage();
|
||||||
|
~Usage();
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Debugger {
|
||||||
|
bool paused;
|
||||||
|
|
||||||
|
struct Flags {
|
||||||
|
bool step;
|
||||||
|
struct CPU {
|
||||||
|
bool stepInto;
|
||||||
|
} cpu;
|
||||||
} flags;
|
} flags;
|
||||||
|
|
||||||
|
struct Debug {
|
||||||
|
bool cpu;
|
||||||
|
bool smp;
|
||||||
|
} debug;
|
||||||
|
|
||||||
|
Usage cpuUsage;
|
||||||
|
Usage apuUsage;
|
||||||
|
void loadUsage();
|
||||||
|
void saveUsage();
|
||||||
|
void resetUsage();
|
||||||
|
|
||||||
void run();
|
void run();
|
||||||
void echo(const string &text);
|
void echo(const string &text);
|
||||||
void resume(); //start running until breakpoint is reached
|
void resume(); //start running until breakpoint is reached
|
||||||
|
@ -18,6 +41,11 @@ struct Debugger {
|
||||||
void cpu_op_read(uint24 addr);
|
void cpu_op_read(uint24 addr);
|
||||||
void cpu_op_write(uint24 addr, uint8 data);
|
void cpu_op_write(uint24 addr, uint8 data);
|
||||||
|
|
||||||
|
//S-SMP
|
||||||
|
void smp_op_exec(uint16 addr);
|
||||||
|
void smp_op_read(uint16 addr);
|
||||||
|
void smp_op_write(uint16 addr, uint8 data);
|
||||||
|
|
||||||
Debugger();
|
Debugger();
|
||||||
|
|
||||||
file fpTracer;
|
file fpTracer;
|
||||||
|
|
|
@ -1,26 +1,70 @@
|
||||||
|
//S-CPU
|
||||||
|
//=====
|
||||||
|
|
||||||
void Debugger::cpu_op_exec(uint24 addr) {
|
void Debugger::cpu_op_exec(uint24 addr) {
|
||||||
if(flags.debugCPU == false) return;
|
cpuUsage.data[addr] |= Usage::Exec;
|
||||||
|
cpuDebugger->opcodePC = addr;
|
||||||
|
bool breakpointHit = breakpointEditor->testExecCPU(addr);
|
||||||
|
|
||||||
|
if(fpTracer.open() || (debug.cpu && flags.step) || flags.cpu.stepInto || breakpointHit) {
|
||||||
char text[512];
|
char text[512];
|
||||||
if(fpTracer.open() || flags.step) {
|
|
||||||
SNES::cpu.disassemble_opcode(text, addr);
|
SNES::cpu.disassemble_opcode(text, addr);
|
||||||
}
|
|
||||||
|
|
||||||
if(fpTracer.open()) {
|
if(fpTracer.open()) fpTracer.print(text, "\n");
|
||||||
fpTracer.print(text, "\n");
|
if((debug.cpu && flags.step) || flags.cpu.stepInto || breakpointHit) {
|
||||||
}
|
|
||||||
|
|
||||||
if(flags.step) {
|
|
||||||
flags.step = false;
|
|
||||||
print(text, "\n");
|
print(text, "\n");
|
||||||
suspend();
|
if(debug.cpu && flags.step) {
|
||||||
consoleWindow->stepButton.setFocused();
|
consoleWindow->stepButton.setFocused();
|
||||||
|
}
|
||||||
|
if(flags.cpu.stepInto) {
|
||||||
|
cpuDebugger->stepInto.setFocused();
|
||||||
|
cpuDebugger->updateDisassembly();
|
||||||
|
}
|
||||||
|
suspend();
|
||||||
SNES::scheduler.debug();
|
SNES::scheduler.debug();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void Debugger::cpu_op_read(uint24 addr) {
|
void Debugger::cpu_op_read(uint24 addr) {
|
||||||
|
cpuUsage.data[addr] |= Usage::Read;
|
||||||
|
bool breakpointHit = breakpointEditor->testReadCPU(addr);
|
||||||
|
|
||||||
|
if(breakpointHit) {
|
||||||
|
char text[512];
|
||||||
|
SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC);
|
||||||
|
print(text, "\n");
|
||||||
|
|
||||||
|
suspend();
|
||||||
|
SNES::scheduler.debug();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Debugger::cpu_op_write(uint24 addr, uint8 data) {
|
void Debugger::cpu_op_write(uint24 addr, uint8 data) {
|
||||||
|
cpuUsage.data[addr] |= Usage::Write;
|
||||||
|
bool breakpointHit = breakpointEditor->testWriteCPU(addr, data);
|
||||||
|
|
||||||
|
if(breakpointHit) {
|
||||||
|
char text[512];
|
||||||
|
SNES::cpu.disassemble_opcode(text, cpuDebugger->opcodePC);
|
||||||
|
print(text, "\n");
|
||||||
|
|
||||||
|
suspend();
|
||||||
|
SNES::scheduler.debug();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//S-SMP
|
||||||
|
//=====
|
||||||
|
|
||||||
|
void Debugger::smp_op_exec(uint16 addr) {
|
||||||
|
apuUsage.data[addr] |= Usage::Exec;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debugger::smp_op_read(uint16 addr) {
|
||||||
|
apuUsage.data[addr] |= Usage::Read;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debugger::smp_op_write(uint16 addr, uint8 data) {
|
||||||
|
apuUsage.data[addr] |= Usage::Write;
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
void Usage::allocate(unsigned size_) {
|
||||||
|
if(data) delete[] data;
|
||||||
|
size = size_;
|
||||||
|
data = new uint8_t[size]();
|
||||||
|
}
|
||||||
|
|
||||||
|
Usage::Usage() {
|
||||||
|
data = nullptr;
|
||||||
|
size = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Usage::~Usage() {
|
||||||
|
if(data) delete[] data;
|
||||||
|
}
|
||||||
|
|
||||||
|
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(fp.open({ interface->pathName, "debug/usage.apu" }, file::mode::read)) {
|
||||||
|
fp.read(apuUsage.data, min(apuUsage.size, fp.size()));
|
||||||
|
fp.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debugger::saveUsage() {
|
||||||
|
string filename;
|
||||||
|
filename = { interface->pathName, "debug/usage.cpu" };
|
||||||
|
file::write(filename, cpuUsage.data, cpuUsage.size);
|
||||||
|
filename = { interface->pathName, "debug/usage.apu" };
|
||||||
|
file::write(filename, apuUsage.data, apuUsage.size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Debugger::resetUsage() {
|
||||||
|
if(cpuUsage.data) memset(cpuUsage.data, 0, cpuUsage.size);
|
||||||
|
if(apuUsage.data) memset(apuUsage.data, 0, apuUsage.size);
|
||||||
|
}
|
|
@ -14,6 +14,8 @@ bool Interface::loadCartridge(const string &filename) {
|
||||||
|
|
||||||
fileName = filename;
|
fileName = filename;
|
||||||
baseName = nall::basename(fileName);
|
baseName = nall::basename(fileName);
|
||||||
|
pathName = dir(baseName);
|
||||||
|
mkdir(string{pathName, "debug/"}, 0755);
|
||||||
|
|
||||||
string markup;
|
string markup;
|
||||||
markup.readfile({ baseName, ".xml" });
|
markup.readfile({ baseName, ".xml" });
|
||||||
|
@ -44,6 +46,8 @@ void Interface::loadMemory() {
|
||||||
delete[] data;
|
delete[] data;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugger->loadUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::saveMemory() {
|
void Interface::saveMemory() {
|
||||||
|
@ -54,6 +58,8 @@ void Interface::saveMemory() {
|
||||||
debugger->print("Saved ", filename, "\n");
|
debugger->print("Saved ", filename, "\n");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
debugger->saveUsage();
|
||||||
}
|
}
|
||||||
|
|
||||||
//hires is always true for accuracy core
|
//hires is always true for accuracy core
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
struct Interface : SNES::Interface {
|
struct Interface : SNES::Interface {
|
||||||
string fileName;
|
string fileName;
|
||||||
string baseName;
|
string baseName;
|
||||||
|
string pathName;
|
||||||
|
|
||||||
bool loadCartridge(const string &filename);
|
bool loadCartridge(const string &filename);
|
||||||
void loadMemory();
|
void loadMemory();
|
||||||
|
|
|
@ -41,11 +41,13 @@ Application::Application(int argc, char **argv) {
|
||||||
consoleWindow = new ConsoleWindow;
|
consoleWindow = new ConsoleWindow;
|
||||||
aboutWindow = new AboutWindow;
|
aboutWindow = new AboutWindow;
|
||||||
videoWindow = new VideoWindow;
|
videoWindow = new VideoWindow;
|
||||||
|
cpuDebugger = new CPUDebugger;
|
||||||
memoryEditor = new MemoryEditor;
|
memoryEditor = new MemoryEditor;
|
||||||
breakpointEditor = new BreakpointEditor;
|
breakpointEditor = new BreakpointEditor;
|
||||||
|
|
||||||
videoWindow->setVisible();
|
|
||||||
consoleWindow->setVisible();
|
consoleWindow->setVisible();
|
||||||
|
videoWindow->setVisible();
|
||||||
|
consoleWindow->setFocused();
|
||||||
|
|
||||||
if(audio.init() == false) {
|
if(audio.init() == false) {
|
||||||
audio.driver("None");
|
audio.driver("None");
|
||||||
|
@ -55,6 +57,8 @@ Application::Application(int argc, char **argv) {
|
||||||
audio.set(Audio::Frequency, 32000u);
|
audio.set(Audio::Frequency, 32000u);
|
||||||
|
|
||||||
if(interface->loadCartridge(filename) == false) return;
|
if(interface->loadCartridge(filename) == false) return;
|
||||||
|
cpuDebugger->updateDisassembly();
|
||||||
|
memoryEditor->selectSource();
|
||||||
|
|
||||||
while(quit == false) {
|
while(quit == false) {
|
||||||
OS::processEvents();
|
OS::processEvents();
|
||||||
|
@ -65,8 +69,10 @@ Application::Application(int argc, char **argv) {
|
||||||
}
|
}
|
||||||
|
|
||||||
Application::~Application() {
|
Application::~Application() {
|
||||||
|
exit(0);
|
||||||
delete breakpointEditor;
|
delete breakpointEditor;
|
||||||
delete memoryEditor;
|
delete memoryEditor;
|
||||||
|
delete cpuDebugger;
|
||||||
delete videoWindow;
|
delete videoWindow;
|
||||||
delete aboutWindow;
|
delete aboutWindow;
|
||||||
delete consoleWindow;
|
delete consoleWindow;
|
||||||
|
|
|
@ -35,8 +35,6 @@ MemoryEditor::MemoryEditor() {
|
||||||
source.onChange = { &MemoryEditor::selectSource, this };
|
source.onChange = { &MemoryEditor::selectSource, this };
|
||||||
editor.onRead = { &MemoryEditor::read, this };
|
editor.onRead = { &MemoryEditor::read, this };
|
||||||
editor.onWrite = { &MemoryEditor::write, this };
|
editor.onWrite = { &MemoryEditor::write, this };
|
||||||
|
|
||||||
selectSource();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8_t MemoryEditor::read(unsigned addr) {
|
uint8_t MemoryEditor::read(unsigned addr) {
|
||||||
|
|
Loading…
Reference in New Issue