The Uxntal Manual
The LIT and BRK opcodes share the same opcode space, the BRK opcode is stored as 00
, whereas the LIT and LIT2 opcodes are stored where LITk and LIT2k would be stored. The extra modes of the BRK/LIT opcode are unused.
Addressing
Labels
A position in a program can be defined with a label, a label is a permanent reference to that specific location in the program. Labels can have sublabels, which are automatically prefixed with their parent label. In the following example, print-str
is a label, and code>print-str/while is a sublabel.
@print-str ( str* -- ) &while LDAk #18 DEO INC2 LDAk ,&while JCN POP2 JMP2r
References
There are 4 ways to reference a label, a zero-page reference can only target a label located within the first 256 bytes of memory. A relative reference can only target a label located 127 bytes before, or 127 bytes after the current location, an absolute reference can target a label anywhere in the program.
.label
, literal zero-page,label
, literal relative;label
, literal absolute:label
, raw absolute
Instructions
Modes
By default, operators operate on bytes, notice how in the following example only the last two bytes #45
and #67
are added, even if there are two shorts on the stack.
#1234 #4567 ADD 12 34 ac 00 00 00 00 00
The short mode consumes two bytes from the stack. In the case of jump opcodes, the short-mode operation jumps to an absolute address in memory. For the memory accessing opcodes, the short mode operation indicates the size of the data to read and write.
#1234 #4567 ADD2 57 9b 00 00 00 00 00 00
The keep mode does not consume items from the stack, and pushes the result on top. The following example adds the two shorts together, but does not consume them. You can see the complete list of stack permutations.
#1234 #4567 ADD2k 12 34 45 67 57 9b 00 00
POPk | a | a | SWPk | a b | a b b a |
DUPk | a | a a a | OVRk | a b | a b a b a |
NIPk | a b | a b b | ROTk | a b c | a b c b c a |
The return mode makes it possible for any opcode to operate on the return-stack directly. For that reason, there is no dedicated return opcode. For example, the JSR
opcode pushes the program's address onto the return stash before jumping, to return to that address, the JMP2r
opcode is used, where instead of using the address on the working-stack, it takes its address from the return-stack.
#1234 #4567 STH ROT STH ADDr STHr 34 45 79 00 00 00 00 00
Opcodes
Break
BRK
Halts the program.--
Literal
LIT
Pushes the next bytes in memory, on the stack.-- a
LIT 12 ( 12 ) LIT2 abcd ( ab cd )
Increment
INC
Adds 1 to the value at the top of the stack.a -- b
#01 INC ( 02 ) #0001 INC2 ( 00 02 ) #0001 INC2k ( 00 01 00 02 )
Pop
POP
Removes the value at the top of the stack.a --
#1234 POP ( 12 ) #1234 POP2 ( ) #1234 POP2k ( 12 34 )
Nip
NIP
Removes the second value from the stack. This is practical to convert a small short into a byte.a b -- b
#1234 NIP ( 34 ) #1234 #5678 NIP2 ( 56 78 ) #1234 #5678 NIP2k ( 12 34 56 78 56 78 )
Swap
SWP
Exchanges the first and second values at the top of the stack.a b -- b a
#1234 SWP ( 34 12 ) #1234 SWPk ( 12 34 34 12 ) #1234 #5678 SWP2 ( 56 78 12 34 ) #1234 #5678 SWP2k ( 12 34 56 78 56 78 12 34 )
Rotate
ROT
Rotates three values at the top of the stack, to the left, wrapping around.a b c -- b c a
#1234 #56 ROT ( 34 56 12 ) #1234 #56 ROTk ( 12 34 56 34 56 12 ) #1234 #5678 #9abc ROT2 ( 56 78 9a bc 12 34 ) #1234 #5678 #9abc ROT2k ( 12 34 56 78 9a bc 56 78 9a bc 12 34 )
Duplicate
DUP
Duplicates the value at the top of the stack.a -- a a
#1234 DUP ( 12 34 34 ) #12 DUPk ( 12 12 12 ) #1234 DUP2 ( 12 34 12 34 )
Over
OVR
Duplicates the second value at the top of the stack.a b -- a b a
#1234 OVR ( 12 34 12 ) #1234 OVRk ( 12 34 12 34 12 ) #1234 #5678 OVR2 ( 12 34 56 78 12 34 ) #1234 #5678 OVR2k ( 12 34 56 78 12 34 56 78 12 34 )
Logic
Equal
EQU
Pushes 01 to the stack if the two values at the top of the stack are equal, 00 otherwise.a b -- bool8
#1212 EQU ( 01 ) #1234 EQUk ( 12 34 00 ) #abcd #ef01 EQU2 ( 00 ) #abcd #abcd EQU2k ( ab cd ab cd 01 )
Not Equal
NEQ
Pushes 01 to the stack if the two values at the top of the stack are not equal, 00 otherwise.a b -- bool8
#1212 NEQ ( 00 ) #1234 NEQk ( 12 34 01 ) #abcd #ef01 NEQ2 ( 01 ) #abcd #abcd NEQ2k ( ab cd ab cd 00 )
Greater Than
GTH
Pushes 01 to the stack if the second value at the top of the stack is greater than the value at the top of the stack, 00 otherwise.a b -- bool8
#1234 GTH ( 00 ) #3412 GTHk ( 34 12 01 ) #3456 #1234 GTH2 ( 01 ) #1234 #3456 GTH2k ( 12 34 34 56 00 )
Lesser Than
LTH
Pushes 01 to the stack if the second value at the top of the stack is lesser than the value at the top of the stack, 00 otherwise.a b -- bool8
#0101 LTH ( 00 ) #0100 LTHk ( 01 00 00 ) #0001 #0000 LTH2 ( 00 ) #0001 #0000 LTH2k ( 00 01 00 00 00 )
Control-Flow
Jump
JMP
Moves the program counter by a signed value equal to the byte on the top of the stack, or an absolute address in short mode.addr --
Jump Conditional
JCN
If the byte preceeding the address is not 00, moves the program counter by a signed value equal to the byte on the top of the stack, or an absolute address in short mode.cond8 addr --
Jump Stash Return
JSR
Pushes the value of the program counter to the return-stack and moves the program counter by a signed value equal to the byte on the top of the stack, or an absolute address in short mode.addr --
Stash
STH
Moves the value at the top of the stack, to the return stack.a --
Memory
Load Zero-Page
LDZ
Pushes the value at an address within the first 256 bytes of memory, to the top of the stack.addr8 -- value
Store Zero-Page
STZ
Writes a value to an address within the first 256 bytes of memory.val addr8 --
Load Relative
LDR
Pushes the value at a relative address, to the top of the stack. The possible relative range is -128 to +127 bytes.addr8 -- value
Store Relative
STR
Writes a value to a relative address. The possible relative range is -128 to +127 bytes.val addr8 --
Load Absolute
LDA
Pushes the value at a absolute address, to the top of the stack.addr16 -- value
Store Absolute
STA
Writes a value to a absolute address.val addr16 --
Device Input
DEI
Pushes a value from the device page, to the top of the stack. The target device might capture the reading to trigger an I/O event.device8 -- value
Device Output
DEO
Writes a value to the device page. The target device might capture the writing to trigger an I/O event.val device8 --
Arithmetic
Add
ADD
Pushes the sum of the two values at the top of the stack.a b -- c
Subtract
SUB
Pushes the difference of the first value minus the second, to the top of the stack.a b -- c
Multiply
MUL
Pushes the product of the first and second values at the top of the stack.a b -- c
Divide
DIV
Pushes the quotient of the first value over the second, to the top of the stack.a b -- c
Bitwise
And
AND
Pushes the result of the bitwise operation AND, to the top of the stack.a b -- c
Or
ORA
Pushes the result of the bitwise operation OR, to the top of the stack.a b -- c
Exclusive Or
EOR
Pushes the result of the bitwise operation XOR, to the top of the stack.a b -- c
Shift
SFT
Shifts the bits of the second value of the stack to the left or right, depending on the control value at the top of the stack. The high nibble of the control value indicates how many bits to shift left, and the low nibble how many bits to shift right. The rightward shift is done first.a shift8 -- c
#34 #10 SFT ( 68 ) #34 #01 SFT ( 1a ) #34 #33 SFTk ( 34 33 30 ) #1248 #34 SFTk2 ( 12 48 34 09 20 )
- Kira Oakley, contributor
- Ismael Venegas Castello, contributor