1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-17 13:29:02 +00:00

Merge pull request #84 from TomHarte/OricColour

Switches to using the original Oric colour ROM to generate Oric composite values
This commit is contained in:
Thomas Harte 2016-12-10 19:37:17 -05:00 committed by GitHub
commit 0653770c63
15 changed files with 166 additions and 78 deletions

View File

@ -60,7 +60,7 @@ Machine::Machine() :
void Machine::setup_output(float aspect_ratio) void Machine::setup_output(float aspect_ratio)
{ {
speaker_.reset(new Speaker); 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); crt_->set_output_device(Outputs::CRT::Television);
// this is the NTSC phase offset function; see below for PAL // 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);" "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; is_pal_region_ = true;
speaker_->set_input_rate((float)(get_clock_rate() / 38.0)); speaker_->set_input_rate((float)(get_clock_rate() / 38.0));

View File

@ -43,8 +43,7 @@ Machine::Machine() :
display_output_position_(0), display_output_position_(0),
audio_output_position_(0), audio_output_position_(0),
current_pixel_line_(-1), current_pixel_line_(-1),
use_fast_tape_hack_(false), use_fast_tape_hack_(false)
phase_(0)
{ {
memset(key_states_, 0, sizeof(key_states_)); memset(key_states_, 0, sizeof(key_states_));
memset(palette_, 0xf, sizeof(palette_)); memset(palette_, 0xf, sizeof(palette_));
@ -452,8 +451,6 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
frame_cycles_ += cycles; frame_cycles_ += cycles;
if(!(frame_cycles_&127)) phase_ += 64;
// deal with frame wraparound by updating the two dependent subsystems // deal with frame wraparound by updating the two dependent subsystems
// as though the exact end of frame had been hit, then reset those // as though the exact end of frame had been hit, then reset those
// and allow the frame cycle counter to assume its real value // 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(this_cycle < 24)
{ {
if(final_cycle < 24) return; 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; display_output_position_ += 24-9;
this_cycle = 24; this_cycle = 24;
// TODO: phase shouldn't be zero on every line // TODO: phase shouldn't be zero on every line

View File

@ -135,7 +135,6 @@ class Machine:
// Counters related to simultaneous subsystems // Counters related to simultaneous subsystems
unsigned int frame_cycles_, display_output_position_; unsigned int frame_cycles_, display_output_position_;
unsigned int audio_output_position_, audio_output_position_error_; unsigned int audio_output_position_, audio_output_position_error_;
uint8_t phase_;
struct { struct {
uint16_t forty1bpp[256]; uint16_t forty1bpp[256];

View File

@ -82,6 +82,10 @@ void Machine::set_rom(ROM rom, const std::vector<uint8_t> &data)
case BASIC11: basic11_rom_ = std::move(data); break; case BASIC11: basic11_rom_ = std::move(data); break;
case BASIC10: basic10_rom_ = std::move(data); break; case BASIC10: basic10_rom_ = std::move(data); break;
case Microdisc: microdisc_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) void Machine::setup_output(float aspect_ratio)
{ {
video_output_.reset(new VideoOutput(ram_));
via_.ay8910.reset(new GI::AY38910()); via_.ay8910.reset(new GI::AY38910());
via_.ay8910->set_clock_rate(1000000); 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() void Machine::close_output()
@ -213,6 +218,11 @@ void Machine::set_use_fast_tape_hack(bool activate)
use_fast_tape_hack_ = 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) void Machine::tape_did_change_input(Storage::Tape::BinaryTapePlayer *tape_player)
{ {
// set CB1 // set CB1

View File

@ -53,7 +53,7 @@ enum Key: uint16_t {
}; };
enum ROM { enum ROM {
BASIC10, BASIC11, Microdisc BASIC10, BASIC11, Microdisc, Colour
}; };
class Machine: class Machine:
@ -73,6 +73,7 @@ class Machine:
void clear_all_keys(); void clear_all_keys();
void set_use_fast_tape_hack(bool activate); void set_use_fast_tape_hack(bool activate);
void set_output_device(Outputs::CRT::OutputDevice output_device);
// to satisfy ConfigurationTarget::Machine // to satisfy ConfigurationTarget::Machine
void configure_as_target(const StaticAnalyser::Target &target); void configure_as_target(const StaticAnalyser::Target &target);
@ -103,7 +104,7 @@ class Machine:
private: private:
// RAM and ROM // RAM and ROM
std::vector<uint8_t> basic11_rom_, basic10_rom_, microdisc_rom_; std::vector<uint8_t> basic11_rom_, basic10_rom_, microdisc_rom_, colour_rom_;
uint8_t ram_[65536], rom_[16384]; uint8_t ram_[65536], rom_[16384];
int cycles_since_video_update_; int cycles_since_video_update_;
inline void update_video(); inline void update_video();

View File

@ -24,24 +24,57 @@ VideoOutput::VideoOutput(uint8_t *memory) :
frame_counter_(0), counter_(0), frame_counter_(0), counter_(0),
is_graphics_mode_(false), is_graphics_mode_(false),
character_set_base_address_(0xb400), character_set_base_address_(0xb400),
phase_(0),
v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition), v_sync_start_position_(PAL50VSyncStartPosition), v_sync_end_position_(PAL50VSyncEndPosition),
counter_period_(PAL50Period), next_frame_is_sixty_hertz_(false), 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( crt_->set_rgb_sampling_function(
"vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)" "vec3 rgb_sample(usampler2D sampler, vec2 coordinate, vec2 icoordinate)"
"{" "{"
"uint texValue = texture(sampler, coordinate).r;" "uint texValue = texture(sampler, coordinate).r;"
"texValue >>= 4 - (int(icoordinate.x * 8) & 4);"
"return vec3( uvec3(texValue) & uvec3(4u, 2u, 1u));" "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)"
"{"
"uint texValue = uint(dot(texture(sampler, coordinate).rg, uvec2(1, 256)));"
"uint iPhase = uint((phase + 3.141592654 + 0.39269908175) * 2.0 / 3.141592654) & 3u;"
"texValue = (texValue >> (4u*(3u - iPhase))) & 15u;"
"return (float(texValue) - 4.0) / 20.0;"
"}"
);
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)); 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<uint8_t> &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;
}
// 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<Outputs::CRT::CRT> VideoOutput::get_crt() std::shared_ptr<Outputs::CRT::CRT> VideoOutput::get_crt()
{ {
return crt_; return crt_;
@ -71,16 +104,14 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
// this is a pixel line // this is a pixel line
if(!h_counter) if(!h_counter)
{ {
ink_ = 0xff; ink_ = 0x7;
paper_ = 0x00; paper_ = 0x0;
use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false; use_alternative_character_set_ = use_double_height_characters_ = blink_text_ = false;
set_character_set_base_address(); set_character_set_base_address();
phase_ += 64; pixel_target_ = (uint16_t *)crt_->allocate_write_area(240);
pixel_target_ = crt_->allocate_write_area(120);
if(!counter_) if(!counter_)
{ {
phase_ += 128; // TODO: incorporate all the lines that were missed
frame_counter_++; frame_counter_++;
v_sync_start_position_ = next_frame_is_sixty_hertz_ ? PAL60VSyncStartPosition : PAL50VSyncStartPosition; v_sync_start_position_ = next_frame_is_sixty_hertz_ ? PAL60VSyncStartPosition : PAL50VSyncStartPosition;
@ -111,35 +142,44 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
pixels = ram_[character_set_base_address_ + (control_byte&127) * 8 + line]; 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; pixels &= blink_mask;
if(control_byte & 0x60) if(control_byte & 0x60)
{ {
if(pixel_target_) if(pixel_target_)
{ {
uint8_t colours[2] = { uint16_t colours[2];
(uint8_t)(paper_ ^ inverse_mask), if(output_device_ == Outputs::CRT::Monitor)
(uint8_t)(ink_ ^ inverse_mask), {
}; colours[0] = (uint8_t)(paper_ ^ inverse_mask);
colours[1] = (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); else
pixel_target_[2] = (colours[(pixels >> 0)&1] & 0x0f) | (colours[(pixels >> 1)&1] & 0xf0); {
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];
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 else
{ {
switch(control_byte & 0x1f) switch(control_byte & 0x1f)
{ {
case 0x00: ink_ = 0x00; break; case 0x00: ink_ = 0x0; break;
case 0x01: ink_ = 0x44; break; case 0x01: ink_ = 0x4; break;
case 0x02: ink_ = 0x22; break; case 0x02: ink_ = 0x2; break;
case 0x03: ink_ = 0x66; break; case 0x03: ink_ = 0x6; break;
case 0x04: ink_ = 0x11; break; case 0x04: ink_ = 0x1; break;
case 0x05: ink_ = 0x55; break; case 0x05: ink_ = 0x5; break;
case 0x06: ink_ = 0x33; break; case 0x06: ink_ = 0x3; break;
case 0x07: ink_ = 0x77; break; case 0x07: ink_ = 0x7; break;
case 0x08: case 0x09: case 0x0a: case 0x0b: case 0x08: case 0x09: case 0x0a: case 0x0b:
case 0x0c: case 0x0d: case 0x0e: case 0x0f: case 0x0c: case 0x0d: case 0x0e: case 0x0f:
@ -149,14 +189,14 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
set_character_set_base_address(); set_character_set_base_address();
break; break;
case 0x10: paper_ = 0x00; break; case 0x10: paper_ = 0x0; break;
case 0x11: paper_ = 0x44; break; case 0x11: paper_ = 0x4; break;
case 0x12: paper_ = 0x22; break; case 0x12: paper_ = 0x2; break;
case 0x13: paper_ = 0x66; break; case 0x13: paper_ = 0x6; break;
case 0x14: paper_ = 0x11; break; case 0x14: paper_ = 0x1; break;
case 0x15: paper_ = 0x55; break; case 0x15: paper_ = 0x5; break;
case 0x16: paper_ = 0x33; break; case 0x16: paper_ = 0x3; break;
case 0x17: paper_ = 0x77; break; case 0x17: paper_ = 0x7; break;
case 0x18: case 0x19: case 0x1a: case 0x1b: case 0x18: case 0x19: case 0x1a: case 0x1b:
case 0x1c: case 0x1d: case 0x1e: case 0x1f: case 0x1c: case 0x1d: case 0x1e: case 0x1f:
@ -166,15 +206,21 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
default: break; 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] =
(output_device_ == Outputs::CRT::Monitor) ? paper_ ^ inverse_mask : colour_forms_[paper_ ^ inverse_mask];
} }
if(pixel_target_) pixel_target_ += 3; }
if(pixel_target_) pixel_target_ += 6;
h_counter++; h_counter++;
} }
if(h_counter == 40) if(h_counter == 40)
{ {
crt_->output_data(40 * 6, 2); crt_->output_data(40 * 6, 1);
} }
} }
else else
@ -196,7 +242,7 @@ void VideoOutput::run_for_cycles(int number_of_cycles)
else if(h_counter < 56) else if(h_counter < 56)
{ {
cycles_run_for = 56 - h_counter; cycles_run_for = 56 - h_counter;
clamp(crt_->output_colour_burst(2 * 6, phase_, 128)); clamp(crt_->output_default_colour_burst(2 * 6));
} }
else else
{ {

View File

@ -18,6 +18,8 @@ class VideoOutput {
VideoOutput(uint8_t *memory); VideoOutput(uint8_t *memory);
std::shared_ptr<Outputs::CRT::CRT> get_crt(); std::shared_ptr<Outputs::CRT::CRT> get_crt();
void run_for_cycles(int number_of_cycles); void run_for_cycles(int number_of_cycles);
void set_colour_rom(const std::vector<uint8_t> &rom);
void set_output_device(Outputs::CRT::OutputDevice output_device);
private: private:
uint8_t *ram_; uint8_t *ram_;
@ -27,8 +29,10 @@ class VideoOutput {
int counter_, frame_counter_; int counter_, frame_counter_;
int v_sync_start_position_, v_sync_end_position_, counter_period_; int v_sync_start_position_, v_sync_end_position_, counter_period_;
// Output target // Output target and device
uint8_t *pixel_target_; uint16_t *pixel_target_;
uint16_t colour_forms_[8];
Outputs::CRT::OutputDevice output_device_;
// Registers // Registers
uint8_t ink_, paper_; uint8_t ink_, paper_;
@ -41,8 +45,6 @@ class VideoOutput {
bool use_alternative_character_set_; bool use_alternative_character_set_;
bool use_double_height_characters_; bool use_double_height_characters_;
bool blink_text_; bool blink_text_;
uint8_t phase_;
}; };
} }

View File

@ -27,10 +27,12 @@
{ {
NSData *basic10 = [self rom:@"basic10"]; NSData *basic10 = [self rom:@"basic10"];
NSData *basic11 = [self rom:@"basic11"]; NSData *basic11 = [self rom:@"basic11"];
NSData *colour = [self rom:@"colour"];
NSData *microdisc = [self rom:@"microdisc"]; NSData *microdisc = [self rom:@"microdisc"];
if(basic10) _oric.set_rom(Oric::BASIC10, basic10.stdVector8); if(basic10) _oric.set_rom(Oric::BASIC10, basic10.stdVector8);
if(basic11) _oric.set_rom(Oric::BASIC11, basic11.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); if(microdisc) _oric.set_rom(Oric::Microdisc, microdisc.stdVector8);
} }
return self; return self;
@ -150,7 +152,7 @@
- (void)setUseCompositeOutput:(BOOL)useCompositeOutput { - (void)setUseCompositeOutput:(BOOL)useCompositeOutput {
@synchronized(self) { @synchronized(self) {
_useCompositeOutput = useCompositeOutput; _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);
} }
} }

View File

@ -14,7 +14,7 @@
using namespace Outputs::CRT; 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); 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." // for horizontal retrace and 500 to 750 µs for vertical retrace in NTSC and PAL TV."
time_multiplier_ = IntermediateBufferWidth / cycles_per_line; 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_; unsigned int multiplied_cycles_per_line = cycles_per_line * time_multiplier_;
// generate timing values implied by the given arbuments // 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) switch(displayType)
{ {
case DisplayType::PAL50: 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; break;
case DisplayType::NTSC60: 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; break;
} }
} }
@ -67,12 +72,13 @@ CRT::CRT(unsigned int common_output_divisor, unsigned int buffer_depth) :
is_writing_composite_run_(false), is_writing_composite_run_(false),
delegate_(nullptr), delegate_(nullptr),
frames_since_last_delegate_call_(0), 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) 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) : 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 // 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) // 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); 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; hsync_requested = false;
vsync_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::StartRetrace && is_writing_composite_run_) ||
(honoured_event == Flywheel::SyncEvent::EndRetrace && !horizontal_flywheel_->is_in_retrace() && !vertical_flywheel_->is_in_retrace()); (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(needs_endpoint)
{ {
if( if(
@ -329,6 +339,17 @@ void CRT::output_colour_burst(unsigned int number_of_cycles, uint8_t phase, uint
output_scan(&scan); 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) 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); openGL_output_builder_.texture_builder.reduce_previous_allocation_to(number_of_cycles / source_divider);

View File

@ -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 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_; unsigned int sync_period_;
// each call to output_* generates a scan. A two-slot queue for scans allows edge extensions.
struct Scan { struct Scan {
enum Type { enum Type {
Sync, Level, Data, Blank, ColourBurst Sync, Level, Data, Blank, ColourBurst
@ -64,6 +63,9 @@ class CRT {
uint16_t colour_burst_time_; uint16_t colour_burst_time_;
bool is_writing_composite_run_; 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 // 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); void advance_cycles(unsigned int number_of_cycles, bool hsync_requested, bool vsync_requested, const bool vsync_charging, const Scan::Type type);
@ -113,7 +115,7 @@ class CRT {
@see @c set_rgb_sampling_function , @c set_composite_sampling_function @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 /*! 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 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 /*! Resets the CRT with new timing information. The CRT then continues as though the new timing had
been provided at construction. */ 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 /*! 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. */ 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); 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. /*! Attempts to allocate the given number of output samples for writing.
The beginning of the most recently allocated area is used as the start The beginning of the most recently allocated area is used as the start

View File

@ -78,7 +78,7 @@ ArrayBuilder::Submission ArrayBuilder::submit()
} }
ArrayBuilder::Buffer::Buffer(size_t size, std::function<void(bool is_input, uint8_t *, size_t)> submission_function) : ArrayBuilder::Buffer::Buffer(size_t size, std::function<void(bool is_input, uint8_t *, size_t)> submission_function) :
is_full(false), was_reset(false), is_full(false),
submission_function_(submission_function), submission_function_(submission_function),
allocated_data(0), flushed_data(0), submitted_data(0) allocated_data(0), flushed_data(0), submitted_data(0)
{ {
@ -137,14 +137,6 @@ void ArrayBuilder::Buffer::flush()
} }
flushed_data = allocated_data; 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) size_t ArrayBuilder::Buffer::submit(bool is_input)
@ -171,6 +163,8 @@ void ArrayBuilder::Buffer::bind()
void ArrayBuilder::Buffer::reset() void ArrayBuilder::Buffer::reset()
{ {
was_reset = true;
is_full = false; is_full = false;
allocated_data = 0;
flushed_data = 0;
submitted_data = 0;
} }

View File

@ -82,7 +82,7 @@ class ArrayBuilder {
void reset(); void reset();
private: private:
bool is_full, was_reset; bool is_full;
GLuint buffer; GLuint buffer;
std::function<void(bool is_input, uint8_t *, size_t)> submission_function_; std::function<void(bool is_input, uint8_t *, size_t)> submission_function_;
std::vector<uint8_t> data; std::vector<uint8_t> data;

View File

@ -36,7 +36,7 @@ const GLsizei InputBufferBuilderWidth = 2048;
const GLsizei InputBufferBuilderHeight = 512; const GLsizei InputBufferBuilderHeight = 512;
// This is the size of the intermediate buffers used during composite to RGB conversion // 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; const GLsizei IntermediateBufferHeight = 512;
// Some internal buffer sizes // Some internal buffer sizes

View File

@ -385,7 +385,7 @@ void OpenGLOutputBuilder::set_timing_uniforms()
float colour_subcarrier_frequency = (float)colour_cycle_numerator_ / (float)colour_cycle_denominator_; 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_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(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); if(rgb_filter_shader_program_) rgb_filter_shader_program_->set_filter_coefficients(cycles_per_line_, (float)input_frequency_ * 0.5f);
} }

View File

@ -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