mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-02 20:18:20 -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
|
// the new value at the correct point rather than from the resume
|
||||||
// state.
|
// state.
|
||||||
pokeHook := func(s *rewind.State) error {
|
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
|
// 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
|
// if we're not poking RAM then we poke the state that we found
|
||||||
// immediately. this is particulatly important for cartridges with
|
// immediately. this is particulatly important for cartridges with
|
||||||
// multiple banks because we need to poke the bank we found.
|
// 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 {
|
if err != nil {
|
||||||
return err
|
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
|
// writes to memory always use a register as the source of the write
|
||||||
var reg rune
|
var reg rune
|
||||||
switch searchState.CPU.LastResult.Defn.Operator {
|
switch searchState.VCS.CPU.LastResult.Defn.Operator {
|
||||||
case instructions.Sta:
|
case instructions.Sta:
|
||||||
reg = 'A'
|
reg = 'A'
|
||||||
case instructions.Stx:
|
case instructions.Stx:
|
||||||
|
@ -136,7 +136,7 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
||||||
case instructions.Sty:
|
case instructions.Sty:
|
||||||
reg = 'Y'
|
reg = 'Y'
|
||||||
default:
|
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)
|
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
|
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")
|
return deepPoking{}, fmt.Errorf("unexpected CPU result with a nil definition")
|
||||||
}
|
}
|
||||||
|
|
||||||
// writes to a register can happen from another register, and immediate
|
// writes to a register can happen from another register, and immediate
|
||||||
// value, or an address in memory (most probably from the cartridge or
|
// value, or an address in memory (most probably from the cartridge or
|
||||||
// VCS RAM)
|
// VCS RAM)
|
||||||
switch searchState.CPU.LastResult.Defn.AddressingMode {
|
switch searchState.VCS.CPU.LastResult.Defn.AddressingMode {
|
||||||
case instructions.Immediate:
|
case instructions.Immediate:
|
||||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.Address, false)
|
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.Address, false)
|
||||||
switch area {
|
switch area {
|
||||||
case memorymap.Cartridge:
|
case memorymap.Cartridge:
|
||||||
pc := registers.NewProgramCounter(ma)
|
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)
|
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM/Cartridge space (%s)", area)
|
||||||
}
|
}
|
||||||
case instructions.AbsoluteIndexedX:
|
case instructions.AbsoluteIndexedX:
|
||||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||||
switch area {
|
switch area {
|
||||||
case memorymap.Cartridge:
|
case memorymap.Cartridge:
|
||||||
pc := registers.NewProgramCounter(ma)
|
pc := registers.NewProgramCounter(ma)
|
||||||
pc.Add(searchState.CPU.X.Address())
|
pc.Add(searchState.VCS.CPU.X.Address())
|
||||||
poking.addr = pc.Address()
|
poking.addr = pc.Address()
|
||||||
poking.state = searchState
|
poking.state = searchState
|
||||||
poking.area = area
|
poking.area = area
|
||||||
return poking, nil
|
return poking, nil
|
||||||
case memorymap.RAM:
|
case memorymap.RAM:
|
||||||
pc := registers.NewProgramCounter(ma)
|
pc := registers.NewProgramCounter(ma)
|
||||||
pc.Add(searchState.CPU.X.Address())
|
pc.Add(searchState.VCS.CPU.X.Address())
|
||||||
poking.addr = pc.Address()
|
poking.addr = pc.Address()
|
||||||
poking.state = searchState
|
poking.state = searchState
|
||||||
poking.area = area
|
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)
|
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM/Cartridge space (%s)", area)
|
||||||
}
|
}
|
||||||
case instructions.AbsoluteIndexedY:
|
case instructions.AbsoluteIndexedY:
|
||||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||||
switch area {
|
switch area {
|
||||||
case memorymap.Cartridge:
|
case memorymap.Cartridge:
|
||||||
pc := registers.NewProgramCounter(ma)
|
pc := registers.NewProgramCounter(ma)
|
||||||
pc.Add(searchState.CPU.Y.Address())
|
pc.Add(searchState.VCS.CPU.Y.Address())
|
||||||
poking.addr = pc.Address()
|
poking.addr = pc.Address()
|
||||||
poking.state = searchState
|
poking.state = searchState
|
||||||
poking.area = area
|
poking.area = area
|
||||||
return poking, nil
|
return poking, nil
|
||||||
case memorymap.RAM:
|
case memorymap.RAM:
|
||||||
pc := registers.NewProgramCounter(ma)
|
pc := registers.NewProgramCounter(ma)
|
||||||
pc.Add(searchState.CPU.Y.Address())
|
pc.Add(searchState.VCS.CPU.Y.Address())
|
||||||
poking.addr = pc.Address()
|
poking.addr = pc.Address()
|
||||||
poking.state = searchState
|
poking.state = searchState
|
||||||
poking.area = area
|
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)
|
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM/Cartridge space (%s)", area)
|
||||||
}
|
}
|
||||||
case instructions.ZeroPage:
|
case instructions.ZeroPage:
|
||||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||||
switch area {
|
switch area {
|
||||||
case memorymap.RAM:
|
case memorymap.RAM:
|
||||||
poking.addr = ma
|
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)
|
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM space (%s)", area)
|
||||||
}
|
}
|
||||||
case instructions.ZeroPageIndexedX:
|
case instructions.ZeroPageIndexedX:
|
||||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||||
switch area {
|
switch area {
|
||||||
case memorymap.RAM:
|
case memorymap.RAM:
|
||||||
pc := registers.NewProgramCounter(ma)
|
pc := registers.NewProgramCounter(ma)
|
||||||
pc.Add(searchState.CPU.X.Address())
|
pc.Add(searchState.VCS.CPU.X.Address())
|
||||||
poking.addr = pc.Address()
|
poking.addr = pc.Address()
|
||||||
poking.state = searchState
|
poking.state = searchState
|
||||||
poking.area = area
|
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)
|
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM space (%s)", area)
|
||||||
}
|
}
|
||||||
case instructions.ZeroPageIndexedY:
|
case instructions.ZeroPageIndexedY:
|
||||||
ma, area := memorymap.MapAddress(searchState.CPU.LastResult.InstructionData, false)
|
ma, area := memorymap.MapAddress(searchState.VCS.CPU.LastResult.InstructionData, false)
|
||||||
switch area {
|
switch area {
|
||||||
case memorymap.RAM:
|
case memorymap.RAM:
|
||||||
pc := registers.NewProgramCounter(ma)
|
pc := registers.NewProgramCounter(ma)
|
||||||
pc.Add(searchState.CPU.Y.Address())
|
pc.Add(searchState.VCS.CPU.Y.Address())
|
||||||
poking.addr = pc.Address()
|
poking.addr = pc.Address()
|
||||||
poking.state = searchState
|
poking.state = searchState
|
||||||
poking.area = area
|
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)
|
return deepPoking{}, fmt.Errorf("not deeppoking through non-RAM space (%s)", area)
|
||||||
}
|
}
|
||||||
case instructions.IndirectIndexed:
|
case instructions.IndirectIndexed:
|
||||||
pc := registers.NewProgramCounter(searchState.CPU.LastResult.InstructionData)
|
pc := registers.NewProgramCounter(searchState.VCS.CPU.LastResult.InstructionData)
|
||||||
lo, err := searchState.Mem.Read(pc.Address())
|
lo, err := searchState.VCS.Mem.Read(pc.Address())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return deepPoking{}, err
|
return deepPoking{}, err
|
||||||
}
|
}
|
||||||
pc.Add(1)
|
pc.Add(1)
|
||||||
hi, err := searchState.Mem.Read(pc.Address())
|
hi, err := searchState.VCS.Mem.Read(pc.Address())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return deepPoking{}, err
|
return deepPoking{}, err
|
||||||
}
|
}
|
||||||
pc.Load((uint16(hi) << 8) | uint16(lo))
|
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)
|
ma, area := memorymap.MapAddress(pc.Address(), false)
|
||||||
switch area {
|
switch area {
|
||||||
|
@ -280,7 +280,7 @@ func (dbg *Debugger) searchDeepPoke(searchState *rewind.State, searchAddr uint16
|
||||||
|
|
||||||
fallthrough
|
fallthrough
|
||||||
default:
|
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) {
|
func (win *winCartRAM) draw(ram []mapper.CartRAM) {
|
||||||
// get comparison data. assuming that there is such a thing and that it's
|
// get comparison data. assuming that there is such a thing and that it's
|
||||||
// safe to get StaticData from.
|
// 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)
|
imgui.BeginTabBarV("", imgui.TabBarFlagsFittingPolicyScroll)
|
||||||
for bank := range ram {
|
for bank := range ram {
|
||||||
|
|
|
@ -75,7 +75,7 @@ func (win *winCartStatic) debuggerDraw() bool {
|
||||||
func (win *winCartStatic) draw(static mapper.CartStatic) {
|
func (win *winCartStatic) draw(static mapper.CartStatic) {
|
||||||
// get comparison data. assuming that there is such a thing and that it's
|
// get comparison data. assuming that there is such a thing and that it's
|
||||||
// safe to get StaticData from.
|
// 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
|
// make a note of cell padding value. this changes for the duration of
|
||||||
// drawByteGrid() but we want the default value for when we call
|
// drawByteGrid() but we want the default value for when we call
|
||||||
|
|
|
@ -61,7 +61,7 @@ func (win *winRAM) debuggerDraw() bool {
|
||||||
func (win *winRAM) draw() {
|
func (win *winRAM) draw() {
|
||||||
var diff []uint8
|
var diff []uint8
|
||||||
if win.img.cache.Rewind.Comparison.State != nil {
|
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 {
|
} else {
|
||||||
diff = win.img.cache.VCS.Mem.RAM.RAM
|
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()
|
return tv.state.Snapshot()
|
||||||
}
|
}
|
||||||
|
|
||||||
// PlumbState attaches an existing television state.
|
// Plumb attaches an existing television state.
|
||||||
func (tv *Television) PlumbState(vcs VCSReturnChannel, s *State) {
|
func (tv *Television) Plumb(vcs VCSReturnChannel, state *State) {
|
||||||
if s == nil {
|
if state == nil {
|
||||||
panic("television: cannot plumb in a nil state")
|
panic("television: cannot plumb in a nil state")
|
||||||
}
|
}
|
||||||
|
|
||||||
tv.state = s
|
tv.state = state.Snapshot()
|
||||||
|
|
||||||
// make sure vcs knows about current spec
|
// make sure vcs knows about current spec
|
||||||
tv.vcs = vcs
|
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
|
// the television is not "part" of the VCS console but it's part of the VCS system
|
||||||
TV *television.Television
|
TV *television.Television
|
||||||
|
|
||||||
// references to the different components of the VCS. do not take copies of
|
// references to the different sub-systems of the VCS. these syb-systems can be
|
||||||
// these pointer values because the rewind feature will change them.
|
// copied with the Snapshot() function creating an instance of the State type
|
||||||
CPU *cpu.CPU
|
CPU *cpu.CPU
|
||||||
Mem *memory.Memory
|
Mem *memory.Memory
|
||||||
RIOT *riot.RIOT
|
RIOT *riot.RIOT
|
||||||
TIA *tia.TIA
|
TIA *tia.TIA
|
||||||
|
|
||||||
|
// the input sub-system. this is not part of the Snapshot() process
|
||||||
Input *input.Input
|
Input *input.Input
|
||||||
|
|
||||||
// The Clock defines the basic speed at which the the machine is runningt. This governs
|
// 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()
|
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
|
// AttachCartridge to this VCS. While this function can be called directly it
|
||||||
// is advised that the setup package be used in most circumstances.
|
// 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/debugger/govern"
|
||||||
"github.com/jetsetilly/gopher2600/hardware"
|
"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"
|
||||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||||
"github.com/jetsetilly/gopher2600/hardware/tia"
|
|
||||||
"github.com/jetsetilly/gopher2600/logger"
|
"github.com/jetsetilly/gopher2600/logger"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -55,21 +51,14 @@ type Runner interface {
|
||||||
// reference.
|
// reference.
|
||||||
type State struct {
|
type State struct {
|
||||||
level snapshotLevel
|
level snapshotLevel
|
||||||
|
VCS *hardware.State
|
||||||
CPU *cpu.CPU
|
TV *television.State
|
||||||
Mem *memory.Memory
|
|
||||||
RIOT *riot.RIOT
|
|
||||||
TIA *tia.TIA
|
|
||||||
TV *television.State
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (s *State) snapshot() *State {
|
func (s *State) snapshot() *State {
|
||||||
return &State{
|
return &State{
|
||||||
level: s.level,
|
level: s.level,
|
||||||
CPU: s.CPU.Snapshot(),
|
VCS: s.VCS.Snapshot(),
|
||||||
Mem: s.Mem.Snapshot(),
|
|
||||||
RIOT: s.RIOT.Snapshot(),
|
|
||||||
TIA: s.TIA.Snapshot(),
|
|
||||||
TV: s.TV.Snapshot(),
|
TV: s.TV.Snapshot(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -340,10 +329,7 @@ func (r *Rewind) lastEntryIdx() int {
|
||||||
func snapshot(vcs *hardware.VCS, level snapshotLevel) *State {
|
func snapshot(vcs *hardware.VCS, level snapshotLevel) *State {
|
||||||
return &State{
|
return &State{
|
||||||
level: level,
|
level: level,
|
||||||
CPU: vcs.CPU.Snapshot(),
|
VCS: vcs.Snapshot(),
|
||||||
Mem: vcs.Mem.Snapshot(),
|
|
||||||
RIOT: vcs.RIOT.Snapshot(),
|
|
||||||
TIA: vcs.TIA.Snapshot(),
|
|
||||||
TV: vcs.TV.Snapshot(),
|
TV: vcs.TV.Snapshot(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -443,18 +429,10 @@ func (r *Rewind) append(s *State) {
|
||||||
func Plumb(vcs *hardware.VCS, state *State, fromDifferentEmulation bool) {
|
func Plumb(vcs *hardware.VCS, state *State, fromDifferentEmulation bool) {
|
||||||
// tv plumbing works a bit different to other areas because we're only
|
// tv plumbing works a bit different to other areas because we're only
|
||||||
// recording the state of the TV not the entire TV itself.
|
// recording the state of the TV not the entire TV itself.
|
||||||
vcs.TV.PlumbState(vcs, state.TV.Snapshot())
|
vcs.TV.Plumb(vcs, state.TV)
|
||||||
|
|
||||||
// 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()
|
|
||||||
|
|
||||||
// finish off plumbing process
|
// finish off plumbing process
|
||||||
vcs.Plumb(fromDifferentEmulation)
|
vcs.Plumb(state.VCS, fromDifferentEmulation)
|
||||||
}
|
}
|
||||||
|
|
||||||
// run from the supplied state until the cooridinates are reached.
|
// run from the supplied state until the cooridinates are reached.
|
||||||
|
|
Loading…
Reference in a new issue