1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-17 13:29:02 +00:00
CLK/Storage/Disk/DiskImage/Formats/DiskCopy42.cpp

121 lines
3.4 KiB
C++
Raw Normal View History

//
// DiskCopy42.cpp
// Clock Signal
//
// Created by Thomas Harte on 02/06/2019.
// Copyright © 2019 Thomas Harte. All rights reserved.
//
#include "DiskCopy42.hpp"
/*
File format specifications as referenced below are largely
sourced from the documentation at
https://wiki.68kmla.org/DiskCopy_4.2_format_specification
*/
using namespace Storage::Disk;
DiskCopy42::DiskCopy42(const std::string &file_name) :
file_(file_name) {
// File format starts with 64 bytes dedicated to the disk name;
// this is a Pascal-style string though there is apparently a
// bug in one version of Disk Copy that can cause the length to
// be one too high.
//
// Validate the length, then skip the rest of the string.
const auto name_length = file_.get8();
if(name_length > 64)
throw Error::InvalidFormat;
// Get the length of the data and tag blocks.
file_.seek(64, SEEK_SET);
const auto data_block_length = file_.get32be();
const auto tag_block_length = file_.get32be();
const auto data_checksum = file_.get32be();
const auto tag_checksum = file_.get32be();
// Check that this is a comprehensible disk encoding.
const auto encoding = file_.get8();
switch(encoding) {
default: throw Error::InvalidFormat;
case 0: encoding_ = Encoding::GCR400; break;
case 1: encoding_ = Encoding::GCR800; break;
case 2: encoding_ = Encoding::MFM720; break;
case 3: encoding_ = Encoding::MFM1440; break;
}
format_ = file_.get8();
// Check the magic number.
const auto magic_number = file_.get16be();
if(magic_number != 0x0100)
throw Error::InvalidFormat;
// Read the data and tags, and verify that enough data
// was present.
data_ = file_.read(data_block_length);
tags_ = file_.read(tag_block_length);
if(data_.size() != data_block_length || tags_.size() != tag_block_length)
throw Error::InvalidFormat;
// Verify the two checksums.
const auto computed_data_checksum = checksum(data_);
const auto computed_tag_checksum = checksum(tags_, 12);
if(computed_tag_checksum != tag_checksum || computed_data_checksum != data_checksum)
throw Error::InvalidFormat;
}
uint32_t DiskCopy42::checksum(const std::vector<uint8_t> &data, size_t bytes_to_skip) {
uint32_t result = 0;
// Checksum algorith is: take each two bytes as a big-endian word; add that to a
// 32-bit accumulator and then rotate the accumulator right one position.
for(size_t c = bytes_to_skip; c < data.size(); c += 2) {
const uint16_t next_word = uint16_t((data[c] << 8) | data[c+1]);
result += next_word;
result = (result >> 1) | (result << 31);
}
return result;
}
HeadPosition DiskCopy42::get_maximum_head_position() {
return HeadPosition(80);
}
int DiskCopy42::get_head_count() {
// Bit 5 in the format field indicates whether this disk is double
// sided, regardless of whether it is GCR or MFM.
return 1 + ((format_ & 0x20) >> 5);
}
bool DiskCopy42::get_is_read_only() {
return true;
}
std::shared_ptr<::Storage::Disk::Track> DiskCopy42::get_track_at_position(::Storage::Disk::Track::Address address) {
/*
The format_ byte has the following meanings:
GCR:
This byte appears on disk as the GCR format nibble in every sector tag.
The low five bits are an interleave factor, either:
'2' for 0 8 1 9 2 10 3 11 4 12 5 13 6 14 7 15; or
'4' for 0 4 8 12 1 5 9 13 2 6 10 14 3 7 11 15.
Bit 5 indicates double sided or not.
MFM:
The low five bits provide sector size as a multiple of 256 bytes.
Bit 5 indicates double sided or not.
*/
// TODO.
return nullptr;
}