mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 23:52:26 +00:00
I'm back to thinking that resets are deferred but pixel counts are live.
This commit is contained in:
parent
758806f924
commit
b01b474e36
@ -23,8 +23,7 @@ Machine::Machine() :
|
||||
_rom(nullptr),
|
||||
_piaDataValue{0xff, 0xff},
|
||||
_tiaInputValue{0xff, 0xff},
|
||||
_upcomingEventsPointer(0),
|
||||
_upcomingPixelsPointer(0)
|
||||
_upcomingEventsPointer(0)
|
||||
{
|
||||
memset(_collisions, 0xff, sizeof(_collisions));
|
||||
set_reset_line(true);
|
||||
@ -80,11 +79,9 @@ Machine::~Machine()
|
||||
|
||||
void Machine::update_timers(int mask)
|
||||
{
|
||||
unsigned int upcomingEventsPointerPlus4 = (_upcomingEventsPointer + 4)%number_of_upcoming_events;
|
||||
unsigned int upcomingEventsPointerPlus1 = (_upcomingEventsPointer + 1)%number_of_upcoming_events;
|
||||
unsigned int upcomingEventsPointerPlus2 = (_upcomingEventsPointer + 2)%number_of_upcoming_events;
|
||||
|
||||
unsigned int upcomingPixelsPointerPlus4 = (_upcomingPixelsPointer + 4)%number_of_upcoming_events;
|
||||
unsigned int upcomingPointerPlus4 = (_upcomingEventsPointer + 4)%number_of_upcoming_events;
|
||||
unsigned int upcomingPointerPlus5 = (_upcomingEventsPointer + 5)%number_of_upcoming_events;
|
||||
unsigned int upcomingPointerPlus6 = (_upcomingEventsPointer + 6)%number_of_upcoming_events;
|
||||
|
||||
// grab the background now, for display in four clocks
|
||||
if(mask & (1 << 5))
|
||||
@ -92,8 +89,8 @@ void Machine::update_timers(int mask)
|
||||
if(!(_horizontalTimer&3))
|
||||
{
|
||||
unsigned int offset = 4 + _horizontalTimer - (horizontalTimerPeriod - 160);
|
||||
_upcomingEvents[upcomingEventsPointerPlus4].updates |= Event::Action::Playfield;
|
||||
_upcomingEvents[upcomingEventsPointerPlus4].playfieldPixel = _playfield[(offset >> 2)%40];
|
||||
_upcomingEvents[upcomingPointerPlus4].updates |= Event::Action::Playfield;
|
||||
_upcomingEvents[upcomingPointerPlus4].playfieldPixel = _playfield[(offset >> 2)%40];
|
||||
}
|
||||
}
|
||||
|
||||
@ -101,15 +98,9 @@ void Machine::update_timers(int mask)
|
||||
{
|
||||
// the ball becomes visible whenever it hits zero, regardless of whether its status
|
||||
// is the result of a counter rollover or a programmatic reset
|
||||
if(!_objectCounter[4]) _pixelCounter[4] = 0;
|
||||
|
||||
if(_pixelCounter[4] < 8 && _ballGraphicsEnable[_ballGraphicsSelector]&2) {
|
||||
int ballSize = 1 << ((_playfieldControl >> 4)&3);
|
||||
_upcomingPixels[upcomingPixelsPointerPlus4] |= (_pixelCounter[4] < ballSize) ? (1 << 4) : 0;
|
||||
}
|
||||
|
||||
_objectCounter[4] = (_objectCounter[4] + 1)%160;
|
||||
_pixelCounter[4] ++;
|
||||
if(!_objectCounter[4].count) _upcomingEvents[upcomingPointerPlus4].pixelCounterResetMask &= ~(1 << 4);
|
||||
_objectCounter[4].count = (_objectCounter[4].count + 1)%160;
|
||||
_objectCounter[4].pixel ++;
|
||||
}
|
||||
|
||||
// check for player and missle triggers
|
||||
@ -119,11 +110,10 @@ void Machine::update_timers(int mask)
|
||||
{
|
||||
// the players and missles become visible only upon overflow to zero, so schedule for
|
||||
// 1/2 clocks ahead from 159
|
||||
if(_objectCounter[c] == 159)
|
||||
if(_objectCounter[c].count == 159)
|
||||
{
|
||||
unsigned int actionSlot = (c < 2) ? upcomingEventsPointerPlus2 : upcomingEventsPointerPlus1;
|
||||
_upcomingEvents[actionSlot].updates |= Event::Action::ResetPixelCounter;
|
||||
_upcomingEvents[actionSlot].pixelCounterMask |= (1 << c);
|
||||
unsigned int actionSlot = (c < 2) ? upcomingPointerPlus6 : upcomingPointerPlus5;
|
||||
_upcomingEvents[actionSlot].pixelCounterResetMask &= ~(1 << c);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -131,14 +121,13 @@ void Machine::update_timers(int mask)
|
||||
// in which case the counter reset (and hence the start of drawing) will occur in 4/5 cycles
|
||||
uint8_t repeatMask = _playerAndMissileSize[c&1] & 7;
|
||||
if(
|
||||
( _objectCounter[c] == 16 && ((repeatMask == 1) || (repeatMask == 3)) ) ||
|
||||
( _objectCounter[c] == 32 && ((repeatMask == 2) || (repeatMask == 3) || (repeatMask == 6)) ) ||
|
||||
( _objectCounter[c] == 64 && ((repeatMask == 4) || (repeatMask == 6)) )
|
||||
( _objectCounter[c].count == 16 && ((repeatMask == 1) || (repeatMask == 3)) ) ||
|
||||
( _objectCounter[c].count == 32 && ((repeatMask == 2) || (repeatMask == 3) || (repeatMask == 6)) ) ||
|
||||
( _objectCounter[c].count == 64 && ((repeatMask == 4) || (repeatMask == 6)) )
|
||||
)
|
||||
{
|
||||
unsigned int actionSlot = (c < 2) ? upcomingEventsPointerPlus1 : _upcomingEventsPointer;
|
||||
_upcomingEvents[actionSlot].updates |= Event::Action::ResetPixelCounter;
|
||||
_upcomingEvents[actionSlot].pixelCounterMask |= (1 << c);
|
||||
unsigned int actionSlot = (c < 2) ? upcomingPointerPlus5 : upcomingPointerPlus4;
|
||||
_upcomingEvents[actionSlot].pixelCounterResetMask &= ~(1 << c);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -149,54 +138,23 @@ void Machine::update_timers(int mask)
|
||||
{
|
||||
if(mask & (1 << c))
|
||||
{
|
||||
int lastSpriteCounter = _spriteCounter[c];
|
||||
_spriteCounter[c]++;
|
||||
int last_broad_pixel = _objectCounter[c].broad_pixel;
|
||||
_objectCounter[c].broad_pixel++;
|
||||
|
||||
uint8_t repeatMask = _playerAndMissileSize[c] & 7;
|
||||
switch(repeatMask)
|
||||
{
|
||||
default: _pixelCounter[c] ++; break;
|
||||
case 5: _pixelCounter[c] += ((lastSpriteCounter ^ _spriteCounter[c]) >> 1)&1; break;
|
||||
case 7: _pixelCounter[c] += ((lastSpriteCounter ^ _spriteCounter[c]) >> 2)&1; break;
|
||||
default: _objectCounter[c].pixel ++; break;
|
||||
case 5: _objectCounter[c].pixel += ((last_broad_pixel ^ _objectCounter[c].broad_pixel) >> 1)&1; break;
|
||||
case 7: _objectCounter[c].pixel += ((last_broad_pixel ^ _objectCounter[c].broad_pixel) >> 2)&1; break;
|
||||
}
|
||||
_objectCounter[c] = (_objectCounter[c] + 1)%160;
|
||||
_objectCounter[c].count = (_objectCounter[c].count + 1)%160;
|
||||
}
|
||||
|
||||
if(mask & (1 << (c + 2)))
|
||||
{
|
||||
_objectCounter[c+2] = (_objectCounter[c+2] + 1)%160;
|
||||
_pixelCounter[c+2] ++;
|
||||
}
|
||||
}
|
||||
|
||||
// apply any resets
|
||||
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::ResetPixelCounter)
|
||||
{
|
||||
for(int c = 0; c < 5; c++)
|
||||
{
|
||||
if(_upcomingEvents[_upcomingEventsPointer].pixelCounterMask & (1 << c))
|
||||
_pixelCounter[c] = 0;
|
||||
}
|
||||
_upcomingEvents[_upcomingEventsPointer].pixelCounterMask = 0;
|
||||
}
|
||||
|
||||
// reload the playfield pixel if appropriate
|
||||
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::Playfield)
|
||||
{
|
||||
_playfieldOutput = _upcomingEvents[_upcomingEventsPointer].playfieldPixel;
|
||||
}
|
||||
|
||||
// determine the pixel masks
|
||||
for(int c = 0; c < 2; c++)
|
||||
{
|
||||
if(_playerGraphics[c] && _pixelCounter[c] < 32) {
|
||||
int flipMask = (_playerReflection[c]&0x8) ? 0 : 7;
|
||||
_upcomingPixels[upcomingPixelsPointerPlus4] |= ((_playerGraphics[_playerGraphicsSelector[c]][c] >> (_pixelCounter[c] ^ flipMask)) & 1) << c;
|
||||
}
|
||||
|
||||
if(_pixelCounter[c+2] < 8 && (_missileGraphicsEnable[c]&2) && !_missileGraphicsReset[c]) {
|
||||
int missileSize = 1 << ((_playerAndMissileSize[c] >> 4)&3);
|
||||
_upcomingPixels[upcomingPixelsPointerPlus4] |= ((_pixelCounter[c+2] < missileSize) ? 1 : 0) << (c + 2);
|
||||
_objectCounter[c+2].count = (_objectCounter[c+2].count + 1)%160;
|
||||
_objectCounter[c+2].pixel ++;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -208,20 +166,27 @@ uint8_t Machine::get_output_pixel()
|
||||
// get the playfield pixel and hence a proposed colour
|
||||
uint8_t playfieldColour = ((_playfieldControl&6) == 2) ? _playerColour[offset / 80] : _playfieldColour;
|
||||
|
||||
// get the ball proposed state
|
||||
uint8_t ballPixel = (_upcomingPixels[_upcomingPixelsPointer] >> 4) & 1;
|
||||
uint8_t ballPixel = 0;
|
||||
if(_objectCounter[4].pixel < 8 && _ballGraphicsEnable[_ballGraphicsSelector]&2) {
|
||||
int ballSize = 1 << ((_playfieldControl >> 4)&3);
|
||||
ballPixel = (_objectCounter[4].pixel < ballSize) ? 1 : 0;
|
||||
}
|
||||
|
||||
// deal with the sprites
|
||||
uint8_t playerPixels[2] =
|
||||
// determine the pixel masks
|
||||
uint8_t playerPixels[2] = { 0, 0 };
|
||||
uint8_t missilePixels[2] = { 0, 0 };
|
||||
for(int c = 0; c < 2; c++)
|
||||
{
|
||||
static_cast<uint8_t>((_upcomingPixels[_upcomingPixelsPointer] >> 0) & 1),
|
||||
static_cast<uint8_t>((_upcomingPixels[_upcomingPixelsPointer] >> 1) & 1)
|
||||
};
|
||||
uint8_t missilePixels[2] =
|
||||
{
|
||||
static_cast<uint8_t>((_upcomingPixels[_upcomingPixelsPointer] >> 2) & 1),
|
||||
static_cast<uint8_t>((_upcomingPixels[_upcomingPixelsPointer] >> 3) & 1),
|
||||
};
|
||||
if(_playerGraphics[c] && _objectCounter[c].pixel < 8) {
|
||||
int flipMask = (_playerReflection[c]&0x8) ? 0 : 7;
|
||||
playerPixels[c] = (_playerGraphics[_playerGraphicsSelector[c]][c] >> (_objectCounter[c].pixel ^ flipMask)) & 1;
|
||||
}
|
||||
|
||||
if(_objectCounter[c+2].pixel < 8 && (_missileGraphicsEnable[c]&2) && !_missileGraphicsReset[c]) {
|
||||
int missileSize = 1 << ((_playerAndMissileSize[c] >> 4)&3);
|
||||
missilePixels[c] = (_objectCounter[c+2].pixel < missileSize) ? 1 : 0;
|
||||
}
|
||||
}
|
||||
|
||||
// accumulate collisions
|
||||
if(playerPixels[0] | playerPixels[1]) {
|
||||
@ -253,10 +218,6 @@ uint8_t Machine::get_output_pixel()
|
||||
if(playerPixels[0] || missilePixels[0]) outputColour = _playerColour[0];
|
||||
}
|
||||
|
||||
// update pixel chain
|
||||
_upcomingPixels[_upcomingPixelsPointer] = 0;
|
||||
_upcomingPixelsPointer = (_upcomingPixelsPointer + 1)%number_of_upcoming_events;
|
||||
|
||||
// return colour
|
||||
return outputColour;
|
||||
}
|
||||
@ -319,7 +280,7 @@ void Machine::output_pixels(unsigned int count)
|
||||
{
|
||||
for(int c = 0; c < 5; c++)
|
||||
{
|
||||
if(((_objectMotion[c] >> 4)^_hMoveCounter) == 7)
|
||||
if(((_objectCounter[c].motion >> 4)^_hMoveCounter) == 7)
|
||||
{
|
||||
_hMoveFlags &= ~(1 << c);
|
||||
}
|
||||
@ -337,6 +298,21 @@ void Machine::output_pixels(unsigned int count)
|
||||
update_timers(_hMoveFlags);
|
||||
}
|
||||
|
||||
// apply any resets
|
||||
_objectCounter[0].pixel *= (_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask >> 0) & 1;
|
||||
_objectCounter[0].broad_pixel *= (_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask >> 0) & 1;
|
||||
_objectCounter[1].pixel *= (_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask >> 1) & 1;
|
||||
_objectCounter[1].broad_pixel *= (_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask >> 1) & 1;
|
||||
_objectCounter[2].pixel *= (_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask >> 2) & 1;
|
||||
_objectCounter[3].pixel *= (_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask >> 3) & 1;
|
||||
_objectCounter[4].pixel *= (_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask >> 4) & 1;
|
||||
|
||||
// reload the playfield pixel if appropriate
|
||||
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::Playfield)
|
||||
{
|
||||
_playfieldOutput = _upcomingEvents[_upcomingEventsPointer].playfieldPixel;
|
||||
}
|
||||
|
||||
// read that state
|
||||
state = _upcomingEvents[_upcomingEventsPointer].state;
|
||||
OutputState actingState = state;
|
||||
@ -378,6 +354,7 @@ void Machine::output_pixels(unsigned int count)
|
||||
|
||||
// advance
|
||||
_upcomingEvents[_upcomingEventsPointer].updates = 0;
|
||||
_upcomingEvents[_upcomingEventsPointer].pixelCounterResetMask = ~0;
|
||||
_upcomingEventsPointer = (_upcomingEventsPointer + 1)%number_of_upcoming_events;
|
||||
|
||||
// advance horizontal timer, perform reset actions if requested
|
||||
@ -566,7 +543,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
|
||||
case 0x10: case 0x11: case 0x12: case 0x13:
|
||||
case 0x14:
|
||||
_objectCounter[decodedAddress - 0x10] = 0;
|
||||
_objectCounter[decodedAddress - 0x10].count = 0;
|
||||
break;
|
||||
|
||||
case 0x1c:
|
||||
@ -587,7 +564,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x24:
|
||||
_objectMotion[decodedAddress - 0x20] = *value;
|
||||
_objectCounter[decodedAddress - 0x20].motion = *value;
|
||||
break;
|
||||
|
||||
case 0x25: _playerGraphicsSelector[0] = (*value)&1; break;
|
||||
@ -600,7 +577,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
int index = decodedAddress - 0x28;
|
||||
if(!(*value&0x02) && _missileGraphicsReset[index])
|
||||
{
|
||||
_objectCounter[index + 2] = _objectCounter[index];
|
||||
_objectCounter[index + 2].count = _objectCounter[index].count;
|
||||
|
||||
uint8_t repeatMask = _playerAndMissileSize[index] & 7;
|
||||
int extra_offset;
|
||||
@ -611,7 +588,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
case 7: extra_offset = 10; break;
|
||||
}
|
||||
|
||||
_objectCounter[index + 2] = (_objectCounter[index + 2] + extra_offset)%160;
|
||||
_objectCounter[index + 2].count = (_objectCounter[index + 2].count + extra_offset)%160;
|
||||
}
|
||||
_missileGraphicsReset[index] = (*value) & 0x02;
|
||||
}
|
||||
@ -624,11 +601,11 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
||||
_upcomingEvents[(_upcomingEventsPointer + 5)%number_of_upcoming_events].updates |= Event::Action::HMoveSetup;
|
||||
break;
|
||||
case 0x2b:
|
||||
_objectMotion[0] =
|
||||
_objectMotion[1] =
|
||||
_objectMotion[2] =
|
||||
_objectMotion[3] =
|
||||
_objectMotion[4] = 0;
|
||||
_objectCounter[0].motion =
|
||||
_objectCounter[1].motion =
|
||||
_objectCounter[2].motion =
|
||||
_objectCounter[3].motion =
|
||||
_objectCounter[4].motion = 0;
|
||||
break;
|
||||
case 0x2c:
|
||||
_collisions[0] = _collisions[1] = _collisions[2] =
|
||||
|
@ -16,7 +16,7 @@
|
||||
|
||||
namespace Atari2600 {
|
||||
|
||||
const unsigned int number_of_upcoming_events = 6;
|
||||
const unsigned int number_of_upcoming_events = 7;
|
||||
|
||||
class Machine: public CPU6502::Processor<Machine> {
|
||||
|
||||
@ -61,26 +61,20 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
struct Event {
|
||||
enum Action {
|
||||
Playfield = 1 << 0,
|
||||
|
||||
ResetPixelCounter = 1 << 3,
|
||||
|
||||
HMoveSetup = 1 << 4,
|
||||
HMoveCompare = 1 << 5,
|
||||
HMoveDecrement = 1 << 6,
|
||||
HMoveSetup = 1 << 1,
|
||||
HMoveCompare = 1 << 2,
|
||||
HMoveDecrement = 1 << 3,
|
||||
};
|
||||
int updates;
|
||||
|
||||
int pixelCounterMask;
|
||||
OutputState state;
|
||||
int pixelCounterResetMask;
|
||||
uint8_t playfieldPixel;
|
||||
|
||||
Event() : updates(0), pixelCounterMask(0), playfieldPixel(0) {}
|
||||
Event() : updates(0), pixelCounterResetMask(~0), playfieldPixel(0) {}
|
||||
} _upcomingEvents[number_of_upcoming_events];
|
||||
unsigned int _upcomingEventsPointer;
|
||||
|
||||
uint8_t _upcomingPixels[number_of_upcoming_events];
|
||||
unsigned int _upcomingPixelsPointer;
|
||||
|
||||
uint8_t _playfieldOutput;
|
||||
|
||||
// player registers
|
||||
@ -108,11 +102,12 @@ class Machine: public CPU6502::Processor<Machine> {
|
||||
// horizontal motion control
|
||||
uint8_t _hMoveCounter;
|
||||
uint8_t _hMoveFlags;
|
||||
uint8_t _objectMotion[5];
|
||||
|
||||
// object counters
|
||||
uint8_t _objectCounter[5];
|
||||
int _pixelCounter[5], _spriteCounter[2];
|
||||
struct {
|
||||
int count, pixel, broad_pixel;
|
||||
uint8_t motion;
|
||||
} _objectCounter[5];
|
||||
|
||||
// joystick state
|
||||
uint8_t _piaDataDirection[2];
|
||||
|
Loading…
Reference in New Issue
Block a user