diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 43be3f4..85aa05c 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -1,17 +1,11 @@ main() { - x := f(1) + f(2) + f(3) - - if x != 9 { - exit(1) - } - - exit(0) + print("Hello", 5) } -exit(code) { - syscall(60, code) +print(address, length) { + write(1, address, length) } -f(x) { - return x + 1 +write(fd, address, length) { + syscall(1, fd, address, length) } \ No newline at end of file diff --git a/src/build/asm/Assembler.go b/src/build/asm/Assembler.go index 3f0ad34..08efb10 100644 --- a/src/build/asm/Assembler.go +++ b/src/build/asm/Assembler.go @@ -1,268 +1,15 @@ package asm -import ( - "encoding/binary" - "fmt" - - "git.akyoto.dev/cli/q/src/build/arch/x64" -) +import "maps" // Assembler contains a list of instructions. type Assembler struct { Instructions []Instruction -} - -// 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) - labels := map[string]Address{} - pointers := []*Pointer{} - - for _, x := range a.Instructions { - switch x.Mnemonic { - case ADD: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x64.AddRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x64.AddRegisterRegister(code, operands.Destination, operands.Source) - } - - case SUB: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x64.SubRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x64.SubRegisterRegister(code, operands.Destination, operands.Source) - } - - case MUL: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x64.MulRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x64.MulRegisterRegister(code, operands.Destination, operands.Source) - } - - case DIV: - code = divide(code, x.Data) - - case CALL: - code = x64.Call(code, 0x00_00_00_00) - size := 4 - label := x.Data.(*Label) - nextInstructionAddress := Address(len(code)) - - pointers = append(pointers, &Pointer{ - Position: Address(len(code) - size), - OpSize: 1, - Size: uint8(size), - Resolve: func() Address { - destination, exists := labels[label.Name] - - if !exists { - panic("unknown call label") - } - - distance := destination - nextInstructionAddress - return Address(distance) - }, - }) - - case COMMENT: - continue - - case COMPARE: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x64.CompareRegisterNumber(code, operands.Register, operands.Number) - case *RegisterRegister: - code = x64.CompareRegisterRegister(code, operands.Destination, operands.Source) - } - - case JE, JNE, JG, JGE, JL, JLE, JUMP: - switch x.Mnemonic { - case JE: - code = x64.Jump8IfEqual(code, 0x00) - case JNE: - code = x64.Jump8IfNotEqual(code, 0x00) - case JG: - code = x64.Jump8IfGreater(code, 0x00) - case JGE: - code = x64.Jump8IfGreaterOrEqual(code, 0x00) - case JL: - code = x64.Jump8IfLess(code, 0x00) - case JLE: - code = x64.Jump8IfLessOrEqual(code, 0x00) - case JUMP: - code = x64.Jump8(code, 0x00) - } - - size := 1 - label := x.Data.(*Label) - nextInstructionAddress := Address(len(code)) - - pointers = append(pointers, &Pointer{ - Position: Address(len(code) - size), - OpSize: 1, - Size: uint8(size), - Resolve: func() Address { - destination, exists := labels[label.Name] - - if !exists { - panic("unknown jump label") - } - - distance := destination - nextInstructionAddress - return Address(distance) - }, - }) - - case LABEL: - labels[x.Data.(*Label).Name] = Address(len(code)) - - case MOVE: - switch operands := x.Data.(type) { - case *RegisterNumber: - code = x64.MoveRegisterNumber32(code, operands.Register, uint32(operands.Number)) - - case *RegisterRegister: - code = x64.MoveRegisterRegister64(code, operands.Destination, operands.Source) - } - - case POP: - switch operands := x.Data.(type) { - case *Register: - code = x64.PopRegister(code, operands.Register) - } - - case PUSH: - switch operands := x.Data.(type) { - case *Register: - code = x64.PushRegister(code, operands.Register) - } - - case RETURN: - code = x64.Return(code) - - case SYSCALL: - code = x64.Syscall(code) - - default: - panic("Unknown mnemonic: " + x.Mnemonic.String()) - } - } - - // dataStart := config.BaseAddress + config.CodeOffset + Address(len(code)) - -restart: - for i, pointer := range pointers { - address := pointer.Resolve() - - if x64.SizeOf(int64(address)) > int(pointer.Size) { - left := code[:pointer.Position-Address(pointer.OpSize)] - right := code[pointer.Position+Address(pointer.Size):] - size := pointer.Size + pointer.OpSize - opCode := code[pointer.Position-Address(pointer.OpSize)] - - var jump []byte - - switch opCode { - case 0x74: // JE - jump = []byte{0x0F, 0x84} - case 0x75: // JNE - jump = []byte{0x0F, 0x85} - case 0x7C: // JL - jump = []byte{0x0F, 0x8C} - case 0x7D: // JGE - jump = []byte{0x0F, 0x8D} - case 0x7E: // JLE - jump = []byte{0x0F, 0x8E} - case 0x7F: // JG - jump = []byte{0x0F, 0x8F} - case 0xEB: // JMP - jump = []byte{0xE9} - default: - panic(fmt.Errorf("failed to increase pointer size for instruction 0x%x", opCode)) - } - - pointer.Position += Address(len(jump) - int(pointer.OpSize)) - pointer.OpSize = uint8(len(jump)) - pointer.Size = 4 - jump = binary.LittleEndian.AppendUint32(jump, uint32(address)) - offset := Address(len(jump)) - Address(size) - - for _, following := range pointers[i+1:] { - following.Position += offset - } - - code = append(left, jump...) - code = append(code, right...) - goto restart - } - - slice := code[pointer.Position : pointer.Position+Address(pointer.Size)] - - switch pointer.Size { - case 1: - slice[0] = uint8(address) - - case 2: - binary.LittleEndian.PutUint16(slice, uint16(address)) - - case 4: - binary.LittleEndian.PutUint32(slice, uint32(address)) - - case 8: - binary.LittleEndian.PutUint64(slice, uint64(address)) - } - } - - return code, data + Data map[string][]byte } // Merge combines the contents of this assembler with another one. func (a *Assembler) Merge(b Assembler) { a.Instructions = append(a.Instructions, b.Instructions...) -} - -// divide implements the division on x64 machines. -func divide(code []byte, data any) []byte { - code = x64.PushRegister(code, x64.RDX) - - switch operands := data.(type) { - case *RegisterNumber: - if operands.Register == x64.RAX { - code = x64.PushRegister(code, x64.RCX) - code = x64.MoveRegisterNumber32(code, x64.RCX, uint32(operands.Number)) - code = x64.ExtendRAXToRDX(code) - code = x64.DivRegister(code, x64.RCX) - code = x64.PopRegister(code, x64.RCX) - } else { - code = x64.PushRegister(code, x64.RAX) - code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Register) - code = x64.MoveRegisterNumber32(code, operands.Register, uint32(operands.Number)) - code = x64.ExtendRAXToRDX(code) - code = x64.DivRegister(code, operands.Register) - code = x64.MoveRegisterRegister64(code, operands.Register, x64.RAX) - code = x64.PopRegister(code, x64.RAX) - } - - case *RegisterRegister: - if operands.Destination == x64.RAX { - code = x64.ExtendRAXToRDX(code) - code = x64.DivRegister(code, operands.Source) - } else { - code = x64.PushRegister(code, x64.RAX) - code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Destination) - code = x64.ExtendRAXToRDX(code) - code = x64.DivRegister(code, operands.Source) - code = x64.MoveRegisterRegister64(code, operands.Destination, x64.RAX) - code = x64.PopRegister(code, x64.RAX) - } - } - - code = x64.PopRegister(code, x64.RDX) - return code + maps.Copy(a.Data, b.Data) } diff --git a/src/build/asm/Finalize.go b/src/build/asm/Finalize.go new file mode 100644 index 0000000..b669153 --- /dev/null +++ b/src/build/asm/Finalize.go @@ -0,0 +1,248 @@ +package asm + +import ( + "encoding/binary" + "fmt" + + "git.akyoto.dev/cli/q/src/build/arch/x64" + "git.akyoto.dev/cli/q/src/build/config" + "git.akyoto.dev/cli/q/src/build/elf" +) + +// 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) + labels := map[string]Address{} + pointers := []*Pointer{} + + for _, x := range a.Instructions { + switch x.Mnemonic { + case ADD: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.AddRegisterNumber(code, operands.Register, operands.Number) + case *RegisterRegister: + code = x64.AddRegisterRegister(code, operands.Destination, operands.Source) + } + + case SUB: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.SubRegisterNumber(code, operands.Register, operands.Number) + case *RegisterRegister: + code = x64.SubRegisterRegister(code, operands.Destination, operands.Source) + } + + case MUL: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.MulRegisterNumber(code, operands.Register, operands.Number) + case *RegisterRegister: + code = x64.MulRegisterRegister(code, operands.Destination, operands.Source) + } + + case DIV: + code = divide(code, x.Data) + + case CALL: + code = x64.Call(code, 0x00_00_00_00) + size := 4 + label := x.Data.(*Label) + nextInstructionAddress := Address(len(code)) + + pointers = append(pointers, &Pointer{ + Position: Address(len(code) - size), + OpSize: 1, + Size: uint8(size), + Resolve: func() Address { + destination, exists := labels[label.Name] + + if !exists { + panic("unknown call label") + } + + distance := destination - nextInstructionAddress + return Address(distance) + }, + }) + + case COMMENT: + continue + + case COMPARE: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.CompareRegisterNumber(code, operands.Register, operands.Number) + case *RegisterRegister: + code = x64.CompareRegisterRegister(code, operands.Destination, operands.Source) + } + + case JE, JNE, JG, JGE, JL, JLE, JUMP: + switch x.Mnemonic { + case JE: + code = x64.Jump8IfEqual(code, 0x00) + case JNE: + code = x64.Jump8IfNotEqual(code, 0x00) + case JG: + code = x64.Jump8IfGreater(code, 0x00) + case JGE: + code = x64.Jump8IfGreaterOrEqual(code, 0x00) + case JL: + code = x64.Jump8IfLess(code, 0x00) + case JLE: + code = x64.Jump8IfLessOrEqual(code, 0x00) + case JUMP: + code = x64.Jump8(code, 0x00) + } + + size := 1 + label := x.Data.(*Label) + nextInstructionAddress := Address(len(code)) + + pointers = append(pointers, &Pointer{ + Position: Address(len(code) - size), + OpSize: 1, + Size: uint8(size), + Resolve: func() Address { + destination, exists := labels[label.Name] + + if !exists { + panic("unknown jump label") + } + + distance := destination - nextInstructionAddress + return Address(distance) + }, + }) + + case LABEL: + labels[x.Data.(*Label).Name] = Address(len(code)) + + case MOVE: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.MoveRegisterNumber32(code, operands.Register, uint32(operands.Number)) + + case *RegisterRegister: + code = x64.MoveRegisterRegister64(code, operands.Destination, operands.Source) + + case *RegisterLabel: + start := len(code) + code = x64.MoveRegisterNumber32(code, operands.Register, 0x00_00_00_00) + size := 4 + opSize := len(code) - size - start + regLabel := x.Data.(*RegisterLabel) + + pointers = append(pointers, &Pointer{ + Position: Address(len(code) - size), + OpSize: uint8(opSize), + Size: uint8(size), + Resolve: func() Address { + destination, exists := labels[regLabel.Label] + + if !exists { + panic("unknown label") + } + + return Address(destination) + }, + }) + } + + case POP: + switch operands := x.Data.(type) { + case *Register: + code = x64.PopRegister(code, operands.Register) + } + + case PUSH: + switch operands := x.Data.(type) { + case *Register: + code = x64.PushRegister(code, operands.Register) + } + + case RETURN: + code = x64.Return(code) + + case SYSCALL: + code = x64.Syscall(code) + + default: + panic("Unknown mnemonic: " + x.Mnemonic.String()) + } + } + + dataStart := config.BaseAddress + config.CodeOffset + Address(len(code)) + dataStart += int32(elf.Padding(int64(dataStart), config.Align)) + + for label, slice := range a.Data { + labels[label] = dataStart + Address(len(data)) + data = append(data, slice...) + } + +restart: + for i, pointer := range pointers { + address := pointer.Resolve() + + if x64.SizeOf(int64(address)) > int(pointer.Size) { + left := code[:pointer.Position-Address(pointer.OpSize)] + right := code[pointer.Position+Address(pointer.Size):] + size := pointer.Size + pointer.OpSize + opCode := code[pointer.Position-Address(pointer.OpSize)] + + var jump []byte + + switch opCode { + case 0x74: // JE + jump = []byte{0x0F, 0x84} + case 0x75: // JNE + jump = []byte{0x0F, 0x85} + case 0x7C: // JL + jump = []byte{0x0F, 0x8C} + case 0x7D: // JGE + jump = []byte{0x0F, 0x8D} + case 0x7E: // JLE + jump = []byte{0x0F, 0x8E} + case 0x7F: // JG + jump = []byte{0x0F, 0x8F} + case 0xEB: // JMP + jump = []byte{0xE9} + default: + panic(fmt.Errorf("failed to increase pointer size for instruction 0x%x", opCode)) + } + + pointer.Position += Address(len(jump) - int(pointer.OpSize)) + pointer.OpSize = uint8(len(jump)) + pointer.Size = 4 + jump = binary.LittleEndian.AppendUint32(jump, uint32(address)) + offset := Address(len(jump)) - Address(size) + + for _, following := range pointers[i+1:] { + following.Position += offset + } + + code = append(left, jump...) + code = append(code, right...) + goto restart + } + + slice := code[pointer.Position : pointer.Position+Address(pointer.Size)] + + switch pointer.Size { + case 1: + slice[0] = uint8(address) + + case 2: + binary.LittleEndian.PutUint16(slice, uint16(address)) + + case 4: + binary.LittleEndian.PutUint32(slice, uint32(address)) + + case 8: + binary.LittleEndian.PutUint64(slice, uint64(address)) + } + } + + return code, data +} diff --git a/src/build/asm/Instructions.go b/src/build/asm/Instructions.go index 805a0ea..4c51e87 100644 --- a/src/build/asm/Instructions.go +++ b/src/build/asm/Instructions.go @@ -1,53 +1,5 @@ package asm -import "git.akyoto.dev/cli/q/src/build/cpu" - -// RegisterNumber adds an instruction with a register and a number. -func (a *Assembler) RegisterNumber(mnemonic Mnemonic, reg cpu.Register, number int) { - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: mnemonic, - Data: &RegisterNumber{ - Register: reg, - Number: number, - }, - }) -} - -// RegisterRegister adds an instruction using two registers. -func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right cpu.Register) { - if a.unnecessary(mnemonic, left, right) { - return - } - - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: mnemonic, - Data: &RegisterRegister{ - Destination: left, - Source: right, - }, - }) -} - -// Register adds an instruction using a single register. -func (a *Assembler) Register(mnemonic Mnemonic, register cpu.Register) { - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: mnemonic, - Data: &Register{ - Register: register, - }, - }) -} - -// Label adds an instruction using a label. -func (a *Assembler) Label(mnemonic Mnemonic, name string) { - a.Instructions = append(a.Instructions, Instruction{ - Mnemonic: mnemonic, - Data: &Label{ - Name: name, - }, - }) -} - // Comment adds a comment at the current position. func (a *Assembler) Comment(text string) { a.Instructions = append(a.Instructions, Instruction{ diff --git a/src/build/asm/Label.go b/src/build/asm/Label.go index 1636fb8..1b463bc 100644 --- a/src/build/asm/Label.go +++ b/src/build/asm/Label.go @@ -9,3 +9,13 @@ type Label struct { func (data *Label) String() string { return data.Name } + +// Label adds an instruction using a label. +func (a *Assembler) Label(mnemonic Mnemonic, name string) { + a.Instructions = append(a.Instructions, Instruction{ + Mnemonic: mnemonic, + Data: &Label{ + Name: name, + }, + }) +} diff --git a/src/build/asm/Register.go b/src/build/asm/Register.go index a618370..0f737c9 100644 --- a/src/build/asm/Register.go +++ b/src/build/asm/Register.go @@ -13,3 +13,13 @@ type Register struct { func (data *Register) String() string { return data.Register.String() } + +// Register adds an instruction using a single register. +func (a *Assembler) Register(mnemonic Mnemonic, register cpu.Register) { + a.Instructions = append(a.Instructions, Instruction{ + Mnemonic: mnemonic, + Data: &Register{ + Register: register, + }, + }) +} diff --git a/src/build/asm/RegisterLabel.go b/src/build/asm/RegisterLabel.go new file mode 100644 index 0000000..4eb23a3 --- /dev/null +++ b/src/build/asm/RegisterLabel.go @@ -0,0 +1,29 @@ +package asm + +import ( + "fmt" + + "git.akyoto.dev/cli/q/src/build/cpu" +) + +// RegisterLabel operates with a register and a label. +type RegisterLabel struct { + Register cpu.Register + Label string +} + +// String returns a human readable version. +func (data *RegisterLabel) String() string { + return fmt.Sprintf("%s, %s", data.Register, data.Label) +} + +// RegisterLabel adds an instruction with a register and a label. +func (a *Assembler) RegisterLabel(mnemonic Mnemonic, reg cpu.Register, label string) { + a.Instructions = append(a.Instructions, Instruction{ + Mnemonic: mnemonic, + Data: &RegisterLabel{ + Register: reg, + Label: label, + }, + }) +} diff --git a/src/build/asm/RegisterNumber.go b/src/build/asm/RegisterNumber.go index ff5d616..b3bcb9e 100644 --- a/src/build/asm/RegisterNumber.go +++ b/src/build/asm/RegisterNumber.go @@ -16,3 +16,14 @@ type RegisterNumber struct { func (data *RegisterNumber) String() string { return fmt.Sprintf("%s, %d", data.Register, data.Number) } + +// RegisterNumber adds an instruction with a register and a number. +func (a *Assembler) RegisterNumber(mnemonic Mnemonic, reg cpu.Register, number int) { + a.Instructions = append(a.Instructions, Instruction{ + Mnemonic: mnemonic, + Data: &RegisterNumber{ + Register: reg, + Number: number, + }, + }) +} diff --git a/src/build/asm/RegisterRegister.go b/src/build/asm/RegisterRegister.go index 5acb084..584058d 100644 --- a/src/build/asm/RegisterRegister.go +++ b/src/build/asm/RegisterRegister.go @@ -16,3 +16,18 @@ type RegisterRegister struct { func (data *RegisterRegister) String() string { return fmt.Sprintf("%s, %s", data.Destination, data.Source) } + +// RegisterRegister adds an instruction using two registers. +func (a *Assembler) RegisterRegister(mnemonic Mnemonic, left cpu.Register, right cpu.Register) { + if a.unnecessary(mnemonic, left, right) { + return + } + + a.Instructions = append(a.Instructions, Instruction{ + Mnemonic: mnemonic, + Data: &RegisterRegister{ + Destination: left, + Source: right, + }, + }) +} diff --git a/src/build/asm/divide.go b/src/build/asm/divide.go new file mode 100644 index 0000000..09a879b --- /dev/null +++ b/src/build/asm/divide.go @@ -0,0 +1,43 @@ +package asm + +import "git.akyoto.dev/cli/q/src/build/arch/x64" + +// divide implements the division on x64 machines. +func divide(code []byte, data any) []byte { + code = x64.PushRegister(code, x64.RDX) + + switch operands := data.(type) { + case *RegisterNumber: + if operands.Register == x64.RAX { + code = x64.PushRegister(code, x64.RCX) + code = x64.MoveRegisterNumber32(code, x64.RCX, uint32(operands.Number)) + code = x64.ExtendRAXToRDX(code) + code = x64.DivRegister(code, x64.RCX) + code = x64.PopRegister(code, x64.RCX) + } else { + code = x64.PushRegister(code, x64.RAX) + code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Register) + code = x64.MoveRegisterNumber32(code, operands.Register, uint32(operands.Number)) + code = x64.ExtendRAXToRDX(code) + code = x64.DivRegister(code, operands.Register) + code = x64.MoveRegisterRegister64(code, operands.Register, x64.RAX) + code = x64.PopRegister(code, x64.RAX) + } + + case *RegisterRegister: + if operands.Destination == x64.RAX { + code = x64.ExtendRAXToRDX(code) + code = x64.DivRegister(code, operands.Source) + } else { + code = x64.PushRegister(code, x64.RAX) + code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Destination) + code = x64.ExtendRAXToRDX(code) + code = x64.DivRegister(code, operands.Source) + code = x64.MoveRegisterRegister64(code, operands.Destination, x64.RAX) + code = x64.PopRegister(code, x64.RAX) + } + } + + code = x64.PopRegister(code, x64.RDX) + return code +} diff --git a/src/build/core/ExecuteLeaf.go b/src/build/core/ExecuteLeaf.go index 1349870..c08866d 100644 --- a/src/build/core/ExecuteLeaf.go +++ b/src/build/core/ExecuteLeaf.go @@ -31,6 +31,11 @@ func (f *Function) ExecuteLeaf(operation token.Token, register cpu.Register, ope } return f.ExecuteRegisterNumber(operation, register, number) + + case token.String: + if operation.Text() == "=" { + return f.TokenToRegister(operand, register) + } } return errors.New(errors.NotImplemented, f.File, operation.Position) diff --git a/src/build/core/Function.go b/src/build/core/Function.go index 7e57da2..961f5d7 100644 --- a/src/build/core/Function.go +++ b/src/build/core/Function.go @@ -27,6 +27,7 @@ func NewFunction(name string, file *fs.File, body token.List) *Function { state: state{ assembler: asm.Assembler{ Instructions: make([]asm.Instruction, 0, 32), + Data: map[string][]byte{}, }, cpu: cpu.CPU{ All: x64.AllRegisters, diff --git a/src/build/core/Instructions.go b/src/build/core/Instructions.go index e6764ca..6f4da46 100644 --- a/src/build/core/Instructions.go +++ b/src/build/core/Instructions.go @@ -53,6 +53,20 @@ func (f *Function) RegisterNumber(mnemonic asm.Mnemonic, a cpu.Register, b int) f.postInstruction() } +func (f *Function) RegisterLabel(mnemonic asm.Mnemonic, register cpu.Register, label string) { + if f.cpu.IsUsed(register) && isDestructive(mnemonic) { + f.SaveRegister(register) + } + + f.assembler.RegisterLabel(mnemonic, register, label) + + if mnemonic == asm.MOVE { + f.cpu.Use(register) + } + + f.postInstruction() +} + func (f *Function) RegisterRegister(mnemonic asm.Mnemonic, a cpu.Register, b cpu.Register) { if mnemonic == asm.MOVE && a == b { return diff --git a/src/build/core/Result.go b/src/build/core/Result.go index 9430012..671a90a 100644 --- a/src/build/core/Result.go +++ b/src/build/core/Result.go @@ -25,6 +25,7 @@ func (r *Result) finalize() ([]byte, []byte) { // a return address on the stack, which allows return statements in `main`. final := asm.Assembler{ Instructions: make([]asm.Instruction, 0, r.InstructionCount+4), + Data: map[string][]byte{}, } final.Call("main") diff --git a/src/build/core/TokenToRegister.go b/src/build/core/TokenToRegister.go index bbc3808..d437cec 100644 --- a/src/build/core/TokenToRegister.go +++ b/src/build/core/TokenToRegister.go @@ -1,6 +1,7 @@ package core import ( + "fmt" "strconv" "git.akyoto.dev/cli/q/src/build/asm" @@ -37,7 +38,12 @@ func (f *Function) TokenToRegister(t token.Token, register cpu.Register) error { return nil case token.String: - return errors.New(errors.NotImplemented, f.File, t.Position) + value := t.Text()[1 : len(t.Bytes)-1] + label := fmt.Sprintf("%s_data_%d", f.Name, f.count.data) + f.assembler.Data[label] = []byte(value) + f.RegisterLabel(asm.MOVE, register, label) + f.count.data++ + return nil default: return errors.New(errors.InvalidExpression, f.File, t.Position) diff --git a/src/build/core/state.go b/src/build/core/state.go index e185533..9269971 100644 --- a/src/build/core/state.go +++ b/src/build/core/state.go @@ -23,8 +23,9 @@ type state struct { // counter stores how often a certain statement appeared so we can generate a unique label from it. type counter struct { - loop int branch int + data int + loop int subBranch int } diff --git a/src/build/elf/ELF.go b/src/build/elf/ELF.go index f284dbc..b6c3e34 100644 --- a/src/build/elf/ELF.go +++ b/src/build/elf/ELF.go @@ -1,6 +1,7 @@ package elf import ( + "bytes" "encoding/binary" "io" @@ -10,13 +11,19 @@ import ( // ELF represents an ELF file. type ELF struct { Header - ProgramHeader - Code []byte - Data []byte + CodeHeader ProgramHeader + PadCode []byte + Code []byte + PadData []byte + Data []byte } // New creates a new ELF binary. func New(code []byte, data []byte) *ELF { + dataOffset := config.CodeOffset + int64(len(code)) + dataPadding := Padding(dataOffset, config.Align) + dataOffset += dataPadding + elf := &ELF{ Header: Header{ Magic: [4]byte{0x7F, 'E', 'L', 'F'}, @@ -39,9 +46,9 @@ func New(code []byte, data []byte) *ELF { SectionHeaderEntryCount: 0, SectionNameStringTableIndex: 0, }, - ProgramHeader: ProgramHeader{ + CodeHeader: ProgramHeader{ Type: ProgramTypeLOAD, - Flags: ProgramFlagsExecutable, + Flags: ProgramFlagsExecutable | ProgramFlagsReadable, Offset: config.CodeOffset, VirtualAddress: config.BaseAddress + config.CodeOffset, PhysicalAddress: config.BaseAddress + config.CodeOffset, @@ -49,18 +56,25 @@ func New(code []byte, data []byte) *ELF { SizeInMemory: int64(len(code)), Align: config.Align, }, - Code: code, - Data: data, + PadCode: bytes.Repeat([]byte{0}, 8), + Code: code, + PadData: bytes.Repeat([]byte{0}, int(dataPadding)), + Data: data, } return elf } +func Padding(n int64, align int64) int64 { + return align - (n % align) +} + // Write writes the ELF64 format to the given writer. func (elf *ELF) Write(writer io.Writer) { binary.Write(writer, binary.LittleEndian, &elf.Header) - binary.Write(writer, binary.LittleEndian, &elf.ProgramHeader) - writer.Write([]byte{0, 0, 0, 0, 0, 0, 0, 0}) + binary.Write(writer, binary.LittleEndian, &elf.CodeHeader) + writer.Write(elf.PadCode) writer.Write(elf.Code) + writer.Write(elf.PadData) writer.Write(elf.Data) } diff --git a/tests/examples_test.go b/tests/examples_test.go index 109734e..f390eb2 100644 --- a/tests/examples_test.go +++ b/tests/examples_test.go @@ -10,7 +10,7 @@ var examples = []struct { ExpectedOutput string ExpectedExitCode int }{ - {"hello", "", 0}, + {"hello", "Hello", 0}, {"fibonacci", "", 55}, }