mirror of
https://github.com/TomHarte/CLK.git
synced 2024-12-25 18:30:21 +00:00
Made a first attempt at Acorn ADFS support plus the start of a suitable analyser.
This commit is contained in:
parent
6084020ab3
commit
de863719d0
@ -350,6 +350,7 @@
|
||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD14B0F1D74627C0088EAD6 /* StaticAnalyser.cpp */; };
|
||||
4BD468F71D8DF41D0084958B /* 1770.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD468F51D8DF41D0084958B /* 1770.cpp */; };
|
||||
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */ = {isa = PBXBuildFile; fileRef = 4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */; };
|
||||
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BD69F921D98760000243FE1 /* AcornADF.cpp */; };
|
||||
4BE77A2E1D84ADFB00BC3827 /* File.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BE77A2C1D84ADFB00BC3827 /* File.cpp */; };
|
||||
4BEE0A6F1D72496600532C7B /* Cartridge.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6A1D72496600532C7B /* Cartridge.cpp */; };
|
||||
4BEE0A701D72496600532C7B /* PRG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BEE0A6D1D72496600532C7B /* PRG.cpp */; };
|
||||
@ -802,6 +803,8 @@
|
||||
4BD468F61D8DF41D0084958B /* 1770.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = 1770.hpp; path = 1770/1770.hpp; sourceTree = "<group>"; };
|
||||
4BD5F1931D13528900631CD1 /* CSBestEffortUpdater.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSBestEffortUpdater.h; path = Updater/CSBestEffortUpdater.h; sourceTree = "<group>"; };
|
||||
4BD5F1941D13528900631CD1 /* CSBestEffortUpdater.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = CSBestEffortUpdater.m; path = Updater/CSBestEffortUpdater.m; sourceTree = "<group>"; };
|
||||
4BD69F921D98760000243FE1 /* AcornADF.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = AcornADF.cpp; sourceTree = "<group>"; };
|
||||
4BD69F931D98760000243FE1 /* AcornADF.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = AcornADF.hpp; sourceTree = "<group>"; };
|
||||
4BE77A2C1D84ADFB00BC3827 /* File.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = File.cpp; path = ../../StaticAnalyser/Commodore/File.cpp; sourceTree = "<group>"; };
|
||||
4BE77A2D1D84ADFB00BC3827 /* File.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = File.hpp; path = ../../StaticAnalyser/Commodore/File.hpp; sourceTree = "<group>"; };
|
||||
4BEE0A6A1D72496600532C7B /* Cartridge.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = Cartridge.cpp; sourceTree = "<group>"; };
|
||||
@ -1133,6 +1136,8 @@
|
||||
4B4C836F1D4F623200CD541F /* D64.hpp */,
|
||||
4BF829611D8F536B001BAE39 /* SSD.cpp */,
|
||||
4BF829621D8F536B001BAE39 /* SSD.hpp */,
|
||||
4BD69F921D98760000243FE1 /* AcornADF.cpp */,
|
||||
4BD69F931D98760000243FE1 /* AcornADF.hpp */,
|
||||
);
|
||||
path = Formats;
|
||||
sourceTree = "<group>";
|
||||
@ -2113,6 +2118,7 @@
|
||||
4BD5F1951D13528900631CD1 /* CSBestEffortUpdater.m in Sources */,
|
||||
4B96F7221D75119A0058BB2D /* Tape.cpp in Sources */,
|
||||
4B0BE4281D3481E700D5256B /* DigitalPhaseLockedLoop.cpp in Sources */,
|
||||
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */,
|
||||
4B73C71A1D036BD90074D992 /* Vic20Document.swift in Sources */,
|
||||
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */,
|
||||
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */,
|
||||
|
@ -19,7 +19,7 @@ class FMParser: public Storage::Disk::Drive {
|
||||
FMParser(bool is_mfm) :
|
||||
Storage::Disk::Drive(4000000, 1, 300),
|
||||
crc_generator_(0x1021, 0xffff),
|
||||
shift_register_(0), track_(0)
|
||||
shift_register_(0), track_(0), is_mfm_(is_mfm)
|
||||
{
|
||||
// Make sure this drive really is at track '1'.
|
||||
while(!get_is_track_zero()) step(-1);
|
||||
@ -58,6 +58,7 @@ class FMParser: public Storage::Disk::Drive {
|
||||
int bit_count_;
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector_cache_[65536];
|
||||
NumberTheory::CRC16 crc_generator_;
|
||||
bool is_mfm_;
|
||||
|
||||
void process_input_bit(int value, unsigned int cycles_since_index_hole)
|
||||
{
|
||||
@ -98,7 +99,18 @@ class FMParser: public Storage::Disk::Drive {
|
||||
while(1)
|
||||
{
|
||||
run_for_cycles(1);
|
||||
if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) break;
|
||||
if(is_mfm_)
|
||||
{
|
||||
if(shift_register_ == Storage::Encodings::MFM::MFMAddressMark)
|
||||
{
|
||||
uint8_t mark = get_next_byte();
|
||||
if(mark == Storage::Encodings::MFM::MFMIDAddressByte) break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) break;
|
||||
}
|
||||
if(index_count_ >= 2) return nullptr;
|
||||
}
|
||||
|
||||
@ -115,8 +127,20 @@ class FMParser: public Storage::Disk::Drive {
|
||||
while(1)
|
||||
{
|
||||
run_for_cycles(1);
|
||||
if(shift_register_ == Storage::Encodings::MFM::FMDataAddressMark) break;
|
||||
if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) return nullptr;
|
||||
if(is_mfm_)
|
||||
{
|
||||
if(shift_register_ == Storage::Encodings::MFM::MFMAddressMark)
|
||||
{
|
||||
uint8_t mark = get_next_byte();
|
||||
if(mark == Storage::Encodings::MFM::MFMDataAddressByte) break;
|
||||
if(mark == Storage::Encodings::MFM::MFMIDAddressByte) return nullptr;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if(shift_register_ == Storage::Encodings::MFM::FMDataAddressMark) break;
|
||||
if(shift_register_ == Storage::Encodings::MFM::FMIDAddressMark) return nullptr;
|
||||
}
|
||||
if(index_count_ >= 2) return nullptr;
|
||||
}
|
||||
|
||||
@ -165,7 +189,7 @@ std::unique_ptr<Catalogue> StaticAnalyser::Acorn::GetDFSCatalogue(const std::sha
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> names = parser.get_sector(0, 0);
|
||||
std::shared_ptr<Storage::Encodings::MFM::Sector> details = parser.get_sector(0, 1);
|
||||
|
||||
if(!names || !details) return catalogue;
|
||||
if(!names || !details) return nullptr;
|
||||
if(names->data.size() != 256 || details->data.size() != 256) return nullptr;
|
||||
|
||||
uint8_t final_file_offset = details->data[5];
|
||||
|
@ -69,6 +69,7 @@ void StaticAnalyser::Acorn::AddTargets(
|
||||
target.probability = 1.0; // TODO: a proper estimation
|
||||
target.acorn.has_dfs = false;
|
||||
target.acorn.has_adfs = false;
|
||||
target.acorn.should_hold_shift = false;
|
||||
|
||||
// strip out inappropriate cartridges
|
||||
target.cartridges = AcornCartridgesFrom(cartridges);
|
||||
@ -116,14 +117,15 @@ void StaticAnalyser::Acorn::AddTargets(
|
||||
if(disks.size() > 0)
|
||||
{
|
||||
std::shared_ptr<Storage::Disk::Disk> disk = disks.front();
|
||||
std::unique_ptr<Catalogue> catalogue = GetDFSCatalogue(disk);
|
||||
if(catalogue == nullptr) catalogue = GetADFSCatalogue(disk);
|
||||
if(catalogue)
|
||||
std::unique_ptr<Catalogue> dfs_catalogue, adfs_catalogue;
|
||||
dfs_catalogue = GetDFSCatalogue(disk);
|
||||
if(dfs_catalogue == nullptr) adfs_catalogue = GetADFSCatalogue(disk);
|
||||
if(dfs_catalogue || adfs_catalogue)
|
||||
{
|
||||
target.disks = disks;
|
||||
target.acorn.has_dfs = true;
|
||||
target.acorn.has_dfs = !!dfs_catalogue;
|
||||
|
||||
switch(catalogue->bootOption)
|
||||
switch((dfs_catalogue ?: adfs_catalogue)->bootOption)
|
||||
{
|
||||
case Catalogue::BootOption::None: target.loadingCommand = "*CAT\n"; break;
|
||||
default: target.acorn.should_hold_shift = true; break;
|
||||
|
@ -20,6 +20,7 @@
|
||||
#include "../Storage/Cartridge/Formats/PRG.hpp"
|
||||
|
||||
// Disks
|
||||
#include "../Storage/Disk/Formats/AcornADF.hpp"
|
||||
#include "../Storage/Disk/Formats/D64.hpp"
|
||||
#include "../Storage/Disk/Formats/G64.hpp"
|
||||
#include "../Storage/Disk/Formats/SSD.hpp"
|
||||
@ -80,6 +81,7 @@ std::list<Target> StaticAnalyser::GetTargets(const char *file_name)
|
||||
}
|
||||
|
||||
Format("a26", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
|
||||
Format("adf", disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF
|
||||
Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
|
||||
Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64
|
||||
Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD
|
||||
|
@ -49,23 +49,23 @@ template <class T> class MFMShifter: public Shifter<T> {
|
||||
}
|
||||
|
||||
void add_index_address_mark() {
|
||||
static_cast<T *>(this)->output_short(output_ = 0x5224);
|
||||
add_byte(0xfc);
|
||||
static_cast<T *>(this)->output_short(output_ = MFMIndexAddressMark);
|
||||
add_byte(MFMIndexAddressByte);
|
||||
}
|
||||
|
||||
void add_ID_address_mark() {
|
||||
static_cast<T *>(this)->output_short(output_ = 0x4489);
|
||||
add_byte(0xfe);
|
||||
static_cast<T *>(this)->output_short(output_ = MFMAddressMark);
|
||||
add_byte(MFMIDAddressByte);
|
||||
}
|
||||
|
||||
void add_data_address_mark() {
|
||||
static_cast<T *>(this)->output_short(output_ = 0x4489);
|
||||
add_byte(0xfb);
|
||||
static_cast<T *>(this)->output_short(output_ = MFMAddressMark);
|
||||
add_byte(MFMDataAddressByte);
|
||||
}
|
||||
|
||||
void add_deleted_data_address_mark() {
|
||||
static_cast<T *>(this)->output_short(output_ = 0x4489);
|
||||
add_byte(0xf8);
|
||||
static_cast<T *>(this)->output_short(output_ = MFMAddressMark);
|
||||
add_byte(MFMDeletedDataAddressByte);
|
||||
}
|
||||
|
||||
private:
|
||||
|
@ -22,6 +22,13 @@ const uint16_t FMIDAddressMark = 0xf57e; // data 0xfe, with clock 0xc7 => 1111
|
||||
const uint16_t FMDataAddressMark = 0xf56f; // data 0xfb, with clock 0xc7 => 1111 1011 with clock 1100 0111 => 1111 0101 0110 1111
|
||||
const uint16_t FMDeletedDataAddressMark = 0xf56a; // data 0xf8, with clock 0xc7 => 1111 1000 with clock 1100 0111 => 1111 0101 0110 1010
|
||||
|
||||
const uint16_t MFMIndexAddressMark = 0x5224;
|
||||
const uint16_t MFMAddressMark = 0x4489;
|
||||
const uint8_t MFMIndexAddressByte = 0xfc;
|
||||
const uint8_t MFMIDAddressByte = 0xfe;
|
||||
const uint8_t MFMDataAddressByte = 0xfb;
|
||||
const uint8_t MFMDeletedDataAddressByte = 0xf8;
|
||||
|
||||
|
||||
struct Sector {
|
||||
uint8_t track, side, sector;
|
||||
|
82
Storage/Disk/Formats/AcornADF.cpp
Normal file
82
Storage/Disk/Formats/AcornADF.cpp
Normal file
@ -0,0 +1,82 @@
|
||||
//
|
||||
// AcornADF.cpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#include "AcornADF.hpp"
|
||||
|
||||
#include <sys/stat.h>
|
||||
#include "../Encodings/MFM.hpp"
|
||||
|
||||
using namespace Storage::Disk;
|
||||
|
||||
AcornADF::AcornADF(const char *file_name) : _file(nullptr)
|
||||
{
|
||||
struct stat file_stats;
|
||||
stat(file_name, &file_stats);
|
||||
|
||||
// very loose validation: the file needs to be a multiple of 256 bytes
|
||||
// and not ungainly large
|
||||
if(file_stats.st_size & 255) throw ErrorNotAcornADF;
|
||||
if(file_stats.st_size < 2048) throw ErrorNotAcornADF;
|
||||
|
||||
_file = fopen(file_name, "rb");
|
||||
if(!_file) throw ErrorCantOpen;
|
||||
|
||||
// check that the initial directory's 'Hugo's are present
|
||||
fseek(_file, 513, SEEK_SET);
|
||||
uint8_t bytes[4];
|
||||
fread(bytes, 1, 4, _file);
|
||||
if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF;
|
||||
|
||||
fseek(_file, 0x6fb, SEEK_SET);
|
||||
fread(bytes, 1, 4, _file);
|
||||
if(bytes[0] != 'H' || bytes[1] != 'u' || bytes[2] != 'g' || bytes[3] != 'o') throw ErrorNotAcornADF;
|
||||
}
|
||||
|
||||
AcornADF::~AcornADF()
|
||||
{
|
||||
if(_file) fclose(_file);
|
||||
}
|
||||
|
||||
unsigned int AcornADF::get_head_position_count()
|
||||
{
|
||||
return 80;
|
||||
}
|
||||
|
||||
unsigned int AcornADF::get_head_count()
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
|
||||
std::shared_ptr<Track> AcornADF::get_track_at_position(unsigned int head, unsigned int position)
|
||||
{
|
||||
std::shared_ptr<Track> track;
|
||||
|
||||
if(head >= 2) return track;
|
||||
long file_offset = (position * 2 + head) * 256 * 16;
|
||||
fseek(_file, file_offset, SEEK_SET);
|
||||
|
||||
std::vector<Storage::Encodings::MFM::Sector> sectors;
|
||||
for(int sector = 0; sector < 16; sector++)
|
||||
{
|
||||
Storage::Encodings::MFM::Sector new_sector;
|
||||
new_sector.track = (uint8_t)position;
|
||||
new_sector.side = 0;
|
||||
new_sector.sector = (uint8_t)sector;
|
||||
|
||||
new_sector.data.resize(256);
|
||||
fread(&new_sector.data[0], 1, 256, _file);
|
||||
if(feof(_file))
|
||||
break;
|
||||
|
||||
sectors.push_back(std::move(new_sector));
|
||||
}
|
||||
|
||||
if(sectors.size()) return Storage::Encodings::MFM::GetMFMTrackWithSectors(sectors);
|
||||
|
||||
return track;
|
||||
}
|
48
Storage/Disk/Formats/AcornADF.hpp
Normal file
48
Storage/Disk/Formats/AcornADF.hpp
Normal file
@ -0,0 +1,48 @@
|
||||
//
|
||||
// AcornADF.hpp
|
||||
// Clock Signal
|
||||
//
|
||||
// Created by Thomas Harte on 25/09/2016.
|
||||
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||
//
|
||||
|
||||
#ifndef AcornADF_hpp
|
||||
#define AcornADF_hpp
|
||||
|
||||
#include "../Disk.hpp"
|
||||
|
||||
namespace Storage {
|
||||
namespace Disk {
|
||||
|
||||
/*!
|
||||
Provies a @c Disk containing an ADF disk image — a decoded sector dump of an Acorn ADFS disk.
|
||||
*/
|
||||
class AcornADF: public Disk {
|
||||
public:
|
||||
/*!
|
||||
Construct an @c AcornADF containing content from the file with name @c file_name.
|
||||
|
||||
@throws ErrorCantOpen if this file can't be opened.
|
||||
@throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image.
|
||||
*/
|
||||
AcornADF(const char *file_name);
|
||||
~AcornADF();
|
||||
|
||||
enum {
|
||||
ErrorCantOpen,
|
||||
ErrorNotAcornADF,
|
||||
};
|
||||
|
||||
// implemented to satisfy @c Disk
|
||||
unsigned int get_head_position_count();
|
||||
unsigned int get_head_count();
|
||||
std::shared_ptr<Track> get_track_at_position(unsigned int head, unsigned int position);
|
||||
|
||||
private:
|
||||
FILE *_file;
|
||||
};
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endif /* AcornADF_hpp */
|
@ -69,7 +69,8 @@ std::shared_ptr<Track> SSD::get_track_at_position(unsigned int head, unsigned in
|
||||
|
||||
new_sector.data.resize(256);
|
||||
fread(&new_sector.data[0], 1, 256, _file);
|
||||
if(feof(_file)) break;
|
||||
if(feof(_file))
|
||||
break;
|
||||
|
||||
sectors.push_back(std::move(new_sector));
|
||||
}
|
||||
|
@ -20,10 +20,10 @@ namespace Disk {
|
||||
class SSD: public Disk {
|
||||
public:
|
||||
/*!
|
||||
Construct a @c D64 containing content from the file with name @c file_name.
|
||||
Construct an @c SSD containing content from the file with name @c file_name.
|
||||
|
||||
@throws ErrorCantOpen if this file can't be opened.
|
||||
@throws ErrorNotD64 if the file doesn't appear to contain a .D64 format image.
|
||||
@throws ErrorNotSSD if the file doesn't appear to contain a .SSD format image.
|
||||
*/
|
||||
SSD(const char *file_name);
|
||||
~SSD();
|
||||
|
Loading…
Reference in New Issue
Block a user