@@ -881,15 +881,9 @@ static void update_timebase(uint64_t mask, uint64_t new_val)
881881
882882static 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
886886static 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