A tune's tick is the tune's own

useTune took the period out of a file's header and then Play wrote its
own straight over it, so every tune played at a sixteenth note at 120
beats a minute whatever it asked for. A tune with a #Tick of 0d250000
lasted half as long as it said.

Nothing noticed because every fixture in the suite asked for exactly the
tick Play had written into itself. A test that agrees with the bug by
coincidence is not a test, and the way to find out is a fixture that
wants something else - so slow.tune is two.tune with twice the period and
nothing else changed, and it has to last twice as long.

The period now belongs to whoever supplied the tune: useTune sets it from
the header, useBuiltIn sets its own, and the start code writes only the
control byte - which has to come after either of them, because writing
control with the run bit set is what loads the period.

Found while reading Play to see how a splash screen would drive the
player, which is a reminder that the second reader of a piece of code is
worth more than the first.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-09-06 09:37:08 -04:00
co-authored by Claude Opus 5
parent 87467a7d3a
commit 6bb1565dea
3 changed files with 47 additions and 9 deletions
+18 -9
View File
@@ -133,15 +133,15 @@ playReady:
; ---- The beat ----
;
; 125,000 cycles is a sixteenth note at 120 beats a minute, which is the smallest thing
; this piece divides a beat into. The period goes in before the control byte, because
; writing control with the run bit set is what loads it.
INIA 0x01
OUTA 0x52
INIA 0xE8
OUTA 0x53
INIA 0x48
OUTA 0x54
; ONLY THE CONTROL BYTE. The period was set by whoever supplied the tune, because a tick
; belongs to the tune and not to this program: a file says what it wants in its header.
;
; It used to be written here as well, AFTER useTune had taken it out of the file - so every
; tune played at a sixteenth note at 120 beats a minute whatever its header said. The only
; reason nothing noticed is that the one fixture in the suite asks for exactly that.
;
; Writing control with the run bit set is what loads the period, which is why this comes
; after whoever set it and not before.
INIA 0x07
OUTA 0x51 ; Run, repeat, interrupt.
SIF
@@ -220,6 +220,15 @@ playNotATune:
; three tables the player asks for, and an order list for each voice. Nothing is relocated,
; because the assembler already wrote the addresses.
useBuiltIn:
; The tick this tune wants. For a file it comes out of the header; for this one it is here,
; so that both hand the player the same things.
INIA 0x01
OUTA 0x52
INIA 0xE8
OUTA 0x53
INIA 0x48
OUTA 0x54 ; 0x01E848 is 125,000: a sixteenth note at 120.
SETD.0 BuiltInPatches
SETD.1 PatchTable
STD.0.1