The memory map gave CosmOS 0x0000 through 0x1FFF of Program Memory and
applications 0x2000 and above. CosmOS is 8141 bytes at the previous commit,
which is fifty one bytes short of the line, and the next thing added to it
went over.
GOING OVER DOES NOT FAIL WHERE IT HAPPENS. Nothing enforces the division: an
application says where it goes with #Base and the loader puts it there, so a
CosmOS that has grown past 0x1FFF simply has the next program loaded written
over the end of it. What breaks is whichever part of the shell that program
happened to cover, at whatever later moment somebody uses it. It turned up here
as the monitor's assemble command answering "I do not know" to valid
instructions, several commands into a session, on a machine that had booted
perfectly well.
Both halves are doubled: applications now start at 0x4000 in Program Memory and
0x2000 in Data Memory. That is 16K of code and 8K of data for the system,
against the 8775 and 2948 it uses today. Both were on the same trajectory, and
moving them together means the twenty files that say #Base are edited once
rather than twice.
The standalone loader's loadable.asm keeps its old base: it belongs to the
loader CosmOS grew out of, not to CosmOS, and its addresses answer to a
different program. The unbased-segment diagnostic keeps its old base too - it
exists to produce an error message that names the address, and the message is
what is recorded.
Tests/docs.sh now reads the two limits out of the table in the README and
measures both segments against them. It reads them rather than being told them
because the table is the specification, and this is the second time in this
project that the thing nobody checked is the thing that rotted.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
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
Last of the four. What was left after the reorder was a document whose first
heading was "General Description" doing a part title's job without being
one, and a section called "Input and Output In the Emulator" that held two
console ports, a worked program, and a file format.
A title and an opening that says what this document is FOR, and what the
other two are for, so a reader who wants the operating system or the
language knows immediately they are in the wrong file.
"General Description" is "The Machine", which matches the three part
headings the reorder gave the rest.
"Input and Output In the Emulator" is "Making It Print Something", which
is what the section is: port 0, and the shortest program that uses it.
THE BOOT IMAGE FORMAT MOVES TO THE ASSEMBLER MANUAL, beside the loadable
program format, for the reason SBEX went there: it is a thing the assembler
WRITES. It is fair that the emulator reads them too - both tools speak it,
the way SplitDisk and sbfs.asm both speak the filesystem - but only one of
them makes one.
And it is called a boot image now, in the text as well as the heading. That
is what this project has been calling these files for a while; the manual
was still saying "binary", which now means either kind of output file and so
means neither.
A CHECK THAT GOT BETTER BY BEING SPLIT. The hello world program and the hex
dump of it were both in the Programming Manual, and docs.sh compared them
with each other and with the assembler. The program stays with the machine,
where the reorder put it just after the instruction list; the dump goes with
the format it demonstrates. So the check now settles THREE things against
each other: what one manual prints, what the other prints, and what the
assembler actually makes. Verified both ways - a wrong byte in the dump, and
the anchor renamed.
The manual is 692 lines and four parts. It was 1,116 lines and nineteen flat
sections when this started.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
386 of the manual's 1,116 lines documented an operating system rather than
a machine. The split inside that file was never tutorial against reference;
it was the machine against the software that happens to run on it.
What A Program May Ask The System For 129 -> CosmOS README
Programs That Come With The System 111 -> CosmOS README
Reading And Writing The Filesystem 64 -> CosmOS README
Loading A Program From A Disk 52 -> Assembler Manual
The Console Library 25 -> CosmOS README
The services are the clearest case: a hundred and thirty lines describing
what CosmOS offers a program, in the manual for a CPU that has no operating
system of its own. A different system on the same machine would offer
different services and that section would be wrong for it.
The loadable program format goes to the Assembler Manual instead, because
SBEX is a thing the assembler WRITES. Nothing in the CPU knows what it is.
The Programming Manual is 716 lines and fourteen sections now, all of them
about the machine.
TWO DUPLICATE DESCRIPTIONS COLLAPSED INTO ONE EACH. The application list
existed in both documents in different words, and the CosmOS copy had gone
stale - no Break, no Stream, no assembler - because only the manual's copy
was checked. Moving the checked one in and deleting the other leaves one
list, and docs.sh follows it.
The second was made by this commit and caught while reading the seams: the
CosmOS README already had a service table, so the move briefly produced two.
That section now says what services are for and points at the one table.
Renaming a section as it moved: "Reading And Writing The Filesystem" is
"The Filesystem Library", which says what it is and reads beside "The
Console Library".
docs.sh follows all five, and each was verified by renaming the heading in
its new home and reading the complaint. The README and the CosmOS README
both described what the other manuals cover, and both were wrong the moment
this landed; they say the division out loud now, since it is the point.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
"All files must be plain ASCII, the user's tooling doesn't support Unicode"
is a standing rule of this repository. Nothing enforced it, so it drifted:
39 em dashes and an ellipsis had collected in the two manuals, every one of
them typed by something that helpfully substituted a nicer character. The
spaced em dash becomes a spaced hyphen, which is what the source comments
and both READMEs use for the same job.
Tests/docs.sh now checks every tracked file and says which line and which
character. Verified that it bites.
THE CHECK READS git ls-files NUL SEPARATED, and that is the whole reason
this went unnoticed. I ran the obvious shell version of this audit two
commits ago - a loop over $(git ls-files) - and reported the repository
clean. It splits on whitespace, so it looked for a file called "SplitBit",
failed into /dev/null, and found nothing wrong with either manual because it
never opened them. Both have spaces in their names.
A check that cannot see the files with spaces in their names is worse than
no check at all, because it answers.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
The manual checks find what they examine by splitting the file on an exact
heading. Ten of the eleven anchors already say what they could not find -
"the Programming Manual has lost its Devices table" and so on. Two did not:
the worked hello world program and the hex dump beside it were reached with
pm.split(anchor)[1] and nothing else, so renaming either produced a Python
traceback and an IndexError.
A traceback is a worse answer than a stale manual. It says a check broke
without saying which heading moved, and it stops the rest of the run, so
whatever else was wrong stays unreported.
Both anchors are now tested before they are used, and both say which one is
missing and what that means. Verified by renaming each and reading the
message.
This is the first of four commits restructuring the Programming Manual, and
it comes first on purpose: the next three move headings around, and they
should be watched by checks that would notice.
IT ALSO CORRECTS THE PLAN. I had written that renaming a heading fails
silently, and set out to fix all eleven. Probing them one at a time showed
that was wrong - ten were already fine, and the job was one check rather
than the whole file. The claim was worth testing before acting on it.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
Programs/CosmOS/Assembler/ is an assembler written in SplitBit assembly. It
runs under CosmOS, reads source off a SplitBit disk, and writes a binary back
to it with no host involved anywhere:
> run Asm.sbx hello.asm
wrote hello.bin: program 17, data 14, labels 2
THE ACCEPTANCE TEST IS THE BYTES. Tests/native.sh assembles Programs/hello.asm
both ways and compares the two files byte for byte, then runs the one the
machine built. "It ran" and "the sizes look right" both pass for a binary with
a label one byte out, which is a program that jumps into the middle of an
instruction - so the only honest test is the one SplitDisk and sbfs.asm
already work under: two implementations of one written specification, each
checking the other. The files are identical and the result prints Hello,
World! in 70 cycles.
hello.asm is the target because it is the oldest program in the repository.
The first thing this machine ever ran is now the first thing it assembles for
itself.
TWO PASSES OVER STREAMED SOURCE. The C assembler reads every token of every
file into one array; that cannot port, because cosmos.asm alone is 56,047
bytes against 64K of Data Memory. The native one streams through a 256 byte
window, twice, and keeps only the label table between the passes. Two passes
suffice because every length is known without resolving anything - an
instruction's from its shape, a value's is one, a string's is its characters
and a zero - so the first pass fixes every address and the second never needs
a fixup list. A forward reference stops being a special case and becomes the
reason there are two passes at all.
The parts, each checked before anything was built on it:
source.asm characters out of a file of any size, with a line number
token.asm tokens out of characters, one character of lookahead
classify.asm what a token is, in the C assembler's order, which IS the
language: keyword, instruction, value, string, label
labels.asm names packed in an arena, four bytes of index each
numbers.asm sixteen bit arithmetic, since sbfs.asm's cannot be reached
table.asm the instruction set, generated by the same script the
monitor's copy is, and now BOTH are checked by docs.sh
readTest.asm and tokenTest.asm check the reader and the tokenizer on their
own, recorded as cosmosSource and cosmosTokens. A wrong classification does
not produce a wrong byte somewhere obvious; it produces a right looking
program of the wrong length, so it is worth catching where it happens.
WHAT IT REFUSES: #Include, #Base, #Align, #Reserve and #Vectors are refused
by name rather than ignored. Skipping a directive would produce a file that
looked right and was the wrong length, which is the worst thing an assembler
can do.
Two traps worth recording, both already known to this project and both hit
again: CALL restores A, B and DP0-DP2, so three routines returning an answer
in A had it undone by their own return; and numStep works on DP0, so three
sites that set DP1 left a pointer that never advanced.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
Two changes that arrived together because both live in cosmos.asm.
THE SERVICES. A loaded program that wanted a file had to include the whole
filesystem, carrying two and a half kilobytes of a private copy of code the
system already had running, and then mount a disk that was already mounted.
Five services are added at pinned numbers 20 to 24: osFileRead, osFileSave,
osFileDelete, osFileRename and osPrintNumber.
The sizes fit the registers exactly in both directions. A file that can be
read into Data Memory is under 64K by definition, so its length is sixteen
bits: coming back it is DP3, going out it is A and B together, and neither
direction needs a record in memory whose shape both sides must agree on.
There is deliberately no service to mount a disk. The system mounts one
before its first prompt, and a program mounting it again was only ever a
consequence of owning a second copy of the library, so that call disappears
rather than moving. Apps/Files.asm writes, reads, renames and deletes a file
in 645 bytes and includes nothing but the service names.
THE MONITOR. Previously an application, now part of the shell, because an
application occupies the one region a loaded application is given: a monitor
that was an application could never examine another one, since loading the
thing to be inspected would replace the thing doing the inspecting.
"monitor" turns it on and the prompt becomes "*". It is a mode rather than a
sub-prompt, and it persists: because the mode is a variable the prompt reads
rather than a second loop, and every path back to the prompt goes through one
place including osExit, a program started with "g" that gives the machine back
arrives at the monitor prompt it was started from. Examining a program and
running it therefore do not interrupt each other. "exit" leaves whatever you
are in.
It supersedes dump, and adds disassembly, writing bytes, and jumping to an
address. Its instruction table is generated from the assembler's own list by
Tests/instructiontable.py rather than typed again, and Tests/docs.sh checks
both that the system's copy matches the generator and that the lengths that
table implies are the ones the manual's Bytes column prints. A disassembler
that disagreed about a length would not print one line wrong, it would lose
its place and print everything after it wrong.
Also here: b refuses a bank that is not registered, since asking the
controller for one is refused and a refusal nobody catches stops the machine;
g records the Stack the way run does, without which a program returning
through osExit restored whatever the last run had left; and make cosmos-disk
now depends on the system as well as the image.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>