mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-22 12:33:29 +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;
|
||||
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;
|
||||
next_cram_value = 0;
|
||||
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) {
|
||||
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))
|
||||
|
||||
// TODO: CRT clock might need to change?
|
||||
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.
|
||||
if(end_column == 342) {
|
||||
this->crt_.output_sync(342 * 4);
|
||||
// TODO: the Mega Drive supports interlaced video, I think?
|
||||
if(end_column == Timing<personality>::CyclesPerLine) {
|
||||
this->crt_.output_sync(
|
||||
this->clock_converter_.to_crt_clock(Timing<personality>::CyclesPerLine)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
// Right border.
|
||||
border(0, 15);
|
||||
border(0, Timing<personality>::EndOfRightBorder);
|
||||
|
||||
// Blanking region; total length is 58 cycles,
|
||||
// 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_end = end - line_buffer.first_pixel_output_column;
|
||||
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::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.
|
||||
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>
|
||||
struct Timing<personality, std::enable_if_t<is_yamaha_vdp(personality)>> {
|
||||
constexpr static int CyclesPerLine = 1368;
|
||||
|
||||
constexpr static int VRAMAccessDelay = 6;
|
||||
constexpr static int FirstPixelCycle = 344;
|
||||
constexpr static bool SupportsTextMode = true;
|
||||
constexpr static int FirstTextCycle = 376;
|
||||
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 <>
|
||||
struct Timing<Personality::MDVDP> {
|
||||
constexpr static int CyclesPerLine = 3420;
|
||||
|
||||
constexpr static int VRAMAccessDelay = 6;
|
||||
constexpr static int FirstPixelCycle = 860;
|
||||
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;
|
||||
@ -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:
|
||||
// Holds current residue in conversion from the external to
|
||||
// internal clock.
|
||||
|
Loading…
Reference in New Issue
Block a user