diff --git a/src/arm/Add.go b/src/arm/Add.go index dd868e9..be6d662 100644 --- a/src/arm/Add.go +++ b/src/arm/Add.go @@ -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) } diff --git a/src/arm/Add_test.go b/src/arm/Add_test.go index 9b403b3..1051b06 100644 --- a/src/arm/Add_test.go +++ b/src/arm/Add_test.go @@ -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) + } +} diff --git a/src/arm/Compare.go b/src/arm/Compare.go index 5fc07ec..5bf7d1a 100644 --- a/src/arm/Compare.go +++ b/src/arm/Compare.go @@ -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) } diff --git a/src/arm/Div.go b/src/arm/Div.go new file mode 100644 index 0000000..c885e00 --- /dev/null +++ b/src/arm/Div.go @@ -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) +} diff --git a/src/arm/Div_test.go b/src/arm/Div_test.go new file mode 100644 index 0000000..81c3f03 --- /dev/null +++ b/src/arm/Div_test.go @@ -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) + } +} diff --git a/src/arm/Load.go b/src/arm/Load.go index 8b82b98..afbd176 100644 --- a/src/arm/Load.go +++ b/src/arm/Load.go @@ -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: diff --git a/src/arm/LoadPair.go b/src/arm/LoadPair.go index 8ca16d7..70f6b39 100644 --- a/src/arm/LoadPair.go +++ b/src/arm/LoadPair.go @@ -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) } diff --git a/src/arm/Move.go b/src/arm/Move.go index b48bd9d..2399d92 100644 --- a/src/arm/Move.go +++ b/src/arm/Move.go @@ -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) } diff --git a/src/arm/Mul.go b/src/arm/Mul.go new file mode 100644 index 0000000..afad2cc --- /dev/null +++ b/src/arm/Mul.go @@ -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) +} diff --git a/src/arm/Mul_test.go b/src/arm/Mul_test.go new file mode 100644 index 0000000..d654208 --- /dev/null +++ b/src/arm/Mul_test.go @@ -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) + } +} diff --git a/src/arm/Registers.go b/src/arm/Registers.go index 388dc0d..9204b69 100644 --- a/src/arm/Registers.go +++ b/src/arm/Registers.go @@ -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 ( diff --git a/src/arm/Store.go b/src/arm/Store.go index d81f6a1..ceeb839 100644 --- a/src/arm/Store.go +++ b/src/arm/Store.go @@ -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: diff --git a/src/arm/StorePair.go b/src/arm/StorePair.go index 006457d..3a2958d 100644 --- a/src/arm/StorePair.go +++ b/src/arm/StorePair.go @@ -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) } diff --git a/src/arm/Sub.go b/src/arm/Sub.go index f468cea..415db8d 100644 --- a/src/arm/Sub.go +++ b/src/arm/Sub.go @@ -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) } diff --git a/src/arm/Sub_test.go b/src/arm/Sub_test.go index 42c4f4d..dc42615 100644 --- a/src/arm/Sub_test.go +++ b/src/arm/Sub_test.go @@ -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) + } +} diff --git a/src/arm/encode.go b/src/arm/encode.go index 312cc37..767573d 100644 --- a/src/arm/encode.go +++ b/src/arm/encode.go @@ -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) } diff --git a/src/asmc/compileARM.go b/src/asmc/compileARM.go index 5379846..fbf01b5 100644 --- a/src/asmc/compileARM.go +++ b/src/asmc/compileARM.go @@ -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") } diff --git a/src/asmc/compileX86.go b/src/asmc/compileX86.go index 2b59340..122873d 100644 --- a/src/asmc/compileX86.go +++ b/src/asmc/compileX86.go @@ -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) }