diff --git a/Storage/Tape/Parsers/Spectrum.cpp b/Storage/Tape/Parsers/Spectrum.cpp index 6b1ff4362..a3221cf3e 100644 --- a/Storage/Tape/Parsers/Spectrum.cpp +++ b/Storage/Tape/Parsers/Spectrum.cpp @@ -96,3 +96,55 @@ void Parser::inspect_waves(const std::vector Parser::find_header(const std::shared_ptr &tape) { + // Find pilot tone. + proceed_to_symbol(tape, SymbolType::Pilot); + if(is_at_end(tape)) return std::nullopt; + + // Find sync. + proceed_to_symbol(tape, SymbolType::Sync); + if(is_at_end(tape)) return std::nullopt; + + // Read market byte. + const auto type = get_byte(tape); + if(!type) return std::nullopt; + if(*type != 0x00) return std::nullopt; + reset_checksum(); + + // Read header contents. + uint8_t header_bytes[17]; + for(size_t c = 0; c < sizeof(header_bytes); c++) { + const auto next_byte = get_byte(tape); + if(!next_byte) return std::nullopt; + header_bytes[c] = *next_byte; + } + + // Check checksum. + const auto post_checksum = get_byte(tape); + if(!post_checksum || *post_checksum) return std::nullopt; + + // Unpack and return. + Header header; + header.type = header_bytes[0]; + memcpy(&header.name, &header_bytes[1], 10); + header.data_length = uint16_t(header_bytes[11] | (header_bytes[12] << 8)); + header.parameters[0] = uint16_t(header_bytes[13] | (header_bytes[14] << 8)); + header.parameters[1] = uint16_t(header_bytes[15] | (header_bytes[16] << 8)); + return header; +} + +void Parser::reset_checksum() { + checksum_ = 0; +} + +std::optional Parser::get_byte(const std::shared_ptr &tape) { + uint8_t result = 0; + for(int c = 0; c < 8; c++) { + const SymbolType symbol = get_next_symbol(tape); + if(symbol != SymbolType::One && symbol != SymbolType::Zero) return std::nullopt; + result = uint8_t((result << 1) | (symbol == SymbolType::One)); + } + checksum_ ^= result; + return result; +} diff --git a/Storage/Tape/Parsers/Spectrum.hpp b/Storage/Tape/Parsers/Spectrum.hpp index 817a064d0..aa2c0bb34 100644 --- a/Storage/Tape/Parsers/Spectrum.hpp +++ b/Storage/Tape/Parsers/Spectrum.hpp @@ -11,6 +11,8 @@ #include "TapeParser.hpp" +#include + namespace Storage { namespace Tape { namespace ZXSpectrum { @@ -35,10 +37,84 @@ enum class SymbolType { Gap, }; +struct Header { + uint8_t type = 0; + char name[11]{}; // 10 bytes on tape; always given a NULL terminator in this code. + uint16_t data_length = 0; + uint16_t parameters[2] = {0, 0}; + + enum class Type { + Program = 0, + NumberArray = 1, + CharacterArray = 2, + Code = 3, + Unknown + }; + Type decoded_type() { + if(type > 3) return Type::Unknown; + return Type(type); + } + + struct BasicParameters { + std::optional autostart_line_number; + uint16_t start_of_variable_area; + }; + BasicParameters basic_parameters() { + const BasicParameters params = { + .autostart_line_number = parameters[0] < 32768 ? std::make_optional(parameters[0]) : std::nullopt, + .start_of_variable_area = parameters[1] + }; + return params; + } + + struct CodeParameters { + uint16_t start_address; + }; + CodeParameters code_parameters() { + const CodeParameters params = { + .start_address = parameters[0] + }; + return params; + } + + struct DataParameters { + char name; + enum class Type { + Numeric, + String + } type; + }; + DataParameters data_parameters() { + #if TARGET_RT_BIG_ENDIAN + const uint8_t data_name = uint8_t(parameters[0]); + #else + const uint8_t data_name = uint8_t(parameters[0] >> 8); + #endif + + using Type = DataParameters::Type; + const DataParameters params = { + .name = char((data_name & 0x1f) + 'a'), + .type = (data_name & 0x40) ? Type::String : Type::Numeric + }; + return params; + } +}; + class Parser: public Storage::Tape::PulseClassificationParser { + public: + /*! + Finds the next header from the tape, if any. + */ + std::optional
find_header(const std::shared_ptr &tape); + + void reset_checksum(); + std::optional get_byte(const std::shared_ptr &tape); + private: void process_pulse(const Storage::Tape::Tape::Pulse &pulse) override; void inspect_waves(const std::vector &waves) override; + + uint8_t checksum_ = 0; }; }