Implemented more arm64 instructions
This commit is contained in:
@ -4,5 +4,10 @@ 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 {
|
||||
return encodeRegisterNumberFlags(0b10, destination, source, number, false)
|
||||
return addRegisterNumber(0b10, 0, destination, source, number)
|
||||
}
|
||||
|
||||
// AddRegisterRegister adds a register to a register.
|
||||
func AddRegisterRegister(destination cpu.Register, source cpu.Register, operand cpu.Register) uint32 {
|
||||
return addRegisterRegister(0b10, 0, destination, source, operand)
|
||||
}
|
||||
|
@ -24,3 +24,20 @@ func TestAddRegisterNumber(t *testing.T) {
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddRegisterRegister(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Source cpu.Register
|
||||
Operand cpu.Register
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, arm.X1, arm.X2, 0x8B020020},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("add %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
|
||||
code := arm.AddRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
@ -4,5 +4,10 @@ import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// CompareRegisterNumber is an alias for a subtraction that updates the conditional flags and discards the result.
|
||||
func CompareRegisterNumber(register cpu.Register, number int) uint32 {
|
||||
return encodeRegisterNumberFlags(0b11, 0b11111, register, number, true)
|
||||
return addRegisterNumber(0b11, 1, ZR, register, number)
|
||||
}
|
||||
|
||||
// CompareRegisterRegister is an alias for a subtraction that updates the conditional flags and discards the result.
|
||||
func CompareRegisterRegister(reg1 cpu.Register, reg2 cpu.Register) uint32 {
|
||||
return addRegisterRegister(0b11, 1, ZR, reg1, reg2)
|
||||
}
|
||||
|
8
src/arm/Div.go
Normal file
8
src/arm/Div.go
Normal file
@ -0,0 +1,8 @@
|
||||
package arm
|
||||
|
||||
import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// DivSigned divides source by operand and stores the value in the destination.
|
||||
func DivSigned(destination cpu.Register, source cpu.Register, operand cpu.Register) uint32 {
|
||||
return 0b10011010110<<21 | 0b000011<<10 | reg3(destination, source, operand)
|
||||
}
|
26
src/arm/Div_test.go
Normal file
26
src/arm/Div_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 TestDivSigned(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Source cpu.Register
|
||||
Operand cpu.Register
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, arm.X1, arm.X2, 0x9AC20C20},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("sdiv %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
|
||||
code := arm.DivSigned(pattern.Destination, pattern.Source, pattern.Operand)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
@ -4,8 +4,7 @@ import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// LoadRegister loads from memory into a register.
|
||||
func LoadRegister(destination cpu.Register, base cpu.Register, offset int, length byte) uint32 {
|
||||
offset &= 0b1_1111_1111
|
||||
common := 1<<22 | uint32(offset)<<12 | uint32(base)<<5 | uint32(destination)
|
||||
common := 1<<22 | memory(destination, base, offset)
|
||||
|
||||
switch length {
|
||||
case 1:
|
||||
|
@ -6,7 +6,5 @@ import "git.urbach.dev/cli/q/src/cpu"
|
||||
// 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 &= 0b111_1111
|
||||
return 0b1010100011<<22 | (uint32(offset) << 15) | (uint32(reg2) << 10) | (uint32(base) << 5) | uint32(reg1)
|
||||
return 0b1010100011<<22 | pair(reg1, reg2, base, offset/8)
|
||||
}
|
||||
|
@ -10,7 +10,7 @@ func MoveRegisterRegister(destination cpu.Register, source cpu.Register) uint32
|
||||
return AddRegisterNumber(destination, source, 0)
|
||||
}
|
||||
|
||||
return 0b10101010<<24 | uint32(source)<<16 | 0b11111<<5 | uint32(destination)
|
||||
return 0b10101010<<24 | reg3(destination, ZR, source)
|
||||
}
|
||||
|
||||
// MoveRegisterNumber moves an integer into the given register.
|
||||
@ -30,5 +30,5 @@ func MoveZero(destination cpu.Register, halfword int, number uint16) uint32 {
|
||||
|
||||
// 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)
|
||||
return 1<<31 | opCode<<29 | 0b100101<<23 | uint32(halfword<<21) | uint32(number<<5) | uint32(destination)
|
||||
}
|
||||
|
13
src/arm/Mul.go
Normal file
13
src/arm/Mul.go
Normal file
@ -0,0 +1,13 @@
|
||||
package arm
|
||||
|
||||
import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// MulRegisterRegister multiplies `multiplicand` with `multiplier` and saves the result in `destination`
|
||||
func MulRegisterRegister(destination cpu.Register, multiplicand cpu.Register, multiplier cpu.Register) uint32 {
|
||||
return 0b10011011000<<21 | reg4(destination, multiplicand, multiplier, ZR)
|
||||
}
|
||||
|
||||
// MultiplySubtract multiplies `multiplicand` with `multiplier`, subtracts `minuend` and saves the result in `destination`.
|
||||
func MultiplySubtract(destination cpu.Register, multiplicand cpu.Register, multiplier cpu.Register, minuend cpu.Register) uint32 {
|
||||
return 0b10011011000<<21 | 1<<15 | reg4(destination, multiplicand, multiplier, minuend)
|
||||
}
|
44
src/arm/Mul_test.go
Normal file
44
src/arm/Mul_test.go
Normal file
@ -0,0 +1,44 @@
|
||||
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 TestMulRegisterRegister(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Source cpu.Register
|
||||
Operand cpu.Register
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, arm.X1, arm.X2, 0x9B027C20},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("mul %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
|
||||
code := arm.MulRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestMultiplySubtract(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Source cpu.Register
|
||||
Operand cpu.Register
|
||||
Extra cpu.Register
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, arm.X1, arm.X2, arm.X3, 0x9B028C20},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("msub %s, %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand, pattern.Extra)
|
||||
code := arm.MultiplySubtract(pattern.Destination, pattern.Source, pattern.Operand, pattern.Extra)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
@ -32,9 +32,10 @@ const (
|
||||
X26
|
||||
X27
|
||||
X28
|
||||
FP // Frame pointer
|
||||
LR // Link register
|
||||
SP // Stack pointer
|
||||
FP // Frame pointer
|
||||
LR // Link register
|
||||
SP // Stack pointer
|
||||
ZR = SP // Zero register uses the same numerical value as SP
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -6,8 +6,7 @@ import (
|
||||
|
||||
// StoreRegister writes the contents of the register to a memory address.
|
||||
func StoreRegister(source cpu.Register, base cpu.Register, offset int, length byte) uint32 {
|
||||
offset &= 0b1_1111_1111
|
||||
common := uint32(offset)<<12 | uint32(base)<<5 | uint32(source)
|
||||
common := memory(source, base, offset)
|
||||
|
||||
switch length {
|
||||
case 1:
|
||||
|
@ -8,7 +8,5 @@ import (
|
||||
// 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 &= 0b111_1111
|
||||
return 0b1010100110<<22 | uint32(offset)<<15 | uint32(reg2)<<10 | uint32(base)<<5 | uint32(reg1)
|
||||
return 0b1010100110<<22 | pair(reg1, reg2, base, offset/8)
|
||||
}
|
||||
|
@ -4,5 +4,10 @@ import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// SubRegisterNumber subtracts a number from the given register.
|
||||
func SubRegisterNumber(destination cpu.Register, source cpu.Register, number int) uint32 {
|
||||
return encodeRegisterNumberFlags(0b11, destination, source, number, false)
|
||||
return addRegisterNumber(0b11, 0, destination, source, number)
|
||||
}
|
||||
|
||||
// SubRegisterRegister subtracts a register from a register.
|
||||
func SubRegisterRegister(destination cpu.Register, source cpu.Register, operand cpu.Register) uint32 {
|
||||
return addRegisterRegister(0b11, 0, destination, source, operand)
|
||||
}
|
||||
|
@ -25,3 +25,20 @@ func TestSubRegisterNumber(t *testing.T) {
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSubRegisterRegister(t *testing.T) {
|
||||
usagePatterns := []struct {
|
||||
Destination cpu.Register
|
||||
Source cpu.Register
|
||||
Operand cpu.Register
|
||||
Code uint32
|
||||
}{
|
||||
{arm.X0, arm.X1, arm.X2, 0xCB020020},
|
||||
}
|
||||
|
||||
for _, pattern := range usagePatterns {
|
||||
t.Logf("sub %s, %s, %s", pattern.Destination, pattern.Source, pattern.Operand)
|
||||
code := arm.SubRegisterRegister(pattern.Destination, pattern.Source, pattern.Operand)
|
||||
assert.DeepEqual(t, code, pattern.Code)
|
||||
}
|
||||
}
|
||||
|
@ -2,15 +2,35 @@ package arm
|
||||
|
||||
import "git.urbach.dev/cli/q/src/cpu"
|
||||
|
||||
// encodeRegisterNumberFlags performs addition or subtraction on the given register
|
||||
// addRegisterNumber performs addition or subtraction on the given register
|
||||
// and optionally updates the condition flags based on the result.
|
||||
func encodeRegisterNumberFlags(op uint32, destination cpu.Register, source cpu.Register, number int, flags bool) uint32 {
|
||||
func addRegisterNumber(op uint32, flags uint32, destination cpu.Register, source cpu.Register, number int) uint32 {
|
||||
number &= 0b1111_1111_1111
|
||||
common := op<<30 | 0b100010<<23 | (uint32(number) << 10) | (uint32(source) << 5) | uint32(destination)
|
||||
|
||||
if flags {
|
||||
return 1<<29 | common
|
||||
}
|
||||
|
||||
return common
|
||||
return op<<30 | flags<<29 | 0b100010<<23 | (uint32(number) << 10) | (uint32(source) << 5) | uint32(destination)
|
||||
}
|
||||
|
||||
// addRegisterRegister performs addition or subtraction on the given registers
|
||||
// and optionally updates the condition flags based on the result.
|
||||
func addRegisterRegister(op uint32, flags uint32, destination cpu.Register, source cpu.Register, operand cpu.Register) uint32 {
|
||||
return op<<30 | flags<<29 | 0b01011000<<21 | reg3(destination, source, operand)
|
||||
}
|
||||
|
||||
// memory encodes an instruction with a register, a base register and an offset.
|
||||
func memory(destination cpu.Register, base cpu.Register, offset int) uint32 {
|
||||
return uint32(offset&0b1_1111_1111)<<12 | uint32(base)<<5 | uint32(destination)
|
||||
}
|
||||
|
||||
// pair encodes an instruction using a register pair with memory.
|
||||
func pair(reg1 cpu.Register, reg2 cpu.Register, base cpu.Register, offset int) uint32 {
|
||||
return uint32(offset&0b111_1111)<<15 | uint32(reg2)<<10 | uint32(base)<<5 | uint32(reg1)
|
||||
}
|
||||
|
||||
// reg3 encodes an instruction with 3 registers.
|
||||
func reg3(d cpu.Register, n cpu.Register, m cpu.Register) uint32 {
|
||||
return uint32(m)<<16 | uint32(n)<<5 | uint32(d)
|
||||
}
|
||||
|
||||
// reg4 encodes an instruction with 4 registers.
|
||||
func reg4(d cpu.Register, n cpu.Register, m cpu.Register, a cpu.Register) uint32 {
|
||||
return uint32(m)<<16 | uint32(a)<<10 | uint32(n)<<5 | uint32(d)
|
||||
}
|
||||
|
@ -105,7 +105,8 @@ func (c *compiler) compileARM(x asm.Instruction) {
|
||||
operand := c.assembler.Param.RegisterNumber[x.Index]
|
||||
c.append(arm.AddRegisterNumber(operand.Register, operand.Register, operand.Number))
|
||||
case asm.TypeRegisterRegister:
|
||||
panic("not implemented")
|
||||
operand := c.assembler.Param.RegisterRegister[x.Index]
|
||||
c.append(arm.AddRegisterRegister(operand.Destination, operand.Destination, operand.Source))
|
||||
}
|
||||
|
||||
case asm.SUB:
|
||||
@ -114,7 +115,8 @@ func (c *compiler) compileARM(x asm.Instruction) {
|
||||
operand := c.assembler.Param.RegisterNumber[x.Index]
|
||||
c.append(arm.SubRegisterNumber(operand.Register, operand.Register, operand.Number))
|
||||
case asm.TypeRegisterRegister:
|
||||
panic("not implemented")
|
||||
operand := c.assembler.Param.RegisterRegister[x.Index]
|
||||
c.append(arm.SubRegisterRegister(operand.Destination, operand.Destination, operand.Source))
|
||||
}
|
||||
|
||||
case asm.COMPARE:
|
||||
@ -123,6 +125,36 @@ func (c *compiler) compileARM(x asm.Instruction) {
|
||||
operand := c.assembler.Param.RegisterNumber[x.Index]
|
||||
c.append(arm.CompareRegisterNumber(operand.Register, operand.Number))
|
||||
case asm.TypeRegisterRegister:
|
||||
operand := c.assembler.Param.RegisterRegister[x.Index]
|
||||
c.append(arm.CompareRegisterRegister(operand.Destination, operand.Source))
|
||||
}
|
||||
|
||||
case asm.DIV:
|
||||
switch x.Type {
|
||||
case asm.TypeRegisterRegister:
|
||||
operand := c.assembler.Param.RegisterRegister[x.Index]
|
||||
c.append(arm.DivSigned(operand.Destination, operand.Destination, operand.Source))
|
||||
case asm.TypeRegisterNumber:
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
case asm.MUL:
|
||||
switch x.Type {
|
||||
case asm.TypeRegisterRegister:
|
||||
operand := c.assembler.Param.RegisterRegister[x.Index]
|
||||
c.append(arm.MulRegisterRegister(operand.Destination, operand.Destination, operand.Source))
|
||||
case asm.TypeRegisterNumber:
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
case asm.MODULO:
|
||||
switch x.Type {
|
||||
case asm.TypeRegisterRegister:
|
||||
operand := c.assembler.Param.RegisterRegister[x.Index]
|
||||
quotient := arm.X28
|
||||
c.append(arm.DivSigned(quotient, operand.Destination, operand.Source))
|
||||
c.append(arm.MultiplySubtract(operand.Destination, quotient, operand.Source, operand.Destination))
|
||||
case asm.TypeRegisterNumber:
|
||||
panic("not implemented")
|
||||
}
|
||||
|
||||
|
@ -51,6 +51,7 @@ func (c *compiler) compileX86(x asm.Instruction) {
|
||||
switch x.Type {
|
||||
case asm.TypeRegisterRegister:
|
||||
operands := c.assembler.Param.RegisterRegister[x.Index]
|
||||
|
||||
if operands.Destination != x86.RAX {
|
||||
c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination)
|
||||
}
|
||||
@ -67,6 +68,7 @@ func (c *compiler) compileX86(x asm.Instruction) {
|
||||
switch x.Type {
|
||||
case asm.TypeRegisterRegister:
|
||||
operands := c.assembler.Param.RegisterRegister[x.Index]
|
||||
|
||||
if operands.Destination != x86.RAX {
|
||||
c.code = x86.MoveRegisterRegister(c.code, x86.RAX, operands.Destination)
|
||||
}
|
||||
|
Reference in New Issue
Block a user