mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 13:48:02 -04:00
break/trap targets are now aware of CPU instruction boundaries
halting conditions are checked every video-cycle. this reverts a temporary change made in a73dcae6dcc61e4ced03f23fe4df504cb966fb36 renamed files in debugger package for clarity
This commit is contained in:
parent
a5d96cf8ba
commit
d788fb46bb
|
@ -75,10 +75,14 @@ func (h *haltCoordination) reset() {
|
|||
|
||||
// check for a halt condition and set the halt flag if found.
|
||||
func (h *haltCoordination) check() {
|
||||
// whether CPU is at an instruction boundary. breakpoints and traps need to
|
||||
// know this because some targets are sensitive to it
|
||||
instructionBoundary := h.dbg.vcs.CPU.LastResult.Final
|
||||
|
||||
// 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()
|
||||
breakMessage := h.breakpoints.check(instructionBoundary)
|
||||
trapMessage := h.traps.check(instructionBoundary)
|
||||
watchMessage := h.watches.check()
|
||||
|
||||
h.dbg.printLine(terminal.StyleFeedback, breakMessage)
|
||||
|
@ -92,7 +96,7 @@ func (h *haltCoordination) check() {
|
|||
}
|
||||
|
||||
// check volatile conditions
|
||||
breakMessage := h.volatileBreakpoints.check()
|
||||
trapMessage := h.volatileTraps.check()
|
||||
breakMessage := h.volatileBreakpoints.check(instructionBoundary)
|
||||
trapMessage := h.volatileTraps.check(instructionBoundary)
|
||||
h.halt = h.halt || breakMessage != "" || trapMessage != ""
|
||||
}
|
||||
|
|
|
@ -203,13 +203,17 @@ func (bp *breakpoints) drop(num int) error {
|
|||
// check compares the current state of the emulation with every breakpoint
|
||||
// condition. returns a string listing every condition that matches (separated
|
||||
// by \n).
|
||||
func (bp *breakpoints) check() string {
|
||||
func (bp *breakpoints) check(instructionBoundary bool) string {
|
||||
if len(bp.breaks) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
checkString := strings.Builder{}
|
||||
for i := range bp.breaks {
|
||||
if bp.breaks[i].target.instructionBoundary && !instructionBoundary {
|
||||
continue // for loop
|
||||
}
|
||||
|
||||
// check current value of target with the requested value
|
||||
if bp.breaks[i].check() == checkMatch {
|
||||
checkString.WriteString(fmt.Sprintf("break on %s\n", bp.breaks[i]))
|
|
@ -33,6 +33,9 @@ type target struct {
|
|||
// must be a comparable type
|
||||
currentValue targetValue
|
||||
format string
|
||||
|
||||
// some targets should only be checked on an instruction boundary
|
||||
instructionBoundary bool
|
||||
}
|
||||
|
||||
func (trg target) Label() string {
|
||||
|
@ -88,7 +91,8 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
ai := dbg.dbgmem.mapAddress(dbg.vcs.CPU.PC.Address(), true)
|
||||
return int(ai.mappedAddress)
|
||||
},
|
||||
format: "%#04x",
|
||||
format: "%#04x",
|
||||
instructionBoundary: true,
|
||||
}
|
||||
|
||||
case "A":
|
||||
|
@ -97,7 +101,8 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return int(dbg.vcs.CPU.A.Value())
|
||||
},
|
||||
format: "%#02x",
|
||||
format: "%#02x",
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
||||
case "X":
|
||||
|
@ -106,7 +111,8 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return int(dbg.vcs.CPU.X.Value())
|
||||
},
|
||||
format: "%#02x",
|
||||
format: "%#02x",
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
||||
case "Y":
|
||||
|
@ -115,7 +121,8 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return int(dbg.vcs.CPU.Y.Value())
|
||||
},
|
||||
format: "%#02x",
|
||||
format: "%#02x",
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
||||
case "SP":
|
||||
|
@ -124,7 +131,8 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return int(dbg.vcs.CPU.SP.Value())
|
||||
},
|
||||
format: "%#02x",
|
||||
format: "%#02x",
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
||||
// tv state
|
||||
|
@ -134,6 +142,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return dbg.vcs.TV.GetState(signal.ReqFramenum)
|
||||
},
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
||||
case "SCANLINE", "SL":
|
||||
|
@ -142,6 +151,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return dbg.vcs.TV.GetState(signal.ReqScanline)
|
||||
},
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
||||
case "CLOCK", "CL":
|
||||
|
@ -150,6 +160,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return dbg.vcs.TV.GetState(signal.ReqClock)
|
||||
},
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
||||
case "BANK":
|
||||
|
@ -172,6 +183,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
}
|
||||
return dbg.vcs.CPU.LastResult.Defn.Operator
|
||||
},
|
||||
instructionBoundary: true,
|
||||
}
|
||||
|
||||
case "ADDRESSMODE", "AM":
|
||||
|
@ -183,6 +195,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
}
|
||||
return int(dbg.vcs.CPU.LastResult.Defn.AddressingMode)
|
||||
},
|
||||
instructionBoundary: true,
|
||||
}
|
||||
|
||||
case "EFFECT", "EFF":
|
||||
|
@ -194,6 +207,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
}
|
||||
return int(dbg.vcs.CPU.LastResult.Defn.Effect)
|
||||
},
|
||||
instructionBoundary: true,
|
||||
}
|
||||
|
||||
case "PAGEFAULT", "PAGE":
|
||||
|
@ -202,6 +216,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
currentValue: func() targetValue {
|
||||
return dbg.vcs.CPU.LastResult.PageFault
|
||||
},
|
||||
instructionBoundary: true,
|
||||
}
|
||||
|
||||
case "BUG":
|
||||
|
@ -214,6 +229,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
}
|
||||
return s
|
||||
},
|
||||
instructionBoundary: true,
|
||||
}
|
||||
|
||||
default:
|
||||
|
@ -239,5 +255,6 @@ func bankTarget(dbg *Debugger) *target {
|
|||
currentValue: func() targetValue {
|
||||
return dbg.vcs.Mem.Cart.GetBank(dbg.vcs.CPU.PC.Address()).Number
|
||||
},
|
||||
instructionBoundary: false,
|
||||
}
|
||||
}
|
|
@ -76,9 +76,13 @@ func (tr *traps) drop(num int) error {
|
|||
|
||||
// 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 {
|
||||
func (tr *traps) check(instructionBoundary bool) string {
|
||||
checkString := strings.Builder{}
|
||||
for i := range tr.traps {
|
||||
if tr.traps[i].target.instructionBoundary && !instructionBoundary {
|
||||
continue // for loop
|
||||
}
|
||||
|
||||
trapValue := tr.traps[i].target.TargetValue()
|
||||
|
||||
if trapValue != tr.traps[i].origValue {
|
|
@ -394,10 +394,6 @@ func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// not updating disassembly. if we end up stopping mid-instruction the
|
||||
// halt branch of the inputLoop() function will give us an opportinity
|
||||
// to update, which is all we need.
|
||||
|
||||
// we do need to update the reflection however
|
||||
err = dbg.ref.Step(dbg.lastBank)
|
||||
if err != nil {
|
||||
|
@ -416,6 +412,11 @@ func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
|||
}
|
||||
}
|
||||
|
||||
// check halt condition. a second check is made after vcs.Step()
|
||||
// returns below
|
||||
dbg.halting.check()
|
||||
dbg.continueEmulation = !dbg.halting.halt
|
||||
|
||||
if dbg.quantum == QuantumVideo || !dbg.continueEmulation {
|
||||
// start another inputLoop() with the clockCycle boolean set to true
|
||||
return dbg.inputLoop(inputter, true)
|
||||
|
@ -433,22 +434,17 @@ func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
|||
// get to check the result of VCS.Step()
|
||||
stepErr := dbg.vcs.Step(callback)
|
||||
|
||||
// check halt condition. checking here means we can only break on CPU
|
||||
// instruction boundaries. checking every video cycle would be nice for
|
||||
// some targets but for others they can be problematic. for instance PC
|
||||
// breakpoints would break too earlier (an instruction would leave a PC on
|
||||
// the target value but would not be ready to execute if the instruction
|
||||
// affected flow)
|
||||
// check halt condition again now that the instruction has finished (the
|
||||
// Final flag is true). this does mean that some breakpoints/traps are
|
||||
// matched twice but that's not currently a problem
|
||||
dbg.halting.check()
|
||||
dbg.continueEmulation = !dbg.halting.halt
|
||||
|
||||
var err error
|
||||
|
||||
// update disassembly after every CPU instruction. no exceptions.
|
||||
dbg.lastResult = dbg.Disasm.ExecutedEntry(dbg.lastBank, dbg.vcs.CPU.LastResult, true, dbg.vcs.CPU.PC.Value())
|
||||
|
||||
// make sure reflection has been updated at the end of the instruction
|
||||
if err = dbg.ref.Step(dbg.lastBank); err != nil {
|
||||
if err := dbg.ref.Step(dbg.lastBank); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue