diff --git a/Analyser/Static/Commodore/StaticAnalyser.cpp b/Analyser/Static/Commodore/StaticAnalyser.cpp index 5a5f41c78..ac846bc76 100644 --- a/Analyser/Static/Commodore/StaticAnalyser.cpp +++ b/Analyser/Static/Commodore/StaticAnalyser.cpp @@ -149,6 +149,9 @@ void Analyser::Static::Commodore::AddTargets(const Media &media, std::vectorregion = Analyser::Static::Commodore::Target::Region::American; } + // Attach a 1540 if there are any disks here. + target->has_c1540 = !target->media.disks.empty(); + destination.push_back(std::move(target)); } } diff --git a/Components/6560/6560.cpp b/Components/6560/6560.cpp index 8790d50bb..73a06b3f4 100644 --- a/Components/6560/6560.cpp +++ b/Components/6560/6560.cpp @@ -106,7 +106,7 @@ static uint8_t noise_pattern[] = { // means every second cycle, etc. void AudioGenerator::get_samples(std::size_t number_of_samples, int16_t *target) { - for(unsigned int c = 0; c < number_of_samples; c++) { + for(unsigned int c = 0; c < number_of_samples; ++c) { update(0, 2, shift); update(1, 1, shift); update(2, 0, shift); @@ -124,7 +124,7 @@ void AudioGenerator::get_samples(std::size_t number_of_samples, int16_t *target) } void AudioGenerator::skip_samples(std::size_t number_of_samples) { - for(unsigned int c = 0; c < number_of_samples; c++) { + for(unsigned int c = 0; c < number_of_samples; ++c) { update(0, 2, shift); update(1, 1, shift); update(2, 0, shift); diff --git a/Components/6560/6560.hpp b/Components/6560/6560.hpp index ab18bb6b3..d680ef2c9 100644 --- a/Components/6560/6560.hpp +++ b/Components/6560/6560.hpp @@ -100,10 +100,10 @@ template class MOS6560 { // Luminances are encoded trivially: on a 0–255 scale. const uint8_t luminances[16] = { - 0, 255, 60, 189, - 80, 144, 40, 227, - 90, 161, 207, 227, - 200, 196, 160, 196 + 0, 255, 64, 192, + 128, 128, 64, 192, + 128, 192, 128, 255, + 192, 192, 128, 255 }; // Chrominances are encoded such that 0–128 is a complete revolution of phase; @@ -130,6 +130,7 @@ template class MOS6560 { display_type = Outputs::CRT::DisplayType::PAL50; timing_.cycles_per_line = 71; timing_.line_counter_increment_offset = 0; + timing_.final_line_increment_position = 71; timing_.lines_per_progressive_field = 312; timing_.supports_interlacing = false; break; @@ -138,7 +139,8 @@ template class MOS6560 { chrominances = ntsc_chrominances; display_type = Outputs::CRT::DisplayType::NTSC60; timing_.cycles_per_line = 65; - timing_.line_counter_increment_offset = 65 - 33; // TODO: this is a bit of a hack; separate vertical and horizontal counting + timing_.line_counter_increment_offset = 40; + timing_.final_line_increment_position = 58; timing_.lines_per_progressive_field = 261; timing_.supports_interlacing = true; break; @@ -176,7 +178,6 @@ template class MOS6560 { // keep track of internal time relative to this scanline horizontal_counter_++; - full_frame_counter_++; if(horizontal_counter_ == timing_.cycles_per_line) { if(horizontal_drawing_latch_) { current_character_row_++; @@ -198,9 +199,8 @@ template class MOS6560 { horizontal_drawing_latch_ = false; vertical_counter_ ++; - if(vertical_counter_ == (registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field)) { + if(vertical_counter_ == lines_this_field()) { vertical_counter_ = 0; - full_frame_counter_ = 0; if(output_mode_ == OutputMode::NTSC) is_odd_frame_ ^= true; current_row_ = 0; @@ -262,7 +262,7 @@ template class MOS6560 { // apply vertical sync if( - (vertical_counter_ < 3 && (is_odd_frame_ || !registers_.interlaced)) || + (vertical_counter_ < 3 && is_odd_frame()) || (registers_.interlaced && ( (vertical_counter_ == 0 && horizontal_counter_ > 32) || @@ -414,11 +414,10 @@ template class MOS6560 { */ uint8_t get_register(int address) { address &= 0xf; - int current_line = (full_frame_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line; switch(address) { default: return registers_.direct_values[address]; - case 0x03: return static_cast(current_line << 7) | (registers_.direct_values[3] & 0x7f); - case 0x04: return (current_line >> 1) & 0xff; + case 0x03: return static_cast(raster_value() << 7) | (registers_.direct_values[3] & 0x7f); + case 0x04: return (raster_value() >> 1) & 0xff; } } @@ -453,7 +452,29 @@ template class MOS6560 { unsigned int cycles_in_state_; // counters that cover an entire field - int horizontal_counter_ = 0, vertical_counter_ = 0, full_frame_counter_; + int horizontal_counter_ = 0, vertical_counter_ = 0; + const int lines_this_field() { + // Necessary knowledge here: only the NTSC 6560 supports interlaced video. + return registers_.interlaced ? (is_odd_frame_ ? 262 : 263) : timing_.lines_per_progressive_field; + } + const int raster_value() { + const int bonus_line = (horizontal_counter_ + timing_.line_counter_increment_offset) / timing_.cycles_per_line; + const int line = vertical_counter_ + bonus_line; + const int final_line = lines_this_field(); + + if(line < final_line) + return line; + + if(is_odd_frame()) { + return (horizontal_counter_ >= timing_.final_line_increment_position) ? 0 : final_line - 1; + } else { + return line % final_line; + } + // Cf. http://www.sleepingelephant.com/ipw-web/bulletin/bb/viewtopic.php?f=14&t=7237&start=15#p80737 + } + bool is_odd_frame() { + return is_odd_frame_ || !registers_.interlaced; + } // latches dictating start and length of drawing bool vertical_drawing_latch_, horizontal_drawing_latch_; @@ -483,6 +504,7 @@ template class MOS6560 { struct { int cycles_per_line; int line_counter_increment_offset; + int final_line_increment_position; int lines_per_progressive_field; bool supports_interlacing; } timing_; diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index ef118bcdb..0a80fa485 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -374,7 +374,7 @@ class ConcreteMachine: type_string(target->loading_command); } - if(target->media.disks.size()) { + if(commodore_target_.has_c1540) { // construct the 1540 c1540_.reset(new ::Commodore::C1540::Machine(Commodore::C1540::Machine::C1540)); @@ -546,12 +546,12 @@ class ConcreteMachine: if(isReadOperation(operation)) { uint8_t result = processor_read_memory_map_[address >> 10] ? processor_read_memory_map_[address >> 10][address & 0x3ff] : 0xff; if((address&0xfc00) == 0x9000) { - if((address&0xff00) == 0x9000) { + if(!(address&0x100)) { update_video(); result &= mos6560_->get_register(address); } - if((address&0xfc10) == 0x9010) result &= user_port_via_.get_register(address); - if((address&0xfc20) == 0x9020) result &= keyboard_via_.get_register(address); + if(address & 0x10) result &= user_port_via_.get_register(address); + if(address & 0x20) result &= keyboard_via_.get_register(address); } *value = result; @@ -630,13 +630,17 @@ class ConcreteMachine: update_video(); ram[address & 0x3ff] = *value; } + // Anything between 0x9000 and 0x9400 is the IO area. if((address&0xfc00) == 0x9000) { - if((address&0xff00) == 0x9000) { + // The VIC is selected by bit 8 = 0 + if(!(address&0x100)) { update_video(); mos6560_->set_register(address, *value); } - if((address&0xfc10) == 0x9010) user_port_via_.set_register(address, *value); - if((address&0xfc20) == 0x9020) keyboard_via_.set_register(address, *value); + // The first VIA is selected by bit 4 = 1. + if(address & 0x10) user_port_via_.set_register(address, *value); + // The second VIA is selected by bit 5 = 1. + if(address & 0x20) keyboard_via_.set_register(address, *value); } } diff --git a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib index 31f72def8..38f331214 100644 --- a/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib +++ b/OSBindings/Mac/Clock Signal/New Group/Base.lproj/MachinePicker.xib @@ -20,11 +20,11 @@ - - + + - + @@ -334,6 +334,9 @@ + +DQ +