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-2014 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 // $Id: TIA.hxx 2838 2014-01-17 23:34:03Z stephena $
18 //============================================================================
19 
20 #ifndef TIA_HXX
21 #define TIA_HXX
22 
23 class Console;
24 class Settings;
25 class Sound;
26 
27 #include "bspf.hxx"
28 #include "Device.hxx"
29 #include "System.hxx"
30 #include "TIATables.hxx"
31 
32 /**
33   This class is a device that emulates the Television Interface Adaptor
34   found in the Atari 2600 and 7800 consoles.  The Television Interface
35   Adaptor is an integrated circuit designed to interface between an
36   eight bit microprocessor and a television video modulator. It converts
37   eight bit parallel data into serial outputs for the color, luminosity,
38   and composite sync required by a video modulator.
39 
40   This class outputs the serial data into a frame buffer which can then
41   be displayed on screen.
42 
43   @author  Bradford W. Mott
44   @version $Id: TIA.hxx 2838 2014-01-17 23:34:03Z stephena $
45 */
46 class TIA : public Device
47 {
48   public:
49     friend class TIADebug;
50     friend class RiotDebug;
51 
52     /**
53       Create a new TIA for the specified console
54 
55       @param console  The console the TIA is associated with
56       @param sound    The sound object the TIA is associated with
57       @param settings The settings object for this TIA device
58     */
59     TIA(Console& console, Sound& sound, Settings& settings);
60 
61     /**
62       Destructor
63     */
64     virtual ~TIA();
65 
66   public:
67     /**
68       Reset device to its power-on state
69     */
70     void reset();
71 
72     /**
73       Reset frame to current YStart/Height properties
74     */
75     void frameReset();
76 
77     /**
78       Notification method invoked by the system right before the
79       system resets its cycle counter to zero.  It may be necessary
80       to override this method for devices that remember cycle counts.
81     */
82     void systemCyclesReset();
83 
84     /**
85       Install TIA in the specified system.  Invoked by the system
86       when the TIA is attached to it.
87 
88       @param system The system the device should install itself in
89     */
90     void install(System& system);
91 
92     /**
93       Install TIA in the specified system and device.  Invoked by
94       the system when the TIA is attached to it.  All devices
95       which invoke this method take responsibility for chaining
96       requests back to *this* device.
97 
98       @param system The system the device should install itself in
99       @param device The device responsible for this address space
100     */
101     void install(System& system, Device& device);
102 
103     /**
104       Save the current state of this device to the given Serializer.
105 
106       @param out  The Serializer object to use
107       @return  False on any errors, else true
108     */
109     bool save(Serializer& out) const;
110 
111     /**
112       Load the current state of this device from the given Serializer.
113 
114       @param in  The Serializer object to use
115       @return  False on any errors, else true
116     */
117     bool load(Serializer& in);
118 
119     /**
120       The following are very similar to save() and load(), except they
121       do a 'deeper' save of the display data itself.
122 
123       Normally, the internal framebuffer doesn't need to be saved to
124       a state file, since the file already contains all the information
125       needed to re-create it, starting from scanline 0.  In effect, when a
126       state is loaded, the framebuffer is empty, and the next call to
127       update() generates valid framebuffer data.
128 
129       However, state files saved from the debugger need more information,
130       such as the exact state of the internal framebuffer itself *before*
131       we call update(), including if the display was in partial frame mode.
132 
133       Essentially, a normal state save has 'frame resolution', whereas
134       the debugger state save has 'cycle resolution', and hence needs
135       more information.  The methods below save/load this extra info,
136       and eliminate having to save approx. 50K to normal state files.
137     */
138     bool saveDisplay(Serializer& out) const;
139     bool loadDisplay(Serializer& in);
140 
141     /**
142       Get a descriptor for the device name (used in error checking).
143 
144       @return The name of the object
145     */
name() const146     string name() const { return "TIA"; }
147 
148     /**
149       Get the byte at the specified address
150 
151       @return The byte at the specified address
152     */
153     uInt8 peek(uInt16 address);
154 
155     /**
156       Change the byte at the specified address to the given value
157 
158       @param address The address where the value should be stored
159       @param value The value to be stored at the address
160 
161       @return  True if the poke changed the device address space, else false
162     */
163     bool poke(uInt16 address, uInt8 value);
164 
165     /**
166       This method should be called at an interval corresponding to the
167       desired frame rate to update the TIA.  Invoking this method will update
168       the graphics buffer and generate the corresponding audio samples.
169     */
170     void update();
171 
172     /**
173       Answers the current frame buffer
174 
175       @return Pointer to the current frame buffer
176     */
currentFrameBuffer() const177     uInt8* currentFrameBuffer() const
178       { return myCurrentFrameBuffer + myFramePointerOffset; }
179 
180     /**
181       Answers the previous frame buffer
182 
183       @return Pointer to the previous frame buffer
184     */
previousFrameBuffer() const185     uInt8* previousFrameBuffer() const
186       { return myPreviousFrameBuffer + myFramePointerOffset; }
187 
188     /**
189       Answers the width and height of the frame buffer
190     */
width() const191     inline uInt32 width() const  { return 160;           }
height() const192     inline uInt32 height() const { return myFrameHeight; }
ystart() const193     inline uInt32 ystart() const { return myFrameYStart; }
194 
195     /**
196       Changes the current Height/YStart properties.
197       Note that calls to these method(s) must be eventually followed by
198       ::frameReset() for the changes to take effect.
199     */
setHeight(uInt32 height)200     void setHeight(uInt32 height) { myFrameHeight = height; }
setYStart(uInt32 ystart)201     void setYStart(uInt32 ystart) { myFrameYStart = ystart; }
202 
203     /**
204       Enables/disables auto-frame calculation.  If enabled, the TIA
205       re-adjusts the framerate at regular intervals.
206 
207       @param mode  Whether to enable or disable all auto-frame calculation
208     */
enableAutoFrame(bool mode)209     void enableAutoFrame(bool mode) { myAutoFrameEnabled = mode; }
210 
211     /**
212       Enables/disables color-loss for PAL modes only.
213 
214       @param mode  Whether to enable or disable PAL color-loss mode
215     */
enableColorLoss(bool mode)216     void enableColorLoss(bool mode)
217       { myColorLossEnabled = myFramerate <= 55 ? mode : false; }
218 
219     /**
220       Answers whether this TIA runs at NTSC or PAL scanrates,
221       based on how many frames of out the total count are PAL frames.
222     */
isPAL()223     bool isPAL()
224       { return float(myPALFrameCounter) / myFrameCounter >= (25.0/60.0); }
225 
getMilliSeconds() const226     uInt64 getMilliSeconds() const {
227         uInt64 ntscFrames = myFrameCounter - myPALFrameCounter;
228         return ntscFrames * (1000.0f/60.0f) + myPALFrameCounter * (1000.0f/50.f);
229     }
230 
231     /**
232       Answers the current color clock we've gotten to on this scanline.
233 
234       @return The current color clock
235     */
clocksThisLine() const236     uInt32 clocksThisLine() const
237       { return ((mySystem->cycles() * 3) - myClockWhenFrameStarted) % 228; }
238 
239     /**
240       Answers the scanline at which the current frame began drawing.
241 
242       @return The starting scanline
243     */
startLine() const244     uInt32 startLine() const
245       { return myStartScanline; }
246 
247     /**
248       Answers the total number of scanlines the TIA generated in producing
249       the current frame buffer. For partial frames, this will be the
250       current scanline.
251 
252       @return The total number of scanlines generated
253     */
scanlines() const254     uInt32 scanlines() const
255       { return ((mySystem->cycles() * 3) - myClockWhenFrameStarted) / 228; }
256 
257     /**
258       Answers whether the TIA is currently in 'partial frame' mode
259       (we're in between a call of startFrame and endFrame).
260 
261       @return If we're in partial frame mode
262     */
partialFrame() const263     bool partialFrame() const { return myPartialFrameFlag; }
264 
265     /**
266       Answers the first scanline at which drawing occured in the last frame.
267 
268       @return The starting scanline
269     */
startScanline() const270     uInt32 startScanline() const { return myStartScanline; }
271 
272     /**
273       Answers the current position of the virtual 'electron beam' used to
274       draw the TIA image.  If not in partial frame mode, the position is
275       defined to be in the lower right corner (@ width/height of the screen).
276       Note that the coordinates are with respect to currentFrameBuffer(),
277       taking any YStart values into account.
278 
279       @return The x/y coordinates of the scanline electron beam, and whether
280               it is in the visible/viewable area of the screen
281     */
282     bool scanlinePos(uInt16& x, uInt16& y) const;
283 
284     /**
285       Enables/disable/toggle the specified (or all) TIA bit(s).  Note that
286       disabling a graphical object also disables its collisions.
287 
288       @param mode  1/0 indicates on/off, and values greater than 1 mean
289                    flip the bit from its current state
290 
291       @return  Whether the bit was enabled or disabled
292     */
293     bool toggleBit(TIABit b, uInt8 mode = 2);
294     bool toggleBits();
295 
296     /**
297       Enables/disable/toggle the specified (or all) TIA bit collision(s).
298 
299       @param mode  1/0 indicates on/off, and values greater than 1 mean
300                    flip the collision from its current state
301 
302       @return  Whether the collision was enabled or disabled
303     */
304     bool toggleCollision(TIABit b, uInt8 mode = 2);
305     bool toggleCollisions();
306 
307     /**
308       Toggle the display of HMOVE blanks.
309 
310       @return  Whether the HMOVE blanking was enabled or disabled
311     */
312     bool toggleHMOVEBlank();
313 
314     /**
315       Enables/disable/toggle 'fixed debug colors' mode.
316 
317       @param mode  1/0 indicates on/off, otherwise flip from
318                    its current state
319 
320       @return  Whether the mode was enabled or disabled
321     */
322     bool toggleFixedColors(uInt8 mode = 2);
323 
324     /**
325       Enable/disable/query state of 'undriven/floating TIA pins'.
326 
327       @param mode  1/0 indicates on/off, otherwise return the current state
328 
329       @return  Whether the mode was enabled or disabled
330     */
331     bool driveUnusedPinsRandom(uInt8 mode = 2);
332 
333 #ifdef DEBUGGER_SUPPORT
334     /**
335       This method should be called to update the TIA with a new scanline.
336     */
337     void updateScanline();
338 
339     /**
340       This method should be called to update the TIA with a new partial
341       scanline by stepping one CPU instruction.
342     */
343     void updateScanlineByStep();
344 
345     /**
346       This method should be called to update the TIA with a new partial
347       scanline by tracing to target address.
348     */
349     void updateScanlineByTrace(int target);
350 #endif
351 
352   private:
353     /**
354       Enables/disables all TIABit bits.  Note that disabling a graphical
355       object also disables its collisions.
356 
357       @param mode  Whether to enable or disable all bits
358     */
359     void enableBits(bool mode);
360 
361     /**
362       Enables/disables all TIABit collisions.
363 
364       @param mode  Whether to enable or disable all collisions
365     */
366     void enableCollisions(bool mode);
367 
368     // Update the current frame buffer to the specified color clock
369     void updateFrame(Int32 clock);
370 
371     // Waste cycles until the current scanline is finished
372     void waitHorizontalSync();
373 
374     // Reset horizontal sync counter
375     void waitHorizontalRSync();
376 
377     // Clear both internal TIA buffers to black (palette color 0)
378     void clearBuffers();
379 
380     // Set up bookkeeping for the next frame
381     void startFrame();
382 
383     // Update bookkeeping at end of frame
384     void endFrame();
385 
386     // Convert resistance from ports to dumped value
387     uInt8 dumpedInputPort(int resistance);
388 
389     // Write the specified value to the HMOVE registers at the given clock
390     void pokeHMP0(uInt8 value, Int32 clock);
391     void pokeHMP1(uInt8 value, Int32 clock);
392     void pokeHMM0(uInt8 value, Int32 clock);
393     void pokeHMM1(uInt8 value, Int32 clock);
394     void pokeHMBL(uInt8 value, Int32 clock);
395 
396     // Apply motion to registers when HMOVE is currently active
397     void applyActiveHMOVEMotion(int hpos, Int16& pos, Int32 motionClock);
398 
399     // Apply motion to registers when HMOVE was previously active
400     void applyPreviousHMOVEMotion(int hpos, Int16& pos, uInt8 motion);
401 
402   private:
403     // Console the TIA is associated with
404     Console& myConsole;
405 
406     // Sound object the TIA is associated with
407     Sound& mySound;
408 
409     // Settings object the TIA is associated with
410     Settings& mySettings;
411 
412     // Pointer to the current frame buffer
413     uInt8* myCurrentFrameBuffer;
414 
415     // Pointer to the previous frame buffer
416     uInt8* myPreviousFrameBuffer;
417 
418     // Pointer to the next pixel that will be drawn in the current frame buffer
419     uInt8* myFramePointer;
420 
421     // Indicates offset used by the exported frame buffer
422     // (the exported frame buffer is a vertical 'sliding window' of the actual buffer)
423     uInt32 myFramePointerOffset;
424 
425     // Indicates the number of 'colour clocks' offset from the base
426     // frame buffer pointer
427     // (this is used when loading state files with a 'partial' frame)
428     uInt32 myFramePointerClocks;
429 
430     // Indicated what scanline the frame should start being drawn at
431     uInt32 myFrameYStart;
432 
433     // Indicates the height of the frame in scanlines
434     uInt32 myFrameHeight;
435 
436     // Indicates offset in color clocks when display should stop
437     uInt32 myStopDisplayOffset;
438 
439     // Indicates color clocks when the current frame began
440     Int32 myClockWhenFrameStarted;
441 
442     // Indicates color clocks when frame should begin to be drawn
443     Int32 myClockStartDisplay;
444 
445     // Indicates color clocks when frame should stop being drawn
446     Int32 myClockStopDisplay;
447 
448     // Indicates color clocks when the frame was last updated
449     Int32 myClockAtLastUpdate;
450 
451     // Indicates how many color clocks remain until the end of
452     // current scanline.  This value is valid during the
453     // displayed portion of the frame.
454     Int32 myClocksToEndOfScanLine;
455 
456     // Indicates the total number of scanlines generated by the last frame
457     uInt32 myScanlineCountForLastFrame;
458 
459     // Indicates the maximum number of scanlines to be generated for a frame
460     uInt32 myMaximumNumberOfScanlines;
461 
462     // Indicates potentially the first scanline at which drawing occurs
463     uInt32 myStartScanline;
464 
465     // Color clock when VSYNC ending causes a new frame to be started
466     Int32 myVSYNCFinishClock;
467 
468     uInt8 myVSYNC;        // Holds the VSYNC register value
469     uInt8 myVBLANK;       // Holds the VBLANK register value
470 
471     uInt8 myNUSIZ0;       // Number and size of player 0 and missle 0
472     uInt8 myNUSIZ1;       // Number and size of player 1 and missle 1
473 
474     uInt8 myPlayfieldPriorityAndScore;
475     uInt8 myPriorityEncoder[2][256];
476     uInt8 myColor[8];
477     uInt8 myFixedColor[8];
478     uInt8* myColorPtr;
479 
480     uInt8 myCTRLPF;       // Playfield control register
481 
482     bool myREFP0;         // Indicates if player 0 is being reflected
483     bool myREFP1;         // Indicates if player 1 is being reflected
484 
485     uInt32 myPF;          // Playfield graphics (19-12:PF2 11-4:PF1 3-0:PF0)
486 
487     uInt8 myGRP0;         // Player 0 graphics register
488     uInt8 myGRP1;         // Player 1 graphics register
489 
490     uInt8 myDGRP0;        // Player 0 delayed graphics register
491     uInt8 myDGRP1;        // Player 1 delayed graphics register
492 
493     bool myENAM0;         // Indicates if missle 0 is enabled
494     bool myENAM1;         // Indicates if missle 1 is enabled
495 
496     bool myENABL;         // Indicates if the ball is enabled
497     bool myDENABL;        // Indicates if the vertically delayed ball is enabled
498 
499     uInt8 myHMP0;         // Player 0 horizontal motion register
500     uInt8 myHMP1;         // Player 1 horizontal motion register
501     uInt8 myHMM0;         // Missle 0 horizontal motion register
502     uInt8 myHMM1;         // Missle 1 horizontal motion register
503     uInt8 myHMBL;         // Ball horizontal motion register
504 
505     bool myVDELP0;        // Indicates if player 0 is being vertically delayed
506     bool myVDELP1;        // Indicates if player 1 is being vertically delayed
507     bool myVDELBL;        // Indicates if the ball is being vertically delayed
508 
509     bool myRESMP0;        // Indicates if missle 0 is reset to player 0
510     bool myRESMP1;        // Indicates if missle 1 is reset to player 1
511 
512     uInt16 myCollision;     // Collision register
513 
514     // Determines whether specified collisions are enabled or disabled
515     // The lower 16 bits are and'ed with the collision register to mask out
516     // any collisions we don't want to be processed
517     // The upper 16 bits are used to store which objects is currently
518     // enabled or disabled
519     // This is necessary since there are 15 collision combinations which
520     // are controlled by 6 objects
521     uInt32 myCollisionEnabledMask;
522 
523     // Note that these position registers contain the color clock
524     // on which the object's serial output should begin (0 to 159)
525     Int16 myPOSP0;        // Player 0 position register
526     Int16 myPOSP1;        // Player 1 position register
527     Int16 myPOSM0;        // Missle 0 position register
528     Int16 myPOSM1;        // Missle 1 position register
529     Int16 myPOSBL;        // Ball position register
530 
531     // The color clocks elapsed so far for each of the graphical objects,
532     // as denoted by 'MOTCK' line described in A. Towers TIA Hardware Notes
533     Int32 myMotionClockP0;
534     Int32 myMotionClockP1;
535     Int32 myMotionClockM0;
536     Int32 myMotionClockM1;
537     Int32 myMotionClockBL;
538 
539     // Indicates 'start' signal for each of the graphical objects as
540     // described in A. Towers TIA Hardware Notes
541     Int32 myStartP0;
542     Int32 myStartP1;
543     Int32 myStartM0;
544     Int32 myStartM1;
545 
546     // Index into the player mask arrays indicating whether display
547     // of the first copy should be suppressed
548     uInt8 mySuppressP0;
549     uInt8 mySuppressP1;
550 
551     // Latches for 'more motion required' as described in A. Towers TIA
552     // Hardware Notes
553     bool myHMP0mmr;
554     bool myHMP1mmr;
555     bool myHMM0mmr;
556     bool myHMM1mmr;
557     bool myHMBLmmr;
558 
559     // Graphics for Player 0 that should be displayed.  This will be
560     // reflected if the player is being reflected.
561     uInt8 myCurrentGRP0;
562 
563     // Graphics for Player 1 that should be displayed.  This will be
564     // reflected if the player is being reflected.
565     uInt8 myCurrentGRP1;
566 
567     // It's VERY important that the BL, M0, M1, P0 and P1 current
568     // mask pointers are always on a uInt32 boundary.  Otherwise,
569     // the TIA code will fail on a good number of CPUs.
570     const uInt8* myP0Mask;
571     const uInt8* myM0Mask;
572     const uInt8* myM1Mask;
573     const uInt8* myP1Mask;
574     const uInt8* myBLMask;
575     const uInt32* myPFMask;
576 
577     // Audio values; only used by TIADebug
578     uInt8 myAUDV0, myAUDV1, myAUDC0, myAUDC1, myAUDF0, myAUDF1;
579 
580     // Indicates when the dump for paddles was last set
581     Int32 myDumpDisabledCycle;
582 
583     // Indicates if the dump is current enabled for the paddles
584     bool myDumpEnabled;
585 
586     // Latches for INPT4 and INPT5
587     uInt8 myINPT4, myINPT5;
588 
589     // Indicates if HMOVE blanks are currently or previously enabled,
590     // and at which horizontal position the HMOVE was initiated
591     Int32 myCurrentHMOVEPos;
592     Int32 myPreviousHMOVEPos;
593     bool myHMOVEBlankEnabled;
594     bool myAllowHMOVEBlanks;
595 
596     // Indicates if unused TIA pins are randomly driven high or low
597     // Otherwise, they take on the value previously on the databus
598     bool myTIAPinsDriven;
599 
600     // Bitmap of the objects that should be considered while drawing
601     uInt8 myEnabledObjects;
602 
603     // Determines whether specified bits (from TIABit) are enabled or disabled
604     // This is and'ed with the enabled objects each scanline to mask out any
605     // objects we don't want to be processed
606     uInt8 myDisabledObjects;
607 
608     // Indicates if color loss should be enabled or disabled.  Color loss
609     // occurs on PAL (and maybe SECAM) systems when the previous frame
610     // contains an odd number of scanlines.
611     bool myColorLossEnabled;
612 
613     // Indicates whether we're done with the current frame. poke() clears this
614     // when VSYNC is strobed or the max scanlines/frame limit is hit.
615     bool myPartialFrameFlag;
616 
617     // Automatic framerate correction based on number of scanlines
618     bool myAutoFrameEnabled;
619 
620     // Number of total frames displayed by this TIA
621     uInt32 myFrameCounter;
622 
623     // Number of PAL frames displayed by this TIA
624     uInt32 myPALFrameCounter;
625 
626     // The framerate currently in use by the Console
627     float myFramerate;
628 
629     // Whether TIA bits/collisions are currently enabled/disabled
630     bool myBitsEnabled, myCollisionsEnabled;
631 
632   private:
633     // Copy constructor isn't supported by this class so make it private
634     TIA(const TIA&);
635 
636     // Assignment operator isn't supported by this class so make it private
637     TIA& operator = (const TIA&);
638 };
639 
640 #endif
641