Skip to content

Commit 60d4750

Browse files
joevtmaximumspatium
authored andcommitted
ppcopcodes: Decrementer fixes.
- Always add a tick to the timeout calculation. - Make sure value of decrementer is < 0 for timed decrementer exception. - Don't recalculate decrementer write time stamp in the case of an immediate decrementer exception.
1 parent 213dd4c commit 60d4750

1 file changed

Lines changed: 35 additions & 17 deletions

File tree

cpu/ppc/ppcopcodes.cpp

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -881,15 +881,9 @@ static void update_timebase(uint64_t mask, uint64_t new_val)
881881

882882
static uint32_t decrementer_timer_id = 0;
883883

884-
static void update_decrementer(uint32_t oldval, uint32_t newval);
884+
static void update_decrementer(bool update_time_stamp, uint32_t oldval, uint32_t newval);
885885

886886
static void trigger_decrementer_exception() {
887-
decrementer_timer_id = 0;
888-
889-
uint32_t new_val = calc_dec_value();
890-
891-
update_decrementer(new_val, new_val);
892-
893887
if (ppc_state.msr & MSR::EE) {
894888
dec_exception_pending = false;
895889
//LOG_F(WARNING, "decrementer exception triggered");
@@ -901,9 +895,27 @@ static void trigger_decrementer_exception() {
901895
}
902896
}
903897

904-
static void update_decrementer(uint32_t oldval, uint32_t newval) {
905-
dec_wr_value = newval;
906-
dec_wr_timestamp = get_virt_time_ns();
898+
static void trigger_timed_decrementer_exception() {
899+
decrementer_timer_id = 0;
900+
uint32_t new_val = calc_dec_value();
901+
if (new_val >= 0 && new_val != uint32_t(-1)) {
902+
new_val = -1;
903+
}
904+
update_decrementer(true, new_val, new_val);
905+
trigger_decrementer_exception();
906+
}
907+
908+
static void trigger_immediate_decrementer_exception() {
909+
decrementer_timer_id = 0;
910+
update_decrementer(false, ppc_state.spr[SPR::DEC_S], ppc_state.spr[SPR::DEC_S]);
911+
trigger_decrementer_exception();
912+
}
913+
914+
static void update_decrementer(bool update_time_stamp, uint32_t oldval, uint32_t newval) {
915+
if (update_time_stamp) {
916+
dec_wr_value = newval;
917+
dec_wr_timestamp = get_virt_time_ns();
918+
}
907919

908920
dec_exception_pending = false;
909921

@@ -913,22 +925,28 @@ static void update_decrementer(uint32_t oldval, uint32_t newval) {
913925

914926
if (bit_changed(oldval, newval, 31) && bit_set(newval, 31)) {
915927
decrementer_timer_id = TimerManager::get_instance()->add_immediate_timer(
916-
trigger_decrementer_exception
928+
trigger_immediate_decrementer_exception
917929
);
918930
return;
919931
}
920932

921-
// add one tick if newval is zero
922-
if (!newval)
923-
newval += (is_601) ? (1 << 7) : 1;
933+
// A count of zero should timeout after one tick so add one to the count
934+
// but make sure the count doesn't overflow.
935+
if (is_601) {
936+
if (newval < uint32_t(-0x80))
937+
newval += 0x80;
938+
} else {
939+
if (newval < uint32_t(-1))
940+
newval += 1;
941+
}
924942

925943
uint64_t time_out;
926944
uint32_t time_out_lo;
927945
_u32xu64(newval, tbr_period_ns, time_out, time_out_lo);
928946
//LOG_F(WARNING, "new decrementer value: 0x%08X, interrupt after %llu ns", newval, time_out);
929947
decrementer_timer_id = TimerManager::get_instance()->add_oneshot_timer(
930948
time_out,
931-
trigger_decrementer_exception
949+
trigger_timed_decrementer_exception
932950
);
933951
}
934952

@@ -1058,8 +1076,8 @@ void dppc_interpreter::ppc_mtspr(uint32_t opcode) {
10581076
case SPR::DEC_S:
10591077
if (is_601)
10601078
val &= ~0x7F;
1061-
update_decrementer(ppc_state.spr[DEC_S], val);
1062-
ppc_state.spr[DEC_S] = val;
1079+
update_decrementer(true, calc_dec_value(), val);
1080+
ppc_state.spr[SPR::DEC_S] = val;
10631081
break;
10641082
case SPR::TBL_S:
10651083
update_timebase(0xFFFFFFFF00000000ULL, val);

0 commit comments

Comments
 (0)