main: while the CPU waits in WAI, step the machine to its next event at once 8e8d9cad1884
At the BASIC prompt, and in any program that waits for VSYNC with WAI,
the 65C02 spends most of its time waiting. step6502() then advances one
clock and returns, so the main loop went round once per clock, about
8 million times a second, stepping VIA1, VERA's beam, I2C, the RTC and
audio and checking for interrupts each time. That was most of narvi's
run time at the prompt.
wai_fast_forward() steps them by many clocks in one go instead, only as
far as the next clock on which anything could raise an interrupt, so
the machine does exactly what it did clock by clock:
- VERA: to the clock on which the beam passes the end of a VGA or NTSC
line (video_clocks_to_line_end()). That is exact while the beam moves
a whole number of eighths a clock (mhz divides 200, so at the X16's
8 MHz): every position is exact in a float, so one big step and many
small ones agree.
- Audio: to the clock on which audio_step() next renders, where the CPU
could see a change (the PCM FIFO, VERA's AFLOW interrupt, the YM2151)
(audio_clocks_to_render()); the sample counters are integers.
- VIA1: a clock at a time, stopping on the clock its interrupt rises.
via_step() is not stepped by the stretch: a step that ends on Timer
1's -1 state takes 1 clock where it should take cnt + 1, so it does
not add up the way single clocks do (an existing inaccuracy, left as
it is).
- The RTC counts clocks; the SD card's SPI is left clock-by-clock while a
byte is in flight (its counter is a float).
Everything not shown to add up that way keeps the clock-by-clock loop:
VIA2, the serial bus, the MIDI card, YM2151 interrupts, the debugger,
BASIC pasting, and the KERNAL hooks at $FEA8 and up.
I2C is now stepped once per pass rather than once per clock: it acts only
when the clock or data line the VIA drives changes, which happens between
instructions, so the second and later calls did nothing. (On its own this
measured within narvi's noise; with the fast-forward it would be called
hundreds of times per pass.)
Measured with the emulator stepping 600 frames, real-time mode, SDL dummy
drivers, CPU seconds per emulated second, median of 3:
narvi idle 2.189 -> 0.698 (46% -> 100% of a real X16, 143% flat out)
narvi busy 1.797 -> 1.647 (56% -> 61%)
x86 idle 0.412 -> 0.216
x86 busy 0.430 -> 0.454 (within this machine's noise)
x86 idle in -warp: 280% -> 1155% of a real X16
Emulation is unchanged. From power-on with -zeroram -nohostfs, five BASIC
workloads, each with sound on and off, give the same registers, RAM (low
and all 64 banks) and screen at frame 900 as before, on x86 and on
riscv64. Two of them are for this change: SLEEP in a loop (the KERNAL
waits in WAI for VSYNC), and a machine-code loop that takes VIA1 Timer 1
interrupts every 16K clocks and WAIs between them, logging the scanline
and the timer at each wake-up.