From c5cf8d95312b9464f63d39d93a7346fa863ee619 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 11 Dec 2016 16:17:51 -0500 Subject: [PATCH] Ensured the video subsystem correctly handles requests to run over a frame boundary. --- Machines/Electron/Electron.cpp | 53 +++----- Machines/Electron/Electron.hpp | 8 +- Machines/Electron/Video.cpp | 230 ++++++++++++++++++--------------- 3 files changed, 151 insertions(+), 140 deletions(-) diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index 45868ce32..9a346e8b0 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -104,10 +104,6 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } else { -// if((address >> 8) == 0xfc) -// { -// printf("d"); -// } switch(address & 0xff0f) { case 0xfe00: @@ -122,6 +118,26 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin evaluate_interrupts(); } break; + case 0xfe07: + if(!isReadOperation(operation)) + { + // update speaker mode + bool new_speaker_is_enabled = (*value & 6) == 2; + if(new_speaker_is_enabled != speaker_is_enabled_) + { + update_audio(); + speaker_->set_is_enabled(new_speaker_is_enabled); + speaker_is_enabled_ = new_speaker_is_enabled; + } + + tape_.set_is_enabled((*value & 6) != 6); + tape_.set_is_in_input_mode((*value & 6) == 0); + tape_.set_is_running(((*value)&0x40) ? true : false); + + // TODO: caps lock LED + } + + // deliberate fallthrough case 0xfe02: case 0xfe03: case 0xfe08: case 0xfe09: case 0xfe0a: case 0xfe0b: case 0xfe0c: case 0xfe0d: case 0xfe0e: case 0xfe0f: @@ -184,28 +200,6 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin tape_.set_counter(*value); } break; - case 0xfe07: - if(!isReadOperation(operation)) - { - update_display(); - video_output_->set_register(address, *value); - - // update speaker mode - bool new_speaker_is_enabled = (*value & 6) == 2; - if(new_speaker_is_enabled != speaker_is_enabled_) - { - update_audio(); - speaker_->set_is_enabled(new_speaker_is_enabled); - speaker_is_enabled_ = new_speaker_is_enabled; - } - - tape_.set_is_enabled((*value & 6) != 6); - tape_.set_is_in_input_mode((*value & 6) == 0); - tape_.set_is_running(((*value)&0x40) ? true : false); - - // TODO: caps lock LED - } - break; case 0xfc04: case 0xfc05: case 0xfc06: case 0xfc07: if(plus3_ && (address&0x00f0) == 0x00c0) @@ -326,11 +320,6 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin } } -// if(operation == CPU6502::BusOperation::ReadOpcode) -// { -// printf("%04x: %02x (%d)\n", address, *value, _fieldCycles); -// } - // const int end_of_field = // if(frame_cycles_ < (256 + first_graphics_line) << 7)) @@ -484,7 +473,7 @@ inline void Machine::evaluate_interrupts() inline void Machine::update_display() { - video_output_->run_for_cycles(frame_cycles_ - display_output_position_); + video_output_->run_for_cycles((int)(frame_cycles_ - display_output_position_)); display_output_position_ = frame_cycles_; } diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index 4647d4430..4b585d473 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -120,12 +120,14 @@ class Machine: uint8_t os_[16384], ram_[32768]; std::vector dfs_, adfs_; - // Things affected by registers, explicitly or otherwise. - uint8_t interrupt_status_, interrupt_control_; - uint8_t key_states_[14]; + // Paging ROMSlot active_rom_; bool keyboard_is_active_, basic_is_active_; + // Interrupt and keyboard state + uint8_t interrupt_status_, interrupt_control_; + uint8_t key_states_[14]; + // Counters related to simultaneous subsystems unsigned int frame_cycles_, display_output_position_; unsigned int audio_output_position_, audio_output_position_error_; diff --git a/Machines/Electron/Video.cpp b/Machines/Electron/Video.cpp index 99979fa22..a919daa8b 100644 --- a/Machines/Electron/Video.cpp +++ b/Machines/Electron/Video.cpp @@ -13,7 +13,7 @@ using namespace Electron; namespace { static const unsigned int cycles_per_line = 128; static const unsigned int lines_per_frame = 625; - static const unsigned int cycles_per_frame = lines_per_frame * cycles_per_line; + 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; @@ -255,130 +255,138 @@ void VideoOutput::run_for_cycles(int number_of_cycles) */ int final_position = output_position_ + number_of_cycles; - int final_line = final_position >> 7; - while(output_position_ < final_position) + + int number_of_frames = 1 + (final_position / cycles_per_frame); + + while(number_of_frames--) { - int line = output_position_ >> 7; + int frame_final = number_of_frames ? cycles_per_frame : (final_position % cycles_per_frame); + int final_line = frame_final >> 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) + while(output_position_ < frame_final) { - // wait for the line to complete before signalling - if(final_line == line) return; - crt_->output_sync(128 * crt_cycles_multiplier); - output_position_ += 128; - continue; - } + int line = output_position_ >> 7; - // 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; - } + // Priority one: sync. + // =================== - // 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 = final_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) + // 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) { - if(final_cycle < 9) return; + // 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); - output_position_ += 9; - this_cycle = 9; + crt_->output_blank(55 * crt_cycles_multiplier); + crt_->output_sync(64 * crt_cycles_multiplier); + output_position_ += 128; + continue; } - if(this_cycle < 24) + // 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_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(final_line == line) return; + crt_->output_sync(9 * crt_cycles_multiplier); + crt_->output_blank(119 * crt_cycles_multiplier); + output_position_ += 128; + continue; } - if(this_cycle < first_graphics_cycle) + // 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) { - 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(); + final_cycle = 128; } - if(this_cycle < first_graphics_cycle + 80) + // 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) { - 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 < 9) + { + if(final_cycle < 9) return; + crt_->output_sync(9 * crt_cycles_multiplier); + output_position_ += 9; + this_cycle = 9; + } - 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; + 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; + } } } - } - output_position_ %= cycles_per_frame; + output_position_ %= cycles_per_frame; + } } void VideoOutput::set_register(int address, uint8_t value) @@ -470,3 +478,15 @@ void VideoOutput::set_register(int address, uint8_t value) break; } } + +#pragma mark - Interrupts + +int VideoOutput::get_cycles_until_next_interrupt() +{ + return 0; +} + +Interrupt VideoOutput::get_next_interrupt() +{ + return DisplayEnd; +}