Followup to #186 - select `MSR[POW]` handling mode once during CPU
initialization instead of treating every processor as `HID0`-gated.
We match documented (and QEMU) behavior:
- 603/7xx CPUs require `HID0` doze, nap, or sleep bits before we enter
the emulator sleep loop
- 604-family CPUs use just for `POW` directly
- 601 does not support `POW` at all
xnu-123.5 and similar vintage kernls enter the PowerPC 750 powersave mode
when the scheduler has no runnable work. `machine_idle_ppc` selects HID0
doze or nap, records a nap timestamp, then executes mtmsr with MSR[POW]
set. It stays in a loop there (in case the power saving had no effect
immediately) - it expects execution to resume at the `machine_idle_ret`
label (when an interrupt occurs), which ends up returning from the
function.
We model this state explicitly:
- When mtmsr sets MSR[POW] with the HID0 doze/nap/sleep flags, set a new
EXEF_SLEEP flag.
- When EXEF_SLEEP is set, keep processing host events without advancing
the guest PC.
Towards getting the 10.0 public beta to boot (#154) - before this we
would enter the DPPC debugger shortly after this instruction (due to a
bogus MSR LE flag eventually being set). Now we make more progress and
end up in a "Still waiting for root device" loop (presumably something
at the ATA layer)
===============
Rairii changes:
memctrlbase: The memory controller may have an endian swap mode.
mpc106: Only mmio regions need endian swap and only if PCIR_LE_MODE is
set.
videoctrl: Add framebuffer_in_main_memory flag. Use needs_swap_endian.
debugger: Fix disasm for little endian mode.
ppcmmu: little endian mode requires munging of the data and instruction
addresses, affecting the 3 least significant bits.
ppcexec: pc_real is always recalculated during little endian mode
because instructions are swapped every 8 bytes (due to the address
munging).
==============
joevt changes:
Add SUPPORTS_MEMORY_CTRL_ENDIAN_MODE for memory controller.
Use cmake -DDPPC_SUPPORT_PPC_LE=ON to enable.
Add SUPPORTS_PPC_LITTLE_ENDIAN_MODE for CPU.
Use cmake -DDPPC_SUPPORT_MEM_LE=ON to enable.
Add ppc_change_endian to handle endian switch for MPC601 (uses HID0
instead of MSR) and later PPC CPUs.
Add is_LE flag to ppc_state to indicate little endian mode for all
processor versions.
Create separate ppc_exec_inner template functions for little and big
endian.
Switch endian mode in the ppc_exec outer functions when power_off_reason
is po_endian_switch.
Since MPC601 doesn't have ILE and LE bits, remove that code for that
CPU.
videoctrl: Uses templated methods. See next commit for a fix that this
change introduces.
Note: all timings are relative to virtual time which is 16 ns per instruction. 8 ns per instruction is too fast for the PDM 'mach' gestalt calculation in firmware.
- For MPC601, bit 7 of RTC and DEC changes at 7.8336 MHz so bit 0 would change at 1.0027008 GHz. Therefore multiply time base frequency by 128. For MPC601, DEC now changes at 1.0027008 GHz instead of 7.8336 MHz.
- Add tbr_freq_shift for cases where time base frequency exceeds 1GHz.
- Change calc_rtcl_value to use time base frequency. For MPC601, RTC now changes at 1.0027008 GHz instead 1GHz.
- For MPC601, the 7 least significant bits of DEC are not implemented so make them not getable or setable.
In #135 we switched from a static OpcodeGrabber table to a
curOpcodeGrabber pointer in ppc_main_opcode. This results in an extra
indirection (as far as generated assembly having an additional load),
which reduces execution speed.
Switch to making the opcode grabber into a parameter to
ppc_main_opcode, and make ppc_exec_inner keep it up to date (via an
EXEF_OPCODE exception flag).
Also fixes FPU instructions in ppctests - we now need to set the FP
MSR bit when initializing the CPU.
Rather than running them normally, they should trigger a "no FPU"
exception. This appears to be required to allow correct graphical
rendering under Mac OS X - the FP bit cleared via mtmsr and rfi
instructions and something else appears to be relying on the exception
to be thrown.
Implemented by maintaining a parallel version of the OpcodeGrabber
table (OpcodeGrabberNoFPU) which contains alternate implementations
for all the floating point instructions. We switch the table whenever
the MSR value changes. This should minimize the overhead of doing
these checks.
Some of these accept a 4th register "C" so they exist in the opcode table 32 times.
Some of these don't accept a 4th register "C" so they exist in the opcode table once.
Instead of a primary opcode lookup table with 64 entries and a few
smaller tables with 4-2048 entries, use a single 64 * 2048 (128K)
entry table to dispatch opcodes.
Helps with performance, since we avoid the function call overhead for
some frequently-used instructions (e.g. branch, integer, floating point).
Saves ~2 seconds from the time to Welcome to Macintosh (same measurement
methodology as #125)
Secondarily also makes opcode registration/decoding a bit more uniform,
and scannable, since it's now all in initialize_ppc_opcode_table.
Replace it wth an explicit opcode parameter that is passed around. That
is both slightly easier to reason about (to trace where it comes from)
and slightly faster, since it can be read from a register.
On my machine takes booting to "Welcome to Macintosh" being output in
a verbose boot of Mac OS X 10.2.8 from 31.8s to 30.6s (average of 5
runs, measured using deterministic mode and looking at when execution
reaches PC 0x90004a88).
ppc_opcode16 and other functions are only needed in the implementation in
ppcexec.cpp, they don't need to be in the header.
fp_return_double and fp_return_uint64 have no uses (as of 2141a72b87)
can can thus be removed altogether.
Similarly ppc_fpu_off has no uses (as of bb3f4e596e)
and can be removed.
Adds support for a --deterministic command-line option that makes
repeated runs the same:
- Keyboard and mouse input is ignored
- The sound server does a periodic pull from the DMA channel (so that
it gets drained), but only does so via a periodic timer (instead of
being driven by a cubeb callback, which could arrive at different
times)
- Disk image writes are disabled (reads of a modified area still
work via an in-memory copy)
- NVRAM writes are disabled
- The current time that ViaCuda initializes the guest OS is always the
same.
This makes execution exactly the same each time, which should
make debugging of more subtle issues easier.
To validate that the deterministic mode is working, I've added a
periodic log of the current "time" (measured in cycle count), PC
and opcode. When comparing two runs with --log-no-uptime, the generated
log files are identical.
There's no reason for it to be a global, we always set it and use it
in instruction implementations, and we never read it directly.
Perhaps the compiler could optimize this away, but it's better to be
simpler (and also be easier to read).
Keeps track of instructions (including operands) that are executed,
to see if there are any hotspots that could be optimized or fastpaths
that should be added.
Also adds a mode where CPU profiler data is periodically output, to
make it easier to get at these instruction counts during startup.
- Rename DEC to DEC_S and add DEC_U.
- MQ, RTCL_U, RTCU_U, and DEC_U should cause an illegal instruction program exception for non-MPC601 CPUs. The exception handler of classic Mac OS uses this to emulate the instruction.
- For mtspr, the SPRs RTCL_U, RTCU_U, and DEC_U are treated as no-op on MPC601.
- For debugging, use the supervisor instead of the user SPR number as the index for storing the values for RTC, TB, and DEC.
- For debugging, RTC, TB, and DEC should be updated after each access. Previously, mfspr and mtspr would only update the half of RTC and TB that was being accessed instead of both halves.
The first option is a flag that enables MPC601 (POWER) instructions for CPUs that are not MPC601.
This can be useful for the following reasons:
1) To produce results similar to classic Mac OS which emulates MPC601 instructions on CPUs that don't implement MPC601 instructions. This option is used to compare the risu traces produced in Mac OS 9 on a G3 or G4 with DPPC.
2) May increase performance in apps that use POWER instructions on emulated machines with CPUs that are not MPC601. It is not known if any such apps exist but there could be since Apple included MPC601 emulation in classic Mac OS.