diff --git a/src/asm/Memory.go b/src/asm/Memory.go index 903ff15..0af7c38 100644 --- a/src/asm/Memory.go +++ b/src/asm/Memory.go @@ -35,9 +35,27 @@ func (mem *Memory) Format(custom string) string { tmp.WriteString(strconv.Itoa(int(mem.Offset))) } - tmp.WriteString("], ") - tmp.WriteString(custom) + tmp.WriteString("]") + + if custom != "" { + tmp.WriteString(", ") + tmp.WriteString(custom) + } + tmp.WriteString(", ") tmp.WriteString(strconv.Itoa(int(mem.Length))) return tmp.String() } + +// String returns a human readable version. +func (mem *Memory) String() string { + return mem.Format("") +} + +// Memory adds an instruction with a memory address. +func (a *Assembler) Memory(mnemonic Mnemonic, address Memory) { + a.Instructions = append(a.Instructions, Instruction{ + Mnemonic: mnemonic, + Data: &address, + }) +} diff --git a/src/asmc/call.go b/src/asmc/call.go index e5d08a7..7a4f51a 100644 --- a/src/asmc/call.go +++ b/src/asmc/call.go @@ -32,5 +32,8 @@ func (c *compiler) call(x asm.Instruction) { case *asm.Register: c.code = x86.CallRegister(c.code, data.Register) + + case *asm.Memory: + c.code = x86.CallAtMemory(c.code, data.Base, data.Offset) } } diff --git a/src/asmc/dllCall.go b/src/asmc/dllCall.go index 75523d1..71d1e0e 100644 --- a/src/asmc/dllCall.go +++ b/src/asmc/dllCall.go @@ -9,7 +9,7 @@ import ( func (c *compiler) dllCall(x asm.Instruction) { size := 4 - c.code = x86.CallAtAddress(c.code, 0x00_00_00_00) + c.code = x86.CallAt(c.code, 0x00_00_00_00) position := len(c.code) - size label := x.Data.(*asm.Label) diff --git a/src/core/CompileCall.go b/src/core/CompileCall.go index 7e47f48..9d94c2b 100644 --- a/src/core/CompileCall.go +++ b/src/core/CompileCall.go @@ -75,10 +75,7 @@ func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error f.Register(asm.CALL, value.Register) case *eval.Memory: - tmp := f.NewRegister() - f.MemoryRegister(asm.LOAD, value.Memory, tmp) - f.Register(asm.CALL, tmp) - f.FreeRegister(tmp) + f.Memory(asm.CALL, value.Memory) } return nil, nil diff --git a/src/register/Memory.go b/src/register/Memory.go new file mode 100644 index 0000000..60c62db --- /dev/null +++ b/src/register/Memory.go @@ -0,0 +1,8 @@ +package register + +import "git.urbach.dev/cli/q/src/asm" + +func (f *Machine) Memory(mnemonic asm.Mnemonic, memory asm.Memory) { + f.Assembler.Memory(mnemonic, memory) + f.postInstruction() +} diff --git a/src/x86/Call.go b/src/x86/Call.go index b6c9194..a19c68e 100644 --- a/src/x86/Call.go +++ b/src/x86/Call.go @@ -34,9 +34,9 @@ func CallRegister(code []byte, register cpu.Register) []byte { ) } -// CallAtAddress calls a function at the address stored at the given memory address. +// CallAt calls a function at the address stored at the given memory address. // The memory address is relative to the next instruction. -func CallAtAddress(code []byte, address uint32) []byte { +func CallAt(code []byte, address uint32) []byte { return append( code, 0xFF, @@ -47,3 +47,33 @@ func CallAtAddress(code []byte, address uint32) []byte { byte(address>>24), ) } + +// CallAtMemory calls a function at the address stored at the given memory address. +// The memory address is relative to the next instruction. +func CallAtMemory(code []byte, register cpu.Register, offset int8) []byte { + mod := AddressMemory + + if offset != 0 || register == RBP || register == R13 { + mod = AddressMemoryOffset8 + } + + reg := byte(0b010) + rm := register + + if rm > 0b111 { + code = append(code, 0x41) + rm &= 0b111 + } + + code = append(code, 0xFF, ModRM(mod, reg, byte(rm))) + + if register == RSP || register == R12 { + code = append(code, SIB(Scale1, 0b100, 0b100)) + } + + if mod == AddressMemoryOffset8 { + code = append(code, byte(offset)) + } + + return code +} diff --git a/src/x86/Call_test.go b/src/x86/Call_test.go index 5aae357..033ee64 100644 --- a/src/x86/Call_test.go +++ b/src/x86/Call_test.go @@ -37,3 +37,51 @@ func TestCallRegister(t *testing.T) { assert.DeepEqual(t, code, pattern.Code) } } + +func TestCallAtMemory(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Offset int8 + Code []byte + }{ + {x86.RAX, 0, []byte{0xFF, 0x10}}, + {x86.RCX, 0, []byte{0xFF, 0x11}}, + {x86.RDX, 0, []byte{0xFF, 0x12}}, + {x86.RBX, 0, []byte{0xFF, 0x13}}, + {x86.RSP, 0, []byte{0xFF, 0x14, 0x24}}, + {x86.RBP, 0, []byte{0xFF, 0x55, 0x00}}, + {x86.RSI, 0, []byte{0xFF, 0x16}}, + {x86.RDI, 0, []byte{0xFF, 0x17}}, + {x86.R8, 0, []byte{0x41, 0xFF, 0x10}}, + {x86.R9, 0, []byte{0x41, 0xFF, 0x11}}, + {x86.R10, 0, []byte{0x41, 0xFF, 0x12}}, + {x86.R11, 0, []byte{0x41, 0xFF, 0x13}}, + {x86.R12, 0, []byte{0x41, 0xFF, 0x14, 0x24}}, + {x86.R13, 0, []byte{0x41, 0xFF, 0x55, 0x00}}, + {x86.R14, 0, []byte{0x41, 0xFF, 0x16}}, + {x86.R15, 0, []byte{0x41, 0xFF, 0x17}}, + + {x86.RAX, 1, []byte{0xFF, 0x50, 0x01}}, + {x86.RCX, 1, []byte{0xFF, 0x51, 0x01}}, + {x86.RDX, 1, []byte{0xFF, 0x52, 0x01}}, + {x86.RBX, 1, []byte{0xFF, 0x53, 0x01}}, + {x86.RSP, 1, []byte{0xFF, 0x54, 0x24, 0x01}}, + {x86.RBP, 1, []byte{0xFF, 0x55, 0x01}}, + {x86.RSI, 1, []byte{0xFF, 0x56, 0x01}}, + {x86.RDI, 1, []byte{0xFF, 0x57, 0x01}}, + {x86.R8, 1, []byte{0x41, 0xFF, 0x50, 0x01}}, + {x86.R9, 1, []byte{0x41, 0xFF, 0x51, 0x01}}, + {x86.R10, 1, []byte{0x41, 0xFF, 0x52, 0x01}}, + {x86.R11, 1, []byte{0x41, 0xFF, 0x53, 0x01}}, + {x86.R12, 1, []byte{0x41, 0xFF, 0x54, 0x24, 0x01}}, + {x86.R13, 1, []byte{0x41, 0xFF, 0x55, 0x01}}, + {x86.R14, 1, []byte{0x41, 0xFF, 0x56, 0x01}}, + {x86.R15, 1, []byte{0x41, 0xFF, 0x57, 0x01}}, + } + + for _, pattern := range usagePatterns { + t.Logf("call [%s+%d]", pattern.Register, pattern.Offset) + code := x86.CallAtMemory(nil, pattern.Register, pattern.Offset) + assert.DeepEqual(t, code, pattern.Code) + } +} diff --git a/src/x86/x86_test.go b/src/x86/x86_test.go index 02e0560..9d6b3bc 100644 --- a/src/x86/x86_test.go +++ b/src/x86/x86_test.go @@ -9,7 +9,7 @@ import ( func TestX86(t *testing.T) { assert.DeepEqual(t, x86.Call(nil, 1), []byte{0xE8, 0x01, 0x00, 0x00, 0x00}) - assert.DeepEqual(t, x86.CallAtAddress(nil, 1), []byte{0xFF, 0x15, 0x01, 0x00, 0x00, 0x00}) + assert.DeepEqual(t, x86.CallAt(nil, 1), []byte{0xFF, 0x15, 0x01, 0x00, 0x00, 0x00}) assert.DeepEqual(t, x86.ExtendRAXToRDX(nil), []byte{0x48, 0x99}) assert.DeepEqual(t, x86.MoveRegisterNumber(nil, 0, 1), []byte{0xB8, 0x01, 0x00, 0x00, 0x00}) assert.DeepEqual(t, x86.MoveRegisterNumber(nil, 1, 1), []byte{0xB9, 0x01, 0x00, 0x00, 0x00})