mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-17 10:06:21 +00:00
Started work on a GCR parser and the helper functions that lie behind that.
This commit is contained in:
parent
df7aed7e8b
commit
9d6dcb80a7
@ -7,13 +7,179 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "Disk.hpp"
|
#include "Disk.hpp"
|
||||||
|
#include "../../Storage/Disk/DiskDrive.hpp"
|
||||||
|
#include "../../Storage/Disk/Encodings/CommodoreGCR.hpp"
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <vector>
|
||||||
|
#include <array>
|
||||||
|
|
||||||
using namespace StaticAnalyser::Commodore;
|
using namespace StaticAnalyser::Commodore;
|
||||||
|
|
||||||
|
class CommodoreGCRParser: public Storage::Disk::Drive {
|
||||||
|
public:
|
||||||
|
CommodoreGCRParser() : Storage::Disk::Drive(4000000, 4, 300), shift_register_(0) {}
|
||||||
|
|
||||||
|
struct Sector
|
||||||
|
{
|
||||||
|
uint8_t sector, track;
|
||||||
|
std::array<uint8_t, 256> data;
|
||||||
|
bool header_checksum_matched;
|
||||||
|
bool data_checksum_matched;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::unique_ptr<Sector> get_sector(uint8_t track, uint8_t sector)
|
||||||
|
{
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Sector> get_sector(uint8_t sector)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Sector> first_sector = get_next_sector();
|
||||||
|
if(!first_sector) return first_sector;
|
||||||
|
if(first_sector->sector == sector) return first_sector;
|
||||||
|
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
std::unique_ptr<Sector> next_sector = get_next_sector();
|
||||||
|
if(next_sector->sector == first_sector->sector) return nullptr;
|
||||||
|
if(next_sector->sector == sector) return next_sector;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<Sector> get_next_sector()
|
||||||
|
{
|
||||||
|
std::unique_ptr<Sector> sector(new Sector);
|
||||||
|
index_count_ = 0;
|
||||||
|
|
||||||
|
while(index_count_ < 2)
|
||||||
|
{
|
||||||
|
// look for a sector header
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if(proceed_to_next_block() == 0x08) break;
|
||||||
|
if(index_count_ >= 2) return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// get sector details, skip if this looks malformed
|
||||||
|
uint8_t checksum = (uint8_t)get_next_byte();
|
||||||
|
sector->sector = (uint8_t)get_next_byte();
|
||||||
|
sector->track = (uint8_t)get_next_byte();
|
||||||
|
uint8_t disk_id[2];
|
||||||
|
disk_id[0] = (uint8_t)get_next_byte();
|
||||||
|
disk_id[1] = (uint8_t)get_next_byte();
|
||||||
|
if(checksum != (sector->sector ^ sector->track ^ disk_id[0] ^ disk_id[1])) continue;
|
||||||
|
|
||||||
|
// look for the following data
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
if(proceed_to_next_block() == 0x07) break;
|
||||||
|
if(index_count_ >= 2) return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
checksum = 0;
|
||||||
|
for(size_t c = 0; c < 256; c++)
|
||||||
|
{
|
||||||
|
sector->data[c] = (uint8_t)get_next_byte();
|
||||||
|
checksum ^= sector->data[c];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(checksum == get_next_byte()) return sector;
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
unsigned int shift_register_;
|
||||||
|
int index_count_;
|
||||||
|
int bit_count_;
|
||||||
|
|
||||||
|
void process_input_bit(int value, unsigned int cycles_since_index_hole)
|
||||||
|
{
|
||||||
|
shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0x3ff;
|
||||||
|
bit_count_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int proceed_to_next_block()
|
||||||
|
{
|
||||||
|
// find GCR lead-in
|
||||||
|
proceed_to_shift_value(0x3ff);
|
||||||
|
if(shift_register_ != 0x3ff) return 0xff;
|
||||||
|
|
||||||
|
// find end of lead-in
|
||||||
|
while(shift_register_ == 0x3ff && index_count_ < 2)
|
||||||
|
{
|
||||||
|
run_for_cycles(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// continue for a further nine bits
|
||||||
|
bit_count_ = 0;
|
||||||
|
while(bit_count_ < 9 && index_count_ < 2)
|
||||||
|
{
|
||||||
|
run_for_cycles(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_);
|
||||||
|
}
|
||||||
|
|
||||||
|
unsigned int get_next_byte()
|
||||||
|
{
|
||||||
|
bit_count_ = 0;
|
||||||
|
while(bit_count_ < 10) run_for_cycles(1);
|
||||||
|
return Storage::Encodings::CommodoreGCR::decoding_from_dectet(shift_register_);
|
||||||
|
}
|
||||||
|
|
||||||
|
void proceed_to_shift_value(unsigned int shift_value)
|
||||||
|
{
|
||||||
|
index_count_ = 0;
|
||||||
|
while(shift_register_ != shift_value && index_count_ < 2)
|
||||||
|
{
|
||||||
|
run_for_cycles(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void process_index_hole()
|
||||||
|
{
|
||||||
|
index_count_++;
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk)
|
std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk)
|
||||||
{
|
{
|
||||||
std::list<File> _files;
|
std::list<File> files;
|
||||||
|
CommodoreGCRParser parser;
|
||||||
|
parser.set_disk(disk);
|
||||||
|
|
||||||
return _files;
|
// find any sector whatsoever to establish the current track
|
||||||
|
std::unique_ptr<CommodoreGCRParser::Sector> sector;
|
||||||
|
|
||||||
|
// attempt to grab a sector from track 0
|
||||||
|
while(!parser.get_is_track_zero()) parser.step(-1);
|
||||||
|
parser.set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(0));
|
||||||
|
sector = parser.get_next_sector();
|
||||||
|
if(!sector) return files;
|
||||||
|
|
||||||
|
// step out to track 18 (== 36)
|
||||||
|
for(int c = 0; c < 36; c++) parser.step(1);
|
||||||
|
|
||||||
|
// assemble disk contents, starting with sector 1
|
||||||
|
parser.set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(1));
|
||||||
|
std::vector<uint8_t> directory;
|
||||||
|
sector = parser.get_sector(1);
|
||||||
|
while(sector)
|
||||||
|
{
|
||||||
|
directory.insert(directory.end(), sector->data.begin(), sector->data.end());
|
||||||
|
uint8_t next_track = sector->data[0];
|
||||||
|
uint8_t next_sector = sector->data[1];
|
||||||
|
|
||||||
|
if(!next_track) break;
|
||||||
|
sector = parser.get_sector(next_sector);
|
||||||
|
|
||||||
|
// TODO: track changes. Allegedly not possible, but definitely happening.
|
||||||
|
}
|
||||||
|
|
||||||
|
return files;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
//
|
//
|
||||||
|
|
||||||
#include "CommodoreGCR.hpp"
|
#include "CommodoreGCR.hpp"
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
using namespace Storage;
|
using namespace Storage;
|
||||||
|
|
||||||
@ -23,34 +24,47 @@ unsigned int Storage::Encodings::CommodoreGCR::encoding_for_nibble(uint8_t nibbl
|
|||||||
{
|
{
|
||||||
switch(nibble & 0xf)
|
switch(nibble & 0xf)
|
||||||
{
|
{
|
||||||
case 0x0: return 0x0a;
|
case 0x0: return 0x0a; case 0x1: return 0x0b;
|
||||||
case 0x1: return 0x0b;
|
case 0x2: return 0x12; case 0x3: return 0x13;
|
||||||
case 0x2: return 0x12;
|
case 0x4: return 0x0e; case 0x5: return 0x0f;
|
||||||
case 0x3: return 0x13;
|
case 0x6: return 0x16; case 0x7: return 0x17;
|
||||||
case 0x4: return 0x0e;
|
case 0x8: return 0x09; case 0x9: return 0x19;
|
||||||
case 0x5: return 0x0f;
|
case 0xa: return 0x1a; case 0xb: return 0x1b;
|
||||||
case 0x6: return 0x16;
|
case 0xc: return 0x0d; case 0xd: return 0x1d;
|
||||||
case 0x7: return 0x17;
|
case 0xe: return 0x1e; case 0xf: return 0x15;
|
||||||
|
|
||||||
case 0x8: return 0x09;
|
|
||||||
case 0x9: return 0x19;
|
|
||||||
case 0xa: return 0x1a;
|
|
||||||
case 0xb: return 0x1b;
|
|
||||||
case 0xc: return 0x0d;
|
|
||||||
case 0xd: return 0x1d;
|
|
||||||
case 0xe: return 0x1e;
|
|
||||||
case 0xf: return 0x15;
|
|
||||||
|
|
||||||
// for the benefit of the compiler; clearly unreachable
|
// for the benefit of the compiler; clearly unreachable
|
||||||
default: return 0xff;
|
default: return 0xff;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int Storage::Encodings::CommodoreGCR::decoding_from_quintet(unsigned int quintet)
|
||||||
|
{
|
||||||
|
switch(quintet & 0x1f)
|
||||||
|
{
|
||||||
|
case 0x0a: return 0x0; case 0x0b: return 0x1;
|
||||||
|
case 0x12: return 0x2; case 0x13: return 0x3;
|
||||||
|
case 0x0e: return 0x4; case 0x0f: return 0x5;
|
||||||
|
case 0x16: return 0x6; case 0x17: return 0x7;
|
||||||
|
case 0x09: return 0x8; case 0x19: return 0x9;
|
||||||
|
case 0x1a: return 0xa; case 0x1b: return 0xb;
|
||||||
|
case 0x0d: return 0xc; case 0x1d: return 0xd;
|
||||||
|
case 0x1e: return 0xe; case 0x15: return 0xf;
|
||||||
|
|
||||||
|
default: return std::numeric_limits<unsigned int>::max();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int Storage::Encodings::CommodoreGCR::encoding_for_byte(uint8_t byte)
|
unsigned int Storage::Encodings::CommodoreGCR::encoding_for_byte(uint8_t byte)
|
||||||
{
|
{
|
||||||
return encoding_for_nibble(byte) | (encoding_for_nibble(byte >> 4) << 5);
|
return encoding_for_nibble(byte) | (encoding_for_nibble(byte >> 4) << 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
unsigned int Storage::Encodings::CommodoreGCR::decoding_from_dectet(unsigned int dectet)
|
||||||
|
{
|
||||||
|
return decoding_from_quintet(dectet) | (decoding_from_quintet(dectet >> 5) << 4);
|
||||||
|
}
|
||||||
|
|
||||||
void Storage::Encodings::CommodoreGCR::encode_block(uint8_t *destination, uint8_t *source)
|
void Storage::Encodings::CommodoreGCR::encode_block(uint8_t *destination, uint8_t *source)
|
||||||
{
|
{
|
||||||
unsigned int encoded_bytes[4] = {
|
unsigned int encoded_bytes[4] = {
|
||||||
|
@ -36,6 +36,16 @@ namespace CommodoreGCR {
|
|||||||
A block is defined to be four source bytes, which encodes to five GCR bytes.
|
A block is defined to be four source bytes, which encodes to five GCR bytes.
|
||||||
*/
|
*/
|
||||||
void encode_block(uint8_t *destination, uint8_t *source);
|
void encode_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.
|
||||||
|
*/
|
||||||
|
unsigned int decoding_from_quintet(unsigned int quintet);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
@returns the byte composted of the low five bit five-bit GCR
|
||||||
|
*/
|
||||||
|
unsigned int decoding_from_dectet(unsigned int dectet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user