mirror of
https://github.com/TomHarte/CLK.git
synced 2024-11-29 12:50:28 +00:00
Merge pull request #89 from TomHarte/OricDiskWrites
Adds write support for the Oric .DSK file format
This commit is contained in:
commit
df6e98fa52
@ -126,7 +126,8 @@ static uint8_t logarithmic_size_for_size(size_t size)
|
|||||||
case 256: return 1;
|
case 256: return 1;
|
||||||
case 512: return 2;
|
case 512: return 2;
|
||||||
case 1024: return 3;
|
case 1024: return 3;
|
||||||
case 2048: return 4;
|
case 2048: return 4; std::vector<uint8_t> get_track(uint8_t track);
|
||||||
|
|
||||||
case 4196: return 5;
|
case 4196: return 5;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -267,7 +268,7 @@ Parser::Parser(bool is_mfm, const std::shared_ptr<Storage::Disk::Track> &track)
|
|||||||
drive->set_disk_with_track(track);
|
drive->set_disk_with_track(track);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Storage::Encodings::MFM::Sector> Parser::get_sector(uint8_t track, uint8_t sector)
|
void Parser::seek_to_track(uint8_t track)
|
||||||
{
|
{
|
||||||
int difference = (int)track - (int)track_;
|
int difference = (int)track - (int)track_;
|
||||||
track_ = track;
|
track_ = track;
|
||||||
@ -279,10 +280,20 @@ std::shared_ptr<Storage::Encodings::MFM::Sector> Parser::get_sector(uint8_t trac
|
|||||||
|
|
||||||
for(int c = 0; c < difference; c++) step(direction);
|
for(int c = 0; c < difference; c++) step(direction);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Storage::Encodings::MFM::Sector> Parser::get_sector(uint8_t track, uint8_t sector)
|
||||||
|
{
|
||||||
|
seek_to_track(track);
|
||||||
return get_sector(sector);
|
return get_sector(sector);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Parser::get_track(uint8_t track)
|
||||||
|
{
|
||||||
|
seek_to_track(track);
|
||||||
|
return get_track();
|
||||||
|
}
|
||||||
|
|
||||||
void Parser::process_input_bit(int value, unsigned int cycles_since_index_hole)
|
void Parser::process_input_bit(int value, unsigned int cycles_since_index_hole)
|
||||||
{
|
{
|
||||||
shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0xffff;
|
shift_register_ = ((shift_register_ << 1) | (unsigned int)value) & 0xffff;
|
||||||
@ -294,23 +305,132 @@ void Parser::process_index_hole()
|
|||||||
index_count_++;
|
index_count_++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t Parser::get_byte_for_shift_value(uint16_t value)
|
||||||
|
{
|
||||||
|
return (uint8_t)(
|
||||||
|
((value&0x0001) >> 0) |
|
||||||
|
((value&0x0004) >> 1) |
|
||||||
|
((value&0x0010) >> 2) |
|
||||||
|
((value&0x0040) >> 3) |
|
||||||
|
((value&0x0100) >> 4) |
|
||||||
|
((value&0x0400) >> 5) |
|
||||||
|
((value&0x1000) >> 6) |
|
||||||
|
((value&0x4000) >> 7));}
|
||||||
|
|
||||||
uint8_t Parser::get_next_byte()
|
uint8_t Parser::get_next_byte()
|
||||||
{
|
{
|
||||||
bit_count_ = 0;
|
bit_count_ = 0;
|
||||||
while(bit_count_ < 16) run_for_cycles(1);
|
while(bit_count_ < 16) run_for_cycles(1);
|
||||||
uint8_t byte = (uint8_t)(
|
uint8_t byte = get_byte_for_shift_value((uint16_t)shift_register_);
|
||||||
((shift_register_&0x0001) >> 0) |
|
|
||||||
((shift_register_&0x0004) >> 1) |
|
|
||||||
((shift_register_&0x0010) >> 2) |
|
|
||||||
((shift_register_&0x0040) >> 3) |
|
|
||||||
((shift_register_&0x0100) >> 4) |
|
|
||||||
((shift_register_&0x0400) >> 5) |
|
|
||||||
((shift_register_&0x1000) >> 6) |
|
|
||||||
((shift_register_&0x4000) >> 7));
|
|
||||||
crc_generator_.add(byte);
|
crc_generator_.add(byte);
|
||||||
return byte;
|
return byte;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> Parser::get_track()
|
||||||
|
{
|
||||||
|
std::vector<uint8_t> result;
|
||||||
|
int distance_until_permissible_sync = 0;
|
||||||
|
uint8_t last_id[6];
|
||||||
|
int last_id_pointer = 0;
|
||||||
|
bool next_is_type = false;
|
||||||
|
|
||||||
|
// align to the next index hole
|
||||||
|
index_count_ = 0;
|
||||||
|
while(!index_count_) run_for_cycles(1);
|
||||||
|
|
||||||
|
// capture every other bit until the next index hole
|
||||||
|
index_count_ = 0;
|
||||||
|
while(1)
|
||||||
|
{
|
||||||
|
// wait until either another bit or the index hole arrives
|
||||||
|
bit_count_ = 0;
|
||||||
|
bool found_sync = false;
|
||||||
|
while(!index_count_ && !found_sync && bit_count_ < 16)
|
||||||
|
{
|
||||||
|
int previous_bit_count = bit_count_;
|
||||||
|
run_for_cycles(1);
|
||||||
|
|
||||||
|
if(!distance_until_permissible_sync && bit_count_ != previous_bit_count)
|
||||||
|
{
|
||||||
|
uint16_t low_shift_register = (shift_register_&0xffff);
|
||||||
|
if(is_mfm_)
|
||||||
|
{
|
||||||
|
found_sync = (low_shift_register == MFMIndexSync) || (low_shift_register == MFMSync);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
found_sync =
|
||||||
|
(low_shift_register == FMIndexAddressMark) ||
|
||||||
|
(low_shift_register == FMIDAddressMark) ||
|
||||||
|
(low_shift_register == FMDataAddressMark) ||
|
||||||
|
(low_shift_register == FMDeletedDataAddressMark);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if that was the index hole then finish
|
||||||
|
if(index_count_)
|
||||||
|
{
|
||||||
|
if(bit_count_) result.push_back(get_byte_for_shift_value((uint16_t)(shift_register_ << (16 - bit_count_))));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// store whatever the current byte is
|
||||||
|
uint8_t byte_value = get_byte_for_shift_value((uint16_t)shift_register_);
|
||||||
|
result.push_back(byte_value);
|
||||||
|
if(last_id_pointer < 6) last_id[last_id_pointer++] = byte_value;
|
||||||
|
|
||||||
|
// if no syncs are permissible here, decrement the waiting period and perform no further contemplation
|
||||||
|
bool found_id = false, found_data = false;
|
||||||
|
if(distance_until_permissible_sync)
|
||||||
|
{
|
||||||
|
distance_until_permissible_sync--;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(found_sync)
|
||||||
|
{
|
||||||
|
if(is_mfm_)
|
||||||
|
{
|
||||||
|
next_is_type = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
switch(shift_register_&0xffff)
|
||||||
|
{
|
||||||
|
case FMIDAddressMark: found_id = true; break;
|
||||||
|
case FMDataAddressMark:
|
||||||
|
case FMDeletedDataAddressMark: found_data = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(next_is_type)
|
||||||
|
{
|
||||||
|
switch(byte_value)
|
||||||
|
{
|
||||||
|
case MFMIDAddressByte: found_id = true; break;
|
||||||
|
case MFMDataAddressByte:
|
||||||
|
case MFMDeletedDataAddressByte: found_data = true; break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(found_id)
|
||||||
|
{
|
||||||
|
distance_until_permissible_sync = 6;
|
||||||
|
last_id_pointer = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(found_data)
|
||||||
|
{
|
||||||
|
distance_until_permissible_sync = 128 << last_id[3];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
std::shared_ptr<Storage::Encodings::MFM::Sector> Parser::get_next_sector()
|
std::shared_ptr<Storage::Encodings::MFM::Sector> Parser::get_next_sector()
|
||||||
{
|
{
|
||||||
std::shared_ptr<Storage::Encodings::MFM::Sector> sector(new Storage::Encodings::MFM::Sector);
|
std::shared_ptr<Storage::Encodings::MFM::Sector> sector(new Storage::Encodings::MFM::Sector);
|
||||||
|
@ -73,6 +73,18 @@ class Parser: public Storage::Disk::Controller {
|
|||||||
*/
|
*/
|
||||||
std::shared_ptr<Storage::Encodings::MFM::Sector> get_sector(uint8_t track, uint8_t sector);
|
std::shared_ptr<Storage::Encodings::MFM::Sector> get_sector(uint8_t track, uint8_t sector);
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Attempts to read the track at @c track, starting from the index hole.
|
||||||
|
|
||||||
|
Decodes data bits only; clocks are omitted. Synchronisation values begin a new
|
||||||
|
byte. If a synchronisation value begins partway through a byte then
|
||||||
|
synchronisation-contributing bits will appear both in the preceding byte and
|
||||||
|
in the next.
|
||||||
|
|
||||||
|
@returns a vector of data found.
|
||||||
|
*/
|
||||||
|
std::vector<uint8_t> get_track(uint8_t track);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Parser(bool is_mfm);
|
Parser(bool is_mfm);
|
||||||
|
|
||||||
@ -84,11 +96,16 @@ class Parser: public Storage::Disk::Controller {
|
|||||||
NumberTheory::CRC16 crc_generator_;
|
NumberTheory::CRC16 crc_generator_;
|
||||||
bool is_mfm_;
|
bool is_mfm_;
|
||||||
|
|
||||||
|
void seek_to_track(uint8_t track);
|
||||||
void process_input_bit(int value, unsigned int cycles_since_index_hole);
|
void process_input_bit(int value, unsigned int cycles_since_index_hole);
|
||||||
void process_index_hole();
|
void process_index_hole();
|
||||||
uint8_t get_next_byte();
|
uint8_t get_next_byte();
|
||||||
|
|
||||||
|
uint8_t get_byte_for_shift_value(uint16_t value);
|
||||||
|
|
||||||
std::shared_ptr<Storage::Encodings::MFM::Sector> get_next_sector();
|
std::shared_ptr<Storage::Encodings::MFM::Sector> get_next_sector();
|
||||||
std::shared_ptr<Storage::Encodings::MFM::Sector> get_sector(uint8_t sector);
|
std::shared_ptr<Storage::Encodings::MFM::Sector> get_sector(uint8_t sector);
|
||||||
|
std::vector<uint8_t> get_track();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -26,6 +26,11 @@ OricMFMDSK::OricMFMDSK(const char *file_name) :
|
|||||||
throw ErrorNotOricMFMDSK;
|
throw ErrorNotOricMFMDSK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
OricMFMDSK::~OricMFMDSK()
|
||||||
|
{
|
||||||
|
flush_updates();
|
||||||
|
}
|
||||||
|
|
||||||
unsigned int OricMFMDSK::get_head_position_count()
|
unsigned int OricMFMDSK::get_head_position_count()
|
||||||
{
|
{
|
||||||
return track_count_;
|
return track_count_;
|
||||||
@ -36,7 +41,12 @@ unsigned int OricMFMDSK::get_head_count()
|
|||||||
return head_count_;
|
return head_count_;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int head, unsigned int position)
|
bool OricMFMDSK::get_is_read_only()
|
||||||
|
{
|
||||||
|
return is_read_only_;
|
||||||
|
}
|
||||||
|
|
||||||
|
long OricMFMDSK::get_file_offset_for_position(unsigned int head, unsigned int position)
|
||||||
{
|
{
|
||||||
long seek_offset = 0;
|
long seek_offset = 0;
|
||||||
switch(geometry_type_)
|
switch(geometry_type_)
|
||||||
@ -48,7 +58,12 @@ std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int h
|
|||||||
seek_offset = (position * track_count_ * head_count_) + head;
|
seek_offset = (position * track_count_ * head_count_) + head;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
fseek(file_, (seek_offset * 6400) + 256, SEEK_SET);
|
return (seek_offset * 6400) + 256;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int head, unsigned int position)
|
||||||
|
{
|
||||||
|
fseek(file_, get_file_offset_for_position(head, position), SEEK_SET);
|
||||||
|
|
||||||
PCMSegment segment;
|
PCMSegment segment;
|
||||||
|
|
||||||
@ -115,3 +130,15 @@ std::shared_ptr<Track> OricMFMDSK::get_uncached_track_at_position(unsigned int h
|
|||||||
std::shared_ptr<PCMTrack> track(new PCMTrack(segment));
|
std::shared_ptr<PCMTrack> track(new PCMTrack(segment));
|
||||||
return track;
|
return track;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void OricMFMDSK::store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex)
|
||||||
|
{
|
||||||
|
Storage::Encodings::MFM::Parser parser(true, track);
|
||||||
|
std::vector<uint8_t> parsed_track = parser.get_track(0);
|
||||||
|
long file_offset = get_file_offset_for_position(head, position);
|
||||||
|
|
||||||
|
std::lock_guard<std::mutex> lock_guard(file_access_mutex);
|
||||||
|
fseek(file_, file_offset, SEEK_SET);
|
||||||
|
size_t track_size = std::min((size_t)6400, parsed_track.size());
|
||||||
|
fwrite(parsed_track.data(), 1, track_size, file_);
|
||||||
|
}
|
||||||
|
@ -27,6 +27,7 @@ class OricMFMDSK: public Disk, public Storage::FileHolder {
|
|||||||
@throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image.
|
@throws ErrorNotAcornADF if the file doesn't appear to contain an Acorn .ADF format image.
|
||||||
*/
|
*/
|
||||||
OricMFMDSK(const char *file_name);
|
OricMFMDSK(const char *file_name);
|
||||||
|
~OricMFMDSK();
|
||||||
|
|
||||||
enum {
|
enum {
|
||||||
ErrorNotOricMFMDSK,
|
ErrorNotOricMFMDSK,
|
||||||
@ -35,9 +36,13 @@ class OricMFMDSK: public Disk, public Storage::FileHolder {
|
|||||||
// implemented to satisfy @c Disk
|
// implemented to satisfy @c Disk
|
||||||
unsigned int get_head_position_count();
|
unsigned int get_head_position_count();
|
||||||
unsigned int get_head_count();
|
unsigned int get_head_count();
|
||||||
|
bool get_is_read_only();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void store_updated_track_at_position(unsigned int head, unsigned int position, const std::shared_ptr<Track> &track, std::mutex &file_access_mutex);
|
||||||
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
std::shared_ptr<Track> get_uncached_track_at_position(unsigned int head, unsigned int position);
|
||||||
|
long get_file_offset_for_position(unsigned int head, unsigned int position);
|
||||||
|
|
||||||
uint32_t head_count_;
|
uint32_t head_count_;
|
||||||
uint32_t track_count_;
|
uint32_t track_count_;
|
||||||
uint32_t geometry_type_;
|
uint32_t geometry_type_;
|
||||||
|
@ -93,7 +93,7 @@ void FileHolder::ensure_file_is_at_least_length(long length)
|
|||||||
if(bytes_to_write > 0)
|
if(bytes_to_write > 0)
|
||||||
{
|
{
|
||||||
uint8_t *empty = new uint8_t[bytes_to_write];
|
uint8_t *empty = new uint8_t[bytes_to_write];
|
||||||
memset(empty, 0, bytes_to_write);
|
memset(empty, 0, (size_t)bytes_to_write);
|
||||||
fwrite(empty, sizeof(uint8_t), (size_t)bytes_to_write, file_);
|
fwrite(empty, sizeof(uint8_t), (size_t)bytes_to_write, file_);
|
||||||
delete[] empty;
|
delete[] empty;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user