From 08e5f6acc99025bddd685ad8edc2c8c9e68ef94e Mon Sep 17 00:00:00 2001 From: Ilari Liusvaara Date: Wed, 3 Jul 2013 04:06:40 +0300 Subject: [PATCH] Probably buggy SNES disassembler --- include/interface/disassembler.hpp | 22 ++ include/interface/romtype.hpp | 18 ++ src/core/disassemble.cpp | 119 +++++++++ src/emulation/bsnes-legacy/Makefile | 2 +- src/emulation/bsnes-legacy/core.cpp | 96 +++---- src/emulation/bsnes-legacy/scpu-disasm.cpp | 279 +++++++++++++++++++++ src/emulation/gambatte/core.cpp | 7 +- src/emulation/sky/sky.cpp | 7 +- src/emulation/test/test.cpp | 7 +- src/interface/disassembler.cpp | 120 +++++++++ src/interface/romtype.cpp | 6 + src/lua/disassemble.cpp | 91 +++++++ 12 files changed, 726 insertions(+), 48 deletions(-) create mode 100644 include/interface/disassembler.hpp create mode 100644 src/core/disassemble.cpp create mode 100644 src/emulation/bsnes-legacy/scpu-disasm.cpp create mode 100644 src/interface/disassembler.cpp create mode 100644 src/lua/disassemble.cpp diff --git a/include/interface/disassembler.hpp b/include/interface/disassembler.hpp new file mode 100644 index 00000000..126a0711 --- /dev/null +++ b/include/interface/disassembler.hpp @@ -0,0 +1,22 @@ +#ifndef _interface__disassembler__hpp__included__ +#define _interface__disassembler__hpp__included__ + +#include +#include +#include + +class disassembler +{ +public: + disassembler(const std::string& name); + virtual ~disassembler(); + virtual std::string disassemble(uint64_t base, std::function fetchpc) = 0; + static disassembler& byname(const std::string& name); + template static T fetch_le(std::function fetchpc); + template static T fetch_be(std::function fetchpc); +private: + std::string name; + static std::map& disasms(); +}; + +#endif diff --git a/include/interface/romtype.hpp b/include/interface/romtype.hpp index ef0d32bf..78eab8af 100644 --- a/include/interface/romtype.hpp +++ b/include/interface/romtype.hpp @@ -17,6 +17,17 @@ struct core_romimage; struct core_romimage_info; 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. */ @@ -371,6 +382,10 @@ struct core_core_params * Execute action. */ void (*execute_action)(unsigned id, const std::vector& p); +/** + * Get set of interface device registers. + */ + const struct interface_device_reg* (*get_registers)(); }; struct core_region @@ -469,6 +484,7 @@ struct core_core void do_unregister_action(const std::string& key); std::set get_actions(); _param_register_proxy param_register_proxy; + const interface_device_reg* get_registers(); private: std::string (*_core_identifier)(); bool (*_set_region)(core_region& region); @@ -494,6 +510,7 @@ private: std::string (*_get_core_shortname)(); void (*_pre_emulate_frame)(controller_frame& cf); void (*_execute_action)(unsigned id, const std::vector& p); + const interface_device_reg* (*_get_registers)(); bool hidden; std::map actions; mutex_class actions_lock; @@ -561,6 +578,7 @@ public: bool is_hidden() { return core->is_hidden(); } void pre_emulate_frame(controller_frame& cf) { return core->pre_emulate_frame(cf); } std::set get_actions() { return core->get_actions(); } + const interface_device_reg* get_registers() { return core->get_registers(); } private: core_type(const core_type&); core_type& operator=(const core_type&); diff --git a/src/core/disassemble.cpp b/src/core/disassemble.cpp new file mode 100644 index 00000000..2698025a --- /dev/null +++ b/src/core/disassemble.cpp @@ -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 +#include +#include + +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(k); + } + return address; + } + + function_ptr_command disassemble(lsnes_cmd, "disassemble", "Disassemble code", + "Syntax: disassemble [] [to ]\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 [] [to ]" << 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(r[4]); + std::string file; + if(r[6] != "") + file = r[6]; + std::list 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(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 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; + }); +} diff --git a/src/emulation/bsnes-legacy/Makefile b/src/emulation/bsnes-legacy/Makefile index 8487cb0e..bdea70ec 100644 --- a/src/emulation/bsnes-legacy/Makefile +++ b/src/emulation/bsnes-legacy/Makefile @@ -1,5 +1,5 @@ ifdef BSNES_VERSION -OBJECTS=core.$(OBJECT_SUFFIX) +OBJECTS=core.$(OBJECT_SUFFIX) scpu-disasm.$(OBJECT_SUFFIX) BSNES_CFLAGS= BSNES_LDFLAGS= ifdef BSNES_IS_COMPAT diff --git a/src/emulation/bsnes-legacy/core.cpp b/src/emulation/bsnes-legacy/core.cpp index 49716ba4..03bb7370 100644 --- a/src/emulation/bsnes-legacy/core.cpp +++ b/src/emulation/bsnes-legacy/core.cpp @@ -94,6 +94,56 @@ namespace 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 "slots.inc" #include "regions.inc" @@ -867,7 +917,8 @@ again2: do_hreset_flag = true; break; } - } + }, + .get_registers = []() -> const interface_device_reg* { return snes_registers; }, }}; core_type type_snes{{ @@ -937,49 +988,6 @@ again2: 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 char snes_debug_cb_keys[SNES::Debugger::Breakpoints]; char snes_debug_cb_trace; diff --git a/src/emulation/bsnes-legacy/scpu-disasm.cpp b/src/emulation/bsnes-legacy/scpu-disasm.cpp new file mode 100644 index 00000000..cf934b08 --- /dev/null +++ b/src/emulation/bsnes-legacy/scpu-disasm.cpp @@ -0,0 +1,279 @@ +#include "interface/disassembler.hpp" +#include "library/string.hpp" +#include +#include + +namespace +{ + std::string tohex(uint8_t x) + { + return (stringfmt() << std::setw(2) << std::setfill('0') << std::hex << static_cast(x)).str(); + } + + std::string tohex(uint16_t x) + { + return (stringfmt() << std::setw(4) << std::setfill('0') << std::hex << x).str(); + } + + template struct varsize {}; + template<> struct varsize { typedef uint8_t type_t; }; + template<> struct varsize { typedef uint16_t type_t; }; + + template struct scpu_disassembler : public disassembler + { + scpu_disassembler(const std::string& n) : disassembler(n) {} + std::string disassemble(uint64_t base, std::function fetchpc); + }; + + struct ssmp_disassembler : public disassembler + { + ssmp_disassembler(const std::string& n) : disassembler(n) {} + std::string disassemble(uint64_t base, std::function fetchpc); + }; + + scpu_disassembler d1("snes-xa"); + scpu_disassembler d2("snes-xA"); + scpu_disassembler d3("snes-Xa"); + scpu_disassembler 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: ??? : 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 + std::string scpu_disassembler::disassemble(uint64_t base, std::function 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::type_t>(fetchpc)); + break; + case 'b': + o << "$" << tohex(fetch_le(fetchpc)); + break; + case 'l': + o << "$" << tohex(fetch_le(fetchpc)); + o << tohex(fetch_le(fetchpc)); + break; + case 'r': + o << "$" << tohex(static_cast(base + 2 + + fetch_le(fetchpc))); + break; + case 'R': + o << "$" << tohex(static_cast(base + 3 + + fetch_le(fetchpc))); + case 'w': + o << "$" << tohex(fetch_le(fetchpc)); + break; + case 'x': + o << "$" << tohex(fetch_le::type_t>(fetchpc)); + break; + } + i++; + } + } + return o.str(); + } + + std::string ssmp_disassembler::disassemble(uint64_t base, std::function 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(fetchpc)); + break; + case 'c': + tmp = fetch_le(fetchpc); + o << "$" << tohex(static_cast(tmp & 0x1FFF)) << ":" << (tmp >> 13); + break; + case 'r': + o << "$" << tohex(static_cast(base + 2 + + fetch_le(fetchpc))); + break; + case 'R': + o << "$" << tohex(static_cast(base + 3 + + fetch_le(fetchpc))); + case 'w': + o << "$" << tohex(fetch_le(fetchpc)); + break; + } + i++; + } + } + return o.str(); + } +} \ No newline at end of file diff --git a/src/emulation/gambatte/core.cpp b/src/emulation/gambatte/core.cpp index 790f74aa..4ce3a3ca 100644 --- a/src/emulation/gambatte/core.cpp +++ b/src/emulation/gambatte/core.cpp @@ -61,6 +61,10 @@ namespace interface_action act_reset(gambatte_core, 0, "Soft reset", "reset", {}); + struct interface_device_reg gb_registers[] = { + {NULL, NULL, NULL} + }; + //Framebuffer. struct framebuffer_info cover_fbinfo = { &_pixel_format_rgb32, //Format. @@ -430,7 +434,8 @@ namespace do_reset_flag = true; break; } - } + }, + .get_registers = []() -> const interface_device_reg* { return gb_registers; }, }}; core_type type_dmg{{ diff --git a/src/emulation/sky/sky.cpp b/src/emulation/sky/sky.cpp index 2e7435e5..aa976c94 100644 --- a/src/emulation/sky/sky.cpp +++ b/src/emulation/sky/sky.cpp @@ -20,6 +20,10 @@ namespace sky {5, 4, 6, 7, 0, 3, 2} }; + struct interface_device_reg sky_registers[] = { + {NULL, NULL, NULL} + }; + //Framebuffer. uint32_t cover_fbmem[320*200]; struct framebuffer_info cover_fbinfo = { @@ -326,7 +330,8 @@ namespace sky }, .get_core_shortname = []() -> std::string { return "sky"; }, .pre_emulate_frame = [](controller_frame& cf) -> void {}, - .execute_action = [](unsigned id, const std::vector& p) -> void {} + .execute_action = [](unsigned id, const std::vector& p) -> void {}, + .get_registers = []() -> const interface_device_reg* { return sky_registers; }, }}; void controller_magic() diff --git a/src/emulation/test/test.cpp b/src/emulation/test/test.cpp index 1428be00..e391f8f6 100644 --- a/src/emulation/test/test.cpp +++ b/src/emulation/test/test.cpp @@ -52,6 +52,10 @@ namespace 0, 0 //Offset. }; + struct interface_device_reg test_registers[] = { + {NULL, NULL, NULL} + }; + #include "ports.inc" #include "slots.inc" #include "regions.inc" @@ -172,7 +176,8 @@ namespace }, .get_core_shortname = []() -> std::string { return "test"; }, .pre_emulate_frame = [](controller_frame& cf) -> void {}, - .execute_action = [](unsigned id, const std::vector& p) -> void {} + .execute_action = [](unsigned id, const std::vector& p) -> void {}, + .get_registers = []() -> const interface_device_reg* { return test_registers; }, }}; core_type type_test{{ diff --git a/src/interface/disassembler.cpp b/src/interface/disassembler.cpp new file mode 100644 index 00000000..ab44ac32 --- /dev/null +++ b/src/interface/disassembler.cpp @@ -0,0 +1,120 @@ +#include "interface/disassembler.hpp" +#include +#include + +namespace +{ + template T fetch_generic(std::function 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(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& disassembler::disasms() +{ + static std::map x; + return x; +} + +template<> int8_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint8_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> int16_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint16_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> int32_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint32_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> int64_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint64_t disassembler::fetch_le(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> int8_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint8_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> int16_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint16_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> int32_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint32_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> int64_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} + +template<> uint64_t disassembler::fetch_be(std::function fetchpc) +{ + return fetch_generic(fetchpc); +} diff --git a/src/interface/romtype.cpp b/src/interface/romtype.cpp index 45259cf0..ae53a894 100644 --- a/src/interface/romtype.cpp +++ b/src/interface/romtype.cpp @@ -366,6 +366,7 @@ core_core::core_core(const core_core_params& params) _get_core_shortname = params.get_core_shortname; _pre_emulate_frame = params.pre_emulate_frame; _execute_action = params.execute_action; + _get_registers = params.get_registers; hidden = false; all_cores_set().insert(this); if(install_handlers_automatically) @@ -519,6 +520,11 @@ void core_core::execute_action(unsigned id, const std::vector int { + uint64_t count = 1; + std::string kind = L.get_string(1, fname.c_str()); + uint64_t addr = L.get_numeric_argument(2, fname.c_str()); + L.get_numeric_argument(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(laddr + bytes++); + })); + L.settable(-3); + + std::vector 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(2, fname.c_str())); + return 0; + } + return 0; + }); + +}