2018-04-22 04:21:57 +00:00
|
|
|
//
|
|
|
|
// NIB.cpp
|
|
|
|
// Clock Signal
|
|
|
|
//
|
|
|
|
// Created by Thomas Harte on 21/04/2018.
|
2018-05-13 19:19:52 +00:00
|
|
|
// Copyright 2018 Thomas Harte. All rights reserved.
|
2018-04-22 04:21:57 +00:00
|
|
|
//
|
|
|
|
|
|
|
|
#include "NIB.hpp"
|
|
|
|
|
2018-04-27 02:49:07 +00:00
|
|
|
#include "../../Track/PCMTrack.hpp"
|
2018-05-25 22:30:55 +00:00
|
|
|
#include "../../Track/TrackSerialiser.hpp"
|
2018-05-04 02:40:45 +00:00
|
|
|
#include "../../Encodings/AppleGCR/Encoder.hpp"
|
2018-04-27 02:49:07 +00:00
|
|
|
|
2020-03-25 01:34:33 +00:00
|
|
|
#include "../../Encodings/AppleGCR/Encoder.hpp"
|
|
|
|
#include "../../Encodings/AppleGCR/SegmentParser.hpp"
|
|
|
|
|
2018-04-27 02:49:07 +00:00
|
|
|
#include <vector>
|
|
|
|
|
2018-04-22 04:21:57 +00:00
|
|
|
using namespace Storage::Disk;
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
|
2018-04-27 02:49:07 +00:00
|
|
|
const long track_length = 6656;
|
2018-04-22 04:21:57 +00:00
|
|
|
const std::size_t number_of_tracks = 35;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
NIB::NIB(const std::string &file_name) :
|
|
|
|
file_(file_name) {
|
|
|
|
// A NIB should be 35 tracks, each 6656 bytes long.
|
|
|
|
if(file_.stats().st_size != track_length*number_of_tracks) {
|
2018-04-28 03:18:45 +00:00
|
|
|
throw Error::InvalidFormat;
|
2018-04-22 04:21:57 +00:00
|
|
|
}
|
|
|
|
|
2018-05-25 22:40:15 +00:00
|
|
|
// A real NIB should have every single top bit set. Yes, 1/8th of the
|
|
|
|
// file size is a complete waste. But it provides a hook for validation.
|
|
|
|
while(true) {
|
|
|
|
uint8_t next = file_.get8();
|
|
|
|
if(file_.eof()) break;
|
|
|
|
if(!(next & 0x80)) throw Error::InvalidFormat;
|
|
|
|
}
|
2018-04-22 04:21:57 +00:00
|
|
|
}
|
|
|
|
|
2018-05-07 03:17:36 +00:00
|
|
|
HeadPosition NIB::get_maximum_head_position() {
|
|
|
|
return HeadPosition(number_of_tracks);
|
2018-04-22 04:21:57 +00:00
|
|
|
}
|
|
|
|
|
2018-05-25 22:30:55 +00:00
|
|
|
bool NIB::get_is_read_only() {
|
|
|
|
return file_.get_is_known_read_only();
|
|
|
|
}
|
|
|
|
|
|
|
|
long NIB::file_offset(Track::Address address) {
|
2020-03-25 01:34:33 +00:00
|
|
|
return long(address.position.as_int()) * track_length;
|
2018-05-25 22:30:55 +00:00
|
|
|
}
|
|
|
|
|
2018-04-24 03:01:12 +00:00
|
|
|
std::shared_ptr<::Storage::Disk::Track> NIB::get_track_at_position(::Storage::Disk::Track::Address address) {
|
|
|
|
// NIBs contain data for even-numbered tracks underneath a single head only.
|
2018-04-27 02:49:07 +00:00
|
|
|
if(address.head) return nullptr;
|
2023-12-09 04:12:41 +00:00
|
|
|
if(address.position.as_quarter() & 2) return nullptr;
|
2018-04-27 02:49:07 +00:00
|
|
|
|
2018-05-25 22:30:55 +00:00
|
|
|
long offset = file_offset(address);
|
|
|
|
std::vector<uint8_t> track_data;
|
|
|
|
{
|
2020-06-15 04:24:10 +00:00
|
|
|
std::lock_guard lock_guard(file_.get_file_access_mutex());
|
2023-12-09 04:12:41 +00:00
|
|
|
if(cached_offset_ == offset && cached_track_) {
|
|
|
|
return cached_track_;
|
|
|
|
}
|
2018-05-25 22:30:55 +00:00
|
|
|
file_.seek(offset, SEEK_SET);
|
|
|
|
track_data = file_.read(track_length);
|
|
|
|
}
|
2018-04-22 04:21:57 +00:00
|
|
|
|
2018-04-27 02:49:07 +00:00
|
|
|
// NIB files leave sync bytes implicit and make no guarantees
|
2023-12-09 04:12:41 +00:00
|
|
|
// about overall track positioning. The attempt works by locating
|
|
|
|
// any runs of FF and marking the last five as including slip bits.
|
2018-04-27 02:49:07 +00:00
|
|
|
std::size_t start_index = 0;
|
2023-12-09 04:12:41 +00:00
|
|
|
std::set<size_t> sync_locations;
|
2018-04-27 02:49:07 +00:00
|
|
|
|
|
|
|
for(size_t index = 0; index < track_data.size(); ++index) {
|
2023-12-09 04:12:41 +00:00
|
|
|
// Count the number of FFs starting from here.
|
|
|
|
size_t length = 0;
|
|
|
|
size_t end = index;
|
|
|
|
while(track_data[end] == 0xff) {
|
|
|
|
end = (end + 1) % track_data.size();
|
|
|
|
++length;
|
2018-04-27 02:49:07 +00:00
|
|
|
}
|
2023-12-08 02:52:51 +00:00
|
|
|
|
2023-12-09 04:12:41 +00:00
|
|
|
// If that's at least five, regress and mark all as syncs.
|
|
|
|
if(length >= 5) {
|
|
|
|
for(int c = 0; c < 5; c++) {
|
|
|
|
end = (end + track_data.size() - 1) % track_data.size();
|
|
|
|
sync_locations.insert(index);
|
2023-12-08 02:52:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-01 16:05:41 +00:00
|
|
|
PCMSegment segment;
|
2018-07-04 00:01:07 +00:00
|
|
|
|
2018-04-27 02:49:07 +00:00
|
|
|
std::size_t index = start_index;
|
2023-12-09 04:12:41 +00:00
|
|
|
while(index < track_data.size()) {
|
|
|
|
// Deal with a run of sync values, if present.
|
|
|
|
const auto sync_start = index;
|
|
|
|
while(sync_locations.find(index) != sync_locations.end() && index < track_data.size()) {
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
if(index != sync_start) {
|
|
|
|
segment += Encodings::AppleGCR::six_and_two_sync(int(index - sync_start));
|
2020-03-25 01:34:33 +00:00
|
|
|
}
|
|
|
|
|
2023-12-09 04:12:41 +00:00
|
|
|
// Deal with regular data.
|
|
|
|
const auto data_start = index;
|
|
|
|
while(sync_locations.find(index) == sync_locations.end() && index < track_data.size()) {
|
2018-04-27 02:49:07 +00:00
|
|
|
++index;
|
2023-12-09 04:12:41 +00:00
|
|
|
}
|
|
|
|
if(index != data_start) {
|
|
|
|
std::vector<uint8_t> data_segment(
|
|
|
|
track_data.begin() + ptrdiff_t(data_start),
|
|
|
|
track_data.begin() + ptrdiff_t(index));
|
|
|
|
segment += PCMSegment(data_segment);
|
2023-12-08 02:52:51 +00:00
|
|
|
}
|
2018-07-04 00:01:07 +00:00
|
|
|
}
|
|
|
|
|
2023-12-09 04:12:41 +00:00
|
|
|
std::lock_guard lock_guard(file_.get_file_access_mutex());
|
|
|
|
cached_offset_ = offset;
|
|
|
|
cached_track_ = std::make_shared<PCMTrack>(segment);;
|
|
|
|
return cached_track_;
|
2018-04-22 04:21:57 +00:00
|
|
|
}
|
2018-05-25 22:30:55 +00:00
|
|
|
|
|
|
|
void NIB::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
|
|
|
|
std::map<Track::Address, std::vector<uint8_t>> tracks_by_address;
|
|
|
|
|
|
|
|
// Convert to a map from address to a vector of data that contains the NIB representation
|
|
|
|
// of the track.
|
|
|
|
for(const auto &pair: tracks) {
|
|
|
|
// Grab the track bit stream.
|
|
|
|
auto segment = Storage::Disk::track_serialisation(*pair.second, Storage::Time(1, 50000));
|
|
|
|
|
|
|
|
// Process to eliminate all sync bits.
|
|
|
|
std::vector<uint8_t> track;
|
|
|
|
track.reserve(track_length);
|
|
|
|
uint8_t shifter = 0;
|
2018-07-04 00:10:22 +00:00
|
|
|
int bit_count = 0;
|
|
|
|
size_t sync_location = 0, location = 0;
|
2018-07-01 16:05:41 +00:00
|
|
|
for(const auto bit: segment.data) {
|
2020-03-25 01:34:33 +00:00
|
|
|
shifter = uint8_t((shifter << 1) | (bit ? 1 : 0));
|
2018-07-04 00:10:22 +00:00
|
|
|
++bit_count;
|
|
|
|
++location;
|
2018-05-25 22:30:55 +00:00
|
|
|
if(shifter & 0x80) {
|
|
|
|
track.push_back(shifter);
|
2018-07-04 00:10:22 +00:00
|
|
|
if(bit_count == 10) {
|
|
|
|
sync_location = location;
|
|
|
|
}
|
2018-05-25 22:30:55 +00:00
|
|
|
shifter = 0;
|
2018-07-04 00:10:22 +00:00
|
|
|
bit_count = 0;
|
2018-05-25 22:30:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-07-04 00:10:22 +00:00
|
|
|
// Trim or pad out to track_length.
|
2018-05-25 22:30:55 +00:00
|
|
|
if(track.size() > track_length) {
|
|
|
|
track.resize(track_length);
|
|
|
|
} else {
|
|
|
|
while(track.size() < track_length) {
|
2020-03-25 01:34:33 +00:00
|
|
|
std::vector<uint8_t> extra_data(size_t(track_length) - track.size(), 0xff);
|
2022-04-27 23:16:37 +00:00
|
|
|
track.insert(track.begin() + ptrdiff_t(sync_location), extra_data.begin(), extra_data.end());
|
2018-05-25 22:30:55 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
tracks_by_address[pair.first] = std::move(track);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Lock the file and spool out.
|
2020-06-15 04:24:10 +00:00
|
|
|
std::lock_guard lock_guard(file_.get_file_access_mutex());
|
2018-05-25 22:30:55 +00:00
|
|
|
for(const auto &track: tracks_by_address) {
|
2018-09-10 00:33:56 +00:00
|
|
|
file_.seek(file_offset(track.first), SEEK_SET);
|
|
|
|
file_.write(track.second);
|
2018-05-25 22:30:55 +00:00
|
|
|
}
|
2023-12-09 04:12:41 +00:00
|
|
|
cached_track_ = nullptr; // Conservative, but safe.
|
2018-05-25 22:30:55 +00:00
|
|
|
}
|