diff --git a/examples/hello/hello.q b/examples/hello/hello.q index 4f30709..1a3745e 100644 --- a/examples/hello/hello.q +++ b/examples/hello/hello.q @@ -10,9 +10,11 @@ hello() { address += 4194304 address += 1 - length += 5 - length *= 2 - length -= 7 + + length += 50 + length -= 20 + length *= 10 + length /= 100 loop { syscall(write, stdout, address, length) diff --git a/src/build/Function.go b/src/build/Function.go index f0298e7..141c210 100644 --- a/src/build/Function.go +++ b/src/build/Function.go @@ -130,6 +130,10 @@ func (f *Function) CompileInstruction(line token.List) error { case "*=": f.Assembler.RegisterNumber(asm.MUL, register, number) return nil + + case "/=": + f.Assembler.RegisterNumber(asm.DIV, register, number) + return nil } } diff --git a/src/build/arch/x64/Div.go b/src/build/arch/x64/Div.go new file mode 100644 index 0000000..0ed4a56 --- /dev/null +++ b/src/build/arch/x64/Div.go @@ -0,0 +1,20 @@ +package x64 + +import "git.akyoto.dev/cli/q/src/build/cpu" + +// DivReg divides RDX:RAX by the value in the register. +func DivReg(code []byte, divisor cpu.Register) []byte { + rex := byte(0x48) + + if divisor >= 8 { + rex++ + divisor -= 8 + } + + return append( + code, + rex, + 0xF7, + 0xF8+byte(divisor), + ) +} diff --git a/src/build/arch/x64/Div_test.go b/src/build/arch/x64/Div_test.go new file mode 100644 index 0000000..04ed063 --- /dev/null +++ b/src/build/arch/x64/Div_test.go @@ -0,0 +1,39 @@ +package x64_test + +import ( + "testing" + + "git.akyoto.dev/cli/q/src/build/arch/x64" + "git.akyoto.dev/cli/q/src/build/cpu" + "git.akyoto.dev/go/assert" +) + +func TestDivRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x64.RAX, []byte{0x48, 0xF7, 0xF8}}, + {x64.RCX, []byte{0x48, 0xF7, 0xF9}}, + {x64.RDX, []byte{0x48, 0xF7, 0xFA}}, + {x64.RBX, []byte{0x48, 0xF7, 0xFB}}, + {x64.RSP, []byte{0x48, 0xF7, 0xFC}}, + {x64.RBP, []byte{0x48, 0xF7, 0xFD}}, + {x64.RSI, []byte{0x48, 0xF7, 0xFE}}, + {x64.RDI, []byte{0x48, 0xF7, 0xFF}}, + {x64.R8, []byte{0x49, 0xF7, 0xF8}}, + {x64.R9, []byte{0x49, 0xF7, 0xF9}}, + {x64.R10, []byte{0x49, 0xF7, 0xFA}}, + {x64.R11, []byte{0x49, 0xF7, 0xFB}}, + {x64.R12, []byte{0x49, 0xF7, 0xFC}}, + {x64.R13, []byte{0x49, 0xF7, 0xFD}}, + {x64.R14, []byte{0x49, 0xF7, 0xFE}}, + {x64.R15, []byte{0x49, 0xF7, 0xFF}}, + } + + for _, pattern := range usagePatterns { + t.Logf("idiv %s", pattern.Register) + code := x64.DivReg(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} diff --git a/src/build/arch/x64/ExtendRAXToRDX.go b/src/build/arch/x64/ExtendRAXToRDX.go new file mode 100644 index 0000000..2f03963 --- /dev/null +++ b/src/build/arch/x64/ExtendRAXToRDX.go @@ -0,0 +1,7 @@ +package x64 + +// ExtendRAXToRDX doubles the size of RAX by sign-extending it to RDX. +// This is also known as CQO. +func ExtendRAXToRDX(code []byte) []byte { + return append(code, 0x48, 0x99) +} diff --git a/src/build/asm/Assembler.go b/src/build/asm/Assembler.go index 959aea2..fb93ed7 100644 --- a/src/build/asm/Assembler.go +++ b/src/build/asm/Assembler.go @@ -45,6 +45,16 @@ func (a *Assembler) Finalize() ([]byte, []byte) { code = x64.MulRegNum(code, operands.Register, operands.Number) } + case DIV: + switch operands := x.Data.(type) { + case *RegisterNumber: + code = x64.MoveRegReg64(code, x64.RAX, operands.Register) + code = x64.MoveRegNum32(code, operands.Register, uint32(operands.Number)) + code = x64.ExtendRAXToRDX(code) + code = x64.DivReg(code, operands.Register) + code = x64.MoveRegReg64(code, operands.Register, x64.RAX) + } + case CALL: code = x64.Call(code, 0x00_00_00_00) size := 4 diff --git a/src/build/asm/Mnemonic.go b/src/build/asm/Mnemonic.go index 6b97585..95fc2a2 100644 --- a/src/build/asm/Mnemonic.go +++ b/src/build/asm/Mnemonic.go @@ -6,6 +6,7 @@ const ( NONE Mnemonic = iota ADD CALL + DIV JUMP MUL LABEL @@ -24,6 +25,9 @@ func (m Mnemonic) String() string { case CALL: return "call" + case DIV: + return "div" + case JUMP: return "jump"