mirror of
https://github.com/TomHarte/CLK.git
synced 2025-08-15 14:27:29 +00:00
It's a bit of a mess but this is probably close to appropriate for Oric TAP files.
This commit is contained in:
@@ -42,6 +42,7 @@
|
|||||||
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
|
||||||
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; };
|
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; };
|
||||||
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; };
|
4B55CE5F1C3B7D960093A61B /* MachineDocument.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */; };
|
||||||
|
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B59199A1DAC6C46005BB85C /* OricTAP.cpp */; };
|
||||||
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; };
|
4B643F3A1D77AD1900D431D6 /* CSStaticAnalyser.mm in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */; };
|
||||||
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
|
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
|
||||||
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
|
4B69FB3D1C4D908A00B5F0AA /* Tape.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B69FB3B1C4D908A00B5F0AA /* Tape.cpp */; };
|
||||||
@@ -462,6 +463,8 @@
|
|||||||
4B55CE5B1C3B7D6F0093A61B /* CSOpenGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSOpenGLView.h; sourceTree = "<group>"; };
|
4B55CE5B1C3B7D6F0093A61B /* CSOpenGLView.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CSOpenGLView.h; sourceTree = "<group>"; };
|
||||||
4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSOpenGLView.m; sourceTree = "<group>"; };
|
4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CSOpenGLView.m; sourceTree = "<group>"; };
|
||||||
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineDocument.swift; sourceTree = "<group>"; };
|
4B55CE5E1C3B7D960093A61B /* MachineDocument.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MachineDocument.swift; sourceTree = "<group>"; };
|
||||||
|
4B59199A1DAC6C46005BB85C /* OricTAP.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OricTAP.cpp; sourceTree = "<group>"; };
|
||||||
|
4B59199B1DAC6C46005BB85C /* OricTAP.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = OricTAP.hpp; sourceTree = "<group>"; };
|
||||||
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
|
4B643F381D77AD1900D431D6 /* CSStaticAnalyser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = CSStaticAnalyser.h; path = StaticAnalyser/CSStaticAnalyser.h; sourceTree = "<group>"; };
|
||||||
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
|
4B643F391D77AD1900D431D6 /* CSStaticAnalyser.mm */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.objcpp; name = CSStaticAnalyser.mm; path = StaticAnalyser/CSStaticAnalyser.mm; sourceTree = "<group>"; };
|
||||||
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
|
4B643F3C1D77AE5C00D431D6 /* CSMachine+Target.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "CSMachine+Target.h"; sourceTree = "<group>"; };
|
||||||
@@ -1138,6 +1141,8 @@
|
|||||||
4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */,
|
4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */,
|
||||||
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */,
|
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */,
|
||||||
4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */,
|
4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */,
|
||||||
|
4B59199A1DAC6C46005BB85C /* OricTAP.cpp */,
|
||||||
|
4B59199B1DAC6C46005BB85C /* OricTAP.hpp */,
|
||||||
);
|
);
|
||||||
path = Formats;
|
path = Formats;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2160,6 +2165,7 @@
|
|||||||
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */,
|
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */,
|
||||||
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
|
||||||
4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */,
|
4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */,
|
||||||
|
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
|
||||||
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
|
||||||
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
|
||||||
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
|
||||||
|
@@ -94,7 +94,7 @@
|
|||||||
<string>tap</string>
|
<string>tap</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CFBundleTypeName</key>
|
<key>CFBundleTypeName</key>
|
||||||
<string>Commodore Tape Image</string>
|
<string>Tape Image</string>
|
||||||
<key>CFBundleTypeRole</key>
|
<key>CFBundleTypeRole</key>
|
||||||
<string>Viewer</string>
|
<string>Viewer</string>
|
||||||
<key>LSTypeIsPackage</key>
|
<key>LSTypeIsPackage</key>
|
||||||
|
@@ -41,9 +41,8 @@
|
|||||||
case StaticAnalyser::Target::Electron: return @"ElectronOptions";
|
case StaticAnalyser::Target::Electron: return @"ElectronOptions";
|
||||||
case StaticAnalyser::Target::Vic20: return @"Vic20Options";
|
case StaticAnalyser::Target::Vic20: return @"Vic20Options";
|
||||||
case StaticAnalyser::Target::Atari2600: return @"Atari2600Options";
|
case StaticAnalyser::Target::Atari2600: return @"Atari2600Options";
|
||||||
|
default: return nil;
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
- (CSMachine *)newMachine
|
- (CSMachine *)newMachine
|
||||||
@@ -53,6 +52,7 @@
|
|||||||
case StaticAnalyser::Target::Electron: return [[CSElectron alloc] init];
|
case StaticAnalyser::Target::Electron: return [[CSElectron alloc] init];
|
||||||
case StaticAnalyser::Target::Vic20: return [[CSVic20 alloc] init];
|
case StaticAnalyser::Target::Vic20: return [[CSVic20 alloc] init];
|
||||||
case StaticAnalyser::Target::Atari2600: return [[CSAtari2600 alloc] init];
|
case StaticAnalyser::Target::Atari2600: return [[CSAtari2600 alloc] init];
|
||||||
|
default: return nil;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -27,6 +27,7 @@
|
|||||||
|
|
||||||
// Tapes
|
// Tapes
|
||||||
#include "../Storage/Tape/Formats/CommodoreTAP.hpp"
|
#include "../Storage/Tape/Formats/CommodoreTAP.hpp"
|
||||||
|
#include "../Storage/Tape/Formats/OricTAP.hpp"
|
||||||
#include "../Storage/Tape/Formats/TapePRG.hpp"
|
#include "../Storage/Tape/Formats/TapePRG.hpp"
|
||||||
#include "../Storage/Tape/Formats/TapeUEF.hpp"
|
#include "../Storage/Tape/Formats/TapeUEF.hpp"
|
||||||
|
|
||||||
@@ -34,7 +35,8 @@ typedef int TargetPlatformType;
|
|||||||
enum class TargetPlatform: TargetPlatformType {
|
enum class TargetPlatform: TargetPlatformType {
|
||||||
Acorn = 1 << 0,
|
Acorn = 1 << 0,
|
||||||
Atari2600 = 1 << 1,
|
Atari2600 = 1 << 1,
|
||||||
Commodore = 1 << 2
|
Commodore = 1 << 2,
|
||||||
|
Oric = 1 << 3
|
||||||
};
|
};
|
||||||
|
|
||||||
using namespace StaticAnalyser;
|
using namespace StaticAnalyser;
|
||||||
@@ -104,7 +106,8 @@ std::list<Target> StaticAnalyser::GetTargets(const char *file_name)
|
|||||||
|
|
||||||
Format("rom", cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM
|
Format("rom", cartridges, Cartridge::BinaryDump, TargetPlatform::Acorn) // ROM
|
||||||
Format("ssd", disks, Disk::SSD, TargetPlatform::Acorn) // SSD
|
Format("ssd", disks, Disk::SSD, TargetPlatform::Acorn) // SSD
|
||||||
Format("tap", tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP
|
Format("tap", tapes, Tape::CommodoreTAP, TargetPlatform::Commodore) // TAP (Commodore)
|
||||||
|
Format("tap", tapes, Tape::OricTAP, TargetPlatform::Oric) // TAP (Oric)
|
||||||
Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
|
Format("uef", tapes, Tape::UEF, TargetPlatform::Acorn) // UEF (tape)
|
||||||
|
|
||||||
#undef Format
|
#undef Format
|
||||||
|
@@ -33,7 +33,8 @@ struct Target {
|
|||||||
enum {
|
enum {
|
||||||
Atari2600,
|
Atari2600,
|
||||||
Electron,
|
Electron,
|
||||||
Vic20
|
Vic20,
|
||||||
|
Oric
|
||||||
} machine;
|
} machine;
|
||||||
float probability;
|
float probability;
|
||||||
|
|
||||||
|
171
Storage/Tape/Formats/OricTAP.cpp
Normal file
171
Storage/Tape/Formats/OricTAP.cpp
Normal file
@@ -0,0 +1,171 @@
|
|||||||
|
//
|
||||||
|
// OricTAP.cpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 10/10/2016.
|
||||||
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#include "OricTAP.hpp"
|
||||||
|
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
using namespace Storage::Tape;
|
||||||
|
|
||||||
|
OricTAP::OricTAP(const char *file_name) : _file(NULL)
|
||||||
|
{
|
||||||
|
struct stat file_stats;
|
||||||
|
stat(file_name, &file_stats);
|
||||||
|
_file_length = (size_t)file_stats.st_size;
|
||||||
|
|
||||||
|
_file = fopen(file_name, "rb");
|
||||||
|
|
||||||
|
if(!_file)
|
||||||
|
throw ErrorNotOricTAP;
|
||||||
|
|
||||||
|
// read and check the file signature
|
||||||
|
uint8_t signature[4];
|
||||||
|
if(fread(signature, 1, 4, _file) != 4)
|
||||||
|
throw ErrorNotOricTAP;
|
||||||
|
|
||||||
|
if(signature[0] != 0x16 || signature[1] != 0x16 || signature[2] != 0x16 || signature[3] != 0x24)
|
||||||
|
throw ErrorNotOricTAP;
|
||||||
|
|
||||||
|
// then rewind and start again
|
||||||
|
virtual_reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
OricTAP::~OricTAP()
|
||||||
|
{
|
||||||
|
if(_file) fclose(_file);
|
||||||
|
}
|
||||||
|
|
||||||
|
void OricTAP::virtual_reset()
|
||||||
|
{
|
||||||
|
fseek(_file, 0, SEEK_SET);
|
||||||
|
_bit_count = 13;
|
||||||
|
_phase = LeadIn;
|
||||||
|
_phase_counter = 0;
|
||||||
|
_pulse_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
Tape::Pulse OricTAP::virtual_get_next_pulse()
|
||||||
|
{
|
||||||
|
// Each byte byte is written as 13 bits: 0, eight bits of data, parity, three 1s.
|
||||||
|
if(_bit_count == 13)
|
||||||
|
{
|
||||||
|
if(_next_phase != _phase)
|
||||||
|
{
|
||||||
|
_phase = _next_phase;
|
||||||
|
_phase_counter = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
_bit_count = 0;
|
||||||
|
uint8_t next_byte = 0;
|
||||||
|
switch(_phase)
|
||||||
|
{
|
||||||
|
case LeadIn:
|
||||||
|
next_byte = 0x16;
|
||||||
|
_phase_counter++;
|
||||||
|
if(_phase_counter == 259) // TODO
|
||||||
|
{
|
||||||
|
_next_phase = Header;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Header:
|
||||||
|
next_byte = (uint8_t)fgetc(_file);
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
if(_phase_counter == 4) _body_length = next_byte;
|
||||||
|
if(_phase_counter == 6) _body_length |= (uint16_t)next_byte << 8;
|
||||||
|
|
||||||
|
_phase_counter++;
|
||||||
|
if(_phase_counter == 10) // TODO
|
||||||
|
{
|
||||||
|
_next_phase = Pause;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Data:
|
||||||
|
next_byte = (uint8_t)fgetc(_file);
|
||||||
|
_phase_counter++;
|
||||||
|
if(_phase_counter == _body_length)
|
||||||
|
{
|
||||||
|
_phase_counter = 0;
|
||||||
|
if((size_t)ftell(_file) == _file_length)
|
||||||
|
{
|
||||||
|
_next_phase = End;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_next_phase = LeadIn;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case Pause:
|
||||||
|
_phase_counter++;
|
||||||
|
if(_phase_counter == 2)
|
||||||
|
{
|
||||||
|
_phase_counter = 0;
|
||||||
|
_next_phase = Data;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case End:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: which way round are bytes streamed?
|
||||||
|
uint8_t parity = next_byte;
|
||||||
|
parity ^= (parity >> 4);
|
||||||
|
parity ^= (parity >> 2);
|
||||||
|
parity ^= (parity >> 1); // TODO: parity odd or even?
|
||||||
|
_current_value = (uint16_t)(((uint16_t)next_byte << 1) | (7 << 10) | ((parity&1) << 9));
|
||||||
|
}
|
||||||
|
|
||||||
|
// In slow mode, a 0 is 4 periods of 1200 Hz, a 1 is 8 periods at 2400 Hz.
|
||||||
|
// In fast mode, a 1 is a single period of 2400 Hz, a 0 is a 2400 Hz pulse followed by a 1200 Hz pulse.
|
||||||
|
// This code models fast mode.
|
||||||
|
Tape::Pulse pulse;
|
||||||
|
pulse.length.clock_rate = 4800;
|
||||||
|
|
||||||
|
switch(_phase)
|
||||||
|
{
|
||||||
|
case Pause:
|
||||||
|
pulse.type = Pulse::High; // TODO
|
||||||
|
pulse.length.length = 20; // TODO
|
||||||
|
_bit_count = 13;
|
||||||
|
return pulse;
|
||||||
|
|
||||||
|
case End:
|
||||||
|
pulse.type = Pulse::Zero;
|
||||||
|
pulse.length.length = 4800;
|
||||||
|
return pulse;
|
||||||
|
|
||||||
|
default:
|
||||||
|
if(_current_value & 1)
|
||||||
|
{
|
||||||
|
pulse.length.length = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
pulse.length.length = _pulse_counter ? 2 : 1;
|
||||||
|
}
|
||||||
|
pulse.type = _pulse_counter ? Pulse::High : Pulse::Low; // TODO
|
||||||
|
|
||||||
|
_pulse_counter ^= 1;
|
||||||
|
if(!_pulse_counter)
|
||||||
|
{
|
||||||
|
_current_value >>= 1;
|
||||||
|
_bit_count++;
|
||||||
|
}
|
||||||
|
return pulse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool OricTAP::is_at_end()
|
||||||
|
{
|
||||||
|
return _phase == End;
|
||||||
|
}
|
59
Storage/Tape/Formats/OricTAP.hpp
Normal file
59
Storage/Tape/Formats/OricTAP.hpp
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
//
|
||||||
|
// OricTAP.hpp
|
||||||
|
// Clock Signal
|
||||||
|
//
|
||||||
|
// Created by Thomas Harte on 10/10/2016.
|
||||||
|
// Copyright © 2016 Thomas Harte. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef OricTAP_hpp
|
||||||
|
#define OricTAP_hpp
|
||||||
|
|
||||||
|
#include "../Tape.hpp"
|
||||||
|
#include <stdint.h>
|
||||||
|
|
||||||
|
namespace Storage {
|
||||||
|
namespace Tape {
|
||||||
|
|
||||||
|
/*!
|
||||||
|
Provides a @c Tape containing an Oric-format tape image, which is a byte stream capture.
|
||||||
|
*/
|
||||||
|
class OricTAP: public Tape {
|
||||||
|
public:
|
||||||
|
/*!
|
||||||
|
Constructs an @c OricTAP containing content from the file with name @c file_name.
|
||||||
|
|
||||||
|
@throws ErrorNotOricTAP if this file could not be opened and recognised as a valid Oric-format TAP.
|
||||||
|
*/
|
||||||
|
OricTAP(const char *file_name);
|
||||||
|
~OricTAP();
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ErrorNotOricTAP
|
||||||
|
};
|
||||||
|
|
||||||
|
// implemented to satisfy @c Tape
|
||||||
|
bool is_at_end();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void virtual_reset();
|
||||||
|
Pulse virtual_get_next_pulse();
|
||||||
|
|
||||||
|
FILE *_file;
|
||||||
|
size_t _file_length;
|
||||||
|
|
||||||
|
uint16_t _current_value;
|
||||||
|
int _bit_count;
|
||||||
|
int _pulse_counter;
|
||||||
|
int _phase_counter;
|
||||||
|
|
||||||
|
enum Phase {
|
||||||
|
LeadIn, Header, Pause, Data, End
|
||||||
|
} _phase, _next_phase;
|
||||||
|
uint16_t _body_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* OricTAP_hpp */
|
Reference in New Issue
Block a user