Compare commits

..
5 Commits
Author SHA1 Message Date
Anachronaut 12cb489268 Minor typo fix to assembler manual. 2026-08-21 17:09:34 -04:00
AnachronautandClaude Opus 5 bd94059c08 Put the screenshot at the top of the README
The picture does what three paragraphs of prose were doing, and does it
first. It has the whole loop in one frame: Asm.sbx assembles hello.asm into
hello.sbx, the shell loads that and runs it, and then the monitor
disassembles what is at 0x2000 - which is the program the machine wrote for
itself at the top of the same screen.

The caption points at that last part, because it is the thing an eye slides
past. The disassembly is not of an example; it is of the output.

Two notes on getting it right rather than nearly right. The file is
Media/CosmOS.png and not the cosmos-screenshot.png it was described as, so
the link would have been dead on arrival - checked by looking rather than by
typing what I was told. And the caption first said the disassembled program
was written "six lines earlier", which was wrong by about four; it now says
"at the top of the screen", which cannot drift.

docs.sh already covers it: the link check treats an image like any other
relative link, and moving the file out makes the suite say so.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-08-21 17:06:46 -04:00
AnachronautandClaude Opus 5 9690ecf21f make sanitize runs the whole suite, not half of it
It built all three tools with the sanitizers and then ran run.sh and
terminal.sh only. So SplitDisk was compiled sanitized and never once
exercised, and native.sh - which drives the assembler and the emulator
harder than anything else here, through two self-hosting builds and a fixed
point - was skipped entirely.

Those are precisely the places worth watching: block and tail arithmetic on
disk images, and buffer indexing in two assemblers, one of which was written
this week.

disk.sh, native.sh and docs.sh now run under the sanitizers with the rest.
Timed first rather than guessed at: 0.41s, 6.05s and 0.14s, against a whole
sanitize run of 33 seconds. All three pass, which is worth knowing given
none of them had ever been run this way.

The README said sanitize ran "the suite", which was generous. Now it does.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-08-21 16:51:03 -04:00
AnachronautandClaude Opus 5 8ace20629b Say what the tests need, and count the tools correctly
Two nitpicks from review, and one of them was only half right, which is why
each was checked rather than swept.

THE TEST DEPENDENCIES were nowhere. The README said what building needs and
stopped, so somebody with a C compiler and nothing else would get through
'make' and fall over on 'make test'. It needs bash and Python 3 - two of the
checking scripts are Python, and one of those opens a pseudo-terminal - plus
stty, timeout and the usual text utilities.

"BOTH TOOLS" WAS WRONG IN TWO PLACES OUT OF SIX. 'make sanitize' builds the
default target, which is all three, so the README and the makefile both
undercounted what they rebuild.

The other four are right and were left alone. assembly.h says both tools
have to agree where the vector table begins, and bootstrap.c and cpu.c say
they share the boot image format and the vector layout - in all three cases
that is the emulator and the assembler, and SplitDisk has no opinion about
any of it. The makefile's POSIX comment is the same story: SplitDisk uses
not one POSIX interface, so it names the two that do rather than counting to
three.

Media/ is a home for the screenshot that is coming, with a note saying what
belongs in it and that docs.sh will catch a link to something removed from
it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-08-21 16:47:13 -04:00
AnachronautandClaude Opus 5 4e5710a1e6 Make the README's references clickable, and check that they land
The two manuals and the CosmOS README were named in prose and nothing else,
so reading about them and getting to them were separate acts. They are links
now, along with every directory in the repository map, which is the other
place somebody reading that page wants to click.

THE MANUALS HAVE SPACES IN THEIR NAMES, so the links carry %20. That detail
is why this is checked rather than eyeballed: a link with a raw space in it
points at a file that exists, so nothing about the filesystem is wrong -
the renderer just stops at the space and the link goes nowhere useful.

Tests/docs.sh now walks every relative link in every tracked markdown file
and complains about both failures: a target that is not there, and a target
with a space that should have been encoded.

Both verified by breaking them. The first attempt at that verification was
itself wrong - I removed the %20 and the check passed, because the file
really does exist under that name. That is what showed the two failures are
different things and both need catching.

13 links, all landing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-08-21 16:29:21 -04:00
6 changed files with 76 additions and 18 deletions
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

+8
View File
@@ -0,0 +1,8 @@
# Media
Screenshots and anything else that is looked at rather than read or built.
Nothing here is used by the build or the tests. The repository's README links to what is
in here, and `Tests/docs.sh` checks that every one of those links lands, so a file removed
from here without the link going too will fail the suite rather than becoming a broken
image on the front page.
+21 -15
View File
@@ -2,7 +2,11 @@
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 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 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. ![CosmOS assembling a program, running it, and disassembling what it built](Media/CosmOS.png)
*The whole loop in one screen. `Asm.sbx` assembles `hello.asm` into `hello.sbx`; the shell loads that and runs it; then the monitor disassembles what is at 0x2000, which is the program the machine wrote for itself at the top of the screen. No host is involved at any point.*
**SplitBit assembles SplitBit.** [`Programs/CosmOS/Assembler/`](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 > load Asm.sbx
@@ -16,15 +20,15 @@ wrote Asm.sbx: program 7533, data 4099, labels 555
| Directory | What it holds | | Directory | What it holds |
| --- | --- | | --- | --- |
| `Source/Emulator` | The machine: CPU, memory controller, devices, console, disk | | [`Source/Emulator`](Source/Emulator) | The machine: CPU, memory controller, devices, console, disk |
| `Source/Assembler` | The assembler that runs on a host | | [`Source/Assembler`](Source/Assembler) | The assembler that runs on a host |
| `Source/DiskTool` | SplitDisk, which reads and writes SplitBit's filesystem | | [`Source/DiskTool`](Source/DiskTool) | SplitDisk, which reads and writes SplitBit's filesystem |
| `Programs/Examples` | Programs to read: hello, a calculator, Fibonacci, a prime sieve, Life | | [`Programs/Examples`](Programs/Examples) | Programs to read: hello, a calculator, Fibonacci, a prime sieve, Life |
| `Programs/Libraries` | Code included by name rather than linked, since there is no linker | | [`Programs/Libraries`](Programs/Libraries) | Code included by name rather than linked, since there is no linker |
| `Programs/Loader` | The standalone loader CosmOS grew out of | | [`Programs/Loader`](Programs/Loader) | The standalone loader CosmOS grew out of |
| `Programs/CosmOS` | The operating system, its applications, and the native assembler | | [`Programs/CosmOS`](Programs/CosmOS) | The operating system, its applications, and the native assembler |
| `Programs/testPrograms` | What the test suite drives | | [`Programs/testPrograms`](Programs/testPrograms) | What the test suite drives |
| `Tests` | The suite: the manifest, the recorded output, and the scripts that check it | | [`Tests`](Tests) | The suite: the manifest, the recorded output, and the scripts that check it |
## The Machine: ## The Machine:
@@ -55,6 +59,8 @@ make
The sources are ISO C and build clean under `-std=c11 -pedantic` with `-Wall -Wextra`. Beyond ISO C they need POSIX.1-2008, which the makefile asks for by name, and `getopt_long` for the long form of the command line options. The sources are ISO C and build clean under `-std=c11 -pedantic` with `-Wall -Wextra`. Beyond ISO C they need POSIX.1-2008, which the makefile asks for by name, and `getopt_long` for the long form of the command line options.
Building needs nothing else. **Running the tests needs `bash` and Python 3**, because two of the checking scripts are Python and one of them opens a pseudo-terminal to ask questions a recorded file cannot answer. They also use `stty`, `timeout`, and the usual text utilities: `sed`, `awk`, `grep`, `cmp`, `diff`, `tr`, `sort`, `wc`.
Assemble something and run it: Assemble something and run it:
``` ```
@@ -174,23 +180,23 @@ because a recorded file cannot answer its question:
A cycle count is deliberately **not** part of a recorded result. The last line of the emulator's output has the number taken out before anything is compared, keeping only whether the program stopped on its own or ran into its limit, which is behaviour. Two instructions added to CosmOS used to move that number in six unrelated files at once, so a real difference would have arrived in a crowd of meaningless ones. Anything that wants to measure cycles should say so in a test of its own. A cycle count is deliberately **not** part of a recorded result. The last line of the emulator's output has the number taken out before anything is compared, keeping only whether the program stopped on its own or ran into its limit, which is behaviour. Two instructions added to CosmOS used to move that number in six unrelated files at once, so a real difference would have arrived in a crowd of meaningless ones. Anything that wants to measure cycles should say so in a test of its own.
To rebuild both tools with the address and undefined behaviour sanitizers and run the suite under them: To rebuild all three tools with the address and undefined behaviour sanitizers and run the suite under them:
``` ```
make sanitize make sanitize
``` ```
This catches reads and writes past the end of an array, use after free, leaks, and undefined arithmetic. It also fills fresh allocations with a junk pattern, which turns a read of uninitialised memory from something that quietly works into something the tests notice. It takes about twice as long as `make test`, and puts the ordinary binaries back when it finishes. This catches reads and writes past the end of an array, use after free, leaks, and undefined arithmetic. It also fills fresh allocations with a junk pattern, which turns a read of uninitialised memory from something that quietly works into something the tests notice. It runs everything `make test` runs, takes about twice as long, and puts the ordinary binaries back when it finishes.
## Documentation: ## Documentation:
Three documents, divided by what they are about rather than by who reads them. Three documents, divided by what they are about rather than by who reads them.
The **SplitBit Programming Manual** describes **the machine**: the instruction set, the registers, the vector table, interrupts, devices, the memory controller, the console, storage, and faults. Everything here is true of any SplitBit, whatever is running on it. **[SplitBit Programming Manual](SplitBit%20Programming%20Manual.md)** describes **the machine**: the instruction set, the registers, the vector table, interrupts, devices, the memory controller, the console, storage, and faults. Everything here is true of any SplitBit, whatever is running on it.
The **SplitBit Assembler Manual** describes **the language and the file formats**: literal values, labels, segments, the directives, the loadable program header, and the assembler that runs on SplitBit itself. **[SplitBit Assembler Manual](SplitBit%20Assembler%20Manual.md)** describes **the language and the file formats**: literal values, labels, segments, the directives, the loadable program header, and the assembler that runs on SplitBit itself.
`Programs/CosmOS/README.md` describes **the operating system**: its shell, its applications, what a program may ask it for, and the libraries it owns. A different system on the same machine would answer all of that differently, which is why it is documented with the system rather than with the CPU. [Programs/CosmOS/README.md](Programs/CosmOS/README.md) describes **the operating system**: its shell, its applications, what a program may ask it for, and the libraries it owns. A different system on the same machine would answer all of that differently, which is why it is documented with the system rather than with the CPU.
## License: ## License:
+1 -1
View File
@@ -1,4 +1,4 @@
# SplitBit Assembler Manual: # The SplitBit Assembler Manual:
SplitBit assembly syntax is similar to many other assembler syntaxes. Whitespace at the start or end of a line is disregarded by the assembler and may be used to make programs more readable to the programmer. The Instruction Mnemonics are listed in the SplitBit Programming Manual, and the assembler is not case sensitive in regard to the mnemonics. SplitBit assembly syntax is similar to many other assembler syntaxes. Whitespace at the start or end of a line is disregarded by the assembler and may be used to make programs more readable to the programmer. The Instruction Mnemonics are listed in the SplitBit Programming Manual, and the assembler is not case sensitive in regard to the mnemonics.
+30
View File
@@ -59,6 +59,36 @@ for name in tracked.split(b"\0"):
% (path, number, ", ".join("%r (U+%04X)" % (c, ord(c)) for c in odd))) % (path, number, ", ".join("%r (U+%04X)" % (c, ord(c)) for c in odd)))
break break
# ---- Every link in the documents goes somewhere ----
#
# A link that does not resolve is the same kind of wrong as a stale claim: it looks like
# information and is not, and nobody notices until a stranger clicks it. The manuals have
# SPACES IN THEIR NAMES, so a link to one carries %20 and has to be unquoted before it can
# be looked for - which is the sort of thing that would otherwise be got wrong once and
# then reported as fine.
import os
import urllib.parse
for name in tracked.split(b"\0"):
if not name or not name.endswith(b".md"):
continue
path = name.decode()
here = os.path.dirname(path)
for match in re.finditer(r"\[[^\]]*\]\(([^)]+)\)", read(path)):
target = match.group(1)
if target.startswith(("http://", "https://", "#", "mailto:")):
continue
# A raw space ends the link early in most renderers, so the file existing is not
# enough - the manuals have spaces in their names and must carry %20.
if " " in target:
problems.append("%s links to \"%s\", which has a space in it: most renderers"
" stop at the space. Write it as %%20."
% (path, target))
continue
wanted = urllib.parse.unquote(target.split("#")[0])
if not os.path.exists(os.path.normpath(os.path.join(here, wanted))):
problems.append("%s links to %s, and there is nothing there" % (path, target))
# ---- Every instruction has a row, and every row is an instruction ---- # ---- Every instruction has a row, and every row is an instruction ----
# #
# A mnemonic begins with a letter, which is what keeps the offset and size columns of the # A mnemonic begins with a letter, which is what keeps the offset and size columns of the
+16 -2
View File
@@ -11,7 +11,8 @@ PREFIX ?= /usr/local
# editing a header rebuilds everything that includes it. # editing a header rebuilds everything that includes it.
DEPFLAGS = -MMD -MP DEPFLAGS = -MMD -MP
# Both tools use POSIX interfaces that ISO C does not have: realpath, clock_gettime, # The emulator and the assembler use POSIX interfaces that ISO C does not have:
# realpath, clock_gettime,
# strdup, dirname and getopt. Asking for POSIX.1-2008 by name means the build does # strdup, dirname and getopt. Asking for POSIX.1-2008 by name means the build does
# not rely on the compiler happening to default to a mode where those are visible, # not rely on the compiler happening to default to a mode where those are visible,
# and it survives someone overriding CFLAGS, which is why it is kept separate. # and it survives someone overriding CFLAGS, which is why it is kept separate.
@@ -104,10 +105,17 @@ test: $(EMU_TARGET) $(ASM_TARGET) $(DSK_TARGET) strict
@echo @echo
@./Tests/docs.sh @./Tests/docs.sh
# Rebuild both tools with the address and undefined behaviour sanitizers and run # Rebuild all three tools with the address and undefined behaviour sanitizers and run
# the test suite under them. Slower than 'make test', and worth running before a # the test suite under them. Slower than 'make test', and worth running before a
# release or after anything that touches memory handling. # release or after anything that touches memory handling.
# #
# THE WHOLE SUITE, which it did not used to be: it built all three tools sanitized and
# then ran only run.sh and terminal.sh, so SplitDisk was compiled with the sanitizers and
# never exercised, and native.sh - which drives the assembler and the emulator harder than
# anything else here - was skipped. Those are the parts where block arithmetic on disk
# images and buffer indexing in two assemblers live, which is exactly what the sanitizers
# are for. Adding the three of them costs about six seconds.
#
# The sanitizers catch reads and writes off the end of an array, use after free, # The sanitizers catch reads and writes off the end of an array, use after free,
# leaks, and undefined arithmetic. They also fill fresh allocations with a junk # leaks, and undefined arithmetic. They also fill fresh allocations with a junk
# pattern, which is what turns a read of uninitialised memory from something that # pattern, which is what turns a read of uninitialised memory from something that
@@ -123,7 +131,13 @@ sanitize:
@echo "Running the test suite under AddressSanitizer and UndefinedBehaviorSanitizer." @echo "Running the test suite under AddressSanitizer and UndefinedBehaviorSanitizer."
@./Tests/run.sh @./Tests/run.sh
@echo @echo
@./Tests/disk.sh
@echo
@./Tests/terminal.sh @./Tests/terminal.sh
@echo
@./Tests/native.sh
@echo
@./Tests/docs.sh
@$(MAKE) --no-print-directory clean @$(MAKE) --no-print-directory clean
@$(MAKE) --no-print-directory @$(MAKE) --no-print-directory
@echo "Sanitizer run finished cleanly. Normal binaries rebuilt." @echo "Sanitizer run finished cleanly. Normal binaries rebuilt."