parent
31c5ed614c
commit
9b2b2578f5
57 changed files with 2661 additions and 0 deletions
15
src/x86/Add.go
Normal file
15
src/x86/Add.go
Normal file
|
@ -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)
|
||||||
|
}
|
88
src/x86/Add_test.go
Normal file
88
src/x86/Add_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
15
src/x86/And.go
Normal file
15
src/x86/And.go
Normal file
|
@ -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)
|
||||||
|
}
|
88
src/x86/And_test.go
Normal file
88
src/x86/And_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
51
src/x86/Call.go
Normal file
51
src/x86/Call.go
Normal file
|
@ -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)
|
||||||
|
}
|
92
src/x86/Call_test.go
Normal file
92
src/x86/Call_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
13
src/x86/Compare.go
Normal file
13
src/x86/Compare.go
Normal file
|
@ -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)
|
||||||
|
}
|
88
src/x86/Compare_test.go
Normal file
88
src/x86/Compare_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
20
src/x86/Div.go
Normal file
20
src/x86/Div.go
Normal file
|
@ -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),
|
||||||
|
)
|
||||||
|
}
|
39
src/x86/Div_test.go
Normal file
39
src/x86/Div_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
7
src/x86/ExtendR0ToR2.go
Normal file
7
src/x86/ExtendR0ToR2.go
Normal file
|
@ -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)
|
||||||
|
}
|
12
src/x86/ExtendR0ToR2_test.go
Normal file
12
src/x86/ExtendR0ToR2_test.go
Normal file
|
@ -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})
|
||||||
|
}
|
37
src/x86/Jump.go
Normal file
37
src/x86/Jump.go
Normal file
|
@ -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))
|
||||||
|
}
|
40
src/x86/Jump_test.go
Normal file
40
src/x86/Jump_test.go
Normal file
|
@ -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})
|
||||||
|
}
|
8
src/x86/Load.go
Normal file
8
src/x86/Load.go
Normal file
|
@ -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)
|
||||||
|
}
|
13
src/x86/LoadAddress.go
Normal file
13
src/x86/LoadAddress.go
Normal file
|
@ -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))
|
||||||
|
}
|
40
src/x86/LoadAddress_test.go
Normal file
40
src/x86/LoadAddress_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
8
src/x86/LoadDynamic.go
Normal file
8
src/x86/LoadDynamic.go
Normal file
|
@ -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)
|
||||||
|
}
|
90
src/x86/LoadDynamic_test.go
Normal file
90
src/x86/LoadDynamic_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
157
src/x86/Load_test.go
Normal file
157
src/x86/Load_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
19
src/x86/ModRM.go
Normal file
19
src/x86/ModRM.go
Normal file
|
@ -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
|
||||||
|
}
|
34
src/x86/ModRM_test.go
Normal file
34
src/x86/ModRM_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
51
src/x86/Move.go
Normal file
51
src/x86/Move.go
Normal file
|
@ -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)
|
||||||
|
}
|
108
src/x86/Move_test.go
Normal file
108
src/x86/Move_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
13
src/x86/Mul.go
Normal file
13
src/x86/Mul.go
Normal file
|
@ -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)
|
||||||
|
}
|
88
src/x86/Mul_test.go
Normal file
88
src/x86/Mul_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
8
src/x86/Negate.go
Normal file
8
src/x86/Negate.go
Normal file
|
@ -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)
|
||||||
|
}
|
39
src/x86/Negate_test.go
Normal file
39
src/x86/Negate_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
15
src/x86/Or.go
Normal file
15
src/x86/Or.go
Normal file
|
@ -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)
|
||||||
|
}
|
88
src/x86/Or_test.go
Normal file
88
src/x86/Or_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
16
src/x86/Pop.go
Normal file
16
src/x86/Pop.go
Normal file
|
@ -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),
|
||||||
|
)
|
||||||
|
}
|
39
src/x86/Pop_test.go
Normal file
39
src/x86/Pop_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
37
src/x86/Push.go
Normal file
37
src/x86/Push.go
Normal file
|
@ -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),
|
||||||
|
)
|
||||||
|
}
|
61
src/x86/Push_test.go
Normal file
61
src/x86/Push_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
7
src/x86/REX.go
Normal file
7
src/x86/REX.go
Normal file
|
@ -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
|
||||||
|
}
|
34
src/x86/REX_test.go
Normal file
34
src/x86/REX_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
22
src/x86/Registers.go
Normal file
22
src/x86/Registers.go
Normal file
|
@ -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
|
||||||
|
)
|
7
src/x86/Return.go
Normal file
7
src/x86/Return.go
Normal file
|
@ -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)
|
||||||
|
}
|
12
src/x86/Return_test.go
Normal file
12
src/x86/Return_test.go
Normal file
|
@ -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})
|
||||||
|
}
|
19
src/x86/SIB.go
Normal file
19
src/x86/SIB.go
Normal file
|
@ -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
|
||||||
|
}
|
34
src/x86/SIB_test.go
Normal file
34
src/x86/SIB_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
17
src/x86/Shift.go
Normal file
17
src/x86/Shift.go
Normal file
|
@ -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)
|
||||||
|
}
|
71
src/x86/Shift_test.go
Normal file
71
src/x86/Shift_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
27
src/x86/Store.go
Normal file
27
src/x86/Store.go
Normal file
|
@ -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)
|
||||||
|
}
|
27
src/x86/StoreDynamic.go
Normal file
27
src/x86/StoreDynamic.go
Normal file
|
@ -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)
|
||||||
|
}
|
171
src/x86/StoreDynamic_test.go
Normal file
171
src/x86/StoreDynamic_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
305
src/x86/Store_test.go
Normal file
305
src/x86/Store_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
15
src/x86/Sub.go
Normal file
15
src/x86/Sub.go
Normal file
|
@ -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)
|
||||||
|
}
|
88
src/x86/Sub_test.go
Normal file
88
src/x86/Sub_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
6
src/x86/Syscall.go
Normal file
6
src/x86/Syscall.go
Normal file
|
@ -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)
|
||||||
|
}
|
12
src/x86/Syscall_test.go
Normal file
12
src/x86/Syscall_test.go
Normal file
|
@ -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})
|
||||||
|
}
|
15
src/x86/Xor.go
Normal file
15
src/x86/Xor.go
Normal file
|
@ -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)
|
||||||
|
}
|
88
src/x86/Xor_test.go
Normal file
88
src/x86/Xor_test.go
Normal file
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
33
src/x86/encode.go
Normal file
33
src/x86/encode.go
Normal file
|
@ -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
|
||||||
|
}
|
19
src/x86/encodeNum.go
Normal file
19
src/x86/encodeNum.go
Normal file
|
@ -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))
|
||||||
|
}
|
34
src/x86/memAccess.go
Normal file
34
src/x86/memAccess.go
Normal file
|
@ -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
|
||||||
|
}
|
61
src/x86/memAccessDynamic.go
Normal file
61
src/x86/memAccessDynamic.go
Normal file
|
@ -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
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue