Implemented more arm64 instructions
This commit is contained in:
@ -1,15 +1,8 @@
|
||||
package arm
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// 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(code []byte, offset uint32) []byte {
|
||||
return binary.LittleEndian.AppendUint32(code, EncodeCall(offset))
|
||||
}
|
||||
|
||||
// EncodeCall returns the raw encoding of a call with the given offset.
|
||||
func EncodeCall(offset uint32) uint32 {
|
||||
func Call(offset uint32) uint32 {
|
||||
return uint32(0b100101<<26) | offset
|
||||
}
|
||||
|
13
src/arm/Load.go
Normal file
13
src/arm/Load.go
Normal file
@ -0,0 +1,13 @@
|
||||
package arm
|
||||
|
||||
import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// LoadRegister loads from memory into a register.
|
||||
func LoadRegister(destination cpu.Register, base cpu.Register, offset int16, length byte) uint32 {
|
||||
if offset < 0 {
|
||||
offset &= 0xFF
|
||||
offset |= 1 << 8
|
||||
}
|
||||
|
||||
return 0b11111000010<<21 | uint32(offset)<<12 | uint32(base)<<5 | uint32(destination)
|
||||
}
|
10
src/arm/LoadAddress.go
Normal file
10
src/arm/LoadAddress.go
Normal file
@ -0,0 +1,10 @@
|
||||
package arm
|
||||
|
||||
import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// LoadAddress calculates the address with the PC-relative offset and writes the result to the destination register.
|
||||
func LoadAddress(destination cpu.Register, offset int) uint32 {
|
||||
hi := uint32(offset) >> 2
|
||||
lo := uint32(offset) & 0b11
|
||||
return lo<<29 | 0b10000<<24 | hi<<5 | uint32(destination)
|
||||
}
|
26
src/arm/LoadAddress_test.go
Normal file
26
src/arm/LoadAddress_test.go
Normal file
@ -0,0 +1,26 @@
|
||||
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 TestLoadAddress(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Number int
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, 56, 0x100001C0},
|
||||
{arm.X1, 80, 0x10000281},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("adr %s, %d", pattern.Destination, pattern.Number)
|
||||
code := arm.LoadAddress(pattern.Destination, pattern.Number)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
31
src/arm/Load_test.go
Normal file
31
src/arm/Load_test.go
Normal file
@ -0,0 +1,31 @@
|
||||
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 TestLoadRegister(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Base cpu.Register
|
||||
Offset int16
|
||||
Length byte
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X2, arm.X1, -8, 8, 0xF85F8022},
|
||||
{arm.X2, arm.X1, 0, 8, 0xF8400022},
|
||||
{arm.X2, arm.X1, 8, 8, 0xF8408022},
|
||||
{arm.X2, arm.X1, -256, 8, 0xF8500022},
|
||||
{arm.X2, arm.X1, 255, 8, 0xF84FF022},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("ldur %s, [%s, %d] %db", pattern.Destination, pattern.Base, pattern.Offset, pattern.Length)
|
||||
code := arm.LoadRegister(pattern.Destination, pattern.Base, pattern.Offset, pattern.Length)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
@ -1,26 +1,27 @@
|
||||
package arm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
)
|
||||
|
||||
// MoveRegisterRegister copies a register to another register.
|
||||
func MoveRegisterRegister(destination cpu.Register, source cpu.Register) uint32 {
|
||||
return 0b10101010<<24 | uint32(source)<<16 | 0b11111<<5 | uint32(destination)
|
||||
}
|
||||
|
||||
// MoveRegisterNumber moves an integer into the given register.
|
||||
func MoveRegisterNumber(code []byte, destination cpu.Register, number int) []byte {
|
||||
return MoveZero(code, destination, 0, uint16(number))
|
||||
func MoveRegisterNumber(destination cpu.Register, number int) uint32 {
|
||||
return MoveZero(destination, 0, uint16(number))
|
||||
}
|
||||
|
||||
// MoveKeep moves a 16-bit integer into the given register and keeps all other bits.
|
||||
func MoveKeep(code []byte, destination cpu.Register, halfword int, number uint16) []byte {
|
||||
x := mov(0b11, halfword, number, destination)
|
||||
return binary.LittleEndian.AppendUint32(code, x)
|
||||
func MoveKeep(destination cpu.Register, halfword int, number uint16) uint32 {
|
||||
return mov(0b11, halfword, number, destination)
|
||||
}
|
||||
|
||||
// MoveZero moves a 16-bit integer into the given register and clears all other bits to zero.
|
||||
func MoveZero(code []byte, destination cpu.Register, halfword int, number uint16) []byte {
|
||||
x := mov(0b10, halfword, number, destination)
|
||||
return binary.LittleEndian.AppendUint32(code, x)
|
||||
func MoveZero(destination cpu.Register, halfword int, number uint16) uint32 {
|
||||
return mov(0b10, halfword, number, destination)
|
||||
}
|
||||
|
||||
// mov encodes a generic move instruction.
|
||||
|
@ -8,19 +8,36 @@ import (
|
||||
"git.urbach.dev/go/assert"
|
||||
)
|
||||
|
||||
func TestMoveRegisterRegister(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Source cpu.Register
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, arm.X1, 0xAA0103E0},
|
||||
{arm.X1, arm.X0, 0xAA0003E1},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("mov %s, %s", pattern.Destination, pattern.Source)
|
||||
code := arm.MoveRegisterRegister(pattern.Destination, pattern.Source)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMoveKeep(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Register cpu.Register
|
||||
Number uint16
|
||||
Code []byte
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, 0, []byte{0x00, 0x00, 0x80, 0xF2}},
|
||||
{arm.X0, 1, []byte{0x20, 0x00, 0x80, 0xF2}},
|
||||
{arm.X0, 0, 0xF2800000},
|
||||
{arm.X0, 1, 0xF2800020},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("movk %s, %x", pattern.Register, pattern.Number)
|
||||
code := arm.MoveKeep(nil, pattern.Register, 0, pattern.Number)
|
||||
code := arm.MoveKeep(pattern.Register, 0, pattern.Number)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
@ -29,15 +46,15 @@ func TestMoveZero(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Register cpu.Register
|
||||
Number uint16
|
||||
Code []byte
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, 0, []byte{0x00, 0x00, 0x80, 0xD2}},
|
||||
{arm.X0, 1, []byte{0x20, 0x00, 0x80, 0xD2}},
|
||||
{arm.X0, 0, 0xD2800000},
|
||||
{arm.X0, 1, 0xD2800020},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("movz %s, %x", pattern.Register, pattern.Number)
|
||||
code := arm.MoveZero(nil, pattern.Register, 0, pattern.Number)
|
||||
code := arm.MoveZero(pattern.Register, 0, pattern.Number)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package arm
|
||||
|
||||
// Nop does nothing. This can be used for alignment purposes.
|
||||
func Nop(code []byte) []byte {
|
||||
return append(code, 0x1F, 0x20, 0x03, 0xD5)
|
||||
func Nop() uint32 {
|
||||
return 0xD503201F
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package arm
|
||||
|
||||
// Return transfers program control to the caller.
|
||||
func Return(code []byte) []byte {
|
||||
return append(code, 0xC0, 0x03, 0x5F, 0xD6)
|
||||
func Return() uint32 {
|
||||
return 0xD65F03C0
|
||||
}
|
||||
|
@ -1,6 +1,6 @@
|
||||
package arm
|
||||
|
||||
// Syscall is the primary way to communicate with the OS kernel.
|
||||
func Syscall(code []byte) []byte {
|
||||
return append(code, 0x01, 0x00, 0x00, 0xD4)
|
||||
func Syscall() uint32 {
|
||||
return 0xD4000001
|
||||
}
|
||||
|
@ -8,9 +8,9 @@ import (
|
||||
)
|
||||
|
||||
func TestARM(t *testing.T) {
|
||||
assert.DeepEqual(t, arm.Call(nil, 0), []byte{0x00, 0x00, 0x00, 0x94})
|
||||
assert.DeepEqual(t, arm.MoveRegisterNumber(nil, arm.X0, 42), arm.MoveZero(nil, arm.X0, 0, 42))
|
||||
assert.DeepEqual(t, arm.Nop(nil), []byte{0x1F, 0x20, 0x03, 0xD5})
|
||||
assert.DeepEqual(t, arm.Return(nil), []byte{0xC0, 0x03, 0x5F, 0xD6})
|
||||
assert.DeepEqual(t, arm.Syscall(nil), []byte{0x01, 0x00, 0x00, 0xD4})
|
||||
assert.DeepEqual(t, arm.Call(0), 0x94000000)
|
||||
assert.DeepEqual(t, arm.MoveRegisterNumber(arm.X0, 42), arm.MoveZero(arm.X0, 0, 42))
|
||||
assert.DeepEqual(t, arm.Nop(), 0xD503201F)
|
||||
assert.DeepEqual(t, arm.Return(), 0xD65F03C0)
|
||||
assert.DeepEqual(t, arm.Syscall(), 0xD4000001)
|
||||
}
|
||||
|
Reference in New Issue
Block a user