mirror of
https://github.com/JetSetIlly/Gopher2600.git
synced 2024-06-02 12:08:01 -04:00
removed InstructionBoundary() interface and television.ReqState()
Instruction boundaries can be inferred by the debugging input loop, which is the only place it is requred. reworked GetAdjustedCoords() in the televsion package (now called AdjCoords()) to cope with this. ReqState() is totally replaced by GetCoords()
This commit is contained in:
parent
63b188b997
commit
31138f16f3
|
@ -38,7 +38,8 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
|
||||
"github.com/jetsetilly/gopher2600/hardware/riot/ports/controllers"
|
||||
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
"github.com/jetsetilly/gopher2600/linter"
|
||||
"github.com/jetsetilly/gopher2600/logger"
|
||||
"github.com/jetsetilly/gopher2600/patch"
|
||||
|
@ -198,14 +199,14 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
dbg.haltImmediately = true
|
||||
|
||||
case cmdStep:
|
||||
adj := 1
|
||||
adjAmount := 1
|
||||
back := false
|
||||
|
||||
if tk, ok := tokens.Get(); ok {
|
||||
switch tk {
|
||||
case "BACK":
|
||||
back = true
|
||||
adj *= -1
|
||||
adjAmount *= -1
|
||||
|
||||
case "OVER":
|
||||
// if next expected opcode is JSR then add a volatile breakpoint to the
|
||||
|
@ -229,33 +230,37 @@ func (dbg *Debugger) processTokens(tokens *commandline.Tokens) error {
|
|||
mode = strings.ToUpper(mode)
|
||||
|
||||
if back {
|
||||
var req signal.StateAdj
|
||||
var instruction bool
|
||||
var adj television.Adj
|
||||
|
||||
switch mode {
|
||||
case "":
|
||||
// continue with current quantum state
|
||||
if dbg.stepQuantum == QuantumInstruction {
|
||||
req = signal.AdjInstruction
|
||||
instruction = true
|
||||
} else {
|
||||
req = signal.AdjClock
|
||||
adj = television.AdjClock
|
||||
}
|
||||
case "INSTRUCTION":
|
||||
dbg.stepQuantum = QuantumInstruction
|
||||
req = signal.AdjInstruction
|
||||
instruction = true
|
||||
case "VIDEO":
|
||||
dbg.stepQuantum = QuantumVideo
|
||||
req = signal.AdjClock
|
||||
adj = television.AdjClock
|
||||
case "SCANLINE":
|
||||
req = signal.AdjScanline
|
||||
adj = television.AdjScanline
|
||||
case "FRAME":
|
||||
req = signal.AdjFramenum
|
||||
adj = television.AdjFrame
|
||||
default:
|
||||
return curated.Errorf("unknown STEP BACK mode (%s)", mode)
|
||||
}
|
||||
|
||||
coords, err := dbg.vcs.TV.GetAdjustedCoords(req, adj, true)
|
||||
if err != nil {
|
||||
return err
|
||||
var coords coords.TelevisionCoords
|
||||
|
||||
if instruction {
|
||||
coords = dbg.lastCPUboundary
|
||||
} else {
|
||||
coords = dbg.vcs.TV.AdjCoords(adj, adjAmount)
|
||||
}
|
||||
|
||||
dbg.setState(emulation.Rewinding)
|
||||
|
|
|
@ -40,6 +40,7 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
|
||||
"github.com/jetsetilly/gopher2600/hardware/riot/ports/savekey"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
"github.com/jetsetilly/gopher2600/logger"
|
||||
"github.com/jetsetilly/gopher2600/reflection"
|
||||
"github.com/jetsetilly/gopher2600/rewind"
|
||||
|
@ -66,6 +67,9 @@ type Debugger struct {
|
|||
lastBank mapper.BankInfo
|
||||
lastResult *disassembly.Entry
|
||||
|
||||
// the television coords of the last CPU instruction
|
||||
lastCPUboundary coords.TelevisionCoords
|
||||
|
||||
// gui, terminal and controllers
|
||||
gui gui.GUI
|
||||
term terminal.Terminal
|
||||
|
@ -242,9 +246,6 @@ func NewDebugger(tv *television.Television, scr gui.GUI, term terminal.Terminal,
|
|||
dbg.vcs.TV.AddFrameTrigger(dbg.Rewind)
|
||||
dbg.vcs.TV.AddFrameTrigger(dbg.ref)
|
||||
|
||||
// plug TV BoundaryTrigger into CPU
|
||||
dbg.vcs.CPU.AddBoundaryTrigger(dbg.vcs.TV)
|
||||
|
||||
// halting coordination
|
||||
dbg.halting, err = newHaltCoordination(dbg)
|
||||
if err != nil {
|
||||
|
|
|
@ -21,7 +21,6 @@ import (
|
|||
|
||||
"github.com/jetsetilly/gopher2600/curated"
|
||||
"github.com/jetsetilly/gopher2600/debugger/terminal/commandline"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
)
|
||||
|
||||
// targetValue represents the underlying value of the target. for example in
|
||||
|
@ -138,7 +137,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
trg = &target{
|
||||
label: "Frame",
|
||||
value: func() targetValue {
|
||||
return dbg.vcs.TV.GetState(signal.ReqFramenum)
|
||||
return dbg.vcs.TV.GetCoords().Frame
|
||||
},
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
@ -147,7 +146,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
trg = &target{
|
||||
label: "Scanline",
|
||||
value: func() targetValue {
|
||||
return dbg.vcs.TV.GetState(signal.ReqScanline)
|
||||
return dbg.vcs.TV.GetCoords().Scanline
|
||||
},
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
@ -156,7 +155,7 @@ func parseTarget(dbg *Debugger, tokens *commandline.Tokens) (*target, error) {
|
|||
trg = &target{
|
||||
label: "Clock",
|
||||
value: func() targetValue {
|
||||
return dbg.vcs.TV.GetState(signal.ReqClock)
|
||||
return dbg.vcs.TV.GetCoords().Clock
|
||||
},
|
||||
instructionBoundary: false,
|
||||
}
|
||||
|
|
|
@ -125,6 +125,9 @@ func (dbg *Debugger) catchupLoop(inputter terminal.Input) error {
|
|||
for !ended {
|
||||
dbg.lastBank = dbg.vcs.Mem.Cart.GetBank(dbg.vcs.CPU.PC.Address())
|
||||
|
||||
// coords of CPU instruction before calling vcs.Step()
|
||||
dbg.lastCPUboundary = dbg.vcs.TV.GetCoords()
|
||||
|
||||
err := dbg.vcs.Step(callback)
|
||||
if err != nil {
|
||||
return err
|
||||
|
@ -435,6 +438,9 @@ func (dbg *Debugger) step(inputter terminal.Input, catchup bool) error {
|
|||
// to happen before we call the VCS.Step() function
|
||||
dbg.lastBank = dbg.vcs.Mem.Cart.GetBank(dbg.vcs.CPU.PC.Address())
|
||||
|
||||
// coords of CPU instruction before calling vcs.Step()
|
||||
dbg.lastCPUboundary = dbg.vcs.TV.GetCoords()
|
||||
|
||||
// not using the err variable because we'll clobber it before we
|
||||
// get to check the result of VCS.Step()
|
||||
stepErr := dbg.vcs.Step(callback)
|
||||
|
|
|
@ -56,7 +56,7 @@ func NewVideo(tv *television.Television) (*Video, error) {
|
|||
|
||||
// length of pixels array contains enough room for the previous frames digest value
|
||||
l := len(dig.digest)
|
||||
l += television.MaxSignalHistory * pixelDepth
|
||||
l += specification.AbsoluteMaxClks * pixelDepth
|
||||
|
||||
// allocate enough pixels for entire frame
|
||||
dig.pixels = make([]byte, l)
|
||||
|
|
|
@ -22,8 +22,8 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge"
|
||||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
)
|
||||
|
||||
// CoProcessor is used to handle the disassembly of instructions from an
|
||||
|
@ -99,7 +99,7 @@ func (cop *CoProcessor) Start() {
|
|||
// have been called on the last CPU cycle of the instruction that triggers
|
||||
// the coprocessor reset. the TV will not have moved onto the beginning of
|
||||
// the next instruction yet so we must figure it out here
|
||||
cop.lastStart, _ = cop.vcs.TV.GetAdjustedCoords(signal.AdjCPUCycle, 1, false)
|
||||
cop.lastStart = cop.vcs.TV.AdjCoords(television.AdjCPUCycle, 1)
|
||||
}
|
||||
|
||||
cop.lastExecution = cop.lastExecution[:0]
|
||||
|
|
|
@ -406,7 +406,7 @@ func (scr *screen) Reflect(ref []reflection.ReflectedVideoStep) error {
|
|||
// array but if we do want to make sure then something like the condition
|
||||
// below would be good.
|
||||
//
|
||||
if len(ref) != television.MaxSignalHistory {
|
||||
if len(ref) != specification.AbsoluteMaxClks {
|
||||
panic("reflected entries not the same length as reflection buffer")
|
||||
}
|
||||
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
|
||||
"github.com/inkyblackness/imgui-go/v4"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
)
|
||||
|
||||
|
@ -237,7 +236,7 @@ func (win *winTimeline) drawTimeline() {
|
|||
|
||||
// comparison frame indicator
|
||||
if win.img.lz.Rewind.Comparison != nil {
|
||||
fr := win.img.lz.Rewind.Comparison.TV.GetState(signal.ReqFramenum) - rewindOffset
|
||||
fr := win.img.lz.Rewind.Comparison.TV.GetCoords().Frame - rewindOffset
|
||||
|
||||
if fr < 0 {
|
||||
// draw triangle indicating that the comparison frame is not
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
// 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 cpu
|
||||
|
||||
// BoundaryTrigger functions are called at key points in the CPU execution.
|
||||
type BoundaryTrigger interface {
|
||||
// InstructionBoundary is called at the start of each new CPU instruction.
|
||||
InstructionBoundary()
|
||||
}
|
|
@ -73,9 +73,6 @@ type CPU struct {
|
|||
// otherwise be considered an error. Resets to false on every call to
|
||||
// ExecuteInstruction()
|
||||
Interrupted bool
|
||||
|
||||
// list of BoundaryTriggers implementations to consult
|
||||
boundaryTriggers []BoundaryTrigger
|
||||
}
|
||||
|
||||
// NewCPU is the preferred method of initialisation for the CPU structure. Note
|
||||
|
@ -96,12 +93,6 @@ func NewCPU(prefs *preferences.Preferences, mem bus.CPUBus) *CPU {
|
|||
}
|
||||
}
|
||||
|
||||
// AddPixelRenderer registers an implementation of BoundaryTrigger. Multiple
|
||||
// implemntations can be added.
|
||||
func (mc *CPU) AddBoundaryTrigger(b BoundaryTrigger) {
|
||||
mc.boundaryTriggers = append(mc.boundaryTriggers, b)
|
||||
}
|
||||
|
||||
// Snapshot creates a copy of the CPU in its current state.
|
||||
func (mc *CPU) Snapshot() *CPU {
|
||||
n := *mc
|
||||
|
@ -538,11 +529,6 @@ func (mc *CPU) ExecuteInstruction(cycleCallback func() error) error {
|
|||
return nil
|
||||
}
|
||||
|
||||
// process all boundaryTriggers
|
||||
for _, b := range mc.boundaryTriggers {
|
||||
b.InstructionBoundary()
|
||||
}
|
||||
|
||||
// prepare new round of results
|
||||
mc.LastResult.Reset()
|
||||
mc.LastResult.Address = mc.PC.Address()
|
||||
|
|
|
@ -17,7 +17,6 @@ package hardware
|
|||
|
||||
import (
|
||||
"github.com/jetsetilly/gopher2600/emulation"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
)
|
||||
|
||||
// checking continue condition every Run iteration is too frequent. A modest
|
||||
|
@ -85,7 +84,7 @@ func (vcs *VCS) RunForFrameCount(numFrames int, continueCheck func(frame int) (e
|
|||
continueCheck = func(frame int) (emulation.State, error) { return emulation.Running, nil }
|
||||
}
|
||||
|
||||
frameNum := vcs.TV.GetState(signal.ReqFramenum)
|
||||
frameNum := vcs.TV.GetCoords().Frame
|
||||
targetFrame := frameNum + numFrames
|
||||
|
||||
state := emulation.Running
|
||||
|
@ -95,7 +94,7 @@ func (vcs *VCS) RunForFrameCount(numFrames int, continueCheck func(frame int) (e
|
|||
return err
|
||||
}
|
||||
|
||||
frameNum = vcs.TV.GetState(signal.ReqFramenum)
|
||||
frameNum = vcs.TV.GetCoords().Frame
|
||||
|
||||
state, err = continueCheck(frameNum)
|
||||
if err != nil {
|
||||
|
|
85
hardware/television/adjustment.go
Normal file
85
hardware/television/adjustment.go
Normal file
|
@ -0,0 +1,85 @@
|
|||
// 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 television
|
||||
|
||||
import (
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
)
|
||||
|
||||
// AdjCoords returns a coords.TelevisionCoords with the current coords adjusted
|
||||
// by the specified amount.
|
||||
func (tv *Television) AdjCoords(adj Adj, amount int) coords.TelevisionCoords {
|
||||
coords := tv.GetCoords()
|
||||
|
||||
switch adj {
|
||||
case AdjCPUCycle:
|
||||
// adjusting by CPU cycle is the same as adjusting by video cycle
|
||||
// accept to say that a CPU cycle is the equivalent of 3 video cycles
|
||||
amount *= 3
|
||||
fallthrough
|
||||
case AdjClock:
|
||||
coords.Clock += amount
|
||||
if coords.Clock >= specification.ClksScanline {
|
||||
coords.Clock -= specification.ClksScanline
|
||||
coords.Scanline++
|
||||
} else if coords.Clock < 0 {
|
||||
coords.Clock += specification.ClksScanline
|
||||
coords.Scanline--
|
||||
}
|
||||
if coords.Scanline > tv.state.frameInfo.TotalScanlines {
|
||||
coords.Scanline -= tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame++
|
||||
} else if coords.Scanline < 0 {
|
||||
coords.Scanline += tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame--
|
||||
}
|
||||
case AdjScanline:
|
||||
coords.Clock = 0
|
||||
coords.Scanline += amount
|
||||
if coords.Scanline > tv.state.frameInfo.TotalScanlines {
|
||||
coords.Scanline -= tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame++
|
||||
} else if coords.Scanline < 0 {
|
||||
coords.Scanline += tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame--
|
||||
}
|
||||
case AdjFrame:
|
||||
coords.Clock = 0
|
||||
coords.Scanline = 0
|
||||
coords.Frame += amount
|
||||
}
|
||||
|
||||
// zero values if frame is less than zero
|
||||
if coords.Frame < 0 {
|
||||
coords.Frame = 0
|
||||
coords.Scanline = 0
|
||||
coords.Clock = 0
|
||||
}
|
||||
|
||||
return coords
|
||||
}
|
||||
|
||||
// Adj is used to specify adjustment scale for the ReqAdjust() function.
|
||||
type Adj int
|
||||
|
||||
// List of valid Adj values.
|
||||
const (
|
||||
AdjFrame Adj = iota
|
||||
AdjScanline
|
||||
AdjCPUCycle
|
||||
AdjClock
|
||||
)
|
|
@ -42,7 +42,9 @@
|
|||
// the current incoming TV signal. For debugging purposes, the framerate can
|
||||
// also be set to a specific value
|
||||
//
|
||||
// Framesize adaptation is also handled by the television package.
|
||||
// Framesize adaptation is also handled by the television package. Current
|
||||
// information about the frame can be acquired with GetFrameInfo(). FrameInfo
|
||||
// will also be sent to the PixelRenderers as appropriate.
|
||||
//
|
||||
// Screen Rolling
|
||||
//
|
||||
|
|
|
@ -17,7 +17,6 @@ package television
|
|||
|
||||
import (
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
)
|
||||
|
||||
// PixelRenderer implementations displays, or otherwise works with, visual
|
||||
|
@ -152,9 +151,6 @@ type AudioMixer interface {
|
|||
Reset()
|
||||
}
|
||||
|
||||
// MaxSignalHistory is the absolute maximum number of entries in a signal history for an entire frame.
|
||||
const MaxSignalHistory = specification.AbsoluteMaxClks
|
||||
|
||||
// VCSReturnChannel is used to send information from the TV back to the parent
|
||||
// console. Named because I think of it as being similar to the Audio Return
|
||||
// Channel (ARC) present in modern TVs.
|
||||
|
|
|
@ -17,7 +17,11 @@
|
|||
// implementation.
|
||||
package signal
|
||||
|
||||
import "strings"
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
)
|
||||
|
||||
// ColorSignal represents the signal that is sent from the VCS to the television.
|
||||
type ColorSignal uint8
|
||||
|
@ -75,36 +79,13 @@ func (a SignalAttributes) String() string {
|
|||
return s.String()
|
||||
}
|
||||
|
||||
// StateReq is used to identify which television attribute is being asked
|
||||
// with the GetState() function.
|
||||
type StateReq int
|
||||
|
||||
// List of valid state requests.
|
||||
const (
|
||||
ReqFramenum StateReq = iota
|
||||
ReqScanline
|
||||
ReqClock
|
||||
)
|
||||
|
||||
// StateAdj is used to specify adjustment scale for the ReqAdjust() function.
|
||||
type StateAdj int
|
||||
|
||||
// List of valid adjustment scales.
|
||||
const (
|
||||
AdjFramenum StateAdj = iota
|
||||
AdjScanline
|
||||
AdjInstruction
|
||||
AdjCPUCycle
|
||||
AdjClock
|
||||
)
|
||||
|
||||
// TelevisionTIA exposes only the functions required by the TIA.
|
||||
type TelevisionTIA interface {
|
||||
Signal(SignalAttributes) error
|
||||
GetState(StateReq) int
|
||||
GetCoords() coords.TelevisionCoords
|
||||
}
|
||||
|
||||
// TelevisionSprite exposes only the functions required by the video sprites.
|
||||
type TelevisionSprite interface {
|
||||
GetState(StateReq) int
|
||||
GetCoords() coords.TelevisionCoords
|
||||
}
|
||||
|
|
|
@ -98,19 +98,6 @@ func (s *State) Snapshot() *State {
|
|||
return &n
|
||||
}
|
||||
|
||||
// GetState Returns information about the current state of the signal.
|
||||
func (s *State) GetState(request signal.StateReq) int {
|
||||
switch request {
|
||||
case signal.ReqFramenum:
|
||||
return s.frameNum
|
||||
case signal.ReqScanline:
|
||||
return s.scanline
|
||||
case signal.ReqClock:
|
||||
return s.clock - specification.ClksHBlank
|
||||
}
|
||||
panic(fmt.Sprintf("television: unhandled tv state request (%v)", request))
|
||||
}
|
||||
|
||||
// GetCoords returns an instance of coords.TelevisionCoords.
|
||||
func (s *State) GetCoords() coords.TelevisionCoords {
|
||||
return coords.TelevisionCoords{
|
||||
|
@ -177,7 +164,7 @@ func NewTelevision(spec string) (*Television, error) {
|
|||
tv := &Television{
|
||||
reqSpecID: strings.ToUpper(spec),
|
||||
state: &State{},
|
||||
signals: make([]signal.SignalAttributes, MaxSignalHistory),
|
||||
signals: make([]signal.SignalAttributes, specification.AbsoluteMaxClks),
|
||||
}
|
||||
|
||||
// initialise frame rate limiter
|
||||
|
@ -416,7 +403,7 @@ func (tv *Television) Signal(sig signal.SignalAttributes) error {
|
|||
tv.state.lastSignal = sig
|
||||
|
||||
// record signal history
|
||||
if tv.currentSignalIdx >= MaxSignalHistory {
|
||||
if tv.currentSignalIdx >= len(tv.signals) {
|
||||
return tv.renderSignals()
|
||||
}
|
||||
|
||||
|
@ -681,8 +668,7 @@ func (tv *Television) GetReqSpecID() string {
|
|||
return tv.reqSpecID
|
||||
}
|
||||
|
||||
// GetFrameInfo returns the television's current frame information. FPS and
|
||||
// RefreshRate is returned by GetReqFPS().
|
||||
// GetFrameInfo returns the television's current frame information.
|
||||
func (tv *Television) GetFrameInfo() FrameInfo {
|
||||
return tv.state.frameInfo
|
||||
}
|
||||
|
@ -693,92 +679,7 @@ func (tv *Television) GetLastSignal() signal.SignalAttributes {
|
|||
return tv.state.lastSignal
|
||||
}
|
||||
|
||||
// GetState returns state information for the TV.
|
||||
func (tv *Television) GetState(request signal.StateReq) int {
|
||||
return tv.state.GetState(request)
|
||||
}
|
||||
|
||||
// GetCoords returns an instance of coords.TelevisionCoords.
|
||||
func (tv *Television) GetCoords() coords.TelevisionCoords {
|
||||
return tv.state.GetCoords()
|
||||
}
|
||||
|
||||
// GetAdjustedCoords returns a coords.TelevisionCoords with the current coords
|
||||
// adjusted by the specified value
|
||||
//
|
||||
// The reset argument instructs the function to return values that have been
|
||||
// reset to zero as appropriate. So when request is ReqFramenum, the scanline
|
||||
// and clock values will be zero; when request is ReqScanline, the clock value
|
||||
// will be zero. It has no affect when request is ReqClock.
|
||||
//
|
||||
// In the case of a StateAdj of AdjCPUCycle the only allowed adjustment value
|
||||
// is -1. Any other value will return an error.
|
||||
func (tv *Television) GetAdjustedCoords(request signal.StateAdj, adjustment int, reset bool) (coords.TelevisionCoords, error) {
|
||||
coords := tv.GetCoords()
|
||||
|
||||
var err error
|
||||
|
||||
switch request {
|
||||
case signal.AdjCPUCycle:
|
||||
// adjusting by CPU cycle is the same as adjusting by video cycle
|
||||
// accept to say that a CPU cycle is the equivalent of 3 video cycles
|
||||
adjustment *= 3
|
||||
fallthrough
|
||||
case signal.AdjClock:
|
||||
coords.Clock += adjustment
|
||||
if coords.Clock >= specification.ClksScanline {
|
||||
coords.Clock -= specification.ClksScanline
|
||||
coords.Scanline++
|
||||
} else if coords.Clock < 0 {
|
||||
coords.Clock += specification.ClksScanline
|
||||
coords.Scanline--
|
||||
}
|
||||
if coords.Scanline > tv.state.frameInfo.TotalScanlines {
|
||||
coords.Scanline -= tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame++
|
||||
} else if coords.Scanline < 0 {
|
||||
coords.Scanline += tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame--
|
||||
}
|
||||
case signal.AdjInstruction:
|
||||
if adjustment != -1 {
|
||||
err = curated.Errorf("television: can only adjust CPU boundary by -1")
|
||||
} else {
|
||||
coords.Clock = tv.state.lastCPUInstruction.Clock
|
||||
coords.Scanline = tv.state.lastCPUInstruction.Scanline
|
||||
coords.Frame = tv.state.lastCPUInstruction.Frame
|
||||
}
|
||||
case signal.AdjScanline:
|
||||
if reset {
|
||||
coords.Clock = 0
|
||||
}
|
||||
coords.Scanline += adjustment
|
||||
if coords.Scanline > tv.state.frameInfo.TotalScanlines {
|
||||
coords.Scanline -= tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame++
|
||||
} else if coords.Scanline < 0 {
|
||||
coords.Scanline += tv.state.frameInfo.TotalScanlines
|
||||
coords.Frame--
|
||||
}
|
||||
case signal.AdjFramenum:
|
||||
if reset {
|
||||
coords.Clock = 0
|
||||
coords.Scanline = 0
|
||||
}
|
||||
coords.Frame += adjustment
|
||||
}
|
||||
|
||||
// zero values if frame is less than zero
|
||||
if coords.Frame < 0 {
|
||||
coords.Frame = 0
|
||||
coords.Scanline = 0
|
||||
coords.Clock = 0
|
||||
}
|
||||
|
||||
return coords, err
|
||||
}
|
||||
|
||||
// InstructionBoundary implements the cpu.BoundaryTrigger interface.
|
||||
func (tv *Television) InstructionBoundary() {
|
||||
tv.state.lastCPUInstruction = tv.state.GetCoords()
|
||||
}
|
||||
|
|
|
@ -308,7 +308,7 @@ func (tia *TIA) resolveDelayedEvents() {
|
|||
// adjust video elements by the number of visible pixels that have
|
||||
// been consumed. adding one to the value because the tv pixel we
|
||||
// want to hit has not been reached just yet
|
||||
adj := tia.tv.GetState(signal.ReqClock) + 1
|
||||
adj := tia.tv.GetCoords().Clock + 1
|
||||
if adj > 0 {
|
||||
tia.Video.RSYNC(adj)
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia/delay"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia/phaseclock"
|
||||
|
@ -327,7 +326,7 @@ func (bs *BallSprite) _futureResetPosition() {
|
|||
|
||||
// the pixel at which the sprite has been reset, in relation to the
|
||||
// left edge of the screen
|
||||
bs.ResetPixel = bs.tia.tv.GetState(signal.ReqClock)
|
||||
bs.ResetPixel = bs.tia.tv.GetCoords().Clock
|
||||
|
||||
if bs.ResetPixel >= 0 {
|
||||
// resetPixel adjusted by 1 because the tv is not yet in the correct
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia/delay"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia/phaseclock"
|
||||
|
@ -262,7 +261,7 @@ func (ms *MissileSprite) tick(resetToPlayer bool) bool {
|
|||
ms.pclk = phaseclock.ResetValue
|
||||
|
||||
// missile-to-player also resets position information
|
||||
ms.ResetPixel = ms.tia.tv.GetState(signal.ReqClock)
|
||||
ms.ResetPixel = ms.tia.tv.GetCoords().Clock
|
||||
ms.HmovedPixel = ms.ResetPixel
|
||||
}
|
||||
|
||||
|
@ -391,7 +390,7 @@ func (ms *MissileSprite) resetPosition() {
|
|||
func (ms *MissileSprite) _futureResetPosition() {
|
||||
// the pixel at which the sprite has been reset, in relation to the
|
||||
// left edge of the screen
|
||||
ms.ResetPixel = ms.tia.tv.GetState(signal.ReqClock)
|
||||
ms.ResetPixel = ms.tia.tv.GetCoords().Clock
|
||||
|
||||
if ms.ResetPixel >= 0 {
|
||||
// resetPixel adjusted by 1 because the tv is not yet in the correct
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia/delay"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia/phaseclock"
|
||||
|
@ -461,7 +460,7 @@ func (ps *PlayerSprite) resetPosition() {
|
|||
// for us.
|
||||
if (*ps.tia.hsync == 16 || *ps.tia.hsync == 18) && *ps.tia.pclk == phaseclock.RisingPhi2 {
|
||||
if ps.tia.rev.Prefs.RESPxHBLANK {
|
||||
hblank = !revision.HeatThreshold(ps.tia.tv.GetState(signal.ReqScanline))
|
||||
hblank = !revision.HeatThreshold(ps.tia.tv.GetCoords().Scanline)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -546,7 +545,7 @@ func (ps *PlayerSprite) resetPosition() {
|
|||
func (ps *PlayerSprite) _futureResetPosition() {
|
||||
// the pixel at which the sprite has been reset, in relation to the
|
||||
// left edge of the screen
|
||||
ps.ResetPixel = ps.tia.tv.GetState(signal.ReqClock)
|
||||
ps.ResetPixel = ps.tia.tv.GetCoords().Clock
|
||||
|
||||
if ps.ResetPixel >= 0 {
|
||||
// resetPixel adjusted by +1 because the tv is not yet in the correct.
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/gui"
|
||||
"github.com/jetsetilly/gopher2600/hardware"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/setup"
|
||||
"github.com/jetsetilly/gopher2600/userinput"
|
||||
)
|
||||
|
@ -72,7 +71,7 @@ func Check(output io.Writer, profile Profile, includeDetail bool,
|
|||
}
|
||||
|
||||
// get starting frame number (should be 0)
|
||||
startFrame := tv.GetState(signal.ReqFramenum)
|
||||
startFrame := tv.GetCoords().Frame
|
||||
|
||||
// run for specified period of time
|
||||
runner := func() error {
|
||||
|
@ -91,7 +90,7 @@ func Check(output io.Writer, profile Profile, includeDetail bool,
|
|||
// signal parent function that 2 second leadtime has elapsed
|
||||
timerChan <- false
|
||||
|
||||
// race condition when GetState() is called
|
||||
// race condition when GetCoords() is called
|
||||
time.AfterFunc(dur, func() {
|
||||
timerChan <- true
|
||||
})
|
||||
|
@ -114,7 +113,7 @@ func Check(output io.Writer, profile Profile, includeDetail bool,
|
|||
// leadtime has concluded. this means the performance
|
||||
// measurement has begun and we should record the start
|
||||
// frame.
|
||||
startFrame = tv.GetState(signal.ReqFramenum)
|
||||
startFrame = tv.GetCoords().Frame
|
||||
default:
|
||||
return emulation.Running, nil
|
||||
}
|
||||
|
@ -136,7 +135,7 @@ func Check(output io.Writer, profile Profile, includeDetail bool,
|
|||
}
|
||||
|
||||
// get ending frame number
|
||||
endFrame := vcs.TV.GetState(signal.ReqFramenum)
|
||||
endFrame := vcs.TV.GetCoords().Frame
|
||||
|
||||
// calculate performance
|
||||
numFrames := endFrame - startFrame
|
||||
|
|
|
@ -29,7 +29,6 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware/riot/ports"
|
||||
"github.com/jetsetilly/gopher2600/hardware/riot/ports/plugging"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/coords"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
)
|
||||
|
||||
type playbackEntry struct {
|
||||
|
@ -62,14 +61,14 @@ type Playback struct {
|
|||
}
|
||||
|
||||
func (plb Playback) String() string {
|
||||
currFrame := plb.digest.GetState(signal.ReqFramenum)
|
||||
currFrame := plb.digest.GetCoords().Frame
|
||||
return fmt.Sprintf("%d/%d (%.1f%%)", currFrame, plb.endFrame, 100*(float64(currFrame)/float64(plb.endFrame)))
|
||||
}
|
||||
|
||||
// EndFrame returns true if emulation has gone past the last frame of the
|
||||
// playback.
|
||||
func (plb Playback) EndFrame() (bool, error) {
|
||||
currFrame := plb.digest.GetState(signal.ReqFramenum)
|
||||
currFrame := plb.digest.GetCoords().Frame
|
||||
if currFrame > plb.endFrame {
|
||||
return true, nil
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/hardware/memory/cartridge/mapper"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
"github.com/jetsetilly/gopher2600/logger"
|
||||
"github.com/jetsetilly/gopher2600/rewind"
|
||||
)
|
||||
|
@ -50,7 +51,7 @@ type Gatherer struct {
|
|||
func NewGatherer(vcs *hardware.VCS) *Gatherer {
|
||||
return &Gatherer{
|
||||
vcs: vcs,
|
||||
history: make([]ReflectedVideoStep, television.MaxSignalHistory),
|
||||
history: make([]ReflectedVideoStep, specification.AbsoluteMaxClks),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ import (
|
|||
"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/signal"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/specification"
|
||||
"github.com/jetsetilly/gopher2600/hardware/tia"
|
||||
"github.com/jetsetilly/gopher2600/logger"
|
||||
|
@ -94,7 +93,7 @@ func (s State) String() string {
|
|||
case levelAdhoc:
|
||||
return "c"
|
||||
}
|
||||
return fmt.Sprintf("%d", s.TV.GetState(signal.ReqFramenum))
|
||||
return fmt.Sprintf("%d", s.TV.GetCoords().Frame)
|
||||
}
|
||||
|
||||
// an overhead of two is required. (1) to accommodate the end index required for
|
||||
|
@ -321,7 +320,7 @@ func (r *Rewind) RecordFrameState() {
|
|||
if r.boundaryNextFrame {
|
||||
r.boundaryNextFrame = false
|
||||
r.reset(levelBoundary)
|
||||
logger.Logf("rewind", "boundary added at frame %d", r.vcs.TV.GetState(signal.ReqFramenum))
|
||||
logger.Logf("rewind", "boundary added at frame %d", r.vcs.TV.GetCoords().Frame)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -395,7 +394,7 @@ func (r *Rewind) append(s *State) {
|
|||
}
|
||||
|
||||
// splice timeline at current frame number
|
||||
r.timeline.splice(r.vcs.TV.GetState(signal.ReqFramenum))
|
||||
r.timeline.splice(r.vcs.TV.GetCoords().Frame)
|
||||
}
|
||||
|
||||
// setContinuePoint sets the splice point to the supplied index. the emulation
|
||||
|
@ -558,13 +557,13 @@ func (r *Rewind) findFrameIndex(frame int) (idx int, fr int, last bool) {
|
|||
// then plumb in the nearest entry
|
||||
|
||||
// is requested frame too old (ie. before the start of the array)
|
||||
fn := r.entries[s].TV.GetState(signal.ReqFramenum)
|
||||
fn := r.entries[s].TV.GetCoords().Frame
|
||||
if sf < fn {
|
||||
return s, fn + 1, false
|
||||
}
|
||||
|
||||
// is requested frame too new (ie. past the end of the array)
|
||||
fn = r.entries[e].TV.GetState(signal.ReqFramenum)
|
||||
fn = r.entries[e].TV.GetCoords().Frame
|
||||
if sf >= fn {
|
||||
e--
|
||||
if e < 0 {
|
||||
|
@ -580,7 +579,7 @@ func (r *Rewind) findFrameIndex(frame int) (idx int, fr int, last bool) {
|
|||
// binary search. if start (lower) is greater then end (upper) then check
|
||||
// which half of the circular array to concentrate on.
|
||||
if r.start > e {
|
||||
fn := r.entries[len(r.entries)-1].TV.GetState(signal.ReqFramenum)
|
||||
fn := r.entries[len(r.entries)-1].TV.GetCoords().Frame
|
||||
if sf <= fn {
|
||||
e = len(r.entries) - 1
|
||||
} else {
|
||||
|
@ -596,7 +595,7 @@ func (r *Rewind) findFrameIndex(frame int) (idx int, fr int, last bool) {
|
|||
for s <= e {
|
||||
idx := (s + e) / 2
|
||||
|
||||
fn := r.entries[idx].TV.GetState(signal.ReqFramenum)
|
||||
fn := r.entries[idx].TV.GetCoords().Frame
|
||||
|
||||
// check for match, taking into consideration the gaps introduced by
|
||||
// the frequency value
|
||||
|
@ -625,7 +624,7 @@ type PokeHook func(res *State) error
|
|||
|
||||
// RunFromState will the run the VCS from one state to another state.
|
||||
func (r *Rewind) RunFromState(from *State, to *State, poke PokeHook) error {
|
||||
ff := from.TV.GetState(signal.ReqFramenum)
|
||||
ff := from.TV.GetCoords().Frame
|
||||
idx, _, _ := r.findFrameIndex(ff)
|
||||
|
||||
if poke != nil {
|
||||
|
@ -647,7 +646,7 @@ func (r *Rewind) RunFromState(from *State, to *State, poke PokeHook) error {
|
|||
// the current state.
|
||||
func (r *Rewind) RerunLastNFrames(frames int) error {
|
||||
to := r.GetCurrentState()
|
||||
ff := to.TV.GetState(signal.ReqFramenum) - frames
|
||||
ff := to.TV.GetCoords().Frame
|
||||
if ff < 0 {
|
||||
ff = 0
|
||||
}
|
||||
|
@ -669,8 +668,8 @@ func (r *Rewind) GotoCoords(coords coords.TelevisionCoords) error {
|
|||
|
||||
// if found index does not point to an immediately suitable state then try
|
||||
// the adhocFrame state if available
|
||||
if coords.Frame != r.entries[idx].TV.GetState(signal.ReqFramenum)+1 {
|
||||
if r.adhocFrame != nil && r.adhocFrame.TV.GetState(signal.ReqFramenum) == coords.Frame-1 {
|
||||
if coords.Frame != r.entries[idx].TV.GetCoords().Frame+1 {
|
||||
if r.adhocFrame != nil && r.adhocFrame.TV.GetCoords().Frame == coords.Frame-1 {
|
||||
return r.plumbState(r.adhocFrame, coords)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ import (
|
|||
"github.com/jetsetilly/gopher2600/curated"
|
||||
"github.com/jetsetilly/gopher2600/emulation"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television"
|
||||
"github.com/jetsetilly/gopher2600/hardware/television/signal"
|
||||
)
|
||||
|
||||
// TimelineCounts is returned by a TimelineCounter implementation. The value
|
||||
|
@ -155,13 +154,13 @@ func (r *Rewind) GetTimeline() Timeline {
|
|||
//
|
||||
// this has a consequence when the first time the circular array wraps
|
||||
// around for the first time (the number of available entries drops by one)
|
||||
sf := r.entries[r.start].TV.GetState(signal.ReqFramenum)
|
||||
sf := r.entries[r.start].TV.GetCoords().Frame
|
||||
if r.entries[r.start].level != levelReset {
|
||||
sf++
|
||||
}
|
||||
|
||||
r.timeline.AvailableStart = sf
|
||||
r.timeline.AvailableEnd = r.entries[e].TV.GetState(signal.ReqFramenum)
|
||||
r.timeline.AvailableEnd = r.entries[e].TV.GetCoords().Frame
|
||||
|
||||
return r.timeline
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue