Files
SplitBit-Emulator/Tests/manifest
T
AnachronautandClaude Opus 5 71f6e215f9 The shell remembers what was typed before it
Up walks back through the last eight lines and Down forward again. It exists
only because the keys reach the system now: until A1 and A2 there was nothing to
press Up at, and the line was assembled somewhere the shell could not see.

A RING RATHER THAN A LIST. A ninth line pushes the oldest out by moving where
the ring starts, not by moving any of the lines - so keeping a line costs a copy
of that line and nothing else, however full the history is. Eight is a power of
two, so which slot an entry lives in is an AND. The ISA had the awkward part
already: A and B are a sixteen bit shift register, so one SHR with B empty turns
a slot number into the offset of a 128 byte slot, high byte and low, ready for
DPUW.

A NINTH SLOT HOLDS WHAT WAS BEING TYPED when Up left it, and Down brings it
back. Losing a half written line to a keypress is the sort of small rudeness
that makes a thing unpleasant to use, and it costs one slot to avoid.

An empty line is not kept, and neither is one the same as the line already at the
top. The test proves the second by looking one further back: if a repeated
command were kept twice, the line behind the newest would be the same line
again.

The redraw had to learn to rub out. One space was enough while the only thing
that shortened a line was taking one character out of it; a recalled line
replaces the whole of it, and a short line over a long one left the tail of the
long one on screen looking like part of what you were typing. It now covers
exactly what was lost - which turned out to be one space fewer than before in
the cases that GREW, so two lines of cosmosEditKeys lost a trailing space that
was never doing anything.

Costs 1157 bytes of Data Memory, taking CosmOS to 6220 of the 8192 it has before
a loaded program's data begins. Worth writing down: that is the budget, and this
is the largest single thing in it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-09-01 12:24:23 -04:00

919 lines
67 KiB
Plaintext

# SplitBit test manifest
# Written by Anachronaut
#
# One test per line, fields separated by '|'. Blank lines and lines starting
# with '#' are ignored.
#
# name | source | mode | stdin | limit | disk | keys
#
# source is relative to Programs/. Everything assembles from there with
# Libraries/ on the include path, and the binary is written into Tests/build.
#
# Modes:
# run Assemble, execute, compare all output against Tests/expected/<name>.out
# assemble Assemble only. For library files that have no entry point to run
# xfail Assembly is expected to fail, and the assembler's diagnostic is recorded
# and compared like any other output. Catches a known breakage that stops
# being detected, an accidental new one, and a message that changes
# without anybody meaning it to
#
# limit is a cycle count, for programs that never halt on their own. It is passed
# to the emulator as --cycles, which bounds the run by cycles rather than by wall
# clock time and so keeps the recorded output identical from one run to the next.
#
# disk names a disk image to attach, made fresh inside Tests/build for every run so that
# nothing a test writes can be seen by the next one. Leave it off for a machine with no
# disk, which is most of them. A trailing :ro attaches it write protected.
#
# keys names a file in Tests/input to be fed to the console as a KEYBOARD rather than as
# standard input, and the difference is the point. Standard input reaches a console that
# believes a terminal is handling the line editing, which is true when one is. A keyboard
# reaches a console that knows it has to do the editing itself - gathering a line, rubbing
# out a backspace, handing it over only at Return - which is what happens behind a window,
# where there is no terminal to do any of it.
#
# A disk name with a directory in it, such as disks/sbfs.img, is one of the images that
# Tests/makedisks.sh builds with SplitDisk before the run. Those are used as they stand,
# so a test can read a filesystem written by the other implementation of the format.
# Every program is run with --fast, since the emulated cycle rate has no bearing
# on what a program prints.
# ---- Programs that halt on their own ----
hello | Examples/hello.asm | run | - | -
printHello | Examples/printHello.asm | run | - | -
8bitFibonacci | Examples/Fibonacci/8bitFibonacci.asm | run | - | -
16bitFibonacci | Examples/Fibonacci/16bitFibonacci.asm | run | - | -
32bitFibonacci | Examples/Fibonacci/32bitFibonacci.asm | run | - | -
colours | Examples/colours.asm | run | - | -
# frames waits for the screen sixty times, which is one second at the emulated rate. It is
# here because the count is exact: a frame is counted in the machine's own cycles, so the
# same program sees the same sixty however fast the host really went.
frames | Examples/frames.asm | run | - | -
# picture draws a whole 320 by 200 bitmap and prints nothing, so what is recorded is only
# that it ran and what it cost. Tests/video.sh is where the pixels are checked.
picture | Examples/picture.asm | run | - | -
# tune plays the Anachronaut Labs theme and prints nothing, so what is recorded is that it
# ran, that it finished on its own, and how much of its time it spent asleep - which is
# 99.8% of it, because the tempo is the screen's frame and a program waiting on a frame is
# not using the bus. Tests/sound.sh is where the samples are checked.
#
# THE BUDGET IS A DURATION, and that is the thing to know if this ever fails. 567 frames of
# music at 16,667 cycles a frame is 9.45 million, so twelve leaves room to lengthen the tune
# without coming back here - and a failure saying "cycle limit reached" means somebody added
# bars, not that anything broke.
tune | Examples/tune.asm | run | - | 12000000
8bitSieve | Examples/primeSieve/8bitSieve.asm | run | - | -
16bitSegmentedSieve | Examples/primeSieve/16bitSieve.asm | run | - | -
# The four pointer rewrite. It emits exactly the same primes as the line above, which
# is the whole point of keeping both: the pair is a direct before and after.
16bitSegmentedSieveModern | Examples/primeSieve/16bitSieveModern.asm | run | - | -
mathTest | testPrograms/mathTest.asm | run | - | -
printTest | testPrograms/printTest.asm | run | - | -
int16print | Libraries/int16print.asm | run | - | -
# ---- The multiple Data Pointer behaviour, which nothing else exercises ----
dataPointerTest | testPrograms/dataPointerTest.asm | run | - | -
twoPointerCopy | testPrograms/twoPointerCopy.asm | run | - | -
pointerTableTest | testPrograms/pointerTableTest.asm | run | - | -
staticTableTest | testPrograms/staticTableTest.asm | run | - | -
dispatchTest | testPrograms/dispatchTest.asm | run | - | -
# ---- Branching both ways round ----
# Each of the four conditions is checked taken and not taken, so a branch that always
# went the same way would be caught rather than looking right half the time.
branchTest | testPrograms/branchTest.asm | run | - | -
# ---- Moving an ALU result back into an operand register ----
moveQTest | testPrograms/moveQTest.asm | run | - | -
# ---- The memory controller ----
# Reading and writing Program Memory, which the instruction set deliberately cannot do,
# and the bank table that says what is reachable.
controllerReadTest | testPrograms/controllerReadTest.asm | run | - | -
controllerWriteTest | testPrograms/controllerWrite.asm | run | - | -
# Block transfers: between banks, within one, overlapping, and one that is refused.
blitTest | testPrograms/blitTest.asm | run | - | -
# Reading a filesystem that the host tool wrote. The two are separate implementations of
# one written format, so this is where any drift between them would show.
sbfsReadTest | testPrograms/sbfsReadTest.asm | run | - | - | disks/sbfs.img
# Walking the directory rather than searching it, which is what listing a disk needs.
# Twelve files is more than the eight an entry block holds, so the walk has to cross into
# the second directory block to see them all.
sbfsWalkTest | testPrograms/sbfsWalkTest.asm | run | - | - | disks/sbfs.img
# Writing a filesystem, then reading back what was written. The disk starts with a file
# on it, so allocation has to find room rather than start at the beginning.
sbfsWriteTest | testPrograms/sbfsWriteTest.asm | run | - | - | disks/write.img
# Deleting, renaming, and saving over something already there, which is what any tool that
# edits a document needs and what none of the tests above touch. The interesting line is
# the third: saving a longer version moves the file, because files here are contiguous and
# do not grow. The last two lines are a rename and a delete having actually happened.
sbfsEditTest | testPrograms/sbfsEditTest.asm | run | - | - | disks/edit.img
# Loading a program off a disk and running it. Everything below this line existed before
# the loader did; the only new part is the sixteen bytes on the front of a loadable
# program saying where it goes.
loader | Loader/loader.asm | run | - | - | disks/load.img
# Storage. The image is made fresh for each run, so block 3 starts as zeroes.
diskTest | testPrograms/diskTest.asm | run | - | - | disk.img
# The same disk attached write protected, so the device bars writes rather than software.
diskProtectTest | testPrograms/diskProtectTest.asm | run | - | - | protected.img:ro
# A program that loads a program: blits code into Program Memory, installs a vector at
# run time, and calls it. If the vector install ever silently failed, the SWI would fault
# with "no handler" rather than printing, so this test cannot pass by accident.
loaderTest | testPrograms/loaderTest.asm | run | - | -
# Giving a bank number to a device's memory, which is what an OS does at boot.
registerBankTest | testPrograms/registerBankTest.asm | run | - | -
# The fence: raised, walked into, clipped by a blit, read through, and lowered again.
fenceTest | testPrograms/fenceTest.asm | run | - | -
# Meant to fault: a bank knows how big it is.
bankBoundsTest | testPrograms/bankBoundsTest.asm | run | - | -
# ---- A device saying no ----
# Refusing is not interrupting: it stops the instruction that asked, rather than asking
# for attention later. The device on port 0x11 refuses everything so this path stays
# tested before the memory controller becomes its only real user.
refusalTest | testPrograms/refusalTest.asm | run | - | -
# The same, with nothing installed to catch it. Meant to fault.
refusalFaultTest | testPrograms/refusalFaultTest.asm | run | - | -
# ---- Asking the machine what it is made of ----
registryTest | testPrograms/registryTest.asm | run | - | -
# ---- Moving the cursor along ----
paddingTest | testPrograms/paddingTest.asm | run | - | -
# ---- Finding the Stack, and moving it ----
stackPointerTest | testPrograms/stackPointerTest.asm | run | - | -
# MVDS, the dangerous one: a system taking its Stack back from a program that stopped
# without unwinding. The sequence is the one the Programming Manual prints under "The
# Stack Pointer, Set By Hand", so the manual's example cannot quietly stop working.
stackReclaimTest | testPrograms/stackReclaimTest.asm | run | - | -
# ---- The Interrupt Flag ----
# Nothing reads the flag yet. This checks that setting and clearing it leaves
# the registers and the Carry Flag it shares a byte with untouched.
interruptFlagTest | testPrograms/interruptFlagTest.asm | run | - | -
# ---- Vectors laid down by the assembler ----
# These are the interrupt path end to end. Until #Vectors existed, none of them could
# be written as a source file at all, because nothing could install a handler.
vectorTest | testPrograms/vectorTest.asm | run | - | -
# Numbers written down and numbers handed out, in one program. A vector number only matters
# where two separately assembled programs have to agree about it, so those are pinned by
# hand from a range the assembler never allocates, and everything else is drawn from above
# it. This pins both ends of that range and leaves a third to the assembler, then calls all
# three by name: a number going astray shows up as the wrong word rather than as silence.
pinnedVectorTest | testPrograms/pinnedVectorTest.asm | run | - | -
# A service handing something back, which RETI otherwise makes impossible: it restores every
# register from the frame, so a handler that worked something out has no way to say so. The
# answer is written into the frame over the saved register. All three cases are here - a
# service that says nothing and leaves Q exactly as it found it, one that answers in Q, and
# one that answers in DP3 - because the first is what makes the other two safe to rely on.
serviceReturnTest | testPrograms/serviceReturnTest.asm | run | - | -
deviceTest | testPrograms/deviceTest.asm | run | - | -
faultResumeTest | testPrograms/faultResumeTest.asm | run | - | -
# The worked example out of the Assembler Manual, so the manual cannot go stale.
interruptExample | testPrograms/interruptExample.asm | run | - | -
# ---- Holding a device off, and then letting it through ----
# Faults on purpose. The fault is the proof that the line was answered, and where it
# lands in the output is the proof of when.
maskTest | testPrograms/maskTest.asm | run | - | -
# ---- Dispatching through a vector with nothing in it ----
# Also meant to fault. Until the assembler can lay a vector table down, every entry
# reads as zero, so this is the only half of the interrupt path a source file can
# reach on its own. The round trip through a handler is covered by hand built
# binaries until #Vectors arrives.
swiFaultTest | testPrograms/swiFaultTest.asm | run | - | -
# ---- Meeting a byte that is not an instruction ----
# This one is meant to fault. It checks the CPU stops, says what it found and
# where, and exits non zero, rather than stepping over it and carrying on.
faultTest | testPrograms/faultTest.asm | run | - | -
# ---- The modern console library ----
# Every routine in console.asm, called with the cases that are easy to get wrong: a zero,
# the largest thing that fits, and a leading zero that should not print. The reading half
# is given a line longer than its buffer, so truncation and the swallowing of the rest
# are shown rather than assumed.
consoleTest | testPrograms/consoleTest.asm | run | consoleTest.in | -
# The console's status and control ports. Input here is a file rather than a terminal, so
# key mode has no terminal to change and only the mode bit moves: that is deliberate, since
# a program has to behave the same either way and a test needing a terminal could not run.
# What it pins down is that READY is clear at the end of input while ENDED is set, so a
# loop reading while READY stops on its own instead of taking imaginary bytes forever.
consoleModeTest | testPrograms/consoleModeTest.asm | run | consoleModeTest.in | -
# The console interrupting instead of being asked. The main program never touches the
# console at all, so every byte in that output was delivered by a handler. It also pins
# down the two things that make the feature usable rather than merely present: the end of
# input raises the line once, so an interrupt-driven program is told when to stop, and a
# handler that does not read the byte is not called again, so nothing storms.
consoleInterruptTest | testPrograms/consoleInterrupt.asm | run | consoleInterruptTest.in | -
# Picking a typed line apart, which is how the shell understands anything. Includes a
# string beginning with a zero: the assembler strips the quotes before deciding what a
# token is, so such a string looked like a malformed literal and was refused.
textTest | testPrograms/textTest.asm | run | - | -
# Strings that spell instructions. The quotes are gone by the time a token is classified, so
# "ADD" in a Data Segment was assembled as the ADD instruction and refused with a message
# about a mistake nobody had made. Mnemonics match without regard to case, so "or" and "and"
# were caught too - ordinary words to want in a message.
stringMnemonic | testPrograms/stringMnemonic.asm | run | - | -
# ---- CosmOS ----
# The system and its shell, driven by a script of commands. This is the first thing that
# uses the machine as a machine rather than exercising one part of it: it boots, mounts a
# disk written by the host tool, reads lines, and walks a directory to answer 'dir'.
cosmos | CosmOS/Source/cosmos.asm | run | cosmos.in | - | disks/sbfs.img
# The same with nothing attached. A shell that only works with a disk in the drive is not
# finished, so the empty machine is a case in its own right rather than an accident.
cosmosNoDisk | CosmOS/Source/cosmos.asm | run | cosmosNoDisk.in | -
# Loading a program and running it, which is the whole machine at once: the filesystem
# finds it, the controller writes it into Program Memory, the vector table carries its
# requests back to the system, and MVDS takes the Stack back when it stops. Every way
# load can refuse is tried first, and run is asked for twice, so the Stack being reclaimed
# rather than merely abandoned is what makes the second one work.
cosmosRun | CosmOS/Source/cosmos.asm | run | cosmosRun.in | - | disks/cosmos.img
# The monitor, which is part of the shell rather than a program: a mode you go into and stay
# in. The targets are chosen to be stable - a loaded program's code and data, and the bank
# table - rather than the system's own code, which would churn whenever any library changed.
#
# Looking at the bank table is worth having on its own. It is the machine describing itself,
# and it shows the disk buffer that sbfsMount registered as bank 3 at boot.
#
# The last part is what the mode is FOR: a program typed in as bytes, run with g, and the
# prompt that comes back is the monitor's own. A program giving the machine back lands where
# it was started from, so looking at something and running it do not interrupt each other.
#
# It also asks to write into bank 2, the controller's own table, which is published read
# only. That used to stop the machine, and the recorded output of this test contained the
# crash without anybody noticing, which is what blessing a result without reading it buys.
#
# It ends with a whole program typed in: a string poked into Data Memory, instructions
# assembled into Program Memory, and the result run - which prints something no assembler
# ever saw. The mnemonics deliberately include a lower case one, both selector forms, an
# instruction that does not exist and one missing its value, so that the refusals are
# recorded next to the successes and a failed line is shown writing nothing.
cosmosMonitor | CosmOS/Source/cosmos.asm | run | cosmosMonitor.in | - | disks/cosmos.img
# The original hello.asm, brought over as an application. It is not much of a program,
# but it is the one that talks to the hardware directly: it writes to port 0x00 instead
# of calling osPrintString, so it is the case where a program reaches past the system and
# the system has to get control back anyway. run is asked for twice for the same reason
# it is in cosmosRun, and here it also says the Stack comes back from a program that never
# entered a service handler at all.
cosmosHello | CosmOS/Source/cosmos.asm | run | cosmosHello.in | - | disks/cosmos.img
# Life, which is the one program that had no end state to reach. It stops when the board
# settles, and the glider does settle: it crosses the field, hits the dead border, and
# collapses into a block at generation 54. NO CYCLE LIMIT ON PURPOSE. Whether it stops on
# its own is the whole point of the port, and a limit would answer that question for it.
#
# NOTHING IS TYPED AFTER run, and that is load bearing rather than tidy. Life polls the
# console between generations, and a byte sitting in the pipe is a byte waiting on the
# console as far as the machine is concerned - exactly as typing ahead at a terminal
# would be. An "exit" on the next line stops it at generation 1 and gets eaten. The shell
# ends the run by reaching the end of input instead, the way cosmosNoDisk does.
cosmosLife | CosmOS/Source/cosmos.asm | run | cosmosLife.in | - | disks/cosmos.img
# The other half of that: a key IS waiting, so it stops at once instead of at generation
# 54. The two together are what say the poll is reading the console rather than always
# answering the same way.
cosmosLifeKey | CosmOS/Source/cosmos.asm | run | cosmosLifeKey.in | - | disks/cosmos.img
# A whole game of Snake, played by a script. Life is watched; this is steered, and it is
# the first program on this machine that reads the console without ever waiting for it.
# One key is taken per frame, so twelve bytes in the pipe are twelve moves rather than
# twelve moves at once: six turn it down the board and six take it left onto the food. So
# what is recorded is a turn, a meal, a longer snake, and then a wall.
cosmosSnake | CosmOS/Source/cosmos.asm | run | cosmosSnake.in | - | disks/cosmos.img
# A loaded program that brings a vector of its own, which nothing could do until the
# loadable format could carry one. It is run TWICE on purpose: installing has to be exactly
# undone by removing, and the way that fails is asymmetrically, so the second run is the one
# that catches it. The dump afterwards is the proof that the table was put back - the
# console's slot at FE00 is zero again, and CosmOS's own disk handler further along is
# untouched by a program having installed over the top of it.
cosmosKeys | CosmOS/Source/cosmos.asm | run | cosmosKeys.in | - | disks/cosmos.img
# The keys that are not characters, both halves of the rule in one run: a line read in line
# mode with an arrow key in the middle of it, which arrives with the arrow dropped because
# line mode delivers characters, and then the same keys in key mode where they are what the
# program asked for. Fed as STANDARD INPUT, so the console is reading bytes that are exactly
# what is in the file - no terminal is involved and no escape sequence is translated, which
# is what makes an arrow key testable at all.
cosmosPress | CosmOS/Source/cosmos.asm | run | cosmosPress.in | - | disks/cosmos.img
# The same program and the same keys, fed as a KEYBOARD instead. That is the other front
# end - the window's path through the gatherer - and it has its own copy of the line mode
# rule, so it needs its own test. The two recordings should agree, which is the point.
cosmosPressKeys | CosmOS/Source/cosmos.asm | run | - | - | disks/cosmos.img | cosmosPress.in
# A program with NO DATA SEGMENT, loaded and run. Every program written for this system had
# something in its data until somebody wrote one that did not, and the loader stopped the
# machine dead: a segment of no bytes asks the controller for the whole 64K, which does not
# fit, so it refused in the middle of loading. The most basic form a program can take, and
# nothing had ever tried it.
#
# It is run twice, because it is a toggle and the second run has to find the mode the first
# one left. The line between them is there so the recording shows the shell still working on
# a forty column screen, which is a width nothing else here ever runs at - and the shell
# reads that width for itself now, every line it edits.
cosmosNoData | CosmOS/Source/cosmos.asm | run | cosmosNoData.in | - | disks/cosmos.img
# The shell editing the line it is being given, which is the whole reason the keys were
# made to arrive. Every line here is typed wrong and then corrected with a different key, and
# every one of them comes out as the same command - so the recording says both what the
# machine printed on the way and that the line the shell finally acted on was right.
#
# The escape sequences in it are the point rather than noise: moving the cursor by hand is
# what a terminal is TOLD about, and they are the only evidence of where the shell thinks it
# is. The last line is eighty five characters at a prompt in column two, so the cursor has to
# be found on the row below - the one case the arithmetic exists for.
cosmosEditKeys | CosmOS/Source/cosmos.asm | run | cosmosEditKeys.in | - | disks/cosmos.img
# And the shell remembering what was typed before it. Nine lines against eight slots, so the
# oldest is pushed out and nine presses of Up can only walk back eight; then the line being
# typed is kept when Up leaves it and comes back on Down; then a line run twice is not kept
# twice, which the LAST case is what proves - if it were, one further back would be the same
# line again instead of the one before it.
#
# The long line at the end is the rubbing out. A short line recalled over a long one leaves
# the tail of the long one on the screen unless something covers it, and one space - which
# is all a Delete ever needs - covers nothing.
cosmosHistory | CosmOS/Source/cosmos.asm | run | cosmosHistory.in | - | disks/cosmos.img
# The shell taking things off a disk and calling them something else, which is the last of
# CosmOS's original four verbs to be built and the first time anything has changed a disk
# from the shell. Its own image, because it changes what is on it: a fixture named with a
# directory is used as it stands, so a test that writes to a shared one changes what every
# test after it sees. The refusals are here too - deleting what is not there, renaming with
# a name missing, and renaming something to a name already taken.
cosmosFiles | CosmOS/Source/cosmos.asm | run | cosmosFiles.in | - | disks/files.img
# A program being told what it is for. Nothing loaded before this could be told anything,
# so every app did the same thing however it was started - which is fine for a demo and no
# use at all to a tool that edits a named document. Run three times: with nothing, with a
# name, and with several words, since what arrives is the rest of the line rather than a
# list and it is the program's business what to make of it.
cosmosSay | CosmOS/Source/cosmos.asm | run | cosmosSay.in | - | disks/cosmos.img
# A program stopping itself to be looked at. Two breakpoints, so what is checked is not only
# that one fires but that the SECOND one does - which is the whole difference between this
# design and one that overwrites an instruction, since an overwritten instruction has to be
# put back to continue and putting it back is the same act as disarming the breakpoint.
# Every value shown comes out of the interrupt frame rather than the registers, because by
# the time the handler runs the registers are the handler's. Break sets all three of the
# pointers it owns and rotates them between the two stops, so every value recorded here is
# one something chose - it used to leave two of them holding whatever the shell had left,
# which tied this record to where CosmOS's code sits and made it churn on changes that had
# nothing to do with breakpoints. DP3 is left as the system set it, being where the program
# was entered.
cosmosBreak | CosmOS/Source/cosmos.asm | run | cosmosBreak.in | - | disks/cosmos.img
# The editor, which is the first program on this machine that makes a file a person typed:
# every byte on every other image here was put there by the host tool. It is run twice in
# one session, and that is the test rather than a flourish - the second run reads back what
# the first one wrote, so the whole path is checked at once: read a name from the argument,
# split a file into lines, edit them, build a file back out of them, and save it over
# something that was already there and is now a different size.
cosmosEdit | CosmOS/Source/cosmos.asm | run | cosmosEdit.in | - | disks/editor.img
# The file services, exercised by a program that includes NOTHING but the service names: no
# filesystem library, no console library. It writes a file, reads it back, says how long it
# was, renames it and deletes it, in 645 bytes - against the editor's 4941, which does less
# with files and carries the filesystem inside it. That difference is the whole case for the
# service layer, and this is where it is checked rather than argued.
cosmosServices | CosmOS/Source/cosmos.asm | run | cosmosFiles2.in | - | disks/services.img
# Streaming, which is what a file bigger than memory needs. The disk here holds 84000
# bytes in one file and the machine has 64K of Data Memory, so there is no arrangement of
# osFileRead that could get at it: the program reads it through a buffer of one block.
#
# THE CHECK THAT MATTERS IS THE SECOND ONE. A small file is read both ways - whole with
# osFileRead, and streamed - and the two checksums have to agree, so streaming is measured
# against the path that already worked rather than against a number somebody wrote down.
# The checksum is Fletcher's rather than a sum, because a sum is the same whatever order
# the bytes arrived in and the order is exactly what streaming has to get right.
#
# The rest of it is the lookup the system keeps so that reading four hundred blocks of one
# file does not search the directory four hundred times. Two files read alternately catch a
# memory that does not notice the name changed, and a rename catches one that does not
# notice the file moved - and that one would otherwise pass, since the blocks are still
# there and still hold the same bytes.
cosmosStream | CosmOS/Source/cosmos.asm | run | cosmosStream.in | - | disks/stream.img
# A useful application made out of the streaming primitive: print a named text file without
# requiring it to fit in Data Memory. The fixture crosses block boundaries, includes a full
# 256-byte block, and also checks empty, missing, and absent filename cases.
cosmosType | CosmOS/Source/cosmos.asm | run | cosmosType.in | - | disks/type.img
# The same stream with interaction layered on it. Three runs exercise q, a whole screen
# with Space, and one line with Return. The input is intentionally packed so that the one
# byte the pager consumes leaves the next shell command immediately behind it.
cosmosMore | CosmOS/Source/cosmos.asm | run | cosmosMore.in | - | disks/type.img
# Copy and Compare exercise both halves of block streaming together. Empty, exact-block,
# part-block and 84,000-byte files are copied through one buffer; Compare checks the copies
# and a same-sized file whose only difference is deep into the input.
cosmosCopyCompare | CosmOS/Source/cosmos.asm | run | cosmosCopyCompare.in | - | disks/copycompare.img
# A working directory deeper than the prompt can print. The prompt is built BACKWARDS into
# 127 bytes, and nothing bounds how deep the directories go - a path is capped at what one
# operation can name, but "cd" a level at a time is well inside that and can be repeated.
# So the walk wrote down past the front of its own buffer and into what the assembler put
# below it, which was the shell's command names: at six directories of twenty two
# characters the word "exit" was gone and the shell no longer knew how to stop.
#
# WHAT IS RECORDED IS THAT THE COMMANDS STILL WORK, and that is the whole point of running
# help and cd and exit from down there rather than just looking at the prompt. A prompt
# that is merely wrong is a cosmetic fault; this one was writing into other variables.
cosmosDeep | CosmOS/Source/cosmos.asm | run | cosmosDeep.in | - | disks/deep.img
# A disk claiming a directory of 8192 blocks, which is 65536 entries. The last of those is
# entry 65535, and the parent field is an index PLUS ONE in two bytes - so it wraps to
# zero, which means the root. Anything created in such a directory goes into the root
# while the caller is told it went where it asked, and looking there afterwards finds
# nothing, so the same create works again and again and fills the root with entries of one
# name. Refused at mount, which is the only place it can be refused once and for all.
cosmosBigDir | CosmOS/Source/cosmos.asm | run | cosmosBigDir.in | - | disks/bigdir.img
# ---- Starting the machine off a disk ----
#
# Stage one is what will one day be in ROM. It knows the disk's ports, that a SplitBit disk
# begins with its own name, and where two numbers sit in that first block - and nothing
# else. It reads the live boot slot into Program Memory and jumps to the first byte.
#
# Two disks, differing only in which slot the superblock names, and the payloads say
# different things. That is what makes this a test of CHOOSING a slot rather than a test
# that some bytes were read: one prints "booted" and the other does not.
bootChain | Boot/stage1.asm | run | - | - | disks/chain.img
bootChainAlt | Boot/stage1.asm | run | - | - | disks/chainAlt.img
# A disk with no boot area cannot be started, and says so in the one character a ROM has
# room to say anything in.
bootNoArea | Boot/stage1.asm | run | - | - | disks/sbfs.img
# A payload that arranges its own Data Segment, by copying it down from just past its own
# code. Stage one places Program Memory and nothing else - knowing where a payload's data
# ended would mean knowing a format - so this is how the real second stage will get the
# variables and the string that sbfs.asm needs.
bootData | Boot/slotData.asm | assemble | - | -
bootDataRuns | Boot/stage1.asm | run | - | - | disks/chainData.img
# ---- The machine starting itself ----
#
# The whole chain: the emulator hands over stage one, stage one reads the live boot slot
# into Program Memory and jumps, stage two mounts the filesystem, finds /System/cosmos.bin,
# takes the image apart and places its code, its data and its vector table, and jumps to
# the entry point the vectors named.
#
# WHAT IS RECORDED IS THAT THE SYSTEM WORKS AFTERWARDS, not that it started. A loaded
# program running is what says the vector table arrived, because a program reaches the
# system through SWI and nothing else; the file written and the directory entered say the
# filesystem and the console came up with it.
selfBoot | Boot/stage1.asm | run | selfBoot.in | 90000000 | disks/selfboot.img
# And a disk with a boot slot but nothing to start, which says so rather than jumping.
selfBootNoSystem | Boot/stage1.asm | run | - | 90000000 | disks/nosystem.img
# ---- And with no image named at all ----
#
# The mode is "rom": the emulator is handed a disk and nothing else, so it shadows its own
# built-in stage one into Program Memory and starts there, which is what a machine with no
# debugger attached does. Naming an image is the debugger, and every other test here is
# using it.
#
# The source column still names stage1.asm even though the binary is thrown away, because
# that is what is IN the ROM: the makefile builds rom.c from that file, so assembling it
# here says the thing the emulator carries is a thing that still assembles.
romBoot | Boot/stage1.asm | rom | selfBoot.in | 90000000 | disks/selfboot.img
# ---- What starts is a setting, not a name compiled into the loader ----
#
# Three disks differing only in /System/Boot/boot.cfg, so each is a test of the file rather
# than of the machinery under it.
#
# cfgBare starts an image with NO OPERATING SYSTEM IN IT, which is what loading an ordinary
# boot image buys: a program wanting the whole machine is a file like any other, and is
# chosen the same way the system is.
cfgBare | Boot/stage1.asm | rom | - | 90000000 | disks/cfgbare.img
# cfgBroken has every kind of mistake in it and a good setting underneath them. The machine
# STILL STARTS - configuration is advice - and says what it could not use on the way, so a
# setting somebody meant which did not happen is not silent.
cfgBroken | Boot/stage1.asm | rom | - | 90000000 | disks/cfgbroken.img
# cfgFallback names a system that is not there. What was tried, and what was tried next.
cfgFallback | Boot/stage1.asm | rom | selfBoot.in | 90000000 | disks/cfgfallback.img
# And the bare image on its own, which has to keep assembling.
bareMetal | Boot/bare.asm | assemble | - | -
# ---- A system that never reaches a prompt ----
#
# Three disks that differ only in the boot state on them, so the three tests read as three
# consecutive starts of one machine while none of them depends on another having run.
#
# wedgeFirst tries it and marks the disk. wedgeSecond finds the mark still set - nothing
# came back to say the last start arrived - and uses the fallback. wedgeThird finds that
# already happened and does the same again WITHOUT retrying, which is the part worth
# pinning down: a system known not to start should not be tried every other boot for ever.
wedgeFirst | Boot/stage1.asm | rom | - | 90000000 | disks/wedgefirst.img
wedgeSecond | Boot/stage1.asm | rom | - | 90000000 | disks/wedgesecond.img
wedgeThird | Boot/stage1.asm | rom | - | 90000000 | disks/wedgethird.img
wedgedImage | Boot/wedged.asm | assemble | - | -
# ---- Closing the loop from inside the machine ----
#
# A machine that says "settle it to try again" and gives you no way to do so has told you
# about a problem it will not let you fix. Settle is a program rather than a shell word:
# one job, reached through SWI, replaceable, and callable by anything that comes to call
# programs in sequence.
# ---- The console doing a terminal's job ----
#
# Behind a window nothing is handling the line editing, so the console does it. Typing "halp",
# backing over it, and arriving at "help" has to reach the shell as "help" - it used to arrive
# with the backspaces still in it, which made a corrected line unrecognisable while looking
# perfectly right on the screen.
cosmosTyped | CosmOS/Source/cosmos.asm | run | - | - | disks/cosmos.img | cosmosTyped.keys
cosmosSettle | CosmOS/Source/cosmos.asm | run | settle.in | 90000000 | disks/settle.img
# Saying so when there is nothing to settle is as much a part of it as doing it.
cosmosSettled | CosmOS/Source/cosmos.asm | run | settle.in | 90000000 | disks/settled.img
settleApp | CosmOS/Apps/Settle.asm | assemble | - | -
# ---- What a program made of it ----
#
# Three programs in a row, each asked after. A status that did not survive the program that
# set it would be no use to the thing this exists for - whatever comes to run programs in
# sequence and has to decide whether to run the next one.
#
# 0 it did what it was asked, 1 it did not, 2 it was asked wrongly.
cosmosStatus | CosmOS/Source/cosmos.asm | run | status.in | 90000000 | disks/status.img
statusApp | CosmOS/Apps/Status.asm | assemble | - | -
# ---- Reading a file with long lines into the editor ----
#
# Opened twice, because once was not enough to see it: the first open corrupted the head of
# the document and the allocator's pointer, and the second walked a list that led back into
# itself and never came back. The emulator kept running and the machine never answered.
#
# Then a file with a line past what the editor can hold, which is REFUSED rather than
# shortened - a line cut here would be written back cut, and the file damaged by having
# been looked at.
cosmosEditLong | CosmOS/Source/cosmos.asm | run | editLong.in | 200000000 | disks/editlong.img
# ---- Starting something else just this once ----
#
# A bare metal program has nowhere to run: starting it from the shell means there is no
# shell, and pointing boot.cfg at it means a machine that keeps starting it. Once writes a
# request the loader reads before boot.cfg and DELETES BEFORE IT JUMPS, so the image runs on
# the next start and on no other, whatever becomes of it.
#
# Three disks, each a start further on, so none of them depends on another having run.
onceAsked | CosmOS/Source/cosmos.asm | run | once.in | 200000000 | disks/onceasked.img
# The start the request was for. It starts an image with no operating system in it at all,
# and THE BOOT STATE IS NOT TOUCHED: a one shot is already self limiting, and marking it
# would report every successful bare metal boot as a start that never arrived.
onceDue | Boot/stage1.asm | rom | - | 200000000 | disks/oncedue.img
# And the start after, which is an ordinary one again.
onceAfter | Boot/stage1.asm | rom | - | 200000000 | disks/onceafter.img
onceApp | CosmOS/Apps/Once.asm | assemble | - | -
# ---- Starting again ----
#
# The loop end to end and inside one session: ask for a one shot, restart, and watch an
# image with no operating system in it take the machine. Before this the only way to
# restart was to stop the emulator, which meant the one thing the machine could not do was
# the thing Once exists for.
#
# A reset repeats HOW THIS MACHINE STARTED. Here no image is named, so the ROM is shadowed
# again and reads the disk.
rebootOnce | Boot/stage1.asm | rom | reboot.in | 300000000 | disks/reboot.img
# And here one IS named, so the reset places it again rather than reading the disk - a
# reset that changed what the machine is would be the one thing a reset must not do.
rebootDirect | CosmOS/Source/cosmos.asm | run | rebootDirect.in | 200000000 | disks/reboot.img
rebootApp | CosmOS/Apps/Reboot.asm | assemble | - | -
# A failed start with NOTHING to fall back to. The mark must not become a reason to refuse
# to start at all - a failure that was passing recovers here, and one that is not leaves
# the machine exactly where it would have been without any of this.
noFallback | Boot/stage1.asm | rom | - | 90000000 | disks/nofallback.img
# Reading a disk that has directories on it. The machine can walk a path at this point but
# cannot make a directory, so the disk is built by the host tool and read here - which is
# the two implementations checking each other rather than either checking itself.
#
# The two Say.sbx are DIFFERENT PROGRAMS under one name - Say echoes its argument, hello
# prints one fixed line - so which one a path reached is written in the output. Two copies
# of the same program would have passed this test with the parent comparison removed
# altogether, which is the one thing it exists to check.
# Then the ways a path does not resolve: through a file, into a directory that is not
# there, onto a directory rather than a program, a component too long to be a name, and
# the root itself. Last, that delete refuses a directory - the refusal that keeps a freed
# entry index from being handed out with children still pointing at it.
cosmosTree | CosmOS/Source/cosmos.asm | run | cosmosTree.in | - | disks/tree.img
# Writing a file onto a disk that has directories on it. The document goes in the root,
# because nothing on the machine can put one anywhere else yet, and the point is that it
# gets there at all: making a file walks the allocator over two directory entries, which
# hold no blocks and must therefore be in nobody's way when a run of free ones is wanted.
# The listing afterwards says where it landed and how big it is.
cosmosTreeWrite | CosmOS/Source/cosmos.asm | run | cosmosTreeWrite.in | - | disks/treewrite.img
# Committing a streamed file bigger than the room reserved for it, which must be refused.
#
# osFileStart sets an extent aside and osFileWrite already refuses a block index outside it,
# so writing off the end is barred. This is the same neighbour reached the other way: claim
# the extra block at commit rather than write into it. A directory entry is the only record
# of what a file owns, so an entry claiming a block it was never given simply owns it, and
# so does whatever owned it before - both files then look perfectly well formed.
#
# The size is compared and not the room it takes up, because those differ: one block and a
# tail occupies what two whole blocks occupy, so bounding the blocks alone would allow a
# file to report two hundred and forty six bytes that were never written to it. The
# reservation here is one block and a tail of ten, and the claim is two whole blocks - the
# same two blocks, and a bigger lie.
#
# The listing at the end is the point: 266 bytes, which is what was reserved and what was
# written, after the refusal and the honest commit that follows it.
cosmosClaim | CosmOS/Source/cosmos.asm | run | cosmosClaim.in | - | disks/claim.img
# The filesystem waiting for a disk that takes time. Every other test here runs with the
# disk finishing before the next instruction starts, which is what it has always done and
# what hides whether anything honours the busy bit. This one gives it a latency.
#
# It is the same directory listing as any other; the point is that it is the SAME. A
# filesystem that did not wait would read the block before the one it asked for, which is
# not an error anywhere - just quietly the wrong bytes - and this listing would be nonsense
# rather than a failure with a message.
cosmosSlowDisk | CosmOS/Source/cosmos.asm | run | cosmosSlowDisk.in | - | disks/cosmos.img@2000
# The machine building its own tree. It starts with a blank version one disk and makes
# every directory on it, which is the half of the filesystem the machine could only read
# until now.
#
# The refusals are most of the test. rmdir will not take a file and delete will not take a
# directory, so neither can be the one that removed more than was asked for; a directory
# with anything in it is refused outright, because a parent is an entry INDEX and a freed
# index goes to the next thing created - the children would turn up inside whatever took
# its place, with nothing pointing downward to find them by. A name already taken in that
# directory is refused, and the same name in a different directory is not, which is the
# whole point of the exercise.
#
# The last dir is there to show the disk still adds up: every entry made and unmade, and
# nothing left over.
cosmosBuild | CosmOS/Source/cosmos.asm | run | cosmosBuild.in | - | disks/build.img
# The working directory. cd moves the machine, the prompt says where it is once that is
# not the root, and dir lists one directory rather than the whole disk.
#
# The two notes.txt say different things ON PURPOSE, and that is the whole test: reading
# "notes.txt" from /A and then from /B has to give two different files. If the remembered
# file were not dropped when the machine moves, the second read would hand back the first
# one - same key, same entry, nothing about it looking wrong.
#
# Then that a program is looked for where you are BEFORE /Apps: /A holds a Say.sbx that is
# really hello, so "Say" from /A prints the wrong thing on purpose, and "Say" from /B has
# to reach the real one in /Apps.
#
# And the two things only a program moving the machine itself can check. Wander calls
# osChangeDir, so after "Wander /A" from /B the prompt has to say /B again - the shell puts
# the working directory back the way it puts the Stack and the vector table back. Reading
# notes.txt straight afterwards has to give B's copy, which is the remembered file being
# dropped when the machine moved. Without Wander neither of those could be made to fail,
# because nothing else on the machine can move it.
cosmosCwd | CosmOS/Source/cosmos.asm | run | cosmosCwd.in | - | disks/cwd.img
# Typing a program's name starts it. The disk is built so that each line of the input asks
# a different question of the one rule: "Say hello there" is a bare name with an argument,
# "Say.sbx" is the same file spelled out in full, and "run once more" says the program that
# was invoked really did land in the loaded slot and can be started again from it.
#
# Then the four ways a word does not become a running program. "dir" is a real program on
# this disk and must still list the disk, because the built-ins are tried first. "notes"
# finds notes.sbx and says it is not a program, which is the answer that has to differ from
# "I do not know" - the file is there. "notes.txt" looks for notes.txt.sbx and finds
# nothing, which is how a text file stays unreachable by name. And the last two are the
# length boundary: eighteen characters is the longest bare name that leaves room for the
# extension, and nineteen cannot be a file name at all, so both are unknown words.
cosmosInvoke | CosmOS/Source/cosmos.asm | run | cosmosInvoke.in | - | disks/invoke.img
# The assembler's front end, checked in two pieces before anything is built on it.
#
# cosmosSource reads a source file and prints it back. The file crosses a block boundary,
# so the seam is exercised rather than assumed, and the line count at the end says the
# reader knows where it is as well as what it holds.
#
# cosmosTokens says what each token IS. That is the part worth checking here rather than at
# the far end: a wrong classification does not produce a wrong byte somewhere obvious, it
# produces a right looking program of the wrong length with everything after it shifted,
# and by then the only symptom is a label pointing into the middle of an instruction.
cosmosSource | CosmOS/Source/cosmos.asm | run | cosmosSource.in | - | disks/asm.img
cosmosTokens | CosmOS/Source/cosmos.asm | run | cosmosTokens.in | - | disks/asm.img
# The programs CosmOS loads, checked on their own so that a failure here reads as "the app
# does not assemble" rather than as a broken disk image.
app-greet | CosmOS/Apps/greet.asm | assemble | - | -
app-hello | CosmOS/Apps/hello.asm | assemble | - | -
app-Life | CosmOS/Apps/Life.asm | assemble | - | -
app-Snake | CosmOS/Apps/Snake.asm | assemble | - | -
app-Keys | CosmOS/Apps/Keys.asm | assemble | - | -
app-Say | CosmOS/Apps/Say.asm | assemble | - | -
app-Break | CosmOS/Apps/Break.asm | assemble | - | -
app-Edit | CosmOS/Apps/Edit.asm | assemble | - | -
app-Files | CosmOS/Apps/Files.asm | assemble | - | -
app-Stream | CosmOS/Apps/Stream.asm | assemble | - | -
app-Type | CosmOS/Apps/Type.asm | assemble | - | -
app-Wander | CosmOS/Apps/Wander.asm | assemble | - | -
app-Pour | CosmOS/Apps/Pour.asm | assemble | - | -
app-Claim | CosmOS/Apps/Claim.asm | assemble | - | -
app-More | CosmOS/Apps/More.asm | assemble | - | -
app-Copy | CosmOS/Apps/Copy.asm | assemble | - | -
app-Compare | CosmOS/Apps/Compare.asm | assemble | - | -
# The assembler that runs on the machine, and its parts. Checked on their own so that a
# failure reads as "it does not assemble" rather than as a broken disk image.
asm-Asm | CosmOS/Assembler/Asm.asm | assemble | - | -
asm-readTest | CosmOS/Assembler/readTest.asm | assemble | - | -
asm-tokenTest | CosmOS/Assembler/tokenTest.asm | assemble | - | -
asm-scratch | CosmOS/Assembler/scratch.asm | assemble | - | -
# ---- Programs driven by console input ----
inputTest | Examples/inputTest.asm | run | inputTest.in | -
inputTestOld | testPrograms/inputTest.asm | run | inputTest.in | -
replCalculator | Examples/replCalculator.asm | run | replCalculator.in | -
# ---- Programs that run forever by design, bounded by a cycle count ----
# 11,200,000 cycles is about fourteen generations of the glider, which puts evolveBoard and
# its pointer juggling through its paces many times over.
#
# It was 3,000,000, and the number changed rather than the amount of work: a cycle used to
# be an instruction and is now a memory access, and the average SplitBit instruction makes
# 3.72 of those. Nothing about these two programs changed at all.
16x16Life | Examples/gameOfLife/16x16Life.asm | run | - | 11200000
# The four pointer rewrite, on the same budget so the two can be compared directly.
# Note that this cannot show a speed difference: frameDelay is 255 by 255 and swamps
# the simulation, so both versions render the same fourteen generations and produce
# identical bytes. What it checks is that the rewrite still evolves the board the same
# way, which is what a regression test is for.
16x16LifeModern | Examples/gameOfLife/16x16LifeModern.asm | run | - | 11200000
# ---- Libraries: no entry point, so only check that they assemble ----
# The CosmOS libraries assemble on their own, unlike print.asm below, which cannot: it
# begins with a branch to a label only the including program defines. That difference is
# the point of the rewrite, so both halves of it are recorded here.
lib-console | CosmOS/Source/console.asm | assemble | - | -
lib-text | CosmOS/Source/text.asm | assemble | - | -
lib-sbfs | CosmOS/Source/sbfs.asm | assemble | - | -
lib-int8 | Libraries/int8.asm | assemble | - | -
lib-int16 | Libraries/int16.asm | assemble | - | -
lib-int32 | Libraries/int32.asm | assemble | - | -
lib-math | Libraries/math.asm | assemble | - | -
# ---- Deliberately broken, to check the assembler still diagnoses them ----
# These are not programs anyone meant to run. Each one contains a single mistake
# that the assembler used to accept quietly, and is here so that it cannot start
# being accepted quietly again.
diagDuplicateLabel | testPrograms/diagnostics/duplicateLabel.asm | xfail | - | -
diagBareInclude | testPrograms/diagnostics/bareInclude.asm | xfail | - | -
diagUnknownVector | testPrograms/diagnostics/unknownVector.asm | xfail | - | -
diagDuplicateVector | testPrograms/diagnostics/duplicateVector.asm | xfail | - | -
# The two mistakes that pinning a number makes possible. Numbers the assembler hands out
# cannot collide; numbers a person writes down can, and can also be written outside the
# range set aside for them.
# A string in the Program Segment, which used to assemble to nothing at all and say so to
# nobody. Strings only have a case for the Data buffer, so one aimed anywhere else fell
# through and vanished.
diagStringInProgram | testPrograms/diagnostics/stringInProgram.asm | xfail | - | -
diagPinnedRange | testPrograms/diagnostics/pinnedVectorRange.asm | xfail | - | -
diagPinnedTaken | testPrograms/diagnostics/pinnedVectorTaken.asm | xfail | - | -
diagBareSWI | testPrograms/diagnostics/bareSWI.asm | xfail | - | -
diagAlignOutside | testPrograms/diagnostics/alignOutside.asm | xfail | - | -
diagBareAlign | testPrograms/diagnostics/bareAlign.asm | xfail | - | -
# One segment based and the other forgotten, which is how the ported Fib-8 put its data
# on top of the console's variables. It assembled and it ran, so nothing but the assembler
# was ever going to catch it.
diagUnbasedSegment | testPrograms/diagnostics/unbasedSegment.asm | xfail | - | -
# ---- Known breakages, recorded rather than ignored ----
# print.asm branches to 'start', which only the including program defines.
lib-print | Libraries/print.asm | xfail | - | -
# These three call print.asm routines but have no #Include line at all. They are
# only ever pulled in by printTest.asm, so they are not standalone programs.
# A string that spells a directive is a string. The quotes are gone by the time a token is
# looked at, so "#Program" in a program's data was read as the directive: the segment
# changed in the middle of the Data Segment and every label after it came out nine bytes
# wrong, in a file that still had a valid header and a plausible length.
stringKeyword | testPrograms/stringKeyword.asm | run | - | -
# The six instructions added after the first sixty four. Four of them move a Data Pointer
# by a register rather than by a byte written into the program, and two are a call that
# puts nothing back.
#
# What is recorded is the DIFFERENCE between the two kinds of call, twice over: the same
# callee sets A, and after the raw one that is what A holds while after the safe one it is
# not; and each reports how far the Stack came down, which is two bytes against ten. A
# version of RCAL that quietly did what CALL does would pass a test that only checked it
# returned to the right place.
rawCallAndOffsets | testPrograms/rawCallAndOffsets.asm | run | - | -
# Falling off the end of a program into Program Memory nobody wrote. It faults on the byte
# after the last instruction rather than executing a run of additions and going wrong
# somewhere else, which is what 0x00 through 0x0F being empty buys.
runOffTest | testPrograms/runOffTest.asm | run | - | -
# WAIT, the seventy first instruction, on a disk given ten thousand cycles of latency. Two
# reads with interrupts masked and no handler anywhere: the machine sleeps through both and
# reads the status port when it wakes. What is recorded here is that it FINISHES - a WAIT
# that never woke would hang, and a hang is what this catches. Whether it slept or spun is
# a question about cycle counts, which settle() strips, so Tests/terminal.sh asks that one.
waitTest | testPrograms/waitTest.asm | run | - | 5000000 | disks/sbfs.img@10000
# The other half of what WAIT is for, and the half that was missing. waitTest checks that a
# masked program which SLEEPS on a device is woken and does not leave the line up; this checks
# the program that never sleeps at all, because the disk finished before the first look. The
# WAIT is unreachable, so nothing but the status read can take the line down. It then sets the
# Interrupt Flag with no handler installed anywhere: a standing line faults there, which is
# how a program loaded by CosmOS used to die on the instruction after its SIF.
diskLineTest | testPrograms/diskLineTest.asm | run | - | 1000000 | disks/sbfs.img
# ---- Scripts ----
#
# The shell reading its lines out of a file instead of off the console. cosmosScript is the
# ordinary case and also checks that comments and blank lines never reach the dispatch, and
# that a script running out hands back to the console rather than ending the shell.
cosmosScript | CosmOS/Source/cosmos.asm | run | cosmosScript.in | 200000000 | disks/cosmos.img
# The three refusals and the stop. A line that fails stops the script - "after" must not be
# in the recording - and a file with no #! is not a script however it is named.
cosmosScriptBad | CosmOS/Source/cosmos.asm | run | cosmosScriptBad.in | 200000000 | disks/cosmos.img
# The two that are about byte positions rather than about behaviour: a command lying across
# the boundary between two blocks, and a last line with no newline after it. Both of these
# were faults before they were checks.
cosmosScriptEdges | CosmOS/Source/cosmos.asm | run | cosmosScriptEdges.in | 200000000 | disks/cosmos.img
# One script inside another, and the limit on how far that goes. The outer script resumes in
# its second block, which is the case the design turns on - the inner one reads its own block
# into the single buffer, so coming back means reading the outer one's again and landing on
# the byte it left. Then a script that runs itself, which stops at four deep and takes every
# level with it, because a build whose helper failed should not carry on in its caller.
cosmosScriptNest | CosmOS/Source/cosmos.asm | run | cosmosScriptNest.in | 200000000 | disks/cosmos.img
# ---- Starting itself ----
#
# /System/Boot/startup.sh runs before anybody can type. This one is also the check on #quiet
# and #loud: the welcome has no prompts interleaved with it, the line after #loud does, and
# the console gets its prompt back when the script ends. And on clear, whose mark on a
# terminal is the escape the console has always sent.
cosmosStartup | CosmOS/Source/cosmos.asm | run | cosmosStartup.in | 200000000 | disks/startup.img
# Present, and not a script. A missing one is ordinary and says nothing; one somebody meant
# to run and got wrong is worth a word.
cosmosStartupBad | CosmOS/Source/cosmos.asm | run | cosmosStartup.in | 200000000 | disks/startupbad.img
# ---- A program that takes the screen, and gives the disk back ----
#
# Grid registers video memory as a bank, and bank numbers are one namespace for the whole
# machine with nothing handing them out. It asked for 3, which is the one CosmOS gives the
# disk's buffer at mount - and that does not fail, it SUCCEEDS: every read the filesystem
# made afterwards came out of video memory. The shell found an empty disk and could not
# start anything by name, and nothing said a word.
#
# So this runs a program by name, then Grid, then the same program again. The second one is
# the check. dir at the end says the disk is still there to be read.
cosmosGrid | CosmOS/Source/cosmos.asm | run | cosmosGrid.in | 60000000 | disks/cosmos.img
# Which disk the registers mean. Several disks are one controller with a drive register
# rather than several devices, because a port is an immediate byte inside the instruction
# that names it and a program cannot compute one. Run with a single disk, so drive 1 is a
# drive that exists with nothing in it - selectable, and failing to read, like an empty
# floppy drive.
driveSelectTest | testPrograms/driveSelectTest.asm | run | - | 200000 | disks/sbfs.img
# ---- Two disks ----
#
# The first thing on this machine to have more than one. drive says which, and going to the
# other shows a disk with nothing in common with the first - so a listing that looked right
# could not have come from the wrong one.
#
# THE WORKING DIRECTORY GOES WITH THE DRIVE, which is the whole point of the mount record.
# Drive 1 is left standing in /notes and drive 0 at the root, and the prompt says which on
# every trip between them - three times each way, because a record that is copied one way
# and not the other would still look right once. A drive this machine has not got is
# refused.
cosmosDrives | CosmOS/Source/cosmos.asm | run | cosmosDrives.in | 60000000 | disks/cosmos.img+disks/other.img
# And a drive named in the path rather than by a command. Handled where every path in the
# system arrives, so it works for anything that takes one. A name beginning with a digit is
# still a name: the colon is the whole of what tells the two apart.
cosmosDrivePath | CosmOS/Source/cosmos.asm | run | cosmosDrivePath.in | 60000000 | disks/cosmos.img+disks/other.img
# ---- Working across two disks ----
#
# The two things anybody expects of a second disk: copying to it, and running a program that
# lives on one disk over files that live on the other.
#
# THE FILE IS MORE THAN ONE BLOCK ON PURPOSE. A copy of a single block looks its source up
# once and never meets the cache that skips the walk - and skipping the walk skipped the
# drive, so block one of every cross-drive copy came off the destination. One block worked
# and two did not.
#
# Say lives in /Apps on drive 0 and is run while standing on drive 1, which is the third
# place the shell looks. The drive it says afterwards is the check that fetching a program
# did not move the person who ran it.
cosmosCrossDisk | CosmOS/Source/cosmos.asm | run | cosmosCrossDisk.in | 90000000 | disks/cosmos.img+disks/other.img
# ---- A beat a program sets for itself ----
#
# That reading the status is what takes the tick down, and that without the repeat bit it
# runs its period out and stops. NOT that the period is the length it says: settle() strips
# cycle counts from these recordings, so how long anything took cannot be checked here.
# Tests/terminal.sh measures that, which is where anything about cycles belongs.
timerTest | testPrograms/timerTest.asm | run | - | 5000000 | -
# And interrupting, which is what a music routine wants. 125,000 cycles is a sixteenth note
# at 120 beats a minute - seven and a half frames, so the screen cannot express it at all.
# Eight of them is a second, and nearly all of that second is spent asleep.
timerBeatTest | testPrograms/timerBeatTest.asm | run | - | 5000000 | -
# ---- A disk made of memory ----
#
# The machine supplies blocks and says the drive is volatile; the SYSTEM decides what that
# means. CosmOS formats a volatile drive it cannot read, because a drive whose contents do
# not survive the machine never had anything to lose - and leaves any other one alone, which
# is why an unformatted floppy is safe from it.
#
# So this boots with a drive of nothing, and the first dir on it is a listing rather than an
# error: the system brought it up. Then a copy onto it, to show it is a disk like any other.
cosmosRamDisk | CosmOS/Source/cosmos.asm | run | cosmosRamDisk.in | 90000000 | disks/cosmos.img+ram:2048
# ---- And the other half of that rule, which is the dangerous half ----
#
# The same blank disk, on a drive the machine has NOT called volatile. It must be refused
# rather than formatted: an unformatted floppy is not an invitation, it is a blank floppy,
# and a system that formatted one on sight is a system you could not safely put a disk into.
#
# blank.img has no directory in its name, so run.sh removes it before every run and the
# emulator makes a fresh one of zeroes - which is exactly the disk this is about.
cosmosBlankDisk | CosmOS/Source/cosmos.asm | run | cosmosBlankDisk.in | 90000000 | disks/cosmos.img+blank.img
printDecimalTest | testPrograms/printDecimalTest.asm | xfail | - | -
printDigitTest | testPrograms/printDigitTest.asm | xfail | - | -
printHexTest | testPrograms/printHexTest.asm | xfail | - | -
# shiftTest calls printDecimal, which print.asm does not have. It looks like the
# routine was renamed and this caller was never updated.
shiftTest | testPrograms/shiftTest.asm | xfail | - | -