1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-11-26 23:52:26 +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:
Thomas Harte 2016-10-11 07:39:48 -04:00
parent 00e3ad9b04
commit df01c78039
7 changed files with 246 additions and 6 deletions

View File

@ -42,6 +42,7 @@
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4B4DC8291D2C27A4003C5BF8 /* SerialBus.cpp */; };
4B55CE5D1C3B7D6F0093A61B /* CSOpenGLView.m in Sources */ = {isa = PBXBuildFile; fileRef = 4B55CE5C1C3B7D6F0093A61B /* CSOpenGLView.m */; };
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 */; };
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4B643F3E1D77B88000D431D6 /* DocumentController.swift */; };
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>"; };
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>"; };
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>"; };
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>"; };
@ -1138,6 +1141,8 @@
4BC91B821D1F160E00884B76 /* CommodoreTAP.hpp */,
4B2BFC5D1D613E0200BA3AA9 /* TapePRG.cpp */,
4B2BFC5E1D613E0200BA3AA9 /* TapePRG.hpp */,
4B59199A1DAC6C46005BB85C /* OricTAP.cpp */,
4B59199B1DAC6C46005BB85C /* OricTAP.hpp */,
);
path = Formats;
sourceTree = "<group>";
@ -2160,6 +2165,7 @@
4BAB62AD1D3272D200DF5BA0 /* Disk.cpp in Sources */,
4BC9DF4F1D04691600F44158 /* 6560.cpp in Sources */,
4BC5E4951D7EE0E0008CF980 /* Utilities.cpp in Sources */,
4B59199C1DAC6C46005BB85C /* OricTAP.cpp in Sources */,
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */,
4BD14B111D74627C0088EAD6 /* StaticAnalyser.cpp in Sources */,
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,

View File

@ -94,7 +94,7 @@
<string>tap</string>
</array>
<key>CFBundleTypeName</key>
<string>Commodore Tape Image</string>
<string>Tape Image</string>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>LSTypeIsPackage</key>

View File

@ -41,9 +41,8 @@
case StaticAnalyser::Target::Electron: return @"ElectronOptions";
case StaticAnalyser::Target::Vic20: return @"Vic20Options";
case StaticAnalyser::Target::Atari2600: return @"Atari2600Options";
default: return nil;
}
return nil;
}
- (CSMachine *)newMachine
@ -53,6 +52,7 @@
case StaticAnalyser::Target::Electron: return [[CSElectron alloc] init];
case StaticAnalyser::Target::Vic20: return [[CSVic20 alloc] init];
case StaticAnalyser::Target::Atari2600: return [[CSAtari2600 alloc] init];
default: return nil;
}
}

View File

@ -27,6 +27,7 @@
// Tapes
#include "../Storage/Tape/Formats/CommodoreTAP.hpp"
#include "../Storage/Tape/Formats/OricTAP.hpp"
#include "../Storage/Tape/Formats/TapePRG.hpp"
#include "../Storage/Tape/Formats/TapeUEF.hpp"
@ -34,7 +35,8 @@ typedef int TargetPlatformType;
enum class TargetPlatform: TargetPlatformType {
Acorn = 1 << 0,
Atari2600 = 1 << 1,
Commodore = 1 << 2
Commodore = 1 << 2,
Oric = 1 << 3
};
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("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)
#undef Format

View File

@ -33,7 +33,8 @@ struct Target {
enum {
Atari2600,
Electron,
Vic20
Vic20,
Oric
} machine;
float probability;

View 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;
}

View 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 */