diff --git a/Machines/AppleII/AppleII.cpp b/Machines/AppleII/AppleII.cpp index 844a636d4..66527c50d 100644 --- a/Machines/AppleII/AppleII.cpp +++ b/Machines/AppleII/AppleII.cpp @@ -88,6 +88,14 @@ template class ConcreteMachine: uint8_t keyboard_input_ = 0x00; bool key_is_down_ = false; + uint8_t get_keyboard_input() { + if(string_serialiser_) { + return string_serialiser_->head() | 0x80; + } else { + return keyboard_input_; + } + } + Concurrency::DeferringAsyncTaskQueue audio_queue_; Audio::Toggle audio_toggle_; Outputs::Speaker::LowpassSpeaker speaker_; @@ -466,11 +474,11 @@ template class ConcreteMachine: default: break; case 0xc000: - if(string_serialiser_) { - *value = string_serialiser_->head() | 0x80; - } else { - *value = keyboard_input_; - } + *value = get_keyboard_input(); + break; + case 0xc001: case 0xc002: case 0xc003: case 0xc004: case 0xc005: case 0xc006: case 0xc007: + case 0xc008: case 0xc009: case 0xc00a: case 0xc00b: case 0xc00c: case 0xc00d: case 0xc00e: case 0xc00f: + *value = (*value & 0x80) | (get_keyboard_input() & 0x7f); break; case 0xc061: // Switch input 0. @@ -507,22 +515,27 @@ template class ConcreteMachine: } break; // The IIe-only state reads follow... - case 0xc011: if(is_iie()) *value = (*value & 0x7f) | (language_card_.bank1 ? 0x80 : 0x00); break; - case 0xc012: if(is_iie()) *value = (*value & 0x7f) | (language_card_.read ? 0x80 : 0x00); break; - case 0xc013: if(is_iie()) *value = (*value & 0x7f) | (read_auxiliary_memory_ ? 0x80 : 0x00); break; - case 0xc014: if(is_iie()) *value = (*value & 0x7f) | (write_auxiliary_memory_ ? 0x80 : 0x00); break; - case 0xc015: if(is_iie()) *value = (*value & 0x7f) | (internal_CX_rom_ ? 0x80 : 0x00); break; - case 0xc016: if(is_iie()) *value = (*value & 0x7f) | (alternative_zero_page_ ? 0x80 : 0x00); break; - case 0xc017: if(is_iie()) *value = (*value & 0x7f) | (slot_C3_rom_ ? 0x80 : 0x00); break; - case 0xc018: if(is_iie()) *value = (*value & 0x7f) | (video_->get_80_store() ? 0x80 : 0x00); break; - case 0xc019: if(is_iie()) *value = (*value & 0x7f) | (video_->get_is_vertical_blank(cycles_since_video_update_) ? 0x00 : 0x80); break; - case 0xc01a: if(is_iie()) *value = (*value & 0x7f) | (video_->get_text() ? 0x80 : 0x00); break; - case 0xc01b: if(is_iie()) *value = (*value & 0x7f) | (video_->get_mixed() ? 0x80 : 0x00); break; - case 0xc01c: if(is_iie()) *value = (*value & 0x7f) | (video_->get_page2() ? 0x80 : 0x00); break; - case 0xc01d: if(is_iie()) *value = (*value & 0x7f) | (video_->get_high_resolution() ? 0x80 : 0x00); break; - case 0xc01e: if(is_iie()) *value = (*value & 0x7f) | (video_->get_alternative_character_set() ? 0x80 : 0x00); break; - case 0xc01f: if(is_iie()) *value = (*value & 0x7f) | (video_->get_80_columns() ? 0x80 : 0x00); break; - case 0xc07f: if(is_iie()) *value = (*value & 0x7f) | (video_->get_double_high_resolution() ? 0x80 : 0x00); break; +#define IIeSwitchRead(s) *value = get_keyboard_input(); if(is_iie()) *value = (*value & 0x7f) | (s ? 0x80 : 0x00); + case 0xc011: IIeSwitchRead(language_card_.bank1); break; + case 0xc012: IIeSwitchRead(language_card_.read); break; + case 0xc013: IIeSwitchRead(read_auxiliary_memory_); break; + case 0xc014: IIeSwitchRead(write_auxiliary_memory_); break; + case 0xc015: IIeSwitchRead(internal_CX_rom_); break; + case 0xc016: IIeSwitchRead(alternative_zero_page_); break; + case 0xc017: IIeSwitchRead(slot_C3_rom_); break; + case 0xc018: IIeSwitchRead(video_->get_80_store()); break; + case 0xc019: IIeSwitchRead(video_->get_is_vertical_blank(cycles_since_video_update_)); break; + case 0xc01a: IIeSwitchRead(video_->get_text()); break; + case 0xc01b: IIeSwitchRead(video_->get_mixed()); break; + case 0xc01c: IIeSwitchRead(video_->get_page2()); break; + case 0xc01d: IIeSwitchRead(video_->get_high_resolution()); break; + case 0xc01e: IIeSwitchRead(video_->get_alternative_character_set()); break; + case 0xc01f: IIeSwitchRead(video_->get_80_columns()); break; +#undef IIeSwitchRead + + case 0xc07f: + if(is_iie()) *value = (*value & 0x7f) | (video_->get_double_high_resolution() ? 0x80 : 0x00); + break; } } else { // Write-only switches. All IIe as currently implemented. @@ -640,7 +653,8 @@ template class ConcreteMachine: } break; - case 0xc030: + case 0xc030: case 0xc031: case 0xc032: case 0xc033: case 0xc034: case 0xc035: case 0xc036: case 0xc037: + case 0xc038: case 0xc039: case 0xc03a: case 0xc03b: case 0xc03c: case 0xc03d: case 0xc03e: case 0xc03f: update_audio(); audio_toggle_.set_output(!audio_toggle_.get_output()); break; diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index f0780f9ff..3c155b699 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -75,19 +75,24 @@ std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { // In either case below, the code aims for exactly 50,000 bits per track. if(sectors_per_track_ == 16) { // Write gap 1. - segment += Encodings::AppleGCR::six_and_two_sync(16); + segment += Encodings::AppleGCR::six_and_two_sync(24); // Write the sectors. for(uint8_t c = 0; c < 16; ++c) { - segment += Encodings::AppleGCR::header(254, track, c); + segment += Encodings::AppleGCR::header(is_prodos_ ? 0x01 : 0xfe, track, c); // Volume number is 0xfe for DOS 3.3, 0x01 for Pro-DOS. segment += Encodings::AppleGCR::six_and_two_sync(7); // Gap 2: 7 sync words. segment += Encodings::AppleGCR::six_and_two_data(&track_data[logical_sector_for_physical_sector(c) * 256]); - segment += Encodings::AppleGCR::six_and_two_sync(16); // Gap 3: 16 sync words. + segment += Encodings::AppleGCR::six_and_two_sync(20); // Gap 3: 20 sync words. } } else { // TODO: 5 and 3, 13-sector format. If DSK actually supports it? } + // Apply inter-track skew; skew is about 40ms between each track; assuming 300RPM that's + // 1/5th of a revolution. + const size_t offset_in_fifths = address.position.as_int() % 5; + segment.rotate_right(offset_in_fifths * segment.data.size() / 5); + return std::make_shared(segment); } diff --git a/Storage/Disk/Track/PCMSegment.cpp b/Storage/Disk/Track/PCMSegment.cpp index d70265780..554882103 100644 --- a/Storage/Disk/Track/PCMSegment.cpp +++ b/Storage/Disk/Track/PCMSegment.cpp @@ -50,6 +50,25 @@ PCMSegment &PCMSegment::operator +=(const PCMSegment &rhs) { return *this; } +void PCMSegment::rotate_right(size_t length) { + length %= data.size(); + if(!length) return; + + // To rotate to the right, front-insert the proper number + // of bits from the end and then resize. To rotate to + // the left, do the opposite. + std::vector data_copy; + if(length > 0) { + data_copy.insert(data_copy.end(), data.end() - static_cast(length), data.end()); + data.erase(data.end() - static_cast(length), data.end()); + data.insert(data.begin(), data_copy.begin(), data_copy.end()); + } else { + data_copy.insert(data_copy.end(), data.begin(), data.begin() - static_cast(length)); + data.erase(data.begin(), data.begin() - static_cast(length)); + data.insert(data.end(), data_copy.begin(), data_copy.end()); + } +} + Storage::Disk::Track::Event PCMSegmentEventSource::get_next_event() { // track the initial bit pointer for potentially considering whether this was an // initial index hole or a subsequent one later on diff --git a/Storage/Disk/Track/PCMSegment.hpp b/Storage/Disk/Track/PCMSegment.hpp index 3e206c0a2..b2a6fb75c 100644 --- a/Storage/Disk/Track/PCMSegment.hpp +++ b/Storage/Disk/Track/PCMSegment.hpp @@ -105,6 +105,13 @@ struct PCMSegment { data.clear(); } + /*! + Rotates all bits in this segment by @c length bits. + + @c length is signed; to rotate left provide a negative number. + */ + void rotate_right(size_t length); + /*! Produces a byte buffer where the contents of @c data are serialised into bytes