dspp: Add a real disassembler

This commit is contained in:
AJR 2025-05-07 09:53:59 -04:00
parent 812782cad5
commit a27347890f
4 changed files with 585 additions and 100 deletions

View File

@ -449,7 +449,7 @@ void dspp_device::parse_operands(uint32_t numops)
else
{
// Sign extend if right justified
val = util::sext(operand, 13);
val = uint16_t(util::sext(operand, 13));
}
m_core->m_operands[opidx++].value = val;
}
@ -1191,7 +1191,7 @@ inline void dspp_device::exec_arithmetic()
}
else
{
m_core->m_acc = util::sext(alu_res, 20) << shift;
m_core->m_acc = util::sext(alu_res << shift, 20);
}
}

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause
// copyright-holders:Philip Bennett
// copyright-holders:Philip Bennett, AJR
/***************************************************************************
dsppdasm.c
@ -21,104 +21,577 @@ uint32_t dspp_disassembler::opcode_alignment() const
return 1;
}
offs_t dspp_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
{
#if 0
uint32_t op = oprom[0] | (oprom[1] << 8) | (oprom[2] << 16) | (oprom[3] << 24);
int opcode = op >> 27;
int cond = (op >> 21) & 1;
int rdst = (op >> 22) & 31;
int rsrc1 = (op >> 16) & 31;
int rsrc2 = op & 0xffff;
int rsrc2_iszero = (!rsrc2 || rsrc2 == 0xffe0);
uint32_t flags = 0;
switch (opcode)
//-------------------------------------------------
// format_operand - Format the value encoded by
// an instruction operand
//-------------------------------------------------
void dspp_disassembler::format_operand(std::ostream &stream, uint16_t operand)
{
uint32_t numregs = 0;
if (operand & 0x8000)
{
case 0x00: sprintf(buffer, "trap $00"); flags = DASMFLAG_STEP_OVER; break;
case 0x01: sprintf(buffer, "b%s $%08x", condition[rdst & 15], pc + ((INT32)(op << 10) >> 8)); break;
case 0x02: if ((op & 0x003fffff) == 3)
{
uint32_t nextop = oprom[4] | (oprom[5] << 8) | (oprom[6] << 16) | (oprom[7] << 24);
if ((nextop >> 27) == 0x10 && ((nextop >> 22) & 31) == rdst && (nextop & 0xffff) == 0)
{
uint32_t nextnextop = oprom[8] | (oprom[9] << 8) | (oprom[10] << 16) | (oprom[11] << 24);
sprintf(buffer, "llit%s $%08x,%s", setcond[cond], nextnextop, reg[rdst]);
return 12 | DASMFLAG_STEP_OVER | DASMFLAG_SUPPORTED;
}
}
if (rdst)
{
flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1);
sprintf(buffer, "bsr %s,$%08x", reg[rdst], pc + ((INT32)(op << 10) >> 8));
}
else
sprintf(buffer, "bra $%08x", pc + ((INT32)(op << 10) >> 8));
break;
case 0x03: sprintf(buffer, "lea%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,2), reg[rdst]); break;
case 0x04: sprintf(buffer, "leah%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break;
case 0x05: sprintf(buffer, "subr%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x06: sprintf(buffer, "xor%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x07: sprintf(buffer, "xorn%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x08: if (!rsrc1 && !rdst && rsrc2_iszero)
sprintf(buffer, "nop");
else if (!rsrc1)
sprintf(buffer, "mov%s %s,%s", setcond[cond], src2(op,0), reg[rdst]);
else if (rsrc2_iszero)
sprintf(buffer, "mov%s %s,%s", setcond[cond], reg[rsrc1], reg[rdst]);
else
sprintf(buffer, "add%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x09: sprintf(buffer, "sub%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x0a: sprintf(buffer, "addc%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x0b: sprintf(buffer, "subc%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x0c: sprintf(buffer, "and%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x0d: sprintf(buffer, "andn%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x0e: if (!rsrc1 && !rdst && rsrc2_iszero)
sprintf(buffer, "nop");
else if (!rsrc1)
sprintf(buffer, "mov%s %s,%s", setcond[cond], src2(op,0), reg[rdst]);
else if (rsrc2_iszero)
sprintf(buffer, "mov%s %s,%s", setcond[cond], reg[rsrc1], reg[rdst]);
else
sprintf(buffer, "or%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x0f: sprintf(buffer, "orn%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x10: sprintf(buffer, "ld%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,2), reg[rdst]); break;
case 0x11: sprintf(buffer, "ldh%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break;
case 0x12: sprintf(buffer, "lduh%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break;
case 0x13: sprintf(buffer, "sth%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,1), reg[rdst]); break;
case 0x14: sprintf(buffer, "st%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,2), reg[rdst]); break;
case 0x15: sprintf(buffer, "ldb%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x16: sprintf(buffer, "ldub%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x17: sprintf(buffer, "stb%s %s[%s],%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x18: sprintf(buffer, "ashr%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x19: sprintf(buffer, "lshr%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x1a: sprintf(buffer, "ashl%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x1b: sprintf(buffer, "rotl%s %s,%s,%s", setcond[cond], reg[rsrc1], src2(op,0), reg[rdst]); break;
case 0x1c: sprintf(buffer, "getps %s", reg[rdst]); break;
case 0x1d: sprintf(buffer, "putps %s", src2(op,0)); break;
case 0x1e: if (rdst && rsrc2_iszero)
{
flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1);
sprintf(buffer, "jsr%s %s,%s", setcond[cond], reg[rdst], reg[rsrc1]);
}
else if (rdst)
{
flags = DASMFLAG_STEP_OVER | DASMFLAG_STEP_OVER_EXTRA(1);
sprintf(buffer, "jsr%s %s,%s[%s]", setcond[cond], reg[rdst], reg[rsrc1], src2(op,2));
}
else if (rsrc2_iszero)
{
if (rsrc1 == 28)
flags = DASMFLAG_STEP_OUT;
sprintf(buffer, "jmp%s %s", setcond[cond], reg[rsrc1]);
}
else
sprintf(buffer, "jmp%s %s[%s]", setcond[cond], reg[rsrc1], src2(op,2));
break;
case 0x1f: sprintf(buffer, "trap $1f"); flags = DASMFLAG_STEP_OVER; break;
// Immediate value
if ((operand & 0xc000) == 0xc000)
{
uint16_t val;
if (operand & 0x2000)
{
// Left justify
val = (operand & 0x1fff) << 3;
}
else
{
// Sign extend if right justified
val = util::sext(operand, 13);
}
util::stream_format(stream, "#0x%04X", val);
}
else if((operand & 0xe000) == 0x8000)
{
// Address operand
uint16_t addr = operand & 0x03ff;
if (operand & 0x0400)
{
// Indirect
stream << '@';
}
util::stream_format(stream, "[0x%03X]", addr);
if (operand & 0x0800)
{
// Write Back
stream << '!';
}
}
else if ((operand & 0xe000) == 0xa000)
{
// 1 or 2 register operand
numregs = (operand & 0x0400) ? 2 : 1;
}
}
else
{
numregs = 3;
}
sprintf(buffer, "????");
#endif
return 1 | SUPPORTED;
if (numregs > 0)
{
uint32_t shifter, regdi;
// Shift successive register operands from a single operand word
for (uint32_t i = 0; i < numregs; ++i)
{
if (i > 0)
stream << ", ";
shifter = ((numregs - i) - 1) * 5;
regdi = (operand >> shifter) & 0x1f;
if (regdi & 0x0010)
{
// Indirect?
stream << '@';
}
util::stream_format(stream, "[R%d]", regdi & 0xf);
if (numregs == 2)
{
if ((i == 0) && (operand & 0x1000))
stream << '!';
else if ((i == 1) && (operand & 0x0800))
stream << '!';
}
else if (numregs == 1)
{
if (operand & 0x800)
stream << '!';
}
}
}
}
//**************************************************************************
// OPCODE DISASSEMBLY
//**************************************************************************
//-------------------------------------------------
// dasm_super_special - Disassemble a super
// special control op
//-------------------------------------------------
offs_t dspp_disassembler::dasm_super_special(std::ostream &stream, uint16_t op)
{
uint32_t sel = (op >> 7) & 7;
switch (sel)
{
case 1: // BAC
{
stream << "BAC";
return 1 | SUPPORTED;
}
case 4: // RTS
{
stream << "RTS";
return 1 | STEP_OUT | SUPPORTED;
}
case 5: // OP_MASK
{
stream << "OP_MASK";
return 1 | SUPPORTED;
}
case 7: // SLEEP
{
stream << "SLEEP";
return 1 | SUPPORTED;
}
case 0: // NOP
{
stream << "NOP";
return 1 | SUPPORTED;
}
default: // Unused
{
util::stream_format(stream, "0x%04X", op);
return 1 | SUPPORTED;
}
}
}
//-------------------------------------------------
// dasm_special - Disassemble a special control op
//-------------------------------------------------
offs_t dspp_disassembler::dasm_special(std::ostream &stream, offs_t pc, const data_buffer &opcodes, uint16_t op)
{
switch ((op >> 10) & 7)
{
case 0:
{
return dasm_super_special(stream, op);
}
case 1: // JUMP
{
util::stream_format(stream, "JUMP 0x%03X", op & 0x3ff);
return 1 | SUPPORTED;
}
case 2: // JSR
{
util::stream_format(stream, "JSR 0x%03X", op & 0x3ff);
return 1 | STEP_OVER | SUPPORTED;
}
case 3: // BFM
{
util::stream_format(stream, "BFM 0x%03X", op & 0x3ff);
return 1 | SUPPORTED;
}
case 4: // MOVEREG
{
uint32_t regdi = op & 0x3f;
util::stream_format(stream, "MOVEREG %sR%d, ", (regdi & 0x0010) ? "@" : "", regdi & 0xf);
format_operand(stream, opcodes.r16(pc + 1));
return 2 | SUPPORTED;
}
case 5: // RBASE
{
util::stream_format(stream, "RBASE%d 0x%03X", (op & 3) << 2, op & 0x3fc);
return 1 | SUPPORTED;
}
case 6: // MOVED
{
util::stream_format(stream, "MOVE [0x%03X], ", op & 0x3ff);
format_operand(stream, opcodes.r16(pc + 1));
return 2 | SUPPORTED;
}
case 7: // MOVEI
{
util::stream_format(stream, "MOVE @[0x%03X], ", op & 0x3ff);
format_operand(stream, opcodes.r16(pc + 1));
return 2 | SUPPORTED;
}
default:
{
util::stream_format(stream, "0x%04X", op);
return 1 | SUPPORTED;
}
}
}
//-------------------------------------------------
// dasm_branch - Disassemble a branch control op
//-------------------------------------------------
offs_t dspp_disassembler::dasm_branch(std::ostream &stream, uint16_t op)
{
uint32_t mode = (op >> 13) & 3;
uint32_t select = (op >> 12) & 1;
uint32_t mask = (op >> 10) & 3;
char flag0, flag1;
if (select == 0)
{
flag0 = 'N';
flag1 = 'V';
}
else
{
flag0 = 'C';
flag1 = 'Z';
}
bool mask0 = (mask & 2) != 0;
bool mask1 = (mask & 1) != 0;
if (mode == 2)
stream << "BFC ";
else
stream << "BTC ";
if (mask0 && mask1)
{
stream << flag1;
stream << flag0;
}
else if (mask0)
{
stream << flag0;
}
else if (mask1)
{
stream << flag1;
}
else
{
stream << '0';
}
util::stream_format(stream, ", 0x%03X", op & 0x3ff);
return 1 | (mask0 || mask1 ? STEP_COND : 0) | SUPPORTED;
}
//-------------------------------------------------
// dasm_complex_branch - Disassemble a complex
// branch control op
//-------------------------------------------------
offs_t dspp_disassembler::dasm_complex_branch(std::ostream &stream, uint16_t op)
{
uint32_t type = (op >> 10) & 7;
switch (type)
{
case 0: // BLT
stream << "BLT";
break;
case 1: // BLE
stream << "BLE";
break;
case 2: // BGE
stream << "BGE";
break;
case 3: // BGT
stream << "BGT";
break;
case 4: // BHI
stream << "BHI";
break;
case 5: // BLS
stream << "BLS";
break;
case 6: // BXS
stream << "BXS";
break;
case 7: // BXC
stream << "BXC";
break;
}
util::stream_format(stream, " 0x%03X", op & 0x3ff);
return 1 | STEP_COND | SUPPORTED;
}
//-------------------------------------------------
// dasm_control - Disassemble a control op
//-------------------------------------------------
offs_t dspp_disassembler::dasm_control(std::ostream &stream, offs_t pc, const data_buffer &opcodes, uint16_t op)
{
uint32_t mode = (op >> 13) & 3;
switch (mode)
{
// Special
case 0:
{
return dasm_special(stream, pc, opcodes, op);
}
// Branches
case 1: case 2:
{
return dasm_branch(stream, op);
}
// Complex branches
case 3:
{
return dasm_complex_branch(stream, op);
}
default:
{
util::stream_format(stream, "0x%04X", op);
return 1 | SUPPORTED;
}
}
}
//-------------------------------------------------
// dasm_arithmetic - Disassemble an arithmetic op
//-------------------------------------------------
offs_t dspp_disassembler::dasm_arithmetic(std::ostream &stream, offs_t pc, const data_buffer &opcodes, uint16_t op)
{
// Decode the various fields
uint32_t numops = (op >> 13) & 3;
uint32_t muxa = (op >> 10) & 3;
uint32_t muxb = (op >> 8) & 3;
uint32_t alu_op = (op >> 4) & 0xf;
uint32_t barrel_code = op & 0xf;
uint32_t opidx = 0;
// Check for operand overflow
if (numops == 0 && ((muxa == 1) || (muxa == 2) || (muxb == 1) || (muxb == 2)))
numops = 4;
// Implicit barrel shift
if (barrel_code == 8)
++numops;
if (muxa == 3 || muxb == 3)
{
uint32_t mul_sel = (op >> 12) & 1;
++opidx;
if (mul_sel)
++opidx;
}
stream << "LOAD ";
if (alu_op != 1)
{
if (alu_op == 9)
stream << "NOT ";
switch (muxa)
{
case 0:
{
stream << "ACC";
break;
}
case 1: case 2:
{
format_operand(stream, opcodes.r16(pc + 1 + opidx++));
break;
}
case 3:
{
uint32_t mul_sel = (op >> 12) & 1;
format_operand(stream, opcodes.r16(pc + 1));
stream << " * ";
if (mul_sel)
format_operand(stream, opcodes.r16(pc + 2));
else
stream << "ACC";
break;
}
}
}
switch (alu_op)
{
case 0: // _TRA
{
break;
}
case 1: // _NEG
{
stream << "NEG ";
break;
}
case 2: // _+
{
stream << " + ";
break;
}
case 3: // _+C
{
stream << " + C";
break;
}
case 4: // _-
{
stream << " - ";
break;
}
case 5: // _-B
{
stream << " - B";
break;
}
case 6: // _++
{
stream << "++";
break;
}
case 7: // _--
{
stream << "--";
break;
}
case 8: // _TRL
{
break;
}
case 9: // _NOT
{
break;
}
case 10: // _AND
{
stream << " AND ";
break;
}
case 11: // _NAND
{
stream << " NAND ";
break;
}
case 12: // _OR
{
stream << " OR ";
break;
}
case 13: // _NOR
{
stream << " NOR ";
break;
}
case 14: // _XOR
{
stream << " XOR ";
break;
}
case 15: // _XNOR
{
stream << " XNOR ";
break;
}
}
if (alu_op == 1 || alu_op == 2 || alu_op == 4 || alu_op >= 10)
{
switch (muxb)
{
case 0:
{
stream << "ACC";
break;
}
case 1: case 2:
{
format_operand(stream, opcodes.r16(pc + 1 + opidx++));
break;
}
case 3:
{
uint32_t mul_sel = (op >> 12) & 1;
format_operand(stream, opcodes.r16(pc + 1));
stream << " * ";
if (mul_sel)
format_operand(stream, opcodes.r16(pc + 2));
else
stream << "ACC";
break;
}
}
}
// Barrel shift
static const int32_t shifts[8] = { 0, 1, 2, 3, 4, 5, 8, 16 };
if (barrel_code == 8)
{
stream << " SHIFT ";
format_operand(stream, opcodes.r16(pc + 1 + opidx++));
}
else if (barrel_code & 8)
{
// Right shift
uint32_t shift = shifts[(~barrel_code + 1) & 7];
if (shift != 0)
{
util::stream_format(stream, " >> %d", shift);
}
}
else
{
// Left shift
uint32_t shift = shifts[barrel_code];
if (shift == 16)
{
// Clip and saturate
stream << " SAT";
}
else if (shift != 0)
{
util::stream_format(stream, " << %d", shift);
}
}
if (opidx < numops)
{
stream << ", ";
format_operand(stream, opcodes.r16(pc + 1 + opidx));
}
return (numops + 1) | SUPPORTED;
}
offs_t dspp_disassembler::disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params)
{
// Decode and disassemble
uint16_t op = opcodes.r16(pc);
if (op & 0x8000)
return dasm_control(stream, pc, opcodes, op);
else
return dasm_arithmetic(stream, pc, opcodes, op);
}

View File

@ -1,5 +1,5 @@
// license:BSD-3-Clause
// copyright-holders:Philip Bennett
// copyright-holders:Philip Bennett, AJR
/*
DSPP disassembler shim
*/
@ -17,6 +17,16 @@ public:
virtual uint32_t opcode_alignment() const override;
virtual offs_t disassemble(std::ostream &stream, offs_t pc, const data_buffer &opcodes, const data_buffer &params) override;
private:
static void format_operand(std::ostream &stream, uint16_t operand);
static offs_t dasm_control(std::ostream &stream, offs_t pc, const data_buffer &opcodes, uint16_t op);
static offs_t dasm_super_special(std::ostream &stream, uint16_t op);
static offs_t dasm_special(std::ostream &stream, offs_t pc, const data_buffer &opcodes, uint16_t op);
static offs_t dasm_branch(std::ostream &stream, uint16_t op);
static offs_t dasm_complex_branch(std::ostream &stream, uint16_t op);
static offs_t dasm_arithmetic(std::ostream &stream, offs_t pc, const data_buffer &opcodes, uint16_t op);
};
#endif // MAME_CPU_DSPP_DSPPDASM_H

View File

@ -48,6 +48,7 @@ using util::BIT;
#include "cpu/dsp32/dsp32dis.h"
#include "cpu/dsp56000/dsp56000d.h"
#include "cpu/dsp56156/dsp56dsm.h"
#include "cpu/dspp/dsppdasm.h"
#include "cpu/e0c6200/e0c6200d.h"
#include "cpu/e132xs/32xsdasm.h"
#include "cpu/es5510/es5510d.h"
@ -452,6 +453,7 @@ static const dasm_table_entry dasm_table[] =
{ "dsp32c", le, 0, []() -> util::disasm_interface * { return new dsp32c_disassembler; } },
{ "dsp56000", be, -2, []() -> util::disasm_interface * { return new dsp56000_disassembler; } },
{ "dsp56156", le, -1, []() -> util::disasm_interface * { return new dsp56156_disassembler; } },
{ "dspp", be, -1, []() -> util::disasm_interface * { return new dspp_disassembler; } },
{ "e0c6200", be, -1, []() -> util::disasm_interface * { return new e0c6200_disassembler; } },
{ "epg3231", le, -1, []() -> util::disasm_interface * { return new epg3231_disassembler; } },
// { "es5510", be, 0, []() -> util::disasm_interface * { return new es5510_disassembler; } }, // Currently does nothing