1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-12-27 01:31:42 +00:00

Made a first attempt at D64 support. Made an error somewhere but this should be 90% of it.

This commit is contained in:
Thomas Harte 2016-08-01 08:41:16 -04:00
parent 58297f1baf
commit a00f9adba3
6 changed files with 163 additions and 3 deletions

View File

@ -49,3 +49,19 @@ unsigned int Storage::Encodings::CommodoreGCR::encoding_for_byte(uint8_t byte)
{
return encoding_for_nibble(byte) | (encoding_for_nibble(byte >> 4) << 5);
}
void Storage::Encodings::CommodoreGCR::encode_block(uint8_t *destination, uint8_t *source)
{
unsigned int encoded_bytes[4] = {
encoding_for_byte(source[0]),
encoding_for_byte(source[1]),
encoding_for_byte(source[2]),
encoding_for_byte(source[3]),
};
destination[0] = (uint8_t)(encoded_bytes[0] >> 2);
destination[1] = (uint8_t)((encoded_bytes[0] << 6) | (encoded_bytes[1] >> 4));
destination[2] = (uint8_t)((encoded_bytes[1] << 4) | (encoded_bytes[2] >> 6));
destination[3] = (uint8_t)((encoded_bytes[2] << 2) | (encoded_bytes[3] >> 8));
destination[4] = (uint8_t)(encoded_bytes[3]);
}

View File

@ -31,6 +31,11 @@ namespace CommodoreGCR {
@returns the ten-bit GCR encoding for @c byte.
*/
unsigned int encoding_for_byte(uint8_t byte);
/*!
A block is defined to be four source bytes, which encodes to five GCR bytes.
*/
void encode_block(uint8_t *destination, uint8_t *source);
}
}
}

View File

@ -8,11 +8,33 @@
#include "D64.hpp"
#include <sys/stat.h>
#include <algorithm>
#include <cstdlib>
#include "../PCMTrack.hpp"
#include "../../../Storage/Disk/Encodings/CommodoreGCR.hpp"
using namespace Storage;
D64::D64(const char *file_name)
{
throw ErrorNotD64;
struct stat file_stats;
stat(file_name, &file_stats);
// in D64, this is it for validation without imposing potential false-negative tests — check that
// the file size appears to be correct. Stone-age stuff.
if(file_stats.st_size != 174848 && file_stats.st_size != 196608)
throw ErrorNotD64;
_number_of_tracks = (file_stats.st_size == 174848) ? 35 : 40;
_file = fopen(file_name, "rb");
if(!_file)
throw ErrorNotD64;
// then, ostensibly, this is a valid file. Hmmm. Pick a disk ID.
_disk_id = (uint16_t)rand();
}
D64::~D64()
@ -22,10 +44,122 @@ D64::~D64()
unsigned int D64::get_head_position_count()
{
return 0;
return _number_of_tracks*2;
}
std::shared_ptr<Track> D64::get_track_at_position(unsigned int position)
{
return std::shared_ptr<Track>();
// every other track is missing
if(position&1) return std::shared_ptr<Track>();
// figure out where this track starts on the disk
int offset_to_track = 0;
int tracks_to_traverse = position >> 1;
int zone_sizes[] = {17, 7, 6, 10};
int sectors_by_zone[] = {21, 19, 18, 17};
int zone = 0;
for(int current_zone = 0; current_zone < 4; current_zone++)
{
int tracks_in_this_zone = std::min(tracks_to_traverse, zone_sizes[current_zone]);
offset_to_track += tracks_in_this_zone * sectors_by_zone[current_zone];
tracks_to_traverse -= tracks_in_this_zone;
if(tracks_in_this_zone == zone_sizes[current_zone]) zone++;
}
// seek to start of data
fseek(_file, offset_to_track * 256, SEEK_SET);
// build up a PCM sampling of the GCR version of this track
// format per sector:
//
// syncronisation: three $FFs directly in GCR
// value $08 to announce a header
// a checksum made of XORing the following four bytes
// sector number (1 byte)
// track number (1 byte)
// disk ID (2 bytes)
// five GCR bytes of value $55
// = [6 bytes -> 7.5 GCR bytes] + ... = 21 GCR bytes
//
// syncronisation: three $FFs directly in GCR
// value $07 to announce data
// 256 data bytes
// a checksum: the XOR of the previous 256 bytes
// two bytes of vaue $00
// = [260 bytes -> 325 GCR bytes] + 3 GCR bytes = 328 GCR bytes
//
// = 349 GCR bytes per sector
PCMSegment track;
size_t track_bytes = 349 * (size_t)sectors_by_zone[zone];
track.number_of_bits = (unsigned int)track_bytes * 8;
uint8_t *data = new uint8_t[track_bytes];
track.data.reset(data);
for(int sector = 0; sector < sectors_by_zone[zone]; sector++)
{
uint8_t *sector_data = &data[sector * 349];
sector_data[0] = sector_data[1] = sector_data[2] = 0xff;
uint8_t sector_number = (uint8_t)(sector+1);
uint8_t track_number = (uint8_t)((position >> 1) + 1);
uint8_t checksum = (uint8_t)(sector_number ^ track_number ^ _disk_id ^ (_disk_id >> 8));
uint8_t header_start[4] = {
0x08, checksum, sector_number, track_number
};
Encodings::CommodoreGCR::encode_block(&sector_data[3], header_start);
uint8_t header_end[4] = {
(uint8_t)(_disk_id & 0xff), (uint8_t)(_disk_id >> 8), 0, 0
};
Encodings::CommodoreGCR::encode_block(&sector_data[8], header_end);
// only the first 2.5 bytes there are what was actually wanted; fill with repeating
// 01010 and then transition back into FF, as per:
sector_data[10] = (sector_data[10] & 0xf0) | 0x05;
sector_data[11] = 0x29;
sector_data[12] = 0x4a;
sector_data[13] = 0x52;
sector_data[14] = 0x94;
sector_data[15] = 0xa5;
sector_data[16] = 0x29;
sector_data[17] = 0x4a;
sector_data[18] = 0x52;
sector_data[19] = 0x94;
sector_data[20] = 0xaf;
// get the actual contents
uint8_t source_data[256];
fread(source_data, 1, 256, _file);
// compute the latest checksum
checksum = 0;
for(int c = 0; c < 256; c++)
checksum ^= source_data[c];
// put in another sync
sector_data[21] = sector_data[22] = sector_data[23] = 0xff;
// now start writing in the actual data
uint8_t start_of_data[4] = {
0x07, source_data[0], source_data[1], source_data[2]
};
Encodings::CommodoreGCR::encode_block(&sector_data[24], start_of_data);
int source_data_offset = 3;
int target_data_offset = 29;
while((source_data_offset+4) < 256)
{
Encodings::CommodoreGCR::encode_block(&sector_data[target_data_offset], &source_data[source_data_offset]);
target_data_offset += 5;
source_data_offset += 4;
}
uint8_t end_of_data[4] = {
source_data[255], checksum, 0, 0
};
Encodings::CommodoreGCR::encode_block(&sector_data[target_data_offset], end_of_data);
}
return std::shared_ptr<Track>(new PCMTrack(std::move(track)));
}

View File

@ -38,6 +38,8 @@ class D64: public Disk {
private:
FILE *_file;
unsigned int _number_of_tracks;
uint16_t _disk_id;
};
};

View File

@ -19,6 +19,8 @@ PCMTrack::PCMTrack(std::vector<PCMSegment> segments)
PCMTrack::PCMTrack(PCMSegment segment)
{
segment.length_of_a_bit.length = 1;
segment.length_of_a_bit.clock_rate = 1;
_segments.push_back(std::move(segment));
fix_length();
}

View File

@ -41,6 +41,7 @@ class PCMTrack: public Track {
/*!
Creates a @c PCMTrack consisting of a single continuous run of data, implying a constant clock rate.
The segment's @c length_of_a_bit will be ignored and therefore need not be filled in.
*/
PCMTrack(PCMSegment segment);