Play reads a tune from a file
The loader, and the reason it is small: everything in a tune is an offset from wherever it was put, so taking one means adding the base to two tables and pointing four voices at their order lists. No sequence is walked. Nothing inside one is an address to be found and corrected, which is the difference between a malformed tune that plays wrongly and one that takes the loader with it. "SBTU", version, the tick in cycles, how many patches and sequences, offsets to the two tables, an order list each, and the patch each voice starts on. The magic is checked before anything else, because from there on the loader follows what the offsets name. break.sh shows what that guard is worth: without it, handing Play a PROGRAM runs the machine away until the cycle limit, rather than saying it is not a tune. PatchTable, SequenceTable and VoiceStart became pointers, so the engine does not care whether a tune came out of a file or was assembled in. Play's built-in tune now hands over the same three addresses a loaded one would, in eight lines - which is what keeps the two paths from drifting, and what made this rung change no scheduler code at all. Tests/maketune.py lays the fixture out byte by byte. IT IS NOT THE COMPILER: the sequences and the patches are literal bytes and the only thing computed is where each piece lands. That is the point - the loader is checked by something that does not share its idea of the format, which is the same reason SplitDisk and sbfs.asm share nothing but a specification. Both notes in the fixture are number 60, so the octave between them is a 0x80 command loading the second patch out of the file: the header, the tick, the relocation, an order list and a patch from a file, measured in one go. Play and the tune live on quiet.img rather than cosmos.img, so a fixture does not move ten recordings every time it changes size. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
co-authored by
Claude Opus 5
parent
5d9b39514b
commit
bfc46d982e
@@ -104,7 +104,29 @@ start:
|
||||
; under the old device this piece would have played with the arpeggio's setting on all four
|
||||
; parts - which is exactly the fault that made Lunar Porter's low fuel warning stop
|
||||
; trilling after the first landing of a run.
|
||||
CALL loadStartPatches
|
||||
; ---- A tune named on the line, or the one in here ----
|
||||
;
|
||||
; Nothing after the name plays what this program was built with. A name reads a file and
|
||||
; plays that instead, which is what makes it a player rather than a program with one song.
|
||||
SETD.0 TuneName
|
||||
INIB 0d63
|
||||
SWI osArgument
|
||||
SETD.0 TuneName
|
||||
LDA.0
|
||||
BRA playBuiltIn ; Nothing was typed after the name.
|
||||
|
||||
SETD.1 TuneBuffer ; DP0 is still the name, which is what osFileRead wants.
|
||||
SWI osFileRead
|
||||
BNQ playNoFile
|
||||
|
||||
SETD.0 TuneBuffer
|
||||
CALL useTune
|
||||
BNQ playNotATune
|
||||
BRI playReady
|
||||
|
||||
playBuiltIn:
|
||||
CALL useBuiltIn
|
||||
playReady:
|
||||
|
||||
INIA 0x60 ; Room over the top for four voices at once.
|
||||
OUTA 0x46
|
||||
@@ -176,6 +198,52 @@ lastRing:
|
||||
OUTA 0x51
|
||||
SWI osExit
|
||||
|
||||
; ---- What it says when it cannot ----
|
||||
;
|
||||
; Before the timer has been started, both of them, so there is nothing to stop and no handler
|
||||
; to take away.
|
||||
playNoFile:
|
||||
SETD.0 NoFileSaid
|
||||
SWI osPrintString
|
||||
INIA 0x01
|
||||
SWI osExit
|
||||
|
||||
playNotATune:
|
||||
SETD.0 NotATuneSaid
|
||||
SWI osPrintString
|
||||
INIA 0x01
|
||||
SWI osExit
|
||||
|
||||
; ---- The tune that is in this program ----
|
||||
;
|
||||
; What useTune does for a file, said in eight lines for a tune that is already in memory: the
|
||||
; three tables the player asks for, and an order list for each voice. Nothing is relocated,
|
||||
; because the assembler already wrote the addresses.
|
||||
useBuiltIn:
|
||||
SETD.0 BuiltInPatches
|
||||
SETD.1 PatchTable
|
||||
STD.0.1
|
||||
SETD.0 BuiltInSequences
|
||||
SETD.1 SequenceTable
|
||||
STD.0.1
|
||||
SETD.0 BuiltInVoiceStart
|
||||
SETD.1 VoiceStartAt
|
||||
STD.0.1
|
||||
SETD.0 Order0
|
||||
SETD.1 Voice0
|
||||
STD.0.1
|
||||
SETD.0 Order1
|
||||
SETD.1 Voice1
|
||||
STD.0.1
|
||||
SETD.0 Order2
|
||||
SETD.1 Voice2
|
||||
STD.0.1
|
||||
SETD.0 Order3
|
||||
SETD.1 Voice3
|
||||
STD.0.1
|
||||
CALL startVoices
|
||||
RET
|
||||
|
||||
; The tick has nothing to do: the loop above is the player and WAIT only needs something to
|
||||
; have happened. A handler still has to exist, because an interrupt with nothing installed to
|
||||
; catch it is a fault. Taking it is what brings the line down - a program that POLLED the
|
||||
@@ -187,6 +255,19 @@ tick:
|
||||
|
||||
#Base 0x3000
|
||||
|
||||
; The name that followed "Play", and room for the tune it names. WHOLE BLOCKS: osFileRead
|
||||
; puts 256 bytes down whatever the file's length, so the room here is a multiple of that and
|
||||
; not a guess at how big a tune is.
|
||||
TuneName:
|
||||
#Reserve 0d64
|
||||
TuneBuffer:
|
||||
#Reserve 0d1024
|
||||
|
||||
NoFileSaid:
|
||||
"no such tune"
|
||||
NotATuneSaid:
|
||||
"that is not a tune"
|
||||
|
||||
; ---- The order lists ----
|
||||
;
|
||||
; A table of sequence addresses each, ending in a zero. Read the four of them across and they
|
||||
@@ -215,10 +296,13 @@ Order3:
|
||||
; Writing these out by hand is exactly the tedium a compiler exists to remove: every sequence
|
||||
; has to be counted into its place above, and moving one means renumbering. That it is
|
||||
; unpleasant is the point of noticing it here rather than after a tool has baked the shape in.
|
||||
PatchTable:
|
||||
; The tables this tune uses. The player holds POINTERS to them rather than the tables, because
|
||||
; a tune read from a file has its tables wherever the file was put - so a built-in one hands
|
||||
; over the same three addresses a loaded one would.
|
||||
BuiltInPatches:
|
||||
OboePatch StringsPatch SquarePatch KalimbaPatch
|
||||
|
||||
SequenceTable:
|
||||
BuiltInSequences:
|
||||
Mel1 Mel2 Mel3 Mel4 ; 0 to 3
|
||||
Har1 Har2 Har3 Har4 ; 4 to 7
|
||||
BassC BassF BassG ; 8 to 10
|
||||
@@ -226,7 +310,7 @@ SequenceTable:
|
||||
|
||||
; The patch each voice starts on. Not assumed, and not four calls in a row: a starting
|
||||
; instrument is state, and state belongs somewhere it can be read.
|
||||
VoiceStart:
|
||||
BuiltInVoiceStart:
|
||||
0d0 0d1 0d2 0d3
|
||||
|
||||
; ---- Four bars of C, F, G, C ----
|
||||
|
||||
Reference in New Issue
Block a user