diff --git a/StaticAnalyser/Acorn/AcornAnalyser.cpp b/StaticAnalyser/Acorn/AcornAnalyser.cpp index 0803c39a2..ce9fc5d5c 100644 --- a/StaticAnalyser/Acorn/AcornAnalyser.cpp +++ b/StaticAnalyser/Acorn/AcornAnalyser.cpp @@ -84,7 +84,7 @@ void StaticAnalyser::Acorn::AddTargets( bool is_basic = true; // 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, // so that's also justification to *RUN diff --git a/StaticAnalyser/Acorn/Tape.cpp b/StaticAnalyser/Acorn/Tape.cpp index 17e148982..2a814ff2a 100644 --- a/StaticAnalyser/Acorn/Tape.cpp +++ b/StaticAnalyser/Acorn/Tape.cpp @@ -12,122 +12,151 @@ 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 class TapeParser { + public: + TapeParser(const std::shared_ptr &tape) : _tape(tape) {} - TapeParser(const std::shared_ptr &tape) : _wave_length_pointer(0), _tape(tape) {} - - int get_next_bit() - { - while(!_tape->is_at_end()) + std::unique_ptr get_next_symbol() { - // skip any gaps - Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse(); - while(!_tape->is_at_end() && next_pulse.type == Storage::Tape::Tape::Pulse::Zero) + while(!_tape->is_at_end()) { - next_pulse = _tape->get_next_pulse(); - } + std::unique_ptr symbol = dequeue_next_symbol(_wave_queue); + if(symbol) return symbol; - _wave_lengths[_wave_length_pointer] = next_pulse.length.get_float(); - _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) + while(!_tape->is_at_end()) { - rotate(2); - return 0; + Storage::Tape::Tape::Pulse next_pulse = _tape->get_next_pulse(); + std::unique_ptr 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 - 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 nullptr; } - 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() - { - 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(); - } + protected: + bool _error_flag; private: + virtual std::unique_ptr get_wave_type_for_pulse(Storage::Tape::Tape::Pulse) = 0; + virtual std::unique_ptr dequeue_next_symbol(std::deque _wave_queue) = 0; + std::deque _wave_queue; + std::shared_ptr _tape; +}; + +enum class WaveType { + Short, Long, Unrecognised +}; + +enum class SymbolType { + One, Zero +}; + +class Acorn1200BaudTapeParser: public TapeParser { + public: + Acorn1200BaudTapeParser(const std::shared_ptr &tape) : TapeParser(tape) {} + + int get_next_bit() + { + std::unique_ptr 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 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(new WaveType(wave_type)); + } + + std::unique_ptr dequeue_next_symbol(std::deque _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(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(new SymbolType(SymbolType::One)); + } + + return nullptr; + } + void add_to_crc(uint8_t value) { _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; - bool _error_flag; - - float _wave_lengths[4]; - int _wave_length_pointer; - std::shared_ptr _tape; }; -static std::unique_ptr GetNextChunk(TapeParser &parser) +static std::unique_ptr GetNextChunk(Acorn1200BaudTapeParser &parser) { std::unique_ptr new_chunk(new File::Chunk); int shift_register = 0; @@ -269,7 +287,7 @@ std::unique_ptr GetNextFile(std::deque &chunks) std::list StaticAnalyser::Acorn::GetFiles(const std::shared_ptr &tape) { - TapeParser parser(tape); + Acorn1200BaudTapeParser parser(tape); // populate chunk list std::deque chunk_list;