From 7f03f5d02f5b7b959d009ca187d4dbc5b58e5308 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 28 Apr 2018 15:18:48 -0400 Subject: [PATCH] Makes a first attempt at six-and-two encoding for DSKs. --- Storage/Disk/DiskImage/Formats/AppleDSK.cpp | 28 +++- Storage/Disk/Encodings/AppleGCR.cpp | 144 +++++++++++++------- Storage/Disk/Encodings/AppleGCR.hpp | 16 ++- 3 files changed, 127 insertions(+), 61 deletions(-) diff --git a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp index cf1c68568..1b11fd490 100644 --- a/Storage/Disk/DiskImage/Formats/AppleDSK.cpp +++ b/Storage/Disk/DiskImage/Formats/AppleDSK.cpp @@ -8,6 +8,9 @@ #include "AppleDSK.hpp" +#include "../../Track/PCMTrack.hpp" +#include "../../Encodings/AppleGCR.hpp" + using namespace Storage::Disk; namespace { @@ -30,8 +33,29 @@ int AppleDSK::get_head_position_count() { std::shared_ptr AppleDSK::get_track_at_position(Track::Address address) { const long file_offset = (address.position >> 2) * bytes_per_sector * sectors_per_track_; file_.seek(file_offset, SEEK_SET); + const std::vector track_data = file_.read(static_cast(bytes_per_sector * sectors_per_track_)); -// std::vector track_data = file_.read(bytes_per_sector * sectors_per_track_); + std::vector segments; + const uint8_t track = static_cast(address.position >> 2); - return nullptr; + // In either case below, the code aims for exactly 50,000 bits per track. + if(sectors_per_track_ == 16) { + + // Write the sectors. + for(uint8_t c = 0; c < 16; ++c) { + segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + segments.push_back(Encodings::AppleGCR::header(0, track, c)); + segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + segments.push_back(Encodings::AppleGCR::six_and_two_data(&track_data[c * 256])); + segments.push_back(Encodings::AppleGCR::six_and_two_sync(10)); + } + + int encoded_length = (80 + 112 + 80 + 2848 + 80) * sectors_per_track_; + segments.push_back(Encodings::AppleGCR::six_and_two_sync((50000 - encoded_length) >> 3)); + + } else { + + } + + return std::shared_ptr(new PCMTrack(segments)); } diff --git a/Storage/Disk/Encodings/AppleGCR.cpp b/Storage/Disk/Encodings/AppleGCR.cpp index c33d19c0e..20d81106b 100644 --- a/Storage/Disk/Encodings/AppleGCR.cpp +++ b/Storage/Disk/Encodings/AppleGCR.cpp @@ -8,55 +8,31 @@ #include "AppleGCR.hpp" -using namespace Storage::Encodings; +namespace { -unsigned int AppleGCR::five_and_three_encoding_for_value(int value) { - static const unsigned int values[] = { - 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba, - 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb, - 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef, - 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff - }; - return values[value & 0x1f]; -} +const unsigned int five_and_three_mapping[] = { + 0xab, 0xad, 0xae, 0xaf, 0xb5, 0xb6, 0xb7, 0xba, + 0xbb, 0xbd, 0xbe, 0xbf, 0xd6, 0xd7, 0xda, 0xdb, + 0xdd, 0xde, 0xdf, 0xea, 0xeb, 0xed, 0xee, 0xef, + 0xf5, 0xf6, 0xf7, 0xfa, 0xfb, 0xfd, 0xfe, 0xff +}; -void AppleGCR::encode_five_and_three_block(uint8_t *destination, uint8_t *source) { - destination[0] = static_cast(five_and_three_encoding_for_value( source[0] >> 3 )); - destination[1] = static_cast(five_and_three_encoding_for_value( (source[0] << 2) | (source[1] >> 6) )); - destination[2] = static_cast(five_and_three_encoding_for_value( source[1] >> 1 )); - destination[3] = static_cast(five_and_three_encoding_for_value( (source[1] << 4) | (source[2] >> 4) )); - destination[4] = static_cast(five_and_three_encoding_for_value( (source[2] << 1) | (source[3] >> 7) )); - destination[5] = static_cast(five_and_three_encoding_for_value( source[3] >> 2 )); - destination[6] = static_cast(five_and_three_encoding_for_value( (source[3] << 3) | (source[4] >> 5) )); - destination[7] = static_cast(five_and_three_encoding_for_value( source[4] )); -} - -unsigned int AppleGCR::six_and_two_encoding_for_value(int value) { - static const unsigned int values[] = { - 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, - 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, - 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, - 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, - 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, - 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, - 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, - 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff - }; - return values[value & 0x3f]; -} - -void AppleGCR::encode_six_and_two_block(uint8_t *destination, uint8_t *source) { - destination[0] = static_cast(six_and_two_encoding_for_value( source[0] >> 2 )); - destination[1] = static_cast(six_and_two_encoding_for_value( (source[0] << 4) | (source[1] >> 4) )); - destination[2] = static_cast(six_and_two_encoding_for_value( (source[1] << 2) | (source[2] >> 6) )); - destination[3] = static_cast(six_and_two_encoding_for_value( source[2] )); -} +const uint8_t six_and_two_mapping[] = { + 0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6, + 0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3, + 0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc, + 0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3, + 0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, + 0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec, + 0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, + 0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff +}; /*! Produces a PCM segment containing @c length sync bytes, each aligned to the beginning of a @c bit_size -sized window. */ -static Storage::Disk::PCMSegment sync(int length, int bit_size) { +Storage::Disk::PCMSegment sync(int length, int bit_size) { Storage::Disk::PCMSegment segment; // Allocate sufficient storage. @@ -73,6 +49,30 @@ static Storage::Disk::PCMSegment sync(int length, int bit_size) { return segment; } +} + +using namespace Storage::Encodings; + + +/*void AppleGCR::encode_five_and_three_block(uint8_t *destination, uint8_t *source) { + destination[0] = static_cast(five_and_three_encoding_for_value( source[0] >> 3 )); + destination[1] = static_cast(five_and_three_encoding_for_value( (source[0] << 2) | (source[1] >> 6) )); + destination[2] = static_cast(five_and_three_encoding_for_value( source[1] >> 1 )); + destination[3] = static_cast(five_and_three_encoding_for_value( (source[1] << 4) | (source[2] >> 4) )); + destination[4] = static_cast(five_and_three_encoding_for_value( (source[2] << 1) | (source[3] >> 7) )); + destination[5] = static_cast(five_and_three_encoding_for_value( source[3] >> 2 )); + destination[6] = static_cast(five_and_three_encoding_for_value( (source[3] << 3) | (source[4] >> 5) )); + destination[7] = static_cast(five_and_three_encoding_for_value( source[4] )); +}*/ + + +/*void AppleGCR::encode_six_and_two_block(uint8_t *destination, uint8_t *source) { + destination[0] = static_cast(six_and_two_encoding_for_value( source[0] >> 2 )); + destination[1] = static_cast(six_and_two_encoding_for_value( (source[0] << 4) | (source[1] >> 4) )); + destination[2] = static_cast(six_and_two_encoding_for_value( (source[1] << 2) | (source[2] >> 6) )); + destination[3] = static_cast(six_and_two_encoding_for_value( source[2] )); +}*/ + Storage::Disk::PCMSegment AppleGCR::six_and_two_sync(int length) { return sync(length, 9); } @@ -111,7 +111,7 @@ Storage::Disk::PCMSegment AppleGCR::header(uint8_t volume, uint8_t track, uint8_ return segment; } -Storage::Disk::PCMSegment AppleGCR::five_and_three_data(uint8_t *source) { +Storage::Disk::PCMSegment AppleGCR::five_and_three_data(const uint8_t *source) { Storage::Disk::PCMSegment segment; segment.data.resize(410 + 7); @@ -119,25 +119,63 @@ Storage::Disk::PCMSegment AppleGCR::five_and_three_data(uint8_t *source) { segment.data[1] = header_prologue[1]; segment.data[2] = header_prologue[2]; - std::size_t source_pointer = 0; - std::size_t destination_pointer = 3; - while(source_pointer < 255) { - encode_five_and_three_block(&segment.data[destination_pointer], &source[source_pointer]); - - source_pointer += 5; - destination_pointer += 8; - } +// std::size_t source_pointer = 0; +// std::size_t destination_pointer = 3; +// while(source_pointer < 255) { +// encode_five_and_three_block(&segment.data[destination_pointer], &source[source_pointer]); +// +// source_pointer += 5; +// destination_pointer += 8; +// } return segment; } -Storage::Disk::PCMSegment AppleGCR::six_and_two_data(uint8_t *source) { +Storage::Disk::PCMSegment AppleGCR::six_and_two_data(const uint8_t *source) { Storage::Disk::PCMSegment segment; - segment.data.resize(342 + 7); + segment.data.resize(349); + + // Add the prologue and epilogue. segment.data[0] = header_prologue[0]; segment.data[1] = header_prologue[1]; segment.data[2] = header_prologue[2]; + segment.data[346] = epilogue[0]; + segment.data[347] = epilogue[1]; + segment.data[348] = epilogue[2]; + + // Fill in byte values: the first 86 bytes contain shuffled + // and combined copies of the bottom two bits of the sector + // contents; the 256 bytes afterwards are the remaining + // six bits. + const uint8_t bit_shuffle[] = {0, 2, 1, 3}; + for(std::size_t c = 0; c < 85; ++c) { + segment.data[3 + c] = + static_cast( + bit_shuffle[source[c]&3] | + (bit_shuffle[source[c + 85]&3] << 2) | + (bit_shuffle[source[c + 170]&3] << 4) + ); + } + segment.data[3 + 85] = bit_shuffle[source[255]&3]; + + for(std::size_t c = 0; c < 256; ++c) { + segment.data[3 + 85 + c] = source[c] >> 2; + } + + // Exclusive OR each byte with the one before it. + segment.data[344] = segment.data[343]; + std::size_t location = 343; + while(location > 3) { + segment.data[location] ^= segment.data[location-1]; + --location; + } + + // Map six-bit values up to full bytes. + for(std::size_t c = 0; c < 343; ++c) { + segment.data[c] = six_and_two_mapping[segment.data[c]]; + } + return segment; } diff --git a/Storage/Disk/Encodings/AppleGCR.hpp b/Storage/Disk/Encodings/AppleGCR.hpp index a09df4624..9073e3a7d 100644 --- a/Storage/Disk/Encodings/AppleGCR.hpp +++ b/Storage/Disk/Encodings/AppleGCR.hpp @@ -20,22 +20,22 @@ namespace AppleGCR { /*! @returns the eight-bit 13-sector GCR encoding for the low five bits of @c value. */ - unsigned int five_and_three_encoding_for_value(int value); +// unsigned int five_and_three_encoding_for_value(int value); /*! @returns the eight-bit 16-sector GCR encoding for the low six bits of @c value. */ - unsigned int six_and_two_encoding_for_value(int value); +// unsigned int six_and_two_encoding_for_value(int value); /*! A block is defined to be five source bytes, which encodes to eight GCR bytes. */ - void encode_five_and_three_block(uint8_t *destination, uint8_t *source); +// void encode_five_and_three_block(uint8_t *destination, uint8_t *source); /*! A block is defined to be three source bytes, which encodes to four GCR bytes. */ - void encode_six_and_two_block(uint8_t *destination, uint8_t *source); +// void encode_six_and_two_block(uint8_t *destination, uint8_t *source); /*! @returns the four bit nibble for the five-bit GCR @c quintet if a valid GCR value; INT_MAX otherwise. @@ -54,12 +54,16 @@ namespace AppleGCR { /// Describes the epilogue that ends both data sections and headers. const uint8_t epilogue[3] = {0xde, 0xaa, 0xeb}; + /*! + Produces the Apple-standard '4 and 4' per-sector header. This is the same + for both the 13- and 16-sector formats, and is 112 bits long. + */ Storage::Disk::PCMSegment header(uint8_t volume, uint8_t track, uint8_t sector); - Storage::Disk::PCMSegment six_and_two_data(uint8_t *source); + Storage::Disk::PCMSegment six_and_two_data(const uint8_t *source); Storage::Disk::PCMSegment six_and_two_sync(int length); - Storage::Disk::PCMSegment five_and_three_data(uint8_t *source); + Storage::Disk::PCMSegment five_and_three_data(const uint8_t *source); Storage::Disk::PCMSegment five_and_three_sync(int length); }