From fd541e1142301f2cea236502e0b29c34aefe4cbf Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 12 Dec 2016 08:01:10 -0500 Subject: [PATCH] An early draft; dealing with the issue that not all cycles are necessarily consumed in a single call. Incomplete; broken. Committing for cross-machine visibility. --- Machines/Electron/Electron.cpp | 23 ++- Machines/Electron/Video.cpp | 290 +++++++++++++++++---------------- Machines/Electron/Video.hpp | 6 +- 3 files changed, 170 insertions(+), 149 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 93e237474..535711adb 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -344,12 +344,13 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) if(target.loadingCommand.length()) // TODO: and automatic loading option enabled { - set_typer_for_string(target.loadingCommand.c_str()); +// set_typer_for_string(target.loadingCommand.c_str()); } + if(target.acorn.should_hold_shift) { - set_key_state(KeyShift, true); - is_holding_shift_ = true; +// set_key_state(KeyShift, true); +// is_holding_shift_ = true; } } @@ -404,8 +405,11 @@ inline void Machine::evaluate_interrupts() inline void Machine::update_display() { - video_output_->run_for_cycles((int)cycles_since_display_update_); - cycles_since_display_update_ = 0; + if(cycles_since_display_update_) + { + video_output_->run_for_cycles((int)cycles_since_display_update_); + cycles_since_display_update_ = 0; + } } inline void Machine::queue_next_display_interrupt() @@ -417,9 +421,12 @@ inline void Machine::queue_next_display_interrupt() inline void Machine::update_audio() { - unsigned int difference = cycles_since_audio_update_ / Speaker::clock_rate_divider; - cycles_since_audio_update_ %= Speaker::clock_rate_divider; - speaker_->run_for_cycles(difference); + if(cycles_since_audio_update_) + { + unsigned int difference = cycles_since_audio_update_ / Speaker::clock_rate_divider; + cycles_since_audio_update_ %= Speaker::clock_rate_divider; + speaker_->run_for_cycles(difference); + } } void Machine::clear_all_keys() diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 0ffe7baf7..5ba469d30 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -14,17 +14,17 @@ using namespace Electron; #define graphics_column(v) ((((v) & 127) - first_graphics_cycle + 128) & 127) namespace { - static const unsigned int cycles_per_line = 128; - static const unsigned int lines_per_frame = 625; + static const int cycles_per_line = 128; + static const int lines_per_frame = 625; static const int cycles_per_frame = lines_per_frame * cycles_per_line; - static const unsigned int crt_cycles_multiplier = 8; - static const unsigned int crt_cycles_per_line = crt_cycles_multiplier * cycles_per_line; + static const int crt_cycles_multiplier = 8; + static const int crt_cycles_per_line = crt_cycles_multiplier * cycles_per_line; - static const unsigned int field_divider_line = 312; // i.e. the line, simultaneous with which, the first field's sync ends. So if + static const int field_divider_line = 312; // i.e. the line, simultaneous with which, the first field's sync ends. So if // the first line with pixels in field 1 is the 20th in the frame, the first line // with pixels in field 2 will be 20+field_divider_line - static const unsigned int first_graphics_line = 31; - static const unsigned int first_graphics_cycle = 33; + static const int first_graphics_line = 31; + static const int first_graphics_cycle = 33; static const int display_end_interrupt_line = 256; @@ -244,6 +244,134 @@ 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) { /* @@ -259,136 +387,18 @@ void VideoOutput::run_for_cycles(int number_of_cycles) |-B- */ - int final_position = output_position_ + number_of_cycles; - - int number_of_frames = 1 + (final_position / cycles_per_frame); + 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--) { - int frame_final = number_of_frames ? cycles_per_frame : (final_position % cycles_per_frame); - int final_line = frame_final >> 7; - - while(output_position_ < frame_final) - { - 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 - unsigned int this_cycle = output_position_&127; - unsigned int final_cycle = frame_final&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; - } - } - } + 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_) +// { +// } output_position_ %= cycles_per_frame; } @@ -575,16 +585,18 @@ VideoOutput::Interrupt VideoOutput::get_next_interrupt() #pragma mark - RAM timing -unsigned int VideoOutput::get_cycles_until_next_ram_availability(unsigned int from_time) +unsigned int VideoOutput::get_cycles_until_next_ram_availability(int from_time) { unsigned int result = 0; -// cycles += 1 + (frame_cycles_&1); + int position = output_position_ + from_time; + + result += 1 + (position&1); // if(screen_mode_ < 4) // { -// const int current_line = graphics_line(frame_cycles_ + (frame_cycles_&1)); -// const int current_column = graphics_column(frame_cycles_ + (frame_cycles_&1)); +// const int current_line = graphics_line(position + (position&1)); +// const int current_column = graphics_column(position + (position&1)); // if(current_line < 256 && current_column < 80 && !is_blank_line_) -// cycles += (unsigned int)(80 - current_column); +// result += (unsigned int)(80 - current_column); // } return result; } diff --git a/Machines/Electron/Video.hpp b/Machines/Electron/Video.hpp index 520e31514..c9fb4e453 100644 --- a/Machines/Electron/Video.hpp +++ b/Machines/Electron/Video.hpp @@ -28,14 +28,16 @@ class VideoOutput { void set_register(int address, uint8_t value); - unsigned int get_cycles_until_next_ram_availability(unsigned int from_time); + unsigned int get_cycles_until_next_ram_availability(int from_time); private: inline void start_pixel_line(); inline void end_pixel_line(); inline void output_pixels(unsigned int number_of_cycles); - int output_position_; + inline void run_for_inner_frame_cycles(int number_of_cycles); + + int output_position_, unused_cycles_; uint8_t palette_[16]; uint8_t screen_mode_;