diff --git a/src/debugger/Debugger.cxx b/src/debugger/Debugger.cxx index 5c0c5e834..57804b9ed 100644 --- a/src/debugger/Debugger.cxx +++ b/src/debugger/Debugger.cxx @@ -306,7 +306,7 @@ int Debugger::step() int cyc = mySystem.cycles(); unlockBankswitchState(); - myOSystem.console().tia().updateScanlineByStep(); + myOSystem.console().tia().updateScanlineByStep().flushLineCache(); lockBankswitchState(); return mySystem.cycles() - cyc; @@ -335,7 +335,7 @@ int Debugger::trace() int targetPC = myCpuDebug->pc() + 3; // return address unlockBankswitchState(); - myOSystem.console().tia().updateScanlineByTrace(targetPC); + myOSystem.console().tia().updateScanlineByTrace(targetPC).flushLineCache(); lockBankswitchState(); return mySystem.cycles() - cyc; @@ -411,6 +411,8 @@ void Debugger::nextScanline(int lines) --lines; } lockBankswitchState(); + + myOSystem.console().tia().flushLineCache(); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/tia/Background.cxx b/src/emucore/tia/Background.cxx index 65a4de825..ed3e127dc 100644 --- a/src/emucore/tia/Background.cxx +++ b/src/emucore/tia/Background.cxx @@ -16,6 +16,7 @@ //============================================================================ #include "Background.hxx" +#include "TIA.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Background::Background() @@ -33,6 +34,8 @@ void Background::reset() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Background::setColor(uInt8 color) { + if (color != myObjectColor) myTIA->flushLineCache(); + myObjectColor = color; applyColors(); } @@ -40,6 +43,7 @@ void Background::setColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Background::setDebugColor(uInt8 color) { + myTIA->flushLineCache(); myDebugColor = color; applyColors(); } @@ -47,6 +51,7 @@ void Background::setDebugColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Background::enableDebugColors(bool enabled) { + myTIA->flushLineCache(); myDebugEnabled = enabled; applyColors(); } diff --git a/src/emucore/tia/Background.hxx b/src/emucore/tia/Background.hxx index ae7bc8562..2d90994e2 100644 --- a/src/emucore/tia/Background.hxx +++ b/src/emucore/tia/Background.hxx @@ -21,12 +21,18 @@ #include "Serializable.hxx" #include "bspf.hxx" +class TIA; + class Background : public Serializable { public: Background(); public: + void setTIA(TIA* tia) { + myTIA = tia; + } + void reset(); void setColor(uInt8 color); @@ -50,6 +56,8 @@ class Background : public Serializable uInt8 myObjectColor, myDebugColor; bool myDebugEnabled; + TIA* myTIA; + private: Background(const Background&) = delete; Background(Background&&) = delete; diff --git a/src/emucore/tia/Ball.cxx b/src/emucore/tia/Ball.cxx index 1f0f57424..720e363a0 100644 --- a/src/emucore/tia/Ball.cxx +++ b/src/emucore/tia/Ball.cxx @@ -16,6 +16,7 @@ //============================================================================ #include "Ball.hxx" +#include "TIA.hxx" enum Count: Int8 { renderCounterOffset = -4 @@ -56,8 +57,14 @@ void Ball::reset() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::enabl(uInt8 value) { + const auto enabledNewOldValue = myIsEnabledNew; + myIsEnabledNew = (value & 0x02) > 0; - updateEnabled(); + + if (myIsEnabledNew != enabledNewOldValue && !myIsDelaying) { + myTIA->flushLineCache(); + updateEnabled(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -80,14 +87,25 @@ void Ball::ctrlpf(uInt8 value) { static constexpr uInt8 ourWidths[] = {1, 2, 4, 8}; - myWidth = ourWidths[(value & 0x30) >> 4]; + const uInt8 newWidth = ourWidths[(value & 0x30) >> 4]; + + if (newWidth != myWidth) { + myTIA->flushLineCache(); + myWidth = newWidth; + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::vdelbl(uInt8 value) { + const auto oldIsDelaying = myIsDelaying; + myIsDelaying = (value & 0x01) > 0; - updateEnabled(); + + if (oldIsDelaying != myIsDelaying) { + myTIA->flushLineCache(); + updateEnabled(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -106,6 +124,8 @@ void Ball::toggleEnabled(bool enabled) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::setColor(uInt8 color) { + if (color != myObjectColor && myIsEnabled) myTIA->flushLineCache(); + myObjectColor = color; applyColors(); } @@ -113,6 +133,8 @@ void Ball::setColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::setDebugColor(uInt8 color) { + myTIA->flushLineCache(); + myDebugColor = color; applyColors(); } @@ -120,6 +142,8 @@ void Ball::setDebugColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::enableDebugColors(bool enabled) { + myTIA->flushLineCache(); + myDebugEnabled = enabled; applyColors(); } @@ -137,25 +161,18 @@ bool Ball::movementTick(uInt32 clock, bool apply) if (clock == myHmmClocks) myIsMoving = false; - if (myIsMoving && apply) { - render(); - tick(false); - } + if (myIsMoving && apply) tick(false); return myIsMoving; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Ball::render() +void Ball::tick(bool isReceivingMclock) { collision = (myIsRendering && myRenderCounter >= 0 && myIsEnabled) ? myCollisionMaskEnabled : myCollisionMaskDisabled; -} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Ball::tick(bool isReceivingMclock) -{ bool starfieldEffect = myIsMoving && isReceivingMclock; if (myCounter == 156) { @@ -189,8 +206,14 @@ void Ball::tick(bool isReceivingMclock) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::shuffleStatus() { + const auto oldIsEnabledOld = myIsEnabledOld; + myIsEnabledOld = myIsEnabledNew; - updateEnabled(); + + if (myIsEnabledOld != oldIsEnabledOld && myIsDelaying) { + myTIA->flushLineCache(); + updateEnabled(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -209,13 +232,15 @@ void Ball::applyColors() uInt8 Ball::getPosition() const { // Mind the sign of renderCounterOffset: it's defined negative above - return (316 - myCounter - Count::renderCounterOffset + myPlayfieldPositionProvider->getPosition()) % 160; + return (316 - myCounter - Count::renderCounterOffset + myTIA->getPosition()) % 160; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Ball::setPosition(uInt8 newPosition) { - myCounter = (316 - newPosition - Count::renderCounterOffset + myPlayfieldPositionProvider->getPosition()) % 160; + myTIA->flushLineCache(); + + myCounter = (316 - newPosition - Count::renderCounterOffset + myTIA->getPosition()) % 160; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/tia/Ball.hxx b/src/emucore/tia/Ball.hxx index a00faacc6..eef249b6e 100644 --- a/src/emucore/tia/Ball.hxx +++ b/src/emucore/tia/Ball.hxx @@ -20,7 +20,8 @@ #include "Serializable.hxx" #include "bspf.hxx" -#include "PlayfieldPositionProvider.hxx" + +class TIA; class Ball : public Serializable { @@ -30,8 +31,8 @@ class Ball : public Serializable public: - void setPlayfieldPositionProvider(PlayfieldPositionProvider* playfieldPositionProvider) { - myPlayfieldPositionProvider = playfieldPositionProvider; + void setTIA(TIA* tia) { + myTIA = tia; } void reset(); @@ -59,8 +60,6 @@ class Ball : public Serializable bool movementTick(uInt32 clock, bool apply); - void render(); - void tick(bool isReceivingMclock = true); uInt8 getPixel(uInt8 colorIn) const { @@ -113,7 +112,7 @@ class Ball : public Serializable bool myIsRendering; Int8 myRenderCounter; - PlayfieldPositionProvider* myPlayfieldPositionProvider; + TIA* myTIA; private: Ball() = delete; diff --git a/src/emucore/tia/Missile.cxx b/src/emucore/tia/Missile.cxx index 62cf1bea1..bea6a6b9e 100644 --- a/src/emucore/tia/Missile.cxx +++ b/src/emucore/tia/Missile.cxx @@ -17,6 +17,7 @@ #include "Missile.hxx" #include "DrawCounterDecodes.hxx" +#include "TIA.hxx" enum Count: Int8 { renderCounterOffset = -4 @@ -57,7 +58,12 @@ void Missile::reset() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Missile::enam(uInt8 value) { + const auto oldEnam = myEnam; + myEnam = (value & 0x02) > 0; + + if (oldEnam != myEnam) myTIA->flushLineCache(); + updateEnabled(); } @@ -109,8 +115,9 @@ void Missile::resmp(uInt8 value, const Player& player) { const uInt8 resmp = value & 0x02; - if (resmp == myResmp) - return; + if (resmp == myResmp) return; + + myTIA->flushLineCache(); myResmp = resmp; @@ -159,28 +166,21 @@ bool Missile::movementTick(uInt8 clock, uInt8 hclock, bool apply) if (clock == myHmmClocks) myIsMoving = false; - if (myIsMoving && apply) { - render(hclock); - tick(hclock); - } + if (myIsMoving && apply) tick(hclock); return myIsMoving; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Missile::render(uInt8 hclock) +void Missile::tick(uInt8 hclock) { - bool render = + const bool render = myIsRendering && (myRenderCounter >= 0 || (myIsMoving && myRenderCounter == -1 && myWidth < 4 && ((hclock + 1) % 4 == 3))) && myIsEnabled; collision = render ? myCollisionMaskEnabled : myCollisionMaskDisabled; -} -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Missile::tick(uInt8 hclock) -{ if (myDecodes[myCounter] && !myResmp) { myIsRendering = true; myRenderCounter = Count::renderCounterOffset; @@ -213,6 +213,8 @@ void Missile::tick(uInt8 hclock) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Missile::setColor(uInt8 color) { + if (color != myObjectColor && myIsEnabled) myTIA->flushLineCache(); + myObjectColor = color; applyColors(); } @@ -220,6 +222,7 @@ void Missile::setColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Missile::setDebugColor(uInt8 color) { + myTIA->flushLineCache(); myDebugColor = color; applyColors(); } @@ -247,13 +250,14 @@ void Missile::applyColors() uInt8 Missile::getPosition() const { // Mind the sign of renderCounterOffset: it's defined negative above - return (316 - myCounter - Count::renderCounterOffset + myPlayfieldPositionProvider->getPosition()) % 160; + return (316 - myCounter - Count::renderCounterOffset + myTIA->getPosition()) % 160; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Missile::setPosition(uInt8 newPosition) { - myCounter = (316 - newPosition - Count::renderCounterOffset + myPlayfieldPositionProvider->getPosition()) % 160; + myTIA->flushLineCache(); + myCounter = (316 - newPosition - Count::renderCounterOffset + myTIA->getPosition()) % 160; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/tia/Missile.hxx b/src/emucore/tia/Missile.hxx index be3154959..cadf7208d 100644 --- a/src/emucore/tia/Missile.hxx +++ b/src/emucore/tia/Missile.hxx @@ -21,7 +21,8 @@ #include "Serializable.hxx" #include "bspf.hxx" #include "Player.hxx" -#include "PlayfieldPositionProvider.hxx" + +class TIA; class Missile : public Serializable { @@ -31,8 +32,8 @@ class Missile : public Serializable public: - void setPlayfieldPositionProvider(PlayfieldPositionProvider* playfieldPositionProvider) { - myPlayfieldPositionProvider = playfieldPositionProvider; + void setTIA(TIA* tia) { + myTIA = tia; } void reset(); @@ -51,8 +52,6 @@ class Missile : public Serializable bool movementTick(uInt8 clock, uInt8 hclock, bool apply); - void render(uInt8 hclock); - void tick(uInt8 hclock); void setColor(uInt8 color); @@ -114,7 +113,7 @@ class Missile : public Serializable uInt8 myObjectColor, myDebugColor; bool myDebugEnabled; - PlayfieldPositionProvider *myPlayfieldPositionProvider; + TIA *myTIA; private: Missile(const Missile&) = delete; diff --git a/src/emucore/tia/Player.cxx b/src/emucore/tia/Player.cxx index 898461d32..a12871eba 100644 --- a/src/emucore/tia/Player.cxx +++ b/src/emucore/tia/Player.cxx @@ -17,6 +17,7 @@ #include "Player.hxx" #include "DrawCounterDecodes.hxx" +#include "TIA.hxx" enum Count: Int8 { renderCounterOffset = -5, @@ -63,7 +64,10 @@ void Player::grp(uInt8 pattern) myPatternNew = pattern; - if (!myIsDelaying && myPatternNew != oldPatternNew) updatePattern(); + if (!myIsDelaying && myPatternNew != oldPatternNew) { + myTIA->flushLineCache(); + updatePattern(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -179,7 +183,10 @@ void Player::refp(uInt8 value) myIsReflected = (value & 0x08) > 0; - if (oldIsReflected != myIsReflected) updatePattern(); + if (oldIsReflected != myIsReflected) { + myTIA->flushLineCache(); + updatePattern(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -189,7 +196,10 @@ void Player::vdelp(uInt8 value) myIsDelaying = (value & 0x01) > 0; - if (oldIsDelaying != myIsDelaying) updatePattern(); + if (oldIsDelaying != myIsDelaying) { + myTIA->flushLineCache(); + updatePattern(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -211,6 +221,8 @@ void Player::toggleCollisions(bool enabled) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Player::setColor(uInt8 color) { + if (color != myObjectColor && myPattern) myTIA->flushLineCache(); + myObjectColor = color; applyColors(); } @@ -218,6 +230,8 @@ void Player::setColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Player::setDebugColor(uInt8 color) { + myTIA->flushLineCache(); + myDebugColor = color; applyColors(); } @@ -225,6 +239,8 @@ void Player::setDebugColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Player::enableDebugColors(bool enabled) { + myTIA->flushLineCache(); + myDebugEnabled = enabled; applyColors(); } @@ -242,28 +258,19 @@ bool Player::movementTick(uInt32 clock, bool apply) myIsMoving = false; } - if (myIsMoving && apply) { - render(); - tick(); - } + if (myIsMoving && apply) tick(); return myIsMoving; } -// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void Player::render() -{ - if (!myIsRendering || myRenderCounter < myRenderCounterTripPoint) { - collision = myCollisionMaskDisabled; - return; - } - - collision = (myPattern & (1 << mySampleCounter)) ? myCollisionMaskEnabled : myCollisionMaskDisabled; -} - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Player::tick() { + if (!myIsRendering || myRenderCounter < myRenderCounterTripPoint) + collision = myCollisionMaskDisabled; + else + collision = (myPattern & (1 << mySampleCounter)) ? myCollisionMaskEnabled : myCollisionMaskDisabled; + if (myDecodes[myCounter]) { myIsRendering = true; mySampleCounter = 0; @@ -304,7 +311,10 @@ void Player::shufflePatterns() myPatternOld = myPatternNew; - if (myIsDelaying && myPatternOld != oldPatternOld) updatePattern(); + if (myIsDelaying && myPatternOld != oldPatternOld) { + myTIA->flushLineCache(); + updatePattern(); + } } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -369,15 +379,17 @@ uInt8 Player::getPosition() const const uInt8 shift = myDivider == 1 ? 0 : 1; // Mind the sign of renderCounterOffset: it's defined negative above - return (316 - myCounter - Count::renderCounterOffset + shift + myPlayfieldPositionProvider->getPosition()) % 160; + return (316 - myCounter - Count::renderCounterOffset + shift + myTIA->getPosition()) % 160; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Player::setPosition(uInt8 newPosition) { + myTIA->flushLineCache(); + const uInt8 shift = myDivider == 1 ? 0 : 1; - myCounter = (316 - newPosition - Count::renderCounterOffset + shift + myPlayfieldPositionProvider->getPosition()) % 160; + myCounter = (316 - newPosition - Count::renderCounterOffset + shift + myTIA->getPosition()) % 160; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/emucore/tia/Player.hxx b/src/emucore/tia/Player.hxx index 44d4cda71..41983d960 100644 --- a/src/emucore/tia/Player.hxx +++ b/src/emucore/tia/Player.hxx @@ -20,7 +20,8 @@ #include "bspf.hxx" #include "Serializable.hxx" -#include "PlayfieldPositionProvider.hxx" + +class TIA; class Player : public Serializable { @@ -29,8 +30,8 @@ class Player : public Serializable public: - void setPlayfieldPositionProvider(PlayfieldPositionProvider* playfieldPositionProvider) { - myPlayfieldPositionProvider = playfieldPositionProvider; + void setTIA(TIA* tia) { + myTIA = tia; } void reset(); @@ -60,8 +61,6 @@ class Player : public Serializable bool movementTick(uInt32 clock, bool apply); - void render(); - void tick(); uInt8 getClock() const { return myCounter; } @@ -126,7 +125,7 @@ class Player : public Serializable bool myIsReflected; bool myIsDelaying; - PlayfieldPositionProvider *myPlayfieldPositionProvider; + TIA* myTIA; private: Player(const Player&) = delete; diff --git a/src/emucore/tia/Playfield.cxx b/src/emucore/tia/Playfield.cxx index 5d4eb6995..e7bc7d3b5 100644 --- a/src/emucore/tia/Playfield.cxx +++ b/src/emucore/tia/Playfield.cxx @@ -16,6 +16,7 @@ //============================================================================ #include "Playfield.hxx" +#include "TIA.hxx" // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Playfield::Playfield(uInt32 collisionMask) @@ -52,8 +53,12 @@ void Playfield::reset() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::pf0(uInt8 value) { + if (myPf0 == value >> 4) return; + + myTIA->flushLineCache(); + myPattern = (myPattern & 0x000FFFF0) | (value >> 4); - myPf0 = value; + myPf0 = value >> 4; updatePattern(); } @@ -61,6 +66,10 @@ void Playfield::pf0(uInt8 value) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::pf1(uInt8 value) { + if (myPf1 == value) return; + + myTIA->flushLineCache(); + myPattern = (myPattern & 0x000FF00F) | ((value & 0x80) >> 3) | ((value & 0x40) >> 1) @@ -78,6 +87,10 @@ void Playfield::pf1(uInt8 value) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::pf2(uInt8 value) { + if (myPf2 == value) return; + + myTIA->flushLineCache(); + myPattern = (myPattern & 0x00000FFF) | (value << 12); myPf2 = value; @@ -87,8 +100,15 @@ void Playfield::pf2(uInt8 value) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::ctrlpf(uInt8 value) { - myReflected = (value & 0x01) > 0; - myColorMode = (value & 0x06) == 0x02 ? ColorMode::score : ColorMode::normal; + const bool reflected = (value & 0x01) > 0; + const ColorMode colorMode = (value & 0x06) == 0x02 ? ColorMode::score : ColorMode::normal; + + if (myReflected == reflected && myColorMode == colorMode) return; + + myTIA->flushLineCache(); + + myReflected = reflected; + myColorMode = colorMode; applyColors(); } @@ -109,6 +129,8 @@ void Playfield::toggleCollisions(bool enabled) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::setColor(uInt8 color) { + if (color != myObjectColor && myColorMode == ColorMode::normal) myTIA->flushLineCache(); + myObjectColor = color; applyColors(); } @@ -116,6 +138,8 @@ void Playfield::setColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::setColorP0(uInt8 color) { + if (color != myColorP0 && myColorMode == ColorMode::score) myTIA->flushLineCache(); + myColorP0 = color; applyColors(); } @@ -123,6 +147,8 @@ void Playfield::setColorP0(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::setColorP1(uInt8 color) { + if (color != myColorP1 && myColorMode == ColorMode::score) myTIA->flushLineCache(); + myColorP1 = color; applyColors(); } @@ -130,6 +156,7 @@ void Playfield::setColorP1(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::setDebugColor(uInt8 color) { + myTIA->flushLineCache(); myDebugColor = color; applyColors(); } @@ -137,6 +164,7 @@ void Playfield::setDebugColor(uInt8 color) // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void Playfield::enableDebugColors(bool enabled) { + myTIA->flushLineCache(); myDebugEnabled = enabled; applyColors(); } diff --git a/src/emucore/tia/Playfield.hxx b/src/emucore/tia/Playfield.hxx index 22c71843e..8e656b426 100644 --- a/src/emucore/tia/Playfield.hxx +++ b/src/emucore/tia/Playfield.hxx @@ -21,6 +21,8 @@ #include "Serializable.hxx" #include "bspf.hxx" +class TIA; + class Playfield : public Serializable { public: @@ -28,6 +30,10 @@ class Playfield : public Serializable public: + void setTIA(TIA* tia) { + myTIA = tia; + } + void reset(); void pf0(uInt8 value); @@ -105,6 +111,8 @@ class Playfield : public Serializable uInt32 myX; + TIA* myTIA; + private: Playfield() = delete; Playfield(const Playfield&) = delete; diff --git a/src/emucore/tia/PlayfieldPositionProvider.hxx b/src/emucore/tia/PlayfieldPositionProvider.hxx deleted file mode 100644 index a6c4d5ea6..000000000 --- a/src/emucore/tia/PlayfieldPositionProvider.hxx +++ /dev/null @@ -1,43 +0,0 @@ -//============================================================================ -// -// SSSS tt lll lll -// SS SS tt ll ll -// SS tttttt eeee ll ll aaaa -// SSSS tt ee ee ll ll aa -// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" -// SS SS tt ee ll ll aa aa -// SSSS ttt eeeee llll llll aaaaa -// -// Copyright (c) 1995-2017 by Bradford W. Mott, Stephen Anthony -// and the Stella Team -// -// See the file "License.txt" for information on usage and redistribution of -// this file, and for a DISCLAIMER OF ALL WARRANTIES. -//============================================================================ - -#ifndef TIA_PLAYFIELD_PROVIDER -#define TIA_PLAYFIELD_PROVIDER - -#include "bspf.hxx" - -/** - This is an abstract interface class that provides a subset of TIA - functionality for sprite positioning while avoiding circular dependencies - between TIA and sprites. - - @author Christian Speckner (DirtyHairy) and Stephen Anthony -*/ -class PlayfieldPositionProvider -{ - public: - /** - Get the current x value - */ - virtual uInt8 getPosition() const = 0; - - protected: - ~PlayfieldPositionProvider() = default; - -}; - -#endif // TIA_POSITIONING_PROVIDER diff --git a/src/emucore/tia/TIA.cxx b/src/emucore/tia/TIA.cxx index 93bda91ef..1cfc66933 100644 --- a/src/emucore/tia/TIA.cxx +++ b/src/emucore/tia/TIA.cxx @@ -95,11 +95,13 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings) myTIAPinsDriven = mySettings.getBool("tiadriven"); - myPlayer0.setPlayfieldPositionProvider(this); - myPlayer1.setPlayfieldPositionProvider(this); - myMissile0.setPlayfieldPositionProvider(this); - myMissile1.setPlayfieldPositionProvider(this); - myBall.setPlayfieldPositionProvider(this); + myBackground.setTIA(this); + myPlayfield.setTIA(this); + myPlayer0.setTIA(this); + myPlayer1.setTIA(this); + myMissile0.setTIA(this); + myMissile1.setTIA(this); + myBall.setTIA(this); reset(); } @@ -114,7 +116,6 @@ void TIA::reset() myMovementClock = 0; myPriority = Priority::normal; myHstate = HState::blank; - myIsFreshLine = true; myCollisionMask = 0; myLinesSinceChange = 0; myCollisionUpdateRequired = false; @@ -223,7 +224,6 @@ bool TIA::save(Serializer& out) const out.putBool(myTIAPinsDriven); out.putInt(int(myHstate)); - out.putBool(myIsFreshLine); out.putInt(myHblankCtr); out.putInt(myHctr); @@ -293,7 +293,6 @@ bool TIA::load(Serializer& in) myTIAPinsDriven = in.getBool(); myHstate = HState(in.getInt()); - myIsFreshLine = in.getBool(); myHblankCtr = in.getInt(); myHctr = in.getInt(); @@ -443,6 +442,7 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case RSYNC: + flushLineCache(); applyRsync(); myShadowRegisters[address] = value; break; @@ -496,13 +496,11 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case COLUBK: - myLinesSinceChange = 0; myBackground.setColor(value & 0xFE); myShadowRegisters[address] = value; break; case COLUP0: - myLinesSinceChange = 0; value &= 0xFE; myPlayfield.setColorP0(value); myMissile0.setColor(value); @@ -511,7 +509,6 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case COLUP1: - myLinesSinceChange = 0; value &= 0xFE; myPlayfield.setColorP1(value); myMissile1.setColor(value); @@ -520,7 +517,7 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case CTRLPF: - myLinesSinceChange = 0; + flushLineCache(); myPriority = (value & 0x04) ? Priority::pfp : (value & 0x02) ? Priority::score : Priority::normal; myPlayfield.ctrlpf(value); @@ -529,7 +526,7 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case COLUPF: - myLinesSinceChange = 0; + flushLineCache(); value &= 0xFE; myPlayfield.setColor(value); myBall.setColor(value); @@ -578,38 +575,36 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case RESM0: - myLinesSinceChange = 0; + flushLineCache(); myMissile0.resm(resxCounter(), myHstate == HState::blank); myShadowRegisters[address] = value; break; case RESM1: - myLinesSinceChange = 0; + flushLineCache(); myMissile1.resm(resxCounter(), myHstate == HState::blank); myShadowRegisters[address] = value; break; case RESMP0: - myLinesSinceChange = 0; myMissile0.resmp(value, myPlayer0); myShadowRegisters[address] = value; break; case RESMP1: - myLinesSinceChange = 0; myMissile1.resmp(value, myPlayer1); myShadowRegisters[address] = value; break; case NUSIZ0: - myLinesSinceChange = 0; + flushLineCache(); myMissile0.nusiz(value); myPlayer0.nusiz(value, myHstate == HState::blank); myShadowRegisters[address] = value; break; case NUSIZ1: - myLinesSinceChange = 0; + flushLineCache(); myMissile1.nusiz(value); myPlayer1.nusiz(value, myHstate == HState::blank); myShadowRegisters[address] = value; @@ -653,13 +648,13 @@ bool TIA::poke(uInt16 address, uInt8 value) } case RESP0: - myLinesSinceChange = 0; + flushLineCache(); myPlayer0.resp(resxCounter()); myShadowRegisters[address] = value; break; case RESP1: - myLinesSinceChange = 0; + flushLineCache(); myPlayer1.resp(resxCounter()); myShadowRegisters[address] = value; break; @@ -673,13 +668,11 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case VDELP0: - myLinesSinceChange = 0; myPlayer0.vdelp(value); myShadowRegisters[address] = value; break; case VDELP1: - myLinesSinceChange = 0; myPlayer1.vdelp(value); myShadowRegisters[address] = value; break; @@ -697,13 +690,12 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case RESBL: - myLinesSinceChange = 0; + flushLineCache(); myBall.resbl(resxCounter()); myShadowRegisters[address] = value; break; case VDELBL: - myLinesSinceChange = 0; myBall.vdelbl(value); myShadowRegisters[address] = value; break; @@ -713,7 +705,7 @@ bool TIA::poke(uInt16 address, uInt8 value) break; case CXCLR: - myLinesSinceChange = 0; + flushLineCache(); myCollisionMask = 0; myShadowRegisters[address] = value; break; @@ -913,27 +905,33 @@ void TIA::setJitterRecoveryFactor(Int32 f) } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TIA::updateScanline() +TIA& TIA::updateScanline() { // Update frame by one scanline at a time uInt32 line = scanlines(); while (line == scanlines()) updateScanlineByStep(); + + return *this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TIA::updateScanlineByStep() +TIA& TIA::updateScanlineByStep() { // Update frame by one CPU instruction/color clock mySystem->m6502().execute(1); updateEmulation(); + + return *this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TIA::updateScanlineByTrace(int target) +TIA& TIA::updateScanlineByTrace(int target) { while (mySystem->m6502().getPC() != target) updateScanlineByStep(); + + return *this; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1005,18 +1003,20 @@ void TIA::cycle(uInt32 colorClocks) myCollisionUpdateRequired = false; - tickMovement(); + if (myLinesSinceChange < 2) { + tickMovement(); - if (myHstate == HState::blank) - tickHblank(); - else - tickHframe(); + if (myHstate == HState::blank) + tickHblank(); + else + tickHframe(); + + if (myCollisionUpdateRequired) updateCollision(); + } if (++myHctr >= 228) nextLine(); - if (myCollisionUpdateRequired) updateCollision(); - myTimestamp++; } } @@ -1027,8 +1027,6 @@ void TIA::tickMovement() if (!myMovementInProgress) return; if ((myHctr & 0x03) == 0) { - myLinesSinceChange = 0; - const bool apply = myHstate == HState::blank; bool m = false; uInt8 movementCounter = myMovementClock > 15 ? 0 : myMovementClock; @@ -1049,9 +1047,8 @@ void TIA::tickMovement() // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TIA::tickHblank() { - if (myIsFreshLine) { + if (myHctr == 0) { myHblankCtr = 0; - myIsFreshLine = false; } if (++myHblankCtr >= 68) myHstate = HState::frame; @@ -1061,23 +1058,11 @@ void TIA::tickHblank() void TIA::tickHframe() { const uInt32 y = myFrameManager.getY(); - const bool lineNotCached = myLinesSinceChange < 2 || y == 0; const uInt32 x = myHctr - 68 - myXDelta; - myCollisionUpdateRequired = lineNotCached; + myCollisionUpdateRequired = true; myPlayfield.tick(x); - - // Render sprites - if (lineNotCached) { - myPlayer0.render(); - myPlayer1.render(); - myMissile0.render(myHctr); - myMissile1.render(myHctr); - myBall.render(); - } - - // Tick sprites myMissile0.tick(myHctr); myMissile1.tick(myHctr); myPlayer0.tick(); @@ -1085,7 +1070,7 @@ void TIA::tickHframe() myBall.tick(); if (myFrameManager.isRendering()) - renderPixel(x, y, lineNotCached); + renderPixel(x, y); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1097,22 +1082,39 @@ void TIA::applyRsync() if (myFrameManager.isRendering()) memset(myCurrentFrameBuffer.get() + myFrameManager.getY() * 160 + x, 0, 160 - x); - myLinesSinceChange = 0; myHctr = 225; } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - void TIA::nextLine() { + if (myLinesSinceChange >= 2) { + cloneLastLine(); + } + myHctr = 0; - myLinesSinceChange++; + + if (!myMovementInProgress && myLinesSinceChange < 2) myLinesSinceChange++; myHstate = HState::blank; - myIsFreshLine = true; myExtendedHblank = false; myXDelta = 0; myFrameManager.nextLine(); + + if (myFrameManager.isRendering() && myFrameManager.getY() == 0) flushLineCache(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::cloneLastLine() +{ + const auto y = myFrameManager.getY(); + + if (!myFrameManager.isRendering() || y == 0) return; + + uInt8* buffer = myCurrentFrameBuffer.get(); + + memcpy(buffer + y * 160, buffer + (y-1) * 160, 160); } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -1129,58 +1131,73 @@ void TIA::updateCollision() } // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -void TIA::renderPixel(uInt32 x, uInt32 y, bool lineNotCached) +void TIA::renderPixel(uInt32 x, uInt32 y) { if (x >= 160) return; - if (lineNotCached) { - uInt8 color = myBackground.getColor(); + uInt8 color = myBackground.getColor(); - switch (myPriority) - { - // Playfield has priority so ScoreBit isn't used - // Priority from highest to lowest: - // BL/PF => P0/M0 => P1/M1 => BK - case Priority::pfp: // CTRLPF D2=1, D1=ignored - color = myMissile1.getPixel(color); - color = myPlayer1.getPixel(color); - color = myMissile0.getPixel(color); - color = myPlayer0.getPixel(color); - color = myPlayfield.getPixel(color); - color = myBall.getPixel(color); - break; + switch (myPriority) + { + // Playfield has priority so ScoreBit isn't used + // Priority from highest to lowest: + // BL/PF => P0/M0 => P1/M1 => BK + case Priority::pfp: // CTRLPF D2=1, D1=ignored + color = myMissile1.getPixel(color); + color = myPlayer1.getPixel(color); + color = myMissile0.getPixel(color); + color = myPlayer0.getPixel(color); + color = myPlayfield.getPixel(color); + color = myBall.getPixel(color); + break; - case Priority::score: // CTRLPF D2=0, D1=1 - // Formally we have (priority from highest to lowest) - // PF/P0/M0 => P1/M1 => BL => BK - // for the first half and - // P0/M0 => PF/P1/M1 => BL => BK - // for the second half. However, the first ordering is equivalent - // to the second (PF has the same color as P0/M0), so we can just - // write - color = myBall.getPixel(color); - color = myMissile1.getPixel(color); - color = myPlayer1.getPixel(color); - color = myPlayfield.getPixel(color); - color = myMissile0.getPixel(color); - color = myPlayer0.getPixel(color); - break; + case Priority::score: // CTRLPF D2=0, D1=1 + // Formally we have (priority from highest to lowest) + // PF/P0/M0 => P1/M1 => BL => BK + // for the first half and + // P0/M0 => PF/P1/M1 => BL => BK + // for the second half. However, the first ordering is equivalent + // to the second (PF has the same color as P0/M0), so we can just + // write + color = myBall.getPixel(color); + color = myMissile1.getPixel(color); + color = myPlayer1.getPixel(color); + color = myPlayfield.getPixel(color); + color = myMissile0.getPixel(color); + color = myPlayer0.getPixel(color); + break; - // Priority from highest to lowest: - // P0/M0 => P1/M1 => BL/PF => BK - case Priority::normal: // CTRLPF D2=0, D1=0 - color = myPlayfield.getPixel(color); - color = myBall.getPixel(color); - color = myMissile1.getPixel(color); - color = myPlayer1.getPixel(color); - color = myMissile0.getPixel(color); - color = myPlayer0.getPixel(color); - break; + // Priority from highest to lowest: + // P0/M0 => P1/M1 => BL/PF => BK + case Priority::normal: // CTRLPF D2=0, D1=0 + color = myPlayfield.getPixel(color); + color = myBall.getPixel(color); + color = myMissile1.getPixel(color); + color = myPlayer1.getPixel(color); + color = myMissile0.getPixel(color); + color = myPlayer0.getPixel(color); + break; + } + + myCurrentFrameBuffer.get()[y * 160 + x] = myFrameManager.vblank() ? 0 : color; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::flushLineCache() +{ + const bool wasCaching = myLinesSinceChange >= 2; + + myLinesSinceChange = 0; + + if (wasCaching) { + const auto rewindCycles = myHctr; + + for (myHctr = 0; myHctr < rewindCycles; myHctr++) { + if (myHstate == HState::blank) + tickHblank(); + else + tickHframe(); } - - myCurrentFrameBuffer.get()[y * 160 + x] = myFrameManager.vblank() ? 0 : color; - } else { - myCurrentFrameBuffer.get()[y * 160 + x] = myCurrentFrameBuffer.get()[(y-1) * 160 + x]; } } @@ -1201,12 +1218,12 @@ void TIA::delayedWrite(uInt8 address, uInt8 value) switch (address) { case VBLANK: - myLinesSinceChange = 0; + flushLineCache(); myFrameManager.setVblank(value & 0x02); break; case HMOVE: - myLinesSinceChange = 0; + flushLineCache(); myMovementClock = 0; myMovementInProgress = true; @@ -1225,32 +1242,26 @@ void TIA::delayedWrite(uInt8 address, uInt8 value) break; case PF0: - myLinesSinceChange = 0; myPlayfield.pf0(value); break; case PF1: - myLinesSinceChange = 0; myPlayfield.pf1(value); break; case PF2: - myLinesSinceChange = 0; myPlayfield.pf2(value); break; case HMM0: - myLinesSinceChange = 0; myMissile0.hmm(value); break; case HMM1: - myLinesSinceChange = 0; myMissile1.hmm(value); break; case HMCLR: - myLinesSinceChange = 0; myMissile0.hmm(0); myMissile1.hmm(0); myPlayer0.hmp(0); @@ -1259,67 +1270,54 @@ void TIA::delayedWrite(uInt8 address, uInt8 value) break; case GRP0: - myLinesSinceChange = 0; myPlayer0.grp(value); break; case GRP1: - myLinesSinceChange = 0; myPlayer1.grp(value); break; case DummyRegisters::shuffleP0: - myLinesSinceChange = 0; myPlayer0.shufflePatterns(); break; case DummyRegisters::shuffleP1: - myLinesSinceChange = 0; myPlayer1.shufflePatterns(); break; case DummyRegisters::shuffleBL: - myLinesSinceChange = 0; myBall.shuffleStatus(); break; case HMP0: - myLinesSinceChange = 0; myPlayer0.hmp(value); break; case HMP1: - myLinesSinceChange = 0; myPlayer1.hmp(value); break; case HMBL: - myLinesSinceChange = 0; myBall.hmbl(value); break; case REFP0: - myLinesSinceChange = 0; myPlayer0.refp(value); break; case REFP1: - myLinesSinceChange = 0; myPlayer1.refp(value); break; case ENABL: - myLinesSinceChange = 0; myBall.enabl(value); break; case ENAM0: - myLinesSinceChange = 0; myMissile0.enam(value); break; case ENAM1: - myLinesSinceChange = 0; myMissile1.enam(value); break; } diff --git a/src/emucore/tia/TIA.hxx b/src/emucore/tia/TIA.hxx index 26eb60384..2a027ea0f 100644 --- a/src/emucore/tia/TIA.hxx +++ b/src/emucore/tia/TIA.hxx @@ -35,7 +35,6 @@ #include "Ball.hxx" #include "LatchedInput.hxx" #include "PaddleReader.hxx" -#include "PlayfieldPositionProvider.hxx" /** This class is a device that emulates the Television Interface Adaptor @@ -50,7 +49,7 @@ @author Christian Speckner (DirtyHairy) and Stephen Anthony */ -class TIA : public Device, public PlayfieldPositionProvider +class TIA : public Device { public: friend class TIADebug; @@ -295,19 +294,19 @@ class TIA : public Device, public PlayfieldPositionProvider /** This method should be called to update the TIA with a new scanline. */ - void updateScanline(); + TIA& updateScanline(); /** This method should be called to update the TIA with a new partial scanline by stepping one CPU instruction. */ - void updateScanlineByStep(); + TIA& updateScanlineByStep(); /** This method should be called to update the TIA with a new partial scanline by tracing to target address. */ - void updateScanlineByTrace(int target); + TIA& updateScanlineByTrace(int target); /** Retrieve the last value written to a certain register @@ -317,10 +316,16 @@ class TIA : public Device, public PlayfieldPositionProvider /** Get the current x value */ - uInt8 getPosition() const override { + uInt8 getPosition() const { return (myHctr < 68) ? 0 : (myHctr - 68 - myXDelta); } + /** + Flush the line cache after an externally triggered state change + (e.g. a register write) + */ + void flushLineCache(); + /** Save the current state of this device to the given Serializer. @@ -388,12 +393,14 @@ class TIA : public Device, public PlayfieldPositionProvider void updateCollision(); - void renderPixel(uInt32 x, uInt32 y, bool lineNotCached); + void renderPixel(uInt32 x, uInt32 y); void clearHmoveComb(); void nextLine(); + void cloneLastLine(); + void delayedWrite(uInt8 address, uInt8 value); void updatePaddle(uInt8 idx); @@ -442,7 +449,6 @@ class TIA : public Device, public PlayfieldPositionProvider bool myTIAPinsDriven; HState myHstate; - bool myIsFreshLine; Int32 myHblankCtr; Int32 myHctr;