mirror of
https://github.com/dingusdev/dingusppc.git
synced 2024-12-23 06:29:38 +00:00
Implement MPC601 style RTC.
This commit is contained in:
parent
4525fd50cc
commit
1d86375061
@ -33,7 +33,8 @@ using namespace std;
|
||||
|
||||
#define MIN_TIMEOUT_NS 200
|
||||
|
||||
#define NS_PER_SEC 1E9
|
||||
#define NS_PER_SEC 1E9
|
||||
#define ONE_BILLION_NS 0x3B9ACA00UL
|
||||
|
||||
#define USECS_TO_NSECS(us) (us) * 1000
|
||||
|
||||
|
@ -84,22 +84,27 @@ extern SetPRS ppc_state;
|
||||
|
||||
/** symbolic names for frequently used SPRs */
|
||||
enum SPR : int {
|
||||
MQ = 0,
|
||||
XER = 1,
|
||||
LR = 8,
|
||||
CTR = 9,
|
||||
DSISR = 18,
|
||||
DAR = 19,
|
||||
DEC = 22,
|
||||
SDR1 = 25,
|
||||
SRR0 = 26,
|
||||
SRR1 = 27,
|
||||
PVR = 287
|
||||
MQ = 0,
|
||||
XER = 1,
|
||||
RTCU_U = 4, // user RTCU
|
||||
RTCL_U = 5, // user RTCL
|
||||
LR = 8,
|
||||
CTR = 9,
|
||||
DSISR = 18,
|
||||
DAR = 19,
|
||||
RTCU_S = 20, // supervisor RTCU
|
||||
RTCL_S = 21, // supervisor RTCL
|
||||
DEC = 22,
|
||||
SDR1 = 25,
|
||||
SRR0 = 26,
|
||||
SRR1 = 27,
|
||||
TBL_U = 268, // user mode TBL
|
||||
TBU_U = 269, // user mode TBU
|
||||
TBL_S = 284, // supervisor TBL
|
||||
TBU_S = 285, // supervisor TBU
|
||||
PVR = 287
|
||||
};
|
||||
|
||||
/** symbolic names for frequently used SPRs */
|
||||
enum TBR : int { TBL = 0, TBU = 1 };
|
||||
|
||||
/** symbolic names for common PPC processors */
|
||||
enum PPC_VER : uint32_t {
|
||||
MPC601 = 0x00010001,
|
||||
@ -154,6 +159,7 @@ extern uint64_t timebase_counter;
|
||||
extern uint64_t tbr_wr_timestamp;
|
||||
extern uint64_t tbr_wr_value;
|
||||
extern uint64_t tbr_freq_hz;
|
||||
extern uint32_t rtc_lo, rtc_hi;
|
||||
|
||||
// Additional steps to prevent overflow?
|
||||
extern int32_t add_result;
|
||||
@ -321,7 +327,7 @@ extern uint64_t exceptions_processed;
|
||||
#endif
|
||||
|
||||
// Function prototypes
|
||||
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version);
|
||||
extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq);
|
||||
extern void ppc_mmu_init(uint32_t cpu_version);
|
||||
|
||||
void ppc_illegalop();
|
||||
|
@ -65,11 +65,13 @@ uint64_t g_icycles;
|
||||
int icnt_factor;
|
||||
|
||||
/* global variables related to the timebase facility */
|
||||
uint64_t tbr_wr_timestamp; // stores vCPU virtual time of the last TBR write
|
||||
uint64_t tbr_wr_timestamp; // stores vCPU virtual time of the last TBR/RTC write
|
||||
uint64_t tbr_wr_value; // last value written to the TBR
|
||||
uint64_t tbr_freq_hz; // TBR driving frequency in Hz
|
||||
uint64_t tbr_freq_hz; // TBR/RTC driving frequency in Hz
|
||||
uint64_t timebase_counter; // internal timebase counter
|
||||
uint32_t decr; // current value of PPC DEC register
|
||||
uint32_t rtc_lo; // MPC601 RTC lower, counts nanoseconds
|
||||
uint32_t rtc_hi; // MPC601 RTC upper, counts seconds
|
||||
|
||||
#ifdef CPU_PROFILING
|
||||
|
||||
@ -734,13 +736,12 @@ void initialize_ppc_opcode_tables() {
|
||||
}
|
||||
}
|
||||
|
||||
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version) {
|
||||
void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, uint64_t tb_freq)
|
||||
{
|
||||
int i;
|
||||
|
||||
mem_ctrl_instance = mem_ctrl;
|
||||
|
||||
//test_timebase_update();
|
||||
|
||||
initialize_ppc_opcode_tables();
|
||||
|
||||
// initialize emulator timers
|
||||
@ -752,7 +753,7 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version) {
|
||||
icnt_factor = 4;
|
||||
tbr_wr_timestamp = 0;
|
||||
tbr_wr_value = 0;
|
||||
tbr_freq_hz = 16705000; // FIXME: this should be set properly during machine initialization
|
||||
tbr_freq_hz = tb_freq;
|
||||
|
||||
exec_flags = 0;
|
||||
|
||||
|
@ -847,6 +847,20 @@ void dppc_interpreter::ppc_mtmsr() {
|
||||
mmu_change_mode();
|
||||
}
|
||||
|
||||
static inline uint64_t calc_rtcl_value()
|
||||
{
|
||||
uint64_t diff = get_virt_time_ns() - tbr_wr_timestamp;
|
||||
uint64_t rtc_inc = diff * tbr_freq_hz / NS_PER_SEC;
|
||||
uint64_t rtc_l = rtc_lo + (rtc_inc << 7);
|
||||
if (rtc_l >= ONE_BILLION_NS) { // check RTCL overflow
|
||||
rtc_hi += rtc_l / ONE_BILLION_NS;
|
||||
rtc_lo = rtc_l % ONE_BILLION_NS;
|
||||
tbr_wr_timestamp = get_virt_time_ns();
|
||||
rtc_l = rtc_lo;
|
||||
}
|
||||
return rtc_l & 0x3FFFFF80UL;
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_mfspr() {
|
||||
uint32_t ref_spr = (((ppc_cur_instruction >> 11) & 31) << 5) | ((ppc_cur_instruction >> 16) & 31);
|
||||
|
||||
@ -855,8 +869,17 @@ void dppc_interpreter::ppc_mfspr() {
|
||||
num_supervisor_instrs++;
|
||||
}
|
||||
#endif
|
||||
reg_d = (ppc_cur_instruction >> 21) & 31;
|
||||
ppc_state.gpr[reg_d] = ppc_state.spr[ref_spr];
|
||||
|
||||
switch (ref_spr) {
|
||||
case SPR::RTCL_U:
|
||||
ppc_state.spr[SPR::RTCL_U] = calc_rtcl_value();
|
||||
break;
|
||||
case SPR::RTCU_U:
|
||||
ppc_state.spr[SPR::RTCL_U] = rtc_hi;
|
||||
break;
|
||||
}
|
||||
|
||||
ppc_state.gpr[(ppc_cur_instruction >> 21) & 31] = ppc_state.spr[ref_spr];
|
||||
}
|
||||
|
||||
static inline uint64_t calc_tbr_value()
|
||||
@ -871,7 +894,6 @@ 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();
|
||||
LOG_F(9, "New TBR value = 0x%llu", tbr_wr_value);
|
||||
}
|
||||
|
||||
void dppc_interpreter::ppc_mtspr() {
|
||||
@ -884,20 +906,30 @@ void dppc_interpreter::ppc_mtspr() {
|
||||
}
|
||||
#endif
|
||||
|
||||
if (ref_spr != 287) {
|
||||
ppc_state.spr[ref_spr] = ppc_state.gpr[reg_s];
|
||||
uint32_t val = ppc_state.gpr[reg_s];
|
||||
|
||||
if (ref_spr != SPR::PVR) { // prevent writes to the read-only PVR
|
||||
ppc_state.spr[ref_spr] = val;
|
||||
}
|
||||
|
||||
if (ref_spr == SPR::SDR1) {
|
||||
if (ref_spr == SPR::SDR1) { // adapt to SDR1 changes
|
||||
mmu_pat_ctx_changed();
|
||||
}
|
||||
|
||||
switch (ref_spr) {
|
||||
case 284:
|
||||
update_timebase(0xFFFFFFFF00000000ULL, ppc_state.gpr[reg_s]);
|
||||
case SPR::RTCL_S:
|
||||
rtc_lo = val & 0x3FFFFF80UL;
|
||||
tbr_wr_timestamp = get_virt_time_ns();
|
||||
break;
|
||||
case 285:
|
||||
update_timebase(0x00000000FFFFFFFFULL, (uint64_t)(ppc_state.gpr[reg_s]) << 32);
|
||||
case SPR::RTCU_S:
|
||||
rtc_hi = val;
|
||||
tbr_wr_timestamp = get_virt_time_ns();
|
||||
break;
|
||||
case SPR::TBL_S:
|
||||
update_timebase(0xFFFFFFFF00000000ULL, val);
|
||||
break;
|
||||
case SPR::TBU_S:
|
||||
update_timebase(0x00000000FFFFFFFFULL, (uint64_t)(val) << 32);
|
||||
break;
|
||||
case 528:
|
||||
case 529:
|
||||
@ -928,10 +960,10 @@ void dppc_interpreter::ppc_mftb() {
|
||||
uint64_t tbr_value = calc_tbr_value();
|
||||
|
||||
switch (ref_spr) {
|
||||
case 268:
|
||||
case SPR::TBL_U:
|
||||
ppc_state.gpr[reg_d] = tbr_value & 0xFFFFFFFFUL;
|
||||
break;
|
||||
case 269:
|
||||
case SPR::TBU_U:
|
||||
ppc_state.gpr[reg_d] = (tbr_value >> 32) & 0xFFFFFFFFUL;
|
||||
break;
|
||||
default:
|
||||
|
@ -101,7 +101,7 @@ int create_gossamer(std::string& id) {
|
||||
18, dynamic_cast<PCIDevice*>(gMachineObj->get_comp_by_name("ATIRage")));
|
||||
|
||||
/* Init virtual CPU and request MPC750 CPU aka G3 */
|
||||
ppc_cpu_init(grackle_obj, PPC_VER::MPC750);
|
||||
ppc_cpu_init(grackle_obj, PPC_VER::MPC750, 16705000ULL);
|
||||
|
||||
// post-initialize all devices
|
||||
if (gMachineObj->postinit_devices()) {
|
||||
|
@ -89,7 +89,7 @@ int create_pdm(std::string& id) {
|
||||
gMachineObj->add_component("SCSI0", new ScsiBus);
|
||||
|
||||
/* Init virtual CPU and request MPC601 */
|
||||
ppc_cpu_init(hmc_obj, PPC_VER::MPC601);
|
||||
ppc_cpu_init(hmc_obj, PPC_VER::MPC601, 7812500ULL);
|
||||
|
||||
// post-initialize all devices
|
||||
if (gMachineObj->postinit_devices()) {
|
||||
|
Loading…
Reference in New Issue
Block a user