Update to v094r04 release.
byuu says: Changelog: - target-ethos/ is now target-higan/ (will unfortunately screw up diffs pretty badly at this point.) - had a serious bug in nall::optional<T>::operator=, which is now fixed. - added tracer (no masking just yet, I need to write a nall::bitvector class because I don't want to hard-code those anymore.) - added usage logging (keep track of RWX/EP states for all bus addresses.) - added read/write to poke at memory (hex also works for reading, but this one can poke at MMIO regs and is for one address only.) - added both run.for (# of instructions) and run.to (program counter address.) - added read/write/execute breakpoints with counters for a given address, and with an optional compare byte (for read/write modes.) About the only major things left now for loki is support for trace masking, memory export, and VRAM/OAM/CGRAM access. For phoenix/Console, I really need to add a history to up+down arrows, and I should support left/right insert-at.
7
Makefile
|
@ -6,10 +6,13 @@ gb := gb
|
|||
gba := gba
|
||||
|
||||
profile := accuracy
|
||||
target := ethos
|
||||
target := higan
|
||||
# target := loki
|
||||
|
||||
# options += debugger
|
||||
ifeq ($(target),loki)
|
||||
options += debugger
|
||||
endif
|
||||
|
||||
# arch := x86
|
||||
# console := true
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "higan";
|
||||
static const char Version[] = "094.02";
|
||||
static const char Version[] = "094.04";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
static const char Website[] = "http://byuu.org/";
|
||||
|
|
|
@ -38,18 +38,18 @@ endif
|
|||
ifeq ($(compiler),)
|
||||
ifeq ($(platform),windows)
|
||||
compiler := g++
|
||||
flags :=
|
||||
flags := -fwrapv
|
||||
link :=
|
||||
else ifeq ($(platform),macosx)
|
||||
compiler := clang++
|
||||
flags := -w -stdlib=libc++
|
||||
flags := -fwrapv -w -stdlib=libc++
|
||||
link := -lc++ -lobjc
|
||||
else ifeq ($(platform),bsd)
|
||||
compiler := clang++
|
||||
flags := -w -I/usr/local/include
|
||||
flags := -fwrapv -w -I/usr/local/include
|
||||
else
|
||||
compiler := g++
|
||||
flags :=
|
||||
flags := -fwrapv
|
||||
link :=
|
||||
endif
|
||||
|
||||
|
|
|
@ -73,13 +73,13 @@ namespace Math {
|
|||
//================
|
||||
|
||||
#if defined(__clang__) || defined(__GNUC__)
|
||||
#define noinline __attribute__((noinline))
|
||||
#define neverinline __attribute__((noinline))
|
||||
#define alwaysinline inline __attribute__((always_inline))
|
||||
#elif defined(_MSC_VER)
|
||||
#define noinline __declspec(noinline)
|
||||
#define neverinline __declspec(noinline)
|
||||
#define alwaysinline inline __forceinline
|
||||
#else
|
||||
#define noinline
|
||||
#define neverinline
|
||||
#define alwaysinline inline
|
||||
#endif
|
||||
|
||||
|
|
|
@ -102,8 +102,7 @@ template<typename TT> struct optional {
|
|||
|
||||
optional& operator=(const optional& source) {
|
||||
reset();
|
||||
valid = source.valid;
|
||||
if(valid) operator=(source);
|
||||
if(source) operator=(source());
|
||||
return *this;
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,9 @@ void pConsole::print(string text) {
|
|||
void pConsole::reset() {
|
||||
}
|
||||
|
||||
void pConsole::setPrompt(string prompt) {
|
||||
}
|
||||
|
||||
void pConsole::constructor() {
|
||||
@autoreleasepool {
|
||||
cocoaView = cocoaConsole = [[CocoaConsole alloc] initWith:console];
|
||||
|
|
|
@ -13,6 +13,7 @@ struct pConsole : public pWidget {
|
|||
|
||||
void print(string text);
|
||||
void reset();
|
||||
void setPrompt(string prompt);
|
||||
|
||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||
void constructor();
|
||||
|
|
|
@ -1221,10 +1221,19 @@ void Console::print(const string& text) {
|
|||
return p.print(text);
|
||||
}
|
||||
|
||||
string Console::prompt() const {
|
||||
return state.prompt;
|
||||
}
|
||||
|
||||
void Console::reset() {
|
||||
return p.reset();
|
||||
}
|
||||
|
||||
void Console::setPrompt(const string& prompt) {
|
||||
state.prompt = prompt;
|
||||
return p.setPrompt(prompt);
|
||||
}
|
||||
|
||||
Console::Console():
|
||||
state(*new State),
|
||||
base_from_member<pConsole&>(*new pConsole(*this)),
|
||||
|
|
|
@ -525,7 +525,9 @@ struct Console : private nall::base_from_member<pConsole&>, Widget {
|
|||
template<typename... Args> void print(Args&&... args) { print({args...}); }
|
||||
|
||||
void print(const nall::string& text);
|
||||
nall::string prompt() const;
|
||||
void reset();
|
||||
void setPrompt(const nall::string& prompt);
|
||||
|
||||
Console();
|
||||
~Console();
|
||||
|
|
|
@ -125,6 +125,7 @@ struct ComboButton::State {
|
|||
};
|
||||
|
||||
struct Console::State {
|
||||
string prompt;
|
||||
};
|
||||
|
||||
struct Frame::State {
|
||||
|
|
|
@ -348,9 +348,11 @@ struct pConsole : public pWidget {
|
|||
GtkWidget* subWidget;
|
||||
GtkTextBuffer* textBuffer;
|
||||
string command;
|
||||
string previousPrompt;
|
||||
|
||||
void print(string text);
|
||||
void reset();
|
||||
void setPrompt(string prompt);
|
||||
|
||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||
void constructor();
|
||||
|
|
|
@ -5,14 +5,34 @@ static bool Console_keyPress(GtkWidget* widget, GdkEventKey* event, Console* sel
|
|||
}
|
||||
|
||||
void pConsole::print(string text) {
|
||||
//insert text before prompt and command, so as not to interrupt the current command
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_iter_set_offset(&iter, gtk_text_iter_get_offset(&iter) - console.prompt().size() - command.size());
|
||||
gtk_text_buffer_insert(textBuffer, &iter, text, -1);
|
||||
seekCursorToEnd();
|
||||
gtk_text_buffer_insert_at_cursor(textBuffer, text, -1);
|
||||
GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
|
||||
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark);
|
||||
}
|
||||
|
||||
void pConsole::reset() {
|
||||
gtk_text_buffer_set_text(textBuffer, "", -1);
|
||||
//flush history and command; draw prompt
|
||||
command.reset();
|
||||
gtk_text_buffer_set_text(textBuffer, console.prompt(), -1);
|
||||
seekCursorToEnd();
|
||||
}
|
||||
|
||||
void pConsole::setPrompt(string prompt) {
|
||||
//erase old prompt; insert new prompt in its place
|
||||
GtkTextIter lhs, rhs, iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &lhs);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &rhs);
|
||||
gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&lhs) - previousPrompt.size() - command.size());
|
||||
gtk_text_iter_set_offset(&rhs, gtk_text_iter_get_offset(&rhs) - command.size());
|
||||
gtk_text_buffer_delete(textBuffer, &lhs, &rhs);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_iter_set_offset(&iter, gtk_text_iter_get_offset(&iter) - command.size());
|
||||
gtk_text_buffer_insert(textBuffer, &iter, prompt, -1);
|
||||
seekCursorToEnd();
|
||||
previousPrompt = prompt;
|
||||
}
|
||||
|
||||
void pConsole::constructor() {
|
||||
|
@ -51,27 +71,37 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
|
|||
if(mask & (GDK_CONTROL_MASK | GDK_MOD1_MASK | GDK_SUPER_MASK)) return false; //allow actions such as Ctrl+C (copy)
|
||||
|
||||
if(scancode == GDK_KEY_Return || scancode == GDK_KEY_KP_Enter) {
|
||||
print("\n");
|
||||
if(console.onActivate) console.onActivate(command);
|
||||
//add current prompt and command to history; print new prompt; execute command
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_buffer_insert(textBuffer, &iter, string{"\n", console.prompt()}, -1);
|
||||
string s = command;
|
||||
command.reset();
|
||||
if(console.onActivate) console.onActivate(s);
|
||||
seekCursorToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode == GDK_KEY_BackSpace) {
|
||||
if(command.size()) {
|
||||
//delete last character of command
|
||||
command.resize(command.size() - 1);
|
||||
GtkTextIter lhs, rhs;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &lhs);
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &rhs);
|
||||
gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&lhs) - 1);
|
||||
gtk_text_buffer_delete(textBuffer, &lhs, &rhs);
|
||||
seekCursorToEnd();
|
||||
}
|
||||
seekCursorToEnd();
|
||||
return true;
|
||||
}
|
||||
|
||||
if(scancode >= 0x20 && scancode <= 0x7e) {
|
||||
print({(char)scancode});
|
||||
//add character to end of command
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_buffer_insert(textBuffer, &iter, string{(char)scancode}, -1);
|
||||
seekCursorToEnd();
|
||||
command.append((char)scancode);
|
||||
return true;
|
||||
}
|
||||
|
@ -80,9 +110,12 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
|
|||
}
|
||||
|
||||
void pConsole::seekCursorToEnd() {
|
||||
//place cursor at end of text buffer; scroll text view to the cursor to ensure it is visible
|
||||
GtkTextIter iter;
|
||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||
gtk_text_buffer_place_cursor(textBuffer, &iter);
|
||||
GtkTextMark* mark = gtk_text_buffer_get_mark(textBuffer, "insert");
|
||||
gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(subWidget), mark);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
/****************************************************************************
|
||||
** Meta object code from reading C++ file 'platform.moc.hpp'
|
||||
**
|
||||
** Created: Mon Jan 20 01:11:54 2014
|
||||
** Created: Wed Jan 29 08:08:49 2014
|
||||
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
|
||||
**
|
||||
** WARNING! All changes made in this file will be lost!
|
||||
|
|
|
@ -432,6 +432,7 @@ public:
|
|||
|
||||
void print(string text);
|
||||
void reset();
|
||||
void setPrompt(string prompt);
|
||||
|
||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||
void constructor();
|
||||
|
|
|
@ -6,6 +6,9 @@ void pConsole::print(string text) {
|
|||
void pConsole::reset() {
|
||||
}
|
||||
|
||||
void pConsole::setPrompt(string prompt) {
|
||||
}
|
||||
|
||||
void pConsole::constructor() {
|
||||
qtWidget = qtConsole = new QtConsole(*this);
|
||||
|
||||
|
|
|
@ -6,6 +6,9 @@ void pConsole::print(string text) {
|
|||
void pConsole::reset() {
|
||||
}
|
||||
|
||||
void pConsole::setPrompt(string prompt) {
|
||||
}
|
||||
|
||||
void pConsole::constructor() {
|
||||
}
|
||||
|
||||
|
|
|
@ -5,6 +5,7 @@ struct pConsole : public pWidget {
|
|||
|
||||
void print(string text);
|
||||
void reset();
|
||||
void setPrompt(string prompt);
|
||||
|
||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||
void constructor();
|
||||
|
|
|
@ -363,6 +363,7 @@ struct pConsole : public pWidget {
|
|||
|
||||
void print(string text);
|
||||
void reset();
|
||||
void setPrompt(string prompt);
|
||||
|
||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||
void constructor();
|
||||
|
|
|
@ -14,6 +14,9 @@ void pConsole::print(string text) {
|
|||
void pConsole::reset() {
|
||||
}
|
||||
|
||||
void pConsole::setPrompt(string prompt) {
|
||||
}
|
||||
|
||||
void pConsole::constructor() {
|
||||
hwnd = CreateWindowEx(
|
||||
WS_EX_CLIENTEDGE, L"EDIT", L"",
|
||||
|
|
|
@ -102,7 +102,11 @@ uint32 R65816::decode(uint8 offset_type, uint32 addr) {
|
|||
return(r & 0xffffff);
|
||||
}
|
||||
|
||||
void R65816::disassemble_opcode(char* output, uint32 addr) {
|
||||
void R65816::disassemble_opcode(char* output) {
|
||||
return disassemble_opcode(output, regs.pc.d, regs.e, regs.p.m, regs.p.x);
|
||||
}
|
||||
|
||||
void R65816::disassemble_opcode(char* output, uint32 addr, bool e, bool m, bool x) {
|
||||
static reg24_t pc;
|
||||
char t[256];
|
||||
char* s = output;
|
||||
|
@ -123,8 +127,8 @@ void R65816::disassemble_opcode(char* output, uint32 addr) {
|
|||
#define op8 ((op0))
|
||||
#define op16 ((op0) | (op1 << 8))
|
||||
#define op24 ((op0) | (op1 << 8) | (op2 << 16))
|
||||
#define a8 (regs.e || regs.p.m)
|
||||
#define x8 (regs.e || regs.p.x)
|
||||
#define a8 (e || m)
|
||||
#define x8 (e || x)
|
||||
|
||||
switch(op) {
|
||||
case 0x00: sprintf(t, "brk #$%.2x ", op8); break;
|
||||
|
|
|
@ -22,7 +22,8 @@ enum : unsigned {
|
|||
OPTYPE_RELW, //relw
|
||||
};
|
||||
|
||||
void disassemble_opcode(char* output, uint32 addr);
|
||||
void disassemble_opcode(char* output);
|
||||
void disassemble_opcode(char* output, uint32 addr, bool e, bool m, bool x);
|
||||
uint8 dreadb(uint32 addr);
|
||||
uint16 dreadw(uint32 addr);
|
||||
uint32 dreadl(uint32 addr);
|
||||
|
|
|
@ -88,7 +88,7 @@ void CPU::op_step() {
|
|||
debugger.op_exec(regs.pc.d);
|
||||
if(interface->tracer.open()) {
|
||||
char text[4096];
|
||||
disassemble_opcode(text, regs.pc.d);
|
||||
disassemble_opcode(text);
|
||||
interface->tracer.print(text, "\n");
|
||||
}
|
||||
|
||||
|
|
|
@ -136,7 +136,7 @@ privileged:
|
|||
|
||||
struct Debugger {
|
||||
hook<void (uint24)> op_exec;
|
||||
hook<void (uint24)> op_read;
|
||||
hook<void (uint24, uint8)> op_read;
|
||||
hook<void (uint24, uint8)> op_write;
|
||||
hook<void ()> op_nmi;
|
||||
hook<void ()> op_irq;
|
||||
|
|
|
@ -11,25 +11,23 @@ void CPU::op_io() {
|
|||
}
|
||||
|
||||
uint8 CPU::op_read(uint32 addr) {
|
||||
debugger.op_read(addr);
|
||||
|
||||
status.clock_count = speed(addr);
|
||||
dma_edge();
|
||||
add_clocks(status.clock_count - 4);
|
||||
regs.mdr = bus.read(addr);
|
||||
add_clocks(4);
|
||||
alu_edge();
|
||||
debugger.op_read(addr, regs.mdr);
|
||||
return regs.mdr;
|
||||
}
|
||||
|
||||
void CPU::op_write(uint32 addr, uint8 data) {
|
||||
debugger.op_write(addr, data);
|
||||
|
||||
alu_edge();
|
||||
status.clock_count = speed(addr);
|
||||
dma_edge();
|
||||
add_clocks(status.clock_count);
|
||||
bus.write(addr, regs.mdr = data);
|
||||
debugger.op_write(addr, regs.mdr);
|
||||
}
|
||||
|
||||
unsigned CPU::speed(unsigned addr) const {
|
||||
|
|
|
@ -8,7 +8,7 @@ include sfc/Makefile
|
|||
include gb/Makefile
|
||||
include gba/Makefile
|
||||
|
||||
ui_objects := ui-ethos ui-configuration ui-interface ui-utility
|
||||
ui_objects := ui-higan ui-configuration ui-interface ui-utility
|
||||
ui_objects += ui-input ui-window ui-general ui-settings ui-tools
|
||||
ui_objects += phoenix ruby
|
||||
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
||||
|
@ -44,7 +44,7 @@ link += $(rubylink)
|
|||
objects := $(ui_objects) $(objects)
|
||||
objects := $(patsubst %,obj/%.o,$(objects))
|
||||
|
||||
obj/ui-ethos.o: $(ui)/ethos.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-higan.o: $(ui)/higan.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-configuration.o: $(ui)/configuration/configuration.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-interface.o: $(ui)/interface/interface.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-utility.o: $(ui)/utility/utility.cpp $(call rwildcard,$(ui)/)
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
ConfigurationSettings* config = nullptr;
|
||||
|
||||
ConfigurationSettings::ConfigurationSettings() {
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
#include "library.cpp"
|
||||
#include "presentation.cpp"
|
||||
#include "dip-switches.cpp"
|
|
@ -1,4 +1,4 @@
|
|||
#include "ethos.hpp"
|
||||
#include "higan.hpp"
|
||||
#include "bootstrap.cpp"
|
||||
#include "resource/resource.cpp"
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
#include "hotkeys.cpp"
|
||||
InputManager* inputManager = nullptr;
|
||||
HID::Null hidNull;
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
Interface* interface = nullptr;
|
||||
|
||||
void Interface::loadRequest(unsigned id, string name, string type) {
|
Before Width: | Height: | Size: 611 B After Width: | Height: | Size: 611 B |
Before Width: | Height: | Size: 592 B After Width: | Height: | Size: 592 B |
Before Width: | Height: | Size: 937 B After Width: | Height: | Size: 937 B |
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
Before Width: | Height: | Size: 606 B After Width: | Height: | Size: 606 B |
Before Width: | Height: | Size: 587 B After Width: | Height: | Size: 587 B |
Before Width: | Height: | Size: 812 B After Width: | Height: | Size: 812 B |
Before Width: | Height: | Size: 408 B After Width: | Height: | Size: 408 B |
Before Width: | Height: | Size: 378 B After Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 897 B After Width: | Height: | Size: 897 B |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 652 B After Width: | Height: | Size: 652 B |
Before Width: | Height: | Size: 662 B After Width: | Height: | Size: 662 B |
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
#include "video.cpp"
|
||||
#include "audio.cpp"
|
||||
#include "input.cpp"
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
#include "cheat-database.cpp"
|
||||
#include "cheat-editor.cpp"
|
||||
#include "state-manager.cpp"
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
|
||||
Utility* utility = nullptr;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
#include "../ethos.hpp"
|
||||
#include "../higan.hpp"
|
||||
WindowManager* windowManager = nullptr;
|
||||
|
||||
void WindowManager::append(Window* window, string name) {
|
|
@ -6,7 +6,9 @@ include processor/Makefile
|
|||
include sfc/Makefile
|
||||
include gb/Makefile
|
||||
|
||||
ui_objects := ui-loki ui-interface ui-presentation ui-terminal
|
||||
ui_objects := ui-loki ui-settings
|
||||
ui_objects += ui-interface ui-debugger
|
||||
ui_objects += ui-presentation ui-terminal
|
||||
ui_objects += ruby phoenix
|
||||
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
||||
|
||||
|
@ -30,7 +32,9 @@ objects := $(ui_objects) $(objects)
|
|||
objects := $(patsubst %,obj/%.o,$(objects))
|
||||
|
||||
obj/ui-loki.o: $(ui)/loki.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-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
|
||||
obj/ui-terminal.o: $(ui)/terminal/terminal.cpp $(call rwildcard,$(ui)/)
|
||||
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
#include "../loki.hpp"
|
||||
Debugger* debugger = nullptr;
|
||||
|
||||
Debugger::Debugger() {
|
||||
debugger = this;
|
||||
SFC::cpu.debugger.op_exec = {&Debugger::cpuExec, this};
|
||||
SFC::cpu.debugger.op_read = {&Debugger::cpuRead, this};
|
||||
SFC::cpu.debugger.op_write = {&Debugger::cpuWrite, this};
|
||||
}
|
||||
|
||||
void Debugger::load() {
|
||||
usageCPU = new uint8_t[0x1000000]();
|
||||
file fp;
|
||||
if(fp.open({interface->pathname, "loki/usage.cpu.bin"}, file::mode::read)) {
|
||||
if(fp.size() == 0x1000000) {
|
||||
fp.read(usageCPU, 0x1000000);
|
||||
}
|
||||
fp.close();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::unload() {
|
||||
if(tracerFile.open()) tracerFile.close();
|
||||
|
||||
file fp;
|
||||
if(fp.open({interface->pathname, "loki/usage.cpu.bin"}, file::mode::write)) {
|
||||
fp.write(usageCPU, 0x1000000);
|
||||
fp.close();
|
||||
}
|
||||
delete[] usageCPU;
|
||||
usageCPU = nullptr;
|
||||
}
|
||||
|
||||
void Debugger::run() {
|
||||
switch(mode) {
|
||||
case Mode::Break:
|
||||
usleep(20 * 1000);
|
||||
break;
|
||||
|
||||
case Mode::Run:
|
||||
emulator->run();
|
||||
break;
|
||||
|
||||
case Mode::Step:
|
||||
emulator->run();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::stop() {
|
||||
mode = Mode::Break;
|
||||
runFor = false;
|
||||
runTo = false;
|
||||
}
|
||||
|
||||
string Debugger::cpuDisassemble() {
|
||||
char text[4096];
|
||||
SFC::cpu.disassemble_opcode(text);
|
||||
return text;
|
||||
}
|
||||
|
||||
string Debugger::cpuDisassemble(unsigned addr, bool e, bool m, bool x) {
|
||||
char text[4096];
|
||||
SFC::cpu.disassemble_opcode(text, addr, e, m, x);
|
||||
return text;
|
||||
}
|
||||
|
||||
void Debugger::cpuExec(uint24 addr) {
|
||||
usageCPU[addr] |= Usage::Execute;
|
||||
if(SFC::cpu.regs.e == 0) usageCPU[addr] &= ~Usage::FlagE;
|
||||
if(SFC::cpu.regs.p.m == 0) usageCPU[addr] &= ~Usage::FlagM;
|
||||
if(SFC::cpu.regs.p.x == 0) usageCPU[addr] &= ~Usage::FlagX;
|
||||
if(SFC::cpu.regs.e == 1) usageCPU[addr] |= Usage::FlagE;
|
||||
if(SFC::cpu.regs.p.m == 1) usageCPU[addr] |= Usage::FlagM;
|
||||
if(SFC::cpu.regs.p.x == 1) usageCPU[addr] |= Usage::FlagX;
|
||||
|
||||
if(tracerFile.open()) {
|
||||
tracerFile.print(cpuDisassemble(), "\n");
|
||||
}
|
||||
|
||||
for(unsigned n = 0; n < breakpoints.size(); n++) {
|
||||
auto& bp = breakpoints[n];
|
||||
if(bp.mode != Breakpoint::Mode::Execute) continue;
|
||||
if(bp.addr != addr) continue;
|
||||
echo("Breakpoint #", n, " triggered (", ++bp.triggered, " time(s))\n");
|
||||
echo(cpuDisassemble(), "\n");
|
||||
stop();
|
||||
SFC::scheduler.debug();
|
||||
}
|
||||
|
||||
if(mode == Mode::Run) {
|
||||
if(runFor) {
|
||||
if(--runFor() == 0) {
|
||||
echo(cpuDisassemble(), "\n");
|
||||
stop();
|
||||
SFC::scheduler.debug();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(runTo) {
|
||||
if(addr == runTo()) {
|
||||
echo(cpuDisassemble(), "\n");
|
||||
stop();
|
||||
SFC::scheduler.debug();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(mode == Mode::Step) {
|
||||
echo(cpuDisassemble(), "\n");
|
||||
if(--stepDuration == 0) {
|
||||
stop();
|
||||
SFC::scheduler.debug();
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::cpuRead(uint24 addr, uint8 data) {
|
||||
usageCPU[addr] |= Usage::Read;
|
||||
|
||||
for(unsigned n = 0; n < breakpoints.size(); n++) {
|
||||
auto& bp = breakpoints[n];
|
||||
if(bp.mode != Breakpoint::Mode::Read) continue;
|
||||
if(bp.addr != addr) continue;
|
||||
if(bp.data && bp.data() != data) continue;
|
||||
echo("Breakpoint #", n, " triggered (", ++bp.triggered, " time(s))\n");
|
||||
echo("Read ", hex<2>(data), "\n");
|
||||
stop();
|
||||
SFC::scheduler.debug();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::cpuWrite(uint24 addr, uint8 data) {
|
||||
usageCPU[addr] |= Usage::Write;
|
||||
|
||||
for(unsigned n = 0; n < breakpoints.size(); n++) {
|
||||
auto& bp = breakpoints[n];
|
||||
if(bp.mode != Breakpoint::Mode::Write) continue;
|
||||
if(bp.addr != addr) continue;
|
||||
if(bp.data && bp.data() != data) continue;
|
||||
echo("Breakpoint #", n, " triggered (", ++bp.triggered, " time(s))\n");
|
||||
echo("Wrote ", hex<2>(data), "\n");
|
||||
stop();
|
||||
SFC::scheduler.debug();
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::echoBreakpoints() {
|
||||
if(breakpoints.size() == 0) return;
|
||||
echo("# type addr data triggered\n");
|
||||
echo("--- -------- ------ ---- ---------\n");
|
||||
for(unsigned n = 0; n < breakpoints.size(); n++) {
|
||||
auto& bp = breakpoints[n];
|
||||
string output = {format<-3>(n), " "};
|
||||
if(bp.mode == Breakpoint::Mode::Disabled) output.append("disabled ");
|
||||
if(bp.mode == Breakpoint::Mode::Read ) output.append("read ");
|
||||
if(bp.mode == Breakpoint::Mode::Write ) output.append("write ");
|
||||
if(bp.mode == Breakpoint::Mode::Execute ) output.append("execute ");
|
||||
output.append(hex<6>(bp.addr), " ");
|
||||
output.append(bp.data ? hex<2>(bp.data()) : " ", " ");
|
||||
output.append(format<-9>(bp.triggered));
|
||||
echo(output, "\n");
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::echoDisassemble(unsigned addr, signed size) {
|
||||
if(!(usageCPU[addr] & Usage::Execute)) {
|
||||
echo("No usage data available for this address\n");
|
||||
return;
|
||||
}
|
||||
|
||||
while(size > 0) {
|
||||
string text = cpuDisassemble(addr, usageCPU[addr] & Usage::FlagE, usageCPU[addr] & Usage::FlagM, usageCPU[addr] & Usage::FlagX);
|
||||
echo(text, "\n");
|
||||
if(--size <= 0) break;
|
||||
|
||||
unsigned displacement = 1;
|
||||
while(displacement < 5) { //maximum opcode length is four bytes
|
||||
if(usageCPU[addr + displacement] & Usage::Execute) break;
|
||||
displacement++;
|
||||
}
|
||||
if(displacement >= 5) {
|
||||
echo("...\n");
|
||||
return;
|
||||
}
|
||||
addr += displacement;
|
||||
}
|
||||
}
|
||||
|
||||
void Debugger::echoHex(unsigned addr, signed size) {
|
||||
while(size > 0) {
|
||||
string hexdata, asciidata;
|
||||
for(unsigned n = 0; n < 16; n++) {
|
||||
unsigned offset = addr;
|
||||
if((offset & 0x40e000) == 0x002000 || (offset & 0x40e000) == 0x004000) {
|
||||
//$00-3f,80-bf:2000-5fff
|
||||
//reading MMIO registers can negatively impact emulation, so disallow these reads
|
||||
hexdata.append("?? ");
|
||||
asciidata.append("?");
|
||||
} else {
|
||||
uint8_t byte = SFC::bus.read(addr + n);
|
||||
hexdata.append(hex<2>(byte), " ");
|
||||
asciidata.append(byte >= 0x20 && byte <= 0x7e ? (char)byte : '.');
|
||||
}
|
||||
}
|
||||
echo(hex<6>(addr), " [ ", hexdata, "] ", asciidata, "\n");
|
||||
addr += 16, size -= 16;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t Debugger::read(unsigned addr) {
|
||||
return SFC::bus.read(addr);
|
||||
}
|
||||
|
||||
void Debugger::write(unsigned addr, uint8_t data) {
|
||||
return SFC::bus.write(addr, data);
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
struct Debugger {
|
||||
Debugger();
|
||||
|
||||
void load();
|
||||
void unload();
|
||||
|
||||
void run();
|
||||
void stop();
|
||||
|
||||
string cpuDisassemble();
|
||||
string cpuDisassemble(unsigned addr, bool e, bool m, bool x);
|
||||
void cpuExec(uint24 addr);
|
||||
void cpuRead(uint24 addr, uint8 data);
|
||||
void cpuWrite(uint24 addr, uint8 data);
|
||||
void echoBreakpoints();
|
||||
void echoDisassemble(unsigned addr, signed size);
|
||||
void echoHex(unsigned addr, signed size);
|
||||
uint8_t read(unsigned addr);
|
||||
void write(unsigned addr, uint8_t data);
|
||||
|
||||
enum class Mode : unsigned { Break, Run, Step } mode = Mode::Break;
|
||||
|
||||
struct Breakpoint {
|
||||
enum class Mode : unsigned { Disabled, Read, Write, Execute } mode = Mode::Disabled;
|
||||
unsigned addr = 0;
|
||||
optional<uint8_t> data = false;
|
||||
unsigned triggered = 0; //counter for number of times breakpoint was hit
|
||||
};
|
||||
|
||||
struct Usage {
|
||||
enum : unsigned {
|
||||
Read = 0x01,
|
||||
Write = 0x02,
|
||||
Execute = 0x04,
|
||||
FlagE = 0x08,
|
||||
FlagM = 0x10,
|
||||
FlagX = 0x20,
|
||||
};
|
||||
};
|
||||
|
||||
vector<Breakpoint> breakpoints;
|
||||
optional<unsigned> runFor = false;
|
||||
optional<unsigned> runTo = false;
|
||||
unsigned stepDuration = 0;
|
||||
file tracerFile;
|
||||
bool tracerMask = false;
|
||||
uint8_t* usageCPU = nullptr;
|
||||
};
|
||||
|
||||
extern Debugger* debugger;
|
|
@ -13,19 +13,21 @@ bool Interface::load(string pathname) {
|
|||
if(!directory::exists(pathname)) return false;
|
||||
|
||||
string type = extension(pathname);
|
||||
pathname.append("/");
|
||||
|
||||
for(auto& media : emulator->media) {
|
||||
if(media.bootable == false) continue;
|
||||
if(type != media.type) continue;
|
||||
|
||||
this->pathname = pathname.append("/");
|
||||
pathnames.reset();
|
||||
pathnames(0) = program->path({media.name, ".sys/"});
|
||||
pathnames(media.id) = pathname;
|
||||
echo("Loaded ", pathname, "\n");
|
||||
|
||||
emulator->load(media.id);
|
||||
emulator->paletteUpdate(Emulator::Interface::PaletteMode::Standard);
|
||||
emulator->power();
|
||||
presentation->setTitle(emulator->title());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -66,12 +68,14 @@ void Interface::loadRequest(unsigned id, string path) {
|
|||
if(file::exists(pathname) == false) return;
|
||||
mmapstream stream(pathname);
|
||||
emulator->load(id, stream);
|
||||
echo("Loaded ", path, "\n");
|
||||
}
|
||||
|
||||
void Interface::saveRequest(unsigned id, string path) {
|
||||
string pathname = {pathnames(emulator->group(id)), path};
|
||||
filestream stream(pathname, file::mode::write);
|
||||
emulator->save(id, stream);
|
||||
echo("Saved ", path, "\n");
|
||||
}
|
||||
|
||||
uint32_t Interface::videoColor(unsigned source, uint16_t alpha, uint16_t red, uint16_t green, uint16_t blue) {
|
||||
|
@ -101,6 +105,7 @@ void Interface::videoRefresh(const uint32_t* palette, const uint32_t* data, unsi
|
|||
}
|
||||
|
||||
void Interface::audioSample(int16_t lsample, int16_t rsample) {
|
||||
if(settings->audio.mute) lsample = 0, rsample = 0;
|
||||
signed samples[] = {lsample, rsample};
|
||||
dspaudio.sample(samples);
|
||||
while(dspaudio.pending()) {
|
||||
|
|
|
@ -18,6 +18,7 @@ struct Interface : Emulator::Interface::Bind {
|
|||
string server();
|
||||
void notify(string text);
|
||||
|
||||
string pathname; //path to game folder
|
||||
lstring pathnames;
|
||||
struct Gamepad {
|
||||
bool up = false;
|
||||
|
|
|
@ -15,12 +15,7 @@ string Program::path(string name) {
|
|||
}
|
||||
|
||||
void Program::main() {
|
||||
if(pause) {
|
||||
usleep(20 * 1000);
|
||||
return;
|
||||
}
|
||||
|
||||
emulator->run();
|
||||
debugger->run();
|
||||
}
|
||||
|
||||
Program::Program(string pathname) {
|
||||
|
@ -31,7 +26,9 @@ Program::Program(string pathname) {
|
|||
sharedpath = {nall::sharedpath(), "loki/"};
|
||||
directory::create(userpath);
|
||||
|
||||
new Settings;
|
||||
new Interface;
|
||||
new Debugger;
|
||||
new Presentation;
|
||||
new Terminal;
|
||||
|
||||
|
@ -39,19 +36,19 @@ Program::Program(string pathname) {
|
|||
terminal->setVisible();
|
||||
Application::processEvents();
|
||||
|
||||
video.driver(video.optimalDriver());
|
||||
video.driver(settings->video.driver);
|
||||
video.set(Video::Handle, presentation->viewport.handle());
|
||||
video.set(Video::Synchronize, false);
|
||||
video.set(Video::Synchronize, settings->video.synchronize);
|
||||
video.set(Video::Filter, Video::FilterNearest);
|
||||
if(video.init() == false) { video.driver("None"); video.init(); }
|
||||
|
||||
audio.driver(audio.optimalDriver());
|
||||
audio.driver(settings->audio.driver);
|
||||
audio.set(Audio::Handle, presentation->viewport.handle());
|
||||
audio.set(Audio::Synchronize, true);
|
||||
audio.set(Audio::Synchronize, settings->audio.synchronize);
|
||||
audio.set(Audio::Frequency, 48000u);
|
||||
if(audio.init() == false) { audio.driver("None"); audio.init(); }
|
||||
|
||||
input.driver(input.optimalDriver());
|
||||
input.driver(settings->input.driver);
|
||||
input.set(Input::Handle, presentation->viewport.handle());
|
||||
if(input.init() == false) { input.driver("None"); input.init(); }
|
||||
input.onChange = {&Interface::inputEvent, interface};
|
||||
|
@ -65,11 +62,14 @@ Program::Program(string pathname) {
|
|||
presentation->showSplash();
|
||||
|
||||
interface->load(pathname);
|
||||
debugger->load();
|
||||
|
||||
Application::main = {&Program::main, this};
|
||||
Application::run();
|
||||
|
||||
debugger->unload();
|
||||
interface->unload();
|
||||
settings->save();
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
#include <sfc/interface/interface.hpp>
|
||||
#include <sfc/sfc.hpp>
|
||||
namespace SFC = SuperFamicom;
|
||||
|
||||
#include <nall/platform.hpp>
|
||||
#include <nall/config.hpp>
|
||||
|
@ -19,7 +20,9 @@ using namespace ruby;
|
|||
#include <phoenix/phoenix.hpp>
|
||||
using namespace phoenix;
|
||||
|
||||
#include "settings/settings.hpp"
|
||||
#include "interface/interface.hpp"
|
||||
#include "debugger/debugger.hpp"
|
||||
#include "presentation/presentation.hpp"
|
||||
#include "terminal/terminal.hpp"
|
||||
#include "resource/resource.hpp"
|
||||
|
@ -29,12 +32,14 @@ struct Program {
|
|||
string userpath;
|
||||
string sharedpath;
|
||||
|
||||
bool pause = true;
|
||||
|
||||
string path(string name);
|
||||
void main();
|
||||
Program(string pathname);
|
||||
};
|
||||
|
||||
template<typename... Args> void echo(Args&&... args) {
|
||||
terminal->print({std::forward<Args>(args)...});
|
||||
}
|
||||
|
||||
extern Program* program;
|
||||
extern DSP dspaudio;
|
||||
|
|
|
@ -14,7 +14,7 @@ Presentation::Presentation() {
|
|||
onClose = &Application::quit;
|
||||
|
||||
splash.allocate(512, 480);
|
||||
splash.verticalGradient(0xff00003f, 0xff000000, 512, 480, 256, 0);
|
||||
splash.verticalGradient(0xff00005f, 0xff000000, 512, 480, 256, 0);
|
||||
nall::image floor;
|
||||
floor.allocate(512, 480);
|
||||
floor.radialGradient(0xffff0000, 0x00000000, 384, 240, 256, 415);
|
||||
|
|
|
@ -0,0 +1,43 @@
|
|||
#include "../loki.hpp"
|
||||
Settings* settings = nullptr;
|
||||
|
||||
Settings::Settings() {
|
||||
settings = this;
|
||||
|
||||
video.append(video.driver = ruby::video.optimalDriver(), "Driver");
|
||||
video.append(video.synchronize = false, "Synchronize");
|
||||
append(video, "Video");
|
||||
|
||||
audio.append(audio.driver = ruby::audio.optimalDriver(), "Driver");
|
||||
audio.append(audio.synchronize = true, "Synchronize");
|
||||
audio.append(audio.mute = false, "Mute");
|
||||
append(audio, "Audio");
|
||||
|
||||
input.append(input.driver = ruby::input.optimalDriver(), "Driver");
|
||||
append(input, "Input");
|
||||
|
||||
load();
|
||||
}
|
||||
|
||||
void Settings::load() {
|
||||
Configuration::Document::load(program->path("settings.bml"));
|
||||
save(); //create configuration file if it does not exist
|
||||
}
|
||||
|
||||
void Settings::save() {
|
||||
Configuration::Document::save(program->path("settings.bml"));
|
||||
}
|
||||
|
||||
void Settings::command(string s, lstring args) {
|
||||
unsigned argc = args.size();
|
||||
s.ltrim<1>("settings.");
|
||||
|
||||
if(s == "video.driver" && argc == 1) { video.driver = args[0]; return; }
|
||||
if(s == "video.synchronize" && argc == 1) { video.synchronize = args[0] != "false"; ruby::video.set(ruby::Video::Synchronize, video.synchronize); return; }
|
||||
if(s == "audio.driver" && argc == 1) { audio.driver = args[0]; return; }
|
||||
if(s == "audio.synchronize" && argc == 1) { audio.synchronize = args[0] != "false"; ruby::audio.set(ruby::Audio::Synchronize, audio.synchronize); return; }
|
||||
if(s == "audio.mute" && argc == 1) { audio.mute = args[0] != "false"; return; }
|
||||
if(s == "input.driver" && argc == 1) { input.driver = args[0]; return; }
|
||||
|
||||
echo("Error: unrecognized setting: ", s, "\n");
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
struct Settings : Configuration::Document {
|
||||
struct Video : Configuration::Node {
|
||||
string driver;
|
||||
bool synchronize;
|
||||
} video;
|
||||
|
||||
struct Audio : Configuration::Node {
|
||||
string driver;
|
||||
bool synchronize;
|
||||
bool mute;
|
||||
} audio;
|
||||
|
||||
struct Input : Configuration::Node {
|
||||
string driver;
|
||||
} input;
|
||||
|
||||
Settings();
|
||||
void load();
|
||||
void save();
|
||||
void command(string s, lstring args);
|
||||
};
|
||||
|
||||
extern Settings* settings;
|
|
@ -4,11 +4,11 @@ Terminal* terminal = nullptr;
|
|||
Terminal::Terminal() {
|
||||
terminal = this;
|
||||
|
||||
setTitle({"loki v", Emulator::Version});
|
||||
setWindowGeometry({0, 480 + frameMargin().height, 800, 480});
|
||||
|
||||
console.setFont(Font::monospace(8));
|
||||
print("loki v", Emulator::Version, "\n\n");
|
||||
print("$ ");
|
||||
console.setPrompt("$ ");
|
||||
|
||||
layout.append(console, {~0, ~0});
|
||||
append(layout);
|
||||
|
@ -17,26 +17,151 @@ Terminal::Terminal() {
|
|||
console.onActivate = {&Terminal::command, this};
|
||||
}
|
||||
|
||||
void Terminal::command(string s) {
|
||||
if(s.empty()) {
|
||||
} else if(s == "quit" || s == "exit") {
|
||||
Application::quit();
|
||||
} else if(s == "clear" || s == "reset") {
|
||||
reset();
|
||||
} else if(s == "r") {
|
||||
program->pause = false;
|
||||
} else if(s == "p") {
|
||||
program->pause = true;
|
||||
} else {
|
||||
print("unrecognized command\n");
|
||||
void Terminal::command(string t) {
|
||||
lstring args = t.qsplit(" ");
|
||||
string s = args.takeFirst();
|
||||
unsigned argc = args.size();
|
||||
|
||||
if(s.empty()) return;
|
||||
|
||||
if(s.beginsWith("settings.")) return settings->command(s, args);
|
||||
|
||||
if(s == "break") {
|
||||
debugger->stop();
|
||||
return;
|
||||
}
|
||||
print("$ ");
|
||||
|
||||
if(s == "breakpoints") {
|
||||
debugger->echoBreakpoints();
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "breakpoints.append" && argc >= 2 && argc <= 3) {
|
||||
Debugger::Breakpoint bp;
|
||||
if(args[0] == "read" ) bp.mode = Debugger::Breakpoint::Mode::Read;
|
||||
if(args[0] == "write" ) bp.mode = Debugger::Breakpoint::Mode::Write;
|
||||
if(args[0] == "execute") bp.mode = Debugger::Breakpoint::Mode::Execute;
|
||||
bp.addr = hex(args[1]);
|
||||
if(argc >= 3) bp.data = {true, (uint8_t)hex(args[2])};
|
||||
debugger->breakpoints.append(bp);
|
||||
debugger->echoBreakpoints();
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "breakpoints.remove" && argc == 1) {
|
||||
unsigned n = decimal(args[0]);
|
||||
if(n < debugger->breakpoints.size()) {
|
||||
debugger->breakpoints.remove(n);
|
||||
}
|
||||
debugger->echoBreakpoints();
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "breakpoints.reset") {
|
||||
debugger->breakpoints.reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "clear") {
|
||||
reset();
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "disassemble" && argc >= 1 && argc <= 2) {
|
||||
debugger->echoDisassemble(hex(args[0]), argc == 2 ? decimal(args[1]) : 16);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "hex" && argc >= 1 && argc <= 2) {
|
||||
debugger->echoHex(hex(args[0]), argc == 2 ? decimal(args[1]) : 256);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "quit") {
|
||||
Application::quit();
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "read" && argc == 1) {
|
||||
unsigned addr = hex(args[0]);
|
||||
uint8_t data = debugger->read(addr);
|
||||
echo(hex<6>(addr), " = ", hex<2>(data), "\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "run") {
|
||||
debugger->mode = Debugger::Mode::Run;
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "run.for" && argc == 1) {
|
||||
debugger->mode = Debugger::Mode::Run;
|
||||
debugger->runFor = {true, (unsigned)decimal(args[0])};
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "run.to" && argc == 1) {
|
||||
debugger->mode == Debugger::Mode::Run;
|
||||
debugger->runTo = {true, (unsigned)hex(args[0])};
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "step" && argc <= 1) {
|
||||
if(debugger->mode != Debugger::Mode::Break) {
|
||||
echo("Error: must break before stepping\n");
|
||||
return;
|
||||
}
|
||||
debugger->mode = Debugger::Mode::Step;
|
||||
debugger->stepDuration = (argc == 1 ? decimal(args[0]) : 1);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "tracer.enable" && argc <= 1) {
|
||||
if(debugger->tracerFile.open() == false) {
|
||||
string filename = {"trace-", string::datetime().transform(" :", "--"), ".log"};
|
||||
if(argc >= 1) filename = args[0];
|
||||
string pathname = {interface->pathname, "loki/"};
|
||||
directory::create(pathname);
|
||||
pathname.append(filename);
|
||||
if(debugger->tracerFile.open(pathname, file::mode::write)) {
|
||||
echo("Tracer enabled\n");
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "tracer.disable") {
|
||||
if(debugger->tracerFile.open() == true) {
|
||||
debugger->tracerFile.close();
|
||||
echo("Tracer disabled\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "tracer.mask" && argc == 1) {
|
||||
debugger->tracerMask = args[0] != "false";
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "usage.reset") {
|
||||
memset(debugger->usageCPU, 0, 0x1000000);
|
||||
return;
|
||||
}
|
||||
|
||||
if(s == "write" && argc == 2) {
|
||||
unsigned addr = hex(args[0]);
|
||||
uint8_t data = hex(args[1]);
|
||||
debugger->write(addr, data);
|
||||
return;
|
||||
}
|
||||
|
||||
echo("Error: unrecognized command: ", s, "\n");
|
||||
}
|
||||
|
||||
void Terminal::reset() {
|
||||
console.reset();
|
||||
}
|
||||
|
||||
template<typename... Args> void Terminal::print(Args&&... args) {
|
||||
console.print(std::forward<Args>(args)...);
|
||||
void Terminal::print(const string& text) {
|
||||
console.print(text);
|
||||
}
|
||||
|
|