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:
parent
8901e94f0f
commit
bdebc18e0a
@ -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 */,
|
||||
|
@ -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;
|
||||
|
@ -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 {
|
||||
|
63
StaticAnalyser/Commodore/Utilities.cpp
Normal file
63
StaticAnalyser/Commodore/Utilities.cpp
Normal 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;
|
||||
}
|
22
StaticAnalyser/Commodore/Utilities.hpp
Normal file
22
StaticAnalyser/Commodore/Utilities.hpp
Normal 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 */
|
Loading…
x
Reference in New Issue
Block a user