~vdupras/duskos

6c32cb4746f353edbf610f8f0b53010807362e4e — Virgil Dupras a month ago 11f6fc6 hal
HAL transition complete!
3 files changed, 28 insertions(+), 79 deletions(-)

M README.md
D fs/doc/stc.txt
M fs/doc/usage.txt
M README.md => README.md +10 -3
@@ 67,13 67,19 @@ generated in the first place. This is so much simpler!
Object files? Global symbols? Nah. C functions that don't have a static storage
type are simple Forth words.

### Harmonized Assembly Layer

Dusk features what we call the [Harmonized Assembly Layer][hal] (HAL for short).
This is a cross-CPU assembler, on which the C compiler relies, which prioritizes
implementation and usage simplicity, but is also designed to generate efficient
native code.

### Shortest path to self-hosting for an "almost C" compiler

Dusk OS self-hosts in about 1000 lines of assembly and a few hundred lines of
Dusk OS self-hosts in about 800 lines of assembly and a few hundred lines of
Forth (the exact number depends on the target machine). From there, it
bootstraps to DuskCC, which is roughly 2000 lines of Forth code (including
arch-specific backend and assembler). To my knowledge, Dusk OS is unique in
that regard.
arch-specific assemblers). To my knowledge, Dusk OS is unique in that regard.

You can pick any C compiler that requires POSIX and it will automatically
require order of magnitudes more lines of code to bootstrap because you need


@@ 266,6 272,7 @@ Press Escape to return to prompt. You can try the same thing with:
[shproj]: https://sr.ht/~vdupras/duskos
[mailinglist]: https://sr.ht/~vdupras/duskos/lists
[duskcc]: fs/doc/cc/index.txt
[hal]: fs/doc/hal.txt
[livebootstrap]: https://github.com/fosslinux/live-bootstrap
[m2planet]: https://git.sr.ht/~oriansj/M2-Planet
[builder-hex0]: https://github.com/ironmeld/builder-hex0

D fs/doc/stc.txt => fs/doc/stc.txt +0 -73
@@ 1,73 0,0 @@
# Dusk OS is a STC

This Forth is a Subroutine Thread Code (STC) Forth, that is, each reference to
words is a native call instead of being a reference. The drawback to this
structure, compared to DTC or ITC forths (the ones with a "next" (not the "next"
from iterators, the "core next of forth")), is significantly bigger binary size.
It's also more complex to "disassemble" a compiled word. The upsides, however,
are real juicy.

First, there's speed. Calling words natively is faster than going through a
"next" loop.

Second, and most importantly, there's the ability for forth code to freely mix
up with native code. That's huge and we use this extensively in Dusk through
its "meta compilation" capabilities.

## Meta compilation

The vast majority of forths have the "compile" class of words. That's your
grampa's compilation and it simply writes down a call to the specified word.
Dusk kernels go further than that and allow the compilation of further native
constructs.

### Direct PS/RS control

One of those constructs is direct control over the Parameter and Return Stacks.
The "p+," compiles native code that grows or shrink PS by the specified number
of bytes. "r+," does the same thing for RS.

For example, here's the implementation of "drop" (from xcomp/bootlo.fs)

code drop 4 p+, exit,

Under a i386 kernel, this is the exact equivalent of writing "bp 4 i) add, ret,"
to "here".

In other words, meta compilation words in Dusk are a kind of limited portable
assembler allowing you to sprinkle native code around.

### The A register

The "A" register is a (ideally) hardware register that helps making some memory-
related operations less "PS-heavy". "A" is for "Address" and it will generally
contain a memory address upon which it will work.

Other forths have "A" registers, such as Collapse OS, and this register is
generally directly used by the operator. Not here. The A register is extremely
short-lived and will be written over all the time. Few words are guaranteed to
preserve A, so you'll typically only use it in local, specific places. For
example, ">r" uses the A register:

: >r -4 r+, RSP>A, A!, ; immediate

RSP>A, compiles native code that copies the RS pointer to the A register and A!,
compiles native code that pops PS into the address contained in the A register.
By the way, "A" words are "binary modulated", so "8b A!," will compile a native
8b write.

The A register generally saves us a roundtrip of the address to PS, resulting
in considerable speedups. Let's use another example that illustrates it better:

: value doer , immediate does> compiling if LIT>A, then toptr@ execute ;

When we refer to a value in compile mode, we need to compile its address and
then apply the appropriate "to" word (if any) to it. In "regular" forth, this
would be implemented with "litn" followed by, for example, "!". This means that
the address has to be pushed to PS and then popped again during !. With the A
register, we save ourselves this roundtrip.

This is a small gain, but it's a gain that we get in values, local variables and
struct field access, so it adds up to nice sums.

See "A register" section in doc/dict for a list of words.

M fs/doc/usage.txt => fs/doc/usage.txt +18 -3
@@ 17,10 17,24 @@ That being said, Dusk OS has some additional features that need explaining:
## Subroutine Threaded Code

This Forth is a Subroutine Thread Code (STC) Forth, that is, each reference to
words is a native call instead of being a reference. This means that we don't
have a "next" interpret loop. It's calls all the way down.
words is a native call instead of being a reference. The drawback to this
structure, compared to DTC or ITC forths (the ones with a "next" (not the "next"
from iterators, the "core next of forth")), is significantly bigger binary size.
It's also more complex to "disassemble" a compiled word. The upsides, however,
are real juicy.

See doc/stc for detailed information on what it implies.
First, there's speed. Calling words natively is faster than going through a
"next" loop.

Second, and most importantly, there's the ability for forth code to freely mix
up with native code. That's huge and we use this extensively in Dusk through
its "meta compilation" capabilities, which is what the HAL is all about.

## The HAL

Dusk features a Harmonized Assembly Layer on which DuskCC relies and allows
some really nice tricks. For regular usage, HAL knowledge isn't needed, so it's
not covered here. You can read about it in doc/hal.

## Number literals



@@ 452,6 466,7 @@ the interpreter, but you are hungry for more. What to read next?
* doc/sys/io is central to many, many things in Dusk.
* doc/sys/file lets you play with the filesystem.
* doc/lib/alloc covers dynamic memory allocation.
* doc/hal covers the HAL on which the coolest Dusk features rely.
* doc/cc covers Dusk's central feature: its C compiler.
* doc/text/ed to edit text files.