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 | #ifndef TIA_TIA |
19 | #define TIA_TIA |
20 | |
21 | #include <functional> |
22 | |
23 | #include "bspf.hxx" |
24 | #include "ConsoleIO.hxx" |
25 | #include "ConsoleTiming.hxx" |
26 | #include "Settings.hxx" |
27 | #include "Device.hxx" |
28 | #include "Serializer.hxx" |
29 | #include "TIAConstants.hxx" |
30 | #include "DelayQueue.hxx" |
31 | #include "DelayQueueIterator.hxx" |
32 | #include "frame-manager/AbstractFrameManager.hxx" |
33 | #include "FrameLayout.hxx" |
34 | #include "Audio.hxx" |
35 | #include "Background.hxx" |
36 | #include "Playfield.hxx" |
37 | #include "Missile.hxx" |
38 | #include "Player.hxx" |
39 | #include "Ball.hxx" |
40 | #include "LatchedInput.hxx" |
41 | #include "PaddleReader.hxx" |
42 | #include "DelayQueueIterator.hxx" |
43 | #include "Control.hxx" |
44 | #include "System.hxx" |
45 | |
46 | class AudioQueue; |
47 | class DispatchResult; |
48 | |
49 | /** |
50 | This class is a device that emulates the Television Interface Adaptor |
51 | found in the Atari 2600 and 7800 consoles. The Television Interface |
52 | Adaptor is an integrated circuit designed to interface between an |
53 | eight bit microprocessor and a television video modulator. It converts |
54 | eight bit parallel data into serial outputs for the color, luminosity, |
55 | and composite sync required by a video modulator. |
56 | |
57 | This class outputs the serial data into a frame buffer which can then |
58 | be displayed on screen. |
59 | |
60 | @author Christian Speckner (DirtyHairy) and Stephen Anthony |
61 | */ |
62 | class TIA : public Device |
63 | { |
64 | public: |
65 | /** |
66 | * These dummy register addresses are used to represent the delayed |
67 | * old / new register swap on writing GRPx and ENABL in the DelayQueue (see below). |
68 | */ |
69 | enum DummyRegisters: uInt8 { |
70 | shuffleP0 = 0xF0, |
71 | shuffleP1 = 0xF1, |
72 | shuffleBL = 0xF2 |
73 | }; |
74 | |
75 | /** |
76 | * Possible palette entries for objects in "fixed debug color mode". |
77 | */ |
78 | enum FixedColor { |
79 | NTSC_RED = 0x30, |
80 | NTSC_ORANGE = 0x38, |
81 | NTSC_YELLOW = 0x1c, |
82 | NTSC_GREEN = 0xc4, |
83 | NTSC_BLUE = 0x9c, |
84 | NTSC_PURPLE = 0x66, |
85 | NTSC_GREY = 0x04, |
86 | |
87 | PAL_RED = 0x62, |
88 | PAL_ORANGE = 0x4a, |
89 | PAL_YELLOW = 0x2e, |
90 | PAL_GREEN = 0x34, |
91 | PAL_BLUE = 0xbc, |
92 | PAL_PURPLE = 0xa6, |
93 | PAL_GREY = 0x06, |
94 | |
95 | SECAM_RED = 0x04, |
96 | SECAM_ORANGE = 0x06, // purple |
97 | SECAM_YELLOW = 0x0c, |
98 | SECAM_GREEN = 0x08, |
99 | SECAM_BLUE = 0x02, |
100 | SECAM_PURPLE = 0x0a, // cyan |
101 | SECAM_GREY = 0x00, |
102 | |
103 | HBLANK_WHITE = 0x0e |
104 | }; |
105 | |
106 | using ConsoleTimingProvider = std::function<ConsoleTiming()>; |
107 | |
108 | public: |
109 | friend class TIADebug; |
110 | friend class RiotDebug; |
111 | |
112 | /** |
113 | Create a new TIA for the specified console |
114 | |
115 | @param console The console the TIA is associated with |
116 | @param settings The settings object for this TIA device |
117 | */ |
118 | TIA(ConsoleIO& console, ConsoleTimingProvider timingProvider, Settings& settings); |
119 | |
120 | virtual ~TIA() = default; |
121 | |
122 | public: |
123 | /** |
124 | Configure the frame manager. |
125 | */ |
126 | void setFrameManager(AbstractFrameManager *frameManager); |
127 | |
128 | /** |
129 | Set the audio queue. This needs to be dynamic as the queue is created after |
130 | the timing has been determined. |
131 | */ |
132 | void setAudioQueue(shared_ptr<AudioQueue> audioQueue); |
133 | |
134 | /** |
135 | Clear the configured frame manager and deteach the lifecycle callbacks. |
136 | */ |
137 | void clearFrameManager(); |
138 | |
139 | /** |
140 | Reset device to its power-on state. |
141 | */ |
142 | void reset() override; |
143 | |
144 | /** |
145 | Install TIA in the specified system. Invoked by the system |
146 | when the TIA is attached to it. |
147 | |
148 | @param system The system the device should install itself in |
149 | */ |
150 | void install(System& system) override; |
151 | |
152 | /** |
153 | Get the byte at the specified address. |
154 | |
155 | @return The byte at the specified address |
156 | */ |
157 | uInt8 peek(uInt16 address) override; |
158 | |
159 | /** |
160 | Change the byte at the specified address to the given value. |
161 | |
162 | @param address The address where the value should be stored |
163 | @param value The value to be stored at the address |
164 | |
165 | @return True if the poke changed the device address space, else false |
166 | */ |
167 | bool poke(uInt16 address, uInt8 value) override; |
168 | |
169 | /** |
170 | Install TIA in the specified system and device. Invoked by |
171 | the system when the TIA is attached to it. All devices |
172 | which invoke this method take responsibility for chaining |
173 | requests back to *this* device. |
174 | |
175 | @param system The system the device should install itself in |
176 | @param device The device responsible for this address space |
177 | */ |
178 | void installDelegate(System& system, Device& device); |
179 | |
180 | /** |
181 | Bind to controllers. |
182 | */ |
183 | void bindToControllers(); |
184 | |
185 | /** |
186 | The following are very similar to save() and load(), except they |
187 | do a 'deeper' save of the display data itself. |
188 | |
189 | Normally, the internal framebuffer doesn't need to be saved to |
190 | a state file, since the file already contains all the information |
191 | needed to re-create it, starting from scanline 0. In effect, when a |
192 | state is loaded, the framebuffer is empty, and the next call to |
193 | update() generates valid framebuffer data. |
194 | |
195 | However, state files saved from the debugger need more information, |
196 | such as the exact state of the internal framebuffer itself *before* |
197 | we call update(), including if the display was in partial frame mode. |
198 | |
199 | Essentially, a normal state save has 'frame resolution', whereas |
200 | the debugger state save has 'cycle resolution', and hence needs |
201 | more information. The methods below save/load this extra info, |
202 | and eliminate having to save approx. 50K to normal state files. |
203 | */ |
204 | bool saveDisplay(Serializer& out) const; |
205 | bool loadDisplay(Serializer& in); |
206 | |
207 | /** |
208 | This method should be called at an interval corresponding to the |
209 | desired frame rate to update the TIA. Invoking this method will update |
210 | the graphics buffer and generate the corresponding audio samples. |
211 | */ |
212 | void update(DispatchResult& result, uInt64 maxCycles = 50000); |
213 | |
214 | void update(uInt64 maxCycles = 50000); |
215 | |
216 | /** |
217 | Did we generate a new frame? |
218 | */ |
219 | bool newFramePending() { return myFramesSinceLastRender > 0; } |
220 | |
221 | /** |
222 | * Clear any pending frames. |
223 | */ |
224 | void clearPendingFrame() { myFramesSinceLastRender = 0; } |
225 | |
226 | /** |
227 | The number of frames since we did last render to the front buffer. |
228 | */ |
229 | uInt32 framesSinceLastRender() { return myFramesSinceLastRender; } |
230 | |
231 | /** |
232 | Render the pending frame to the framebuffer and clear the flag. |
233 | */ |
234 | void renderToFrameBuffer(); |
235 | |
236 | /** |
237 | Return the buffer that holds the currently drawing TIA frame |
238 | (the TIA output widget needs this). |
239 | */ |
240 | uInt8* outputBuffer() { return myBackBuffer.data(); } |
241 | |
242 | /** |
243 | Returns a pointer to the internal frame buffer. |
244 | */ |
245 | uInt8* frameBuffer() { return myFramebuffer.data(); } |
246 | |
247 | /** |
248 | Answers dimensional info about the framebuffer. |
249 | */ |
250 | uInt32 width() const { return TIAConstants::H_PIXEL; } |
251 | uInt32 height() const { return myFrameManager->height(); } |
252 | uInt32 ystart() const { return myFrameManager->ystart(); } |
253 | |
254 | /** |
255 | Changes the current YStart property. |
256 | */ |
257 | void setYStart(uInt32 ystart) { myFrameManager->setYstart(ystart); } |
258 | |
259 | void setLayout(FrameLayout layout) { myFrameManager->setLayout(layout); } |
260 | FrameLayout frameLayout() const { return myFrameManager->layout(); } |
261 | |
262 | /** |
263 | Enables/disables color-loss for PAL modes only. |
264 | |
265 | @param enabled Whether to enable or disable PAL color-loss mode |
266 | */ |
267 | bool enableColorLoss(bool enabled); |
268 | |
269 | /** |
270 | Answers whether color-loss is enabled. |
271 | |
272 | @return Colour-loss is enabled |
273 | */ |
274 | bool colorLossEnabled() const { return myColorLossEnabled; } |
275 | |
276 | /** |
277 | Answers whether colour-loss is applicable for the current frame. |
278 | |
279 | @return Colour-loss is active for this frame |
280 | */ |
281 | bool colorLossActive() const { return myColorLossActive; } |
282 | |
283 | /** |
284 | Answers the current color clock we've gotten to on this scanline. |
285 | |
286 | @return The current color clock |
287 | */ |
288 | uInt32 clocksThisLine() const { return myHctr - myHctrDelta; } |
289 | |
290 | /** |
291 | Answers the total number of scanlines the TIA generated in producing |
292 | the current frame buffer. For partial frames, this will be the |
293 | current scanline. |
294 | |
295 | @return The total number of scanlines generated |
296 | */ |
297 | uInt32 scanlines() const { return myFrameManager->scanlines(); } |
298 | |
299 | /** |
300 | Answers the total number of scanlines the TIA generated in the |
301 | previous frame. |
302 | |
303 | @return The total number of scanlines generated in the last frame. |
304 | */ |
305 | uInt32 scanlinesLastFrame() const { return myFrameManager->scanlinesLastFrame(); } |
306 | |
307 | /** |
308 | The same, but for the frame in the frame buffer. |
309 | */ |
310 | uInt32 frameBufferScanlinesLastFrame() const { return myFrameBufferScanlines; } |
311 | |
312 | /** |
313 | Answers the total system cycles from the start of the emulation. |
314 | */ |
315 | uInt64 cycles() const { return uInt64(mySystem->cycles()); } |
316 | |
317 | /** |
318 | Answers the frame count from the start of the emulation. |
319 | */ |
320 | uInt32 frameCount() const { return myFrameManager->frameCount(); } |
321 | |
322 | /** |
323 | Answers the system cycles from the start of the current frame. |
324 | */ |
325 | uInt32 frameCycles() const { |
326 | return uInt32(mySystem->cycles() - myCyclesAtFrameStart); |
327 | } |
328 | |
329 | /** |
330 | Answers whether the TIA is currently in being rendered |
331 | (we're in between the start and end of drawing a frame). |
332 | |
333 | @return If the frame is in rendering mode |
334 | */ |
335 | bool isRendering() const { return myFrameManager->isRendering(); } |
336 | |
337 | /** |
338 | Answers the current position of the virtual 'electron beam' used |
339 | when drawing the TIA image in debugger mode. |
340 | |
341 | @return The x/y coordinates of the scanline electron beam, and whether |
342 | it is in the visible/viewable area of the screen |
343 | */ |
344 | bool electronBeamPos(uInt32& x, uInt32& y) const; |
345 | |
346 | /** |
347 | Enables/disable/toggle the specified (or all) TIA bit(s). Note that |
348 | disabling a graphical object also disables its collisions. |
349 | |
350 | @param mode 1/0 indicates on/off, and values greater than 1 mean |
351 | flip the bit from its current state |
352 | |
353 | @return Whether the bit was enabled or disabled |
354 | */ |
355 | bool toggleBit(TIABit b, uInt8 mode = 2); |
356 | bool toggleBits(); |
357 | |
358 | /** |
359 | Enables/disable/toggle the specified (or all) TIA bit collision(s). |
360 | |
361 | @param mode 1/0 indicates on/off, and values greater than 1 mean |
362 | flip the collision from its current state |
363 | |
364 | @return Whether the collision was enabled or disabled |
365 | */ |
366 | bool toggleCollision(TIABit b, uInt8 mode = 2); |
367 | bool toggleCollisions(); |
368 | |
369 | /** |
370 | Enables/disable/toggle/query 'fixed debug colors' mode. |
371 | |
372 | @param enable Whether to enable fixed debug colors mode |
373 | |
374 | @return Whether the mode was enabled or disabled |
375 | */ |
376 | bool enableFixedColors(bool enable); |
377 | bool toggleFixedColors() { return enableFixedColors(!usingFixedColors()); } |
378 | bool usingFixedColors() const { return myColorHBlank != 0x00; } |
379 | |
380 | /** |
381 | Sets the color of each object in 'fixed debug colors' mode. |
382 | Note that this doesn't enable/disable fixed colors; it simply |
383 | updates the palette that is used. |
384 | |
385 | @param colors Each character in the 6-char string represents the |
386 | first letter of the color to use for |
387 | P0/M0/P1/M1/PF/BL, respectively. |
388 | |
389 | @return True if colors were changed successfully, else false |
390 | */ |
391 | bool setFixedColorPalette(const string& colors); |
392 | |
393 | /** |
394 | Enable/disable/query state of 'undriven/floating TIA pins'. |
395 | |
396 | @param mode 1/0 indicates on/off, otherwise return the current state |
397 | |
398 | @return Whether the mode was enabled or disabled |
399 | */ |
400 | bool driveUnusedPinsRandom(uInt8 mode = 2); |
401 | |
402 | /** |
403 | Enables/disable/toggle 'scanline jittering' mode, and set the |
404 | recovery 'factor'. |
405 | |
406 | @param mode 1/0 indicates on/off, otherwise flip from |
407 | its current state |
408 | |
409 | @return Whether the mode was enabled or disabled |
410 | */ |
411 | bool toggleJitter(uInt8 mode = 2); |
412 | void setJitterRecoveryFactor(Int32 factor); |
413 | |
414 | /** |
415 | Enables/disables delayed playfield bits values. |
416 | |
417 | @param delayed Wether to enable delayed playfield delays |
418 | */ |
419 | void setPFBitsDelay(bool delayed); |
420 | |
421 | /** |
422 | Enables/disables delayed playfield colors. |
423 | |
424 | @param delayed Wether to enable delayed playfield colors |
425 | */ |
426 | void setPFColorDelay(bool delayed); |
427 | |
428 | /** |
429 | Enables/disables delayed player swapping. |
430 | |
431 | @param delayed Wether to enable delayed player swapping |
432 | */ |
433 | void setPlSwapDelay(bool delayed); |
434 | |
435 | /** |
436 | Enables/disables delayed ball swapping. |
437 | |
438 | @param delayed Wether to enable delayed ball swapping |
439 | */ |
440 | void setBlSwapDelay(bool delayed); |
441 | |
442 | /** |
443 | Enables/disables inverted HMOVE phase clock for players. |
444 | |
445 | @param enable Wether to enable inverted HMOVE phase clock for players |
446 | */ |
447 | void setPlInvertedPhaseClock(bool enable); |
448 | |
449 | /** |
450 | Enables/disables inverted HMOVE phase clock for missiles. |
451 | |
452 | @param enable Wether to enable inverted HMOVE phase clock for missiles |
453 | */ |
454 | void setMsInvertedPhaseClock(bool enable); |
455 | |
456 | /** |
457 | Enables/disables inverted HMOVE phase clock for ball. |
458 | |
459 | @param enable Wether to enable inverted HMOVE phase clock for ball |
460 | */ |
461 | void setBlInvertedPhaseClock(bool enable); |
462 | |
463 | /** |
464 | This method should be called to update the TIA with a new scanline. |
465 | */ |
466 | TIA& updateScanline(); |
467 | |
468 | /** |
469 | This method should be called to update the TIA with a new partial |
470 | scanline by stepping one CPU instruction. |
471 | */ |
472 | TIA& updateScanlineByStep(); |
473 | |
474 | /** |
475 | Retrieve the last value written to a certain register. |
476 | */ |
477 | uInt8 registerValue(uInt8 reg) const; |
478 | |
479 | /** |
480 | Get the current x value. |
481 | */ |
482 | uInt8 getPosition() const { |
483 | uInt8 realHctr = myHctr - myHctrDelta; |
484 | |
485 | return (realHctr < TIAConstants::H_BLANK_CLOCKS) ? 0 : (realHctr - TIAConstants::H_BLANK_CLOCKS); |
486 | } |
487 | |
488 | /** |
489 | Flush the line cache after an externally triggered state change |
490 | (e.g. a register write). |
491 | */ |
492 | void flushLineCache(); |
493 | |
494 | /** |
495 | Schedule a collision update |
496 | */ |
497 | void scheduleCollisionUpdate(); |
498 | |
499 | /** |
500 | Create a new delayQueueIterator for the debugger. |
501 | */ |
502 | shared_ptr<DelayQueueIterator> delayQueueIterator() const; |
503 | |
504 | /** |
505 | Save the current state of this device to the given Serializer. |
506 | |
507 | @param out The Serializer object to use |
508 | @return False on any errors, else true |
509 | */ |
510 | bool save(Serializer& out) const override; |
511 | |
512 | /** |
513 | Load the current state of this device from the given Serializer. |
514 | |
515 | @param in The Serializer object to use |
516 | @return False on any errors, else true |
517 | */ |
518 | bool load(Serializer& in) override; |
519 | |
520 | /** |
521 | * Run and forward TIA emulation to the current system clock. |
522 | */ |
523 | void updateEmulation(); |
524 | |
525 | private: |
526 | /** |
527 | * During each line, the TIA cycles through these two states. |
528 | */ |
529 | enum class HState {blank, frame}; |
530 | |
531 | /** |
532 | * The three different modes of the priority encoder. Check TIA::renderPixel |
533 | * for a precise definition. |
534 | */ |
535 | enum class Priority {pfp, score, normal}; |
536 | |
537 | /** |
538 | * Palette and indices for fixed debug colors. |
539 | */ |
540 | enum FixedObject { P0, M0, P1, M1, PF, BL, BK }; |
541 | FixedColor myFixedColorPalette[3][7]; |
542 | string myFixedColorNames[7]; |
543 | |
544 | private: |
545 | /** |
546 | * This callback is invoked by FrameManager when a new frame starts. |
547 | */ |
548 | void onFrameStart(); |
549 | |
550 | /** |
551 | * This callback is invoked by FrameManager when the current frame completes. |
552 | */ |
553 | void onFrameComplete(); |
554 | |
555 | /** |
556 | * Called when the CPU enters halt state (RDY pulled low). Execution continues |
557 | * immediatelly afterwards, so we have to adjust the system clock to account |
558 | * for the cycles the 6502 spent in halt state. |
559 | */ |
560 | void onHalt(); |
561 | |
562 | /** |
563 | * Execute colorClocks cycles of TIA simulation. |
564 | */ |
565 | void cycle(uInt32 colorClocks); |
566 | |
567 | /** |
568 | * Advance the movement logic by a single clock. |
569 | */ |
570 | void tickMovement(); |
571 | |
572 | /** |
573 | * Advance a single clock during hblank. |
574 | */ |
575 | void tickHblank(); |
576 | |
577 | /** |
578 | * Advance a single clock duing the visible part of the scanline. |
579 | */ |
580 | void tickHframe(); |
581 | |
582 | /** |
583 | * Update the collision bitfield. |
584 | */ |
585 | void updateCollision(); |
586 | |
587 | /** |
588 | * Execute a RSYNC. |
589 | */ |
590 | void applyRsync(); |
591 | |
592 | /** |
593 | * Render the current pixel into the framebuffer. |
594 | */ |
595 | void renderPixel(uInt32 x, uInt32 y); |
596 | |
597 | /** |
598 | * Clear the first 8 pixels of a scanline with black if we are in hblank |
599 | * (called during HMOVE). |
600 | */ |
601 | void clearHmoveComb(); |
602 | |
603 | /** |
604 | * Advance a line and update our state accordingly. |
605 | */ |
606 | void nextLine(); |
607 | |
608 | /** |
609 | * Clone the last line. Called in nextLine if TIA state was unchanged. |
610 | */ |
611 | void cloneLastLine(); |
612 | |
613 | /** |
614 | * Execute a delayed write. Called when the DelayQueue is pumped. |
615 | */ |
616 | void delayedWrite(uInt8 address, uInt8 value); |
617 | |
618 | /** |
619 | * Update all paddle readout circuits to the current controller state. |
620 | */ |
621 | void updatePaddle(uInt8 idx); |
622 | |
623 | /** |
624 | * Get the target counter value during a RESx. This essentially depends on |
625 | * the position in the current scanline. |
626 | */ |
627 | uInt8 resxCounter(); |
628 | |
629 | /** |
630 | * Get the result of the specified collision register. |
631 | */ |
632 | uInt8 collCXM0P() const; |
633 | uInt8 collCXM1P() const; |
634 | uInt8 collCXP0FB() const; |
635 | uInt8 collCXP1FB() const; |
636 | uInt8 collCXM0FB() const; |
637 | uInt8 collCXM1FB() const; |
638 | uInt8 collCXPPMM() const; |
639 | uInt8 collCXBLPF() const; |
640 | |
641 | /** |
642 | * Toggle the specified collision bits |
643 | */ |
644 | void toggleCollP0PF(); |
645 | void toggleCollP0BL(); |
646 | void toggleCollP0M1(); |
647 | void toggleCollP0M0(); |
648 | void toggleCollP0P1(); |
649 | void toggleCollP1PF(); |
650 | void toggleCollP1BL(); |
651 | void toggleCollP1M1(); |
652 | void toggleCollP1M0(); |
653 | void toggleCollM0PF(); |
654 | void toggleCollM0BL(); |
655 | void toggleCollM0M1(); |
656 | void toggleCollM1PF(); |
657 | void toggleCollM1BL(); |
658 | void toggleCollBLPF(); |
659 | |
660 | /** |
661 | * Re-apply developer settings from the settings object. |
662 | * This should be done each time the device is reset, or after |
663 | * a state load occurs. |
664 | */ |
665 | void applyDeveloperSettings(); |
666 | |
667 | #ifdef DEBUGGER_SUPPORT |
668 | void createAccessBase(); |
669 | |
670 | /** |
671 | * Query the given address type for the associated disassembly flags. |
672 | * |
673 | * @param address The address to query |
674 | */ |
675 | uInt8 getAccessFlags(uInt16 address) const override; |
676 | /** |
677 | * Change the given address to use the given disassembly flags. |
678 | * |
679 | * @param address The address to modify |
680 | * @param flags A bitfield of DisasmType directives for the given address |
681 | */ |
682 | void setAccessFlags(uInt16 address, uInt8 flags) override; |
683 | #endif // DEBUGGER_SUPPORT |
684 | |
685 | private: |
686 | ConsoleIO& myConsole; |
687 | ConsoleTimingProvider myTimingProvider; |
688 | Settings& mySettings; |
689 | |
690 | /** |
691 | * The length of the delay queue (maximum number of clocks delay) |
692 | */ |
693 | static constexpr unsigned delayQueueLength = 16; |
694 | |
695 | /** |
696 | * The size of the delay queue (maximum number of writes scheduled in a single slot). |
697 | */ |
698 | static constexpr unsigned delayQueueSize = 16; |
699 | |
700 | /** |
701 | * A list of delayed writes that are queued up for future execution. Delayed |
702 | * writes can be both actual writes whose effect is delayed by one or more clocks |
703 | * on real hardware and delayed side effects of certain operations (GRPx!). |
704 | */ |
705 | DelayQueue<delayQueueLength, delayQueueSize> myDelayQueue; |
706 | |
707 | /** |
708 | Variable delay values for TIA writes. |
709 | */ |
710 | uInt8 myPFBitsDelay; |
711 | uInt8 myPFColorDelay; |
712 | uInt8 myPlSwapDelay; |
713 | uInt8 myBlSwapDelay; |
714 | |
715 | /** |
716 | * The frame manager is responsible for detecting frame boundaries and the visible |
717 | * region of each frame. |
718 | */ |
719 | AbstractFrameManager* myFrameManager; |
720 | |
721 | /** |
722 | * The various TIA objects. |
723 | */ |
724 | Background myBackground; |
725 | Playfield myPlayfield; |
726 | Missile myMissile0; |
727 | Missile myMissile1; |
728 | Player myPlayer0; |
729 | Player myPlayer1; |
730 | Ball myBall; |
731 | |
732 | Audio myAudio; |
733 | |
734 | /** |
735 | * The paddle readout circuits. |
736 | */ |
737 | std::array<PaddleReader, 4> myPaddleReaders; |
738 | |
739 | /** |
740 | * Circuits for the "latched inputs". |
741 | */ |
742 | LatchedInput myInput0; |
743 | LatchedInput myInput1; |
744 | |
745 | // Pointer to the internal color-index-based frame buffer |
746 | std::array<uInt8, TIAConstants::H_PIXEL * TIAConstants::frameBufferHeight> myFramebuffer; |
747 | |
748 | // The frame is rendered to the backbuffer and only copied to the framebuffer |
749 | // upon completion |
750 | std::array<uInt8, TIAConstants::H_PIXEL * TIAConstants::frameBufferHeight> myBackBuffer; |
751 | std::array<uInt8, TIAConstants::H_PIXEL * TIAConstants::frameBufferHeight> myFrontBuffer; |
752 | |
753 | // We snapshot frame statistics when the back buffer is copied to the front buffer |
754 | // and when the front buffer is copied to the frame buffer |
755 | uInt32 myFrontBufferScanlines, myFrameBufferScanlines; |
756 | |
757 | // Frames since the last time a frame was rendered to the render buffer |
758 | uInt32 myFramesSinceLastRender; |
759 | |
760 | /** |
761 | * Setting this to true injects random values into undefined reads. |
762 | */ |
763 | bool myTIAPinsDriven; |
764 | |
765 | /** |
766 | * The current "line state" --- either hblank or frame. |
767 | */ |
768 | HState myHstate; |
769 | |
770 | /** |
771 | * Master line counter |
772 | */ |
773 | uInt8 myHctr; |
774 | |
775 | /** |
776 | * Delta between master line counter and actual color clock. Nonzero after |
777 | * RSYNC (before the scanline terminates) |
778 | */ |
779 | Int32 myHctrDelta; |
780 | |
781 | /** |
782 | * Electron beam x at rendering start (used for blanking out any pixels from |
783 | * the last frame that are not overwritten) |
784 | */ |
785 | uInt8 myXAtRenderingStart; |
786 | |
787 | /** |
788 | * Do we need to update the collision mask this clock? |
789 | */ |
790 | bool myCollisionUpdateRequired; |
791 | |
792 | /** |
793 | * Force schedule a collision update |
794 | */ |
795 | bool myCollisionUpdateScheduled; |
796 | |
797 | /** |
798 | * The collision latches are represented by 15 bits in a bitfield. |
799 | */ |
800 | uInt32 myCollisionMask; |
801 | |
802 | /** |
803 | * The movement clock counts the extra ticks sent to the objects during |
804 | * movement. |
805 | */ |
806 | uInt32 myMovementClock; |
807 | |
808 | /** |
809 | * Movement mode --- are we sending movement clocks? |
810 | */ |
811 | bool myMovementInProgress; |
812 | |
813 | /** |
814 | * Do we have an extended hblank this line? Get set by strobing HMOVE and |
815 | * cleared when the line wraps. |
816 | */ |
817 | bool myExtendedHblank; |
818 | |
819 | /** |
820 | * Counts the number of line wraps since the last external TIA state change. |
821 | * If at least two line breaks have passed, the TIA will suspend simulation |
822 | * and just reuse the last line instead. |
823 | */ |
824 | uInt32 myLinesSinceChange; |
825 | |
826 | /** |
827 | * The current mode of the priority encoder. |
828 | */ |
829 | Priority myPriority; |
830 | |
831 | /** |
832 | * The index of the last CPU cycle that was included in the simulation. |
833 | */ |
834 | uInt64 myLastCycle; |
835 | |
836 | /** |
837 | * Keeps track of a possible fractional number of clocks that still need |
838 | * to be simulated. |
839 | */ |
840 | uInt8 mySubClock; |
841 | |
842 | /** |
843 | * Bitmasks that track which sprites / collisions are enabled / disabled. |
844 | */ |
845 | uInt8 mySpriteEnabledBits; |
846 | uInt8 myCollisionsEnabledBits; |
847 | |
848 | /** |
849 | * The color used to highlight HMOVE blanks (if enabled). |
850 | */ |
851 | uInt8 myColorHBlank; |
852 | |
853 | /** |
854 | * The total number of color clocks since emulation started. |
855 | */ |
856 | uInt64 myTimestamp; |
857 | |
858 | /** |
859 | * The "shadow registers" track the last written register value for the |
860 | * debugger. |
861 | */ |
862 | std::array<uInt8, 64> myShadowRegisters; |
863 | |
864 | /** |
865 | * Indicates if color loss should be enabled or disabled. Color loss |
866 | * occurs on PAL-like systems when the previous frame contains an odd |
867 | * number of scanlines. |
868 | */ |
869 | bool myColorLossEnabled; |
870 | bool myColorLossActive; |
871 | |
872 | /** |
873 | * System cycles at the end of the previous frame / beginning of next frame. |
874 | */ |
875 | uInt64 myCyclesAtFrameStart; |
876 | |
877 | /** |
878 | * The frame manager can change during our lifetime, so we buffer those two. |
879 | */ |
880 | bool myEnableJitter; |
881 | uInt8 myJitterFactor; |
882 | |
883 | static constexpr uInt16 |
884 | TIA_SIZE = 0x40, TIA_MASK = TIA_SIZE - 1, TIA_READ_MASK = 0x0f, TIA_BIT = 0x080, TIA_DELAY = 2; |
885 | |
886 | #ifdef DEBUGGER_SUPPORT |
887 | // The arrays containing information about every byte of TIA |
888 | // indicating whether and how (RW) it is used. |
889 | std::array<uInt8, TIA_SIZE> myAccessBase; |
890 | |
891 | // The array used to skip the first two TIA access trackings |
892 | std::array<uInt8, TIA_SIZE> myAccessDelay; |
893 | #endif // DEBUGGER_SUPPORT |
894 | |
895 | private: |
896 | TIA() = delete; |
897 | TIA(const TIA&) = delete; |
898 | TIA(TIA&&) = delete; |
899 | TIA& operator=(const TIA&) = delete; |
900 | TIA& operator=(TIA&&) = delete; |
901 | }; |
902 | |
903 | #endif // TIA_TIA |
904 | |