diff --git a/Components/68901/MFP68901.cpp b/Components/68901/MFP68901.cpp index f61fffffa..efb1de927 100644 --- a/Components/68901/MFP68901.cpp +++ b/Components/68901/MFP68901.cpp @@ -341,7 +341,7 @@ int MFP68901::acknowledge_interrupt() { int selected = 0; while((1 << selected) != mask) ++selected; - LOG("Interrupt acknowledged: " << selected); +// LOG("Interrupt acknowledged: " << selected); return (interrupt_vector_ & 0xf0) | uint8_t(selected); } diff --git a/Machines/Atari/ST/AtariST.cpp b/Machines/Atari/ST/AtariST.cpp index 447c20ce5..35fc403c0 100644 --- a/Machines/Atari/ST/AtariST.cpp +++ b/Machines/Atari/ST/AtariST.cpp @@ -507,7 +507,7 @@ class ConcreteMachine: // that's implemented, just offers magical zero-cost DMA insertion and // extrication. if(dma_->get_bus_request_line()) { - dma_->bus_grant(reinterpret_cast(ram_.data()), ram_.size()); + dma_->bus_grant(reinterpret_cast(ram_.data()), ram_.size() >> 1); } } void set_gpip_input() { @@ -578,7 +578,7 @@ class ConcreteMachine: // TODO: ? } else { /* - TODO: Port A: + Port A: b7: reserved b6: "freely usable output (monitor jack)" b5: centronics strobe diff --git a/Machines/Atari/ST/DMAController.cpp b/Machines/Atari/ST/DMAController.cpp index 9f6322424..723ff1248 100644 --- a/Machines/Atari/ST/DMAController.cpp +++ b/Machines/Atari/ST/DMAController.cpp @@ -102,10 +102,18 @@ void DMAController::write(int address, uint16_t value) { control_ = value; break; - // DMA addressing. + // DMA addressing; cf. http://www.atari-forum.com/viewtopic.php?t=30289 on a hardware + // feature emulated here: 'carry' will ripple upwards if a write resets the top bit + // of the byte it is adjusting. case 4: address_ = int((address_ & 0x00ffff) | ((value & 0xff) << 16)); break; - case 5: address_ = int((address_ & 0xff00ff) | ((value & 0xff) << 8)); break; - case 6: address_ = int((address_ & 0xffff00) | ((value & 0xff) << 0)); break; + case 5: + if((value << 8) ^ address_ & ~(value << 8) & 0x8000) address_ += 0x10000; + address_ = int((address_ & 0xff00ff) | ((value & 0xff) << 8)); + break; + case 6: + if(value ^ address_ & ~value & 0x80) address_ += 0x100; + address_ = int((address_ & 0xffff00) | ((value & 0xfe) << 0)); + break; // Lowest bit: discarded. } } @@ -171,6 +179,7 @@ int DMAController::bus_grant(uint16_t *ram, size_t size) { bus_request_line_ = false; if(delegate_) delegate_->dma_controller_did_change_output(this); + size <<= 1; // Convert to bytes. if(control_ & Control::Direction) { // TODO: writes. return 0; @@ -179,10 +188,12 @@ int DMAController::bus_grant(uint16_t *ram, size_t size) { if(!buffer_[active_buffer_ ^ 1].is_full) return 0; for(int c = 0; c < 8; ++c) { - ram[size_t(address_ >> 1) & (size - 1)] = uint16_t( - (buffer_[active_buffer_ ^ 1].contents[(c << 1) + 0] << 8) | - (buffer_[active_buffer_ ^ 1].contents[(c << 1) + 1] << 0) - ); + if(size_t(address_) < size) { + ram[address_ >> 1] = uint16_t( + (buffer_[active_buffer_ ^ 1].contents[(c << 1) + 0] << 8) | + (buffer_[active_buffer_ ^ 1].contents[(c << 1) + 1] << 0) + ); + } address_ += 2; } buffer_[active_buffer_ ^ 1].is_full = false; @@ -191,10 +202,12 @@ int DMAController::bus_grant(uint16_t *ram, size_t size) { if(!buffer_[active_buffer_ ].is_full) return 8; for(int c = 0; c < 8; ++c) { - ram[size_t(address_ >> 1) & (size - 1)] = uint16_t( - (buffer_[active_buffer_].contents[(c << 1) + 0] << 8) | - (buffer_[active_buffer_].contents[(c << 1) + 1] << 0) - ); + if(size_t(address_) < size) { + ram[address_ >> 1] = uint16_t( + (buffer_[active_buffer_].contents[(c << 1) + 0] << 8) | + (buffer_[active_buffer_].contents[(c << 1) + 1] << 0) + ); + } address_ += 2; } buffer_[active_buffer_].is_full = false; diff --git a/Machines/Atari/ST/Video.cpp b/Machines/Atari/ST/Video.cpp index 22e41770b..94446c59b 100644 --- a/Machines/Atari/ST/Video.cpp +++ b/Machines/Atari/ST/Video.cpp @@ -262,6 +262,11 @@ void Video::advance(HalfCycles duration) { next_vertical_.enable = false; } else if(next_y_ == vertical_timings.height) { next_y_ = 0; + } else if(y_ == 0) { + next_vertical_.sync_schedule = VerticalState::SyncSchedule::Begin; + } else if(y_ == 3) { + next_vertical_.sync_schedule = VerticalState::SyncSchedule::End; + current_address_ = base_address_ >> 1; reset_fifo(); // TODO: remove this, I think, once otherwise stable. @@ -272,10 +277,6 @@ void Video::advance(HalfCycles duration) { range_observer_->video_did_change_access_range(this); } } - } else if(y_ == 0) { - next_vertical_.sync_schedule = VerticalState::SyncSchedule::Begin; - } else if(y_ == 3) { - next_vertical_.sync_schedule = VerticalState::SyncSchedule::End; } } diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme index 175c1d515..7b1a6914b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme +++ b/OSBindings/Mac/Clock Signal.xcodeproj/xcshareddata/xcschemes/Clock Signal.xcscheme @@ -67,7 +67,7 @@ + using namespace Storage::Disk; MSA::MSA(const std::string &file_name) : @@ -31,6 +33,9 @@ MSA::MSA(const std::string &file_name) : // This is an uncompressed track. uncompressed_tracks_.push_back(file_.read(data_length)); } else { +#ifndef NDEBUG + const auto start_of_track = file_.tell(); +#endif // This is an RLE-compressed track. std::vector track; track.reserve(sectors_per_track_ * 512); @@ -56,12 +61,18 @@ MSA::MSA(const std::string &file_name) : } } - if(pointer != data_length || track.size() != sectors_per_track_ * 512) throw Error::InvalidFormat; +#ifndef NDEBUG + assert(file_.tell() - start_of_track == pointer); +#endif + + if(pointer != data_length || track.size() != sectors_per_track_ * 512) + throw Error::InvalidFormat; uncompressed_tracks_.push_back(std::move(track)); } } - if(uncompressed_tracks_.size() != size_t((ending_track_ - starting_track_ + 1)*sides_)) throw Error::InvalidFormat; + if(uncompressed_tracks_.size() != size_t((ending_track_ - starting_track_ + 1)*sides_)) + throw Error::InvalidFormat; } std::shared_ptr<::Storage::Disk::Track> MSA::get_track_at_position(::Storage::Disk::Track::Address address) { @@ -71,7 +82,8 @@ std::shared_ptr<::Storage::Disk::Track> MSA::get_track_at_position(::Storage::Di if(position < starting_track_) return nullptr; if(position >= ending_track_) return nullptr; - const auto &track = uncompressed_tracks_[size_t(position) * size_t(sides_) + size_t(address.head)]; + const auto &track = uncompressed_tracks_[size_t(position - starting_track_) * size_t(sides_) + size_t(address.head)]; + assert(!track.empty()); return track_for_sectors(track.data(), sectors_per_track_, uint8_t(position), uint8_t(address.head), 1, 2, true); } diff --git a/Storage/Disk/Encodings/MFM/Encoder.cpp b/Storage/Disk/Encodings/MFM/Encoder.cpp index 515ed50ef..122010952 100644 --- a/Storage/Disk/Encodings/MFM/Encoder.cpp +++ b/Storage/Disk/Encodings/MFM/Encoder.cpp @@ -12,10 +12,16 @@ #include "../../Track/PCMTrack.hpp" #include "../../../../NumberTheory/CRC.hpp" +#include #include using namespace Storage::Encodings::MFM; +enum class SurfaceItem { + Mark, + Data +}; + class MFMEncoder: public Encoder { public: MFMEncoder(std::vector &target) : Encoder(target) {} @@ -59,6 +65,14 @@ class MFMEncoder: public Encoder { add_byte(DeletedDataAddressByte); } + size_t item_size(SurfaceItem item) { + switch(item) { + case SurfaceItem::Mark: return 8; // Three syncs plus the mark type. + case SurfaceItem::Data: return 2; // Just a single encoded byte. + default: assert(false); + } + } + private: uint16_t last_output_; void output_short(uint16_t value) { @@ -116,6 +130,11 @@ class FMEncoder: public Encoder { crc_generator_.add(DeletedDataAddressByte); output_short(FMDeletedDataAddressMark); } + + size_t item_size(SurfaceItem item) { + // Marks are just slightly-invalid bytes, so everything is the same length. + return 2; + } }; template std::shared_ptr @@ -131,18 +150,53 @@ template std::shared_ptr segment.data.reserve(expected_track_bytes * 8); T shifter(segment.data); - // output the index mark + // Make a pre-estimate of output size, in case any of the idealised gaps + // provided need to be shortened. + const size_t data_size = shifter.item_size(SurfaceItem::Data); + const size_t mark_size = shifter.item_size(SurfaceItem::Mark); + const size_t max_size = (expected_track_bytes + (expected_track_bytes / 10)) * 8; + + size_t total_sector_bytes = 0; + for(const auto sector : sectors) { + total_sector_bytes += size_t(128 << sector->size) + 2; + } + + while(true) { + const size_t size = + mark_size + + post_index_address_mark_bytes * data_size + + total_sector_bytes * data_size + + sectors.size() * ( + (pre_address_mark_bytes + 6 + post_address_mark_bytes + pre_data_mark_bytes + post_data_bytes) * data_size + 2 * mark_size + ); + + // If this track already fits, do nothing. + if(size*8 < max_size) break; + + // If all gaps are already zero, do nothing. + if(!post_index_address_mark_bytes && !pre_address_mark_bytes && !post_address_mark_bytes && !pre_data_mark_bytes && !post_data_bytes) + break; + + // Very simple solution: try halving all gaps. + post_index_address_mark_bytes >>= 1; + pre_address_mark_bytes >>= 1; + post_address_mark_bytes >>= 1; + pre_data_mark_bytes >>= 1; + post_data_bytes >>= 1; + } + + // Output the index mark. shifter.add_index_address_mark(); - // add the post-index mark + // Add the post-index mark. for(std::size_t c = 0; c < post_index_address_mark_bytes; c++) shifter.add_byte(post_index_address_mark_value); - // add sectors + // Add sectors. for(const Sector *sector : sectors) { - // gap + // Gap. for(std::size_t c = 0; c < pre_address_mark_bytes; c++) shifter.add_byte(0x00); - // sector header + // Sector header. shifter.add_ID_address_mark(); shifter.add_byte(sector->address.track); shifter.add_byte(sector->address.side); @@ -150,11 +204,11 @@ template std::shared_ptr shifter.add_byte(sector->size); shifter.add_crc(sector->has_header_crc_error); - // gap + // Gap. for(std::size_t c = 0; c < post_address_mark_bytes; c++) shifter.add_byte(post_address_mark_value); for(std::size_t c = 0; c < pre_data_mark_bytes; c++) shifter.add_byte(0x00); - // data, if attached + // Data, if attached. // TODO: allow for weak/fuzzy data. if(!sector->samples.empty()) { if(sector->is_deleted) @@ -173,14 +227,13 @@ template std::shared_ptr shifter.add_crc(sector->has_data_crc_error); } - // gap + // Gap. for(std::size_t c = 0; c < post_data_bytes; c++) shifter.add_byte(post_data_value); } while(segment.data.size() < expected_track_bytes*8) shifter.add_byte(0x00); // Allow the amount of data written to be up to 10% more than the expected size. Which is generous. - const std::size_t max_size = (expected_track_bytes + (expected_track_bytes / 10)) * 8; if(segment.data.size() > max_size) segment.data.resize(max_size); return std::shared_ptr(new Storage::Disk::PCMTrack(std::move(segment)));