Implemented arm64 instructions: ldp and stp

This commit is contained in:
2025-03-12 12:29:55 +01:00
parent 03c8dfa34c
commit 450e634d79
8 changed files with 100 additions and 4 deletions

9
src/arm/Add.go Normal file
View File

@ -0,0 +1,9 @@
package arm
import "git.urbach.dev/cli/q/src/cpu"
// AddRegisterNumber adds a number to a register
func AddRegisterNumber(destination cpu.Register, source cpu.Register, number int) uint32 {
number &= 0xFFF
return 0b100100010<<23 | (uint32(number) << 10) | (uint32(source) << 5) | uint32(destination)
}

12
src/arm/LoadPair.go Normal file
View File

@ -0,0 +1,12 @@
package arm
import "git.urbach.dev/cli/q/src/cpu"
// LoadPair calculates an address from a base register value and an immediate offset,
// loads two 64-bit doublewords from memory, and writes them to two registers.
// This is the post-index version of the instruction so the offset is applied to the base register after the memory access.
func LoadPair(reg1 cpu.Register, reg2 cpu.Register, base cpu.Register, offset int) uint32 {
offset /= 8
offset &= 0b1111111
return 0b1010100011<<22 | (uint32(offset) << 15) | (uint32(reg2) << 10) | (uint32(base) << 5) | uint32(reg1)
}

28
src/arm/LoadPair_test.go Normal file
View File

@ -0,0 +1,28 @@
package arm_test
import (
"testing"
"git.urbach.dev/cli/q/src/arm"
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/go/assert"
)
func TestLoadPair(t *testing.T) {
usagePatterns := []struct {
Reg1 cpu.Register
Reg2 cpu.Register
Base cpu.Register
Offset int
Code uint32
}{
{arm.FP, arm.LR, arm.SP, 32, 0xA8C27BFD},
{arm.FP, arm.LR, arm.SP, 16, 0xA8C17BFD},
}
for _, pattern := range usagePatterns {
t.Logf("ldp %s, %s, [%s], #%d", pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
code := arm.LoadPair(pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
assert.DeepEqual(t, code, pattern.Code)
}
}

View File

@ -6,6 +6,10 @@ import (
// MoveRegisterRegister copies a register to another register.
func MoveRegisterRegister(destination cpu.Register, source cpu.Register) uint32 {
if source == SP || destination == SP {
return AddRegisterNumber(destination, source, 0)
}
return 0b10101010<<24 | uint32(source)<<16 | 0b11111<<5 | uint32(destination)
}

View File

@ -16,6 +16,8 @@ func TestMoveRegisterRegister(t *testing.T) {
}{
{arm.X0, arm.X1, 0xAA0103E0},
{arm.X1, arm.X0, 0xAA0003E1},
{arm.FP, arm.SP, 0x910003FD},
{arm.SP, arm.FP, 0x910003BF},
}
for _, pattern := range usagePatterns {

14
src/arm/StorePair.go Normal file
View File

@ -0,0 +1,14 @@
package arm
import (
"git.urbach.dev/cli/q/src/cpu"
)
// StorePair calculates an address from a base register value and an immediate offset multiplied by 8,
// and stores the values of two registers to the calculated address.
// This is the pre-index version of the instruction so the offset is applied to the base register before the memory access.
func StorePair(reg1 cpu.Register, reg2 cpu.Register, base cpu.Register, offset int) uint32 {
offset /= 8
offset &= 0b1111111
return 0b1010100110<<22 | uint32(offset)<<15 | uint32(reg2)<<10 | uint32(base)<<5 | uint32(reg1)
}

28
src/arm/StorePair_test.go Normal file
View File

@ -0,0 +1,28 @@
package arm_test
import (
"testing"
"git.urbach.dev/cli/q/src/arm"
"git.urbach.dev/cli/q/src/cpu"
"git.urbach.dev/go/assert"
)
func TestStorePair(t *testing.T) {
usagePatterns := []struct {
Reg1 cpu.Register
Reg2 cpu.Register
Base cpu.Register
Offset int
Code uint32
}{
{arm.FP, arm.LR, arm.SP, -32, 0xA9BE7BFD},
{arm.FP, arm.LR, arm.SP, -16, 0xA9BF7BFD},
}
for _, pattern := range usagePatterns {
t.Logf("stp %s, %s, [%s, #%d]!", pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
code := arm.StorePair(pattern.Reg1, pattern.Reg2, pattern.Base, pattern.Offset)
assert.DeepEqual(t, code, pattern.Code)
}
}

View File

@ -42,8 +42,8 @@ func (c *compiler) compileARM(x asm.Instruction) {
case asm.LABEL:
label := c.assembler.Param.Label[x.Index]
c.codeLabels[label.Name] = Address(len(c.code))
c.append(0xa9be7bfd)
c.append(0x910003fd)
c.append(arm.StorePair(arm.FP, arm.LR, arm.SP, -16))
c.append(arm.MoveRegisterRegister(arm.FP, arm.SP))
case asm.LOAD:
switch x.Type {
@ -96,8 +96,7 @@ func (c *compiler) compileARM(x asm.Instruction) {
}
case asm.RETURN:
c.append(0xa8c27bfd)
c.append(0xd65f03c0)
c.append(arm.LoadPair(arm.FP, arm.LR, arm.SP, 16))
c.append(arm.Return())
case asm.SYSCALL: