TuneC: a written tune becomes the bytes the player reads
The compiler, and the last thing the ladder was waiting for. A tune names
its instruments, writes sequences of notes and durations, and gives each
voice an order list of sequence names - which is where repetition comes
from, since a phrase played four times is written once and named four
times.
#Tick 0d125000
#Patch Oboe oboe.patch
#Voice 0d0 Oboe
#Sequence Verse
0d64 0d4 0d67 0d4 0d72 0d8
#Order 0d0
Verse Verse Ending
"#" is a directive and ";" is a comment, exactly as in SplitBit assembly
and in the shell's scripts, and numbers are written the way the assembler
writes them. One rule across the machine rather than a third dialect -
and the rule earned itself immediately: the first tune I wrote said
"#Voice 0" and was refused, correctly, for a bare number.
WHAT IT REFUSES IS EVERYTHING THE PLAYER CANNOT NOTICE. The machine has
no names, so it cannot say a sequence does not exist. It has no lengths,
so it cannot say the voices will come apart four bars after the mistake.
A duration of nought is counted down to 255 and held, which sounds like a
hang rather than an error. And by the time a tune is loaded, "no starting
instrument" and "instrument nought" are the same byte - so the user's
ruling, that a voice with a part and no instrument is an error, can only
be kept here.
SoundPatch gains --blob, writing the same table as raw bytes. It stays
the only thing that reads soundThing's JSON: a second program parsing
that format is a second opinion about what a patch means, and the seam
between two opinions is where the LFO bug lived for a fortnight. Patches
are found beside the tune and then on a -I path, the way an include is.
THE TEST IS THAT TWO IMPLEMENTATIONS AGREE. maketune.py lays the fixture
out by hand and TuneC compiles a written source, and the suite checks
they match byte for byte - the discipline SplitDisk and sbfs.asm are held
to, for the same reason: either alone is only self-consistent. The
fixture predates the compiler, so this is also TuneC checked against
something written before it existed. Four more checks cover the four
refusals.
Also: the SoundPatch binary was tracked, alone among the six tools, and
.gitignore lists every other one. Untracked, and TuneC added beside it.
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
bfc46d982e
commit
2808688fa1
@@ -50,6 +50,7 @@ SRC_DIR_ASM = Source/Assembler
|
||||
SRC_DIR_DSK = Source/DiskTool
|
||||
SRC_DIR_LINT = Source/Linter
|
||||
SRC_DIR_PATCH = Source/Patch
|
||||
SRC_DIR_TUNE = Source/Tune
|
||||
OBJ_DIR = Object
|
||||
|
||||
# Source files
|
||||
@@ -65,6 +66,7 @@ ASM_SRCS = Assembler.c assembly.c firstPass.c Assm-util.c secondPass.c
|
||||
DSK_SRCS = SplitDisk.c
|
||||
LINT_SRCS = Linter.c
|
||||
PATCH_SRCS = SoundPatch.c
|
||||
TUNE_SRCS = TuneC.c
|
||||
|
||||
EMU_OBJS = $(EMU_SRCS:%.c=$(OBJ_DIR)/%.o)
|
||||
VOY_OBJS = $(VOY_SRCS:%.c=$(OBJ_DIR)/%.o)
|
||||
@@ -72,6 +74,7 @@ ASM_OBJS = $(ASM_SRCS:%.c=$(OBJ_DIR)/%.o)
|
||||
DSK_OBJS = $(DSK_SRCS:%.c=$(OBJ_DIR)/%.o)
|
||||
LINT_OBJS = $(LINT_SRCS:%.c=$(OBJ_DIR)/%.o) $(OBJ_DIR)/assembly.o
|
||||
PATCH_OBJS = $(PATCH_SRCS:%.c=$(OBJ_DIR)/%.o)
|
||||
TUNE_OBJS = $(TUNE_SRCS:%.c=$(OBJ_DIR)/%.o)
|
||||
|
||||
# Output binary names
|
||||
EMU_TARGET = SplitBit
|
||||
@@ -80,6 +83,7 @@ ASM_TARGET = Assembler
|
||||
DSK_TARGET = SplitDisk
|
||||
LINT_TARGET = SplitLint
|
||||
PATCH_TARGET = SoundPatch
|
||||
TUNE_TARGET = TuneC
|
||||
|
||||
# ---- Whether this machine can build Voyager ----
|
||||
#
|
||||
@@ -212,7 +216,7 @@ SCRATCH_BLOCKS = 2048
|
||||
# linter, the whole test suite - has to build on a machine with no graphics library at all,
|
||||
# because a project about a small understandable CPU should not need OpenGL to run its
|
||||
# tests. Where Raylib is missing, 'make' says so once and builds everything else.
|
||||
TOOLS = $(EMU_TARGET) $(ASM_TARGET) $(DSK_TARGET) $(LINT_TARGET) $(PATCH_TARGET)
|
||||
TOOLS = $(EMU_TARGET) $(ASM_TARGET) $(DSK_TARGET) $(LINT_TARGET) $(PATCH_TARGET) $(TUNE_TARGET)
|
||||
|
||||
# ---- What 'make' builds ----
|
||||
#
|
||||
@@ -305,6 +309,17 @@ $(PATCH_TARGET): $(PATCH_OBJS)
|
||||
$(CC) $(CFLAGS) -o $(PATCH_TARGET) $(PATCH_OBJS) -lm
|
||||
|
||||
# Compile patch tool source files to object files
|
||||
# ---- TuneC, which turns a written tune into the bytes the player reads ----
|
||||
#
|
||||
# It needs nothing: patches reach it as bytes from SoundPatch, which stays the only thing that
|
||||
# understands what a soundThing patch means.
|
||||
$(TUNE_TARGET): $(TUNE_OBJS)
|
||||
$(CC) $(CFLAGS) -o $(TUNE_TARGET) $(TUNE_OBJS)
|
||||
|
||||
$(OBJ_DIR)/%.o: $(SRC_DIR_TUNE)/%.c
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(CC) $(CFLAGS) -MMD -MP -c $< -o $@
|
||||
|
||||
$(OBJ_DIR)/%.o: $(SRC_DIR_PATCH)/%.c
|
||||
@mkdir -p $(OBJ_DIR)
|
||||
$(CC) $(CFLAGS) $(POSIXFLAGS) $(DEPFLAGS) -c $< -o $@
|
||||
@@ -329,7 +344,7 @@ $(OBJ_DIR)/%.o: $(SRC_DIR_ASM)/%.c
|
||||
# disagreed with everything else about how big the struct was, and smashed its stack on every
|
||||
# run. A clean build hid it, which is why 'make sanitize' would never have found it either.
|
||||
# Tests/voyager.sh did, by failing all 115 tests that start the machine.
|
||||
-include $(EMU_OBJS:.o=.d) $(VOY_OBJS:.o=.d) $(ASM_OBJS:.o=.d) $(DSK_OBJS:.o=.d) $(LINT_OBJS:.o=.d) $(PATCH_OBJS:.o=.d)
|
||||
-include $(EMU_OBJS:.o=.d) $(VOY_OBJS:.o=.d) $(ASM_OBJS:.o=.d) $(DSK_OBJS:.o=.d) $(LINT_OBJS:.o=.d) $(PATCH_OBJS:.o=.d) $(TUNE_OBJS:.o=.d)
|
||||
|
||||
# ---- The strict build the README promises ----
|
||||
#
|
||||
@@ -443,7 +458,7 @@ clean:
|
||||
rm -rf Tests/build
|
||||
rm -rf $(PROG_BUILD)
|
||||
rm -f $(SRC_DIR_EMU)/rom.c
|
||||
rm -f $(EMU_TARGET) $(ASM_TARGET) $(DSK_TARGET) $(LINT_TARGET) $(VOY_TARGET) $(PATCH_TARGET)
|
||||
rm -f $(EMU_TARGET) $(ASM_TARGET) $(DSK_TARGET) $(LINT_TARGET) $(VOY_TARGET) $(PATCH_TARGET) $(TUNE_TARGET)
|
||||
|
||||
# Install compiled binaries
|
||||
install: $(EMU_TARGET) $(ASM_TARGET) $(DSK_TARGET) $(LINT_TARGET)
|
||||
|
||||
Reference in New Issue
Block a user