1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-09 21:29:53 +00:00
CLK/Storage/Tape/Formats/TapePRG.cpp

197 lines
4.9 KiB
C++
Raw Normal View History

//
// TapePRG.cpp
// Clock Signal
//
// Created by Thomas Harte on 14/08/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "TapePRG.hpp"
#include <sys/stat.h>
using namespace Storage;
TapePRG::TapePRG(const char *file_name) : _file(nullptr), _bitPhase(3), _filePhase(FilePhaseLeadIn), _phaseOffset(0), _copy_mask(0x80)
{
struct stat file_stats;
stat(file_name, &file_stats);
// There's really no way to validate other than that if this file is larger than 64kb,
// of if load address + length > 65536 then it's broken.
if(file_stats.st_size >= 65538 || file_stats.st_size < 3)
throw ErrorBadFormat;
_file = fopen(file_name, "rb");
if(!_file) throw ErrorBadFormat;
_load_address = (uint16_t)fgetc(_file);
_load_address |= (uint16_t)fgetc(_file) << 8;
_length = (uint16_t)(file_stats.st_size - 2);
fseek(_file, 2, SEEK_SET);
if (_load_address + _length >= 65536)
throw ErrorBadFormat;
}
TapePRG::~TapePRG()
{
if(_file) fclose(_file);
}
Tape::Pulse TapePRG::get_next_pulse()
{
// these are all microseconds per pole
static const unsigned int leader_zero_length = 179;
static const unsigned int zero_length = 169;
static const unsigned int one_length = 247;
static const unsigned int marker_length = 328;
_bitPhase = (_bitPhase+1)&3;
if(!_bitPhase) get_next_output_token();
Tape::Pulse pulse;
switch(_outputToken)
{
case Leader: pulse.length.length = leader_zero_length; break;
case Zero: pulse.length.length = (_bitPhase&2) ? one_length : zero_length; break;
case One: pulse.length.length = (_bitPhase&2) ? zero_length : one_length; break;
case WordMarker: pulse.length.length = (_bitPhase&2) ? one_length : marker_length; break;
case EndOfBlock: pulse.length.length = (_bitPhase&2) ? zero_length : marker_length; break;
}
pulse.length.clock_rate = 1000000;
pulse.type = (_bitPhase&1) ? Pulse::High : Pulse::Low;
return pulse;
}
void TapePRG::reset()
{
_bitPhase = 3;
fseek(_file, 2, SEEK_SET);
_filePhase = FilePhaseLeadIn;
_phaseOffset = 0;
_copy_mask = 0x80;
}
void TapePRG::get_next_output_token()
{
static const int block_length = 192; // not counting the checksum
static const int countdown_bytes = 9;
// the lead-in is 20,000 instances of the lead-in pair; every other phase begins with 5000
// before doing whatever it should be doing
if((_filePhase == FilePhaseLeadIn || _filePhase == FilePhaseHeaderDataGap) || _phaseOffset < 50)
{
_outputToken = Leader;
_phaseOffset++;
if(
(_filePhase == FilePhaseLeadIn && _phaseOffset == 20000) ||
(_filePhase == FilePhaseHeaderDataGap && _phaseOffset == 5586)
)
{
_phaseOffset = 0;
_filePhase = (_filePhase == FilePhaseLeadIn) ? FilePhaseHeader : FilePhaseData;
}
return;
}
// determine whether a new byte needs to be queued up
int block_offset = _phaseOffset - 50;
int bit_offset = block_offset % 10;
int byte_offset = block_offset / 10;
_phaseOffset++;
if(byte_offset == block_length + countdown_bytes + 1) // i.e. after the checksum
{
_outputToken = EndOfBlock;
_phaseOffset = 0;
switch(_filePhase)
{
default: break;
// case FilePhaseLeadIn:
// _filePhase = FilePhaseHeader;
// break;
case FilePhaseHeader:
_copy_mask ^= 0x80;
if(_copy_mask) _filePhase = FilePhaseHeaderDataGap;
break;
case FilePhaseData:
if(feof(_file))
{
_copy_mask ^= 0x80;
fseek(_file, 2, SEEK_SET);
}
break;
}
printf("\n===\n");
return;
}
if(bit_offset == 0)
{
// the first nine bytes are countdown; the high bit is set if this is a header
if(byte_offset < countdown_bytes)
{
_output_byte = (uint8_t)(countdown_bytes - byte_offset) | _copy_mask;
}
else if(byte_offset == countdown_bytes + block_length)
{
_output_byte = _check_digit;
}
else
{
if(byte_offset == countdown_bytes) _check_digit = 0;
if(_filePhase == FilePhaseHeader)
{
switch(byte_offset - countdown_bytes)
{
case 0: _output_byte = 0x03; break;
case 1: _output_byte = _load_address & 0xff; break;
case 2: _output_byte = (_load_address >> 8)&0xff; break;
case 3: _output_byte = (_load_address + _length) & 0xff; break;
case 4: _output_byte = ((_load_address + _length) >> 8) & 0xff; break;
case 5: _output_byte = 0x50; break; // P
case 6: _output_byte = 0x52; break; // R
case 7: _output_byte = 0x47; break; // G
default:
_output_byte = 0x20;
break;
}
}
else
{
_output_byte = (uint8_t)fgetc(_file);
if(feof(_file)) _output_byte = 0x00;
}
2016-08-16 02:44:36 +00:00
_check_digit ^= _output_byte;
}
printf(" %02x", _output_byte);
}
switch(bit_offset)
{
case 0:
_outputToken = WordMarker;
break;
default: // i.e. 18
_outputToken = (_output_byte & (1 << (bit_offset - 1))) ? One : Zero;
break;
case 9:
{
uint8_t parity = _output_byte;
parity ^= (parity >> 4);
parity ^= (parity >> 2);
parity ^= (parity >> 1);
_outputToken = (parity&1) ? Zero : One;
printf("[%d]", parity&1);
}
break;
}
}