From 3b5962b1714f2ede877c380917ced8d20fc44495 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Dec 2016 20:01:27 -0500 Subject: [PATCH 01/10] This is an initial attempt at using the actual Oric colour ROM values for composite video generation. --- Machines/Oric/Video.cpp | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index c4cac87d4..fa5054de7 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -37,6 +37,26 @@ VideoOutput::VideoOutput(uint8_t *memory) : "texValue >>= 4 - (int(icoordinate.x * 8) & 4);" "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));" "}"); + crt_->set_composite_sampling_function( + "float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)" + "{" + "float[] array = float[](" +"0.0000, 0.0000, 0.0000, 0.0000, " +"0.1100, 0.3250, 0.3750, 0.1600, " +"0.4100, 0.2150, 0.3250, 0.5200, " +"0.3750, 0.4100, 0.5700, 0.5200, " +"0.2650, 0.2150, 0.0500, 0.1100, " +"0.2650, 0.4100, 0.3250, 0.1600, " +"0.5200, 0.3250, 0.2650, 0.4600, " +"0.5200, 0.5200, 0.5200, 0.5200" + ");" + "uint texValue = texture(sampler, coordinate).r;" + "texValue >>= 4 - (int(icoordinate.x * 8) & 4);" + "uint iPhase = uint((phase + 3.141592654 + 0.39269908175) * 2.0 / 3.141592654) & 3u;" + "return array[((texValue & 7u) << 2) + iPhase];" // (texValue << 2) +// "return (mod(phase, 2.0 * 3.141592654) > 3.141592654) ? 0.7273 : 0.3636;" // (texValue << 2) + "}" + ); crt_->set_output_device(Outputs::CRT::Television); crt_->set_visible_area(crt_->get_rect_for_area(50, 224, 16 * 6, 40 * 6, 4.0f / 3.0f)); From 6cdd41e5a99b90f4d20469b76c2a21f161105814 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Dec 2016 22:17:10 -0500 Subject: [PATCH 02/10] Added direct use of the colour ROM, uploading 16 bits per pixel to contain the entire ROM composite wave. --- Machines/Oric/Oric.cpp | 7 +- Machines/Oric/Oric.hpp | 4 +- Machines/Oric/Video.cpp | 66 +++++++++++-------- Machines/Oric/Video.hpp | 4 +- .../Clock Signal/Machine/Wrappers/CSOric.mm | 2 + 5 files changed, 52 insertions(+), 31 deletions(-) diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 037519eb2..6a6a8310c 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -82,6 +82,10 @@ void Machine::set_rom(ROM rom, const std::vector &data) case BASIC11: basic11_rom_ = std::move(data); break; case BASIC10: basic10_rom_ = std::move(data); break; case Microdisc: microdisc_rom_ = std::move(data); break; + case Colour: + colour_rom_ = std::move(data); + if(video_output_) video_output_->set_colour_rom(colour_rom_); + break; } } @@ -172,9 +176,10 @@ void Machine::update_video() void Machine::setup_output(float aspect_ratio) { - video_output_.reset(new VideoOutput(ram_)); via_.ay8910.reset(new GI::AY38910()); via_.ay8910->set_clock_rate(1000000); + video_output_.reset(new VideoOutput(ram_)); + if(!colour_rom_.empty()) video_output_->set_colour_rom(colour_rom_); } void Machine::close_output() diff --git a/Machines/Oric/Oric.hpp b/Machines/Oric/Oric.hpp index a069d7edc..b7c72889c 100644 --- a/Machines/Oric/Oric.hpp +++ b/Machines/Oric/Oric.hpp @@ -53,7 +53,7 @@ enum Key: uint16_t { }; enum ROM { - BASIC10, BASIC11, Microdisc + BASIC10, BASIC11, Microdisc, Colour }; class Machine: @@ -103,7 +103,7 @@ class Machine: private: // RAM and ROM - std::vector basic11_rom_, basic10_rom_, microdisc_rom_; + std::vector basic11_rom_, basic10_rom_, microdisc_rom_, colour_rom_; uint8_t ram_[65536], rom_[16384]; int cycles_since_video_update_; inline void update_video(); diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index fa5054de7..3326bde69 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -27,7 +27,7 @@ VideoOutput::VideoOutput(uint8_t *memory) : phase_(0), v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition), counter_period_(PAL50Period), next_frame_is_sixty_hertz_(false), - crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 1)) + crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 2)) { // TODO: this is a copy and paste from the Electron; factor out. crt_->set_rgb_sampling_function( @@ -40,21 +40,10 @@ VideoOutput::VideoOutput(uint8_t *memory) : crt_->set_composite_sampling_function( "float composite_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate, float phase, float amplitude)" "{" - "float[] array = float[](" -"0.0000, 0.0000, 0.0000, 0.0000, " -"0.1100, 0.3250, 0.3750, 0.1600, " -"0.4100, 0.2150, 0.3250, 0.5200, " -"0.3750, 0.4100, 0.5700, 0.5200, " -"0.2650, 0.2150, 0.0500, 0.1100, " -"0.2650, 0.4100, 0.3250, 0.1600, " -"0.5200, 0.3250, 0.2650, 0.4600, " -"0.5200, 0.5200, 0.5200, 0.5200" - ");" - "uint texValue = texture(sampler, coordinate).r;" - "texValue >>= 4 - (int(icoordinate.x * 8) & 4);" + "uint texValue = uint(dot(texture(sampler, coordinate).rg, uvec2(1, 256)));" "uint iPhase = uint((phase + 3.141592654 + 0.39269908175) * 2.0 / 3.141592654) & 3u;" - "return array[((texValue & 7u) << 2) + iPhase];" // (texValue << 2) -// "return (mod(phase, 2.0 * 3.141592654) > 3.141592654) ? 0.7273 : 0.3636;" // (texValue << 2) + "texValue = (texValue >> (4u*(3u - iPhase))) & 15u;" + "return (float(texValue) - 4.0) / 20.0;" "}" ); @@ -62,6 +51,17 @@ VideoOutput::VideoOutput(uint8_t *memory) : crt_->set_visible_area(crt_->get_rect_for_area(50, 224, 16 * 6, 40 * 6, 4.0f / 3.0f)); } +void VideoOutput::set_colour_rom(const std::vector &rom) +{ + for(size_t c = 0; c < 8; c++) + { + size_t index = (c << 2); + uint16_t rom_value = (uint16_t)(((uint16_t)rom[index] << 8) | (uint16_t)rom[index+1]); + rom_value = (rom_value & 0xff00) | ((rom_value >> 4)&0x000f) | ((rom_value << 4)&0x00f0); + colour_forms_[c] = rom_value; + } +} + std::shared_ptr VideoOutput::get_crt() { return crt_; @@ -96,11 +96,11 @@ void VideoOutput::run_for_cycles(int number_of_cycles) use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false; set_character_set_base_address(); phase_ += 64; - pixel_target_ = crt_->allocate_write_area(120); + pixel_target_ = (uint16_t *)crt_->allocate_write_area(240); if(!counter_) { - phase_ += 128; // TODO: incorporate all the lines that were missed + phase_ += 3; // TODO: incorporate all the lines that were missed frame_counter_++; v_sync_start_position_ = next_frame_is_sixty_hertz_ ? PAL60VSyncStartPosition : PAL50VSyncStartPosition; @@ -138,14 +138,25 @@ void VideoOutput::run_for_cycles(int number_of_cycles) { if(pixel_target_) { - uint8_t colours[2] = { - (uint8_t)(paper_ ^ inverse_mask), - (uint8_t)(ink_ ^ inverse_mask), - }; +// uint8_t colours[2] = { +// (uint8_t)(paper_ ^ inverse_mask), +// (uint8_t)(ink_ ^ inverse_mask), +// }; +// +// pixel_target_[0] = (colours[(pixels >> 4)&1] & 0x0f) | (colours[(pixels >> 5)&1] & 0xf0); +// pixel_target_[1] = (colours[(pixels >> 2)&1] & 0x0f) | (colours[(pixels >> 3)&1] & 0xf0); +// pixel_target_[2] = (colours[(pixels >> 0)&1] & 0x0f) | (colours[(pixels >> 1)&1] & 0xf0); - pixel_target_[0] = (colours[(pixels >> 4)&1] & 0x0f) | (colours[(pixels >> 5)&1] & 0xf0); - pixel_target_[1] = (colours[(pixels >> 2)&1] & 0x0f) | (colours[(pixels >> 3)&1] & 0xf0); - pixel_target_[2] = (colours[(pixels >> 0)&1] & 0x0f) | (colours[(pixels >> 1)&1] & 0xf0); + uint16_t colours[2] = { + colour_forms_[(paper_ ^ inverse_mask)&0xf], + colour_forms_[(ink_ ^ inverse_mask)&0xf], + }; + pixel_target_[0] = colours[(pixels >> 5)&1]; + pixel_target_[1] = colours[(pixels >> 4)&1]; + pixel_target_[2] = colours[(pixels >> 3)&1]; + pixel_target_[3] = colours[(pixels >> 2)&1]; + pixel_target_[4] = colours[(pixels >> 1)&1]; + pixel_target_[5] = colours[(pixels >> 0)&1]; } } else @@ -186,15 +197,16 @@ void VideoOutput::run_for_cycles(int number_of_cycles) default: break; } - if(pixel_target_) pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = (uint8_t)(paper_ ^ inverse_mask); +// if(pixel_target_) pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = (uint8_t)(paper_ ^ inverse_mask); + if(pixel_target_) pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = pixel_target_[3] = pixel_target_[4] = pixel_target_[5] = colour_forms_[(paper_&0xf) ^ inverse_mask]; } - if(pixel_target_) pixel_target_ += 3; + if(pixel_target_) pixel_target_ += 6; h_counter++; } if(h_counter == 40) { - crt_->output_data(40 * 6, 2); + crt_->output_data(40 * 6, 1); } } else diff --git a/Machines/Oric/Video.hpp b/Machines/Oric/Video.hpp index 2629d3ddd..b8126558e 100644 --- a/Machines/Oric/Video.hpp +++ b/Machines/Oric/Video.hpp @@ -18,6 +18,7 @@ class VideoOutput { VideoOutput(uint8_t *memory); std::shared_ptr get_crt(); void run_for_cycles(int number_of_cycles); + void set_colour_rom(const std::vector &rom); private: uint8_t *ram_; @@ -28,7 +29,8 @@ class VideoOutput { int v_sync_start_position_, v_sync_end_position_, counter_period_; // Output target - uint8_t *pixel_target_; + uint16_t *pixel_target_; + uint16_t colour_forms_[8]; // Registers uint8_t ink_, paper_; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm index 6a565abb4..efc8c6e69 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm @@ -27,10 +27,12 @@ { NSData *basic10 = [self rom:@"basic10"]; NSData *basic11 = [self rom:@"basic11"]; + NSData *colour = [self rom:@"colour"]; NSData *microdisc = [self rom:@"microdisc"]; if(basic10) _oric.set_rom(Oric::BASIC10, basic10.stdVector8); if(basic11) _oric.set_rom(Oric::BASIC11, basic11.stdVector8); + if(colour) _oric.set_rom(Oric::Colour, colour.stdVector8); if(microdisc) _oric.set_rom(Oric::Microdisc, microdisc.stdVector8); } return self; From e359441e2fadc3424731d339b231fb981edfec77 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Fri, 9 Dec 2016 22:18:11 -0500 Subject: [PATCH 03/10] Added a readme.txt for the omitted Oric ROMs. --- ROMImages/Oric/readme.txt | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 ROMImages/Oric/readme.txt diff --git a/ROMImages/Oric/readme.txt b/ROMImages/Oric/readme.txt new file mode 100644 index 000000000..95ece9a35 --- /dev/null +++ b/ROMImages/Oric/readme.txt @@ -0,0 +1,8 @@ +ROM files would ordinarily go here; the copyright status of these is uncertain so they have not been included in this repository. + +Expected files: + +basic10.rom +basic11.rom +colour.rom +microdisc.rom From a549fd1ecc50091ac761e22b12504f7b43c89290 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Dec 2016 13:42:34 -0500 Subject: [PATCH 04/10] Introduced the ability simply to piggy-back off the CRT's natural phase for the colour burst, thereby eliminating a couple of redundant independent attempts in the Oric and Electron. --- Machines/Atari2600/Atari2600.cpp | 4 ++-- Machines/Electron/Electron.cpp | 7 ++----- Machines/Electron/Electron.hpp | 1 - Machines/Oric/Video.cpp | 5 +---- Machines/Oric/Video.hpp | 2 -- Outputs/CRT/CRT.cpp | 33 ++++++++++++++++++++++++++------ Outputs/CRT/CRT.hpp | 16 ++++++++++++---- 7 files changed, 44 insertions(+), 24 deletions(-) diff --git a/Machines/Atari2600/Atari2600.cpp b/Machines/Atari2600/Atari2600.cpp index d2af0dd6f..47df984ea 100644 --- a/Machines/Atari2600/Atari2600.cpp +++ b/Machines/Atari2600/Atari2600.cpp @@ -60,7 +60,7 @@ Machine::Machine() : void Machine::setup_output(float aspect_ratio) { speaker_.reset(new Speaker); - crt_.reset(new Outputs::CRT::CRT(228, 1, 263, Outputs::CRT::ColourSpace::YIQ, 228, 1, 1)); + crt_.reset(new Outputs::CRT::CRT(228, 1, 263, Outputs::CRT::ColourSpace::YIQ, 228, 1, false, 1)); crt_->set_output_device(Outputs::CRT::Television); // this is the NTSC phase offset function; see below for PAL @@ -93,7 +93,7 @@ void Machine::switch_region() "return mix(float(y) / 14.0, step(4, (iPhase + 2u) & 15u) * cos(phase + phaseOffset), amplitude);" "}"); - crt_->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1); + crt_->set_new_timing(228, 312, Outputs::CRT::ColourSpace::YUV, 228, 1, true); is_pal_region_ = true; speaker_->set_input_rate((float)(get_clock_rate() / 38.0)); diff --git a/Machines/Electron/Electron.cpp b/Machines/Electron/Electron.cpp index aee65f36d..c8e156915 100644 --- a/Machines/Electron/Electron.cpp +++ b/Machines/Electron/Electron.cpp @@ -43,8 +43,7 @@ Machine::Machine() : display_output_position_(0), audio_output_position_(0), current_pixel_line_(-1), - use_fast_tape_hack_(false), - phase_(0) + use_fast_tape_hack_(false) { memset(key_states_, 0, sizeof(key_states_)); memset(palette_, 0xf, sizeof(palette_)); @@ -452,8 +451,6 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin frame_cycles_ += cycles; - if(!(frame_cycles_&127)) phase_ += 64; - // 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 @@ -874,7 +871,7 @@ inline void Machine::update_display() if(this_cycle < 24) { if(final_cycle < 24) return; - crt_->output_colour_burst((24-9) * crt_cycles_multiplier, phase_, 12); + crt_->output_default_colour_burst((24-9) * crt_cycles_multiplier); display_output_position_ += 24-9; this_cycle = 24; // TODO: phase shouldn't be zero on every line diff --git a/Machines/Electron/Electron.hpp b/Machines/Electron/Electron.hpp index 38ccaa8ee..980de1e7d 100644 --- a/Machines/Electron/Electron.hpp +++ b/Machines/Electron/Electron.hpp @@ -135,7 +135,6 @@ class Machine: // Counters related to simultaneous subsystems unsigned int frame_cycles_, display_output_position_; unsigned int audio_output_position_, audio_output_position_error_; - uint8_t phase_; struct { uint16_t forty1bpp[256]; diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index 3326bde69..b8838e53b 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -24,7 +24,6 @@ VideoOutput::VideoOutput(uint8_t *memory) : frame_counter_(0), counter_(0), is_graphics_mode_(false), character_set_base_address_(0xb400), - phase_(0), v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition), counter_period_(PAL50Period), next_frame_is_sixty_hertz_(false), crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 2)) @@ -95,12 +94,10 @@ void VideoOutput::run_for_cycles(int number_of_cycles) paper_ = 0x00; use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false; set_character_set_base_address(); - phase_ += 64; pixel_target_ = (uint16_t *)crt_->allocate_write_area(240); if(!counter_) { - phase_ += 3; // TODO: incorporate all the lines that were missed frame_counter_++; v_sync_start_position_ = next_frame_is_sixty_hertz_ ? PAL60VSyncStartPosition : PAL50VSyncStartPosition; @@ -228,7 +225,7 @@ void VideoOutput::run_for_cycles(int number_of_cycles) else if(h_counter < 56) { cycles_run_for = 56 - h_counter; - clamp(crt_->output_colour_burst(2 * 6, phase_, 128)); + clamp(crt_->output_default_colour_burst(2 * 6)); } else { diff --git a/Machines/Oric/Video.hpp b/Machines/Oric/Video.hpp index b8126558e..fe7b91d53 100644 --- a/Machines/Oric/Video.hpp +++ b/Machines/Oric/Video.hpp @@ -43,8 +43,6 @@ class VideoOutput { bool use_alternative_character_set_; bool use_double_height_characters_; bool blink_text_; - - uint8_t phase_; }; } diff --git a/Outputs/CRT/CRT.cpp b/Outputs/CRT/CRT.cpp index 0096fc0c9..8e7694761 100644 --- a/Outputs/CRT/CRT.cpp +++ b/Outputs/CRT/CRT.cpp @@ -14,7 +14,7 @@ using namespace Outputs::CRT; -void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator) +void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, bool should_alternate) { openGL_output_builder_.set_colour_format(colour_space, colour_cycle_numerator, colour_cycle_denominator); @@ -29,6 +29,11 @@ void CRT::set_new_timing(unsigned int cycles_per_line, unsigned int height_of_di // for horizontal retrace and 500 to 750 µs for vertical retrace in NTSC and PAL TV." time_multiplier_ = IntermediateBufferWidth / cycles_per_line; + phase_denominator_ = cycles_per_line * colour_cycle_denominator; + phase_numerator_ = 0; + colour_cycle_numerator_ = colour_cycle_numerator * time_multiplier_; + phase_alternates_ = should_alternate; + is_alernate_line_ &= phase_alternates_; unsigned int multiplied_cycles_per_line = cycles_per_line * time_multiplier_; // generate timing values implied by the given arbuments @@ -50,11 +55,11 @@ void CRT::set_new_display_type(unsigned int cycles_per_line, DisplayType display switch(displayType) { case DisplayType::PAL50: - set_new_timing(cycles_per_line, 312, ColourSpace::YUV, 709379, 2500); // i.e. 283.7516 + set_new_timing(cycles_per_line, 312, ColourSpace::YUV, 709379, 2500, true); // i.e. 283.7516 break; case DisplayType::NTSC60: - set_new_timing(cycles_per_line, 262, ColourSpace::YIQ, 545, 2); + set_new_timing(cycles_per_line, 262, ColourSpace::YIQ, 545, 2, false); break; } } @@ -67,12 +72,13 @@ CRT::CRT(unsigned int common_output_divisor, unsigned int buffer_depth) : is_writing_composite_run_(false), delegate_(nullptr), frames_since_last_delegate_call_(0), - openGL_output_builder_(buffer_depth) {} + openGL_output_builder_(buffer_depth), + is_alernate_line_(false) {} -CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int buffer_depth) : +CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, bool should_alternate, unsigned int buffer_depth) : CRT(common_output_divisor, buffer_depth) { - set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator); + set_new_timing(cycles_per_line, height_of_display, colour_space, colour_cycle_numerator, colour_cycle_denominator, should_alternate); } CRT::CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, DisplayType displayType, unsigned int buffer_depth) : @@ -124,6 +130,8 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo // get the next sync event and its timing; hsync request is instantaneous (being edge triggered) so // set it to false for the next run through this loop (if any) unsigned int next_run_length = std::min(time_until_vertical_sync_event, time_until_horizontal_sync_event); + phase_numerator_ += next_run_length * colour_cycle_numerator_; + phase_numerator_ %= phase_denominator_; hsync_requested = false; vsync_requested = false; @@ -171,6 +179,8 @@ void CRT::advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bo (honoured_event == Flywheel::SyncEvent::StartRetrace && is_writing_composite_run_) || (honoured_event == Flywheel::SyncEvent::EndRetrace && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); + if(next_run_length == time_until_horizontal_sync_event && next_horizontal_sync_event == Flywheel::SyncEvent::StartRetrace) is_alernate_line_ ^= phase_alternates_; + if(needs_endpoint) { if( @@ -329,6 +339,17 @@ void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint output_scan(&scan); } +void CRT::output_default_colour_burst(unsigned int number_of_cycles) +{ + Scan scan { + .type = Scan::Type::ColourBurst, + .number_of_cycles = number_of_cycles, + .phase = (uint8_t)((phase_numerator_ * 255) / phase_denominator_ + (is_alernate_line_ ? 128 : 0)), + .amplitude = 32 + }; + output_scan(&scan); +} + void CRT::output_data(unsigned int number_of_cycles, unsigned int source_divider) { openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_cycles / source_divider); diff --git a/Outputs/CRT/CRT.hpp b/Outputs/CRT/CRT.hpp index d2482c25b..0b07910f9 100644 --- a/Outputs/CRT/CRT.hpp +++ b/Outputs/CRT/CRT.hpp @@ -46,7 +46,6 @@ class CRT { int sync_capacitor_charge_threshold_; // this charges up during times of sync and depletes otherwise; needs to hit a required threshold to trigger a vertical sync unsigned int sync_period_; - // each call to output_* generates a scan. A two-slot queue for scans allows edge extensions. struct Scan { enum Type { Sync, Level, Data, Blank, ColourBurst @@ -64,6 +63,9 @@ class CRT { uint16_t colour_burst_time_; bool is_writing_composite_run_; + unsigned int phase_denominator_, phase_numerator_, colour_cycle_numerator_; + bool is_alernate_line_, phase_alternates_; + // the outer entry point for dispatching output_sync, output_blank, output_level and output_data void advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type); @@ -91,7 +93,7 @@ class CRT { @param cycles_per_line The clock rate at which this CRT will be driven, specified as the number of cycles expected to take up one whole scanline of the display. - + @param common_output_divisor The greatest a priori common divisor of all cycle counts that will be supplied to @c output_sync, @c output_data, etc; supply 1 if no greater divisor is known. For many machines output will run at a fixed multiple of the clock rate; knowing this divisor can improve @@ -113,7 +115,7 @@ class CRT { @see @c set_rgb_sampling_function , @c set_composite_sampling_function */ - CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, unsigned int buffer_depth); + CRT(unsigned int cycles_per_line, unsigned int common_output_divisor, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, bool should_alternate, unsigned int buffer_depth); /*! Constructs the CRT with the specified clock rate, with the display height and colour subcarrier frequency dictated by a standard display type and with the requested number of @@ -126,7 +128,7 @@ class CRT { /*! Resets the CRT with new timing information. The CRT then continues as though the new timing had been provided at construction. */ - void set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator); + void set_new_timing(unsigned int cycles_per_line, unsigned int height_of_display, ColourSpace colour_space, unsigned int colour_cycle_numerator, unsigned int colour_cycle_denominator, bool should_alternate); /*! Resets the CRT with new timing information derived from a new display type. The CRT then continues as though the new timing had been provided at construction. */ @@ -175,6 +177,12 @@ class CRT { */ void output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint8_t amplitude); + /*! Outputs a colour burst exactly in phase with CRT expectations using the idiomatic amplitude. + + @param number_of_cycles The length of the colour burst; + */ + void output_default_colour_burst(unsigned int number_of_cycles); + /*! Attempts to allocate the given number of output samples for writing. The beginning of the most recently allocated area is used as the start From 580f3477278f7532a8f29a43f2b81929fc44a13d Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Dec 2016 13:55:56 -0500 Subject: [PATCH 05/10] Fixed Oric SCART mode by having it change what it's giving to the CRT based on which shader it knows will be active. --- Machines/Oric/Oric.cpp | 5 +++ Machines/Oric/Oric.hpp | 1 + Machines/Oric/Video.cpp | 42 +++++++++++-------- Machines/Oric/Video.hpp | 4 +- .../Clock Signal/Machine/Wrappers/CSOric.mm | 2 +- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/Machines/Oric/Oric.cpp b/Machines/Oric/Oric.cpp index 6a6a8310c..55d7c3806 100644 --- a/Machines/Oric/Oric.cpp +++ b/Machines/Oric/Oric.cpp @@ -218,6 +218,11 @@ void Machine::set_use_fast_tape_hack(bool activate) use_fast_tape_hack_ = activate; } +void Machine::set_output_device(Outputs::CRT::OutputDevice output_device) +{ + video_output_->set_output_device(output_device); +} + void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player) { // set CB1 diff --git a/Machines/Oric/Oric.hpp b/Machines/Oric/Oric.hpp index b7c72889c..aec4d0495 100644 --- a/Machines/Oric/Oric.hpp +++ b/Machines/Oric/Oric.hpp @@ -73,6 +73,7 @@ class Machine: void clear_all_keys(); void set_use_fast_tape_hack(bool activate); + void set_output_device(Outputs::CRT::OutputDevice output_device); // to satisfy ConfigurationTarget::Machine void configure_as_target(const StaticAnalyser::Target &target); diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index b8838e53b..71dec70e8 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -33,7 +33,6 @@ VideoOutput::VideoOutput(uint8_t *memory) : "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" "{" "uint texValue = texture(sampler, coordinate).r;" - "texValue >>= 4 - (int(icoordinate.x * 8) & 4);" "return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));" "}"); crt_->set_composite_sampling_function( @@ -46,10 +45,16 @@ VideoOutput::VideoOutput(uint8_t *memory) : "}" ); - crt_->set_output_device(Outputs::CRT::Television); + set_output_device(Outputs::CRT::Television); crt_->set_visible_area(crt_->get_rect_for_area(50, 224, 16 * 6, 40 * 6, 4.0f / 3.0f)); } +void VideoOutput::set_output_device(Outputs::CRT::OutputDevice output_device) +{ + output_device_ = output_device; + crt_->set_output_device(output_device); +} + void VideoOutput::set_colour_rom(const std::vector &rom) { for(size_t c = 0; c < 8; c++) @@ -135,19 +140,17 @@ void VideoOutput::run_for_cycles(int number_of_cycles) { if(pixel_target_) { -// uint8_t colours[2] = { -// (uint8_t)(paper_ ^ inverse_mask), -// (uint8_t)(ink_ ^ inverse_mask), -// }; -// -// pixel_target_[0] = (colours[(pixels >> 4)&1] & 0x0f) | (colours[(pixels >> 5)&1] & 0xf0); -// pixel_target_[1] = (colours[(pixels >> 2)&1] & 0x0f) | (colours[(pixels >> 3)&1] & 0xf0); -// pixel_target_[2] = (colours[(pixels >> 0)&1] & 0x0f) | (colours[(pixels >> 1)&1] & 0xf0); - - uint16_t colours[2] = { - colour_forms_[(paper_ ^ inverse_mask)&0xf], - colour_forms_[(ink_ ^ inverse_mask)&0xf], - }; + uint16_t colours[2]; + if(output_device_ == Outputs::CRT::Monitor) + { + colours[0] = (uint8_t)((paper_ ^ inverse_mask) & 0xf); + colours[1] = (uint8_t)((ink_ ^ inverse_mask) & 0xf); + } + else + { + colours[0] = colour_forms_[(paper_ ^ inverse_mask)&0xf]; + colours[1] = colour_forms_[(ink_ ^ inverse_mask)&0xf]; + } pixel_target_[0] = colours[(pixels >> 5)&1]; pixel_target_[1] = colours[(pixels >> 4)&1]; pixel_target_[2] = colours[(pixels >> 3)&1]; @@ -194,8 +197,13 @@ void VideoOutput::run_for_cycles(int number_of_cycles) default: break; } -// if(pixel_target_) pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = (uint8_t)(paper_ ^ inverse_mask); - if(pixel_target_) pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = pixel_target_[3] = pixel_target_[4] = pixel_target_[5] = colour_forms_[(paper_&0xf) ^ inverse_mask]; + if(pixel_target_) + { + pixel_target_[0] = pixel_target_[1] = + pixel_target_[2] = pixel_target_[3] = + pixel_target_[4] = pixel_target_[5] = + (output_device_ == Outputs::CRT::Monitor) ? (paper_ ^ inverse_mask)&0xf : colour_forms_[(paper_ ^ inverse_mask)&0xf]; + } } if(pixel_target_) pixel_target_ += 6; h_counter++; diff --git a/Machines/Oric/Video.hpp b/Machines/Oric/Video.hpp index fe7b91d53..992d73470 100644 --- a/Machines/Oric/Video.hpp +++ b/Machines/Oric/Video.hpp @@ -19,6 +19,7 @@ class VideoOutput { std::shared_ptr get_crt(); void run_for_cycles(int number_of_cycles); void set_colour_rom(const std::vector &rom); + void set_output_device(Outputs::CRT::OutputDevice output_device); private: uint8_t *ram_; @@ -28,9 +29,10 @@ class VideoOutput { int counter_, frame_counter_; int v_sync_start_position_, v_sync_end_position_, counter_period_; - // Output target + // Output target and device uint16_t *pixel_target_; uint16_t colour_forms_[8]; + Outputs::CRT::OutputDevice output_device_; // Registers uint8_t ink_, paper_; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm index efc8c6e69..c2f38479d 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSOric.mm @@ -152,7 +152,7 @@ - (void)setUseCompositeOutput:(BOOL)useCompositeOutput { @synchronized(self) { _useCompositeOutput = useCompositeOutput; - _oric.get_crt()->set_output_device(useCompositeOutput ? Outputs::CRT::Television : Outputs::CRT::Monitor); + _oric.set_output_device(useCompositeOutput ? Outputs::CRT::Television : Outputs::CRT::Monitor); } } From 0e71802b92dbdd2bc494d209414bfe755ddee7ac Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Dec 2016 14:17:46 -0500 Subject: [PATCH 06/10] Reduced Oric video to single nibble constants. Removed attempt at asynchronous flush as no longer required. --- Machines/Oric/Video.cpp | 48 +++++++++++++------------- Outputs/CRT/Internals/ArrayBuilder.cpp | 14 +++----- Outputs/CRT/Internals/ArrayBuilder.hpp | 2 +- 3 files changed, 29 insertions(+), 35 deletions(-) diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index 71dec70e8..28c2af5df 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -95,8 +95,8 @@ void VideoOutput::run_for_cycles(int number_of_cycles) // this is a pixel line if(!h_counter) { - ink_ = 0xff; - paper_ = 0x00; + ink_ = 0xf; + paper_ = 0x0; use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false; set_character_set_base_address(); pixel_target_ = (uint16_t *)crt_->allocate_write_area(240); @@ -133,7 +133,7 @@ void VideoOutput::run_for_cycles(int number_of_cycles) pixels = ram_[character_set_base_address_ + (control_byte&127) * 8 + line]; } - uint8_t inverse_mask = (control_byte & 0x80) ? 0x77 : 0x00; + uint8_t inverse_mask = (control_byte & 0x80) ? 0x7 : 0x0; pixels &= blink_mask; if(control_byte & 0x60) @@ -143,13 +143,13 @@ void VideoOutput::run_for_cycles(int number_of_cycles) uint16_t colours[2]; if(output_device_ == Outputs::CRT::Monitor) { - colours[0] = (uint8_t)((paper_ ^ inverse_mask) & 0xf); - colours[1] = (uint8_t)((ink_ ^ inverse_mask) & 0xf); + colours[0] = (uint8_t)(paper_ ^ inverse_mask); + colours[1] = (uint8_t)(ink_ ^ inverse_mask); } else { - colours[0] = colour_forms_[(paper_ ^ inverse_mask)&0xf]; - colours[1] = colour_forms_[(ink_ ^ inverse_mask)&0xf]; + colours[0] = colour_forms_[paper_ ^ inverse_mask]; + colours[1] = colour_forms_[ink_ ^ inverse_mask]; } pixel_target_[0] = colours[(pixels >> 5)&1]; pixel_target_[1] = colours[(pixels >> 4)&1]; @@ -163,14 +163,14 @@ void VideoOutput::run_for_cycles(int number_of_cycles) { switch(control_byte & 0x1f) { - case 0x00: ink_ = 0x00; break; - case 0x01: ink_ = 0x44; break; - case 0x02: ink_ = 0x22; break; - case 0x03: ink_ = 0x66; break; - case 0x04: ink_ = 0x11; break; - case 0x05: ink_ = 0x55; break; - case 0x06: ink_ = 0x33; break; - case 0x07: ink_ = 0x77; break; + case 0x00: ink_ = 0x0; break; + case 0x01: ink_ = 0x4; break; + case 0x02: ink_ = 0x2; break; + case 0x03: ink_ = 0x6; break; + case 0x04: ink_ = 0x1; break; + case 0x05: ink_ = 0x5; break; + case 0x06: ink_ = 0x3; break; + case 0x07: ink_ = 0x7; break; case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x0c: case 0x0d: case 0x0e: case 0x0f: @@ -180,14 +180,14 @@ void VideoOutput::run_for_cycles(int number_of_cycles) set_character_set_base_address(); break; - case 0x10: paper_ = 0x00; break; - case 0x11: paper_ = 0x44; break; - case 0x12: paper_ = 0x22; break; - case 0x13: paper_ = 0x66; break; - case 0x14: paper_ = 0x11; break; - case 0x15: paper_ = 0x55; break; - case 0x16: paper_ = 0x33; break; - case 0x17: paper_ = 0x77; break; + case 0x10: paper_ = 0x0; break; + case 0x11: paper_ = 0x4; break; + case 0x12: paper_ = 0x2; break; + case 0x13: paper_ = 0x6; break; + case 0x14: paper_ = 0x1; break; + case 0x15: paper_ = 0x5; break; + case 0x16: paper_ = 0x3; break; + case 0x17: paper_ = 0x7; break; case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x1c: case 0x1d: case 0x1e: case 0x1f: @@ -202,7 +202,7 @@ void VideoOutput::run_for_cycles(int number_of_cycles) pixel_target_[0] = pixel_target_[1] = pixel_target_[2] = pixel_target_[3] = pixel_target_[4] = pixel_target_[5] = - (output_device_ == Outputs::CRT::Monitor) ? (paper_ ^ inverse_mask)&0xf : colour_forms_[(paper_ ^ inverse_mask)&0xf]; + (output_device_ == Outputs::CRT::Monitor) ? paper_ ^ inverse_mask : colour_forms_[paper_ ^ inverse_mask]; } } if(pixel_target_) pixel_target_ += 6; diff --git a/Outputs/CRT/Internals/ArrayBuilder.cpp b/Outputs/CRT/Internals/ArrayBuilder.cpp index 61a9be2fb..0793dc372 100644 --- a/Outputs/CRT/Internals/ArrayBuilder.cpp +++ b/Outputs/CRT/Internals/ArrayBuilder.cpp @@ -78,7 +78,7 @@ ArrayBuilder::Submission ArrayBuilder::submit() } ArrayBuilder::Buffer::Buffer(size_t size, std::function submission_function) : - is_full(false), was_reset(false), + is_full(false), submission_function_(submission_function), allocated_data(0), flushed_data(0), submitted_data(0) { @@ -137,14 +137,6 @@ void ArrayBuilder::Buffer::flush() } flushed_data = allocated_data; - - if(was_reset) - { - allocated_data = 0; - flushed_data = 0; - submitted_data = 0; - was_reset = false; - } } size_t ArrayBuilder::Buffer::submit(bool is_input) @@ -171,6 +163,8 @@ void ArrayBuilder::Buffer::bind() void ArrayBuilder::Buffer::reset() { - was_reset = true; is_full = false; + allocated_data = 0; + flushed_data = 0; + submitted_data = 0; } diff --git a/Outputs/CRT/Internals/ArrayBuilder.hpp b/Outputs/CRT/Internals/ArrayBuilder.hpp index 135a84c24..1c02e5e68 100644 --- a/Outputs/CRT/Internals/ArrayBuilder.hpp +++ b/Outputs/CRT/Internals/ArrayBuilder.hpp @@ -82,7 +82,7 @@ class ArrayBuilder { void reset(); private: - bool is_full, was_reset; + bool is_full; GLuint buffer; std::function submission_function_; std::vector data; From a5683dfb21a246c3428015ed1f8be644323b69ca Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Dec 2016 15:19:48 -0500 Subject: [PATCH 07/10] Removed now untrue comment. --- Machines/Oric/Video.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index 28c2af5df..320cbd34b 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -28,7 +28,6 @@ VideoOutput::VideoOutput(uint8_t *memory) : counter_period_(PAL50Period), next_frame_is_sixty_hertz_(false), crt_(new Outputs::CRT::CRT(64*6, 6, Outputs::CRT::DisplayType::PAL50, 2)) { - // TODO: this is a copy and paste from the Electron; factor out. crt_->set_rgb_sampling_function( "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" "{" @@ -95,7 +94,7 @@ void VideoOutput::run_for_cycles(int number_of_cycles) // this is a pixel line if(!h_counter) { - ink_ = 0xf; + ink_ = 0x7; paper_ = 0x0; use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false; set_character_set_base_address(); From 81a102d95147e7f08aaf6fadfd8059794d44eaa3 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Dec 2016 15:20:10 -0500 Subject: [PATCH 08/10] Upped intermediate buffer size, at least temporarily, while I look for the source of the interference patterns I'm seeing. --- Outputs/CRT/Internals/CRTConstants.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Outputs/CRT/Internals/CRTConstants.hpp b/Outputs/CRT/Internals/CRTConstants.hpp index 54f14edbe..b7fefe8b9 100644 --- a/Outputs/CRT/Internals/CRTConstants.hpp +++ b/Outputs/CRT/Internals/CRTConstants.hpp @@ -36,7 +36,7 @@ const GLsizei InputBufferBuilderWidth = 2048; const GLsizei InputBufferBuilderHeight = 512; // This is the size of the intermediate buffers used during composite to RGB conversion -const GLsizei IntermediateBufferWidth = 2048; +const GLsizei IntermediateBufferWidth = 4096; const GLsizei IntermediateBufferHeight = 512; // Some internal buffer sizes From 34d213dec487d0c4d775c2b027d3b31fecd6230a Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Dec 2016 15:35:38 -0500 Subject: [PATCH 09/10] Decreased Y resolution, again also hopefully temporarily. --- Outputs/CRT/Internals/CRTOpenGL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Outputs/CRT/Internals/CRTOpenGL.cpp b/Outputs/CRT/Internals/CRTOpenGL.cpp index 5cb634ca8..6296b3e61 100644 --- a/Outputs/CRT/Internals/CRTOpenGL.cpp +++ b/Outputs/CRT/Internals/CRTOpenGL.cpp @@ -385,7 +385,7 @@ void OpenGLOutputBuilder::set_timing_uniforms() float colour_subcarrier_frequency = (float)colour_cycle_numerator_ / (float)colour_cycle_denominator_; if(composite_separation_filter_program_) composite_separation_filter_program_->set_separation_frequency(cycles_per_line_, colour_subcarrier_frequency); - if(composite_y_filter_shader_program_) composite_y_filter_shader_program_->set_filter_coefficients(cycles_per_line_, colour_subcarrier_frequency * 0.66f); + if(composite_y_filter_shader_program_) composite_y_filter_shader_program_->set_filter_coefficients(cycles_per_line_, colour_subcarrier_frequency * 0.25f); if(composite_chrominance_filter_shader_program_) composite_chrominance_filter_shader_program_->set_filter_coefficients(cycles_per_line_, colour_subcarrier_frequency * 0.5f); if(rgb_filter_shader_program_) rgb_filter_shader_program_->set_filter_coefficients(cycles_per_line_, (float)input_frequency_ * 0.5f); } From e62be036739f5bf7f6d8ab2c60716fb787ec79b5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 10 Dec 2016 19:10:33 -0500 Subject: [PATCH 10/10] Removed endianness assumption. --- Machines/Oric/Video.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Machines/Oric/Video.cpp b/Machines/Oric/Video.cpp index 320cbd34b..81281022d 100644 --- a/Machines/Oric/Video.cpp +++ b/Machines/Oric/Video.cpp @@ -63,6 +63,16 @@ void VideoOutput::set_colour_rom(const std::vector &rom) rom_value = (rom_value & 0xff00) | ((rom_value >> 4)&0x000f) | ((rom_value << 4)&0x00f0); colour_forms_[c] = rom_value; } + + // check for big endianness and byte swap if required + uint16_t test_value = 0x0001; + if(*(uint8_t *)&test_value != 0x01) + { + for(size_t c = 0; c < 8; c++) + { + colour_forms_[c] = (uint16_t)((colour_forms_[c] >> 8) | (colour_forms_[c] << 8)); + } + } } std::shared_ptr VideoOutput::get_crt()