adea75e50a
I'm pretty happy about how lightweight the implementation turns out to be.
150 lines
5.7 KiB
Plaintext
150 lines
5.7 KiB
Plaintext
# Collapse OS usage guide
|
|
|
|
If you already know Forth, start here. Otherwise, read primer
|
|
first.
|
|
|
|
We begin with a few oddities in Collapse OS compared to tradi-
|
|
tional forths, then cover higher level operations.
|
|
|
|
# Signed-ness
|
|
|
|
For simplicity purposes, numbers are generally considered
|
|
unsigned. For convenience, decimal parsing and formatting
|
|
support the "-" prefix, but under the hood, it's all unsigned.
|
|
|
|
This leads to some oddities. For example, "-1 0 <" is false.
|
|
To compare whether something is negative, use the "0<" word
|
|
which is the equivalent to "0x7fff >".
|
|
|
|
# Branching
|
|
|
|
Branching in Collapse OS is limited to 8-bit. This represents
|
|
64 word references forward or backward. While this might seem
|
|
a bit tight at first, having this limit saves us a non-
|
|
negligible amount of resource usage.
|
|
|
|
The reasoning behind this intentional limit is that huge
|
|
branches are generally an indicator that a logic ought to be
|
|
simplified. So here's one more constraint for you to help you
|
|
towards simplicity.
|
|
|
|
# Interpreter I/O
|
|
|
|
The INTERPRET loop, the heart of Collapse OS, feeds itself
|
|
from the C< word, which yields a character every time it is
|
|
called. If no character is available to interpret, it blocks.
|
|
|
|
During normal operations, C< is simply a buffered layer over
|
|
KEY, which has the same behavior (but unbuffered). Before
|
|
yielding any character, the C< routine fetches a whole line
|
|
from KEY, puts it in a buffer, then yields the buffered line,
|
|
one character at a time.
|
|
|
|
Both C< and KEY can be overridden by setting an alternate
|
|
routine at the proper RAM offset (see impl.txt). For example,
|
|
C< overrides are used during LOAD so that input comes from disk
|
|
blocks instead of keyboard.
|
|
|
|
KEY overrides can be used to, for example, temporarily give
|
|
prompt control to a RS-232 device instead of the keyboard.
|
|
|
|
Interpreter output is unbuffered and only has EMIT. This
|
|
word can also be overriden, mostly as a companion to the
|
|
raison d'etre of your KEY override.
|
|
|
|
# Aliases and Switches
|
|
|
|
A common pattern in Forth is to add an indirection layer with
|
|
a pointer word. For example, if you have a word "FOO" for
|
|
which you would like to add an indirection layer, you would
|
|
rename "FOO" to "_FOO", add a variable "FOO*" pointing to
|
|
"_FOO" and re-defining "FOO" as ": FOO FOO* @ EXECUTE".
|
|
|
|
This is all well and good, but it is resource intensive and
|
|
verbose, which make us want to avoid this pattern for words
|
|
that are often used.
|
|
|
|
For this purpose, Collapse OS has two special word types:
|
|
alias and switches.
|
|
|
|
An alias is a variable that contains a pointer to another word.
|
|
When invoked, we invoke the specified pointer with minimal over-
|
|
head. Using our FOO example above, we would create an alias
|
|
with "' _FOO :* FOO". Invoking FOO will then invoke "_FOO". You
|
|
can change the alias' pointer with "*!" like this:
|
|
"' BAR ' FOO *!". FOO now invokes BAR.
|
|
|
|
A switch is like an alias, but with a second level of indi-
|
|
rection. The variable points to a cell pointing to our word.
|
|
It works like an alias, except you have to use ":**" and "**!".
|
|
Switches are used by core code which point to hardcoded
|
|
addresses in RAM (because the core code is designed to run from
|
|
ROM, we can't have regular variables). You are unlikely to
|
|
need switches in regular code.
|
|
|
|
# Addressed devices
|
|
|
|
A@ and A! are the indirect versions of C@ and C!. They are
|
|
aliases and initially point to C@ and C!. There is also a AMOVE
|
|
word that is the same as MOVE but using A@ and A!.
|
|
|
|
Addressed device words can be useful to "pipe" processing to
|
|
places outside of regular memory.
|
|
|
|
# Disk blocks
|
|
|
|
Disk blocks are Collapse OS' main access to permanent storage.
|
|
The system is exceedingly simple: blocks are contiguous
|
|
chunks of 1024 bytes each living on some permanent media such
|
|
as floppy disks or SD cards. They are mostly used for text,
|
|
either informational or source code, which is organized into
|
|
16 lines of 64 characters each.
|
|
|
|
Blocks are referred to by number, 0-indexed. They are read
|
|
through BLK@ and written through BLK!. When a block is read,
|
|
its 1024 bytes content is copied to an in-memory buffer
|
|
starting at BLK( and ending at BLK). Those read/write
|
|
operations are often implicit. For example, LIST calls BLK@.
|
|
|
|
When a word modifies the buffer, it sets the buffer as dirty
|
|
by calling BLK!!. BLK@ checks, before it reads its buffer,
|
|
whether the current buffer is dirty and implicitly calls BLK!
|
|
when it is.
|
|
|
|
The index of the block currently in memory is kept in BLK>.
|
|
|
|
Many blocks contain code. That code can be interpreted through
|
|
LOAD. Programs stored in blocks frequently have "loader blocks"
|
|
that take care of loading all blocks relevant to the program.
|
|
|
|
Blocks spanning multiple disks are tricky. If your media isn't
|
|
large enough to hold all Collapse OS blocks in one unit, you'll
|
|
have to make it span multiple disks. Block reference in
|
|
informational texts aren't a problem: When you swap your disk,
|
|
you mentally adjust the block number you fetch.
|
|
|
|
However, absolute LOAD operations in Collapse OS aren't aware
|
|
of disk spanning and will not work properly in your spanned
|
|
system.
|
|
|
|
Although the usage of absolute LOAD calls are minimally used
|
|
(relative LOADs are preferred), they are sometimes unavoidable.
|
|
When you span Collapse OS over multiple disks, don't forget to
|
|
adjust those absolute LOADs.
|
|
|
|
# How blocks are organized
|
|
|
|
Organization of contiguous blocks is an ongoing challenge and
|
|
Collapse OS' blocks are never as tidy as they should, but we
|
|
try to strive towards a few goals:
|
|
|
|
1. Block 0 contains documentation discovery core keys to the
|
|
uninitiated.
|
|
2. B1-B4 are for a master index of blocks.
|
|
3. B5-B259 are for programs loaded at runtime.
|
|
4. B260-B599 are for bootstrapping a new core.
|
|
5. B600-B650 are for recipes.
|
|
|
|
Recipes blocks do not live in the main blkfs, but each recipe
|
|
has its own blkfs overlay, with blocks beginning at 600.
|