mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 13:48:02 -04:00
158 lines
4.8 KiB
Go
158 lines
4.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/govern"
|
|
"github.com/jetsetilly/gopher2600/debugger/terminal"
|
|
"github.com/jetsetilly/gopher2600/gui"
|
|
"github.com/jetsetilly/gopher2600/hardware"
|
|
"github.com/jetsetilly/gopher2600/userinput"
|
|
)
|
|
|
|
func (dbg *Debugger) forceROMSelector() error {
|
|
dbg.forcedROMselection = make(chan bool, 1)
|
|
err := dbg.gui.SetFeature(gui.ReqROMSelector)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (dbg *Debugger) playLoop() error {
|
|
// if forcedROMSelection is active (is not nil) then the event loop is
|
|
// simplified for the duration (until it is set to nil)
|
|
for dbg.forcedROMselection != nil {
|
|
select {
|
|
case sig := <-dbg.events.Signal:
|
|
return dbg.events.SignalHandler(sig)
|
|
case ev := <-dbg.events.PushedFunction:
|
|
ev()
|
|
case ev := <-dbg.events.PushedFunctionImmediate:
|
|
ev()
|
|
return nil
|
|
case ev := <-dbg.events.UserInput:
|
|
if _, ok := ev.(userinput.EventQuit); ok {
|
|
return terminal.UserQuit
|
|
}
|
|
case <-dbg.forcedROMselection:
|
|
dbg.forcedROMselection = nil
|
|
}
|
|
}
|
|
|
|
// only check for end of measurement period every PerformanceBrake CPU
|
|
// instructions
|
|
performanceBrake := 0
|
|
|
|
// update lastBank at the start of the play loop
|
|
dbg.liveBankInfo = dbg.vcs.Mem.Cart.GetBank(dbg.vcs.CPU.PC.Address())
|
|
|
|
// run and handle events
|
|
return dbg.vcs.Run(func() (govern.State, error) {
|
|
|
|
// update counters. because of the way LastResult works we need to make
|
|
// sure we only use it in the event that the CPU RdyFlag is set
|
|
//
|
|
// if it isn't then we know that the CPU ticked once before returning
|
|
// and allowing this function to run. meaning the the number of cycles
|
|
// tell the counter to Step() is ColorClocksPerCPUCycle
|
|
//
|
|
// in all other cases the number of cycles to count is ColorClocksPerCPUCycle
|
|
// multiplied by the number of cycles in the instruction
|
|
if dbg.vcs.CPU.RdyFlg {
|
|
dbg.counter.Step(dbg.vcs.CPU.LastResult.Cycles*hardware.ColorClocksPerCPUCycle, dbg.liveBankInfo)
|
|
} else {
|
|
dbg.counter.Step(hardware.ColorClocksPerCPUCycle, dbg.liveBankInfo)
|
|
}
|
|
|
|
// we must keep lastBank updated during the play loop
|
|
dbg.liveBankInfo = dbg.vcs.Mem.Cart.GetBank(dbg.vcs.CPU.PC.Address())
|
|
|
|
// record state. we do this before any of the conditions below that may
|
|
// result in an early return from the function
|
|
if dbg.state.Load().(govern.State) == govern.Running {
|
|
dbg.Rewind.RecordState()
|
|
}
|
|
|
|
// run continueCheck() function is called every CPU instruction. for
|
|
// some halt conditions this is too infrequent
|
|
//
|
|
// for this reason we should never find ourselves in the playLoop if
|
|
// these halt conditions exist. see setMode() function
|
|
dbg.halting.check()
|
|
if dbg.halting.halt {
|
|
// set debugging mode. halting messages will be preserved and
|
|
// shown when entering debugging mode
|
|
dbg.setMode(govern.ModeDebugger)
|
|
return govern.Ending, nil
|
|
}
|
|
|
|
if dbg.Mode() != govern.ModePlay {
|
|
return govern.Ending, nil
|
|
}
|
|
|
|
// return without checking interface unless we exceed the
|
|
// PerformanceBrake value
|
|
performanceBrake++
|
|
if performanceBrake < hardware.PerformanceBrake {
|
|
return dbg.State(), nil
|
|
}
|
|
performanceBrake = 0
|
|
|
|
// how we wait for read events depends on whether the emulation is
|
|
// paused or not. if emulation is paused then we halt until an event is
|
|
// received. this reduces CPU usage when paused
|
|
if dbg.state.Load().(govern.State) == govern.Paused {
|
|
select {
|
|
case <-dbg.readEventsPulse.C:
|
|
err := dbg.readEventsHandler()
|
|
if err != nil {
|
|
return govern.Ending, err
|
|
}
|
|
}
|
|
} else {
|
|
select {
|
|
case <-dbg.readEventsPulse.C:
|
|
err := dbg.readEventsHandler()
|
|
if err != nil {
|
|
return govern.Ending, err
|
|
}
|
|
default:
|
|
}
|
|
}
|
|
|
|
// resolve rewinding
|
|
if dbg.rewindKeyboardAccumulation != 0 {
|
|
amount := 0
|
|
if dbg.rewindKeyboardAccumulation < 0 {
|
|
if dbg.rewindKeyboardAccumulation > -100 {
|
|
dbg.rewindKeyboardAccumulation--
|
|
}
|
|
amount = (dbg.rewindKeyboardAccumulation / 10) - 1
|
|
} else {
|
|
if dbg.rewindKeyboardAccumulation < 100 {
|
|
dbg.rewindKeyboardAccumulation++
|
|
}
|
|
amount = (dbg.rewindKeyboardAccumulation / 10) + 1
|
|
}
|
|
dbg.RewindByAmount(amount)
|
|
}
|
|
|
|
return dbg.State(), nil
|
|
})
|
|
}
|