XXIIVV

Uxntal Syntax

In stack programming there are no variables and no precedence rules, the calculations are merely performed in the sequence in which they are presented. The order with which elements come off the stack is known as Last In, First Out. In the stack a b c, the c item was the last to be added, and will be the first to be removed.

#01 DUP ADD #03 MUL program
 01  01  02  03  06 stack
     01      02

Numbers in Uxntal are not decimal, they are expressed in hexadecimal. Which means that counting goes like: one, two, three, four, five, six, seven, eight, nine, ha, be, ce, de, he, fe, ten! It takes some getting used to, but don't worry, you'll get the hang of it.

Now, without further ado..

Let's dive into it!

The following example program prints the phrase "Hello World!" by pushing the address to a label on the stack, and iterating through each letter found at that address with a loop that increments the pointer until it reaches end of the phrase, at which point, the stack is emptied and the evaluation halts.

A word starting with @ defines a label, and one starting with ; pushes the absolute address of a label to the stack. With that in mind, ;text pushes the two bytes equal to the address of the @text label to the stack. In the interpreter above, press "step" to walk through each step of the evaluation.

Next, we define a new label named @while, to mark the start of the loop that will print each character stored at the text label.

The LDAk opcode loads a byte at the address currently at the top of the stack, in this case, the ascii letter H(48). The k-mode indicates that the operator will not consume the address.

The DUP opcode makes a copy of the letter. The ?{ pops that letter from the stack, and if it is not zero, we jump to the corresponding }, which is an anonymous label(λ00).

( Disassembly of the example above:
|addr   bytecode   Uxntal
-----   --------   ------- )
|0100   a0 01 12   ( ;text )

@while
|0103   94         ( LDAk )
|0104   06         ( DUP )
|0105   20 00 03   ( ?λ00 )
|0108   02         ( POP )
|0109   22         ( POP2 )
|010a   00         ( BRK )

The #18 word pushes a number to the stack, which maps to the Console/write port(#18), followed by the DEO opcode that pops both bytes(the letter and the port) and sends the letter to that device port, telling the Console to print it, leaving only the address on top of the stack.

The INC2 opcode increments the address, moving the text pointer to the next letter. The 2-mode is used because the address is made of two bytes.

@λ00
|010b   80 18      ( #18 )
|010d   17         ( DEO )
|010e   21         ( INC2 )
|010f   40 ff f1   ( !while )

Finally, with !while we jump back to the @while label, and repeat the loop until there are no more letters to load. When that happens, we POP to remove the duplicated letter, and POP2 to remove the address on the stack to keep the stack clean at the end of the evaluation.

@text
|0112   48 65 6c   ( H e l )
|0115   6c 6f 20   ( l o   )
|0117   57 6f 72   ( W o r )
|011b   6c 64 21   ( l d ! )

Summary

Comments are within parentheses, numbers are lowercase hexadecimal shorts or bytes and opcodes are uppercased reserved words with lowercased modes. A rune is a non-alphanumeric symbol at the start of a word, literal numbers are prefixed with a # rune, addressing is done by one of six runes. Labels and macros are unique non-numeric, non-opcode and non-runic symbols.

Padding RunesNumber Rune
|absolute $relative #literal number
Label RunesAscii Runes
@parent &child "raw ascii
Addressing RunesWrapping Runes
,literal relative _raw relative ( )comment
.literal zero-page -raw zero-page { }anonymous
;literal absolute =raw absolute [ ]ignored
Immediate RunesPre-processor Runes
!jmi ?jci % { }macro
OSZAR »