From fe5659fbe36388e66bc3253067e9c23b4660e928 Mon Sep 17 00:00:00 2001 From: Mihai Parparita Date: Wed, 12 Aug 2026 23:54:54 -0700 Subject: [PATCH] ppc: Model configurable CPU frequencies Virtual time previously advanced by a fixed 16 ns per interpreted instruction, making every CPU behave like the legacy 62.5 MHz model regardless of the selected machine. G3 guests also received ad hoc HID1 values from individual machine implementations, while Gazelle supported several frequencies that could not share that logic. Require each machine to provide its hardware-appropriate bus, core, and timebase clocks through one `PPC_CPU_Config` configuration object. We can then derive things from it (e.g. supported MPC603ev and MPC750 HID1 PLL encodings). We can also populate the `include_601` global based on `version` instead of a separate flag. To model time with more flexibility, we now use 60.4 fixed-point nanoseconds for instruction timing. This lets us have a broad set of frequencies and still support running the CPU for ~35 years of virtual time before it overflows. This results in two emulation accuracy improvements: - Tiger successfully gets to the Finder. The virtual time we were exposing to the guest was advancing too quickly, preventing loginwindow from connecting before WindowServer's internal five-second no-client timer expired. The server then exited normally, loginwindow aborted when it could not establish its CoreGraphics connection, and we remained stuck at the last textual output from a verbose boot ("Waiting for IFC"). - Animations are now more accurate (e.g. Mac OS X dock icon bouncing is no longer too fast on a G3) --- benchmark/bench1.cpp | 13 +++- core/hostevents_sdl.cpp | 12 +-- cpu/ppc/ppcemu.h | 17 +++-- cpu/ppc/ppcexec.cpp | 138 +++++++++++++++++++++++------------ machines/machinealchemy.cpp | 15 ++-- machines/machinebondi.cpp | 13 ++-- machines/machinecatalyst.cpp | 23 ++++-- machines/machinefactory.cpp | 1 + machines/machinegazelle.cpp | 29 +++----- machines/machinegossamer.cpp | 13 ++-- machines/machinepdm.cpp | 15 +++- machines/machinepippin.cpp | 7 +- machines/machinetnt.cpp | 34 +++++++-- machines/machineyosemite.cpp | 13 ++-- 14 files changed, 228 insertions(+), 115 deletions(-) diff --git a/benchmark/bench1.cpp b/benchmark/bench1.cpp index d170de13cc..f9d45a5541 100644 --- a/benchmark/bench1.cpp +++ b/benchmark/bench1.cpp @@ -69,9 +69,16 @@ int main(int argc, char** argv) { return -1; } - constexpr uint64_t tbr_freq = 16705000; - - ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, tbr_freq); + constexpr uint64_t bus_freq = 66'820'000ULL; + constexpr uint64_t tbr_freq = bus_freq / 4; + constexpr uint64_t core_freq = bus_freq * 7 / 2; // 233.87 MHz (CPU PLL ratio of 3.5) + + ppc_cpu_init(grackle_obj, { + .version = PPC_VER::MPC750, + .timebase_freq_hz = tbr_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); /* load executable code into RAM at address 0 */ for (i = 0; i < sizeof(cs_code) / sizeof(cs_code[0]); i++) { diff --git a/core/hostevents_sdl.cpp b/core/hostevents_sdl.cpp index 9110929b04..d06d5ce438 100644 --- a/core/hostevents_sdl.cpp +++ b/core/hostevents_sdl.cpp @@ -124,23 +124,23 @@ void EventManager::poll_events() { } return; } - // Control-Alt-Shift-+: speed up icnt_factor + // Control-Alt-Shift-+: increase instruction period if (event.key.keysym.sym == SDLK_EQUALS && (event.key.keysym.mod & KMOD_ALL) == (KMOD_LCTRL | KMOD_LALT | KMOD_LSHIFT) ) { if (event.type == SDL_KEYUP) { - int icnt_counter_val = increment_icnt_factor(); - LOG_F(INFO, "Incremented icnt_factor: %d", icnt_counter_val); + uint64_t instruction_period = increment_instruction_period(); + LOG_F(INFO, "Incremented instruction period: %d", (int)instruction_period); } return; } - // Control-Alt-Shift--: slow down icnt_factor + // Control-Alt-Shift--: decrease instruction period if (event.key.keysym.sym == SDLK_MINUS && (event.key.keysym.mod & KMOD_ALL) == (KMOD_LCTRL | KMOD_LALT | KMOD_LSHIFT) ) { if (event.type == SDL_KEYUP) { - int icnt_counter_val = decrement_icnt_factor(); - LOG_F(INFO, "Decremented icnt_factor: %d", icnt_counter_val); + uint64_t instruction_period = decrement_instruction_period(); + LOG_F(INFO, "Decremented instruction period: %d", (int)instruction_period); } return; } diff --git a/cpu/ppc/ppcemu.h b/cpu/ppc/ppcemu.h index af75394e51..05ee983c4b 100644 --- a/cpu/ppc/ppcemu.h +++ b/cpu/ppc/ppcemu.h @@ -470,8 +470,15 @@ typedef enum { // Placeholder value for cases where we don't have a currently-executing instruction. constexpr uint32_t NO_OPCODE = 0; +struct PPC_CPU_Config { + uint32_t version; + uint64_t timebase_freq_hz; + uint64_t bus_freq_hz; + uint64_t core_freq_hz; +}; + // Function prototypes -extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool include_601, uint64_t tb_freq); +extern void ppc_cpu_init(MemCtrlBase* mem_ctrl, const PPC_CPU_Config& config); extern void ppc_mmu_init(); void ppc_illegalop(uint32_t opcode); @@ -720,10 +727,10 @@ extern void ppc_change_endian(bool newLE); uint64_t get_reg(std::string reg_name); /* get content of the register reg_name */ void set_reg(std::string reg_name, uint64_t val); /* set reg_name to val */ -/* icnt_factor control */ -extern int increment_icnt_factor(); -extern int decrement_icnt_factor(); -extern int get_icnt_factor(); +/* instruction period control */ +extern uint64_t increment_instruction_period(); +extern uint64_t decrement_instruction_period(); +extern uint64_t get_instruction_period(); /* toggle_g_realtime */ extern bool toggle_g_realtime(); diff --git a/cpu/ppc/ppcexec.cpp b/cpu/ppc/ppcexec.cpp index e089312d80..bc3e1eef21 100644 --- a/cpu/ppc/ppcexec.cpp +++ b/cpu/ppc/ppcexec.cpp @@ -26,6 +26,7 @@ along with this program. If not, see . #include "ppcdisasm.h" #include +#include #include #include #include @@ -132,8 +133,18 @@ bool dec_exception_pending = false; /* variables related to virtual time */ bool g_realtime = false; uint64_t g_nanoseconds_base; -uint64_t g_icycles; -int icnt_factor; +// Fixed-point nanoseconds, with four fractional bits. This provides enough +// precision for the supported CPU clocks and over 36 years before overflow. +using FixedNanoseconds = uint64_t; +static constexpr int VIRT_TIME_FRAC_BITS = 4; +static FixedNanoseconds g_virt_time; +static FixedNanoseconds g_instruction_period; + +static constexpr FixedNanoseconds instruction_period_for_frequency(uint64_t core_freq_hz) +{ + return (((uint64_t)NS_PER_SEC << VIRT_TIME_FRAC_BITS) + core_freq_hz / 2) / + core_freq_hz; +} /* global variables related to the timebase facility */ uint64_t tbr_wr_timestamp; // stores vCPU virtual time of the last TBR write @@ -356,7 +367,7 @@ uint64_t get_virt_time_ns() if (g_realtime) { return cpu_now_ns() - g_nanoseconds_base; } else { - return g_icycles << icnt_factor; + return g_virt_time >> VIRT_TIME_FRAC_BITS; } } @@ -365,7 +376,7 @@ void set_virt_time_ns(uint64_t time_now) if (g_realtime) { g_nanoseconds_base = cpu_now_ns() - time_now - 5000; } else { - g_icycles = time_now >> icnt_factor; + g_virt_time = time_now << VIRT_TIME_FRAC_BITS; } uint64_t time_new = get_virt_time_ns(); if (g_realtime && time_new > time_now) { @@ -375,16 +386,16 @@ void set_virt_time_ns(uint64_t time_now) LOG_F(INFO, "time before: %lld after: %lld change: %lld", time_now, time_new, time_new - time_now); } -static uint64_t process_events() +static FixedNanoseconds process_events() { exec_timer = false; uint64_t slice_ns = TimerManager::get_instance()->process_timers(); if (slice_ns == 0) { - // execute 25.000 cycles + // execute 25,000 instructions // if there are no pending timers - return g_icycles + 25000; + return g_virt_time + 25'000 * g_instruction_period; } - return g_icycles + (slice_ns >> icnt_factor) + 1; + return g_virt_time + (slice_ns << VIRT_TIME_FRAC_BITS); } static void force_cycle_counter_reload() @@ -393,29 +404,29 @@ static void force_cycle_counter_reload() exec_timer = true; } -int increment_icnt_factor() +uint64_t increment_instruction_period() { uint64_t time_now = get_virt_time_ns(); - icnt_factor += 1; + g_instruction_period += 1; set_virt_time_ns(time_now); force_cycle_counter_reload(); - return icnt_factor; + return g_instruction_period; } -int decrement_icnt_factor() +uint64_t decrement_instruction_period() { - if (icnt_factor > 0) { + if (g_instruction_period > 0) { uint64_t time_now = get_virt_time_ns(); - icnt_factor -= 1; + g_instruction_period -= 1; set_virt_time_ns(time_now); force_cycle_counter_reload(); } - return icnt_factor; + return g_instruction_period; } -int get_icnt_factor() +uint64_t get_instruction_period() { - return icnt_factor; + return g_instruction_period; } bool toggle_g_realtime() @@ -437,7 +448,10 @@ typedef enum { template static void ppc_exec_inner(uint32_t start_addr, uint32_t size) { - uint64_t max_cycles = 0; + FixedNanoseconds event_deadline = 0; + // Keep these hot-loop values in registers. + FixedNanoseconds virt_time = g_virt_time; + FixedNanoseconds instruction_period = g_instruction_period; uint32_t page_start, eb_start, eb_end = 0; uint32_t opcode; PPCOpcode* opcode_grabber = ppc_opcode_grabber; @@ -460,21 +474,29 @@ static void ppc_exec_inner(uint32_t start_addr, uint32_t size) opcode = ppc_read_instruction(pc_real); ppc_main_opcode(opcode_grabber, opcode); - if (g_icycles++ >= max_cycles || exec_timer) [[unlikely]] - max_cycles = process_events(); + virt_time += instruction_period; + g_virt_time = virt_time; + if (virt_time >= event_deadline || exec_timer) [[unlikely]] { + event_deadline = process_events(); + virt_time = g_virt_time; + instruction_period = g_instruction_period; + } if (exec_flags) { if ((exec_flags & EXEF_SLEEP) && !(exec_flags & EXEF_EXCEPTION)) [[unlikely]] { while (power_on && (exec_flags & EXEF_SLEEP)) { - max_cycles = process_events(); + event_deadline = process_events(); + virt_time = g_virt_time; + instruction_period = g_instruction_period; if (!(exec_flags & EXEF_SLEEP)) { break; } - if (max_cycles > g_icycles) { - g_icycles = max_cycles; + if (event_deadline > virt_time) { + virt_time = event_deadline; } else { - g_icycles++; + virt_time += instruction_period; } + g_virt_time = virt_time; } } if (exec_flags & EXEF_OPC_DECODER) [[unlikely]] { @@ -552,7 +574,7 @@ void ppc_exec_single() uint8_t* pc_real = mmu_translate_imem(ppc_state.pc ATPCP); // &pcp uint32_t opcode = ppc_read_instruction(pc_real); ppc_main_opcode(ppc_opcode_grabber, opcode); - g_icycles++; + g_virt_time += g_instruction_period; process_events(); if (exec_flags) { @@ -993,21 +1015,59 @@ void initialize_ppc_opcode_table() { } } -void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool do_include_601, uint64_t tb_freq) +static uint32_t get_cpu_pll_value(const PPC_CPU_Config& config) { + switch (config.version) { + case PPC_VER::MPC603EV: + if (config.core_freq_hz * 2 == config.bus_freq_hz * 9) + return 7; // 4.5:1 ratio + if (config.core_freq_hz == config.bus_freq_hz * 5) + return 11; // 5:1 ratio + if (config.core_freq_hz * 2 == config.bus_freq_hz * 11) + return 9; // 5.5:1 ratio + break; + case PPC_VER::MPC750: + if (config.core_freq_hz * 2 == config.bus_freq_hz * 7) + return 14; // 3.5:1 ratio + if (config.core_freq_hz == config.bus_freq_hz * 6) + return 13; // 6:1 ratio + if (config.core_freq_hz == config.bus_freq_hz * 8) + return 12; // 8:1 ratio + break; + default: + break; + } + + ABORT_F("Unsupported CPU/bus frequency combination: %" PRIu64 "/%" PRIu64 " Hz", + config.core_freq_hz, config.bus_freq_hz); +} + +void ppc_cpu_init(MemCtrlBase* mem_ctrl, const PPC_CPU_Config& config) +{ + if (!config.timebase_freq_hz || !config.bus_freq_hz || !config.core_freq_hz) + ABORT_F("CPU clock frequencies must be non-zero"); + mem_ctrl_instance = mem_ctrl; int_pin = false; std::memset(&ppc_state, 0, sizeof(ppc_state)); set_host_rounding_mode(0); - ppc_state.spr[SPR::PVR] = cpu_version; - is_601 = (cpu_version >> 16) == 1; - include_601 = !is_601 & do_include_601; + ppc_state.spr[SPR::PVR] = config.version; + switch (config.version) { + case PPC_VER::MPC603EV: + case PPC_VER::MPC750: + ppc_state.spr[SPR::HID1] = get_cpu_pll_value(config) << 28; + break; + default: + break; + } + is_601 = (config.version >> 16) == 1; + include_601 = is_601; ppc_pow_mode = PPCPowMode::None; ppc_pow_hid0_mask = 0; - switch (cpu_version) { + switch (config.version) { case PPC_VER::MPC603: case PPC_VER::MPC603E: case PPC_VER::MPC603EV: @@ -1037,25 +1097,13 @@ void ppc_cpu_init(MemCtrlBase* mem_ctrl, uint32_t cpu_version, bool do_include_6 mach_timebase_info(&timebase_info); #endif g_nanoseconds_base = cpu_now_ns(); - g_icycles = 0; - -// // // PDM cpu clock calculated at 0x403036CC in r3 -// icnt_factor = 11; // 1 instruction = 2048 ns = 0.488 MHz // 00068034 = 0.426036 MHz = 2347.219 ns // floppy doesn't work -// icnt_factor = 10; // 1 instruction = 1024 ns = 0.977 MHz // 000D204C = 0.860236 MHz = 1162.471 ns // [0..10] MHz = invalid clock for PDM gestalt calculation -// icnt_factor = 9; // 1 instruction = 512 ns = 1.953 MHz // 001A6081 = 1.728641 MHz = 578.489 ns // [0..10] MHz = invalid clock for PDM gestalt calculation -// icnt_factor = 8; // 1 instruction = 256 ns = 3.906 MHz // 0034E477 = 3.466359 MHz = 288.487 ns // [0..10] MHz = invalid clock for PDM gestalt calculation -// icnt_factor = 7; // 1 instruction = 128 ns = 7.813 MHz // 0069E54C = 6.939980 MHz = 144.092 ns // [0..10] MHz = invalid clock for PDM gestalt calculation -// icnt_factor = 6; // 1 instruction = 64 ns = 15.625 MHz // 00D3E6F5 = 13.887221 MHz = 72.008 ns // (10..60] = 50, (60..73] = 66, (73..100] = 80 MHz -// icnt_factor = 5; // 1 instruction = 32 ns = 31.250 MHz // 01A7B672 = 27.768434 MHz = 36.012 ns // - icnt_factor = 4; // 1 instruction = 16 ns = 62.500 MHz // 034F0F0F = 55.512847 MHz = 18.013 ns // 6100/60 in Apple System Profiler -// icnt_factor = 3; // 1 instruction = 8 ns = 125.000 MHz // 069E1E1E = 111.025694 MHz = 9.006 ns // (100...) MHz = invalid clock for PDM gestalt calculation -// icnt_factor = 2; // 1 instruction = 4 ns = 250.000 MHz // 0D3C3C3C = 222.051388 MHz = 4.503 ns // (100...) MHz = invalid clock for PDM gestalt calculation -// icnt_factor = 1; // 1 instruction = 2 ns = 500.000 MHz // 1A611A7B = 442.571387 MHz = 2.259 ns // (100...) MHz = invalid clock for PDM gestalt calculation -// icnt_factor = 0; // 1 instruction = 1 ns = 1500.000 MHz // 3465B2D9 = 879.080153 MHz = 1.137 ns // (100...) MHz = invalid clock for PDM gestalt calculation + g_virt_time = 0; + g_instruction_period = instruction_period_for_frequency(config.core_freq_hz); tbr_wr_timestamp = 0; rtc_timestamp = 0; tbr_wr_value = 0; + uint64_t tb_freq = config.timebase_freq_hz; if (is_601) tb_freq <<= 7; tbr_freq_shift = 0; diff --git a/machines/machinealchemy.cpp b/machines/machinealchemy.cpp index d7c0b6d84d..36ad0844bc 100644 --- a/machines/machinealchemy.cpp +++ b/machines/machinealchemy.cpp @@ -99,16 +99,19 @@ int MachineAlchemy::initialize(const std::string &id) { psx_obj->map_phys_ram(); // configure CPU clocks - uint64_t bus_freq = 40000000ULL; + uint64_t bus_freq = 40'000'000ULL; uint64_t timebase_freq = bus_freq / 4; + uint64_t core_freq = 180'000'000ULL; psx_obj->set_bus_speed(PSX_BUS_SPEED_40); - // init virtual CPU and request MPC603ev - ppc_cpu_init(psx_obj, PPC_VER::MPC603E, false, timebase_freq); - - // CPU frequency is hardcoded to 225 MHz for now - //ppc_state.spr[SPR::HID1] = get_cpu_pll_value(225000000) << 28; + // init virtual CPU and request MPC603e + ppc_cpu_init(psx_obj, { + .version = PPC_VER::MPC603E, + .timebase_freq_hz = timebase_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); return 0; } diff --git a/machines/machinebondi.cpp b/machines/machinebondi.cpp index bac4f5c1f0..65117d70d4 100644 --- a/machines/machinebondi.cpp +++ b/machines/machinebondi.cpp @@ -88,14 +88,17 @@ int MachineBondi::initialize(const std::string &id) { setup_ram_slot("RAM_DIMM_2", 0x51, bank_2_size); // configure CPU clocks - uint64_t bus_freq = 66820000ULL; + uint64_t bus_freq = 66'820'000ULL; uint64_t timebase_freq = bus_freq / 4; + uint64_t core_freq = bus_freq * 7 / 2; // 233.87 MHz (CPU PLL ratio of 3.5) // initialize virtual CPU and request MPC750 CPU aka G3 - ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, timebase_freq); - - // set CPU PLL ratio to 3.5 - ppc_state.spr[SPR::HID1] = 0xE << 28; + ppc_cpu_init(grackle_obj, { + .version = PPC_VER::MPC750, + .timebase_freq_hz = timebase_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); return 0; } diff --git a/machines/machinecatalyst.cpp b/machines/machinecatalyst.cpp index 67fbc09933..86786cee15 100644 --- a/machines/machinecatalyst.cpp +++ b/machines/machinecatalyst.cpp @@ -96,19 +96,30 @@ int MachineCatalyst::initialize(const std::string &id) { std::string cpu = GET_STR_PROP("cpu"); if (cpu == "601") { + uint64_t bus_freq = 37'500'000ULL; + uint64_t core_freq = 75'000'000ULL; + // init virtual CPU and request MPC601 - ppc_cpu_init(platinum_obj, PPC_VER::MPC601, true, 7833600ULL); + ppc_cpu_init(platinum_obj, { + .version = PPC_VER::MPC601, + .timebase_freq_hz = 7'833'600ULL, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); } else if (cpu == "750") { // configure CPU clocks - uint64_t bus_freq = 50000000ULL; + uint64_t bus_freq = 50'000'000ULL; uint64_t timebase_freq = bus_freq / 4; + uint64_t core_freq = bus_freq * 8; // 400 MHz (matches G3 upgrade cards; CPU PLL ratio to 8) // initialize virtual CPU and request MPC750 CPU aka G3 - ppc_cpu_init(platinum_obj, PPC_VER::MPC750, false, timebase_freq); - - // set CPU PLL ratio to 3.5 - ppc_state.spr[SPR::HID1] = 0xE << 28; + ppc_cpu_init(platinum_obj, { + .version = PPC_VER::MPC750, + .timebase_freq_hz = timebase_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); } return 0; diff --git a/machines/machinefactory.cpp b/machines/machinefactory.cpp index b3dbfb7f98..acc92ea287 100644 --- a/machines/machinefactory.cpp +++ b/machines/machinefactory.cpp @@ -95,6 +95,7 @@ static const map PropHelp = { {"serial_backend", "specifies the backend for the serial port"}, {"emmo", "enables/disables factory HW tests during startup"}, {"cpu", "specifies CPU"}, + {"cpu_freq", "specifies CPU frequency in MHz"}, {"adb_devices", "specifies which ADB device(s) to attach"}, {"pds", "specify device for the processsor direct slot"}, }; diff --git a/machines/machinegazelle.cpp b/machines/machinegazelle.cpp index 894a9349a2..c7e19be27c 100644 --- a/machines/machinegazelle.cpp +++ b/machines/machinegazelle.cpp @@ -46,20 +46,6 @@ static std::vector psx_irq_map = { {"pci_F1", DEV_FUN(0x12,0), IntSrc::PCI_F}, }; -// TODO: move it to /cpu/ppc -int get_cpu_pll_value(const uint64_t cpu_freq_hz) { - switch (cpu_freq_hz / 1000000) { - case 225: - return 7; // 4.5:1 ratio - case 250: - return 11; // 5:1 ratio - case 275: - return 9; // 5.5:1 ratio - default: - ABORT_F("Unsupported CPU frequency %llu Hz", cpu_freq_hz); - } -} - class MachineGazelle : public Machine { public: static std::unique_ptr create5500() { @@ -126,14 +112,17 @@ int MachineGazelle::initialize(const std::string &id) { psx_obj->set_bus_speed(PSX_BUS_SPEED_50); // configure CPU clocks - uint64_t bus_freq = 50000000ULL; + uint64_t bus_freq = 50'000'000ULL; uint64_t timebase_freq = bus_freq / 4; + uint64_t core_freq = GET_INT_PROP("cpu_freq") * 1'000'000ULL; // 225, 250, or 275 MHz // init virtual CPU and request MPC603ev - ppc_cpu_init(psx_obj, PPC_VER::MPC603EV, false, timebase_freq); - - // CPU frequency is hardcoded to 225 MHz for now - ppc_state.spr[SPR::HID1] = get_cpu_pll_value(225000000) << 28; + ppc_cpu_init(psx_obj, { + .version = PPC_VER::MPC603EV, + .timebase_freq_hz = timebase_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); return 0; } @@ -147,6 +136,8 @@ static const PropMap pm6500_settings = { new IntProperty( 0, std::vector({0, 4, 8, 16, 32, 64}))}, {"emmo", new BinProperty(0)}, + {"cpu_freq", + new IntProperty(225, std::vector({225, 250, 275}))}, {"hdd_config", new StrProperty("Ide0:0")}, {"pci_F1", diff --git a/machines/machinegossamer.cpp b/machines/machinegossamer.cpp index 143362de86..4f746551dd 100644 --- a/machines/machinegossamer.cpp +++ b/machines/machinegossamer.cpp @@ -188,14 +188,17 @@ int MachineGossamer::initialize(const std::string &id) { i2c_bus->register_device(0x53, perch_id); // configure CPU clocks - uint64_t bus_freq = 66820000ULL; + uint64_t bus_freq = 66'820'000ULL; uint64_t timebase_freq = bus_freq / 4; + uint64_t core_freq = bus_freq * 7 / 2; // 233.87 MHz (CPU PLL ratio of 3.5) // initialize virtual CPU and request MPC750 CPU aka G3 - ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, timebase_freq); - - // set CPU PLL ratio to 3.5 - ppc_state.spr[SPR::HID1] = 0xE << 28; + ppc_cpu_init(grackle_obj, { + .version = PPC_VER::MPC750, + .timebase_freq_hz = timebase_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); return 0; } diff --git a/machines/machinepdm.cpp b/machines/machinepdm.cpp index 443a7752d0..2f94d48881 100644 --- a/machines/machinepdm.cpp +++ b/machines/machinepdm.cpp @@ -87,16 +87,24 @@ int MachinePdm::initialize(const std::string &id) { LOG_F(INFO, "Building machine PDM..."); uint16_t machine_id; + uint64_t bus_freq; + uint64_t core_freq; // get raw pointer to HMC object HMC* hmc_obj = dynamic_cast(gMachineObj->get_comp_by_name("HMC")); if (id == "pm6100") { machine_id = 0x3010; + bus_freq = 30'000'000ULL; + core_freq = 60'000'000ULL; } else if (id == "pm7100") { machine_id = 0x3012; + bus_freq = 33'000'000ULL; + core_freq = 66'000'000ULL; } else if (id == "pm8100") { machine_id = 0x3013; + bus_freq = 40'000'000ULL; + core_freq = 80'000'000ULL; } else { LOG_F(ERROR, "Unknown machine ID: %s!", id.c_str()); return -1; @@ -136,7 +144,12 @@ int MachinePdm::initialize(const std::string &id) { setup_pds(); // Init virtual CPU and request MPC601 - ppc_cpu_init(hmc_obj, PPC_VER::MPC601, true, 7833600ULL); + ppc_cpu_init(hmc_obj, { + .version = PPC_VER::MPC601, + .timebase_freq_hz = 7'833'600ULL, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); return 0; } diff --git a/machines/machinepippin.cpp b/machines/machinepippin.cpp index 8ced27d21b..839b959b8e 100644 --- a/machines/machinepippin.cpp +++ b/machines/machinepippin.cpp @@ -74,7 +74,12 @@ int MachinePippin::initialize(const std::string &id) { aspen_obj->insert_ram_dimm(3, GET_INT_PROP("rambank4_size")); // RAM expansion slot // init virtual CPU - ppc_cpu_init(aspen_obj, PPC_VER::MPC603, false, 16500000ULL); + ppc_cpu_init(aspen_obj, { + .version = PPC_VER::MPC603, + .timebase_freq_hz = 16'500'000ULL, + .bus_freq_hz = 33'000'000ULL, + .core_freq_hz = 66'000'000ULL, + }); return 0; } diff --git a/machines/machinetnt.cpp b/machines/machinetnt.cpp index 349ce232e5..f23adc020d 100644 --- a/machines/machinetnt.cpp +++ b/machines/machinetnt.cpp @@ -136,21 +136,39 @@ int MachineTnt::initialize(const std::string &id) { // init virtual CPU std::string cpu = GET_STR_PROP("cpu"); if (cpu == "604e") - ppc_cpu_init(memctrl_obj, PPC_VER::MPC604E, false, 12500000ULL); + ppc_cpu_init(memctrl_obj, { + .version = PPC_VER::MPC604E, + .timebase_freq_hz = 12'500'000ULL, + .bus_freq_hz = 50'000'000ULL, + .core_freq_hz = 200'000'000ULL, // 200 MHz + }); else if (cpu == "604") - ppc_cpu_init(memctrl_obj, PPC_VER::MPC604, false, 12500000ULL); + ppc_cpu_init(memctrl_obj, { + .version = PPC_VER::MPC604, + .timebase_freq_hz = 12'500'000ULL, + .bus_freq_hz = 50'000'000ULL, + .core_freq_hz = 150'000'000ULL, // 150 MHz + }); else if (cpu == "601") - ppc_cpu_init(memctrl_obj, PPC_VER::MPC601, true, 7833600ULL); + ppc_cpu_init(memctrl_obj, { + .version = PPC_VER::MPC601, + .timebase_freq_hz = 7'833'600ULL, + .bus_freq_hz = 50'000'000ULL, + .core_freq_hz = 100'000'000ULL, // 100 MHz + }); else if (cpu == "750") { // configure CPU clocks - uint64_t bus_freq = 50000000ULL; + uint64_t bus_freq = 50'000'000ULL; uint64_t timebase_freq = bus_freq / 4; + uint64_t core_freq = bus_freq * 6; // 300 MHz (matches G3 upgrade cards; CPU PLL ratio of 6) // initialize virtual CPU and request MPC750 CPU aka G3 - ppc_cpu_init(memctrl_obj, PPC_VER::MPC750, false, timebase_freq); - - // set CPU PLL ratio to 3.5 - ppc_state.spr[SPR::HID1] = 0xE << 28; + ppc_cpu_init(memctrl_obj, { + .version = PPC_VER::MPC750, + .timebase_freq_hz = timebase_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); } return 0; diff --git a/machines/machineyosemite.cpp b/machines/machineyosemite.cpp index 34a619d517..e88305bf80 100644 --- a/machines/machineyosemite.cpp +++ b/machines/machineyosemite.cpp @@ -100,14 +100,17 @@ int MachineYosemite::initialize(const std::string &id) { setup_ram_slot("RAM_DIMM_4", 0x53, GET_INT_PROP("rambank4_size")); // configure CPU clocks - uint64_t bus_freq = 66820000ULL; + uint64_t bus_freq = 100'000'000ULL; uint64_t timebase_freq = bus_freq / 4; + uint64_t core_freq = bus_freq * 7 / 2; // 350 MHz (CPU PLL ratio of 3.5) // initialize virtual CPU and request MPC750 CPU aka G3 - ppc_cpu_init(grackle_obj, PPC_VER::MPC750, false, timebase_freq); - - // set CPU PLL ratio to 3.5 - ppc_state.spr[SPR::HID1] = 0xE << 28; + ppc_cpu_init(grackle_obj, { + .version = PPC_VER::MPC750, + .timebase_freq_hz = timebase_freq, + .bus_freq_hz = bus_freq, + .core_freq_hz = core_freq, + }); return 0; }