L1VM - course 12
L1VM - assembly VOL I
Here I will show how to read the assembly output of my compiler Brackets. A program can be build in the l1vm directory:
./build.sh prog/hello
This creates the bytecode in “prog/hello.l1obj” and in the root directory: “out.l1asm” and “out.l1dbg”. Here is “prog/hello.l1com” in Brackets:
// hello.l1com
// Brackets - Hello world!
//
#include <intr.l1h>
(main func)
(set int64 1 zero 0)
(set int64 1 x 23)
(set int64 1 y 42)
(set int64 1 a 0)
(set string 13 hello "Hello world!")
// print string
print_s (hello)
print_n
((x y *) a =)
print_i (a)
print_n
exit (zero)
(funcend)
And here is “out.l1asm”:
.data
Q, 1, zero
@, 0Q, 0
Q, 1, x
@, 8Q, 23
Q, 1, y
@, 16Q, 42
Q, 1, a
@, 24Q, 0
B, 13, hello
@, 32Q, "Hello world!"
Q, 1, helloaddr
@, 45Q, 32Q
.dend
.code
:main
loada zero, 0, 0
loada helloaddr, 0, 1
intr0 6, 1, 0, 0
intr0 7, 0, 0, 0
loada x, 0, 2
loada y, 0, 3
muli 2, 3, 4
load a, 0, 5
pullqw 4, 5, 0
loada a, 0, 6
intr0 4, 6, 0, 0
intr0 7, 0, 0, 0
intr0 255, 0, 0, 0
rts
.cend
The first line begins the “.data” section with all variable declarations. The second and third line declare the variable “zero”:
Q, 1, zero
This sets “Q” for “int64” type, “1” for one variable and “zero” as the variable name.
@, 0Q, 0
Now here we have the memory address “0Q” and the value “0”.
And on line four and five we have:
Q, 1, x
This sets “Q” for “int64” type, “1” for one variable and “x” as the variable name.
@, 8Q, 23
Here we get the memory address “8Q” and the value “23”. So the memory address is increased by 8 byte to store the variable “x”. The compiler sets the right memory addresses for us, so have no worries about this!
The string declaration is different:
B, 13, hello
Here we have type “B” for byte or string. And “13” chars. The name is “hello”.
@, 32Q, "Hello world!"
The address is “32Q” and the value “Hello world!”. However here are two more lines:
Q, 1, helloaddr
This defines a “int64” type variable “helloaddr” storing the memory address of the string “hello”:
@, 45Q, 32Q
The memory address “32Q” is stored at location “45Q”. Internally a string variable is referenced by its address. This is done automatically.
Lets have a look at the multiplication code:
Brackets:
((x y *) a =)
Assembly:
loada x, 0, 2
loada y, 0, 3
muli 2, 3, 4
load a, 0, 5
pullqw 4, 5, 0
Here the “loada” opcode loads the “int64” variable “x” into register “2”. Then the next “loada” opcode loads the “int64” variable “y” into register “3”.
Now the “muli” opcode just multiplies the registers “2” and “3” and stores the result in the target register “4”. Now the compiler needs to store the register back into the variable “a”. This is done by load the address of “a” into register “5”:
load a, 0, 5
Now the register “4” is pulled into address “5” (a) by the “pullqw” opcode:
pullqw 4, 5, 0
I hope this posting could tell you more about what is going on in my compiler.
Have some fun!