# The Oberon System

Dusk OS hosts a port of the Oberon System, which is enabled by the Oberon
Compiler [comp/oberon]. Because it's not quite the same as Project Oberon, we
call this port Duskberon.

This is not emulation, this is a native port! That is, this code doesn't compile
to some RISC5 bytecode to be emulated on ran on the Oberon-specific FPGA. It's
compiled through the HAL [doc/hal], using native bytecode for all CPUs supported
by Dusk.

This of course requires a significant rework of all the lower layers of the
Oberon system which are RISC5-specific. We strip all those parts and hook them
to the appropriate Dusk drivers.

This implies changing a few things here and there. The goals here is to allow
Oberon code to run with as few changes as possible while hooking into Dusk
abstractions as elegantly as possible. Notes about those changes follow below.

## Work in Progress

Duskberon is a work in progress. Although it somewhat works as described here,
there are a lot of bugs lurking all around.

Moreover, Duskberon as it is isn't very useful. The vision for Duskberon is much
tighter integration with Dusk itself, for example to run Grid or graphical
applications in Duskberon itself, or having a Forth console in there.

The end goal here is to have most systems with graphical capability boot
directly to a Duskberon system and allows its operator to have full powers over
the system through that interface.

There's also a lot of documentation to be written. Oberon documentation is kind
of sufficient as a kind of academic systems programming course, but it isn't a
particularly good usage guide, or even a good reference. Those docs need to be
rewritten.

So yeah, still a WIP...

## Usage

You load the Oberon system by loading [oberon/sys.fs] with "needs oberon/sys".
This loads [comp/oberon] and compiles a wrapper unit that loads all the
important Oberon modules.

You launch the Oberon system with "oberonloop". This enters the Oberon runloop
and returns to the console on System.Quit. An alternate way to quit is to press
Ctrl+Q.

The system then runs as documented in Project Oberon, with a few differences,
documented below.

## Files

The Files module thinly wraps [fs/core]. FileDir doesn't exist and its public
API is included in Files.

A Rider hooks to a File and calls "seek" on its target file whenever it needs
to. This means "multiple riders" are going to be slow compared to Oberon design.

Oberon has this concept of creating new named files without registering them.
This doesn't map cleanly to fs/core. What we do is that New() maps to [mem/pool]
and thus yields a new pool stream. Old() does a lookup+open. Register() does not
exist. Instead, we have Ensure(name) which does ensurefile+open.

While the Close() routine maps to [io/stream]'s "close", it's not enough because
Oberon doesn't free file resources like Dusk does. It relies on its GC.

Therefore, what we need to do is to have Files.File hold onto a StreamRef (see
[comp/oberon/gc]). This way, when the GC collects it, it also closes the file.

The original Oberon uses its peculiar FS design to effectively read and write
from the same file without overwriting contents we're about to read. We can't do
that here. Instead, we have another mechanism, which is more memory hungry, but
has the same effect: we write to a mem/pool file (with New()), then use
Copy(src, dst: File) to an Ensure()d file to commit the contents. Copy() is a
proxy for "spit" from [io/stream].

## Display

Ideally, the display module would be a thin layer over [gr/pix] and friends, but
the specified raster operations don't map to the API that gr/pix currently
provides. It's a problem.

Therefore, until a graphics refactoring takes place, the Oberon display will be
a separate buffer (the color one. We don't port Monochrome logic) with 4-bit
per pixel and the same size as the screen. We define a palette for each of the
16 possible colors.

This allows the DisplayC module to be ported pretty much as is. After each
operation, however, we add an "invalidate" call so that the buffer can be pushed
to the screen.

## Input

Oberon's Input module maps rather straight to [io/kbd] and [io/mouse]. We
diverge from original with Input.Available(), in which case we yield a NKC
rather than a PS/2 key code. No code in the Oberon system uses Input.Available()
for its code anyways, only for its nonzero state.

## LF is the new CR

To harmonize Duskberon with the rest of Dusk, LF is the line terminator instead
of CR.

## Scanner classes

In Texts, the "name" class include the "/" character. That class is used for
file paths scanning and Dusk paths include "/", so we need this.
