From 78aee7999b1308a011a1624ac8d4c2b8ada90c60 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Thu, 6 Feb 2025 23:26:10 +0100 Subject: [PATCH] Added asmc package --- src/asm/Finalize.go | 361 ------------------ src/asmc/Finalize.go | 32 ++ src/asmc/call.go | 31 ++ src/{asm/CodeOffset.go => asmc/codeOffset.go} | 6 +- src/asmc/compile.go | 162 ++++++++ src/asmc/compiler.go | 15 + src/asmc/dllCall.go | 41 ++ src/asmc/jump.go | 47 +++ src/asmc/move.go | 58 +++ src/{asm => asmc}/pointer.go | 2 +- src/{asm => asmc}/resolvePointers.go | 38 +- src/asmc/store.go | 53 +++ src/compiler/Result.go | 3 +- src/readme.md | 1 + 14 files changed, 464 insertions(+), 386 deletions(-) delete mode 100644 src/asm/Finalize.go create mode 100644 src/asmc/Finalize.go create mode 100644 src/asmc/call.go rename src/{asm/CodeOffset.go => asmc/codeOffset.go} (81%) create mode 100644 src/asmc/compile.go create mode 100644 src/asmc/compiler.go create mode 100644 src/asmc/dllCall.go create mode 100644 src/asmc/jump.go create mode 100644 src/asmc/move.go rename src/{asm => asmc}/pointer.go (96%) rename src/{asm => asmc}/resolvePointers.go (62%) create mode 100644 src/asmc/store.go diff --git a/src/asm/Finalize.go b/src/asm/Finalize.go deleted file mode 100644 index 5ea281c..0000000 --- a/src/asm/Finalize.go +++ /dev/null @@ -1,361 +0,0 @@ -package asm - -import ( - "math" - "strings" - - "git.akyoto.dev/cli/q/src/config" - "git.akyoto.dev/cli/q/src/dll" - "git.akyoto.dev/cli/q/src/x86" -) - -// Finalize generates the final machine code. -func (a Assembler) Finalize(dlls dll.List) ([]byte, []byte) { - var ( - code = make([]byte, 0, len(a.Instructions)*8) - data []byte - codeLabels = map[string]Address{} - dataLabels map[string]Address - codePointers []*pointer - dataPointers []*pointer - dllPointers []*pointer - codeStart = CodeOffset() - ) - - for _, x := range a.Instructions { - switch x.Mnemonic { - case ADD: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.AddRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x86.AddRegisterRegister(code, operands.Destination, operands.Source) - } - - case AND: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.AndRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x86.AndRegisterRegister(code, operands.Destination, operands.Source) - } - - case SUB: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.SubRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x86.SubRegisterRegister(code, operands.Destination, operands.Source) - } - - case MUL: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.MulRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x86.MulRegisterRegister(code, operands.Destination, operands.Source) - } - - case DIV: - switch operands := x.Data.(type) { - case *RegisterRegister: - if operands.Destination != x86.RAX { - code = x86.MoveRegisterRegister(code, x86.RAX, operands.Destination) - } - - code = x86.ExtendRAXToRDX(code) - code = x86.DivRegister(code, operands.Source) - - if operands.Destination != x86.RAX { - code = x86.MoveRegisterRegister(code, operands.Destination, x86.RAX) - } - } - - case MODULO: - switch operands := x.Data.(type) { - case *RegisterRegister: - if operands.Destination != x86.RAX { - code = x86.MoveRegisterRegister(code, x86.RAX, operands.Destination) - } - - code = x86.ExtendRAXToRDX(code) - code = x86.DivRegister(code, operands.Source) - - if operands.Destination != x86.RDX { - code = x86.MoveRegisterRegister(code, operands.Destination, x86.RDX) - } - } - - case CALL: - code = x86.Call(code, 0x00_00_00_00) - size := 4 - label := x.Data.(*Label) - - pointer := &pointer{ - Position: Address(len(code) - size), - OpSize: 1, - Size: uint8(size), - } - - pointer.Resolve = func() Address { - destination, exists := codeLabels[label.Name] - - if !exists { - panic("unknown jump label") - } - - distance := destination - (pointer.Position + Address(pointer.Size)) - return Address(distance) - } - - codePointers = append(codePointers, pointer) - - case COMMENT: - continue - - case COMPARE: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.CompareRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x86.CompareRegisterRegister(code, operands.Destination, operands.Source) - } - - case DLLCALL: - size := 4 - // TODO: R15 could be in use. - code = x86.MoveRegisterRegister(code, x86.R15, x86.RSP) - code = x86.AlignStack(code) - code = x86.SubRegisterNumber(code, x86.RSP, 32) - code = x86.CallAtAddress(code, 0x00_00_00_00) - position := len(code) - size - code = x86.MoveRegisterRegister(code, x86.RSP, x86.R15) - - label := x.Data.(*Label) - pointer := &pointer{ - Position: Address(position), - OpSize: 2, - Size: uint8(size), - } - - pointer.Resolve = func() Address { - dot := strings.Index(label.Name, ".") - library := label.Name[:dot] - funcName := label.Name[dot+1:] - index := dlls.Index(library, funcName) - - if index == -1 { - panic("unknown DLL function " + label.Name) - } - - return Address(index * 8) - } - - dllPointers = append(dllPointers, pointer) - - case JE, JNE, JG, JGE, JL, JLE, JUMP: - switch x.Mnemonic { - case JE: - code = x86.Jump8IfEqual(code, 0x00) - case JNE: - code = x86.Jump8IfNotEqual(code, 0x00) - case JG: - code = x86.Jump8IfGreater(code, 0x00) - case JGE: - code = x86.Jump8IfGreaterOrEqual(code, 0x00) - case JL: - code = x86.Jump8IfLess(code, 0x00) - case JLE: - code = x86.Jump8IfLessOrEqual(code, 0x00) - case JUMP: - code = x86.Jump8(code, 0x00) - } - - size := 1 - label := x.Data.(*Label) - - pointer := &pointer{ - Position: Address(len(code) - size), - OpSize: 1, - Size: uint8(size), - } - - pointer.Resolve = func() Address { - destination, exists := codeLabels[label.Name] - - if !exists { - panic("unknown jump label") - } - - distance := destination - (pointer.Position + Address(pointer.Size)) - return Address(distance) - } - - codePointers = append(codePointers, pointer) - - case LABEL: - codeLabels[x.Data.(*Label).Name] = Address(len(code)) - - case LOAD: - switch operands := x.Data.(type) { - case *MemoryRegister: - code = x86.LoadRegister(code, operands.Register, operands.Address.Offset, operands.Address.Length, operands.Address.Base) - } - - case MOVE: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.MoveRegisterNumber(code, operands.Register, operands.Number) - - case *RegisterRegister: - code = x86.MoveRegisterRegister(code, operands.Destination, operands.Source) - - case *RegisterLabel: - start := len(code) - code = x86.MoveRegisterNumber(code, operands.Register, 0x00_00_00_00) - size := 4 - opSize := len(code) - size - start - regLabel := x.Data.(*RegisterLabel) - - if strings.HasPrefix(regLabel.Label, "data_") { - dataPointers = append(dataPointers, &pointer{ - Position: Address(len(code) - size), - OpSize: uint8(opSize), - Size: uint8(size), - Resolve: func() Address { - destination, exists := dataLabels[regLabel.Label] - - if !exists { - panic("unknown label") - } - - return Address(destination) - }, - }) - } else { - codePointers = append(codePointers, &pointer{ - Position: Address(len(code) - size), - OpSize: uint8(opSize), - Size: uint8(size), - Resolve: func() Address { - destination, exists := codeLabels[regLabel.Label] - - if !exists { - panic("unknown label") - } - - return config.BaseAddress + codeStart + destination - }, - }) - } - } - - case NEGATE: - switch operands := x.Data.(type) { - case *Register: - code = x86.NegateRegister(code, operands.Register) - } - - case OR: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.OrRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x86.OrRegisterRegister(code, operands.Destination, operands.Source) - } - - case POP: - switch operands := x.Data.(type) { - case *Register: - code = x86.PopRegister(code, operands.Register) - } - - case PUSH: - switch operands := x.Data.(type) { - case *Register: - code = x86.PushRegister(code, operands.Register) - } - - case RETURN: - code = x86.Return(code) - - case SHIFTL: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.ShiftLeftNumber(code, operands.Register, byte(operands.Number)&0b111111) - } - - case SHIFTRS: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.ShiftRightSignedNumber(code, operands.Register, byte(operands.Number)&0b111111) - } - - case STORE: - switch operands := x.Data.(type) { - case *MemoryNumber: - if operands.Address.OffsetRegister == math.MaxUint8 { - code = x86.StoreNumber(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number) - } else { - code = x86.StoreDynamicNumber(code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Number) - } - case *MemoryLabel: - start := len(code) - - if operands.Address.OffsetRegister == math.MaxUint8 { - code = x86.StoreNumber(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, 0b00_00_00_00) - } else { - code = x86.StoreDynamicNumber(code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, 0b00_00_00_00) - } - - size := 4 - opSize := len(code) - size - start - memLabel := x.Data.(*MemoryLabel) - - codePointers = append(codePointers, &pointer{ - Position: Address(len(code) - size), - OpSize: uint8(opSize), - Size: uint8(size), - Resolve: func() Address { - destination, exists := codeLabels[memLabel.Label] - - if !exists { - panic("unknown label") - } - - return config.BaseAddress + codeStart + destination - }, - }) - case *MemoryRegister: - if operands.Address.OffsetRegister == math.MaxUint8 { - code = x86.StoreRegister(code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) - } else { - code = x86.StoreDynamicRegister(code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Register) - } - } - - case SYSCALL: - code = x86.Syscall(code) - - case XOR: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x86.XorRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x86.XorRegisterRegister(code, operands.Destination, operands.Source) - } - - default: - panic("unknown mnemonic: " + x.Mnemonic.String()) - } - } - - data, dataLabels = a.Data.Finalize() - - if config.TargetOS == config.Windows && len(data) == 0 { - data = []byte{0} - } - - code = a.resolvePointers(code, data, codeStart, codeLabels, codePointers, dataPointers, dllPointers) - return code, data -} diff --git a/src/asmc/Finalize.go b/src/asmc/Finalize.go new file mode 100644 index 0000000..ff47fb3 --- /dev/null +++ b/src/asmc/Finalize.go @@ -0,0 +1,32 @@ +package asmc + +import ( + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/dll" +) + +// Finalize generates the final machine code. +func Finalize(a asm.Assembler, dlls dll.List) ([]byte, []byte) { + data, dataLabels := a.Data.Finalize() + + if config.TargetOS == config.Windows && len(data) == 0 { + data = []byte{0} + } + + c := compiler{ + code: make([]byte, 0, len(a.Instructions)*8), + codeLabels: map[string]Address{}, + codeStart: codeOffset(), + data: data, + dataLabels: dataLabels, + dlls: dlls, + } + + for _, x := range a.Instructions { + c.compile(x) + } + + c.resolvePointers() + return c.code, c.data +} diff --git a/src/asmc/call.go b/src/asmc/call.go new file mode 100644 index 0000000..940c0da --- /dev/null +++ b/src/asmc/call.go @@ -0,0 +1,31 @@ +package asmc + +import ( + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/x86" +) + +func (c *compiler) call(x asm.Instruction) { + c.code = x86.Call(c.code, 0x00_00_00_00) + size := 4 + label := x.Data.(*asm.Label) + + pointer := &pointer{ + Position: Address(len(c.code) - size), + OpSize: 1, + Size: uint8(size), + } + + pointer.Resolve = func() Address { + destination, exists := c.codeLabels[label.Name] + + if !exists { + panic("unknown jump label") + } + + distance := destination - (pointer.Position + Address(pointer.Size)) + return Address(distance) + } + + c.codePointers = append(c.codePointers, pointer) +} diff --git a/src/asm/CodeOffset.go b/src/asmc/codeOffset.go similarity index 81% rename from src/asm/CodeOffset.go rename to src/asmc/codeOffset.go index 4b8d4ba..306adc0 100644 --- a/src/asm/CodeOffset.go +++ b/src/asmc/codeOffset.go @@ -1,4 +1,4 @@ -package asm +package asmc import ( "git.akyoto.dev/cli/q/src/config" @@ -8,8 +8,8 @@ import ( "git.akyoto.dev/cli/q/src/pe" ) -// CodeOffset returns the file offset of the code section. -func CodeOffset() Address { +// codeOffset returns the file offset of the code section. +func codeOffset() Address { headerEnd := Address(0) switch config.TargetOS { diff --git a/src/asmc/compile.go b/src/asmc/compile.go new file mode 100644 index 0000000..e671884 --- /dev/null +++ b/src/asmc/compile.go @@ -0,0 +1,162 @@ +package asmc + +import ( + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/x86" +) + +func (c *compiler) compile(x asm.Instruction) { + switch x.Mnemonic { + case asm.ADD: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.AddRegisterNumber(c.code, operands.Register, operands.Number) + case *asm.RegisterRegister: + c.code = x86.AddRegisterRegister(c.code, operands.Destination, operands.Source) + } + + case asm.AND: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.AndRegisterNumber(c.code, operands.Register, operands.Number) + case *asm.RegisterRegister: + c.code = x86.AndRegisterRegister(c.code, operands.Destination, operands.Source) + } + + case asm.SUB: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.SubRegisterNumber(c.code, operands.Register, operands.Number) + case *asm.RegisterRegister: + c.code = x86.SubRegisterRegister(c.code, operands.Destination, operands.Source) + } + + case asm.MUL: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.MulRegisterNumber(c.code, operands.Register, operands.Number) + case *asm.RegisterRegister: + c.code = x86.MulRegisterRegister(c.code, operands.Destination, operands.Source) + } + + case asm.DIV: + switch operands := x.Data.(type) { + case *asm.RegisterRegister: + if operands.Destination != x86.RAX { + c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) + } + + c.code = x86.ExtendRAXToRDX(c.code) + c.code = x86.DivRegister(c.code, operands.Source) + + if operands.Destination != x86.RAX { + c.code = x86.MoveRegisterRegister(c.code, operands.Destination, x86.RAX) + } + } + + case asm.MODULO: + switch operands := x.Data.(type) { + case *asm.RegisterRegister: + if operands.Destination != x86.RAX { + c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination) + } + + c.code = x86.ExtendRAXToRDX(c.code) + c.code = x86.DivRegister(c.code, operands.Source) + + if operands.Destination != x86.RDX { + c.code = x86.MoveRegisterRegister(c.code, operands.Destination, x86.RDX) + } + } + + case asm.CALL: + c.call(x) + + case asm.COMMENT: + return + + case asm.COMPARE: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.CompareRegisterNumber(c.code, operands.Register, operands.Number) + case *asm.RegisterRegister: + c.code = x86.CompareRegisterRegister(c.code, operands.Destination, operands.Source) + } + + case asm.DLLCALL: + c.dllCall(x) + + case asm.JE, asm.JNE, asm.JG, asm.JGE, asm.JL, asm.JLE, asm.JUMP: + c.jump(x) + + case asm.LABEL: + c.codeLabels[x.Data.(*asm.Label).Name] = Address(len(c.code)) + + case asm.LOAD: + switch operands := x.Data.(type) { + case *asm.MemoryRegister: + c.code = x86.LoadRegister(c.code, operands.Register, operands.Address.Offset, operands.Address.Length, operands.Address.Base) + } + + case asm.MOVE: + c.move(x) + + case asm.NEGATE: + switch operands := x.Data.(type) { + case *asm.Register: + c.code = x86.NegateRegister(c.code, operands.Register) + } + + case asm.OR: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.OrRegisterNumber(c.code, operands.Register, operands.Number) + case *asm.RegisterRegister: + c.code = x86.OrRegisterRegister(c.code, operands.Destination, operands.Source) + } + + case asm.POP: + switch operands := x.Data.(type) { + case *asm.Register: + c.code = x86.PopRegister(c.code, operands.Register) + } + + case asm.PUSH: + switch operands := x.Data.(type) { + case *asm.Register: + c.code = x86.PushRegister(c.code, operands.Register) + } + + case asm.RETURN: + c.code = x86.Return(c.code) + + case asm.SHIFTL: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.ShiftLeftNumber(c.code, operands.Register, byte(operands.Number)&0b111111) + } + + case asm.SHIFTRS: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.ShiftRightSignedNumber(c.code, operands.Register, byte(operands.Number)&0b111111) + } + + case asm.STORE: + c.store(x) + + case asm.SYSCALL: + c.code = x86.Syscall(c.code) + + case asm.XOR: + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.XorRegisterNumber(c.code, operands.Register, operands.Number) + case *asm.RegisterRegister: + c.code = x86.XorRegisterRegister(c.code, operands.Destination, operands.Source) + } + + default: + panic("unknown mnemonic: " + x.Mnemonic.String()) + } +} diff --git a/src/asmc/compiler.go b/src/asmc/compiler.go new file mode 100644 index 0000000..0847707 --- /dev/null +++ b/src/asmc/compiler.go @@ -0,0 +1,15 @@ +package asmc + +import "git.akyoto.dev/cli/q/src/dll" + +type compiler struct { + code []byte + data []byte + codeLabels map[string]Address + dataLabels map[string]Address + codePointers []*pointer + dataPointers []*pointer + codeStart Address + dlls dll.List + dllPointers []*pointer +} diff --git a/src/asmc/dllCall.go b/src/asmc/dllCall.go new file mode 100644 index 0000000..8c9856e --- /dev/null +++ b/src/asmc/dllCall.go @@ -0,0 +1,41 @@ +package asmc + +import ( + "strings" + + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/x86" +) + +func (c *compiler) dllCall(x asm.Instruction) { + size := 4 + // TODO: R15 could be in use. + c.code = x86.MoveRegisterRegister(c.code, x86.R15, x86.RSP) + c.code = x86.AlignStack(c.code) + c.code = x86.SubRegisterNumber(c.code, x86.RSP, 32) + c.code = x86.CallAtAddress(c.code, 0x00_00_00_00) + position := len(c.code) - size + c.code = x86.MoveRegisterRegister(c.code, x86.RSP, x86.R15) + + label := x.Data.(*asm.Label) + pointer := &pointer{ + Position: Address(position), + OpSize: 2, + Size: uint8(size), + } + + pointer.Resolve = func() Address { + dot := strings.Index(label.Name, ".") + library := label.Name[:dot] + funcName := label.Name[dot+1:] + index := c.dlls.Index(library, funcName) + + if index == -1 { + panic("unknown DLL function " + label.Name) + } + + return Address(index * 8) + } + + c.dllPointers = append(c.dllPointers, pointer) +} diff --git a/src/asmc/jump.go b/src/asmc/jump.go new file mode 100644 index 0000000..e6d8f65 --- /dev/null +++ b/src/asmc/jump.go @@ -0,0 +1,47 @@ +package asmc + +import ( + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/x86" +) + +func (c *compiler) jump(x asm.Instruction) { + switch x.Mnemonic { + case asm.JE: + c.code = x86.Jump8IfEqual(c.code, 0x00) + case asm.JNE: + c.code = x86.Jump8IfNotEqual(c.code, 0x00) + case asm.JG: + c.code = x86.Jump8IfGreater(c.code, 0x00) + case asm.JGE: + c.code = x86.Jump8IfGreaterOrEqual(c.code, 0x00) + case asm.JL: + c.code = x86.Jump8IfLess(c.code, 0x00) + case asm.JLE: + c.code = x86.Jump8IfLessOrEqual(c.code, 0x00) + case asm.JUMP: + c.code = x86.Jump8(c.code, 0x00) + } + + size := 1 + label := x.Data.(*asm.Label) + + pointer := &pointer{ + Position: Address(len(c.code) - size), + OpSize: 1, + Size: uint8(size), + } + + pointer.Resolve = func() Address { + destination, exists := c.codeLabels[label.Name] + + if !exists { + panic("unknown jump label") + } + + distance := destination - (pointer.Position + Address(pointer.Size)) + return Address(distance) + } + + c.codePointers = append(c.codePointers, pointer) +} diff --git a/src/asmc/move.go b/src/asmc/move.go new file mode 100644 index 0000000..8d5a12d --- /dev/null +++ b/src/asmc/move.go @@ -0,0 +1,58 @@ +package asmc + +import ( + "strings" + + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/x86" +) + +func (c *compiler) move(x asm.Instruction) { + switch operands := x.Data.(type) { + case *asm.RegisterNumber: + c.code = x86.MoveRegisterNumber(c.code, operands.Register, operands.Number) + + case *asm.RegisterRegister: + c.code = x86.MoveRegisterRegister(c.code, operands.Destination, operands.Source) + + case *asm.RegisterLabel: + start := len(c.code) + c.code = x86.MoveRegisterNumber(c.code, operands.Register, 0x00_00_00_00) + size := 4 + opSize := len(c.code) - size - start + regLabel := x.Data.(*asm.RegisterLabel) + + if strings.HasPrefix(regLabel.Label, "data_") { + c.dataPointers = append(c.dataPointers, &pointer{ + Position: Address(len(c.code) - size), + OpSize: uint8(opSize), + Size: uint8(size), + Resolve: func() Address { + destination, exists := c.dataLabels[regLabel.Label] + + if !exists { + panic("unknown label") + } + + return Address(destination) + }, + }) + } else { + c.codePointers = append(c.codePointers, &pointer{ + Position: Address(len(c.code) - size), + OpSize: uint8(opSize), + Size: uint8(size), + Resolve: func() Address { + destination, exists := c.codeLabels[regLabel.Label] + + if !exists { + panic("unknown label") + } + + return config.BaseAddress + c.codeStart + destination + }, + }) + } + } +} diff --git a/src/asm/pointer.go b/src/asmc/pointer.go similarity index 96% rename from src/asm/pointer.go rename to src/asmc/pointer.go index 1d4880b..f299758 100644 --- a/src/asm/pointer.go +++ b/src/asmc/pointer.go @@ -1,4 +1,4 @@ -package asm +package asmc // Address represents a memory address. type Address = int32 diff --git a/src/asm/resolvePointers.go b/src/asmc/resolvePointers.go similarity index 62% rename from src/asm/resolvePointers.go rename to src/asmc/resolvePointers.go index 40ecbb9..a437555 100644 --- a/src/asm/resolvePointers.go +++ b/src/asmc/resolvePointers.go @@ -1,4 +1,4 @@ -package asm +package asmc import ( "encoding/binary" @@ -11,16 +11,16 @@ import ( ) // resolvePointers resolves the addresses of all pointers within the code and writes the correct addresses to the code slice. -func (a Assembler) resolvePointers(code []byte, data []byte, codeStart Address, codeLabels map[string]Address, codePointers []*pointer, dataPointers []*pointer, dllPointers []*pointer) []byte { +func (c *compiler) resolvePointers() { restart: - for i, pointer := range codePointers { + for i, pointer := range c.codePointers { address := pointer.Resolve() if sizeof.Signed(int64(address)) > int(pointer.Size) { - left := code[:pointer.Position-Address(pointer.OpSize)] - right := code[pointer.Position+Address(pointer.Size):] + left := c.code[:pointer.Position-Address(pointer.OpSize)] + right := c.code[pointer.Position+Address(pointer.Size):] size := pointer.Size + pointer.OpSize - opCode := code[pointer.Position-Address(pointer.OpSize)] + opCode := c.code[pointer.Position-Address(pointer.OpSize)] var jump []byte @@ -49,21 +49,21 @@ restart: jump = binary.LittleEndian.AppendUint32(jump, uint32(address)) offset := Address(len(jump)) - Address(size) - for _, following := range codePointers[i+1:] { + for _, following := range c.codePointers[i+1:] { following.Position += offset } - for key, address := range codeLabels { + for key, address := range c.codeLabels { if address > pointer.Position { - codeLabels[key] += offset + c.codeLabels[key] += offset } } - code = slices.Concat(left, jump, right) + c.code = slices.Concat(left, jump, right) goto restart } - slice := code[pointer.Position : pointer.Position+Address(pointer.Size)] + slice := c.code[pointer.Position : pointer.Position+Address(pointer.Size)] switch pointer.Size { case 1: @@ -77,24 +77,22 @@ restart: } } - dataStart, _ := fs.Align(codeStart+Address(len(code)), config.Align) + dataStart, _ := fs.Align(c.codeStart+Address(len(c.code)), config.Align) - for _, pointer := range dataPointers { + for _, pointer := range c.dataPointers { address := config.BaseAddress + Address(dataStart) + pointer.Resolve() - slice := code[pointer.Position : pointer.Position+4] + slice := c.code[pointer.Position : pointer.Position+4] binary.LittleEndian.PutUint32(slice, uint32(address)) } if config.TargetOS == config.Windows { - importsStart, _ := fs.Align(dataStart+Address(len(data)), config.Align) + importsStart, _ := fs.Align(dataStart+Address(len(c.data)), config.Align) - for _, pointer := range dllPointers { + for _, pointer := range c.dllPointers { destination := Address(importsStart) + pointer.Resolve() - delta := destination - Address(codeStart+pointer.Position+Address(pointer.Size)) - slice := code[pointer.Position : pointer.Position+4] + delta := destination - Address(c.codeStart+pointer.Position+Address(pointer.Size)) + slice := c.code[pointer.Position : pointer.Position+4] binary.LittleEndian.PutUint32(slice, uint32(delta)) } } - - return code } diff --git a/src/asmc/store.go b/src/asmc/store.go new file mode 100644 index 0000000..b5e8f0d --- /dev/null +++ b/src/asmc/store.go @@ -0,0 +1,53 @@ +package asmc + +import ( + "math" + + "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/config" + "git.akyoto.dev/cli/q/src/x86" +) + +func (c *compiler) store(x asm.Instruction) { + switch operands := x.Data.(type) { + case *asm.MemoryNumber: + if operands.Address.OffsetRegister == math.MaxUint8 { + c.code = x86.StoreNumber(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Number) + } else { + c.code = x86.StoreDynamicNumber(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Number) + } + case *asm.MemoryLabel: + start := len(c.code) + + if operands.Address.OffsetRegister == math.MaxUint8 { + c.code = x86.StoreNumber(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, 0b00_00_00_00) + } else { + c.code = x86.StoreDynamicNumber(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, 0b00_00_00_00) + } + + size := 4 + opSize := len(c.code) - size - start + memLabel := x.Data.(*asm.MemoryLabel) + + c.codePointers = append(c.codePointers, &pointer{ + Position: Address(len(c.code) - size), + OpSize: uint8(opSize), + Size: uint8(size), + Resolve: func() Address { + destination, exists := c.codeLabels[memLabel.Label] + + if !exists { + panic("unknown label") + } + + return config.BaseAddress + c.codeStart + destination + }, + }) + case *asm.MemoryRegister: + if operands.Address.OffsetRegister == math.MaxUint8 { + c.code = x86.StoreRegister(c.code, operands.Address.Base, operands.Address.Offset, operands.Address.Length, operands.Register) + } else { + c.code = x86.StoreDynamicRegister(c.code, operands.Address.Base, operands.Address.OffsetRegister, operands.Address.Length, operands.Register) + } + } +} diff --git a/src/compiler/Result.go b/src/compiler/Result.go index 6d01851..0788a36 100644 --- a/src/compiler/Result.go +++ b/src/compiler/Result.go @@ -7,6 +7,7 @@ import ( "os" "git.akyoto.dev/cli/q/src/asm" + "git.akyoto.dev/cli/q/src/asmc" "git.akyoto.dev/cli/q/src/config" "git.akyoto.dev/cli/q/src/core" "git.akyoto.dev/cli/q/src/dll" @@ -87,7 +88,7 @@ func (r *Result) finalize() { final.DLLCall("kernel32.ExitProcess") } - r.Code, r.Data = final.Finalize(r.DLLs) + r.Code, r.Data = asmc.Finalize(final, r.DLLs) } // eachFunction recursively finds all the calls to external functions. diff --git a/src/readme.md b/src/readme.md index 4ee22a5..c793954 100644 --- a/src/readme.md +++ b/src/readme.md @@ -2,6 +2,7 @@ - [arm64](arm64) - ARM64 implementation (w.i.p.) - [asm](asm) - Pseudo-assembler stage +- [asmc](asmc) - Compiles assembler to actual machine code - [ast](ast) - Abstract syntax tree generation with the `Parse` function - [build](build) - Build command - [cli](cli) - Command line interface