Thrift is a 2 instruction "Forth" for Uxn in 24 bytes, inspired by
"THRee Instruction ForTh",
the olny two instrucitons are quote and unquote.
"To quote, or to unquote, that is the question." -- Not William Shakespeare.
@on-reset ( -> )
|0100 40 00 03 ( !/reset )
"e
|0103 80 12 16 ( .Console/read DEI )
&reset
|0106 a0 01 0d ( ;on-console )
|0109 80 10 37 ( .Console/vector-hi DEO2 )
|010c 00 ( BRK )
@on-console ( -> )
|010d 80 12 16 ( .Console/read DEI )
|0110 8c ( JMPk )
&unquote
|0111 13 00 00 ( STR $1 BRK )
"e
|0114 80 11 17 ( .Console/vector-lo DEO )
|0117 00 ( BRK )
When assembled the last trailing null byte is ellided because uxn memory is zeroed out on reset, leaving us with a 23 bytes rom.
4000 0380 1216 a001 0d80 1037 0080 1216
8c13 0000 8011 17
- Quote: pushes a byte to the remote working stack, functionally equivalent to
LIT. - Unquote: evaluates the byte at the top of the remote working stack as an opcode.
Serial communication is emulated via the Console/write device ports, used to
send bytes between the local and remote computers.
All the thrift API is prepended with colon : to indicate that this is either a
macro to create remote commands or a routine that is evaluated in the remote uxn
computer, routines may take arguments from the main or the remote computer,
i.e.:
@:foo ( :a :b -- :c )
@:bar ( a b -- :c)
:foo is a routine that takes it's arugments from the remote working stack,
while :bar takes it's arguments from the local working stack, but leaves a
byte in the remote working stack.
uxncli: Uxn/Varvara emulator.drifblim: Uxntal assembler.socat: Emulates serial connection in Linux.
$ make # build roms.
$ make install # copy roms to ~/roms$ make run # test default example (fizzbuzz)All the Varavara device labels are defined for convenience.
:asm/setup: setups a copy of thrift at:ffe9, resets the assembler to0100and jumps to:ffe9.:asm/set ( addr* -- ): sets:asm/pointer_to an absolute address.:asm/reset ( -- ): resets:asm/pointer_to the default value (0100).:asm/get: gets the current short value of:asm/pointer_.:asm/write: write a byte at:asm/pointer_absolute address and increment:asm/pointer_.
:asm/send-len ( length* start* -- )sends a number of bytes to be evaluated as an expression.:asm/send-blk ( start* end* -- ): sends a block of bytes to be evaluated as an expression.
In order to copy code you have to make sure that absolute addressing is respected as expected by the code, or that it is patched, it is best to use relative opcodes to make the code as rellocatable as possible.
:asm/copy-len ( length* start* -- )copies a number of bytes to be assembled starting at:asm/pointer_absolute address.:asm/copy-blk ( start* end* -- ): copies a block of bytes to be assembled starting at:asm/pointer_absolute address.
The Macros provide a low lewel API used to create commands in the local which are then sent to the remote to be executed as "expressions":
:quot: quotes a byte (treating it as a literal to be pushed to the :WST).:unqt: unquotes a byte (treating it as an opcode to executed immediatly).
@main ( -> )
#0004 ;:cmd :asm/send-len ( :0a :03 )
;:mod ;:mod/end :asm/send-blk ( :01 )
:DBG
BRK
@:cmd [ :quot 0a :quot 03 ]
%:divk { :quot DIVk :unqt } %:mul { :quot MUL :unqt} %:sub { :quot SUB :unqt}
@:mod [ :divk :mul :sub ] &end
Output:
WST 00 00 00 00 00 00 00|01 <01
RST 00 00 00 00 00 00 00 00|<00
Originally there were macros like :divk for each opcode, but the drifblim
assembler does not support the creation of that many macros.
Notice that each byte must be quoted independently.
These routines provide a higer level API for evaluating expressions on the remote:
@main ( -> )
[ :LIT2 0a03 ] :MOD ( :01 )
:DBG
BRK
@:MOD ( :a :b -- :(a%b) ) :DIVk :MUL !:SUB
Output:
WST 00 00 00 00 00 00 00|01 <01
RST 00 00 00 00 00 00 00 00|<00
Remember all remote pseudo opcodes are routines, so you can jump to them at tail
position: !:SUB vs :SUB JMP2r.
Relative opcodes don't make sense when evaluating using the thrift VM, since unquoting is done at the same absolute address each time, which is why only absolute addressing is supported in expressions.
:JCI: use:JCN2instead.:JMI: use:JMP2instead.:JSI: use:JSR2instead.
Only :POPk is implemented, and well, it does nothing as you would expect.
:POPkr: use:POPkinstead.:POP2k: use:POPkinstead.:POP2kr: use:POPkinstead.:NIPk: use:POPkinstead.:NIP2k: use:POPkinstead.:NIPkr: use:POPkinstead.:NIP2kr: use:POPkinstead.
:JMP: use:JMP2instead.:JMPk: use:JMP2kinstead.:JCN: use:JCN2instead.:JCNk: use:JCN2kinstead.:JSR: use:JSR2instead.:JSRk: use:JSR2kinstead.:LDR: use:LDAinstead.:LDRk: use:LDAkinstead.:LDR2r: use:LDA2rinstead.:LDR2kr: use:LDA2krinstead.:STR: use:STAinstead.:STRk: use:STAkinstead.:STR2r: use:STA2rinstead.:STR2kr: use:STA2krinstead.