1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 16:31:31 +00:00

Merge pull request #692 from TomHarte/11Sectors

Compacts gaps when necessary to fit more sectors.
This commit is contained in:
Thomas Harte 2019-12-19 23:33:25 -05:00 committed by GitHub
commit 56cc191a8b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 110 additions and 31 deletions

View File

@ -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);
}

View File

@ -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<uint16_t *>(ram_.data()), ram_.size());
dma_->bus_grant(reinterpret_cast<uint16_t *>(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

View File

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

View File

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

View File

@ -67,7 +67,7 @@
</Testables>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
enableASanStackUseAfterReturn = "YES"

View File

@ -10,6 +10,8 @@
#include "Utility/ImplicitSectors.hpp"
#include <cassert>
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<uint8_t> 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);
}

View File

@ -12,10 +12,16 @@
#include "../../Track/PCMTrack.hpp"
#include "../../../../NumberTheory/CRC.hpp"
#include <cassert>
#include <set>
using namespace Storage::Encodings::MFM;
enum class SurfaceItem {
Mark,
Data
};
class MFMEncoder: public Encoder {
public:
MFMEncoder(std::vector<bool> &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<class T> std::shared_ptr<Storage::Disk::Track>
@ -131,18 +150,53 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
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<class T> std::shared_ptr<Storage::Disk::Track>
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<class T> std::shared_ptr<Storage::Disk::Track>
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<Storage::Disk::Track>(new Storage::Disk::PCMTrack(std::move(segment)));