1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-07-05 10:28:58 +00:00

Merge pull request #148 from TomHarte/CSW

Adds support for the CSW file format
This commit is contained in:
Thomas Harte 2017-07-15 15:33:05 -04:00 committed by GitHub
commit 6ca712b498
15 changed files with 266 additions and 39 deletions

View File

@ -78,8 +78,11 @@ void Tape::process_input_pulse(Storage::Tape::Tape::Pulse pulse) {
crossings_[3] = Tape::Unrecognised;
if(pulse.type != Storage::Tape::Tape::Pulse::Zero) {
float pulse_length = (float)pulse.length.length / (float)pulse.length.clock_rate;
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) crossings_[3] = Tape::Short;
if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) crossings_[3] = Tape::Long;
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 1200.0) {
crossings_[3] = pulse_length > 1.0 / 3000.0 ? Tape::Long : Tape::Short;
}
// if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 2400.0) crossings_[3] = Tape::Short;
// if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) crossings_[3] = Tape::Long;
}
if(crossings_[0] == Tape::Long && crossings_[1] == Tape::Long) {

View File

@ -51,6 +51,7 @@
4B3BA0CF1D318B44005DD7A7 /* MOS6522Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0C91D318B44005DD7A7 /* MOS6522Bridge.mm */; };
4B3BA0D01D318B44005DD7A7 /* MOS6532Bridge.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */; };
4B3BA0D11D318B44005DD7A7 /* TestMachine6502.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */; };
4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3BF5AE1F146264005B6C36 /* CSW.cpp */; };
4B3F1B461E0388D200DB26EE /* PCMPatchedTrack.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */; };
4B44EBF51DC987AF00A7820C /* AllSuiteA.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */; };
4B44EBF71DC9883B00A7820C /* 6502_functional_test.bin in Resources */ = {isa = PBXBuildFile; fileRef = 4B44EBF61DC9883B00A7820C /* 6502_functional_test.bin */; };
@ -539,6 +540,8 @@
4B3BA0CB1D318B44005DD7A7 /* MOS6532Bridge.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = MOS6532Bridge.mm; sourceTree = "<group>"; };
4B3BA0CC1D318B44005DD7A7 /* TestMachine6502.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = TestMachine6502.h; sourceTree = "<group>"; };
4B3BA0CD1D318B44005DD7A7 /* TestMachine6502.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; path = TestMachine6502.mm; sourceTree = "<group>"; };
4B3BF5AE1F146264005B6C36 /* CSW.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = CSW.cpp; sourceTree = "<group>"; };
4B3BF5AF1F146264005B6C36 /* CSW.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = CSW.hpp; sourceTree = "<group>"; };
4B3F1B441E0388D200DB26EE /* PCMPatchedTrack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = PCMPatchedTrack.cpp; sourceTree = "<group>"; };
4B3F1B451E0388D200DB26EE /* PCMPatchedTrack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = PCMPatchedTrack.hpp; sourceTree = "<group>"; };
4B44EBF41DC987AE00A7820C /* AllSuiteA.bin */ = {isa = PBXFileReference; lastKnownFileType = archive.macbinary; name = AllSuiteA.bin; path = AllSuiteA/AllSuiteA.bin; sourceTree = "<group>"; };
@ -1394,17 +1397,19 @@
4B69FB411C4D941400B5F0AA /* Formats */ = {
isa = PBXGroup;
children = (
4B69FB451C4D950F00B5F0AA /* libz.tbd */,
4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */,
4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */,
4BC91B811D1F160E00884B76 /* CommodoreTAP.cpp */,
4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */,
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */,
4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */,
4B3BF5AE1F146264005B6C36 /* CSW.cpp */,
4B59199A1DAC6C46005BB85C /* OricTAP.cpp */,
4B59199B1DAC6C46005BB85C /* OricTAP.hpp */,
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */,
4B69FB421C4D941400B5F0AA /* TapeUEF.cpp */,
4B1497861EE4A1DA00CE2596 /* ZX80O81P.cpp */,
4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */,
4B3BF5AF1F146264005B6C36 /* CSW.hpp */,
4B59199B1DAC6C46005BB85C /* OricTAP.hpp */,
4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */,
4B69FB431C4D941400B5F0AA /* TapeUEF.hpp */,
4B1497871EE4A1DA00CE2596 /* ZX80O81P.hpp */,
4B69FB451C4D950F00B5F0AA /* libz.tbd */,
);
path = Formats;
sourceTree = "<group>";
@ -2593,6 +2598,7 @@
4BD69F941D98760000243FE1 /* AcornADF.cpp in Sources */,
4BBF99181C8FBA6F0075DAFB /* TextureTarget.cpp in Sources */,
4BC76E691C98E31700E6EF73 /* FIRFilter.cpp in Sources */,
4B3BF5B01F146265005B6C36 /* CSW.cpp in Sources */,
4B2A332A1DB8544D002876E3 /* MemoryFuzzer.cpp in Sources */,
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */,
4B2A332F1DB86869002876E3 /* OricOptionsPanel.swift in Sources */,

View File

@ -141,6 +141,8 @@
<string>Electron/BBC Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
@ -155,6 +157,8 @@
<string>Disk Image</string>
<key>CFBundleTypeRole</key>
<string>Editor</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
@ -170,6 +174,8 @@
<string>ZX80 Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
@ -185,6 +191,24 @@
<string>ZX81 Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>csw</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>cassette</string>
<key>CFBundleTypeName</key>
<string>Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>
<integer>0</integer>
<key>NSDocumentClass</key>
<string>$(PRODUCT_MODULE_NAME).MachineDocument</string>
</dict>

View File

@ -75,6 +75,7 @@ void StaticAnalyser::Acorn::AddTargets(
if(tapes.size() > 0) {
std::shared_ptr<Storage::Tape::Tape> tape = tapes.front();
std::list<File> files = GetFiles(tape);
tape->reset();
// continue if there are any files
if(files.size()) {

View File

@ -60,6 +60,8 @@ static std::unique_ptr<File::Chunk> GetNextChunk(const std::shared_ptr<Storage::
stored_header_crc = (uint16_t)((stored_header_crc >> 8) | (stored_header_crc << 8));
new_chunk->header_crc_matched = stored_header_crc == calculated_header_crc;
if(!new_chunk->header_crc_matched) return nullptr;
parser.reset_crc();
new_chunk->data.reserve(new_chunk->block_length);
for(int c = 0; c < new_chunk->block_length; c++) {

View File

@ -68,6 +68,7 @@ void StaticAnalyser::Commodore::AddTargets(
// check tapes
for(auto &tape : tapes) {
std::list<File> tape_files = GetFiles(tape);
tape->reset();
if(tape_files.size()) {
files.splice(files.end(), tape_files);
target.tapes = tapes;

View File

@ -84,8 +84,9 @@ void StaticAnalyser::Oric::AddTargets(
int basic10_votes = 0;
int basic11_votes = 0;
for(auto tape : tapes) {
for(auto &tape : tapes) {
std::list<File> tape_files = GetFiles(tape);
tape->reset();
if(tape_files.size()) {
for(auto file : tape_files) {
if(file.data_type == File::MachineCode) {

View File

@ -30,6 +30,7 @@
// Tapes
#include "../Storage/Tape/Formats/CommodoreTAP.hpp"
#include "../Storage/Tape/Formats/CSW.hpp"
#include "../Storage/Tape/Formats/OricTAP.hpp"
#include "../Storage/Tape/Formats/TapePRG.hpp"
#include "../Storage/Tape/Formats/TapeUEF.hpp"
@ -42,6 +43,8 @@ enum class TargetPlatform: TargetPlatformType {
Commodore = 1 << 2,
Oric = 1 << 3,
ZX8081 = 1 << 4,
AllTape = Acorn | Commodore | Oric | ZX8081,
};
using namespace StaticAnalyser;
@ -94,6 +97,7 @@ std::list<Target> StaticAnalyser::GetTargets(const char *file_name)
Format("a26", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // A26
Format("adf", disks, Disk::AcornADF, TargetPlatform::Acorn) // ADF
Format("bin", cartridges, Cartridge::BinaryDump, TargetPlatform::Atari2600) // BIN
Format("csw", tapes, Tape::CSW, TargetPlatform::AllTape) // CSW
Format("d64", disks, Disk::D64, TargetPlatform::Commodore) // D64
Format("dsd", disks, Disk::SSD, TargetPlatform::Acorn) // DSD
Format("dsk", disks, Disk::OricMFMDSK, TargetPlatform::Oric) // DSK

View File

@ -35,6 +35,7 @@ void StaticAnalyser::ZX8081::AddTargets(
if(!tapes.empty()) {
std::vector<Storage::Data::ZX8081::File> files = GetFiles(tapes.front());
tapes.front()->reset();
if(!files.empty()) {
StaticAnalyser::Target target;
target.machine = Target::ZX8081;

View File

@ -51,7 +51,7 @@ static std::shared_ptr<File> ZX81FileFromData(const std::vector<uint8_t> &data)
// Look for a file name.
size_t data_pointer = 0;
int c = 11;
while(c--) {
while(c < data.size() && c--) {
if(data[data_pointer] & 0x80) break;
data_pointer++;
}

View File

@ -0,0 +1,130 @@
//
// CSW.cpp
// Clock Signal
//
// Created by Thomas Harte on 10/07/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#include "CSW.hpp"
using namespace Storage::Tape;
CSW::CSW(const char *file_name) :
Storage::FileHolder(file_name),
source_data_pointer_(0) {
if(file_stats_.st_size < 0x20) throw ErrorNotCSW;
// Check signature.
char identifier[22];
char signature[] = "Compressed Square Wave";
fread(identifier, 1, 22, file_);
if(memcmp(identifier, signature, strlen(signature))) throw ErrorNotCSW;
// Check terminating byte.
if(fgetc(file_) != 0x1a) throw ErrorNotCSW;
// Get version file number.
uint8_t major_version = (uint8_t)fgetc(file_);
uint8_t minor_version = (uint8_t)fgetc(file_);
// Reject if this is an unknown version.
if(major_version > 2 || !major_version || minor_version > 1) throw ErrorNotCSW;
// The header now diverges based on version.
uint32_t number_of_waves = 0;
if(major_version == 1) {
pulse_.length.clock_rate = fgetc16le();
if(fgetc(file_) != 1) throw ErrorNotCSW;
compression_type_ = RLE;
pulse_.type = (fgetc(file_) & 1) ? Pulse::High : Pulse::Low;
fseek(file_, 0x20, SEEK_SET);
} else {
pulse_.length.clock_rate = fgetc32le();
number_of_waves = fgetc32le();
switch(fgetc(file_)) {
case 1: compression_type_ = RLE; break;
case 2: compression_type_ = ZRLE; break;
default: throw ErrorNotCSW;
}
pulse_.type = (fgetc(file_) & 1) ? Pulse::High : Pulse::Low;
uint8_t extension_length = (uint8_t)fgetc(file_);
if(file_stats_.st_size < 0x34 + extension_length) throw ErrorNotCSW;
fseek(file_, 0x34 + extension_length, SEEK_SET);
}
if(compression_type_ == ZRLE) {
source_data_.resize((size_t)number_of_waves);
std::vector<uint8_t> file_data;
size_t remaining_data = (size_t)file_stats_.st_size - (size_t)ftell(file_);
file_data.resize(remaining_data);
fread(file_data.data(), sizeof(uint8_t), remaining_data, file_);
uLongf output_length = (uLongf)number_of_waves;
uncompress(source_data_.data(), &output_length, file_data.data(), file_data.size());
source_data_.resize((size_t)output_length);
} else {
rle_start_ = ftell(file_);
}
invert_pulse();
}
uint8_t CSW::get_next_byte() {
switch(compression_type_) {
case RLE: return (uint8_t)fgetc(file_);
case ZRLE: {
if(source_data_pointer_ == source_data_.size()) return 0xff;
uint8_t result = source_data_[source_data_pointer_];
source_data_pointer_++;
return result;
}
}
}
uint32_t CSW::get_next_int32le() {
switch(compression_type_) {
case RLE: return fgetc32le();
case ZRLE: {
if(source_data_pointer_ > source_data_.size() - 4) return 0xffff;
uint32_t result = (uint32_t)(
(source_data_[source_data_pointer_ + 0] << 0) |
(source_data_[source_data_pointer_ + 1] << 8) |
(source_data_[source_data_pointer_ + 2] << 16) |
(source_data_[source_data_pointer_ + 3] << 24));
source_data_pointer_ += 4;
return result;
}
}
}
void CSW::invert_pulse() {
pulse_.type = (pulse_.type == Pulse::High) ? Pulse::Low : Pulse::High;
}
bool CSW::is_at_end() {
switch(compression_type_) {
case RLE: return (bool)feof(file_);
case ZRLE: return source_data_pointer_ == source_data_.size();
}
}
void CSW::virtual_reset() {
switch(compression_type_) {
case RLE: fseek(file_, rle_start_, SEEK_SET); break;
case ZRLE: source_data_pointer_ = 0; break;
}
}
Tape::Pulse CSW::virtual_get_next_pulse() {
invert_pulse();
pulse_.length.length = get_next_byte();
if(!pulse_.length.length) pulse_.length.length = get_next_int32le();
return pulse_;
}

View File

@ -0,0 +1,63 @@
//
// CSW.hpp
// Clock Signal
//
// Created by Thomas Harte on 10/07/2017.
// Copyright © 2017 Thomas Harte. All rights reserved.
//
#ifndef CSW_hpp
#define CSW_hpp
#include "../Tape.hpp"
#include "../../FileHolder.hpp"
#include <zlib.h>
#include <vector>
namespace Storage {
namespace Tape {
/*!
Provides a @c Tape containing a CSW tape image, which is a compressed 1-bit sampling.
*/
class CSW: public Tape, public Storage::FileHolder {
public:
/*!
Constructs a @c CSW containing content from the file with name @c file_name.
@throws ErrorNotCSW if this file could not be opened and recognised as a valid CSW file.
*/
CSW(const char *file_name);
enum {
ErrorNotCSW
};
// implemented to satisfy @c Tape
bool is_at_end();
private:
void virtual_reset();
Pulse virtual_get_next_pulse();
Pulse pulse_;
enum CompressionType {
RLE,
ZRLE
} compression_type_;
uint8_t get_next_byte();
uint32_t get_next_int32le();
void invert_pulse();
std::vector<uint8_t> source_data_;
size_t source_data_pointer_;
long rle_start_;
};
}
}
#endif /* CSW_hpp */

View File

@ -24,9 +24,9 @@ namespace Tape {
class ZX80O81P: public Tape, public Storage::FileHolder {
public:
/*!
Constructs an @c ZX80O containing content from the file with name @c file_name.
Constructs a @c ZX80O containing content from the file with name @c file_name.
@throws ErrorNotZX80O if this file could not be opened and recognised as a valid ZX80-format .O.
@throws ErrorNotZX80O81P if this file could not be opened and recognised as a valid ZX80-format .O.
*/
ZX80O81P(const char *file_name);

View File

@ -14,27 +14,22 @@ Parser::Parser() :
::Storage::Tape::Parser<WaveType, SymbolType>(),
crc_(0x1021, 0x0000) {}
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int Parser::get_next_bit(const std::shared_ptr<Storage::Tape::Tape> &tape) {
SymbolType symbol = get_next_symbol(tape);
return (symbol == SymbolType::One) ? 1 : 0;
}
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape) {
int value = 0;
int c = 8;
if(get_next_bit(tape))
{
if(get_next_bit(tape)) {
set_error_flag();
return -1;
}
while(c--)
{
while(c--) {
value = (value >> 1) | (get_next_bit(tape) << 7);
}
if(!get_next_bit(tape))
{
if(!get_next_bit(tape)) {
set_error_flag();
return -1;
}
@ -42,15 +37,13 @@ int Parser::get_next_byte(const std::shared_ptr<Storage::Tape::Tape> &tape)
return value;
}
int Parser::get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int Parser::get_next_short(const std::shared_ptr<Storage::Tape::Tape> &tape) {
int result = get_next_byte(tape);
result |= get_next_byte(tape) << 8;
return result;
}
int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape)
{
int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape) {
int result = get_next_short(tape);
result |= get_next_short(tape) << 8;
return result;
@ -59,28 +52,25 @@ int Parser::get_next_word(const std::shared_ptr<Storage::Tape::Tape> &tape)
void Parser::reset_crc() { crc_.reset(); }
uint16_t Parser::get_crc() { return crc_.get_value(); }
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse)
{
switch(pulse.type)
{
void Parser::process_pulse(Storage::Tape::Tape::Pulse pulse) {
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) { push_wave(WaveType::Short); return; }
if(pulse_length >= 0.35 / 1200.0 && pulse_length < 0.7 / 1200.0) { push_wave(WaveType::Long); return; }
if(pulse_length >= 0.35 / 2400.0 && pulse_length < 0.7 / 1200.0) {
push_wave(pulse_length > 1.0 / 3000.0 ? WaveType::Long : WaveType::Short); return;
}
break;
}
push_wave(WaveType::Unrecognised);
}
void Parser::inspect_waves(const std::vector<WaveType> &waves)
{
void Parser::inspect_waves(const std::vector<WaveType> &waves) {
if(waves.size() < 2) return;
if(waves[0] == WaveType::Long && waves[1] == WaveType::Long)
{
if(waves[0] == WaveType::Long && waves[1] == WaveType::Long) {
push_symbol(SymbolType::Zero, 2);
return;
}
@ -90,8 +80,7 @@ void Parser::inspect_waves(const std::vector<WaveType> &waves)
if( waves[0] == WaveType::Short &&
waves[1] == WaveType::Short &&
waves[2] == WaveType::Short &&
waves[3] == WaveType::Short)
{
waves[3] == WaveType::Short) {
push_symbol(SymbolType::One, 4);
return;
}

View File

@ -58,6 +58,8 @@ class Tape {
/// Advances or reverses the tape to the last time before or at @c time from which a pulse starts.
virtual void seek(Time &time);
virtual ~Tape() {};
private:
Time current_time_, next_time_;