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

Edge towards clock-independent line composition.

This commit is contained in:
Thomas Harte 2023-01-07 14:57:32 -05:00
parent cdf547ac82
commit 72e0bfecc1
2 changed files with 71 additions and 7 deletions

View File

@ -319,9 +319,11 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
int read_cycles = target_read_cycles - read_cycles_performed; int read_cycles = target_read_cycles - read_cycles_performed;
if(!read_cycles) continue; if(!read_cycles) continue;
// Grab the next CRAM dot value and schedule a break in output if applicable.
const uint32_t cram_value = next_cram_value; const uint32_t cram_value = next_cram_value;
next_cram_value = 0;
if constexpr (is_sega_vdp(personality)) { if constexpr (is_sega_vdp(personality)) {
next_cram_value = 0;
if(!this->upcoming_cram_dots_.empty() && this->upcoming_cram_dots_.front().location.row == this->read_pointer_.row) { if(!this->upcoming_cram_dots_.empty() && this->upcoming_cram_dots_.front().location.row == this->read_pointer_.row) {
int time_until_dot = this->upcoming_cram_dots_.front().location.column - this->read_pointer_.column; int time_until_dot = this->upcoming_cram_dots_.front().location.column - this->read_pointer_.column;
@ -353,16 +355,21 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
#define border(left, right) intersect(left, right, this->output_border(end - start, cram_value)) #define border(left, right) intersect(left, right, this->output_border(end - start, cram_value))
// TODO: CRT clock might need to change?
if(line_buffer.line_mode == LineMode::Refresh || this->read_pointer_.row > this->mode_timing_.pixel_lines) { if(line_buffer.line_mode == LineMode::Refresh || this->read_pointer_.row > this->mode_timing_.pixel_lines) {
if(this->read_pointer_.row >= this->mode_timing_.first_vsync_line && this->read_pointer_.row < this->mode_timing_.first_vsync_line+4) { if(
this->read_pointer_.row >= this->mode_timing_.first_vsync_line &&
this->read_pointer_.row < this->mode_timing_.first_vsync_line + 4
) {
// Vertical sync. // Vertical sync.
if(end_column == 342) { // TODO: the Mega Drive supports interlaced video, I think?
this->crt_.output_sync(342 * 4); if(end_column == Timing<personality>::CyclesPerLine) {
this->crt_.output_sync(
this->clock_converter_.to_crt_clock(Timing<personality>::CyclesPerLine)
);
} }
} else { } else {
// Right border. // Right border.
border(0, 15); border(0, Timing<personality>::EndOfRightBorder);
// Blanking region; total length is 58 cycles, // Blanking region; total length is 58 cycles,
// and 58+15 = 73. So output the lot when the // and 58+15 = 73. So output the lot when the
@ -409,7 +416,7 @@ void TMS9918<personality>::run_for(const HalfCycles cycles) {
const int relative_start = start - line_buffer.first_pixel_output_column; const int relative_start = start - line_buffer.first_pixel_output_column;
const int relative_end = end - line_buffer.first_pixel_output_column; const int relative_end = end - line_buffer.first_pixel_output_column;
switch(line_buffer.line_mode) { switch(line_buffer.line_mode) {
case LineMode::SMS: this->draw_sms(relative_start, relative_end, cram_value); break; case LineMode::SMS: this->draw_sms(relative_start, relative_end, cram_value); break;
case LineMode::Character: this->draw_tms_character(relative_start, relative_end); break; case LineMode::Character: this->draw_tms_character(relative_start, relative_end); break;
case LineMode::Text: this->draw_tms_text(relative_start, relative_end); break; case LineMode::Text: this->draw_tms_text(relative_start, relative_end); break;

View File

@ -40,24 +40,61 @@ struct Timing<personality, std::enable_if_t<is_classic_vdp(personality)>> {
/// The final internal cycle at which pixels will be output text mode. /// The final internal cycle at which pixels will be output text mode.
constexpr static int LastTextCycle = 334; constexpr static int LastTextCycle = 334;
// For the below, the fixed portion of line layout is:
//
// [0, EndOfRightBorder): right border colour
// [EndOfRightBorder, StartOfSync): blank
// [StartOfSync, EndOfSync): sync
// [EndOfSync, StartOfColourBurst): blank
// [StartOfColourBurst, EndOfColourBurst): the colour burst
// [EndOfColourBurst, StartOfLeftBorder): blank
//
// The region from StartOfLeftBorder until the end is then filled with
// some combination of pixels and more border, depending on the vertical
// position of this line and the current screen mode.
constexpr static int EndOfRightBorder = 15;
constexpr static int StartOfSync = 23;
constexpr static int EndOfSync = 49;
constexpr static int StartOfColourBurst = 51;
constexpr static int EndOfColourBurst = 65;
constexpr static int StartOfLeftBorder = 73;
}; };
template <Personality personality> template <Personality personality>
struct Timing<personality, std::enable_if_t<is_yamaha_vdp(personality)>> { struct Timing<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
constexpr static int CyclesPerLine = 1368; constexpr static int CyclesPerLine = 1368;
constexpr static int VRAMAccessDelay = 6; constexpr static int VRAMAccessDelay = 6;
constexpr static int FirstPixelCycle = 344; constexpr static int FirstPixelCycle = 344;
constexpr static bool SupportsTextMode = true; constexpr static bool SupportsTextMode = true;
constexpr static int FirstTextCycle = 376; constexpr static int FirstTextCycle = 376;
constexpr static int LastTextCycle = 1336; constexpr static int LastTextCycle = 1336;
constexpr static int EndOfRightBorder = 15 * 4;
constexpr static int StartOfSync = 23 * 4;
constexpr static int EndOfSync = 49 * 4;
constexpr static int StartOfColourBurst = 51 * 4;
constexpr static int EndOfColourBurst = 65 * 4;
constexpr static int StartOfLeftBorder = 73 * 4;
}; };
template <> template <>
struct Timing<Personality::MDVDP> { struct Timing<Personality::MDVDP> {
constexpr static int CyclesPerLine = 3420; constexpr static int CyclesPerLine = 3420;
constexpr static int VRAMAccessDelay = 6; constexpr static int VRAMAccessDelay = 6;
constexpr static int FirstPixelCycle = 860; constexpr static int FirstPixelCycle = 860;
constexpr static bool SupportsTextMode = false; constexpr static bool SupportsTextMode = false;
// Implementation note: these currently need to be multiples of 2.5
// per the stateless Mega Drive -> CRT clock conversion.
constexpr static int EndOfRightBorder = 15 * 10;
constexpr static int StartOfSync = 23 * 10;
constexpr static int EndOfSync = 49 * 10;
constexpr static int StartOfColourBurst = 51 * 10;
constexpr static int EndOfColourBurst = 65 * 10;
constexpr static int StartOfLeftBorder = 73 * 10;
}; };
constexpr int TMSAccessWindowsPerLine = 171; constexpr int TMSAccessWindowsPerLine = 171;
@ -170,6 +207,26 @@ template <Personality personality> class ClockConverter {
} }
} }
/*!
Convers a position in internal cycles to its corresponding position
on the CRT's output clock, which [TODO] is clocked so that
1368 cycles is 228 NTSC colour cycles.
*/
static constexpr int to_crt_clock(int source) {
switch(personality) {
default:
return source * 4;
case Personality::V9938:
case Personality::V9958:
return source;
case Personality::MDVDP:
return (source * 2) / 5;
}
}
private: private:
// Holds current residue in conversion from the external to // Holds current residue in conversion from the external to
// internal clock. // internal clock.