Varvara is a hardware abstraction layer for Uxn.

Varvara is a specification for devices communicating with the Uxn machine intended to run little audio and visual programs..

Varvara
00system80controller
10console90mouse
20screena0file
30audiob0
40c0datetime
50d0Reserved
60e0
70f0

The two reserved devices can be used for implementation specific features that do not need to be part of the specs, or other Uxn/Varvara instances.

System Device

system 00Unused*08red*
0109
02expansion*0agreen*
030b
04wst0cblue*
05rst0d
06metadata*0edebug
070fstate
|00 @System/vector $2 &expansion $2 &wst $1 &rst $1 &metadata $2 &r $2 &g $2 &b $2 &debug $1 &state $1

The System/expansion* port expects an address to an operation, it allows basic memory management for roms that must keep information outside of the addressable range, or cache the content of a file in order to seek through the data.

nameoperationfields
fill00length*bank*addr*value
Fill a length of memory with a value, starting at address.
cpyl01length*src bank*src addr*dst bank*dst addr*
Copy memory starting by the first byte.
cpyr02length*src bank*src addr*dst bank*dst addr*
Copy memory starting by the last byte.
@on-reset ( -> )
	;cmd .System/expansion DEO2
	;dst print-str
	BRK

@cmd [ 01 000b 0000 =src 0000 =dst ]
@src "Hello 20 "World $1
@dst $c

Reading, and writing, to the System/wst and System/rst ports will get, or set, a byte value as the working and return stack indexes. The System/metadata* port notifies the emulator that metadata about the rom is present at the address specified. The emulator can choose to utilize this information or to ignore it.

@on-reset ( -> )
	;meta .System/metadata DEO2
	BRK

@meta 00
	"Nasu 0a
	"A 20 "Sprite 20 "Editor 0a
	"By 20 "Hundred 20 "Rabbits 0a
	"Jan 20 "8, 20 "2023 $2
color0color1color2color3
#fff #000 #7db #f62

This device is holding 3 shorts to be used for application customization, for simplicity we call them the System/red*, System/green* and System/blue* shorts. These colors are typically used by the screen device to form four application colors.

@on-reset ( -> )
	set-theme
	BRK

@set-theme ( -- )
	#f07f .System/r DEO2
	#f0d6 .System/g DEO2
	#f0b2 .System/b DEO2
	JMP2r

Sending a non-null byte to the System/debug port will print the content of the stacks or pause the evaluation if the emulator includes a step-debugger. The recommended output for the #01 debug value, with a working stack containing 3 items, is as follow:

WST 00 00 00 00 00|12 34 56 < 
RST 00 00 00 00 00 00 00 00|<

If at the end of the current vector, the value in System/state is not zero, the application will stop listening to new events and terminate. On systems that can handle exit codes, the error code is the 0x7f portion of the byte. So, 0x01 terminates the program with an error, and 0x80 terminates the program succesfully.

@on-reset ( -> )
	( exit code 0, success )
	#80 .System/state DEO
	BRK

Console Device

console 10vector*18write
1119error
12read1a--
13--1b--
14--1caddr*
15--1d
16--1emode
17type1fexec
|10 @Console/vector $2 &read $5 &type $1 &write $1 &error $1

The Console/vector* is evaluated when a byte is received. The Console/type port holds one of 5 known types: no-queue(0), stdin(1), argument(2), argument-spacer(3), argument-end(4). During the reset vector, a program should be able to query the type port and get a null byte when there is no arguments to be expected, a 1 when arguments are present.

uxncli file.rom arg1 arg2
                ^   ^^   ^
                2   32   4

The Console/read port is used to listen to incoming bytes during a Console vector event.

@on-reset ( -> )
	;on-console .Console/vector DEO2
	BRK

@on-console ( -> )
	.Console/read DEI .Console/write DEO
	#20 .Console/write DEO
	BRK

The Console/write port is used to send data through the console. For example, a program sending the text "Hello", will trigger the console's port 5 times; once for each character.

@on-reset ( -> )
	;text
	&while ( -- )
		LDAk .Console/write DEO
		INC2 LDAk ?&while
	POP2
	BRK

@text "Hello $1

Screen Device

screen 20vector*28x*
2129
22width*2ay*
232b
24height*2caddr*
252d
26auto2epixel
27--2fsprite
|20 @Screen/vector $2 &width $2 &height $2 &auto $2 &x $2 &y $2 &addr $2 &pixel $1 &sprite $1

The Screen/vector* is evaluated 60 times per second. The screen device is capable of displaying graphics in any four colors, which are defined by the system device. The screen is made of two selfstanding layers, the foreground layer treats color0 as transparent.

@on-reset ( -> )
	;on-screen .Screen/vector DEO2
	BRK

@on-screen ( -> )
	[ LIT &frame $1 ] INCk ,&frame STR
	#01 AND ?&>skip
		( 30 times per second )
		&>skip
	BRK

The Screen/width* and Screen/height* ports can be set to resize the screen for systems that supports it, but reading from these ports should ALWAYS return the actual sizes, as programs make use of the screen sizes to position responsive graphics on the screen.

@on-reset ( -> )
	set-theme
	.Screen/width DEI2 #01 SFT2 .Screen/x DEO2
	.Screen/height DEI2 #01 SFT2 .Screen/y DEO2
	#01 .Screen/pixel DEO ( paint a black pixel, on background )
	#43 .Screen/pixel DEO ( paint a red pixel, on foreground )
	#93 .Screen/pixel DEO ( fill a red rectangle bottom-left, on background )
	#e2 .Screen/pixel DEO ( fill a cyan rectangle top-right, on foreground )
	BRK

The Screen/pixel port defines the pixel or fill mode, layer to draw on, optional horizontal/vertical flipping of the quadrant to fill, and which of the four colors to use. When the fill bit is active, the operation will fill a portion of the screen starting at the x,y position until the edges of the screen. The default quadrant is bottom-right, flipping the x bit will fill the buttom-left quadrant, and so on.

           M L Y X 3 2 1 0
Fill   ----+ | | | | | | +---- Color bit 0
Layer  ------+ | | | | +------ Color bit 1
Flip Y --------+ | | +-------- Unused
Flip X ----------+ +---------- Unused

The Screen/sprite port defines the 1-bit or 2-bit mode, layer to draw on, optional horizontal and vertical flipping of the sprite, and the colors to use.

           M L Y X 3 2 1 0
2bpp   ----+ | | | | | | +---- Color bit 0
Layer  ------+ | | | | +------ Color bit 1
Flip Y --------+ | | +-------- Color bit 2
Flip X ----------+ +---------- Color bit 3

The 8x8 sprite data to draw is specified by writing its location in memory to the Screen/addr* port.

@on-reset ( -> )
	set-theme
	#0020 .Screen/x DEO2
	#0040 .Screen/y DEO2
	;sprite-icn .Screen/addr DEO2
	#01 .Screen/sprite DEO
	BRK

@sprite-icn
	00 66 ff ff ff 7e 3c 18

(
. . . . . . . . 00
. # # . . # # . 66
# # # # # # # # ff
# # # # # # # # ff
# # # # # # # # ff
. # # # # # # . 7e
. . # # # # . . 3c
. . . # # . . . 18
)
0 4 8 c
1 5 9 d
2 6 a e
3 7 b f

The color nibble defines which color is drawn for each pixel of a sprite. The following table presents all possible combinations, assuming a sprite has a background of value 0 and three concentric circles of values 1, 2, and 3 (counting from the outside). For 1-bit sprites, only values 0 and 1 are applicable.

c = !ch ? (color % 5 ? color >> 2 : 0) : color % 4 + ch == 1 ? 0 : (ch - 2 + (color & 3)) % 3 + 1;

blending[4][16] = {
	{0, 0, 0, 0, 1, 0, 1, 1, 2, 2, 0, 2, 3, 3, 3, 0},
	{0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 3},
	{1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1, 1, 2, 3, 1},
	{2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2, 2, 3, 1, 2}};

The Screen/auto port automates the incrementation of the position and sprite address whenever a drawing command is sent, so the program does not need to manually move to the next sprite, or the next position.

                 3 2 1 0 * A Y X
Length bit 3 ----+ | | |   | | +---- Auto X
Length bit 2 ------+ | |   | +------ Auto Y
Length bit 1 --------+ |   +-------- Auto Addr
Length bit 0 ----------+

The length nibble controls the number of extra sprites that will be drawn in addition to the first, each time the sprite port is written to. When set to 0, only a single sprite will be drawn. As many as 16 sprites can be drawn at once with a value of 15.

The auto-x and auto-y bits control the increment of the x and y ports respectively, as well as the layout of the extra sprites drawn when the length nibble is not null. The extra sprites are drawn as columns moving rightward for auto-x, and rows moving downward for auto-y. If the flip bits of the sprite byte are set, the directions are reversed. If the auto-addr bit is set, the address port will be incremented for each sprite drawn by increments of 8 for 1-bit, and by increments of 16 for 2-bit.

@paint-sprite ( x* y* -- )
	.Screen/y DEO2 ( set y position )
	.Screen/x DEO2 ( set x position )
	#16 .Screen/auto DEO ( set length 2 with auto y and addr )
	;23x-icns .Screen/addr DEO2 ( set addr )
	#01 .Screen/sprite DEOk DEOk DEO ( draw 3 rows of 1-bit sprites )
	JMP2r

@23x-icns
	( 0 ) ( 1 )
	( 0 ) 0010 2847 2810 0001 0000 00e0 2040 8000
	( 1 ) 0204 080f 0000 0001 0000 00e0 2040 8000
	( 2 ) 0204 080f 0000 0000 0010 28c4 2810 0000

Audio Device

audio 30vector*38adsr*
3139
32position*3alength*
333b
34output3caddr*
35--3d
36--3evolume
37--3fpitch
|30 @Audio0/vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1
|40 @Audio1/vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1
|50 @Audio2/vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1
|60 @Audio3/vector $2 &position $2 &output $1 &pad $3 &adsr $2 &length $2 &addr $2 &volume $1 &pitch $1

The Audio/vector* is evaluated when a note ends. All samples used by the audio devices are unsigned 8-bit mono.

When a byte is written to the Audio/pitch port, any sample that is currently playing will be replaced with the sample defined by all the values set in the device, it starts playing an audio sample from memory located at Audio/addr* with length of Audio/length*. It loops the sample until it reaches the end of the envelope defined by Audio/adsr*. Several fields contain more than one component:

ADSR*PitchVolume
SubfieldAttackDecaySustainReleaseLoopNoteLeftRight
Size (bits)44441744

Each of the ADSR components is measured in 15ths of a second, so writing #ffff to Audio/adsr* will play a note that lasts for exactly four seconds, with each section of the envelope lasting one second. If Audio/adsr* is #0000 then no envelope will be applied: this is most useful for longer samples that are set to play once by setting the most significant bit of Audio/pitch to 1.

The envelope varies the amplitude as follows: starting at 0%, rising to 100% over the Attack section, falling to 50% over the Decay section, remaining at 50% throughout the Sustain section and finally falling to 0% over the Release section. The envelope is linearly interpolated throughout each section.

@on-reset ( -> )
	#0248 .Audio/adsr DEO2
	#88 .Audio/volume DEO
	;note-pcm .Audio/addr DEO2
	#0010 .Audio/length DEO2
	#30 .Audio/pitch DEO
	BRK

@square-pcm
	ff ff ff ff ff ff ff ff 00 00 00 00 00 00 00 00

The two Audio/volume components balance how loudly the next sample will play in each ear. #ff sets maximum volume for both speakers. While the sample is playing, the Audio/output byte can be read to find the loudness of the envelope at that moment.

Controller Device

controller 80vector*88--
8189--
82button8a--
83key8b--
84player8c--
85--8d--
86--8e--
87--8f--
|80 @Controller/vector $2 &button $1 &key $1

The Controller/vector* is evaluated when a button, is pressed or released, and when a key is pressed. The key port is set to zero after the vector is called.

0x01A Ctrl0x10Up
0x02B Alt0x20Down
0x04Select Shift0x40Left
0x08Start Home0x80Right

The Controller/button port works similarly to an NES controller, where there the state of each one of the 8 buttons is stored as a bit in a single byte.

@on-reset ( -> )
	;on-controller .Controller/vector DEO2
	BRK

@on-controller ( -> )
	.Controller/button DEI
	DUP #20 AND ?on-dpad-down
	DUP #80 AND ?on-dpad-right
	POP BRK

@on-dpad-down ( button -> )
@on-dpad-right ( button -> )

The Controller/key port holds the character data that is pressed during the vector event.

@on-controller ( -> )
	.Controller/key DEI DUP ?on-key POP
	BRK

@on-key ( key -> )

Would the need for multi-player games arise, the Controller/player ports can hold the id of the player sending the controller event.

Mouse Device

mouse 90vector*98--
9199--
92x*9ascrollx*
939b
94y*9cscrolly*
959d
96state9e--
97--9f--
|90 @Mouse/vector $2 &x $2 &y $2 &state $4 &scrollx $2 &scrolly

The Mouse/vector* is evaluated when the mouse is moved and when a button is pressed or released.

@on-reset ( -> )
	;on-mouse .Mouse/vector DEO2
	BRK

@on-mouse ( -> )
	.Mouse/state DEI ?&down
	BRK
	&down ( -> )
		.Mouse/x DEI2 print-dec
		LIT ", .Console/write DEO
		.Mouse/y DEI2 print-dec
		#0a .Console/write DEO
		BRK

The Mouse/state port holds a byte in which each bit is a button state. The byte value of holding down the mouse1 button is 01, and holding down mouse1+mouse3 button is 05. The Mouse/scroll-x* and Mouse/scroll-y* ports hold signed shorts, either ffff and 0001, for -1 and +1, after firing the vector on a scrolling event, the ports are both immediately set to 0.

File Device

file a0vector*a8name*
a1a9
a2success*aalength*
a3ab
a4stat*acread*
a5ad
a6deleteaewrite*
a7appendaf
|a0 @File1/vector $2 &success $2 &stat $2 &delete $1 &append $1 &name $2 &length $2 &read $2 &write $2
|b0 @File2/vector $2 &success $2 &stat $2 &delete $1 &append $1 &name $2 &length $2 &read $2 &write $2

The File/vector* is normally unused, but is reserved for systems where a portable data format(disk, etc..) can be inserted. There is no specs for disk handling at this time.

When File/name* resolves to a file, writing an address to File/read* will write the file's data at that address up to the length in File/length*, the length of data read is then put in File/success*. If the file is longer, subsequent writes to File/read* will read the next chunk of data into the memory region, so it is possible to read the contents of very large files one chunk at a time.

@on-reset ( -> )
	;filename .File/name DEO2
	#0010 .File/length DEO2
	;buffer .File/read DEO2
	BRK

@filename "in.txt $1
@buffer $10

When File/name* resolves to a directory, writing the address to File/read* will read the directory content in the following format: Each file or directory is on its own line, prefixed with a four characters for the file details, followed by a space, the file's name and a linebreak.

001a file.txt
???? large file.mp4
---- directory/

The file details part of the directory listing line can also be obtained via the File/stat* port, the File/length* specifies the length of the buffer to write to. A file size is padded with zeroes and will always include the lowest nibbles. The length of the details will always fill the requested length whether it is with zeros for a file size, or with dashes for the directory.

@is-folder ( name* -- bool )
	.File/name DEO2
	#0001 .File/length DEO2
	;&b .File/stat DEO2
	[ LIT2 &b $1 "- ] EQU JMP2r

Writing files is performed by writing to File/write* will write the data in memory at that address up to the length in File/length*, the length of data writte is then put in File/success*. If File/append is set to 0x01, then the data in the memory region will be written after the end of the file, if it is 0x00 it will replace the contents of the file.

If the file doesn't previously exist then it will be created. File/success* will be set to the length written. As with reading files and directories, subsequent writes to File/write* will write more chunks of data to the file.

@on-reset ( -> )
	;filename .File/name DEO2
	#0005 .File/length DEO2
	;data .File/write DEO2
	BRK

@filename "out.txt $1
@data "hello $1

Finally, to delete a file, write any value to the File/delete byte, if successful, File/success* will be set to 0x0001, otherwise 0x0000. Writing to File/name* closes the previously opened file or directory. The device may not access files outside of the working directory. When reading or writing, if the length of data required exceeds the working memory(0xffff), the length is set to the maximum available.

Datetime Device

datetime c0year*c8doty
c1c9
c2monthcaisdst
c3daycb--
c4hourcc--
c5minutecd--
c6secondce--
c7dotwcf--
|c0 @DateTime &year $2 &month $1 &day $1 &hour $1 &minute $1 &second $1 &dotw $1 &doty $2 &isdst $1

All the ports are zero-based, the dotw port begins on sunday[0].

@print-date-num ( -- )
	.DateTime/year DEI2k print-dec
	[ LIT "- ] .Console/write DEO
	INC INC DEIk INC print-dec-pad
	[ LIT "- ] .Console/write DEO
	INC DEI !print-dec-pad

A collection of Varvara application support standards.

This is a collection of various libraries used in the Varvara roms.

Snarf files are the clipboard standard for Varvara software.

A .snarf file contains a clipboard buffer, programs will handle clipboard operations by writing to that external invisible file. To quickly write to the snarf file before pasting:

cat > .snarf

The cut operation deletes the selected text from the screen and puts it in the snarf buffer, snarf copies the selected text to the buffer without deleting it, paste replaces the selected text with the contents of the buffer.

|0100 @snarf/length

@snarf/copy ( data* length* -- )
	;&path .File/name DEO2
	.File/length DEO2
	.File/write DEO2
	JMP2r

@snarf/paste ( addr* -- length* )
	;&path .File/name DEO2
	;&length .File/length DEO2
	;&buf .File/read DEO2
	.File/success DEI2
	JMP2r

&buf $&length
Snarf is the term for Copy on the Plan9 operating system. The operation is not to copy but to snarf. It's called snarf because snarf is what it does. There is no design document.

Symbol files are debug information about an assembled rom.

A symbols(*.tal.sym) file is a binary format that contains a series of symbols in the format: an absolute addresses, where the first byte is the most significan byte, the second is the least significant byte, followed by a null-terminated string. They are generated by Uxntal assemblers, such as Drif, and are used by debugging tools, such as uxndis and Beetbug, which do not otherwise have access to a program's labels.

0140 on-draw
0142 on-draw/loop
01a8 clip-tile
01f2 ...

Metadata exposes additional information about a Varvara rom.

A program's metadata can be found by forming a 16-bit address from the second and third bytes of a compatible rom file. A compatible rom must begin with a0, a two bytes address, and the subsequent 3 bytes should always be 80 06 37.

First six bytes
LIT2addressLITportDEO2...
a0hblb800637...

The address corresponds to the absolute location of the metadata, once loaded in memory, so it has a 0x100 bytes offset(the zero-page) which needs to be accounted for when accessing the metadata externally.

The body of the metadata can be anything, but it is recommended that it be plain-text with 0a as line separator, and that the first line be the name of the application. Here are some suggestions for lines to include in the body of the metadata:

The metadata is stored in the rom itself to allow the program to make use of this information internally. The entire size of the metadata should be at most 256 bytes.

Implementation

The metadata format begins with a byte for the varvara version, followed by a null-terminating text:

|0100 ( -> )

	;meta #06 DEO2

BRK

@meta 00 &body
	( name ) "Nasu 0a
	( details ) "A 20 "Sprite 20 "Editor 0a
	( author ) "By 20 "Hundred 20 "Rabbits 0a
	( date ) "Jan 20 "8, 20 "2023 00
	02
		( icon ) 83 =appicon
		( mask ) 41 1705

If supported by the emulator, writing the metadata's address to the system device, via #06 DEO2, informs the emulator of the metadata's location, and it may choose to handle this information when a ROM is started.

Extended

The length of the metadata can be extended and host various other informations. The format for the fields is: a single byte id field, followed by a 2 bytes value field.

Applicationidvalue
--41Expected device mask.
Potato8324x24 icon in the chr format.
8864x64 icon in the icn format.
a0The application manifest.

Reading Metadata

A program can check a rom for metadata with the following routine:

@has-meta ( filename* -- bool )

	.File/name DEO2
	#0003 .File/length DEO2
	;&b .File/read DEO2k DEO2
	,&litport LDR2 #8006 EQU2
	,&deo2 LDR #37 EQU AND

JMP2r
	&b &litport $2 &deo2 $1

Manifest exposes a rom's features.

A manifest is a simple data structure that allows Varvara applications to specify what feature can be reached by shortcuts.

The manifest.tal is a support library created to this end. It is also used in the definition of applications in Potato.

@manifest
	04 "Application $1
		01 'n :file-new "New $1
		01 'r :file-rename "Rename $1
		01 'o :file-open "Open $1
		01 's :file-save "Save $1
	03 "Edit $1
		01 'c :edit-copy "Copy $1
		01 'v :edit-paste "Paste $1
		01 'x :edit-cut "Cut $1
	02 "Select $1
		00 1b :select-reset "Reset $1
		01 'a :select-all "All $1
	$1

Indental is a plaintext dictionary-type database format designed to store elements accessible by name.

In the Indental file, an unindented line declares the key to a new root node, indented lines associate parameters or lists to their root node, a key-value pair is declared with a colon, and a list is declared by a sequence of equally indented lines.

NAME
	KEY : VALUE
	LIST
		ITEM1
		ITEM2

Or, {NAME:{KEY:VALUE,LIST:[ITEM1,ITEM2])}

This database format is used to store the plain-text content of the Oscean wiki.