Uxntal is the programming language for the Uxn stack-machine.
The programming language for creating Uxn-compatible applications is called Uxntal or simply Tal, it is a unique flavor of assembly designed for this unique stack-machine. The program that tranforms Tal files, into Uxn-compatible roms, is the Uxn Assembler(uxnasm).
Stack machine programming might look at bit odd, as it uses a postfix notation, which means that operators are always found at the end of an operation. For instance, one would write
3 4 + instead of
3 + 4. The expression written
(5 + 10) * 3 in conventional notation would be written
3 10 5 + * in reverse Polish notation.
There are 32 opcodes, each opcode occupies 5 bits of a byte, the remaining 3 are used to select modes of that opcode, modes are explained below.
Operator modes are indicated by appending extra characters at the end of the opcode, for example, the short mode for the
ADD opcode is
ADD2, modes can also be combined, for example:
|0x05||a b||b a||0x15||val a*|
|0x06||a b||a b a||0x16||a||val|
|0x07||a b c||b c a||0x17||val a|
|0x08||a b||flag||0x18||a b||res|
|0x09||a b||flag||0x19||a b||res|
|0x0a||a b||flag||0x1a||a b||res|
|0x0b||a b||flag||0x1b||a b||res|
|0x0d||flag a||0x1d||a b||res|
The jump operators will jump to a relative address in normal mode, and to an absolute address in short mode. All memory operators expect a single byte, except for the absolute operators expecting an absolute address. Shift in short mode is expecting a single byte.
The keep mode makes it possible for any operator to operate without consuming items from the stack and simply pushing the result on top.
The short mode makes it possible for any operator to operate on 16-bits by pushing and popping the necessary extra items from the stack. In the case of jump opcodes(
JCN2) the short mode operation jumps to an absolute address in memory. For the getters and setters, the short mode operation indicates the size of the data to read/write.
The return mode makes it possible for any operator to operate on the return-stack directly, for that reason there is no dedicated return opcode. For example, the JumpStash(
JSR) operator 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 directly from the return-stack.
Uxntal has no reserved words besides the 32 opcodes, each element of the program has its own rune. Comments are within parentheses, the curlies are used in the definition of macros, and the square brackets are ignored.
|sublabel spacer||raw char|
In the following example, our program begins with
;hello-world pushing the absolute address of the label
@hello-world to the top of the stack. An absolute address is made of two bytes. We then assign the label
&loop to this position of the program so we can return to it later.
LDAk opcode takes two bytes at the top of the stack to form an absolute address, and loads the value in memory found at that address in memory, in this case, the ascii value of the letter H. That value is sent to the Console's port named
char(#18) which prints that character to the terminal.
Next, we increment the address found on top of the stack by 1, we use
ADD2 because we know the items on the stack to be an address made of two bytes. We load the incremented value, the
JCN opcode will jump to the position of label
&loop as long as the item before the address is not zero. We complete the program with
POP2 to remove the address on the stack, to keep the stack clean.
( dev/console ) |10 @Console [ &pad $8 &char ] ( init ) |0100 ( -> ) ;hello-word &loop ( send ) LDAk .Console/char DEO ( incr ) #0001 ADD2 ( loop ) LDAk ,&loop JCN POP2 BRK @hello-word "Hello 20 "World!
The memory and stacks contains 8-bits values, to differentiate operations from literal numbers, the
LIT opcode will push the following byte from memory onto the stack, the 16-bits mode
LIT2 opcode will push the following short.
Immediate, or literal, addressing allows to directly specify a byte or short constant. Deferred addressing is a form of immediate addressing where the address of a label is put on the stack.
Uxntal allows for basic control flow, here are some of them:
#00 #0d &loop ( body ) SWP #01 ADD SWP LTHk ,&loop JCN POP2
#00 #0d &while ( body ) DUP2 EQU ,&end JCN SWP #01 ADD SWP ,&while JMP &end POP2
#02 DUP #01 NEQ ,&b JCN ( a ) &b DUP #02 NEQ ,&c JCN ( b ) &c DUP #03 NEQ ,&default JCN ( c ) &default POP
incoming(4): roms uxn noodle bifurcan
Last update on 15K07, edited 21 times. +68/142fh