update docs

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

@ -1,6 +1,6 @@
# CROISSANT VIRTUAL MACHINE # 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 ## FAQ
@ -8,12 +8,20 @@ Croissant (or Crsn for short) is an extensible runtime emulating a weird microco
F U N 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 # 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 ## Registers
- 8 general purpose registers `reg0`-`reg7` - 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 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. 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 8-, 16-, 32-bit and floating point arithmetic is not currently implemented, but will be added later. Probably. Maybe.
added later. Probably. Maybe.
## Status Flags ## Status flags
Arithmetic and other operations set status flags that can be used for conditional jumps. 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 - Negative … Value is negative
- Overflow … Arithmetic overflow or underflow, buffer underflow, etc. - Overflow … Arithmetic overflow or underflow, buffer underflow, etc.
- Invalid … Invalid arguments for an instruction - 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: 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 Instructions are written using S-expressions, because they are easy to parse
and everyone loves Lisp. and everyone loves Lisp.
## Program
A program has this format: 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: 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: Args are either:
- one of the registers (`reg0`, `arg3` etc) - One of the registers (`reg0`, `arg3` etc)
- `_` to discard an output value - Names of constants defined earlier in the program (e.g. `SCREEN_WIDTH`)
- a literal value (decimal, hex or binary) - Symbols defined as register aliases (e.g. `x`)
- label or routine name - The "discard register" `_` to discard an output value. That is used when you only care about side effects or status flags.
- condition flag keyword - Literal values (decimal, hex or binary)
- anything else an extension supports... - Label or routine name (e.g. `factorial`, `:again`)
- ...or anything else an installed crsn extension supports
### Conditional branches
Conditonal branches are written like this: Conditonal branches are written like this:
``` ```
(Condition? Instructions…) (<cond>? <instructions>...)
``` ```
If there is more than one conditional branch chained to an instruction, - 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 then only one branch is taken - there is no fall-through.
is preserved, i.e. if the `inval` flag is to be checked, it should be done - 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, before checking e.g. `nz`, which is, incidentally, true by default, because most flags are cleared by instructions that affects flags.
because all flags start cleared.
## Routines
Example routine to calculate the factorial of `arg0`: A routine is defined as:
``` ```
(fac (proc <name>/<arity> instructions...)
(cmp arg0 2 ```
(eq? (ret 2)))
- `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) (sub r0 arg0 1)
(call fac r0) (call fac r0)
(mul r0 arg0 res0) (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 # Instruction Set
Crsn instruction set is composed of extensions. 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 ## 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. - 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). - 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. 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 a varying number of skips and conditional instructions by the assembler. Only use skips if you really know what you're doing.
Skipping across conditional branches may have *surprising results* - conditional branches are expanded Jumping to a label is always safer than a manual skip.
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.
## Built-in Instructions ## Built-in Instructions
@ -150,6 +224,8 @@ These are defined as part of the built-in instruction set (see below).
; Mark a jump target. ; Mark a jump target.
(:LABEL) (:LABEL)
; Numbered labels
(:#NUMBER)
; Mark a far jump target (can be jumped to from another routine). ; Mark a far jump target (can be jumped to from another routine).
; This label is preserved in optimized code. ; 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 ; Jump to a label
(j :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 ; Jump to a label that can be in another function
(fj :LABEL) (fj :LABEL)
; Far jump to a label if a flag is set
(fjif COND :LABEL)
; Skip backward or forward ; Skip backward or forward
(s COUNT) (s COUNT)
; Skip if a flag is set
(sif COND COUNT)
; Mark a routine entry point (call target). ; Mark a routine entry point (call target).
(routine NAME) (routine NAME)
(routine NAME/ARITY)
; Call a routine with arguments. ; Call a routine with arguments.
; The arguments are passed as argX. Return values are stored in resX registers. ; 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 ; 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. ; Deny jumps, skips and run across this address, producing a run-time fault with a message.
(barrier) (barrier)
(barrier "message text") (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 ; Generate a run-time fault with a debugger message
(fault) (fault)
(fault "message text") (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 ; Load status flags from a register
(sld SRC) (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 ## Arithmetic Module
@ -282,5 +361,38 @@ Many instructions have two forms:
; Arithmetic shift left (this is identical to `lsl`, added for completeness) ; Arithmetic shift left (this is identical to `lsl`, added for completeness)
(asl DST A B) (asl DST A B)
(asl DST 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