Files
SplitBit-Emulator/Tests/manifest
T
AnachronautandClaude Opus 5 2defbb49e2 A program can ask where it came from
SWI osWhereAmI hands back the path the program was loaded from, on the
same terms as osArgument, and Libraries/path.asm joins a name to the
place another thing is in. Between them an application can find its own
assets: ask where you are, then pathBeside that and the file's name.

The answer FOLLOWS THE PROGRAM AND NOT THE PERSON, which is the whole
point and the reason the working directory could not serve. A program's
assets are relative to the program and its arguments are relative to
whoever ran it, and cwd can only be one of them - setting it to the
program's own would mean "Play mytune.tune", typed by somebody in their
own directory, looked in Play's.

It is made absolute before the program starts, because the path the
search settled on may be a bare name: a program found where somebody was
standing is named by the word that was typed, and a bare name means the
working directory - which a program is entitled to move out of. Worked
out once, at the start, since where a program came from is a fact about
its start and cannot change afterwards.

Joining is a LIBRARY and not a service. A service that opened a file
relative to the program would need a twin for every file operation there
is - read, save, info, block, start, write, done, delete, rename - while
one service handing back a path composes with all of them.

---- And the root's own path was "//" ----

Found by the first caller that asks. shellPath prepends a separator in
front of whatever string it is given, so being handed the separator
itself wrote two of them. Nothing saw it while the only caller was the
prompt, which asks where it is only when that is not the root. It is
handed an empty string now, and cosmosWhere runs a program from the root.

Where.sbx exists to be run rather than read, and is on the test disk
twice: at the root, where it is found by the bare word typed, and in
/Apps, where it is found by a path that already says where it is.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
2026-09-06 15:27:57 -04:00

1208 lines
88 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 | pad
#
# 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.
#
# IT IS A PROGRAM NOW rather than a boot image, so the shell starts it and the machine it
# plays on is the one somebody would really be sitting at. That also means its vector is
# INSTALLED BY THE SYSTEM and taken back out when it stops, which is a thing a boot image
# never had to have right.
tune | CosmOS/Source/cosmos.asm | run | tune.in | 12000000 | disks/cosmos.img
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 | - | -
# ---- Multiplying, on a machine with no multiplier ----
#
# a * b = qs[a+b] - qs[|a-b|], where qs[n] is n squared over four: two lookups and a subtract.
# And the table of quarter squares is built by ADDING, because qs[n] = qs[n-1] + n/2 - so
# nothing in the arrangement needs a multiply in order to exist, which is the point of it.
#
# The cases are nought, the commutation both ways round, a square, and 255 times 255, which is
# the largest product two bytes hold. A square is the case the identity leans on hardest: the
# difference term is nought and the whole answer comes from one entry.
mulTest | testPrograms/mulTest.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 | - | -
# And the same fault CAUGHT, which nothing could do until the machine had somewhere to send
# it. Calling a service the system does not implement used to be fatal, and it is an ordinary
# mistake. The number of the empty entry arrives in Q, and the handler steps over the SWI and
# carries on - so the recording says both that the fault was delivered and that a program can
# live through one.
noHandlerTest | testPrograms/noHandlerTest.asm | run | - | -
# The other half: a device raising its line with nothing installed to answer it. Nothing is
# typed at this on purpose - the console raises its line once when input ENDS as well as when
# a byte arrives, and with no input at all that end is what turns up.
noDeviceTest | testPrograms/noDeviceTest.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 system catching a fault instead of the machine stopping on one. All four causes, one
# after another, and a dir at the end: THE POINT IS THE LINE AFTER THE FAULT. Every one of
# these used to stop the machine dead, and behind a window it looked like a hang because the
# diagnosis went to a standard error nobody was looking at.
#
# The addresses are exact and are the reason the recording is worth having. Note that the
# missing service reports the address PAST its SWI, where the others report the instruction
# that failed: those two faults are the only ones where the instruction did dispatch and it
# was the entry that was empty.
cosmosFault | CosmOS/Source/cosmos.asm | run | cosmosFault.in | - | disks/cosmos.img
# And a fault in the system's own code, which has nowhere to go back to.
#
# THE BYTE IS PUT THERE RATHER THAN FOUND THERE. This used to jump into the space above
# CosmOS and below where programs load, on the grounds that it was zeroes - which stopped
# being true the moment CosmOS grew into it, and the test then jumped into the middle of the
# shell and did something else entirely. Writing the byte first means the address is the
# system's half of memory and nothing more is assumed about it.
cosmosFaultSystem | CosmOS/Source/cosmos.asm | run | cosmosFaultSys.in | - | disks/cosmos.img
# A word the MONITOR does not know. There was nothing at the end of its command list, so an
# unrecognised word fell through into sayPrompt - a routine, whose RET then had nothing of its
# own to return to and went wherever the Stack happened to point. That was 0x0003 in the
# middle of newLine one time and inside sbfsFormat another, which formatted the disk the
# machine had booted from.
#
# So all three cases are here: a program run by name, a program that faults, and a word that
# is nothing at all. The monitor is a mode of the shell and everything the shell does is
# meant to work in it.
cosmosMonitorRun | CosmOS/Source/cosmos.asm | run | cosmosMonitorRun.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
# Tab finishing a word somebody started, which is a thing an eight bit shell could not do
# for a structural reason rather than a hard one: it never saw the keystroke, so there was no
# moment at which half a word existed and the system knew.
#
# Every case is here. One match goes in with a space after it. Four that agree on nothing
# more are listed and the line put back underneath - and the line coming back is the half
# worth watching, because the prompt has been reprinted somewhere else and the editor's idea
# of where the line starts has to be asked again rather than remembered. Nothing typed and
# nothing matching both do nothing quietly, and a second Tab later in the line does nothing
# because finishing an argument is not built yet.
#
# The last line is the one that says it works on what is actually there rather than on what
# was typed: two backspaces take "help" down to "he", and Tab finishes it again.
cosmosTab | CosmOS/Source/cosmos.asm | run | cosmosTab.in | - | disks/cosmos.img
# And the half of it that reaches the disk. A program is offered under the name somebody
# would type - the extension taken off - because that is what a first word can usefully be;
# anywhere after the first word everything is offered as it really is. A directory answers
# with a separator rather than a space, which says what it is and lets the next part be
# typed straight away.
#
# A path in the word says where to look, and the shell looks in the three places it would
# look to RUN something: where you are, /Apps on the disk you are on, and /Apps on drive 0.
# Offering what the shell would not find would be finishing a word into something that then
# does not work.
#
# The echoed lines here have no trailing space on them, and that is the line trimming rather
# than the completion: Tab still leaves a space after the word it finished, and the shell
# takes it off the end of the line before anything reads it. So what echo is given is what
# somebody would have typed. See cosmosTrim.
cosmosTabPath | CosmOS/Source/cosmos.asm | run | cosmosTabPath.in | - | disks/cosmos.img
# The spaces at the ends of a line, which the shell takes off both of now.
#
# The leading ones were always taken off, for indented blocks. The trailing ones were not,
# and Tab completion puts one there: it finishes a word and leaves a space, which is right
# when another word is coming and was fatal when one was not. "load greet.sbx" worked and
# the same line finished with Tab did not, because the shell looked for a file whose name
# ended in a space - which took most of the good out of completion.
#
# Four lines: completed with Tab and run straight away, the same trailing space typed by
# hand, spaces at both ends at once, and a line of nothing but spaces, which has to stay a
# line that does nothing rather than becoming one that fails.
cosmosTrim | CosmOS/Source/cosmos.asm | run | cosmosTrim.in | - | disks/cosmos.img
# Names for things, which is what makes a script able to compose a command rather than only
# to contain one. The expansion happens on every line the shell is about to run, typed or read
# from a file, so no command below has to know that variables exist.
#
# A NAME NOTHING WAS SET TO DOES NOT RUN THE LINE. Every other shell expands it to nothing,
# and that is the wrong answer here: a mistyped name would quietly become an empty path,
# which is exactly the kind of silent wrong answer the rest of this system spends its effort
# refusing. Somebody who wants an empty value writes "set name" and gets one - the escape
# hatch exists and has to be asked for.
cosmosVars | CosmOS/Source/cosmos.asm | run | cosmosVars.in | - | disks/cosmos.img
# The Stack is where it was, after eight lines that failed.
#
# EVERY FAILURE IN THIS SHELL ABANDONS A FRAME: commandFailed is reached with CALL and never
# returns, which is the idiom every command uses and is why a failure needs no unwinding.
# Nothing gave those frames back, so the Stack Pointer only ever moved one way - FFFD to FE6D
# over twenty failures. It took thousands to reach anything, which is why nobody had noticed;
# a loop in a script would have got there.
#
# Break is what makes it visible, because it prints the registers. The two dumps have to
# agree, and the failures between them are what would move it.
cosmosStack | CosmOS/Source/cosmos.asm | run | cosmosStack.in | - | disks/cosmos.img
# Lines that are only run sometimes. "if" takes a COMMAND and what follows runs only if that
# command worked - the Bourne shell's answer, and the reason "test" exists there: one rule in
# if, and comparing two things is just another command that can fail. Here that command is
# "same", and the shell already had the other half in LineFailed.
#
# AN if INSIDE A BRANCH NOBODY IS TAKING IS NOT A QUESTION. Its else must not run either, so
# it is pushed as a block that neither branch of can be taken - which is why a block has two
# kinds of not-running rather than one, and why nesting needs no looking down the stack.
#
# THE BRANCH NOBODY TAKES IS NOT EVEN LOOKED AT. The script names a variable that was never
# set, inside the branch that is not taken, and that must not be an error - so the skipping
# happens before the names are filled in, and a line nobody is running cannot fail.
cosmosBlocks | CosmOS/Source/cosmos.asm | run | cosmosBlocks.in | - | disks/cosmos.img
# And the two loops. Both go back to the line that opened them, which is why the script
# reader keeps the position of every line before reading it: by the time a line has been read
# the reader is past it, and a line is not a fixed size to subtract.
#
# A while is TAKEN AWAY at its end and its line asks the question again. A for is not: what it
# has used is in the block, and the line reads itself again and counts one more word off the
# front - which is a byte in a block rather than a copy of the list in each one.
#
# Nested loops, an if inside a loop, a loop inside a branch nobody takes, and a for with no
# words at all, which runs no times rather than once. Neither loop makes sense typed at a
# prompt, and both say so.
cosmosLoops | CosmOS/Source/cosmos.asm | run | cosmosLoops.in | - | disks/cosmos.img
# The same editing offered to a PROGRAM, through osReadLine. Edit reads its lines that way,
# so a word typed with two letters the wrong way round is put right without starting the line
# again - which is the whole of what A4 buys.
#
# It also says what a program does NOT get. Up and Down do nothing while Edit is reading:
# were a program's line walking the shell's history, the second line here would come out as
# the echo command at the top instead of the word. And the last Up proves the reverse - that
# everything Edit read went nowhere near the history, since one press finds the line typed
# before Edit was started.
cosmosEditService | CosmOS/Source/cosmos.asm | run | cosmosEditService.in | - | disks/editor.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
# ---- The same file, on the other screen ----
#
# More shows a screenful less the prompt and asks the rows register how big that is, so the
# page is 47 lines on the eighty column screen and 22 on the forty column one. Mode is run
# first to get there, and it is the only way a test can: nothing else changes the screen.
#
# What this catches is a page size that went back to being a constant. The recording above
# and the recording here are the same file paged two different ways, and a More that had
# stopped asking would make them the same length.
cosmosMoreNarrow | CosmOS/Source/cosmos.asm | run | cosmosMoreNarrow.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.
# It also ends on the state a nested script gives back. The saved block is copied by a fixed
# count, that count was six bytes short of the block, and quiet was in the six - so a helper
# that went quiet left its caller quiet. "echo aloud again" being ECHOED is the check.
cosmosScriptNest | CosmOS/Source/cosmos.asm | run | cosmosScriptNest.in | 200000000 | disks/cosmos.img
# ---- What a script is given ----
#
# $1 to $9 are the words it was handed and $args is all of them. Nothing is stored per
# parameter: the line is kept whole and the word wanted is walked out of it.
#
# Five claims in one session. Both ways of starting a script pass words on - "do" and typing
# the name. A LAUNCHER can hand on what it was told without knowing what any of it means,
# which is the reason this rung is on the ladder at all. A nested script gets its own words
# and the caller still has its own afterwards. And a word that was not given is an ERROR that
# stops the script, like every other name the shell does not know - not silently nothing,
# which would let a command run with an argument missing. At the prompt they are ordinary
# unset names, because there is no script to have been given anything.
cosmosScriptArgs | CosmOS/Source/cosmos.asm | run | cosmosScriptArgs.in | 200000000 | disks/cosmos.img
# ---- Where a program came from ----
#
# osWhereAmI and Libraries/path.asm, which are what an application with assets stands on.
#
# THE ANSWER FOLLOWS THE PROGRAM AND NOT THE PERSON, which is the claim and the reason the
# working directory could not serve: a program's assets are relative to the program and its
# arguments are relative to whoever ran it. Standing in /work, the copy in /Apps says /Apps
# and puts its asset there, while the argument still means what the person typed.
#
# Two copies of it on this disk reach the system by different routes. The one at the root is
# found where you are standing and named by the bare word typed, so the path has to be worked
# out from where the person was; the one in /Apps arrives already absolute. Running it from
# the root is also the check on the root's own path, which came out as "//" until the first
# caller that asks there rather than only when somebody has moved.
#
# And load then run, because that is the other way into the same place.
cosmosWhere | CosmOS/Source/cosmos.asm | run | cosmosWhere.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
# ---- The screen nobody is looking at ----
#
# Flip draws a whole screen into the bank that is not shown. What is checked here is not the
# picture - video.sh has that - but the LINE IT PRINTS, which is the thing the program claims
# will still be there when it comes back. It was not, twice over: the assembler has no string
# escapes, so a "\n" written in a literal printed as two characters; and the program called
# osTakeScreen, which restores the screen as it was BEFORE the line was printed and so wiped
# out the one thing it was pointing at. A program that damages nothing should not ask.
cosmosFlip | CosmOS/Source/cosmos.asm | run | cosmosFlip.in | 60000000 | disks/cosmos.img
# ---- A thing that moves without the screen moving ----
#
# Sprite draws a ball over the shell's own text and writes not one byte of the map to do it,
# which is what a sprite is for. The picture is video.sh's business; what is checked here is
# that the words either side of it are untouched - the line printed before it ran and the one
# printed after are both still readable, because nothing underneath the ball was ever written.
#
# A KEYBOARD FIXTURE and not standard input, which is the difference between a test that
# runs this program and one that only looks like it does. A null on standard input is a
# character like any other, so the first of them satisfied the "has a key arrived" test and
# the ball never moved at all - the whole 150 frames of it went past in the shell, reading a
# line made of nulls. Through a keyboard a null is silence, which is what makes it a wait.
cosmosSprite | CosmOS/Source/cosmos.asm | run | - | 60000000 | disks/cosmos.img | cosmosSprite.keys
# ---- A controller, which is a level and not an event ----
#
# The eighth column is a pad fixture: one byte a frame, each byte the buttons held during it.
# It exists for the same reason the keyboard fixture does - a device only a person can work is
# a device nothing checks - and it matters more here, because a pad is the ONE THING THE
# CONSOLE CANNOT DO. A terminal reports which key was pressed and can never report one coming
# up, so holding a direction, or holding two buttons at once, has no expression there at all.
#
# The recording presses up, holds it, adds right, lets go of up, lets go of everything, then
# presses A and B together. The program reads each frame twice to show that looking does not
# take it away.
padTest | testPrograms/padTest.asm | run | - | - | - | - | padTest.pad
# ---- Lunar Porter, arriving ----
#
# The same program, the same keys, and two different controllers. One holds Right and never
# stops, so it arrives sliding; the other pulses the thruster six frames in sixteen and comes
# down slowly enough to survive.
#
# WHAT SAYS WHICH IS THE SPEED AT THE MOMENT IT ARRIVES, and nothing else - the terrain is the
# same terrain, from the same fixed seed, so the two runs differ only in what was held.
#
# SLIDING RATHER THAN FALLING, and the difference matters. The crash fixture used to hold
# nothing at all, and stopped crashing the day the lander began its day above a base: a short
# drop onto high ground is survivable, which is correct and made the test say nothing. Lateral
# speed has no limit and nothing slows it, so a slide always ends badly however the rest is
# tuned.
cosmosLanderCrash | CosmOS/Source/cosmos.asm | run | - | 60000000 | disks/cosmos.img | lander.keys | landerSlide.pad
# ---- And the same landing, flown from the second controller ----
#
# Two pad fixtures, comma separated: the first holds nothing and the second is the recording
# that lands. So the flying is done entirely on pad ONE, and a game that read only pad nought
# would sit there and crash.
#
# Which is not hypothetical - it is what this did. A controller does not always arrive on
# nought; the front end hands out the numbers the host gave it, so reading only the first
# works on the machine it was written on and silently does nothing on the next one.
# ---- A tank runs out, and gravity is patient ----
#
# The thruster is held from the first frame to the last and it still crashes. Fuel is a byte
# and a thruster costs one a tick, so about forty seconds of holding the engine open empties
# it - and an empty tank is not an ending, it is a lander that is still flying and can no
# longer do anything about where.
#
# Seventy million cycles, and it was forty. A ceiling arrived: holding the thruster now pins
# the lander sixty four pixels above the screen instead of sending it off for ever, so it
# spends the whole tank up there and only then falls the length of the world. The ending is
# the same and it takes longer to arrive at.
#
# Which is what says the fuel is real. A lander that could hold Up for ever would land every
# time, and the whole economy this is the first half of would have nothing to buy.
cosmosLanderDry | CosmOS/Source/cosmos.asm | run | - | 70000000 | disks/cosmos.img | lander.keys | landerBurn.pad
# ---- And the thing that says whether a controller is being seen at all ----
#
# Three states look identical from inside a game that is not responding: a pad the front end
# never noticed, a pad it noticed and mapped to nothing, and a mapping that is simply wrong.
# Pad tells them apart by printing what the MACHINE can see, which is a different question
# from what a front end thinks it is sending - and the gap between those two is where a
# controller that was detected, mapped and reported still did nothing, because the port
# saying which pads exist counted only the recorded ones.
cosmosPadApp | CosmOS/Source/cosmos.asm | run | - | 30000000 | disks/cosmos.img | pad.keys | padApp.pad
# 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
# ---- And a SCRIPT run from the other disk ----
#
# The same question one step harder. A program is wholly in memory before its first
# instruction runs, so fetching it can move the drive and putting it back afterwards is
# enough. A script is read a block at a time WHILE its lines run, and its name carries the
# drive it lives on - so every refill resolves "0:/Apps/where.sh" and every resolution moves
# the machine to drive 0 unless something puts it back. What the script says about where it
# is standing is the check, and it must say 1.
#
# THEN IT MOVES ITSELF and says so again, across another block boundary. A refill has to put
# back the drive the script left the machine on rather than the one it was opened from, or a
# script that goes to another disk to work is dragged home between two of its own lines.
cosmosScriptDrive | CosmOS/Source/cosmos.asm | run | cosmosScriptDrive.in | 60000000 | 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 | - | -