1
0
mirror of https://github.com/TomHarte/CLK.git synced 2025-01-26 15:32:04 +00:00

Added a header parser for Commodore tapes. No time to grab file bodies now; time to go to work.

This commit is contained in:
Thomas Harte 2016-09-06 08:49:32 -04:00
parent 8901e94f0f
commit bdebc18e0a
5 changed files with 296 additions and 2 deletions

View File

@ -336,6 +336,7 @@
4BC3B74F1CD194CC00F86E85 /* Shader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B74D1CD194CC00F86E85 /* Shader.cpp */; };
4BC3B7521CD1956900F86E85 /* OutputShader.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC3B7501CD1956900F86E85 /* OutputShader.cpp */; };
4BC5E4921D7ED365008CF980 /* CommodoreAnalyser.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */; };
4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */; };
4BC751B21D157E61006C31D9 /* 6522Tests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BC751B11D157E61006C31D9 /* 6522Tests.swift */; };
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */; };
4BC76E6B1C98F43700E6EF73 /* Accelerate.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4BC76E6A1C98F43700E6EF73 /* Accelerate.framework */; };
@ -764,6 +765,8 @@
4BC3B7511CD1956900F86E85 /* OutputShader.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OutputShader.hpp; sourceTree = "<group>"; };
4BC5E4901D7ED365008CF980 /* CommodoreAnalyser.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = CommodoreAnalyser.cpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.cpp; sourceTree = "<group>"; };
4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = CommodoreAnalyser.hpp; path = ../../StaticAnalyser/Commodore/CommodoreAnalyser.hpp; sourceTree = "<group>"; };
4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Utilities.cpp; path = ../../StaticAnalyser/Commodore/Utilities.cpp; sourceTree = "<group>"; };
4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Utilities.hpp; path = ../../StaticAnalyser/Commodore/Utilities.hpp; sourceTree = "<group>"; };
4BC751B11D157E61006C31D9 /* 6522Tests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = 6522Tests.swift; sourceTree = "<group>"; };
4BC76E671C98E31700E6EF73 /* FIRFilter.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = FIRFilter.cpp; sourceTree = "<group>"; };
4BC76E681C98E31700E6EF73 /* FIRFilter.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = FIRFilter.hpp; sourceTree = "<group>"; };
@ -1522,6 +1525,8 @@
4BC5E4911D7ED365008CF980 /* CommodoreAnalyser.hpp */,
4BC830CF1D6E7C690000A26F /* Tape.cpp */,
4BC830D01D6E7C690000A26F /* Tape.hpp */,
4BC5E4931D7EE0E0008CF980 /* Utilities.cpp */,
4BC5E4941D7EE0E0008CF980 /* Utilities.hpp */,
);
name = Commodore;
sourceTree = "<group>";
@ -2024,6 +2029,7 @@
4B2BFC5F1D613E0200BA3AA9 /* TapePRG.cpp in Sources */,
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */,
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */,
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
4BD14B111D74627C0088EAD6 /* AcornAnalyser.cpp in Sources */,
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,

View File

@ -8,8 +8,8 @@
#include "Tape.hpp"
#include <deque>
#include "../TapeParser.hpp"
#include "Utilities.hpp"
using namespace StaticAnalyser::Commodore;
@ -21,12 +21,114 @@ enum class SymbolType {
One, Zero, Word, EndOfBlock, LeadIn
};
struct Header {
enum {
RelocatableProgram,
DataBlock,
NonRelocatableProgram,
SequenceHeader,
EndOfTape,
Unknown
} type;
std::vector<uint8_t> data;
std::wstring name;
std::vector<uint8_t> raw_name;
uint16_t starting_address;
uint16_t ending_address;
bool parity_was_valid;
bool duplicate_matched;
};
class CommodoreROMTapeParser: public StaticAnalyer::TapeParser<WaveType, SymbolType> {
public:
CommodoreROMTapeParser(const std::shared_ptr<Storage::Tape::Tape> &tape) :
TapeParser(tape),
_wave_period(0.0f),
_previous_was_high(false) {}
_previous_was_high(false),
_parity_byte(0) {}
/*!
Advances to the next header block on the tape, then consumes, parses, and returns it.
Returns @c nullptr if any wave-encoding level errors are encountered.
*/
std::unique_ptr<Header> get_next_header()
{
std::unique_ptr<Header> header(new Header);
reset_error_flag();
// find and proceed beyond lead-in tone
proceed_to_symbol(SymbolType::LeadIn);
// look for landing zone
proceed_to_landing_zone(true);
reset_parity_byte();
// get header type
uint8_t header_type = get_next_byte();
switch(header_type)
{
default: header->type = Header::Unknown; break;
case 0x01: header->type = Header::RelocatableProgram; break;
case 0x02: header->type = Header::DataBlock; break;
case 0x03: header->type = Header::NonRelocatableProgram; break;
case 0x04: header->type = Header::SequenceHeader; break;
case 0x05: header->type = Header::EndOfTape; break;
}
// grab rest of data
header->data.reserve(191);
for(size_t c = 0; c < 191; c++)
{
header->data.push_back(get_next_byte());
}
uint8_t parity_byte = get_parity_byte();
header->parity_was_valid = get_next_byte() == parity_byte;
// check that the duplicate matches
proceed_to_landing_zone(false);
header->duplicate_matched = true;
if(get_next_byte() != header_type) header->duplicate_matched = false;
for(size_t c = 0; c < 191; c++)
{
if(header->data[c] != get_next_byte()) header->duplicate_matched = false;
}
if(get_next_byte() != parity_byte) header->duplicate_matched = false;
// parse if this is not pure data
if(header->type != Header::DataBlock)
{
header->starting_address = (uint16_t)(header->data[0] | (header->data[1] << 8));
header->ending_address = (uint16_t)(header->data[2] | (header->data[3] << 8));
for(size_t c = 0; c < 16; c++)
{
header->raw_name.push_back(header->data[4 + c]);
}
header->name = petscii_from_bytes(&header->raw_name[0], 16, false);
}
if(get_error_flag()) return nullptr;
return header;
}
std::unique_ptr<std::vector<uint8_t>> get_next_data()
{
std::unique_ptr<std::vector<uint8_t>> data(new std::vector<uint8_t>);
// find and proceed beyond lead-in tone to the next landing zone
proceed_to_symbol(SymbolType::LeadIn);
proceed_to_landing_zone(true);
// accumulate until the next lead-in tone is hit
// while(!is_at_end())
// {
// data->push_back(get_next_byte());
// }
return data;
}
void spin()
{
@ -45,6 +147,100 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser<WaveType, SymbolT
}
private:
/*!
Finds and completes the next landing zone.
*/
void proceed_to_landing_zone(bool is_original)
{
uint8_t landing_zone[9] = {0, 0, 0, 0, 0, 0, 0, 0, 0};
while(!is_at_end())
{
memmove(landing_zone, &landing_zone[1], sizeof(uint8_t) * 8);
landing_zone[8] = get_next_byte();
bool is_landing_zone = true;
for(int c = 0; c < 9; c++)
{
if(landing_zone[c] != ((is_original ? 0x80 : 0x00) | 0x9) - c)
{
is_landing_zone = false;
break;
}
}
if(is_landing_zone) break;
}
}
/*!
Swallows symbols until it reaches the first instance of the required symbol, swallows that
and returns.
*/
void proceed_to_symbol(SymbolType required_symbol)
{
while(!is_at_end())
{
SymbolType symbol = get_next_symbol();
if(symbol == required_symbol) return;
}
}
/*!
Swallows the next byte; sets the error flag if it is not equal to @c value.
*/
void expect_byte(uint8_t value)
{
uint8_t next_byte = get_next_byte();
if(next_byte != value) _error_flag = true;
}
uint8_t _parity_byte;
void reset_parity_byte() { _parity_byte = 0; }
uint8_t get_parity_byte() { return _parity_byte; }
void add_parity_byte(uint8_t byte) { _parity_byte ^= byte; }
/*!
Proceeds to the next word marker then reads the nine symbols following it. Applies a binary
test to each to differentiate between ::One and not-::One. Returns a byte composed of the first
eight of those as bits; sets the error flag if any symbol is not ::One and not ::Zero or if
the ninth bit is not equal to the odd parity of the other eight.
*/
uint8_t get_next_byte()
{
int byte_plus_parity = 0;
proceed_to_symbol(SymbolType::Word);
int c = 9;
while(c--)
{
SymbolType next_symbol = get_next_symbol();
if((next_symbol != SymbolType::One) && (next_symbol != SymbolType::Zero)) _error_flag = true;
byte_plus_parity = (byte_plus_parity >> 1) | (((next_symbol == SymbolType::One) ? 1 : 0) << 8);
}
int check = byte_plus_parity;
check ^= (check >> 4);
check ^= (check >> 2);
check ^= (check >> 1);
if((check&1) == (byte_plus_parity >> 8)) _error_flag = true;
add_parity_byte((uint8_t)byte_plus_parity);
return (uint8_t)byte_plus_parity;
}
/*!
Returns the result of two consecutive @c get_next_byte calls, arranged in little-endian format.
*/
uint16_t get_next_short()
{
uint16_t value = get_next_byte();
value |= get_next_byte() << 8;
return value;
}
/*!
Per the contract with StaticAnalyser::TapeParser; sums time across pulses. If this pulse
indicates a high to low transition, inspects the time since the last transition, to produce
a long, medium, short or unrecognised wave period.
*/
void process_pulse(Storage::Tape::Tape::Pulse pulse)
{
bool is_high = pulse.type == Storage::Tape::Tape::Pulse::High;
@ -64,6 +260,10 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser<WaveType, SymbolT
bool _previous_was_high;
float _wave_period;
/*!
Per the contract with StaticAnalyser::TapeParser; produces any of a word marker, an end-of-block marker,
a zero, a one or a lead-in symbol based on the currently captured waves.
*/
void inspect_waves(const std::vector<WaveType> &waves)
{
if(waves.size() < 2) return;
@ -106,6 +306,7 @@ class CommodoreROMTapeParser: public StaticAnalyer::TapeParser<WaveType, SymbolT
std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
CommodoreROMTapeParser parser(tape);
parser.get_next_header();
parser.spin();
std::list<File> file_list;

View File

@ -16,6 +16,8 @@ namespace StaticAnalyser {
namespace Commodore {
struct File {
std::wstring name;
std::vector<uint8_t> raw_name;
uint16_t starting_address;
uint16_t ending_address;
enum {

View File

@ -0,0 +1,63 @@
//
// Utilities.cpp
// Clock Signal
//
// Created by Thomas Harte on 06/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Utilities.hpp"
std::wstring StaticAnalyser::Commodore::petscii_from_bytes(const uint8_t *string, int length, bool shifted)
{
std::wstring result;
wchar_t unshifted_characters[256] =
{
L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0',
L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0',
L' ', L'!', L'"', L'#', L'$', L'%', L'&', L'\'', L'(', L')', L'*', L'+', L',', L'-', L'.', L'/',
L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'"', L';', L'<', L'=', L'>', L'?',
L'@', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O',
L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'[', L'£', L']', L'', L'',
L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'', L'', L'<EFBFBD>', L'', L'', L'<EFBFBD>',
L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'', L'', L'', L'<EFBFBD>', L'', L'', L'<EFBFBD>', L'', L'π', L'',
L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0',
L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0',
L' ', L'', L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'', L'', L'<EFBFBD>', L'', L'', L'<EFBFBD>',
L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'', L'', L'', L'<EFBFBD>', L'', L'', L'<EFBFBD>', L'', L'π', L'',
L' ', L'', L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'<EFBFBD>', L'', L'', L'', L'', L'π',
};
wchar_t shifted_characters[256] =
{
L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0',
L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0',
L' ', L'!', L'"', L'#', L'$', L'%', L'&', L'\'', L'(', L')', L'*', L'+', L',', L'-', L'.', L'/',
L'0', L'1', L'2', L'3', L'4', L'5', L'6', L'7', L'8', L'9', L'"', L';', L'<', L'=', L'>', L'?',
L'@', L'a', L'b', L'c', L'd', L'e', L'f', L'g', L'h', L'i', L'j', L'k', L'l', L'm', L'n', L'o',
L'p', L'q', L'r', L's', L't', L'u', L'v', L'w', L'x', L'y', L'z', L'[', L'£', L']', L'', L'',
L'', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O',
L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'', L'<EFBFBD>', L'', L'', L'',
L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\r', L'\0', L'\0',
L'\0', L'\0', L'\0', L'\0', L'\b', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0', L'\0',
L' ', L'', L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'', L'', L'', L'', L'', L'',
L'', L'A', L'B', L'C', L'D', L'E', L'F', L'G', L'H', L'I', L'J', L'K', L'L', L'M', L'N', L'O',
L'P', L'Q', L'R', L'S', L'T', L'U', L'V', L'W', L'X', L'Y', L'Z', L'', L'<EFBFBD>', L'', L'', L'<EFBFBD>',
L' ', L'', L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'', L'', L'', L'',
L'', L'', L'', L'', L'', L'', L'<EFBFBD>', L'<EFBFBD>', L'<EFBFBD>', L'', L'', L'', L'', L'', L'', L'',
};
wchar_t *table = shifted ? shifted_characters : unshifted_characters;
for(int c = 0; c < length; c++)
{
wchar_t next_character = table[string[c]];
if(next_character) result.push_back(next_character);
}
return result;
}

View File

@ -0,0 +1,22 @@
//
// Utilities.hpp
// Clock Signal
//
// Created by Thomas Harte on 06/09/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#ifndef Analyser_Commodore_Utilities_hpp
#define Analyser_Commodore_Utilities_hpp
#include <string>
namespace StaticAnalyser {
namespace Commodore {
std::wstring petscii_from_bytes(const uint8_t *string, int length, bool shifted);
}
}
#endif /* Utilities_hpp */