Working With Assembly Language

RETRO runs on a virtual machine called Nga. It provides a standard assembler for this called Muri.

Muri is a simple, multipass model that's not fancy, but suffices for RETRO's needs.

Assembling A Standalone File

A small example (test.muri)

~~~ i liju.... r main : c:put i liiire.. i 0 : main i lilica.. d 97 i liju.... r main ~~~

Assembling it:

retro-muri test.muri

So breaking down: Muri extracts the assembly code blocks to assemble, then proceeds to do the assembly. Each source line starts with a directive, followed by a space, and then ending with a value.

The directives are:

:    value is a label i    value is an instruction bundle d    value is a numeric value r    value is a reference s    value is a string to inline

Instructions for Nga are provided as bundles. Each memory location can store up to four instructions. And each instruction gets a two character identifier.

From the list of instructions:

0 nop   5 push  10 ret   15 fetch 20 div   25 zret 1 lit   6 pop   11 eq    16 store 21 and   26 halt 2 dup   7 jump  12 neq   17 add   22 or    27 ienum 3 drop  8 call  13 lt    18 sub   23 xor   28 iquery 4 swap  9 ccall 14 gt    19 mul   24 shift 29 iinvoke

This reduces to:

0 ..    5 pu    10 re    15 fe    20 di    25 zr 1 li    6 po    11 eq    16 st    21 an    26 ha 2 du    7 ju    12 ne    17 ad    22 or    27 ie 3 dr    8 ca    13 lt    18 su    23 xo    28 iq 4 sw    9 cc    14 gt    19 mu    24 sh    29 ii

Most are just the first two letters of the instruction name. I use .. instead of no for NOP, and the first letter of each I/O instruction name. So a bundle may look like:

dumure..

(This would correspond to dup multiply return nop).

Runtime Assembler

RETRO also has a runtime variation of Muri that can be used when you need to generate more optimal code. So one can write:

:n:square dup * ;

Or:

:n:square \dumure.. ;

The second one will be faster, as the entire definition is one bundle, which reduces memory reads and decoding by 2/3.

Doing this is less readable, so I only recommend doing so after you have finalized working RETRO level code and determined the best places to optimize.

The runtime assembler has the following directives:

i    value is an instruction bundle d    value is a numeric value r    value is a reference

Additionally, in the runtime assembler, these are reversed:

'dudumu.. i

Instead of:

i dudumu..

The runtime assembler also provides three prefixes for use in inlining machine code into a definition. These are:

 \    Treat token as an assembly sequence  `    Treat token as a numeric value  ^    Treat token as a reference

E.g., instead of doing something like:

:n:square as{ 'dumu.... i }as ; :test as{ 'lilica.... i #22 d 'n:square r }as ;

Just write:

:n:square \dumu.... ; :test     \lilica.. `22 ^n:square ;