1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-17 13:29:02 +00:00
CLK/Storage/Tape/Parsers/Spectrum.cpp
2021-03-07 21:20:35 -05:00

153 lines
3.8 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// Spectrum.cpp
// Clock Signal
//
// Created by Thomas Harte on 07/03/2021.
// Copyright © 2021 Thomas Harte. All rights reserved.
//
#include "Spectrum.hpp"
#include <cstring>
//
// Source used for the logic below was primarily https://sinclair.wiki.zxnet.co.uk/wiki/Spectrum_tape_interface
//
using namespace Storage::Tape::ZXSpectrum;
void Parser::process_pulse(const Storage::Tape::Tape::Pulse &pulse) {
if(pulse.type == Storage::Tape::Tape::Pulse::Type::Zero) {
push_wave(WaveType::Gap);
return;
}
// Only pulse duration matters; the ZX Spectrum et al do not rely on polarity.
const float t_states = pulse.length.get<float>() * 3'500'000.0f;
// Too long => gap.
if(t_states > 2400.0f) {
push_wave(WaveType::Gap);
return;
}
// 19402400 t-states => pilot.
if(t_states > 1940.0f) {
push_wave(WaveType::Pilot);
return;
}
// 12821940 t-states => one.
if(t_states > 1282.0f) {
push_wave(WaveType::One);
return;
}
// 8951282 => zero.
if(t_states > 795.0f) {
push_wave(WaveType::Zero);
return;
}
// 701895 => sync 2.
if(t_states > 701.0f) {
push_wave(WaveType::Sync2);
return;
}
// Anything remaining above 600 => sync 1.
if(t_states > 600.0f) {
push_wave(WaveType::Sync1);
return;
}
// Whatever this was, it's too short. Call it a gap.
push_wave(WaveType::Gap);
}
void Parser::inspect_waves(const std::vector<Storage::Tape::ZXSpectrum::WaveType> &waves) {
switch(waves[0]) {
// Gap and Pilot map directly.
case WaveType::Gap: push_symbol(SymbolType::Gap, 1); break;
case WaveType::Pilot: push_symbol(SymbolType::Pilot, 1); break;
// Encountering a sync 2 on its own is unexpected.
case WaveType::Sync2:
push_symbol(SymbolType::Gap, 1);
break;
// A sync 1 should be followed by a sync 2 in order to make a sync.
case WaveType::Sync1:
if(waves.size() < 2) return;
if(waves[1] == WaveType::Sync2) {
push_symbol(SymbolType::Sync, 2);
} else {
push_symbol(SymbolType::Gap, 1);
}
break;
// Both one and zero waves should come in pairs.
case WaveType::One:
case WaveType::Zero:
if(waves.size() < 2) return;
if(waves[1] == waves[0]) {
push_symbol(waves[0] == WaveType::One ? SymbolType::One : SymbolType::Zero, 2);
} else {
push_symbol(SymbolType::Gap, 1);
}
break;
}
}
std::optional<Header> Parser::find_header(const std::shared_ptr<Storage::Tape::Tape> &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<uint8_t> Parser::get_byte(const std::shared_ptr<Storage::Tape::Tape> &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;
}