1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-08-17 07:29:04 +00:00
CLK/StaticAnalyser/Commodore/Disk.cpp
Thomas Harte 96bf133924 Withdraws requirement for DiskController users to specify a PLL multiplier or to provide rotation speed.
In the latter case because it's no longer of any interest to the controller, and in the former because I'd rather it be picked automatically.
2017-09-10 22:56:05 -04:00

240 lines
6.8 KiB
C++

//
// Disk.cpp
// Clock Signal
//
// Created by Thomas Harte on 13/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Disk.hpp"
#include "../../Storage/Disk/DiskController.hpp"
#include "../../Storage/Disk/Encodings/CommodoreGCR.hpp"
#include "../../Storage/Data/Commodore.hpp"
#include <limits>
#include <vector>
#include <array>
using namespace StaticAnalyser::Commodore;
class CommodoreGCRParser: public Storage::Disk::Controller {
public:
std::shared_ptr<Storage::Disk::Drive> drive;
CommodoreGCRParser() : Storage::Disk::Controller(4000000), shift_register_(0), track_(1) {
drive.reset(new Storage::Disk::Drive(4000000, 300));
set_drive(drive);
drive->set_motor_on(true);
}
struct Sector {
uint8_t sector, track;
std::array<uint8_t, 256> data;
bool header_checksum_matched;
bool data_checksum_matched;
};
/*!
Attempts to read the sector located at @c track and @c sector.
@returns a sector if one was found; @c nullptr otherwise.
*/
std::shared_ptr<Sector> get_sector(uint8_t track, uint8_t sector) {
int difference = (int)track - (int)track_;
track_ = track;
if(difference) {
int direction = difference < 0 ? -1 : 1;
difference *= 2 * direction;
for(int c = 0; c < difference; c++) get_drive().step(direction);
unsigned int zone = 3;
if(track >= 18) zone = 2;
else if(track >= 25) zone = 1;
else if(track >= 31) zone = 0;
set_expected_bit_length(Storage::Encodings::CommodoreGCR::length_of_a_bit_in_time_zone(zone));
}
return get_sector(sector);
}
private:
unsigned int shift_register_;
int index_count_;
int bit_count_;
uint8_t track_;
std::shared_ptr<Sector> sector_cache_[65536];
void process_input_bit(int value) {
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::shared_ptr<Sector> get_sector(uint8_t sector) {
uint16_t sector_address = (uint16_t)((track_ << 8) | sector);
if(sector_cache_[sector_address]) return sector_cache_[sector_address];
std::shared_ptr<Sector> first_sector = get_next_sector();
if(!first_sector) return first_sector;
if(first_sector->sector == sector) return first_sector;
while(1) {
std::shared_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::shared_ptr<Sector> get_next_sector() {
std::shared_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()) {
uint16_t sector_address = (uint16_t)((sector->track << 8) | sector->sector);
sector_cache_[sector_address] = sector;
return sector;
}
}
return nullptr;
}
};
std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storage::Disk::Disk> &disk) {
std::list<File> files;
CommodoreGCRParser parser;
parser.drive->set_disk(disk);
// find any sector whatsoever to establish the current track
std::shared_ptr<CommodoreGCRParser::Sector> sector;
// assemble directory
std::vector<uint8_t> directory;
uint8_t next_track = 18;
uint8_t next_sector = 1;
while(1) {
sector = parser.get_sector(next_track, next_sector);
if(!sector) break;
directory.insert(directory.end(), sector->data.begin(), sector->data.end());
next_track = sector->data[0];
next_sector = sector->data[1];
if(!next_track) break;
}
// parse directory
size_t header_pointer = (size_t)-32;
while(header_pointer+32+31 < directory.size()) {
header_pointer += 32;
File new_file;
switch(directory[header_pointer + 2] & 7) {
case 0: // DEL files
default: continue; // Unknown file types
case 1: new_file.type = File::DataSequence; break;
case 2: new_file.type = File::RelocatableProgram; break; // TODO: need a "don't know about relocatable" program?
case 3: new_file.type = File::User; break;
// case 4: new_file.type = File::Relative; break; // Can't handle REL files yet
}
next_track = directory[header_pointer + 3];
next_sector = directory[header_pointer + 4];
new_file.raw_name.reserve(16);
for(size_t c = 0; c < 16; c++) {
new_file.raw_name.push_back(directory[header_pointer + 5 + c]);
}
new_file.name = Storage::Data::Commodore::petscii_from_bytes(&new_file.raw_name[0], 16, false);
size_t number_of_sectors = (size_t)directory[header_pointer + 0x1e] + ((size_t)directory[header_pointer + 0x1f] << 8);
new_file.data.reserve((number_of_sectors - 1) * 254 + 252);
bool is_first_sector = true;
while(next_track) {
sector = parser.get_sector(next_track, next_sector);
if(!sector) break;
next_track = sector->data[0];
next_sector = sector->data[1];
if(is_first_sector) new_file.starting_address = (uint16_t)sector->data[2] | (uint16_t)(sector->data[3] << 8);
if(next_track)
new_file.data.insert(new_file.data.end(), sector->data.begin() + (is_first_sector ? 4 : 2), sector->data.end());
else
new_file.data.insert(new_file.data.end(), sector->data.begin() + 2, sector->data.begin() + next_sector);
is_first_sector = false;
}
if(!next_track) files.push_back(new_file);
}
return files;
}