Skip to content

fix: make ?cpuMultiplier= run the CPU faster than the peripherals again - #725

Merged
mattgodbolt merged 3 commits into
mainfrom
claude/fix-cpu-multiplier-wiring
Jul 26, 2026
Merged

fix: make ?cpuMultiplier= run the CPU faster than the peripherals again#725
mattgodbolt merged 3 commits into
mainfrom
claude/fix-cpu-multiplier-wiring

Conversation

@mattgodbolt

Copy link
Copy Markdown
Owner

?cpuMultiplier= is meant to run the CPU faster relative to the peripherals, leaving video, sound and VIA timing at real-world rates. That takes two halves working together:

  1. main.js feeds cpuMultiplier * cpuSpeed cycles per wall-clock second into execute().
  2. Cpu6502.buildPolltime() divides videoCycles and peripheralCycles back down by this.cpuMultiplier, so peripherals still see real time.

Half 2 was dead. emulationConfig captured the cpuMultiplier local by value before the query string was parsed into it, so config.cpuMultiplier was always 1. Only half 1 applied, and the whole machine sped up, sound pitch and screen refresh included.

Archaeology

README.md's long-standing caveat that "disc loads become unreliable with a too-slow CPU" only makes sense under the 2015 semantics, where the FDC runs at real time while the CPU is slowed.

Changes

  • Resolve cpuMultiplier from parsedQuery where it is declared, so the config cannot capture a stale value, and drop the now-false comment.
  • keyboard.js: paste timing no longer multiplies by cpuMultiplier. The paste task lives on the processor's scheduler, which is polled with peripheral cycles, so its delays were already in real time; the multiplier would have stretched paste by the multiplier now that it is live. This path had only ever run at 1.
  • Plumb cpuMultiplier through fake6502 / MachineSession, alongside tube, and scale TestMachine.type()'s key hold, which is counted in CPU cycles rather than scheduler ticks.
  • New tests/unit/test-6502.js pins the ratio behaviourally: at multiplier 2, 1000 CPU cycles advance the scheduler epoch and video by 500; at 1 the unscaled path passes every cycle straight through.

Sanity-checked headless at multiplier 2: the machine boots, PRINT 6*7 gives 42, and BASIC's TIME advances by the same amount for twice as many CPU cycles.

Behaviour change

This changes emulation behaviour for anyone currently passing ?cpuMultiplier=, restoring the 2015 semantics: the CPU speeds up relative to the peripherals rather than the whole machine speeding up.

Fixes #716

🤖 Generated with Claude Code

The parameter was parsed after the emulation config literal captured it, so
Cpu6502 always saw a multiplier of 1 and the polltime path that divides video
and peripheral cycles back down never ran. The whole machine sped up, sound
pitch and screen refresh included, instead of just the CPU.

Resolve the multiplier from the query string where it is declared, so there is
no window in which the config can capture a stale value.

Paste timing no longer scales by the multiplier: the paste task lives on the
processor's scheduler, which is polled with peripheral cycles, so its delays
were already in real time. The headless harness plumbs cpuMultiplier through
and scales its key hold, which is counted in CPU cycles.

Fixes #716

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Restores the intended ?cpuMultiplier= behavior where the CPU runs faster/slower relative to peripherals (video/sound/VIA timing stays real-time), fixing a regression where cpuMultiplier never reached Cpu6502 via emulationConfig.

Changes:

  • Resolve cpuMultiplier directly from parsedQuery before constructing emulationConfig, and remove the now-incorrect comment in src/main.js.
  • Adjust paste/autotype timing to remain real-time under a live CPU multiplier, and plumb cpuMultiplier through headless/test wiring (MachineSession/fake6502).
  • Add/adjust unit tests to pin the CPU-vs-peripheral scaling behavior and update existing keyboard tests accordingly.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
tests/unit/test-keyboard.js Updates paste timing expectations to be based on peripheral cycles (real-time), not CPU multiplier.
tests/unit/test-6502.js Adds new unit coverage for peripheral/video scaling under cpuMultiplier.
tests/test-machine.js Scales key-hold timing in TestMachine.type() by cpuMultiplier to keep holds constant in real time.
src/main.js Fixes config wiring so config.cpuMultiplier reflects the parsed query value.
src/machine-session.js Exposes cpuMultiplier as a per-session option and forwards it into TestMachine.
src/keyboard.js Removes multiplier scaling from paste delays (scheduler already runs at peripheral rate).
src/fake6502.js Passes cpuMultiplier into the CPU config for headless/test instances.
README.md Clarifies that cpuMultiplier is relative to peripherals, not whole-machine speed.
Comments suppressed due to low confidence (1)

tests/test-machine.js:419

  • As above for the BBC path, Atom holdCycles can become non-integer for fractional cpuMultiplier values. Ensure the cycle count is integral before using it for runFor()/cycle comparisons.
        const holdCycles = 80000 * this.processor.cpuMultiplier; // Atom at 1 MHz needs longer hold than BBC at 2 MHz

Comment thread tests/test-machine.js Outdated
Comment thread tests/unit/test-6502.js Outdated
Also coerce the scaled key hold to an integer: cpuMultiplier is a float, and
the hold feeds runFor() and so execute()'s target cycle count.

Addresses review feedback on #725.
#720 added hasTeletextAdaptor plumbing to the same two harness call sites
this branch adds cpuMultiplier to. Both are additive; kept both options.
@mattgodbolt
mattgodbolt merged commit 4c57345 into main Jul 26, 2026
5 checks passed
@mattgodbolt
mattgodbolt deleted the claude/fix-cpu-multiplier-wiring branch July 26, 2026 21:22
This was referenced Jul 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

?cpuMultiplier= never reaches Cpu6502: emulationConfig.cpuMultiplier is always 1

2 participants