update docs

pull/21/head
Ondřej Hruška 4 years ago
parent 017ec53b14
commit 003a214e06
  1. 196
      README.md

@ -1,6 +1,6 @@
# CROISSANT VIRTUAL MACHINE
Croissant (or Crsn for short) is an extensible runtime emulating a weird microcomputer.
Croissant (or Crsn for short) is an extensible runtime emulating a weird microcomputer (or not so micro, that depends on what extensions you install).
## FAQ
@ -8,12 +8,20 @@ Croissant (or Crsn for short) is an extensible runtime emulating a weird microco
F U N
#### What if I don't enjoy writing assembly that looks like Lisp?
#### What if I don't enjoy writing assembly that looks like weird Lisp?
maybe go play fortnite instead
Maybe this is not for you
# Architecture
The runtime is built as a register machine with a stack and status flags.
- All mutable state (registers and status), called "execution frame", is local to the running routine or the root of the program.
- A call pushes the active frame onto a frame stack and a clean frame is created for the callee.
- The frame stack is not accessible to the running program, it is entirely handled by the runtime.
- When a call is made, the new frame's argument registers are pre-filled with arguments passed by the caller.
- Return values are inserted into the callee's frame's result registers before its execution resumes.
## Registers
- 8 general purpose registers `reg0`-`reg7`
@ -23,10 +31,9 @@ maybe go play fortnite instead
All registers are 64-bit unsigned integers that can be treated as
signed, if you want to. Overflow is allowed and reported by status flags.
8-, 16-, and 32-bit arithmetic is not currently implemented (only 64-bit), but will be
added later. Probably. Maybe.
8-, 16-, 32-bit and floating point arithmetic is not currently implemented, but will be added later. Probably. Maybe.
## Status Flags
## Status flags
Arithmetic and other operations set status flags that can be used for conditional jumps.
@ -38,9 +45,9 @@ Arithmetic and other operations set status flags that can be used for conditiona
- Negative … Value is negative
- Overflow … Arithmetic overflow or underflow, buffer underflow, etc.
- Invalid … Invalid arguments for an instruction
- Carry … Arithmetic carry *this is not currently used for anything*
- Carry … Arithmetic carry *this is currently unused*
### Status Tests
### Status tests (conditions)
These keywords (among others) are used in conditional branches to specify flag tests:
@ -71,47 +78,96 @@ is valid at the time this file is added to version control.*
Instructions are written using S-expressions, because they are easy to parse
and everyone loves Lisp.
## Program
A program has this format:
```
(
(RoutineName Instructions…)
...<instructions and routines>...
)
```
e.g.
```
(
(ld r0 100) ; load value into a register
(:again) ; a label
(sub r0 1 ; subtract from a register
(nz? ; conditional branch "not zero?"
(j :again))) ; jump to the label :again
)
```
The same program can be written in a compact form:
```
((ld r0 100)(:again)(sub r0 1 (nz? (j :again))))
```
## Instruction
Instructions are written like this:
```
(Keyword Args… ConditionalBranches…)
(<keyword> <args>... <conditional branches>...)
```
### Conditional instructions
All instructions can be made conditional by appending `.<cond>` to the keyword, i.e. `j.ne` means "jump if not equal".
This is used internally by the assembler when translating conditional branches to executable code.
### Instruction arguments
Args are either:
- one of the registers (`reg0`, `arg3` etc)
- `_` to discard an output value
- a literal value (decimal, hex or binary)
- label or routine name
- condition flag keyword
- anything else an extension supports...
- One of the registers (`reg0`, `arg3` etc)
- Names of constants defined earlier in the program (e.g. `SCREEN_WIDTH`)
- Symbols defined as register aliases (e.g. `x`)
- The "discard register" `_` to discard an output value. That is used when you only care about side effects or status flags.
- Literal values (decimal, hex or binary)
- Label or routine name (e.g. `factorial`, `:again`)
- ...or anything else an installed crsn extension supports
### Conditional branches
Conditonal branches are written like this:
```
(Condition? Instructions…)
(<cond>? <instructions>...)
```
If there is more than one conditional branch chained to an instruction,
then only one branch is taken - there is no fall-through. The definition order
is preserved, i.e. if the `inval` flag is to be checked, it should be done
before checking e.g. `nz`, which is, incidentally, true by default,
because all flags start cleared.
- If there is more than one conditional branch chained to an instruction,
then only one branch is taken - there is no fall-through.
- The definition order is preserved, i.e. if the `inval` flag is to be checked, it should be done
before checking e.g. `nz`, which is, incidentally, true by default, because most flags are cleared by instructions that affects flags.
## Routines
Example routine to calculate the factorial of `arg0`:
A routine is defined as:
```
(fac
(cmp arg0 2
(eq? (ret 2)))
(proc <name>/<arity> instructions...)
```
- `name` is a unique routine name
- `arity` is the number of arguments it takes, e.g. `3`.
- you can define multiple routines with the same name and different arities, the correct one will be used depending on how it's called
Or, with named arguments:
```
(proc <name> <arguments>... instructions...)
```
Arguments are simply aliases for the argument registers that can then be used inside the routine.
Here is an example routine to calculate the factorial of `arg0`:
```
(proc fac/1
(cmp arg0 2 (eq? (ret 2)))
(sub r0 arg0 1)
(call fac r0)
(mul r0 arg0 res0)
@ -119,11 +175,27 @@ Example routine to calculate the factorial of `arg0`:
)
```
It can also be written like this:
```
(proc fac num
...
)
```
...or by specifying both the arity and argument names:
```
(proc fac/1 num
...
)
```
# Instruction Set
Crsn instruction set is composed of extensions.
Extensions can also define special syntax for their instructions, so long as it's valid S-expressions.
Extensions can define new instructions as well as new syntax, so long as it's composed of valid S-expressions.
## Labels, jumps and barriers
@ -133,11 +205,13 @@ These are defined as part of the built-in instruction set (see below).
- Local labels - can be jumped to within the same routine, both forward and backward.
- Far labels - can be jumped to from any place in the code using a far jump (disregarding barriers).
This is a very cursed functionality that may or may not have some valid use case.
- Skips - cannot cross a barrier, similar to a local label but without explicitly defining a label.
- Skips - cannot cross a barrier, similar to a jump but without explicitly defining a label.
All local jumps are turned into skips by the assembler.
Skipping across conditional branches may have *surprising results* - conditional branches are expanded
to series of simple and conditional skips by the assembler. Only use skips if you really know what you're doing.
Jumping to a label is always a safer choice.
to a varying number of skips and conditional instructions by the assembler. Only use skips if you really know what you're doing.
Jumping to a label is always safer than a manual skip.
## Built-in Instructions
@ -150,6 +224,8 @@ These are defined as part of the built-in instruction set (see below).
; Mark a jump target.
(:LABEL)
; Numbered labels
(:#NUMBER)
; Mark a far jump target (can be jumped to from another routine).
; This label is preserved in optimized code.
@ -158,35 +234,32 @@ These are defined as part of the built-in instruction set (see below).
; Jump to a label
(j :LABEL)
; Jump to a label if a flag is set
(jif COND :LABEL)
; Jump to a label that can be in another function
(fj :LABEL)
; Far jump to a label if a flag is set
(fjif COND :LABEL)
; Skip backward or forward
(s COUNT)
; Skip if a flag is set
(sif COND COUNT)
; Mark a routine entry point (call target).
(routine NAME)
(routine NAME/ARITY)
; Call a routine with arguments.
; The arguments are passed as argX. Return values are stored in resX registers.
(call ROUTINE ARGS…)
(call ROUTINE ARGUMENTS...)
; Exit the current routine with return values
(ret VALS…)
(ret VALUES...)
; Deny jumps, skips and run across this address, producing a run-time fault with a message.
(barrier)
(barrier "message text")
; Block barriers are used for routines. They are automatically skipped in execution
; and the whole pair can be jumped *across*
(barrier-open LABEL)
(barrier-close LABEL)
; Generate a run-time fault with a debugger message
(fault)
(fault "message text")
@ -199,6 +272,12 @@ These are defined as part of the built-in instruction set (see below).
; Load status flags from a register
(sld SRC)
; Define a register alias. The alias is only valid in the current routine or in the root of the program.
(sym ALIAS REGISTER)
; Define a constant. These are valid in the whole program.
(def NAME VALUE)
```
## Arithmetic Module
@ -282,5 +361,38 @@ Many instructions have two forms:
; Arithmetic shift left (this is identical to `lsl`, added for completeness)
(asl DST A B)
(asl DST B)
; Delete an object by its handle. Objects are used by some extensions.
(drop @REG)
```
## Stacks Module
This module defines data stacks. Stacks can be shared by routines by passing a handle.
```
; Create a stack. The register then contains the stack handle.
(stack REG)
; Push to a stack (insert to the end)
(push @REG VALUE)
; Pop from a stack (remove from the end)
(pop DST @REG)
; Reverse push to a stack (insert to the beginning)
(rpush @REG VALUE)
; Reverse pop from a stack (remove from the beginning)
(rpop DST @REG)
```
To delete a stack, drop its handle - `(drop @REG)`
## Screen module
This module uses the minifb rust crate to provide a framebuffer with key and mouse input.
Documentation TBD, see the extension's source code or the example programs
.

Loading…
Cancel
Save