XXIIVV

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.


Opcodes

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.

modeopcode
keepreturnshort00000

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: ADD2r.

Stack Memory
0x00BRK Break 0x10LDZ LoadZeropageaval
0x01LIT Literal++ 0x11STZ StoreZeropageval a
0x02--- 0x12LDR LoadRelativeaval
0x03POP Popa 0x13STR StoreRelativeval a
0x04DUP Duplicateaa a 0x14LDA LoadAbsolutea*val
0x05SWP Swapa bb a 0x15STA StoreAbsoluteval a*
0x06OVR Overa ba b a 0x16DEI Device Inaval
0x07ROT Rotatea b cb c a 0x17DEO Device Outval a
Logic Arithmetic
0x08EQU Equala bflag 0x18ADD Adda bres
0x09NEQ NotEquala bflag 0x19SUB Subtracta bres
0x0aGTH GreaterThana bflag 0x1aMUL Multiplya bres
0x0bLTH LesserThana bflag 0x1bDIV Dividea bres
0x0cJMP Jumpa 0x1cAND Anda bres
0x0dJCN JumpCondflag a 0x1dORA Ora bres
0x0eJSR JumpStashars 0x1eEOR ExclusiveOra bres
0x0fSTH Stashars 0x1fSFT Shifta bres

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.

Modes

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(JMP2, JSR2, and 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.

Programming

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.

Runes
%macro-define #literal hex
|pad(absolute) .literal addr(zero-page)
$pad(relative) ,literal addr(relative)
@label-define ;literal addr(absolute)
&sublabel-define :raw addr
/sublabel spacer 'raw char
"raw word

Example

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.

Next, the 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 #0001 and 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!

Addressing

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.

ByteShort
Immediate#ab#cdef
DeferredZero-pageRelativeAbsolute
.label,label;label
RawByteCharShortAbsolute
ab'Qcdef:label

Control flow

Uxntal allows for basic control flow, here are some of them:

ForLoopWhileLoopSwitch
#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
— Submit an edit to uxntal.htm(159 lines)

incoming(4): roms uxn noodle bifurcan

Last update on 15K07, edited 21 times. +68/142fh ----||