Uxn assembly notes

Loose notes as I figure out how to use Uxn assembly.

Transferring tile map data to the screen


OpcodeStack beforeStack afterDetails
SWPa bb aSwap
OVRa ba b aOver
ROTa b cb c aRotate. Rotates left. This is also handy when you want to swap a BYTE with a SHORT, e.g. a bc > bc a. Guess ROT ROT would be necessary to switch a SHORT with a BYE e.g. ab c > b c a > c ab
LDZavalLoadZeropage. Loads value from address in the 0x0000 to 0x00FF range?
STZval aSTore Zero page. Stores to addresses in the 0x0000 to 0x00FF range?
LDRavalLoaD Relative. Relative addresses are at labels defined in the program, not a specific memory address?
STRval aAFTSTore Relative. Can be a position +0x7f -0x80 forward or back from the current position.
LDAa*valLoaD Absolute. This will always work (as opposed to relative and zero page) but takes more space to specify the full address. NB: Actually, it doesn't seem to load values that are in the 0100 area correctly? I had issues using it but they disappeared when I changed calls to LDZ.
STAval a*STore Absolute.
DEIavalDEvice In. Reads from the specified device port.
DEOval aDEvice Out. Writes value to the specified device port.
EQUa bflagEQUal. Tests if a and b are equal, if so adds a 0x01 to the stack, otherwise 0x00.
NEQa bflagNot EQual. is this the same as EQU, but returns opposite flags?
GTHa bflagGreater THan. If a > b, returns 0x01 otherwise 0x00.
LTHa bflagLess THan. Same as GTH, but tests a < b.
JMPaJuMP. Program control moves to the address a.
JCNflag aJump CoNditional. If flag is 0x01, control moves to the address a. Otherwise, continues on to next instruction.
JSRarsJump Stash. Places program counter on return stack then jumps. JMP2r can be used to return to the point JSR was called.
STHarsStash. Places value on the return stack. STH2r pops the top item from the return stack and puts it on the working stack.
ADDa bresultADD. Adds two numbers together. What happens with overflow?
SUBa bresultSUBtract. Subtracts b from a.
MULa bresultMULtiply. Multiplies a and b.
DIVa bresultDIVide. Divides a by b.
ANDa bresultLogical AND.
ORAa bresultLogical OR.
EORa bresultLogical Exclusive OR.
SFTa bresultShiFT. Bitwise shift of a, by b. b is in the format #LR, where L is the number of places to shift left, R is the amount to shift right. e.g. #08 #02 SFT would shift 0x08 2 places right, resulting in 0x02. #08 #20 SFT would shift 2 places left, resulting in 0x20. I assume shifting by #22 would have no effect.

Unsorted tips

Anything in parentheses is a comment, and is ignored by the assembler. There must be white space between the comment parentheses and their content, otherwise the (example woudl be regarded as a non-existent macro.

If you are unexpectedly under/overflowing the stack, check you haven't missed the spaces in a comment somewhere.

You can do #1234 #01 SUB if you're sure that the first byte won't go below zero. Same with ADD.

#0a .Console/char DEO will give a new line. #20 will give a space and #21 an exclamation mark. Useful for debugging.


I reviewed the source to some of the programs that accompany the C Uxn emulator to learn how they work.

	app/neralie : clock with arvelie date

		- use splash screen when FPS calculation is unstable

Anything in parentheses is a comment, and is ignored by the assembler. There must be white space between the comment parentheses and their content.

%h { .DateTime/hour   DEI }
%m { .DateTime/minute DEI }
%s { .DateTime/second DEI }
%8** { #30 SFT2 }

These four lines are defining macros. For example, the macro in the first line causes any occurence of h in the code to be replaced with the code between the curly braces.

The first three macros are for loading the system's date device values into the address preceding the macro identifier?

The fourth macro 8** performs an arithmetic shift... #30 is decimal 48, but shifting 48 places doesn't seem right.

( devices )

|00 @System [ &vector $2 &pad $6 &r $2 &g $2 &b $2 ]
|10 @Console [ &vector $2 &pad $6 &char $1 &byte $1 &short $2 &string $2 ]
|20 @Screen [ &vector $2 &width $2 &height $2 &pad $2 &x $2 &y $2 &addr $2 &color $1 ]
|b0 @DateTime [ &year $2 &month $1 &day $1 &hour $1 &minute $1 &second $1 &dotw $1 &doty $2 &isdst $1 ]

These lines are mapping the system devices to labels. The |xx values are indicating the place in memory where this device lies, then the @Xxx is defining a label for that address. The rest of the line is then creating offsets from the label using 'sub-labels' - these can be addressed like .Screen/x. The $x indicate padding, how much space is between each of the sub-labels.

( variables )


@fps [ ¤t $1 &next $1 &second $1 ]
@number [ &started $1 &count $1 ]
@lines [ &x1 $2 &x2 $2 &y1 $2 &y2 $2 &addr $1 ]
@neralie [ &n0123 $2 &n4 $1 &n5 $1 &n6 $1 &n7 $1 &n8 $1 &n9 $1 &color $1 &x $2 &y $2 &w $2 &h $2 ]
@mul [ &ahi $1 &alo $1 &bhi $1 &blo $1 ]

These are memory addresses for variables, arranged into labels and sub-labels. The $x values indicate the padding before the address corresponding to the next label. So, @fps (and @fps/¤t) are at memory address 0000. ¤t is intended to take up 1 byte, so there is a padding of $1 afterwards, then &next is at address 0x0001, and so on. It looks like these run up to 0x0023 (35 bytes).

( program )


	( theme )  #03fd .System/r DEO2 #0ef3 .System/g DEO2 #0bf2 .System/b DEO2
	( vectors )  ;on-screen .Screen/vector DEO2
	#01 .fps/current STZ

|0100 puts the following code at position 0x0100 in memory. I'm not sure if this was just to leave space for any more variables that started at 0x0000 or there's some other reason for it.

a b DEO2 will load a 2-byte value a into device port b. Specifically, these are colour values used to set the four system colours. The second line is also inputting a value to a device port, in this case it's an address called on-screen which is being stored in the screen device's vector. The vector will be called every screen frame; presumably @on-screen is defined later in the program and contains instructions for updating the screen.

The last line stores #01 into the address at .fps/current. I'm not sure how STZ (Store Zero Page) works differently from Store Absolute here... perhaps the addresses up to 0x00FF are considered the zero page, and can be declared as variables?

	DUP2 .lines/x1 STZ2
	DUP2 .lines/y1 STZ2
	DUP2 .Screen/width DEI2 SWP2 SUB2 #0001 SUB2 .lines/x2 STZ2
	     .Screen/height DEI2 SWP2 SUB2 .lines/y2 STZ2

Store #000c at lines/x1 and lines/y1, then (Screen/width - 12 - 1) and (Screen/height - 12) into x2 and y2. This looks like it's setting up a padding of 12 pixels around the screen.

	#02 .neralie/color STZ
	.lines/x1 LDZ2 .lines/x2 LDZ2
	OVR2 OVR2 .lines/y1 LDZ2 ;h JSR2
	          .lines/y2 LDZ2 ;h JSR2
	.lines/y1 LDZ2 #0001 SUB2 .lines/y2 LDZ2 #0001 ADD2
	OVR2 OVR2 .lines/x1 LDZ2 ;v JSR2
	          .lines/x2 LDZ2 ;v JSR2