mirror of
https://github.com/devinacker/bsnes-plus.git
synced 2025-04-02 10:52:46 -04:00
420 lines
11 KiB
C++
420 lines
11 KiB
C++
#ifdef SUPERFX_CPP
|
|
|
|
#define case4(id) \
|
|
case id+ 0: case id+ 1: case id+ 2: case id+ 3
|
|
#define case6(id) \
|
|
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5
|
|
#define case12(id) \
|
|
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
|
|
case id+ 8: case id+ 9: case id+10: case id+11
|
|
#define case15(id) \
|
|
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
|
|
case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14
|
|
#define case16(id) \
|
|
case id+ 0: case id+ 1: case id+ 2: case id+ 3: case id+ 4: case id+ 5: case id+ 6: case id+ 7: \
|
|
case id+ 8: case id+ 9: case id+10: case id+11: case id+12: case id+13: case id+14: case id+15
|
|
|
|
uint32 SuperFX::decode(uint8 offset_type, uint32 addr, uint32 pc) {
|
|
uint32 r = 0;
|
|
|
|
switch(offset_type) {
|
|
case ImpliedLoop:
|
|
r = (pc & 0xff0000) | regs.r[13];
|
|
break;
|
|
case ImpliedROM:
|
|
r = (regs.rombr << 16) + regs.r[14];
|
|
break;
|
|
case ImpliedRAM:
|
|
r = 0x700000 + (regs.rambr << 16) + regs.ramaddr;
|
|
break;
|
|
case Relative: // addr is offset in second byte
|
|
r = (pc & 0xff0000) + (((pc & 0xffff) + 2 + (int8)addr) & 0xffff);
|
|
break;
|
|
case LinkRelative: // addr is offset in low nibble of instruction
|
|
r = (pc & 0xff0000) + (((pc & 0xffff) + 1 + addr) & 0xffff);
|
|
break;
|
|
case RegisterJump: // addr is register no. in low nibble of instruction
|
|
r = (pc & 0xff0000) | regs.r[addr];
|
|
break;
|
|
case RegisterLJump: // addr is register no. in low nibble of instruction
|
|
r = (regs.r[addr] << 16) | regs.sr();
|
|
break;
|
|
case IRegister: // addr is register no. in low nibble of instruction
|
|
r = 0x700000 + (regs.rambr << 16) + regs.r[addr];
|
|
break;
|
|
case RegImmediateLong: // addr is immediate address in second two bytes
|
|
r = (pc & 0xff0000) | addr;
|
|
break;
|
|
case RegDirect: // addr is RAM buffer offset in second byte
|
|
r = 0x700000 + (regs.rambr << 16) + (addr << 1);
|
|
break;
|
|
case RegAbsolute: // addr is RAM buffer offset in second two
|
|
r = 0x700000 + (regs.rambr << 16) + addr;
|
|
break;
|
|
}
|
|
|
|
return(r & 0xffffff);
|
|
}
|
|
|
|
void SuperFX::disassemble_opcode_ex(SuperFX::Opcode &opcode, uint32 addr, bool alt1, bool alt2) {
|
|
uint8 param[3];
|
|
|
|
SNES::debugger.bus_access = true;
|
|
param[0] = superfxbus.read(addr + 0);
|
|
param[1] = superfxbus.read(addr + 1);
|
|
param[2] = superfxbus.read(addr + 2);
|
|
SNES::debugger.bus_access = false;
|
|
|
|
SuperFX::OpcodeInfo op = opcode_info(alt1, alt2, param[0]);
|
|
opcode.set(0, op.mode, op.name, param, opcode_length(op.mode) - 1);
|
|
|
|
switch (param[0]) {
|
|
case (0x05):
|
|
case6 (0x98):
|
|
opcode.flags |= Opcode::FLAG_BRA;
|
|
if (param[0] == 0x9b /* jmp to link register */)
|
|
opcode.flags |= Opcode::FLAG_RETURN;
|
|
break;
|
|
case4 (0x06):
|
|
case6 (0x0a):
|
|
case (0x3c):
|
|
opcode.flags |= Opcode::FLAG_BRA_CONTINUE;
|
|
break;
|
|
case12(0x30):
|
|
case12(0x40):
|
|
opcode.flags |= Opcode::FLAG_INDIRECT;
|
|
break;
|
|
case (0x3d):
|
|
opcode.flags |= Opcode::FLAG_SET_ALT1;
|
|
break;
|
|
case (0x3e):
|
|
opcode.flags |= Opcode::FLAG_SET_ALT2;
|
|
break;
|
|
case (0x3f):
|
|
opcode.flags |= (Opcode::FLAG_SET_ALT1 | Opcode::FLAG_SET_ALT2);
|
|
break;
|
|
}
|
|
}
|
|
|
|
void SuperFX::disassemble_opcode(char *output, uint32 addr, bool track_regs) {
|
|
char t[256] = "";
|
|
*output = 0;
|
|
|
|
SNES::debugger.bus_access = true;
|
|
uint8 op0 = superfxbus.read(addr + 0);
|
|
uint8 op1 = superfxbus.read(addr + 1);
|
|
uint8 op2 = superfxbus.read(addr + 2);
|
|
SNES::debugger.bus_access = false;
|
|
|
|
uint16 disassemble_regs = ((1 << regs.dreg) | (1 << regs.sreg));
|
|
|
|
OpcodeInfo op_info = opcode_info(regs.sfr.alt1, regs.sfr.alt2, op0);
|
|
switch (op_info.mode) {
|
|
default:
|
|
case Implied:
|
|
case ImpliedROM:
|
|
case ImpliedRAM:
|
|
break;
|
|
case ImpliedPlot:
|
|
disassemble_regs |= ((1 << 1) | (1 << 2));
|
|
break;
|
|
case ImpliedLongMul:
|
|
disassemble_regs |= ((1 << 4) | (1 << 6));
|
|
break;
|
|
case ImpliedFixedMul:
|
|
disassemble_regs |= (1 << 6);
|
|
break;
|
|
case ImpliedMerge:
|
|
disassemble_regs |= ((1 << 7) | (1 << 8));
|
|
break;
|
|
case ImpliedLoop:
|
|
disassemble_regs |= ((1 << 12) | (1 << 13));
|
|
break;
|
|
case Relative:
|
|
sprintf(t, "$%.4x", (addr + (int8_t)op1 + 2) & 0xffff);
|
|
break;
|
|
case LinkRelative:
|
|
disassemble_regs |= (1 << 11);
|
|
sprintf(t, "$%.4x", (addr + (op0 & 15) + 1) & 0xffff);
|
|
break;
|
|
case Register:
|
|
case RegisterJump:
|
|
case RegisterLJump:
|
|
disassemble_regs |= (1 << (op0 & 15));
|
|
sprintf(t, "r%u", op0 & 15);
|
|
break;
|
|
case IRegister:
|
|
disassemble_regs |= (1 << (op0 & 15));
|
|
sprintf(t, "(r%u)", op0 & 15);
|
|
break;
|
|
case RegImmediate:
|
|
disassemble_regs |= (1 << (op0 & 15));
|
|
sprintf(t, "r%u, #$%.2x", op0 & 15, op1);
|
|
break;
|
|
case RegImmediateLong:
|
|
disassemble_regs |= (1 << (op0 & 15));
|
|
sprintf(t, "r%u, #$%.2x%.2x", op0 & 15, op2, op1);
|
|
break;
|
|
case RegDirect:
|
|
disassemble_regs |= (1 << (op0 & 15));
|
|
sprintf(t, "r%u, $%.3x", op0 & 15, op1 << 1);
|
|
break;
|
|
case RegAbsolute:
|
|
disassemble_regs |= (1 << (op0 & 15));
|
|
sprintf(t, "r%u, $%.2x%.2x", op0 & 15, op2, op1);
|
|
break;
|
|
case ImmediateShort:
|
|
sprintf(t, "#$0%x", op0 & 15);
|
|
break;
|
|
}
|
|
|
|
sprintf(output, "%.6x %-5s %-18s", addr, op_info.name, t);
|
|
|
|
// status register and some flags (TODO: other flags?)
|
|
sprintf(t, "S:%.4x %c%c%c%c ",
|
|
(unsigned) regs.sfr,
|
|
(unsigned) regs.sfr & 2 ? 'Z' : '.',
|
|
(unsigned) regs.sfr & 4 ? 'C' : '.',
|
|
(unsigned) regs.sfr & 8 ? 'N' : '.',
|
|
(unsigned) regs.sfr & 16 ? 'V' : '.');
|
|
strcat(output, t);
|
|
|
|
// print all current and past used registers
|
|
if (track_regs) {
|
|
for (int i = 0; i < 16; i++) {
|
|
if ((disassemble_regs | disassemble_lastregs) & (1 << i)) {
|
|
sprintf(t, "R%-2u:%.4x ", i, (unsigned) regs.r[i]);
|
|
strcat(output, t);
|
|
}
|
|
}
|
|
|
|
disassemble_lastregs = disassemble_regs;
|
|
}
|
|
}
|
|
|
|
SuperFX::OpcodeInfo SuperFX::opcode_info(bool alt1, bool alt2, uint8 opcode) const
|
|
{
|
|
bool alt3 = alt1 && alt2;
|
|
|
|
switch (opcode) {
|
|
default:
|
|
case (0x00):
|
|
return { "stop ", Implied };
|
|
case (0x01):
|
|
return { "nop ", Implied };
|
|
case (0x02):
|
|
return { "cache", Implied };
|
|
case (0x03):
|
|
return { "lsr ", Implied };
|
|
case (0x04):
|
|
return { "rol ", Implied };
|
|
case (0x05):
|
|
return { "bra ", Relative };
|
|
case (0x06):
|
|
return { "bge ", Relative };
|
|
case (0x07):
|
|
return { "blt ", Relative };
|
|
case (0x08):
|
|
return { "bne ", Relative };
|
|
case (0x09):
|
|
return { "beq ", Relative };
|
|
case (0x0a):
|
|
return { "bpl ", Relative };
|
|
case (0x0b):
|
|
return { "bmi ", Relative };
|
|
case (0x0c):
|
|
return { "bcc ", Relative };
|
|
case (0x0d):
|
|
return { "bcs ", Relative };
|
|
case (0x0e):
|
|
return { "bvc ", Relative };
|
|
case (0x0f):
|
|
return { "bvs ", Relative };
|
|
case16(0x10):
|
|
return { "to ", Register };
|
|
case16(0x20):
|
|
return { "with ", Register };
|
|
case12(0x30):
|
|
if (alt1) {
|
|
return { "stb ", IRegister };
|
|
} else {
|
|
return { "stw ", IRegister };
|
|
}
|
|
case (0x3c):
|
|
return { "loop ", ImpliedLoop };
|
|
case (0x3d):
|
|
return { "alt1 ", Implied };
|
|
case (0x3e):
|
|
return { "alt2 ", Implied };
|
|
case (0x3f):
|
|
return { "alt3 ", Implied };
|
|
case12(0x40):
|
|
if (alt1) {
|
|
return { "ldb ", IRegister };
|
|
} else {
|
|
return { "ldw ", IRegister };
|
|
}
|
|
case (0x4c):
|
|
if (alt1) {
|
|
return { "rpix ", ImpliedPlot };
|
|
} else {
|
|
return { "plot ", ImpliedPlot };
|
|
}
|
|
case (0x4d):
|
|
return { "swap ", Implied };
|
|
case (0x4e):
|
|
if (alt1) {
|
|
return { "cmode", Implied };
|
|
} else {
|
|
return { "color", Implied };
|
|
}
|
|
case (0x4f):
|
|
return { "not ", Implied };
|
|
case16(0x50):
|
|
if (alt3) {
|
|
return { "adc ", ImmediateShort };
|
|
} else if (alt2) {
|
|
return { "add ", ImmediateShort };
|
|
} else if (alt1) {
|
|
return { "adc ", Register };
|
|
} else {
|
|
return { "add ", Register };
|
|
}
|
|
case16(0x60):
|
|
if (alt3) {
|
|
return { "cmp ", Register };
|
|
} else if (alt2) {
|
|
return { "sub ", ImmediateShort };
|
|
} else if (alt1) {
|
|
return { "sbc ", Register };
|
|
} else {
|
|
return { "sub ", Register };
|
|
}
|
|
case (0x70):
|
|
return { "merge", ImpliedMerge };
|
|
case15(0x71):
|
|
if (alt3) {
|
|
return { "bic ", ImmediateShort };
|
|
} else if (alt2) {
|
|
return { "and ", ImmediateShort };
|
|
} else if (alt1) {
|
|
return { "bic ", Register };
|
|
} else {
|
|
return { "and ", Register };
|
|
}
|
|
case16(0x80):
|
|
if (alt3) {
|
|
return { "umult", ImmediateShort };
|
|
} else if (alt2) {
|
|
return { "mult ", ImmediateShort };
|
|
} else if (alt1) {
|
|
return { "umult", Register };
|
|
} else {
|
|
return { "mult ", Register };
|
|
}
|
|
case (0x90):
|
|
return { "sbk ", ImpliedRAM };
|
|
case4 (0x91):
|
|
return { "link ", LinkRelative };
|
|
case (0x95):
|
|
return { "sex ", Implied };
|
|
case (0x96):
|
|
if (alt1) {
|
|
return { "div2 ", Implied };
|
|
} else {
|
|
return { "asr ", Implied };
|
|
}
|
|
case (0x97):
|
|
return { "ror ", Implied };
|
|
case6 (0x98):
|
|
if (alt1) {
|
|
return { "ljmp ", RegisterLJump };
|
|
} else {
|
|
return { "jmp ", RegisterJump };
|
|
}
|
|
case (0x9e):
|
|
return { "lob ", Implied };
|
|
case (0x9f):
|
|
if (alt1) {
|
|
return { "lmult", ImpliedLongMul };
|
|
} else {
|
|
return { "fmult", ImpliedFixedMul };
|
|
}
|
|
case16(0xa0):
|
|
if (alt3) {
|
|
return { "lms ", Register };
|
|
} else if (alt2) {
|
|
return { "sms ", RegDirect };
|
|
} else if (alt1) {
|
|
return { "lms ", RegDirect };
|
|
} else {
|
|
return { "ibt ", RegImmediate };
|
|
}
|
|
case16(0xb0):
|
|
return { "from ", Register };
|
|
case (0xc0):
|
|
return { "hib ", Implied };
|
|
case15(0xc1):
|
|
if (alt3) {
|
|
return { "xor ", ImmediateShort };
|
|
} else if (alt2) {
|
|
return { "or ", ImmediateShort };
|
|
} else if (alt1) {
|
|
return { "xor ", Register };
|
|
} else {
|
|
return { "or ", Register };
|
|
}
|
|
case15(0xd0):
|
|
return { "inc ", Register };
|
|
case (0xdf):
|
|
if (alt3) {
|
|
return { "romb ", Implied };
|
|
} else if (alt2) {
|
|
return { "ramb ", Implied };
|
|
} else {
|
|
return { "getc ", ImpliedROM };
|
|
}
|
|
case15(0xe0):
|
|
return { "dec ", Register };
|
|
case (0xef):
|
|
if (alt3) {
|
|
return { "getbs", ImpliedROM };
|
|
} else if (alt2) {
|
|
return { "getbl", ImpliedROM };
|
|
} else if (alt1) {
|
|
return { "getbh", ImpliedROM };
|
|
} else {
|
|
return { "getb ", ImpliedROM };
|
|
}
|
|
case16(0xf0):
|
|
if (alt2) {
|
|
return { "sm ", RegAbsolute };
|
|
} if (alt1) {
|
|
return { "lm ", RegAbsolute };
|
|
} else {
|
|
return { "iwt ", RegImmediateLong };
|
|
}
|
|
}
|
|
}
|
|
|
|
uint8 SuperFX::opcode_length(uint8 offset_type) {
|
|
switch (offset_type) {
|
|
default:
|
|
return 1;
|
|
case Relative:
|
|
case RegImmediate:
|
|
case RegDirect:
|
|
return 2;
|
|
case RegImmediateLong:
|
|
case RegAbsolute:
|
|
return 3;
|
|
}
|
|
}
|
|
|
|
#undef case4
|
|
#undef case6
|
|
#undef case12
|
|
#undef case15
|
|
#undef case16
|
|
|
|
#endif
|