Muri is my minimalist assembler for Nga. Using it requires some knowledge of the Nga architecture to be useful.
Nga has 30 instructions. These are:
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
The mnemonics allow for each name to be reduced to just two characters. In the same order as above:
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
Up to four instructions can be packed into a single memory location. (You can only use nop after a jump, call, ccall, ret, or zret as these alter the instruction pointer.)
So a bundled sequence like:
lit 100 lit 200 add ret
Would look like:
'liliadre i 100 d 200 d
And:
lit s:eq? call
Would become:
'lica.... i 's:eq? r
Note the use of .. instead of no for the nop's; this is done to improve readability a little.
Instruction bundles are specified as strings, and are converted to actual instructions by the i word. As in the standard Muri assembler, the RETRO version uses d for decimal values and r for references to named functions.
This implements an extended version of i, the instruction assembler. It allows for use of hex constants (in uppercase) in place of (or in addition to) the instruction names. This can be useful if you are running on a VM with an extended instruction set.
When loaded, it will replace the original i with a jump to the one provided here, allowing existing words (like sigil:*) to use this instead.
I'm keeping everything in a private namespace to keep the final dictionary clean.
It begins with an array of instruction names. The index matches the opcode, so these must be in order.
Then I define a quad combinator to simplify the later debundling of the instruction names.
Next, a word to handle hex numbers. A standard Retro system only handles decimal by default, so this just implements a quick hex conversion.
Decoding an instruction is simple. If it's in the Instructions array, return the index. If not, convert to a number using the hex conversion above.
The debundle word breaks a string into four two byte substrings and runs decode against each.
Once debundled and decoded, I can then pack the opcodes into a single cell. This is simple, just some quick shifts and addition.
Nearing completion, I wrap everything up into a single word and then patch the original i to jump to this.
(The 1793 corresponds to the liju.... instruction sequence)
And finally, close off the namespace leaving the dictionary clean of all the words used to implement this.