-
Notifications
You must be signed in to change notification settings - Fork 19
/
run.go
127 lines (105 loc) · 3.37 KB
/
run.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
// 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 (
"fmt"
"github.com/jetsetilly/gopher2600/debugger/govern"
)
// While the continueCheck() function only runs at the end of a CPU instruction
// (unlike the corresponding function in VCS.Step() which runs every color clock),
// it can still be expensive to do a full continue check every time.
//
// It depends on context whether it is used or not but the PerformanceBrake is
// a standard value that can be used to filter out expensive code paths within
// a continueCheck() implementation. For example:
//
// performanceFilter++
// if performanceFilter >= hardware.PerfomrmanceBrake {
// performanceFilter = 0
// if end_condition == true {
// return govern.Ending, nill
// }
// }
// return govern.Running, nill
const PerformanceBrake = 100
// Run sets the emulation running as quickly as possible
func (vcs *VCS) Run(continueCheck func() (govern.State, error)) error {
if continueCheck == nil {
continueCheck = func() (govern.State, error) { return govern.Running, nil }
}
// see the equivalient colorClock() in the VCS.Step() function for an
// explanation for what's going on here:
colorClock := func() error {
if err := vcs.Input.Handle(); err != nil {
return err
}
vcs.TIA.QuickStep(1)
vcs.TIA.QuickStep(2)
if reg, ok := vcs.Mem.TIA.ChipHasChanged(); ok {
vcs.TIA.Step(reg, 3)
} else {
vcs.TIA.QuickStep(3)
}
if reg, ok := vcs.Mem.RIOT.ChipHasChanged(); ok {
vcs.RIOT.Step(reg)
} else {
vcs.RIOT.QuickStep()
}
vcs.Mem.Cart.Step(vcs.Clock)
return nil
}
var err error
state := govern.Running
for state != govern.Ending && state != govern.Initialising {
switch state {
case govern.Running:
err := vcs.CPU.ExecuteInstruction(colorClock)
if err != nil {
return err
}
case govern.Paused:
default:
return fmt.Errorf("vcs: unsupported emulation state (%d) in Run() function", state)
}
state, err = continueCheck()
if err != nil {
return err
}
}
return nil
}
// RunForFrameCount sets emulator running for the specified number of frames.
// Useful for FPS and regression tests. Not used by the debugger because traps
// (and volatile traps) are more flexible.
func (vcs *VCS) RunForFrameCount(numFrames int, continueCheck func(frame int) (govern.State, error)) error {
if continueCheck == nil {
continueCheck = func(frame int) (govern.State, error) { return govern.Running, nil }
}
frameNum := vcs.TV.GetCoords().Frame
targetFrame := frameNum + numFrames
state := govern.Running
for frameNum != targetFrame && state != govern.Ending {
err := vcs.Step(nil)
if err != nil {
return err
}
frameNum = vcs.TV.GetCoords().Frame
state, err = continueCheck(frameNum)
if err != nil {
return err
}
}
return nil
}