Skip to content

Commit

Permalink
user input recorded for rewind system
Browse files Browse the repository at this point in the history
input is re-inserted as appropriate on playback
  • Loading branch information
JetSetIlly committed Dec 14, 2024
1 parent ca642b8 commit b8eea8a
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 133 deletions.
4 changes: 4 additions & 0 deletions debugger/debugger.go
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,9 @@ func NewDebugger(opts CommandLineOptions, create CreateUserInterface) (*Debugger
return nil, fmt.Errorf("debugger: %w", err)
}

// attach rewind system as an event recorder
dbg.vcs.Input.AddRecorder(dbg.Rewind)

// add reflection system to the GUI
dbg.ref = reflection.NewReflector(dbg.vcs)
if r, ok := dbg.gui.(reflection.Broker); ok {
Expand Down Expand Up @@ -576,6 +579,7 @@ func (dbg *Debugger) setState(state govern.State, subState govern.SubState) {
dbg.ref.SetEmulationState(state)
}
dbg.CoProcDev.SetEmulationState(state)
dbg.Rewind.SetEmulationState(state)

dbg.state.Store(state)
dbg.subState.Store(subState)
Expand Down
28 changes: 15 additions & 13 deletions hardware/input/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -81,24 +81,26 @@ func (inp *Input) PeripheralID(id plugging.PortID) plugging.PeripheralID {
// If a playback is currently active the input will not be handled and false
// will be returned.
func (inp *Input) HandleInputEvent(ev ports.InputEvent) (bool, error) {
for _, r := range inp.recorder {
err := r.RecordEvent(ports.TimedInputEvent{Time: inp.tv.GetCoords(), InputEvent: ev})
if err != nil {
return false, err
}
}

handled, err := inp.ports.HandleInputEvent(ev)
if err != nil {
return handled, err
}

// forward to passenger if one is defined
if handled && inp.toPassenger != nil {
select {
case inp.toPassenger <- ports.TimedInputEvent{Time: inp.tv.GetCoords(), InputEvent: ev}:
default:
return handled, fmt.Errorf("input: passenger event queue is full: input dropped")
if handled {
for _, r := range inp.recorder {
err := r.RecordEvent(ports.TimedInputEvent{Time: inp.tv.GetCoords(), InputEvent: ev})
if err != nil {
return false, err
}
}

// forward to passenger if one is defined
if handled && inp.toPassenger != nil {
select {
case inp.toPassenger <- ports.TimedInputEvent{Time: inp.tv.GetCoords(), InputEvent: ev}:
default:
return handled, fmt.Errorf("input: passenger event queue is full: input dropped")
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions hardware/input/recording.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ func (inp *Input) handlePlaybackEvents() error {
return nil
}

// loop with GetPlayback() until we encounter a NoPortID or NoEvent
// loop with GetPlayback() until we encounter an PortUnplugged or NoEvent
// condition. there might be more than one entry for a particular
// frame/scanline/horizpas state so we need to make sure we've processed
// them all.
Expand All @@ -88,10 +88,13 @@ func (inp *Input) handlePlaybackEvents() error {

morePlayback = ev.Port != plugging.PortUnplugged && ev.Ev != ports.NoEvent
if morePlayback {
_, err := inp.ports.HandleInputEvent(ev.InputEvent)
handled, err := inp.ports.HandleInputEvent(ev.InputEvent)
if err != nil {
return err
}
if !handled {
continue
}

// forward to passenger if necessary
if inp.toPassenger != nil {
Expand Down
8 changes: 8 additions & 0 deletions hardware/riot/ports/events.go
Original file line number Diff line number Diff line change
Expand Up @@ -170,9 +170,17 @@ type InputEvent struct {
D EventData
}

func (ev InputEvent) String() string {
return fmt.Sprintf("%s=%v on %s", ev.Ev, ev.D, ev.Port)
}

// TimedInputEvent embeds the InputEvent type and adds a Time field (time
// measured by TelevisionCoords).
type TimedInputEvent struct {
Time coords.TelevisionCoords
InputEvent
}

func (ev TimedInputEvent) String() string {
return fmt.Sprintf("%s @ %s", ev.InputEvent, ev.Time)
}
53 changes: 32 additions & 21 deletions hardware/television/coords/coords.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,36 +56,36 @@ func (c TelevisionCoords) String() string {
//
// If the Frame field is undefined for either argument then the Frame field is
// ignored for the test.
func Equal(A, B TelevisionCoords) bool {
if A.Frame == FrameIsUndefined || B.Frame == FrameIsUndefined {
return A.Scanline == B.Scanline && A.Clock == B.Clock
func Equal(a, b TelevisionCoords) bool {
if a.Frame == FrameIsUndefined || b.Frame == FrameIsUndefined {
return a.Scanline == b.Scanline && a.Clock == b.Clock
}
return A.Frame == B.Frame && A.Scanline == B.Scanline && A.Clock == B.Clock
return a.Frame == b.Frame && a.Scanline == b.Scanline && a.Clock == b.Clock
}

// GreaterThanOrEqual compares two instances of TelevisionCoords and return
// true if A is greater than or equal to B.
//
// If the Frame field is undefined for either argument then the Frame field is
// ignored for the test.
func GreaterThanOrEqual(A, B TelevisionCoords) bool {
if A.Frame == FrameIsUndefined || B.Frame == FrameIsUndefined {
return A.Scanline > B.Scanline || (A.Scanline == B.Scanline && A.Clock >= B.Clock)
func GreaterThanOrEqual(a, b TelevisionCoords) bool {
if a.Frame == FrameIsUndefined || b.Frame == FrameIsUndefined {
return a.Scanline > b.Scanline || (a.Scanline == b.Scanline && a.Clock >= b.Clock)
}

return A.Frame > B.Frame || (A.Frame == B.Frame && A.Scanline > B.Scanline) || (A.Frame == B.Frame && A.Scanline == B.Scanline && A.Clock >= B.Clock)
return a.Frame > b.Frame || (a.Frame == b.Frame && a.Scanline > b.Scanline) || (a.Frame == b.Frame && a.Scanline == b.Scanline && a.Clock >= b.Clock)
}

// GreaterThan compares two instances of TelevisionCoords and return true if A
// is greater than to B.
//
// If the Frame field is undefined for either argument then the Frame field is
// ignored for the test.
func GreaterThan(A, B TelevisionCoords) bool {
if A.Frame == FrameIsUndefined || B.Frame == FrameIsUndefined {
return A.Scanline > B.Scanline || (A.Scanline == B.Scanline && A.Clock > B.Clock)
func GreaterThan(a, b TelevisionCoords) bool {
if a.Frame == FrameIsUndefined || b.Frame == FrameIsUndefined {
return a.Scanline > b.Scanline || (a.Scanline == b.Scanline && a.Clock > b.Clock)
}
return A.Frame > B.Frame || (A.Frame == B.Frame && A.Scanline > B.Scanline) || (A.Frame == B.Frame && A.Scanline == B.Scanline && A.Clock > B.Clock)
return a.Frame > b.Frame || (a.Frame == b.Frame && a.Scanline > b.Scanline) || (a.Frame == b.Frame && a.Scanline == b.Scanline && a.Clock > b.Clock)
}

// Diff returns the difference between the B and A instances. The
Expand All @@ -95,11 +95,11 @@ func GreaterThan(A, B TelevisionCoords) bool {
//
// If the Frame field is undefined for either TelevisionCoords argument then the
// Frame field in the result of the function is also undefined.
func Diff(A, B TelevisionCoords, scanlinesPerFrame int) TelevisionCoords {
func Diff(a, b TelevisionCoords, scanlinesPerFrame int) TelevisionCoords {
D := TelevisionCoords{
Frame: A.Frame - B.Frame,
Scanline: A.Scanline - B.Scanline,
Clock: A.Clock - B.Clock,
Frame: a.Frame - b.Frame,
Scanline: a.Scanline - b.Scanline,
Clock: a.Clock - b.Clock,
}

if D.Clock < specification.ClksHBlank {
Expand All @@ -120,7 +120,7 @@ func Diff(A, B TelevisionCoords, scanlinesPerFrame int) TelevisionCoords {

// if the Frame field in either A or B is undefined then we can set the diff
// Frame field as undefined alse
if A.Frame == FrameIsUndefined || B.Frame == FrameIsUndefined {
if a.Frame == FrameIsUndefined || b.Frame == FrameIsUndefined {
D.Frame = FrameIsUndefined
}

Expand All @@ -131,11 +131,22 @@ func Diff(A, B TelevisionCoords, scanlinesPerFrame int) TelevisionCoords {
//
// If the Frame field is undefined for the TelevisionCoords then the Frame field
// in the result of the function is also undefined.
func Sum(A TelevisionCoords, scanlinesPerFrame int) int {
if A.Frame == FrameIsUndefined {
return (A.Scanline * specification.ClksScanline) + A.Clock
func Sum(a TelevisionCoords, scanlinesPerFrame int) int {
if a.Frame == FrameIsUndefined {
return (a.Scanline * specification.ClksScanline) + a.Clock
}

numPerFrame := scanlinesPerFrame * specification.ClksScanline
return (A.Frame * numPerFrame) + (A.Scanline * specification.ClksScanline) + A.Clock
return (a.Frame * numPerFrame) + (a.Scanline * specification.ClksScanline) + a.Clock
}

// Cmp returns 0 if A and B are equal, 1 if A > B and -1 if A < B
func Cmp(a, b TelevisionCoords) int {
if Equal(a, b) {
return 0
}
if GreaterThan(a, b) {
return 1
}
return -1
}
52 changes: 52 additions & 0 deletions rewind/comparison.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
// 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 rewind

// ComparisonState is returned by GetComparisonState()
type ComparisonState struct {
State *State
Locked bool
}

// GetComparisonState gets a reference to current comparison point
func (r *Rewind) GetComparisonState() ComparisonState {
return ComparisonState{
State: r.comparison.snapshot(),
Locked: r.comparisonLocked,
}
}

// UpdateComparison points comparison to the current state
func (r *Rewind) UpdateComparison() {
if r.comparisonLocked {
return
}
r.comparison = r.GetCurrentState()
}

// SetComparison points comparison to the supplied state
func (r *Rewind) SetComparison(frame int) {
res := r.findFrameIndexExact(frame)
s := r.entries[res.nearestIdx]
if s != nil {
r.comparison = s.snapshot()
}
}

// LockComparison stops the comparison point from being updated
func (r *Rewind) LockComparison(locked bool) {
r.comparisonLocked = locked
}
Loading

0 comments on commit b8eea8a

Please sign in to comment.