Skip to content

Commit 0c05d4f

Browse files
joevtdingusdev
authored andcommitted
ppcopcodes: Add emulation of THRMn registers.
Required for booting Mac OS 9.2.2 on iMac.
1 parent 529fff7 commit 0c05d4f

4 files changed

Lines changed: 93 additions & 3 deletions

File tree

‎cpu/ppc/ppcemu.h‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,9 @@ enum SPR : int {
126126
SDA = 959,
127127
HID0 = 1008,
128128
HID1 = 1009,
129+
THRM1 = 1020,
130+
THRM2 = 1021,
131+
THRM3 = 1022,
129132
};
130133

131134
/** symbolic names for common PPC processors */
@@ -378,6 +381,7 @@ void p_power_off(const char* file, int line, Po_Cause new_reason);
378381

379382
extern bool int_pin;
380383
extern bool dec_exception_pending;
384+
extern bool thrm_exception_pending;
381385

382386
extern bool is_601; // For PowerPC 601 Emulation
383387
extern bool include_601; // For non-PowerPC 601 emulation with 601 extras

‎cpu/ppc/ppcexceptions.cpp‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ void ppc_exception_handler(Except_Type exception_type, uint32_t srr1_bits) {
6363

6464
case Except_Type::EXC_EXT_INT:
6565
case Except_Type::EXC_DECR:
66+
case Except_Type::EXC_THRM_MGMT_INT:
6667
if (exec_flags & ~EXEF_OPC_DECODER) {
6768
ppc_state.spr[SPR::SRR0] = ppc_next_instruction_address;
6869
} else {
@@ -171,6 +172,10 @@ void ppc_exception_handler(Except_Type exception_type, uint32_t srr1_bits) {
171172
exc_descriptor = "Trace exception occurred";
172173
break;
173174

175+
case Except_Type::EXC_THRM_MGMT_INT:
176+
exc_descriptor = "Thermal management interrupt exception occurred";
177+
break;
178+
174179
default:
175180
char buffer[100];
176181
snprintf(buffer, sizeof(buffer), "Exception 0x%X occurred", int(exception_type));

‎cpu/ppc/ppcexec.cpp‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ unsigned exec_flags; // execution control flags
128128
volatile bool exec_timer;
129129
bool int_pin = false; // interrupt request pin state: true - asserted
130130
bool dec_exception_pending = false;
131+
bool thrm_exception_pending = false;
131132

132133
/* variables related to virtual time */
133134
bool g_realtime = false;
@@ -1110,7 +1111,7 @@ static map<string, int> SPRName2Num = {
11101111
{"DBAT2L", 541}, {"DBAT3U", 542}, {"DBAT3L", 543},
11111112
{"HID0", SPR::HID0}, {"HID1", SPR::HID1}, {"IABR", 1010},
11121113
{"DABR", 1013}, {"L2CR", 1017}, {"ICTC", 1019},
1113-
{"THRM1", 1020}, {"THRM2", 1021}, {"THRM3", 1022},
1114+
{"THRM1", SPR::THRM1}, {"THRM2", SPR::THRM2}, {"THRM3", SPR::THRM3},
11141115
{"PIR", 1023}, {"TBL", SPR::TBL_S}, {"TBU", SPR::TBU_S},
11151116
{"SDR1", SPR::SDR1}, {"MQ", SPR::MQ}, {"RTCU", SPR::RTCU_S},
11161117
{"RTCL", SPR::RTCL_S}, {"DSISR", SPR::DSISR}, {"DAR", SPR::DAR},

‎cpu/ppc/ppcopcodes.cpp‎

Lines changed: 82 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,9 @@ along with this program. If not, see <https://www.gnu.org/licenses/>.
3131
#include <cinttypes>
3232
#include <vector>
3333

34+
static int junction_temperature = 24;
35+
static TimerInfo thermal_timer;
36+
3437
//Extract the registers desired and the values of the registers.
3538

3639
// Affects CR Field 0 - For integer operations
@@ -820,6 +823,9 @@ void dppc_interpreter::ppc_mtmsr(uint32_t opcode) {
820823
dec_exception_pending = false;
821824
//LOG_F(WARNING, "MTMSR: decrementer exception triggered");
822825
ppc_exception_handler(Except_Type::EXC_DECR, 0);
826+
} else if ((ppc_state.msr & MSR::EE) && thrm_exception_pending) {
827+
thrm_exception_pending = false;
828+
ppc_exception_handler(Except_Type::EXC_THRM_MGMT_INT, 0);
823829
} else if (ppc_state.msr & MSR::POW) [[unlikely]] {
824830
bool enter_sleep = false;
825831

@@ -980,6 +986,48 @@ static void update_decrementer(bool update_time_stamp, uint32_t oldval, uint32_t
980986
);
981987
}
982988

989+
static void update_thermal(uint64_t, uint64_t) {
990+
thermal_timer.active = 0;
991+
992+
uint32_t val = ppc_state.spr[SPR::THRM3];
993+
auto sampled_interval_timer_value = (val >> 1) & 0x1FFF;
994+
auto enabled = val & 1;
995+
996+
bool interrupt = false;
997+
for (int the_reg = 0; the_reg < 2; the_reg++) {
998+
val = ppc_state.spr[SPR::THRM1 + the_reg];
999+
uint32_t t_interrupt; // = (val >> 31) & 1;
1000+
uint32_t t_int_valid; // = (val >> 30) & 1;
1001+
uint32_t threshold = (val >> 23) & 0x7F;
1002+
uint32_t t_int_direction = (val >> 2) & 1;
1003+
uint32_t t_int_enable = (val >> 1) & 1;
1004+
uint32_t valid = (val >> 0) & 1;
1005+
1006+
if (!valid) {
1007+
t_interrupt = 0;
1008+
t_int_valid = 0;
1009+
} else {
1010+
t_interrupt = t_int_direction ?
1011+
junction_temperature < threshold
1012+
:
1013+
junction_temperature > threshold;
1014+
t_int_valid = 1;
1015+
interrupt |= t_int_enable && t_interrupt;
1016+
}
1017+
ppc_state.spr[SPR::THRM1 + the_reg] = (t_interrupt << 31) | (t_int_valid << 30) | (val & 0x3FFFFFFF);
1018+
}
1019+
1020+
if (interrupt && enabled) {
1021+
if (ppc_state.msr & MSR::EE) {
1022+
thrm_exception_pending = false;
1023+
ppc_exception_handler(Except_Type::EXC_THRM_MGMT_INT, 0);
1024+
}
1025+
else {
1026+
thrm_exception_pending = true;
1027+
}
1028+
}
1029+
}
1030+
9831031
void dppc_interpreter::ppc_mfspr(uint32_t opcode) {
9841032
ppc_grab_dab(opcode);
9851033
uint32_t ref_spr = (reg_b << 5) | reg_a;
@@ -1052,6 +1100,15 @@ void dppc_interpreter::ppc_mfspr(uint32_t opcode) {
10521100
ppc_state.spr[TBL_S] = uint32_t(tbr_value);
10531101
break;
10541102
}
1103+
case SPR::THRM1:
1104+
case SPR::THRM2: {
1105+
uint32_t val = ppc_state.spr[ref_spr];
1106+
ppc_state.gpr[reg_d] = val;
1107+
break;
1108+
}
1109+
case SPR::THRM3:
1110+
ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr];
1111+
break;
10551112
default:
10561113
// FIXME: Unknown SPR should be noop or illegal instruction.
10571114
ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr];
@@ -1157,6 +1214,25 @@ void dppc_interpreter::ppc_mtspr(uint32_t opcode) {
11571214
ppc_change_endian((val & 0x10000000) != 0);
11581215
}
11591216
break;
1217+
case SPR::THRM1:
1218+
case SPR::THRM2: {
1219+
ppc_state.spr[ref_spr] = val & 0x3F800007;
1220+
1221+
if (thermal_timer.active)
1222+
TimerManager::get_instance()->cancel_timer(thermal_timer);
1223+
TimerManager::get_instance()->add_oneshot_timer(thermal_timer, USECS_TO_NSECS(20), update_thermal);
1224+
break;
1225+
}
1226+
case SPR::THRM3: {
1227+
ppc_state.spr[ref_spr] = val & 0x00003FFF;
1228+
ppc_state.spr[SPR::THRM1] &= ~(3<<30);
1229+
ppc_state.spr[SPR::THRM2] &= ~(3<<30);
1230+
1231+
if (thermal_timer.active)
1232+
TimerManager::get_instance()->cancel_timer(thermal_timer);
1233+
TimerManager::get_instance()->add_oneshot_timer(thermal_timer, USECS_TO_NSECS(20), update_thermal);
1234+
break;
1235+
}
11601236
default:
11611237
// FIXME: Unknown SPR should be noop or illegal instruction.
11621238
ppc_state.spr[ref_spr] = val;
@@ -1518,14 +1594,18 @@ void dppc_interpreter::ppc_rfi(uint32_t opcode) {
15181594
ppc_msr_did_change(ppc_state.msr, new_msr_val);
15191595

15201596
// setting MSR[EE] may enable pending exceptions
1521-
if ((ppc_state.msr & MSR::EE) && (int_pin || dec_exception_pending)) {
1597+
if ((ppc_state.msr & MSR::EE) && (int_pin || dec_exception_pending || thrm_exception_pending)) {
15221598
uint32_t save_srr0 = ppc_state.spr[SPR::SRR0] & ~3UL;
15231599
if (int_pin) // check for pending external exceptions (higher priority)
15241600
ppc_exception_handler(Except_Type::EXC_EXT_INT, 0);
1525-
else {
1601+
else if (dec_exception_pending) {
15261602
dec_exception_pending = false;
15271603
ppc_exception_handler(Except_Type::EXC_DECR, 0);
15281604
}
1605+
else if (thrm_exception_pending) {
1606+
thrm_exception_pending = false;
1607+
ppc_exception_handler(Except_Type::EXC_THRM_MGMT_INT, 0);
1608+
}
15291609
ppc_state.spr[SPR::SRR0] = save_srr0;
15301610
return;
15311611
}

0 commit comments

Comments
 (0)