1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Switched to a table-based dispatch of line-by-line actions, primarily to simplify.

This commit is contained in:
Thomas Harte 2016-12-15 18:07:46 -05:00
parent fd541e1142
commit b58b11fc93
2 changed files with 99 additions and 208 deletions

View File

@ -38,9 +38,14 @@ VideoOutput::VideoOutput(uint8_t *memory) :
ram_(memory),
current_pixel_line_(-1),
output_position_(0),
screen_mode_(6)
screen_mode_(6),
screen_map_pointer_(0),
cycles_into_draw_action_(0)
{
memset(palette_, 0xf, sizeof(palette_));
setup_screen_map();
crt_.reset(new Outputs::CRT::CRT(crt_cycles_per_line, 8, Outputs::CRT::DisplayType::PAL50, 1));
crt_->set_rgb_sampling_function(
@ -244,163 +249,29 @@ void VideoOutput::output_pixels(unsigned int number_of_cycles)
}
}
void VideoOutput::run_for_inner_frame_cycles(int number_of_cycles)
{
int target_output_position = output_position_ + number_of_cycles;
int final_line = target_output_position >> 7;
while(output_position_ < target_output_position)
{
int line = output_position_ >> 7;
// Priority one: sync.
// ===================
// full sync lines are 0, 1, field_divider_line+1 and field_divider_line+2
if(line == 0 || line == 1 || line == field_divider_line+1 || line == field_divider_line+2)
{
// wait for the line to complete before signalling
if(final_line == line) return;
crt_->output_sync(128 * crt_cycles_multiplier);
output_position_ += 128;
continue;
}
// line 2 is a left-sync line
if(line == 2)
{
// wait for the line to complete before signalling
if(final_line == line) return;
crt_->output_sync(64 * crt_cycles_multiplier);
crt_->output_blank(64 * crt_cycles_multiplier);
output_position_ += 128;
continue;
}
// line field_divider_line is a right-sync line
if(line == field_divider_line)
{
// wait for the line to complete before signalling
if(final_line == line) return;
crt_->output_sync(9 * crt_cycles_multiplier);
crt_->output_blank(55 * crt_cycles_multiplier);
crt_->output_sync(64 * crt_cycles_multiplier);
output_position_ += 128;
continue;
}
// Priority two: blank lines.
// ==========================
//
// Given that it is not a sync line, this is a blank line if it is less than first_graphics_line, or greater
// than first_graphics_line+255 and less than first_graphics_line+field_divider_line, or greater than
// first_graphics_line+field_divider_line+255 (TODO: or this is Mode 3 or 6 and this should be blank)
if(
line < first_graphics_line ||
(line > first_graphics_line+255 && line < first_graphics_line+field_divider_line) ||
line > first_graphics_line+field_divider_line+255)
{
if(final_line == line) return;
crt_->output_sync(9 * crt_cycles_multiplier);
crt_->output_blank(119 * crt_cycles_multiplier);
output_position_ += 128;
continue;
}
// Final possibility: this is a pixel line.
// ========================================
// determine how far we're going from left to right
int this_cycle = output_position_&127;
int final_cycle = target_output_position&127;
if(final_line > line)
{
final_cycle = 128;
}
// output format is:
// 9 cycles: sync
// ... to 24 cycles: colour burst
// ... to first_graphics_cycle: blank
// ... for 80 cycles: pixels
// ... until end of line: blank
while(this_cycle < final_cycle)
{
if(this_cycle < 9)
{
if(final_cycle < 9) return;
crt_->output_sync(9 * crt_cycles_multiplier);
output_position_ += 9;
this_cycle = 9;
}
if(this_cycle < 24)
{
if(final_cycle < 24) return;
crt_->output_default_colour_burst((24-9) * crt_cycles_multiplier);
output_position_ += 24-9;
this_cycle = 24;
// TODO: phase shouldn't be zero on every line
}
if(this_cycle < first_graphics_cycle)
{
if(final_cycle < first_graphics_cycle) return;
crt_->output_blank((first_graphics_cycle - 24) * crt_cycles_multiplier);
output_position_ += first_graphics_cycle - 24;
this_cycle = first_graphics_cycle;
start_pixel_line();
}
if(this_cycle < first_graphics_cycle + 80)
{
unsigned int length_to_output = std::min(final_cycle, (first_graphics_cycle + 80)) - this_cycle;
output_pixels(length_to_output);
output_position_ += length_to_output;
this_cycle += length_to_output;
}
if(this_cycle >= first_graphics_cycle + 80)
{
if(final_cycle < 128) return;
end_pixel_line();
crt_->output_blank((128 - (first_graphics_cycle + 80)) * crt_cycles_multiplier);
output_position_ += 128 - (first_graphics_cycle + 80);
this_cycle = 128;
}
}
}
}
void VideoOutput::run_for_cycles(int number_of_cycles)
{
/*
Odd field: Even field:
|--S--| -S-|
|--S--| |--S--|
|-S-B-| = 3 |--S--| = 2.5
|--B--| |--B--|
|--P--| |--P--|
|--B--| = 312 |--B--| = 312.5
|-B-
*/
int cycles_at_end = unused_cycles_ + output_position_ + number_of_cycles;
unused_cycles_ = 0;
int number_of_frames = 1 + (cycles_at_end / cycles_per_frame);
while(number_of_frames--)
while(number_of_cycles)
{
int frame_target = number_of_frames ? cycles_per_frame : (cycles_at_end % cycles_per_frame);
run_for_inner_frame_cycles(frame_target - output_position_);
// unused_cycles_ += (frame_final - output_position_);
// if(unused_cycles_)
// {
// }
int draw_action_length = screen_map_[screen_map_pointer_].length;
int time_left_in_action = std::min(number_of_cycles, draw_action_length - cycles_into_draw_action_);
if(screen_map_[screen_map_pointer_].type == DrawAction::Pixels) output_pixels((unsigned int)time_left_in_action);
output_position_ %= cycles_per_frame;
number_of_cycles -= time_left_in_action;
cycles_into_draw_action_ += time_left_in_action;
if(cycles_into_draw_action_ == draw_action_length)
{
switch(screen_map_[screen_map_pointer_].type)
{
case DrawAction::Sync: crt_->output_sync((unsigned int)(draw_action_length * crt_cycles_multiplier)); break;
case DrawAction::ColourBurst: crt_->output_default_colour_burst((unsigned int)(draw_action_length * crt_cycles_multiplier)); break;
case DrawAction::Blank: crt_->output_blank((unsigned int)(draw_action_length * crt_cycles_multiplier)); break;
case DrawAction::Pixels: end_pixel_line(); break;
}
screen_map_pointer_ = (screen_map_pointer_ + 1) % screen_map_.size();
cycles_into_draw_action_ = 0;
if(screen_map_[screen_map_pointer_].type == DrawAction::Pixels) start_pixel_line();
}
}
}
@ -484,9 +355,9 @@ void VideoOutput::set_register(int address, uint8_t value)
target[2] = pack(palette_[(byte&0x08) >> 0], palette_[(byte&0x04) << 1]);
target[3] = pack(palette_[(byte&0x02) << 2], palette_[(byte&0x01) << 3]);
palette_tables_.forty2bpp[byte] = pack(palette_[((byte&0x80) >> 4) | ((byte&0x08) >> 2)], palette_[((byte&0x40) >> 3) | ((byte&0x04) >> 1)]);
palette_tables_.forty2bpp[byte] = pack( palette_[((byte&0x80) >> 4) | ((byte&0x08) >> 2)], palette_[((byte&0x40) >> 3) | ((byte&0x04) >> 1)]);
palette_tables_.eighty4bpp[byte] = pack( palette_[((byte&0x80) >> 4) | ((byte&0x20) >> 3) | ((byte&0x08) >> 2) | ((byte&0x02) >> 1)],
palette_[((byte&0x40) >> 3) | ((byte&0x10) >> 2) | ((byte&0x04) >> 1) | ((byte&0x01) >> 0)]);
palette_[((byte&0x40) >> 3) | ((byte&0x10) >> 2) | ((byte&0x04) >> 1) | ((byte&0x01) >> 0)]);
}
#undef pack
}
@ -496,56 +367,6 @@ void VideoOutput::set_register(int address, uint8_t value)
#pragma mark - Interrupts
//int VideoOutput::get_cycles_until_next_interrupt()
//{
// const int end_of_field =
// if(frame_cycles_ < (256 + first_graphics_line) << 7))
// const unsigned int pixel_line_clock = frame_cycles_;// + 128 - first_graphics_cycle + 80;
// const unsigned int line_before_cycle = graphics_line(pixel_line_clock);
// const unsigned int line_after_cycle = graphics_line(pixel_line_clock + cycles);
// implicit assumption here: the number of 2Mhz cycles this bus operation will take
// is never longer than a line. On the Electron, it's a safe one.
// if(line_before_cycle != line_after_cycle)
// {
// switch(line_before_cycle)
// {
// case real_time_clock_interrupt_line: signal_interrupt(Interrupt::RealTimeClock); break;
// case real_time_clock_interrupt_line+1: clear_interrupt(Interrupt::RealTimeClock); break;
// case display_end_interrupt_line: signal_interrupt(Interrupt::DisplayEnd); break;
// case display_end_interrupt_line+1: clear_interrupt(Interrupt::DisplayEnd); break;
// }
// }
// if(
// (pixel_line_clock < real_time_clock_interrupt_1 && pixel_line_clock + cycles >= real_time_clock_interrupt_1) ||
// (pixel_line_clock < real_time_clock_interrupt_2 && pixel_line_clock + cycles >= real_time_clock_interrupt_2))
// {
// signal_interrupt(Interrupt::RealTimeClock);
// }
// frame_cycles_ += cycles;
// deal with frame wraparound by updating the two dependent subsystems
// as though the exact end of frame had been hit, then reset those
// and allow the frame cycle counter to assume its real value
// if(frame_cycles_ >= cycles_per_frame)
// {
// unsigned int nextFrameCycles = frame_cycles_ - cycles_per_frame;
// frame_cycles_ = cycles_per_frame;
// update_display();
// update_audio();
// display_output_position_ = 0;
// audio_output_position_ = 0;
// frame_cycles_ = nextFrameCycles;
// }
// if(!(frame_cycles_&16383))
// update_audio();
// return 0;
//}
VideoOutput::Interrupt VideoOutput::get_next_interrupt()
{
VideoOutput::Interrupt interrupt;
@ -600,3 +421,61 @@ unsigned int VideoOutput::get_cycles_until_next_ram_availability(int from_time)
// }
return result;
}
#pragma mark - The screen map
void VideoOutput::setup_screen_map()
{
/*
Odd field: Even field:
|--S--| -S-|
|--S--| |--S--|
|-S-B-| = 3 |--S--| = 2.5
|--B--| |--B--|
|--P--| |--P--|
|--B--| = 312 |--B--| = 312.5
|-B-
*/
for(int c = 0; c < 2; c++)
{
if(c&1)
{
screen_map_.emplace_back(DrawAction::Sync, (cycles_per_line * 5) >> 1);
screen_map_.emplace_back(DrawAction::Blank, cycles_per_line >> 1);
}
else
{
screen_map_.emplace_back(DrawAction::Blank, cycles_per_line >> 1);
screen_map_.emplace_back(DrawAction::Sync, (cycles_per_line * 5) >> 1);
}
for(int c = 0; c < first_graphics_line - 3; c++) emplace_blank_line();
for(int c = 0; c < 256; c++) emplace_pixel_line();
for(int c = 256 + first_graphics_line; c < 312; c++) emplace_blank_line();
if(c&1) emplace_blank_line();
}
}
void VideoOutput::emplace_blank_line()
{
screen_map_.emplace_back(DrawAction::Sync, 9);
screen_map_.emplace_back(DrawAction::ColourBurst, 24 - 9);
screen_map_.emplace_back(DrawAction::Blank, 128 - 24);
}
void VideoOutput::emplace_pixel_line()
{
// output format is:
// 9 cycles: sync
// ... to 24 cycles: colour burst
// ... to first_graphics_cycle: blank
// ... for 80 cycles: pixels
// ... until end of line: blank
screen_map_.emplace_back(DrawAction::Sync, 9);
screen_map_.emplace_back(DrawAction::ColourBurst, 24 - 9);
screen_map_.emplace_back(DrawAction::Blank, first_graphics_cycle - 24);
screen_map_.emplace_back(DrawAction::Pixels, 80);
screen_map_.emplace_back(DrawAction::Blank, 48 - first_graphics_cycle);
}

View File

@ -35,8 +35,6 @@ class VideoOutput {
inline void end_pixel_line();
inline void output_pixels(unsigned int number_of_cycles);
inline void run_for_inner_frame_cycles(int number_of_cycles);
int output_position_, unused_cycles_;
uint8_t palette_[16];
@ -64,6 +62,20 @@ class VideoOutput {
unsigned int current_output_divider_;
std::shared_ptr<Outputs::CRT::CRT> crt_;
struct DrawAction {
enum Type {
Sync, ColourBurst, Blank, Pixels
} type;
int length;
DrawAction(Type type, int length) : type(type), length(length) {}
};
std::vector<DrawAction> screen_map_;
void setup_screen_map();
void emplace_blank_line();
void emplace_pixel_line();
size_t screen_map_pointer_;
int cycles_into_draw_action_;
};
}