https://wiki.xxiivv.com/site/uxntal.html
XXIIVV
* subleq
* brainfuck
* chip8
* uxn
* secd
* uxntal
* varvara
* porporo
* uxntal stacks
* uxntal notation
* uxntal numbers
* uxntal opcodes
* uxntal labels
* uxntal macros
* uxntal memory
* uxntal devices
* uxntal software
Rostiger's Uxn ZineRostiger's Uxn Zine16X08
A programming language for the Uxn virtual machine.
[uxn]
Uxn programs are written in a concatenative flavor of assembly
designed especially to map to the idiosyncrasies of this strange
little computer.
See the Quick Setup to get started.
* Cheatsheet, by Weeble.
* Zine, by Clemens Scott.
* Tutorial, by Compudanzas
* Manual(7), by Eirikr Asheim.
* BNF Notation, by Jack Leightcap.
Introduction
Firstly, there are 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
Uxntal numbers 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 copy from
the stack, and if it is not zero, we jump to the corresponding },
which is an anonymous label(l00).
( Disassembly of the example above:
|addr bytecode Uxntal
----- -------- ------- )
|0100 a0 01 12 ( ;text )
@while
|0103 94 ( LDAk )
|0104 06 ( DUP )
|0105 20 00 03 ( ?l00 )
|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 address addresses are
always made of two bytes.
@l00
|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 )
|011a 6c 64 21 ( l d ! )
Summary
Comments are within parentheses, numbers are lowercase hexadecimal
shorts or bytes, and opcodes are uppercase reserved words with
lowercase modes. Addressing is done by one of six runes. Valid label
and macro names are unique non-numeric, non-opcode and non-runic.
+----------------------------------------------------------+
| Padding Runes | Number Rune |
|-------------------------------------+--------------------|
|||absolute |$|relative |# |literal |
| | | | | |number |
|-------------------------------------+--------------------|
| Label Runes | Ascii Runes |
|-------------------------------------+--------------------|
|@|parent |&|child |" |raw ascii |
|-------------------------------------+--------------------|
| Addressing Runes | Wrapping Runes |
|-------------------------------------+--------------------|
|,|literal relative |_|raw relative |( ) |comment |
|-+------------------+-+--------------+------+-------------|
|.|literal zero-page |-|raw zero-page |{ } |anonymous |
|-+------------------+-+--------------+------+-------------|
|;|literal absolute |=|raw absolute |[ ] |ignored |
|-------------------------------------+--------------------|
| Immediate Runes |Pre-processor Runes |
|-------------------------------------+--------------------|
|!|jmi |?|jci |% { } |macro |||
+----------------------------------------------------------+
Uxntal Stacks
All programming in Uxntal is done by manipulating the working stack,
and return stack, each stack contains 256 bytes. Here are some stack
primitives assuming the initial state of the stack is a b c where c
is the top of the stack:
+------------------------------------+
|POP|a b |Discard top item. |
|---+-------+------------------------|
|NIP|a c |Discard second item. |
|---+-------+------------------------|
|SWP|a c b |Move second item to top.|
|---+-------+------------------------|
|ROT|b c a |Move third item to top. |
|---+-------+------------------------|
|DUP|a b c c|Copy top item. |
|---+-------+------------------------|
|OVR|a b c b|Copy second item to top.|
+------------------------------------+
A byte is a number between 0-255(256 values), a short is a number
between 0-65535(65536 values) made of two bytes, each byte in a short
can be manipulated individually:
#0a #0b POP 0a
#12 #3456 NIP 12 56
#1234 DUP 12 34 34
The two stacks are circular, to pop an empty stack does not trigger
an error, but merely means to set the stack pointer to 255. There are
no invalid programs, any sequence of bytes is a potential Uxn
program. Values are moved between stacks with the STH opcode.
WST 00 00 00 00 00 00|12 34 <02
RST 00 00 00 00 00 00 00|56 <01
The program above contains 12 and 34 on the working stack, and 56 on
the return stack. The stack content can always be printed by sending
a non-null byte to the System/debug port.
Uxntal Notation
The stack-effect notation follows that of the Forth programming
language, where each item on the left of the -- spacer is the state
of the stack before, and to the right, the state of the stack after.
On each side, the right-most item is the last to the pushed and the
first to be removed:
@routine ( a b -- a b res )
ADDk JMP2r
Single items are a byte long, and shorts are indicated with a *
suffix, the order in which they appear is the order of the stack with
the top item to the right:
@routine ( a b* -- b* a )
ROT JMP2r
The dot notation is used to indicate that stack-effects to the right
of the dot are happening on the return stack:
@routine ( a . b -- c )
STHr ADD JMP2r
If a routine is a vector, it uses the arrow notation.
@on-event ( -> )
BRK
This notation also holds for macros as well, the notation goes before
the macro's body:
%macro ( a b -- res ) {
DIVk MUL SUB }
The stack notation is merely present to help readability but can be
altogether disregarded without impacting the program.
Comments
A comment starts with any token beginning with opened parenthesis,
and ends at its corresponding closed parenthesis. Comments may be
nested, the enclosed comments parentheses must be whitespace
separated on both sides.
( ( nested ) )
( 1+2*(4/3) )
Outermost comments may be named, which means that sometimes the open
parenthesis is immediately followed by a word holding some meaning to
external tools.
(doc
This is a docstring. )
Special comments are sometimes used to group routines together, they
are similar to the pragma mark notation:
(
@|Group )
Brackets
The square brackets do nothing, they are used as an aid for
readability and formatting, they are useful for making explicit
certain things like grouping behaviors, joining literals or
indicating lookup tables.
@routine ( -- )
[ LIT2 20 -Console/write ] DEO JMP2r
%min ( a b -- r ) {
GTHk [ JMP SWP ] POP }
@sprite [ 00 66 ff ff ff 7e 3c 18 ]
Uxntal Numbers
Uxntal uses only lowercase unsigned hexadecimal numbers of either 2
or 4 characters in length. There are two types of numbers:
* A Literal Hex, like #ab, denotes a number that will be pushed on
the stack when evaluated, it is made of a LIT opcode that matches
its length, followed by a Raw Hex number.
* A Raw Hex, like aa, is the standard textual encoding of data in a
program, generally speaking these are more often loaded than
evaluated. It can be anything, an opcode, an ascii byte, an
address, part of a sprite.
#12 #34 LIT2 5678 ADD2 68 ac
Uxntal Opcodes
Uxn has 32 standard opcodes and 4 immediate opcodes. In the table
below, the pipe(|) character indicates an effect on the return stack,
the pc is the program counter, a value8 indicates a byte length, a
value* indicates a short length, an unspecified length follows the
short mode and a [value] is read from memory.
Stack I Logic Memory I Arithmetic
BRK -- EQU a b -- a=b LDZ abs8 -- [abs8] ADD a b -- a+b
INC a -- a+1 NEQ a b -- a[?]b STZ val abs8 -- SUB a b -- a-b
POP a -- GTH a b -- a>b LDR rel8 -- [rel8] MUL a b -- axb
NIP a b -- b LTH a b -- a )
#0007 fact
BRK
@fact ( n* -- res* )
ORAk ?{ POP2 #0001 JMP2r }
DUP2 #0001 SUB2 fact MUL2
JMP2r
To learn more about each opcode, see the Opcode Reference.
Uxntal Labels
A label is a non-numeric, non-opcode, and non-runic symbol that
correspond to a number between 0 and 65536. A label name is made of
two parts, a scope and a sublabel. Sublabels can be added to a scope
with the &name rune, or by writing the full name, like @scope/name.
Note that a labels like bed, add and cafe are considered numeric.
Functions are simply labels that will be jumped to, and returned
from.
@func ( a b -- c )
&loop
INC GTHk ?&loop
ADD JMP2r
Constants are labels that hold a specific value through the entire
execution of the program. They allow to assign a name to a number,
making the code more readable.
|1400 @limit
Enums are labels with padded members of equal sizes that can be used
as constants in a program, they typically begin by rolling back the
program address with |00:
|00 @Suit &clubs $1 &diamonds $1 &hearts $1 &spades
Structs are labels with padded members of different sizes, that maps
on a data-structure, they typically space the different members with
$1:
|00 @Person &name $2 &age $1 &height $2
Labels can also be used with the padding runes to define a global
length. For example, if one needs to specify a length of 0x30 for
multiple members of a struct, a value can be specified as follow:
|30 @length
|00 @Struct &field $length
Scope
Uxntal objects are defined statically and allow for the enclosed
methods to access encapsulated local &members. The example below
contains an object with the method set-color, accessible from outside
the scope as pen/set-color.
@pen
&position &x $2 &y $2
&color $1
&set-color ( color -- )
,/color STR
JMP2r
New methods and members can extend an existing scope from anywhere by
creating a label with the scope name followed by a slash and the name
of the extension. The &labels declared within the extension have the
same access to local labels as the rest of the object.
@pen/get-position ( -- x* y* )
,/x LDR2 ,/y LDR2
JMP2r
When calling local methods the scope's name can be omitted, starting
at the slash, like /method:
@pen/paint ( -- )
/get-position canvas/draw-line-to
JMP2r
Addressing
A labels is a way of assigning a name to a number. There are six ways
to get the number corresponding to that label. Literal addressing
prefixes the label with a LIT for Relative and Zero-Page addressing,
and LIT2 for absolute addressing.
* Literal Relative, like ,label, pushes a relative distance byte to
the label.
* Literal Zero-Page, like .label, pushes an absolute address byte
to the label.
* Literal Absolute, like ;label, pushes an absolute address short
to the label.
* Raw Relative, like _label, writes a relative distance byte to the
label.
* Raw Zero-Page, like -label, writes an absolute address byte to
the label.
* Raw Absolute, like =label, writes an absolute address short to
the label.
Raw addressing is used for building data-structures and more advanced
programs. A relatively common usage of raw runes is to create
literals directly into the return stack:
[ LIT2r 08 -label ] LDZr ADDr | [.label]+8
Anonymous Labels
Anonymous labels are designated with a curly bracket that points to
its associated closing bracket, and can be nested. Under the hood,
the opening bracket assembles to the address of the closing bracket
which allows the destination address to be used like any other label
such as a JCI ?{, a JMI, !{ or a plain literal ;{. Here are some
example data-structures:
@counted-string
_{ "foo 20 "bar }
@linked-list
={ ={ "A } ={ "B ={ "C } } }
Unless Blocks
It is important to notice that in the case of a conditional jump, the
lambda's content is jumped over when the flag byte is true.
[ LIT2 &last $1 -Mouse/state ] DEI DUP ,&last STR
DUP2 #0001 NEQ2 ?{ ( on down ) }
DUP2 #0101 NEQ2 ?{ ( on drag ) }
DUP2 #0100 NEQ2 ?{ ( on release ) }
POP2
The opening curly bracket assembles to a unique label reference, and
the closing bracket to a corresponding matching label definition.
They do not affect the scope.
Uxntal Macros
A macro is a way of defining inline routines, it allows to create new
words that will be replaced by the body of the macro, as opposed to a
jump where the program counter will move to a routine and back,
therefore it needs to be defined before its usage, as follow:
%modulo ( num denum -- res ) {
DIVk MUL SUB }
@routine ( -- c* )
#18 #03 modulo JMP2r
In the previous example, the token modulo will get replaced by the
body of the macro during assembly:
@routine ( -- c* )
#18 #03 DIVk MUL SUB JMP2r
[uxn]
Uxntal Memory
There are 64kb of addressable memory. Roms are always loaded at
0x0100, which is the address of the Reset Vector and where evaluation
begins. During boot, the stacks, device and addressable memories are
zeroed. During a soft-reboot, the content of the zero-page is
preserved.
+-----------------------------------------------+
|Shared |Memory|RAM |Data |64kb pages|
|-------+------+-------------+-------+----------|
| | | |Data |256 bytes |
| | |Working Stack|-------+----------|
| | | |Pointer|1 byte |
| |Stacks|-------------+-------+----------|
|Private| | |Data |256 bytes |
| | |Return Stack |-------+----------|
| | | |Pointer|1 byte |
| |------+-------------+-------+----------|
| | IO |Devices |Data |256 bytes |
+-----------------------------------------------+
The device page and stacks are located outside of addressable memory.
* An Absolute Padding, like |100 moves the program generation to an
address specified by a number or label.
* A Relative Padding, like $18 moves the program generation by a
distance specified by a number or label.
|18 @width
|100 @on-reset ( -> )
;buffer/end BRK 02 18
|200 @buffer $width &end
Memory is big-endian, when writing or reading a short from memory,
the position is that of the high-byte. The low-byte of a short
written at 0xffff wraps to 0x0000.
#12 #0200 STA 0x0200=12
#3456 #0400 STA2 0x0400=34, 0x0401=56
#0400 LDA 34
The zero-page is the memory located below 0x0100, its purpose is to
store variables that will be accessed often, or needs to be preserved
across a soft-reboot. It is sligthly faster to read and write from
the zero-page using the LDZ and STZ opcodes as they use only a single
byte instead of a short. This memory space cannot be pre-filled in
the rom prior to initialization. The low-byte of a short written at
0xff wraps to 0x00.
#1234 #80 STZ2 0x0080=12, 0x0081=34
#81 LDZ 34
Uxntal Devices
Uxn is non-interruptible, vectors are locations in programs that are
evaluated when certain events occur. A vector is evaluated until a
BRK opcode is encountered. Uxn can communicate with a maximum of 16
devices, each device has 16 ports, each port handles a specific I/O
message. Ports are mapped to the devices memory page, which is
located outside of the main addressable memory.
[uxn]
All programs begin by executing the reset vector located at 0x100.
The content of the stacks are preserved between vectors, but it is
discouraged to use the stacks to pass data between vectors.
@on-reset ( -> )
( set vector )
;on-mouse .Mouse/vector DEO2
BRK
@on-mouse ( -> )
( read state )
.Mouse/state DEI ?&on-touch
BRK
&on-touch ( -> )
( A mouse button is pressed )
BRK
For example, the address stored in the Mouse/vector ports points to a
part of the program to be evaluated when the cursor is moved, or a
button state has changed.
[varvara]
Uxntal Utilities
Here's a list of small self-hosted development tools:
* Drifblim is an assembler that also emits a symbols file.
* Uxnfor is a formatter that standardize the source code, this is
the formatting style used across the Uxntal documentation.
* Uxnlin is a peephole optimizer that reveals potential
optimizations in opcode sequences.
* Uxnbal is a program validator that warns when routines do not
match their definitions.
* Uxndis is a disassembler that prints the opcodes in a rom file.
incoming: left noodle drifblim theme catclock oquonie bifurcan yufo
programming languages concatenative gly format ufx format ulz format
proquints brainfuck uxn uxntal reference uxntal alphabet bicycle
beetbug arvelie about oscean computer 2025 2021
Uxn Powered NoNazis! Devine Lu Linvega * 2008-2025
BY-NC-SA 4.0 CreativeCommons Webring Merveilles
---------------------------------------------------------------------