Say "boot image" where that is what is meant

"Binary" was doing three jobs. It meant an SPBT file that the machine starts
from; it meant whatever the assembler happened to produce, which is now
either that or a loadable program; and it meant a compiled host tool. A word
that means three things means none of them, and the first of the three has a
name already - this project has been calling them boot images for a while
and the manuals had not caught up.

  Where it means an SPBT file       -> boot image
  Where it means either output      -> output
  Where it means a host executable  -> left alone
  Where it means base two           -> left alone

The user facing messages move with it:

  Error: No boot image specified.
  Usage: ./SplitBit [OPTIONS] <boot image>
  Error: This is not a SplitBit boot image.
  Error: This boot image is in format version 2, and this emulator reads 1.
  Successfully wrote SplitBit boot image to "hello.bin".

The assembler's own help was the interesting case. Its -o writes either
format, so "the binary" there was never right - it is "the output" now, and
the message that names the format is the one that says which it wrote.

No recorded output contained the word, so nothing needed re-blessing.
Checked before starting rather than after.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
This commit is contained in:
Anachronaut
2026-08-21 14:50:03 -04:00
co-authored by Claude Opus 5
parent 306b4dce92
commit b6004bdcde
12 changed files with 41 additions and 40 deletions
+4 -3
View File
@@ -6,7 +6,8 @@
; Loading and running are separate commands in this shell, so the file to assemble is the
; argument to run rather than a second name after the program's.
;
; Reads assembly source off the disk and writes a binary back to it, with no host involved
; Reads assembly source off the disk and writes a boot image or a loadable program back to it,
; with no host involved
; anywhere. The output has to be byte for byte what the C assembler produces from the same
; source, which is the only honest test of it and the one the suite runs.
;
@@ -2046,7 +2047,7 @@ ImgHold:
DropWalk:
0x00 0x00
; How big a binary this can build. Everything the assembler makes has to fit here at once,
; How big an output file this can build. Everything the assembler makes has to fit here at once,
; because a file is written in one call and there is nowhere to put half of one. CosmOS
; itself comes to 9,564 bytes.
ImgRoom:
@@ -2152,7 +2153,7 @@ BasesText:
at zero, on top of whatever is there. Give both a #Base, or neither.
"
TooBigText:
"the binary would be bigger than this assembler has room to build
"the output would be bigger than this assembler has room to build
"
NoWriteText:
"it would not write "
+1 -1
View File
@@ -17,7 +17,7 @@
;
; 0x8000 3072 the label index, 768 entries of four
; 0x8C00 8192 the label names, packed end to end
; 0xAC00 13312 the binary being built
; 0xAC00 13312 the output file being built
; 0xE000 1792 the vector names, 64 entries of twenty eight
; 0xE700 1758 the reader's stack, six levels of 293
; 0xEE00 368 which files have been included, sixteen names of 23
+6 -6
View File
@@ -2,7 +2,7 @@
SplitBit is an 8 bit computer that does not exist: a CPU with its own instruction set, split Program and Data memories, an interrupt and vector system, a bus that programs can enumerate, a memory controller that can write code into memory, and a disk. This repository is a C implementation of the machine, an assembler for it, a tool for its disks, and the software that runs on it, which now includes an operating system and an assembler written in SplitBit's own assembly language.
**SplitBit assembles SplitBit.** `Programs/CosmOS/Assembler/` runs on the machine, reads source off a SplitBit disk, and writes a binary back to it with no host involved. It builds the operating system it is running under, and it builds itself, both byte for byte identical to what the C assembler produces from the same source. The test suite then boots the CosmOS that CosmOS built and has *that* one assemble CosmOS again, and the second generation is identical to the first, so the machinery has been through itself. After that the host is a convenience rather than a necessity.
**SplitBit assembles SplitBit.** `Programs/CosmOS/Assembler/` runs on the machine, reads source off a SplitBit disk, and writes a boot image or a loadable program back to it with no host involved. It builds the operating system it is running under, and it builds itself, both byte for byte identical to what the C assembler produces from the same source. The test suite then boots the CosmOS that CosmOS built and has *that* one assemble CosmOS again, and the second generation is identical to the first, so the machinery has been through itself. After that the host is a convenience rather than a necessity.
```
> load Asm.sbx
@@ -74,7 +74,7 @@ Then `dir` to see what is there, `load Snake.sbx` and `run` to play something, o
## Running Programs: SplitBit
```
./SplitBit [options] [binary file]
./SplitBit [options] [boot image]
```
| Option | What it does |
@@ -96,12 +96,12 @@ If the CPU reads a byte that is not an instruction, it goes to the fault handler
| Option | What it does |
| --- | --- |
| `-o <file>` | Write the binary to this path. |
| `-o <file>` | Write the output to this path. |
| `-I <dir>` | Look in this directory for included files. May be given more than once. |
| `-M <file>` | Write out which source files the binary depends on, as a make rule. |
| `-M <file>` | Write out which source files the output depends on, as a make rule. |
| `-h`, `--help` | Show help and usage information. |
Without `-o` the binary takes the source file's name with a `.bin` extension, in the directory you called the assembler from. Included files are looked for beside the file that includes them, and then along the directories given with `-I`.
Without `-o` the output takes the source file's name with a `.bin` extension, in the directory you called the assembler from. Included files are looked for beside the file that includes them, and then along the directories given with `-I`.
## Managing Disks: SplitDisk
@@ -123,7 +123,7 @@ Files are laid down contiguously, so a disk can have free blocks without having
## Building Programs With Make:
The assembler is built to work with make. `-o` puts the binary where the build system wants it, and `-M` writes out which libraries went into it, so that editing a library reassembles everything that includes it.
The assembler is built to work with make. `-o` puts the output where the build system wants it, and `-M` writes out which libraries went into it, so that editing a library reassembles everything that includes it.
`Programs/makefile` does this for the programs in this repository:
+3 -3
View File
@@ -91,13 +91,13 @@ void printUsage(const char *programName) {
printf("Usage: %s [OPTIONS] <sourcefile>\n", programName);
printf("\n");
printf("Options:\n");
printf(" -o <file> Write the binary to this path instead of alongside the source.\n");
printf(" -o <file> Write the output to this path instead of alongside the source.\n");
printf(" -I <dir> Look in this directory for included files. May be given more than once.\n");
printf(" -M <file> Write the source files this binary depends on, as a make rule.\n");
printf(" -M <file> Write the source files this output depends on, as a make rule.\n");
printf(" -h, --help Display this help message.\n");
}
// Writes a make rule naming every source file that went into the binary, so that a
// Writes a make rule naming every source file that went into the output, so that a
// build system knows to reassemble when any of them changes. The empty rules after it
// are so that deleting a library does not leave make with a prerequisite it cannot
// build; without them the build stops instead of just reassembling.
+7 -7
View File
@@ -8,13 +8,13 @@
#ifndef ASSEMBLY_H
#define ASSEMBLY_H
// ---- The SplitBit binary format ----
// ---- The SplitBit boot image format ----
//
// A binary starts with a file header, then the Program Segment, then the Data
// A boot image starts with a file header, then the Program Segment, then the Data
// Segment. All multi byte numbers are stored most significant byte first.
//
// Offset Size Field
// 0 4 "SPBT", so a file that is not a SplitBit binary is spotted at once
// 0 4 "SPBT", so a file that is not a boot image is spotted at once
// 4 1 Format version
// 5 4 Required feature flags
// 9 3 "PRG"
@@ -27,14 +27,14 @@
// .. 2 Vector Segment length, in bytes
// .. K Vector Segment, four bytes per entry
//
// The Vector Segment is optional and comes last, so a binary written before it existed
// The Vector Segment is optional and comes last, so an image written before it existed
// simply ends after its Data Segment and still loads. Each entry is two bytes saying
// where in Program Memory the vector sits, then two bytes saying where its handler is,
// most significant byte first. It is a list rather than an image of the table, so a
// program with three handlers costs twelve bytes instead of a padded kilobyte.
//
// The feature flags are how a binary says it needs something the base machine does
// not provide, so that an emulator which cannot provide it refuses to run the binary
// The feature flags are how a boot image says it needs something the base machine does
// not provide, so that an emulator which cannot provide it refuses to run the image
// rather than quietly doing the wrong thing. No features are defined yet; the field
// is here so that adding one later does not need another format version.
@@ -110,7 +110,7 @@
#define SPLITBIT_HEADER_BYTES (SPLITBIT_MAGIC_LENGTH + 1 + SPLITBIT_FLAGS_LENGTH \
+ 2 * (SEGMENT_MARKER_LENGTH + SEGMENT_LENGTH_BYTES))
// Features this build of the emulator can provide. A binary asking for anything
// Features this build of the emulator can provide. An image asking for anything
// outside this set is refused.
#define SPLITBIT_FEATURES_SUPPORTED 0x00000000u
+3 -3
View File
@@ -653,7 +653,7 @@ void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCo
}
// Write the file header: the magic, the format version, and the features this
// binary needs from the machine. An emulator that cannot provide one of those
// boot image needs from the machine. An emulator that cannot provide one of those
// features refuses the file rather than running it and going quietly wrong.
fwrite(SPLITBIT_MAGIC, sizeof(char), SPLITBIT_MAGIC_LENGTH, outputFile);
fputc(SPLITBIT_FORMAT_VERSION, outputFile);
@@ -703,7 +703,7 @@ void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCo
}
// The Vector Segment, only if the program named any. Leaving it out entirely is
// what lets a binary written before vectors existed still load: the reader treats
// what lets an image written before vectors existed still load: the reader treats
// the end of the file as an empty table rather than a missing one.
int installed = 0;
for (int i = 0; i < vectorArrayCount; i++) {
@@ -730,7 +730,7 @@ void writeOutputFile(const char *outputFileName, uint8_t *Program, int programCo
}
fclose(outputFile);
printf("Successfully wrote SplitBit binary to \"%s\".\n", outputFileName);
printf("Successfully wrote SplitBit boot image to \"%s\".\n", outputFileName);
printf(GREEN " Program Segment size: %d bytes.\n Data Segment size: %d bytes.\n" RESET, programCount, dataCount);
if (installed > 0) {
printf(GREEN " Vectors: %d.\n" RESET, installed);
+1 -1
View File
@@ -8,7 +8,7 @@
// has to change there in the same breath.
//
// All multi byte numbers are most significant byte first, the same as every other number
// SplitBit stores: addresses, the SPBT binary header, and the vector table.
// SplitBit stores: addresses, the SPBT boot image header, and the vector table.
//
// Written by Anachronaut
+7 -7
View File
@@ -4,7 +4,7 @@
// 10/16/2024
#include "bootstrap.h"
#include "../Assembler/assembly.h" // For the binary format, which both tools share.
#include "../Assembler/assembly.h" // For the boot image format, which both tools share.
#include <stdio.h>
#include <string.h>
@@ -39,14 +39,14 @@ static uint8_t readMarker(FILE *file, const char *expected, int length, char *fo
return strncmp(found, expected, length) != 0;
}
// Reads the file header: the magic, the format version, and the features the binary
// Reads the file header: the magic, the format version, and the features the boot image
// says it needs from the machine.
static uint8_t readFileHeader(FILE *file) {
char magic[SPLITBIT_MAGIC_LENGTH + 1];
if (readMarker(file, SPLITBIT_MAGIC, SPLITBIT_MAGIC_LENGTH, magic)) {
fprintf(stderr, "Error: This is not a SplitBit binary.\n");
fprintf(stderr, "Error: This is not a SplitBit boot image.\n");
if (strncmp(magic, "PRG", 3) == 0) {
fprintf(stderr, " It looks like a binary from before the format carried a version.\n Reassemble it and try again.\n");
fprintf(stderr, " It looks like a boot image from before the format carried a version.\n Reassemble it and try again.\n");
} else {
fprintf(stderr, " Expected the file to begin with \"%s\", found \"%s\".\n", SPLITBIT_MAGIC, magic);
}
@@ -58,7 +58,7 @@ static uint8_t readFileHeader(FILE *file) {
return 1;
}
if (version != SPLITBIT_FORMAT_VERSION) {
fprintf(stderr, "Error: This binary is in format version %u, and this emulator reads version %u.\n", version, SPLITBIT_FORMAT_VERSION);
fprintf(stderr, "Error: This boot image is in format version %u, and this emulator reads version %u.\n", version, SPLITBIT_FORMAT_VERSION);
return 1;
}
@@ -68,7 +68,7 @@ static uint8_t readFileHeader(FILE *file) {
}
uint32_t missing = required & ~(uint32_t)SPLITBIT_FEATURES_SUPPORTED;
if (missing) {
fprintf(stderr, "Error: This binary was built for a machine this emulator cannot provide.\n");
fprintf(stderr, "Error: This boot image was built for a machine this emulator cannot provide.\n");
fprintf(stderr, " It asks for feature bits 0x%08X, which are not implemented here.\n", missing);
return 1;
}
@@ -135,7 +135,7 @@ static uint8_t readVectorSegment(FILE *file, uint8_t *Program) {
return 1;
}
if (slot < SOFTWARE_VECTOR_BASE) {
fprintf(stderr, "Error: This binary puts a vector at 0x%04X, which is below the vector table.\n", slot);
fprintf(stderr, "Error: This boot image puts a vector at 0x%04X, which is below the vector table.\n", slot);
return 1;
}
Program[slot] = (handler >> 8) & 0xFF;
+1 -1
View File
@@ -10,7 +10,7 @@
uint16_t shiftRegister;
// Reads one entry out of a vector table. Most significant byte first, matching the
// branch instructions and the binary format.
// branch instructions and both file formats.
static uint16_t readVector(const uint8_t *programMemory, uint16_t base, uint8_t index) {
uint16_t address = base + (uint16_t)index * VECTOR_ENTRY_BYTES;
return ((uint16_t)programMemory[address] << 8) | (uint16_t)programMemory[address + 1];
+1 -1
View File
@@ -79,7 +79,7 @@ int main (int argc, char *argv[]) {
programFile = argv[optind];
optind++;
} else {
fprintf(stderr, "Error: No binary file specified.\n");
fprintf(stderr, "Error: No boot image specified.\n");
printHelp(argv[0]);
return 1;
}
+1 -1
View File
@@ -11,7 +11,7 @@
#include "../Assembler/assembly.h"
void printHelp(const char *programName) {
printf("Usage: %s [OPTIONS] <binaryfile>\n", programName);
printf("Usage: %s [OPTIONS] <boot image>\n", programName);
printf("\n");
printf("Options:\n");
printf(" -d, --debug Enable debug mode.\n");
+6 -6
View File
@@ -417,16 +417,16 @@ Assembler [options] <sourcefile>
| Option | Meaning |
| -- | -- |
| -o, --output \<file\> | Write the binary to this path. Without it, the binary is named after the source file, with a .bin extension, in the directory the assembler was run from. |
| -o, --output \<file\> | Write the output to this path. Without it, the output is named after the source file, with a .bin extension, in the directory the assembler was run from. |
| -I, --include \<dir\> | Look in this directory for included files. May be given more than once, and the directories are searched in the order given. |
| -M, --depend \<file\> | Write out which source files went into the binary, as a make rule. |
| -M, --depend \<file\> | Write out which source files went into the output, as a make rule. |
| -h, --help | Print the options and stop. |
The assembler stops at the first error, says which file and line it was in, and exits without writing a binary.
The assembler stops at the first error, says which file and line it was in, and exits without writing anything.
## Building With Make:
The -o and -M options are there so that the assembler fits into a build system. -o puts the binary wherever the build wants it, and -M writes down which libraries went into it, so that editing a library reassembles every program that includes it.
The -o and -M options are there so that the assembler fits into a build system. -o puts the output wherever the build wants it, and -M writes down which libraries went into it, so that editing a library reassembles every program that includes it.
```
$(BUILD)/%.bin: %.asm
@@ -589,7 +589,7 @@ wrote hello.bin: program 17, data 14, labels 2
Loading and running are separate commands in CosmOS, so the source file is the argument to `run`.
**Its output must be byte for byte what the host assembler produces from the same source**, and `Tests/native.sh` checks exactly that: it assembles `Programs/Examples/hello.asm` both ways and compares the files, then runs the one the machine built. This is the discipline SplitDisk and `sbfs.asm` already work under - two implementations of one written specification, each one checking the other. "It ran" is not good enough for an assembler, because a binary with a label one byte out runs right up until it jumps into the middle of an instruction.
**Its output must be byte for byte what the host assembler produces from the same source**, and `Tests/native.sh` checks exactly that: it assembles `Programs/Examples/hello.asm` both ways and compares the files, then runs the one the machine built. This is the discipline SplitDisk and `sbfs.asm` already work under - two implementations of one written specification, each one checking the other. "It ran" is not good enough for an assembler, because a file with a label one byte out runs right up until it jumps into the middle of an instruction.
### How It Differs Inside:
@@ -649,7 +649,7 @@ wrote Asm.sbx: program 7533, data 4099, labels 555
Both come out **byte for byte identical** to what the host assembler builds from the same source. `make run-cosmos` puts every source file on the disk, so this can be done rather than read about.
**The check that matters most is the third one.** A binary that matches could still have been built by an assembler wrong in some way this particular source happens not to exercise. So `Tests/native.sh` boots the CosmOS that CosmOS built and has *that* assemble CosmOS again - and the second generation is identical to the first, down to the cycle count. It is a fixed point, which means the machinery has been through itself.
**The check that matters most is the third one.** A file that matches could still have been built by an assembler wrong in some way this particular source happens not to exercise. So `Tests/native.sh` boots the CosmOS that CosmOS built and has *that* assemble CosmOS again - and the second generation is identical to the first, down to the cycle count. It is a fixed point, which means the machinery has been through itself.
After that the host is a convenience rather than a necessity.