summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/dspp/dsppfe.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/dspp/dsppfe.cpp')
-rw-r--r--src/devices/cpu/dspp/dsppfe.cpp625
1 files changed, 625 insertions, 0 deletions
diff --git a/src/devices/cpu/dspp/dsppfe.cpp b/src/devices/cpu/dspp/dsppfe.cpp
new file mode 100644
index 00000000000..533a020ab89
--- /dev/null
+++ b/src/devices/cpu/dspp/dsppfe.cpp
@@ -0,0 +1,625 @@
+// license:BSD-3-Clause
+// copyright-holders:Philip Bennett
+
+/******************************************************************************
+
+ Front-end for DSPP recompiler
+
+******************************************************************************/
+
+#include "emu.h"
+#include "dsppfe.h"
+
+
+//#define REG_USED(desc,x) do { (desc).regin[0] |= 1 << (x); } while(0)
+//#define REG_MODIFIED(desc,x) do { (desc).regout[0] |= 1 << (x); } while(0)
+
+#define CC_C_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0)
+#define CC_C_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0)
+#define CC_Z_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0)
+#define CC_Z_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0)
+#define CC_N_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0)
+#define CC_N_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0)
+#define CC_V_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0)
+#define CC_V_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0)
+#define CC_X_USED(desc) do { (desc).regin[0] |= 1 << 16; } while(0)
+#define CC_X_MODIFIED(desc) do { (desc).regout[0] |= 1 << 16; } while(0)
+
+#define CC_FLAGS_MODIFIED(desc) do { } while(0)
+//#define MULT_FLAGS_MODIFIED(desc) do { MN_MODIFIED(desc);MV_MODIFIED(desc);MU_MODIFIED(desc);MI_MODIFIED(desc); } while(0)
+//#define SHIFT_FLAGS_MODIFIED(desc) do { SZ_MODIFIED(desc);SV_MODIFIED(desc);SS_MODIFIED(desc); } while(0)
+
+dspp_frontend::dspp_frontend(dspp_device *dspp, uint32_t window_start, uint32_t window_end, uint32_t max_sequence)
+ : drc_frontend(*dspp, window_start, window_end, max_sequence),
+ m_dspp(dspp)
+{
+
+}
+
+
+#if 0
+// opcode branch flags
+const uint32_t OPFLAG_IS_UNCONDITIONAL_BRANCH = 0x00000001; // instruction is unconditional branch
+const uint32_t OPFLAG_IS_CONDITIONAL_BRANCH = 0x00000002; // instruction is conditional branch
+const uint32_t OPFLAG_IS_BRANCH = (OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_IS_CONDITIONAL_BRANCH);
+const uint32_t OPFLAG_IS_BRANCH_TARGET = 0x00000004; // instruction is the target of a branch
+const uint32_t OPFLAG_IN_DELAY_SLOT = 0x00000008; // instruction is in the delay slot of a branch
+const uint32_t OPFLAG_INTRABLOCK_BRANCH = 0x00000010; // instruction branches within the block
+
+// opcode exception flags
+const uint32_t OPFLAG_CAN_TRIGGER_SW_INT = 0x00000020; // instruction can trigger a software interrupt
+const uint32_t OPFLAG_CAN_EXPOSE_EXTERNAL_INT = 0x00000040; // instruction can expose an external interrupt
+const uint32_t OPFLAG_CAN_CAUSE_EXCEPTION = 0x00000080; // instruction may generate exception
+const uint32_t OPFLAG_WILL_CAUSE_EXCEPTION = 0x00000100; // instruction will generate exception
+const uint32_t OPFLAG_PRIVILEGED = 0x00000200; // instruction is privileged
+
+// opcode virtual->physical translation flags
+const uint32_t OPFLAG_VALIDATE_TLB = 0x00000400; // instruction must validate TLB before execution
+const uint32_t OPFLAG_MODIFIES_TRANSLATION = 0x00000800; // instruction modifies the TLB
+const uint32_t OPFLAG_COMPILER_PAGE_FAULT = 0x00001000; // compiler hit a page fault when parsing
+const uint32_t OPFLAG_COMPILER_UNMAPPED = 0x00002000; // compiler hit unmapped memory when parsing
+
+// opcode flags
+const uint32_t OPFLAG_INVALID_OPCODE = 0x00004000; // instruction is invalid
+const uint32_t OPFLAG_VIRTUAL_NOOP = 0x00008000; // instruction is a virtual no-op
+
+// opcode sequence flow flags
+const uint32_t OPFLAG_REDISPATCH = 0x00010000; // instruction must redispatch after completion
+const uint32_t OPFLAG_RETURN_TO_START = 0x00020000; // instruction must jump back to the beginning after completion
+const uint32_t OPFLAG_END_SEQUENCE = 0x00040000; // this is the last instruction in a sequence
+const uint32_t OPFLAG_CAN_CHANGE_MODES = 0x00080000; // instruction can change modes
+
+// execution semantics
+const uint32_t OPFLAG_READS_MEMORY = 0x00100000; // instruction reads memory
+const uint32_t OPFLAG_WRITES_MEMORY = 0x00200000; // instruction writes memory
+#endif
+
+bool dspp_frontend::describe(opcode_desc &desc, const opcode_desc *prev)
+{
+ uint16_t op = desc.opptr.w[0] = m_dspp->read_op(desc.physpc);
+
+ // TODO: NOOOOPE
+ desc.cycles = 1; // TODO: Extra cycles for extra operands
+ desc.length = 2;
+
+ // Decode and execute
+ if (op & 0x8000)
+ {
+ switch ((op >> 13) & 3)
+ {
+ case 0:
+ return describe_special(op, desc);
+
+ case 1:
+ case 2:
+ return describe_branch(op, desc);
+
+ case 3:
+ return describe_complex_branch(op, desc);
+ }
+
+ return false;
+ }
+ else
+ {
+ return describe_arithmetic(op, desc);
+ }
+
+ return false;
+}
+
+
+bool dspp_frontend::describe_special(uint16_t op, opcode_desc &desc)
+{
+ switch ((op >> 10) & 7)
+ {
+ case 0:
+ {
+ // Super-special
+ switch ((op >> 7) & 7)
+ {
+ case 1: // BAC - TODO: MERGE?
+ {
+ //desc.regin[0] = m_acc;
+ desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
+ desc.targetpc = BRANCH_TARGET_DYNAMIC;
+ return true;
+ }
+ case 4: // RTS
+ {
+ desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
+ desc.targetpc = BRANCH_TARGET_DYNAMIC;
+ return true;
+ }
+ case 5: // OP_MASK
+ {
+ // TODO
+ return true;
+ }
+
+ case 7: // SLEEP
+ {
+ // TODO
+ return true;
+ }
+
+ case 0: // NOP
+ case 2: // Unused
+ case 3:
+ case 6:
+ return true;
+ }
+
+ break;
+ }
+ case 1: // JUMP - TODO: MERGE?
+ {
+ desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
+ desc.targetpc = op & 0x3ff;
+ return true;
+ }
+ case 2: // JSR
+ {
+ desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
+ desc.targetpc = op & 0x3ff;
+ return true;
+ }
+ case 3: // BFM
+ {
+ // TODO: What sort of branch is this?
+// desc.flags |= OPFLAG_IS_UNCONDITIONAL_BRANCH | OPFLAG_END_SEQUENCE;
+// desc.targetpc = 0; // FIXME
+ return false;
+ }
+ case 4: // MOVEREG
+ {
+ desc.flags |= OPFLAG_WRITES_MEMORY;
+
+ // Indirect
+ if (op & 0x0010)
+ desc.flags |= OPFLAG_READS_MEMORY;
+
+ return true;
+ }
+ case 5: // RBASE
+ {
+ return true;
+ }
+ case 6: // MOVED
+ {
+ desc.flags |= OPFLAG_WRITES_MEMORY;
+ return true;
+ }
+ case 7: // MOVEI
+ {
+ desc.flags |= OPFLAG_READS_MEMORY | OPFLAG_WRITES_MEMORY;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool dspp_frontend::describe_branch(uint16_t op, opcode_desc &desc)
+{
+ const uint32_t select = (op >> 12) & 1;
+
+ if (select == 0)
+ {
+ CC_N_USED(desc);
+ CC_V_USED(desc);
+ }
+ else
+ {
+ CC_C_USED(desc);
+ CC_Z_USED(desc);
+ }
+
+ // TODO: Can these be unconditional?
+ desc.flags |= OPFLAG_IS_CONDITIONAL_BRANCH;
+ desc.targetpc = op & 0x3ff;
+
+ return true;
+}
+
+bool dspp_frontend::describe_complex_branch(uint16_t op, opcode_desc &desc)
+{
+ switch ((op >> 10) & 7)
+ {
+ case 0: // BLT
+ CC_N_USED(desc);
+ CC_V_USED(desc);
+ break;
+ case 1: // BLE
+ CC_N_USED(desc);
+ CC_V_USED(desc);
+ CC_Z_USED(desc);
+ break;
+ case 2: // BGE
+ CC_N_USED(desc);
+ CC_V_USED(desc);
+ break;
+ case 3: // BGT
+ CC_N_USED(desc);
+ CC_V_USED(desc);
+ CC_Z_USED(desc);
+ break;
+ case 4: // BHI
+ case 5: // BLS
+ CC_C_USED(desc);
+ CC_Z_USED(desc);
+ break;
+ case 6: // BXS
+ case 7: // BXC
+ CC_X_USED(desc);
+ break;
+ }
+
+ desc.flags |= OPFLAG_IS_CONDITIONAL_BRANCH;
+ desc.targetpc = op & 0x3ff;
+
+ return true;
+}
+
+bool dspp_frontend::describe_arithmetic(uint16_t op, opcode_desc &desc)
+{
+ #if 0
+ // 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;
+
+ int32_t mul_res = 0;
+ uint32_t alu_res = 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;
+
+ // TODO: We need to know:
+ // Number of cycles
+ // Number of bytes
+ // Registers read
+ // Registers written
+ // Does it read memory?
+ // Does it write memory?
+
+// parse_operands(numops);
+
+ if (muxa == 3 || muxb == 3)
+ {
+ uint32_t mul_sel = (op >> 12) & 1;
+
+ int32_t op1 = sign_extend16(read_next_operand());
+ int32_t op2 = sign_extend16(mul_sel ? read_next_operand() : m_core->m_acc >> 4);
+
+ mul_res = (op1 * op2) >> 11;
+ }
+
+ int32_t alu_a, alu_b;
+
+ switch (muxa)
+ {
+ case 0:
+ {
+ ACCUMULATOR_USED
+ alu_a = m_core->m_acc;
+ break;
+ }
+ case 1: case 2:
+ {
+ alu_a = read_next_operand() << 4;
+ break;
+ }
+ case 3:
+ {
+ alu_a = mul_res;
+ break;
+ }
+ }
+
+ switch (muxb)
+ {
+ case 0:
+ {
+ ACCUMULATOR_USED
+ alu_b = m_core->m_acc;
+ break;
+ }
+ case 1: case 2:
+ {
+ alu_b = read_next_operand() << 4;
+ break;
+ }
+ case 3:
+ {
+ alu_b = mul_res;
+ break;
+ }
+ }
+
+ // All flags ar emodifed
+ CC_FLAGS_MODIFIED(desc);
+
+ switch (alu_op)
+ {
+ case 0: // _TRA
+ {
+// alu_res = alu_a;
+ break;
+ }
+ case 1: // _NEG
+ {
+// alu_res = -alu_b;
+ break;
+ }
+ case 2: // _+
+ {
+// alu_res = alu_a + alu_b;
+
+// if ((alu_a & 0x80000) == (alu_b & 0x80000) &&
+// (alu_a & 0x80000) != (alu_res & 0x80000))
+// m_core->m_flags |= DSPI_FLAG_CC_OVER;
+
+// if (alu_res & 0x00100000)
+// m_core->m_flags |= DSPI_FLAG_CC_CARRY;
+
+// CC_V_MODIFIED(desc);
+// CC_C_MODIFIED(desc);
+ break;
+ }
+ case 3: // _+C
+ {
+// alu_res = alu_a + (m_core->m_flags & DSPI_FLAG_CC_CARRY) ? (1 << 4) : 0;
+
+// if (alu_res & 0x00100000)
+// m_core->m_flags |= DSPI_FLAG_CC_CARRY;
+
+ CC_C_USED(desc);
+ CC_C_MODIFIED(desc);
+ break;
+ }
+ case 4: // _-
+ {
+// alu_res = alu_a - alu_b;
+
+// if ((alu_a & 0x80000) == (~alu_b & 0x80000) &&
+// (alu_a & 0x80000) != (alu_res & 0x80000))
+// m_core->m_flags |= DSPI_FLAG_CC_OVER;
+
+// if (alu_res & 0x00100000)
+// m_core->m_flags |= DSPI_FLAG_CC_CARRY;
+
+ CC_C_MODIFIED(desc);
+ CC_V_MODIFIED(desc);
+ break;
+ }
+ case 5: // _-B
+ {
+// alu_res = alu_a - (m_core->m_flags & DSPI_FLAG_CC_CARRY) ? (1 << 4) : 0;
+
+// if (alu_res & 0x00100000)
+// m_core->m_flags |= DSPI_FLAG_CC_CARRY;
+
+ CC_C_USED(desc);
+ CC_C_MODIFIED(desc);
+ break;
+ }
+ case 6: // _++
+ {
+// alu_res = alu_a + 1;
+
+// if (!(alu_a & 0x80000) && (alu_res & 0x80000))
+// m_core->m_flags |= DSPI_FLAG_CC_OVER;
+
+ CC_V_MODIFIED(desc);
+ break;
+ }
+ case 7: // _--
+ {
+// alu_res = alu_a - 1;
+
+// if ((alu_a & 0x80000) && !(alu_res & 0x80000))
+// m_core->m_flags |= DSPI_FLAG_CC_OVER;
+
+ CC_V_MODIFIED(desc);
+ break;
+ }
+ case 8: // _TRL
+ {
+ //alu_res = alu_a;
+ break;
+ }
+ case 9: // _NOT
+ {
+ //alu_res = ~alu_a;
+ break;
+ }
+ case 10: // _AND
+ {
+ //alu_res = alu_a & alu_b;
+ break;
+ }
+ case 11: // _NAND
+ {
+ //alu_res = ~(alu_a & alu_b);
+ break;
+ }
+ case 12: // _OR
+ {
+ //alu_res = alu_a | alu_b;
+ break;
+ }
+ case 13: // _NOR
+ {
+ //alu_res = ~(alu_a | alu_b);
+ break;
+ }
+ case 14: // _XOR
+ {
+ //alu_res = alu_a ^ alu_b;
+ break;
+ }
+ case 15: // _XNOR
+ {
+ //alu_res = ~(alu_a ^ alu_b);
+ break;
+ }
+ }
+
+
+ if (alu_res & 0x00080000)
+ m_core->m_flags |= DSPI_FLAG_CC_NEG;
+
+ if ((alu_res & 0x000ffff0) == 0)
+ m_core->m_flags |= DSPI_FLAG_CC_ZERO;
+
+ if ((alu_res & 0x0000000f) == 0)
+ m_core->m_flags |= DSPI_FLAG_CC_EXACT;
+
+ CC_N_MODIFIED(desc);
+ CC_Z_MODIFIED(desc);
+ CC_X_MODIFIED(desc);
+
+ ACCUMULATOR_MODIFIED_ALWAYS;
+
+ // Barrel shift
+ static const int32_t shifts[8] = { 0, 1, 2, 3, 4, 5, 8, 16 };
+
+ if (barrel_code == 8)
+ barrel_code = read_next_operand();
+
+ if (barrel_code & 8)
+ {
+ // Right shift
+ uint32_t shift = shifts[(~barrel_code + 1) & 7];
+
+ if (alu_op < 8)
+ {
+ // Arithmetic
+ m_core->m_acc = sign_extend20(alu_res) >> shift;
+ }
+ else
+ {
+ // Logical
+ m_core->m_acc = (alu_res & 0xfffff) >> shift;
+ }
+ }
+ else
+ {
+ // Left shift
+ uint32_t shift = shifts[barrel_code];
+
+ if (shift == 16)
+ {
+ // Clip and saturate
+ if (m_core->m_flags & DSPI_FLAG_CC_OVER)
+ m_core->m_acc = (m_core->m_flags & DSPI_FLAG_CC_NEG) ? 0x7ffff : 0xfff80000;
+ else
+ m_core->m_acc = sign_extend20(alu_res);
+ }
+ else
+ {
+ m_core->m_acc = sign_extend20(alu_res) << shift;
+ }
+ }
+
+ if (m_core->m_writeback >= 0)
+ {
+ write_data(m_core->m_writeback, m_core->m_acc >> 4);
+ m_core->m_writeback = -1;
+ }
+ else if (opidx < numops)
+ {
+ write_next_operand(m_core->m_acc >> 4);
+ }
+#endif
+ return true;
+}
+
+
+#if 0
+void dspp_device::parse_operands(uint32_t numops)
+{
+ uint32_t addr, val = 0xBAD;
+ uint32_t opidx = 0;
+ uint32_t operand = 0;
+ uint32_t numregs = 0;
+
+ uint32_t opidx = 0;
+
+ while (opidx < numops)
+ {
+ uint16_t op = desc.opptr.w[opidx + 1] = m_dspp->read_op(desc.physpc + opidx * 2);
+
+ desc.length += 2;
+ ++desc.cycles;
+
+ if (op & 0x8000)
+ {
+ // Immediate value
+ if ((op & 0xc000) == 0xc000)
+ {
+ // Nothing?-
+ }
+ else if((op & 0xe000) == 0x8000)
+ {
+ if (op & 0x0400) // Indirect
+ desc.flags |= OPFLAG_READS_MEMORY;
+
+ if (op & 0x0800 )// Write Back
+ desc.flags |= OPFLAG_WRITES_MEMORY;
+
+ ++opidx;
+ }
+ else if ((op & 0xe000) == 0xa000)
+ {
+ // 1 or 2 register operand
+ numregs = (op & 0x0400) ? 2 : 1;
+ }
+ }
+ else
+ {
+ numregs = 3;
+ }
+
+ if (numregs > 0)
+ {
+ // Shift successive register operands from a single operand word
+ for (uint32_t i = 0; i < numregs; ++i)
+ {
+ uint32_t shift = ((numregs - i) - 1) * 5;
+ uint32_t regdi = (operand >> shift) & 0x1f;
+ // OP USES REGBASE?
+
+ if (regdi & 0x0010)
+ {
+ // Indirect
+ desc.flags |= OPFLAG_READS_MEMORY;
+ }
+
+ if (numregs == 2)
+ {
+ // Write back
+ if ((i == 0) && (operand & 0x1000))
+ desc.flags |= OPFLAG_WRITES_MEMORY;
+ else if ((i == 1) && (operand & 0x0800))
+ desc.flags |= OPFLAG_WRITES_MEMORY;
+ }
+ else if (numregs == 1)
+ {
+ if (operand & 0x800)
+ desc.flags |= OPFLAG_WRITES_MEMORY;
+ }
+ }
+ numregs = 0;
+ }
+ }
+}
+#endif