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

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 

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

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

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

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
			DUP STHkr [ LIT2 &fn $2 ] JSR2
			INC GTHk ?&x
		INC GTHk ?&h

incoming uxntal memory uxntal devices