'do <file>' runs the lines in a file as though they had been typed. The
only thing a script changes is where the next line comes from: everything
below shellReadLine - splitting the line, matching it, loading a program -
cannot tell the difference and does not have to.
What makes a file a script is '#!' on the front of it, not its name and not
a flag in its entry. The rule this filesystem keeps is that an entry holds
only what the content cannot say about itself, and a script can say what it
is; the loader already refuses anything that is not SBEX, so the two kinds
of runnable file turn each other away without either knowing about the
other. It is also the deferred half of the file-typing design, which said
to wait for a second kind of runnable thing before building any of it. This
is that second kind.
'#' is a directive and ';' is a comment, as in SplitBit assembly - one rule
across the machine rather than two dialects. Not Unix's convention: there
'#!' really is a comment that only the kernel reads, while here the shell
requires it and refuses the file without it, so calling it a comment would
be a lie about what it does.
A script stops at the first line that does not work, which is what the
LineFailed groundwork was for. Comments and blank lines are dropped by the
reader rather than by the dispatch, so they are not echoed either. A script
running out hands back to the console rather than ending the shell, because
running out of file and running out of typing are not the same thing. The
interactive assembler reads through the same path, so a script can contain
a block of assembly.
Three things this cost that were not obvious:
- RET puts A and B back, so a routine cannot answer in them. scriptByte
returning the character in A assembled, ran, and handed the caller its
own A back every time. It answers in memory now.
- A last line with no newline is still a line. Text files do not reliably
end with one and an editor eating it is a bad way to find out a command
did not run.
- Not LastStatus. See the commit before this one.
Six checks in three tests, two of which are about byte positions rather
than behaviour - a command lying across the boundary between two blocks,
and that missing newline - so their fixtures are generated rather than
committed, where an editor cannot helpfully repair them.
Nesting is not in yet: a script cannot run a script. That wants a stack of
positions rather than the one the reader keeps.
Also derives native.sh's self-hosting source list from cosmos.asm's own
#Include lines. It was a hand written list and went stale the moment
script.asm existed - the fourth time a list beside a thing has drifted from
the thing - so it now asks the thing.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
784 lines
57 KiB
Plaintext
784 lines
57 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 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
|
|
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 | - | -
|