1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +00:00

Made an attempt better to generalise the idea of things with 4 CLK delays.

This commit is contained in:
Thomas Harte 2016-05-16 21:54:27 -04:00
parent 4ad074fc78
commit 3d003070b3
2 changed files with 121 additions and 67 deletions

View File

@ -23,7 +23,8 @@ Machine::Machine() :
_rom(nullptr), _rom(nullptr),
_hMoveWillCount(false), _hMoveWillCount(false),
_piaDataValue{0xff, 0xff}, _piaDataValue{0xff, 0xff},
_tiaInputValue{0xff, 0xff} _tiaInputValue{0xff, 0xff},
_upcomingEventsPointer(0)
{ {
memset(_collisions, 0xff, sizeof(_collisions)); memset(_collisions, 0xff, sizeof(_collisions));
set_reset_line(true); set_reset_line(true);
@ -77,17 +78,33 @@ Machine::~Machine()
close_output(); close_output();
} }
uint8_t Machine::get_output_pixel() void Machine::update_upcoming_event()
{ {
// get the playfield pixel and hence a proposed colour _upcomingEvents[_upcomingEventsPointer].updates = 0;
unsigned int offset = _horizontalTimer - (horizontalTimerPeriod - 160);
unsigned int offset = 4 + _horizontalTimer - (horizontalTimerPeriod - 160);
if(!(offset&3)) if(!(offset&3))
{ {
_playfieldPixel = _nextPlayfieldPixel; _upcomingEvents[_upcomingEventsPointer].updates |= Event::Action::Playfield;
_nextPlayfieldPixel = _playfield[(1 + (offset >> 2))%40]; _upcomingEvents[_upcomingEventsPointer].playfieldOutput = _playfield[(offset >> 2)%40];
} }
}
uint8_t Machine::get_output_pixel()
{
unsigned int offset = _horizontalTimer - (horizontalTimerPeriod - 160);
// get the playfield pixel and hence a proposed colour
uint8_t playfieldColour = ((_playfieldControl&6) == 2) ? _playerColour[offset / 80] : _playfieldColour; uint8_t playfieldColour = ((_playfieldControl&6) == 2) ? _playerColour[offset / 80] : _playfieldColour;
// get the ball proposed colour
// uint8_t ballPixel = 0;
// if(_ballGraphicsEnable&2) {
// int ballIndex = _objectCounter[4];
// int ballSize = 1 << ((_playfieldControl >> 4)&3);
// ballPixel = (ballIndex >= 0 && ballIndex < ballSize) ? 1 : 0;
// }
// get player and missile proposed pixels // get player and missile proposed pixels
/* uint8_t playerPixels[2] = {0, 0}, missilePixels[2] = {0, 0}; /* uint8_t playerPixels[2] = {0, 0}, missilePixels[2] = {0, 0};
for(int c = 0; c < 2; c++) for(int c = 0; c < 2; c++)
@ -141,14 +158,6 @@ uint8_t Machine::get_output_pixel()
} }
} }
// get the ball proposed colour
uint8_t ballPixel = 0;
if(_ballGraphicsEnable&2) {
int ballIndex = _objectCounter[4] - 4;
int ballSize = 1 << ((_playfieldControl >> 4)&3);
ballPixel = (ballIndex >= 0 && ballIndex < ballSize) ? 1 : 0;
}
// accumulate collisions // accumulate collisions
if(playerPixels[0] | playerPixels[1]) { if(playerPixels[0] | playerPixels[1]) {
_collisions[0] |= ((missilePixels[0] & playerPixels[1]) << 7) | ((missilePixels[0] & playerPixels[0]) << 6); _collisions[0] |= ((missilePixels[0] & playerPixels[1]) << 7) | ((missilePixels[0] & playerPixels[0]) << 6);
@ -180,12 +189,12 @@ uint8_t Machine::get_output_pixel()
}*/ }*/
// return colour // return colour
return _playfieldPixel ? playfieldColour : _backgroundColour; return _playfieldOutput ? playfieldColour : _backgroundColour;
} }
// in imputing the knowledge that all we're dealing with is the rollover from 159 to 0, // in imputing the knowledge that all we're dealing with is the rollover from 159 to 0,
// this is faster than the straightforward +1)%160 per profiling // this is faster than the straightforward +1)%160 per profiling
#define increment_object_counter(c) _objectCounter[c] = (_objectCounter[c]+1)&~((158-_objectCounter[c]) >> 8) //#define increment_object_counter(c) _objectCounter[c] = (_objectCounter[c]+1)&~((158-_objectCounter[c]) >> 8)
void Machine::output_pixels(unsigned int count) void Machine::output_pixels(unsigned int count)
{ {
@ -193,13 +202,14 @@ void Machine::output_pixels(unsigned int count)
{ {
OutputState state; OutputState state;
// determine which output will start this cycle; all outputs are delayed by 4 CLKs momentarily...
switch(_horizontalTimer >> 2) switch(_horizontalTimer >> 2)
{ {
case 0: case 1: case 2: case 3: state = OutputState::Blank; break; case 227: case 0: case 1: case 2: state = OutputState::Blank; break;
case 4: case 5: case 6: case 7: state = OutputState::Sync; break; case 3: case 4: case 5: case 6: state = OutputState::Sync; break;
case 8: case 9: case 10: case 11: state = OutputState::ColourBurst; break; case 7: case 8: case 9: case 10: state = OutputState::ColourBurst; break;
case 12: case 13: case 14: case 15: case 16: state = OutputState::Blank; break; case 11: case 12: case 13: case 14: case 15: state = OutputState::Blank; break;
case 17: case 18: state = _vBlankExtend ? OutputState::Blank : OutputState::Pixel; break; case 16: case 17: state = _vBlankExtend ? OutputState::Blank : OutputState::Pixel; break;
default: state = OutputState::Pixel; break; default: state = OutputState::Pixel; break;
} }
@ -208,6 +218,57 @@ void Machine::output_pixels(unsigned int count)
state = (state = OutputState::Sync) ? OutputState::Blank : OutputState::Sync; state = (state = OutputState::Sync) ? OutputState::Blank : OutputState::Sync;
} }
// write that state as the one that will become effective in four clocks
_upcomingEvents[_upcomingEventsPointer].state = state;
// grab pixel state if desired
if(state == OutputState::Pixel)
{
update_upcoming_event();
}
// advance, hitting the state that will become active now
_upcomingEventsPointer = (_upcomingEventsPointer + 1)&3;
// apply any queued changes
if(_upcomingEvents[_upcomingEventsPointer].updates & Event::Action::Playfield)
{
_playfieldOutput = _upcomingEvents[_upcomingEventsPointer].playfieldOutput;
}
// read that state
state = _upcomingEvents[_upcomingEventsPointer].state;
// decide what that means needs to be communicated to the CRT
_lastOutputStateDuration++;
if(state != _lastOutputState) {
switch(_lastOutputState) {
case OutputState::Blank: _crt->output_blank(_lastOutputStateDuration); break;
case OutputState::Sync: _crt->output_sync(_lastOutputStateDuration); break;
case OutputState::ColourBurst: _crt->output_colour_burst(_lastOutputStateDuration, 96, 0); break;
case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration, 1); break;
}
_lastOutputStateDuration = 0;
_lastOutputState = state;
if(state == OutputState::Pixel) {
_outputBuffer = _crt->allocate_write_area(160);
} else {
_outputBuffer = nullptr;
}
}
// decide on a pixel colour if that's what's happening
if(state == OutputState::Pixel)
{
uint8_t colour = get_output_pixel();
if(_outputBuffer)
{
*_outputBuffer = colour;
_outputBuffer++;
}
}
// update hmove // update hmove
// if(!(_horizontalTimer&3)) { // if(!(_horizontalTimer&3)) {
// //
@ -225,43 +286,24 @@ void Machine::output_pixels(unsigned int count)
// } // }
// } // }
// if(state == OutputState::Pixel)
_lastOutputStateDuration++; // {
if(state != _lastOutputState) { // uint8_t colour = get_output_pixel();
switch(_lastOutputState) { // if(_outputBuffer)
case OutputState::Blank: _crt->output_blank(_lastOutputStateDuration); break; // {
case OutputState::Sync: _crt->output_sync(_lastOutputStateDuration); break; // *_outputBuffer = colour;
case OutputState::ColourBurst: _crt->output_colour_burst(_lastOutputStateDuration, 96, 0); break; // _outputBuffer++;
case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration, 1); break; // }
} // }
_lastOutputStateDuration = 0; // else
_lastOutputState = state; // {
if(state == OutputState::Pixel) {
_outputBuffer = _crt->allocate_write_area(160);
} else {
_outputBuffer = nullptr;
}
}
if(state == OutputState::Pixel)
{
uint8_t colour = get_output_pixel();
if(_outputBuffer)
{
*_outputBuffer = colour;
_outputBuffer++;
}
}
else
{
// fetch this for the entire blank period just to ensure it's in place when needed // fetch this for the entire blank period just to ensure it's in place when needed
if(!(_horizontalTimer&3)) // if(!(_horizonta lTimer&3))
{ // {
unsigned int offset = 4 + _horizontalTimer - (horizontalTimerPeriod - 160); // unsigned int offset = 4 + _horizontalTimer - (horizontalTimerPeriod - 160);
_nextPlayfieldPixel = _playfield[(offset >> 2)%40]; // _nextPlayfieldPixel = _playfield[(offset >> 2)%40];
} // }
} // }
/* if(_horizontalTimer < (_vBlankExtend ? 152 : 160)) { /* if(_horizontalTimer < (_vBlankExtend ? 152 : 160)) {
uint8_t throwaway_pixel; uint8_t throwaway_pixel;
@ -275,6 +317,7 @@ void Machine::output_pixels(unsigned int count)
increment_object_counter(4); increment_object_counter(4);
}*/ }*/
// advance horizontal timer, perform reset actions if requested
_horizontalTimer = (_horizontalTimer + 1) % horizontalTimerPeriod; _horizontalTimer = (_horizontalTimer + 1) % horizontalTimerPeriod;
if(!_horizontalTimer) if(!_horizontalTimer)
{ {

View File

@ -48,9 +48,26 @@ class Machine: public CPU6502::Processor<Machine> {
uint8_t _backgroundColour; uint8_t _backgroundColour;
uint8_t _playfield[40]; uint8_t _playfield[40];
// playfield outputs // delayed clock events
uint8_t _playfieldPixel; // the pixel currently being output enum OutputState {
uint8_t _nextPlayfieldPixel; // the next pixel to be output; latched ahead of time Sync,
Blank,
ColourBurst,
Pixel
};
struct Event {
enum Action {
OutputSate = 1 << 0,
Playfield = 1 << 1,
};
unsigned int updates;
uint8_t playfieldOutput;
OutputState state;
} _upcomingEvents[4];
unsigned int _upcomingEventsPointer;
uint8_t _playfieldOutput;
// player registers // player registers
uint8_t _playerColour[2]; uint8_t _playerColour[2];
@ -86,15 +103,9 @@ class Machine: public CPU6502::Processor<Machine> {
// collisions // collisions
uint8_t _collisions[8]; uint8_t _collisions[8];
enum OutputState {
Sync,
Blank,
ColourBurst,
Pixel
};
void output_pixels(unsigned int count); void output_pixels(unsigned int count);
uint8_t get_output_pixel(); uint8_t get_output_pixel();
void update_upcoming_event();
Outputs::CRT::CRT *_crt; Outputs::CRT::CRT *_crt;
// latched output state // latched output state