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