diff --git a/src/emucore/tia/core_6502ts/DrawCounterDecodes.cxx b/src/emucore/tia/core_6502ts/DrawCounterDecodes.cxx new file mode 100644 index 000000000..380fb5090 --- /dev/null +++ b/src/emucore/tia/core_6502ts/DrawCounterDecodes.cxx @@ -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; + } +} + +} \ No newline at end of file diff --git a/src/emucore/tia/core_6502ts/DrawCounterDecodes.hxx b/src/emucore/tia/core_6502ts/DrawCounterDecodes.hxx new file mode 100644 index 000000000..8c35a1443 --- /dev/null +++ b/src/emucore/tia/core_6502ts/DrawCounterDecodes.hxx @@ -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 diff --git a/src/emucore/tia/core_6502ts/Missile.cpp b/src/emucore/tia/core_6502ts/Missile.cpp new file mode 100644 index 000000000..6b2024f34 --- /dev/null +++ b/src/emucore/tia/core_6502ts/Missile.cpp @@ -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 \ No newline at end of file diff --git a/src/emucore/tia/core_6502ts/Missile.hxx b/src/emucore/tia/core_6502ts/Missile.hxx new file mode 100644 index 000000000..30620a55c --- /dev/null +++ b/src/emucore/tia/core_6502ts/Missile.hxx @@ -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 \ No newline at end of file diff --git a/src/emucore/tia/core_6502ts/TIA.cxx b/src/emucore/tia/core_6502ts/TIA.cxx index 3c0ae98bd..a98ccb575 100644 --- a/src/emucore/tia/core_6502ts/TIA.cxx +++ b/src/emucore/tia/core_6502ts/TIA.cxx @@ -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(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; } } diff --git a/src/emucore/tia/core_6502ts/TIA.hxx b/src/emucore/tia/core_6502ts/TIA.hxx index 186fffc22..df1ac47f6 100644 --- a/src/emucore/tia/core_6502ts/TIA.hxx +++ b/src/emucore/tia/core_6502ts/TIA.hxx @@ -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: diff --git a/src/emucore/tia/core_6502ts/module.mk b/src/emucore/tia/core_6502ts/module.mk index e6a7a5d1c..d743d5668 100644 --- a/src/emucore/tia/core_6502ts/module.mk +++ b/src/emucore/tia/core_6502ts/module.mk @@ -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 += \