doc: out-of-system documentation
I've changed my mind about having documentation in-system. It doesn't serve much of a purpose and make blkfs significantly heavier. This commit is the first step in writing a documentation outside of the blkfs.
This commit is contained in:
parent
c4db9d4420
commit
ffcd93699e
33
doc/intro.txt
Normal file
33
doc/intro.txt
Normal file
@ -0,0 +1,33 @@
|
||||
# Introduction to Collapse OS
|
||||
|
||||
Collapse OS is a minimal operating system created to preserve
|
||||
the ability to program microcontrollers through civilizational
|
||||
collapse. Its author expect the collapse of the global supply
|
||||
chain means the loss of our computer production capability. Many
|
||||
microcontrollers require a computer to program them.
|
||||
|
||||
Collapse OS innovates by self-hosting on extremely tight resour-
|
||||
ces and is thus (theoretically thus far) able to operate and be
|
||||
improved in a world without modern computers.
|
||||
|
||||
# Forth
|
||||
|
||||
This OS is a Forth. It doesn't adhere to any pre-collapse stand-
|
||||
ard, but is pretty close to the Forth described by Starting
|
||||
Forth by Leo Brodie. It is therefore the recommended introduct-
|
||||
ory material to learn Forth in the context of Collapse OS.
|
||||
|
||||
If you don't have access to this book and don't know anything
|
||||
about Forth, learning Collapse OS could be a rough ride, but
|
||||
don't despair. There's a Forth primer in primer.txt.
|
||||
|
||||
# Documentation and self-hosting
|
||||
|
||||
Collapse OS is self-hosting, its documentation is not, that is,
|
||||
Collapse OS cannot read this document you're reading. Text
|
||||
blocks could, of course, be part of Collapse OS' blocks, but
|
||||
doing so needlessly uses blocks and make the system heavier than
|
||||
it should.
|
||||
|
||||
This documentation is expected to be printed before the last
|
||||
modern computer of your community dies.
|
201
doc/primer.txt
Normal file
201
doc/primer.txt
Normal file
@ -0,0 +1,201 @@
|
||||
# Forth Primer
|
||||
|
||||
# First steps
|
||||
|
||||
Before you read this primer, let's try a few commands, just for
|
||||
fun.
|
||||
|
||||
42 .
|
||||
|
||||
This will push the number 42 to the stack, then print the number
|
||||
at the top of the stack.
|
||||
|
||||
4 2 + .
|
||||
|
||||
This pushes 4, then 2 to the stack, then adds the 2 numbers on
|
||||
the top of the stack, then prints the result.
|
||||
|
||||
42 0x8000 C! 0x8000 C@ .
|
||||
|
||||
This writes the byte "42" at address 0x8000, and then reads
|
||||
back that bytes from the same address and print it.
|
||||
|
||||
# Interpreter loop
|
||||
|
||||
Forth's main interpeter loop is very simple:
|
||||
|
||||
1. Read a word from input
|
||||
2. Look it up in the dictionary
|
||||
3. Found? Execute.
|
||||
4. Not found?
|
||||
4.1. Is it a number?
|
||||
4.2. Yes? Parse and push on the Parameter Stack.
|
||||
4.3. No? Error.
|
||||
5. Repeat
|
||||
|
||||
# Word
|
||||
|
||||
A word is a string of non-whitepace characters. We consider that
|
||||
we're finished reading a word when we encounter a whitespace
|
||||
after having read at least one non-whitespace character
|
||||
|
||||
# Character encoding
|
||||
|
||||
Collapse OS doesn't support any other encoding than 7bit ASCII.
|
||||
A character smaller than 0x21 is considered a whitespace,
|
||||
others are considered non-whitespace.
|
||||
|
||||
Characters above 0x7f have no special meaning and can be used in
|
||||
words (if your system has glyphs for them).
|
||||
|
||||
# Dictionary
|
||||
|
||||
Forth's dictionary link words to code. On boot, this dictionary
|
||||
contains the system's words (look in B30 for a list of them),
|
||||
but you can define new words with the ":" word. For example:
|
||||
|
||||
: FOO 42 . ;
|
||||
|
||||
defines a new word "FOO" with the code "42 ." linked to it. The
|
||||
word ";" closes the definition. Once defined, a word can be
|
||||
executed like any other word.
|
||||
|
||||
You can define a word that already exists. In that case, the new
|
||||
definition will overshadow the old one. However, any word def-
|
||||
ined *before* the overshadowing took place will still use the
|
||||
old word.
|
||||
|
||||
# Cell size
|
||||
|
||||
The cell size in Collapse OS is 16 bit, that is, each item in
|
||||
stacks is 16 bit, @ and ! read and write 16 bit numbers.
|
||||
Whenever we refer to a number, a pointer, we speak of 16 bit.
|
||||
|
||||
To read and write bytes, use C@ and C!.
|
||||
|
||||
# Number literals
|
||||
|
||||
Traditional Forth often uses HEX/DEC switches to go from deci-
|
||||
mal to hexadecimal parsing. Collapse OS parses literals in a
|
||||
way that is closer to C.
|
||||
|
||||
Straight numbers are decimals, numbers starting with "0x"
|
||||
are hexadecimals (example "0x12ef"), "0b" prefixes indicate
|
||||
binary (example "0b1010"), char literals are single characters
|
||||
surrounded by ' (example 'X'). Char literals can't be used for
|
||||
whitespaces.
|
||||
|
||||
# Parameter Stack
|
||||
|
||||
Unlike most programming languages, Forth execute words directly,
|
||||
without arguments. The Parameter Stack (PS) replaces them. There
|
||||
is only one, and we're constantly pushing to and popping from
|
||||
it. All the time.
|
||||
|
||||
For example, the word "+" pops the 2 number on the Top Of Stack
|
||||
(TOS), adds them, then pushes back the result on the same stack.
|
||||
It thus has the "stack signature" of "a b -- n". Every word in
|
||||
a dictionary specifies that signature because stack balance, as
|
||||
you can guess, is paramount. It's easy to get confused so you
|
||||
need to know the stack signature of words you use very well.
|
||||
|
||||
# Return Stack
|
||||
|
||||
There's a second stack, the Return Stack (RS), which is used to
|
||||
keep track of execution, that is, to know where to go back after
|
||||
we've executed a word. It is also used in other contexts, but
|
||||
this is outside of the scope of this primer.
|
||||
|
||||
# Conditional execution
|
||||
|
||||
Code can be executed conditionally with IF/ELSE/THEN. IF pops
|
||||
PS and checks whether its nonzero. If it is, it does nothing.
|
||||
If it's zero, it jumps to the following ELSE or the following
|
||||
THEN. Similarly, when ELSE is encountered in the context of a
|
||||
nonzero IF, we jump to the following THEN.
|
||||
|
||||
Because IFs involve jumping, they only work inside word defin-
|
||||
itions. You can't use IF directly in the interpreter loop.
|
||||
|
||||
Example usage:
|
||||
|
||||
: FOO IF 42 ELSE 43 THEN . ;
|
||||
0 FOO --> 42
|
||||
1 FOO --> 43
|
||||
|
||||
# Loops
|
||||
|
||||
Loops work a bit like conditionals, and there's 3 forms:
|
||||
|
||||
BEGIN..AGAIN --> Loop forever
|
||||
BEGIN..UNTIL --> Loop conditionally
|
||||
DO..LOOP --> Loop X times
|
||||
|
||||
UNTIL works exactly like IF, but instead of jumping forward to
|
||||
THEN, it jumps backward to BEGIN.
|
||||
|
||||
DO pops the lower, then the higher bounds of the loop to be
|
||||
executed, then pushes them on RS. Then, each time LOOP is
|
||||
encountered, RS' TOS is increased. As long as the 2 numbers at
|
||||
RS' TOS aren't equal, we jump back to DO.
|
||||
|
||||
The word "I" copies RS' TOS to PS, which can be used to get our
|
||||
"loop counter".
|
||||
|
||||
Beware: the bounds arguments for DO are unintuitive. We begin
|
||||
with the upper bound. Example:
|
||||
|
||||
42 0 DO I . SPC LOOP
|
||||
|
||||
Will print numbers 0 to 41, separated by a space.
|
||||
|
||||
# Variables
|
||||
|
||||
We can read and write to arbitrary memory address with @ and !
|
||||
(C@ and C! for bytes). For example, "1234 0x8000 !" writes the
|
||||
word 1234 to address 0x8000.
|
||||
|
||||
The word "VARIABLE" link a name to an address. For example,
|
||||
"VARIABLE FOO" defines the word "FOO" and "reserves" 2 bytes of
|
||||
memory. Then, when FOO is executed, it pushes the address of the
|
||||
"reserved" area to PS.
|
||||
|
||||
For example, "1234 FOO !" writes 1234 to memory address reserved
|
||||
for FOO.
|
||||
|
||||
# IMMEDIATE
|
||||
|
||||
We approach the end of our primer. So far, we've covered the
|
||||
"cute and cuddly" parts of the language. However, that's not
|
||||
what makes Forth powerful. Forth becomes mind-bending when we
|
||||
throw IMMEDIATE into the mix.
|
||||
|
||||
A word can be declared immediate thus:
|
||||
|
||||
: FOO ; IMMEDIATE
|
||||
|
||||
That is, when the IMMEDIATE word is executed, it makes the
|
||||
latest defined word immediate.
|
||||
|
||||
An immediate word, when used in a definition, is executed
|
||||
immediately instead of being compiled. This seemingly simple
|
||||
mechanism (and it *is* simple) has very wide implications.
|
||||
|
||||
For example, The words "(" and ")" are comment indicators. In
|
||||
the definition:
|
||||
|
||||
: FOO 42 ( this is a comment ) . ;
|
||||
|
||||
The word "(" is read like any other word. What prevents us from
|
||||
trying to compile "this" and generate an error because the word
|
||||
doesn't exist? Because "(" is immediate. Then, that word reads
|
||||
from input stream until a ")" is met, and then returns to word
|
||||
compilation.
|
||||
|
||||
Words like "IF", "DO", ";" are all regular Forth words, but
|
||||
their "power" come from the fact that they're immediate.
|
||||
|
||||
Starting Forth by Leo Brodie explain all of this in details.
|
||||
Read this if you can. If you can't, well, let this sink in for
|
||||
a while, browse the dictionary (B30) and try to understand why
|
||||
this or that word is immediate. Good luck!
|
Loading…
Reference in New Issue
Block a user