nxu/uxntal.1

255 lines
9.0 KiB
Groff
Raw Normal View History

2024-08-01 23:28:16 -04:00
.\" Manpage reference for uxntal.
.\" Contact d_m@plastic-idolatry.com to correct errors or typos.
.TH uxntal 1 "01 Aug 2024" "1.0" "Uxntal Reference Guide"
.SH NAME
uxntal \- assembly langauge for Varvara virtual machine
.SH DESCRIPTION
Uxntal is an 8-bit instruction set for programming the Varvara virtual machine.
It uses the lower 5-bits to specify an opcode, and the upper 3-bits to specify
optional modes.
2024-08-02 11:35:19 -04:00
ROMs consist of a 16-bit address space of bytes. Any byte can be interpreted as either data or an instruction. A 2-byte program counter (\fIpc\fP) determines the address of the next instruction to decode and run.
2024-08-01 23:28:16 -04:00
Instructions manipulate data using two stacks: a working stack (\fBwst\fP) and a return stack (\fBrst\fP). Each stack consists of 256 bytes, and in the case of overflow or underflow the stack pointer will wrap (the stacks are circular).
2024-08-02 11:35:19 -04:00
There are also 256 bytes of device memory, which are used to interact with the virtual machine and its devices.
2024-08-01 23:28:16 -04:00
2024-08-02 11:35:19 -04:00
.SH INSTRUCTION LAYOUT
2024-08-01 23:28:16 -04:00
2024-08-02 11:35:19 -04:00
0x01 ----
0x02 \\
0x04 +- \fIopcode\fP
0x08 /
0x10 ----
2024-08-01 23:28:16 -04:00
0x20 ---- 2: \fIshort\fP mode
0x40 ---- r: \fIreturn\fP mode
0x80 ---- k: \fIkeep\fP mode
.SH OPCODE LAYOUT
There are 32 base values for opcodes:
0x00 \fB***\fP 0x08 \fBEQU\fP 0x10 \fBLDZ\fP 0x18 \fBADD\fP
0x01 \fBINC\fP 0x09 \fBNEQ\fP 0x11 \fBSTZ\fP 0x19 \fBSUB\fP
0x02 \fBPOP\fP 0x0a \fBGTH\fP 0x12 \fBLDR\fP 0x1a \fBMUL\fP
0x03 \fBNIP\fP 0x0b \fBLTH\fP 0x13 \fBSTR\fP 0x1b \fBDIV\fP
0x04 \fBSWP\fP 0x0c \fBJMP\fP 0x14 \fBLDA\fP 0x1c \fBAND\fP
0x05 \fBROT\fP 0x0d \fBJCN\fP 0x15 \fBSTA\fP 0x1d \fBORA\fP
0x06 \fBDUP\fP 0x0e \fBJSR\fP 0x16 \fBDEI\fP 0x1e \fBEOR\fP
0x07 \fBOVR\fP 0x0f \fBSTH\fP 0x17 \fBDEO\fP 0x1f \fBSFT\fP
The "complete" opcode's value can be derived by combining the base value with its flags.
For example, \fBADD2k\fP is \fB(0x18 | 0x20 | 0x80)\fP = \fB0xb8\fP.
Unlike other opcodes, \fB0x00\fP is contextual. Its meaning depends on the \fImode\fP bits provided:
0x00 \fBBRK\fP 0x80 \fBLIT\fP
0x20 \fBJCI\fP 0xa0 \fBLIT2\fP
0x40 \fBJMI\fP 0xc0 \fBLITr\fP
0x60 \fBJSI\fP 0xe0 \fBLIT2r\fP
.SH REGULAR INSTRUCTIONS
Regular instructions have a single stack effect which is modified in a predictable way by any additional modes.
For example the generic effect for \fBADD\fP is ( x y -- x+y ). The eight combinations of modes have the following effects:
\fBADD\fP ( x^ y^ -- x+y^ ) sum two bytes using \fBwst\fP
\fBADDr\fP ( [x^ y^] -- [x+y^] ) sum two bytes using \fBrst\fP
\fBADD2\fP ( x* y* -- x+y* ) sum two shorts using \fBwst\fP
\fBADD2r\fP ( [x* y*] -- [x+y*] ) sum two shorts using \fBrst\fP
\fBADDk\fP ( x^ y^ -- x^ y^ x+y^ ) sum two bytes using \fBwst\fP, retain arguments
\fBADDkr\fP ( [x^ y^] -- [x^ y^ x+y^] ) sum two bytes using \fBrst\fP, retain arguments
\fBADD2k\fP ( x* y* -- x* y* x+y* ) sum two shorts using \fBwst\fP, retain arguments
\fBADD2kr\fP ( [x* y*] -- [x* y* x+y*] ) sum two shorts using \fBrst\fP, retain arguments
Thus for regular instructions writing a "generic" effect (leaving sigils off values whose size depends on \fIshort\fP mode) is sufficient to describe its behavior across all eight variations. Note that some instructions always read values of a fixed size. (For example the boolean condition read by \fBJCN\fP is always one byte, no matter what modes are used.)
.SS INC
( x -- x+1 )
.SS POP
( x -- )
.SS NIP
( x y -- y )
.SS SWP
( x y -- y x )
.SS ROT
( x y z -- y z x )
.SS DUP
( x -- x x )
.SS OVR
( x y -- x y x )
2024-08-02 11:31:03 -04:00
.SS EQU
( x y -- x==y^ )
2024-08-02 13:26:17 -04:00
Result is guaranteed to be boolean (\fB0x00\fP or \fB0x01\fP).
2024-08-02 11:31:03 -04:00
.SS NEQ
( x y -- x!=y^ )
2024-08-02 13:26:17 -04:00
Result is guaranteed to be boolean (\fB0x00\fP or \fB0x01\fP).
2024-08-02 11:31:03 -04:00
.SS GTH
( x y -- x>y^ )
2024-08-02 13:26:17 -04:00
Result is guaranteed to be boolean (\fB0x00\fP or \fB0x01\fP).
2024-08-02 11:31:03 -04:00
.SS LTH
( x y -- x<y^ )
2024-08-02 13:26:17 -04:00
Result is guaranteed to be boolean (\fB0x00\fP or \fB0x01\fP).
2024-08-02 11:31:03 -04:00
.SS JMP
( x -- ; pc <- x )
The program counter (\fIpc\fP) is unconditionally updated. When \fIx\fP is a byte, it is treated as relative (\fBpc += x\fP) and when \fIx\fP is a short it is treated as absolute (\fBpc = x\fP).
.SS JCN
( x bool^ -- ; pc <- x if bool )
The program counter (\fIpc\fP) is updated when \fIbool\fP is non-zero. When \fIx\fP is a byte, it is treated as relative (\fBpc += x\fP) and when \fIx\fP is a short it is treated as absolute (\fBpc = x\fP).
.SS JSR
( x -- [pc+1*] )
Store the next address to execute before unconditionally updating the program counter (\fIpc\fP). This instruction is usually used to invoke subroutines, which use the \fBJMP2r\fP to return.
.SS STH
( x -- [x] )
.SS LDZ
( zp^ -- x )
Load data from a zero-page address (\fB0x0000 - 0x00ff\fP).
.SS STZ
( x zp^ -- )
Store data at a zero-page address (\fB0x0000 - 0x00ff\fP).
.SS LDR
( rel^ -- x )
Load data from a relative address (\fBpc + x\fP).
2024-08-02 11:35:19 -04:00
Note that unlike \fBLDZk\fP and \fBLDAk\fP the \fBLDRk\fP instruction is not very useful, since a relative address is usually only meaningful when run from a particular address (i.e. for a particular \fIpc\fP value).
2024-08-02 11:31:03 -04:00
.SS STR
( x rel^ -- )
Store data at a relative address (\fBpc + x\fP).
2024-08-02 11:35:19 -04:00
Note that unlike \fBSTZk\fP and \fBSTAk\fP the \fBSTRk\fP instruction is not very useful, since a relative address is usually only meaningful when run from a particular address (i.e. for a particular \fIpc\fP value).
2024-08-02 11:31:03 -04:00
.SS LDA
( abs* -- x )
Load data from an absolute address (\fB0x0000 - 0xffff\fP).
.SS STA
( x abs* -- )
Store data at an absolute address (\fB0x0000 - 0xffff\fP).
.SS DEI
( dev^ -- x )
Read data from a device port (\fB0x00 - 0xff\fP).
Reading from some ports may have an effect on the underlying VM; in other cases it will simply read values from device memory. See Varvara device documentation for more details.
.SS DEO
( x dev^ -- )
Write data to a device port (\fB0x00 - 0xff\fP).
Writing to some ports may have an effect on the underlying VM; in other cases it will simply write values to device memory. See Varvara device documentation for more details.
2024-08-02 13:26:17 -04:00
.SS ADD
( x y -- x+y )
Overflow will be truncated, so \fB#ff #03 ADD\fP will evaluate to \fB0x02\fP.
.SS SUB
( x y -- x-y )
Underflow will be truncated, so \fB#01 #03 SUB\fP will evaluate to \fB0xfe\fP.
.SS MUL
( x y -- xy )
Overflow will be truncated, so \fB#11 #11 MUL\fP will evaluate to \fB0x21\fP.
.SS DIV
( x y -- x/y )
\fBDIV\fP implements \fIEuclidean division\fP, which is also known as \fIinteger division\fP. It returns whole numbers, so \fB#08 #09 DIV\fP evaluates to \fB0x00\fP.
Division by zero will return zero (instead of signaling an error).
Unlike \fBADD\fP, \fBSUB\fP, and \fBMUL\fP, \fBDIV\fP does not behave correctly for numbers which should be treated as signed. For example, the signed byte representation of \fB-2\fP is \fB0xfe\fP, but \fB#06 #fe DIV\fP evaluates to \fB0x00\fP (\fB6 / 254 = 0\fP). For signed values the correct result should instead be \fB0xfd\fP (\fB6 / -2 = -3\fP).
There is no \fIremainder\fP instruction, but the phrase \fBDIVk MUL SUB\fP can be used to compute the remainder.
.SS AND
( x y -- x&y )
.SS ORA
( x y -- x|y )
.SS EOR
( x y -- x^y )
.SS SFT
( x rl^ -- (x>>l)<<r )
Given a byte \fIrl\fP consisting of a low nibble (\fIl\fP) and a high nibble (\fIh\fP), this instruction shifts \fIx\fP left by \fIl\fP and then right by \fIr\fP.
Right shifts are unsigned (they introduce zero bits); there are no signed shifts.
Since the largest values (\fIshort\fP) are 16-bit, one nibble (\fB0x0 - 0xf\fP) is sufficient to express all useful left or right shifts.
Right: \fB#ff #03 SFT\fP evaluates to \fB0x1f\fP.
Left: \fB#ff #20 SFT\fP evaluates to \fB0xfc\fP.
Both: \fB#ff #23 SFT\fP evaluates to \fB0x7c\fP.
2024-08-01 23:28:16 -04:00
.SH SPECIAL INSTRUCTIONS
2024-08-02 11:31:03 -04:00
These instructions do not accept all mode flags (some do not accept any).
2024-08-01 23:28:16 -04:00
.SS BRK
The break instruction is used to end a vector call and return control to the virtual machine.
.SS JCI, JMI, and JSI
The "immediate jump" instructions are produced by the assembler. They interpret the next 2 bytes of the ROM as a relative address (\fIaddr\fP) and have the following effects:
2024-08-01 23:33:19 -04:00
\fBJMI\fP ( -- ) jump to \fIaddr\fP unconditionally
\fBJCI\fP ( bool^ -- ) jump to \fIaddr\fP if \fIbool\fP is non-zero
2024-08-02 11:31:03 -04:00
\fBJSI\fP ( -- [pc*] ) jump to \fIaddr\fP saving the current address (\fIpc\fP) on the return stack
2024-08-01 23:28:16 -04:00
(The instruction pointer will be moved forward 2 bytes, past the relative address.)
.SS LIT, LIT2, LITr, and LIT2r
The "literal" instructions are used to push new data onto the stacks. They interpret the next 1-2 bytes of the ROM (\fIwx\fP, \fIwxyz\fP) as data and push it onto the corresponding stack:
\fBLIT\fP ( -- wx^ ) push literal byte \fIwx\fP onto the \fBwst\fP
\fBLITr\fP ( -- [wx^] ) push literal byte \fIwx\fP onto the \fBrst\fP
\fBLIT2\fP ( -- wxyz* ) push literal short \fIwxyz\fP (2 bytes) onto the \fBwst\fP
\fBLIT2r\fP ( -- [wxyz*] ) push literal short \fIwxyz\fP (2 bytes) onto the \fBrst\fP
(The instruction pointer will be moved forward 1-2 bytes, past the literal data.)