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.
This commit is contained in:
Tim Allen 2014-02-03 08:02:18 +11:00
parent 187ba0eec6
commit 10e2a6d497
101 changed files with 623 additions and 77 deletions

View File

@ -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

View File

@ -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/";

View File

@ -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

View File

@ -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

View File

@ -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;
}

View File

@ -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];

View File

@ -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();

View File

@ -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)),

View File

@ -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();

View File

@ -125,6 +125,7 @@ struct ComboButton::State {
};
struct Console::State {
string prompt;
};
struct Frame::State {

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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!

View File

@ -432,6 +432,7 @@ public:
void print(string text);
void reset();
void setPrompt(string prompt);
pConsole(Console& console) : pWidget(console), console(console) {}
void constructor();

View File

@ -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);

View File

@ -6,6 +6,9 @@ void pConsole::print(string text) {
void pConsole::reset() {
}
void pConsole::setPrompt(string prompt) {
}
void pConsole::constructor() {
}

View File

@ -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();

View File

@ -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();

View File

@ -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"",

View File

@ -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;

View File

@ -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);

View File

@ -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");
}

View File

@ -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;

View File

@ -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 {

View File

@ -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)/)

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
ConfigurationSettings* config = nullptr;
ConfigurationSettings::ConfigurationSettings() {

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
#include "library.cpp"
#include "presentation.cpp"
#include "dip-switches.cpp"

View File

@ -1,4 +1,4 @@
#include "ethos.hpp"
#include "higan.hpp"
#include "bootstrap.cpp"
#include "resource/resource.cpp"

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
#include "hotkeys.cpp"
InputManager* inputManager = nullptr;
HID::Null hidNull;

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
Interface* interface = nullptr;
void Interface::loadRequest(unsigned id, string name, string type) {

View File

Before

Width:  |  Height:  |  Size: 611 B

After

Width:  |  Height:  |  Size: 611 B

View File

Before

Width:  |  Height:  |  Size: 592 B

After

Width:  |  Height:  |  Size: 592 B

View File

Before

Width:  |  Height:  |  Size: 937 B

After

Width:  |  Height:  |  Size: 937 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

View File

Before

Width:  |  Height:  |  Size: 606 B

After

Width:  |  Height:  |  Size: 606 B

View File

Before

Width:  |  Height:  |  Size: 587 B

After

Width:  |  Height:  |  Size: 587 B

View File

Before

Width:  |  Height:  |  Size: 812 B

After

Width:  |  Height:  |  Size: 812 B

View File

Before

Width:  |  Height:  |  Size: 408 B

After

Width:  |  Height:  |  Size: 408 B

View File

Before

Width:  |  Height:  |  Size: 378 B

After

Width:  |  Height:  |  Size: 378 B

View File

Before

Width:  |  Height:  |  Size: 897 B

After

Width:  |  Height:  |  Size: 897 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 652 B

After

Width:  |  Height:  |  Size: 652 B

View File

Before

Width:  |  Height:  |  Size: 662 B

After

Width:  |  Height:  |  Size: 662 B

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
#include "video.cpp"
#include "audio.cpp"
#include "input.cpp"

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
#include "cheat-database.cpp"
#include "cheat-editor.cpp"
#include "state-manager.cpp"

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
Utility* utility = nullptr;

View File

@ -1,4 +1,4 @@
#include "../ethos.hpp"
#include "../higan.hpp"
WindowManager* windowManager = nullptr;
void WindowManager::append(Window* window, string name) {

View File

@ -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)/)

View File

@ -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);
}

View File

@ -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;

View File

@ -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()) {

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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);

View File

@ -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");
}

View File

@ -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;

View File

@ -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);
}

Some files were not shown because too many files have changed in this diff Show More