Improved assembler
This commit is contained in:
42
src/asm/Assembler.go
Normal file
42
src/asm/Assembler.go
Normal file
@ -0,0 +1,42 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"git.akyoto.dev/cli/q/src/register"
|
||||
)
|
||||
|
||||
// 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 (list *Assembler) Finalize() *Result {
|
||||
final := Result{}
|
||||
|
||||
for _, instr := range list.Instructions {
|
||||
instr.Write(&final.Code)
|
||||
}
|
||||
|
||||
return &final
|
||||
}
|
||||
|
||||
func (list *Assembler) MoveRegisterNumber(reg register.ID, number uint64) {
|
||||
list.Instructions = append(list.Instructions, Instruction{
|
||||
Mnemonic: MOV,
|
||||
Destination: reg,
|
||||
Number: number,
|
||||
})
|
||||
}
|
||||
|
||||
func (list *Assembler) Syscall() {
|
||||
list.Instructions = append(list.Instructions, Instruction{
|
||||
Mnemonic: SYSCALL,
|
||||
})
|
||||
}
|
@ -1,23 +0,0 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"io"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/asm/x64"
|
||||
)
|
||||
|
||||
// Base represents the data that is common among all instructions.
|
||||
type Base struct {
|
||||
Mnemonic Mnemonic
|
||||
}
|
||||
|
||||
func (x *Base) Write(w io.ByteWriter) {
|
||||
switch x.Mnemonic {
|
||||
case SYSCALL:
|
||||
x64.Syscall(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Base) String() string {
|
||||
return x.Mnemonic.String()
|
||||
}
|
@ -1,8 +1,38 @@
|
||||
package asm
|
||||
|
||||
import "io"
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
type Instruction interface {
|
||||
Write(io.ByteWriter)
|
||||
String() string
|
||||
"git.akyoto.dev/cli/q/src/asm/x64"
|
||||
"git.akyoto.dev/cli/q/src/register"
|
||||
)
|
||||
|
||||
// Instruction represents a single instruction which can be converted to machine code.
|
||||
type Instruction struct {
|
||||
Mnemonic Mnemonic
|
||||
Source register.ID
|
||||
Destination register.ID
|
||||
Number uint64
|
||||
}
|
||||
|
||||
// Write writes the machine code of the instruction.
|
||||
func (x *Instruction) Write(w io.ByteWriter) {
|
||||
switch x.Mnemonic {
|
||||
case MOV:
|
||||
x64.MoveRegNum32(w, uint8(x.Destination), uint32(x.Number))
|
||||
case SYSCALL:
|
||||
x64.Syscall(w)
|
||||
}
|
||||
}
|
||||
|
||||
func (x *Instruction) String() string {
|
||||
switch x.Mnemonic {
|
||||
case MOV:
|
||||
return fmt.Sprintf("%s %s, %x", x.Mnemonic, x.Destination, x.Number)
|
||||
case SYSCALL:
|
||||
return x.Mnemonic.String()
|
||||
default:
|
||||
return ""
|
||||
}
|
||||
}
|
||||
|
@ -1,47 +0,0 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/register"
|
||||
)
|
||||
|
||||
type InstructionList struct {
|
||||
Instructions []Instruction
|
||||
}
|
||||
|
||||
// Finalize generates the final assembly code.
|
||||
func (list *InstructionList) Finalize() *Result {
|
||||
final := Result{}
|
||||
|
||||
for _, instr := range list.Instructions {
|
||||
instr.Write(&final.Code)
|
||||
fmt.Println(instr.String())
|
||||
}
|
||||
|
||||
return &final
|
||||
}
|
||||
|
||||
func (list *InstructionList) MoveRegisterNumber(reg register.Register, number uint64) {
|
||||
list.addRegisterNumber(MOV, reg, number)
|
||||
}
|
||||
|
||||
func (list *InstructionList) Syscall() {
|
||||
list.add(SYSCALL)
|
||||
}
|
||||
|
||||
// add adds an instruction without any operands.
|
||||
func (list *InstructionList) add(mnemonic Mnemonic) {
|
||||
list.Instructions = append(list.Instructions, &Base{Mnemonic: mnemonic})
|
||||
}
|
||||
|
||||
// addRegisterNumber adds an instruction using a register and a number.
|
||||
func (list *InstructionList) addRegisterNumber(mnemonic Mnemonic, reg register.Register, number uint64) {
|
||||
list.Instructions = append(list.Instructions, &RegisterNumber{
|
||||
Base: Base{
|
||||
Mnemonic: mnemonic,
|
||||
},
|
||||
Register: reg,
|
||||
Number: number,
|
||||
})
|
||||
}
|
@ -1,26 +0,0 @@
|
||||
package asm
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"git.akyoto.dev/cli/q/src/asm/x64"
|
||||
"git.akyoto.dev/cli/q/src/register"
|
||||
)
|
||||
|
||||
type RegisterNumber struct {
|
||||
Base
|
||||
Register register.Register
|
||||
Number uint64
|
||||
}
|
||||
|
||||
func (x *RegisterNumber) Write(w io.ByteWriter) {
|
||||
switch x.Mnemonic {
|
||||
case MOV:
|
||||
x64.MoveRegNum32(w, uint8(x.Register), uint32(x.Number))
|
||||
}
|
||||
}
|
||||
|
||||
func (x *RegisterNumber) String() string {
|
||||
return fmt.Sprintf("%s %s, %x", x.Mnemonic, x.Register, x.Number)
|
||||
}
|
@ -2,6 +2,7 @@ package asm
|
||||
|
||||
import "bytes"
|
||||
|
||||
// Result is the compilation result and contains the machine code as well as the data.
|
||||
type Result struct {
|
||||
Code bytes.Buffer
|
||||
Data bytes.Buffer
|
||||
|
10
src/asm/x64/Call.go
Normal file
10
src/asm/x64/Call.go
Normal file
@ -0,0 +1,10 @@
|
||||
package x64
|
||||
|
||||
import "io"
|
||||
|
||||
// Call places the return address on the top of the stack and continues
|
||||
// program flow at the new address. The address is relative to the next instruction.
|
||||
func Call(w io.ByteWriter, address uint32) {
|
||||
w.WriteByte(0xe8)
|
||||
appendUint32(w, address)
|
||||
}
|
@ -7,5 +7,5 @@ import (
|
||||
// MoveRegNum32 moves a 32 bit integer into the given register.
|
||||
func MoveRegNum32(w io.ByteWriter, register uint8, number uint32) {
|
||||
w.WriteByte(0xb8 + register)
|
||||
AppendUint32(w, number)
|
||||
appendUint32(w, number)
|
||||
}
|
9
src/asm/x64/Return.go
Normal file
9
src/asm/x64/Return.go
Normal file
@ -0,0 +1,9 @@
|
||||
package x64
|
||||
|
||||
import "io"
|
||||
|
||||
// Return transfers program control to a return address located on the top of the stack.
|
||||
// The address is usually placed on the stack by a Call instruction.
|
||||
func Return(w io.ByteWriter) {
|
||||
w.WriteByte(0xc3)
|
||||
}
|
@ -2,8 +2,8 @@ package x64
|
||||
|
||||
import "io"
|
||||
|
||||
// AppendUint32 appends a 32 bit integer in Little Endian to the given writer.
|
||||
func AppendUint32(w io.ByteWriter, number uint32) {
|
||||
// appendUint32 appends a 32 bit integer in Little Endian to the given writer.
|
||||
func appendUint32(w io.ByteWriter, number uint32) {
|
||||
w.WriteByte(byte(number))
|
||||
w.WriteByte(byte(number >> 8))
|
||||
w.WriteByte(byte(number >> 16))
|
Reference in New Issue
Block a user