From cb88bab67ded0afb55cbf97ca7a7ca4d65d956fc Mon Sep 17 00:00:00 2001 From: joevt Date: Tue, 9 Apr 2024 00:57:51 -0700 Subject: [PATCH] ppcopcodes: Fixes for SPRs. - Rename DEC to DEC_S and add DEC_U. - MQ, RTCL_U, RTCU_U, and DEC_U should cause an illegal instruction program exception for non-MPC601 CPUs. The exception handler of classic Mac OS uses this to emulate the instruction. - For mtspr, the SPRs RTCL_U, RTCU_U, and DEC_U are treated as no-op on MPC601. - For debugging, use the supervisor instead of the user SPR number as the index for storing the values for RTC, TB, and DEC. - For debugging, RTC, TB, and DEC should be updated after each access. Previously, mfspr and mtspr would only update the half of RTC and TB that was being accessed instead of both halves. --- cpu/ppc/ppcemu.h | 13 ++++--- cpu/ppc/ppcexec.cpp | 25 ++++++++----- cpu/ppc/ppcopcodes.cpp | 83 +++++++++++++++++++++++++++++++++--------- 3 files changed, 88 insertions(+), 33 deletions(-) diff --git a/cpu/ppc/ppcemu.h b/cpu/ppc/ppcemu.h index 539a898..5d2168f 100644 --- a/cpu/ppc/ppcemu.h +++ b/cpu/ppc/ppcemu.h @@ -85,17 +85,18 @@ extern SetPRS ppc_state; /** symbolic names for frequently used SPRs */ enum SPR : int { - MQ = 0, + MQ = 0, // MQ (601) XER = 1, - RTCU_U = 4, // user RTCU - RTCL_U = 5, // user RTCL + RTCU_U = 4, // user mode RTCU (601) + RTCL_U = 5, // user mode RTCL (601) + DEC_U = 6, // user mode decrementer (601) LR = 8, CTR = 9, DSISR = 18, DAR = 19, - RTCU_S = 20, // supervisor RTCU - RTCL_S = 21, // supervisor RTCL - DEC = 22, // decrementer + RTCU_S = 20, // supervisor RTCU (601) + RTCL_S = 21, // supervisor RTCL (601) + DEC_S = 22, // supervisor decrementer SDR1 = 25, SRR0 = 26, SRR1 = 27, diff --git a/cpu/ppc/ppcexec.cpp b/cpu/ppc/ppcexec.cpp index f8a26a4..e1909a9 100644 --- a/cpu/ppc/ppcexec.cpp +++ b/cpu/ppc/ppcexec.cpp @@ -864,8 +864,8 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool include_601, /* MPC601 sets MSR[ME] bit during hard reset / Power-On */ ppc_state.msr = (MSR::ME + MSR::IP); } else { - ppc_state.msr = MSR::IP; - ppc_state.spr[SPR::DEC] = 0xFFFFFFFFUL; + ppc_state.msr = MSR::IP; + ppc_state.spr[SPR::DEC_S] = 0xFFFFFFFFUL; } ppc_mmu_init(); @@ -885,10 +885,10 @@ void print_fprs() { } static map SPRName2Num = { - {"XER", SPR::XER}, {"LR", SPR::LR}, {"CTR", SPR::CTR}, - {"DEC", SPR::DEC}, {"PVR", SPR::PVR}, {"SPRG0", SPR::SPRG0}, - {"SPRG1", SPR::SPRG1}, {"SPRG2", SPR::SPRG2}, {"SPRG3", SPR::SPRG3}, - {"SRR0", SPR::SRR0}, {"SRR1", SPR::SRR1}, {"IBAT0U", 528}, + {"XER", SPR::XER}, {"LR", SPR::LR}, {"CTR", SPR::CTR}, + {"DEC", SPR::DEC_S}, {"PVR", SPR::PVR}, {"SPRG0", SPR::SPRG0}, + {"SPRG1", SPR::SPRG1}, {"SPRG2", SPR::SPRG2}, {"SPRG3", SPR::SPRG3}, + {"SRR0", SPR::SRR0}, {"SRR1", SPR::SRR1}, {"IBAT0U", 528}, {"IBAT0L", 529}, {"IBAT1U", 530}, {"IBAT1L", 531}, {"IBAT2U", 532}, {"IBAT2L", 533}, {"IBAT3U", 534}, {"IBAT3L", 535}, {"DBAT0U", 536}, {"DBAT0L", 537}, @@ -897,9 +897,9 @@ static map SPRName2Num = { {"HID0", SPR::HID0}, {"HID1", SPR::HID1}, {"IABR", 1010}, {"DABR", 1013}, {"L2CR", 1017}, {"ICTC", 1019}, {"THRM1", 1020}, {"THRM2", 1021}, {"THRM3", 1022}, - {"PIR", 1023}, {"TBL", SPR::TBL_U}, {"TBU", SPR::TBU_U}, - {"SDR1", SPR::SDR1}, {"MQ", SPR::MQ}, {"RTCU", SPR::RTCU_U}, - {"RTCL", SPR::RTCL_U}, {"DSISR", SPR::DSISR}, {"DAR", SPR::DAR}, + {"PIR", 1023}, {"TBL", SPR::TBL_S}, {"TBU", SPR::TBU_S}, + {"SDR1", SPR::SDR1}, {"MQ", SPR::MQ}, {"RTCU", SPR::RTCU_S}, + {"RTCL", SPR::RTCL_S}, {"DSISR", SPR::DSISR}, {"DAR", SPR::DAR}, {"MMCR0", SPR::MMCR0}, {"PMC1", SPR::PMC1}, {"PMC2", SPR::PMC2}, {"SDA", SPR::SDA}, {"SIA", SPR::SIA}, {"MMCR1", SPR::MMCR1} }; @@ -974,6 +974,13 @@ uint64_t reg_op(string& reg_name, uint64_t val, bool is_write) { reg_num_str = reg_name_u.substr(3); reg_num = (unsigned)stoul(reg_num_str, NULL, 0); if (reg_num < 1024) { + switch (reg_num) { + case SPR::DEC_U : reg_num = SPR::DEC_S ; break; + case SPR::RTCL_U : reg_num = SPR::RTCL_S ; break; + case SPR::RTCU_U : reg_num = SPR::RTCU_S ; break; + case SPR::TBL_U : reg_num = SPR::TBL_S ; break; + case SPR::TBU_U : reg_num = SPR::TBU_S ; break; + } if (is_write) ppc_state.spr[reg_num] = (uint32_t)val; return ppc_state.spr[reg_num]; diff --git a/cpu/ppc/ppcopcodes.cpp b/cpu/ppc/ppcopcodes.cpp index 2830c07..4889f66 100644 --- a/cpu/ppc/ppcopcodes.cpp +++ b/cpu/ppc/ppcopcodes.cpp @@ -898,20 +898,42 @@ void dppc_interpreter::ppc_mfspr() { } switch (ref_spr) { + case SPR::MQ: + if (!is_601) { + ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP); + } + ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr]; + break; case SPR::RTCL_U: + if (!is_601) { + ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP); + } calc_rtcl_value(); - ppc_state.spr[SPR::RTCL_U] = rtc_lo & 0x3FFFFF80UL; + ppc_state.gpr[reg_d] = + ppc_state.spr[SPR::RTCL_S] = rtc_lo & 0x3FFFFF80UL; + ppc_state.spr[SPR::RTCU_S] = rtc_hi; break; case SPR::RTCU_U: + if (!is_601) { + ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP); + } calc_rtcl_value(); - ppc_state.spr[SPR::RTCU_U] = rtc_hi; + ppc_state.gpr[reg_d] = + ppc_state.spr[SPR::RTCU_S] = rtc_hi; + ppc_state.spr[SPR::RTCL_S] = rtc_lo; break; - case SPR::DEC: - ppc_state.spr[SPR::DEC] = calc_dec_value(); + case SPR::DEC_U: + if (!is_601) { + ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP); + } + // fallthrough + case SPR::DEC_S: + ppc_state.gpr[reg_d] = ppc_state.spr[SPR::DEC_S] = calc_dec_value(); break; + default: + // FIXME: Unknown SPR should be noop or illegal instruction. + ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr]; } - - ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr]; } void dppc_interpreter::ppc_mtspr() { @@ -927,38 +949,54 @@ void dppc_interpreter::ppc_mtspr() { } } - if (ref_spr == SPR::PVR || ( - ref_spr == SPR::MQ && !is_601 - )) { // prevent writes to the read-only registers - return; - } - uint32_t val = ppc_state.gpr[reg_d]; - ppc_state.spr[ref_spr] = val; switch (ref_spr) { + case SPR::MQ: + if (!is_601) { + ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP); + } + ppc_state.spr[ref_spr] = val; + break; + case SPR::RTCL_U: + case SPR::RTCU_U: + case SPR::DEC_U: + if (!is_601) { + ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP); + } + break; case SPR::XER: ppc_state.spr[ref_spr] = val & 0xe000ff7f; break; case SPR::SDR1: + ppc_state.spr[ref_spr] = val; mmu_pat_ctx_changed(); // adapt to SDR1 changes break; case SPR::RTCL_S: calc_rtcl_value(); - rtc_lo = val & 0x3FFFFF80UL; + ppc_state.spr[RTCL_S] = rtc_lo = val & 0x3FFFFF80UL; + ppc_state.spr[RTCU_S] = rtc_hi; break; case SPR::RTCU_S: calc_rtcl_value(); - rtc_hi = val; + ppc_state.spr[RTCL_S] = rtc_lo; + ppc_state.spr[RTCU_S] = rtc_hi = val; break; - case SPR::DEC: + case SPR::DEC_S: + ppc_state.spr[DEC_S] = val; update_decrementer(val); break; case SPR::TBL_S: update_timebase(0xFFFFFFFF00000000ULL, val); + ppc_state.spr[TBL_S] = val; + ppc_state.spr[TBU_S] = tbr_wr_value >> 32; break; case SPR::TBU_S: update_timebase(0x00000000FFFFFFFFULL, uint64_t(val) << 32); + ppc_state.spr[TBL_S] = (uint32_t)tbr_wr_value; + ppc_state.spr[TBU_S] = val; + break; + case SPR::PVR: break; case 528: case 529: @@ -968,6 +1006,7 @@ void dppc_interpreter::ppc_mtspr() { case 533: case 534: case 535: + ppc_state.spr[ref_spr] = val; ibat_update(ref_spr); break; case 536: @@ -978,7 +1017,11 @@ void dppc_interpreter::ppc_mtspr() { case 541: case 542: case 543: + ppc_state.spr[ref_spr] = val; dbat_update(ref_spr); + default: + // FIXME: Unknown SPR should be noop or illegal instruction. + ppc_state.spr[ref_spr] = val; } } @@ -990,10 +1033,14 @@ void dppc_interpreter::ppc_mftb() { switch (ref_spr) { case SPR::TBL_U: - ppc_state.gpr[reg_d] = uint32_t(tbr_value); + ppc_state.gpr[reg_d] = + ppc_state.spr[TBL_S] = uint32_t(tbr_value); + ppc_state.spr[TBU_S] = uint32_t(tbr_value >> 32); break; case SPR::TBU_U: - ppc_state.gpr[reg_d] = uint32_t(tbr_value >> 32); + ppc_state.gpr[reg_d] = + ppc_state.spr[TBU_S] = uint32_t(tbr_value >> 32); + ppc_state.spr[TBL_S] = uint32_t(tbr_value); break; default: ppc_exception_handler(Except_Type::EXC_PROGRAM, Exc_Cause::ILLEGAL_OP);