|
|
|
@ -76,8 +76,8 @@ These keywords (among others) are used in conditional branches to specify flag t |
|
|
|
|
- `nneg` … NonNegative, |
|
|
|
|
- `c` … Carry, |
|
|
|
|
- `nc` … NotCarry, |
|
|
|
|
- `valid` … Valid, |
|
|
|
|
- `inval` … Invalid, |
|
|
|
|
- `val`, `valid`, `ok` … Valid, |
|
|
|
|
- `inval`, `nok` … Invalid, |
|
|
|
|
- `ov` … Overflow, |
|
|
|
|
- `nov` … NotOverflow, |
|
|
|
|
|
|
|
|
@ -127,19 +127,50 @@ Instructions are written like this: |
|
|
|
|
|
|
|
|
|
### 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. |
|
|
|
|
All instructions can be made conditional by appending `.<cond>` to the keyword, i.e. `(j.ne :LABEL)` means "jump if not equal". |
|
|
|
|
These modifiers are mainly used by the assembler when translating conditional branches to executable code. |
|
|
|
|
|
|
|
|
|
Note that the flags can only be tested immediately after the instruction that produced them, or after instructions that do not |
|
|
|
|
affect flags (pseudo-instructions like `def` and `sym`, `nop`, `j`, `fj`, `s`, `call` etc). Instructions that can set flags first |
|
|
|
|
clear all flags to make the result predictable. |
|
|
|
|
|
|
|
|
|
Status flags can be saved to and restored from a register using the `stf` and `ldf` instructions. This can also be used to set |
|
|
|
|
or test flags manually, but the binary format may change |
|
|
|
|
|
|
|
|
|
### Instruction arguments |
|
|
|
|
|
|
|
|
|
Args are either: |
|
|
|
|
- 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 |
|
|
|
|
Arguments are always ordered writes-first, reads-last. |
|
|
|
|
|
|
|
|
|
This document uses the following notation for arguments: |
|
|
|
|
- `REG` - one of the registers (`regX`, `argX`, `resX`) |
|
|
|
|
- `SYM` - a symbol defined as a register alias (e.g. `(sym x r0)`) |
|
|
|
|
- `@REG` / `@SYM` - access an object referenced by a handle. Handle is simply a numeric value stored in a register of some kind. |
|
|
|
|
- `_` - a special "register" that discards anything written to it. |
|
|
|
|
The "discard register" is used when you do not need the value and only care about side effects or status flags. |
|
|
|
|
- `CONST` - name of a constant defined earlier in the program (e.g. `(def SCREEN_WIDTH 640)`) |
|
|
|
|
- `NUM` - literal values |
|
|
|
|
- unsigned `123` |
|
|
|
|
- signed `-123` |
|
|
|
|
- float `-45.6789` |
|
|
|
|
- hex `0xabcd`, `#abcd` |
|
|
|
|
- binary `0b0101` |
|
|
|
|
- character `'a'`, `'🐁'`. Supports unicode and C-style escapes. Use `\\` for a literal backslash. |
|
|
|
|
- `"str"` - a double-quoted string (`"ahoj\n"`). Supports unicode and C-style escapes. Use `\\` for a literal backslash. |
|
|
|
|
- `:LABEL` - label name |
|
|
|
|
- `PROC` - routine name |
|
|
|
|
- `PROC/A` - routine name with arity (number of arguments) |
|
|
|
|
|
|
|
|
|
The different ways to specify a value can be grouped as "reads" and "writes": |
|
|
|
|
|
|
|
|
|
- `Rd` - read: `REG`, `SYM`, `@REG`, `@SYM`, `VALUE`, `CONST` |
|
|
|
|
- `Wr` - writes: `REG`, `SYM`, `@REG`, `@SYM`, `_` |
|
|
|
|
- `RW` - intersection of the two sets, capable of reading and writing: `REG`, `SYM`, `@REG`, `@SYM` |
|
|
|
|
|
|
|
|
|
Objects (`@reg`, `@sym`) can be read or written as if they were a register, but only if the referenced object supports it. |
|
|
|
|
Other objects may produce a runtime fault or set the INVALID flag. |
|
|
|
|
|
|
|
|
|
In the instruction lists below, I will use the symbols `Rd` for reads, `Wr` for writes, `RW` for read-writes, and `@Obj` for object handles, |
|
|
|
|
with optional description after a colon, such as: `(add Wr:dst Rd:a Rd:b)`. |
|
|
|
|
|
|
|
|
|
### Conditional branches |
|
|
|
|
|
|
|
|
@ -236,7 +267,7 @@ Jumping to a label is always safer than a manual skip. |
|
|
|
|
; Mark a jump target. |
|
|
|
|
(:LABEL) |
|
|
|
|
; Numbered labels |
|
|
|
|
(:#NUMBER) |
|
|
|
|
(:#NUM) |
|
|
|
|
|
|
|
|
|
; Mark a far jump target (can be jumped to from another routine). |
|
|
|
|
; This label is preserved in optimized code. |
|
|
|
@ -249,46 +280,50 @@ Jumping to a label is always safer than a manual skip. |
|
|
|
|
(fj :LABEL) |
|
|
|
|
|
|
|
|
|
; Skip backward or forward |
|
|
|
|
(s COUNT) |
|
|
|
|
(s Rd) |
|
|
|
|
|
|
|
|
|
; Mark a routine entry point (call target). |
|
|
|
|
(routine NAME) |
|
|
|
|
(routine NAME/ARITY) |
|
|
|
|
(routine PROC) |
|
|
|
|
(routine PROC/A) |
|
|
|
|
|
|
|
|
|
; Call a routine with arguments. |
|
|
|
|
; The arguments are passed as argX. Return values are stored in resX registers. |
|
|
|
|
(call ROUTINE ARGUMENTS...) |
|
|
|
|
(call PROC Rd...) |
|
|
|
|
|
|
|
|
|
; Exit the current routine with return values |
|
|
|
|
(ret VALUES...) |
|
|
|
|
(ret Rd...) |
|
|
|
|
|
|
|
|
|
; Deny jumps, skips and run across this address, producing a run-time fault with a message. |
|
|
|
|
(barrier) |
|
|
|
|
(barrier message) |
|
|
|
|
(barrier "message text") |
|
|
|
|
|
|
|
|
|
; Block barriers are used for routines. They are automatically skipped in execution |
|
|
|
|
; and the whole pair can be jumped *across* |
|
|
|
|
; and the whole pair can be jumped *across*. |
|
|
|
|
; The label can be a numeric or string label, its sole purpose is tying the two together. They must be unique in the program. |
|
|
|
|
(barrier-open LABEL) |
|
|
|
|
(barrier-close LABEL) |
|
|
|
|
|
|
|
|
|
; Generate a run-time fault with a debugger message |
|
|
|
|
(fault) |
|
|
|
|
(fault message) |
|
|
|
|
(fault "message text") |
|
|
|
|
|
|
|
|
|
; Copy value |
|
|
|
|
(ld DST SRC) |
|
|
|
|
(ld Wr Rd) |
|
|
|
|
|
|
|
|
|
; Store status flags to a register |
|
|
|
|
(sst DST) |
|
|
|
|
(stf Wr) |
|
|
|
|
|
|
|
|
|
; Load status flags from a register |
|
|
|
|
(sld SRC) |
|
|
|
|
(ldf Rd) |
|
|
|
|
|
|
|
|
|
; Define a register alias. The alias is only valid in the current routine or in the root of the program. |
|
|
|
|
(sym ALIAS REGISTER) |
|
|
|
|
(sym SYM REG) |
|
|
|
|
|
|
|
|
|
; Define a constant. These are valid in the whole program. |
|
|
|
|
(def NAME VALUE) |
|
|
|
|
; Value must be known at compile time. |
|
|
|
|
(def CONST VALUE) |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
## Arithmetic Module |
|
|
|
@ -304,101 +339,152 @@ Many instructions have two forms: |
|
|
|
|
(tst SRC) |
|
|
|
|
|
|
|
|
|
; Compare two values |
|
|
|
|
(cmp A B) |
|
|
|
|
(cmp Rd Rd) |
|
|
|
|
|
|
|
|
|
; Add A+B |
|
|
|
|
(add DST A B) |
|
|
|
|
(add DST B) |
|
|
|
|
(add Wr Rd Rd) |
|
|
|
|
(add RW Rd) |
|
|
|
|
|
|
|
|
|
; Subtract A-B |
|
|
|
|
(sub DST A B) |
|
|
|
|
(sub DST B) |
|
|
|
|
(sub Wr Rd Rd) |
|
|
|
|
(sub RW Rd) |
|
|
|
|
|
|
|
|
|
; Multiply A*B |
|
|
|
|
(mul DST A B) |
|
|
|
|
(mul DST B) |
|
|
|
|
(mul Wr Rd Rd) |
|
|
|
|
(mul RW Rd) |
|
|
|
|
|
|
|
|
|
; Divide A/B |
|
|
|
|
(div DST A B) |
|
|
|
|
(div DST B) |
|
|
|
|
(div Wr Rd Rd:divider) |
|
|
|
|
(div RW Rd:divider) |
|
|
|
|
|
|
|
|
|
; Divide and get remainder |
|
|
|
|
; Both DST and REM are output registers |
|
|
|
|
(divr DST REM A B) |
|
|
|
|
(divr DST REM B) |
|
|
|
|
(divr Wr:result Wr:remainder Rd Rd:divider) |
|
|
|
|
(divr RW Wr:remainder Rd:divider) |
|
|
|
|
|
|
|
|
|
; Get remainder A%B |
|
|
|
|
; This is equivalent to (divr _ REM A B), |
|
|
|
|
; except status flags are updated by the remainder value |
|
|
|
|
(mod DST A B) |
|
|
|
|
(mod DST B) |
|
|
|
|
(mod Wr Rd Rd-divider) |
|
|
|
|
(mod RW Rd-divider) |
|
|
|
|
|
|
|
|
|
; AND A&B |
|
|
|
|
(and DST A B) |
|
|
|
|
(and DST B) |
|
|
|
|
(and Wr Rd Rd) |
|
|
|
|
(and RW Rd) |
|
|
|
|
|
|
|
|
|
; OR A|B |
|
|
|
|
(or DST A B) |
|
|
|
|
(or DST B) |
|
|
|
|
(or Wr Rd Rd) |
|
|
|
|
(or RW Rd) |
|
|
|
|
|
|
|
|
|
; XOR A&B |
|
|
|
|
(xor DST A B) |
|
|
|
|
(xor DST B) |
|
|
|
|
(xor Wr Rd Rd) |
|
|
|
|
(xor RW Rd) |
|
|
|
|
|
|
|
|
|
; CPL ~A (negate all bits) |
|
|
|
|
(cpl DST A) |
|
|
|
|
(cpl DST) |
|
|
|
|
|
|
|
|
|
; Rotate right (wrap around) |
|
|
|
|
(ror DST A B) |
|
|
|
|
(ror DST B) |
|
|
|
|
(ror Wr Rd Rd) |
|
|
|
|
(ror RW Rd) |
|
|
|
|
|
|
|
|
|
; Rotate left (wrap around) |
|
|
|
|
(rol DST A B) |
|
|
|
|
(rol DST B) |
|
|
|
|
(rol Wr Rd:value Rd:count) |
|
|
|
|
(rol RW Rd:count) |
|
|
|
|
|
|
|
|
|
; Logical shift right (fill with zeros) |
|
|
|
|
(lsr DST A B) |
|
|
|
|
(lsr DST B) |
|
|
|
|
(lsr Wr Rd Rd:count) |
|
|
|
|
(lsr RW Rd:count) |
|
|
|
|
|
|
|
|
|
; Logical shift left (fill with zeros) |
|
|
|
|
(lsl DST A B) |
|
|
|
|
(lsl DST B) |
|
|
|
|
(lsl Wr Rd Rd:count) |
|
|
|
|
(lsl RW Rd:count) |
|
|
|
|
|
|
|
|
|
; Arithmetic shift right (copy sign bit) |
|
|
|
|
(asr DST A B) |
|
|
|
|
(asr DST B) |
|
|
|
|
(asr Wr Rd Rd:count) |
|
|
|
|
(asr RW Rd:count) |
|
|
|
|
|
|
|
|
|
; Arithmetic shift left (this is identical to `lsl`, added for completeness) |
|
|
|
|
(asl DST A B) |
|
|
|
|
(asl DST B) |
|
|
|
|
(asl Wr Rd Rd:count) |
|
|
|
|
(asl RW Rd:count) |
|
|
|
|
|
|
|
|
|
; Delete an object by its handle. Objects are used by some extensions. |
|
|
|
|
(del @REG) |
|
|
|
|
(del @Rd) |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
## Buffers Module |
|
|
|
|
|
|
|
|
|
This module defines dynamic size integer buffers. |
|
|
|
|
|
|
|
|
|
A buffer needs to be created using one of the init instructions: |
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
; Create an empty buffer and store its handle into a register |
|
|
|
|
(mkbf Wr) |
|
|
|
|
|
|
|
|
|
; Create a buffer of a certain size, filled with zeros. |
|
|
|
|
; COUNT may be a register or an immediate value |
|
|
|
|
(mkbf Wr Rd:count) |
|
|
|
|
|
|
|
|
|
; Create a buffer and fill it with characters from a string (unicode code points) |
|
|
|
|
(mkbf Wr "string") |
|
|
|
|
|
|
|
|
|
; Create a buffer and fill it with values. |
|
|
|
|
(mkbf Wr (Rd...)) |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
Primitive buffer ops (position is always 0-based) |
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
; Get buffer size |
|
|
|
|
(bfsz Wr @Obj) |
|
|
|
|
|
|
|
|
|
; Read from a position |
|
|
|
|
(bfrd Wr @Obj Rd:index) |
|
|
|
|
|
|
|
|
|
; Write to a position |
|
|
|
|
(bfwr @Obj Rd:index Rd) |
|
|
|
|
|
|
|
|
|
; Insert at a position, shifting the rest to the right |
|
|
|
|
(bfins @Obj Rd:index Rd) |
|
|
|
|
|
|
|
|
|
; Remove item at a position, shifting the rest to the left to fill the empty space |
|
|
|
|
(bfrm Wr @Obj Rd:index) |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
Whole buffer manipulation: |
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
; Resize the buffer. Removes trailing elements or inserts zero to match the new size. |
|
|
|
|
(bfrsz @Obj Rd:len) |
|
|
|
|
|
|
|
|
|
## Stacks Module |
|
|
|
|
; Reverse a buffer |
|
|
|
|
(bfrev @Obj) |
|
|
|
|
|
|
|
|
|
This module defines data stacks. Stacks can be shared by routines by passing a handle. |
|
|
|
|
; Append a buffer |
|
|
|
|
(bfapp @Obj @Obj:other) |
|
|
|
|
|
|
|
|
|
; Prepend a buffer |
|
|
|
|
(bfprep @Obj @Obj:other) |
|
|
|
|
``` |
|
|
|
|
; Create a stack. The register then contains the stack handle. |
|
|
|
|
(stack REG) |
|
|
|
|
|
|
|
|
|
; Push to a stack (insert to the end) |
|
|
|
|
(push @REG VALUE) |
|
|
|
|
Stack-style buffer ops: |
|
|
|
|
|
|
|
|
|
``` |
|
|
|
|
; Push (insert at the end) |
|
|
|
|
(bfpush @Obj Rd) |
|
|
|
|
|
|
|
|
|
; Pop from a stack (remove from the end) |
|
|
|
|
(pop DST @REG) |
|
|
|
|
; Pop (remove from the end) |
|
|
|
|
(bfpop Wr @Obj) |
|
|
|
|
|
|
|
|
|
; Reverse push to a stack (insert to the beginning) |
|
|
|
|
(rpush @REG VALUE) |
|
|
|
|
; Reverse push (insert to the beginning) |
|
|
|
|
(bfrpush @Obj Rd) |
|
|
|
|
|
|
|
|
|
; Reverse pop from a stack (remove from the beginning) |
|
|
|
|
(rpop DST @REG) |
|
|
|
|
; Reverse pop (remove from the beginning) |
|
|
|
|
(bfrpop Wr @Obj) |
|
|
|
|
``` |
|
|
|
|
|
|
|
|
|
To delete a stack, use the `del` instruction - `(del @REG)` |
|
|
|
|
To delete a buffer, use the `del` instruction - `(del @Obj)` |
|
|
|
|
|
|
|
|
|
## Screen module |
|
|
|
|
|
|
|
|
@ -456,7 +542,7 @@ such as animations. |
|
|
|
|
|
|
|
|
|
## Stdio module |
|
|
|
|
|
|
|
|
|
- This module currently defines two global handles: `@stdin` and `@stdout`. |
|
|
|
|
- This module currently defines two global handles (resp. constants): `@stdin` and `@stdout`. |
|
|
|
|
- You can think of these handles as streams or SFRs (special function registers). |
|
|
|
|
To use them, simply load data to or from the handles (e.g. `(ld r0 @stdin)`). |
|
|
|
|
- They operate over unicode code points, which are a superset of ASCII. |
|
|
|
|