// license:BSD-3-Clause // copyright-holders:Ryan Holtz //================================================================ // // sparcv7v8.cpp - Emulation for the SPARCv7/v8 line of // processors. // // Notes: // - The CPU core implementation has been simplified // somewhat compared to the spec. In particular, bus // holding on read/write accesses is disabled, as there // is currently no use made of it, and it is unlikely to // ever be. // // To-Do: // - Test: SPARCv8 ops are untested // - Extended-precision FPU support // - Coprocessor support // //================================================================ #include "emu.h" #include "sparc.h" #include "sparcdefs.h" #include "debugger.h" #include "softfloat3/source/include/softfloat.h" DEFINE_DEVICE_TYPE(SPARCV7, sparcv7_device, "sparcv7", "Sun SPARC v7") const int sparcv7_device::NWINDOWS = 7; #if LOG_FCODES #include "ss1fcode.ipp" #endif #if SPARCV8 #include "sparcv8ops.ipp" #endif //------------------------------------------------- // sparcv7_device - constructor //------------------------------------------------- sparcv7_device::sparcv7_device(const machine_config &mconfig, const char *tag, device_t *owner, uint32_t clock) : cpu_device(mconfig, SPARCV7, tag, owner, clock) , m_mmu(*this, finder_base::DUMMY_TAG) { m_default_config = address_space_config("program", ENDIANNESS_BIG, 32, 32); } void sparcv7_device::device_start() { #if LOG_FCODES m_ss1_fcode_table.clear(); FILE* input = fopen("names.txt", "rb"); if (input != NULL) { fseek(input, 0, SEEK_END); size_t filesize = ftell(input); fseek(input, 0, SEEK_SET); uint8_t *buf = new uint8_t[filesize]; fread(buf, 1, filesize, input); fclose(input); size_t pos = 0; while (pos < filesize) { // eat newlines while (pos < filesize && (buf[pos] == 0x0d || buf[pos] == 0x0a)) pos++; if (pos >= filesize) break; // get opcode uint16_t opcode = 0; for (int shift = 12; shift >= 0 && pos < filesize; shift -= 4) { uint8_t digit = buf[pos]; if (digit >= 'a' && digit <= 'z') { digit &= ~0x20; } if (digit >= '0' && digit <= '9') { opcode |= (digit - 0x30) << shift; } else if (digit >= 'A' && digit <= 'F') { opcode |= ((digit - 0x41) + 10) << shift; } pos++; } if (pos >= filesize) break; // skip " : " pos += 3; if (pos >= filesize) break; // read description up to the first space std::string description; while (buf[pos] != ' ' && pos < filesize) { description += char(buf[pos]); pos++; } if (pos >= filesize) break; // skip everything else up to the trailing semicolon while (buf[pos] != ';' && pos < filesize) pos++; if (pos >= filesize) break; if (buf[pos] == ';') pos++; m_ss1_fcode_table[opcode] = description; } delete [] buf; } m_log_fcodes = false; #endif m_bp_reset_in = false; m_bp_fpu_present = true; m_bp_cp_present = false; m_pb_error = false; m_pb_block_ldst_byte = false; m_pb_block_ldst_word = false; m_bp_irl = 0; m_irq_state = 0; memset(m_dbgregs, 0, 24 * sizeof(uint32_t)); memset(m_illegal_instruction_asr, 0, 32 * sizeof(bool)); memset(m_privileged_asr, 1, 32 * sizeof(bool)); m_privileged_asr[0] = false; memset(m_alu_setcc, 0, 64 * sizeof(bool)); m_alu_setcc[OP3_ADDCC] = true; m_alu_setcc[OP3_ANDCC] = true; m_alu_setcc[OP3_ORCC] = true; m_alu_setcc[OP3_XORCC] = true; m_alu_setcc[OP3_SUBCC] = true; m_alu_setcc[OP3_ANDNCC] = true; m_alu_setcc[OP3_ORNCC] = true; m_alu_setcc[OP3_XNORCC] = true; m_alu_setcc[OP3_ADDXCC] = true; m_alu_setcc[OP3_SUBXCC] = true; m_alu_setcc[OP3_TADDCC] = true; m_alu_setcc[OP3_TSUBCC] = true; m_alu_setcc[OP3_TADDCCTV] = true; m_alu_setcc[OP3_TSUBCCTV] = true; m_alu_setcc[OP3_MULSCC] = true; #if SPARCV8 m_alu_setcc[OP3_UMULCC] = true; m_alu_setcc[OP3_SMULCC] = true; m_alu_setcc[OP3_UDIVCC] = true; m_alu_setcc[OP3_SDIVCC] = true; #endif memset(m_alu_op3_assigned, 0, 64 * sizeof(bool)); m_alu_op3_assigned[OP3_ADD] = true; m_alu_op3_assigned[OP3_AND] = true; m_alu_op3_assigned[OP3_OR] = true; m_alu_op3_assigned[OP3_XOR] = true; m_alu_op3_assigned[OP3_SUB] = true; m_alu_op3_assigned[OP3_ANDN] = true; m_alu_op3_assigned[OP3_ORN] = true; m_alu_op3_assigned[OP3_XNOR] = true; m_alu_op3_assigned[OP3_ADDX] = true; m_alu_op3_assigned[OP3_SUBX] = true; m_alu_op3_assigned[OP3_ADDCC] = true; m_alu_op3_assigned[OP3_ANDCC] = true; m_alu_op3_assigned[OP3_ORCC] = true; m_alu_op3_assigned[OP3_XORCC] = true; m_alu_op3_assigned[OP3_SUBCC] = true; m_alu_op3_assigned[OP3_ANDNCC] = true; m_alu_op3_assigned[OP3_ORNCC] = true; m_alu_op3_assigned[OP3_XNORCC] = true; m_alu_op3_assigned[OP3_ADDXCC] = true; m_alu_op3_assigned[OP3_SUBXCC] = true; m_alu_op3_assigned[OP3_TADDCC] = true; m_alu_op3_assigned[OP3_TSUBCC] = true; m_alu_op3_assigned[OP3_TADDCCTV] = true; m_alu_op3_assigned[OP3_TSUBCCTV] = true; m_alu_op3_assigned[OP3_MULSCC] = true; m_alu_op3_assigned[OP3_SLL] = true; m_alu_op3_assigned[OP3_SRL] = true; m_alu_op3_assigned[OP3_SRA] = true; m_alu_op3_assigned[OP3_RDASR] = true; m_alu_op3_assigned[OP3_RDPSR] = true; m_alu_op3_assigned[OP3_RDWIM] = true; m_alu_op3_assigned[OP3_RDTBR] = true; m_alu_op3_assigned[OP3_WRASR] = true; m_alu_op3_assigned[OP3_WRPSR] = true; m_alu_op3_assigned[OP3_WRWIM] = true; m_alu_op3_assigned[OP3_WRTBR] = true; m_alu_op3_assigned[OP3_FPOP1] = true; m_alu_op3_assigned[OP3_FPOP2] = true; m_alu_op3_assigned[OP3_CPOP1] = true; m_alu_op3_assigned[OP3_CPOP2] = true; m_alu_op3_assigned[OP3_JMPL] = true; m_alu_op3_assigned[OP3_RETT] = true; m_alu_op3_assigned[OP3_TICC] = true; m_alu_op3_assigned[OP3_IFLUSH] = true; m_alu_op3_assigned[OP3_SAVE] = true; m_alu_op3_assigned[OP3_RESTORE] = true; #if SPARCV8 m_alu_op3_assigned[OP3_UMUL] = true; m_alu_op3_assigned[OP3_SMUL] = true; m_alu_op3_assigned[OP3_UDIV] = true; m_alu_op3_assigned[OP3_SDIV] = true; m_alu_op3_assigned[OP3_UMULCC] = true; m_alu_op3_assigned[OP3_SMULCC] = true; m_alu_op3_assigned[OP3_UDIVCC] = true; m_alu_op3_assigned[OP3_SDIVCC] = true; #endif memset(m_ldst_op3_assigned, 0, 64 * sizeof(bool)); m_ldst_op3_assigned[OP3_LD] = true; m_ldst_op3_assigned[OP3_LDUB] = true; m_ldst_op3_assigned[OP3_LDUH] = true; m_ldst_op3_assigned[OP3_LDD] = true; m_ldst_op3_assigned[OP3_ST] = true; m_ldst_op3_assigned[OP3_STB] = true; m_ldst_op3_assigned[OP3_STH] = true; m_ldst_op3_assigned[OP3_STD] = true; m_ldst_op3_assigned[OP3_LDSB] = true; m_ldst_op3_assigned[OP3_LDSH] = true; m_ldst_op3_assigned[OP3_LDSTUB] = true; m_ldst_op3_assigned[OP3_LDA] = true; m_ldst_op3_assigned[OP3_LDUBA] = true; m_ldst_op3_assigned[OP3_LDUHA] = true; m_ldst_op3_assigned[OP3_LDDA] = true; m_ldst_op3_assigned[OP3_STA] = true; m_ldst_op3_assigned[OP3_STBA] = true; m_ldst_op3_assigned[OP3_STHA] = true; m_ldst_op3_assigned[OP3_STDA] = true; m_ldst_op3_assigned[OP3_LDSBA] = true; m_ldst_op3_assigned[OP3_LDSHA] = true; m_ldst_op3_assigned[OP3_LDSTUBA] = true; m_ldst_op3_assigned[OP3_LDFPR] = true; m_ldst_op3_assigned[OP3_LDFSR] = true; m_ldst_op3_assigned[OP3_LDDFPR] = true; m_ldst_op3_assigned[OP3_STFPR] = true; m_ldst_op3_assigned[OP3_STFSR] = true; m_ldst_op3_assigned[OP3_STDFQ] = true; m_ldst_op3_assigned[OP3_STDFPR] = true; #if SPARCV8 m_ldst_op3_assigned[OP3_SWAP] = true; m_ldst_op3_assigned[OP3_SWAPA] = true; m_ldst_op3_assigned[OP3_LDCPR] = true; m_ldst_op3_assigned[OP3_LDCSR] = true; m_ldst_op3_assigned[OP3_LDDCPR] = true; m_ldst_op3_assigned[OP3_STCPR] = true; m_ldst_op3_assigned[OP3_STCSR] = true; m_ldst_op3_assigned[OP3_STDCQ] = true; m_ldst_op3_assigned[OP3_STDCPR] = true; #endif // register our state for the debugger state_add(STATE_GENPC, "GENPC", m_pc).noshow(); state_add(STATE_GENPCBASE, "CURPC", m_pc).noshow(); state_add(STATE_GENFLAGS, "GENFLAGS", m_psr).callimport().callexport().formatstr("%6s").noshow(); state_add(SPARC_PC, "PC", m_pc).formatstr("%08X"); state_add(SPARC_NPC, "nPC", m_npc).formatstr("%08X"); state_add(SPARC_PSR, "PSR", m_psr).formatstr("%08X"); state_add(SPARC_WIM, "WIM", m_wim).formatstr("%08X"); state_add(SPARC_TBR, "TBR", m_tbr).formatstr("%08X"); state_add(SPARC_Y, "Y", m_y).formatstr("%08X"); state_add(SPARC_ANNUL, "ANNUL", m_no_annul).formatstr("%01u"); state_add(SPARC_ICC, "icc", m_icc).formatstr("%4s"); state_add(SPARC_CWP, "CWP", m_cwp).formatstr("%2d"); for (int i = 0; i < 8; i++) state_add(SPARC_G0 + i, util::string_format("g%d", i).c_str(), m_r[i]).formatstr("%08X"); for (int i = 0; i < 8; i++) state_add(SPARC_O0 + i, util::string_format("o%d", i).c_str(), m_dbgregs[i]).formatstr("%08X"); for (int i = 0; i < 8; i++) state_add(SPARC_L0 + i, util::string_format("l%d", i).c_str(), m_dbgregs[8+i]).formatstr("%08X"); for (int i = 0; i < 8; i++) state_add(SPARC_I0 + i, util::string_format("i%d", i).c_str(), m_dbgregs[16+i]).formatstr("%08X"); state_add(SPARC_EC, "EC", m_ec).formatstr("%1u"); state_add(SPARC_EF, "EF", m_ef).formatstr("%1u"); state_add(SPARC_ET, "ET", m_et).formatstr("%1u"); state_add(SPARC_PIL, "PIL", m_pil).formatstr("%2d"); state_add(SPARC_S, "S", m_s).formatstr("%1u"); state_add(SPARC_PS, "PS", m_ps).formatstr("%1u"); state_add(SPARC_FSR, "FSR", m_fsr).formatstr("%08X"); for (int i = 0; i < 32; i++) state_add(SPARC_F0 + i, util::string_format("f%d", i).c_str(), m_fpr[i]); for (int i = 0; i < 120; i++) state_add(SPARC_R0 + i, util::string_format("r%d", i).c_str(), m_r[i]).formatstr("%08X"); save_item(NAME(m_r)); save_item(NAME(m_fpr)); save_item(NAME(m_fsr)); save_item(NAME(m_ftt)); save_item(NAME(m_pc)); save_item(NAME(m_npc)); save_item(NAME(m_psr)); save_item(NAME(m_wim)); save_item(NAME(m_tbr)); save_item(NAME(m_y)); save_item(NAME(m_bp_reset_in)); save_item(NAME(m_bp_irl)); save_item(NAME(m_bp_fpu_present)); save_item(NAME(m_bp_cp_present)); save_item(NAME(m_pb_error)); save_item(NAME(m_pb_block_ldst_byte)); save_item(NAME(m_pb_block_ldst_word)); save_item(NAME(m_irq_state)); save_item(NAME(m_trap)); save_item(NAME(m_tt)); save_item(NAME(m_ticc_trap_type)); save_item(NAME(m_interrupt_level)); save_item(NAME(m_privileged_instruction)); save_item(NAME(m_illegal_instruction)); save_item(NAME(m_mem_address_not_aligned)); save_item(NAME(m_fp_disabled)); save_item(NAME(m_fp_exception)); save_item(NAME(m_cp_disabled)); save_item(NAME(m_cp_exception)); save_item(NAME(m_unimplemented_FLUSH)); save_item(NAME(m_r_register_access_error)); save_item(NAME(m_instruction_access_error)); save_item(NAME(m_instruction_access_exception)); save_item(NAME(m_data_access_error)); save_item(NAME(m_data_store_error)); save_item(NAME(m_data_access_exception)); save_item(NAME(m_division_by_zero)); save_item(NAME(m_trap_instruction)); save_item(NAME(m_window_underflow)); save_item(NAME(m_window_overflow)); save_item(NAME(m_tag_overflow)); save_item(NAME(m_reset_mode)); save_item(NAME(m_reset_trap)); save_item(NAME(m_execute_mode)); save_item(NAME(m_error_mode)); save_item(NAME(m_fpu_sequence_err)); save_item(NAME(m_cp_sequence_err)); save_item(NAME(m_impl)); save_item(NAME(m_ver)); save_item(NAME(m_icc)); save_item(NAME(m_ec)); save_item(NAME(m_ef)); save_item(NAME(m_pil)); save_item(NAME(m_s)); save_item(NAME(m_ps)); save_item(NAME(m_et)); save_item(NAME(m_cwp)); save_item(NAME(m_alu_op3_assigned)); save_item(NAME(m_ldst_op3_assigned)); save_item(NAME(m_alu_setcc)); save_item(NAME(m_privileged_asr)); save_item(NAME(m_illegal_instruction_asr)); save_item(NAME(m_mae)); save_item(NAME(m_no_annul)); save_item(NAME(m_hold_bus)); save_item(NAME(m_icount)); save_item(NAME(m_stashed_icount)); save_item(NAME(m_insn_space)); save_item(NAME(m_data_space)); #if LOG_FCODES save_item(NAME(m_ss1_next_pc)); save_item(NAME(m_ss1_next_opcode)); save_item(NAME(m_ss1_next_handler_base)); save_item(NAME(m_ss1_next_entry_point)); save_item(NAME(m_ss1_next_stack)); save_item(NAME(m_log_fcodes)); #endif // set our instruction counter set_icountptr(m_icount); } void sparcv7_device::device_stop() { } void sparcv7_device::device_resolve_objects() { m_mmu->set_host(this); } void sparcv7_device::device_reset() { m_trap = 0; m_tt = 0; m_ticc_trap_type = 0; m_privileged_instruction = 0; m_illegal_instruction = 0; m_mem_address_not_aligned = 0; m_fp_disabled = 0; m_cp_disabled = 0; m_instruction_access_exception = 0; m_trap_instruction = 0; m_window_underflow = 0; m_window_overflow = 0; m_tag_overflow = 0; m_reset_mode = 1; m_reset_trap = 0; m_execute_mode = 0; m_error_mode = 0; m_fpu_sequence_err = 0; m_cp_sequence_err = 0; m_bp_irl = 0; m_irq_state = 0; m_stashed_icount = -1; MAE = false; HOLD_BUS = false; m_no_annul = true; PC = 0; nPC = 4; memset(m_r, 0, sizeof(uint32_t) * 120); memset(m_fpr, 0, sizeof(uint32_t) * 32); WIM = 0; TBR = 0; Y = 0; PSR = PSR_S_MASK | PSR_PS_MASK; m_s = true; m_data_space = 11; for (int i = 0; i < 8; i++) { m_regs[i] = m_r + i; } update_gpr_pointers(); #if LOG_FCODES m_ss1_next_pc = ~0; m_ss1_next_opcode = ~0; m_ss1_next_handler_base = ~0; m_ss1_next_entry_point = ~0; m_ss1_next_stack = ~0; #endif } //------------------------------------------------- // device_post_load - update register pointers // after loading a savestate //------------------------------------------------- void sparcv7_device::device_post_load() { update_gpr_pointers(); } //------------------------------------------------- // memory_space_config - return the configuration // of the specified address space, or nullptr if // the space doesn't exist //------------------------------------------------- device_memory_interface::space_config_vector sparcv7_device::memory_space_config() const { space_config_vector config_vector; config_vector.push_back(std::make_pair(AS_PROGRAM, &m_default_config)); return config_vector; } //------------------------------------------------- // read_sized_word - read a value from a given // address space and address, shifting the data // that is read into the appropriate location of // a 32-bit word in a big-endian system. //------------------------------------------------- uint32_t sparcv7_device::read_sized_word(const uint8_t asi, const uint32_t address, const uint32_t mem_mask) { assert(asi < 0x20); // We do not currently support ASIs outside the range used by actual Sun machines. return m_mmu->read_asi(asi, address >> 2, mem_mask); } //------------------------------------------------- // write_sized_word - write a value to a given // address space and address, shifting the data // that is written into the least significant // bits as appropriate in order to write the // value to a memory system with separate data // size handlers //------------------------------------------------- void sparcv7_device::write_sized_word(const uint8_t asi, const uint32_t address, const uint32_t data, const uint32_t mem_mask) { assert(asi < 0x20); // We do not currently support ASIs outside the range used by actual Sun machines. m_mmu->write_asi(asi, address >> 2, data, mem_mask); } //------------------------------------------------- // state_string_export - export state as a string // for the debugger //------------------------------------------------- void sparcv7_device::state_string_export(const device_state_entry &entry, std::string &str) const { switch (entry.index()) { case STATE_GENFLAGS: case SPARC_ANNUL: str = string_format("%01u", m_no_annul ? 0 : 1); break; case SPARC_CWP: str = string_format("%2u", PSR & PSR_CWP_MASK); break; case SPARC_EC: str = string_format("%01u", PSR & PSR_EC_MASK ? 1 : 0); break; case SPARC_EF: str = string_format("%01u", PSR & PSR_EF_MASK ? 1 : 0); break; case SPARC_ET: str = string_format("%01u", PSR & PSR_ET_MASK ? 1 : 0); break; case SPARC_PS: str = string_format("%01u", PSR & PSR_PS_MASK ? 1 : 0); break; case SPARC_S: str = string_format("%01u", PSR & PSR_S_MASK ? 1 : 0); break; case SPARC_PIL: str = string_format("%02u", (PSR & PSR_PIL_MASK) >> PSR_PIL_SHIFT); break; case SPARC_ICC: str = string_format("%c%c%c%c", ICC_N_SET ? 'n' : ' ', ICC_Z_SET ? 'z' : ' ', ICC_V_SET ? 'v' : ' ', ICC_C_SET ? 'c' : ' '); break; case SPARC_O0: case SPARC_O1: case SPARC_O2: case SPARC_O3: case SPARC_O4: case SPARC_O5: case SPARC_O6: case SPARC_O7: str = string_format("%08X", m_dbgregs[entry.index() - SPARC_O0]); break; case SPARC_L0: case SPARC_L1: case SPARC_L2: case SPARC_L3: case SPARC_L4: case SPARC_L5: case SPARC_L6: case SPARC_L7: str = string_format("%08X", m_dbgregs[8 + (entry.index() - SPARC_L0)]); break; case SPARC_I0: case SPARC_I1: case SPARC_I2: case SPARC_I3: case SPARC_I4: case SPARC_I5: case SPARC_I6: case SPARC_I7: str = string_format("%08X", m_dbgregs[16 + (entry.index() - SPARC_I0)]); break; } } //------------------------------------------------- // disassemble - call the disassembly // helper function //------------------------------------------------- std::unique_ptr sparcv7_device::create_disassembler() { auto dasm = std::make_unique(static_cast(this), 7); m_asi_desc_adder(dasm.get()); return std::move(dasm); } //************************************************************************** // CORE EXECUTION LOOP //************************************************************************** //------------------------------------------------- // execute_min_cycles - return minimum number of // cycles it takes for one instruction to execute //------------------------------------------------- uint32_t sparcv7_device::execute_min_cycles() const noexcept { return 1; } //------------------------------------------------- // execute_max_cycles - return maximum number of // cycles it takes for one instruction to execute //------------------------------------------------- uint32_t sparcv7_device::execute_max_cycles() const noexcept { return 4; } //------------------------------------------------- // execute_input_lines - return the number of // input/interrupt lines //------------------------------------------------- uint32_t sparcv7_device::execute_input_lines() const noexcept { return 16; } //------------------------------------------------- // execute_set_input - set the state of an input // line during execution //------------------------------------------------- void sparcv7_device::execute_set_input(int inputnum, int state) { switch (inputnum) { case SPARC_IRQ1: case SPARC_IRQ2: case SPARC_IRQ3: case SPARC_IRQ4: case SPARC_IRQ5: case SPARC_IRQ6: case SPARC_IRQ7: case SPARC_IRQ8: case SPARC_IRQ9: case SPARC_IRQ10: case SPARC_IRQ11: case SPARC_IRQ12: case SPARC_IRQ13: case SPARC_IRQ14: case SPARC_NMI: { int index = (inputnum - SPARC_IRQ1) + 1; if (state) { m_irq_state |= 1 << index; } else { m_irq_state &= ~(1 << index); } for(index = 15; index > 0; index--) { if (m_irq_state & (1 << index)) { break; } } m_bp_irl = index; break; } case SPARC_MAE: m_mae = (state != 0) ? 1 : 0; break; case SPARC_RESET: m_bp_reset_in = (state != 0) ? 1 : 0; break; } } //------------------------------------------------- // execute_add - execute an add-type opcode //------------------------------------------------- void sparcv7_device::execute_add(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 173, "Appendix C - ISP Descriptions - Add Instructions" (SPARCv8.pdf, pg. 170) operand2 := if (i = 0) then r[rs2] else sign_extend(simm13); if (ADD or ADDcc) then result <- r[rs1] + operand2; else if (ADDX or ADDXcc) then result <= r[rs1] + operand2 + C; next; if (rd != 0) then r[rd] <- result; if (ADDcc or ADDXcc) then ( N <- result<31>; Z <- if (result = 0) then 1 else 0; V <- (r[rs1]<31> and operand2<31> and (not result<31>)) or ((not r[rs1]<31>) and (not operand2<31>) and result<31>); C <- (r[rs1]<31> and operand2<31>) or ((not result<31>) and (r[rs1]<31> or operand2<31>)) ); */ uint32_t rs1 = RS1REG; uint32_t operand2 = USEIMM ? SIMM13 : RS2REG; uint32_t result = 0; if (ADD || ADDCC) result = rs1 + operand2; else if (ADDX || ADDXCC) result = rs1 + operand2 + ICC_C; if (RDBITS) RDREG = result; if (ADDCC || ADDXCC) { CLEAR_ICC; PSR |= (BIT31(result)) ? PSR_N_MASK : 0; PSR |= (result == 0) ? PSR_Z_MASK : 0; PSR |= ((BIT31(rs1) && BIT31(operand2) && !BIT31(result)) || (!BIT31(rs1) && !BIT31(operand2) && BIT31(result))) ? PSR_V_MASK : 0; PSR |= ((BIT31(rs1) && BIT31(operand2)) || (!BIT31(result) && (BIT31(rs1) || BIT31(operand2)))) ? PSR_C_MASK : 0; } PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_taddcc - execute a tagged add-type // opcode //------------------------------------------------- void sparcv7_device::execute_taddcc(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 173, "Appendix C - ISP Descriptions - Tagged Add Instructions" (SPARCv8.pdf, pg. 170) operand2 := if (i = 0) then r[rs2] else sign_extend(simm13); result <- r[rs1] + operand2; next; temp_V <- (r[rs1]<31> and operand2<31> and (not result<31>)) or ((not r[rs1]<31>) and (not operand2<31>) and result<31>) or (r[rs1]<1:0> != 0 or operand2<1:0> != 0); next; if (TADDccTV and (temp_V = 1)) then ( trap <- 1; tag_overflow <- 1 ) else ( N <- result<31>; Z <- if (result = 0) then 1 else 0; V <- temp_V; C <- (r[rs1]<31> and operand2<31>) or ((not result<31>) and (r[rs1]<31> or operand2<31>)); if (rd != 0) then r[rd] <- result; ); */ uint32_t rs1 = RS1REG; uint32_t operand2 = USEIMM ? SIMM13 : RS2REG; uint32_t result = rs1 + operand2; bool temp_v = (BIT31(rs1) && BIT31(operand2) && !BIT31(result)) || (!BIT31(rs1) && !BIT31(operand2) && BIT31(result)) || ((rs1 & 3) != 0 || (operand2 & 3) != 0) ? true : false; if (TADDCCTV && temp_v) { m_trap = 1; m_tag_overflow = true; m_stashed_icount = m_icount; m_icount = 0; return; } CLEAR_ICC; PSR |= (BIT31(result)) ? PSR_N_MASK : 0; PSR |= (result == 0) ? PSR_Z_MASK : 0; PSR |= temp_v ? PSR_V_MASK : 0; PSR |= ((BIT31(rs1) && BIT31(operand2)) || (!BIT31(result) && (BIT31(rs1) || BIT31(operand2)))) ? PSR_C_MASK : 0; if (RDBITS) RDREG = result; PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_sub - execute a subtraction-type // opcode //------------------------------------------------- void sparcv7_device::execute_sub(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 174, "Appendix C - ISP Descriptions - Subtract Instructions" (SPARCv8.pdf, pg. 171) operand2 := if (i = 0) then r[rs2] else sign_extend(simm13); if (SUB or SUBcc) then result <- r[rs1] - operand2; else if (SUBX or SUBXcc) then result <= r[rs1] - operand2 - C; next; if (rd != 0) then r[rd] <- result; if (SUBcc or SUBXcc) then ( N <- result<31>; Z <- if (result = 0) then 1 else 0; V <- (r[rs1]<31> and (not operand2<31>) and (not result<31>)) or ((not r[rs1]<31>) and operand2<31> and result<31>); C <- ((not r[rs1]<31>) and operand2<31>) or (result<31> and ((not r[rs1]<31>) or operand2<31>)) ); */ uint32_t rs1 = RS1REG; uint32_t operand2 = USEIMM ? SIMM13 : RS2REG; uint32_t result = 0; if (SUB || SUBCC) result = rs1 - operand2; else if (SUBX || SUBXCC) result = rs1 - operand2 - ICC_C; if (RDBITS) RDREG = result; if (SUBCC || SUBXCC) { CLEAR_ICC; PSR |= (BIT31(result)) ? PSR_N_MASK : 0; PSR |= (result == 0) ? PSR_Z_MASK : 0; PSR |= ((BIT31(rs1) && !BIT31(operand2) && !BIT31(result)) || (!BIT31(rs1) && BIT31(operand2) && BIT31(result))) ? PSR_V_MASK : 0; PSR |= ((!BIT31(rs1) && BIT31(operand2)) || (BIT31(result) && (!BIT31(rs1) || BIT31(operand2)))) ? PSR_C_MASK : 0; } PC = nPC; nPC = nPC + 4; } //-------------------------------------------------- // execute_tsubcc - execute a tagged subtract-type // opcode //-------------------------------------------------- void sparcv7_device::execute_tsubcc(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 174, "Appendix C - ISP Descriptions - Tagged Subtract Instructions" (SPARCv8.pdf, pg. 171) operand2 := if (i = 0) then r[rs2] else sign_extend(simm13); result <- r[rs1] - operand2; next; temp_V <- (r[rs1]<31> and (not operand2<31>) and (not result<31>)) or ((not r[rs1]<31>) and operand2<31> and result<31>) or (r[rs1]<1:0> != 0 or operand2<1:0> != 0); next; if (TSUBccTV and (temp_V = 1)) then ( trap <- 1; tag_overflow <- 1 ) else ( N <- result<31>; Z <- if (result = 0) then 1 else 0; V <- temp_V; C <- ((not r[rs1]<31>) and operand2<31>) or (result<31> and ((not r[rs1]<31>) or operand2<31>)); if (rd != 0) then r[rd] <- result; ); */ uint32_t rs1 = RS1REG; uint32_t operand2 = USEIMM ? SIMM13 : RS2REG; uint32_t result = rs1 - operand2; bool temp_v = (BIT31(rs1) && !BIT31(operand2) && !BIT31(result)) || (!BIT31(rs1) && BIT31(operand2) && BIT31(result)) || ((rs1 & 3) != 0 || (operand2 & 3) != 0) ? true : false; if (TSUBCCTV && temp_v) { m_trap = 1; m_tag_overflow = 1; m_stashed_icount = m_icount; m_icount = 0; return; } CLEAR_ICC; PSR |= (BIT31(result)) ? PSR_N_MASK : 0; PSR |= (result == 0) ? PSR_Z_MASK : 0; PSR |= temp_v ? PSR_V_MASK : 0; PSR |= ((!BIT31(rs1) && BIT31(operand2)) || (BIT31(result) && (!BIT31(rs1) || BIT31(operand2)))) ? PSR_C_MASK : 0; if (RDBITS) RDREG = result; PC = nPC; nPC = nPC + 4; } /* The SPARC Instruction Manual: Version 8, page 172, "Appendix C - ISP Descriptions - Logical Instructions" (SPARCv8.pdf, pg. 169) operand2 := if (i = 0) then r[rs2] else sign_extend(simm13); if ( AND or ANDcc) then result <- r[rs1] and operand2 if (ANDN or ANDNcc) then result <- r[rs1] and not operand2 if ( OR or ORcc) then result <- r[rs1] or operand2 if ( ORN or ORNcc) then result <- r[rs1] or not operand2 if ( XOR or XORcc) then result <- r[rs1] xor operand2 if (XNOR or XNORcc) then result <- r[rs1] xor not operand2; next; if (rd != 0) then r[rd] <- result; if (ANDcccc or ANDNcc or ORcc or ORNcc or XORcc or XNORcc) then ( N <- result<31>; Z <- if (result = 0) then 1 else 0; V <- 0 C <- 0 ); */ template void sparcv7_device::execute_and(const uint32_t op) { const uint32_t result = RS1REG & (USEIMM ? SIMM13 : RS2REG); if (RDBITS) RDREG = result; if (SETCC) { CLEAR_ICC; if (result & 0x80000000) PSR |= PSR_N_MASK; else if (!result) PSR |= PSR_Z_MASK; } PC = nPC; nPC = nPC + 4; } template void sparcv7_device::execute_or(const uint32_t op) { const uint32_t result = RS1REG | (USEIMM ? SIMM13 : RS2REG); if (RDBITS) RDREG = result; if (SETCC) { CLEAR_ICC; if (result & 0x80000000) PSR |= PSR_N_MASK; else if (!result) PSR |= PSR_Z_MASK; } PC = nPC; nPC = nPC + 4; } template void sparcv7_device::execute_xor(const uint32_t op) { const uint32_t result = RS1REG ^ (USEIMM ? SIMM13 : RS2REG); if (RDBITS) RDREG = result; if (SETCC) { CLEAR_ICC; if (result & 0x80000000) PSR |= PSR_N_MASK; else if (!result) PSR |= PSR_Z_MASK; } PC = nPC; nPC = nPC + 4; } template void sparcv7_device::execute_andn(const uint32_t op) { const uint32_t result = RS1REG & ~(USEIMM ? SIMM13 : RS2REG); if (RDBITS) RDREG = result; if (SETCC) { CLEAR_ICC; if (result & 0x80000000) PSR |= PSR_N_MASK; else if (!result) PSR |= PSR_Z_MASK; } PC = nPC; nPC = nPC + 4; } template void sparcv7_device::execute_orn(const uint32_t op) { const uint32_t result = RS1REG | ~(USEIMM ? SIMM13 : RS2REG); if (RDBITS) RDREG = result; if (SETCC) { CLEAR_ICC; if (result & 0x80000000) PSR |= PSR_N_MASK; else if (!result) PSR |= PSR_Z_MASK; } PC = nPC; nPC = nPC + 4; } template void sparcv7_device::execute_xnor(const uint32_t op) { const uint32_t result = RS1REG ^ ~(USEIMM ? SIMM13 : RS2REG); if (RDBITS) RDREG = result; if (SETCC) { CLEAR_ICC; if (result & 0x80000000) PSR |= PSR_N_MASK; else if (!result) PSR |= PSR_Z_MASK; } PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_shift - execute a shift-type opcode, // sll/srl/sra //------------------------------------------------- void sparcv7_device::execute_shift(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 172, "Appendix C - ISP Descriptions - Shift Instructions" (SPARCv8.pdf, pg. 169) shift_count := if (i = 0) then r[rs2]<4:0> else shcnt; if (SLL and (rd != 0) ) then r[rd] <- shift_left_logical(r[rs1], shift_count) else if (SRL and (rd != 0) ) then r[rd] <- shift_right_logical(r[rs1], shift_count) else if (SRA and (rd != 0) ) then r[rd] <- shift_right_arithmetic(r[rs1], shift_count) */ uint32_t shift_count = USEIMM ? (SIMM13 & 31) : (RS2REG & 31); if (RDBITS) { if (SLL) RDREG = RS1REG << shift_count; else if (SRL) RDREG = uint32_t(RS1REG) >> shift_count; else if (SRA) RDREG = int32_t(RS1REG) >> shift_count; } PC = nPC; nPC = nPC + 4; } //-------------------------------------------------- // execute_mulscc - execute a multiply step opcode //-------------------------------------------------- void sparcv7_device::execute_mulscc(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 175, "Appendix C - ISP Descriptions - Multiply Step Instruction" (SPARCv8.pdf, pg. 172) operand1 := (N xor V) [] (r[rs1]<31:1>); operand2 := ( if (Y<0> = 0) then 0 else if (i = 0) then r[rs2] else sign_extend(simm13) ); result <- operand1 + operand2; Y <- r[rs1]<0> [] Y<31:1>; next; if (rd != 0) then ( r[rd] <- result; ) N <- result<31>; Z <- if (result = 0) then 1 else 0; V <- (operand1<31> and operand2<31> and (not result<31>)) or ((not operand1<31>) and (not operand2<31>) and result<31>); C <- (operand1<31> and operand2<31>) or ((not result<31>) and (operand1<31> or operand2<31>)) */ uint32_t operand1 = ((ICC_N != ICC_V) ? 0x80000000 : 0) | (RS1REG >> 1); uint32_t operand2 = (Y & 1) ? (USEIMM ? SIMM13 : RS2REG) : 0; uint32_t result = operand1 + operand2; Y = ((RS1REG & 1) ? 0x80000000 : 0) | (Y >> 1); if (RDBITS) RDREG = result; CLEAR_ICC; PSR |= (BIT31(result)) ? PSR_N_MASK : 0; PSR |= (result == 0) ? PSR_Z_MASK : 0; PSR |= ((BIT31(operand1) && BIT31(operand2) && !BIT31(result)) || (!BIT31(operand1) && !BIT31(operand2) && BIT31(result))) ? PSR_V_MASK : 0; PSR |= ((BIT31(operand1) && BIT31(operand2)) || (!BIT31(result) && (BIT31(operand1) || BIT31(operand2)))) ? PSR_C_MASK : 0; PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_rdsr - execute a status register read // opcode //------------------------------------------------- void sparcv7_device::execute_rdsr(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 182, "Appendix C - ISP Descriptions - Read State Register Instructions" (SPARCv8.pdf, pg. 179) if ((RDPSR or RDWIM or RDBTR or (RDASR and (privileged_ASR(rs1) = 1))) and (S = 0)) then ( trap <- 1; privileged_instruction <- 1; else if (illegal_instruction_ASR(rs1) = 1) then ( trap <- 1; illegal_instruction <- 1 else if (rd != 0) then ( if (RDY) then r[rd] <- Y else if (RDASR) then r[rd] <- ASR[rs1] else if (RDPSR) then r[rd] <- PSR else if (RDWIM) then r[rd] <- WIM else if (RDTBR) then r[rd] <- TBR; ); */ if (((WRPSR || WRWIM || WRTBR) || (WRASR && m_privileged_asr[RS1])) && IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (m_illegal_instruction_asr[RS1]) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if (RDASR) { if (RS1 == 0) { RDREG = Y; } } else if (RDPSR) { RDREG = PSR; } else if (RDWIM) RDREG = WIM; else if (RDTBR) RDREG = TBR; } PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_wrsr - execute a status register write // opcode //------------------------------------------------- void sparcv7_device::execute_wrsr(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 183, "Appendix C - ISP Descriptions - Write State Register Instructions" (SPARCv8.pdf, pg. 180) operand2 := if (i = 0) then r[rs2] else sign_extend(simm13); result := r[rs1] xor operand2; if (WRY) then ( Y'''' <- result ) else if (WRASR) then ( if ( (privileged_ASR(rd) = 1) and (S = 0) ) then ( trap <- 1; privileged_instruction <- 1 ) else if (illegal_instruction_ASR(rd) = 1) then ( trap <- 1; illegal_instruction <- 1 ) else ( ASR[rd]'''' <- result ) ) else if (WRPSR) then ( if (S = 0) then ( trap <- 1; privileged_instruction <- 1 ) else if (result<4:0> >= NWINDOWS) then ( trap <- 1; illegal_instruction <- 1 ) else ( PSR'''' <- result ) ) else if (WRWIM) then ( if (S = 0) then ( trap <- 1; privileged_instruction <- 1 ) else ( WIM'''' <- result ) ) else if (WRBTR) then ( if (S = 0) then ( trap <- 1; privileged_instruction <- 1 ) else ( WIM'''' <- result ) ); */ uint32_t operand2 = USEIMM ? SIMM13 : RS2REG; uint32_t result = RS1REG ^ operand2; if (WRASR && RD == 0) { Y = result; PC = nPC; nPC = nPC + 4; } else if (WRASR) { if (m_privileged_asr[RD] && IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (m_illegal_instruction_asr[RD]) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else { // SPARCv8 PC = nPC; nPC = nPC + 4; } } else if (WRPSR) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if ((result & 31) >= NWINDOWS) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } PSR = result &~ PSR_ZERO_MASK; update_gpr_pointers(); m_et = PSR & PSR_ET_MASK; m_pil = (PSR & PSR_PIL_MASK) >> PSR_PIL_SHIFT; m_s = PSR & PSR_S_MASK; if (m_s) { m_data_space = 11; } else { m_data_space = 10; } PC = nPC; nPC = nPC + 4; } else if (WRWIM) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } WIM = result & 0x7f; PC = nPC; nPC = nPC + 4; } else if (WRTBR) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } TBR = result & 0xfffff000; PC = nPC; nPC = nPC + 4; } } //------------------------------------------------- // execute_rett - execute a return-from-trap // opcode //------------------------------------------------- void sparcv7_device::execute_rett(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 181, "Appendix C - ISP Descriptions - Return from Trap Instructions" (SPARCv8.pdf, pg. 178) new_cwp <- (CWP + 1) modulo NWINDOWS; address <- r[rs1] + (if (i = 0) then r[rs2] else sign_extend(simm13)); next; if (ET = 1) then ( trap <- 1; if (S = 0) then privileged_instruction <- 1 else if (S != 0) then illegal_instruction <- 1 ) else if (S = 0) then ( trap <- 1; privileged_instruction <- 1 tt <- 00000011; { trap type for privileged_instruction } execute_mode <- 0; error_mode = 1 ) else if ((WIM and (1 << new_cwp)) != 0) then ( trap <- 1; window_underflow <- 1; tt <- 00000110; { trap type for window_underflow } execute_mode = 0; error_mode = 1 ) else if (address<1:0> != 0) then ( trap = 1; mem_address_not_aligned = 1; tt = 7; { trap type for mem_address_not_aligned } execute_mode = 0; error_mode = 1 ) else ( ET <- 1; PC <- nPC; nPC <- address; CWP <- new_cwp; S <- PS ) */ uint8_t new_cwp = ((PSR & PSR_CWP_MASK) + 1) % NWINDOWS; uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (m_et) { m_trap = 1; if (IS_USER) { m_privileged_instruction = 1; } else { m_illegal_instruction = 1; } m_stashed_icount = m_icount; m_icount = 0; return; } else if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_tt = 3; m_execute_mode = 0; m_error_mode = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if ((WIM & (1 << new_cwp)) != 0) { m_trap = 1; m_window_underflow = 1; m_tt = 6; m_execute_mode = 0; m_error_mode = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_tt = 7; m_execute_mode = 0; m_error_mode = 1; m_stashed_icount = m_icount; m_icount = 0; return; } PSR |= PSR_ET_MASK; m_et = true; PC = nPC; nPC = address; PSR &= ~PSR_CWP_MASK; PSR |= new_cwp; if (PSR & PSR_PS_MASK) { PSR |= PSR_S_MASK; m_s = true; m_data_space = 11; } else { PSR &= ~PSR_S_MASK; m_s = false; m_data_space = 10; } update_gpr_pointers(); } //------------------------------------------------- // execute_saverestore - execute a save or restore // opcode //------------------------------------------------- void sparcv7_device::execute_saverestore(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 177, "Appendix C - ISP Descriptions - SAVE and RESTORE Instructions" (SPARCv8.pdf, pg. 174) operand2 := if (i = 0) then r[rs2] else sign_extend(simm13); if (SAVE) then ( new_cwp <- (CWP - 1) modulo NWINDOWS; next; if ((WIM and (1 << new_cwp)) != 0) then ( trap <- 1; window_overflow <- 1 ) else ( result <- r[rs1] + operand2; { operands from old window } CWP <- new_cwp ) ) else if (RESTORE) then ( new_cwp <- (CWP + 1) modulo NWINDOWS; next; if ((WIM and (1 << new_cwp)) != 0) then ( trap <- 1; window_overflow <- 1 ) else ( result <- r[rs1] + operand2; { operands from old window } CWP <- new_cwp ) ); next; if ((trap = 0) and (rd != 0)) then r[rd] <- result { destination in new window } */ uint32_t rs1 = RS1REG; uint32_t operand2 = USEIMM ? SIMM13 : RS2REG; uint32_t result = 0; if (SAVE) { uint8_t new_cwp = (((PSR & PSR_CWP_MASK) + NWINDOWS) - 1) % NWINDOWS; if ((WIM & (1 << new_cwp)) != 0) { m_trap = 1; m_window_overflow = 1; m_stashed_icount = m_icount; m_icount = 0; return; } result = rs1 + operand2; PSR &= ~PSR_CWP_MASK; PSR |= new_cwp; } else if (RESTORE) { uint8_t new_cwp = ((PSR & PSR_CWP_MASK) + 1) % NWINDOWS; if ((WIM & (1 << new_cwp)) != 0) { m_trap = 1; m_window_underflow = 1; m_stashed_icount = m_icount; m_icount = 0; return; } result = rs1 + operand2; PSR &= ~PSR_CWP_MASK; PSR |= new_cwp; } update_gpr_pointers(); if (RDBITS) RDREG = result; PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_jmpl - execute a jump and link opcode //------------------------------------------------- void sparcv7_device::execute_jmpl(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 180, "Appendix C - ISP Descriptions - SAVE and RESTORE Instructions" (SPARCv8.pdf, pg. 177) jump_address <- r[rs1] + (if (i = 0) then r[rs2] else sign_extend(simm13)); next; if (jump_address<1:0> != 0) then ( trap <- 1; mem_address_not_aligned <- 1 ) else ( if (rd != 0) then r[rd] <- PC; PC <- nPC; nPC <- jump_address ) */ uint32_t jump_address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (jump_address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; } else { if (RDBITS) RDREG = PC; PC = nPC; nPC = jump_address; } } execute_extra_group2 //------------------------------------------------- // execute_group2 - execute an opcode in group 2, // mostly ALU ops //------------------------------------------------- inline void sparcv7_device::execute_group2(uint32_t op) { switch (OP3) { case OP3_ADD: case OP3_ADDX: case OP3_ADDCC: case OP3_ADDXCC: execute_add(op); break; case OP3_SUB: case OP3_SUBX: case OP3_SUBCC: case OP3_SUBXCC: execute_sub(op); break; case OP3_TADDCC: case OP3_TADDCCTV: execute_taddcc(op); break; case OP3_TSUBCC: case OP3_TSUBCCTV: execute_tsubcc(op); break; case OP3_AND: execute_and(op); break; case OP3_OR: execute_or(op); break; case OP3_XOR: execute_xor(op); break; case OP3_ANDN: execute_andn(op); break; case OP3_ORN: execute_orn(op); break; case OP3_XNOR: execute_xnor(op); break; case OP3_ANDCC: execute_and(op); break; case OP3_ORCC: execute_or(op); break; case OP3_XORCC: execute_xor(op); break; case OP3_ANDNCC: execute_andn(op); break; case OP3_ORNCC: execute_orn(op); break; case OP3_XNORCC: execute_xnor(op); break; case OP3_MULSCC: execute_mulscc(op); break; case OP3_SLL: case OP3_SRL: case OP3_SRA: execute_shift(op); break; case OP3_RDASR: case OP3_RDPSR: case OP3_RDWIM: case OP3_RDTBR: execute_rdsr(op); break; case OP3_WRASR: case OP3_WRPSR: case OP3_WRWIM: case OP3_WRTBR: execute_wrsr(op); break; case OP3_FPOP1: case OP3_FPOP2: if (!(PSR & PSR_EF_MASK) || !m_bp_fpu_present) { m_trap = 1; m_fp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; } complete_fp_execution(op); return; case OP3_JMPL: execute_jmpl(op); break; case OP3_RETT: execute_rett(op); break; case OP3_TICC: execute_ticc(op); break; case OP3_IFLUSH: // Ignored PC = nPC; nPC = nPC + 4; break; case OP3_SAVE: case OP3_RESTORE: execute_saverestore(op); break; #if SPARCV8 case OP3_UMUL: case OP3_SMUL: case OP3_UMULCC: case OP3_SMULCC: execute_mul(op); break; case OP3_UDIV: case OP3_SDIV: case OP3_UDIVCC: case OP3_SDIVCC: execute_div(op); break; case OP3_CPOP1: case OP3_CPOP2: logerror("fpop @ %08x: %08x\n", PC, op); m_trap = 1; m_cp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; #endif default: if (!execute_extra_group2(op)) { logerror("illegal instruction at %08x: %08x\n", PC, op); m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; } break; } } //------------------------------------------------- // update_gpr_pointers - cache pointers to // the registers in our current window //------------------------------------------------- void sparcv7_device::update_gpr_pointers() { int cwp = PSR & PSR_CWP_MASK; for (int i = 0; i < 8; i++) { m_regs[ 8 + i] = &m_r[8 + (( 0 + cwp * 16 + i) % (NWINDOWS * 16))]; m_regs[16 + i] = &m_r[8 + (( 8 + cwp * 16 + i) % (NWINDOWS * 16))]; m_regs[24 + i] = &m_r[8 + ((16 + cwp * 16 + i) % (NWINDOWS * 16))]; } } //------------------------------------------------- // execute_store - execute a store-type opcode //------------------------------------------------- void sparcv7_device::execute_store(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 165, "Appendix C - ISP Descriptions - Store Instructions" (SPARCv8.pdf, pg. 162) if ( (S = 0) and (STDA or STA or STHA or STBA or STDFQ or STDCQ) ) then ( trap <- 1; privileged_instruction <- 1 ) else if ((i = 1) and (STDA or STA or STHA or STBA)) then ( trap <- 1; illegal_instruction <- 1 ); next; if (trap = 0) then ( if (STD or ST or STH or STB or STF or STDF or STFSR or STDFQ or STCSR or STC or STDC or STDCQ) then ( address <- r[rs1] + (if (i = 0) then r[rs2] else sign_extend(simm13)); addr_space <- (if (S = 0) then 10 else 11) ) else if (STDA or STA or STHA or STBA) then ( address <- r[rs1] + r[rs2]; addr_space <- asi ); if ((STF or STDF or STFSR or STDFQ) and ((EF = 0) or (bp_FPU_present = 0)) ) then ( trap <- 1; fp_disabled <- 1; ); if ((STC or STDC or STCSR or STDCQ) and ((EC = 0) or (bp_CP_present = 0)) ) then ( trap <- 1; cp_disabled <- 1; ) ); next; if (trap = 0) then ( if ((STH or STHA) and (address<0> != 0)) then ( trap <- 1; mem_address_not_aligned <- 1 ) else if ((ST or STA or STF or STFSR or STC or STCSR) and (address<1:0> != 0)) then ( trap <- 1; mem_address_not_aligned <- 1 ) else if ((STD or STDA or STDF or STDFQ or STDC or STDCQ) and (address<2:0> != 0)) then ( trap <- 1; mem_address_not_aligned <- 1 ) else ( if (STDFQ and ((implementation has no floating-point queue) or (FSR.qne = 0))) then ( trap <- 1; fp_exception <- 1; ftt <- sequence_error; ); if (STDCQ and ((implementation has no coprocessor queue)) then ( trap <- 1; cp_exception <- 1; { possibly additional implementation-dependent actions } ); if (STDF and (rd<0> != 0)) then ( trap <- 1; fp_exception <- 1; ftt <- invalid_fp_register; ) ) ); next; if (trap = 0) then ( if (STF) then ( byte_mask <- 1111; data0 <- f[rd] ) else if (STC) then ( byte_mask <- 1111; data0 <- implementation_dependent_value ) else if (STDF) then ( byte_mask <- 1111; data0 <- f[rd & 0x1e] ) else if (STDC) then ( byte_mask <- 1111; data0 <- implementation_dependent_value ) else if (STD or STDA) then ( byte_mask <- 1111; data0 <- r[rd & 0x1e] ) else if (STDFQ) then ( byte_mask <- 1111; data0 <- implementation_dependent_value ) else if (STDCQ) then ( byte_mask <- 1111; data0 <- implementation_dependent_value ) else if (STFSR) then ( while ((FSR.qne = 1) and (trap = 0)) ( // wait for pending floating-point instructions to complete ) next; byte_mask <- 1111; data0 <- FSR ) else if (STCSR) then ( { implementation-dependent actions } byte_mask <- 1111; data0 <- CSR ) else if (ST or STA) then ( byte_mask <- 1111; data0 = r[rd] ) else if (STH or STHA) then ( if (address<1:0> = 0) then ( byte_mask <- 1100; data0 <- shift_left_logical(r[rd], 16) ) else if (address<1:0> = 2) then ( byte_mask <- 0011; data0 <- r[rd] ) ) else if (STB or STBA) then ( if (address<1:0> = 0) then ( byte_mask <- 1000; data0 <- shift_left_logical(r[rd], 24) ) ) else if (address<1:0> = 1) then ( byte_mask <- 0100; data0 <- shift_left_logical(r[rd], 16) ) ) else if (address<1:0> = 2) then ( byte_mask <- 0010; data0 <- shift_left_logical(r[rd], 8) ) ) else if (address<1:0> = 3) then ( byte_mask <- 0001; data0 <- r[rd] ) ) ); ); next; if (trap = 0) then ( MAE <- memory_write(addr_space, address, byte_mask, data1); next; if (MAE = 1) then ( trap <- 1; data_access_exception <- 1 ) ); if ((trap = 0) and (STD or STDA or STDF or STDC or STDFQ or STDCQ)) then ( if (STD or STDA) then ( data1 <- r[rd or 00001] ) else if (STDF) then ( data1 <- f[rd or 00001] ) else if (STDC) then ( data1 <- implementation_dependent_value } else if (STDFQ) then ( data1 <- implementation_dependent_value } else if (STDCQ) then ( data1 <- implementation_dependent_value } next; MAE <- memory_write(addr_space, address + 4, 1111, data1); next; if (MAE = 1) then ( { MAE = 1 only due to a "non-resumable machine-check error" } trap <- 1; data_access_exception <- 1 ) ); */ if (IS_USER && (STDA || STA || STHA || STBA || STDFQ || STDCQ)) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM && (STDA || STA || STHA || STBA)) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } uint32_t address = 0; uint8_t addr_space = 0; if (STD || ST || STH || STB || STF || STDF || STFSR || STDFQ || STCSR || STC || STDC || STDCQ) { address = RS1REG + (USEIMM ? SIMM13 : RS2REG); addr_space = m_data_space; } else if (STDA || STA || STHA || STBA) { address = RS1REG + RS2REG; addr_space = ASI; } if ((STF || STDF || STFSR || STDFQ) && (!(PSR & PSR_EF_MASK) || !m_bp_fpu_present)) { m_trap = 1; m_fp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if ((STC || STDC || STCSR || STDCQ) && (!(PSR & PSR_EC_MASK) || !m_bp_cp_present)) { m_trap = 1; m_cp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if ((STH || STHA) && ((address & 1) != 0)) { m_trap = 1; m_stashed_icount = m_icount; m_icount = 0; m_mem_address_not_aligned = 1; return; } else if ((ST || STA || STF || STFSR || STC || STCSR) && ((address & 3) != 0)) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if ((STD || STDA || STDF || STDFQ || STDC || STDCQ) && ((address & 7) != 0)) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (STDFQ) { // assume no floating-point queue for now m_trap = 1; m_fp_exception = 1; m_ftt = m_fpu_sequence_err; m_stashed_icount = m_icount; m_icount = 0; return; } if (STDCQ) { // assume no coprocessor queue for now m_trap = 1; m_cp_exception = 1; m_stashed_icount = m_icount; m_icount = 0; // { possibly additional implementation-dependent actions } return; } if (STDF && ((RD & 1) != 0)) { m_trap = 1; m_fp_exception = 1; m_ftt = 0xff; m_stashed_icount = m_icount; m_icount = 0; return; } uint32_t data0 = 0; //uint8_t byte_mask; if (STF) { //byte_mask = 15; data0 = FREG(RD); } else if (STC) { //byte_mask = 15; data0 = 0; } else if (STDF) { //byte_mask = 15; data0 = FREG(RD & 0x1e); } else if (STDC) { //byte_mask = 15; data0 = 0; } else if (STD || STDA) { //byte_mask = 15; data0 = REG(RD & 0x1e); } else if (STDFQ) { //byte_mask = 15; data0 = 0; } else if (STDCQ) { //byte_mask = 15; data0 = 0; } else if (STFSR) { // while ((FSR.qne = 1) and (trap = 0)) ( // wait for pending floating-point instructions to complete // ) // next; //byte_mask = 15; data0 = FSR; } else if (STCSR) { // { implementation-dependent actions } //byte_mask = 15; data0 = 0; } else if (ST || STA) { //byte_mask = 15; data0 = REG(RD); } else if (STH || STHA) { if ((address & 3) == 0) { //byte_mask = 12; data0 = REG(RD) << 16; } else if ((address & 3) == 2) { //byte_mask = 3; data0 = REG(RD); } } else if (STB || STBA) { if ((address & 3) == 0) { //byte_mask = 8; data0 = REG(RD) << 24; } else if ((address & 3) == 1) { //byte_mask = 4; data0 = REG(RD) << 16; } else if ((address & 3) == 2) { //byte_mask = 2; data0 = REG(RD) << 8; } else if ((address & 3) == 3) { //byte_mask = 1; data0 = REG(RD); } } static const uint32_t mask16[4] = { 0xffff0000, 0x00000000, 0x0000ffff, 0x00000000 }; static const uint32_t mask8[4] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }; m_mmu->write_asi(addr_space, address >> 2, data0, (ST || STA || STD || STDA || STF || STDF || STDFQ || STFSR || STC || STDC || STDCQ || STCSR) ? 0xffffffff : ((STH || STHA) ? mask16[address & 2] : mask8[address & 3])); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (STD || STDA || STDF || STDC || STDFQ || STDCQ) { uint32_t data1 = 0; if (STD || STDA) { data1 = REG(RD | 1); } else if (STDF) { data1 = FREG(RD | 1); } else if (STDC) { data1 = 0; } else if (STDFQ) { data1 = 0; } else if (STDCQ) { data1 = 0; } m_mmu->write_asi(addr_space, (address + 4) >> 2, data1, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } } PC = nPC; nPC = nPC + 4; } /* The SPARC Instruction Manual: Version 8, page 163, "Appendix C - ISP Descriptions - C.9. Instruction Defintions - Load Instructions" (SPARCv8.pdf, pg. 160) if (LDD or LD or LDSH or LDUH or LDSB or LDUB or LDDF or LDF or LDFSR or LDDC or LDC or LDCSR) then ( address <- r[rs1] + (if (i = 0) then r[rs2] else sign_extend(simm13)); addr_space <- (if (S = 0) then 10 else 11) ) else if (LDDA or LDA or LDSHA or LDUHA or LDSBA or LDUBA) then ( if (S = 0) then ( trap <- 1; privileged_instruction <- 1 ) else if (i = 1) then ( trap <- 1; illegal_instruction <- 1 ) else ( address <- r[rs1] + r[rs2]; addr_space <- asi ) ) next; if (trap = 0) then ( if ( (LDF or LDDF or LDFSR) and ((EF = 0) or (bp_FPU_present = 0)) then ( trap <- 1; fp_disabled <- 1 ) else if ( (LDC or LDDC or LDCSR) and ((EC = 0) or (bp_CP_present = 0)) then ( trap <- 1; cp_disabled <- 1 ) else if ( ( (LDD or LDDA or LDDF or LDDC) and (address<2:0> != 0)) or ((LD or LDA or LDF or LDFSR or LDC or LDCSR) and (address<1:0> != 0)) or ((LDSH or LDSHA or LDUH or LDUHA) and address<0> != 0) ) then ( trap <- 1; mem_address_not_aligned <- 1 ) else if (LDDF and (rd<0> != 0)) then ( trap <- 1; fp_exception <- 1; ftt <- invalid_fpr_register ) else if ((LDF or LDDF or LDFSR) and (an FPU sequence error is detected)) then ( trap <- 1; fp_exception <- 1; ftt <- sequence_error ) else if ((LDC or LDDC or LDCSR) and (a CP sequence error is detected)) then ( trap <- 1; cp_exception <- 1; { possibly additional implementation-dependent actions } ) ); next; if (trap = 0) then { (data, MAE) <- memory_read(addr_space, address); next; if (MAE = 1) then ( trap <- 1; data_access_exception <- 1; ) else ( if (LDSB or LDSBA or LDUB or LDUBA) then ( if (address<1:0> = 0) then byte <- data<31:24> else if (address<1:0> = 1) then byte <- data<23:16> else if (address<1:0> = 2) then byte <- data<15: 8> else if (address<1:0> = 3) then byte <- data< 7: 0> next; if (LDSB or LDSBA) then word0 <- sign_extend_byte(byte) else word0 <- zero_extend_byte(byte) ) else if (LDSH or LDSHA or LDUH or LDUHA) then ( if (address<1:0> = 0) then halfword <- data<31:16> else if (address<1:0> = 2) then halfword <- data<15: 0> next; if (LDSH or LDSHA) then word0 <- sign_extend_halfword(halfword) else word0 <- zero_extend_halfword(halfword) ) else word0 <- data ) ); next; if (trap = 0) then ( if ( (rd != 0) and (LD or LDA or LDSH or LDSHA or LDUHA or LDUH or LDSB or LDSBA or LDUB or LDUBA) ) then r[rd] <- word0 else if (LDF) then f[rd] <- word0 else if (LDC) then { implementation-dependent actions } else if (LDFSR) then FSR <- word0 else if (LDCSR) then CSR <- word0 else if (LDD or LDDA) then r[rd and 11110] <- word0 else if (LDDF) then f[rd and 11110] <- word0 else if (LDDC) then { implementation-dependent actions } ); next; if (((trap = 0) and (LDD or LDDA or LDDF or LDDC)) then ( (word1, MAE) <- memory_read(addr_space, address + 4); next; if (MAE = 1) then ( { MAE = 1 only due to a "non-resumable machine-check error" } trap <- 1; data_access_exception <- 1 ) else if (LDD or LDDA) then r[rd or 1] <- word1 else if (LDDF) then f[rd or 1] <- word1 else if (LDDC) then { implementation-dependent actions } ); */ inline void sparcv7_device::execute_ldd(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (address & 7) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; } const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) RDREG = data; const uint32_t word1 = m_mmu->read_asi(m_data_space, (address + 4) >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } REG(RD | 1) = word1; PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ld(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) RDREG = data; PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldsh(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (address & 1) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } static const uint32_t mask16[4] = { 0xffff0000, 0x00000000, 0x0000ffff, 0x00000000 }; const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, mask16[address & 2]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if ((address & 3) == 0) RDREG = (int32_t)data >> 16; else if ((address & 3) == 2) RDREG = ((int32_t)data << 16) >> 16; } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_lduh(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (address & 1) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } static const uint32_t mask16[4] = { 0xffff0000, 0x00000000, 0x0000ffff, 0x00000000 }; const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, mask16[address & 2]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if ((address & 3) == 0) RDREG = data >> 16; else if ((address & 3) == 2) RDREG = data & 0xffff; } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldsb(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); static const uint32_t mask8[4] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }; const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, mask8[address & 3]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if ((address & 3) == 0) RDREG = (int32_t)data >> 24; else if ((address & 3) == 1) RDREG = ((int32_t)data << 8) >> 24; else if ((address & 3) == 2) RDREG = ((int32_t)data << 16) >> 24; else if ((address & 3) == 3) RDREG = ((int32_t)data << 24) >> 24; } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldub(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); static const uint32_t mask8[4] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }; const uint32_t byte_idx = address & 3; const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, mask8[byte_idx]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { static const int shifts[4] = { 24, 16, 8, 0 }; RDREG = (uint8_t)(data >> shifts[byte_idx]); } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_lddfpr(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (!(PSR & PSR_EF_MASK) || m_bp_fpu_present == 0) { m_trap = 1; m_fp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (address & 7) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RD & 1) { m_trap = 1; m_fp_exception = 1; m_ftt = 0xff; m_stashed_icount = m_icount; m_icount = 0; return; } if (m_fpu_sequence_err) { m_trap = 1; m_fp_exception = 1; m_ftt = m_fpu_sequence_err; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } FREG(RD & 0x1e) = data; const uint32_t word1 = m_mmu->read_asi(m_data_space, (address + 4) >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } FREG(RD | 1) = word1; PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldfpr(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (!(PSR & PSR_EF_MASK) || m_bp_fpu_present == 0) { m_trap = 1; m_fp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (m_fpu_sequence_err) { m_trap = 1; m_fp_exception = 1; m_ftt = m_fpu_sequence_err; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } FDREG = data; PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldfsr(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (!(PSR & PSR_EF_MASK) || m_bp_fpu_present == 0) { m_trap = 1; m_fp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (m_fpu_sequence_err) { m_trap = 1; m_fp_exception = 1; m_ftt = m_fpu_sequence_err; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t data = m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } FSR = (data & ~FSR_RESV_MASK) | FSR_VER; switch (FSR & FSR_RD_MASK) { case FSR_RD_NEAR: softfloat_roundingMode = softfloat_round_near_even; break; case FSR_RD_ZERO: softfloat_roundingMode = softfloat_round_minMag; break; case FSR_RD_UP: softfloat_roundingMode = softfloat_round_max; break; case FSR_RD_DOWN: softfloat_roundingMode = softfloat_round_min; break; } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_lddcpr(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (!(PSR & PSR_EC_MASK) || !m_bp_cp_present) { m_trap = 1; m_cp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (address & 7) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (m_cp_sequence_err) { m_trap = 1; m_cp_exception = 1; m_stashed_icount = m_icount; m_icount = 0; // possibly additional implementation-dependent actions return; } m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } // implementation-dependent actions m_mmu->read_asi(m_data_space, (address + 4) >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } // implementation-dependent actions PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldcpr(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (!(PSR & PSR_EC_MASK) || !m_bp_cp_present) { m_trap = 1; m_cp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (m_cp_sequence_err) { m_trap = 1; m_cp_exception = 1; m_stashed_icount = m_icount; m_icount = 0; // possibly additional implementation-dependent actions return; } m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } // implementation-dependent actions PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldcsr(uint32_t op) { const uint32_t address = RS1REG + (USEIMM ? SIMM13 : RS2REG); if (!(PSR & PSR_EC_MASK) || !m_bp_cp_present) { m_trap = 1; m_cp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (m_cp_sequence_err) { m_trap = 1; m_cp_exception = 1; m_stashed_icount = m_icount; m_icount = 0; // possibly additional implementation-dependent actions return; } m_mmu->read_asi(m_data_space, address >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } // implementation-dependent actions PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldda(uint32_t op) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t address = RS1REG + RS2REG; const uint32_t addr_space = ASI; if (address & 7) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t data = m_mmu->read_asi(addr_space, address >> 2, 0xffffffff); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) RDREG = data; uint32_t word1 = m_mmu->read_asi(addr_space, (address + 4) >> 2, 0xffffffff); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } REG(RD | 1) = word1; PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_lda(uint32_t op) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t address = RS1REG + RS2REG; if (address & 3) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t data = m_mmu->read_asi(ASI, address >> 2, 0xffffffff); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) RDREG = data; PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldsha(uint32_t op) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t address = RS1REG + RS2REG; if (address & 1) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } static const uint32_t mask16[4] = { 0xffff0000, 0x00000000, 0x0000ffff, 0x00000000 }; const uint32_t data = m_mmu->read_asi(ASI, address >> 2, mask16[address & 2]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if ((address & 3) == 0) RDREG = (int32_t)data >> 16; else if ((address & 3) == 2) RDREG = ((int32_t)data << 16) >> 16; } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_lduha(uint32_t op) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } const uint32_t address = RS1REG + RS2REG; if (address & 1) { m_trap = 1; m_mem_address_not_aligned = 1; m_stashed_icount = m_icount; m_icount = 0; return; } static const uint32_t mask16[4] = { 0xffff0000, 0x00000000, 0x0000ffff, 0x00000000 }; const uint32_t data = m_mmu->read_asi(ASI, address >> 2, mask16[address & 2]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if ((address & 3) == 0) RDREG = data >> 16; else if ((address & 3) == 2) RDREG = data & 0xffff; } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_ldsba(uint32_t op) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } static const uint32_t mask8[4] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }; const uint32_t address = RS1REG + RS2REG; const uint32_t data = m_mmu->read_asi(ASI, address >> 2, mask8[address & 3]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if ((address & 3) == 0) RDREG = (int32_t)data >> 24; else if ((address & 3) == 1) RDREG = ((int32_t)data << 8) >> 24; else if ((address & 3) == 2) RDREG = ((int32_t)data << 16) >> 24; else if ((address & 3) == 3) RDREG = ((int32_t)data << 24) >> 24; } PC = nPC; nPC = nPC + 4; } inline void sparcv7_device::execute_lduba(uint32_t op) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } static const uint32_t mask8[4] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }; const uint32_t address = RS1REG + RS2REG; const uint32_t data = m_mmu->read_asi(ASI, address >> 2, mask8[address & 3]); if (m_mae) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } if (RDBITS) { if ((address & 3) == 0) RDREG = data >> 24; else if ((address & 3) == 1) RDREG = (data >> 16) & 0xff; else if ((address & 3) == 2) RDREG = (data >> 8) & 0xff; else if ((address & 3) == 3) RDREG = data & 0xff; } PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_ldstub - execute an atomic load-store // instruction //------------------------------------------------- void sparcv7_device::execute_ldstub(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 169, "Appendix C - ISP Descriptions - Atomic Load-Store Unsigned Byte Instructions" (SPARCv8.pdf, pg. 166) if (LDSTUB) then ( address <- r[rs1] + (if (i = 0) then r[rs2] else sign_extend(simm13)); addr_space <- (if (S = 0) then 10 else 11) } else if (LDSTUBA) then ( if (S = 0) then ( trap <- 1; privileged_instruction <- 1 ) else if (i = 1) then ( trap <- 1; illegal_instruction <- 1 ) else ( address <- r[rs1] + r[rs2]; addr_space <- asi ) ); next; if (trap = 0) then ( while ( (pb_block_ldst_byte = 1) or (pb_block_ldst_word = 1) ) then ( { wait for lock(s) to be lifted } { an implementation actually need only block when another LDSTUB or SWAP is pending on the same byte in memory as the one addressed by this LDSTUB } }; next; pb_block_ldst_byte <- 1; next; (data, MAE) <- memory_read(addr_space, address); next; if (MAE = 1) then ( trap <- 1; data_access_exception <- 1 ) ) next; if (trap = 0) then ( if (address<1:0> = 0) then ( byte_mask <- 1000 ) else if (address<1:0> = 1) then ( byte_mask <- 0100 ) else if (address<1:0> = 2) then ( byte_mask <- 0010 ) else if (address<1:0> = 3) then ( byte_mask <- 0001 ) ; next; MAE <- memory_write(addr_space, address, byte_mask, FFFFFFFF); next; pb_block_ldst_byte <- 0; if (MAE = 1) then ( { MAE = 1 only due to a "non-resumable machine-check error" } trap <- 1; data_access_exception <- 1 ) else ( if (address<1:0> = 0) then word <- zero_extend_byte(data<31:24>) else if (address<1:0> = 1) then word <- zero_extend_byte(data<23:24>) else if (address<1:0> = 2) then word <- zero_extend_byte(data<15: 8>) else if (address<1:0> = 3) then word <- zero_extend_byte(data< 7: 0>) next; if (rd != 0) then r[rd] <- word ) ); */ uint32_t address = 0; uint8_t addr_space = 0; if (LDSTUB) { address = RS1REG + (USEIMM ? SIMM13 : RS2REG); addr_space = (IS_USER ? 10 : 11); } else if (LDSTUBA) { if (IS_USER) { m_trap = 1; m_privileged_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (USEIMM) { m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else { address = RS1REG + RS2REG; addr_space = ASI; return; } } uint32_t data(0); //while (m_pb_block_ldst_byte || m_pb_block_ldst_word) //{ // { wait for lock(s) to be lifted } // { an implementation actually need only block when another LDSTUB or SWAP // is pending on the same byte in memory as the one addressed by this LDSTUB } //} m_pb_block_ldst_byte = 1; static const uint32_t mask8[4] = { 0xff000000, 0x00ff0000, 0x0000ff00, 0x000000ff }; data = m_mmu->read_asi(addr_space, address >> 2, mask8[address & 3]); if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } m_mmu->write_asi(addr_space, address >> 2, 0xffffffff, mask8[address & 3]); m_pb_block_ldst_byte = 0; if (MAE) { m_trap = 1; m_data_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } uint32_t word; if ((address & 3) == 0) { word = (data >> 24) & 0xff; } else if ((address & 3) == 1) { word = (data >> 16) & 0xff; } else if ((address & 3) == 2) { word = (data >> 8) & 0xff; } else // if ((address & 3) == 3) { word = data & 0xff; } if (RDBITS) RDREG = word; PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_group3 - execute an opcode in group 3 // (load/store) //------------------------------------------------- inline void sparcv7_device::execute_group3(uint32_t op) { static const int ldst_cycles[64] = { 1, 1, 1, 2, 2, 2, 2, 3, 0, 1, 1, 0, 0, 3, 0, 0, 1, 1, 1, 2, 2, 2, 2, 3, 0, 1, 1, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }; switch (OP3) { case OP3_LD: execute_ld(op); break; case OP3_LDUB: execute_ldub(op); break; case OP3_LDUH: execute_lduh(op); break; case OP3_LDD: execute_ldd(op); break; case OP3_LDSB: execute_ldsb(op); break; case OP3_LDSH: execute_ldsh(op); break; case OP3_LDA: execute_lda(op); break; case OP3_LDUBA: execute_lduba(op); break; case OP3_LDUHA: execute_lduha(op); break; case OP3_LDDA: execute_ldda(op); break; case OP3_LDSBA: execute_ldsba(op); break; case OP3_LDSHA: execute_ldsha(op); break; case OP3_LDFPR: execute_ldfpr(op); break; case OP3_LDFSR: execute_ldfsr(op); break; case OP3_LDDFPR: execute_lddfpr(op); break; case OP3_LDCPR: execute_ldcpr(op); break; case OP3_LDCSR: execute_ldcsr(op); break; case OP3_LDDCPR: execute_lddcpr(op); break; case OP3_ST: case OP3_STB: case OP3_STH: case OP3_STD: case OP3_STA: case OP3_STBA: case OP3_STHA: case OP3_STDA: case OP3_STFPR: case OP3_STFSR: case OP3_STDFQ: case OP3_STDFPR: case OP3_STCPR: case OP3_STCSR: case OP3_STDCQ: case OP3_STDCPR: execute_store(op); break; case OP3_LDSTUB: case OP3_LDSTUBA: execute_ldstub(op); break; #if SPARCV8 case OP3_SWAP: case OP3_SWAPA: execute_swap(op); break; #endif default: if (!execute_extended_Op( logerror("illegal instruction at %08x: %08x\n", PC, op); m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; break; } if (MAE /*|| HOLD_BUS*/) m_icount--; else m_icount -= ldst_cycles[OP3]; } //------------------------------------------------- // evaluate_fp_condition - evaluate a given fp // condition code //------------------------------------------------- bool sparcv7_device::evaluate_fp_condition(uint32_t op) { // COND & 8 // 0 8 // fbn fba // fbne fbe // fblg fbue // fbul fbge // fbl fbuge // fbug fble // fbg fbule // fbu fbo static const uint32_t EQ_BIT = (1 << (FSR_FCC_EQ >> FSR_FCC_SHIFT)); static const uint32_t LT_BIT = (1 << (FSR_FCC_LT >> FSR_FCC_SHIFT)); static const uint32_t GT_BIT = (1 << (FSR_FCC_GT >> FSR_FCC_SHIFT)); static const uint32_t UO_BIT = (1 << (FSR_FCC_UO >> FSR_FCC_SHIFT)); const uint32_t fcc_bit = 1 << ((m_fsr & FSR_FCC_MASK) >> FSR_FCC_SHIFT); switch(COND) { case 0: return false; case 1: return fcc_bit & (LT_BIT | GT_BIT | UO_BIT); case 2: return fcc_bit & (LT_BIT | GT_BIT); case 3: return fcc_bit & (LT_BIT | UO_BIT); case 4: return fcc_bit & (LT_BIT); case 5: return fcc_bit & (GT_BIT | UO_BIT); case 6: return fcc_bit & (GT_BIT); case 7: return fcc_bit & (UO_BIT); case 8: return true; case 9: return fcc_bit & (EQ_BIT); case 10: return fcc_bit & (EQ_BIT | UO_BIT); case 11: return fcc_bit & (EQ_BIT | GT_BIT); case 12: return fcc_bit & (EQ_BIT | GT_BIT | UO_BIT); case 13: return fcc_bit & (EQ_BIT | LT_BIT); case 14: return fcc_bit & (EQ_BIT | LT_BIT | UO_BIT); case 15: return fcc_bit & (EQ_BIT | LT_BIT | GT_BIT); } return false; } //------------------------------------------------- // execute_fbfcc - execute an fp branch opcode //------------------------------------------------- void sparcv7_device::execute_fbfcc(uint32_t op) { bool branch_taken = evaluate_fp_condition(op); uint32_t pc = PC; PC = nPC; if (branch_taken) { nPC = pc + DISP22; if (COND == COND_BA && ANNUL) m_no_annul = false; } else { nPC = nPC + 4; if (ANNUL) m_no_annul = false; } } //------------------------------------------------- // evaluate_condition - evaluate a given integer // condition code //------------------------------------------------- bool sparcv7_device::evaluate_condition(uint32_t op) { // COND & 8 // 0 8 // bn ba // bz bne // ble bg // bl bge // bleu bgu // bcs bcc // bneg bpos // bvs bvc switch(COND) { case 0: return false; case 1: return ICC_Z_SET; case 2: return ICC_Z_SET || (ICC_N != ICC_Z); case 3: return (ICC_N != ICC_V); case 4: return ICC_C_SET || ICC_Z_SET; case 5: return ICC_C_SET; case 6: return ICC_N_SET; case 7: return ICC_V_SET; case 8: return true; case 9: return ICC_Z_CLEAR; case 10: return ICC_Z_CLEAR && ICC_N_CLEAR; case 11: return (ICC_N == ICC_V); case 12: return ICC_C_CLEAR && ICC_Z_CLEAR; case 13: return ICC_C_CLEAR; case 14: return ICC_N_CLEAR; case 15: return ICC_V_CLEAR; } return false; } //------------------------------------------------- // execute_bicc - execute a branch opcode //------------------------------------------------- void sparcv7_device::execute_bicc(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 178, "Appendix C - ISP Descriptions - Branch on Integer Condition Instructions" (SPARCv8.pdf, pg. 175) eval_icc := ( if (BNE) then (if (Z = 0) then 1 else 0); if (BE) then (if (Z = 1) then 1 else 0); if (BG) then (if ((Z or (N xor V)) = 0) then 1 else 0); if (BLE) then (if ((Z or (N xor V)) = 1) then 1 else 0); if (BGE) then (if ((N xor V) = 0) then 1 else 0); if (BL) then (if ((N xor V) = 1) then 1 else 0); if (BGU) then (if ((C = 0) and (Z = 0)) then 1 else 0); if (BLEU) then (if ((C = 1) or (Z = 1)) then 1 else 0); if (BCC) then (if (C = 0) then 1 else 0); if (BCS) then (if (C = 1) then 1 else 0); if (BPOS) then (if (N = 0) then 1 else 0); if (BNEG) then (if (N = 1) then 1 else 0); if (BVC) then (if (V = 0) then 1 else 0); if (BVS) then (if (V = 1) then 1 else 0); if (BA) then 1; if (BN) then 0; ) PC <- nPC; if (eval_icc = 1) then ( nPC <- PC + sign_extend(disp22[]00); if (BA and (a = 1)) then annul <- 1 { only for annulling Branch-Always } ) else ( nPC <- nPC + 4; if (a = 1) then annul <- 1 { only for annulling branches other than BA } ) */ bool branch_taken = evaluate_condition(op); uint32_t pc = PC; PC = nPC; if (branch_taken) { nPC = pc + DISP22; if (COND == COND_BA && ANNUL) m_no_annul = false; } else { nPC = nPC + 4; if (ANNUL) m_no_annul = false; } } //------------------------------------------------- // execute_ticc - execute a conditional trap //------------------------------------------------- void sparcv7_device::execute_ticc(uint32_t op) { /* The SPARC Instruction Manual: Version 8, page 182, "Appendix C - ISP Descriptions - Trap on Integer Condition Instructions" (SPARCv8.pdf, pg. 179) trap_eval_icc := ( if (TNE) then (if (Z = 0) then 1 else 0); if (TE) then (if (Z = 1) then 1 else 0); if (TG) then (if ((Z or (N xor V)) = 0) then 1 else 0); if (TLE) then (if ((Z or (N xor V)) = 1) then 1 else 0); if (TGE) then (if ((N xor V) = 0) then 1 else 0); if (TL) then (if ((N xor V) = 1) then 1 else 0); if (TGU) then (if ((C = 0) and (Z = 0)) then 1 else 0); if (TLEU) then (if ((C = 1) or (Z = 1)) then 1 else 0); if (TCC) then (if (C = 0) then 1 else 0); if (TCS) then (if (C = 1) then 1 else 0); if (TPOS) then (if (N = 0) then 1 else 0); if (TNEG) then (if (N = 1) then 1 else 0); if (TVC) then (if (V = 0) then 1 else 0); if (TVS) then (if (V = 1) then 1 else 0); if (TA) then 1; if (TN) then 0; ) trap_number := r[rs1] + (if (i = 0) then r[rs2] else sign_extend(software_trap#)); if (Ticc) then ( if (trap_eval_icc = 1) then ( trap <- 1; trap_instruction <- 1; ticc_trap_type <- trap_number<6:0> ) else ( PC <- nPC; nPC <- nPC + 4; ) ); */ bool trap_eval_icc = evaluate_condition(op); uint8_t trap_number = RS1REG + (USEIMM ? SIMM7 : RS2REG); if (COND) { if (trap_eval_icc) { m_trap = 1; m_trap_instruction = 1; m_ticc_trap_type = trap_number & 0x7f; m_stashed_icount = m_icount; m_icount = 0; } else { PC = nPC; nPC = nPC + 4; } } } //------------------------------------------------- // select_trap - prioritize traps and perform any // additional functions from taking them //------------------------------------------------- void sparcv7_device::select_trap() { if (!m_trap) return; if (m_reset_trap) { m_trap = 0; return; } else if (!m_et) { m_execute_mode = 0; m_error_mode = 1; m_stashed_icount = m_icount; m_icount = 0; } else if (m_data_store_error) m_tt = 0x2b; else if (m_instruction_access_error) m_tt = 0x21; else if (m_r_register_access_error) m_tt = 0x20; else if (m_instruction_access_exception) m_tt = 0x01; else if (m_privileged_instruction) m_tt = 0x03; else if (m_illegal_instruction) m_tt = 0x02; else if (m_fp_disabled) m_tt = 0x04; else if (m_cp_disabled) m_tt = 0x24; else if (m_unimplemented_FLUSH) m_tt = 0x25; else if (m_window_overflow) m_tt = 0x05; else if (m_window_underflow) m_tt = 0x06; else if (m_mem_address_not_aligned) m_tt = 0x07; else if (m_fp_exception) m_tt = 0x08; else if (m_cp_exception) m_tt = 0x28; else if (m_data_access_error) m_tt = 0x29; else if (m_data_access_exception) m_tt = 0x09; else if (m_tag_overflow) m_tt = 0x0a; else if (m_division_by_zero) m_tt = 0x2a; else if (m_trap_instruction) m_tt = 0x80 | m_ticc_trap_type; else if (m_interrupt_level > 0) m_tt = 0x10 | m_interrupt_level; TBR = (TBR & 0xfffff000) | (m_tt << 4); m_trap = 0; m_instruction_access_exception = 0; m_illegal_instruction = 0; m_privileged_instruction = 0; m_fp_disabled = 0; m_cp_disabled = 0; m_window_overflow = 0; m_window_underflow = 0; m_mem_address_not_aligned = 0; m_fp_exception = 0; m_cp_exception = 0; m_data_access_exception = 0; m_tag_overflow = 0; m_division_by_zero = 0; m_trap_instruction = 0; m_interrupt_level = 0; m_mae = 0; } //------------------------------------------------- // execute_trap - prioritize and invoke traps // that have been flagged by the previous // instructions, if any. //------------------------------------------------- void sparcv7_device::execute_trap() { /* The SPARC Instruction Manual: Version 8, page 161, "Appendix C - C.8. Traps" (SPARCv8.pdf, pg. 158) select_trap; { see below } next; if (error_mode = 0) then ( ET <- 0; PS <- S; CWP <- (CWP - 1) modulo NWINDOWS; next; if (annul = 0) then ( r[17] <- PC; r[18] <- nPC; ) else { annul != 0) } ( r[17] <- nPC; r[18] <- nPC + 4; annul <- 0; ) next; S <- 1; if (reset_trap = 0) then ( PC <- TBR; nPC <- TBR + 4; ) else { reset_trap = 1 } ( PC <- 0; nPC <- 4; reset_trap <- 0; ) ); select_trap := ( if (reset_trap = 1) then { ignore ET, and leave tt unchanged } else if (ET = 0) then ( execute_mode <- 0; error_mode <- 1 ) else if (data_store_error = 1) then tt <- 00101011 else if (instruction_access_error = 1) then tt <- 00100001 else if (r_register_access_error = 1) then tt <- 00100000 else if (instruction_access_exception = 1) then tt <- 00000001 else if (privileged_instruction = 1) then tt <- 00000011 else if (illegal_instruction = 1) then tt <- 00000010 else if (fp_disabled = 1) then tt <- 00000100 else if (cp_disabled = 1) then tt <- 00100100 else if (unimplemented_FLUSH = 1) then tt <- 00100101 else if (window_overflow = 1) then tt <- 00000101 else if (window_underflow = 1) then tt <- 00000110 else if (mem_address_not_aligned = 1) then tt <- 00000111 else if (fp_exception = 1) then tt <- 00001000 else if (cp_exception = 1) then tt <- 00101000 else if (data_access_error = 1) then tt <- 00101001 else if (data_access_exception = 1) then tt <- 00001001 else if (tag_overflow = 1) then tt <- 00001010 else if (division_by_zero = 1) then tt <- 00101010 else if (trap_instruction = 1) then tt <- 1[]ticc_trap_type else if (interrupt_level > 0) then tt <- 0001[]interrupt_level; next; trap <- 0; instruction_access_exception <- 0; illegal_instruction <- 0; privileged_instruction <- 0; fp_disabled <- 0; cp_disabled <- 0; window_overflow <- 0; window_underflow <- 0; mem_address_not_aligned <- 0; fp_exception <- 0; cp_exception <- 0; data_access_exception <- 0; tag_overflow <- 0; division_by_zero <- 0; trap_instruction <- 0; interrupt_level <- 0; ); */ if (!m_trap) { return; } select_trap(); if (!m_error_mode) { PSR &= ~PSR_ET_MASK; m_et = false; if (IS_USER) PSR &= ~PSR_PS_MASK; else PSR |= PSR_PS_MASK; PSR |= PSR_S_MASK; m_s = true; m_data_space = 11; int cwp = PSR & PSR_CWP_MASK; int new_cwp = ((cwp + NWINDOWS) - 1) % NWINDOWS; PSR &= ~PSR_CWP_MASK; PSR |= new_cwp; update_gpr_pointers(); if (m_no_annul) { REG(17) = PC; REG(18) = nPC; } else { REG(17) = nPC; REG(18) = nPC + 4; m_no_annul = true; } if (!m_reset_trap) { PC = TBR; nPC = TBR + 4; } else { PC = 0; nPC = 4; m_reset_trap = 0; } } } //------------------------------------------------- // dispatch_instruction - executes a // single fetched instruction. //------------------------------------------------- /* The SPARC Instruction Manual: Version 8, page 159, "Appendix C - ISP Descriptions - C.6. Instruction Dispatch" (SPARCv8.pdf, pg. 156) illegal_IU_instr :- ( if ( ( (op == 00) and (op2 == 000) ) { UNIMP instruction } or ( ((op=11) or (op=10)) and (op3=unassigned) ) then 1 else 0 if (illegal_IU_instr = 1) then ( trap <- 1 illegal_instruction <- 1 ); if ((FPop1 or FPop2 or FBfcc) and ((EF = 0) or (bp_FPU_present = 0))) then ( trap <- 1; fp_disabled <- 1 ); if (CPop1 or CPop2 or CBccc) and ((EC = 0) or (bp_CP_present = 0))) then ( trap <- 1; cp_disabled <- 1 ); next; if (trap = 0) then ( { code for specific instruction, defined below } ); */ inline void sparcv7_device::dispatch_instruction(uint32_t op) { const uint8_t op_type = OP; switch (op_type) { case OP_TYPE0: // Bicc, SETHI, FBfcc switch (OP2) { case OP2_UNIMP: // unimp logerror("unimp @ %x\n", PC); break; case OP2_BICC: // branch on integer condition codes execute_bicc(op); break; case OP2_SETHI: // sethi *m_regs[RD] = op << 10; m_r[0] = 0; PC = nPC; nPC = nPC + 4; break; case OP2_FBFCC: // branch on floating-point condition codes if (!(PSR & PSR_EF_MASK) || !m_bp_fpu_present) { m_trap = 1; m_fp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } execute_fbfcc(op); break; #if SPARCV8 case OP2_CBCCC: // branch on coprocessor condition codes, SPARCv8 if (!(PSR & PSR_EC_MASK) || !m_bp_cp_present) { logerror("cbccc @ %08x: %08x\n", PC, op); m_trap = 1; m_cp_disabled = 1; m_stashed_icount = m_icount; m_icount = 0; return; } return; #endif default: logerror("illegal instruction at %08x: %08x\n", PC, op); m_trap = 1; m_illegal_instruction = 1; m_stashed_icount = m_icount; m_icount = 0; return; } break; case OP_CALL: { uint32_t pc = PC; uint32_t callpc = PC + DISP30; PC = nPC; nPC = callpc; REG(15) = pc; break; } case OP_ALU: execute_group2(op); break; case OP_LDST: execute_group3(op); break; } } void sparcv7_device::check_fdiv_zero_exception() { m_fsr |= FSR_CEXC_DZC; if (m_fsr & FSR_TEM_DZM) { m_fsr = (m_fsr & ~FSR_FTT_MASK) | FSR_FTT_IEEE; m_trap = 1; m_fp_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } m_fsr |= FSR_AEXC_DZA; } bool sparcv7_device::check_fp_exceptions() { if (softfloat_exceptionFlags & softfloat_flag_inexact) m_fsr |= FSR_CEXC_NXC; if (softfloat_exceptionFlags & softfloat_flag_underflow) m_fsr |= FSR_CEXC_UFC; if (softfloat_exceptionFlags & softfloat_flag_overflow) m_fsr |= FSR_CEXC_OFC; if (softfloat_exceptionFlags & softfloat_flag_invalid) m_fsr |= FSR_CEXC_NVC; // accrue disabled exceptions const uint32_t cexc = m_fsr & FSR_CEXC_MASK; const uint32_t tem = (m_fsr & FSR_TEM_MASK) >> FSR_TEM_SHIFT; m_fsr |= (~tem & cexc) << FSR_AEXC_SHIFT; // check if exception is enabled if (tem & cexc) { m_fsr = (m_fsr & ~FSR_FTT_MASK) | FSR_FTT_IEEE; m_trap = 1; m_fp_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return true; } return false; } bool sparcv7_device::set_fpr32(const uint32_t rd, const uint32_t data) { if (softfloat_exceptionFlags && check_fp_exceptions()) return true; m_fpr[rd] = data; return false; } bool sparcv7_device::set_fpr64(const uint32_t rd, const uint64_t data) { if (softfloat_exceptionFlags && check_fp_exceptions()) return true; m_fpr[rd] = (uint32_t)(data >> 32); m_fpr[rd + 1] = (uint32_t)data; return false; } //------------------------------------------------- // complete_fp_execution - completes execution // of a floating-point operation //------------------------------------------------- void sparcv7_device::complete_fp_execution(uint32_t op) { softfloat_exceptionFlags = 0; const uint32_t fpop = (op >> 5) & 0x1ff; switch (fpop) { case FPOP_FMOVS: FDREG = FREG(RS2); printf("FMOVs %08x\n", FDREG); break; case FPOP_FNEGS: { const uint32_t rs2 = FREG(RS2); const float32_t fs2 = float32_t{ rs2 }; if (set_fpr32(RD, f32_mul(fs2, i32_to_f32(-1)).v)) { printf("FNEGs %08x -> Exception\n", rs2); return; } printf("FNEGs %08x = %08x\n", rs2, FDREG); break; } case FPOP_FABSS: { const uint32_t rs2 = FREG(RS2); const float32_t fs2 = float32_t{ rs2 }; if (f32_lt(fs2, float32_t{0})) { if (set_fpr32(RD, f32_mul(fs2, i32_to_f32(-1)).v)) { printf("FABSs %08x -> Exception\n", rs2); return; } } else if (set_fpr32(RD, rs2)) { printf("FABSs %08x -> Exception\n", rs2); return; } printf("FABSs %08x = %08x\n", rs2, FDREG); break; } case FPOP_FSQRTS: { const uint32_t rs2 = FREG(RS2); const float32_t fs2 = float32_t{ rs2 }; if (set_fpr32(RD, f32_sqrt(fs2).v)) { printf("FSQRTs %08x -> Exception\n", rs2); return; } printf("FSQRTs %08x -> %08x\n", rs2, FDREG); break; } case FPOP_FSQRTD: { const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs2 = float64_t{ rs2 }; if (set_fpr64(RD_D, f64_sqrt(fs2).v)) { printf("FSQRTd %08x%08x -> Exception\n", (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } printf("FSQRTd %08x%08x -> %08x%08x\n", (uint32_t)(rs2 >> 32), (uint32_t)rs2, FREG(RD_D), FREG(RD_D + 1)); break; } case FPOP_FSQRTX: fatalerror("Unimplemented FPop: FSQRTx\n"); break; case FPOP_FADDS: { const uint32_t rs1 = FREG(RS1); const uint32_t rs2 = FREG(RS2); const float32_t fs1 = float32_t{ rs1 }; const float32_t fs2 = float32_t{ rs2 }; if (set_fpr32(RD, f32_add(fs1, fs2).v)) { printf("FADDs %08x + %08x -> Exception\n", rs1, rs2); return; } printf("FADDs %08x + %08x = %08x\n", rs1, rs2, FDREG); break; } case FPOP_FADDD: { const uint64_t rs1 = ((uint64_t)FREG(RS1_D) << 32) | FREG(RS1_D + 1); const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs1 = float64_t{ rs1 }; const float64_t fs2 = float64_t{ rs2 }; if (set_fpr64(RD_D, f64_add(fs1, fs2).v)) { printf("FADDd %08x%08x + %08x%08x -> Exception\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } printf("FADDd %08x%08x + %08x%08x = %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2, FREG(RD_D), FREG(RD_D + 1)); break; } case FPOP_FADDX: fatalerror("Unimplemented FPop: FADDx\n"); break; case FPOP_FSUBS: { const uint32_t rs1 = FREG(RS1); const uint32_t rs2 = FREG(RS2); const float32_t fs1 = float32_t{ rs1 }; const float32_t fs2 = float32_t{ rs2 }; if (set_fpr32(RD, f32_sub(fs1, fs2).v)) { printf("FSUBs %08x - %08x -> Exception\n", rs1, rs2); return; } printf("FSUBs %08x - %08x = %08x\n", rs1, rs2, FDREG); break; } case FPOP_FSUBD: { const uint64_t rs1 = ((uint64_t)FREG(RS1_D) << 32) | FREG(RS1_D + 1); const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs1 = float64_t{ rs1 }; const float64_t fs2 = float64_t{ rs2 }; if (set_fpr64(RD_D, f64_sub(fs1, fs2).v)) { printf("FSUBd %08x%08x - %08x%08x -> Exception\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } printf("FSUBd %08x%08x - %08x%08x = %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2, FREG(RD_D), FREG(RD_D + 1)); break; } case FPOP_FSUBX: fatalerror("Unimplemented FPop: FSUBx\n"); break; case FPOP_FMULS: { const uint32_t rs1 = FREG(RS1); const uint32_t rs2 = FREG(RS2); const float32_t fs1 = float32_t{ rs1 }; const float32_t fs2 = float32_t{ rs2 }; if (set_fpr32(RD, f32_mul(fs1, fs2).v)) { printf("FMULs %08x * %08x -> Exception\n", rs1, rs2); return; } printf("FMULs %08x * %08x = %08x\n", rs1, rs2, FDREG); break; } case FPOP_FMULD: { const uint64_t rs1 = ((uint64_t)FREG(RS1_D) << 32) | FREG(RS1_D + 1); const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs1 = float64_t{ rs1 }; const float64_t fs2 = float64_t{ rs2 }; if (set_fpr64(RD_D, f64_mul(fs1, fs2).v)) { printf("FMULd %08x%08x * %08x%08x -> Exception\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } printf("FMULd %08x%08x * %08x%08x = %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2, FREG(RD_D), FREG(RD_D + 1)); break; } case FPOP_FMULX: fatalerror("Unimplemented FPop: FMULx\n"); break; case FPOP_FDIVS: { const uint32_t rs1 = FREG(RS1); const uint32_t rs2 = FREG(RS2); if (rs2 == 0) { check_fdiv_zero_exception(); printf("FDIVs %08x / %08x -> divide by zero\n", rs1, rs2); return; } const float32_t fs1 = float32_t{ rs1 }; const float32_t fs2 = float32_t{ rs2 }; if (set_fpr32(RD, f32_div(fs1, fs2).v)) { printf("FDIVs %08x / %08x -> Exception\n", rs1, rs2); return; } printf("FDIVs %08x / %08x = %08x\n", rs1, rs2, FDREG); break; } case FPOP_FDIVD: { const uint64_t rs1 = ((uint64_t)FREG(RS1_D) << 32) | FREG(RS1_D + 1); const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); if (rs2 == 0) { check_fdiv_zero_exception(); printf("FDIVd %08x%08x / %08x%08x -> divide by zero\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } const float64_t fs1 = float64_t{ rs1 }; const float64_t fs2 = float64_t{ rs2 }; if (set_fpr64(RD_D, f64_div(fs1, fs2).v)) { printf("FDIVd %08x%08x / %08x%08x -> Exception\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } printf("FDIVd %08x%08x / %08x%08x = %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2, FREG(RD_D), FREG(RD_D + 1)); break; } case FPOP_FDIVX: fatalerror("Unimplemented FPop: FDIVx\n"); break; case FPOP_FITOS: { const uint32_t rs2 = FREG(RS2); if (set_fpr32(RD, i32_to_f32(int32_t(rs2)).v)) { printf("FiTOs %08x -> Exception\n", rs2); return; } printf("FiTOs %08x -> %08x\n", rs2, FDREG); break; } case FPOP_FDTOS: { const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs2 = float64_t{ rs2 }; if (set_fpr32(RD, f64_to_f32(fs2).v)) { printf("FdTOs %08x%08x -> Exception\n", (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } printf("FdTOs %08x%08x -> %08x\n", (uint32_t)(rs2 >> 32), (uint32_t)rs2, FDREG); break; } case FPOP_FXTOS: fatalerror("Unimplemented FPop: FxTOs\n"); break; case FPOP_FITOD: { const uint32_t rs2 = FREG(RS2); if (set_fpr64(RD_D, i32_to_f64(int32_t(rs2)).v)) { printf("FiTOd %08x -> Exception\n", rs2); return; } printf("FiTOd %08x -> %08x%08x\n", rs2, FREG(RD_D), FREG(RD_D + 1)); break; } case FPOP_FSTOD: { const uint32_t rs2 = FREG(RS2); const float32_t fs = float32_t{ rs2 }; if (set_fpr64(RD_D, f32_to_f64(fs).v)) { printf("FsTOd %08x -> Exception\n", rs2); return; } printf("FsTOd %08x -> %08x%08x\n", rs2, FREG(RD_D), FREG(RD_D + 1)); break; } case FPOP_FXTOD: fatalerror("Unimplemented FPop: FxTOd\n"); break; case FPOP_FITOX: fatalerror("Unimplemented FPop: FiTOx\n"); break; case FPOP_FSTOX: fatalerror("Unimplemented FPop: FsTOx\n"); break; case FPOP_FDTOX: fatalerror("Unimplemented FPop: FdTOx\n"); break; case FPOP_FSTOI: { const uint32_t rs2 = FREG(RS2); const float32_t fs2 = float32_t{ rs2 }; if (set_fpr32(RD, f32_to_i32(fs2, softfloat_roundingMode, true))) { printf("FsTOi %08x -> Exception\n", rs2); return; } printf("FsTOi %08x -> %08x\n", rs2, FDREG); break; } case FPOP_FDTOI: { const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs2 = float64_t{ rs2 }; if (set_fpr32(RD, f64_to_i32(fs2, softfloat_roundingMode, true))) { printf("FdTOi %08x%08x -> Exception\n", (uint32_t)(rs2 >> 32), (uint32_t)rs2); return; } printf("FdTOi %08x%08x -> %08x\n", (uint32_t)(rs2 >> 32), (uint32_t)rs2, FDREG); break; } case FPOP_FXTOI: fatalerror("Unimplemented FPop: FxTOi\n"); break; case FPOP_FCMPS: { const uint32_t rs1 = FREG(RS1); const uint32_t rs2 = FREG(RS2); const float32_t fs1 = float32_t{ rs1 }; const float32_t fs2 = float32_t{ rs2 }; bool equal = f32_eq(fs1, fs2); if (softfloat_exceptionFlags & softfloat_flag_invalid) { printf("FCMPs %08x ? %08x\n", rs1, rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_UO; } else if (equal) { printf("FCMPs %08x = %08x\n", rs1, rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_EQ; } else if (f32_lt(fs1, fs2)) { printf("FCMPs %08x < %08x\n", rs1, rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_LT; } else { printf("FCMPs %08x > %08x\n", rs1, rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_GT; } break; } case FPOP_FCMPD: { const uint64_t rs1 = ((uint64_t)FREG(RS1_D) << 32) | FREG(RS1_D + 1); const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs1 = float64_t{ rs1 }; const float64_t fs2 = float64_t{ rs2 }; bool equal = f64_eq(fs1, fs2); if (softfloat_exceptionFlags & softfloat_flag_invalid) { printf("FCMPd %08x%08x ? %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_UO; } else if (equal) { printf("FCMPd %08x%08x = %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_EQ; } else if (f64_lt(fs1, fs2)) { printf("FCMPd %08x%08x < %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_LT; } else { printf("FCMPd %08x%08x > %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_GT; } break; } case FPOP_FCMPX: fatalerror("Unimplemented FPop: FCMPx\n"); break; case FPOP_FCMPES: fatalerror("Unimplemented FPop: FCMPEs\n"); break; case FPOP_FCMPED: { const uint64_t rs1 = ((uint64_t)FREG(RS1_D) << 32) | FREG(RS1_D + 1); const uint64_t rs2 = ((uint64_t)FREG(RS2_D) << 32) | FREG(RS2_D + 1); const float64_t fs1 = float64_t{ rs1 }; const float64_t fs2 = float64_t{ rs2 }; bool equal = f64_eq(fs1, fs2); if (softfloat_exceptionFlags & softfloat_flag_invalid) { printf("FCMPEd %08x%08x ? %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_UO; m_fsr |= FSR_CEXC_NVC; if (m_fsr & FSR_TEM_NVM) { m_fsr = (m_fsr & ~FSR_FTT_MASK) | FSR_FTT_IEEE; m_trap = 1; m_fp_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } m_fsr |= FSR_AEXC_NVA; break; } else if (equal) { printf("FCMPEd %08x%08x = %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_EQ; break; } else if (f64_lt(fs1, fs2)) { printf("FCMPEd %08x%08x < %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_LT; break; } else { printf("FCMPEd %08x%08x > %08x%08x\n", (uint32_t)(rs1 >> 32), (uint32_t)rs1, (uint32_t)(rs2 >> 32), (uint32_t)rs2); m_fsr = (m_fsr & ~FSR_FCC_MASK) | FSR_FCC_GT; break; } break; } case FPOP_FCMPEX: fatalerror("Unimplemented FPop: FCMPEx\n"); break; default: fatalerror("Unknown FPop: %03x\n", fpop); break; } PC = nPC; nPC = nPC + 4; } //------------------------------------------------- // execute_step - perform one step in execute // mode (versus error or reset modes) //------------------------------------------------- inline void sparcv7_device::execute_step() { /* The SPARC Instruction Manual: Version 8, page 156, "Appendix C - ISP Descriptions - C.5. Processor States and Instruction Dispatch" (SPARCv8.pdf, pg. 153) if (bp_reset_in = 1) then ( execute_mode <- 0; reset_mode <- 1; break { out of while (execute_mode = 1) loop } ) else if ((ET = 1) and ((bp_IRL = 15) or (bp_IRL > PIL))) then ( trap <- 1; interrupt_level <- bp_IRL ); next; if (trap = 1) then execute_trap; { See Section C.8 } if (execute_mode = 1) then ( { execute_trap may have set execute_mode to 0 } { the following code emulates the delayed nature of the write-state-register instructions. PSR <- PSR'; PSR' <- PSR''; PSR'' <- PSR'''; PSR''' <- PSR''''; ASR <- ASR'; ASR' <- ASR''; ASR'' <- ASR'''; ASR''' <- ASR''''; TBR <- TBR'; TBR' <- TBR''; TBR'' <- TBR'''; TBR''' <- TBR''''; WIM <- WIM'; WIM' <- WIM''; WIM'' <- WIM'''; WIM''' <- WIM''''; Y <- Y'; Y' <- Y''; Y'' <- Y'''; Y''' <- Y''''; next; addr_space := (if (S = 0) then 8 else 9); (instruction, MAE) <- memory_read(addr_space, PC); next; if ( (MAE = 1) and (annul = 0) ) then ( trap <- 1; instruction_access_exception <- 1 ) else ( if (annul = 0) then ( dispatch_instruction ; { See Section C.6 } next; if (FPop1 or FPop2) then ( complete_fp_execution { See Section C.7 } ) next; if ( (trap = 0) and not (CALL or RETT or JMPL or Bicc or FBfcc or CBccc or Ticc) ) then ( PC <- nPC; nPC <- nPC + 4 ) ) else { annul != 0 } ( annul <- 0; PC <- nPC; nPC <- nPC + 4 ) ) ) */ // write-state-register delay not yet implemented const uint32_t op = m_mmu->fetch_insn(m_s, PC >> 2); #if LOG_FCODES //if (m_log_fcodes) { log_fcodes(); } #endif if (m_no_annul) { if (MAE) { m_trap = 1; m_instruction_access_exception = 1; m_stashed_icount = m_icount; m_icount = 0; return; } dispatch_instruction(op); } else { m_no_annul = true; PC = nPC; nPC = nPC + 4; } } //------------------------------------------------- // reset_step - step one cycle in reset mode //------------------------------------------------- void sparcv7_device::reset_step() { /* The SPARC Instruction Manual: Version 8, page 156, "Appendix C - ISP Descriptions - C.5. Processor States and Instruction Dispatch" (SPARCv8.pdf, pg. 153) while (reset_mode = 1) ( if (bp_reset_in = 0) then ( reset_mode <- 0; execute_mode <- 1; trap <- 1; reset_trap <- 1; ) ); */ if (!m_bp_reset_in) { m_reset_mode = 0; m_execute_mode = 1; m_trap = 1; m_reset_trap = 1; m_stashed_icount = m_icount; m_icount = 0; } } //------------------------------------------------- // error_step - step one cycle in error mode //------------------------------------------------- void sparcv7_device::error_step() { /* The SPARC Instruction Manual: Version 8, page 157, "Appendix C - ISP Descriptions - C.5. Processor States and Instruction Dispatch" (SPARCv8.pdf, pg. 154) while (error_mode = 1) ( if (bp_reset_in = 1) then ( error_mode <- 0; reset_mode <- 1; pb_error <- 0 ) ); */ if (m_bp_reset_in) { m_error_mode = 0; m_reset_mode = 1; m_pb_error = 0; m_stashed_icount = m_icount; m_icount = 0; } } template void sparcv7_device::run_loop() { do { /*if (HOLD_BUS) { m_icount--; continue; }*/ if (CHECK_DEBUG) debugger_instruction_hook(PC); if (MODE == MODE_RESET) { reset_step(); } else if (MODE == MODE_ERROR) { error_step(); } else if (MODE == MODE_EXECUTE) { execute_step(); } if (CHECK_DEBUG) { for (int i = 0; i < 8; i++) { m_dbgregs[i] = *m_regs[8 + i]; m_dbgregs[8 + i] = *m_regs[16 + i]; m_dbgregs[16 + i] = *m_regs[24 + i]; } } --m_icount; } while (m_icount >= 0); } //------------------------------------------------- // execute_run - execute a timeslice's worth of // opcodes //------------------------------------------------- void sparcv7_device::execute_run() { bool debug = machine().debug_flags & DEBUG_FLAG_ENABLED; if (m_bp_reset_in) { m_execute_mode = 0; m_error_mode = 0; m_reset_mode = 1; m_stashed_icount = m_icount; m_icount = 0; return; } else if (m_et && (m_bp_irl == 15 || m_bp_irl > m_pil)) { m_trap = 1; m_interrupt_level = m_bp_irl; } do { if (m_trap) { execute_trap(); } if (debug) { if (m_reset_mode) run_loop(); else if (m_error_mode) run_loop(); else run_loop(); } else { if (m_reset_mode) run_loop(); else if (m_error_mode) run_loop(); else run_loop(); } if (m_stashed_icount >= 0) { m_icount = m_stashed_icount; m_stashed_icount = -1; } } while (m_icount >= 0); } //------------------------------------------------- // get_reg_r - get integer register value for // disassembler //------------------------------------------------- uint64_t sparcv7_device::get_reg_r(unsigned index) const { return REG(index & 31); } //------------------------------------------------- // get_reg_pc - get program counter value for // disassembler //------------------------------------------------- uint64_t sparcv7_device::get_translated_pc() const { // FIXME: how do we apply translation to the address so it's in the same space the disassembler sees? return m_pc; } //------------------------------------------------- // get_icc - get integer condition codes for // disassembler //------------------------------------------------- uint8_t sparcv7_device::get_icc() const { return (m_psr & PSR_ICC_MASK) >> PSR_ICC_SHIFT; } //------------------------------------------------- // get_icc - get extended integer condition codes // for disassembler //------------------------------------------------- uint8_t sparcv7_device::get_xcc() const { // not present before SPARCv9 return 0; } //------------------------------------------------- // get_icc - get extended integer condition codes // for disassembler //------------------------------------------------- uint8_t sparcv7_device::get_fcc(unsigned index) const { // only one fcc instance before SPARCv9 return (m_fsr >> 10) & 3; }