uxn/doc/uxn.md

102 lines
8.3 KiB
Markdown
Raw Normal View History

2022-12-10 19:51:28 +00:00
# UXN
Reformatted from [the reference document](https://wiki.xxiivv.com/site/uxntal_reference.html)
UXN is a deterministic, non-interrupting, 8-bit instruction set and word stack machine with a 16bi address space.
It interacts with the outside world via port mapped I/O devices, of which 16 are supported.
## Memory model
UXN presents a linear uniform main memory with a 16bi address space.
Programs may do I/O across the entire address space using the `LDR2` and `STR2` instructions.
The UXN address space is divided into chunks called pages, each with 8bi of addressing.
The 0 page (`0x00[00-FF]`) is special and, for convenience, can be accessed using the `LDZ` and `STRZ` instructions.
The UXN stack machine, in keeping with [the forth tradition](http://www.forth.org/svfig/Len/softstak.htm) has two stacks.
These are referred to as `DATA` (stack 0) and `RETURN` (stack 1) by convention.
Instructions may choose to transpose the stacks, allowing for computation on either stack.
See below for more on the `return` flag.
The `LDR` and `STR` instructions are stack relative and allow for computed loads and stores.
## Instruction set
UXN instructions are 8-bit values.
| k | r | 2 | opcode |
|------|--------|-------|-----------|
| keep | return | short | 0 0 0 0 0 |
The three most significant bits of the instructions are mode flags of which we will say more.
The least significant five bits select an instruction.
Effects are written using forth-style notation `inputs -- outputs`
| Opcode | Memonic | Long name | Data stack effect | Control stack effect | PC effect | Memory effect |
|--------|---------|--------------------------|---------------------------------------------|----------------------|---------------------|-------------------|
2022-12-12 05:50:41 +00:00
| 0x00 | BRK | Break | -- | -- | | |
2022-12-10 19:51:28 +00:00
| 0x01 | INC | Increment | a -- a+1 | | | |
| 0x02 | POP | Pop | a -- | | | |
| 0x03 | NIP | Nip | b a -- a | | | |
| 0x04 | SWP | Swap | b a -- a b | | | |
| 0x05 | ROT | Rotate | a b c -- b c a | | | |
| 0x06 | DUP | Duplicate | a -- a a | | | |
| 0x07 | OVR | Over | a b -- a b a | | | |
| 0x08 | EQU | Equal | b a -- (a == b) | | | |
| 0x09 | NEQ | Not equal | b a -- (a != b) | | | |
| 0x0A | GTH | Greater than | b a -- (b > a) | | | |
| 0x0B | LTH | Less than | b a -- (b < a) | | | |
| 0x0C | JMP | Jump | a -- | | PC+=a | |
| 0x0D | JCN | Conditional jump | b a -- | | PC+=(a if b else 1) | |
| 0x0E | JSR | Jump stash return (call) | a -- | -- pc | PC+=a | |
| 0x0F | STH | Stash | a -- | -- a | | |
| 0x10 | LDZ | Load zero page | a: u8 -- MEM[a] | | | |
| 0x11 | STZ | Store zero page | b a: u8 -- | | | MEM[a] = b |
| 0x12 | LDR | Load | a: i8 -- MEM[DATA + a] | | | |
| 0x13 | STR | Store | b a: i8 -- | | | MEM[DATA + a] = b |
| 0x14 | LDA | Load absolute | a: i16 -- MEM[a] | | | |
| 0x15 | STA | Store absolute | b a: i16 -- | | | MEM[a] = b |
| 0x16 | DEI | Device input | a: u8 -- DEV[a] | | | |
| 0x17 | DEO | Device output | b a: u8 -- | | | DEV[a] = b |
| 0x18 | ADD | Add | b a -- a + b | | | |
| 0x19 | SUB | Subtract | b a -- a - b | | | |
| 0x1A | MUL | Multiply | b a -- a * b | | | |
| 0x1B | DIV | Divide | b a -- a / b | | | |
| 0x1C | AND | And | b a -- a && b | | | |
| 0x1D | ORA | Or | b a -- a\|b | | | |
| 0x1E | EOR | Exclusive or (xor) | b a -- a^b | | | |
| 0x1F | SFT | Shift | b a -- b >> (a & 0x0f) << ((a & 0xf0) >> 4) | | | |
### Mode bits
Note that all these instructions are listed with `keep=0,return=0,short=0`.
The UXN documentation refers to `ADD` with `keep=1` as `ADDk`.
`ADD` with `short=1` is `ADD2`, and with `keep=1` would be `ADD2k`.
`POP` with `return=1` is `POPr`.
Conventionally these suffixes are formatted `%/2?k?r?/` as required.
#### Short mode
In When the `short` bit is set, the opcode will pop two 8bi quantities from the stack where it would only consume one in `byte` mode.
Each `b a` pair so popped is considered to be the unsigned 16bi quantity `a + b<<8`
For instance `ADDs` would be `a b c d - (a<<8 + b) + (c<<8+d)`.
**Exceptions:**
- The load/store operations move two 8bi values not one. `STR` becomes `c b a -- MEM[DATA + a] = ` Relative address computation is unaffected.
- The jump opcodes become absolute rather than computed relative jumps.
#### Keep mode
When the `keep` bit is set, no values are popped from the stack.
For instance `ADD2k` would be `a b c d -- a b c d e f`.
#### Return mode
"return mode" flips the stacks.
If `STH` pops from data and pushes to return, `STHr` pops from return and pushes to data.
Likewise if `ADD` pops from data and pushes to data, `ADDr` would pop from data and push to data.
`JMP2` would load `b a` from the data stack and branch absolutely, `JMP2r` would do the same from the control stack.
`DUP2r` would `a b -- a b a b` the return stack.
The bytecode sequence `LIT 0x01 STH JSRr BRK` would load `0x1` to the data stack, pop and stash it to the return stack, perform a relative jump by `+1` while pushing `PC` to the data stack and halt.