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.
This commit is contained in:
joevt 2024-04-09 00:57:51 -07:00 committed by dingusdev
parent 67a5c39b1c
commit cb88bab67d
3 changed files with 88 additions and 33 deletions

View File

@ -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,

View File

@ -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<string, int> 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<string, int> 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];

View File

@ -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);