Port improved line caching from 6502.ts .

This commit is contained in:
Christian Speckner 2017-04-23 23:37:30 +02:00
parent 38deb5b1cf
commit 24cb2417fc
13 changed files with 281 additions and 239 deletions

View File

@ -16,6 +16,7 @@
//============================================================================ //============================================================================
#include "Background.hxx" #include "Background.hxx"
#include "TIA.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Background::Background() Background::Background()
@ -33,6 +34,8 @@ void Background::reset()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Background::setColor(uInt8 color) void Background::setColor(uInt8 color)
{ {
if (color != myObjectColor) myTIA->flushLineCache();
myObjectColor = color; myObjectColor = color;
applyColors(); applyColors();
} }
@ -40,6 +43,7 @@ void Background::setColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Background::setDebugColor(uInt8 color) void Background::setDebugColor(uInt8 color)
{ {
myTIA->flushLineCache();
myDebugColor = color; myDebugColor = color;
applyColors(); applyColors();
} }
@ -47,6 +51,7 @@ void Background::setDebugColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Background::enableDebugColors(bool enabled) void Background::enableDebugColors(bool enabled)
{ {
myTIA->flushLineCache();
myDebugEnabled = enabled; myDebugEnabled = enabled;
applyColors(); applyColors();
} }

View File

@ -21,12 +21,18 @@
#include "Serializable.hxx" #include "Serializable.hxx"
#include "bspf.hxx" #include "bspf.hxx"
class TIA;
class Background : public Serializable class Background : public Serializable
{ {
public: public:
Background(); Background();
public: public:
void setTIA(TIA* tia) {
myTIA = tia;
}
void reset(); void reset();
void setColor(uInt8 color); void setColor(uInt8 color);
@ -50,6 +56,8 @@ class Background : public Serializable
uInt8 myObjectColor, myDebugColor; uInt8 myObjectColor, myDebugColor;
bool myDebugEnabled; bool myDebugEnabled;
TIA* myTIA;
private: private:
Background(const Background&) = delete; Background(const Background&) = delete;
Background(Background&&) = delete; Background(Background&&) = delete;

View File

@ -16,6 +16,7 @@
//============================================================================ //============================================================================
#include "Ball.hxx" #include "Ball.hxx"
#include "TIA.hxx"
enum Count: Int8 { enum Count: Int8 {
renderCounterOffset = -4 renderCounterOffset = -4
@ -56,8 +57,14 @@ void Ball::reset()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Ball::enabl(uInt8 value) void Ball::enabl(uInt8 value)
{ {
const auto enabledNewOldValue = myIsEnabledNew;
myIsEnabledNew = (value & 0x02) > 0; 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}; 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) void Ball::vdelbl(uInt8 value)
{ {
const auto oldIsDelaying = myIsDelaying;
myIsDelaying = (value & 0x01) > 0; 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) void Ball::setColor(uInt8 color)
{ {
if (color != myObjectColor && myIsEnabled) myTIA->flushLineCache();
myObjectColor = color; myObjectColor = color;
applyColors(); applyColors();
} }
@ -113,6 +133,8 @@ void Ball::setColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Ball::setDebugColor(uInt8 color) void Ball::setDebugColor(uInt8 color)
{ {
myTIA->flushLineCache();
myDebugColor = color; myDebugColor = color;
applyColors(); applyColors();
} }
@ -120,6 +142,8 @@ void Ball::setDebugColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Ball::enableDebugColors(bool enabled) void Ball::enableDebugColors(bool enabled)
{ {
myTIA->flushLineCache();
myDebugEnabled = enabled; myDebugEnabled = enabled;
applyColors(); applyColors();
} }
@ -137,25 +161,18 @@ bool Ball::movementTick(uInt32 clock, bool apply)
if (clock == myHmmClocks) myIsMoving = false; if (clock == myHmmClocks) myIsMoving = false;
if (myIsMoving && apply) { if (myIsMoving && apply) tick(false);
render();
tick(false);
}
return myIsMoving; return myIsMoving;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Ball::render() void Ball::tick(bool isReceivingMclock)
{ {
collision = (myIsRendering && myRenderCounter >= 0 && myIsEnabled) ? collision = (myIsRendering && myRenderCounter >= 0 && myIsEnabled) ?
myCollisionMaskEnabled : myCollisionMaskEnabled :
myCollisionMaskDisabled; myCollisionMaskDisabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Ball::tick(bool isReceivingMclock)
{
bool starfieldEffect = myIsMoving && isReceivingMclock; bool starfieldEffect = myIsMoving && isReceivingMclock;
if (myCounter == 156) { if (myCounter == 156) {
@ -189,8 +206,14 @@ void Ball::tick(bool isReceivingMclock)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Ball::shuffleStatus() void Ball::shuffleStatus()
{ {
const auto oldIsEnabledOld = myIsEnabledOld;
myIsEnabledOld = myIsEnabledNew; myIsEnabledOld = myIsEnabledNew;
updateEnabled();
if (myIsEnabledOld != oldIsEnabledOld && myIsDelaying) {
myTIA->flushLineCache();
updateEnabled();
}
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -209,13 +232,15 @@ void Ball::applyColors()
uInt8 Ball::getPosition() const uInt8 Ball::getPosition() const
{ {
// Mind the sign of renderCounterOffset: it's defined negative above // 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) void Ball::setPosition(uInt8 newPosition)
{ {
myCounter = (316 - newPosition - Count::renderCounterOffset + myPlayfieldPositionProvider->getPosition()) % 160; myTIA->flushLineCache();
myCounter = (316 - newPosition - Count::renderCounterOffset + myTIA->getPosition()) % 160;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -20,7 +20,8 @@
#include "Serializable.hxx" #include "Serializable.hxx"
#include "bspf.hxx" #include "bspf.hxx"
#include "PlayfieldPositionProvider.hxx"
class TIA;
class Ball : public Serializable class Ball : public Serializable
{ {
@ -30,8 +31,8 @@ class Ball : public Serializable
public: public:
void setPlayfieldPositionProvider(PlayfieldPositionProvider* playfieldPositionProvider) { void setTIA(TIA* tia) {
myPlayfieldPositionProvider = playfieldPositionProvider; myTIA = tia;
} }
void reset(); void reset();
@ -59,8 +60,6 @@ class Ball : public Serializable
bool movementTick(uInt32 clock, bool apply); bool movementTick(uInt32 clock, bool apply);
void render();
void tick(bool isReceivingMclock = true); void tick(bool isReceivingMclock = true);
uInt8 getPixel(uInt8 colorIn) const { uInt8 getPixel(uInt8 colorIn) const {
@ -113,7 +112,7 @@ class Ball : public Serializable
bool myIsRendering; bool myIsRendering;
Int8 myRenderCounter; Int8 myRenderCounter;
PlayfieldPositionProvider* myPlayfieldPositionProvider; TIA* myTIA;
private: private:
Ball() = delete; Ball() = delete;

View File

@ -17,6 +17,7 @@
#include "Missile.hxx" #include "Missile.hxx"
#include "DrawCounterDecodes.hxx" #include "DrawCounterDecodes.hxx"
#include "TIA.hxx"
enum Count: Int8 { enum Count: Int8 {
renderCounterOffset = -4 renderCounterOffset = -4
@ -57,7 +58,12 @@ void Missile::reset()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Missile::enam(uInt8 value) void Missile::enam(uInt8 value)
{ {
const auto oldEnam = myEnam;
myEnam = (value & 0x02) > 0; myEnam = (value & 0x02) > 0;
if (oldEnam != myEnam) myTIA->flushLineCache();
updateEnabled(); updateEnabled();
} }
@ -109,8 +115,9 @@ void Missile::resmp(uInt8 value, const Player& player)
{ {
const uInt8 resmp = value & 0x02; const uInt8 resmp = value & 0x02;
if (resmp == myResmp) if (resmp == myResmp) return;
return;
myTIA->flushLineCache();
myResmp = resmp; myResmp = resmp;
@ -159,28 +166,21 @@ bool Missile::movementTick(uInt8 clock, uInt8 hclock, bool apply)
if (clock == myHmmClocks) myIsMoving = false; if (clock == myHmmClocks) myIsMoving = false;
if (myIsMoving && apply) { if (myIsMoving && apply) tick(hclock);
render(hclock);
tick(hclock);
}
return myIsMoving; return myIsMoving;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Missile::render(uInt8 hclock) void Missile::tick(uInt8 hclock)
{ {
bool render = const bool render =
myIsRendering && myIsRendering &&
(myRenderCounter >= 0 || (myIsMoving && myRenderCounter == -1 && myWidth < 4 && ((hclock + 1) % 4 == 3))) && (myRenderCounter >= 0 || (myIsMoving && myRenderCounter == -1 && myWidth < 4 && ((hclock + 1) % 4 == 3))) &&
myIsEnabled; myIsEnabled;
collision = render ? myCollisionMaskEnabled : myCollisionMaskDisabled; collision = render ? myCollisionMaskEnabled : myCollisionMaskDisabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Missile::tick(uInt8 hclock)
{
if (myDecodes[myCounter] && !myResmp) { if (myDecodes[myCounter] && !myResmp) {
myIsRendering = true; myIsRendering = true;
myRenderCounter = Count::renderCounterOffset; myRenderCounter = Count::renderCounterOffset;
@ -213,6 +213,8 @@ void Missile::tick(uInt8 hclock)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Missile::setColor(uInt8 color) void Missile::setColor(uInt8 color)
{ {
if (color != myObjectColor && myIsEnabled) myTIA->flushLineCache();
myObjectColor = color; myObjectColor = color;
applyColors(); applyColors();
} }
@ -220,6 +222,7 @@ void Missile::setColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Missile::setDebugColor(uInt8 color) void Missile::setDebugColor(uInt8 color)
{ {
myTIA->flushLineCache();
myDebugColor = color; myDebugColor = color;
applyColors(); applyColors();
} }
@ -247,13 +250,14 @@ void Missile::applyColors()
uInt8 Missile::getPosition() const uInt8 Missile::getPosition() const
{ {
// Mind the sign of renderCounterOffset: it's defined negative above // 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) void Missile::setPosition(uInt8 newPosition)
{ {
myCounter = (316 - newPosition - Count::renderCounterOffset + myPlayfieldPositionProvider->getPosition()) % 160; myTIA->flushLineCache();
myCounter = (316 - newPosition - Count::renderCounterOffset + myTIA->getPosition()) % 160;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -21,7 +21,8 @@
#include "Serializable.hxx" #include "Serializable.hxx"
#include "bspf.hxx" #include "bspf.hxx"
#include "Player.hxx" #include "Player.hxx"
#include "PlayfieldPositionProvider.hxx"
class TIA;
class Missile : public Serializable class Missile : public Serializable
{ {
@ -31,8 +32,8 @@ class Missile : public Serializable
public: public:
void setPlayfieldPositionProvider(PlayfieldPositionProvider* playfieldPositionProvider) { void setTIA(TIA* tia) {
myPlayfieldPositionProvider = playfieldPositionProvider; myTIA = tia;
} }
void reset(); void reset();
@ -51,8 +52,6 @@ class Missile : public Serializable
bool movementTick(uInt8 clock, uInt8 hclock, bool apply); bool movementTick(uInt8 clock, uInt8 hclock, bool apply);
void render(uInt8 hclock);
void tick(uInt8 hclock); void tick(uInt8 hclock);
void setColor(uInt8 color); void setColor(uInt8 color);
@ -114,7 +113,7 @@ class Missile : public Serializable
uInt8 myObjectColor, myDebugColor; uInt8 myObjectColor, myDebugColor;
bool myDebugEnabled; bool myDebugEnabled;
PlayfieldPositionProvider *myPlayfieldPositionProvider; TIA *myTIA;
private: private:
Missile(const Missile&) = delete; Missile(const Missile&) = delete;

View File

@ -17,6 +17,7 @@
#include "Player.hxx" #include "Player.hxx"
#include "DrawCounterDecodes.hxx" #include "DrawCounterDecodes.hxx"
#include "TIA.hxx"
enum Count: Int8 { enum Count: Int8 {
renderCounterOffset = -5, renderCounterOffset = -5,
@ -63,7 +64,10 @@ void Player::grp(uInt8 pattern)
myPatternNew = 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; 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; 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) void Player::setColor(uInt8 color)
{ {
if (color != myObjectColor && myPattern) myTIA->flushLineCache();
myObjectColor = color; myObjectColor = color;
applyColors(); applyColors();
} }
@ -218,6 +230,8 @@ void Player::setColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::setDebugColor(uInt8 color) void Player::setDebugColor(uInt8 color)
{ {
myTIA->flushLineCache();
myDebugColor = color; myDebugColor = color;
applyColors(); applyColors();
} }
@ -225,6 +239,8 @@ void Player::setDebugColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::enableDebugColors(bool enabled) void Player::enableDebugColors(bool enabled)
{ {
myTIA->flushLineCache();
myDebugEnabled = enabled; myDebugEnabled = enabled;
applyColors(); applyColors();
} }
@ -242,28 +258,19 @@ bool Player::movementTick(uInt32 clock, bool apply)
myIsMoving = false; myIsMoving = false;
} }
if (myIsMoving && apply) { if (myIsMoving && apply) tick();
render();
tick();
}
return myIsMoving; return myIsMoving;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::render()
{
if (!myIsRendering || myRenderCounter < myRenderCounterTripPoint) {
collision = myCollisionMaskDisabled;
return;
}
collision = (myPattern & (1 << mySampleCounter)) ? myCollisionMaskEnabled : myCollisionMaskDisabled;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Player::tick() void Player::tick()
{ {
if (!myIsRendering || myRenderCounter < myRenderCounterTripPoint)
collision = myCollisionMaskDisabled;
else
collision = (myPattern & (1 << mySampleCounter)) ? myCollisionMaskEnabled : myCollisionMaskDisabled;
if (myDecodes[myCounter]) { if (myDecodes[myCounter]) {
myIsRendering = true; myIsRendering = true;
mySampleCounter = 0; mySampleCounter = 0;
@ -304,7 +311,10 @@ void Player::shufflePatterns()
myPatternOld = myPatternNew; 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; const uInt8 shift = myDivider == 1 ? 0 : 1;
// Mind the sign of renderCounterOffset: it's defined negative above // 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) void Player::setPosition(uInt8 newPosition)
{ {
myTIA->flushLineCache();
const uInt8 shift = myDivider == 1 ? 0 : 1; 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;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -20,7 +20,8 @@
#include "bspf.hxx" #include "bspf.hxx"
#include "Serializable.hxx" #include "Serializable.hxx"
#include "PlayfieldPositionProvider.hxx"
class TIA;
class Player : public Serializable class Player : public Serializable
{ {
@ -29,8 +30,8 @@ class Player : public Serializable
public: public:
void setPlayfieldPositionProvider(PlayfieldPositionProvider* playfieldPositionProvider) { void setTIA(TIA* tia) {
myPlayfieldPositionProvider = playfieldPositionProvider; myTIA = tia;
} }
void reset(); void reset();
@ -60,8 +61,6 @@ class Player : public Serializable
bool movementTick(uInt32 clock, bool apply); bool movementTick(uInt32 clock, bool apply);
void render();
void tick(); void tick();
uInt8 getClock() const { return myCounter; } uInt8 getClock() const { return myCounter; }
@ -126,7 +125,7 @@ class Player : public Serializable
bool myIsReflected; bool myIsReflected;
bool myIsDelaying; bool myIsDelaying;
PlayfieldPositionProvider *myPlayfieldPositionProvider; TIA* myTIA;
private: private:
Player(const Player&) = delete; Player(const Player&) = delete;

View File

@ -16,6 +16,7 @@
//============================================================================ //============================================================================
#include "Playfield.hxx" #include "Playfield.hxx"
#include "TIA.hxx"
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Playfield::Playfield(uInt32 collisionMask) Playfield::Playfield(uInt32 collisionMask)
@ -52,8 +53,12 @@ void Playfield::reset()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::pf0(uInt8 value) void Playfield::pf0(uInt8 value)
{ {
if (myPf0 == value >> 4) return;
myTIA->flushLineCache();
myPattern = (myPattern & 0x000FFFF0) | (value >> 4); myPattern = (myPattern & 0x000FFFF0) | (value >> 4);
myPf0 = value; myPf0 = value >> 4;
updatePattern(); updatePattern();
} }
@ -61,6 +66,10 @@ void Playfield::pf0(uInt8 value)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::pf1(uInt8 value) void Playfield::pf1(uInt8 value)
{ {
if (myPf1 == value) return;
myTIA->flushLineCache();
myPattern = (myPattern & 0x000FF00F) myPattern = (myPattern & 0x000FF00F)
| ((value & 0x80) >> 3) | ((value & 0x80) >> 3)
| ((value & 0x40) >> 1) | ((value & 0x40) >> 1)
@ -78,6 +87,10 @@ void Playfield::pf1(uInt8 value)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::pf2(uInt8 value) void Playfield::pf2(uInt8 value)
{ {
if (myPf2 == value) return;
myTIA->flushLineCache();
myPattern = (myPattern & 0x00000FFF) | (value << 12); myPattern = (myPattern & 0x00000FFF) | (value << 12);
myPf2 = value; myPf2 = value;
@ -87,8 +100,15 @@ void Playfield::pf2(uInt8 value)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::ctrlpf(uInt8 value) void Playfield::ctrlpf(uInt8 value)
{ {
myReflected = (value & 0x01) > 0; const bool reflected = (value & 0x01) > 0;
myColorMode = (value & 0x06) == 0x02 ? ColorMode::score : ColorMode::normal; const ColorMode colorMode = (value & 0x06) == 0x02 ? ColorMode::score : ColorMode::normal;
if (myReflected == reflected && myColorMode == colorMode) return;
myTIA->flushLineCache();
myReflected = reflected;
myColorMode = colorMode;
applyColors(); applyColors();
} }
@ -109,6 +129,8 @@ void Playfield::toggleCollisions(bool enabled)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::setColor(uInt8 color) void Playfield::setColor(uInt8 color)
{ {
if (color != myObjectColor && myColorMode == ColorMode::normal) myTIA->flushLineCache();
myObjectColor = color; myObjectColor = color;
applyColors(); applyColors();
} }
@ -116,6 +138,8 @@ void Playfield::setColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::setColorP0(uInt8 color) void Playfield::setColorP0(uInt8 color)
{ {
if (color != myColorP0 && myColorMode == ColorMode::score) myTIA->flushLineCache();
myColorP0 = color; myColorP0 = color;
applyColors(); applyColors();
} }
@ -123,6 +147,8 @@ void Playfield::setColorP0(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::setColorP1(uInt8 color) void Playfield::setColorP1(uInt8 color)
{ {
if (color != myColorP1 && myColorMode == ColorMode::score) myTIA->flushLineCache();
myColorP1 = color; myColorP1 = color;
applyColors(); applyColors();
} }
@ -130,6 +156,7 @@ void Playfield::setColorP1(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::setDebugColor(uInt8 color) void Playfield::setDebugColor(uInt8 color)
{ {
myTIA->flushLineCache();
myDebugColor = color; myDebugColor = color;
applyColors(); applyColors();
} }
@ -137,6 +164,7 @@ void Playfield::setDebugColor(uInt8 color)
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void Playfield::enableDebugColors(bool enabled) void Playfield::enableDebugColors(bool enabled)
{ {
myTIA->flushLineCache();
myDebugEnabled = enabled; myDebugEnabled = enabled;
applyColors(); applyColors();
} }

View File

@ -21,6 +21,8 @@
#include "Serializable.hxx" #include "Serializable.hxx"
#include "bspf.hxx" #include "bspf.hxx"
class TIA;
class Playfield : public Serializable class Playfield : public Serializable
{ {
public: public:
@ -28,6 +30,10 @@ class Playfield : public Serializable
public: public:
void setTIA(TIA* tia) {
myTIA = tia;
}
void reset(); void reset();
void pf0(uInt8 value); void pf0(uInt8 value);
@ -105,6 +111,8 @@ class Playfield : public Serializable
uInt32 myX; uInt32 myX;
TIA* myTIA;
private: private:
Playfield() = delete; Playfield() = delete;
Playfield(const Playfield&) = delete; Playfield(const Playfield&) = delete;

View File

@ -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

View File

@ -95,11 +95,13 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings)
myTIAPinsDriven = mySettings.getBool("tiadriven"); myTIAPinsDriven = mySettings.getBool("tiadriven");
myPlayer0.setPlayfieldPositionProvider(this); myBackground.setTIA(this);
myPlayer1.setPlayfieldPositionProvider(this); myPlayfield.setTIA(this);
myMissile0.setPlayfieldPositionProvider(this); myPlayer0.setTIA(this);
myMissile1.setPlayfieldPositionProvider(this); myPlayer1.setTIA(this);
myBall.setPlayfieldPositionProvider(this); myMissile0.setTIA(this);
myMissile1.setTIA(this);
myBall.setTIA(this);
reset(); reset();
} }
@ -114,7 +116,6 @@ void TIA::reset()
myMovementClock = 0; myMovementClock = 0;
myPriority = Priority::normal; myPriority = Priority::normal;
myHstate = HState::blank; myHstate = HState::blank;
myIsFreshLine = true;
myCollisionMask = 0; myCollisionMask = 0;
myLinesSinceChange = 0; myLinesSinceChange = 0;
myCollisionUpdateRequired = false; myCollisionUpdateRequired = false;
@ -223,7 +224,6 @@ bool TIA::save(Serializer& out) const
out.putBool(myTIAPinsDriven); out.putBool(myTIAPinsDriven);
out.putInt(int(myHstate)); out.putInt(int(myHstate));
out.putBool(myIsFreshLine);
out.putInt(myHblankCtr); out.putInt(myHblankCtr);
out.putInt(myHctr); out.putInt(myHctr);
@ -293,7 +293,6 @@ bool TIA::load(Serializer& in)
myTIAPinsDriven = in.getBool(); myTIAPinsDriven = in.getBool();
myHstate = HState(in.getInt()); myHstate = HState(in.getInt());
myIsFreshLine = in.getBool();
myHblankCtr = in.getInt(); myHblankCtr = in.getInt();
myHctr = in.getInt(); myHctr = in.getInt();
@ -443,6 +442,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case RSYNC: case RSYNC:
flushLineCache();
applyRsync(); applyRsync();
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
@ -496,13 +496,11 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case COLUBK: case COLUBK:
myLinesSinceChange = 0;
myBackground.setColor(value & 0xFE); myBackground.setColor(value & 0xFE);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case COLUP0: case COLUP0:
myLinesSinceChange = 0;
value &= 0xFE; value &= 0xFE;
myPlayfield.setColorP0(value); myPlayfield.setColorP0(value);
myMissile0.setColor(value); myMissile0.setColor(value);
@ -511,7 +509,6 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case COLUP1: case COLUP1:
myLinesSinceChange = 0;
value &= 0xFE; value &= 0xFE;
myPlayfield.setColorP1(value); myPlayfield.setColorP1(value);
myMissile1.setColor(value); myMissile1.setColor(value);
@ -520,7 +517,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case CTRLPF: case CTRLPF:
myLinesSinceChange = 0; flushLineCache();
myPriority = (value & 0x04) ? Priority::pfp : myPriority = (value & 0x04) ? Priority::pfp :
(value & 0x02) ? Priority::score : Priority::normal; (value & 0x02) ? Priority::score : Priority::normal;
myPlayfield.ctrlpf(value); myPlayfield.ctrlpf(value);
@ -529,7 +526,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case COLUPF: case COLUPF:
myLinesSinceChange = 0; flushLineCache();
value &= 0xFE; value &= 0xFE;
myPlayfield.setColor(value); myPlayfield.setColor(value);
myBall.setColor(value); myBall.setColor(value);
@ -578,38 +575,36 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case RESM0: case RESM0:
myLinesSinceChange = 0; flushLineCache();
myMissile0.resm(resxCounter(), myHstate == HState::blank); myMissile0.resm(resxCounter(), myHstate == HState::blank);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case RESM1: case RESM1:
myLinesSinceChange = 0; flushLineCache();
myMissile1.resm(resxCounter(), myHstate == HState::blank); myMissile1.resm(resxCounter(), myHstate == HState::blank);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case RESMP0: case RESMP0:
myLinesSinceChange = 0;
myMissile0.resmp(value, myPlayer0); myMissile0.resmp(value, myPlayer0);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case RESMP1: case RESMP1:
myLinesSinceChange = 0;
myMissile1.resmp(value, myPlayer1); myMissile1.resmp(value, myPlayer1);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case NUSIZ0: case NUSIZ0:
myLinesSinceChange = 0; flushLineCache();
myMissile0.nusiz(value); myMissile0.nusiz(value);
myPlayer0.nusiz(value, myHstate == HState::blank); myPlayer0.nusiz(value, myHstate == HState::blank);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case NUSIZ1: case NUSIZ1:
myLinesSinceChange = 0; flushLineCache();
myMissile1.nusiz(value); myMissile1.nusiz(value);
myPlayer1.nusiz(value, myHstate == HState::blank); myPlayer1.nusiz(value, myHstate == HState::blank);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
@ -653,13 +648,13 @@ bool TIA::poke(uInt16 address, uInt8 value)
} }
case RESP0: case RESP0:
myLinesSinceChange = 0; flushLineCache();
myPlayer0.resp(resxCounter()); myPlayer0.resp(resxCounter());
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case RESP1: case RESP1:
myLinesSinceChange = 0; flushLineCache();
myPlayer1.resp(resxCounter()); myPlayer1.resp(resxCounter());
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
@ -673,13 +668,11 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case VDELP0: case VDELP0:
myLinesSinceChange = 0;
myPlayer0.vdelp(value); myPlayer0.vdelp(value);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case VDELP1: case VDELP1:
myLinesSinceChange = 0;
myPlayer1.vdelp(value); myPlayer1.vdelp(value);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
@ -697,13 +690,12 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case RESBL: case RESBL:
myLinesSinceChange = 0; flushLineCache();
myBall.resbl(resxCounter()); myBall.resbl(resxCounter());
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
case VDELBL: case VDELBL:
myLinesSinceChange = 0;
myBall.vdelbl(value); myBall.vdelbl(value);
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
@ -713,7 +705,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
break; break;
case CXCLR: case CXCLR:
myLinesSinceChange = 0; flushLineCache();
myCollisionMask = 0; myCollisionMask = 0;
myShadowRegisters[address] = value; myShadowRegisters[address] = value;
break; break;
@ -1005,18 +997,20 @@ void TIA::cycle(uInt32 colorClocks)
myCollisionUpdateRequired = false; myCollisionUpdateRequired = false;
tickMovement(); if (myLinesSinceChange < 2) {
tickMovement();
if (myHstate == HState::blank) if (myHstate == HState::blank)
tickHblank(); tickHblank();
else else
tickHframe(); tickHframe();
if (myCollisionUpdateRequired) updateCollision();
}
if (++myHctr >= 228) if (++myHctr >= 228)
nextLine(); nextLine();
if (myCollisionUpdateRequired) updateCollision();
myTimestamp++; myTimestamp++;
} }
} }
@ -1027,8 +1021,6 @@ void TIA::tickMovement()
if (!myMovementInProgress) return; if (!myMovementInProgress) return;
if ((myHctr & 0x03) == 0) { if ((myHctr & 0x03) == 0) {
myLinesSinceChange = 0;
const bool apply = myHstate == HState::blank; const bool apply = myHstate == HState::blank;
bool m = false; bool m = false;
uInt8 movementCounter = myMovementClock > 15 ? 0 : myMovementClock; uInt8 movementCounter = myMovementClock > 15 ? 0 : myMovementClock;
@ -1049,9 +1041,8 @@ void TIA::tickMovement()
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::tickHblank() void TIA::tickHblank()
{ {
if (myIsFreshLine) { if (myHctr == 0) {
myHblankCtr = 0; myHblankCtr = 0;
myIsFreshLine = false;
} }
if (++myHblankCtr >= 68) myHstate = HState::frame; if (++myHblankCtr >= 68) myHstate = HState::frame;
@ -1061,23 +1052,11 @@ void TIA::tickHblank()
void TIA::tickHframe() void TIA::tickHframe()
{ {
const uInt32 y = myFrameManager.getY(); const uInt32 y = myFrameManager.getY();
const bool lineNotCached = myLinesSinceChange < 2 || y == 0;
const uInt32 x = myHctr - 68 - myXDelta; const uInt32 x = myHctr - 68 - myXDelta;
myCollisionUpdateRequired = lineNotCached; myCollisionUpdateRequired = true;
myPlayfield.tick(x); myPlayfield.tick(x);
// Render sprites
if (lineNotCached) {
myPlayer0.render();
myPlayer1.render();
myMissile0.render(myHctr);
myMissile1.render(myHctr);
myBall.render();
}
// Tick sprites
myMissile0.tick(myHctr); myMissile0.tick(myHctr);
myMissile1.tick(myHctr); myMissile1.tick(myHctr);
myPlayer0.tick(); myPlayer0.tick();
@ -1085,7 +1064,7 @@ void TIA::tickHframe()
myBall.tick(); myBall.tick();
if (myFrameManager.isRendering()) if (myFrameManager.isRendering())
renderPixel(x, y, lineNotCached); renderPixel(x, y);
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@ -1097,22 +1076,39 @@ void TIA::applyRsync()
if (myFrameManager.isRendering()) if (myFrameManager.isRendering())
memset(myCurrentFrameBuffer.get() + myFrameManager.getY() * 160 + x, 0, 160 - x); memset(myCurrentFrameBuffer.get() + myFrameManager.getY() * 160 + x, 0, 160 - x);
myLinesSinceChange = 0;
myHctr = 225; myHctr = 225;
} }
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
void TIA::nextLine() void TIA::nextLine()
{ {
if (myLinesSinceChange >= 2) {
cloneLastLine();
}
myHctr = 0; myHctr = 0;
myLinesSinceChange++;
if (!myMovementInProgress && myLinesSinceChange < 2) myLinesSinceChange++;
myHstate = HState::blank; myHstate = HState::blank;
myIsFreshLine = true;
myExtendedHblank = false; myExtendedHblank = false;
myXDelta = 0; myXDelta = 0;
myFrameManager.nextLine(); 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 +1125,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 (x >= 160) return;
if (lineNotCached) { uInt8 color = myBackground.getColor();
uInt8 color = myBackground.getColor();
switch (myPriority) switch (myPriority)
{ {
// Playfield has priority so ScoreBit isn't used // Playfield has priority so ScoreBit isn't used
// Priority from highest to lowest: // Priority from highest to lowest:
// BL/PF => P0/M0 => P1/M1 => BK // BL/PF => P0/M0 => P1/M1 => BK
case Priority::pfp: // CTRLPF D2=1, D1=ignored case Priority::pfp: // CTRLPF D2=1, D1=ignored
color = myMissile1.getPixel(color); color = myMissile1.getPixel(color);
color = myPlayer1.getPixel(color); color = myPlayer1.getPixel(color);
color = myMissile0.getPixel(color); color = myMissile0.getPixel(color);
color = myPlayer0.getPixel(color); color = myPlayer0.getPixel(color);
color = myPlayfield.getPixel(color); color = myPlayfield.getPixel(color);
color = myBall.getPixel(color); color = myBall.getPixel(color);
break; break;
case Priority::score: // CTRLPF D2=0, D1=1 case Priority::score: // CTRLPF D2=0, D1=1
// Formally we have (priority from highest to lowest) // Formally we have (priority from highest to lowest)
// PF/P0/M0 => P1/M1 => BL => BK // PF/P0/M0 => P1/M1 => BL => BK
// for the first half and // for the first half and
// P0/M0 => PF/P1/M1 => BL => BK // P0/M0 => PF/P1/M1 => BL => BK
// for the second half. However, the first ordering is equivalent // 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 // to the second (PF has the same color as P0/M0), so we can just
// write // write
color = myBall.getPixel(color); color = myBall.getPixel(color);
color = myMissile1.getPixel(color); color = myMissile1.getPixel(color);
color = myPlayer1.getPixel(color); color = myPlayer1.getPixel(color);
color = myPlayfield.getPixel(color); color = myPlayfield.getPixel(color);
color = myMissile0.getPixel(color); color = myMissile0.getPixel(color);
color = myPlayer0.getPixel(color); color = myPlayer0.getPixel(color);
break; break;
// Priority from highest to lowest: // Priority from highest to lowest:
// P0/M0 => P1/M1 => BL/PF => BK // P0/M0 => P1/M1 => BL/PF => BK
case Priority::normal: // CTRLPF D2=0, D1=0 case Priority::normal: // CTRLPF D2=0, D1=0
color = myPlayfield.getPixel(color); color = myPlayfield.getPixel(color);
color = myBall.getPixel(color); color = myBall.getPixel(color);
color = myMissile1.getPixel(color); color = myMissile1.getPixel(color);
color = myPlayer1.getPixel(color); color = myPlayer1.getPixel(color);
color = myMissile0.getPixel(color); color = myMissile0.getPixel(color);
color = myPlayer0.getPixel(color); color = myPlayer0.getPixel(color);
break; 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 +1212,12 @@ void TIA::delayedWrite(uInt8 address, uInt8 value)
switch (address) switch (address)
{ {
case VBLANK: case VBLANK:
myLinesSinceChange = 0; flushLineCache();
myFrameManager.setVblank(value & 0x02); myFrameManager.setVblank(value & 0x02);
break; break;
case HMOVE: case HMOVE:
myLinesSinceChange = 0; flushLineCache();
myMovementClock = 0; myMovementClock = 0;
myMovementInProgress = true; myMovementInProgress = true;
@ -1225,32 +1236,26 @@ void TIA::delayedWrite(uInt8 address, uInt8 value)
break; break;
case PF0: case PF0:
myLinesSinceChange = 0;
myPlayfield.pf0(value); myPlayfield.pf0(value);
break; break;
case PF1: case PF1:
myLinesSinceChange = 0;
myPlayfield.pf1(value); myPlayfield.pf1(value);
break; break;
case PF2: case PF2:
myLinesSinceChange = 0;
myPlayfield.pf2(value); myPlayfield.pf2(value);
break; break;
case HMM0: case HMM0:
myLinesSinceChange = 0;
myMissile0.hmm(value); myMissile0.hmm(value);
break; break;
case HMM1: case HMM1:
myLinesSinceChange = 0;
myMissile1.hmm(value); myMissile1.hmm(value);
break; break;
case HMCLR: case HMCLR:
myLinesSinceChange = 0;
myMissile0.hmm(0); myMissile0.hmm(0);
myMissile1.hmm(0); myMissile1.hmm(0);
myPlayer0.hmp(0); myPlayer0.hmp(0);
@ -1259,67 +1264,54 @@ void TIA::delayedWrite(uInt8 address, uInt8 value)
break; break;
case GRP0: case GRP0:
myLinesSinceChange = 0;
myPlayer0.grp(value); myPlayer0.grp(value);
break; break;
case GRP1: case GRP1:
myLinesSinceChange = 0;
myPlayer1.grp(value); myPlayer1.grp(value);
break; break;
case DummyRegisters::shuffleP0: case DummyRegisters::shuffleP0:
myLinesSinceChange = 0;
myPlayer0.shufflePatterns(); myPlayer0.shufflePatterns();
break; break;
case DummyRegisters::shuffleP1: case DummyRegisters::shuffleP1:
myLinesSinceChange = 0;
myPlayer1.shufflePatterns(); myPlayer1.shufflePatterns();
break; break;
case DummyRegisters::shuffleBL: case DummyRegisters::shuffleBL:
myLinesSinceChange = 0;
myBall.shuffleStatus(); myBall.shuffleStatus();
break; break;
case HMP0: case HMP0:
myLinesSinceChange = 0;
myPlayer0.hmp(value); myPlayer0.hmp(value);
break; break;
case HMP1: case HMP1:
myLinesSinceChange = 0;
myPlayer1.hmp(value); myPlayer1.hmp(value);
break; break;
case HMBL: case HMBL:
myLinesSinceChange = 0;
myBall.hmbl(value); myBall.hmbl(value);
break; break;
case REFP0: case REFP0:
myLinesSinceChange = 0;
myPlayer0.refp(value); myPlayer0.refp(value);
break; break;
case REFP1: case REFP1:
myLinesSinceChange = 0;
myPlayer1.refp(value); myPlayer1.refp(value);
break; break;
case ENABL: case ENABL:
myLinesSinceChange = 0;
myBall.enabl(value); myBall.enabl(value);
break; break;
case ENAM0: case ENAM0:
myLinesSinceChange = 0;
myMissile0.enam(value); myMissile0.enam(value);
break; break;
case ENAM1: case ENAM1:
myLinesSinceChange = 0;
myMissile1.enam(value); myMissile1.enam(value);
break; break;
} }

View File

@ -35,7 +35,6 @@
#include "Ball.hxx" #include "Ball.hxx"
#include "LatchedInput.hxx" #include "LatchedInput.hxx"
#include "PaddleReader.hxx" #include "PaddleReader.hxx"
#include "PlayfieldPositionProvider.hxx"
/** /**
This class is a device that emulates the Television Interface Adaptor This class is a device that emulates the Television Interface Adaptor
@ -50,7 +49,7 @@
@author Christian Speckner (DirtyHairy) and Stephen Anthony @author Christian Speckner (DirtyHairy) and Stephen Anthony
*/ */
class TIA : public Device, public PlayfieldPositionProvider class TIA : public Device
{ {
public: public:
friend class TIADebug; friend class TIADebug;
@ -317,10 +316,16 @@ class TIA : public Device, public PlayfieldPositionProvider
/** /**
Get the current x value Get the current x value
*/ */
uInt8 getPosition() const override { uInt8 getPosition() const {
return (myHctr < 68) ? 0 : (myHctr - 68 - myXDelta); 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. Save the current state of this device to the given Serializer.
@ -388,12 +393,14 @@ class TIA : public Device, public PlayfieldPositionProvider
void updateCollision(); void updateCollision();
void renderPixel(uInt32 x, uInt32 y, bool lineNotCached); void renderPixel(uInt32 x, uInt32 y);
void clearHmoveComb(); void clearHmoveComb();
void nextLine(); void nextLine();
void cloneLastLine();
void delayedWrite(uInt8 address, uInt8 value); void delayedWrite(uInt8 address, uInt8 value);
void updatePaddle(uInt8 idx); void updatePaddle(uInt8 idx);
@ -442,7 +449,6 @@ class TIA : public Device, public PlayfieldPositionProvider
bool myTIAPinsDriven; bool myTIAPinsDriven;
HState myHstate; HState myHstate;
bool myIsFreshLine;
Int32 myHblankCtr; Int32 myHblankCtr;
Int32 myHctr; Int32 myHctr;