q/src/build/asm/Assembler.go

73 lines
1.6 KiB
Go

package asm
import (
"encoding/binary"
"fmt"
"git.akyoto.dev/cli/q/src/build/arch/x64"
"git.akyoto.dev/cli/q/src/build/config"
)
// Assembler contains a list of instructions.
type Assembler struct {
Instructions []Instruction
}
// New creates a new assembler.
func New() *Assembler {
return &Assembler{
Instructions: make([]Instruction, 0, 8),
}
}
// Finalize generates the final machine code.
func (a *Assembler) Finalize() ([]byte, []byte) {
code := make([]byte, 0, len(a.Instructions)*8)
data := make([]byte, 0, 16)
pointers := []Pointer{}
for _, x := range a.Instructions {
switch x.Mnemonic {
case MOVE:
code = x64.MoveRegNum32(code, uint8(x.Data.(RegisterNumber).Register), uint32(x.Data.(RegisterNumber).Number))
if x.Data.(RegisterNumber).IsPointer {
pointers = append(pointers, Pointer{
Position: Address(len(code) - 4),
Address: Address(x.Data.(RegisterNumber).Number),
})
}
case RETURN:
code = x64.Return(code)
case SYSCALL:
code = x64.Syscall(code)
default:
panic("Unknown mnemonic: " + x.Mnemonic.String())
}
}
if config.Verbose {
for _, x := range a.Instructions {
fmt.Println("[asm]", x.String())
}
}
dataStart := config.BaseAddress + config.CodeOffset + Address(len(code))
for _, pointer := range pointers {
slice := code[pointer.Position : pointer.Position+4]
address := dataStart + pointer.Address
binary.LittleEndian.PutUint32(slice, address)
}
return code, data
}
// Merge combines the contents of this assembler with another one.
func (a *Assembler) Merge(b *Assembler) {
a.Instructions = append(a.Instructions, b.Instructions...)
}