package breakpoint import ( "context" "fmt" "okemu/gval" "regexp" "strconv" "strings" log "github.com/sirupsen/logrus" ) const MaxBreakpoints = 16383 const BpTmp1 = MaxBreakpoints + 1 const BpTmp2 = MaxBreakpoints + 2 const ( BPTypeSimplePC = iota // Simple PC=nn breakpoint BPTypeSimpleSP // Simple SP>=nn breakpoint BPTypeExpression // Complex expression breakpoint ) type Breakpoint struct { addr uint16 cond string eval gval.Evaluable bpType int pass uint16 passCount uint16 enabled bool mBank uint8 } var andMatch = regexp.MustCompile(`\s+AND\s+`) var orMatch = regexp.MustCompile(`\s+OR\s+`) // var xorMatch = regexp.MustCompile(`\s+XOR\s+`) var hexHMatch = regexp.MustCompile(`[[:xdigit:]]+H`) var eqMatch = regexp.MustCompile(`[^=>", "!=") return ex } var pcMatch = regexp.MustCompile(`^PC=[[:xdigit:]]+h$`) var spMatch = regexp.MustCompile(`^SP>=[[:xdigit:]]+$`) func getSecondUint16(param string, sep string) (uint16, error) { p := strings.Split(param, sep) v := p[1] base := 0 if strings.HasSuffix(v, "h") || strings.HasSuffix(v, "H") { v = strings.TrimSuffix(v, "H") v = strings.TrimSuffix(v, "h") base = 16 } a, e := strconv.ParseUint(v, base, 16) if e != nil { return 0, e } return uint16(a), nil } func NewBreakpoint(expr string, mBank uint8) (*Breakpoint, error) { bp := Breakpoint{ addr: 0, enabled: false, passCount: 0, pass: 0, bpType: BPTypeSimplePC, mBank: mBank, } // Check if BP is simple PC=addr expr = strings.TrimSpace(expr) bp.cond = expr pcMatched := pcMatch.MatchString(expr) spMatched := spMatch.MatchString(expr) if pcMatched { // PC=xxxxh bp.bpType = BPTypeSimplePC v, e := getSecondUint16(expr, "=") if e != nil { return nil, e } bp.addr = v } else if spMatched { // SP>=xxxx bp.bpType = BPTypeSimpleSP v, e := getSecondUint16(expr, "=") if e != nil { return nil, e } bp.addr = v } else { // complex expression bp.bpType = BPTypeExpression ex := patchExpression(expr) log.Debugf("Original Expression: '%s'", expr) log.Debugf(" Patched Expression: '%s'", ex) err := bp.SetExpression(ex) if err != nil { return nil, err } } return &bp, nil } func (b *Breakpoint) Enabled() bool { return b.enabled } func (b *Breakpoint) SetEnabled(enabled bool) { b.enabled = enabled } func (b *Breakpoint) PassCount() uint16 { return b.passCount } func (b *Breakpoint) SetPassCount(passCount uint16) { b.passCount = passCount } func (b *Breakpoint) Pass() uint16 { return b.pass } func (b *Breakpoint) SetPass(pass uint16) { b.pass = pass } func (b *Breakpoint) IncPass() { b.pass++ } func (b *Breakpoint) Addr() uint16 { return b.addr } func (b *Breakpoint) SetAddr(addr uint16) { b.addr = addr } func (b *Breakpoint) Type() int { return b.bpType } func (b *Breakpoint) SetType(bpType int) { b.bpType = bpType } func getUint16(name string, ctx map[string]interface{}) uint16 { if v, ok := ctx[name]; ok { if v == nil { return 0 } // most frequent case if v, ok := v.(uint16); ok { return v } // for less frequent cases switch value := v.(type) { case int: return uint16(value) case int8: return uint16(value) case int16: return uint16(value) case int32: return uint16(value) case int64: return uint16(value) case uint: return uint16(value) case uint8: return uint16(value) case uint32: return uint16(value) case uint64: return uint16(value) default: log.Errorf("Unknown type %v for variable %s", value, name) return 0 } } else { log.Errorf("Variable %s not found in context!", name) } return 0 } func (b *Breakpoint) Hit(ctx map[string]interface{}) bool { if !b.enabled { return false } if b.bpType == BPTypeSimplePC { pc := getUint16("PC", ctx) if pc == b.addr { log.Debugf("Breakpoint Hit PC=0x%04X", b.addr) } return pc == b.addr } else if b.bpType == BPTypeSimpleSP { sp := getUint16("SP", ctx) if sp >= b.addr { log.Debugf("Breakpoint Hit SP>=0x%04X", b.addr) } return sp >= b.addr } value, err := b.eval.EvalBool(context.Background(), ctx) if err != nil { fmt.Println(err) } return value } var language gval.Language func init() { language = gval.NewLanguage(gval.Base(), gval.Arithmetic(), gval.Bitmask(), gval.PropositionalLogic()) } func (b *Breakpoint) SetExpression(expression string) error { var err error b.eval, err = language.NewEvaluable(expression) if err != nil { log.Error("Illegal expression", err) return err } b.bpType = BPTypeExpression return nil } func (b *Breakpoint) Expression() string { return b.cond } func (b *Breakpoint) MBank() uint8 { return b.mBank }