mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-05-20 05:40:49 -04:00
added VCS.Snapshot() function. simplifies the rewind package
also allows easy access to the VCS snapshot process when there is no need to use the rewind package
This commit is contained in:
parent
5b7a22ebb9
commit
58315e5182
|
@ -77,7 +77,7 @@ func (dbg *Debugger) doDeepPoke(searchState *rewind.State, addr uint16, value ui
|
|||
// the new value at the correct point rather than from the resume
|
||||
// state.
|
||||
pokeHook := func(s *rewind.State) error {
|
||||
return deepPoke(s.Mem, poking, newValue, valueMask)
|
||||
return deepPoke(s.VCS.Mem, poking, newValue, valueMask)
|
||||
}
|
||||
|
||||
// run from found poking.state to the "current state" in the real emulation
|
||||
|
@ -89,7 +89,7 @@ func (dbg *Debugger) doDeepPoke(searchState *rewind.State, addr uint16, value ui
|
|||
// if we're not poking RAM then we poke the state that we found
|
||||
// immediately. this is particulatly important for cartridges with
|
||||
// multiple banks because we need to poke the bank we found.
|
||||
err := deepPoke(poking.state.Mem, poking, newValue, valueMask)
|
||||
err := deepPoke(poking.state.VCS.Mem, poking, newValue, valueMask)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -128,7 +128,7 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
|
||||
// writes to memory always use a register as the source of the write
|
||||
var reg rune
|
||||
switch searchState.CPU.LastResult.Defn.Operator {
|
||||
switch searchState.VCS.CPU.LastResult.Defn.Operator {
|
||||
case instructions.Sta:
|
||||
reg = 'A'
|
||||
case instructions.Stx:
|
||||
|
@ -136,7 +136,7 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
case instructions.Sty:
|
||||
reg = 'Y'
|
||||
default:
|
||||
return deepPoking{}, fmt.Errorf("unexpected write sequence (%s)", searchState.CPU.LastResult.String())
|
||||
return deepPoking{}, fmt.Errorf("unexpected write sequence (%s)", searchState.VCS.CPU.LastResult.String())
|
||||
}
|
||||
|
||||
searchState, err = dbg.Rewind.SearchRegisterWrite(searchState, reg, value, valueMask)
|
||||
|
@ -147,16 +147,16 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
return poking, nil
|
||||
}
|
||||
|
||||
if searchState.CPU.LastResult.Defn == nil {
|
||||
if searchState.VCS.CPU.LastResult.Defn == nil {
|
||||
return deepPoking{}, fmt.Errorf("unexpected CPU result with a nil definition")
|
||||
}
|
||||
|
||||
// writes to a register can happen from another register, and immediate
|
||||
// value, or an address in memory (most probably from the cartridge or
|
||||
// VCS RAM)
|
||||
switch searchState.CPU.LastResult.Defn.AddressingMode {
|
||||
switch searchState.VCS.CPU.LastResult.Defn.AddressingMode {
|
||||
case instructions.Immediate:
|
||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.Address, false)
|
||||
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.Address, false)
|
||||
switch area {
|
||||
case memorymap.Cartridge:
|
||||
pc := registers.NewProgramCounter(ma)
|
||||
|
@ -169,18 +169,18 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM/Cartridge space (%s)", area)
|
||||
}
|
||||
case instructions.AbsoluteIndexedX:
|
||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
||||
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||
switch area {
|
||||
case memorymap.Cartridge:
|
||||
pc := registers.NewProgramCounter(ma)
|
||||
pc.Add(searchState.CPU.X.Address())
|
||||
pc.Add(searchState.VCS.CPU.X.Address())
|
||||
poking.addr = pc.Address()
|
||||
poking.state = searchState
|
||||
poking.area = area
|
||||
return poking, nil
|
||||
case memorymap.RAM:
|
||||
pc := registers.NewProgramCounter(ma)
|
||||
pc.Add(searchState.CPU.X.Address())
|
||||
pc.Add(searchState.VCS.CPU.X.Address())
|
||||
poking.addr = pc.Address()
|
||||
poking.state = searchState
|
||||
poking.area = area
|
||||
|
@ -188,18 +188,18 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM/Cartridge space (%s)", area)
|
||||
}
|
||||
case instructions.AbsoluteIndexedY:
|
||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
||||
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||
switch area {
|
||||
case memorymap.Cartridge:
|
||||
pc := registers.NewProgramCounter(ma)
|
||||
pc.Add(searchState.CPU.Y.Address())
|
||||
pc.Add(searchState.VCS.CPU.Y.Address())
|
||||
poking.addr = pc.Address()
|
||||
poking.state = searchState
|
||||
poking.area = area
|
||||
return poking, nil
|
||||
case memorymap.RAM:
|
||||
pc := registers.NewProgramCounter(ma)
|
||||
pc.Add(searchState.CPU.Y.Address())
|
||||
pc.Add(searchState.VCS.CPU.Y.Address())
|
||||
poking.addr = pc.Address()
|
||||
poking.state = searchState
|
||||
poking.area = area
|
||||
|
@ -207,7 +207,7 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM/Cartridge space (%s)", area)
|
||||
}
|
||||
case instructions.ZeroPage:
|
||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
||||
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||
switch area {
|
||||
case memorymap.RAM:
|
||||
poking.addr = ma
|
||||
|
@ -220,11 +220,11 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM space (%s)", area)
|
||||
}
|
||||
case instructions.ZeroPageIndexedX:
|
||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
||||
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||
switch area {
|
||||
case memorymap.RAM:
|
||||
pc := registers.NewProgramCounter(ma)
|
||||
pc.Add(searchState.CPU.X.Address())
|
||||
pc.Add(searchState.VCS.CPU.X.Address())
|
||||
poking.addr = pc.Address()
|
||||
poking.state = searchState
|
||||
poking.area = area
|
||||
|
@ -235,11 +235,11 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM space (%s)", area)
|
||||
}
|
||||
case instructions.ZeroPageIndexedY:
|
||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
||||
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||
switch area {
|
||||
case memorymap.RAM:
|
||||
pc := registers.NewProgramCounter(ma)
|
||||
pc.Add(searchState.CPU.Y.Address())
|
||||
pc.Add(searchState.VCS.CPU.Y.Address())
|
||||
poking.addr = pc.Address()
|
||||
poking.state = searchState
|
||||
poking.area = area
|
||||
|
@ -250,18 +250,18 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM space (%s)", area)
|
||||
}
|
||||
case instructions.IndirectIndexed:
|
||||
pc := registers.NewProgramCounter(searchState.CPU.LastResult.InstructionData)
|
||||
lo, err := searchState.Mem.Read(pc.Address())
|
||||
pc := registers.NewProgramCounter(searchState.VCS.CPU.LastResult.InstructionData)
|
||||
lo, err := searchState.VCS.Mem.Read(pc.Address())
|
||||
if err != nil {
|
||||
return deepPoking{}, err
|
||||
}
|
||||
pc.Add(1)
|
||||
hi, err := searchState.Mem.Read(pc.Address())
|
||||
hi, err := searchState.VCS.Mem.Read(pc.Address())
|
||||
if err != nil {
|
||||
return deepPoking{}, err
|
||||
}
|
||||
pc.Load((uint16(hi) << 8) | uint16(lo))
|
||||
pc.Add(searchState.CPU.Y.Address())
|
||||
pc.Add(searchState.VCS.CPU.Y.Address())
|
||||
|
||||
ma, area := memorymap.MapAddress(pc.Address(), false)
|
||||
switch area {
|
||||
|
@ -280,7 +280,7 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
|||
|
||||
fallthrough
|
||||
default:
|
||||
return deepPoking{}, fmt.Errorf("unsupported addressing mode (%s)", searchState.CPU.LastResult.String())
|
||||
return deepPoking{}, fmt.Errorf("unsupported addressing mode (%s)", searchState.VCS.CPU.LastResult.String())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -82,7 +82,7 @@ func (win *winCartRAM) debuggerDraw() bool {
|
|||
func (win *winCartRAM) draw(ram []mapper.CartRAM) {
|
||||
// get comparison data. assuming that there is such a thing and that it's
|
||||
// safe to get StaticData from.
|
||||
comp := win.img.cache.Rewind.Comparison.State.Mem.Cart.GetRAMbus().GetRAM()
|
||||
comp := win.img.cache.Rewind.Comparison.State.VCS.Mem.Cart.GetRAMbus().GetRAM()
|
||||
|
||||
imgui.BeginTabBarV("", imgui.TabBarFlagsFittingPolicyScroll)
|
||||
for bank := range ram {
|
||||
|
|
|
@ -75,7 +75,7 @@ func (win *winCartStatic) debuggerDraw() bool {
|
|||
func (win *winCartStatic) draw(static mapper.CartStatic) {
|
||||
// get comparison data. assuming that there is such a thing and that it's
|
||||
// safe to get StaticData from.
|
||||
compStatic := win.img.cache.Rewind.Comparison.State.Mem.Cart.GetStaticBus().GetStatic()
|
||||
compStatic := win.img.cache.Rewind.Comparison.State.VCS.Mem.Cart.GetStaticBus().GetStatic()
|
||||
|
||||
// make a note of cell padding value. this changes for the duration of
|
||||
// drawByteGrid() but we want the default value for when we call
|
||||
|
|
|
@ -61,7 +61,7 @@ func (win *winRAM) debuggerDraw() bool {
|
|||
func (win *winRAM) draw() {
|
||||
var diff []uint8
|
||||
if win.img.cache.Rewind.Comparison.State != nil {
|
||||
diff = win.img.cache.Rewind.Comparison.State.Mem.RAM.RAM
|
||||
diff = win.img.cache.Rewind.Comparison.State.VCS.Mem.RAM.RAM
|
||||
} else {
|
||||
diff = win.img.cache.VCS.Mem.RAM.RAM
|
||||
}
|
||||
|
|
84
hardware/snapshot.go
Normal file
84
hardware/snapshot.go
Normal file
|
@ -0,0 +1,84 @@
|
|||
// 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 hardware
|
||||
|
||||
import (
|
||||
"github.com/jetsetilly/gopher2600/hardware/cpu"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory"
|
||||
"github.com/jetsetilly/gopher2600/hardware/riot"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia"
|
||||
)
|
||||
|
||||
// State stores the VCS sub-systems. It is produced by the Snapshot() function
|
||||
// and can be restored with the Plumb() function
|
||||
//
|
||||
// Note in particular that the TV is not part of the snapshot process
|
||||
type State struct {
|
||||
CPU *cpu.CPU
|
||||
Mem *memory.Memory
|
||||
RIOT *riot.RIOT
|
||||
TIA *tia.TIA
|
||||
}
|
||||
|
||||
// Snapshot creates a copy of a previously snapshotted VCS State
|
||||
func (s *State) Snapshot() *State {
|
||||
return &State{
|
||||
CPU: s.CPU.Snapshot(),
|
||||
Mem: s.Mem.Snapshot(),
|
||||
RIOT: s.RIOT.Snapshot(),
|
||||
TIA: s.TIA.Snapshot(),
|
||||
}
|
||||
}
|
||||
|
||||
// Snapshot the state of the VCS sub-systems
|
||||
func (vcs *VCS) Snapshot() *State {
|
||||
return &State{
|
||||
CPU: vcs.CPU.Snapshot(),
|
||||
Mem: vcs.Mem.Snapshot(),
|
||||
RIOT: vcs.RIOT.Snapshot(),
|
||||
TIA: vcs.TIA.Snapshot(),
|
||||
}
|
||||
}
|
||||
|
||||
// Plumb a previously snapshotted system
|
||||
//
|
||||
// The fromDifferentEmulation indicates that the State has been created by a
|
||||
// different VCS emulation than the one being plumbed into
|
||||
func (vcs *VCS) Plumb(state *State, fromDifferentEmulation bool) {
|
||||
if state == nil {
|
||||
panic("vcs: cannot plumb in a nil state")
|
||||
}
|
||||
|
||||
// take another snapshot of the state before plumbing. we don't want the
|
||||
// machine to change what we have stored in our state array (we learned
|
||||
// that lesson the hard way :-)
|
||||
vcs.CPU = state.CPU.Snapshot()
|
||||
vcs.Mem = state.Mem.Snapshot()
|
||||
vcs.RIOT = state.RIOT.Snapshot()
|
||||
vcs.TIA = state.TIA.Snapshot()
|
||||
|
||||
vcs.CPU.Plumb(vcs.Env, vcs.Mem)
|
||||
vcs.Mem.Plumb(vcs.Env, fromDifferentEmulation)
|
||||
vcs.RIOT.Plumb(vcs.Env, vcs.Mem.RIOT, vcs.Mem.TIA)
|
||||
vcs.TIA.Plumb(vcs.Env, vcs.TV, vcs.Mem.TIA, vcs.RIOT.Ports, vcs.CPU)
|
||||
|
||||
// reset peripherals after new state has been plumbed. without this,
|
||||
// controllers can feel odd if the newly plumbed state has left RIOT memory
|
||||
// in a latched state
|
||||
vcs.RIOT.Ports.ResetPeripherals()
|
||||
|
||||
vcs.Input.Plumb(vcs.TV, vcs.RIOT.Ports)
|
||||
}
|
|
@ -267,13 +267,13 @@ func (tv *Television) Snapshot() *State {
|
|||
return tv.state.Snapshot()
|
||||
}
|
||||
|
||||
// PlumbState attaches an existing television state.
|
||||
func (tv *Television) PlumbState(vcs VCSReturnChannel, s *State) {
|
||||
if s == nil {
|
||||
// Plumb attaches an existing television state.
|
||||
func (tv *Television) Plumb(vcs VCSReturnChannel, state *State) {
|
||||
if state == nil {
|
||||
panic("television: cannot plumb in a nil state")
|
||||
}
|
||||
|
||||
tv.state = s
|
||||
tv.state = state.Snapshot()
|
||||
|
||||
// make sure vcs knows about current spec
|
||||
tv.vcs = vcs
|
||||
|
|
|
@ -47,13 +47,14 @@ type VCS struct {
|
|||
// the television is not "part" of the VCS console but it's part of the VCS system
|
||||
TV *television.Television
|
||||
|
||||
// references to the different components of the VCS. do not take copies of
|
||||
// these pointer values because the rewind feature will change them.
|
||||
// references to the different sub-systems of the VCS. these syb-systems can be
|
||||
// copied with the Snapshot() function creating an instance of the State type
|
||||
CPU *cpu.CPU
|
||||
Mem *memory.Memory
|
||||
RIOT *riot.RIOT
|
||||
TIA *tia.TIA
|
||||
|
||||
// the input sub-system. this is not part of the Snapshot() process
|
||||
Input *input.Input
|
||||
|
||||
// The Clock defines the basic speed at which the the machine is runningt. This governs
|
||||
|
@ -133,24 +134,6 @@ func (vcs *VCS) End() {
|
|||
vcs.RIOT.Ports.End()
|
||||
}
|
||||
|
||||
// Plumb the various VCS sub-systems together after a rewind.
|
||||
//
|
||||
// The fromDifferentEmulation indicates that the State has been created by a
|
||||
// different VCS emulation than the one being plumbed into.
|
||||
func (vcs *VCS) Plumb(fromDifferentEmulation bool) {
|
||||
vcs.CPU.Plumb(vcs.Env, vcs.Mem)
|
||||
vcs.Mem.Plumb(vcs.Env, fromDifferentEmulation)
|
||||
vcs.RIOT.Plumb(vcs.Env, vcs.Mem.RIOT, vcs.Mem.TIA)
|
||||
vcs.TIA.Plumb(vcs.Env, vcs.TV, vcs.Mem.TIA, vcs.RIOT.Ports, vcs.CPU)
|
||||
|
||||
// reset peripherals after new state has been plumbed. without this,
|
||||
// controllers can feel odd if the newly plumbed state has left RIOT memory
|
||||
// in a latched state
|
||||
vcs.RIOT.Ports.ResetPeripherals()
|
||||
|
||||
vcs.Input.Plumb(vcs.TV, vcs.RIOT.Ports)
|
||||
}
|
||||
|
||||
// AttachCartridge to this VCS. While this function can be called directly it
|
||||
// is advised that the setup package be used in most circumstances.
|
||||
//
|
||||
|
|
|
@ -21,13 +21,9 @@ import (
|
|||
|
||||
"github.com/jetsetilly/gopher2600/debugger/govern"
|
||||
"github.com/jetsetilly/gopher2600/hardware"
|
||||
"github.com/jetsetilly/gopher2600/hardware/cpu"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory"
|
||||
"github.com/jetsetilly/gopher2600/hardware/riot"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia"
|
||||
"github.com/jetsetilly/gopher2600/logger"
|
||||
)
|
||||
|
||||
|
@ -55,21 +51,14 @@ type Runner interface {
|
|||
// reference.
|
||||
type State struct {
|
||||
level snapshotLevel
|
||||
|
||||
CPU *cpu.CPU
|
||||
Mem *memory.Memory
|
||||
RIOT *riot.RIOT
|
||||
TIA *tia.TIA
|
||||
TV *television.State
|
||||
VCS *hardware.State
|
||||
TV *television.State
|
||||
}
|
||||
|
||||
func (s *State) snapshot() *State {
|
||||
return &State{
|
||||
level: s.level,
|
||||
CPU: s.CPU.Snapshot(),
|
||||
Mem: s.Mem.Snapshot(),
|
||||
RIOT: s.RIOT.Snapshot(),
|
||||
TIA: s.TIA.Snapshot(),
|
||||
VCS: s.VCS.Snapshot(),
|
||||
TV: s.TV.Snapshot(),
|
||||
}
|
||||
}
|
||||
|
@ -340,10 +329,7 @@ func (r *Rewind) lastEntryIdx() int {
|
|||
func snapshot(vcs *hardware.VCS, level snapshotLevel) *State {
|
||||
return &State{
|
||||
level: level,
|
||||
CPU: vcs.CPU.Snapshot(),
|
||||
Mem: vcs.Mem.Snapshot(),
|
||||
RIOT: vcs.RIOT.Snapshot(),
|
||||
TIA: vcs.TIA.Snapshot(),
|
||||
VCS: vcs.Snapshot(),
|
||||
TV: vcs.TV.Snapshot(),
|
||||
}
|
||||
}
|
||||
|
@ -443,18 +429,10 @@ func (r *Rewind) append(s *State) {
|
|||
func Plumb(vcs *hardware.VCS, state *State, fromDifferentEmulation bool) {
|
||||
// tv plumbing works a bit different to other areas because we're only
|
||||
// recording the state of the TV not the entire TV itself.
|
||||
vcs.TV.PlumbState(vcs, state.TV.Snapshot())
|
||||
|
||||
// take another snapshot of the state before plumbing. we don't want the
|
||||
// machine to change what we have stored in our state array (we learned
|
||||
// that lesson the hard way :-)
|
||||
vcs.CPU = state.CPU.Snapshot()
|
||||
vcs.Mem = state.Mem.Snapshot()
|
||||
vcs.RIOT = state.RIOT.Snapshot()
|
||||
vcs.TIA = state.TIA.Snapshot()
|
||||
vcs.TV.Plumb(vcs, state.TV)
|
||||
|
||||
// finish off plumbing process
|
||||
vcs.Plumb(fromDifferentEmulation)
|
||||
vcs.Plumb(state.VCS, fromDifferentEmulation)
|
||||
}
|
||||
|
||||
// run from the supplied state until the cooridinates are reached.
|
||||
|
|
Loading…
Reference in a new issue