# CosmOS ## Overview: CosmOS is a small, single-tasking disk operating environment for the SplitBit 8-bit computer. It boots the machine, finds and mounts an SBFS filesystem, provides a command line and memory monitor, loads applications from disk, and takes control back when they finish. CosmOS is written entirely in SplitBit assembly. It is closer in scale and purpose to a resident monitor or an early disk operating system than to a modern multitasking OS: one program owns the machine at a time, there is no privilege boundary, and applications are assembled for fixed regions of memory. What it provides is a stable home from which those programs can be found, run, and given services without each one having to boot the machine for itself. ### Features: - Interactive Shell: Read commands from the SplitBit console and continue until `exit` or the end of input. - SBFS Filesystem: Mount, list, read, write, delete, and rename files on a SplitBit disk. - Paths: Anywhere a filename is taken, a path may be given instead - names with `/` between them, with `.` and `..`. Directories are read but not yet made; the host tool makes them. - Working Directory: `cd` moves the machine, `dir` lists where it is, and the prompt says where that is once it is not the root. A program may move too, and the shell puts the working directory back when the program stops. - Making Directories: `mkdir` and `rmdir` on the machine, and files written where their path says, so a disk can be organised without the host tool. - Loadable Applications: Validate SBEX files, copy their Program and Data segments into the addresses for which they were assembled, and start them at their declared entry point. - Invocation By Name: A word the shell has no command for is looked for on the disk as `.sbx`, and loaded and started if it is there. Built-in commands are tried first. - Resident Services: Applications can print strings and numbers, read lines, receive their command arguments, read and write files, and return to the shell through named software interrupts. - Application Vectors: Install interrupt vectors carried by a loadable program and restore whatever they replaced when the program exits. - Stack Reclamation: Save the system Stack before launching an application and take it back on exit, so an application need not unwind itself before returning. - Memory Monitor: Inspect and modify Program Memory, Data Memory, and registered device-memory banks through the SplitBit memory controller, disassemble instructions, and begin execution at an address. - Hardware Discovery: Mount the disk through the device registry rather than assuming that one is present at a particular controller bank. - Native Applications: Includes demonstrations, mathematical programs, interactive programs, a game, and a line-oriented text editor. - Reproducible Disk Image: The makefile assembles the system and every application, then constructs a fresh SBFS image containing the resulting executables. ## Building and Running: CosmOS currently lives inside the SplitBit Emulator repository and uses its assembler, emulator, and disk-image tool. From the repository root, build those tools first: ```sh make ``` That builds CosmOS, all of its applications, and a disk to boot them from - one makefile covers the machine and the system. To rebuild only part of it: ```sh make cosmos make disk ``` To boot CosmOS with that disk attached: ```sh make run-cosmos ``` Or on the Voyager, which is the same machine with a screen and a speaker instead of a terminal: ```sh make run-voyager ``` Both depend on the disk rather than merely using it, which is worth knowing: **what is on a disk is whatever was built when the disk was made.** A machine whose console has changed will start an old image quite happily and its programs will draw whatever the old way now means, which is a confusing thing to debug and an easy thing to avoid. The generated files are kept under `Programs/build/`: - `CosmOS/Source/cosmos.bin` is the bootable CosmOS image. - `CosmOS/Apps/*.sbx` are loadable application images. - `cosmos.img` is the SBFS disk containing those applications. **The disk carries every source in `Programs/`, mirrored.** Not a list kept in the makefile - a list goes stale the moment somebody adds a program and forgets to name it, and what they forgot is invisible until they go looking for it on the machine. Putting a file where the others live is the whole of putting it on the disk. That matters most for the things nobody thought worth shipping a binary of. A demo that is not interesting enough to build by default is still worth having the source of, because the machine can build it: ``` > cd /Source/Examples /Source/Examples> Asm colours.asm wrote colours.bin: program 114, data 86, labels 8 ``` Two things are left behind. `build`, because what a project builds is not what it wrote. And anything whose name is longer than a directory entry holds, which is **refused rather than skipped**: a disk quietly missing a file is exactly the failure a mirror exists to prevent, so the build stops and says which name to shorten. `/Lib` still holds the library sources separately, because that is where an `#Include` looks after looking beside the file that asked. The same files therefore appear twice - once as what a program includes, once as part of the source tree - and that is the difference between an installed library and a copy of the source. The disk is rebuilt from scratch when its applications change, so its contents describe the current source tree rather than accumulating files left by older builds. ## Scripts: `do ` runs the lines in a file as though somebody had typed them. Every command works the same way it does at the prompt, because the only thing a script changes is where the next line comes from - the shell splits it, matches it and runs it without knowing the difference. ``` #! script ; Build the system and put it where the machine will find it. echo building CosmOS cd /Source/CosmOS Asm cosmos.asm echo done ``` `echo` is a command rather than a program on purpose. `Say.sbx` has printed words since before there were scripts and is the wrong shape for one: being a program, it has to be found on the disk and loaded and started, it prefixes what it was told with `it says:`, and the system prints `finished` after it - three lines of noise around one line of narration. **The first two bytes must be `#!`**, or the shell refuses the file and says so. That is what tells a script from anything else, and it is deliberately not the name and not a flag in the directory 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 does not begin `SBEX`, so the two kinds of runnable file turn each other away without either of them having been told about the other. What follows the `#!` is ignored. It is where the name of an interpreter goes if there is ever a second one; today there is one and it is this shell. **`#` is a directive and `;` is a comment, exactly as in SplitBit assembly.** One rule across the machine rather than two dialects: `#` means this line is about the file, `;` means ignore this line. Comments and blank lines never reach the shell at all - they are dropped by the reader, so they are not echoed and the dispatch never sees a line it would have to know to ignore. This is not Unix's convention and is not trying to be; there `#!` genuinely is a comment that only the kernel looks at, while here the shell requires it. **Each line is echoed as it runs**, after the prompt, so that a script reads exactly like somebody typing it and a script that stops says where. **A script stops at the first line that does not work.** A build whose first step failed and whose second step ran anyway produces something wrong and reports success, which is the whole reason the shell now remembers whether a line worked. What counts as not working is a command that failed, a name the shell does not know, or a program that exited with a status. Nothing is printed but `stopped: that line did not work` - whatever failed has already said what was wrong in words. **A script running out is not the same as typing running out.** The console ending means there is nobody there and the shell stops; a script ending means go back to whoever asked for it, so the next line comes from the console again. The interactive assembler reads its lines the same way, so a script can contain a block of assembly and end it with a `.` just as you would by hand. **A script can run another script, four deep.** What is remembered when one script starts another is a position and not a buffer - the name, which block comes next, how many are left, and where in the block it had got to. The block itself is read again on the way back, which costs one disk read per return and saves a 257-byte buffer per level. Four is deep enough for a script calling a script that calls a helper, and shallow enough that a script which runs itself says `do: scripts are only four deep` rather than filling memory. **A line that fails stops every level**, not just the innermost. A build whose helper script failed should not carry on in the script that called the helper. ## Shell Commands: CosmOS currently provides these built-in commands: | Command | Description | | -- | -- | | `dir` | List the files on the mounted disk and their sizes. | | `load ` | Read and validate an SBEX application, then place its code and data where its header requests. | | `run [words]` | Start the loaded application and make the rest of the line available to it as an argument. | | `do