Probably buggy SNES disassembler

This commit is contained in:
Ilari Liusvaara 2013-07-03 04:06:40 +03:00
parent 6ce7b3dc7b
commit 08e5f6acc9
12 changed files with 726 additions and 48 deletions

View file

@ -0,0 +1,22 @@
#ifndef _interface__disassembler__hpp__included__
#define _interface__disassembler__hpp__included__
#include <functional>
#include <string>
#include <map>
class disassembler
{
public:
disassembler(const std::string& name);
virtual ~disassembler();
virtual std::string disassemble(uint64_t base, std::function<unsigned char()> fetchpc) = 0;
static disassembler& byname(const std::string& name);
template<typename T> static T fetch_le(std::function<unsigned char()> fetchpc);
template<typename T> static T fetch_be(std::function<unsigned char()> fetchpc);
private:
std::string name;
static std::map<std::string, disassembler*>& disasms();
};
#endif

View file

@ -17,6 +17,17 @@ struct core_romimage;
struct core_romimage_info; struct core_romimage_info;
struct core_core; struct core_core;
/**
* Interface device register.
*/
struct interface_device_reg
{
const char* name;
uint64_t (*read)();
void (*write)(uint64_t v);
bool boolean;
};
/** /**
* An parameter for action in interface. * An parameter for action in interface.
*/ */
@ -371,6 +382,10 @@ struct core_core_params
* Execute action. * Execute action.
*/ */
void (*execute_action)(unsigned id, const std::vector<interface_action_paramval>& p); void (*execute_action)(unsigned id, const std::vector<interface_action_paramval>& p);
/**
* Get set of interface device registers.
*/
const struct interface_device_reg* (*get_registers)();
}; };
struct core_region struct core_region
@ -469,6 +484,7 @@ struct core_core
void do_unregister_action(const std::string& key); void do_unregister_action(const std::string& key);
std::set<const interface_action*> get_actions(); std::set<const interface_action*> get_actions();
_param_register_proxy param_register_proxy; _param_register_proxy param_register_proxy;
const interface_device_reg* get_registers();
private: private:
std::string (*_core_identifier)(); std::string (*_core_identifier)();
bool (*_set_region)(core_region& region); bool (*_set_region)(core_region& region);
@ -494,6 +510,7 @@ private:
std::string (*_get_core_shortname)(); std::string (*_get_core_shortname)();
void (*_pre_emulate_frame)(controller_frame& cf); void (*_pre_emulate_frame)(controller_frame& cf);
void (*_execute_action)(unsigned id, const std::vector<interface_action_paramval>& p); void (*_execute_action)(unsigned id, const std::vector<interface_action_paramval>& p);
const interface_device_reg* (*_get_registers)();
bool hidden; bool hidden;
std::map<std::string, interface_action*> actions; std::map<std::string, interface_action*> actions;
mutex_class actions_lock; mutex_class actions_lock;
@ -561,6 +578,7 @@ public:
bool is_hidden() { return core->is_hidden(); } bool is_hidden() { return core->is_hidden(); }
void pre_emulate_frame(controller_frame& cf) { return core->pre_emulate_frame(cf); } void pre_emulate_frame(controller_frame& cf) { return core->pre_emulate_frame(cf); }
std::set<const interface_action*> get_actions() { return core->get_actions(); } std::set<const interface_action*> get_actions() { return core->get_actions(); }
const interface_device_reg* get_registers() { return core->get_registers(); }
private: private:
core_type(const core_type&); core_type(const core_type&);
core_type& operator=(const core_type&); core_type& operator=(const core_type&);

119
src/core/disassemble.cpp Normal file
View file

@ -0,0 +1,119 @@
#include "core/command.hpp"
#include "interface/disassembler.hpp"
#include "library/bintohex.hpp"
#include "library/minmax.hpp"
#include "core/memorymanip.hpp"
#include "core/window.hpp"
#include "library/string.hpp"
#include <iomanip>
#include <fstream>
#include <iostream>
namespace
{
struct dres
{
uint64_t addr;
uint64_t len;
std::string disasm;
};
unsigned char hex(char ch)
{
switch(ch) {
case '0': return 0;
case '1': return 1;
case '2': return 2;
case '3': return 3;
case '4': return 4;
case '5': return 5;
case '6': return 6;
case '7': return 7;
case '8': return 8;
case '9': return 9;
case 'a': case 'A': return 10;
case 'b': case 'B': return 11;
case 'c': case 'C': return 12;
case 'd': case 'D': return 13;
case 'e': case 'E': return 14;
case 'f': case 'F': return 15;
};
throw std::runtime_error("Bad hex character");
}
uint64_t parse_hexordec(const std::string& k)
{
regex_results t;
uint64_t address = 0;
if(t = regex("0x(.+)", k)) {
if(t[1].length() > 16)
throw 42;
address = 0;
for(unsigned i = 0; i < t[1].length(); i++)
address = 16 * address + hex(t[1][i]);
} else {
address = parse_value<uint64_t>(k);
}
return address;
}
function_ptr_command<const std::string&> disassemble(lsnes_cmd, "disassemble", "Disassemble code",
"Syntax: disassemble <kind> <addr> [<count>] [to <filename>]\nDisassemble code\n",
[](const std::string& t) throw(std::bad_alloc, std::runtime_error) {
regex_results r = regex("([^ \t]+)[ \t]+([0-9]+|0x[0-9A-Fa-f]+)([ \t]+([0-9]+))?"
"([ \t]+to[ \t]+(.+))?", t);
if(!r) {
messages << "Syntax: disassemble <kind> <addr> [<count>] [to <filename>]" << std::endl;
return;
}
std::string kind = r[1];
uint64_t addr = parse_hexordec(r[2]);
uint64_t count = 1;
if(r[4] != "")
count = parse_value<uint64_t>(r[4]);
std::string file;
if(r[6] != "")
file = r[6];
std::list<dres> result;
disassembler* d;
try {
d = &disassembler::byname(kind);
} catch(std::exception& e) {
messages << "Can't find such disassembler" << std::endl;
return;
}
uint64_t laddr = addr;
uint64_t longest = 0;
for(uint64_t i = 1; i <= count; i++) {
uint64_t bytes = 0;
dres x;
x.addr = laddr;
x.disasm = d->disassemble(laddr, [&bytes, laddr]() -> unsigned char {
return lsnes_memory.read<uint8_t>(laddr + bytes++);
});
x.len = bytes;
result.push_back(x);
longest = max(longest, bytes);
laddr += bytes;
}
std::ostream* strm = &messages.getstream();
if(file != "") {
strm = new std::ofstream(file);
if(!*strm) {
messages << "Can't open output file" << std::endl;
return;
}
}
for(auto i : result) {
std::vector<unsigned char> tmp;
tmp.resize(i.len);
lsnes_memory.read_range(i.addr, &tmp[0], i.len);
std::string l = (stringfmt() << std::setw(16) << std::setfill('0') << std::hex
<< i.addr).str() + " " + binary_to_hex(&tmp[0], i.len) + " "
+ i.disasm;
(*strm) << l << std::endl;
}
if(file != "")
delete strm;
});
}

View file

@ -1,5 +1,5 @@
ifdef BSNES_VERSION ifdef BSNES_VERSION
OBJECTS=core.$(OBJECT_SUFFIX) OBJECTS=core.$(OBJECT_SUFFIX) scpu-disasm.$(OBJECT_SUFFIX)
BSNES_CFLAGS= BSNES_CFLAGS=
BSNES_LDFLAGS= BSNES_LDFLAGS=
ifdef BSNES_IS_COMPAT ifdef BSNES_IS_COMPAT

View file

@ -94,6 +94,56 @@ namespace
0, 0 //Offset. 0, 0 //Offset.
}; };
struct interface_device_reg snes_registers[] = {
{"pbpc", []() -> uint64_t { return SNES::cpu.regs.pc; }, [](uint64_t v) { SNES::cpu.regs.pc = v; }},
{"pb", []() -> uint64_t { return SNES::cpu.regs.pc >> 16; },
[](uint64_t v) { SNES::cpu.regs.pc = (v << 16) | (SNES::cpu.regs.pc & 0xFFFF); }},
{"pc", []() -> uint64_t { return SNES::cpu.regs.pc & 0xFFFF; },
[](uint64_t v) { SNES::cpu.regs.pc = (v & 0xFFFF) | (SNES::cpu.regs.pc & ~0xFFFF); }},
{"r0", []() -> uint64_t { return SNES::cpu.regs.r[0]; }, [](uint64_t v) { SNES::cpu.regs.r[0] = v; }},
{"r1", []() -> uint64_t { return SNES::cpu.regs.r[1]; }, [](uint64_t v) { SNES::cpu.regs.r[1] = v; }},
{"r2", []() -> uint64_t { return SNES::cpu.regs.r[2]; }, [](uint64_t v) { SNES::cpu.regs.r[2] = v; }},
{"r3", []() -> uint64_t { return SNES::cpu.regs.r[3]; }, [](uint64_t v) { SNES::cpu.regs.r[3] = v; }},
{"r4", []() -> uint64_t { return SNES::cpu.regs.r[4]; }, [](uint64_t v) { SNES::cpu.regs.r[4] = v; }},
{"r5", []() -> uint64_t { return SNES::cpu.regs.r[5]; }, [](uint64_t v) { SNES::cpu.regs.r[5] = v; }},
{"a", []() -> uint64_t { return SNES::cpu.regs.a; }, [](uint64_t v) { SNES::cpu.regs.a = v; }},
{"x", []() -> uint64_t { return SNES::cpu.regs.x; }, [](uint64_t v) { SNES::cpu.regs.x = v; }},
{"y", []() -> uint64_t { return SNES::cpu.regs.y; }, [](uint64_t v) { SNES::cpu.regs.y = v; }},
{"z", []() -> uint64_t { return SNES::cpu.regs.z; }, [](uint64_t v) { SNES::cpu.regs.z = v; }},
{"s", []() -> uint64_t { return SNES::cpu.regs.s; }, [](uint64_t v) { SNES::cpu.regs.s = v; }},
{"d", []() -> uint64_t { return SNES::cpu.regs.d; }, [](uint64_t v) { SNES::cpu.regs.d = v; }},
{"db", []() -> uint64_t { return SNES::cpu.regs.db; }, [](uint64_t v) { SNES::cpu.regs.db = v; }},
{"p", []() -> uint64_t { return SNES::cpu.regs.p; }, [](uint64_t v) { SNES::cpu.regs.p = v; }},
{"e", []() -> uint64_t { return SNES::cpu.regs.e; }, [](uint64_t v) { SNES::cpu.regs.e = v; }},
{"irq", []() -> uint64_t { return SNES::cpu.regs.irq; }, [](uint64_t v) { SNES::cpu.regs.irq = v; }},
{"wai", []() -> uint64_t { return SNES::cpu.regs.wai; }, [](uint64_t v) { SNES::cpu.regs.wai = v; }},
{"mdr", []() -> uint64_t { return SNES::cpu.regs.mdr; }, [](uint64_t v) { SNES::cpu.regs.mdr = v; }},
{"vector", []() -> uint64_t { return SNES::cpu.regs.vector; },
[](uint64_t v) { SNES::cpu.regs.vector = v; }},
{"aa", []() -> uint64_t { return SNES::cpu.aa; }, [](uint64_t v) { SNES::cpu.aa = v; }},
{"rd", []() -> uint64_t { return SNES::cpu.rd; }, [](uint64_t v) { SNES::cpu.rd = v; }},
{"sp", []() -> uint64_t { return SNES::cpu.sp; }, [](uint64_t v) { SNES::cpu.sp = v; }},
{"dp", []() -> uint64_t { return SNES::cpu.dp; }, [](uint64_t v) { SNES::cpu.dp = v; }},
{"p_n", []() -> uint64_t { return SNES::cpu.regs.p.n; }, [](uint64_t v) { SNES::cpu.regs.p.n = v; },
true},
{"p_v", []() -> uint64_t { return SNES::cpu.regs.p.v; }, [](uint64_t v) { SNES::cpu.regs.p.v = v; },
true},
{"p_m", []() -> uint64_t { return SNES::cpu.regs.p.m; }, [](uint64_t v) { SNES::cpu.regs.p.m = v; },
true},
{"p_x", []() -> uint64_t { return SNES::cpu.regs.p.x; }, [](uint64_t v) { SNES::cpu.regs.p.x = v; },
true},
{"p_d", []() -> uint64_t { return SNES::cpu.regs.p.d; }, [](uint64_t v) { SNES::cpu.regs.p.d = v; },
true},
{"p_i", []() -> uint64_t { return SNES::cpu.regs.p.i; }, [](uint64_t v) { SNES::cpu.regs.p.i = v; },
true},
{"p_z", []() -> uint64_t { return SNES::cpu.regs.p.z; }, [](uint64_t v) { SNES::cpu.regs.p.z = v; },
true},
{"p_c", []() -> uint64_t { return SNES::cpu.regs.p.c; }, [](uint64_t v) { SNES::cpu.regs.p.c = v; },
true},
//TODO: SMP registers, DSP registers, chip registers.
{NULL, NULL, NULL}
};
#include "ports.inc" #include "ports.inc"
#include "slots.inc" #include "slots.inc"
#include "regions.inc" #include "regions.inc"
@ -867,7 +917,8 @@ again2:
do_hreset_flag = true; do_hreset_flag = true;
break; break;
} }
} },
.get_registers = []() -> const interface_device_reg* { return snes_registers; },
}}; }};
core_type type_snes{{ core_type type_snes{{
@ -937,49 +988,6 @@ again2:
x.write(&out[0], out.size()); x.write(&out[0], out.size());
}); });
function_ptr_luafun lua_memory_readreg(LS, "memory.getregister", [](lua_state& L, const std::string& fname) ->
int {
std::string r = L.get_string(1, fname.c_str());
auto& c = SNES::cpu.regs;
auto& c2 = SNES::cpu;
if(r == "pbpc") L.pushnumber((unsigned)c.pc);
else if(r == "pb") L.pushnumber((unsigned)c.pc >> 16);
else if(r == "pc") L.pushnumber((unsigned)c.pc & 0xFFFF);
else if(r == "r0") L.pushnumber((unsigned)c.r[0]);
else if(r == "r1") L.pushnumber((unsigned)c.r[1]);
else if(r == "r2") L.pushnumber((unsigned)c.r[2]);
else if(r == "r3") L.pushnumber((unsigned)c.r[3]);
else if(r == "r4") L.pushnumber((unsigned)c.r[4]);
else if(r == "r5") L.pushnumber((unsigned)c.r[5]);
else if(r == "a") L.pushnumber((unsigned)c.a);
else if(r == "x") L.pushnumber((unsigned)c.x);
else if(r == "y") L.pushnumber((unsigned)c.y);
else if(r == "z") L.pushnumber((unsigned)c.z);
else if(r == "s") L.pushnumber((unsigned)c.s);
else if(r == "d") L.pushnumber((unsigned)c.d);
else if(r == "db") L.pushnumber((unsigned)c.db);
else if(r == "p") L.pushnumber((unsigned)c.p);
else if(r == "p_n") L.pushboolean(c.p.n);
else if(r == "p_v") L.pushboolean(c.p.v);
else if(r == "p_m") L.pushboolean(c.p.m);
else if(r == "p_x") L.pushboolean(c.p.x);
else if(r == "p_d") L.pushboolean(c.p.d);
else if(r == "p_i") L.pushboolean(c.p.i);
else if(r == "p_z") L.pushboolean(c.p.z);
else if(r == "p_c") L.pushboolean(c.p.c);
else if(r == "e") L.pushboolean(c.e);
else if(r == "irq") L.pushboolean(c.irq);
else if(r == "wai") L.pushboolean(c.wai);
else if(r == "mdr") L.pushnumber((unsigned)c.mdr);
else if(r == "vector") L.pushnumber((unsigned)c.vector);
else if(r == "aa") L.pushnumber((unsigned)c2.aa);
else if(r == "rd") L.pushnumber((unsigned)c2.rd);
else if(r == "sp") L.pushnumber((unsigned)c2.sp);
else if(r == "dp") L.pushnumber((unsigned)c2.dp);
else L.pushnil();
return 1;
});
#ifdef BSNES_HAS_DEBUGGER #ifdef BSNES_HAS_DEBUGGER
char snes_debug_cb_keys[SNES::Debugger::Breakpoints]; char snes_debug_cb_keys[SNES::Debugger::Breakpoints];
char snes_debug_cb_trace; char snes_debug_cb_trace;

View file

@ -0,0 +1,279 @@
#include "interface/disassembler.hpp"
#include "library/string.hpp"
#include <sstream>
#include <iomanip>
namespace
{
std::string tohex(uint8_t x)
{
return (stringfmt() << std::setw(2) << std::setfill('0') << std::hex << static_cast<int>(x)).str();
}
std::string tohex(uint16_t x)
{
return (stringfmt() << std::setw(4) << std::setfill('0') << std::hex << x).str();
}
template<bool k> struct varsize {};
template<> struct varsize<false> { typedef uint8_t type_t; };
template<> struct varsize<true> { typedef uint16_t type_t; };
template<bool laddr, bool lacc> struct scpu_disassembler : public disassembler
{
scpu_disassembler(const std::string& n) : disassembler(n) {}
std::string disassemble(uint64_t base, std::function<unsigned char()> fetchpc);
};
struct ssmp_disassembler : public disassembler
{
ssmp_disassembler(const std::string& n) : disassembler(n) {}
std::string disassemble(uint64_t base, std::function<unsigned char()> fetchpc);
};
scpu_disassembler<false, false> d1("snes-xa");
scpu_disassembler<false, true> d2("snes-xA");
scpu_disassembler<true, false> d3("snes-Xa");
scpu_disassembler<true, true> d4("snes-XA");
ssmp_disassembler d5("snes-smp");
//%0: Byte-sized quantity, loaded before %b.
//%a: Accumulator-sized quantity.
//%b: Byte-sized quantity.
//%l: Long address.
//%r: Byte relative.
//%R: Word relative.
//%w: Word-sized quantity.
//%x: Index-sizeed quantity.
const char* scpu_instructions[] = {
"brk #%b", "ora (%b,x)", "cop #%b", "ora %b,s", //00
"tsb %b", "ora %b", "asl %b", "ora [%b]", //04
"php", "ora #%a", "asl a", "phd", //08
"tsb %w", "ora %w", "asl %w", "ora %l", //0C
"bpl %r", "ora (%b),y", "ora (%b)", "ora (%b,s),y", //10
"trb %b", "ora %b,x", "asl %b,x", "ora [%b],y", //14
"clc", "ora %w,y", "inc", "tcs", //18
"trb %w", "ora %w,x", "asl %w,x", "ora %l,x", //1C
"jsr %w", "and (%b,x)", "jsl %l", "and %b,s", //20
"bit %b", "and %b", "rol %b", "and [%b]", //24
"plp", "and #%a", "rol a", "pld", //28
"bit %w", "and %w", "rol %w", "and %l", //2C
"bmi %r", "and (%b),y", "and (%b)", "and (%b,s),y", //30
"bit %b", "and %b,x", "rol %b,x", "and [%b],y", //34
"sec", "and %w,y", "dec", "tsc", //38
"bit %w,x", "and %w,x", "rol %w,x", "and %l,x", //3C
"rti", "eor (%b,x)", "wdm #%b", "eor %b,s", //40
"mvp %b,%0", "eor %b", "lsr %b", "eor [%b]", //44
"pha", "eor #%a", "lsr a", "phk", //48
"jmp %w", "eor %w", "lsr %w", "eor %l", //4C
"bvc %r", "eor (%b),y", "eor (%b)", "eor (%b,s),y", //50
"mnv %b,%0", "eor %b,x", "lsr %b,x", "eor [%b],y", //54
"cli", "eor %w,y", "phy", "tcd", //58
"jml %l", "eor %w,x", "lsr %w,x", "eor %l,x", //5C
"rts", "adc (%b,x)", "per %w", "adc %b,s", //60
"stz %b", "adc %b", "ror %b", "adc [%b]", //64
"pla", "adc #%a", "ror a", "rtl", //68
"jmp (%w)", "adc %w", "ror %w", "adc %l", //6C
"bvs %r", "adc (%b),y", "adc (%b)", "adc (%b,s),y", //70
"stz %b,x", "adc %b,x", "ror %b,x", "adc [%b],y", //74
"sei", "adc %w,y", "ply", "tdc", //78
"jmp (%w,x)", "adc %w,x", "ror %w,x", "adc %l,x", //7C
"bra %r", "sta (%b,x)", "brl %R", "sta %b,s", //80
"sty %b", "sta %b", "stx %b", "sta [%b]", //84
"dey", "bit #%a", "txa", "phb", //88
"sty %w", "sta %w", "stx %w", "sta %l", //8C
"bcc %r", "sta (%b),y", "sta (%b)", "sta (%b,s),y", //90
"sty %b,x", "sta %b,x", "stx %b,y", "sta [%b],y", //94
"tya", "sta %w,y", "txs", "txy", //98
"stz %w", "sta %w,x", "stz %w,x", "sta %l,x", //9C
"ldy #%x", "lda (%b,x)", "ldx #%x", "lda %b,s", //A0
"ldy %b", "lda %b", "ldx %b", "lda [%b]", //A4
"tay", "lda #%a", "tax", "plb", //A8
"ldy %w", "lda %w", "ldx %w", "lda %l", //AC
"bcs %r", "lda (%b),y", "lda (%b)", "lda (%b,s),y", //B0
"ldy %b,x", "lda %b,x", "ldx %b,y", "lda [%b],y", //B4
"clv", "lda %w,y", "tsx", "tyx", //B8
"ldy %w,x", "lda %w,x", "ldx %w,x", "lda %l,x", //BC
"cpy #%x", "cmp (%b,x)", "rep #%b", "cmp %b,s", //C0
"cpy %b", "cmp %b", "dec %b", "cmp [%b]", //C4
"iny", "cmp #%a", "dex", "wai", //C8
"cpy %w", "cmp %w", "dec %w", "cmp %l", //CC
"bne %r", "cmp (%b),y", "cmp (%b)", "cmp (%b,s),y", //D0
"pei (%b)", "cmp %b,x", "dec %b,y", "cmp [%b],y", //D4
"cld", "cmp %w,y", "phx", "stp", //D8
"jmp [%w]", "cmp %w,x", "dec %w,x", "cmp %l,x", //DC
"cpx #%x", "sbc (%b,x)", "sep #%b", "sbc %b,s", //E0
"cpx %b", "sbc %b", "inc %b", "sbc [%b]", //E4
"inx", "sbc #%a", "nop", "xba", //E8
"cpx %w", "sbc %w", "inc %w", "sbc %l", //EC
"beq %r", "sbc (%b),y", "sbc (%b)", "sbc (%b,s),y", //F0
"pea %w", "sbc %b,x", "inc %b,y", "sbc [%b],y", //F4
"sed", "sbc %w,y", "plx", "xce", //F8
"jsr (%w,x)", "sbc %w,x", "inc %w,x", "sbc %l,x" //FC
};
//%0: Byte-sized quantity, loaded before %b.
//%b: Byte-sized quantity.
//%c: ??? <n>:<m> construct.
//%r: Relative
//%R: Relative
//%w: Word-sized quantity.
const char* ssmp_instructions[] = {
"nop", "jst $ffde", "set %b:0", "bbs %b:0=%R", //00
"ora %b", "ora %w", "ora (x)", "ora (%b,x)", //04
"ora #%b", "orr %b=%0", "orc %c", "asl %b", //08
"asl %w", "php", "tsb %w", "brk", //0C
"bpl %r", "jst $ffdc", "clr %b:0", "bbc %b:0=%R", //10
"ora %b,x", "ora %w,x", "ora %w,y", "ora (%b),y", //14
"orr %b=#%0", "orr (x)=(y)", "dew %b", "asl %b,x", //18
"asl", "dex", "cpx %w", "jmp (%w,x)", //1C
"clp", "jst $ffda", "set %b:1", "bbs %b:1=%R", //20
"and %b", "and %w", "and (x)", "and (%b,x)", //24
"and #%b", "and %b=%0", "orc !%c", "rol %b", //28
"rol %w", "pha", "bne %b=%R", "bra %r", //2C
"bmi %r", "jst $ffd8", "clr %b:1", "bbc %b:1=$R", //30
"and %b,x", "and %w,x", "and %w,y", "and (%b),y", //34
"and %b=#%0", "and (x)=(y)", "inw %b", "rol $b,x", //38
"rol", "inx", "cpx %b", "jsr %w", //3C
"sep", "jst $ffd6", "set %b:2", "bbs %b:2=$R", //40
"eor %b", "eor %w", "eor (x)", "eor ($b,x)", //44
"eor #%b", "eor %b=%0", "and %c", "lsr %b", //48
"lsr %w", "phx", "trb %w", "jsp $ff", //4C
"bvc %r", "jst $ffd4", "clr %b:2", "bbc %b:2=%R", //50
"eor %b,x", "eor %w,x", "eor %w,y", "eor (%b),y", //54
"eor %b=#%0", "eor (x)=(y)", "cpw %w", "lsr %b,x", //58
"lsr", "tax", "cpy %w", "jmp %w", //5C
"clc", "jst $ffd2", "set %b:3", "bbs %b:3=$R", //60
"cmp %b", "cmp %w", "cmp (x)", "cmp ($b,x)", //64
"cmp #%b", "cmp %b=%0", "and !%c", "ror %b", //68
"ror %w", "phy", "bne --$b=%R", "rts", //6C
"bvs %r", "jst $ffd0", "clr %b:3", "bbc %b:3=%R", //70
"cmp %b,x", "cmp %w,x", "cmp %w,y", "cmp (%b),y", //74
"cmp %b=#%0", "cmp (x)=(y)", "adw %w", "ror %b,x", //78
"ror", "txa", "cpy %b", "rti", //7C
"sec", "jst $ffce", "set %b:4", "bbs %b:4=$R", //80
"adc %b", "adc %w", "adc (x)", "adc ($b,x)", //84
"adc #%b", "cmp %b=%0", "eor %c", "dec %b", //88
"dec %w", "ldy #%b", "plp", "str %b=#%0", //8C
"bcc %r", "jst $ffcc", "clr %b:4", "bbc %b:4=%R", //90
"adc %b,x", "adc %w,x", "adc %w,y", "adc (%b),y", //94
"adc %b=#%0", "adc (x)=(y)", "sbw %w", "dec %b,x", //98
"dec", "tsx", "div", "xcn", //9C
"sei", "jst $ffca", "set %b:5", "bbs %b:5=$R", //A0
"sbc %b", "sbc %w", "sbc (x)", "sbc ($b,x)", //A4
"sbc #%b", "sbc %b=%0", "ldc %c", "inc %b", //A8
"inc %w", "cpy #%b", "pla", "sta (x++)", //AC
"bcs %r", "jst $ffc8", "clr %b:5", "bbc %b:5=%R", //B0
"sbc %b,x", "sbc %w,x", "sbc %w,y", "sbc (%b),y", //B4
"sbc %b=#%0", "sbc (x)=(y)", "ldw %b", "inc %b,x", //B8
"inc", "txs", "das", "lda (x++)", //BC
"cli", "jst $ffc6", "set %b:6", "bbs %b:6=$R", //C0
"sta %b", "sta %w", "sta (x)", "sta ($b,x)", //C4
"cpx #%b", "stx %w", "stc %c", "sty %b", //C8
"sty %w", "ldx #%b", "plx", "mul", //CC
"bne %r", "jst $ffc4", "clr %b:6", "bbc %b:6=%R", //D0
"sta %b,x", "sta %w,x", "sta %w,y", "sta (%b),y", //D4
"stx %b", "stx %b,y", "stw %b", "stw %b,x", //D8
"dey", "tya", "bne %b,x=%R", "daa", //DC
"clv", "jst $ffc2", "set %b:7", "bbs %b:7=$R", //E0
"lda %b", "lda %w", "lda (x)", "lda ($b,x)", //E4
"lda #%b", "ldx %w", "not %c", "ldy %b", //E8
"ldy %w", "cmc", "ply", "wai", //EC
"beq %r", "jst $ffc0", "clr %b:7", "bbc %b:7=%R", //F0
"lda %b,x", "lda %w,x", "lda %w,y", "lda (%b),y", //F4
"ldx %b", "ldx %b,y", "str %b=%0", "ldy %b,x", //F8
"iny", "tay", "bne --y=%r", "stp" //FC
};
template<bool laddr, bool lacc>
std::string scpu_disassembler<laddr, lacc>::disassemble(uint64_t base, std::function<unsigned char()> fetchpc)
{
std::ostringstream o;
const char* ins = scpu_instructions[fetchpc()];
uint8_t x = 0;
//Handle %0 specially.
for(size_t i = 0; ins[i]; i++)
if(ins[i] == '%' && ins[i + 1] == '0')
x = fetchpc();
for(size_t i = 0; ins[i]; i++) {
if(ins[i] != '%')
o << ins[i];
else {
switch(ins[i + 1]) {
case '0':
o << "$" << tohex(x);
break;
case 'a':
o << "$" << tohex(fetch_le<typename varsize<lacc>::type_t>(fetchpc));
break;
case 'b':
o << "$" << tohex(fetch_le<uint8_t>(fetchpc));
break;
case 'l':
o << "$" << tohex(fetch_le<uint8_t>(fetchpc));
o << tohex(fetch_le<uint16_t>(fetchpc));
break;
case 'r':
o << "$" << tohex(static_cast<uint16_t>(base + 2 +
fetch_le<int8_t>(fetchpc)));
break;
case 'R':
o << "$" << tohex(static_cast<uint16_t>(base + 3 +
fetch_le<int16_t>(fetchpc)));
case 'w':
o << "$" << tohex(fetch_le<uint16_t>(fetchpc));
break;
case 'x':
o << "$" << tohex(fetch_le<typename varsize<laddr>::type_t>(fetchpc));
break;
}
i++;
}
}
return o.str();
}
std::string ssmp_disassembler::disassemble(uint64_t base, std::function<unsigned char()> fetchpc)
{
std::ostringstream o;
const char* ins = ssmp_instructions[fetchpc()];
uint8_t x = 0;
uint16_t tmp = 0;
//Handle %0 specially.
for(size_t i = 0; ins[i]; i++)
if(ins[i] == '%' && ins[i + 1] == '0')
x = fetchpc();
for(size_t i = 0; ins[i]; i++) {
if(ins[i] != '%')
o << ins[i];
else {
switch(ins[i + 1]) {
case '0':
o << "$" << tohex(x);
break;
case 'b':
o << "$" << tohex(fetch_le<uint8_t>(fetchpc));
break;
case 'c':
tmp = fetch_le<uint16_t>(fetchpc);
o << "$" << tohex(static_cast<uint16_t>(tmp & 0x1FFF)) << ":" << (tmp >> 13);
break;
case 'r':
o << "$" << tohex(static_cast<uint16_t>(base + 2 +
fetch_le<int8_t>(fetchpc)));
break;
case 'R':
o << "$" << tohex(static_cast<uint16_t>(base + 3 +
fetch_le<int8_t>(fetchpc)));
case 'w':
o << "$" << tohex(fetch_le<uint16_t>(fetchpc));
break;
}
i++;
}
}
return o.str();
}
}

View file

@ -61,6 +61,10 @@ namespace
interface_action act_reset(gambatte_core, 0, "Soft reset", "reset", {}); interface_action act_reset(gambatte_core, 0, "Soft reset", "reset", {});
struct interface_device_reg gb_registers[] = {
{NULL, NULL, NULL}
};
//Framebuffer. //Framebuffer.
struct framebuffer_info cover_fbinfo = { struct framebuffer_info cover_fbinfo = {
&_pixel_format_rgb32, //Format. &_pixel_format_rgb32, //Format.
@ -430,7 +434,8 @@ namespace
do_reset_flag = true; do_reset_flag = true;
break; break;
} }
} },
.get_registers = []() -> const interface_device_reg* { return gb_registers; },
}}; }};
core_type type_dmg{{ core_type type_dmg{{

View file

@ -20,6 +20,10 @@ namespace sky
{5, 4, 6, 7, 0, 3, 2} {5, 4, 6, 7, 0, 3, 2}
}; };
struct interface_device_reg sky_registers[] = {
{NULL, NULL, NULL}
};
//Framebuffer. //Framebuffer.
uint32_t cover_fbmem[320*200]; uint32_t cover_fbmem[320*200];
struct framebuffer_info cover_fbinfo = { struct framebuffer_info cover_fbinfo = {
@ -326,7 +330,8 @@ namespace sky
}, },
.get_core_shortname = []() -> std::string { return "sky"; }, .get_core_shortname = []() -> std::string { return "sky"; },
.pre_emulate_frame = [](controller_frame& cf) -> void {}, .pre_emulate_frame = [](controller_frame& cf) -> void {},
.execute_action = [](unsigned id, const std::vector<interface_action_paramval>& p) -> void {} .execute_action = [](unsigned id, const std::vector<interface_action_paramval>& p) -> void {},
.get_registers = []() -> const interface_device_reg* { return sky_registers; },
}}; }};
void controller_magic() void controller_magic()

View file

@ -52,6 +52,10 @@ namespace
0, 0 //Offset. 0, 0 //Offset.
}; };
struct interface_device_reg test_registers[] = {
{NULL, NULL, NULL}
};
#include "ports.inc" #include "ports.inc"
#include "slots.inc" #include "slots.inc"
#include "regions.inc" #include "regions.inc"
@ -172,7 +176,8 @@ namespace
}, },
.get_core_shortname = []() -> std::string { return "test"; }, .get_core_shortname = []() -> std::string { return "test"; },
.pre_emulate_frame = [](controller_frame& cf) -> void {}, .pre_emulate_frame = [](controller_frame& cf) -> void {},
.execute_action = [](unsigned id, const std::vector<interface_action_paramval>& p) -> void {} .execute_action = [](unsigned id, const std::vector<interface_action_paramval>& p) -> void {},
.get_registers = []() -> const interface_device_reg* { return test_registers; },
}}; }};
core_type type_test{{ core_type type_test{{

View file

@ -0,0 +1,120 @@
#include "interface/disassembler.hpp"
#include <stdexcept>
#include <iostream>
namespace
{
template<typename T, bool be> T fetch_generic(std::function<unsigned char()> fetchpc)
{
size_t b = sizeof(T);
T res = 0;
for(size_t i = 0; i < b; i++) {
size_t bit = 8 * (be ? (b - i - 1) : i);
res |= (static_cast<T>(fetchpc()) << bit);
}
return res;
}
}
disassembler::disassembler(const std::string& _name)
{
disasms()[name = _name] = this;
}
disassembler::~disassembler()
{
disasms().erase(name);
}
disassembler& disassembler::byname(const std::string& name)
{
if(disasms().count(name))
return *disasms()[name];
throw std::runtime_error("No such disassembler known");
}
std::map<std::string, disassembler*>& disassembler::disasms()
{
static std::map<std::string, disassembler*> x;
return x;
}
template<> int8_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int8_t, false>(fetchpc);
}
template<> uint8_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint8_t, false>(fetchpc);
}
template<> int16_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int16_t, false>(fetchpc);
}
template<> uint16_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint16_t, false>(fetchpc);
}
template<> int32_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int32_t, false>(fetchpc);
}
template<> uint32_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint32_t, false>(fetchpc);
}
template<> int64_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int64_t, false>(fetchpc);
}
template<> uint64_t disassembler::fetch_le(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint64_t, false>(fetchpc);
}
template<> int8_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int8_t, true>(fetchpc);
}
template<> uint8_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint8_t, true>(fetchpc);
}
template<> int16_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int16_t, true>(fetchpc);
}
template<> uint16_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint16_t, true>(fetchpc);
}
template<> int32_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int32_t, true>(fetchpc);
}
template<> uint32_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint32_t, true>(fetchpc);
}
template<> int64_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<int64_t, true>(fetchpc);
}
template<> uint64_t disassembler::fetch_be(std::function<unsigned char()> fetchpc)
{
return fetch_generic<uint64_t, true>(fetchpc);
}

View file

@ -366,6 +366,7 @@ core_core::core_core(const core_core_params& params)
_get_core_shortname = params.get_core_shortname; _get_core_shortname = params.get_core_shortname;
_pre_emulate_frame = params.pre_emulate_frame; _pre_emulate_frame = params.pre_emulate_frame;
_execute_action = params.execute_action; _execute_action = params.execute_action;
_get_registers = params.get_registers;
hidden = false; hidden = false;
all_cores_set().insert(this); all_cores_set().insert(this);
if(install_handlers_automatically) if(install_handlers_automatically)
@ -519,6 +520,11 @@ void core_core::execute_action(unsigned id, const std::vector<interface_action_p
return _execute_action(id, p); return _execute_action(id, p);
} }
const struct interface_device_reg* core_core::get_registers()
{
return _get_registers();
}
void core_core::do_register_action(const std::string& key, interface_action& act) void core_core::do_register_action(const std::string& key, interface_action& act)
{ {
umutex_class h(actions_lock); umutex_class h(actions_lock);

91
src/lua/disassemble.cpp Normal file
View file

@ -0,0 +1,91 @@
#include "lua/internal.hpp"
#include "interface/disassembler.hpp"
#include "interface/romtype.hpp"
#include "library/bintohex.hpp"
#include "core/memorymanip.hpp"
#include "core/moviedata.hpp"
namespace
{
function_ptr_luafun memdisass(LS, "memory.disassemble", [](lua_state& L, const std::string& fname) -> int {
uint64_t count = 1;
std::string kind = L.get_string(1, fname.c_str());
uint64_t addr = L.get_numeric_argument<uint64_t>(2, fname.c_str());
L.get_numeric_argument<uint64_t>(3, count, fname.c_str());
disassembler* d;
try {
d = &disassembler::byname(kind);
} catch(std::exception& e) {
L.pushstring(e.what());
L.error();
return 0;
}
L.newtable();
uint64_t laddr = addr;
for(uint64_t i = 1; i <= count; i++) {
uint64_t bytes = 0;
L.pushnumber(i);
L.newtable();
L.pushstring("addr");
L.pushnumber(laddr);
L.settable(-3);
L.pushstring("disasm");
L.pushlstring(d->disassemble(laddr, [&bytes, laddr]() -> unsigned char {
return lsnes_memory.read<uint8_t>(laddr + bytes++);
}));
L.settable(-3);
std::vector<unsigned char> tmp;
tmp.resize(bytes);
lsnes_memory.read_range(laddr, &tmp[0], bytes);
L.pushstring("bytes");
L.pushlstring(binary_to_hex(&tmp[0], bytes));
L.settable(-3);
L.settable(-3);
laddr += bytes;
}
return 1;
});
function_ptr_luafun getreg(LS, "memory.getregister", [](lua_state& L, const std::string& fname) -> int {
std::string r = L.get_string(1, fname.c_str());
const interface_device_reg* regs = our_rom->rtype->get_registers();
if(!regs) {
L.pushnil();
return 1;
}
for(size_t i = 0; regs[i].name; i++) {
if(r != regs[i].name)
continue;
if(regs[i].boolean)
L.pushboolean(regs[i].read() != 0);
else
L.pushnumber(regs[i].read());
return 1;
}
L.pushnil();
return 1;
});
function_ptr_luafun setreg(LS, "memory.setregister", [](lua_state& L, const std::string& fname) -> int {
std::string r = L.get_string(1, fname.c_str());
const interface_device_reg* regs = our_rom->rtype->get_registers();
if(!regs) {
return 0;
}
for(size_t i = 0; regs[i].name; i++) {
if(r != regs[i].name)
continue;
if(!regs[i].write)
break;
if(regs[i].boolean)
regs[i].write(L.get_bool(2, fname.c_str()) ? 1 : 0);
else
regs[i].write(L.get_numeric_argument<uint64_t>(2, fname.c_str()));
return 0;
}
return 0;
});
}