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
|
gba := gba
|
||||||
|
|
||||||
profile := accuracy
|
profile := accuracy
|
||||||
target := ethos
|
target := higan
|
||||||
# target := loki
|
# target := loki
|
||||||
|
|
||||||
# options += debugger
|
ifeq ($(target),loki)
|
||||||
|
options += debugger
|
||||||
|
endif
|
||||||
|
|
||||||
# arch := x86
|
# arch := x86
|
||||||
# console := true
|
# console := true
|
||||||
|
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const char Name[] = "higan";
|
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 Author[] = "byuu";
|
||||||
static const char License[] = "GPLv3";
|
static const char License[] = "GPLv3";
|
||||||
static const char Website[] = "http://byuu.org/";
|
static const char Website[] = "http://byuu.org/";
|
||||||
|
|
|
@ -38,18 +38,18 @@ endif
|
||||||
ifeq ($(compiler),)
|
ifeq ($(compiler),)
|
||||||
ifeq ($(platform),windows)
|
ifeq ($(platform),windows)
|
||||||
compiler := g++
|
compiler := g++
|
||||||
flags :=
|
flags := -fwrapv
|
||||||
link :=
|
link :=
|
||||||
else ifeq ($(platform),macosx)
|
else ifeq ($(platform),macosx)
|
||||||
compiler := clang++
|
compiler := clang++
|
||||||
flags := -w -stdlib=libc++
|
flags := -fwrapv -w -stdlib=libc++
|
||||||
link := -lc++ -lobjc
|
link := -lc++ -lobjc
|
||||||
else ifeq ($(platform),bsd)
|
else ifeq ($(platform),bsd)
|
||||||
compiler := clang++
|
compiler := clang++
|
||||||
flags := -w -I/usr/local/include
|
flags := -fwrapv -w -I/usr/local/include
|
||||||
else
|
else
|
||||||
compiler := g++
|
compiler := g++
|
||||||
flags :=
|
flags := -fwrapv
|
||||||
link :=
|
link :=
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -73,13 +73,13 @@ namespace Math {
|
||||||
//================
|
//================
|
||||||
|
|
||||||
#if defined(__clang__) || defined(__GNUC__)
|
#if defined(__clang__) || defined(__GNUC__)
|
||||||
#define noinline __attribute__((noinline))
|
#define neverinline __attribute__((noinline))
|
||||||
#define alwaysinline inline __attribute__((always_inline))
|
#define alwaysinline inline __attribute__((always_inline))
|
||||||
#elif defined(_MSC_VER)
|
#elif defined(_MSC_VER)
|
||||||
#define noinline __declspec(noinline)
|
#define neverinline __declspec(noinline)
|
||||||
#define alwaysinline inline __forceinline
|
#define alwaysinline inline __forceinline
|
||||||
#else
|
#else
|
||||||
#define noinline
|
#define neverinline
|
||||||
#define alwaysinline inline
|
#define alwaysinline inline
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
|
|
@ -102,8 +102,7 @@ template<typename TT> struct optional {
|
||||||
|
|
||||||
optional& operator=(const optional& source) {
|
optional& operator=(const optional& source) {
|
||||||
reset();
|
reset();
|
||||||
valid = source.valid;
|
if(source) operator=(source());
|
||||||
if(valid) operator=(source);
|
|
||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,9 @@ void pConsole::print(string text) {
|
||||||
void pConsole::reset() {
|
void pConsole::reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pConsole::setPrompt(string prompt) {
|
||||||
|
}
|
||||||
|
|
||||||
void pConsole::constructor() {
|
void pConsole::constructor() {
|
||||||
@autoreleasepool {
|
@autoreleasepool {
|
||||||
cocoaView = cocoaConsole = [[CocoaConsole alloc] initWith:console];
|
cocoaView = cocoaConsole = [[CocoaConsole alloc] initWith:console];
|
||||||
|
|
|
@ -13,6 +13,7 @@ struct pConsole : public pWidget {
|
||||||
|
|
||||||
void print(string text);
|
void print(string text);
|
||||||
void reset();
|
void reset();
|
||||||
|
void setPrompt(string prompt);
|
||||||
|
|
||||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||||
void constructor();
|
void constructor();
|
||||||
|
|
|
@ -1221,10 +1221,19 @@ void Console::print(const string& text) {
|
||||||
return p.print(text);
|
return p.print(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string Console::prompt() const {
|
||||||
|
return state.prompt;
|
||||||
|
}
|
||||||
|
|
||||||
void Console::reset() {
|
void Console::reset() {
|
||||||
return p.reset();
|
return p.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Console::setPrompt(const string& prompt) {
|
||||||
|
state.prompt = prompt;
|
||||||
|
return p.setPrompt(prompt);
|
||||||
|
}
|
||||||
|
|
||||||
Console::Console():
|
Console::Console():
|
||||||
state(*new State),
|
state(*new State),
|
||||||
base_from_member<pConsole&>(*new pConsole(*this)),
|
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...}); }
|
template<typename... Args> void print(Args&&... args) { print({args...}); }
|
||||||
|
|
||||||
void print(const nall::string& text);
|
void print(const nall::string& text);
|
||||||
|
nall::string prompt() const;
|
||||||
void reset();
|
void reset();
|
||||||
|
void setPrompt(const nall::string& prompt);
|
||||||
|
|
||||||
Console();
|
Console();
|
||||||
~Console();
|
~Console();
|
||||||
|
|
|
@ -125,6 +125,7 @@ struct ComboButton::State {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Console::State {
|
struct Console::State {
|
||||||
|
string prompt;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Frame::State {
|
struct Frame::State {
|
||||||
|
|
|
@ -348,9 +348,11 @@ struct pConsole : public pWidget {
|
||||||
GtkWidget* subWidget;
|
GtkWidget* subWidget;
|
||||||
GtkTextBuffer* textBuffer;
|
GtkTextBuffer* textBuffer;
|
||||||
string command;
|
string command;
|
||||||
|
string previousPrompt;
|
||||||
|
|
||||||
void print(string text);
|
void print(string text);
|
||||||
void reset();
|
void reset();
|
||||||
|
void setPrompt(string prompt);
|
||||||
|
|
||||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||||
void constructor();
|
void constructor();
|
||||||
|
|
|
@ -5,14 +5,34 @@ static bool Console_keyPress(GtkWidget* widget, GdkEventKey* event, Console* sel
|
||||||
}
|
}
|
||||||
|
|
||||||
void pConsole::print(string text) {
|
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();
|
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() {
|
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() {
|
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(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) {
|
if(scancode == GDK_KEY_Return || scancode == GDK_KEY_KP_Enter) {
|
||||||
print("\n");
|
//add current prompt and command to history; print new prompt; execute command
|
||||||
if(console.onActivate) console.onActivate(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();
|
command.reset();
|
||||||
|
if(console.onActivate) console.onActivate(s);
|
||||||
|
seekCursorToEnd();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(scancode == GDK_KEY_BackSpace) {
|
if(scancode == GDK_KEY_BackSpace) {
|
||||||
if(command.size()) {
|
if(command.size()) {
|
||||||
|
//delete last character of command
|
||||||
command.resize(command.size() - 1);
|
command.resize(command.size() - 1);
|
||||||
GtkTextIter lhs, rhs;
|
GtkTextIter lhs, rhs;
|
||||||
gtk_text_buffer_get_end_iter(textBuffer, &lhs);
|
gtk_text_buffer_get_end_iter(textBuffer, &lhs);
|
||||||
gtk_text_buffer_get_end_iter(textBuffer, &rhs);
|
gtk_text_buffer_get_end_iter(textBuffer, &rhs);
|
||||||
gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&lhs) - 1);
|
gtk_text_iter_set_offset(&lhs, gtk_text_iter_get_offset(&lhs) - 1);
|
||||||
gtk_text_buffer_delete(textBuffer, &lhs, &rhs);
|
gtk_text_buffer_delete(textBuffer, &lhs, &rhs);
|
||||||
seekCursorToEnd();
|
|
||||||
}
|
}
|
||||||
|
seekCursorToEnd();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(scancode >= 0x20 && scancode <= 0x7e) {
|
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);
|
command.append((char)scancode);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -80,9 +110,12 @@ bool pConsole::keyPress(unsigned scancode, unsigned mask) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void pConsole::seekCursorToEnd() {
|
void pConsole::seekCursorToEnd() {
|
||||||
|
//place cursor at end of text buffer; scroll text view to the cursor to ensure it is visible
|
||||||
GtkTextIter iter;
|
GtkTextIter iter;
|
||||||
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
gtk_text_buffer_get_end_iter(textBuffer, &iter);
|
||||||
gtk_text_buffer_place_cursor(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'
|
** 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)
|
** by: The Qt Meta Object Compiler version 63 (Qt 4.8.2)
|
||||||
**
|
**
|
||||||
** WARNING! All changes made in this file will be lost!
|
** WARNING! All changes made in this file will be lost!
|
||||||
|
|
|
@ -432,6 +432,7 @@ public:
|
||||||
|
|
||||||
void print(string text);
|
void print(string text);
|
||||||
void reset();
|
void reset();
|
||||||
|
void setPrompt(string prompt);
|
||||||
|
|
||||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||||
void constructor();
|
void constructor();
|
||||||
|
|
|
@ -6,6 +6,9 @@ void pConsole::print(string text) {
|
||||||
void pConsole::reset() {
|
void pConsole::reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pConsole::setPrompt(string prompt) {
|
||||||
|
}
|
||||||
|
|
||||||
void pConsole::constructor() {
|
void pConsole::constructor() {
|
||||||
qtWidget = qtConsole = new QtConsole(*this);
|
qtWidget = qtConsole = new QtConsole(*this);
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,9 @@ void pConsole::print(string text) {
|
||||||
void pConsole::reset() {
|
void pConsole::reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pConsole::setPrompt(string prompt) {
|
||||||
|
}
|
||||||
|
|
||||||
void pConsole::constructor() {
|
void pConsole::constructor() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ struct pConsole : public pWidget {
|
||||||
|
|
||||||
void print(string text);
|
void print(string text);
|
||||||
void reset();
|
void reset();
|
||||||
|
void setPrompt(string prompt);
|
||||||
|
|
||||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||||
void constructor();
|
void constructor();
|
||||||
|
|
|
@ -363,6 +363,7 @@ struct pConsole : public pWidget {
|
||||||
|
|
||||||
void print(string text);
|
void print(string text);
|
||||||
void reset();
|
void reset();
|
||||||
|
void setPrompt(string prompt);
|
||||||
|
|
||||||
pConsole(Console& console) : pWidget(console), console(console) {}
|
pConsole(Console& console) : pWidget(console), console(console) {}
|
||||||
void constructor();
|
void constructor();
|
||||||
|
|
|
@ -14,6 +14,9 @@ void pConsole::print(string text) {
|
||||||
void pConsole::reset() {
|
void pConsole::reset() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void pConsole::setPrompt(string prompt) {
|
||||||
|
}
|
||||||
|
|
||||||
void pConsole::constructor() {
|
void pConsole::constructor() {
|
||||||
hwnd = CreateWindowEx(
|
hwnd = CreateWindowEx(
|
||||||
WS_EX_CLIENTEDGE, L"EDIT", L"",
|
WS_EX_CLIENTEDGE, L"EDIT", L"",
|
||||||
|
|
|
@ -102,7 +102,11 @@ uint32 R65816::decode(uint8 offset_type, uint32 addr) {
|
||||||
return(r & 0xffffff);
|
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;
|
static reg24_t pc;
|
||||||
char t[256];
|
char t[256];
|
||||||
char* s = output;
|
char* s = output;
|
||||||
|
@ -123,8 +127,8 @@ void R65816::disassemble_opcode(char* output, uint32 addr) {
|
||||||
#define op8 ((op0))
|
#define op8 ((op0))
|
||||||
#define op16 ((op0) | (op1 << 8))
|
#define op16 ((op0) | (op1 << 8))
|
||||||
#define op24 ((op0) | (op1 << 8) | (op2 << 16))
|
#define op24 ((op0) | (op1 << 8) | (op2 << 16))
|
||||||
#define a8 (regs.e || regs.p.m)
|
#define a8 (e || m)
|
||||||
#define x8 (regs.e || regs.p.x)
|
#define x8 (e || x)
|
||||||
|
|
||||||
switch(op) {
|
switch(op) {
|
||||||
case 0x00: sprintf(t, "brk #$%.2x ", op8); break;
|
case 0x00: sprintf(t, "brk #$%.2x ", op8); break;
|
||||||
|
|
|
@ -22,7 +22,8 @@ enum : unsigned {
|
||||||
OPTYPE_RELW, //relw
|
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);
|
uint8 dreadb(uint32 addr);
|
||||||
uint16 dreadw(uint32 addr);
|
uint16 dreadw(uint32 addr);
|
||||||
uint32 dreadl(uint32 addr);
|
uint32 dreadl(uint32 addr);
|
||||||
|
|
|
@ -88,7 +88,7 @@ void CPU::op_step() {
|
||||||
debugger.op_exec(regs.pc.d);
|
debugger.op_exec(regs.pc.d);
|
||||||
if(interface->tracer.open()) {
|
if(interface->tracer.open()) {
|
||||||
char text[4096];
|
char text[4096];
|
||||||
disassemble_opcode(text, regs.pc.d);
|
disassemble_opcode(text);
|
||||||
interface->tracer.print(text, "\n");
|
interface->tracer.print(text, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -136,7 +136,7 @@ privileged:
|
||||||
|
|
||||||
struct Debugger {
|
struct Debugger {
|
||||||
hook<void (uint24)> op_exec;
|
hook<void (uint24)> op_exec;
|
||||||
hook<void (uint24)> op_read;
|
hook<void (uint24, uint8)> op_read;
|
||||||
hook<void (uint24, uint8)> op_write;
|
hook<void (uint24, uint8)> op_write;
|
||||||
hook<void ()> op_nmi;
|
hook<void ()> op_nmi;
|
||||||
hook<void ()> op_irq;
|
hook<void ()> op_irq;
|
||||||
|
|
|
@ -11,25 +11,23 @@ void CPU::op_io() {
|
||||||
}
|
}
|
||||||
|
|
||||||
uint8 CPU::op_read(uint32 addr) {
|
uint8 CPU::op_read(uint32 addr) {
|
||||||
debugger.op_read(addr);
|
|
||||||
|
|
||||||
status.clock_count = speed(addr);
|
status.clock_count = speed(addr);
|
||||||
dma_edge();
|
dma_edge();
|
||||||
add_clocks(status.clock_count - 4);
|
add_clocks(status.clock_count - 4);
|
||||||
regs.mdr = bus.read(addr);
|
regs.mdr = bus.read(addr);
|
||||||
add_clocks(4);
|
add_clocks(4);
|
||||||
alu_edge();
|
alu_edge();
|
||||||
|
debugger.op_read(addr, regs.mdr);
|
||||||
return regs.mdr;
|
return regs.mdr;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::op_write(uint32 addr, uint8 data) {
|
void CPU::op_write(uint32 addr, uint8 data) {
|
||||||
debugger.op_write(addr, data);
|
|
||||||
|
|
||||||
alu_edge();
|
alu_edge();
|
||||||
status.clock_count = speed(addr);
|
status.clock_count = speed(addr);
|
||||||
dma_edge();
|
dma_edge();
|
||||||
add_clocks(status.clock_count);
|
add_clocks(status.clock_count);
|
||||||
bus.write(addr, regs.mdr = data);
|
bus.write(addr, regs.mdr = data);
|
||||||
|
debugger.op_write(addr, regs.mdr);
|
||||||
}
|
}
|
||||||
|
|
||||||
unsigned CPU::speed(unsigned addr) const {
|
unsigned CPU::speed(unsigned addr) const {
|
||||||
|
|
|
@ -8,7 +8,7 @@ include sfc/Makefile
|
||||||
include gb/Makefile
|
include gb/Makefile
|
||||||
include gba/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 += ui-input ui-window ui-general ui-settings ui-tools
|
||||||
ui_objects += phoenix ruby
|
ui_objects += phoenix ruby
|
||||||
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
||||||
|
@ -44,7 +44,7 @@ link += $(rubylink)
|
||||||
objects := $(ui_objects) $(objects)
|
objects := $(ui_objects) $(objects)
|
||||||
objects := $(patsubst %,obj/%.o,$(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-configuration.o: $(ui)/configuration/configuration.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-utility.o: $(ui)/utility/utility.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* config = nullptr;
|
||||||
|
|
||||||
ConfigurationSettings::ConfigurationSettings() {
|
ConfigurationSettings::ConfigurationSettings() {
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../ethos.hpp"
|
#include "../higan.hpp"
|
||||||
#include "library.cpp"
|
#include "library.cpp"
|
||||||
#include "presentation.cpp"
|
#include "presentation.cpp"
|
||||||
#include "dip-switches.cpp"
|
#include "dip-switches.cpp"
|
|
@ -1,4 +1,4 @@
|
||||||
#include "ethos.hpp"
|
#include "higan.hpp"
|
||||||
#include "bootstrap.cpp"
|
#include "bootstrap.cpp"
|
||||||
#include "resource/resource.cpp"
|
#include "resource/resource.cpp"
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../ethos.hpp"
|
#include "../higan.hpp"
|
||||||
#include "hotkeys.cpp"
|
#include "hotkeys.cpp"
|
||||||
InputManager* inputManager = nullptr;
|
InputManager* inputManager = nullptr;
|
||||||
HID::Null hidNull;
|
HID::Null hidNull;
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../ethos.hpp"
|
#include "../higan.hpp"
|
||||||
Interface* interface = nullptr;
|
Interface* interface = nullptr;
|
||||||
|
|
||||||
void Interface::loadRequest(unsigned id, string name, string type) {
|
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 "video.cpp"
|
||||||
#include "audio.cpp"
|
#include "audio.cpp"
|
||||||
#include "input.cpp"
|
#include "input.cpp"
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../ethos.hpp"
|
#include "../higan.hpp"
|
||||||
#include "cheat-database.cpp"
|
#include "cheat-database.cpp"
|
||||||
#include "cheat-editor.cpp"
|
#include "cheat-editor.cpp"
|
||||||
#include "state-manager.cpp"
|
#include "state-manager.cpp"
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../ethos.hpp"
|
#include "../higan.hpp"
|
||||||
|
|
||||||
Utility* utility = nullptr;
|
Utility* utility = nullptr;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
#include "../ethos.hpp"
|
#include "../higan.hpp"
|
||||||
WindowManager* windowManager = nullptr;
|
WindowManager* windowManager = nullptr;
|
||||||
|
|
||||||
void WindowManager::append(Window* window, string name) {
|
void WindowManager::append(Window* window, string name) {
|
|
@ -6,7 +6,9 @@ include processor/Makefile
|
||||||
include sfc/Makefile
|
include sfc/Makefile
|
||||||
include gb/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 += ruby phoenix
|
||||||
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
ui_objects += $(if $(call streq,$(platform),windows),resource)
|
||||||
|
|
||||||
|
@ -30,7 +32,9 @@ objects := $(ui_objects) $(objects)
|
||||||
objects := $(patsubst %,obj/%.o,$(objects))
|
objects := $(patsubst %,obj/%.o,$(objects))
|
||||||
|
|
||||||
obj/ui-loki.o: $(ui)/loki.cpp $(call rwildcard,$(ui)/)
|
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-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-presentation.o: $(ui)/presentation/presentation.cpp $(call rwildcard,$(ui)/)
|
||||||
obj/ui-terminal.o: $(ui)/terminal/terminal.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;
|
if(!directory::exists(pathname)) return false;
|
||||||
|
|
||||||
string type = extension(pathname);
|
string type = extension(pathname);
|
||||||
pathname.append("/");
|
|
||||||
|
|
||||||
for(auto& media : emulator->media) {
|
for(auto& media : emulator->media) {
|
||||||
if(media.bootable == false) continue;
|
if(media.bootable == false) continue;
|
||||||
if(type != media.type) continue;
|
if(type != media.type) continue;
|
||||||
|
|
||||||
|
this->pathname = pathname.append("/");
|
||||||
pathnames.reset();
|
pathnames.reset();
|
||||||
pathnames(0) = program->path({media.name, ".sys/"});
|
pathnames(0) = program->path({media.name, ".sys/"});
|
||||||
pathnames(media.id) = pathname;
|
pathnames(media.id) = pathname;
|
||||||
|
echo("Loaded ", pathname, "\n");
|
||||||
|
|
||||||
emulator->load(media.id);
|
emulator->load(media.id);
|
||||||
emulator->paletteUpdate(Emulator::Interface::PaletteMode::Standard);
|
emulator->paletteUpdate(Emulator::Interface::PaletteMode::Standard);
|
||||||
emulator->power();
|
emulator->power();
|
||||||
|
presentation->setTitle(emulator->title());
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -66,12 +68,14 @@ void Interface::loadRequest(unsigned id, string path) {
|
||||||
if(file::exists(pathname) == false) return;
|
if(file::exists(pathname) == false) return;
|
||||||
mmapstream stream(pathname);
|
mmapstream stream(pathname);
|
||||||
emulator->load(id, stream);
|
emulator->load(id, stream);
|
||||||
|
echo("Loaded ", path, "\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
void Interface::saveRequest(unsigned id, string path) {
|
void Interface::saveRequest(unsigned id, string path) {
|
||||||
string pathname = {pathnames(emulator->group(id)), path};
|
string pathname = {pathnames(emulator->group(id)), path};
|
||||||
filestream stream(pathname, file::mode::write);
|
filestream stream(pathname, file::mode::write);
|
||||||
emulator->save(id, stream);
|
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) {
|
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) {
|
void Interface::audioSample(int16_t lsample, int16_t rsample) {
|
||||||
|
if(settings->audio.mute) lsample = 0, rsample = 0;
|
||||||
signed samples[] = {lsample, rsample};
|
signed samples[] = {lsample, rsample};
|
||||||
dspaudio.sample(samples);
|
dspaudio.sample(samples);
|
||||||
while(dspaudio.pending()) {
|
while(dspaudio.pending()) {
|
||||||
|
|
|
@ -18,6 +18,7 @@ struct Interface : Emulator::Interface::Bind {
|
||||||
string server();
|
string server();
|
||||||
void notify(string text);
|
void notify(string text);
|
||||||
|
|
||||||
|
string pathname; //path to game folder
|
||||||
lstring pathnames;
|
lstring pathnames;
|
||||||
struct Gamepad {
|
struct Gamepad {
|
||||||
bool up = false;
|
bool up = false;
|
||||||
|
|
|
@ -15,12 +15,7 @@ string Program::path(string name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void Program::main() {
|
void Program::main() {
|
||||||
if(pause) {
|
debugger->run();
|
||||||
usleep(20 * 1000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
emulator->run();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Program::Program(string pathname) {
|
Program::Program(string pathname) {
|
||||||
|
@ -31,7 +26,9 @@ Program::Program(string pathname) {
|
||||||
sharedpath = {nall::sharedpath(), "loki/"};
|
sharedpath = {nall::sharedpath(), "loki/"};
|
||||||
directory::create(userpath);
|
directory::create(userpath);
|
||||||
|
|
||||||
|
new Settings;
|
||||||
new Interface;
|
new Interface;
|
||||||
|
new Debugger;
|
||||||
new Presentation;
|
new Presentation;
|
||||||
new Terminal;
|
new Terminal;
|
||||||
|
|
||||||
|
@ -39,19 +36,19 @@ Program::Program(string pathname) {
|
||||||
terminal->setVisible();
|
terminal->setVisible();
|
||||||
Application::processEvents();
|
Application::processEvents();
|
||||||
|
|
||||||
video.driver(video.optimalDriver());
|
video.driver(settings->video.driver);
|
||||||
video.set(Video::Handle, presentation->viewport.handle());
|
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);
|
video.set(Video::Filter, Video::FilterNearest);
|
||||||
if(video.init() == false) { video.driver("None"); video.init(); }
|
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::Handle, presentation->viewport.handle());
|
||||||
audio.set(Audio::Synchronize, true);
|
audio.set(Audio::Synchronize, settings->audio.synchronize);
|
||||||
audio.set(Audio::Frequency, 48000u);
|
audio.set(Audio::Frequency, 48000u);
|
||||||
if(audio.init() == false) { audio.driver("None"); audio.init(); }
|
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());
|
input.set(Input::Handle, presentation->viewport.handle());
|
||||||
if(input.init() == false) { input.driver("None"); input.init(); }
|
if(input.init() == false) { input.driver("None"); input.init(); }
|
||||||
input.onChange = {&Interface::inputEvent, interface};
|
input.onChange = {&Interface::inputEvent, interface};
|
||||||
|
@ -65,11 +62,14 @@ Program::Program(string pathname) {
|
||||||
presentation->showSplash();
|
presentation->showSplash();
|
||||||
|
|
||||||
interface->load(pathname);
|
interface->load(pathname);
|
||||||
|
debugger->load();
|
||||||
|
|
||||||
Application::main = {&Program::main, this};
|
Application::main = {&Program::main, this};
|
||||||
Application::run();
|
Application::run();
|
||||||
|
|
||||||
|
debugger->unload();
|
||||||
interface->unload();
|
interface->unload();
|
||||||
|
settings->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
int main(int argc, char** argv) {
|
int main(int argc, char** argv) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
#include <emulator/emulator.hpp>
|
#include <emulator/emulator.hpp>
|
||||||
#include <sfc/interface/interface.hpp>
|
#include <sfc/sfc.hpp>
|
||||||
|
namespace SFC = SuperFamicom;
|
||||||
|
|
||||||
#include <nall/platform.hpp>
|
#include <nall/platform.hpp>
|
||||||
#include <nall/config.hpp>
|
#include <nall/config.hpp>
|
||||||
|
@ -19,7 +20,9 @@ using namespace ruby;
|
||||||
#include <phoenix/phoenix.hpp>
|
#include <phoenix/phoenix.hpp>
|
||||||
using namespace phoenix;
|
using namespace phoenix;
|
||||||
|
|
||||||
|
#include "settings/settings.hpp"
|
||||||
#include "interface/interface.hpp"
|
#include "interface/interface.hpp"
|
||||||
|
#include "debugger/debugger.hpp"
|
||||||
#include "presentation/presentation.hpp"
|
#include "presentation/presentation.hpp"
|
||||||
#include "terminal/terminal.hpp"
|
#include "terminal/terminal.hpp"
|
||||||
#include "resource/resource.hpp"
|
#include "resource/resource.hpp"
|
||||||
|
@ -29,12 +32,14 @@ struct Program {
|
||||||
string userpath;
|
string userpath;
|
||||||
string sharedpath;
|
string sharedpath;
|
||||||
|
|
||||||
bool pause = true;
|
|
||||||
|
|
||||||
string path(string name);
|
string path(string name);
|
||||||
void main();
|
void main();
|
||||||
Program(string pathname);
|
Program(string pathname);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename... Args> void echo(Args&&... args) {
|
||||||
|
terminal->print({std::forward<Args>(args)...});
|
||||||
|
}
|
||||||
|
|
||||||
extern Program* program;
|
extern Program* program;
|
||||||
extern DSP dspaudio;
|
extern DSP dspaudio;
|
||||||
|
|
|
@ -14,7 +14,7 @@ Presentation::Presentation() {
|
||||||
onClose = &Application::quit;
|
onClose = &Application::quit;
|
||||||
|
|
||||||
splash.allocate(512, 480);
|
splash.allocate(512, 480);
|
||||||
splash.verticalGradient(0xff00003f, 0xff000000, 512, 480, 256, 0);
|
splash.verticalGradient(0xff00005f, 0xff000000, 512, 480, 256, 0);
|
||||||
nall::image floor;
|
nall::image floor;
|
||||||
floor.allocate(512, 480);
|
floor.allocate(512, 480);
|
||||||
floor.radialGradient(0xffff0000, 0x00000000, 384, 240, 256, 415);
|
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::Terminal() {
|
||||||
terminal = this;
|
terminal = this;
|
||||||
|
|
||||||
|
setTitle({"loki v", Emulator::Version});
|
||||||
setWindowGeometry({0, 480 + frameMargin().height, 800, 480});
|
setWindowGeometry({0, 480 + frameMargin().height, 800, 480});
|
||||||
|
|
||||||
console.setFont(Font::monospace(8));
|
console.setFont(Font::monospace(8));
|
||||||
print("loki v", Emulator::Version, "\n\n");
|
console.setPrompt("$ ");
|
||||||
print("$ ");
|
|
||||||
|
|
||||||
layout.append(console, {~0, ~0});
|
layout.append(console, {~0, ~0});
|
||||||
append(layout);
|
append(layout);
|
||||||
|
@ -17,26 +17,151 @@ Terminal::Terminal() {
|
||||||
console.onActivate = {&Terminal::command, this};
|
console.onActivate = {&Terminal::command, this};
|
||||||
}
|
}
|
||||||
|
|
||||||
void Terminal::command(string s) {
|
void Terminal::command(string t) {
|
||||||
if(s.empty()) {
|
lstring args = t.qsplit(" ");
|
||||||
} else if(s == "quit" || s == "exit") {
|
string s = args.takeFirst();
|
||||||
Application::quit();
|
unsigned argc = args.size();
|
||||||
} else if(s == "clear" || s == "reset") {
|
|
||||||
reset();
|
if(s.empty()) return;
|
||||||
} else if(s == "r") {
|
|
||||||
program->pause = false;
|
if(s.beginsWith("settings.")) return settings->command(s, args);
|
||||||
} else if(s == "p") {
|
|
||||||
program->pause = true;
|
if(s == "break") {
|
||||||
} else {
|
debugger->stop();
|
||||||
print("unrecognized command\n");
|
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() {
|
void Terminal::reset() {
|
||||||
console.reset();
|
console.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
template<typename... Args> void Terminal::print(Args&&... args) {
|
void Terminal::print(const string& text) {
|
||||||
console.print(std::forward<Args>(args)...);
|
console.print(text);
|
||||||
}
|
}
|
||||||
|
|