Fix timing glitch, port missiles.

This commit is contained in:
Christian Speckner 2016-11-17 23:24:12 +01:00
parent c8dc5e2db9
commit baecc4e3a1
7 changed files with 528 additions and 21 deletions

View File

@ -0,0 +1,91 @@
//============================================================================
//
// 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-2016 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.
//
// $Id$
//============================================================================
// namespace TIA6502tsCore
#include "DrawCounterDecodes.hxx"
namespace TIA6502tsCore {
const uInt8* const* DrawCounterDecodes::playerDecodes() const {
return myPlayerDecodes;
}
const uInt8* const* DrawCounterDecodes::missileDecodes() const {
return myMissileDecodes;
}
DrawCounterDecodes DrawCounterDecodes::myInstance;
DrawCounterDecodes& DrawCounterDecodes::DrawCounterDecodes::get()
{
return myInstance;
}
DrawCounterDecodes::DrawCounterDecodes()
{
myDecodes0 = new uInt8[160];
myDecodes1 = new uInt8[160];
myDecodes2 = new uInt8[160];
myDecodes3 = new uInt8[160];
myDecodes4 = new uInt8[160];
myDecodes6 = new uInt8[160];
myDecodesWide = new uInt8[160];
for (uInt8 *decodes : {myDecodes0, myDecodes1, myDecodes2, myDecodes3, myDecodes4, myDecodes6}) {
memset(decodes, 0, 160);
decodes[156] = 1;
}
memset(myDecodesWide, 0, 160);
myDecodesWide[157] = 1;
myDecodes1[12] = 1;
myDecodes2[28] = 1;
myDecodes3[12] = myDecodes3[28] = 1;
myDecodes4[60] = 1;
myDecodes6[28] = myDecodes6[60] = 1;
myPlayerDecodes[0] = myDecodes0;
myPlayerDecodes[1] = myDecodes1;
myPlayerDecodes[2] = myDecodes2;
myPlayerDecodes[3] = myDecodes3;
myPlayerDecodes[4] = myDecodes4;
myPlayerDecodes[5] = myDecodesWide;
myPlayerDecodes[6] = myDecodes6;
myPlayerDecodes[7] = myDecodesWide;
myMissileDecodes[0] = myDecodes0;
myMissileDecodes[1] = myDecodes1;
myMissileDecodes[2] = myDecodes2;
myMissileDecodes[3] = myDecodes3;
myMissileDecodes[4] = myDecodes4;
myMissileDecodes[5] = myDecodes0;
myMissileDecodes[6] = myDecodes6;
myMissileDecodes[7] = myDecodes0;
}
DrawCounterDecodes::~DrawCounterDecodes()
{
for (uInt8 *decodes : {myDecodes0, myDecodes1, myDecodes2, myDecodes3, myDecodes4, myDecodes6, myDecodesWide}) {
delete[] decodes;
}
}
}

View File

@ -0,0 +1,63 @@
//============================================================================
//
// 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-2016 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.
//
// $Id$
//============================================================================
#ifndef TIA_6502TS_CORE_DRAW_COUNTER_DECODES
#define TIA_6502TS_CORE_DRAW_COUNTER_DECODES
#include "bspf.hxx"
namespace TIA6502tsCore {
class DrawCounterDecodes {
public:
const uInt8* const* playerDecodes() const;
const uInt8* const* missileDecodes() const;
static DrawCounterDecodes& get();
~DrawCounterDecodes();
protected:
DrawCounterDecodes();
private:
uInt8 *myPlayerDecodes[8];
uInt8 *myMissileDecodes[8];
uInt8 *myDecodes0, *myDecodes1, *myDecodes2, *myDecodes3, *myDecodes4, *myDecodes6, *myDecodesWide;
static DrawCounterDecodes myInstance;
private:
DrawCounterDecodes(const DrawCounterDecodes&) = delete;
DrawCounterDecodes(DrawCounterDecodes&&) = delete;
DrawCounterDecodes& operator=(const DrawCounterDecodes&) = delete;
DrawCounterDecodes& operator=(DrawCounterDecodes&&) = delete;
};
} // namespace TIA6502tsCore
#endif // TIA_6502TS_CORE_DRAW_COUNTER_DECODES

View File

@ -0,0 +1,121 @@
//============================================================================
//
// 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-2016 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.
//
// $Id$
//============================================================================
#include "Missile.hxx"
#include "DrawCounterDecodes.hxx"
enum Count {
renderCounterOffset = -4
};
namespace TIA6502tsCore {
uInt8 Missile::myWidths[4] = {1, 2, 4, 8};
Missile::Missile(uInt32 collisionMask)
: myCollisionMask(collisionMask)
{
reset();
}
void Missile::reset()
{
myDecodes = DrawCounterDecodes::get().missileDecodes()[0];
myEnabled = false;
myEnam = false;
myResmp = 0;
myHmmClocks = 0;
myCounter = 0;
myIsMoving = false;
myWidth = 1;
myIsRendering = false;
myRenderCounter = 0;
myColor = 0;
}
void Missile::enam(uInt8 value)
{
myEnam = (value & 0x02) > 0;
myEnabled = myEnam && (myResmp == 0);
}
void Missile::hmm(uInt8 value)
{
myHmmClocks = (value >> 4) ^ 0x08;
}
void Missile::resm(bool hblank)
{
myCounter = hblank ? 159 : 157;
}
void Missile::nusiz(uInt8 value)
{
myWidth = myWidths[(value & 0x30) >> 4];
myDecodes = DrawCounterDecodes::get().missileDecodes()[value & 0x07];
if (myIsRendering && myRenderCounter >= myWidth)
myIsRendering = false;
}
void Missile::startMovement()
{
myIsMoving = true;
}
bool Missile::movementTick(uInt32 clock, bool apply)
{
if (clock == myHmmClocks) myIsMoving = false;
if (myIsMoving && apply) {
render();
tick();
}
return myIsMoving;
}
void Missile::render()
{
collision = (myIsRendering && myRenderCounter >= 0 && myEnabled) ? 0 : myCollisionMask;
}
void Missile::tick()
{
if (myDecodes[myCounter]) {
myIsRendering = true;
myRenderCounter = Count::renderCounterOffset;
} else if (myIsRendering && ++myRenderCounter >= myWidth) {
myIsRendering = false;
}
if (++myCounter >= 160) myCounter = 0;
}
void Missile::setColor(uInt8 color)
{
myColor = color;
}
uInt8 Missile::getPixel(uInt8 colorIn) const
{
return collision ? colorIn : myColor;
}
} // namespace TIA6502tsCore

View File

@ -0,0 +1,94 @@
//============================================================================
//
// 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-2016 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.
//
// $Id$
//============================================================================
#ifndef TIA_6502TS_CORE_MISSILE
#define TIA_6502TS_CORE_MISSILE
#include "bspf.hxx"
namespace TIA6502tsCore {
class Missile {
public:
Missile(uInt32 collisionMask);
public:
void reset();
void enam(uInt8 value);
void hmm(uInt8 value);
void resm(bool hblank);
// TODO: resmp
void nusiz(uInt8 value);
void startMovement();
bool movementTick(uInt32 clock, bool apply);
void render();
void tick();
void setColor(uInt8 color);
uInt8 getPixel(uInt8 colorIn) const;
public:
uInt32 collision;
private:
uInt32 myCollisionMask;
bool myEnabled;
bool myEnam;
uInt8 myResmp;
uInt32 myHmmClocks;
uInt8 myCounter;
bool myIsMoving;
uInt8 myWidth;
bool myIsRendering;
Int8 myRenderCounter;
const uInt8 *myDecodes;
static uInt8 myWidths[4];
uInt8 myColor;
private:
Missile(const Missile&) = delete;
Missile(Missile&&) = delete;
Missile& operator=(const Missile&) = delete;
Missile& operator=(Missile&&) = delete;
};
} // namespace TIA6502tsCore
#endif // TIA_6502TS_CORE_MISSILE

View File

@ -48,11 +48,17 @@ TIA::TIA(Console& console, Sound& sound, Settings& settings)
mySound(sound),
mySettings(settings),
myDelayQueue(10, 20),
myPlayfield(CollisionMask::playfield)
myPlayfield(CollisionMask::playfield),
myMissile0(CollisionMask::missile0),
myMissile1(CollisionMask::missile1)
{
myFrameManager.setHandlers(
[this] () {onFrameStart();},
[this] () {onFrameComplete();}
[this] () {
myCurrentFrameBuffer.swap(myPreviousFrameBuffer);
},
[this] () {
mySystem->m6502().stop();
}
);
myCurrentFrameBuffer = make_ptr<uInt8[]>(160 * 320);
@ -74,10 +80,13 @@ void TIA::reset()
myCollisionMask = 0;
myLinesSinceChange = 0;
myCollisionUpdateRequired = false;
myColorBk = 0;
myLastCycle = 0;
myPlayfield.reset();
myMissile0.reset();
myMissile1.reset();
mySound.reset();
myDelayQueue.reset();
@ -139,7 +148,7 @@ bool TIA::poke(uInt16 address, uInt8 value)
switch (address) {
case WSYNC:
mySystem->incrementCycles((227 - myHctr) / 3);
mySystem->incrementCycles((227 - myHctr) / 3 + 1);
break;
case VSYNC:
@ -157,17 +166,32 @@ bool TIA::poke(uInt16 address, uInt8 value)
case AUDC0:
case AUDC1:
mySound.set(address, value, mySystem->cycles());
break;
case HMOVE:
myDelayQueue.push(HMOVE, value, Delay::hmove);
break;
case COLUBK:
myColorBk = value & 0xFE;
break;
case COLUP0:
myLinesSinceChange = 0;
myPlayfield.setColorP0(value & 0xFE);
value &= 0xFE;
myPlayfield.setColorP0(value);
myMissile0.setColor(value);
break;
case COLUP1:
myLinesSinceChange = 0;
myPlayfield.setColorP1(value & 0xFE);
value &= 0xFE;
myPlayfield.setColorP1(value);
myMissile1.setColor(value);
break;
@ -197,6 +221,57 @@ bool TIA::poke(uInt16 address, uInt8 value)
case PF2:
myDelayQueue.push(PF2, value, Delay::pf);
break;
case ENAM0:
myLinesSinceChange = 0;
myMissile0.enam(value);
break;
case ENAM1:
myLinesSinceChange = 0;
myMissile1.enam(value);
break;
case RESM0:
myLinesSinceChange = 0;
myMissile0.resm(myHstate == HState::blank);
break;
case RESM1:
myLinesSinceChange = 0;
myMissile1.resm(myHstate == HState::blank);
break;
case NUSIZ0:
myLinesSinceChange = 0;
myMissile0.nusiz(value);
break;
case NUSIZ1:
myLinesSinceChange = 0;
myMissile1.nusiz(value);
break;
case HMM0:
myDelayQueue.push(HMM0, value, Delay::hmm);
break;
case HMM1:
myDelayQueue.push(HMM1, value, Delay::hmm);
break;
case HMCLR:
myDelayQueue.push(HMCLR, value, Delay::hmclr);
break;
}
@ -379,7 +454,7 @@ void TIA::cycle(uInt32 colorClocks)
void TIA::tickMovement()
{
if (myMovementInProgress) return;
if (!myMovementInProgress) return;
if ((myHctr & 0x03) == 0) {
myLinesSinceChange = 0;
@ -388,7 +463,8 @@ void TIA::tickMovement()
bool m = false;
// TODO: propagate movement to sprites
m = myMissile0.movementTick(myMovementClock, apply) || m;
m = myMissile1.movementTick(myMovementClock, apply) || m;
myMovementInProgress = m;
myCollisionUpdateRequired = m;
@ -419,15 +495,28 @@ void TIA::tickHframe()
myPlayfield.tick(x);
// TODO: render sprites
if (lineNotCached)
renderSprites(x);
// TODO: tick sprites
tickSprites();
if (myFrameManager.isRendering()) renderPixel(x, y, lineNotCached);
if (++myHctr >= 228) nextLine();
}
void TIA::renderSprites(uInt32 x)
{
myMissile0.render();
myMissile1.render();
}
void TIA::tickSprites()
{
myMissile0.tick();
myMissile1.tick();
}
void TIA::nextLine()
{
myHctr = 0;
@ -448,11 +537,15 @@ void TIA::updateCollision()
void TIA::renderPixel(uInt32 x, uInt32 y, bool lineNotCached)
{
if (lineNotCached) {
uInt8 color = 0;
uInt8 color = myColorBk;
if (myPriority == Priority::normal) {
color = myPlayfield.getPixel(color);
color = myMissile1.getPixel(color);
color = myMissile0.getPixel(color);
} else {
color = myMissile1.getPixel(color);
color = myMissile0.getPixel(color);
color = myPlayfield.getPixel(color);
}
@ -462,19 +555,32 @@ void TIA::renderPixel(uInt32 x, uInt32 y, bool lineNotCached)
}
}
void TIA::onFrameComplete()
void TIA::clearHmoveComb()
{
mySystem->m6502().stop();
}
void TIA::onFrameStart()
{
myCurrentFrameBuffer.swap(myPreviousFrameBuffer);
if (myFrameManager.isRendering() && myHstate == HState::blank)
memset(myCurrentFrameBuffer.get() + myFrameManager.currentLine() * 160, 0, 8);
}
void TIA::delayedWrite(uInt8 address, uInt8 value)
{
switch (address) {
case HMOVE:
myLinesSinceChange = 0;
myMovementClock = 0;
myMovementInProgress = true;
if (!myExtendedHblank) {
myHblankCtr -= 8;
clearHmoveComb();
myExtendedHblank = true;
}
myMissile0.startMovement();
myMissile1.startMovement();
break;
case PF0:
myLinesSinceChange = 0;
myPlayfield.pf0(value);
@ -491,6 +597,25 @@ void TIA::delayedWrite(uInt8 address, uInt8 value)
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);
break;
}
}

View File

@ -28,6 +28,7 @@
#include "DelayQueue.hxx"
#include "FrameManager.hxx"
#include "Playfield.hxx"
#include "Missile.hxx"
class Console;
@ -137,8 +138,14 @@ class TIA : public AbstractTIA {
void updateCollision();
void renderSprites(uInt32 x);
void tickSprites();
void renderPixel(uInt32 x, uInt32 y, bool lineNotCached);
void clearHmoveComb();
void nextLine();
void onFrameComplete();
@ -159,8 +166,8 @@ class TIA : public AbstractTIA {
HState myHstate;
bool myIsFreshLine;
uInt32 myHblankCtr;
uInt32 myHctr;
Int32 myHblankCtr;
Int32 myHctr;
bool myCollisionUpdateRequired;
uInt32 myCollisionMask;
@ -175,10 +182,14 @@ class TIA : public AbstractTIA {
uInt32 myLastCycle;
uInt8 myColorBk;
BytePtr myCurrentFrameBuffer;
BytePtr myPreviousFrameBuffer;
Playfield myPlayfield;
Missile myMissile0;
Missile myMissile1;
private:

View File

@ -5,7 +5,9 @@ MODULE_OBJS := \
src/emucore/tia/core_6502ts/DelayQueueMember.o \
src/emucore/tia/core_6502ts/DelayQueue.o \
src/emucore/tia/core_6502ts/FrameManager.o \
src/emucore/tia/core_6502ts/Playfield.o
src/emucore/tia/core_6502ts/Playfield.o \
src/emucore/tia/core_6502ts/DrawCounterDecodes.o \
src/emucore/tia/core_6502ts/Missile.o
MODULE_DIRS += \