diff --git a/src/arm/Call.go b/src/arm/Call.go index d311431..98765e9 100644 --- a/src/arm/Call.go +++ b/src/arm/Call.go @@ -1,8 +1,15 @@ package arm +import "git.urbach.dev/cli/q/src/cpu" + // Call branches to a PC-relative offset, setting the register X30 to PC+4. // The offset starts from the address of this instruction and is encoded as "imm26" times 4. // This instruction is also known as BL (branch with link). func Call(offset int) uint32 { return uint32(0b100101<<26) | uint32(offset&mask26) +} + +// Calls a function whose address is stored in the given register. +func CallRegister(register cpu.Register) uint32 { + return uint32(0b1101011000111111<<16) | uint32(register)<<5 } \ No newline at end of file diff --git a/src/arm/Call_test.go b/src/arm/Call_test.go index 206f90e..7ab06c5 100644 --- a/src/arm/Call_test.go +++ b/src/arm/Call_test.go @@ -4,6 +4,7 @@ import ( "testing" "git.urbach.dev/cli/q/src/arm" + "git.urbach.dev/cli/q/src/cpu" "git.urbach.dev/go/assert" ) @@ -22,4 +23,21 @@ func TestCall(t *testing.T) { code := arm.Call(pattern.Offset) assert.Equal(t, code, pattern.Code) } +} + +func TestCallRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code uint32 + }{ + {arm.X0, 0xD63F0000}, + {arm.X1, 0xD63F0020}, + {arm.X2, 0xD63F0040}, + } + + for _, pattern := range usagePatterns { + t.Logf("blr %d", pattern.Register) + code := arm.CallRegister(pattern.Register) + assert.Equal(t, code, pattern.Code) + } } \ No newline at end of file diff --git a/src/asm/compilerARM.go b/src/asm/compilerARM.go index 297c06c..d743afd 100644 --- a/src/asm/compilerARM.go +++ b/src/asm/compilerARM.go @@ -2,6 +2,7 @@ package asm import ( "encoding/binary" + "fmt" "git.urbach.dev/cli/q/src/arm" ) @@ -33,11 +34,24 @@ func (c *compilerARM) Compile(instr Instruction) { binary.LittleEndian.PutUint32(c.code[start:start+4], arm.Call(offset)) }) case *CallExtern: - panic("not implemented") + start := len(c.code) + c.append(arm.LoadAddress(arm.X0, 0)) + c.append(arm.LoadRegister(arm.X0, arm.X0, 0, 8)) + c.append(arm.CallRegister(arm.X0)) + + c.Defer(func() { + index := c.libraries.Index(instr.Library, instr.Function) + + if index == -1 { + panic(fmt.Sprintf("unknown extern function '%s' in library '%s'", instr.Function, instr.Library)) + } + + address := c.importsStart + index*8 + offset := address - start + binary.LittleEndian.PutUint32(c.code[start:start+4], arm.LoadAddress(arm.X0, offset)) + }) case *CallExternStart: - panic("not implemented") case *CallExternEnd: - panic("not implemented") case *Jump: start := len(c.code) c.append(arm.Jump(0))