Complement bench1's integer workload with a replay through the real FPU
opcode dispatcher. The 512 instruction and register snapshots are based
on observed FPU operations while running GLTeapot on BeOS PR1.
Report median host time per instruction and provide a checksum mode for
checking that different implementations produce the same results,
without guest boot, display updates, or timer overhead.
Combine the host exception checks and only clear exceptions when needed.
On Apple ARM64, use ACLE intrinsics to read and update FPSR directly,
preserving unrelated status bits. Skip these checks when FE_ALL_EXCEPT
is zero.
On an M2 Max, the FPU replay benchmark drops from 32.6 ns/instruction to
21.3 ns with combined checks and 17.9 ns with the ARM64 helpers, giving
roughly 1.5x and 1.8x throughput respectively.
First, fix when event gets set. Previously, for emulated mode, event was
always set even if the irq_line_state is unchanged. Now, the event gets
set only when the level transitions.
For native mode, event is set only when level transitions from 0 to 1.
This remains correct behavior.
Second, call signal_cpu_int only if event changed. Previously, it was
always called even if irq_lane_state or int_event are unchanged.
Apply the Grand Central fixes from dingusdev/dingusppc#240 to O'Hare,
Heathrow, and Paddington; the Heathrow ERS shows that it expects to
have the same behavior:
- Reevaluate the CPU interrupt when either mask changes, releasing it
if no pending events remain enabled in either bank (section 7.1).
- Keep events latched until software writes InterruptClear, even if a
device deasserts its interrupt line or repeats an assertion (sections
7.2.1 and 7.2.5).
- Replace writable InterruptMask2 bits instead of ORing them, allowing
sources to be disabled while preserving the read-only IntMode bit
(section 7.2.6).
Per the Hammerhead ERS the ROM region is 0xFF000000-0xFFFFFFFF. We were
only mapping the ROM at the uppermost 4 MB of that (0xFFC00000). The fact
that copies are returned at other addresses in that range is not
explicitly stated in the ERS, but it does appear to be emprically true.
It's also corroborated by the 9600 wiring diagram, which shows only enough
address bits for 4MB being used for the ROM.
Also does the same fix for Platinum/7200, which has the same mapping and
address bits, and adds some notes about this to the documentation.
Fixes SheepShaver in BeOS guests, which was using 0xFF000000 to get at
the hardware ROM (see https://github.com/cebix/macemu/blob/96e512bd6376e78a2869f16dcc8a9028bce5ee72/BasiliskII/src/BeOS/SheepDriver/sheep_driver.c#L454).
Two correctness fixes, based on the Grand Central ERS:
- Reevaluate the CPU interrupt when the mask changes, releasing it if
no pending events remain enabled (section 6.1).
- Keep events latched until software writes InterruptClear, even if a
device deasserts its interrupt line or repeats an assertion (section
6.2.1).
This fixes occasional freezes on BeOS (especially in realtime mode).
Previously, the CPU interrupt could remain asserted with no enabled
events, trapping BeOS in its interrupt handler.
T2 readback wrapped after N+1 clocks, but its interrupt was scheduled
for N+3. Mac OS Microseconds could see a wrapped counter with no pending
interrupt, then clear the newly asserted interrupt by reading T2CL. The
one-shot timer would never be serviced or rearmed.
Abuse paces its intro text by repeatedly calling Microseconds until 150
ms have elapsed. This tight polling loop exposes the race. Once the
interrupt is lost, the OS stops accumulating elapsed timer periods,
while the hardware counter keeps wrapping. Microseconds then cycles
within a roughly 84 ms window instead of advancing monotonically, so the
150 ms wait can no longer complete normally. The guest keeps running,
but the text's pacing loop effectively stalls, accounting for the
reported frozen or extremely slow scrolling.
Use a shared time origin for counter readback and the N+1.5-clock
interrupt, including the initial half-clock delay. Before reading T2 or
IFR, also assert any overdue interrupt and cancel its queued callback,
so realtime reads and acknowledgments don't depend on timer polling.
Verified native Abuse intro and gameplay, native and Emscripten builds,
and 84 timer checks covering rollover, acknowledgment and overdue reads.
Co-Authored-By: joevt <950609+joevt@users.noreply.github.com>
Co-Authored-By: Mihai Parparita <513813+mihaip@users.noreply.github.com>
We would reschedule timers based on their interval and the current time, which
would cause timers with the same interval to get aligned. This may be not be
desired (if a subsystem has two intentionally out-of-phase timers), so we
instead advance the timer based on its previously scheduled time and multiples
of the interval.
Not strictly necessary for the codebase as it stands today (we no longer use
two cyclic intervals for the VBL interval) but it still seems like a worthwhile
correctness improvement, if we decide to reintroduce that approach.
We were relying on two separate cyclic timers for the VBL interval, but
in realtime mode we may end up falling behind in timer execution, and the exact
phase may not be preserved. In the worst case they would end up exactly aligned,
and the VBL interval would be effectively lost (the guest polls for the VBL bit
and would never see it)
Switch to only one cyclic timer, which repeatedly sets a one-shot timer for
the VBL end.
Use the expected end time as the start time for the next iteration
rather than using the current time. This removes accumulative lag that
process_events might introduce.
We only created SCSI CD-ROM drives when an image was supplied at
startup, so later insertion requests had no drive to go to. The
Beige G3 already worked because its ATAPI drive is always created.
Extend the ATAPI approach (where a `cdr_config` property defines the bus
where a CD-ROM drive should be attached) for SCSI-based machines too.
Use it to define default drive on the appropriate bus for PDM, TNT,
Catalyst, Alchemy, Gazelle, and Pippin. Supplying images at startup still
uses the existing drive allocation behavior.
The 7300 ROM tries to read from an empty CD-ROM drive during boot.
We would try to transfer data from an unallocated buffer and crash.
Return NOT READY / MEDIUM NOT PRESENT for reads, capacity requests,
and TOC requests. Also covers the separate ATAPI READ CD path. This
matches the behavior described in ATAPI 2.6 section 8.5 and the
per-command error tables:
https://www.grix.it/UserFiles/marco.c/INF-8020.pdf
Use existing helpers for more instructions instructions (twi, mtsr,
mtmsr, etc.). Prompted by the recent fix for tw - these were all correct
already, but might as well make things more uniform.
The manual decode swapped rA and rB. Fix thix by using the standard
`ppc_grab_regsab` decode helper.
Fixes Macintosh Common Lisp 4.2 hanging at startup (with 7.5.5 booting
using the 7500 ROM).
When the interpreter can run faster than realtime (depends on the
host system's performance), we would end up checking timer more
frequently.
This is because the `max_cycles` value returned by `process_events()`
was based on the absolute timer deadline converted to instruction
counts. Once `g_icycles` got ahead of that value, we would check timers
after every instruction.
Instead, use the time remaining until the next timer to schedule an
instruction batch relative to `g_icycles`, capped using the existing
500us polling interval.
Log a warning whenever an illegal opcode exception is being raised,
since it's usually a clue that we have a correctness bug.
Just a warning (as opposed to an error) because the guest OS may handle
the exception and continue execution.
Normally rA (where the effective address is stored) cannot be 0, but the
MPC601 user manual explicitly says that it's allowed for POWER
compatibility (the update is skipped in that case).
Fixes mouse movement in Barrack under on the 6100, which would previously
stop executing because of an illegal opcode exeception being raised
(and unhandled).
The documentation for `mach_absolute_time` recommends replacing it with
`clock_gettime_nsec_np(CLOCK_UPTIME_RAW)`. The latter is also slightly
simpler to use (no need for initialization and a conversion step).
Adds a --realtime CLI flag to enable realtime mode immediately at
startup.
Also tweaks documentation to mention the Control-Alt-R realtime toggle
and the relationship between the realtime mode and deterministic mode.
With the shared `TimerInfo` structs refactor from 529fff7d, it's easier
to accidentally enqueue the same timer multiple twice. Help the the
programmer out by logging an error if the timer is already `active` when
adding it. To also reduce the boilerplate, one-shot timers and and
cancellation automatically clear the flag.
The error could be upgraded to a CHECK_F once we're more confident that
we've fixed all of the callsites.
Solarian II repeatedly restarts sound DMA without the stream being
closed, and thus we would end up in `SoundServer::start_out_stream`
without going through `close_out_stream`. The `add_cyclic_timer` did
call did not check if the timer was already active, and we would end up
with a duplicate entry in the timer queue, which slowdowns and other
incorrect behavior.
Fixed by checking the `active` flag, like all of the other callsites.
When the Displays Enabler extension would load under Mac OS 7.6.1 and
the 7300 ROM, there would be several hundred ERROR level lines about
MON_SENSE register writes with unexpected values (more than 5 bits set).
These were actually harmless (they had the read-only sense input bits
6-8), tweak the mask so that they remain at level 9.
Makes both flavors of Control enable the `draw_fb_is_dynamic` optimization,
which lets us skip SDL screen update when the the framebuffer content
has not changed.
Improve BeOS PR1 booting on the 7300 from 15.8-46s to 13.9-14.2. The
variability appears to be due to time spent in `SDL_RenderPresent`,
per https://developer.apple.com/documentation/metal/onscreen-presentation
this may block on macOS (with the Metal backend) until the next display
refresh, and we were generating a lot more frames to present before.
Also improves smoothness of the BeOS GL Teapot demo (the displayed
framerate does not change since the timer is driven by instruction
cycles, which don't change).
BeOS DR8.2 and DR8.3 use this - not handling it was causing graphical
corruption during boot.
We do the operation as usual in big endian mode (which handles the bank
logic) and then byte-swap the results. Note that the byte swapping
depends on the bit width, not the payload length (patent US5640545A
is presumed to describe some of the logic)
Makes it possible to run time-limited OS versions like BeOS DR8.2
(which does not work after 1997-04-01).
This is now also used for deterministic mode (which had already been
hardcoding a 2001-03-24 RTC time).
Also document both flags in the manual.
When changing the bit depth, there are two display attributes that change: the
frame buffer pitch and the pixel width.
Mac OS (7.6.1) and BeOS (R3) do those updates in different order: Mac OS updates
the pixel width first, then the frame buffer pitch; BeOS does the opposite.
For Control and Platinum video, those are handled in separate components
(`ControlVideo`/`PlatinumCtrl` vs. `AppleRamdac`), and only the former had the
ability to trigger display configuration updates (which determines the
`convert_fb_cb` function that is selected). BeOS would thus be left with a stale
value, leading to display corruption.
To fix this, add a pixel width update callback (similar to the hardware cursor
change one) and use it to trigger a display configuration update from the
`AppleRamdac` side.