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:
commit
56cc191a8b
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -67,7 +67,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
buildConfiguration = "Release"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
enableASanStackUseAfterReturn = "YES"
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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)));
|
||||
|
Loading…
Reference in New Issue
Block a user