Merge remote-tracking branch 'stella-emu/master'

This commit is contained in:
Darrell Spice, Jr 2017-04-23 18:07:54 -05:00
commit 3b0b6127cc
14 changed files with 297 additions and 247 deletions

View File

@ -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();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

@ -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();
}

View File

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

View File

@ -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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

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

View File

@ -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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

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

View File

@ -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;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

View File

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

View File

@ -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();
}

View File

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

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");
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;
}

View File

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