1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-04-09 00:37:27 +00:00

Set about documenting the CRT; while doing so decided to add an optional clock divider for input; having done so decided to try to exploit it with the Electron.

This commit is contained in:
Thomas Harte 2016-01-21 21:17:47 -05:00
parent baef1ccd57
commit 48ddd3c497
5 changed files with 103 additions and 31 deletions

View File

@ -223,7 +223,7 @@ void Machine::output_pixels(unsigned int count)
{
case OutputState::Blank: _crt->output_blank(_lastOutputStateDuration); break;
case OutputState::Sync: _crt->output_sync(_lastOutputStateDuration); break;
case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration); break;
case OutputState::Pixel: _crt->output_data(_lastOutputStateDuration, 1); break;
}
_lastOutputStateDuration = 0;
_lastOutputState = state;

View File

@ -12,10 +12,10 @@
using namespace Electron;
static const int cycles_per_line = 128;
static const int cycles_per_frame = 312*cycles_per_line;
static const int crt_cycles_multiplier = 8;
static const int crt_cycles_per_line = crt_cycles_multiplier * cycles_per_line;
static const unsigned int cycles_per_line = 128;
static const unsigned int cycles_per_frame = 312*cycles_per_line;
static const unsigned int crt_cycles_multiplier = 8;
static const unsigned int crt_cycles_per_line = crt_cycles_multiplier * cycles_per_line;
Machine::Machine() :
_interruptControl(0),
@ -459,8 +459,15 @@ inline void Machine::update_display()
_crt->output_blank(15 * crt_cycles_multiplier);
_displayOutputPosition += 15;
_crt->allocate_write_area(80 * crt_cycles_multiplier);
_currentLine = (uint8_t *)_crt->get_write_target_for_buffer(0);
switch(_screenMode)
{
case 0: case 3: _currentOutputDivider = 1; break;
case 1: case 4: case 6: _currentOutputDivider = 2; break;
case 2: case 5: _currentOutputDivider = 4; break;
}
_crt->allocate_write_area(80 * crt_cycles_multiplier / _currentOutputDivider);
_currentLine = _writePointer = (uint8_t *)_crt->get_write_target_for_buffer(0);
if(current_line == first_graphics_line)
_startLineAddress = _startScreenAddress;
@ -469,6 +476,32 @@ inline void Machine::update_display()
if(line_position >= 24 && line_position < 104)
{
unsigned int newDivider = 0;
switch(_screenMode)
{
case 0: case 3: newDivider = 1; break;
case 1: case 4: case 6: newDivider = 2; break;
case 2: case 5: newDivider = 4; break;
}
if(newDivider != _currentOutputDivider)
{
_crt->output_data((unsigned int)((_writePointer - _currentLine) * _currentOutputDivider * crt_cycles_multiplier), _currentOutputDivider);
_currentOutputDivider = newDivider;
_crt->allocate_write_area((int)((104 - (unsigned int)line_position) * crt_cycles_multiplier / _currentOutputDivider));
_currentLine = _writePointer = (uint8_t *)_crt->get_write_target_for_buffer(0);
}
// TODO: determine whether we need to change divider
// int pixels_to_output = std::max(_frameCycles - _displayOutputPosition, 104 - line_position);
// if(_screenMode >= 4)
// {
// // just shifting wouldn't be enough if both
// if(_displayOutputPosition&1) pixels_to_output++;
// pixels_to_output >>= 1;
// }
//
// swi
if(_currentLine && ((_screenMode < 4) || !(line_position&1)))
{
if(_currentScreenAddress&32768)
@ -477,7 +510,6 @@ inline void Machine::update_display()
}
uint8_t pixels = _ram[_currentScreenAddress];
_currentScreenAddress = _currentScreenAddress+8;
int output_ptr = (line_position - 24) << 3;
switch(_screenMode)
{
@ -486,49 +518,52 @@ inline void Machine::update_display()
for(int c = 0; c < 8; c++)
{
uint8_t colour = (pixels&0x80) >> 4;
_currentLine[output_ptr + c] = _palette[colour];
_writePointer[c] = _palette[colour];
pixels <<= 1;
}
_writePointer += 8;
break;
case 1:
for(int c = 0; c < 8; c += 2)
for(int c = 0; c < 4; c ++)
{
uint8_t colour = ((pixels&0x80) >> 4) | ((pixels&0x08) >> 2);
_currentLine[output_ptr + c + 0] = _currentLine[output_ptr + c + 1] = _palette[colour];
_writePointer[c] = _palette[colour];
pixels <<= 1;
}
_writePointer += 4;
break;
case 2:
for(int c = 0; c < 8; c += 4)
for(int c = 0; c < 2; c ++)
{
uint8_t colour = ((pixels&0x80) >> 4) | ((pixels&0x20) >> 3) | ((pixels&0x08) >> 2) | ((pixels&0x02) >> 1);
_currentLine[output_ptr + c + 0] = _currentLine[output_ptr + c + 1] =
_currentLine[output_ptr + c + 2] = _currentLine[output_ptr + c + 3] = _palette[colour];
_writePointer[c] = _palette[colour];
pixels <<= 1;
}
_writePointer += 2;
break;
case 5:
for(int c = 0; c < 16; c += 4)
for(int c = 0; c < 4; c ++)
{
uint8_t colour = ((pixels&0x80) >> 4) | ((pixels&0x08) >> 2);
_currentLine[output_ptr + c + 0] = _currentLine[output_ptr + c + 1] =
_currentLine[output_ptr + c + 2] = _currentLine[output_ptr + c + 3] = _palette[colour];
_writePointer[c] = _palette[colour];
pixels <<= 1;
}
_writePointer += 4;
break;
default:
case 4:
case 6:
for(int c = 0; c < 16; c += 2)
for(int c = 0; c < 8; c ++)
{
uint8_t colour = (pixels&0x80) >> 4;
_currentLine[output_ptr + c] = _currentLine[output_ptr + c + 1] = _palette[colour];
_writePointer[c] = _palette[colour];
pixels <<= 1;
}
_writePointer += 8;
break;
}
}
@ -545,10 +580,10 @@ inline void Machine::update_display()
else
_startLineAddress++;
_currentLine = nullptr;
_crt->output_data(80 * crt_cycles_multiplier);
_crt->output_data((unsigned int)((_writePointer - _currentLine) * _currentOutputDivider), _currentOutputDivider);
_crt->output_blank(24 * crt_cycles_multiplier);
_displayOutputPosition += 24;
_currentLine = nullptr;
}
}
}

View File

@ -110,7 +110,8 @@ class Machine: public CPU6502::Processor<Machine> {
// Display generation.
uint16_t _startLineAddress, _currentScreenAddress;
int _currentOutputLine;
uint8_t *_currentLine;
unsigned int _currentOutputDivider;
uint8_t *_currentLine, *_writePointer;
// Tape.
struct Tape {

View File

@ -175,7 +175,7 @@ CRT::SyncEvent CRT::get_next_horizontal_sync_event(bool hsync_is_requested, unsi
return proposedEvent;
}
void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Type type)
void CRT::advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Type type)
{
number_of_cycles *= _time_multiplier;
@ -252,7 +252,7 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo
position_y(5) = InternalToUInt16(kCRTFixedPointOffset + _rasterPosition.y + _beamWidth[lengthMask].y);
// if this is a data run then advance the buffer pointer
if(type == Type::Data) tex_x += next_run_length / _time_multiplier;
if(type == Type::Data) tex_x += next_run_length / (_time_multiplier * source_divider);
// if this is a data or level run then store the end point
tex_x(2) = tex_x(3) = tex_x(5) = tex_x;
@ -356,28 +356,28 @@ void CRT::output_sync(unsigned int number_of_cycles)
{
bool _hsync_requested = !_is_receiving_sync; // ensure this really is edge triggered; someone calling output_sync twice in succession shouldn't trigger it twice
_is_receiving_sync = true;
advance_cycles(number_of_cycles, _hsync_requested, false, true, Type::Sync);
advance_cycles(number_of_cycles, 1, _hsync_requested, false, true, Type::Sync);
}
void CRT::output_blank(unsigned int number_of_cycles)
{
bool _vsync_requested = _is_receiving_sync;
_is_receiving_sync = false;
advance_cycles(number_of_cycles, false, _vsync_requested, false, Type::Blank);
advance_cycles(number_of_cycles, 1, false, _vsync_requested, false, Type::Blank);
}
void CRT::output_level(unsigned int number_of_cycles)
{
bool _vsync_requested = _is_receiving_sync;
_is_receiving_sync = false;
advance_cycles(number_of_cycles, false, _vsync_requested, false, Type::Level);
advance_cycles(number_of_cycles, 1, false, _vsync_requested, false, Type::Level);
}
void CRT::output_data(unsigned int number_of_cycles)
void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider)
{
bool _vsync_requested = _is_receiving_sync;
_is_receiving_sync = false;
advance_cycles(number_of_cycles, false, _vsync_requested, false, Type::Data);
advance_cycles(number_of_cycles, source_divider, false, _vsync_requested, false, Type::Data);
}
#pragma mark - Buffer supply

View File

@ -53,10 +53,46 @@ class CRT {
void set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display);
/*! Output at the sync level.
@param number_of_cycles The amount of time to putput sync for.
*/
void output_sync(unsigned int number_of_cycles);
/*! Output at the blanking level.
@param number_of_cycles The amount of time to putput the blanking level for.
*/
void output_blank(unsigned int number_of_cycles);
/*! Outputs the first written to the most-recently created run of data repeatedly for a prolonged period.
@param number_of_cycles The number of cycles to repeat the output for.
*/
void output_level(unsigned int number_of_cycles);
void output_data(unsigned int number_of_cycles);
/*! Declares that the caller has created a run of data via @c allocate_write_area and @c get_write_target_for_buffer
that is at least @c number_of_cycles long, and that the first @c number_of_cycles/source_divider should be spread
over that amount of time.
@param number_of_cycles The amount of data to output.
@param source_divider A divider for source data; if the divider is 1 then one source pixel is output every cycle,
if it is 2 then one source pixel covers two cycles; if it is n then one source pixel covers n cycles.
*/
void output_data(unsigned int number_of_cycles, unsigned int source_divider);
/*! Outputs a colour burst.
@param number_of_cycles The length of the colour burst.
@param phase The initial phase of the colour burst in a measuring system with 256 units
per circle, e.g. 0 = 0 degrees, 128 = 180 degrees, 256 = 360 degree.
@param magnitude The magnitude of the colour burst in 1/256ths of the magnitude of the
positive portion of the wave.
*/
void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t magnitude);
class Delegate {
public:
@ -110,7 +146,7 @@ class CRT {
enum Type {
Sync, Level, Data, Blank
} type;
void advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bool vsync_requested, bool vsync_charging, Type type);
void advance_cycles(unsigned int number_of_cycles, unsigned int source_divider, bool hsync_requested, bool vsync_requested, bool vsync_charging, Type type);
// the inner entry point that determines whether and when the next sync event will occur within
// the current output window