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:
parent
cdf547ac82
commit
72e0bfecc1
@ -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;
|
||||||
|
|
||||||
|
@ -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.
|
||||||
|
Loading…
Reference in New Issue
Block a user