Implemented calls using memory addresses

This commit is contained in:
Eduard Urbach 2025-03-03 00:53:41 +01:00
parent ea233d789d
commit 08660ad845
Signed by: akyoto
GPG Key ID: C874F672B1AF20C0
8 changed files with 114 additions and 10 deletions

View File

@ -35,9 +35,27 @@ func (mem *Memory) Format(custom string) string {
tmp.WriteString(strconv.Itoa(int(mem.Offset))) tmp.WriteString(strconv.Itoa(int(mem.Offset)))
} }
tmp.WriteString("], ") tmp.WriteString("]")
if custom != "" {
tmp.WriteString(", ")
tmp.WriteString(custom) tmp.WriteString(custom)
}
tmp.WriteString(", ") tmp.WriteString(", ")
tmp.WriteString(strconv.Itoa(int(mem.Length))) tmp.WriteString(strconv.Itoa(int(mem.Length)))
return tmp.String() 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,
})
}

View File

@ -32,5 +32,8 @@ func (c *compiler) call(x asm.Instruction) {
case *asm.Register: case *asm.Register:
c.code = x86.CallRegister(c.code, data.Register) c.code = x86.CallRegister(c.code, data.Register)
case *asm.Memory:
c.code = x86.CallAtMemory(c.code, data.Base, data.Offset)
} }
} }

View File

@ -9,7 +9,7 @@ import (
func (c *compiler) dllCall(x asm.Instruction) { func (c *compiler) dllCall(x asm.Instruction) {
size := 4 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 position := len(c.code) - size
label := x.Data.(*asm.Label) label := x.Data.(*asm.Label)

View File

@ -75,10 +75,7 @@ func (f *Function) CompileCall(root *expression.Expression) ([]types.Type, error
f.Register(asm.CALL, value.Register) f.Register(asm.CALL, value.Register)
case *eval.Memory: case *eval.Memory:
tmp := f.NewRegister() f.Memory(asm.CALL, value.Memory)
f.MemoryRegister(asm.LOAD, value.Memory, tmp)
f.Register(asm.CALL, tmp)
f.FreeRegister(tmp)
} }
return nil, nil return nil, nil

8
src/register/Memory.go Normal file
View File

@ -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()
}

View File

@ -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. // 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( return append(
code, code,
0xFF, 0xFF,
@ -47,3 +47,33 @@ func CallAtAddress(code []byte, address uint32) []byte {
byte(address>>24), 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
}

View File

@ -37,3 +37,51 @@ func TestCallRegister(t *testing.T) {
assert.DeepEqual(t, code, pattern.Code) 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)
}
}

View File

@ -9,7 +9,7 @@ import (
func TestX86(t *testing.T) { func TestX86(t *testing.T) {
assert.DeepEqual(t, x86.Call(nil, 1), []byte{0xE8, 0x01, 0x00, 0x00, 0x00}) 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.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, 0, 1), []byte{0xB8, 0x01, 0x00, 0x00, 0x00})
assert.DeepEqual(t, x86.MoveRegisterNumber(nil, 1, 1), []byte{0xB9, 0x01, 0x00, 0x00, 0x00}) assert.DeepEqual(t, x86.MoveRegisterNumber(nil, 1, 1), []byte{0xB9, 0x01, 0x00, 0x00, 0x00})