mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-26 08:49:37 +00:00
Ensured the video subsystem correctly handles requests to run over a frame boundary.
This commit is contained in:
parent
52028432e1
commit
c5cf8d9531
@ -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_;
|
||||
}
|
||||
|
||||
|
@ -120,12 +120,14 @@ class Machine:
|
||||
uint8_t os_[16384], ram_[32768];
|
||||
std::vector<uint8_t> 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_;
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user