XXIIVV

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 @text/sz
|2000 @text/buf $&sz

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
The notion of an interface is what truly characterizes objects - not classes, not inheritance, not mutable state. Gilad Bracha

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.

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 Doors

The ability to treat instructions as data makes programs that write programs possible. Self-modifying code(SMC) is generally considered harmful, and is therefore not permitted in most modern computer architectures today.

Action at a distance is an anti-pattern in computer science in which behavior in one part of a program modifies operations in another part of the program. This anti-pattern should be avoided whenever possible, but if wielded carefully SMC can become a practical ally when writing Uxntal.

A door is an allocation of local memory that can store state across vectors.

@routine ( -- i )
	[ LIT &door $1 ] INCk ,&door STR
	JMP2r

Caching Doors

In most cases, SMC is used to cache data that would otherwise be difficult or slow to retrieve, like when writing a responsive application that would make frequent requests to a device.

In the following door, we are comparing the state of the mouse device between vector events, we could store the previous state in a zero-page variable, but keeping the value locally allows to reserve a byte from within the context where it is needed, and is faster by being inlined.

@on-mouse ( -> )
	[ LIT2 &last $1 -Mouse/state ] DEI 
		DUP ,&last STR
		EORk ?&changed 
	POP2
	BRK

Callback Doors

To chain operations across vectors, one might try passing the next operation pointer on the stack, but since we cannot be certain which vector will happen next, we can't expect a specific stack state between events. A safer way is to write the next operation directly into a door where it will be needed, ideally preserving the label scope.

@set-animation ( callback* -- )
	,&callback STR2
	;&run .Screen/vector DEO2
	JMP2r

&run ( -> )
	[ LIT &time f0 ] 
		INCk ,&time STR
		#00 EQU ?&done
	try-redraw
	BRK

&done ( -> )
	[ LIT2 &callback $2 ] JSR2
	BRK

Depth-Punching Doors

Routines should try and avoid accessing stack values that are further than 2 or 3 shorts deep on either stacks, but sometimes it cannot be helped. In the following example, we want to run a function over each value of a 2d array. Instead of juggling the stacks on each iteration to bring out the function pointer, it is often more efficient to write the function pointer across the nested loop.

@each-pixel ( fn* -- )
	,&fn STR2
	#1000
	&h
		STHk
		#2000
		&x
			DUP STHkr [ LIT2 &fn $2 ] JSR2
			INC GTHk ?&x
		POP2
		POPr
		INC GTHk ?&h
	POP2
	JMP2r

incoming: uxntal uxntal strings uxntal macros 2025 2024 2023