diff options
author | 2016-06-18 17:06:49 +0200 | |
---|---|---|
committer | 2016-06-18 17:06:49 +0200 | |
commit | caf907403afd76dc1d662df666d67273072d55dd (patch) | |
tree | 531261cce537d032612e3f4f648d10e6cef3dd01 | |
parent | 6d50e3cb360945da98531acc685e8193e309157f (diff) |
Add (untested) trap support to SPARC core
-rw-r--r-- | src/devices/cpu/sparc/mb86901.cpp | 303 | ||||
-rw-r--r-- | src/devices/cpu/sparc/sparc.h | 9 | ||||
-rw-r--r-- | src/devices/cpu/sparc/sparcdasm.cpp | 18 | ||||
-rw-r--r-- | src/devices/cpu/sparc/sparcdefs.h | 36 |
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 |