XXIIVV

Uxntal Software

Here's a list of small self-hosted development tools:

Boostrapping

Since the assembler is written in the language it is assembling, you have a few choices:

  1. Download a pre-assembled rom.
  2. Assemble your own with the live REPL.
  3. Bootstrap from a hexdump.

If you are unable to assemble your own copy of drifblim, lost its source file, or simply want to make sure that the assembler is unaltered, you will need the hexadecimal data of drifblim.rom. Here are some inspectable tools to help with the bootstrapping process on non-unix systems:

Step 0: xh.rom

If for some reason you do not have access to the unix xxd command, you can convert hexadecimal dump to a rom with the xh program, which reads a console stream of text and output the actual bytes through the console. This step expects a way to manually create a binary file, but not having a convenient way of creating large files manually.

a001 0780 1037 0080 1216 8030 1906 800a
0b20 000c 8027 1906 8010 0b20 0002 0200
c040 5ecf 2000 051d 8018 1700 cf1f
cat drifblim.rom.txt | uxncli xh.rom > drifblim.rom

Step 1: cat.rom

If for some reason you do not have access to the unix cat program, you can read files and output their content through the console with the cat program, which reads a file and outputs the content via the console.

a001 0780 1037 0080 1216 0680 1f0a 2000
1502 6000 1aa0 000f 13a0 0317 1608 2000
04a0 800f 1700 a000 0081 80fb 1331 00a0
0000 80a8 37a0 0001 80aa 37a0 014b 80ac
b780 a316 2000 0302 226c a000 1817 40ff
ef
uxncli cat.rom drifblim.rom.txt | uxncli xh.rom > drifblim.rom

Step 2: drifboot.rom

At this point, you have recovered your own drifblim.rom from a hex dump. The next step is to make a new drifblim.rom from its source code, using the newly assembled rom.

uxncli drifblim.rom drifblim.tal.txt drifboot.rom

Note that in Uxntal, hexadecimal numbers are valid code and so any rom can be recovered from a hex dump with a working assembler.

Step 3: hx.rom

To close the circle, the assembled rom needs to turned back into a hex dump, we'll print its hex dump using a the following utility:

@on-reset ( -> )
	;on-console #10 DEO2 BRK
@on-console ( -> )
	#0417 DEI NEQ [ JMP BRK ]
	#12 DEI DUP #04 SFT emit emit INC
@spacer ( c -> )
	DUP #0f AND ?{ #0a18 DEO BRK }
	DUP #01 AND ?{ #2018 DEO } BRK
@emit ( c -- )
	#0f AND DUP #09 GTH #27 MUL ADD
	LIT "0 ADD #18 DEO JMP2r
uxncli drifblim.rom hx.tal.txt hx.rom
cat drifboot.rom | uxncli hx.rom > drifboot.rom.txt

Step 4: eq.rom

Finally, we should have two identical hex dumps of the assembler, where one was assembled from the textual source. If for some reason you do not have access to the unix diff command, you can compare the two hexadecimal dumps with the eq program, which takes two filepaths and compare their content.

uxncli drifblim.rom eq.tal.txt eq.rom
uxncli eq.rom drifblim.rom.txt drifboot.rom.txt

Alternatively, if your Varvara implementation does not support the File device, use the Drifloon assembler(tal/rom). To validate your own assembler, see the tests, and disassemble the result with uxndis.

A collection of commonly used routines in Uxntal projects.

The following snippets are in the standard format. If you discover faster and smaller helpers, please get in touch with me.

Hexadecimal Numbers

To print an hexadecimal number:

@<phex> ( short* -: )
	SWP /b
	&b ( byte -: )
		DUP #04 SFT /c
	&c ( byte -: )
		#0f AND DUP #09 GTH #27 MUL ADD [ LIT "0 ] ADD #18 DEO
		JMP2r

To convert an hexadecimal string to a value:

@shex ( str* -: val* )
	[ LIT2r 0000 ]
	&w ( str* `acc* -: val* )
	LDAk chex INC #00 EQU ?{
		[ LITr 40 ] SFT2r LDAk chex [ LITr 00 ] STH
		ADD2r INC2 LDAk ?&w }
	POP2 STH2r JMP2r

To convert an hexadecimal character to a nibble:

@chex ( c -: val! )
	( dec ) [ LIT "0 ] SUB DUP #09 GTH ?{ JMP2r }
	( hex ) #27 SUB DUP #0f GTH ?{ JMP2r }
	( err ) POP #ff JMP2r

Decimal Numbers

To print a decimal short to decimal:

@pdec ( short* -- )
	#000a SWP2 [ LITr ff ]
	&>get ( -- )
		SWP2k DIV2k MUL2 SUB2 STH
		POP OVR2 DIV2 ORAk ?&>get
	POP2 POP2
	&>put ( -- )
		STHr INCk ?{ POP JMP2r }
		[ LIT "0 ] ADD #18 DEO !&>put

To print a decimal byte to decimal:

@print-dec ( dec -- )
	DUP #64 DIV print-num/try
	DUP #0a DIV print-num/try
	( >> )

@print-num ( num -- )
	#0a DIVk MUL SUB [ LIT "0 ] ADD #18 DEO
	JMP2r
	&try ( num -- )
		DUP ?print-num
		POP JMP2r

To convert a decimal string to a hexadecimal value.

@sdec ( str* -- val* )
	[ LIT2r 0000 ]
	&w ( -- )
		( validate ) LDAk [ LIT "0 ] SUB #09 GTH ?&end
		( accumulate ) [ LIT2r 000a ] MUL2r
		( combine ) LDAk [ LIT "0 ] SUB [ LITr 00 ] STH ADD2r
		( continue ) INC2 LDAk ?&w
	&end POP2 STH2r JMP2r

Strings

To print a string.

@<pstr> ( str* -: )
	LDAk #18 DEO
	INC2 & LDAk ?<pstr>
	POP2 JMP2r

Helpers for strings:

[TODO]

Memory

To print an entire page of memory:

@pmem ( addr* -- )
	#0000
	&l ( -- )
		ADD2k LDA phex/b
		DUP #0f AND #0f NEQ #16 MUL #0a ADD #18 DEO
		INC NEQk ?&l
	POP2 POP2 JMP2r

Helpers for memory.

[TODO]

Helpers for bitwise operations.

@popcount ( byte -- count ) LITr 00 #00 &w SFTk #01 AND STH ADDr INC SFTk ?&w POP2 STHr JMP2r
@popcnt ( v* -- num ) LITr 00 &>w #01 ANDk STH ADDr SFT2 ORAk ?&>w POP2 STHr JMP2r

Dates

To find the day of the week from a given date, Tomohiko Sakamoto's method:

@dotw ( y* m d -- dotw )
	( y -= m < 3; )
	OVR STH SWP2 #00 STHr #02 LTH SUB2
	STH2
	( t[m-1] + d )
	#00 ROT ;&t ADD2 LDA #00 SWP
	ROT #00 SWP ADD2
	( y + y/4 - y/100 + y/400 )
	STH2kr
	STH2kr #02 SFT2 ADD2
	STH2kr #0064 DIV2 SUB2
	STH2r #0190 DIV2 ADD2
	ADD2
	( % 7 )
	#0007 DIV2k MUL2 SUB2 NIP
	JMP2r
		&t [ 00 03 02 05 00 03 05 01 04 06 02 04 ]

To find if a year is a leap year:

@is-leap-year ( year* -- bool )
	( leap year if perfectly divisible by 400 )
	DUP2 #0190 ( MOD2 ) DIV2k MUL2 SUB2 #0000 EQU2 ?&leap
	( not a leap year if divisible by 100 )
	( but not divisible by 400 )
	DUP2 #0064 ( MOD2 ) DIV2k MUL2 SUB2 #0000 EQU2 ?¬-leap
	( leap year if not divisible by 100 )
	( but divisible by 4 )
	DUP2 #0003 AND2 #0000 EQU2 ?&leap
	( all other years are not leap years )
	¬-leap
	POP2 #00
	JMP2r
		&leap POP2 #01 JMP2r

Memory

@msfl ( b* a* len* -- )
	STH2
	SWP2 EQU2k ?&end
	&l ( -- )
		DUP2k STH2kr ADD2 LDA ROT ROT STA
		INC2 GTH2k ?&l
	POP2 POP2 &end POP2r JMP2r

@msfr ( b* a* len* -- )
	STH2
	EQU2k ?&end
	&l ( -- )
		DUP2 LDAk ROT ROT STH2kr ADD2 STA
		#0001 SUB2 LTH2k ?&l
	POP2 POP2 &end POP2r JMP2r

Random

@prng-init ( -- )
	[ LIT2 00 -DateTime/second ] DEI
		[ LIT2 00 -DateTime/minute ] DEI #60 SFT2 EOR2
		[ LIT2 00 -DateTime/hour ] DEI #c0 SFT2 EOR2 ,prng/x STR2
	[ LIT2 00 -DateTime/hour ] DEI #04 SFT2
		[ LIT2 00 -DateTime/day ] DEI #10 SFT2 EOR2
		[ LIT2 00 -DateTime/month ] DEI #60 SFT2 EOR2
		.DateTime/year DEI2 #a0 SFT2 EOR2 ,prng/y STR2
	JMP2r

@prng ( -- number* )
	[ LIT2 &x $2 ]
		DUP2 #50 SFT2 EOR2
		DUP2 #03 SFT2 EOR2
	[ LIT2 &y $2 ] DUP2 ,&x STR2
		DUP2 #01 SFT2 EOR2 EOR2
		,&y STR2k POP
	JMP2r

Misc

To convert a signed byte to a signed short.

DUP #7f GTH #ff MUL SWP
@smax ( x* y* -> smax* ) EOR2k POP #80 AND ?min !max
@min ( x* y* -> min* ) LTH2k JMP SWP2 POP2 JMP2r
@max ( x* y* -> max* ) LTH2k JMP SWP2 NIP2 JMP2r
@mod ( x y -- z ) DIVk MUL SUB JMP2r
@mod2 ( x* y* -- z* ) DIV2k MUL2 SUB2 JMP2r

( Signed macros )

@abs ( a -- b ) DUP #80 LTH ?{ #00 SWP SUB } JMP2r
@abs2 ( a* -- b* ) DUP2k #1f SFT2 MUL2 SUB2 JMP2r
@lts2 ( a* b* -- f ) #8000 STH2k ADD2 SWP2 STH2r ADD2 GTH2 JMP2r
@gts2 ( a* b* -- f ) #8000 STH2k ADD2 SWP2 STH2r ADD2 LTH2 JMP2r

( Binary macros )

@rol ( x y -- z ) DUP #07 SFT SWP #10 SFT ADD JMP2r
@ror ( x y -- z ) DUP #70 SFT SWP #01 SFT ADD JMP2r
@rol2 ( x* y* -- z* ) DUP2 #0f SFT2 SWP2 #10 SFT2 ADD2 JMP2r
@ror2 ( x* y* -- z* ) DUP2 #f0 SFT2 SWP2 #01 SFT2 ADD2 JMP2r

Uxnfor is a formatter for Uxntal.

The formatter expects the standard Uxntal Notation, sometimes called Drifblim-style.

If a routine is printing, drawing, or sometimes ingesting all of its arguments, the routine can be wrapped within angular brackets to indicate that it should end a line to Uxnfor.

@<emit-num> ( num* -- )
	LIT "0 ADD .Console/write DEO JMP2r

Uxnbal is a stack effect validator.

Program validation is done at compile-time by comparing a routine's stack effect, against the resulting balance of all stack changes occurring in the routine's code. Words that do not pass the stack-checker are generating a warning, and so essentially this defines a very basic and permissive type system that nevertheless catches some invalid programs and enables compiler optimizations. For more details, see Uxnbal.

The simplest case is when a piece of code does not have any branches or recursion, and merely pushes literals and calls words. The stack effect routines is always known statically from the declaration.

@add-eight ( a -- a+8 )
	#0008 ADD JMP2r
Working-stack imbalance of +1, in add-eight.

In the case of branching, each branch is evaluated and if an imbalance occurs inside one of the branches, the branch name is indicated:

@branching ( a* -- c )
	LDAk #01 EQU ?&one
	LDAk #02 EQU ?&two
	POP2 #ff JMP2r
	&one ( a* -- c ) POP2 #12 JMP2r
	&two ( a* -- c d ) ADD JMP2r
Working-stack imbalance of -1, in branching/two.

In the case of a recursion, the validator will use the stack effect instead of repeatedly walking through the body of the routine.

@print-string ( str* -- )
	LDAk DUP ?{ POP POP2 JMP2r }
	emit-letter 
	INC2 !print-string

For loops that exits without affecting the stack depth, a > prefixed label is used as a shorthand to reduce the need for extraneous stack effect definitions in cases where it can be inferred:

@many-times ( a -- )
	DUP
	&>l
		INC DUP ?&>l
	POP2 JMP2r

Routines that pull items from the stack beyond their allowed depth will also raise a warning, making the stack effect act a sort of boundary:

@shallow ( a -- )
	POP2 JMP2r
Working-stack depth error of 1, in shallow.

Lastly, a runtime specific solution to validate the stack state at any one point during the execution of a program, is to read the System/wst port and compare it against a given stack pointer byte value.

@on-reset ( -> )
	#abcd DUP2 
	.System/wst DEI #05 EQU ?{
		#01 .System/debug DEO }
	BRK

Uxnlin is an optimizer for Uxntal.

It formats stuff so I don't have too.

Bicycle is an interactive Uxntal playground.

Bicycle is a little Uxntal interpreter designed for teach the language in front of an audience. Bicycle allows you to vizualize source code next to its equivalent bytecode and step through the evaluation.

As Companion

To launch Bicycle as a companion application, you need just pipe the left.rom instance of uxnemu to the one of bicycle.rom. Left can send both a selection of text to be evaluated, or the entire working tal file, by pressing ctrl+p. The result should be instantly displayed in the Bicycle window.

uxnemu left.rom | uxnemu bicycle.rom
Drawing by Rekka Bellum

As an alternative, to experiment with Uxntal you can also use the web-based REPL.

Beetbug is a step debugger.

Inspired from Uxn32's fantastic debugger, Beetbug is a step debugger for Varvara, written in Uxntal. It is the perfect tool to step through a running program. It uses Varvara's Console write port to print debug to the interface, and the System's debug port to put a breakpoint.

Beetbug supports symbol files, and will highlight both the return address on top of the return stack, and the address on top of the working stack, giving you a visual indication of the addresses that are being accessed.

Manual

Launch beetbug with the target rom to debug:

uxnemu beetbug.rom some_project.rom

Beetbug will evaluate the reset vector, starting at 0100, until a BRK is reached, to pause evaluation, you can use the System's debug port as follow:

#010e DEO

Controls

incoming: uxntal devlog