diff --git a/src/build/asm/Finalize.go b/src/build/asm/Finalize.go index a34920a..0d68d91 100644 --- a/src/build/asm/Finalize.go +++ b/src/build/asm/Finalize.go @@ -119,6 +119,9 @@ func (a Assembler) Finalize() ([]byte, []byte) { case LABEL: labels[x.Data.(*Label).Name] = Address(len(code)) + case MODULO: + code = modulo(code, x.Data) + case MOVE: switch operands := x.Data.(type) { case *RegisterNumber: diff --git a/src/build/asm/Mnemonic.go b/src/build/asm/Mnemonic.go index a1967a7..223666a 100644 --- a/src/build/asm/Mnemonic.go +++ b/src/build/asm/Mnemonic.go @@ -19,6 +19,7 @@ const ( MUL LABEL LOAD + MODULO MOVE POP PUSH @@ -59,6 +60,8 @@ func (m Mnemonic) String() string { return "label" case LOAD: return "load" + case MODULO: + return "mod" case MOVE: return "move" case MUL: diff --git a/src/build/asm/divide.go b/src/build/asm/divide.go index 09a879b..e795c63 100644 --- a/src/build/asm/divide.go +++ b/src/build/asm/divide.go @@ -41,3 +41,28 @@ func divide(code []byte, data any) []byte { code = x64.PopRegister(code, x64.RDX) return code } + +// modulo calculates the division remainder on x64 machines. +func modulo(code []byte, data any) []byte { + code = x64.PushRegister(code, x64.RDX) + code = x64.PushRegister(code, x64.RAX) + + switch operands := data.(type) { + case *RegisterNumber: + code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Register) + code = x64.MoveRegisterNumber32(code, operands.Register, uint32(operands.Number)) + code = x64.ExtendRAXToRDX(code) + code = x64.DivRegister(code, operands.Register) + code = x64.MoveRegisterRegister64(code, operands.Register, x64.RDX) + + case *RegisterRegister: + code = x64.MoveRegisterRegister64(code, x64.RAX, operands.Destination) + code = x64.ExtendRAXToRDX(code) + code = x64.DivRegister(code, operands.Source) + code = x64.MoveRegisterRegister64(code, operands.Destination, x64.RDX) + } + + code = x64.PopRegister(code, x64.RAX) + code = x64.PopRegister(code, x64.RDX) + return code +} diff --git a/src/build/core/ExecuteRegisterNumber.go b/src/build/core/ExecuteRegisterNumber.go index 43675a2..d870264 100644 --- a/src/build/core/ExecuteRegisterNumber.go +++ b/src/build/core/ExecuteRegisterNumber.go @@ -22,6 +22,9 @@ func (f *Function) ExecuteRegisterNumber(operation token.Token, register cpu.Reg case token.Div, token.DivAssign: f.RegisterNumber(asm.DIV, register, number) + case token.Mod, token.ModAssign: + f.RegisterNumber(asm.MODULO, register, number) + case token.Equal, token.NotEqual, token.Less, token.LessEqual, token.Greater, token.GreaterEqual: f.RegisterNumber(asm.COMPARE, register, number) diff --git a/src/build/core/ExecuteRegisterRegister.go b/src/build/core/ExecuteRegisterRegister.go index 8206b4f..97ee94b 100644 --- a/src/build/core/ExecuteRegisterRegister.go +++ b/src/build/core/ExecuteRegisterRegister.go @@ -22,6 +22,9 @@ func (f *Function) ExecuteRegisterRegister(operation token.Token, destination cp case token.Div, token.DivAssign: f.RegisterRegister(asm.DIV, destination, source) + case token.Mod, token.ModAssign: + f.RegisterRegister(asm.MODULO, destination, source) + case token.Equal, token.NotEqual, token.Less, token.LessEqual, token.Greater, token.GreaterEqual: f.RegisterRegister(asm.COMPARE, destination, source) diff --git a/tests/programs/remainder.q b/tests/programs/remainder.q new file mode 100644 index 0000000..392c6c8 --- /dev/null +++ b/tests/programs/remainder.q @@ -0,0 +1,37 @@ +import sys + +main() { + if 0 % 1 != 0 { + sys.exit(1) + } + + if 1 % 1 != 0 { + sys.exit(1) + } + + if 2 % 1 != 0 { + sys.exit(1) + } + + if 0 % 2 != 0 { + sys.exit(1) + } + + if 1 % 2 != 1 { + sys.exit(1) + } + + if 2 % 2 != 0 { + sys.exit(1) + } + + if 3 % 2 != 1 { + sys.exit(1) + } + + if 256 % 10 != 6 { + sys.exit(1) + } + + sys.exit(0) +} \ No newline at end of file diff --git a/tests/programs_test.go b/tests/programs_test.go index 5d4e5ff..08a1e12 100644 --- a/tests/programs_test.go +++ b/tests/programs_test.go @@ -35,6 +35,7 @@ var programs = []struct { {"jump-near", "", "", 0}, {"loop", "", "", 0}, {"loop-lifetime", "", "", 0}, + {"remainder", "", "", 0}, } func TestPrograms(t *testing.T) {