diff --git a/operand/checks.go b/operand/checks.go index cb7b0d7..1186c38 100644 --- a/operand/checks.go +++ b/operand/checks.go @@ -1,71 +1,104 @@ package operand import ( + "math" + + "github.com/mmcloughlin/avo/reg" + "github.com/mmcloughlin/avo" ) +// Is1 returns true if op is the immediate constant 1. func Is1(op avo.Operand) bool { - return false + i, ok := op.(Imm) + return ok && i == 1 } +// Is3 returns true if op is the immediate constant 3. func Is3(op avo.Operand) bool { - return false + i, ok := op.(Imm) + return ok && i == 3 } +// IsImm2u returns true if op is a 2-bit unsigned immediate (less than 4). func IsImm2u(op avo.Operand) bool { - return false + i, ok := op.(Imm) + return ok && i < 4 } +// IsImm8 returns true is op is an 8-bit immediate. func IsImm8(op avo.Operand) bool { - return false + i, ok := op.(Imm) + return ok && i <= math.MaxUint8 } +// IsImm16 returns true is op is a 16-bit immediate. func IsImm16(op avo.Operand) bool { - return false + i, ok := op.(Imm) + return ok && i <= math.MaxUint16 } +// IsImm32 returns true is op is a 32-bit immediate. func IsImm32(op avo.Operand) bool { - return false + i, ok := op.(Imm) + return ok && i <= math.MaxUint32 } +// IsImm64 returns true is op is a 64-bit immediate. func IsImm64(op avo.Operand) bool { - return false + _, ok := op.(Imm) + return ok } +// IsAl returns true if op is the AL register. func IsAl(op avo.Operand) bool { - return false + return op == reg.AL } +// IsCl returns true if op is the CL register. func IsCl(op avo.Operand) bool { - return false + return op == reg.CL } +// IsAx returns true if op is the 16-bit AX register. func IsAx(op avo.Operand) bool { - return false + return op == reg.AX } +// IsEax returns true if op is the 32-bit EAX register. func IsEax(op avo.Operand) bool { - return false + return op == reg.EAX } +// IsRax returns true if op is the 64-bit RAX register. func IsRax(op avo.Operand) bool { - return false + return op == reg.RAX } +// IsR8 returns true if op is an 8-bit general-purpose register. func IsR8(op avo.Operand) bool { - return false + return IsGP(op, 1) } +// IsR16 returns true if op is a 16-bit general-purpose register. func IsR16(op avo.Operand) bool { - return false + return IsGP(op, 2) } +// IsR32 returns true if op is a 32-bit general-purpose register. func IsR32(op avo.Operand) bool { - return false + return IsGP(op, 4) } +// IsR64 returns true if op is a 64-bit general-purpose register. func IsR64(op avo.Operand) bool { - return false + return IsGP(op, 8) +} + +// IsGP returns true if op is a general-purpose register of size n bytes. +func IsGP(op avo.Operand, n uint) bool { + r, ok := op.(reg.Register) + return ok && r.Kind() == reg.GeneralPurpose.Kind && r.Bytes() == n } func IsXmm0(op avo.Operand) bool { diff --git a/operand/checks_test.go b/operand/checks_test.go new file mode 100644 index 0000000..cbd6068 --- /dev/null +++ b/operand/checks_test.go @@ -0,0 +1,69 @@ +package operand + +import ( + "reflect" + "runtime" + "testing" + + "github.com/mmcloughlin/avo/reg" + + "github.com/mmcloughlin/avo" +) + +func TestChecks(t *testing.T) { + cases := []struct { + Predicate func(avo.Operand) bool + Operand avo.Operand + Expect bool + }{ + // Immediates + {Is1, Imm(1), true}, + {Is1, Imm(23), false}, + {Is3, Imm(3), true}, + {Is3, Imm(23), false}, + {IsImm2u, Imm(3), true}, + {IsImm2u, Imm(4), false}, + {IsImm8, Imm(255), true}, + {IsImm8, Imm(256), false}, + {IsImm16, Imm((1 << 16) - 1), true}, + {IsImm16, Imm(1 << 16), false}, + {IsImm32, Imm((1 << 32) - 1), true}, + {IsImm32, Imm(1 << 32), false}, + {IsImm64, Imm((1 << 64) - 1), true}, + + // Specific registers + {IsAl, reg.AL, true}, + {IsAl, reg.CL, false}, + {IsCl, reg.CL, true}, + {IsCl, reg.DH, false}, + {IsAx, reg.AX, true}, + {IsAx, reg.DX, false}, + {IsEax, reg.EAX, true}, + {IsEax, reg.ECX, false}, + {IsRax, reg.RAX, true}, + {IsRax, reg.R13, false}, + + // General-purpose registers + {IsR8, reg.AL, true}, + {IsR8, reg.CH, true}, + {IsR8, reg.EAX, false}, + {IsR16, reg.DX, true}, + {IsR16, reg.R10W, true}, + {IsR16, reg.R10B, false}, + {IsR32, reg.EBP, true}, + {IsR32, reg.R14L, true}, + {IsR32, reg.R8, false}, + {IsR64, reg.RDX, true}, + {IsR64, reg.R10, true}, + {IsR64, reg.EBX, false}, + } + for _, c := range cases { + if c.Predicate(c.Operand) != c.Expect { + t.Errorf("%s( %#v ) != %v", funcname(c.Predicate), c.Operand, c.Expect) + } + } +} + +func funcname(f interface{}) string { + return runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name() +} diff --git a/operand/types.go b/operand/types.go new file mode 100644 index 0000000..60da545 --- /dev/null +++ b/operand/types.go @@ -0,0 +1,34 @@ +package operand + +import ( + "fmt" + + "github.com/mmcloughlin/avo/reg" +) + +type Mem struct { + Disp int + Base reg.Register + Index reg.Register + Scale uint8 +} + +func (m Mem) Asm() string { + a := "" + if m.Disp != 0 { + a += fmt.Sprintf("%d", m.Disp) + } + if m.Base != nil { + a += fmt.Sprintf("(%s)", m.Base.Asm()) + } + if m.Index != nil && m.Scale != 0 { + a += fmt.Sprintf("(%s*%d)", m.Index.Asm(), m.Scale) + } + return a +} + +type Imm uint64 + +func (i Imm) Asm() string { + return fmt.Sprintf("%#x", uint64(i)) +} diff --git a/operand/types_test.go b/operand/types_test.go new file mode 100644 index 0000000..2a839e0 --- /dev/null +++ b/operand/types_test.go @@ -0,0 +1,28 @@ +package operand + +import ( + "testing" + + "github.com/mmcloughlin/avo/reg" +) + +func TestMemAsm(t *testing.T) { + cases := []struct { + Mem Mem + Expect string + }{ + {Mem{Base: reg.EAX}, "(AX)"}, + {Mem{Disp: 16, Base: reg.RAX}, "16(AX)"}, + {Mem{Base: reg.R11, Index: reg.RAX, Scale: 4}, "(R11)(AX*4)"}, + {Mem{Base: reg.R11, Index: reg.RAX, Scale: 1}, "(R11)(AX*1)"}, + {Mem{Base: reg.R11, Index: reg.RAX}, "(R11)"}, + {Mem{Base: reg.R11, Scale: 8}, "(R11)"}, + {Mem{Disp: 2048, Base: reg.R11, Index: reg.RAX, Scale: 8}, "2048(R11)(AX*8)"}, + } + for _, c := range cases { + got := c.Mem.Asm() + if got != c.Expect { + t.Errorf("%#v.Asm() = %s expected %s", c.Mem, got, c.Expect) + } + } +}