mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 13:48:02 -04:00
147 lines
3.8 KiB
Go
147 lines
3.8 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/>.
|
|
|
|
package debugger
|
|
|
|
import (
|
|
"github.com/jetsetilly/gopher2600/debugger/terminal"
|
|
)
|
|
|
|
// haltingCoordination ties all the mechanisms that can interrupt the normal
|
|
// running of the emulation.
|
|
//
|
|
// reset() and update() control the coordination itself. updating of the
|
|
// breakpoints, etc. need to be done directly on those fields
|
|
type haltCoordination struct {
|
|
dbg *Debugger
|
|
|
|
// has a halt condition been met since halt has been reset(). once halt
|
|
// has been set to true it will remain set until explicitely set to false
|
|
// (via reset())
|
|
halt bool
|
|
|
|
// the cartridge has issued a yield signal that we should stop the debugger for
|
|
cartridgeYield bool
|
|
|
|
// the emulation must yield to the cartridge but it must be delayed until it
|
|
// is in a better state
|
|
deferredCartridgeYield bool
|
|
|
|
// halt conditions
|
|
breakpoints *breakpoints
|
|
traps *traps
|
|
watches *watches
|
|
|
|
// volatile conditions. if these are non-empty they will take precedence
|
|
// over the non-volatile conditions above.
|
|
//
|
|
// volatile conditions are always cleared in the input loop before
|
|
// emulation continues after a halt
|
|
volatileBreakpoints *breakpoints
|
|
volatileTraps *traps
|
|
}
|
|
|
|
func newHaltCoordination(dbg *Debugger) (*haltCoordination, error) {
|
|
h := &haltCoordination{dbg: dbg}
|
|
|
|
var err error
|
|
|
|
// set up breakpoints/traps
|
|
h.breakpoints, err = newBreakpoints(dbg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h.traps = newTraps(dbg)
|
|
h.watches = newWatches(dbg)
|
|
|
|
h.volatileBreakpoints, err = newBreakpoints(dbg)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h.volatileTraps = newTraps(dbg)
|
|
|
|
return h, nil
|
|
}
|
|
|
|
// reset halt condition.
|
|
func (h *haltCoordination) reset() {
|
|
h.halt = false
|
|
h.cartridgeYield = false
|
|
}
|
|
|
|
// check for a halt condition and set the halt flag if found. returns true if
|
|
// emulation should continue and false if the emulation should halt
|
|
func (h *haltCoordination) check() bool {
|
|
if h.cartridgeYield {
|
|
h.halt = true
|
|
return !h.halt
|
|
}
|
|
|
|
// we don't check for regular break/trap/wathes if there are volatileTraps in place
|
|
if h.volatileTraps.isEmpty() && h.volatileBreakpoints.isEmpty() {
|
|
breakMessage := h.breakpoints.check()
|
|
trapMessage := h.traps.check()
|
|
watchMessage := h.watches.check()
|
|
|
|
if breakMessage != "" {
|
|
h.dbg.printLine(terminal.StyleFeedback, breakMessage)
|
|
h.halt = true
|
|
}
|
|
|
|
if trapMessage != "" {
|
|
h.dbg.printLine(terminal.StyleFeedback, trapMessage)
|
|
h.halt = true
|
|
}
|
|
|
|
if watchMessage != "" {
|
|
h.dbg.printLine(terminal.StyleFeedback, watchMessage)
|
|
h.halt = true
|
|
}
|
|
|
|
return !h.halt
|
|
}
|
|
|
|
// check volatile conditions
|
|
breakMessage := h.volatileBreakpoints.check()
|
|
trapMessage := h.volatileTraps.check()
|
|
h.halt = h.halt || breakMessage != "" || trapMessage != ""
|
|
|
|
return !h.halt
|
|
}
|
|
|
|
// returns false if a breakpoint or trap target has the notInPlaymode flag set
|
|
func (h *haltCoordination) allowPlaymode() bool {
|
|
for _, b := range h.breakpoints.breaks {
|
|
if b.target.notInPlaymode {
|
|
return false
|
|
}
|
|
n := b.next
|
|
for n != nil {
|
|
if n.target.notInPlaymode {
|
|
return false
|
|
}
|
|
n = n.next
|
|
}
|
|
}
|
|
|
|
for _, t := range h.traps.traps {
|
|
if t.target.notInPlaymode {
|
|
return false
|
|
}
|
|
}
|
|
|
|
return true
|
|
}
|