1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18// #define TIA_FRAMEMANAGER_DEBUG_LOG
19
20#include <algorithm>
21
22#include "FrameManager.hxx"
23
24enum Metrics: uInt32 {
25 vblankNTSC = 37,
26 vblankPAL = 45,
27 kernelNTSC = 192,
28 kernelPAL = 228,
29 overscanNTSC = 30,
30 overscanPAL = 36,
31 vsync = 3,
32 maxLinesVsync = 50,
33 visibleOverscan = 20,
34 initialGarbageFrames = TIAConstants::initialGarbageFrames
35};
36
37// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
38FrameManager::FrameManager()
39 : myState(State::waitForVsyncStart),
40 myLineInState(0),
41 myVsyncLines(0),
42 myY(0), myLastY(0),
43 myVblankLines(0),
44 myKernelLines(0),
45 myOverscanLines(0),
46 myFrameLines(0),
47 myHeight(0),
48 myYStart(0),
49 myJitterEnabled(false)
50{
51 reset();
52 onLayoutChange();
53}
54
55// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
56void FrameManager::onReset()
57{
58 myState = State::waitForVsyncStart;
59 myLineInState = 0;
60 myTotalFrames = 0;
61 myVsyncLines = 0;
62 myY = 0;
63
64 myJitterEmulation.reset();
65}
66
67// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
68void FrameManager::onNextLine()
69{
70 Int32 jitter;
71
72 State previousState = myState;
73 ++myLineInState;
74
75 switch (myState)
76 {
77 case State::waitForVsyncStart:
78 if ((myCurrentFrameTotalLines > myFrameLines - 3) || myTotalFrames == 0)
79 ++myVsyncLines;
80
81 if (myVsyncLines > Metrics::maxLinesVsync) setState(State::waitForFrameStart);
82
83 break;
84
85 case State::waitForVsyncEnd:
86 if (++myVsyncLines > Metrics::maxLinesVsync)
87 setState(State::waitForFrameStart);
88
89 break;
90
91 case State::waitForFrameStart:
92 jitter =
93 (myJitterEnabled && myTotalFrames > Metrics::initialGarbageFrames) ? myJitterEmulation.jitter() : 0;
94
95 if (myLineInState >= (myYStart + jitter)) setState(State::frame);
96 break;
97
98 case State::frame:
99 if (myLineInState >= myHeight)
100 {
101 myLastY = ystart() + myY; // Last line drawn in this frame
102 setState(State::waitForVsyncStart);
103 }
104 break;
105
106 default:
107 throw runtime_error("frame manager: invalid state");
108 }
109
110 if (myState == State::frame && previousState == State::frame) ++myY;
111}
112
113// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
114Int32 FrameManager::missingScanlines() const
115{
116 if (myLastY == myYStart + myY)
117 return 0;
118 else {
119 return myHeight - myY;
120 }
121}
122
123// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
124void FrameManager::setYstart(uInt32 ystart)
125{
126 myYStart = ystart;
127 myJitterEmulation.setYStart(ystart);
128}
129
130// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
131void FrameManager::onSetVsync()
132{
133 if (myState == State::waitForVsyncEnd) setState(State::waitForFrameStart);
134 else setState(State::waitForVsyncEnd);
135}
136
137// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
138void FrameManager::setState(FrameManager::State state)
139{
140 if (myState == state) return;
141
142 myState = state;
143 myLineInState = 0;
144
145 switch (myState) {
146 case State::waitForFrameStart:
147 notifyFrameComplete();
148
149 if (myTotalFrames > Metrics::initialGarbageFrames)
150 myJitterEmulation.frameComplete(myCurrentFrameFinalLines);
151
152 notifyFrameStart();
153
154 myVsyncLines = 0;
155 break;
156
157 case State::frame:
158 myVsyncLines = 0;
159 myY = 0;
160 break;
161
162 default:
163 break;
164 }
165
166 updateIsRendering();
167}
168
169// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
170void FrameManager::onLayoutChange()
171{
172 switch (layout())
173 {
174 case FrameLayout::ntsc:
175 myVblankLines = Metrics::vblankNTSC;
176 myKernelLines = Metrics::kernelNTSC;
177 myOverscanLines = Metrics::overscanNTSC;
178 break;
179
180 case FrameLayout::pal:
181 myVblankLines = Metrics::vblankPAL;
182 myKernelLines = Metrics::kernelPAL;
183 myOverscanLines = Metrics::overscanPAL;
184 break;
185
186 default:
187 throw runtime_error("frame manager: invalid TV mode");
188 }
189
190 myFrameLines = Metrics::vsync + myVblankLines + myKernelLines + myOverscanLines;
191 myHeight = myKernelLines + Metrics::visibleOverscan;
192}
193
194// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
195void FrameManager::updateIsRendering() {
196 myIsRendering = myState == State::frame;
197}
198
199// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
200bool FrameManager::onSave(Serializer& out) const
201{
202 if (!myJitterEmulation.save(out)) return false;
203
204 out.putInt(uInt32(myState));
205 out.putInt(myLineInState);
206 out.putInt(myVsyncLines);
207 out.putInt(myY);
208 out.putInt(myLastY);
209
210 out.putInt(myVblankLines);
211 out.putInt(myKernelLines);
212 out.putInt(myOverscanLines);
213 out.putInt(myFrameLines);
214 out.putInt(myHeight);
215 out.putInt(myYStart);
216
217 out.putBool(myJitterEnabled);
218
219 return true;
220}
221
222// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
223bool FrameManager::onLoad(Serializer& in)
224{
225 if (!myJitterEmulation.load(in)) return false;
226
227 myState = State(in.getInt());
228 myLineInState = in.getInt();
229 myVsyncLines = in.getInt();
230 myY = in.getInt();
231 myLastY = in.getInt();
232
233 myVblankLines = in.getInt();
234 myKernelLines = in.getInt();
235 myOverscanLines = in.getInt();
236 myFrameLines = in.getInt();
237 myHeight = in.getInt();
238 myYStart = in.getInt();
239
240 myJitterEnabled = in.getBool();
241
242 return true;
243}
244