Gopher2600/debugger/halt_traps.go
JetSetIlly 3aa5885ebe removed curated pacakge. replaced with wrapped errors
curated package predated the standard errors package introduced in
go1.13

the standard package does a better job of what curated attempted to do

the change of package also gave me a opportunity to clean up the error
messages a little bit
2023-02-13 21:58:39 +00:00

134 lines
3.4 KiB
Go

// This file is part of Gopher2600.
//
// Gopher2600 is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Gopher2600 is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Gopher2600. If not, see <https://www.gnu.org/licenses/>.
// traps are used to halt execution of the emulator when the target *changes*
// from its current value to any other value. compare to breakpoints which halt
// execution when the target is *changed to* a specific value.
package debugger
import (
"fmt"
"strings"
"github.com/jetsetilly/gopher2600/debugger/terminal"
"github.com/jetsetilly/gopher2600/debugger/terminal/commandline"
)
type traps struct {
dbg *Debugger
traps []trapper
}
type trapper struct {
target *target
origValue targetValue
}
func (tr trapper) String() string {
return tr.target.label
}
// newTraps is the preferred method of initialisation for the traps type.
func newTraps(dbg *Debugger) *traps {
tr := &traps{dbg: dbg}
tr.clear()
return tr
}
// clear all traps.
func (tr *traps) clear() {
tr.traps = make([]trapper, 0, 10)
}
// isEmpty returns true if there are no currently defined traps.
func (tr *traps) isEmpty() bool {
return len(tr.traps) == 0
}
// drop the numbered trap from the list.
func (tr *traps) drop(num int) error {
if len(tr.traps)-1 < num {
return fmt.Errorf("trap #%d is not defined", num)
}
h := tr.traps[:num]
t := tr.traps[num+1:]
tr.traps = make([]trapper, len(h)+len(t), cap(tr.traps))
copy(tr.traps, h)
copy(tr.traps[len(h):], t)
return nil
}
// check compares the current state of the emulation with every trap condition.
// returns a string listing every condition that matches (separated by \n).
func (tr *traps) check() string {
if len(tr.traps) == 0 {
return ""
}
checkString := strings.Builder{}
for i := range tr.traps {
if tr.traps[i].target.instructionBoundary && !tr.dbg.vcs.CPU.LastResult.Final {
continue // for loop
}
trapValue := tr.traps[i].target.value()
if trapValue != tr.traps[i].origValue {
checkString.WriteString(fmt.Sprintf("trap on %s [%v->%v]\n", tr.traps[i].target.label, tr.traps[i].origValue, trapValue))
tr.traps[i].origValue = trapValue
}
}
return checkString.String()
}
// list currently defined traps.
func (tr traps) list() {
if len(tr.traps) == 0 {
tr.dbg.printLine(terminal.StyleFeedback, "no traps")
} else {
tr.dbg.printLine(terminal.StyleFeedback, "traps:")
for i := range tr.traps {
tr.dbg.printLine(terminal.StyleFeedback, "% 2d: %s", i, tr.traps[i].target.label)
}
}
}
// parse tokens and add new trap. the silent argument will prevent error
// messages being printed.
func (tr *traps) parseCommand(tokens *commandline.Tokens) error {
_, present := tokens.Peek()
for present {
tgt, err := parseTarget(tr.dbg, tokens)
if err != nil {
return err
}
for _, t := range tr.traps {
if t.target.label == tgt.label {
return fmt.Errorf("trap exists (%s)", t)
}
}
tr.traps = append(tr.traps, trapper{target: tgt, origValue: tgt.value()})
_, present = tokens.Peek()
}
return nil
}