1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-02-19 23:29:05 +00:00

Begin CRTC rejig.

This commit is contained in:
Thomas Harte 2024-09-23 21:11:54 -04:00
parent 10f8318e79
commit ea25dbfd1e

View File

@ -185,55 +185,102 @@ template <class BusHandlerT, Personality personality, CursorType cursor_type> cl
status_ |= 0x40; status_ |= 0x40;
} }
bool eof_latched_ = false;
bool extra_scanline_ = false;
uint8_t next_line_counter_ = 0;
bool adjustment_in_progress_ = false;
uint16_t next_row_address_ = 0;
void run_for(Cycles cycles) { void run_for(Cycles cycles) {
auto cyles_remaining = cycles.as_integral(); auto cyles_remaining = cycles.as_integral();
while(cyles_remaining--) { while(cyles_remaining--) {
// Check for end of visible characters. const bool character_total_hit = character_counter_ == layout_.horizontal.total;
if(character_counter_ == layout_.horizontal.displayed) { const bool new_frame = character_total_hit && eof_latched_ && (layout_.interlace_mode_ == InterlaceMode::Off || !(bus_state_.field_count&1) || extra_scanline_);
// TODO: consider skew in character_is_visible_. Or maybe defer until perform_bus_cycle?
character_is_visible_ = false;
end_of_line_address_ = bus_state_.refresh_address;
}
perform_bus_cycle_phase1(); //
bus_state_.refresh_address = (bus_state_.refresh_address + 1) & RefreshMask; // Horizontal.
//
bus_state_.cursor = is_cursor_line_ &&
bus_state_.refresh_address == layout_.cursor_address;
// Check for end-of-line. // Check for end-of-line.
if(character_counter_ == layout_.horizontal.total) { if(character_total_hit) {
character_counter_ = 0; character_counter_ = 0;
do_end_of_line();
character_is_visible_ = true; character_is_visible_ = true;
} else { } else {
// Increment counter.
character_counter_++; character_counter_++;
} }
// Check for start of horizontal sync. // Check for end of visible characters.
if(character_counter_ == layout_.horizontal.displayed) {
character_is_visible_ = false;
}
// Update horizontal sync.
if(bus_state_.hsync) {
++hsync_counter_;
bus_state_.hsync = hsync_counter_ != layout_.horizontal.sync_width;
}
if(character_counter_ == layout_.horizontal.start_sync) { if(character_counter_ == layout_.horizontal.start_sync) {
hsync_counter_ = 0; hsync_counter_ = 0;
bus_state_.hsync = true; bus_state_.hsync = true;
} }
// Check for end of horizontal sync; note that a sync time of zero will result in an immediate
// cancellation of the plan to perform sync if this is an HD6845S or UM6845R; otherwise zero //
// will end up counting as 16 as it won't be checked until after overflow. // Vertical.
if(bus_state_.hsync) { //
switch(personality) { const bool line_total_hit = line_counter_ == layout_.vertical.displayed && !adjustment_in_progress_;
case Personality::HD6845S:
case Personality::UM6845R: // Line counter.
bus_state_.hsync = hsync_counter_ != layout_.horizontal.sync_width; if(new_frame) {
hsync_counter_ = (hsync_counter_ + 1) & 15; line_counter_ = 0;
break; } else if(character_total_hit) {
default: line_counter_ = next_line_counter_;
hsync_counter_ = (hsync_counter_ + 1) & 15; }
bus_state_.hsync = hsync_counter_ != layout_.horizontal.sync_width;
break; if(line_total_hit) {
next_line_counter_ = 0;
} else if(layout_.interlace_mode_ != InterlaceMode::InterlaceSyncAndVideo || adjustment_in_progress_) {
next_line_counter_ = line_counter_ + 1;
} else {
next_line_counter_ = (line_counter_ + 2) & ~1;
}
// Row counter.
bus_state_.row_address = next_row_address_;
if(new_frame) {
next_row_address_ = 0;
} else if(line_total_hit && character_total_hit) {
next_row_address_ = bus_state_.row_address + 1;
} else {
next_row_address_ = bus_state_.row_address;
}
// Sync. [TODO]
const bool vsync_hit = line_counter_ == layout_.vertical.start_sync;
if(character_total_hit) {
if(vsync_hit) {
vsync_counter_ = 0;
} else {
++vsync_counter_;
} }
} }
//
// Addressing.
//
if(new_frame) {
}
// Do bus work.
// bus_state_.refresh_address = (bus_state_.refresh_address + 1) & RefreshMask;
//
// bus_state_.cursor = is_cursor_line_ &&
// bus_state_.refresh_address == layout_.cursor_address;
perform_bus_cycle_phase1();
perform_bus_cycle_phase2(); perform_bus_cycle_phase2();
} }
} }
@ -256,100 +303,100 @@ template <class BusHandlerT, Personality personality, CursorType cursor_type> cl
bus_handler_.perform_bus_cycle_phase2(bus_state_); bus_handler_.perform_bus_cycle_phase2(bus_state_);
} }
inline void do_end_of_line() { // inline void do_end_of_line() {
if constexpr (cursor_type != CursorType::None) { // if constexpr (cursor_type != CursorType::None) {
// Check for cursor disable. // // Check for cursor disable.
// TODO: this is handled differently on the EGA, should I ever implement that. // // TODO: this is handled differently on the EGA, should I ever implement that.
is_cursor_line_ &= bus_state_.row_address != layout_.vertical.end_cursor; // is_cursor_line_ &= bus_state_.row_address != layout_.vertical.end_cursor;
} // }
//
// Check for end of vertical sync. // // Check for end of vertical sync.
if(bus_state_.vsync) { // if(bus_state_.vsync) {
vsync_counter_ = (vsync_counter_ + 1) & 15; // vsync_counter_ = (vsync_counter_ + 1) & 15;
// On the UM6845R and AMS40226, honour the programmed vertical sync time; on the other CRTCs // // On the UM6845R and AMS40226, honour the programmed vertical sync time; on the other CRTCs
// always use a vertical sync count of 16. // // always use a vertical sync count of 16.
switch(personality) { // switch(personality) {
case Personality::HD6845S: // case Personality::HD6845S:
case Personality::AMS40226: // case Personality::AMS40226:
bus_state_.vsync = vsync_counter_ != layout_.vertical.sync_lines; // bus_state_.vsync = vsync_counter_ != layout_.vertical.sync_lines;
break; // break;
default: // default:
bus_state_.vsync = vsync_counter_ != 0; // bus_state_.vsync = vsync_counter_ != 0;
break; // break;
} // }
} // }
//
if(is_in_adjustment_period_) { // if(is_in_adjustment_period_) {
line_counter_++; // line_counter_++;
if(line_counter_ == layout_.vertical.adjust) { // if(line_counter_ == layout_.vertical.adjust) {
is_in_adjustment_period_ = false; // is_in_adjustment_period_ = false;
do_end_of_frame(); // do_end_of_frame();
} // }
} else { // } else {
// Advance vertical counter. // // Advance vertical counter.
if(bus_state_.row_address == layout_.vertical.end_row) { // if(bus_state_.row_address == layout_.end_row()) {
bus_state_.row_address = 0; // bus_state_.row_address = 0;
line_address_ = end_of_line_address_; // line_address_ = end_of_line_address_;
//
// Check for entry into the overflow area. // // Check for entry into the overflow area.
if(line_counter_ == layout_.vertical.total) { // if(line_counter_ == layout_.vertical.total) {
if(layout_.vertical.adjust) { // if(layout_.vertical.adjust) {
line_counter_ = 0; // line_counter_ = 0;
is_in_adjustment_period_ = true; // is_in_adjustment_period_ = true;
} else { // } else {
do_end_of_frame(); // do_end_of_frame();
} // }
} else { // } else {
line_counter_ = (line_counter_ + 1) & 0x7f; // line_counter_ = (line_counter_ + 1) & 0x7f;
} // }
//
// Check for start of vertical sync. // // Check for start of vertical sync.
if(line_counter_ == layout_.vertical.start_sync) { // if(line_counter_ == layout_.vertical.start_sync) {
bus_state_.vsync = true; // bus_state_.vsync = true;
vsync_counter_ = 0; // vsync_counter_ = 0;
} // }
//
// Check for end of visible lines. // // Check for end of visible lines.
if(line_counter_ == layout_.vertical.displayed) { // if(line_counter_ == layout_.vertical.displayed) {
line_is_visible_ = false; // line_is_visible_ = false;
} // }
} else { // } else {
bus_state_.row_address = (bus_state_.row_address + 1) & 0x1f; // bus_state_.row_address = (bus_state_.row_address + 1) & 0x1f;
} // }
} // }
//
bus_state_.refresh_address = line_address_; // bus_state_.refresh_address = line_address_;
character_counter_ = 0; // character_counter_ = 0;
character_is_visible_ = (layout_.horizontal.displayed != 0); // character_is_visible_ = (layout_.horizontal.displayed != 0);
//
if constexpr (cursor_type != CursorType::None) { // if constexpr (cursor_type != CursorType::None) {
// Check for cursor enable. // // Check for cursor enable.
is_cursor_line_ |= bus_state_.row_address == layout_.vertical.start_cursor; // is_cursor_line_ |= bus_state_.row_address == layout_.vertical.start_cursor;
//
switch(cursor_type) { // switch(cursor_type) {
// MDA-style blinking. // // MDA-style blinking.
// https://retrocomputing.stackexchange.com/questions/27803/what-are-the-blinking-rates-of-the-caret-and-of-blinking-text-on-pc-graphics-car // // https://retrocomputing.stackexchange.com/questions/27803/what-are-the-blinking-rates-of-the-caret-and-of-blinking-text-on-pc-graphics-car
// gives an 8/8 pattern for regular blinking though mode 11 is then just a guess. // // gives an 8/8 pattern for regular blinking though mode 11 is then just a guess.
case CursorType::MDA: // case CursorType::MDA:
switch(layout_.cursor_flags) { // switch(layout_.cursor_flags) {
case 0b11: is_cursor_line_ &= (bus_state_.field_count & 8) < 3; break; // case 0b11: is_cursor_line_ &= (bus_state_.field_count & 8) < 3; break;
case 0b00: is_cursor_line_ &= bool(bus_state_.field_count & 8); break; // case 0b00: is_cursor_line_ &= bool(bus_state_.field_count & 8); break;
case 0b01: is_cursor_line_ = false; break; // case 0b01: is_cursor_line_ = false; break;
case 0b10: is_cursor_line_ = true; break; // case 0b10: is_cursor_line_ = true; break;
default: break; // default: break;
} // }
break; // break;
} // }
} // }
} // }
//
inline void do_end_of_frame() { // inline void do_end_of_frame() {
line_counter_ = 0; // line_counter_ = 0;
line_is_visible_ = true; // line_is_visible_ = true;
line_address_ = layout_.start_address; // line_address_ = layout_.start_address;
bus_state_.refresh_address = line_address_; // bus_state_.refresh_address = line_address_;
++bus_state_.field_count; // ++bus_state_.field_count;
} // }
BusHandlerT &bus_handler_; BusHandlerT &bus_handler_;
BusState bus_state_; BusState bus_state_;
@ -383,6 +430,10 @@ template <class BusHandlerT, Personality personality, CursorType cursor_type> cl
} vertical; } vertical;
InterlaceMode interlace_mode_ = InterlaceMode::Off; InterlaceMode interlace_mode_ = InterlaceMode::Off;
uint8_t end_row() const {
return interlace_mode_ == InterlaceMode::InterlaceSyncAndVideo ? vertical.end_row & ~1 : vertical.end_row;
}
uint16_t start_address; uint16_t start_address;
uint16_t cursor_address; uint16_t cursor_address;
uint16_t light_pen_address; uint16_t light_pen_address;