mirror of
https://github.com/TomHarte/CLK.git
synced 2024-10-18 08:24:17 +00:00
Factored out the stuff I expect to be common to this tape nonsense, started looking at the one currently-failing tape. More on the latter to do.
This commit is contained in:
parent
6f62803814
commit
93f5b5303e
@ -84,7 +84,7 @@ void StaticAnalyser::Acorn::AddTargets(
|
|||||||
bool is_basic = true;
|
bool is_basic = true;
|
||||||
|
|
||||||
// protected files are always for *RUNning only
|
// protected files are always for *RUNning only
|
||||||
if(files.front().is_protected) is_basic = false;
|
// if(files.front().is_protected) is_basic = false;
|
||||||
|
|
||||||
// check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code,
|
// check also for a continuous threading of BASIC lines; if none then this probably isn't BASIC code,
|
||||||
// so that's also justification to *RUN
|
// so that's also justification to *RUN
|
||||||
|
@ -12,122 +12,151 @@
|
|||||||
|
|
||||||
using namespace StaticAnalyser::Acorn;
|
using namespace StaticAnalyser::Acorn;
|
||||||
|
|
||||||
struct TapeParser {
|
/*!
|
||||||
|
A partly-abstract base class to help in the authorship of tape format parsers;
|
||||||
|
provides hooks for a
|
||||||
|
*/
|
||||||
|
template <typename WaveType, typename SymbolType> class TapeParser {
|
||||||
|
public:
|
||||||
|
TapeParser(const std::shared_ptr<Storage::Tape::Tape> &tape) : _tape(tape) {}
|
||||||
|
|
||||||
TapeParser(const std::shared_ptr<Storage::Tape::Tape> &tape) : _wave_length_pointer(0), _tape(tape) {}
|
std::unique_ptr<SymbolType> get_next_symbol()
|
||||||
|
|
||||||
int get_next_bit()
|
|
||||||
{
|
|
||||||
while(!_tape->is_at_end())
|
|
||||||
{
|
{
|
||||||
// skip any gaps
|
while(!_tape->is_at_end())
|
||||||
Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse();
|
|
||||||
while(!_tape->is_at_end() && next_pulse.type == Storage::Tape::Tape::Pulse::Zero)
|
|
||||||
{
|
{
|
||||||
next_pulse = _tape->get_next_pulse();
|
std::unique_ptr<SymbolType> symbol = dequeue_next_symbol(_wave_queue);
|
||||||
}
|
if(symbol) return symbol;
|
||||||
|
|
||||||
_wave_lengths[_wave_length_pointer] = next_pulse.length.get_float();
|
while(!_tape->is_at_end())
|
||||||
_wave_length_pointer++;
|
|
||||||
|
|
||||||
// if first wave is too short or too long, drop it
|
|
||||||
if(_wave_lengths[0] < 1.0f / 4800.0f || _wave_lengths[0] >= 5.0f / 4800.0f)
|
|
||||||
{
|
|
||||||
rotate(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
// if first two waves add up to a correct-length cycle, pop them and this is a 0
|
|
||||||
if(_wave_length_pointer >= 2)
|
|
||||||
{
|
|
||||||
float length = _wave_lengths[0] + _wave_lengths[1];
|
|
||||||
if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f)
|
|
||||||
{
|
{
|
||||||
rotate(2);
|
Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse();
|
||||||
return 0;
|
std::unique_ptr<WaveType> next_wave = get_wave_type_for_pulse(next_pulse);
|
||||||
|
if(next_wave)
|
||||||
|
{
|
||||||
|
_wave_queue.push_back(*next_wave);
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// if all four waves add up to a correct-length cycle, pop them and this is a 1
|
return nullptr;
|
||||||
if(_wave_length_pointer == 4)
|
|
||||||
{
|
|
||||||
float length = _wave_lengths[0] + _wave_lengths[1] + _wave_lengths[2] + _wave_lengths[3];
|
|
||||||
if(length >= 3.0f / 4800.0f && length < 5.0f / 4800.0f)
|
|
||||||
{
|
|
||||||
rotate(4);
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
rotate(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
void reset_error_flag() { _error_flag = false; }
|
||||||
}
|
bool get_error_flag() { return _error_flag; }
|
||||||
|
bool is_at_end() { return _tape->is_at_end(); }
|
||||||
|
|
||||||
int get_next_byte()
|
protected:
|
||||||
{
|
bool _error_flag;
|
||||||
int value = 0;
|
|
||||||
int c = 8;
|
|
||||||
if(get_next_bit())
|
|
||||||
{
|
|
||||||
_error_flag = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
while(c--)
|
|
||||||
{
|
|
||||||
value = (value >> 1) | (get_next_bit() << 7);
|
|
||||||
}
|
|
||||||
if(!get_next_bit())
|
|
||||||
{
|
|
||||||
_error_flag = true;
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
add_to_crc((uint8_t)value);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_next_short()
|
|
||||||
{
|
|
||||||
int result = get_next_byte();
|
|
||||||
result |= get_next_byte() << 8;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
int get_next_word()
|
|
||||||
{
|
|
||||||
int result = get_next_short();
|
|
||||||
result |= get_next_short() << 8;
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_error_flag()
|
|
||||||
{
|
|
||||||
_error_flag = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool get_error_flag()
|
|
||||||
{
|
|
||||||
return _error_flag;
|
|
||||||
}
|
|
||||||
|
|
||||||
void reset_crc()
|
|
||||||
{
|
|
||||||
_crc = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t get_crc()
|
|
||||||
{
|
|
||||||
return _crc;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool is_at_end()
|
|
||||||
{
|
|
||||||
return _tape->is_at_end();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
virtual std::unique_ptr<WaveType> get_wave_type_for_pulse(Storage::Tape::Tape::Pulse) = 0;
|
||||||
|
virtual std::unique_ptr<SymbolType> dequeue_next_symbol(std::deque<WaveType> _wave_queue) = 0;
|
||||||
|
std::deque<WaveType> _wave_queue;
|
||||||
|
std::shared_ptr<Storage::Tape::Tape> _tape;
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class WaveType {
|
||||||
|
Short, Long, Unrecognised
|
||||||
|
};
|
||||||
|
|
||||||
|
enum class SymbolType {
|
||||||
|
One, Zero
|
||||||
|
};
|
||||||
|
|
||||||
|
class Acorn1200BaudTapeParser: public TapeParser<WaveType, SymbolType> {
|
||||||
|
public:
|
||||||
|
Acorn1200BaudTapeParser(const std::shared_ptr<Storage::Tape::Tape> &tape) : TapeParser(tape) {}
|
||||||
|
|
||||||
|
int get_next_bit()
|
||||||
|
{
|
||||||
|
std::unique_ptr<SymbolType> symbol = get_next_symbol();
|
||||||
|
return (symbol && *symbol == SymbolType::One) ? 1 : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_next_byte()
|
||||||
|
{
|
||||||
|
int value = 0;
|
||||||
|
int c = 8;
|
||||||
|
if(get_next_bit())
|
||||||
|
{
|
||||||
|
_error_flag = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
while(c--)
|
||||||
|
{
|
||||||
|
value = (value >> 1) | (get_next_bit() << 7);
|
||||||
|
}
|
||||||
|
if(!get_next_bit())
|
||||||
|
{
|
||||||
|
_error_flag = true;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
add_to_crc((uint8_t)value);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_next_short()
|
||||||
|
{
|
||||||
|
int result = get_next_byte();
|
||||||
|
result |= get_next_byte() << 8;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
int get_next_word()
|
||||||
|
{
|
||||||
|
int result = get_next_short();
|
||||||
|
result |= get_next_short() << 8;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
void reset_crc() { _crc = 0; }
|
||||||
|
uint16_t get_crc() { return _crc; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<WaveType> get_wave_type_for_pulse(Storage::Tape::Tape::Pulse pulse)
|
||||||
|
{
|
||||||
|
WaveType wave_type = WaveType::Unrecognised;
|
||||||
|
switch(pulse.type)
|
||||||
|
{
|
||||||
|
default: break;
|
||||||
|
case Storage::Tape::Tape::Pulse::High:
|
||||||
|
case Storage::Tape::Tape::Pulse::Low:
|
||||||
|
float pulse_length = pulse.length.get_float();
|
||||||
|
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) wave_type = WaveType::Short;
|
||||||
|
if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) wave_type = WaveType::Long;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::unique_ptr<WaveType>(new WaveType(wave_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::unique_ptr<SymbolType> dequeue_next_symbol(std::deque<WaveType> _wave_queue)
|
||||||
|
{
|
||||||
|
while(_wave_queue.size() && _wave_queue.front() == WaveType::Unrecognised)
|
||||||
|
{
|
||||||
|
_wave_queue.pop_front();
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_wave_queue.size() >= 2 && _wave_queue[0] == WaveType::Long && _wave_queue[1] == WaveType::Long)
|
||||||
|
{
|
||||||
|
_wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+2);
|
||||||
|
return std::unique_ptr<SymbolType>(new SymbolType(SymbolType::Zero));
|
||||||
|
}
|
||||||
|
|
||||||
|
if( _wave_queue.size() >= 4 &&
|
||||||
|
_wave_queue[0] == WaveType::Short &&
|
||||||
|
_wave_queue[1] == WaveType::Short &&
|
||||||
|
_wave_queue[2] == WaveType::Short &&
|
||||||
|
_wave_queue[3] == WaveType::Short)
|
||||||
|
{
|
||||||
|
_wave_queue.erase(_wave_queue.begin(), _wave_queue.begin()+4);
|
||||||
|
return std::unique_ptr<SymbolType>(new SymbolType(SymbolType::One));
|
||||||
|
}
|
||||||
|
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
void add_to_crc(uint8_t value)
|
void add_to_crc(uint8_t value)
|
||||||
{
|
{
|
||||||
_crc ^= (uint16_t)value << 8;
|
_crc ^= (uint16_t)value << 8;
|
||||||
@ -138,21 +167,10 @@ struct TapeParser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void rotate(int places)
|
|
||||||
{
|
|
||||||
_wave_length_pointer -= places;
|
|
||||||
if(places < 4) memmove(_wave_lengths, &_wave_lengths[places], (size_t)(4 - places) * sizeof(float));
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t _crc;
|
uint16_t _crc;
|
||||||
bool _error_flag;
|
|
||||||
|
|
||||||
float _wave_lengths[4];
|
|
||||||
int _wave_length_pointer;
|
|
||||||
std::shared_ptr<Storage::Tape::Tape> _tape;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
static std::unique_ptr<File::Chunk> GetNextChunk(TapeParser &parser)
|
static std::unique_ptr<File::Chunk> GetNextChunk(Acorn1200BaudTapeParser &parser)
|
||||||
{
|
{
|
||||||
std::unique_ptr<File::Chunk> new_chunk(new File::Chunk);
|
std::unique_ptr<File::Chunk> new_chunk(new File::Chunk);
|
||||||
int shift_register = 0;
|
int shift_register = 0;
|
||||||
@ -269,7 +287,7 @@ std::unique_ptr<File> GetNextFile(std::deque<File::Chunk> &chunks)
|
|||||||
|
|
||||||
std::list<File> StaticAnalyser::Acorn::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape)
|
std::list<File> StaticAnalyser::Acorn::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape)
|
||||||
{
|
{
|
||||||
TapeParser parser(tape);
|
Acorn1200BaudTapeParser parser(tape);
|
||||||
|
|
||||||
// populate chunk list
|
// populate chunk list
|
||||||
std::deque<File::Chunk> chunk_list;
|
std::deque<File::Chunk> chunk_list;
|
||||||
|
Loading…
Reference in New Issue
Block a user