diff --git a/cpu/ppc/ppcemu.h b/cpu/ppc/ppcemu.h index a99275b..72cb444 100644 --- a/cpu/ppc/ppcemu.h +++ b/cpu/ppc/ppcemu.h @@ -94,7 +94,7 @@ enum SPR : int { DAR = 19, RTCU_S = 20, // supervisor RTCU RTCL_S = 21, // supervisor RTCL - DEC = 22, + DEC = 22, // decrementer SDR1 = 25, SRR0 = 26, SRR1 = 27, @@ -160,8 +160,10 @@ extern uint32_t opcode_value; // used for interpreting opcodes extern uint64_t timebase_counter; extern uint64_t tbr_wr_timestamp; +extern uint64_t dec_wr_timestamp; extern uint64_t rtc_timestamp; extern uint64_t tbr_wr_value; +extern uint32_t dec_wr_value; extern uint32_t tbr_freq_ghz; extern uint32_t rtc_lo, rtc_hi; diff --git a/cpu/ppc/ppcexec.cpp b/cpu/ppc/ppcexec.cpp index 08ebb45..9d7971a 100644 --- a/cpu/ppc/ppcexec.cpp +++ b/cpu/ppc/ppcexec.cpp @@ -71,7 +71,8 @@ uint64_t rtc_timestamp; // stores vCPU virtual time of the last RTC write uint64_t tbr_wr_value; // last value written to the TBR uint32_t tbr_freq_ghz; // TBR/RTC driving frequency in GHz expressed as a 32 bit fraction less than 1.0. uint64_t timebase_counter; // internal timebase counter -uint32_t decr; // current value of PPC DEC register +uint64_t dec_wr_timestamp; // stores vCPU virtual time of the last DEC write +uint32_t dec_wr_value; // last value written to the DEC register uint32_t rtc_lo; // MPC601 RTC lower, counts nanoseconds uint32_t rtc_hi; // MPC601 RTC upper, counts seconds @@ -771,7 +772,7 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq) exec_flags = 0; timebase_counter = 0; - decr = 0; + dec_wr_value = 0; /* zero all GPRs as prescribed for MPC601 */ /* For later PPC CPUs, GPR content is undefined */ diff --git a/cpu/ppc/ppcopcodes.cpp b/cpu/ppc/ppcopcodes.cpp index 895bc23..73004d6 100644 --- a/cpu/ppc/ppcopcodes.cpp +++ b/cpu/ppc/ppcopcodes.cpp @@ -870,6 +870,35 @@ static inline void calc_rtcl_value() rtc_timestamp = new_ts; } +static inline uint64_t calc_tbr_value() +{ + uint64_t tbr_inc; + uint32_t tbr_inc_lo; + uint64_t diff = get_virt_time_ns() - tbr_wr_timestamp; + _u32xu64(tbr_freq_ghz, diff, tbr_inc, tbr_inc_lo); + return (tbr_wr_value + tbr_inc); +} + +static inline uint32_t calc_dec_value() { + uint64_t dec_adj; + uint32_t dec_adj_lo; + uint64_t diff = get_virt_time_ns() - dec_wr_timestamp; + _u32xu64(tbr_freq_ghz, diff, dec_adj, dec_adj_lo); + return (dec_wr_value - dec_adj); +} + +static void update_timebase(uint64_t mask, uint64_t new_val) +{ + uint64_t tbr_value = calc_tbr_value(); + tbr_wr_value = (tbr_value & mask) | new_val; + tbr_wr_timestamp = get_virt_time_ns(); +} + +static void update_decrementer(uint32_t val) { + dec_wr_value = val; + dec_wr_timestamp = get_virt_time_ns(); +} + void dppc_interpreter::ppc_mfspr() { uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31); @@ -888,25 +917,14 @@ void dppc_interpreter::ppc_mfspr() { calc_rtcl_value(); ppc_state.spr[SPR::RTCU_U] = rtc_hi; break; + case SPR::DEC: + ppc_state.spr[SPR::DEC] = calc_dec_value(); + break; } ppc_state.gpr[(ppc_cur_instruction >> 21) & 31] = ppc_state.spr[ref_spr]; } -static inline uint64_t calc_tbr_value() -{ - uint64_t diff = get_virt_time_ns() - tbr_wr_timestamp; - uint64_t tbr_inc; uint32_t tbr_inc_lo; _u32xu64(tbr_freq_ghz, diff, tbr_inc, tbr_inc_lo); - return (tbr_wr_value + tbr_inc); -} - -static void update_timebase(uint64_t mask, uint64_t new_val) -{ - uint64_t tbr_value = calc_tbr_value(); - tbr_wr_value = (tbr_value & mask) | new_val; - tbr_wr_timestamp = get_virt_time_ns(); -} - void dppc_interpreter::ppc_mtspr() { uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31); reg_s = (ppc_cur_instruction >> 21) & 31; @@ -936,6 +954,9 @@ void dppc_interpreter::ppc_mtspr() { calc_rtcl_value(); rtc_hi = val; break; + case SPR::DEC: + update_decrementer(val); + break; case SPR::TBL_S: update_timebase(0xFFFFFFFF00000000ULL, val); break;