From 9b2b2578f55bdeb4ffdcd573b39e32b937a93000 Mon Sep 17 00:00:00 2001 From: Eduard Urbach Date: Mon, 23 Jun 2025 11:43:09 +0200 Subject: [PATCH] Added x86 package --- src/x86/Add.go | 15 ++ src/x86/Add_test.go | 88 ++++++++++ src/x86/And.go | 15 ++ src/x86/And_test.go | 88 ++++++++++ src/x86/Call.go | 51 ++++++ src/x86/Call_test.go | 92 +++++++++++ src/x86/Compare.go | 13 ++ src/x86/Compare_test.go | 88 ++++++++++ src/x86/Div.go | 20 +++ src/x86/Div_test.go | 39 +++++ src/x86/ExtendR0ToR2.go | 7 + src/x86/ExtendR0ToR2_test.go | 12 ++ src/x86/Jump.go | 37 +++++ src/x86/Jump_test.go | 40 +++++ src/x86/Load.go | 8 + src/x86/LoadAddress.go | 13 ++ src/x86/LoadAddress_test.go | 40 +++++ src/x86/LoadDynamic.go | 8 + src/x86/LoadDynamic_test.go | 90 +++++++++++ src/x86/Load_test.go | 157 ++++++++++++++++++ src/x86/ModRM.go | 19 +++ src/x86/ModRM_test.go | 34 ++++ src/x86/Move.go | 51 ++++++ src/x86/Move_test.go | 108 +++++++++++++ src/x86/Mul.go | 13 ++ src/x86/Mul_test.go | 88 ++++++++++ src/x86/Negate.go | 8 + src/x86/Negate_test.go | 39 +++++ src/x86/Or.go | 15 ++ src/x86/Or_test.go | 88 ++++++++++ src/x86/Pop.go | 16 ++ src/x86/Pop_test.go | 39 +++++ src/x86/Push.go | 37 +++++ src/x86/Push_test.go | 61 +++++++ src/x86/REX.go | 7 + src/x86/REX_test.go | 34 ++++ src/x86/Registers.go | 22 +++ src/x86/Return.go | 7 + src/x86/Return_test.go | 12 ++ src/x86/SIB.go | 19 +++ src/x86/SIB_test.go | 34 ++++ src/x86/Shift.go | 17 ++ src/x86/Shift_test.go | 71 ++++++++ src/x86/Store.go | 27 ++++ src/x86/StoreDynamic.go | 27 ++++ src/x86/StoreDynamic_test.go | 171 ++++++++++++++++++++ src/x86/Store_test.go | 305 +++++++++++++++++++++++++++++++++++ src/x86/Sub.go | 15 ++ src/x86/Sub_test.go | 88 ++++++++++ src/x86/Syscall.go | 6 + src/x86/Syscall_test.go | 12 ++ src/x86/Xor.go | 15 ++ src/x86/Xor_test.go | 88 ++++++++++ src/x86/encode.go | 33 ++++ src/x86/encodeNum.go | 19 +++ src/x86/memAccess.go | 34 ++++ src/x86/memAccessDynamic.go | 61 +++++++ 57 files changed, 2661 insertions(+) create mode 100644 src/x86/Add.go create mode 100644 src/x86/Add_test.go create mode 100644 src/x86/And.go create mode 100644 src/x86/And_test.go create mode 100644 src/x86/Call.go create mode 100644 src/x86/Call_test.go create mode 100644 src/x86/Compare.go create mode 100644 src/x86/Compare_test.go create mode 100644 src/x86/Div.go create mode 100644 src/x86/Div_test.go create mode 100644 src/x86/ExtendR0ToR2.go create mode 100644 src/x86/ExtendR0ToR2_test.go create mode 100644 src/x86/Jump.go create mode 100644 src/x86/Jump_test.go create mode 100644 src/x86/Load.go create mode 100644 src/x86/LoadAddress.go create mode 100644 src/x86/LoadAddress_test.go create mode 100644 src/x86/LoadDynamic.go create mode 100644 src/x86/LoadDynamic_test.go create mode 100644 src/x86/Load_test.go create mode 100644 src/x86/ModRM.go create mode 100644 src/x86/ModRM_test.go create mode 100644 src/x86/Move.go create mode 100644 src/x86/Move_test.go create mode 100644 src/x86/Mul.go create mode 100644 src/x86/Mul_test.go create mode 100644 src/x86/Negate.go create mode 100644 src/x86/Negate_test.go create mode 100644 src/x86/Or.go create mode 100644 src/x86/Or_test.go create mode 100644 src/x86/Pop.go create mode 100644 src/x86/Pop_test.go create mode 100644 src/x86/Push.go create mode 100644 src/x86/Push_test.go create mode 100644 src/x86/REX.go create mode 100644 src/x86/REX_test.go create mode 100644 src/x86/Registers.go create mode 100644 src/x86/Return.go create mode 100644 src/x86/Return_test.go create mode 100644 src/x86/SIB.go create mode 100644 src/x86/SIB_test.go create mode 100644 src/x86/Shift.go create mode 100644 src/x86/Shift_test.go create mode 100644 src/x86/Store.go create mode 100644 src/x86/StoreDynamic.go create mode 100644 src/x86/StoreDynamic_test.go create mode 100644 src/x86/Store_test.go create mode 100644 src/x86/Sub.go create mode 100644 src/x86/Sub_test.go create mode 100644 src/x86/Syscall.go create mode 100644 src/x86/Syscall_test.go create mode 100644 src/x86/Xor.go create mode 100644 src/x86/Xor_test.go create mode 100644 src/x86/encode.go create mode 100644 src/x86/encodeNum.go create mode 100644 src/x86/memAccess.go create mode 100644 src/x86/memAccessDynamic.go diff --git a/src/x86/Add.go b/src/x86/Add.go new file mode 100644 index 0000000..7527514 --- /dev/null +++ b/src/x86/Add.go @@ -0,0 +1,15 @@ +package x86 + +import ( + "git.urbach.dev/cli/q/src/cpu" +) + +// AddRegisterNumber adds a number to the given register. +func AddRegisterNumber(code []byte, register cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b000, register, number, 0x83, 0x81) +} + +// AddRegisterRegister adds a register value into another register. +func AddRegisterRegister(code []byte, register cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, register, 8, 0x01) +} \ No newline at end of file diff --git a/src/x86/Add_test.go b/src/x86/Add_test.go new file mode 100644 index 0000000..a7f3d17 --- /dev/null +++ b/src/x86/Add_test.go @@ -0,0 +1,88 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestAddRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0x83, 0xC0, 0x01}}, + {x86.R1, 1, []byte{0x48, 0x83, 0xC1, 0x01}}, + {x86.R2, 1, []byte{0x48, 0x83, 0xC2, 0x01}}, + {x86.R3, 1, []byte{0x48, 0x83, 0xC3, 0x01}}, + {x86.SP, 1, []byte{0x48, 0x83, 0xC4, 0x01}}, + {x86.R5, 1, []byte{0x48, 0x83, 0xC5, 0x01}}, + {x86.R6, 1, []byte{0x48, 0x83, 0xC6, 0x01}}, + {x86.R7, 1, []byte{0x48, 0x83, 0xC7, 0x01}}, + {x86.R8, 1, []byte{0x49, 0x83, 0xC0, 0x01}}, + {x86.R9, 1, []byte{0x49, 0x83, 0xC1, 0x01}}, + {x86.R10, 1, []byte{0x49, 0x83, 0xC2, 0x01}}, + {x86.R11, 1, []byte{0x49, 0x83, 0xC3, 0x01}}, + {x86.R12, 1, []byte{0x49, 0x83, 0xC4, 0x01}}, + {x86.R13, 1, []byte{0x49, 0x83, 0xC5, 0x01}}, + {x86.R14, 1, []byte{0x49, 0x83, 0xC6, 0x01}}, + {x86.R15, 1, []byte{0x49, 0x83, 0xC7, 0x01}}, + + {x86.R0, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC1, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC3, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC5, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC7, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC1, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC3, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC5, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC7, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("add %s, %x", pattern.Register, pattern.Number) + code := x86.AddRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestAddRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x4C, 0x01, 0xF8}}, + {x86.R1, x86.R14, []byte{0x4C, 0x01, 0xF1}}, + {x86.R2, x86.R13, []byte{0x4C, 0x01, 0xEA}}, + {x86.R3, x86.R12, []byte{0x4C, 0x01, 0xE3}}, + {x86.SP, x86.R11, []byte{0x4C, 0x01, 0xDC}}, + {x86.R5, x86.R10, []byte{0x4C, 0x01, 0xD5}}, + {x86.R6, x86.R9, []byte{0x4C, 0x01, 0xCE}}, + {x86.R7, x86.R8, []byte{0x4C, 0x01, 0xC7}}, + {x86.R8, x86.R7, []byte{0x49, 0x01, 0xF8}}, + {x86.R9, x86.R6, []byte{0x49, 0x01, 0xF1}}, + {x86.R10, x86.R5, []byte{0x49, 0x01, 0xEA}}, + {x86.R11, x86.SP, []byte{0x49, 0x01, 0xE3}}, + {x86.R12, x86.R3, []byte{0x49, 0x01, 0xDC}}, + {x86.R13, x86.R2, []byte{0x49, 0x01, 0xD5}}, + {x86.R14, x86.R1, []byte{0x49, 0x01, 0xCE}}, + {x86.R15, x86.R0, []byte{0x49, 0x01, 0xC7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("add %s, %s", pattern.Left, pattern.Right) + code := x86.AddRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/And.go b/src/x86/And.go new file mode 100644 index 0000000..e3cdb9a --- /dev/null +++ b/src/x86/And.go @@ -0,0 +1,15 @@ +package x86 + +import ( + "git.urbach.dev/cli/q/src/cpu" +) + +// AndRegisterNumber performs a bitwise AND using a register and a number. +func AndRegisterNumber(code []byte, register cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b100, register, number, 0x83, 0x81) +} + +// AndRegisterRegister performs a bitwise AND using two registers. +func AndRegisterRegister(code []byte, register cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, register, 8, 0x21) +} \ No newline at end of file diff --git a/src/x86/And_test.go b/src/x86/And_test.go new file mode 100644 index 0000000..bf3c383 --- /dev/null +++ b/src/x86/And_test.go @@ -0,0 +1,88 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestAndRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0x83, 0xE0, 0x01}}, + {x86.R1, 1, []byte{0x48, 0x83, 0xE1, 0x01}}, + {x86.R2, 1, []byte{0x48, 0x83, 0xE2, 0x01}}, + {x86.R3, 1, []byte{0x48, 0x83, 0xE3, 0x01}}, + {x86.SP, 1, []byte{0x48, 0x83, 0xE4, 0x01}}, + {x86.R5, 1, []byte{0x48, 0x83, 0xE5, 0x01}}, + {x86.R6, 1, []byte{0x48, 0x83, 0xE6, 0x01}}, + {x86.R7, 1, []byte{0x48, 0x83, 0xE7, 0x01}}, + {x86.R8, 1, []byte{0x49, 0x83, 0xE0, 0x01}}, + {x86.R9, 1, []byte{0x49, 0x83, 0xE1, 0x01}}, + {x86.R10, 1, []byte{0x49, 0x83, 0xE2, 0x01}}, + {x86.R11, 1, []byte{0x49, 0x83, 0xE3, 0x01}}, + {x86.R12, 1, []byte{0x49, 0x83, 0xE4, 0x01}}, + {x86.R13, 1, []byte{0x49, 0x83, 0xE5, 0x01}}, + {x86.R14, 1, []byte{0x49, 0x83, 0xE6, 0x01}}, + {x86.R15, 1, []byte{0x49, 0x83, 0xE7, 0x01}}, + + {x86.R0, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE1, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE3, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE5, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE7, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE1, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE3, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE5, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE7, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("and %s, %x", pattern.Register, pattern.Number) + code := x86.AndRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestAndRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x4C, 0x21, 0xF8}}, + {x86.R1, x86.R14, []byte{0x4C, 0x21, 0xF1}}, + {x86.R2, x86.R13, []byte{0x4C, 0x21, 0xEA}}, + {x86.R3, x86.R12, []byte{0x4C, 0x21, 0xE3}}, + {x86.SP, x86.R11, []byte{0x4C, 0x21, 0xDC}}, + {x86.R5, x86.R10, []byte{0x4C, 0x21, 0xD5}}, + {x86.R6, x86.R9, []byte{0x4C, 0x21, 0xCE}}, + {x86.R7, x86.R8, []byte{0x4C, 0x21, 0xC7}}, + {x86.R8, x86.R7, []byte{0x49, 0x21, 0xF8}}, + {x86.R9, x86.R6, []byte{0x49, 0x21, 0xF1}}, + {x86.R10, x86.R5, []byte{0x49, 0x21, 0xEA}}, + {x86.R11, x86.SP, []byte{0x49, 0x21, 0xE3}}, + {x86.R12, x86.R3, []byte{0x49, 0x21, 0xDC}}, + {x86.R13, x86.R2, []byte{0x49, 0x21, 0xD5}}, + {x86.R14, x86.R1, []byte{0x49, 0x21, 0xCE}}, + {x86.R15, x86.R0, []byte{0x49, 0x21, 0xC7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("and %s, %s", pattern.Left, pattern.Right) + code := x86.AndRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Call.go b/src/x86/Call.go new file mode 100644 index 0000000..7a1da75 --- /dev/null +++ b/src/x86/Call.go @@ -0,0 +1,51 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// Call places the return address on the top of the stack and continues +// program flow at the new address. +// The address is relative to the next instruction. +func Call(code []byte, offset uint32) []byte { + return append( + code, + 0xE8, + byte(offset), + byte(offset>>8), + byte(offset>>16), + byte(offset>>24), + ) +} + +// Calls a function whose address is stored in the given register. +func CallRegister(code []byte, register cpu.Register) []byte { + if register > 0b111 { + code = append(code, 0x41) + register &= 0b111 + } + + return append( + code, + 0xFF, + 0xD0+byte(register), + ) +} + +// CallAt calls a function at the address stored at the given memory address. +// The memory address is relative to the next instruction. +func CallAt(code []byte, address uint32) []byte { + return append( + code, + 0xFF, + 0x15, + byte(address), + byte(address>>8), + byte(address>>16), + byte(address>>24), + ) +} + +// CallAtMemory calls a function at the address stored at the given memory address. +// The memory address is relative to the next instruction. +func CallAtMemory(code []byte, base cpu.Register, offset int8) []byte { + return memAccess(code, 0xFF, 0xFF, 0b010, base, offset, 4) +} \ No newline at end of file diff --git a/src/x86/Call_test.go b/src/x86/Call_test.go new file mode 100644 index 0000000..3dcd0b2 --- /dev/null +++ b/src/x86/Call_test.go @@ -0,0 +1,92 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestCall(t *testing.T) { + assert.DeepEqual(t, x86.Call(nil, 1), []byte{0xE8, 0x01, 0x00, 0x00, 0x00}) + assert.DeepEqual(t, x86.CallAt(nil, 1), []byte{0xFF, 0x15, 0x01, 0x00, 0x00, 0x00}) +} + +func TestCallRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x86.R0, []byte{0xFF, 0xD0}}, + {x86.R1, []byte{0xFF, 0xD1}}, + {x86.R2, []byte{0xFF, 0xD2}}, + {x86.R3, []byte{0xFF, 0xD3}}, + {x86.SP, []byte{0xFF, 0xD4}}, + {x86.R5, []byte{0xFF, 0xD5}}, + {x86.R6, []byte{0xFF, 0xD6}}, + {x86.R7, []byte{0xFF, 0xD7}}, + {x86.R8, []byte{0x41, 0xFF, 0xD0}}, + {x86.R9, []byte{0x41, 0xFF, 0xD1}}, + {x86.R10, []byte{0x41, 0xFF, 0xD2}}, + {x86.R11, []byte{0x41, 0xFF, 0xD3}}, + {x86.R12, []byte{0x41, 0xFF, 0xD4}}, + {x86.R13, []byte{0x41, 0xFF, 0xD5}}, + {x86.R14, []byte{0x41, 0xFF, 0xD6}}, + {x86.R15, []byte{0x41, 0xFF, 0xD7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("call %s", pattern.Register) + code := x86.CallRegister(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestCallAtMemory(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Offset int8 + Code []byte + }{ + {x86.R0, 0, []byte{0xFF, 0x10}}, + {x86.R1, 0, []byte{0xFF, 0x11}}, + {x86.R2, 0, []byte{0xFF, 0x12}}, + {x86.R3, 0, []byte{0xFF, 0x13}}, + {x86.SP, 0, []byte{0xFF, 0x14, 0x24}}, + {x86.R5, 0, []byte{0xFF, 0x55, 0x00}}, + {x86.R6, 0, []byte{0xFF, 0x16}}, + {x86.R7, 0, []byte{0xFF, 0x17}}, + {x86.R8, 0, []byte{0x41, 0xFF, 0x10}}, + {x86.R9, 0, []byte{0x41, 0xFF, 0x11}}, + {x86.R10, 0, []byte{0x41, 0xFF, 0x12}}, + {x86.R11, 0, []byte{0x41, 0xFF, 0x13}}, + {x86.R12, 0, []byte{0x41, 0xFF, 0x14, 0x24}}, + {x86.R13, 0, []byte{0x41, 0xFF, 0x55, 0x00}}, + {x86.R14, 0, []byte{0x41, 0xFF, 0x16}}, + {x86.R15, 0, []byte{0x41, 0xFF, 0x17}}, + + {x86.R0, 1, []byte{0xFF, 0x50, 0x01}}, + {x86.R1, 1, []byte{0xFF, 0x51, 0x01}}, + {x86.R2, 1, []byte{0xFF, 0x52, 0x01}}, + {x86.R3, 1, []byte{0xFF, 0x53, 0x01}}, + {x86.SP, 1, []byte{0xFF, 0x54, 0x24, 0x01}}, + {x86.R5, 1, []byte{0xFF, 0x55, 0x01}}, + {x86.R6, 1, []byte{0xFF, 0x56, 0x01}}, + {x86.R7, 1, []byte{0xFF, 0x57, 0x01}}, + {x86.R8, 1, []byte{0x41, 0xFF, 0x50, 0x01}}, + {x86.R9, 1, []byte{0x41, 0xFF, 0x51, 0x01}}, + {x86.R10, 1, []byte{0x41, 0xFF, 0x52, 0x01}}, + {x86.R11, 1, []byte{0x41, 0xFF, 0x53, 0x01}}, + {x86.R12, 1, []byte{0x41, 0xFF, 0x54, 0x24, 0x01}}, + {x86.R13, 1, []byte{0x41, 0xFF, 0x55, 0x01}}, + {x86.R14, 1, []byte{0x41, 0xFF, 0x56, 0x01}}, + {x86.R15, 1, []byte{0x41, 0xFF, 0x57, 0x01}}, + } + + for _, pattern := range usagePatterns { + t.Logf("call [%s+%d]", pattern.Register, pattern.Offset) + code := x86.CallAtMemory(nil, pattern.Register, pattern.Offset) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Compare.go b/src/x86/Compare.go new file mode 100644 index 0000000..496e92b --- /dev/null +++ b/src/x86/Compare.go @@ -0,0 +1,13 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// Compares the register with the number and sets the status flags in the EFLAGS register. +func CompareRegisterNumber(code []byte, register cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b111, register, number, 0x83, 0x81) +} + +// CompareRegisterRegister compares a register with a register and sets the status flags in the EFLAGS register. +func CompareRegisterRegister(code []byte, registerA cpu.Register, registerB cpu.Register) []byte { + return encode(code, AddressDirect, registerB, registerA, 8, 0x39) +} \ No newline at end of file diff --git a/src/x86/Compare_test.go b/src/x86/Compare_test.go new file mode 100644 index 0000000..a374deb --- /dev/null +++ b/src/x86/Compare_test.go @@ -0,0 +1,88 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestCompareRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0x83, 0xF8, 0x01}}, + {x86.R1, 1, []byte{0x48, 0x83, 0xF9, 0x01}}, + {x86.R2, 1, []byte{0x48, 0x83, 0xFA, 0x01}}, + {x86.R3, 1, []byte{0x48, 0x83, 0xFB, 0x01}}, + {x86.SP, 1, []byte{0x48, 0x83, 0xFC, 0x01}}, + {x86.R5, 1, []byte{0x48, 0x83, 0xFD, 0x01}}, + {x86.R6, 1, []byte{0x48, 0x83, 0xFE, 0x01}}, + {x86.R7, 1, []byte{0x48, 0x83, 0xFF, 0x01}}, + {x86.R8, 1, []byte{0x49, 0x83, 0xF8, 0x01}}, + {x86.R9, 1, []byte{0x49, 0x83, 0xF9, 0x01}}, + {x86.R10, 1, []byte{0x49, 0x83, 0xFA, 0x01}}, + {x86.R11, 1, []byte{0x49, 0x83, 0xFB, 0x01}}, + {x86.R12, 1, []byte{0x49, 0x83, 0xFC, 0x01}}, + {x86.R13, 1, []byte{0x49, 0x83, 0xFD, 0x01}}, + {x86.R14, 1, []byte{0x49, 0x83, 0xFE, 0x01}}, + {x86.R15, 1, []byte{0x49, 0x83, 0xFF, 0x01}}, + + {x86.R0, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0x48, 0x81, 0xFA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0x48, 0x81, 0xFB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0x48, 0x81, 0xFC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0x48, 0x81, 0xFD, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0x48, 0x81, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0x48, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x49, 0x81, 0xFA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x49, 0x81, 0xFB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x49, 0x81, 0xFC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x49, 0x81, 0xFD, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x49, 0x81, 0xFE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x49, 0x81, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("cmp %s, %x", pattern.Register, pattern.Number) + code := x86.CompareRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestCompareRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x4C, 0x39, 0xF8}}, + {x86.R1, x86.R14, []byte{0x4C, 0x39, 0xF1}}, + {x86.R2, x86.R13, []byte{0x4C, 0x39, 0xEA}}, + {x86.R3, x86.R12, []byte{0x4C, 0x39, 0xE3}}, + {x86.SP, x86.R11, []byte{0x4C, 0x39, 0xDC}}, + {x86.R5, x86.R10, []byte{0x4C, 0x39, 0xD5}}, + {x86.R6, x86.R9, []byte{0x4C, 0x39, 0xCE}}, + {x86.R7, x86.R8, []byte{0x4C, 0x39, 0xC7}}, + {x86.R8, x86.R7, []byte{0x49, 0x39, 0xF8}}, + {x86.R9, x86.R6, []byte{0x49, 0x39, 0xF1}}, + {x86.R10, x86.R5, []byte{0x49, 0x39, 0xEA}}, + {x86.R11, x86.SP, []byte{0x49, 0x39, 0xE3}}, + {x86.R12, x86.R3, []byte{0x49, 0x39, 0xDC}}, + {x86.R13, x86.R2, []byte{0x49, 0x39, 0xD5}}, + {x86.R14, x86.R1, []byte{0x49, 0x39, 0xCE}}, + {x86.R15, x86.R0, []byte{0x49, 0x39, 0xC7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("cmp %s, %s", pattern.Left, pattern.Right) + code := x86.CompareRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Div.go b/src/x86/Div.go new file mode 100644 index 0000000..6919c93 --- /dev/null +++ b/src/x86/Div.go @@ -0,0 +1,20 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// DivRegister divides RDX:RAX by the value in the register. +func DivRegister(code []byte, divisor cpu.Register) []byte { + rex := byte(0x48) + + if divisor > 0b111 { + rex++ + divisor &= 0b111 + } + + return append( + code, + rex, + 0xF7, + 0xF8+byte(divisor), + ) +} \ No newline at end of file diff --git a/src/x86/Div_test.go b/src/x86/Div_test.go new file mode 100644 index 0000000..1926e13 --- /dev/null +++ b/src/x86/Div_test.go @@ -0,0 +1,39 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestDivRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x86.R0, []byte{0x48, 0xF7, 0xF8}}, + {x86.R1, []byte{0x48, 0xF7, 0xF9}}, + {x86.R2, []byte{0x48, 0xF7, 0xFA}}, + {x86.R3, []byte{0x48, 0xF7, 0xFB}}, + {x86.SP, []byte{0x48, 0xF7, 0xFC}}, + {x86.R5, []byte{0x48, 0xF7, 0xFD}}, + {x86.R6, []byte{0x48, 0xF7, 0xFE}}, + {x86.R7, []byte{0x48, 0xF7, 0xFF}}, + {x86.R8, []byte{0x49, 0xF7, 0xF8}}, + {x86.R9, []byte{0x49, 0xF7, 0xF9}}, + {x86.R10, []byte{0x49, 0xF7, 0xFA}}, + {x86.R11, []byte{0x49, 0xF7, 0xFB}}, + {x86.R12, []byte{0x49, 0xF7, 0xFC}}, + {x86.R13, []byte{0x49, 0xF7, 0xFD}}, + {x86.R14, []byte{0x49, 0xF7, 0xFE}}, + {x86.R15, []byte{0x49, 0xF7, 0xFF}}, + } + + for _, pattern := range usagePatterns { + t.Logf("idiv %s", pattern.Register) + code := x86.DivRegister(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/ExtendR0ToR2.go b/src/x86/ExtendR0ToR2.go new file mode 100644 index 0000000..9f314aa --- /dev/null +++ b/src/x86/ExtendR0ToR2.go @@ -0,0 +1,7 @@ +package x86 + +// ExtendR0ToR2 doubles the size of R0 (RAX) by sign-extending it to R2 (RDX). +// This is also known as CQO. +func ExtendR0ToR2(code []byte) []byte { + return append(code, 0x48, 0x99) +} \ No newline at end of file diff --git a/src/x86/ExtendR0ToR2_test.go b/src/x86/ExtendR0ToR2_test.go new file mode 100644 index 0000000..eac2aa1 --- /dev/null +++ b/src/x86/ExtendR0ToR2_test.go @@ -0,0 +1,12 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestExtendR0ToR2(t *testing.T) { + assert.DeepEqual(t, x86.ExtendR0ToR2(nil), []byte{0x48, 0x99}) +} \ No newline at end of file diff --git a/src/x86/Jump.go b/src/x86/Jump.go new file mode 100644 index 0000000..58a58b9 --- /dev/null +++ b/src/x86/Jump.go @@ -0,0 +1,37 @@ +package x86 + +// Jump continues program flow at the new offset. +// The offset is relative to the next instruction. +func Jump8(code []byte, offset int8) []byte { + return append(code, 0xEB, byte(offset)) +} + +// JumpIfEqual jumps if the result was equal. +func Jump8IfEqual(code []byte, offset int8) []byte { + return append(code, 0x74, byte(offset)) +} + +// JumpIfNotEqual jumps if the result was not equal. +func Jump8IfNotEqual(code []byte, offset int8) []byte { + return append(code, 0x75, byte(offset)) +} + +// JumpIfGreater jumps if the result was greater. +func Jump8IfGreater(code []byte, offset int8) []byte { + return append(code, 0x7F, byte(offset)) +} + +// JumpIfGreaterOrEqual jumps if the result was greater or equal. +func Jump8IfGreaterOrEqual(code []byte, offset int8) []byte { + return append(code, 0x7D, byte(offset)) +} + +// JumpIfLess jumps if the result was less. +func Jump8IfLess(code []byte, offset int8) []byte { + return append(code, 0x7C, byte(offset)) +} + +// JumpIfLessOrEqual jumps if the result was less or equal. +func Jump8IfLessOrEqual(code []byte, offset int8) []byte { + return append(code, 0x7E, byte(offset)) +} \ No newline at end of file diff --git a/src/x86/Jump_test.go b/src/x86/Jump_test.go new file mode 100644 index 0000000..8bb81b1 --- /dev/null +++ b/src/x86/Jump_test.go @@ -0,0 +1,40 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestJump(t *testing.T) { + usagePatterns := []struct { + Offset int8 + Code []byte + }{ + {0, []byte{0xEB, 0x00}}, + {1, []byte{0xEB, 0x01}}, + {2, []byte{0xEB, 0x02}}, + {3, []byte{0xEB, 0x03}}, + {127, []byte{0xEB, 0x7F}}, + {-1, []byte{0xEB, 0xFF}}, + {-2, []byte{0xEB, 0xFE}}, + {-3, []byte{0xEB, 0xFD}}, + {-128, []byte{0xEB, 0x80}}, + } + + for _, pattern := range usagePatterns { + t.Logf("jmp %x", pattern.Offset) + code := x86.Jump8(nil, pattern.Offset) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestConditionalJump(t *testing.T) { + assert.DeepEqual(t, x86.Jump8IfEqual(nil, 1), []byte{0x74, 0x01}) + assert.DeepEqual(t, x86.Jump8IfNotEqual(nil, 1), []byte{0x75, 0x01}) + assert.DeepEqual(t, x86.Jump8IfLess(nil, 1), []byte{0x7C, 0x01}) + assert.DeepEqual(t, x86.Jump8IfGreaterOrEqual(nil, 1), []byte{0x7D, 0x01}) + assert.DeepEqual(t, x86.Jump8IfLessOrEqual(nil, 1), []byte{0x7E, 0x01}) + assert.DeepEqual(t, x86.Jump8IfGreater(nil, 1), []byte{0x7F, 0x01}) +} \ No newline at end of file diff --git a/src/x86/Load.go b/src/x86/Load.go new file mode 100644 index 0000000..018f05e --- /dev/null +++ b/src/x86/Load.go @@ -0,0 +1,8 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// LoadRegister loads from memory into a register. +func LoadRegister(code []byte, destination cpu.Register, base cpu.Register, offset int8, length byte) []byte { + return memAccess(code, 0x8A, 0x8B, destination, base, offset, length) +} \ No newline at end of file diff --git a/src/x86/LoadAddress.go b/src/x86/LoadAddress.go new file mode 100644 index 0000000..cd34a57 --- /dev/null +++ b/src/x86/LoadAddress.go @@ -0,0 +1,13 @@ +package x86 + +import ( + "encoding/binary" + + "git.urbach.dev/cli/q/src/cpu" +) + +// LoadAddress calculates the address with the RIP-relative offset and writes the result to the destination register. +func LoadAddress(code []byte, destination cpu.Register, offset int) []byte { + code = encode(code, AddressMemory, destination, 0b101, 8, 0x8D) + return binary.LittleEndian.AppendUint32(code, uint32(offset)) +} \ No newline at end of file diff --git a/src/x86/LoadAddress_test.go b/src/x86/LoadAddress_test.go new file mode 100644 index 0000000..ec80007 --- /dev/null +++ b/src/x86/LoadAddress_test.go @@ -0,0 +1,40 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestLoadAddress(t *testing.T) { + usagePatterns := []struct { + Destination cpu.Register + Offset int + Code []byte + }{ + {x86.R0, 0, []byte{0x48, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00}}, + {x86.R1, 0, []byte{0x48, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00}}, + {x86.R2, 0, []byte{0x48, 0x8D, 0x15, 0x00, 0x00, 0x00, 0x00}}, + {x86.R3, 0, []byte{0x48, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00}}, + {x86.SP, 0, []byte{0x48, 0x8D, 0x25, 0x00, 0x00, 0x00, 0x00}}, + {x86.R5, 0, []byte{0x48, 0x8D, 0x2D, 0x00, 0x00, 0x00, 0x00}}, + {x86.R6, 0, []byte{0x48, 0x8D, 0x35, 0x00, 0x00, 0x00, 0x00}}, + {x86.R7, 0, []byte{0x48, 0x8D, 0x3D, 0x00, 0x00, 0x00, 0x00}}, + {x86.R8, 0, []byte{0x4C, 0x8D, 0x05, 0x00, 0x00, 0x00, 0x00}}, + {x86.R9, 0, []byte{0x4C, 0x8D, 0x0D, 0x00, 0x00, 0x00, 0x00}}, + {x86.R10, 0, []byte{0x4C, 0x8D, 0x15, 0x00, 0x00, 0x00, 0x00}}, + {x86.R11, 0, []byte{0x4C, 0x8D, 0x1D, 0x00, 0x00, 0x00, 0x00}}, + {x86.R12, 0, []byte{0x4C, 0x8D, 0x25, 0x00, 0x00, 0x00, 0x00}}, + {x86.R13, 0, []byte{0x4C, 0x8D, 0x2D, 0x00, 0x00, 0x00, 0x00}}, + {x86.R14, 0, []byte{0x4C, 0x8D, 0x35, 0x00, 0x00, 0x00, 0x00}}, + {x86.R15, 0, []byte{0x4C, 0x8D, 0x3D, 0x00, 0x00, 0x00, 0x00}}, + } + + for _, pattern := range usagePatterns { + t.Logf("lea %s, [rip+%d]", pattern.Destination, pattern.Offset) + code := x86.LoadAddress(nil, pattern.Destination, pattern.Offset) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/LoadDynamic.go b/src/x86/LoadDynamic.go new file mode 100644 index 0000000..695746e --- /dev/null +++ b/src/x86/LoadDynamic.go @@ -0,0 +1,8 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// LoadDynamicRegister loads from memory with a register offset into a register. +func LoadDynamicRegister(code []byte, destination cpu.Register, base cpu.Register, offset cpu.Register, length byte) []byte { + return memAccessDynamic(code, 0x8A, 0x8B, destination, base, offset, length) +} \ No newline at end of file diff --git a/src/x86/LoadDynamic_test.go b/src/x86/LoadDynamic_test.go new file mode 100644 index 0000000..0e897f0 --- /dev/null +++ b/src/x86/LoadDynamic_test.go @@ -0,0 +1,90 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestLoadDynamicRegister(t *testing.T) { + usagePatterns := []struct { + Destination cpu.Register + Length byte + Base cpu.Register + OffsetRegister cpu.Register + Code []byte + }{ + {x86.R15, 8, x86.R0, x86.R15, []byte{0x4E, 0x8B, 0x3C, 0x38}}, + {x86.R15, 4, x86.R0, x86.R15, []byte{0x46, 0x8B, 0x3C, 0x38}}, + {x86.R15, 2, x86.R0, x86.R15, []byte{0x66, 0x46, 0x8B, 0x3C, 0x38}}, + {x86.R15, 1, x86.R0, x86.R15, []byte{0x46, 0x8A, 0x3C, 0x38}}, + {x86.R14, 8, x86.R1, x86.R14, []byte{0x4E, 0x8B, 0x34, 0x31}}, + {x86.R14, 4, x86.R1, x86.R14, []byte{0x46, 0x8B, 0x34, 0x31}}, + {x86.R14, 2, x86.R1, x86.R14, []byte{0x66, 0x46, 0x8B, 0x34, 0x31}}, + {x86.R14, 1, x86.R1, x86.R14, []byte{0x46, 0x8A, 0x34, 0x31}}, + {x86.R13, 8, x86.R2, x86.R13, []byte{0x4E, 0x8B, 0x2C, 0x2A}}, + {x86.R13, 4, x86.R2, x86.R13, []byte{0x46, 0x8B, 0x2C, 0x2A}}, + {x86.R13, 2, x86.R2, x86.R13, []byte{0x66, 0x46, 0x8B, 0x2C, 0x2A}}, + {x86.R13, 1, x86.R2, x86.R13, []byte{0x46, 0x8A, 0x2C, 0x2A}}, + {x86.R12, 8, x86.R3, x86.R12, []byte{0x4E, 0x8B, 0x24, 0x23}}, + {x86.R12, 4, x86.R3, x86.R12, []byte{0x46, 0x8B, 0x24, 0x23}}, + {x86.R12, 2, x86.R3, x86.R12, []byte{0x66, 0x46, 0x8B, 0x24, 0x23}}, + {x86.R12, 1, x86.R3, x86.R12, []byte{0x46, 0x8A, 0x24, 0x23}}, + {x86.R11, 8, x86.SP, x86.R11, []byte{0x4E, 0x8B, 0x1C, 0x1C}}, + {x86.R11, 4, x86.SP, x86.R11, []byte{0x46, 0x8B, 0x1C, 0x1C}}, + {x86.R11, 2, x86.SP, x86.R11, []byte{0x66, 0x46, 0x8B, 0x1C, 0x1C}}, + {x86.R11, 1, x86.SP, x86.R11, []byte{0x46, 0x8A, 0x1C, 0x1C}}, + {x86.R10, 8, x86.R5, x86.R10, []byte{0x4E, 0x8B, 0x54, 0x15, 0x00}}, + {x86.R10, 4, x86.R5, x86.R10, []byte{0x46, 0x8B, 0x54, 0x15, 0x00}}, + {x86.R10, 2, x86.R5, x86.R10, []byte{0x66, 0x46, 0x8B, 0x54, 0x15, 0x00}}, + {x86.R10, 1, x86.R5, x86.R10, []byte{0x46, 0x8A, 0x54, 0x15, 0x00}}, + {x86.R9, 8, x86.R6, x86.R9, []byte{0x4E, 0x8B, 0x0C, 0x0E}}, + {x86.R9, 4, x86.R6, x86.R9, []byte{0x46, 0x8B, 0x0C, 0x0E}}, + {x86.R9, 2, x86.R6, x86.R9, []byte{0x66, 0x46, 0x8B, 0x0C, 0x0E}}, + {x86.R9, 1, x86.R6, x86.R9, []byte{0x46, 0x8A, 0x0C, 0x0E}}, + {x86.R8, 8, x86.R7, x86.R8, []byte{0x4E, 0x8B, 0x04, 0x07}}, + {x86.R8, 4, x86.R7, x86.R8, []byte{0x46, 0x8B, 0x04, 0x07}}, + {x86.R8, 2, x86.R7, x86.R8, []byte{0x66, 0x46, 0x8B, 0x04, 0x07}}, + {x86.R8, 1, x86.R7, x86.R8, []byte{0x46, 0x8A, 0x04, 0x07}}, + {x86.R7, 8, x86.R8, x86.R7, []byte{0x49, 0x8B, 0x3C, 0x38}}, + {x86.R7, 4, x86.R8, x86.R7, []byte{0x41, 0x8B, 0x3C, 0x38}}, + {x86.R7, 2, x86.R8, x86.R7, []byte{0x66, 0x41, 0x8B, 0x3C, 0x38}}, + {x86.R7, 1, x86.R8, x86.R7, []byte{0x41, 0x8A, 0x3C, 0x38}}, + {x86.R6, 8, x86.R9, x86.R6, []byte{0x49, 0x8B, 0x34, 0x31}}, + {x86.R6, 4, x86.R9, x86.R6, []byte{0x41, 0x8B, 0x34, 0x31}}, + {x86.R6, 2, x86.R9, x86.R6, []byte{0x66, 0x41, 0x8B, 0x34, 0x31}}, + {x86.R6, 1, x86.R9, x86.R6, []byte{0x41, 0x8A, 0x34, 0x31}}, + {x86.R5, 8, x86.R10, x86.R5, []byte{0x49, 0x8B, 0x2C, 0x2A}}, + {x86.R5, 4, x86.R10, x86.R5, []byte{0x41, 0x8B, 0x2C, 0x2A}}, + {x86.R5, 2, x86.R10, x86.R5, []byte{0x66, 0x41, 0x8B, 0x2C, 0x2A}}, + {x86.R5, 1, x86.R10, x86.R5, []byte{0x41, 0x8A, 0x2C, 0x2A}}, + {x86.SP, 8, x86.R11, x86.SP, []byte{0x4A, 0x8B, 0x24, 0x1C}}, + {x86.SP, 4, x86.R11, x86.SP, []byte{0x42, 0x8B, 0x24, 0x1C}}, + {x86.SP, 2, x86.R11, x86.SP, []byte{0x66, 0x42, 0x8B, 0x24, 0x1C}}, + {x86.SP, 1, x86.R11, x86.SP, []byte{0x42, 0x8A, 0x24, 0x1C}}, + {x86.R3, 8, x86.R12, x86.R3, []byte{0x49, 0x8B, 0x1C, 0x1C}}, + {x86.R3, 4, x86.R12, x86.R3, []byte{0x41, 0x8B, 0x1C, 0x1C}}, + {x86.R3, 2, x86.R12, x86.R3, []byte{0x66, 0x41, 0x8B, 0x1C, 0x1C}}, + {x86.R3, 1, x86.R12, x86.R3, []byte{0x41, 0x8A, 0x1C, 0x1C}}, + {x86.R2, 8, x86.R13, x86.R2, []byte{0x49, 0x8B, 0x54, 0x15, 0x00}}, + {x86.R2, 4, x86.R13, x86.R2, []byte{0x41, 0x8B, 0x54, 0x15, 0x00}}, + {x86.R2, 2, x86.R13, x86.R2, []byte{0x66, 0x41, 0x8B, 0x54, 0x15, 0x00}}, + {x86.R2, 1, x86.R13, x86.R2, []byte{0x41, 0x8A, 0x54, 0x15, 0x00}}, + {x86.R1, 8, x86.R14, x86.R1, []byte{0x49, 0x8B, 0x0C, 0x0E}}, + {x86.R1, 4, x86.R14, x86.R1, []byte{0x41, 0x8B, 0x0C, 0x0E}}, + {x86.R1, 2, x86.R14, x86.R1, []byte{0x66, 0x41, 0x8B, 0x0C, 0x0E}}, + {x86.R1, 1, x86.R14, x86.R1, []byte{0x41, 0x8A, 0x0C, 0x0E}}, + {x86.R0, 8, x86.R15, x86.R0, []byte{0x49, 0x8B, 0x04, 0x07}}, + {x86.R0, 4, x86.R15, x86.R0, []byte{0x41, 0x8B, 0x04, 0x07}}, + {x86.R0, 2, x86.R15, x86.R0, []byte{0x66, 0x41, 0x8B, 0x04, 0x07}}, + {x86.R0, 1, x86.R15, x86.R0, []byte{0x41, 0x8A, 0x04, 0x07}}, + } + + for _, pattern := range usagePatterns { + t.Logf("load %dB %s, [%s+%s]", pattern.Length, pattern.Destination, pattern.Base, pattern.OffsetRegister) + code := x86.LoadDynamicRegister(nil, pattern.Destination, pattern.Base, pattern.OffsetRegister, pattern.Length) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Load_test.go b/src/x86/Load_test.go new file mode 100644 index 0000000..3051818 --- /dev/null +++ b/src/x86/Load_test.go @@ -0,0 +1,157 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestLoadRegister(t *testing.T) { + usagePatterns := []struct { + Destination cpu.Register + Base cpu.Register + Offset int8 + Length byte + Code []byte + }{ + // No offset + {x86.R0, x86.R15, 0, 8, []byte{0x49, 0x8B, 0x07}}, + {x86.R0, x86.R15, 0, 4, []byte{0x41, 0x8B, 0x07}}, + {x86.R0, x86.R15, 0, 2, []byte{0x66, 0x41, 0x8B, 0x07}}, + {x86.R0, x86.R15, 0, 1, []byte{0x41, 0x8A, 0x07}}, + {x86.R1, x86.R14, 0, 8, []byte{0x49, 0x8B, 0x0E}}, + {x86.R1, x86.R14, 0, 4, []byte{0x41, 0x8B, 0x0E}}, + {x86.R1, x86.R14, 0, 2, []byte{0x66, 0x41, 0x8B, 0x0E}}, + {x86.R1, x86.R14, 0, 1, []byte{0x41, 0x8A, 0x0E}}, + {x86.R2, x86.R13, 0, 8, []byte{0x49, 0x8B, 0x55, 0x00}}, + {x86.R2, x86.R13, 0, 4, []byte{0x41, 0x8B, 0x55, 0x00}}, + {x86.R2, x86.R13, 0, 2, []byte{0x66, 0x41, 0x8B, 0x55, 0x00}}, + {x86.R2, x86.R13, 0, 1, []byte{0x41, 0x8A, 0x55, 0x00}}, + {x86.R3, x86.R12, 0, 8, []byte{0x49, 0x8B, 0x1C, 0x24}}, + {x86.R3, x86.R12, 0, 4, []byte{0x41, 0x8B, 0x1C, 0x24}}, + {x86.R3, x86.R12, 0, 2, []byte{0x66, 0x41, 0x8B, 0x1C, 0x24}}, + {x86.R3, x86.R12, 0, 1, []byte{0x41, 0x8A, 0x1C, 0x24}}, + {x86.SP, x86.R11, 0, 8, []byte{0x49, 0x8B, 0x23}}, + {x86.SP, x86.R11, 0, 4, []byte{0x41, 0x8B, 0x23}}, + {x86.SP, x86.R11, 0, 2, []byte{0x66, 0x41, 0x8B, 0x23}}, + {x86.SP, x86.R11, 0, 1, []byte{0x41, 0x8A, 0x23}}, + {x86.R5, x86.R10, 0, 8, []byte{0x49, 0x8B, 0x2A}}, + {x86.R5, x86.R10, 0, 4, []byte{0x41, 0x8B, 0x2A}}, + {x86.R5, x86.R10, 0, 2, []byte{0x66, 0x41, 0x8B, 0x2A}}, + {x86.R5, x86.R10, 0, 1, []byte{0x41, 0x8A, 0x2A}}, + {x86.R6, x86.R9, 0, 8, []byte{0x49, 0x8B, 0x31}}, + {x86.R6, x86.R9, 0, 4, []byte{0x41, 0x8B, 0x31}}, + {x86.R6, x86.R9, 0, 2, []byte{0x66, 0x41, 0x8B, 0x31}}, + {x86.R6, x86.R9, 0, 1, []byte{0x41, 0x8A, 0x31}}, + {x86.R7, x86.R8, 0, 8, []byte{0x49, 0x8B, 0x38}}, + {x86.R7, x86.R8, 0, 4, []byte{0x41, 0x8B, 0x38}}, + {x86.R7, x86.R8, 0, 2, []byte{0x66, 0x41, 0x8B, 0x38}}, + {x86.R7, x86.R8, 0, 1, []byte{0x41, 0x8A, 0x38}}, + {x86.R8, x86.R7, 0, 8, []byte{0x4C, 0x8B, 0x07}}, + {x86.R8, x86.R7, 0, 4, []byte{0x44, 0x8B, 0x07}}, + {x86.R8, x86.R7, 0, 2, []byte{0x66, 0x44, 0x8B, 0x07}}, + {x86.R8, x86.R7, 0, 1, []byte{0x44, 0x8A, 0x07}}, + {x86.R9, x86.R6, 0, 8, []byte{0x4C, 0x8B, 0x0E}}, + {x86.R9, x86.R6, 0, 4, []byte{0x44, 0x8B, 0x0E}}, + {x86.R9, x86.R6, 0, 2, []byte{0x66, 0x44, 0x8B, 0x0E}}, + {x86.R9, x86.R6, 0, 1, []byte{0x44, 0x8A, 0x0E}}, + {x86.R10, x86.R5, 0, 8, []byte{0x4C, 0x8B, 0x55, 0x00}}, + {x86.R10, x86.R5, 0, 4, []byte{0x44, 0x8B, 0x55, 0x00}}, + {x86.R10, x86.R5, 0, 2, []byte{0x66, 0x44, 0x8B, 0x55, 0x00}}, + {x86.R10, x86.R5, 0, 1, []byte{0x44, 0x8A, 0x55, 0x00}}, + {x86.R11, x86.SP, 0, 8, []byte{0x4C, 0x8B, 0x1C, 0x24}}, + {x86.R11, x86.SP, 0, 4, []byte{0x44, 0x8B, 0x1C, 0x24}}, + {x86.R11, x86.SP, 0, 2, []byte{0x66, 0x44, 0x8B, 0x1C, 0x24}}, + {x86.R11, x86.SP, 0, 1, []byte{0x44, 0x8A, 0x1C, 0x24}}, + {x86.R12, x86.R3, 0, 8, []byte{0x4C, 0x8B, 0x23}}, + {x86.R12, x86.R3, 0, 4, []byte{0x44, 0x8B, 0x23}}, + {x86.R12, x86.R3, 0, 2, []byte{0x66, 0x44, 0x8B, 0x23}}, + {x86.R12, x86.R3, 0, 1, []byte{0x44, 0x8A, 0x23}}, + {x86.R13, x86.R2, 0, 8, []byte{0x4C, 0x8B, 0x2A}}, + {x86.R13, x86.R2, 0, 4, []byte{0x44, 0x8B, 0x2A}}, + {x86.R13, x86.R2, 0, 2, []byte{0x66, 0x44, 0x8B, 0x2A}}, + {x86.R13, x86.R2, 0, 1, []byte{0x44, 0x8A, 0x2A}}, + {x86.R14, x86.R1, 0, 8, []byte{0x4C, 0x8B, 0x31}}, + {x86.R14, x86.R1, 0, 4, []byte{0x44, 0x8B, 0x31}}, + {x86.R14, x86.R1, 0, 2, []byte{0x66, 0x44, 0x8B, 0x31}}, + {x86.R14, x86.R1, 0, 1, []byte{0x44, 0x8A, 0x31}}, + {x86.R15, x86.R0, 0, 8, []byte{0x4C, 0x8B, 0x38}}, + {x86.R15, x86.R0, 0, 4, []byte{0x44, 0x8B, 0x38}}, + {x86.R15, x86.R0, 0, 2, []byte{0x66, 0x44, 0x8B, 0x38}}, + {x86.R15, x86.R0, 0, 1, []byte{0x44, 0x8A, 0x38}}, + + // Offset of 1 + {x86.R0, x86.R15, 1, 8, []byte{0x49, 0x8B, 0x47, 0x01}}, + {x86.R0, x86.R15, 1, 4, []byte{0x41, 0x8B, 0x47, 0x01}}, + {x86.R0, x86.R15, 1, 2, []byte{0x66, 0x41, 0x8B, 0x47, 0x01}}, + {x86.R0, x86.R15, 1, 1, []byte{0x41, 0x8A, 0x47, 0x01}}, + {x86.R1, x86.R14, 1, 8, []byte{0x49, 0x8B, 0x4E, 0x01}}, + {x86.R1, x86.R14, 1, 4, []byte{0x41, 0x8B, 0x4E, 0x01}}, + {x86.R1, x86.R14, 1, 2, []byte{0x66, 0x41, 0x8B, 0x4E, 0x01}}, + {x86.R1, x86.R14, 1, 1, []byte{0x41, 0x8A, 0x4E, 0x01}}, + {x86.R2, x86.R13, 1, 8, []byte{0x49, 0x8B, 0x55, 0x01}}, + {x86.R2, x86.R13, 1, 4, []byte{0x41, 0x8B, 0x55, 0x01}}, + {x86.R2, x86.R13, 1, 2, []byte{0x66, 0x41, 0x8B, 0x55, 0x01}}, + {x86.R2, x86.R13, 1, 1, []byte{0x41, 0x8A, 0x55, 0x01}}, + {x86.R3, x86.R12, 1, 8, []byte{0x49, 0x8B, 0x5C, 0x24, 0x01}}, + {x86.R3, x86.R12, 1, 4, []byte{0x41, 0x8B, 0x5C, 0x24, 0x01}}, + {x86.R3, x86.R12, 1, 2, []byte{0x66, 0x41, 0x8B, 0x5C, 0x24, 0x01}}, + {x86.R3, x86.R12, 1, 1, []byte{0x41, 0x8A, 0x5C, 0x24, 0x01}}, + {x86.SP, x86.R11, 1, 8, []byte{0x49, 0x8B, 0x63, 0x01}}, + {x86.SP, x86.R11, 1, 4, []byte{0x41, 0x8B, 0x63, 0x01}}, + {x86.SP, x86.R11, 1, 2, []byte{0x66, 0x41, 0x8B, 0x63, 0x01}}, + {x86.SP, x86.R11, 1, 1, []byte{0x41, 0x8A, 0x63, 0x01}}, + {x86.R5, x86.R10, 1, 8, []byte{0x49, 0x8B, 0x6A, 0x01}}, + {x86.R5, x86.R10, 1, 4, []byte{0x41, 0x8B, 0x6A, 0x01}}, + {x86.R5, x86.R10, 1, 2, []byte{0x66, 0x41, 0x8B, 0x6A, 0x01}}, + {x86.R5, x86.R10, 1, 1, []byte{0x41, 0x8A, 0x6A, 0x01}}, + {x86.R6, x86.R9, 1, 8, []byte{0x49, 0x8B, 0x71, 0x01}}, + {x86.R6, x86.R9, 1, 4, []byte{0x41, 0x8B, 0x71, 0x01}}, + {x86.R6, x86.R9, 1, 2, []byte{0x66, 0x41, 0x8B, 0x71, 0x01}}, + {x86.R6, x86.R9, 1, 1, []byte{0x41, 0x8A, 0x71, 0x01}}, + {x86.R7, x86.R8, 1, 8, []byte{0x49, 0x8B, 0x78, 0x01}}, + {x86.R7, x86.R8, 1, 4, []byte{0x41, 0x8B, 0x78, 0x01}}, + {x86.R7, x86.R8, 1, 2, []byte{0x66, 0x41, 0x8B, 0x78, 0x01}}, + {x86.R7, x86.R8, 1, 1, []byte{0x41, 0x8A, 0x78, 0x01}}, + {x86.R8, x86.R7, 1, 8, []byte{0x4C, 0x8B, 0x47, 0x01}}, + {x86.R8, x86.R7, 1, 4, []byte{0x44, 0x8B, 0x47, 0x01}}, + {x86.R8, x86.R7, 1, 2, []byte{0x66, 0x44, 0x8B, 0x47, 0x01}}, + {x86.R8, x86.R7, 1, 1, []byte{0x44, 0x8A, 0x47, 0x01}}, + {x86.R9, x86.R6, 1, 8, []byte{0x4C, 0x8B, 0x4E, 0x01}}, + {x86.R9, x86.R6, 1, 4, []byte{0x44, 0x8B, 0x4E, 0x01}}, + {x86.R9, x86.R6, 1, 2, []byte{0x66, 0x44, 0x8B, 0x4E, 0x01}}, + {x86.R9, x86.R6, 1, 1, []byte{0x44, 0x8A, 0x4E, 0x01}}, + {x86.R10, x86.R5, 1, 8, []byte{0x4C, 0x8B, 0x55, 0x01}}, + {x86.R10, x86.R5, 1, 4, []byte{0x44, 0x8B, 0x55, 0x01}}, + {x86.R10, x86.R5, 1, 2, []byte{0x66, 0x44, 0x8B, 0x55, 0x01}}, + {x86.R10, x86.R5, 1, 1, []byte{0x44, 0x8A, 0x55, 0x01}}, + {x86.R11, x86.SP, 1, 8, []byte{0x4C, 0x8B, 0x5C, 0x24, 0x01}}, + {x86.R11, x86.SP, 1, 4, []byte{0x44, 0x8B, 0x5C, 0x24, 0x01}}, + {x86.R11, x86.SP, 1, 2, []byte{0x66, 0x44, 0x8B, 0x5C, 0x24, 0x01}}, + {x86.R11, x86.SP, 1, 1, []byte{0x44, 0x8A, 0x5C, 0x24, 0x01}}, + {x86.R12, x86.R3, 1, 8, []byte{0x4C, 0x8B, 0x63, 0x01}}, + {x86.R12, x86.R3, 1, 4, []byte{0x44, 0x8B, 0x63, 0x01}}, + {x86.R12, x86.R3, 1, 2, []byte{0x66, 0x44, 0x8B, 0x63, 0x01}}, + {x86.R12, x86.R3, 1, 1, []byte{0x44, 0x8A, 0x63, 0x01}}, + {x86.R13, x86.R2, 1, 8, []byte{0x4C, 0x8B, 0x6A, 0x01}}, + {x86.R13, x86.R2, 1, 4, []byte{0x44, 0x8B, 0x6A, 0x01}}, + {x86.R13, x86.R2, 1, 2, []byte{0x66, 0x44, 0x8B, 0x6A, 0x01}}, + {x86.R13, x86.R2, 1, 1, []byte{0x44, 0x8A, 0x6A, 0x01}}, + {x86.R14, x86.R1, 1, 8, []byte{0x4C, 0x8B, 0x71, 0x01}}, + {x86.R14, x86.R1, 1, 4, []byte{0x44, 0x8B, 0x71, 0x01}}, + {x86.R14, x86.R1, 1, 2, []byte{0x66, 0x44, 0x8B, 0x71, 0x01}}, + {x86.R14, x86.R1, 1, 1, []byte{0x44, 0x8A, 0x71, 0x01}}, + {x86.R15, x86.R0, 1, 8, []byte{0x4C, 0x8B, 0x78, 0x01}}, + {x86.R15, x86.R0, 1, 4, []byte{0x44, 0x8B, 0x78, 0x01}}, + {x86.R15, x86.R0, 1, 2, []byte{0x66, 0x44, 0x8B, 0x78, 0x01}}, + {x86.R15, x86.R0, 1, 1, []byte{0x44, 0x8A, 0x78, 0x01}}, + } + + for _, pattern := range usagePatterns { + t.Logf("load %dB %s, [%s+%d]", pattern.Length, pattern.Destination, pattern.Base, pattern.Offset) + code := x86.LoadRegister(nil, pattern.Destination, pattern.Base, pattern.Offset, pattern.Length) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/ModRM.go b/src/x86/ModRM.go new file mode 100644 index 0000000..5be3f8e --- /dev/null +++ b/src/x86/ModRM.go @@ -0,0 +1,19 @@ +package x86 + +// AddressMode encodes the addressing mode. +type AddressMode = byte + +const ( + AddressMemory = AddressMode(0b00) + AddressMemoryOffset8 = AddressMode(0b01) + AddressMemoryOffset32 = AddressMode(0b10) + AddressDirect = AddressMode(0b11) +) + +// ModRM is used to generate a mode-register-memory suffix. +// - mod: 2 bits. The addressing mode. +// - reg: 3 bits. Register reference or opcode extension. +// - rm: 3 bits. Register operand. +func ModRM(mod AddressMode, reg byte, rm byte) byte { + return (mod << 6) | (reg << 3) | rm +} \ No newline at end of file diff --git a/src/x86/ModRM_test.go b/src/x86/ModRM_test.go new file mode 100644 index 0000000..4f36419 --- /dev/null +++ b/src/x86/ModRM_test.go @@ -0,0 +1,34 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestModRM(t *testing.T) { + testData := []struct{ mod, reg, rm, expected byte }{ + {0b_00, 0b_111, 0b_000, 0b_00_111_000}, + {0b_00, 0b_110, 0b_001, 0b_00_110_001}, + {0b_00, 0b_101, 0b_010, 0b_00_101_010}, + {0b_00, 0b_100, 0b_011, 0b_00_100_011}, + {0b_00, 0b_011, 0b_100, 0b_00_011_100}, + {0b_00, 0b_010, 0b_101, 0b_00_010_101}, + {0b_00, 0b_001, 0b_110, 0b_00_001_110}, + {0b_00, 0b_000, 0b_111, 0b_00_000_111}, + {0b_11, 0b_111, 0b_000, 0b_11_111_000}, + {0b_11, 0b_110, 0b_001, 0b_11_110_001}, + {0b_11, 0b_101, 0b_010, 0b_11_101_010}, + {0b_11, 0b_100, 0b_011, 0b_11_100_011}, + {0b_11, 0b_011, 0b_100, 0b_11_011_100}, + {0b_11, 0b_010, 0b_101, 0b_11_010_101}, + {0b_11, 0b_001, 0b_110, 0b_11_001_110}, + {0b_11, 0b_000, 0b_111, 0b_11_000_111}, + } + + for _, test := range testData { + modRM := x86.ModRM(test.mod, test.reg, test.rm) + assert.Equal(t, modRM, test.expected) + } +} \ No newline at end of file diff --git a/src/x86/Move.go b/src/x86/Move.go new file mode 100644 index 0000000..43bceae --- /dev/null +++ b/src/x86/Move.go @@ -0,0 +1,51 @@ +package x86 + +import ( + "encoding/binary" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/sizeof" +) + +// MoveRegisterNumber moves an integer into the given register. +func MoveRegisterNumber(code []byte, destination cpu.Register, number int) []byte { + w := byte(0) + + if sizeof.Signed(int64(number)) == 8 { + w = 1 + } + + if w == 0 && number < 0 { + return moveRegisterNumber32(code, destination, number) + } + + b := byte(0) + + if destination > 0b111 { + b = 1 + destination &= 0b111 + } + + if w != 0 || b != 0 { + code = append(code, REX(w, 0, 0, b)) + } + + code = append(code, 0xB8+byte(destination)) + + if w == 1 { + return binary.LittleEndian.AppendUint64(code, uint64(number)) + } + + return binary.LittleEndian.AppendUint32(code, uint32(number)) +} + +// moveRegisterNumber32 moves an integer into the given register and sign-extends the register. +func moveRegisterNumber32(code []byte, destination cpu.Register, number int) []byte { + code = encode(code, AddressDirect, 0, destination, 8, 0xC7) + return binary.LittleEndian.AppendUint32(code, uint32(number)) +} + +// MoveRegisterRegister copies a register to another register. +func MoveRegisterRegister(code []byte, destination cpu.Register, source cpu.Register) []byte { + return encode(code, AddressDirect, source, destination, 8, 0x89) +} \ No newline at end of file diff --git a/src/x86/Move_test.go b/src/x86/Move_test.go new file mode 100644 index 0000000..3c588cf --- /dev/null +++ b/src/x86/Move_test.go @@ -0,0 +1,108 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestMoveRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + // 32 bits + {x86.R0, 0x7FFFFFFF, []byte{0xB8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0xB9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0xBA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0xBB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0xBC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0xBD, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0xBE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0xBF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x41, 0xB8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x41, 0xB9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x41, 0xBA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x41, 0xBB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x41, 0xBC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x41, 0xBD, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x41, 0xBE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x41, 0xBF, 0xFF, 0xFF, 0xFF, 0x7F}}, + + // 64 bits + {x86.R0, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xBA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xBB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xBD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xBE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFFFFFFFFFF, []byte{0x48, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xB8, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xB9, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xBA, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xBB, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xBC, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xBD, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xBE, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFFFFFFFFFF, []byte{0x49, 0xBF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + + // Negative numbers + {x86.R0, -1, []byte{0x48, 0xC7, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R1, -1, []byte{0x48, 0xC7, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R2, -1, []byte{0x48, 0xC7, 0xC2, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R3, -1, []byte{0x48, 0xC7, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.SP, -1, []byte{0x48, 0xC7, 0xC4, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R5, -1, []byte{0x48, 0xC7, 0xC5, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R6, -1, []byte{0x48, 0xC7, 0xC6, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R7, -1, []byte{0x48, 0xC7, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R8, -1, []byte{0x49, 0xC7, 0xC0, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R9, -1, []byte{0x49, 0xC7, 0xC1, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R10, -1, []byte{0x49, 0xC7, 0xC2, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R11, -1, []byte{0x49, 0xC7, 0xC3, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R12, -1, []byte{0x49, 0xC7, 0xC4, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R13, -1, []byte{0x49, 0xC7, 0xC5, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R14, -1, []byte{0x49, 0xC7, 0xC6, 0xFF, 0xFF, 0xFF, 0xFF}}, + {x86.R15, -1, []byte{0x49, 0xC7, 0xC7, 0xFF, 0xFF, 0xFF, 0xFF}}, + } + + for _, pattern := range usagePatterns { + t.Logf("mov %s, %x", pattern.Register, pattern.Number) + code := x86.MoveRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestMoveRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x4C, 0x89, 0xF8}}, + {x86.R1, x86.R14, []byte{0x4C, 0x89, 0xF1}}, + {x86.R2, x86.R13, []byte{0x4C, 0x89, 0xEA}}, + {x86.R3, x86.R12, []byte{0x4C, 0x89, 0xE3}}, + {x86.SP, x86.R11, []byte{0x4C, 0x89, 0xDC}}, + {x86.R5, x86.R10, []byte{0x4C, 0x89, 0xD5}}, + {x86.R6, x86.R9, []byte{0x4C, 0x89, 0xCE}}, + {x86.R7, x86.R8, []byte{0x4C, 0x89, 0xC7}}, + {x86.R8, x86.R7, []byte{0x49, 0x89, 0xF8}}, + {x86.R9, x86.R6, []byte{0x49, 0x89, 0xF1}}, + {x86.R10, x86.R5, []byte{0x49, 0x89, 0xEA}}, + {x86.R11, x86.SP, []byte{0x49, 0x89, 0xE3}}, + {x86.R12, x86.R3, []byte{0x49, 0x89, 0xDC}}, + {x86.R13, x86.R2, []byte{0x49, 0x89, 0xD5}}, + {x86.R14, x86.R1, []byte{0x49, 0x89, 0xCE}}, + {x86.R15, x86.R0, []byte{0x49, 0x89, 0xC7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("mov %s, %s", pattern.Left, pattern.Right) + code := x86.MoveRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Mul.go b/src/x86/Mul.go new file mode 100644 index 0000000..511cbee --- /dev/null +++ b/src/x86/Mul.go @@ -0,0 +1,13 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// MulRegisterNumber multiplies a register with a number. +func MulRegisterNumber(code []byte, register cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, register, register, number, 0x6B, 0x69) +} + +// MulRegisterRegister multiplies a register with another register. +func MulRegisterRegister(code []byte, register cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, register, operand, 8, 0x0F, 0xAF) +} \ No newline at end of file diff --git a/src/x86/Mul_test.go b/src/x86/Mul_test.go new file mode 100644 index 0000000..630096c --- /dev/null +++ b/src/x86/Mul_test.go @@ -0,0 +1,88 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestMulRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0x6B, 0xC0, 0x01}}, + {x86.R1, 1, []byte{0x48, 0x6B, 0xC9, 0x01}}, + {x86.R2, 1, []byte{0x48, 0x6B, 0xD2, 0x01}}, + {x86.R3, 1, []byte{0x48, 0x6B, 0xDB, 0x01}}, + {x86.SP, 1, []byte{0x48, 0x6B, 0xE4, 0x01}}, + {x86.R5, 1, []byte{0x48, 0x6B, 0xED, 0x01}}, + {x86.R6, 1, []byte{0x48, 0x6B, 0xF6, 0x01}}, + {x86.R7, 1, []byte{0x48, 0x6B, 0xFF, 0x01}}, + {x86.R8, 1, []byte{0x4D, 0x6B, 0xC0, 0x01}}, + {x86.R9, 1, []byte{0x4D, 0x6B, 0xC9, 0x01}}, + {x86.R10, 1, []byte{0x4D, 0x6B, 0xD2, 0x01}}, + {x86.R11, 1, []byte{0x4D, 0x6B, 0xDB, 0x01}}, + {x86.R12, 1, []byte{0x4D, 0x6B, 0xE4, 0x01}}, + {x86.R13, 1, []byte{0x4D, 0x6B, 0xED, 0x01}}, + {x86.R14, 1, []byte{0x4D, 0x6B, 0xF6, 0x01}}, + {x86.R15, 1, []byte{0x4D, 0x6B, 0xFF, 0x01}}, + + {x86.R0, 0x7FFFFFFF, []byte{0x48, 0x69, 0xC0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0x48, 0x69, 0xC9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0x48, 0x69, 0xD2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0x48, 0x69, 0xDB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0x48, 0x69, 0xE4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0x48, 0x69, 0xED, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0x48, 0x69, 0xF6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0x48, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xC0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xC9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xD2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xDB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xE4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xED, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xF6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x4D, 0x69, 0xFF, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("mul %s, %x", pattern.Register, pattern.Number) + code := x86.MulRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestMulRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x49, 0x0F, 0xAF, 0xC7}}, + {x86.R1, x86.R14, []byte{0x49, 0x0F, 0xAF, 0xCE}}, + {x86.R2, x86.R13, []byte{0x49, 0x0F, 0xAF, 0xD5}}, + {x86.R3, x86.R12, []byte{0x49, 0x0F, 0xAF, 0xDC}}, + {x86.SP, x86.R11, []byte{0x49, 0x0F, 0xAF, 0xE3}}, + {x86.R5, x86.R10, []byte{0x49, 0x0F, 0xAF, 0xEA}}, + {x86.R6, x86.R9, []byte{0x49, 0x0F, 0xAF, 0xF1}}, + {x86.R7, x86.R8, []byte{0x49, 0x0F, 0xAF, 0xF8}}, + {x86.R8, x86.R7, []byte{0x4C, 0x0F, 0xAF, 0xC7}}, + {x86.R9, x86.R6, []byte{0x4C, 0x0F, 0xAF, 0xCE}}, + {x86.R10, x86.R5, []byte{0x4C, 0x0F, 0xAF, 0xD5}}, + {x86.R11, x86.SP, []byte{0x4C, 0x0F, 0xAF, 0xDC}}, + {x86.R12, x86.R3, []byte{0x4C, 0x0F, 0xAF, 0xE3}}, + {x86.R13, x86.R2, []byte{0x4C, 0x0F, 0xAF, 0xEA}}, + {x86.R14, x86.R1, []byte{0x4C, 0x0F, 0xAF, 0xF1}}, + {x86.R15, x86.R0, []byte{0x4C, 0x0F, 0xAF, 0xF8}}, + } + + for _, pattern := range usagePatterns { + t.Logf("mul %s, %s", pattern.Left, pattern.Right) + code := x86.MulRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Negate.go b/src/x86/Negate.go new file mode 100644 index 0000000..2ffcfe7 --- /dev/null +++ b/src/x86/Negate.go @@ -0,0 +1,8 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// NegateRegister negates the value in the register. +func NegateRegister(code []byte, register cpu.Register) []byte { + return encode(code, AddressDirect, 0b011, register, 8, 0xF7) +} \ No newline at end of file diff --git a/src/x86/Negate_test.go b/src/x86/Negate_test.go new file mode 100644 index 0000000..813d7c0 --- /dev/null +++ b/src/x86/Negate_test.go @@ -0,0 +1,39 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestNegateRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x86.R0, []byte{0x48, 0xF7, 0xD8}}, + {x86.R1, []byte{0x48, 0xF7, 0xD9}}, + {x86.R2, []byte{0x48, 0xF7, 0xDA}}, + {x86.R3, []byte{0x48, 0xF7, 0xDB}}, + {x86.SP, []byte{0x48, 0xF7, 0xDC}}, + {x86.R5, []byte{0x48, 0xF7, 0xDD}}, + {x86.R6, []byte{0x48, 0xF7, 0xDE}}, + {x86.R7, []byte{0x48, 0xF7, 0xDF}}, + {x86.R8, []byte{0x49, 0xF7, 0xD8}}, + {x86.R9, []byte{0x49, 0xF7, 0xD9}}, + {x86.R10, []byte{0x49, 0xF7, 0xDA}}, + {x86.R11, []byte{0x49, 0xF7, 0xDB}}, + {x86.R12, []byte{0x49, 0xF7, 0xDC}}, + {x86.R13, []byte{0x49, 0xF7, 0xDD}}, + {x86.R14, []byte{0x49, 0xF7, 0xDE}}, + {x86.R15, []byte{0x49, 0xF7, 0xDF}}, + } + + for _, pattern := range usagePatterns { + t.Logf("neg %s", pattern.Register) + code := x86.NegateRegister(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Or.go b/src/x86/Or.go new file mode 100644 index 0000000..5e17ba3 --- /dev/null +++ b/src/x86/Or.go @@ -0,0 +1,15 @@ +package x86 + +import ( + "git.urbach.dev/cli/q/src/cpu" +) + +// OrRegisterNumber performs a bitwise OR using a register and a number. +func OrRegisterNumber(code []byte, register cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b001, register, number, 0x83, 0x81) +} + +// OrRegisterRegister performs a bitwise OR using two registers. +func OrRegisterRegister(code []byte, register cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, register, 8, 0x09) +} \ No newline at end of file diff --git a/src/x86/Or_test.go b/src/x86/Or_test.go new file mode 100644 index 0000000..13b5a1e --- /dev/null +++ b/src/x86/Or_test.go @@ -0,0 +1,88 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestOrRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0x83, 0xC8, 0x01}}, + {x86.R1, 1, []byte{0x48, 0x83, 0xC9, 0x01}}, + {x86.R2, 1, []byte{0x48, 0x83, 0xCA, 0x01}}, + {x86.R3, 1, []byte{0x48, 0x83, 0xCB, 0x01}}, + {x86.SP, 1, []byte{0x48, 0x83, 0xCC, 0x01}}, + {x86.R5, 1, []byte{0x48, 0x83, 0xCD, 0x01}}, + {x86.R6, 1, []byte{0x48, 0x83, 0xCE, 0x01}}, + {x86.R7, 1, []byte{0x48, 0x83, 0xCF, 0x01}}, + {x86.R8, 1, []byte{0x49, 0x83, 0xC8, 0x01}}, + {x86.R9, 1, []byte{0x49, 0x83, 0xC9, 0x01}}, + {x86.R10, 1, []byte{0x49, 0x83, 0xCA, 0x01}}, + {x86.R11, 1, []byte{0x49, 0x83, 0xCB, 0x01}}, + {x86.R12, 1, []byte{0x49, 0x83, 0xCC, 0x01}}, + {x86.R13, 1, []byte{0x49, 0x83, 0xCD, 0x01}}, + {x86.R14, 1, []byte{0x49, 0x83, 0xCE, 0x01}}, + {x86.R15, 1, []byte{0x49, 0x83, 0xCF, 0x01}}, + + {x86.R0, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0x48, 0x81, 0xC9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0x48, 0x81, 0xCA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0x48, 0x81, 0xCB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0x48, 0x81, 0xCC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0x48, 0x81, 0xCD, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0x48, 0x81, 0xCE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0x48, 0x81, 0xCF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x49, 0x81, 0xC9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x49, 0x81, 0xCA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x49, 0x81, 0xCB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x49, 0x81, 0xCC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x49, 0x81, 0xCD, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x49, 0x81, 0xCE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x49, 0x81, 0xCF, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("or %s, %x", pattern.Register, pattern.Number) + code := x86.OrRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestOrRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x4C, 0x09, 0xF8}}, + {x86.R1, x86.R14, []byte{0x4C, 0x09, 0xF1}}, + {x86.R2, x86.R13, []byte{0x4C, 0x09, 0xEA}}, + {x86.R3, x86.R12, []byte{0x4C, 0x09, 0xE3}}, + {x86.SP, x86.R11, []byte{0x4C, 0x09, 0xDC}}, + {x86.R5, x86.R10, []byte{0x4C, 0x09, 0xD5}}, + {x86.R6, x86.R9, []byte{0x4C, 0x09, 0xCE}}, + {x86.R7, x86.R8, []byte{0x4C, 0x09, 0xC7}}, + {x86.R8, x86.R7, []byte{0x49, 0x09, 0xF8}}, + {x86.R9, x86.R6, []byte{0x49, 0x09, 0xF1}}, + {x86.R10, x86.R5, []byte{0x49, 0x09, 0xEA}}, + {x86.R11, x86.SP, []byte{0x49, 0x09, 0xE3}}, + {x86.R12, x86.R3, []byte{0x49, 0x09, 0xDC}}, + {x86.R13, x86.R2, []byte{0x49, 0x09, 0xD5}}, + {x86.R14, x86.R1, []byte{0x49, 0x09, 0xCE}}, + {x86.R15, x86.R0, []byte{0x49, 0x09, 0xC7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("or %s, %s", pattern.Left, pattern.Right) + code := x86.OrRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Pop.go b/src/x86/Pop.go new file mode 100644 index 0000000..d53b24c --- /dev/null +++ b/src/x86/Pop.go @@ -0,0 +1,16 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// PopRegister pops a value from the stack and saves it into the register. +func PopRegister(code []byte, register cpu.Register) []byte { + if register > 0b111 { + code = append(code, REX(0, 0, 0, 1)) + register &= 0b111 + } + + return append( + code, + 0x58+byte(register), + ) +} \ No newline at end of file diff --git a/src/x86/Pop_test.go b/src/x86/Pop_test.go new file mode 100644 index 0000000..3bf4bc6 --- /dev/null +++ b/src/x86/Pop_test.go @@ -0,0 +1,39 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestPopRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x86.R0, []byte{0x58}}, + {x86.R1, []byte{0x59}}, + {x86.R2, []byte{0x5A}}, + {x86.R3, []byte{0x5B}}, + {x86.SP, []byte{0x5C}}, + {x86.R5, []byte{0x5D}}, + {x86.R6, []byte{0x5E}}, + {x86.R7, []byte{0x5F}}, + {x86.R8, []byte{0x41, 0x58}}, + {x86.R9, []byte{0x41, 0x59}}, + {x86.R10, []byte{0x41, 0x5A}}, + {x86.R11, []byte{0x41, 0x5B}}, + {x86.R12, []byte{0x41, 0x5C}}, + {x86.R13, []byte{0x41, 0x5D}}, + {x86.R14, []byte{0x41, 0x5E}}, + {x86.R15, []byte{0x41, 0x5F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("pop %s", pattern.Register) + code := x86.PopRegister(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Push.go b/src/x86/Push.go new file mode 100644 index 0000000..8459a23 --- /dev/null +++ b/src/x86/Push.go @@ -0,0 +1,37 @@ +package x86 + +import ( + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/sizeof" +) + +// PushNumber pushes a number onto the stack. +func PushNumber(code []byte, number int32) []byte { + length := sizeof.Signed(number) + + if length >= 2 { + return append( + code, + 0x68, + byte(number), + byte(number>>8), + byte(number>>16), + byte(number>>24), + ) + } + + return append(code, 0x6A, byte(number)) +} + +// PushRegister pushes the value inside the register onto the stack. +func PushRegister(code []byte, register cpu.Register) []byte { + if register > 0b111 { + code = append(code, REX(0, 0, 0, 1)) + register &= 0b111 + } + + return append( + code, + 0x50+byte(register), + ) +} \ No newline at end of file diff --git a/src/x86/Push_test.go b/src/x86/Push_test.go new file mode 100644 index 0000000..406f975 --- /dev/null +++ b/src/x86/Push_test.go @@ -0,0 +1,61 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestPushNumber(t *testing.T) { + usagePatterns := []struct { + Number int32 + Code []byte + }{ + {0, []byte{0x6A, 0x00}}, + {1, []byte{0x6A, 0x01}}, + {-1, []byte{0x6A, 0xFF}}, + {127, []byte{0x6A, 0x7F}}, + {128, []byte{0x68, 0x80, 0x00, 0x00, 0x00}}, + {0xFF, []byte{0x68, 0xFF, 0x00, 0x00, 0x00}}, + {0xFFFF, []byte{0x68, 0xFF, 0xFF, 0x00, 0x00}}, + {0x7FFFFFFF, []byte{0x68, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("push %d", pattern.Number) + code := x86.PushNumber(nil, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestPushRegister(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Code []byte + }{ + {x86.R0, []byte{0x50}}, + {x86.R1, []byte{0x51}}, + {x86.R2, []byte{0x52}}, + {x86.R3, []byte{0x53}}, + {x86.SP, []byte{0x54}}, + {x86.R5, []byte{0x55}}, + {x86.R6, []byte{0x56}}, + {x86.R7, []byte{0x57}}, + {x86.R8, []byte{0x41, 0x50}}, + {x86.R9, []byte{0x41, 0x51}}, + {x86.R10, []byte{0x41, 0x52}}, + {x86.R11, []byte{0x41, 0x53}}, + {x86.R12, []byte{0x41, 0x54}}, + {x86.R13, []byte{0x41, 0x55}}, + {x86.R14, []byte{0x41, 0x56}}, + {x86.R15, []byte{0x41, 0x57}}, + } + + for _, pattern := range usagePatterns { + t.Logf("push %s", pattern.Register) + code := x86.PushRegister(nil, pattern.Register) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/REX.go b/src/x86/REX.go new file mode 100644 index 0000000..b6bb4db --- /dev/null +++ b/src/x86/REX.go @@ -0,0 +1,7 @@ +package x86 + +// REX is used to generate a REX prefix. +// w, r, x and b can only be set to either 0 or 1. +func REX(w, r, x, b byte) byte { + return 0b0100_0000 | (w << 3) | (r << 2) | (x << 1) | b +} \ No newline at end of file diff --git a/src/x86/REX_test.go b/src/x86/REX_test.go new file mode 100644 index 0000000..c2423c9 --- /dev/null +++ b/src/x86/REX_test.go @@ -0,0 +1,34 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestREX(t *testing.T) { + testData := []struct{ w, r, x, b, expected byte }{ + {0, 0, 0, 0, 0b_0100_0000}, + {0, 0, 0, 1, 0b_0100_0001}, + {0, 0, 1, 0, 0b_0100_0010}, + {0, 0, 1, 1, 0b_0100_0011}, + {0, 1, 0, 0, 0b_0100_0100}, + {0, 1, 0, 1, 0b_0100_0101}, + {0, 1, 1, 0, 0b_0100_0110}, + {0, 1, 1, 1, 0b_0100_0111}, + {1, 0, 0, 0, 0b_0100_1000}, + {1, 0, 0, 1, 0b_0100_1001}, + {1, 0, 1, 0, 0b_0100_1010}, + {1, 0, 1, 1, 0b_0100_1011}, + {1, 1, 0, 0, 0b_0100_1100}, + {1, 1, 0, 1, 0b_0100_1101}, + {1, 1, 1, 0, 0b_0100_1110}, + {1, 1, 1, 1, 0b_0100_1111}, + } + + for _, test := range testData { + rex := x86.REX(test.w, test.r, test.x, test.b) + assert.Equal(t, rex, test.expected) + } +} \ No newline at end of file diff --git a/src/x86/Registers.go b/src/x86/Registers.go new file mode 100644 index 0000000..daea8a2 --- /dev/null +++ b/src/x86/Registers.go @@ -0,0 +1,22 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +const ( + R0 cpu.Register = iota // RAX + R1 // RCX + R2 // RDX + R3 // RBX + SP // Stack pointer + R5 // RBP + R6 // RSI + R7 // RDI + R8 + R9 + R10 + R11 + R12 + R13 + R14 + R15 +) \ No newline at end of file diff --git a/src/x86/Return.go b/src/x86/Return.go new file mode 100644 index 0000000..48db12a --- /dev/null +++ b/src/x86/Return.go @@ -0,0 +1,7 @@ +package x86 + +// Return transfers program control to a return address located on the top of the stack. +// The address is usually placed on the stack by a Call instruction. +func Return(code []byte) []byte { + return append(code, 0xC3) +} \ No newline at end of file diff --git a/src/x86/Return_test.go b/src/x86/Return_test.go new file mode 100644 index 0000000..dc9563b --- /dev/null +++ b/src/x86/Return_test.go @@ -0,0 +1,12 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestReturn(t *testing.T) { + assert.DeepEqual(t, x86.Return(nil), []byte{0xC3}) +} \ No newline at end of file diff --git a/src/x86/SIB.go b/src/x86/SIB.go new file mode 100644 index 0000000..d23adc2 --- /dev/null +++ b/src/x86/SIB.go @@ -0,0 +1,19 @@ +package x86 + +// ScaleFactor encodes the scale factor. +type ScaleFactor = byte + +const ( + Scale1 = ScaleFactor(0b00) + Scale2 = ScaleFactor(0b01) + Scale4 = ScaleFactor(0b10) + Scale8 = ScaleFactor(0b11) +) + +// SIB is used to generate an SIB byte. +// - scale: 2 bits. Multiplies the value of the index. +// - index: 3 bits. Specifies the index register. +// - base: 3 bits. Specifies the base register. +func SIB(scale ScaleFactor, index byte, base byte) byte { + return (scale << 6) | (index << 3) | base +} \ No newline at end of file diff --git a/src/x86/SIB_test.go b/src/x86/SIB_test.go new file mode 100644 index 0000000..4be060d --- /dev/null +++ b/src/x86/SIB_test.go @@ -0,0 +1,34 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestSIB(t *testing.T) { + testData := []struct{ scale, index, base, expected byte }{ + {0b_00, 0b_111, 0b_000, 0b_00_111_000}, + {0b_00, 0b_110, 0b_001, 0b_00_110_001}, + {0b_00, 0b_101, 0b_010, 0b_00_101_010}, + {0b_00, 0b_100, 0b_011, 0b_00_100_011}, + {0b_00, 0b_011, 0b_100, 0b_00_011_100}, + {0b_00, 0b_010, 0b_101, 0b_00_010_101}, + {0b_00, 0b_001, 0b_110, 0b_00_001_110}, + {0b_00, 0b_000, 0b_111, 0b_00_000_111}, + {0b_11, 0b_111, 0b_000, 0b_11_111_000}, + {0b_11, 0b_110, 0b_001, 0b_11_110_001}, + {0b_11, 0b_101, 0b_010, 0b_11_101_010}, + {0b_11, 0b_100, 0b_011, 0b_11_100_011}, + {0b_11, 0b_011, 0b_100, 0b_11_011_100}, + {0b_11, 0b_010, 0b_101, 0b_11_010_101}, + {0b_11, 0b_001, 0b_110, 0b_11_001_110}, + {0b_11, 0b_000, 0b_111, 0b_11_000_111}, + } + + for _, test := range testData { + sib := x86.SIB(test.scale, test.index, test.base) + assert.Equal(t, sib, test.expected) + } +} \ No newline at end of file diff --git a/src/x86/Shift.go b/src/x86/Shift.go new file mode 100644 index 0000000..fd91e0a --- /dev/null +++ b/src/x86/Shift.go @@ -0,0 +1,17 @@ +package x86 + +import ( + "git.urbach.dev/cli/q/src/cpu" +) + +// ShiftLeftNumber shifts the register value by `bitCount` bits to the left. +func ShiftLeftNumber(code []byte, register cpu.Register, bitCount byte) []byte { + code = encode(code, AddressDirect, 0b100, register, 8, 0xC1) + return append(code, bitCount) +} + +// ShiftRightSignedNumber shifts the signed register value by `bitCount` bits to the right. +func ShiftRightSignedNumber(code []byte, register cpu.Register, bitCount byte) []byte { + code = encode(code, AddressDirect, 0b111, register, 8, 0xC1) + return append(code, bitCount) +} \ No newline at end of file diff --git a/src/x86/Shift_test.go b/src/x86/Shift_test.go new file mode 100644 index 0000000..904af69 --- /dev/null +++ b/src/x86/Shift_test.go @@ -0,0 +1,71 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestShiftLeftNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0xC1, 0xE0, 0x01}}, + {x86.R1, 1, []byte{0x48, 0xC1, 0xE1, 0x01}}, + {x86.R2, 1, []byte{0x48, 0xC1, 0xE2, 0x01}}, + {x86.R3, 1, []byte{0x48, 0xC1, 0xE3, 0x01}}, + {x86.SP, 1, []byte{0x48, 0xC1, 0xE4, 0x01}}, + {x86.R5, 1, []byte{0x48, 0xC1, 0xE5, 0x01}}, + {x86.R6, 1, []byte{0x48, 0xC1, 0xE6, 0x01}}, + {x86.R7, 1, []byte{0x48, 0xC1, 0xE7, 0x01}}, + {x86.R8, 1, []byte{0x49, 0xC1, 0xE0, 0x01}}, + {x86.R9, 1, []byte{0x49, 0xC1, 0xE1, 0x01}}, + {x86.R10, 1, []byte{0x49, 0xC1, 0xE2, 0x01}}, + {x86.R11, 1, []byte{0x49, 0xC1, 0xE3, 0x01}}, + {x86.R12, 1, []byte{0x49, 0xC1, 0xE4, 0x01}}, + {x86.R13, 1, []byte{0x49, 0xC1, 0xE5, 0x01}}, + {x86.R14, 1, []byte{0x49, 0xC1, 0xE6, 0x01}}, + {x86.R15, 1, []byte{0x49, 0xC1, 0xE7, 0x01}}, + } + + for _, pattern := range usagePatterns { + t.Logf("shl %s, %x", pattern.Register, pattern.Number) + code := x86.ShiftLeftNumber(nil, pattern.Register, byte(pattern.Number)) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestShiftRightSignedNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0xC1, 0xF8, 0x01}}, + {x86.R1, 1, []byte{0x48, 0xC1, 0xF9, 0x01}}, + {x86.R2, 1, []byte{0x48, 0xC1, 0xFA, 0x01}}, + {x86.R3, 1, []byte{0x48, 0xC1, 0xFB, 0x01}}, + {x86.SP, 1, []byte{0x48, 0xC1, 0xFC, 0x01}}, + {x86.R5, 1, []byte{0x48, 0xC1, 0xFD, 0x01}}, + {x86.R6, 1, []byte{0x48, 0xC1, 0xFE, 0x01}}, + {x86.R7, 1, []byte{0x48, 0xC1, 0xFF, 0x01}}, + {x86.R8, 1, []byte{0x49, 0xC1, 0xF8, 0x01}}, + {x86.R9, 1, []byte{0x49, 0xC1, 0xF9, 0x01}}, + {x86.R10, 1, []byte{0x49, 0xC1, 0xFA, 0x01}}, + {x86.R11, 1, []byte{0x49, 0xC1, 0xFB, 0x01}}, + {x86.R12, 1, []byte{0x49, 0xC1, 0xFC, 0x01}}, + {x86.R13, 1, []byte{0x49, 0xC1, 0xFD, 0x01}}, + {x86.R14, 1, []byte{0x49, 0xC1, 0xFE, 0x01}}, + {x86.R15, 1, []byte{0x49, 0xC1, 0xFF, 0x01}}, + } + + for _, pattern := range usagePatterns { + t.Logf("sar %s, %x", pattern.Register, pattern.Number) + code := x86.ShiftRightSignedNumber(nil, pattern.Register, byte(pattern.Number)) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Store.go b/src/x86/Store.go new file mode 100644 index 0000000..8779881 --- /dev/null +++ b/src/x86/Store.go @@ -0,0 +1,27 @@ +package x86 + +import ( + "encoding/binary" + + "git.urbach.dev/cli/q/src/cpu" +) + +// StoreNumber writes a number to a memory address. +func StoreNumber(code []byte, base cpu.Register, offset int8, length byte, number int) []byte { + code = memAccess(code, 0xC6, 0xC7, 0b000, base, offset, length) + + switch length { + case 8, 4: + return binary.LittleEndian.AppendUint32(code, uint32(number)) + + case 2: + return binary.LittleEndian.AppendUint16(code, uint16(number)) + } + + return append(code, byte(number)) +} + +// StoreRegister writes the contents of the register to a memory address. +func StoreRegister(code []byte, base cpu.Register, offset int8, length byte, register cpu.Register) []byte { + return memAccess(code, 0x88, 0x89, register, base, offset, length) +} \ No newline at end of file diff --git a/src/x86/StoreDynamic.go b/src/x86/StoreDynamic.go new file mode 100644 index 0000000..1bc8e08 --- /dev/null +++ b/src/x86/StoreDynamic.go @@ -0,0 +1,27 @@ +package x86 + +import ( + "encoding/binary" + + "git.urbach.dev/cli/q/src/cpu" +) + +// StoreDynamicNumber writes a number to a memory address with a register offset. +func StoreDynamicNumber(code []byte, base cpu.Register, offset cpu.Register, length byte, number int) []byte { + code = memAccessDynamic(code, 0xC6, 0xC7, 0b000, base, offset, length) + + switch length { + case 8, 4: + return binary.LittleEndian.AppendUint32(code, uint32(number)) + + case 2: + return binary.LittleEndian.AppendUint16(code, uint16(number)) + } + + return append(code, byte(number)) +} + +// StoreDynamicRegister writes the contents of a register to a memory address with a register offset. +func StoreDynamicRegister(code []byte, base cpu.Register, offset cpu.Register, length byte, source cpu.Register) []byte { + return memAccessDynamic(code, 0x88, 0x89, source, base, offset, length) +} \ No newline at end of file diff --git a/src/x86/StoreDynamic_test.go b/src/x86/StoreDynamic_test.go new file mode 100644 index 0000000..e1efac3 --- /dev/null +++ b/src/x86/StoreDynamic_test.go @@ -0,0 +1,171 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestStoreDynamicNumber(t *testing.T) { + usagePatterns := []struct { + RegisterTo cpu.Register + Offset cpu.Register + Length byte + Number int + Code []byte + }{ + {x86.R0, x86.R15, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R0, x86.R15, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R0, x86.R15, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x38, 0x7F, 0x00}}, + {x86.R0, x86.R15, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x38, 0x7F}}, + {x86.R1, x86.R14, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R1, x86.R14, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R1, x86.R14, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x31, 0x7F, 0x00}}, + {x86.R1, x86.R14, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x31, 0x7F}}, + {x86.R2, x86.R13, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R2, x86.R13, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R2, x86.R13, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x2A, 0x7F, 0x00}}, + {x86.R2, x86.R13, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x2A, 0x7F}}, + {x86.R3, x86.R12, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x23, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R3, x86.R12, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x23, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R3, x86.R12, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x23, 0x7F, 0x00}}, + {x86.R3, x86.R12, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x23, 0x7F}}, + {x86.SP, x86.R11, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x86.SP, x86.R11, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x86.SP, x86.R11, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00}}, + {x86.SP, x86.R11, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x1C, 0x7F}}, + {x86.R5, x86.R10, 8, 0x7F, []byte{0x4A, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R5, x86.R10, 4, 0x7F, []byte{0x42, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R5, x86.R10, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00}}, + {x86.R5, x86.R10, 1, 0x7F, []byte{0x42, 0xC6, 0x44, 0x15, 0x00, 0x7F}}, + {x86.R6, x86.R9, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R6, x86.R9, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R6, x86.R9, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x0E, 0x7F, 0x00}}, + {x86.R6, x86.R9, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x0E, 0x7F}}, + {x86.R7, x86.R8, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R7, x86.R8, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R7, x86.R8, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x07, 0x7F, 0x00}}, + {x86.R7, x86.R8, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x07, 0x7F}}, + {x86.R8, x86.R7, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R8, x86.R7, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x38, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R8, x86.R7, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x38, 0x7F, 0x00}}, + {x86.R8, x86.R7, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x38, 0x7F}}, + {x86.R9, x86.R6, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R9, x86.R6, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x31, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R9, x86.R6, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x31, 0x7F, 0x00}}, + {x86.R9, x86.R6, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x31, 0x7F}}, + {x86.R10, x86.R5, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R10, x86.R5, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x2A, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R10, x86.R5, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x2A, 0x7F, 0x00}}, + {x86.R10, x86.R5, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x2A, 0x7F}}, + {x86.R11, x86.SP, 8, 0x7F, []byte{0x4A, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R11, x86.SP, 4, 0x7F, []byte{0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R11, x86.SP, 2, 0x7F, []byte{0x66, 0x42, 0xC7, 0x04, 0x1C, 0x7F, 0x00}}, + {x86.R11, x86.SP, 1, 0x7F, []byte{0x42, 0xC6, 0x04, 0x1C, 0x7F}}, + {x86.R12, x86.R3, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R12, x86.R3, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x1C, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R12, x86.R3, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x1C, 0x7F, 0x00}}, + {x86.R12, x86.R3, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x1C, 0x7F}}, + {x86.R13, x86.R2, 8, 0x7F, []byte{0x49, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R13, x86.R2, 4, 0x7F, []byte{0x41, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R13, x86.R2, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x44, 0x15, 0x00, 0x7F, 0x00}}, + {x86.R13, x86.R2, 1, 0x7F, []byte{0x41, 0xC6, 0x44, 0x15, 0x00, 0x7F}}, + {x86.R14, x86.R1, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R14, x86.R1, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x0E, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R14, x86.R1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x0E, 0x7F, 0x00}}, + {x86.R14, x86.R1, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x0E, 0x7F}}, + {x86.R15, x86.R0, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R15, x86.R0, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R15, x86.R0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x07, 0x7F, 0x00}}, + {x86.R15, x86.R0, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x07, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("store %dB [%s+%s], %d", pattern.Length, pattern.RegisterTo, pattern.Offset, pattern.Number) + code := x86.StoreDynamicNumber(nil, pattern.RegisterTo, pattern.Offset, pattern.Length, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestStoreDynamicRegister(t *testing.T) { + usagePatterns := []struct { + RegisterTo cpu.Register + Offset cpu.Register + Length byte + RegisterFrom cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, 8, x86.R15, []byte{0x4E, 0x89, 0x3C, 0x38}}, + {x86.R0, x86.R15, 4, x86.R15, []byte{0x46, 0x89, 0x3C, 0x38}}, + {x86.R0, x86.R15, 2, x86.R15, []byte{0x66, 0x46, 0x89, 0x3C, 0x38}}, + {x86.R0, x86.R15, 1, x86.R15, []byte{0x46, 0x88, 0x3C, 0x38}}, + {x86.R1, x86.R14, 8, x86.R14, []byte{0x4E, 0x89, 0x34, 0x31}}, + {x86.R1, x86.R14, 4, x86.R14, []byte{0x46, 0x89, 0x34, 0x31}}, + {x86.R1, x86.R14, 2, x86.R14, []byte{0x66, 0x46, 0x89, 0x34, 0x31}}, + {x86.R1, x86.R14, 1, x86.R14, []byte{0x46, 0x88, 0x34, 0x31}}, + {x86.R2, x86.R13, 8, x86.R13, []byte{0x4E, 0x89, 0x2C, 0x2A}}, + {x86.R2, x86.R13, 4, x86.R13, []byte{0x46, 0x89, 0x2C, 0x2A}}, + {x86.R2, x86.R13, 2, x86.R13, []byte{0x66, 0x46, 0x89, 0x2C, 0x2A}}, + {x86.R2, x86.R13, 1, x86.R13, []byte{0x46, 0x88, 0x2C, 0x2A}}, + {x86.R3, x86.R12, 8, x86.R12, []byte{0x4E, 0x89, 0x24, 0x23}}, + {x86.R3, x86.R12, 4, x86.R12, []byte{0x46, 0x89, 0x24, 0x23}}, + {x86.R3, x86.R12, 2, x86.R12, []byte{0x66, 0x46, 0x89, 0x24, 0x23}}, + {x86.R3, x86.R12, 1, x86.R12, []byte{0x46, 0x88, 0x24, 0x23}}, + {x86.SP, x86.R11, 8, x86.R11, []byte{0x4E, 0x89, 0x1C, 0x1C}}, + {x86.SP, x86.R11, 4, x86.R11, []byte{0x46, 0x89, 0x1C, 0x1C}}, + {x86.SP, x86.R11, 2, x86.R11, []byte{0x66, 0x46, 0x89, 0x1C, 0x1C}}, + {x86.SP, x86.R11, 1, x86.R11, []byte{0x46, 0x88, 0x1C, 0x1C}}, + {x86.R5, x86.R10, 8, x86.R10, []byte{0x4E, 0x89, 0x54, 0x15, 0x00}}, + {x86.R5, x86.R10, 4, x86.R10, []byte{0x46, 0x89, 0x54, 0x15, 0x00}}, + {x86.R5, x86.R10, 2, x86.R10, []byte{0x66, 0x46, 0x89, 0x54, 0x15, 0x00}}, + {x86.R5, x86.R10, 1, x86.R10, []byte{0x46, 0x88, 0x54, 0x15, 0x00}}, + {x86.R6, x86.R9, 8, x86.R9, []byte{0x4E, 0x89, 0x0C, 0x0E}}, + {x86.R6, x86.R9, 4, x86.R9, []byte{0x46, 0x89, 0x0C, 0x0E}}, + {x86.R6, x86.R9, 2, x86.R9, []byte{0x66, 0x46, 0x89, 0x0C, 0x0E}}, + {x86.R6, x86.R9, 1, x86.R9, []byte{0x46, 0x88, 0x0C, 0x0E}}, + {x86.R7, x86.R8, 8, x86.R8, []byte{0x4E, 0x89, 0x04, 0x07}}, + {x86.R7, x86.R8, 4, x86.R8, []byte{0x46, 0x89, 0x04, 0x07}}, + {x86.R7, x86.R8, 2, x86.R8, []byte{0x66, 0x46, 0x89, 0x04, 0x07}}, + {x86.R7, x86.R8, 1, x86.R8, []byte{0x46, 0x88, 0x04, 0x07}}, + {x86.R8, x86.R7, 8, x86.R7, []byte{0x49, 0x89, 0x3C, 0x38}}, + {x86.R8, x86.R7, 4, x86.R7, []byte{0x41, 0x89, 0x3C, 0x38}}, + {x86.R8, x86.R7, 2, x86.R7, []byte{0x66, 0x41, 0x89, 0x3C, 0x38}}, + {x86.R8, x86.R7, 1, x86.R7, []byte{0x41, 0x88, 0x3C, 0x38}}, + {x86.R9, x86.R6, 8, x86.R6, []byte{0x49, 0x89, 0x34, 0x31}}, + {x86.R9, x86.R6, 4, x86.R6, []byte{0x41, 0x89, 0x34, 0x31}}, + {x86.R9, x86.R6, 2, x86.R6, []byte{0x66, 0x41, 0x89, 0x34, 0x31}}, + {x86.R9, x86.R6, 1, x86.R6, []byte{0x41, 0x88, 0x34, 0x31}}, + {x86.R10, x86.R5, 8, x86.R5, []byte{0x49, 0x89, 0x2C, 0x2A}}, + {x86.R10, x86.R5, 4, x86.R5, []byte{0x41, 0x89, 0x2C, 0x2A}}, + {x86.R10, x86.R5, 2, x86.R5, []byte{0x66, 0x41, 0x89, 0x2C, 0x2A}}, + {x86.R10, x86.R5, 1, x86.R5, []byte{0x41, 0x88, 0x2C, 0x2A}}, + {x86.R11, x86.SP, 8, x86.SP, []byte{0x4A, 0x89, 0x24, 0x1C}}, + {x86.R11, x86.SP, 4, x86.SP, []byte{0x42, 0x89, 0x24, 0x1C}}, + {x86.R11, x86.SP, 2, x86.SP, []byte{0x66, 0x42, 0x89, 0x24, 0x1C}}, + {x86.R11, x86.SP, 1, x86.SP, []byte{0x42, 0x88, 0x24, 0x1C}}, + {x86.R12, x86.R3, 8, x86.R3, []byte{0x49, 0x89, 0x1C, 0x1C}}, + {x86.R12, x86.R3, 4, x86.R3, []byte{0x41, 0x89, 0x1C, 0x1C}}, + {x86.R12, x86.R3, 2, x86.R3, []byte{0x66, 0x41, 0x89, 0x1C, 0x1C}}, + {x86.R12, x86.R3, 1, x86.R3, []byte{0x41, 0x88, 0x1C, 0x1C}}, + {x86.R13, x86.R2, 8, x86.R2, []byte{0x49, 0x89, 0x54, 0x15, 0x00}}, + {x86.R13, x86.R2, 4, x86.R2, []byte{0x41, 0x89, 0x54, 0x15, 0x00}}, + {x86.R13, x86.R2, 2, x86.R2, []byte{0x66, 0x41, 0x89, 0x54, 0x15, 0x00}}, + {x86.R13, x86.R2, 1, x86.R2, []byte{0x41, 0x88, 0x54, 0x15, 0x00}}, + {x86.R14, x86.R1, 8, x86.R1, []byte{0x49, 0x89, 0x0C, 0x0E}}, + {x86.R14, x86.R1, 4, x86.R1, []byte{0x41, 0x89, 0x0C, 0x0E}}, + {x86.R14, x86.R1, 2, x86.R1, []byte{0x66, 0x41, 0x89, 0x0C, 0x0E}}, + {x86.R14, x86.R1, 1, x86.R1, []byte{0x41, 0x88, 0x0C, 0x0E}}, + {x86.R15, x86.R0, 8, x86.R0, []byte{0x49, 0x89, 0x04, 0x07}}, + {x86.R15, x86.R0, 4, x86.R0, []byte{0x41, 0x89, 0x04, 0x07}}, + {x86.R15, x86.R0, 2, x86.R0, []byte{0x66, 0x41, 0x89, 0x04, 0x07}}, + {x86.R15, x86.R0, 1, x86.R0, []byte{0x41, 0x88, 0x04, 0x07}}, + } + + for _, pattern := range usagePatterns { + t.Logf("store %dB [%s+%s], %s", pattern.Length, pattern.RegisterTo, pattern.Offset, pattern.RegisterFrom) + code := x86.StoreDynamicRegister(nil, pattern.RegisterTo, pattern.Offset, pattern.Length, pattern.RegisterFrom) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Store_test.go b/src/x86/Store_test.go new file mode 100644 index 0000000..43f168c --- /dev/null +++ b/src/x86/Store_test.go @@ -0,0 +1,305 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestStoreNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Offset int8 + Length byte + Number int + Code []byte + }{ + // No offset + {x86.R0, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R0, 0, 4, 0x7F, []byte{0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R0, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x00, 0x7F, 0x00}}, + {x86.R0, 0, 1, 0x7F, []byte{0xC6, 0x00, 0x7F}}, + {x86.R1, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R1, 0, 4, 0x7F, []byte{0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R1, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x01, 0x7F, 0x00}}, + {x86.R1, 0, 1, 0x7F, []byte{0xC6, 0x01, 0x7F}}, + {x86.R2, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R2, 0, 4, 0x7F, []byte{0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R2, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x02, 0x7F, 0x00}}, + {x86.R2, 0, 1, 0x7F, []byte{0xC6, 0x02, 0x7F}}, + {x86.R3, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R3, 0, 4, 0x7F, []byte{0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R3, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x03, 0x7F, 0x00}}, + {x86.R3, 0, 1, 0x7F, []byte{0xC6, 0x03, 0x7F}}, + {x86.SP, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x86.SP, 0, 4, 0x7F, []byte{0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x86.SP, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x04, 0x24, 0x7F, 0x00}}, + {x86.SP, 0, 1, 0x7F, []byte{0xC6, 0x04, 0x24, 0x7F}}, + {x86.R5, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R5, 0, 4, 0x7F, []byte{0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R5, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x45, 0x00, 0x7F, 0x00}}, + {x86.R5, 0, 1, 0x7F, []byte{0xC6, 0x45, 0x00, 0x7F}}, + {x86.R6, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R6, 0, 4, 0x7F, []byte{0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R6, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x06, 0x7F, 0x00}}, + {x86.R6, 0, 1, 0x7F, []byte{0xC6, 0x06, 0x7F}}, + {x86.R7, 0, 8, 0x7F, []byte{0x48, 0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R7, 0, 4, 0x7F, []byte{0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R7, 0, 2, 0x7F, []byte{0x66, 0xC7, 0x07, 0x7F, 0x00}}, + {x86.R7, 0, 1, 0x7F, []byte{0xC6, 0x07, 0x7F}}, + {x86.R8, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R8, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R8, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x00, 0x7F, 0x00}}, + {x86.R8, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x00, 0x7F}}, + {x86.R9, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R9, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R9, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x01, 0x7F, 0x00}}, + {x86.R9, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x01, 0x7F}}, + {x86.R10, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R10, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x02, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R10, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x02, 0x7F, 0x00}}, + {x86.R10, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x02, 0x7F}}, + {x86.R11, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R11, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x03, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R11, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x03, 0x7F, 0x00}}, + {x86.R11, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x03, 0x7F}}, + {x86.R12, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R12, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x04, 0x24, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R12, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x04, 0x24, 0x7F, 0x00}}, + {x86.R12, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x04, 0x24, 0x7F}}, + {x86.R13, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R13, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x45, 0x00, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R13, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x45, 0x00, 0x7F, 0x00}}, + {x86.R13, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x45, 0x00, 0x7F}}, + {x86.R14, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R14, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x06, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R14, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x06, 0x7F, 0x00}}, + {x86.R14, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x06, 0x7F}}, + {x86.R15, 0, 8, 0x7F, []byte{0x49, 0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R15, 0, 4, 0x7F, []byte{0x41, 0xC7, 0x07, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R15, 0, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x07, 0x7F, 0x00}}, + {x86.R15, 0, 1, 0x7F, []byte{0x41, 0xC6, 0x07, 0x7F}}, + + // Offset of 1 + {x86.R0, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R0, 1, 4, 0x7F, []byte{0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R0, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x40, 0x01, 0x7F, 0x00}}, + {x86.R0, 1, 1, 0x7F, []byte{0xC6, 0x40, 0x01, 0x7F}}, + {x86.R1, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R1, 1, 4, 0x7F, []byte{0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R1, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x41, 0x01, 0x7F, 0x00}}, + {x86.R1, 1, 1, 0x7F, []byte{0xC6, 0x41, 0x01, 0x7F}}, + {x86.R2, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R2, 1, 4, 0x7F, []byte{0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R2, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x42, 0x01, 0x7F, 0x00}}, + {x86.R2, 1, 1, 0x7F, []byte{0xC6, 0x42, 0x01, 0x7F}}, + {x86.R3, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R3, 1, 4, 0x7F, []byte{0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R3, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x43, 0x01, 0x7F, 0x00}}, + {x86.R3, 1, 1, 0x7F, []byte{0xC6, 0x43, 0x01, 0x7F}}, + {x86.SP, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.SP, 1, 4, 0x7F, []byte{0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.SP, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00}}, + {x86.SP, 1, 1, 0x7F, []byte{0xC6, 0x44, 0x24, 0x01, 0x7F}}, + {x86.R5, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R5, 1, 4, 0x7F, []byte{0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R5, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x45, 0x01, 0x7F, 0x00}}, + {x86.R5, 1, 1, 0x7F, []byte{0xC6, 0x45, 0x01, 0x7F}}, + {x86.R6, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R6, 1, 4, 0x7F, []byte{0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R6, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x46, 0x01, 0x7F, 0x00}}, + {x86.R6, 1, 1, 0x7F, []byte{0xC6, 0x46, 0x01, 0x7F}}, + {x86.R7, 1, 8, 0x7F, []byte{0x48, 0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R7, 1, 4, 0x7F, []byte{0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R7, 1, 2, 0x7F, []byte{0x66, 0xC7, 0x47, 0x01, 0x7F, 0x00}}, + {x86.R7, 1, 1, 0x7F, []byte{0xC6, 0x47, 0x01, 0x7F}}, + {x86.R8, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R8, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x40, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R8, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x40, 0x01, 0x7F, 0x00}}, + {x86.R8, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x40, 0x01, 0x7F}}, + {x86.R9, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R9, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x41, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R9, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x41, 0x01, 0x7F, 0x00}}, + {x86.R9, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x41, 0x01, 0x7F}}, + {x86.R10, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R10, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x42, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R10, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x42, 0x01, 0x7F, 0x00}}, + {x86.R10, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x42, 0x01, 0x7F}}, + {x86.R11, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R11, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x43, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R11, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x43, 0x01, 0x7F, 0x00}}, + {x86.R11, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x43, 0x01, 0x7F}}, + {x86.R12, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R12, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R12, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x44, 0x24, 0x01, 0x7F, 0x00}}, + {x86.R12, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x44, 0x24, 0x01, 0x7F}}, + {x86.R13, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R13, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x45, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R13, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x45, 0x01, 0x7F, 0x00}}, + {x86.R13, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x45, 0x01, 0x7F}}, + {x86.R14, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R14, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x46, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R14, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x46, 0x01, 0x7F, 0x00}}, + {x86.R14, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x46, 0x01, 0x7F}}, + {x86.R15, 1, 8, 0x7F, []byte{0x49, 0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R15, 1, 4, 0x7F, []byte{0x41, 0xC7, 0x47, 0x01, 0x7F, 0x00, 0x00, 0x00}}, + {x86.R15, 1, 2, 0x7F, []byte{0x66, 0x41, 0xC7, 0x47, 0x01, 0x7F, 0x00}}, + {x86.R15, 1, 1, 0x7F, []byte{0x41, 0xC6, 0x47, 0x01, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("store %dB [%s+%d], %d", pattern.Length, pattern.Register, pattern.Offset, pattern.Number) + code := x86.StoreNumber(nil, pattern.Register, pattern.Offset, pattern.Length, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestStoreRegister(t *testing.T) { + usagePatterns := []struct { + RegisterTo cpu.Register + Offset int8 + Length byte + RegisterFrom cpu.Register + Code []byte + }{ + // No offset + {x86.R0, 0, 8, x86.R15, []byte{0x4C, 0x89, 0x38}}, + {x86.R0, 0, 4, x86.R15, []byte{0x44, 0x89, 0x38}}, + {x86.R0, 0, 2, x86.R15, []byte{0x66, 0x44, 0x89, 0x38}}, + {x86.R0, 0, 1, x86.R15, []byte{0x44, 0x88, 0x38}}, + {x86.R1, 0, 8, x86.R14, []byte{0x4C, 0x89, 0x31}}, + {x86.R1, 0, 4, x86.R14, []byte{0x44, 0x89, 0x31}}, + {x86.R1, 0, 2, x86.R14, []byte{0x66, 0x44, 0x89, 0x31}}, + {x86.R1, 0, 1, x86.R14, []byte{0x44, 0x88, 0x31}}, + {x86.R2, 0, 8, x86.R13, []byte{0x4C, 0x89, 0x2A}}, + {x86.R2, 0, 4, x86.R13, []byte{0x44, 0x89, 0x2A}}, + {x86.R2, 0, 2, x86.R13, []byte{0x66, 0x44, 0x89, 0x2A}}, + {x86.R2, 0, 1, x86.R13, []byte{0x44, 0x88, 0x2A}}, + {x86.R3, 0, 8, x86.R12, []byte{0x4C, 0x89, 0x23}}, + {x86.R3, 0, 4, x86.R12, []byte{0x44, 0x89, 0x23}}, + {x86.R3, 0, 2, x86.R12, []byte{0x66, 0x44, 0x89, 0x23}}, + {x86.R3, 0, 1, x86.R12, []byte{0x44, 0x88, 0x23}}, + {x86.SP, 0, 8, x86.R11, []byte{0x4C, 0x89, 0x1C, 0x24}}, + {x86.SP, 0, 4, x86.R11, []byte{0x44, 0x89, 0x1C, 0x24}}, + {x86.SP, 0, 2, x86.R11, []byte{0x66, 0x44, 0x89, 0x1C, 0x24}}, + {x86.SP, 0, 1, x86.R11, []byte{0x44, 0x88, 0x1C, 0x24}}, + {x86.R5, 0, 8, x86.R10, []byte{0x4C, 0x89, 0x55, 0x00}}, + {x86.R5, 0, 4, x86.R10, []byte{0x44, 0x89, 0x55, 0x00}}, + {x86.R5, 0, 2, x86.R10, []byte{0x66, 0x44, 0x89, 0x55, 0x00}}, + {x86.R5, 0, 1, x86.R10, []byte{0x44, 0x88, 0x55, 0x00}}, + {x86.R6, 0, 8, x86.R9, []byte{0x4C, 0x89, 0x0E}}, + {x86.R6, 0, 4, x86.R9, []byte{0x44, 0x89, 0x0E}}, + {x86.R6, 0, 2, x86.R9, []byte{0x66, 0x44, 0x89, 0x0E}}, + {x86.R6, 0, 1, x86.R9, []byte{0x44, 0x88, 0x0E}}, + {x86.R7, 0, 8, x86.R8, []byte{0x4C, 0x89, 0x07}}, + {x86.R7, 0, 4, x86.R8, []byte{0x44, 0x89, 0x07}}, + {x86.R7, 0, 2, x86.R8, []byte{0x66, 0x44, 0x89, 0x07}}, + {x86.R7, 0, 1, x86.R8, []byte{0x44, 0x88, 0x07}}, + {x86.R8, 0, 8, x86.R7, []byte{0x49, 0x89, 0x38}}, + {x86.R8, 0, 4, x86.R7, []byte{0x41, 0x89, 0x38}}, + {x86.R8, 0, 2, x86.R7, []byte{0x66, 0x41, 0x89, 0x38}}, + {x86.R8, 0, 1, x86.R7, []byte{0x41, 0x88, 0x38}}, + {x86.R9, 0, 8, x86.R6, []byte{0x49, 0x89, 0x31}}, + {x86.R9, 0, 4, x86.R6, []byte{0x41, 0x89, 0x31}}, + {x86.R9, 0, 2, x86.R6, []byte{0x66, 0x41, 0x89, 0x31}}, + {x86.R9, 0, 1, x86.R6, []byte{0x41, 0x88, 0x31}}, + {x86.R10, 0, 8, x86.R5, []byte{0x49, 0x89, 0x2A}}, + {x86.R10, 0, 4, x86.R5, []byte{0x41, 0x89, 0x2A}}, + {x86.R10, 0, 2, x86.R5, []byte{0x66, 0x41, 0x89, 0x2A}}, + {x86.R10, 0, 1, x86.R5, []byte{0x41, 0x88, 0x2A}}, + {x86.R11, 0, 8, x86.SP, []byte{0x49, 0x89, 0x23}}, + {x86.R11, 0, 4, x86.SP, []byte{0x41, 0x89, 0x23}}, + {x86.R11, 0, 2, x86.SP, []byte{0x66, 0x41, 0x89, 0x23}}, + {x86.R11, 0, 1, x86.SP, []byte{0x41, 0x88, 0x23}}, + {x86.R12, 0, 8, x86.R3, []byte{0x49, 0x89, 0x1C, 0x24}}, + {x86.R12, 0, 4, x86.R3, []byte{0x41, 0x89, 0x1C, 0x24}}, + {x86.R12, 0, 2, x86.R3, []byte{0x66, 0x41, 0x89, 0x1C, 0x24}}, + {x86.R12, 0, 1, x86.R3, []byte{0x41, 0x88, 0x1C, 0x24}}, + {x86.R13, 0, 8, x86.R2, []byte{0x49, 0x89, 0x55, 0x00}}, + {x86.R13, 0, 4, x86.R2, []byte{0x41, 0x89, 0x55, 0x00}}, + {x86.R13, 0, 2, x86.R2, []byte{0x66, 0x41, 0x89, 0x55, 0x00}}, + {x86.R13, 0, 1, x86.R2, []byte{0x41, 0x88, 0x55, 0x00}}, + {x86.R14, 0, 8, x86.R1, []byte{0x49, 0x89, 0x0E}}, + {x86.R14, 0, 4, x86.R1, []byte{0x41, 0x89, 0x0E}}, + {x86.R14, 0, 2, x86.R1, []byte{0x66, 0x41, 0x89, 0x0E}}, + {x86.R14, 0, 1, x86.R1, []byte{0x41, 0x88, 0x0E}}, + {x86.R15, 0, 8, x86.R0, []byte{0x49, 0x89, 0x07}}, + {x86.R15, 0, 4, x86.R0, []byte{0x41, 0x89, 0x07}}, + {x86.R15, 0, 2, x86.R0, []byte{0x66, 0x41, 0x89, 0x07}}, + {x86.R15, 0, 1, x86.R0, []byte{0x41, 0x88, 0x07}}, + + // Offset of 1 + {x86.R0, 1, 8, x86.R15, []byte{0x4C, 0x89, 0x78, 0x01}}, + {x86.R0, 1, 4, x86.R15, []byte{0x44, 0x89, 0x78, 0x01}}, + {x86.R0, 1, 2, x86.R15, []byte{0x66, 0x44, 0x89, 0x78, 0x01}}, + {x86.R0, 1, 1, x86.R15, []byte{0x44, 0x88, 0x78, 0x01}}, + {x86.R1, 1, 8, x86.R14, []byte{0x4C, 0x89, 0x71, 0x01}}, + {x86.R1, 1, 4, x86.R14, []byte{0x44, 0x89, 0x71, 0x01}}, + {x86.R1, 1, 2, x86.R14, []byte{0x66, 0x44, 0x89, 0x71, 0x01}}, + {x86.R1, 1, 1, x86.R14, []byte{0x44, 0x88, 0x71, 0x01}}, + {x86.R2, 1, 8, x86.R13, []byte{0x4C, 0x89, 0x6A, 0x01}}, + {x86.R2, 1, 4, x86.R13, []byte{0x44, 0x89, 0x6A, 0x01}}, + {x86.R2, 1, 2, x86.R13, []byte{0x66, 0x44, 0x89, 0x6A, 0x01}}, + {x86.R2, 1, 1, x86.R13, []byte{0x44, 0x88, 0x6A, 0x01}}, + {x86.R3, 1, 8, x86.R12, []byte{0x4C, 0x89, 0x63, 0x01}}, + {x86.R3, 1, 4, x86.R12, []byte{0x44, 0x89, 0x63, 0x01}}, + {x86.R3, 1, 2, x86.R12, []byte{0x66, 0x44, 0x89, 0x63, 0x01}}, + {x86.R3, 1, 1, x86.R12, []byte{0x44, 0x88, 0x63, 0x01}}, + {x86.SP, 1, 8, x86.R11, []byte{0x4C, 0x89, 0x5C, 0x24, 0x01}}, + {x86.SP, 1, 4, x86.R11, []byte{0x44, 0x89, 0x5C, 0x24, 0x01}}, + {x86.SP, 1, 2, x86.R11, []byte{0x66, 0x44, 0x89, 0x5C, 0x24, 0x01}}, + {x86.SP, 1, 1, x86.R11, []byte{0x44, 0x88, 0x5C, 0x24, 0x01}}, + {x86.R5, 1, 8, x86.R10, []byte{0x4C, 0x89, 0x55, 0x01}}, + {x86.R5, 1, 4, x86.R10, []byte{0x44, 0x89, 0x55, 0x01}}, + {x86.R5, 1, 2, x86.R10, []byte{0x66, 0x44, 0x89, 0x55, 0x01}}, + {x86.R5, 1, 1, x86.R10, []byte{0x44, 0x88, 0x55, 0x01}}, + {x86.R6, 1, 8, x86.R9, []byte{0x4C, 0x89, 0x4E, 0x01}}, + {x86.R6, 1, 4, x86.R9, []byte{0x44, 0x89, 0x4E, 0x01}}, + {x86.R6, 1, 2, x86.R9, []byte{0x66, 0x44, 0x89, 0x4E, 0x01}}, + {x86.R6, 1, 1, x86.R9, []byte{0x44, 0x88, 0x4E, 0x01}}, + {x86.R7, 1, 8, x86.R8, []byte{0x4C, 0x89, 0x47, 0x01}}, + {x86.R7, 1, 4, x86.R8, []byte{0x44, 0x89, 0x47, 0x01}}, + {x86.R7, 1, 2, x86.R8, []byte{0x66, 0x44, 0x89, 0x47, 0x01}}, + {x86.R7, 1, 1, x86.R8, []byte{0x44, 0x88, 0x47, 0x01}}, + {x86.R8, 1, 8, x86.R7, []byte{0x49, 0x89, 0x78, 0x01}}, + {x86.R8, 1, 4, x86.R7, []byte{0x41, 0x89, 0x78, 0x01}}, + {x86.R8, 1, 2, x86.R7, []byte{0x66, 0x41, 0x89, 0x78, 0x01}}, + {x86.R8, 1, 1, x86.R7, []byte{0x41, 0x88, 0x78, 0x01}}, + {x86.R9, 1, 8, x86.R6, []byte{0x49, 0x89, 0x71, 0x01}}, + {x86.R9, 1, 4, x86.R6, []byte{0x41, 0x89, 0x71, 0x01}}, + {x86.R9, 1, 2, x86.R6, []byte{0x66, 0x41, 0x89, 0x71, 0x01}}, + {x86.R9, 1, 1, x86.R6, []byte{0x41, 0x88, 0x71, 0x01}}, + {x86.R10, 1, 8, x86.R5, []byte{0x49, 0x89, 0x6A, 0x01}}, + {x86.R10, 1, 4, x86.R5, []byte{0x41, 0x89, 0x6A, 0x01}}, + {x86.R10, 1, 2, x86.R5, []byte{0x66, 0x41, 0x89, 0x6A, 0x01}}, + {x86.R10, 1, 1, x86.R5, []byte{0x41, 0x88, 0x6A, 0x01}}, + {x86.R11, 1, 8, x86.SP, []byte{0x49, 0x89, 0x63, 0x01}}, + {x86.R11, 1, 4, x86.SP, []byte{0x41, 0x89, 0x63, 0x01}}, + {x86.R11, 1, 2, x86.SP, []byte{0x66, 0x41, 0x89, 0x63, 0x01}}, + {x86.R11, 1, 1, x86.SP, []byte{0x41, 0x88, 0x63, 0x01}}, + {x86.R12, 1, 8, x86.R3, []byte{0x49, 0x89, 0x5C, 0x24, 0x01}}, + {x86.R12, 1, 4, x86.R3, []byte{0x41, 0x89, 0x5C, 0x24, 0x01}}, + {x86.R12, 1, 2, x86.R3, []byte{0x66, 0x41, 0x89, 0x5C, 0x24, 0x01}}, + {x86.R12, 1, 1, x86.R3, []byte{0x41, 0x88, 0x5C, 0x24, 01}}, + {x86.R13, 1, 8, x86.R2, []byte{0x49, 0x89, 0x55, 0x01}}, + {x86.R13, 1, 4, x86.R2, []byte{0x41, 0x89, 0x55, 0x01}}, + {x86.R13, 1, 2, x86.R2, []byte{0x66, 0x41, 0x89, 0x55, 0x01}}, + {x86.R13, 1, 1, x86.R2, []byte{0x41, 0x88, 0x55, 0x01}}, + {x86.R14, 1, 8, x86.R1, []byte{0x49, 0x89, 0x4E, 0x01}}, + {x86.R14, 1, 4, x86.R1, []byte{0x41, 0x89, 0x4E, 0x01}}, + {x86.R14, 1, 2, x86.R1, []byte{0x66, 0x41, 0x89, 0x4E, 0x01}}, + {x86.R14, 1, 1, x86.R1, []byte{0x41, 0x88, 0x4E, 0x01}}, + {x86.R15, 1, 8, x86.R0, []byte{0x49, 0x89, 0x47, 0x01}}, + {x86.R15, 1, 4, x86.R0, []byte{0x41, 0x89, 0x47, 0x01}}, + {x86.R15, 1, 2, x86.R0, []byte{0x66, 0x41, 0x89, 0x47, 0x01}}, + {x86.R15, 1, 1, x86.R0, []byte{0x41, 0x88, 0x47, 0x01}}, + } + + for _, pattern := range usagePatterns { + t.Logf("store %dB [%s+%d], %s", pattern.Length, pattern.RegisterTo, pattern.Offset, pattern.RegisterFrom) + code := x86.StoreRegister(nil, pattern.RegisterTo, pattern.Offset, pattern.Length, pattern.RegisterFrom) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Sub.go b/src/x86/Sub.go new file mode 100644 index 0000000..1ee1c66 --- /dev/null +++ b/src/x86/Sub.go @@ -0,0 +1,15 @@ +package x86 + +import ( + "git.urbach.dev/cli/q/src/cpu" +) + +// SubRegisterNumber subtracts a number from the given register. +func SubRegisterNumber(code []byte, register cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b101, register, number, 0x83, 0x81) +} + +// SubRegisterRegister subtracts a register value from another register. +func SubRegisterRegister(code []byte, register cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, register, 8, 0x29) +} \ No newline at end of file diff --git a/src/x86/Sub_test.go b/src/x86/Sub_test.go new file mode 100644 index 0000000..29ef144 --- /dev/null +++ b/src/x86/Sub_test.go @@ -0,0 +1,88 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestSubRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0x83, 0xE8, 0x01}}, + {x86.R1, 1, []byte{0x48, 0x83, 0xE9, 0x01}}, + {x86.R2, 1, []byte{0x48, 0x83, 0xEA, 0x01}}, + {x86.R3, 1, []byte{0x48, 0x83, 0xEB, 0x01}}, + {x86.SP, 1, []byte{0x48, 0x83, 0xEC, 0x01}}, + {x86.R5, 1, []byte{0x48, 0x83, 0xED, 0x01}}, + {x86.R6, 1, []byte{0x48, 0x83, 0xEE, 0x01}}, + {x86.R7, 1, []byte{0x48, 0x83, 0xEF, 0x01}}, + {x86.R8, 1, []byte{0x49, 0x83, 0xE8, 0x01}}, + {x86.R9, 1, []byte{0x49, 0x83, 0xE9, 0x01}}, + {x86.R10, 1, []byte{0x49, 0x83, 0xEA, 0x01}}, + {x86.R11, 1, []byte{0x49, 0x83, 0xEB, 0x01}}, + {x86.R12, 1, []byte{0x49, 0x83, 0xEC, 0x01}}, + {x86.R13, 1, []byte{0x49, 0x83, 0xED, 0x01}}, + {x86.R14, 1, []byte{0x49, 0x83, 0xEE, 0x01}}, + {x86.R15, 1, []byte{0x49, 0x83, 0xEF, 0x01}}, + + {x86.R0, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0x48, 0x81, 0xE9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0x48, 0x81, 0xEA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0x48, 0x81, 0xEB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0x48, 0x81, 0xEC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0x48, 0x81, 0xED, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0x48, 0x81, 0xEE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0x48, 0x81, 0xEF, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE8, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x49, 0x81, 0xE9, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x49, 0x81, 0xEA, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x49, 0x81, 0xEB, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x49, 0x81, 0xEC, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x49, 0x81, 0xED, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x49, 0x81, 0xEE, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x49, 0x81, 0xEF, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("sub %s, %x", pattern.Register, pattern.Number) + code := x86.SubRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestSubRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x4C, 0x29, 0xF8}}, + {x86.R1, x86.R14, []byte{0x4C, 0x29, 0xF1}}, + {x86.R2, x86.R13, []byte{0x4C, 0x29, 0xEA}}, + {x86.R3, x86.R12, []byte{0x4C, 0x29, 0xE3}}, + {x86.SP, x86.R11, []byte{0x4C, 0x29, 0xDC}}, + {x86.R5, x86.R10, []byte{0x4C, 0x29, 0xD5}}, + {x86.R6, x86.R9, []byte{0x4C, 0x29, 0xCE}}, + {x86.R7, x86.R8, []byte{0x4C, 0x29, 0xC7}}, + {x86.R8, x86.R7, []byte{0x49, 0x29, 0xF8}}, + {x86.R9, x86.R6, []byte{0x49, 0x29, 0xF1}}, + {x86.R10, x86.R5, []byte{0x49, 0x29, 0xEA}}, + {x86.R11, x86.SP, []byte{0x49, 0x29, 0xE3}}, + {x86.R12, x86.R3, []byte{0x49, 0x29, 0xDC}}, + {x86.R13, x86.R2, []byte{0x49, 0x29, 0xD5}}, + {x86.R14, x86.R1, []byte{0x49, 0x29, 0xCE}}, + {x86.R15, x86.R0, []byte{0x49, 0x29, 0xC7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("sub %s, %s", pattern.Left, pattern.Right) + code := x86.SubRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/Syscall.go b/src/x86/Syscall.go new file mode 100644 index 0000000..2d4199f --- /dev/null +++ b/src/x86/Syscall.go @@ -0,0 +1,6 @@ +package x86 + +// Syscall is the primary way to communicate with the OS kernel. +func Syscall(code []byte) []byte { + return append(code, 0x0F, 0x05) +} \ No newline at end of file diff --git a/src/x86/Syscall_test.go b/src/x86/Syscall_test.go new file mode 100644 index 0000000..f33f78d --- /dev/null +++ b/src/x86/Syscall_test.go @@ -0,0 +1,12 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestSyscall(t *testing.T) { + assert.DeepEqual(t, x86.Syscall(nil), []byte{0x0F, 0x05}) +} \ No newline at end of file diff --git a/src/x86/Xor.go b/src/x86/Xor.go new file mode 100644 index 0000000..af969f8 --- /dev/null +++ b/src/x86/Xor.go @@ -0,0 +1,15 @@ +package x86 + +import ( + "git.urbach.dev/cli/q/src/cpu" +) + +// XorRegisterNumber performs a bitwise XOR using a register and a number. +func XorRegisterNumber(code []byte, register cpu.Register, number int) []byte { + return encodeNum(code, AddressDirect, 0b110, register, number, 0x83, 0x81) +} + +// XorRegisterRegister performs a bitwise XOR using two registers. +func XorRegisterRegister(code []byte, register cpu.Register, operand cpu.Register) []byte { + return encode(code, AddressDirect, operand, register, 8, 0x31) +} \ No newline at end of file diff --git a/src/x86/Xor_test.go b/src/x86/Xor_test.go new file mode 100644 index 0000000..dfddcc3 --- /dev/null +++ b/src/x86/Xor_test.go @@ -0,0 +1,88 @@ +package x86_test + +import ( + "testing" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/x86" + "git.urbach.dev/go/assert" +) + +func TestXorRegisterNumber(t *testing.T) { + usagePatterns := []struct { + Register cpu.Register + Number int + Code []byte + }{ + {x86.R0, 1, []byte{0x48, 0x83, 0xF0, 0x01}}, + {x86.R1, 1, []byte{0x48, 0x83, 0xF1, 0x01}}, + {x86.R2, 1, []byte{0x48, 0x83, 0xF2, 0x01}}, + {x86.R3, 1, []byte{0x48, 0x83, 0xF3, 0x01}}, + {x86.SP, 1, []byte{0x48, 0x83, 0xF4, 0x01}}, + {x86.R5, 1, []byte{0x48, 0x83, 0xF5, 0x01}}, + {x86.R6, 1, []byte{0x48, 0x83, 0xF6, 0x01}}, + {x86.R7, 1, []byte{0x48, 0x83, 0xF7, 0x01}}, + {x86.R8, 1, []byte{0x49, 0x83, 0xF0, 0x01}}, + {x86.R9, 1, []byte{0x49, 0x83, 0xF1, 0x01}}, + {x86.R10, 1, []byte{0x49, 0x83, 0xF2, 0x01}}, + {x86.R11, 1, []byte{0x49, 0x83, 0xF3, 0x01}}, + {x86.R12, 1, []byte{0x49, 0x83, 0xF4, 0x01}}, + {x86.R13, 1, []byte{0x49, 0x83, 0xF5, 0x01}}, + {x86.R14, 1, []byte{0x49, 0x83, 0xF6, 0x01}}, + {x86.R15, 1, []byte{0x49, 0x83, 0xF7, 0x01}}, + + {x86.R0, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R1, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF1, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R2, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R3, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF3, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.SP, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R5, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF5, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R6, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R7, 0x7FFFFFFF, []byte{0x48, 0x81, 0xF7, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R8, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF0, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R9, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF1, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R10, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF2, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R11, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF3, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R12, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF4, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R13, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF5, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R14, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF6, 0xFF, 0xFF, 0xFF, 0x7F}}, + {x86.R15, 0x7FFFFFFF, []byte{0x49, 0x81, 0xF7, 0xFF, 0xFF, 0xFF, 0x7F}}, + } + + for _, pattern := range usagePatterns { + t.Logf("xor %s, %x", pattern.Register, pattern.Number) + code := x86.XorRegisterNumber(nil, pattern.Register, pattern.Number) + assert.DeepEqual(t, code, pattern.Code) + } +} + +func TestXorRegisterRegister(t *testing.T) { + usagePatterns := []struct { + Left cpu.Register + Right cpu.Register + Code []byte + }{ + {x86.R0, x86.R15, []byte{0x4C, 0x31, 0xF8}}, + {x86.R1, x86.R14, []byte{0x4C, 0x31, 0xF1}}, + {x86.R2, x86.R13, []byte{0x4C, 0x31, 0xEA}}, + {x86.R3, x86.R12, []byte{0x4C, 0x31, 0xE3}}, + {x86.SP, x86.R11, []byte{0x4C, 0x31, 0xDC}}, + {x86.R5, x86.R10, []byte{0x4C, 0x31, 0xD5}}, + {x86.R6, x86.R9, []byte{0x4C, 0x31, 0xCE}}, + {x86.R7, x86.R8, []byte{0x4C, 0x31, 0xC7}}, + {x86.R8, x86.R7, []byte{0x49, 0x31, 0xF8}}, + {x86.R9, x86.R6, []byte{0x49, 0x31, 0xF1}}, + {x86.R10, x86.R5, []byte{0x49, 0x31, 0xEA}}, + {x86.R11, x86.SP, []byte{0x49, 0x31, 0xE3}}, + {x86.R12, x86.R3, []byte{0x49, 0x31, 0xDC}}, + {x86.R13, x86.R2, []byte{0x49, 0x31, 0xD5}}, + {x86.R14, x86.R1, []byte{0x49, 0x31, 0xCE}}, + {x86.R15, x86.R0, []byte{0x49, 0x31, 0xC7}}, + } + + for _, pattern := range usagePatterns { + t.Logf("xor %s, %s", pattern.Left, pattern.Right) + code := x86.XorRegisterRegister(nil, pattern.Left, pattern.Right) + assert.DeepEqual(t, code, pattern.Code) + } +} \ No newline at end of file diff --git a/src/x86/encode.go b/src/x86/encode.go new file mode 100644 index 0000000..b20f436 --- /dev/null +++ b/src/x86/encode.go @@ -0,0 +1,33 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// encode is the core function that encodes an instruction. +func encode(code []byte, mod AddressMode, reg cpu.Register, rm cpu.Register, numBytes byte, opCodes ...byte) []byte { + w := byte(0) // Indicates a 64-bit register. + r := byte(0) // Extension to the "reg" field in ModRM. + x := byte(0) // Extension to the SIB index field. + b := byte(0) // Extension to the "rm" field in ModRM or the SIB base (r8 up to r15 use this). + + if numBytes == 8 { + w = 1 + } + + if reg > 0b111 { + r = 1 + reg &= 0b111 + } + + if rm > 0b111 { + b = 1 + rm &= 0b111 + } + + if w != 0 || r != 0 || x != 0 || b != 0 || (numBytes == 1 && (reg == SP || reg == R5 || reg == R6 || reg == R7)) { + code = append(code, REX(w, r, x, b)) + } + + code = append(code, opCodes...) + code = append(code, ModRM(mod, byte(reg), byte(rm))) + return code +} \ No newline at end of file diff --git a/src/x86/encodeNum.go b/src/x86/encodeNum.go new file mode 100644 index 0000000..e1c2073 --- /dev/null +++ b/src/x86/encodeNum.go @@ -0,0 +1,19 @@ +package x86 + +import ( + "encoding/binary" + + "git.urbach.dev/cli/q/src/cpu" + "git.urbach.dev/cli/q/src/sizeof" +) + +// encodeNum encodes an instruction with up to two registers and a number parameter. +func encodeNum(code []byte, mod AddressMode, reg cpu.Register, rm cpu.Register, number int, opCode8 byte, opCode32 byte) []byte { + if sizeof.Signed(int64(number)) == 1 { + code = encode(code, mod, reg, rm, 8, opCode8) + return append(code, byte(number)) + } + + code = encode(code, mod, reg, rm, 8, opCode32) + return binary.LittleEndian.AppendUint32(code, uint32(number)) +} \ No newline at end of file diff --git a/src/x86/memAccess.go b/src/x86/memAccess.go new file mode 100644 index 0000000..ea32757 --- /dev/null +++ b/src/x86/memAccess.go @@ -0,0 +1,34 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// memAccess encodes a memory access. +func memAccess(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, base cpu.Register, offset int8, length byte) []byte { + opCode := opCode32 + + if length == 1 { + opCode = opCode8 + } + + mod := AddressMemory + + if offset != 0 || base == R5 || base == R13 { + mod = AddressMemoryOffset8 + } + + if length == 2 { + code = append(code, 0x66) + } + + code = encode(code, mod, register, base, length, opCode) + + if base == SP || base == R12 { + code = append(code, SIB(Scale1, 0b100, 0b100)) + } + + if mod == AddressMemoryOffset8 { + code = append(code, byte(offset)) + } + + return code +} \ No newline at end of file diff --git a/src/x86/memAccessDynamic.go b/src/x86/memAccessDynamic.go new file mode 100644 index 0000000..2e65d43 --- /dev/null +++ b/src/x86/memAccessDynamic.go @@ -0,0 +1,61 @@ +package x86 + +import "git.urbach.dev/cli/q/src/cpu" + +// memAccessDynamic encodes a memory access using the value of a register as an offset. +func memAccessDynamic(code []byte, opCode8 byte, opCode32 byte, register cpu.Register, base cpu.Register, offset cpu.Register, length byte) []byte { + var ( + w = byte(0) + r = byte(0) + x = byte(0) + b = byte(0) + opCode = opCode32 + mod = AddressMemory + ) + + if length == 1 { + opCode = opCode8 + } + + if offset == SP { + offset, base = base, offset + } + + if length == 8 { + w = 1 + } + + if register > 0b111 { + r = 1 + register &= 0b111 + } + + if offset > 0b111 { + x = 1 + offset &= 0b111 + } + + if base > 0b111 { + b = 1 + base &= 0b111 + } + + if base == R5 || base == R13 { + mod = AddressMemoryOffset8 + } + + if length == 2 { + code = append(code, 0x66) + } + + code = append(code, REX(w, r, x, b)) + code = append(code, opCode) + code = append(code, ModRM(mod, byte(register), 0b100)) + code = append(code, SIB(Scale1, byte(offset), byte(base))) + + if mod == AddressMemoryOffset8 { + code = append(code, 0x00) + } + + return code +} \ No newline at end of file