Added basic support for arm64
This commit is contained in:
10
src/arm/Call.go
Normal file
10
src/arm/Call.go
Normal file
@ -0,0 +1,10 @@
|
||||
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, uint32(0b100101<<26)|offset)
|
||||
}
|
29
src/arm/Move.go
Normal file
29
src/arm/Move.go
Normal file
@ -0,0 +1,29 @@
|
||||
package arm
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"git.urbach.dev/cli/q/src/cpu"
|
||||
)
|
||||
|
||||
// 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))
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
||||
|
||||
// mov encodes a generic move instruction.
|
||||
func mov(opCode uint32, halfword int, number uint16, destination cpu.Register) uint32 {
|
||||
return (1 << 31) | (opCode << 29) | (0b100101 << 23) | uint32(halfword<<21) | uint32(number<<5) | uint32(destination)
|
||||
}
|
43
src/arm/Move_test.go
Normal file
43
src/arm/Move_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
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 TestMoveKeep(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Register cpu.Register
|
||||
Number uint16
|
||||
Code []byte
|
||||
}{
|
||||
{arm.X0, 0, []byte{0x00, 0x00, 0x80, 0xF2}},
|
||||
{arm.X0, 1, []byte{0x20, 0x00, 0x80, 0xF2}},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("movk %s, %x", pattern.Register, pattern.Number)
|
||||
code := arm.MoveKeep(nil, pattern.Register, 0, pattern.Number)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMoveZero(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Register cpu.Register
|
||||
Number uint16
|
||||
Code []byte
|
||||
}{
|
||||
{arm.X0, 0, []byte{0x00, 0x00, 0x80, 0xD2}},
|
||||
{arm.X0, 1, []byte{0x20, 0x00, 0x80, 0xD2}},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("movz %s, %x", pattern.Register, pattern.Number)
|
||||
code := arm.MoveZero(nil, pattern.Register, 0, pattern.Number)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
6
src/arm/Nop.go
Normal file
6
src/arm/Nop.go
Normal file
@ -0,0 +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)
|
||||
}
|
@ -38,7 +38,20 @@ const (
|
||||
)
|
||||
|
||||
var (
|
||||
GeneralRegisters = []cpu.Register{X9, X10, X11, X12, X13, X14, X15, X16, X17, X18, X19, X20, X21, X22, X23, X24, X25, X26, X27, X28}
|
||||
InputRegisters = SyscallInputRegisters
|
||||
OutputRegisters = SyscallInputRegisters
|
||||
SyscallInputRegisters = []cpu.Register{X8, X0, X1, X2, X3, X4, X5}
|
||||
SyscallOutputRegisters = []cpu.Register{X0, X1}
|
||||
WindowsInputRegisters = []cpu.Register{X0, X1, X2, X3, X4, X5, X6, X7}
|
||||
WindowsOutputRegisters = []cpu.Register{X0, X1}
|
||||
|
||||
CPU = cpu.CPU{
|
||||
General: GeneralRegisters,
|
||||
Input: InputRegisters,
|
||||
Output: OutputRegisters,
|
||||
SyscallInput: SyscallInputRegisters,
|
||||
SyscallOutput: SyscallOutputRegisters,
|
||||
NumRegisters: 32,
|
||||
}
|
||||
)
|
||||
|
6
src/arm/Return.go
Normal file
6
src/arm/Return.go
Normal file
@ -0,0 +1,6 @@
|
||||
package arm
|
||||
|
||||
// Return transfers program control to the caller.
|
||||
func Return(code []byte) []byte {
|
||||
return append(code, 0xC0, 0x03, 0x5F, 0xD6)
|
||||
}
|
6
src/arm/Syscall.go
Normal file
6
src/arm/Syscall.go
Normal file
@ -0,0 +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)
|
||||
}
|
16
src/arm/arm_test.go
Normal file
16
src/arm/arm_test.go
Normal file
@ -0,0 +1,16 @@
|
||||
package arm_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"git.urbach.dev/cli/q/src/arm"
|
||||
"git.urbach.dev/go/assert"
|
||||
)
|
||||
|
||||
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})
|
||||
}
|
Reference in New Issue
Block a user