summaryrefslogtreecommitdiffstatshomepage
diff options
context:
space:
mode:
author therealmogminer@gmail.com <therealmogminer@gmail.com>2016-06-18 17:06:49 +0200
committer therealmogminer@gmail.com <therealmogminer@gmail.com>2016-06-18 17:06:49 +0200
commitcaf907403afd76dc1d662df666d67273072d55dd (patch)
tree531261cce537d032612e3f4f648d10e6cef3dd01
parent6d50e3cb360945da98531acc685e8193e309157f (diff)
Add (untested) trap support to SPARC core
-rw-r--r--src/devices/cpu/sparc/mb86901.cpp303
-rw-r--r--src/devices/cpu/sparc/sparc.h9
-rw-r--r--src/devices/cpu/sparc/sparcdasm.cpp18
-rw-r--r--src/devices/cpu/sparc/sparcdefs.h36
4 files changed, 333 insertions, 33 deletions
diff --git a/src/devices/cpu/sparc/mb86901.cpp b/src/devices/cpu/sparc/mb86901.cpp
index 9d96afa71b9..462cd044f9e 100644
--- a/src/devices/cpu/sparc/mb86901.cpp
+++ b/src/devices/cpu/sparc/mb86901.cpp
@@ -9,8 +9,7 @@
// compatible instruction set.
//
// To-Do:
-// - Traps
-// - Ops: Ticc, FBFcc, RETT, LDF, STF, SPARCv8 ops
+// - Ops: FBFcc, LDF, STF, SPARCv8 ops
// - FPU support
// - Coprocessor support
//
@@ -43,6 +42,39 @@ mb86901_device::mb86901_device(const machine_config &mconfig, const char *tag, d
void mb86901_device::device_start()
{
+ m_trap_priorities[0] = 1;
+ m_trap_priorities[1] = 2;
+ m_trap_priorities[2] = 3;
+ m_trap_priorities[3] = 4;
+ m_trap_priorities[4] = 5;
+ m_trap_priorities[5] = 6;
+ m_trap_priorities[6] = 6;
+ m_trap_priorities[7] = 7;
+ m_trap_priorities[8] = 8;
+ m_trap_priorities[9] = 10;
+ m_trap_priorities[10] = 11;
+ for (int i = 11; i <= 16; i++)
+ m_trap_priorities[i] = 31;
+ m_trap_priorities[17] = 27;
+ m_trap_priorities[18] = 26;
+ m_trap_priorities[19] = 25;
+ m_trap_priorities[20] = 24;
+ m_trap_priorities[21] = 23;
+ m_trap_priorities[22] = 22;
+ m_trap_priorities[23] = 21;
+ m_trap_priorities[24] = 20;
+ m_trap_priorities[25] = 19;
+ m_trap_priorities[26] = 18;
+ m_trap_priorities[27] = 17;
+ m_trap_priorities[28] = 16;
+ m_trap_priorities[29] = 15;
+ m_trap_priorities[30] = 14;
+ m_trap_priorities[31] = 13;
+ for (int i = 32; i < 128; i++)
+ m_trap_priorities[i] = 31;
+ for (int i = 128; i < 256; i++)
+ m_trap_priorities[i] = 12;
+
memset(m_dbgregs, 0, 24 * sizeof(UINT32));
m_user_insn = &space(AS_USER_INSN);
@@ -206,6 +238,7 @@ const address_space_config *mb86901_device::memory_space_config(address_spacenum
UINT32 mb86901_device::read_byte(UINT8 asi, UINT32 address)
{
m_asi = asi;
+
// TODO: check for traps
return LOAD_UBA(asi, address);
}
@@ -220,28 +253,28 @@ INT32 mb86901_device::read_signed_byte(UINT8 asi, UINT32 address)
UINT32 mb86901_device::read_half(UINT8 asi, UINT32 address)
{
m_asi = asi;
- // TODO: check for traps
+ // TODO: data_access_exception, data_access_error traps
return LOAD_UHA(asi, address);
}
INT32 mb86901_device::read_signed_half(UINT8 asi, UINT32 address)
{
m_asi = asi;
- // TODO: check for traps
+ // TODO: data_access_exception, data_access_error traps
return LOAD_SHA(asi, address);
}
UINT32 mb86901_device::read_word(UINT8 asi, UINT32 address)
{
m_asi = asi;
- // TODO: check for traps
+ // TODO: data_access_exception, data_access_error traps
return LOAD_WA(asi, address);
}
UINT64 mb86901_device::read_doubleword(UINT8 asi, UINT32 address)
{
m_asi = asi;
- // TODO: check for traps
+ // TODO: data_access_exception, data_access_error traps
return LOAD_DA(asi, address);
}
@@ -574,7 +607,7 @@ bool mb86901_device::execute_group2(UINT32 op)
bool v = ((arg1 & 0x80000000) == (arg2 & 0x80000000) && (arg2 & 0x80000000) != (result & 0x80000000)) || ((arg1 & 3) != 0) || ((arg2 & 3) != 0);
if (v)
{
- // TODO: trap
+ queue_trap(sparc_tag_overflow);
}
else
{
@@ -591,7 +624,7 @@ bool mb86901_device::execute_group2(UINT32 op)
bool v = ((arg1 & 0x80000000) == (arg2 & 0x80000000) && (arg2 & 0x80000000) != (result & 0x80000000)) || ((arg1 & 3) != 0) || ((arg2 & 3) != 0);
if (v)
{
- // TODO: trap
+ queue_trap(sparc_tag_overflow);
}
else
{
@@ -603,7 +636,45 @@ bool mb86901_device::execute_group2(UINT32 op)
break;
}
case 36: // mulscc
+ {
+ // Explanatory quotes from The SPARC Architecture Manual Version 8, pg. 112 (pg. 110 in sparcv8.pdf)
+
+ // (1) The multiplier is established as r[rs2] if the i field is zero, or sign_ext(simm13) if the i field is one.
+ UINT32 multiplier = arg2;
+
+ // (2) A 32-bit value is computed by shifting r[rs1] right by one bit with "N xor V" from the PSR replacing the
+ // high-order bit. (This is the proper sign for the previous partial product.)
+ UINT32 rs1 = arg1;
+ bool n = ICC_N_SET;
+ bool v = ICC_V_SET;
+ UINT32 shifted = (rs1 >> 1) | ((n ^ v) ? 0x80000000 : 0);
+
+ if (m_y & 1)
+ { // (3) If the least significant bit of the Y register = 1, the shifted value from step (2) is added to the multiplier.
+ arg1 = multiplier;
+ arg2 = shifted;
+ }
+ else
+ { // If the LSB of the Y register = 0, then 0 is added to the shifted value from step (2).
+ arg1 = shifted;
+ arg2 = 0;
+ }
+
+ // (4) The sum from step (3) is written into r[rd].
+ UINT32 result = arg1 + arg2;
+ SET_RDREG(result);
+
+ // (5) The integer condition codes, icc, are updated according to the addition performed in step (3).
+ TEST_ICC_NZ(result);
+ if ((arg1 & 0x80000000) == (arg2 & 0x80000000) && (arg2 & 0x80000000) != (result & 0x80000000))
+ SET_ICC_V_FLAG;
+ if (result < arg1 || result < arg2)
+ SET_ICC_C_FLAG;
+
+ // (6) The Y register is shifted right by one bit, with the LSB of the unshifted r[rs1] replacing the MSB of Y.
+ m_y = (m_y >> 1) | ((rs1 & 1) ? 0x80000000 : 0);
break;
+ }
case 37: // sll
SET_RDREG(arg1 << arg2);
break;
@@ -669,7 +740,7 @@ bool mb86901_device::execute_group2(UINT32 op)
m_icount--;
if (addr & 3)
{
- // mem_address_not_aligned trap
+ queue_trap(sparc_mem_address_not_aligned);
}
else
{
@@ -681,9 +752,51 @@ bool mb86901_device::execute_group2(UINT32 op)
break;
}
case 57: // rett
- break;
+ {
+ UINT8 new_cwp = (m_cwp + 1) % WINDOW_COUNT;
+ if (TRAPS_ENABLED)
+ {
+ if (IS_USER)
+ {
+ queue_trap(sparc_privileged_instruction);
+ }
+ else
+ {
+ queue_trap(sparc_illegal_instruction);
+ }
+ break;
+ }
+ else
+ {
+ if (IS_USER)
+ {
+ queue_trap(sparc_reset, sparc_privileged_instruction);
+ break;
+ }
+ else if (m_wim & (1 << new_cwp))
+ {
+ queue_trap(sparc_reset, sparc_window_underflow);
+ break;
+ }
+ else if (ADDRESS & 3)
+ {
+ queue_trap(sparc_reset, sparc_mem_address_not_aligned);
+ break;
+ }
+ }
+
+ m_cwp = new_cwp;
+
+ m_s = m_ps;
+ m_et = true;
+
+ UINT32 target = arg1 + arg2;
+ PC = nPC;
+ nPC = target;
+ return false;
+ }
case 58: // ticc
- break;
+ return execute_ticc(op);
case 59:
if (RD == 0)
{ // flush, SPARCv8
@@ -694,7 +807,7 @@ bool mb86901_device::execute_group2(UINT32 op)
UINT8 new_cwp = ((m_cwp + WINDOW_COUNT) - 1) % WINDOW_COUNT;
if (m_wim & (1 << new_cwp))
{
- // TODO: generate window_overflow trap
+ queue_trap(sparc_window_overflow);
}
else
{
@@ -707,7 +820,7 @@ bool mb86901_device::execute_group2(UINT32 op)
UINT8 new_cwp = (m_cwp + 1) % WINDOW_COUNT;
if (m_wim & (1 << new_cwp))
{
- // TODO: generate window_underflow trap
+ queue_trap(sparc_window_overflow);
}
else
{
@@ -739,6 +852,26 @@ void mb86901_device::update_gpr_pointers()
}
}
+bool mb86901_device::check_main_traps(UINT32 op, bool privileged, UINT32 alignment, UINT8 registeralign, bool noimmediate)
+{
+ bool trap_queued = false;
+ if (privileged && !m_s)
+ {
+ queue_trap(sparc_privileged_instruction);
+ trap_queued = true;
+ }
+ if (alignment & ADDRESS)
+ {
+ queue_trap(sparc_mem_address_not_aligned);
+ trap_queued = true;
+ }
+ if ((registeralign & RD) || (noimmediate && USEIMM))
+ {
+ queue_trap(sparc_illegal_instruction);
+ trap_queued = true;
+ }
+ return trap_queued;
+}
//-------------------------------------------------
// execute_group3 - execute an opcode in group 3
@@ -761,29 +894,34 @@ void mb86901_device::execute_group3(UINT32 op)
switch (OP3)
{
case 0: // ld
+ check_main_traps(op, false, 3, 0, false);
SET_RDREG(read_word(m_s ? 10 : 11, ADDRESS));
break;
case 1: // ldub
SET_RDREG(read_byte(m_s ? 10 : 11, ADDRESS));
break;
case 2: // lduh
+ check_main_traps(op, false, 1, 0, false);
SET_RDREG(read_half(m_s ? 10 : 11, ADDRESS));
break;
case 3: // ldd
+ check_main_traps(op, false, 7, 1, false);
SET_RDREG(read_word(m_s ? 10 : 11, ADDRESS));
REG(RD+1) = read_word(m_s ? 10 : 11, ADDRESS+4);
break;
case 4: // st
+ check_main_traps(op, false, 3, 0, false);
write_word(m_s ? 10 : 11, ADDRESS, RDREG);
break;
case 5: // stb
write_byte(m_s ? 10 : 11, ADDRESS, UINT8(RDREG));
break;
case 6: // sth
+ check_main_traps(op, false, 1, 0, false);
write_word(m_s ? 10 : 11, ADDRESS, UINT16(RDREG));
break;
case 7: // std
- // TODO: trap if RD is odd
+ check_main_traps(op, false, 7, 1, false);
write_word(m_s ? 10 : 11, ADDRESS, RDREG);
write_word(m_s ? 10 : 11, ADDRESS, REG(RD+1));
break;
@@ -791,6 +929,7 @@ void mb86901_device::execute_group3(UINT32 op)
SET_RDREG(read_signed_byte(m_s ? 10 : 11, ADDRESS));
break;
case 10: // lsdh
+ check_main_traps(op, false, 1, 0, false);
SET_RDREG(read_signed_half(m_s ? 10 : 11, ADDRESS));
break;
case 13: // ldstub
@@ -800,39 +939,49 @@ void mb86901_device::execute_group3(UINT32 op)
case 15: // swap, SPARCv8
break;
case 16: // lda
+ check_main_traps(op, true, 3, 0, true);
SET_RDREG(read_word(ASI, ADDRESS));
break;
case 17: // lduba
+ check_main_traps(op, true, 0, 0, true);
SET_RDREG(read_byte(ASI, ADDRESS));
break;
case 18: // lduha
+ check_main_traps(op, true, 1, 0, true);
SET_RDREG(read_half(ASI, ADDRESS));
break;
case 19: // ldda
+ check_main_traps(op, true, 7, 1, true);
SET_RDREG(read_word(ASI, ADDRESS));
REG(RD+1) = read_word(ASI, ADDRESS+4);
break;
case 20: // sta
+ check_main_traps(op, true, 3, 0, true);
write_word(ASI, ADDRESS, RDREG);
break;
case 21: // stba
+ check_main_traps(op, true, 0, 0, true);
write_byte(ASI, ADDRESS, UINT8(RDREG));
break;
case 22: // stha
+ check_main_traps(op, true, 1, 0, true);
write_half(ASI, ADDRESS, UINT16(RDREG));
break;
case 23: // stda
- // TODO: trap if RD is odd
+ check_main_traps(op, true, 7, 1, true);
write_word(ASI, ADDRESS, RDREG);
write_word(ASI, ADDRESS+4, REG(RD+1));
break;
case 25: // ldsba
+ check_main_traps(op, true, 0, 0, true);
SET_RDREG(read_signed_byte(ASI, ADDRESS));
break;
case 26: // ldsha
+ check_main_traps(op, true, 1, 0, true);
SET_RDREG(read_signed_half(ASI, ADDRESS));
break;
case 29: // ldstuba
+ check_main_traps(op, true, 0, 0, true);
SET_RDREG(read_byte(ASI, ADDRESS));
write_byte(ASI, ADDRESS, 0xff);
break;
@@ -873,31 +1022,43 @@ void mb86901_device::execute_group3(UINT32 op)
//-------------------------------------------------
-// execute_bicc - execute a branch opcode
+// evaluate_condition - evaluate a given integer
+// condition code
//-------------------------------------------------
-bool mb86901_device::execute_bicc(UINT32 op)
+bool mb86901_device::evaluate_condition(UINT32 op)
{
- bool branch_taken = false;
+ bool take = false;
bool n = ICC_N_SET;
bool z = ICC_Z_SET;
bool v = ICC_V_SET;
bool c = ICC_C_SET;
- switch(COND & 7) // COND & 8
- { // 0 8
- case 0: branch_taken = false; break; // bn ba
- case 1: branch_taken = z; break; // bz bne
- case 2: branch_taken = z | (n ^ z); break; // ble bg
- case 3: branch_taken = n ^ z; break; // bl bge
- case 4: branch_taken = c | z; break; // bleu bgu
- case 5: branch_taken = c; break; // bcs bcc
- case 6: branch_taken = n; break; // bneg bpos
- case 7: branch_taken = v; break; // bvs bvc
+ switch(COND & 7) // COND & 8
+ { // 0 8
+ case 0: take = false; break; // bn ba
+ case 1: take = z; break; // bz bne
+ case 2: take = z | (n ^ z); break; // ble bg
+ case 3: take = n ^ z; break; // bl bge
+ case 4: take = c | z; break; // bleu bgu
+ case 5: take = c; break; // bcs bcc
+ case 6: take = n; break; // bneg bpos
+ case 7: take = v; break; // bvs bvc
}
if (COND & 8)
- branch_taken = !branch_taken;
+ take = !take;
+
+ return take;
+}
+
+//-------------------------------------------------
+// execute_bicc - execute a branch opcode
+//-------------------------------------------------
+
+bool mb86901_device::execute_bicc(UINT32 op)
+{
+ bool branch_taken = evaluate_condition(op);
if (branch_taken)
{
@@ -922,6 +1083,87 @@ bool mb86901_device::execute_bicc(UINT32 op)
return true;
}
+
+//-------------------------------------------------
+// execute_ticc - execute a conditional trap
+//-------------------------------------------------
+
+bool mb86901_device::execute_ticc(UINT32 op)
+{
+ bool trap_taken = evaluate_condition(op);
+
+ if (trap_taken)
+ {
+ UINT32 arg2 = USEIMM ? SIMM7 : RS2REG;
+ UINT8 tt = 128 + ((RS1REG + arg2) & 0x7f);
+ queue_trap(sparc_trap_instruction, tt);
+ return false;
+ }
+
+ return true;
+}
+
+
+//-------------------------------------------------
+// queue_trap - flag an incoming trap of a given
+// type
+//-------------------------------------------------
+
+void mb86901_device::queue_trap(UINT8 type, UINT8 tt_override)
+{
+ if (type == sparc_reset)
+ {
+ m_queued_priority = m_trap_priorities[0];
+ m_queued_tt = tt_override;
+ }
+ else
+ {
+ if (type == sparc_trap_instruction)
+ {
+ type = tt_override;
+ }
+
+ if (m_trap_priorities[type] < m_queued_priority)
+ {
+ m_queued_priority = m_trap_priorities[type];
+ m_queued_tt = type;
+ }
+ }
+}
+
+
+//-------------------------------------------------
+// invoke_queued_traps - prioritize and invoke
+// traps that have been queued by the previous
+// instruction, if any. Returns true if a trap
+// was invoked.
+//-------------------------------------------------
+
+bool mb86901_device::invoke_queued_traps()
+{
+ if (m_queued_priority > 0)
+ {
+ m_queued_priority = 0;
+
+ m_et = false;
+ m_ps = m_s;
+ m_s = true;
+ m_cwp = ((m_cwp + WINDOW_COUNT) - 1) % WINDOW_COUNT;
+ MAKE_PSR;
+ update_gpr_pointers();
+ REG(17) = PC;
+ REG(18) = nPC;
+ m_tbr |= m_queued_tt << 4;
+
+ PC = m_tbr;
+ nPC = m_tbr + 4;
+ return true;
+ }
+
+ return false;
+}
+
+
//-------------------------------------------------
// execute_run - execute a timeslice's worth of
// opcodes
@@ -984,7 +1226,8 @@ void mb86901_device::execute_run()
REG(0) = 0;
- if (update_npc)
+ bool trap_taken = invoke_queued_traps();
+ if (!trap_taken && update_npc)
{
PC = nPC;
nPC = PC + 4;
diff --git a/src/devices/cpu/sparc/sparc.h b/src/devices/cpu/sparc/sparc.h
index 5e963e18df9..337fa1bcfc1 100644
--- a/src/devices/cpu/sparc/sparc.h
+++ b/src/devices/cpu/sparc/sparc.h
@@ -46,11 +46,17 @@ public:
UINT32 pc() { return m_pc; }
protected:
+ void queue_trap(UINT8 type, UINT8 tt_override = 0);
+ bool invoke_queued_traps();
+ bool check_main_traps(UINT32 op, bool privileged, UINT32 alignment, UINT8 registeralign, bool noimmediate);
+
void update_gpr_pointers();
void save_restore_update_cwp(UINT32 op, UINT8 new_cwp);
bool execute_group2(UINT32 op);
void execute_group3(UINT32 op);
bool execute_bicc(UINT32 op);
+ bool execute_ticc(UINT32 op);
+ bool evaluate_condition(UINT32 op);
// address spaces
const address_space_config m_as8_config;
@@ -102,6 +108,9 @@ protected:
UINT8 m_asi;
// other internal states
+ UINT8 m_trap_priorities[256];
+ UINT8 m_queued_tt;
+ UINT8 m_queued_priority;
UINT32 m_nextnpc;
int m_icount;
diff --git a/src/devices/cpu/sparc/sparcdasm.cpp b/src/devices/cpu/sparc/sparcdasm.cpp
index 62bd2fa42a4..c1251ded4b0 100644
--- a/src/devices/cpu/sparc/sparcdasm.cpp
+++ b/src/devices/cpu/sparc/sparcdasm.cpp
@@ -285,7 +285,15 @@ offs_t sparc_dasm(char *buffer, offs_t pc, UINT32 op)
print(buffer, "andn %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]);
break;
case 6: print(buffer, "orn %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]); break;
- case 7: print(buffer, "xnor %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]); break;
+ case 7:
+ if (SIMM13 == 0)
+ if (RS1 == RD)
+ print(buffer, "not %s", regnames[RD]);
+ else
+ print(buffer, "not %s,%s", regnames[RS1], regnames[RD]);
+ else
+ print(buffer, "xnor %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]);
+ break;
case 8: print(buffer, "addx %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]); break;
case 10: print(buffer, "umul %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]); break; // SPARCv8
case 11: print(buffer, "smul %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]); break; // SPARCv8
@@ -398,7 +406,13 @@ offs_t sparc_dasm(char *buffer, offs_t pc, UINT32 op)
else
print(buffer, "jmpl %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]);
break;
- case 57: print(buffer, "rett %s,%d,%s", regnames[RS1], SIMM13, regnames[RD]); break;
+ case 57:
+ {
+ char address[256];
+ sparc_dasm_address(address, op);
+ print(buffer, "rett %s", address);
+ break;
+ }
case 58: print(buffer, "%s%s,%d,%s", trapnames[COND], regnames[RS1], IMM7, regnames[RD]); break;
case 59:
if (RD == 0)
diff --git a/src/devices/cpu/sparc/sparcdefs.h b/src/devices/cpu/sparc/sparcdefs.h
index a4ff4b94dc0..e3eda8310b2 100644
--- a/src/devices/cpu/sparc/sparcdefs.h
+++ b/src/devices/cpu/sparc/sparcdefs.h
@@ -95,6 +95,9 @@
#define IS_SUPERVISOR (m_psr & PSR_S_MASK)
#define IS_USER (!IS_SUPERVISOR)
+#define TRAPS_ENABLED (m_psr & PSR_ET_MASK)
+#define TRAPS_DISABLED (!TRAPS_ENABLED)
+
#define OP (op >> 30) // gangnam style
#define OP2 ((op >> 22) & 7)
#define OP3 ((op >> 19) & 63)
@@ -106,7 +109,7 @@
#define CONST22 (op & 0x3fffff)
#define SIMM13 (((INT32)(op << 19)) >> 19)
#define IMM7 (op & 0x7f)
-
+#define SIMM7 (((INT32)(op << 25)) >> 25)
#define OPIMM (op & 0x00002000)
#define USEIMM ((op >> 13) & 1)
@@ -128,4 +131,35 @@
#define PC m_pc
#define nPC m_npc
+enum sparc_trap_type
+{
+ sparc_reset = 0,
+ sparc_instruction_access_exception = 1,
+ sparc_illegal_instruction = 2,
+ sparc_privileged_instruction = 3,
+ sparc_floating_point_disabled = 4,
+ sparc_window_overflow = 5,
+ sparc_window_underflow = 6,
+ sparc_mem_address_not_aligned = 7,
+ sparc_floating_point_exception = 8,
+ sparc_data_access_exception = 9,
+ sparc_tag_overflow = 10,
+ sparc_int1 = 17,
+ sparc_int2 = 18,
+ sparc_int3 = 19,
+ sparc_int4 = 20,
+ sparc_int5 = 21,
+ sparc_int6 = 22,
+ sparc_int7 = 23,
+ sparc_int8 = 24,
+ sparc_int9 = 25,
+ sparc_int10 = 26,
+ sparc_int11 = 27,
+ sparc_int12 = 28,
+ sparc_int13 = 29,
+ sparc_int14 = 30,
+ sparc_int15 = 31,
+ sparc_trap_instruction = 128
+};
+
#endif // __MB86901_DEFS_H__ \ No newline at end of file