"do build.sh cosmos.asm" looked for a file called "build.sh cosmos.asm", because scriptOpen copied the whole rest of the line into the name. So a script could be told nothing, and a launcher - a script whose entire job is to hand on what it was told - could not exist. Now the name is cut off the front and what follows is kept whole. $1 to $9 are the words of it, walked out on demand, and $args is all of them. Nothing is stored per parameter, so there is no limit on how many a script may be handed and no second number to keep in step. Both ways of starting a script pass them on: "do" and typing the name. A word that was not given is an error that stops the script, like every other name this shell does not know. Expanding it to nothing would let a command run with an argument missing and then report success, which is what stop-on-failure exists to prevent. $args is always set inside a script, empty if it was given nothing, so "if same $args" can be asked. ---- And the count that describes the block was already wrong ---- Found while adding a field to it. The state one script keeps for another is saved by a single copy of a fixed number of bytes, and that number was 71 against a block of 77: six bytes of line position had been added in the middle of it years after the count was written. So the tail of every saved script was never saved, and #quiet in a helper stayed behind in the script that called it - the opposite of what this file's own comment promises and the README documents. The unsaved line position turned out not to matter, because a loop keeps its own copy in the block record. Nothing said so. Three numbers describe this block and all three now say so in a comment, and cosmosScriptNest ends on a helper that goes quiet and a caller that must not stay that way. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01E2JrLzFvuFX9fgi1LDRjrW
1191 lines
87 KiB
Plaintext
1191 lines
87 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
|
|
# ---- 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 | - | -
|