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:
parent
baef1ccd57
commit
48ddd3c497
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
Loading…
x
Reference in New Issue
Block a user