mirror of
https://github.com/TomHarte/CLK.git
synced 2024-09-04 22:29:29 +00:00
Merge pull request #449 from TomHarte/WOZWriting
Implements write support for WOZ files
This commit is contained in:
commit
2dc2c2ce79
@ -10,46 +10,99 @@
|
|||||||
#define CRC_hpp
|
#define CRC_hpp
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
namespace NumberTheory {
|
namespace CRC {
|
||||||
|
|
||||||
/*! Provides a class capable of accumulating a CRC16 from source data. */
|
/*! Provides a class capable of generating a CRC from source data. */
|
||||||
class CRC16 {
|
template <typename T, T reset_value, T xor_output, bool reflect_input, bool reflect_output> class Generator {
|
||||||
public:
|
public:
|
||||||
/*!
|
/*!
|
||||||
Instantiates a CRC16 that will compute the CRC16 specified by the supplied
|
Instantiates a CRC16 that will compute the CRC16 specified by the supplied
|
||||||
@c polynomial and @c reset_value.
|
@c polynomial and @c reset_value.
|
||||||
*/
|
*/
|
||||||
CRC16(uint16_t polynomial, uint16_t reset_value) :
|
Generator(T polynomial): value_(reset_value) {
|
||||||
reset_value_(reset_value), value_(reset_value) {
|
const T top_bit = T(~(T(~0) >> 1));
|
||||||
for(int c = 0; c < 256; c++) {
|
for(int c = 0; c < 256; c++) {
|
||||||
uint16_t shift_value = static_cast<uint16_t>(c << 8);
|
T shift_value = static_cast<T>(c << multibyte_shift);
|
||||||
for(int b = 0; b < 8; b++) {
|
for(int b = 0; b < 8; b++) {
|
||||||
uint16_t exclusive_or = (shift_value&0x8000) ? polynomial : 0x0000;
|
T exclusive_or = (shift_value&top_bit) ? polynomial : 0;
|
||||||
shift_value = static_cast<uint16_t>(shift_value << 1) ^ exclusive_or;
|
shift_value = static_cast<T>(shift_value << 1) ^ exclusive_or;
|
||||||
}
|
}
|
||||||
xor_table[c] = static_cast<uint16_t>(shift_value);
|
xor_table[c] = shift_value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resets the CRC to the reset value.
|
/// Resets the CRC to the reset value.
|
||||||
inline void reset() { value_ = reset_value_; }
|
void reset() { value_ = reset_value; }
|
||||||
|
|
||||||
/// Updates the CRC to include @c byte.
|
/// Updates the CRC to include @c byte.
|
||||||
inline void add(uint8_t byte) {
|
void add(uint8_t byte) {
|
||||||
value_ = static_cast<uint16_t>((value_ << 8) ^ xor_table[(value_ >> 8) ^ byte]);
|
if(reflect_input) byte = reverse_byte(byte);
|
||||||
|
value_ = static_cast<T>((value_ << 8) ^ xor_table[(value_ >> multibyte_shift) ^ byte]);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// @returns The current value of the CRC.
|
/// @returns The current value of the CRC.
|
||||||
inline uint16_t get_value() const { return value_; }
|
inline T get_value() const {
|
||||||
|
T result = value_^xor_output;
|
||||||
|
if(reflect_output) {
|
||||||
|
T reflected_output = 0;
|
||||||
|
for(std::size_t c = 0; c < sizeof(T); ++c) {
|
||||||
|
reflected_output = T(reflected_output << 8) | T(reverse_byte(result & 0xff));
|
||||||
|
result >>= 8;
|
||||||
|
}
|
||||||
|
return reflected_output;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
/// Sets the current value of the CRC.
|
/// Sets the current value of the CRC.
|
||||||
inline void set_value(uint16_t value) { value_ = value; }
|
inline void set_value(T value) { value_ = value; }
|
||||||
|
|
||||||
|
/*!
|
||||||
|
A compound for:
|
||||||
|
|
||||||
|
reset()
|
||||||
|
[add all data from @c data]
|
||||||
|
get_value()
|
||||||
|
*/
|
||||||
|
T compute_crc(const std::vector<uint8_t> &data) {
|
||||||
|
reset();
|
||||||
|
for(const auto &byte: data) add(byte);
|
||||||
|
return get_value();
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
const uint16_t reset_value_;
|
static constexpr int multibyte_shift = (sizeof(T) * 8) - 8;
|
||||||
uint16_t xor_table[256];
|
T xor_table[256];
|
||||||
uint16_t value_;
|
T value_;
|
||||||
|
|
||||||
|
constexpr uint8_t reverse_byte(uint8_t byte) const {
|
||||||
|
return
|
||||||
|
((byte & 0x80) ? 0x01 : 0x00) |
|
||||||
|
((byte & 0x40) ? 0x02 : 0x00) |
|
||||||
|
((byte & 0x20) ? 0x04 : 0x00) |
|
||||||
|
((byte & 0x10) ? 0x08 : 0x00) |
|
||||||
|
((byte & 0x08) ? 0x10 : 0x00) |
|
||||||
|
((byte & 0x04) ? 0x20 : 0x00) |
|
||||||
|
((byte & 0x02) ? 0x40 : 0x00) |
|
||||||
|
((byte & 0x01) ? 0x80 : 0x00);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Provides a generator of 16-bit CCITT CRCs, which amongst other uses are
|
||||||
|
those used by the FM and MFM disk encodings.
|
||||||
|
*/
|
||||||
|
struct CCITT: public Generator<uint16_t, 0xffff, 0x0000, false, false> {
|
||||||
|
CCITT() : Generator(0x1021) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Provides a generator of "standard 32-bit" CRCs.
|
||||||
|
*/
|
||||||
|
struct CRC32: public Generator<uint32_t, 0xffffffff, 0xffffffff, true, true> {
|
||||||
|
CRC32() : Generator(0x04c11db7) {}
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -8,18 +8,14 @@
|
|||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
#import <XCTest/XCTest.h>
|
||||||
#include "CRC.hpp"
|
#include "CRC.hpp"
|
||||||
|
#include <string>
|
||||||
|
|
||||||
@interface CRCTests : XCTestCase
|
@interface CRCTests : XCTestCase
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@implementation CRCTests
|
@implementation CRCTests
|
||||||
|
|
||||||
- (NumberTheory::CRC16)mfmCRCGenerator
|
- (uint16_t)crcOfData:(uint8_t *)data length:(size_t)length generator:(CRC::CCITT &)generator
|
||||||
{
|
|
||||||
return NumberTheory::CRC16(0x1021, 0xffff);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (uint16_t)crcOfData:(uint8_t *)data length:(size_t)length generator:(NumberTheory::CRC16 &)generator
|
|
||||||
{
|
{
|
||||||
generator.reset();
|
generator.reset();
|
||||||
for(size_t c = 0; c < length; c++)
|
for(size_t c = 0; c < length; c++)
|
||||||
@ -34,7 +30,7 @@
|
|||||||
0xa1, 0xa1, 0xa1, 0xfe, 0x00, 0x00, 0x01, 0x01
|
0xa1, 0xa1, 0xa1, 0xfe, 0x00, 0x00, 0x01, 0x01
|
||||||
};
|
};
|
||||||
uint16_t crc = 0xfa0c;
|
uint16_t crc = 0xfa0c;
|
||||||
NumberTheory::CRC16 crcGenerator = self.mfmCRCGenerator;
|
CRC::CCITT crcGenerator;
|
||||||
|
|
||||||
uint16_t computedCRC = [self crcOfData:IDMark length:sizeof(IDMark) generator:crcGenerator];
|
uint16_t computedCRC = [self crcOfData:IDMark length:sizeof(IDMark) generator:crcGenerator];
|
||||||
XCTAssert(computedCRC == crc, @"Calculated CRC should have been %04x, was %04x", crc, computedCRC);
|
XCTAssert(computedCRC == crc, @"Calculated CRC should have been %04x, was %04x", crc, computedCRC);
|
||||||
@ -63,10 +59,26 @@
|
|||||||
0x20, 0x20, 0x20, 0x20
|
0x20, 0x20, 0x20, 0x20
|
||||||
};
|
};
|
||||||
uint16_t crc = 0x4de7;
|
uint16_t crc = 0x4de7;
|
||||||
NumberTheory::CRC16 crcGenerator = self.mfmCRCGenerator;
|
CRC::CCITT crcGenerator;
|
||||||
|
|
||||||
uint16_t computedCRC = [self crcOfData:sectorData length:sizeof(sectorData) generator:crcGenerator];
|
uint16_t computedCRC = [self crcOfData:sectorData length:sizeof(sectorData) generator:crcGenerator];
|
||||||
XCTAssert(computedCRC == crc, @"Calculated CRC should have been %04x, was %04x", crc, computedCRC);
|
XCTAssert(computedCRC == crc, @"Calculated CRC should have been %04x, was %04x", crc, computedCRC);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
- (void)testCCITTCheck {
|
||||||
|
CRC::CCITT crcGenerator;
|
||||||
|
for(auto c: std::string("123456789")) {
|
||||||
|
crcGenerator.add(c);
|
||||||
|
}
|
||||||
|
XCTAssertEqual(crcGenerator.get_value(), 0x29b1);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)testCRC32Check {
|
||||||
|
CRC::CRC32 crcGenerator;
|
||||||
|
for(auto c: std::string("123456789")) {
|
||||||
|
crcGenerator.add(c);
|
||||||
|
}
|
||||||
|
XCTAssertEqual(crcGenerator.get_value(), 0xcbf43926);
|
||||||
|
}
|
||||||
|
|
||||||
@end
|
@end
|
||||||
|
@ -14,8 +14,7 @@ using namespace Storage::Disk;
|
|||||||
|
|
||||||
MFMController::MFMController(Cycles clock_rate) :
|
MFMController::MFMController(Cycles clock_rate) :
|
||||||
Storage::Disk::Controller(clock_rate),
|
Storage::Disk::Controller(clock_rate),
|
||||||
shifter_(&crc_generator_),
|
shifter_(&crc_generator_) {
|
||||||
crc_generator_(0x1021, 0xffff) {
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void MFMController::process_index_hole() {
|
void MFMController::process_index_hole() {
|
||||||
@ -49,7 +48,7 @@ MFMController::Token MFMController::get_latest_token() {
|
|||||||
return latest_token_;
|
return latest_token_;
|
||||||
}
|
}
|
||||||
|
|
||||||
NumberTheory::CRC16 &MFMController::get_crc_generator() {
|
CRC::CCITT &MFMController::get_crc_generator() {
|
||||||
return crc_generator_;
|
return crc_generator_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +72,7 @@ class MFMController: public Controller {
|
|||||||
Token get_latest_token();
|
Token get_latest_token();
|
||||||
|
|
||||||
/// @returns The controller's CRC generator. This is automatically fed during reading.
|
/// @returns The controller's CRC generator. This is automatically fed during reading.
|
||||||
NumberTheory::CRC16 &get_crc_generator();
|
CRC::CCITT &get_crc_generator();
|
||||||
|
|
||||||
// Events
|
// Events
|
||||||
enum class Event: int {
|
enum class Event: int {
|
||||||
@ -163,7 +163,7 @@ class MFMController: public Controller {
|
|||||||
int last_bit_;
|
int last_bit_;
|
||||||
|
|
||||||
// CRC generator
|
// CRC generator
|
||||||
NumberTheory::CRC16 crc_generator_;
|
CRC::CCITT crc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -111,6 +111,7 @@ void AppleDSK::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>>
|
|||||||
tracks_by_address[pair.first] = std::move(track_contents);
|
tracks_by_address[pair.first] = std::move(track_contents);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Grab the file lock and write out the new tracks.
|
||||||
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
|
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
|
||||||
for(const auto &pair: tracks_by_address) {
|
for(const auto &pair: tracks_by_address) {
|
||||||
file_.seek(file_offset(pair.first), SEEK_SET);
|
file_.seek(file_offset(pair.first), SEEK_SET);
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
#include "WOZ.hpp"
|
#include "WOZ.hpp"
|
||||||
|
|
||||||
#include "../../Track/PCMTrack.hpp"
|
#include "../../Track/PCMTrack.hpp"
|
||||||
|
#include "../../Track/TrackSerialiser.hpp"
|
||||||
|
|
||||||
using namespace Storage::Disk;
|
using namespace Storage::Disk;
|
||||||
|
|
||||||
@ -21,8 +22,20 @@ WOZ::WOZ(const std::string &file_name) :
|
|||||||
};
|
};
|
||||||
if(!file_.check_signature(signature, 8)) throw Error::InvalidFormat;
|
if(!file_.check_signature(signature, 8)) throw Error::InvalidFormat;
|
||||||
|
|
||||||
// TODO: check CRC32, instead of skipping it.
|
// Get the file's CRC32.
|
||||||
file_.seek(4, SEEK_CUR);
|
const uint32_t crc = file_.get32le();
|
||||||
|
|
||||||
|
// Get the collection of all data that contributes to the CRC.
|
||||||
|
post_crc_contents_ = file_.read(static_cast<std::size_t>(file_.stats().st_size - 12));
|
||||||
|
|
||||||
|
// Test the CRC.
|
||||||
|
const uint32_t computed_crc = crc_generator.compute_crc(post_crc_contents_);
|
||||||
|
if(crc != computed_crc) {
|
||||||
|
throw Error::InvalidFormat;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Retreat to the first byte after the CRC.
|
||||||
|
file_.seek(12, SEEK_SET);
|
||||||
|
|
||||||
// Parse all chunks up front.
|
// Parse all chunks up front.
|
||||||
bool has_tmap = false;
|
bool has_tmap = false;
|
||||||
@ -77,23 +90,65 @@ int WOZ::get_head_count() {
|
|||||||
return is_3_5_disk_ ? 2 : 1;
|
return is_3_5_disk_ ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> WOZ::get_track_at_position(Track::Address address) {
|
long WOZ::file_offset(Track::Address address) {
|
||||||
// Calculate table position; if this track is defined to be unformatted, return no track.
|
// Calculate table position; if this track is defined to be unformatted, return no track.
|
||||||
const int table_position = address.head * (is_3_5_disk_ ? 80 : 160) + (is_3_5_disk_ ? address.position.as_int() : address.position.as_quarter());
|
const int table_position = address.head * (is_3_5_disk_ ? 80 : 160) + (is_3_5_disk_ ? address.position.as_int() : address.position.as_quarter());
|
||||||
if(track_map_[table_position] == 0xff) return nullptr;
|
if(track_map_[table_position] == 0xff) return NoSuchTrack;
|
||||||
|
|
||||||
// Seek to the real track.
|
// Seek to the real track.
|
||||||
file_.seek(tracks_offset_ + track_map_[table_position] * 6656, SEEK_SET);
|
return tracks_offset_ + track_map_[table_position] * 6656;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Track> WOZ::get_track_at_position(Track::Address address) {
|
||||||
|
long offset = file_offset(address);
|
||||||
|
if(offset == NoSuchTrack) return nullptr;
|
||||||
|
|
||||||
|
// Seek to the real track.
|
||||||
PCMSegment track_contents;
|
PCMSegment track_contents;
|
||||||
|
{
|
||||||
|
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
|
||||||
|
file_.seek(offset, SEEK_SET);
|
||||||
|
|
||||||
|
// In WOZ a track is up to 6646 bytes of data, followed by a two-byte record of the
|
||||||
|
// number of bytes that actually had data in them, then a two-byte count of the number
|
||||||
|
// of bits that were used. Other information follows but is not intended for emulation.
|
||||||
track_contents.data = file_.read(6646);
|
track_contents.data = file_.read(6646);
|
||||||
track_contents.data.resize(file_.get16le());
|
track_contents.data.resize(file_.get16le());
|
||||||
track_contents.number_of_bits = file_.get16le();
|
track_contents.number_of_bits = file_.get16le();
|
||||||
|
|
||||||
const uint16_t splice_point = file_.get16le();
|
|
||||||
if(splice_point != 0xffff) {
|
|
||||||
// TODO: expand track from splice_point?
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return std::shared_ptr<PCMTrack>(new PCMTrack(track_contents));
|
return std::shared_ptr<PCMTrack>(new PCMTrack(track_contents));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void WOZ::set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) {
|
||||||
|
for(const auto &pair: tracks) {
|
||||||
|
// Decode the track and store, patching into the post_crc_contents_.
|
||||||
|
auto segment = Storage::Disk::track_serialisation(*pair.second, Storage::Time(1, 50000));
|
||||||
|
|
||||||
|
auto offset = static_cast<std::size_t>(file_offset(pair.first) - 12);
|
||||||
|
memcpy(&post_crc_contents_[offset - 12], segment.data.data(), segment.number_of_bits >> 3);
|
||||||
|
|
||||||
|
// Write number of bytes and number of bits.
|
||||||
|
post_crc_contents_[offset + 6646] = static_cast<uint8_t>(segment.number_of_bits >> 3);
|
||||||
|
post_crc_contents_[offset + 6647] = static_cast<uint8_t>(segment.number_of_bits >> 11);
|
||||||
|
post_crc_contents_[offset + 6648] = static_cast<uint8_t>(segment.number_of_bits);
|
||||||
|
post_crc_contents_[offset + 6649] = static_cast<uint8_t>(segment.number_of_bits >> 8);
|
||||||
|
|
||||||
|
// Set no splice information now provided, since it's been lost if ever it was known.
|
||||||
|
post_crc_contents_[offset + 6650] = 0xff;
|
||||||
|
post_crc_contents_[offset + 6651] = 0xff;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Calculate the new CRC.
|
||||||
|
const uint32_t crc = crc_generator.compute_crc(post_crc_contents_);
|
||||||
|
|
||||||
|
// Grab the file lock, then write the CRC, then just dump the entire file buffer.
|
||||||
|
std::lock_guard<std::mutex> lock_guard(file_.get_file_access_mutex());
|
||||||
|
file_.seek(8, SEEK_SET);
|
||||||
|
file_.put_le(crc);
|
||||||
|
file_.write(post_crc_contents_);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool WOZ::get_is_read_only() {
|
||||||
|
return file_.get_is_known_read_only();
|
||||||
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
#include "../DiskImage.hpp"
|
#include "../DiskImage.hpp"
|
||||||
#include "../../../FileHolder.hpp"
|
#include "../../../FileHolder.hpp"
|
||||||
|
#include "../../../../NumberTheory/CRC.hpp"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@ -24,9 +25,12 @@ class WOZ: public DiskImage {
|
|||||||
public:
|
public:
|
||||||
WOZ(const std::string &file_name);
|
WOZ(const std::string &file_name);
|
||||||
|
|
||||||
|
// Implemented to satisfy @c Disk.
|
||||||
HeadPosition get_maximum_head_position() override;
|
HeadPosition get_maximum_head_position() override;
|
||||||
int get_head_count() override;
|
int get_head_count() override;
|
||||||
std::shared_ptr<Track> get_track_at_position(Track::Address address) override;
|
std::shared_ptr<Track> get_track_at_position(Track::Address address) override;
|
||||||
|
void set_tracks(const std::map<Track::Address, std::shared_ptr<Track>> &tracks) override;
|
||||||
|
bool get_is_read_only() override;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Storage::FileHolder file_;
|
Storage::FileHolder file_;
|
||||||
@ -34,6 +38,18 @@ class WOZ: public DiskImage {
|
|||||||
bool is_3_5_disk_ = false;
|
bool is_3_5_disk_ = false;
|
||||||
uint8_t track_map_[160];
|
uint8_t track_map_[160];
|
||||||
long tracks_offset_ = -1;
|
long tracks_offset_ = -1;
|
||||||
|
|
||||||
|
std::vector<uint8_t> post_crc_contents_;
|
||||||
|
CRC::CRC32 crc_generator;
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Gets the in-file offset of a track.
|
||||||
|
|
||||||
|
@returns The offset within the file of the track at @c address or @c NoSuchTrack if
|
||||||
|
the track does not exit.
|
||||||
|
*/
|
||||||
|
long file_offset(Track::Address address);
|
||||||
|
constexpr static long NoSuchTrack = 0; // This is definitely an offset a track can't lie at.
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -187,7 +187,6 @@ template<class T> std::shared_ptr<Storage::Disk::Track>
|
|||||||
}
|
}
|
||||||
|
|
||||||
Encoder::Encoder(std::vector<uint8_t> &target) :
|
Encoder::Encoder(std::vector<uint8_t> &target) :
|
||||||
crc_generator_(0x1021, 0xffff),
|
|
||||||
target_(target) {}
|
target_(target) {}
|
||||||
|
|
||||||
void Encoder::output_short(uint16_t value) {
|
void Encoder::output_short(uint16_t value) {
|
||||||
|
@ -56,7 +56,7 @@ class Encoder {
|
|||||||
void add_crc(bool incorrectly);
|
void add_crc(bool incorrectly);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
NumberTheory::CRC16 crc_generator_;
|
CRC::CCITT crc_generator_;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::vector<uint8_t> &target_;
|
std::vector<uint8_t> &target_;
|
||||||
|
@ -11,8 +11,8 @@
|
|||||||
|
|
||||||
using namespace Storage::Encodings::MFM;
|
using namespace Storage::Encodings::MFM;
|
||||||
|
|
||||||
Shifter::Shifter() : owned_crc_generator_(new NumberTheory::CRC16(0x1021, 0xffff)), crc_generator_(owned_crc_generator_.get()) {}
|
Shifter::Shifter() : owned_crc_generator_(new CRC::CCITT()), crc_generator_(owned_crc_generator_.get()) {}
|
||||||
Shifter::Shifter(NumberTheory::CRC16 *crc_generator) : crc_generator_(crc_generator) {}
|
Shifter::Shifter(CRC::CCITT *crc_generator) : crc_generator_(crc_generator) {}
|
||||||
|
|
||||||
void Shifter::set_is_double_density(bool is_double_density) {
|
void Shifter::set_is_double_density(bool is_double_density) {
|
||||||
is_double_density_ = is_double_density;
|
is_double_density_ = is_double_density;
|
||||||
|
@ -44,7 +44,7 @@ namespace MFM {
|
|||||||
class Shifter {
|
class Shifter {
|
||||||
public:
|
public:
|
||||||
Shifter();
|
Shifter();
|
||||||
Shifter(NumberTheory::CRC16 *crc_generator);
|
Shifter(CRC::CCITT *crc_generator);
|
||||||
|
|
||||||
void set_is_double_density(bool is_double_density);
|
void set_is_double_density(bool is_double_density);
|
||||||
void set_should_obey_syncs(bool should_obey_syncs);
|
void set_should_obey_syncs(bool should_obey_syncs);
|
||||||
@ -57,7 +57,7 @@ class Shifter {
|
|||||||
Token get_token() const {
|
Token get_token() const {
|
||||||
return token_;
|
return token_;
|
||||||
}
|
}
|
||||||
NumberTheory::CRC16 &get_crc_generator() {
|
CRC::CCITT &get_crc_generator() {
|
||||||
return *crc_generator_;
|
return *crc_generator_;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -72,8 +72,8 @@ class Shifter {
|
|||||||
// input configuration
|
// input configuration
|
||||||
bool is_double_density_ = false;
|
bool is_double_density_ = false;
|
||||||
|
|
||||||
std::unique_ptr<NumberTheory::CRC16> owned_crc_generator_;
|
std::unique_ptr<CRC::CCITT> owned_crc_generator_;
|
||||||
NumberTheory::CRC16 *crc_generator_;
|
CRC::CCITT *crc_generator_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,6 +52,28 @@ class FileHolder final {
|
|||||||
*/
|
*/
|
||||||
uint32_t get32le();
|
uint32_t get32le();
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Writes @c value using successive @c put8s, in little endian order.
|
||||||
|
*/
|
||||||
|
template <typename T> void put_le(T value) {
|
||||||
|
auto bytes = sizeof(T);
|
||||||
|
while(bytes--) {
|
||||||
|
put8(value&0xff);
|
||||||
|
value >>= 8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Writes @c value using successive @c put8s, in big endian order.
|
||||||
|
*/
|
||||||
|
template <typename T> void put_be(T value) {
|
||||||
|
auto shift = sizeof(T) * 8;
|
||||||
|
while(shift) {
|
||||||
|
shift -= 8;
|
||||||
|
put8((value >> shift)&0xff);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*!
|
/*!
|
||||||
Performs @c get8 four times on @c file, casting each result to a @c uint32_t
|
Performs @c get8 four times on @c file, casting each result to a @c uint32_t
|
||||||
and returning the four assembled in big endian order.
|
and returning the four assembled in big endian order.
|
||||||
|
@ -14,7 +14,7 @@ namespace {
|
|||||||
const int PLLClockRate = 1920000;
|
const int PLLClockRate = 1920000;
|
||||||
}
|
}
|
||||||
|
|
||||||
Parser::Parser() : crc_(0x1021, 0x0000) {
|
Parser::Parser() {
|
||||||
shifter_.set_delegate(this);
|
shifter_.set_delegate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ class Parser: public Storage::Tape::Parser<SymbolType>, public Shifter::Delegate
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool did_update_shifter(int new_value, int length);
|
bool did_update_shifter(int new_value, int length);
|
||||||
NumberTheory::CRC16 crc_;
|
CRC::CCITT crc_;
|
||||||
Shifter shifter_;
|
Shifter shifter_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user