32d22c51fa
FossilOrigin-Name: 94245d967728aac960b4ce6a834a4e51d84238490d0be4c9e849751a385f6fb6
118 lines
3 KiB
Text
118 lines
3 KiB
Text
# 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 ;
|