mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2025-04-02 11:02:17 -04:00
this allows us to remove the global DisableSaving flag from the prefs package. it also allows us to remove the preferences.go file from the debugger package, simplifying things a little bit the ReqROMSelector request has been removed from the gui package and handling from the sdlimgui package. the decision on whether to open the ROM selector is done entirely inside the sdlimgui package now. this is good because there shouldn't be any need for the debugger package to worry about that kind of thing
146 lines
4.6 KiB
Go
146 lines
4.6 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/hardware"
|
|
"github.com/jetsetilly/gopher2600/userinput"
|
|
)
|
|
|
|
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
|
|
})
|
|
}
|