start to implement operand types and checks

This commit is contained in:
Michael McLoughlin
2018-11-26 22:14:36 -08:00
parent bed7e7e2c2
commit 3050882621
4 changed files with 180 additions and 16 deletions

View File

@@ -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 {

69
operand/checks_test.go Normal file
View File

@@ -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()
}

34
operand/types.go Normal file
View File

@@ -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))
}

28
operand/types_test.go Normal file
View File

@@ -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)
}
}
}