mirror of
https://github.com/TomHarte/CLK.git
synced 2025-02-08 11:32:02 +00:00
Some data is marginally reaching the CPU from the tape.
This commit is contained in:
parent
832797182f
commit
e65cd4cf06
@ -23,7 +23,8 @@ Machine::Machine() :
|
|||||||
_displayOutputPosition(0),
|
_displayOutputPosition(0),
|
||||||
_audioOutputPosition(0),
|
_audioOutputPosition(0),
|
||||||
_audioOutputPositionError(0),
|
_audioOutputPositionError(0),
|
||||||
_currentOutputLine(0)
|
_currentOutputLine(0),
|
||||||
|
_tape({.is_running = false, .dataRegister = 0})
|
||||||
{
|
{
|
||||||
memset(_keyStates, 0, sizeof(_keyStates));
|
memset(_keyStates, 0, sizeof(_keyStates));
|
||||||
memset(_palette, 0xf, sizeof(_palette));
|
memset(_palette, 0xf, sizeof(_palette));
|
||||||
@ -100,6 +101,10 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
_startScreenAddress = (_startScreenAddress & 0x01ff) | (uint16_t)(((*value) & 0x3f) << 9);
|
_startScreenAddress = (_startScreenAddress & 0x01ff) | (uint16_t)(((*value) & 0x3f) << 9);
|
||||||
break;
|
break;
|
||||||
case 0x4:
|
case 0x4:
|
||||||
|
if(isReadOperation(operation))
|
||||||
|
{
|
||||||
|
*value = (uint8_t)_tape.dataRegister;
|
||||||
|
}
|
||||||
printf("Cassette\n");
|
printf("Cassette\n");
|
||||||
break;
|
break;
|
||||||
case 0x5:
|
case 0x5:
|
||||||
@ -170,6 +175,7 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
}
|
}
|
||||||
|
|
||||||
// TODO: tape mode, tape motor, caps lock LED
|
// TODO: tape mode, tape motor, caps lock LED
|
||||||
|
_tape.is_running = ((*value)&0x40) ? true : false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
@ -270,12 +276,86 @@ unsigned int Machine::perform_bus_operation(CPU6502::BusOperation operation, uin
|
|||||||
_audioOutputPosition = 0;
|
_audioOutputPosition = 0;
|
||||||
_currentOutputLine = 0;
|
_currentOutputLine = 0;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_tape.is_running && _tape.media != nullptr)
|
||||||
|
{
|
||||||
|
_tape.time_into_pulse += (unsigned int)_tape.pulseStepper->step();
|
||||||
|
if(_tape.time_into_pulse == _tape.currentPulse.length.length)
|
||||||
|
{
|
||||||
|
get_next_tape_pulse();
|
||||||
|
|
||||||
|
_tape.crossings[0] = _tape.crossings[1];
|
||||||
|
_tape.crossings[1] = _tape.crossings[2];
|
||||||
|
_tape.crossings[2] = _tape.crossings[3];
|
||||||
|
|
||||||
|
_tape.crossings[3] = Tape::Unrecognised;
|
||||||
|
if(_tape.currentPulse.type != Storage::Tape::Pulse::Zero)
|
||||||
|
{
|
||||||
|
float pulse_length = (float)_tape.currentPulse.length.length / (float)_tape.currentPulse.length.clock_rate;
|
||||||
|
if(pulse_length > 0.4 / 2400.0 && pulse_length < 0.6 / 2400.0) _tape.crossings[3] = Tape::Short;
|
||||||
|
if(pulse_length > 0.4 / 1200.0 && pulse_length < 0.6 / 1200.0) _tape.crossings[3] = Tape::Long;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_tape.crossings[0] == Tape::Long && _tape.crossings[1] == Tape::Long)
|
||||||
|
{
|
||||||
|
push_tape_bit(0);
|
||||||
|
_tape.crossings[1] = Tape::Unrecognised;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if(_tape.crossings[0] == Tape::Short && _tape.crossings[1] == Tape::Short && _tape.crossings[2] == Tape::Short && _tape.crossings[3] == Tape::Short)
|
||||||
|
{
|
||||||
|
push_tape_bit(1);
|
||||||
|
_tape.crossings[3] = Tape::Unrecognised;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return cycles;
|
return cycles;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline void Machine::get_next_tape_pulse()
|
||||||
|
{
|
||||||
|
_tape.time_into_pulse = 0;
|
||||||
|
_tape.currentPulse = _tape.media->get_next_pulse();
|
||||||
|
if(_tape.pulseStepper == nullptr || _tape.currentPulse.length.clock_rate != _tape.pulseStepper->get_output_rate())
|
||||||
|
{
|
||||||
|
_tape.pulseStepper = std::shared_ptr<SignalProcessing::Stepper>(new SignalProcessing::Stepper(_tape.currentPulse.length.clock_rate, 2000000));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
inline void Machine::push_tape_bit(uint16_t bit)
|
||||||
|
{
|
||||||
|
_tape.dataRegister = (uint16_t)((_tape.dataRegister >> 1) | (bit << 9));
|
||||||
|
|
||||||
|
if(_tape.dataRegister == 0x3ff)
|
||||||
|
_interruptStatus |= InterruptHighToneDetect;
|
||||||
|
else
|
||||||
|
_interruptStatus &= !InterruptHighToneDetect;
|
||||||
|
|
||||||
|
if(_tape.bits_since_start > 0)
|
||||||
|
{
|
||||||
|
_tape.bits_since_start--;
|
||||||
|
|
||||||
|
if(_tape.bits_since_start == 0)
|
||||||
|
{
|
||||||
|
printf("%02x [%c]\n", _tape.dataRegister&0xff, _tape.dataRegister&0x7f);
|
||||||
|
_interruptStatus |= InterruptTransmitDataEmpty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!bit && !_tape.bits_since_start)
|
||||||
|
{
|
||||||
|
_tape.bits_since_start = 10;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf(".");
|
||||||
|
|
||||||
|
evaluate_interrupts();
|
||||||
|
}
|
||||||
|
|
||||||
void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data)
|
void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data)
|
||||||
{
|
{
|
||||||
uint8_t *target = nullptr;
|
uint8_t *target = nullptr;
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
#include "../../Processors/6502/CPU6502.hpp"
|
#include "../../Processors/6502/CPU6502.hpp"
|
||||||
#include "../../Outputs/CRT.hpp"
|
#include "../../Outputs/CRT.hpp"
|
||||||
#include "../../Outputs/Speaker.hpp"
|
#include "../../Outputs/Speaker.hpp"
|
||||||
|
#include "../../Storage/Tape/Tape.hpp"
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include "Atari2600Inputs.h"
|
#include "Atari2600Inputs.h"
|
||||||
|
|
||||||
@ -67,6 +68,8 @@ class Machine: public CPU6502::Processor<Machine> {
|
|||||||
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
|
unsigned int perform_bus_operation(CPU6502::BusOperation operation, uint16_t address, uint8_t *value);
|
||||||
|
|
||||||
void set_rom(ROMSlot slot, size_t length, const uint8_t *data);
|
void set_rom(ROMSlot slot, size_t length, const uint8_t *data);
|
||||||
|
void set_tape(std::shared_ptr<Storage::Tape> tape) { _tape.media = tape; get_next_tape_pulse(); }
|
||||||
|
|
||||||
void set_key_state(Key key, bool isPressed);
|
void set_key_state(Key key, bool isPressed);
|
||||||
|
|
||||||
Outputs::CRT *get_crt() { return _crt; }
|
Outputs::CRT *get_crt() { return _crt; }
|
||||||
@ -74,27 +77,53 @@ class Machine: public CPU6502::Processor<Machine> {
|
|||||||
const char *get_signal_decoder();
|
const char *get_signal_decoder();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
||||||
|
inline void update_display();
|
||||||
|
inline void update_audio();
|
||||||
|
inline void signal_interrupt(Interrupt interrupt);
|
||||||
|
inline void evaluate_interrupts();
|
||||||
|
|
||||||
|
inline void get_next_tape_pulse();
|
||||||
|
inline void push_tape_bit(uint16_t bit);
|
||||||
|
|
||||||
|
// Things that directly constitute the memory map.
|
||||||
uint8_t _roms[16][16384];
|
uint8_t _roms[16][16384];
|
||||||
uint8_t _os[16384], _ram[32768];
|
uint8_t _os[16384], _ram[32768];
|
||||||
|
|
||||||
|
// Things affected by registers, explicitly or otherwise.
|
||||||
uint8_t _interruptStatus, _interruptControl;
|
uint8_t _interruptStatus, _interruptControl;
|
||||||
uint8_t _palette[16];
|
uint8_t _palette[16];
|
||||||
uint8_t _keyStates[14];
|
uint8_t _keyStates[14];
|
||||||
ROMSlot _activeRom;
|
ROMSlot _activeRom;
|
||||||
uint8_t _screenMode;
|
uint8_t _screenMode;
|
||||||
uint16_t _screenModeBaseAddress;
|
uint16_t _screenModeBaseAddress;
|
||||||
|
uint16_t _startScreenAddress;
|
||||||
|
|
||||||
Outputs::CRT *_crt;
|
// Counters related to simultaneous subsystems;
|
||||||
|
|
||||||
int _frameCycles, _displayOutputPosition, _audioOutputPosition, _audioOutputPositionError;
|
int _frameCycles, _displayOutputPosition, _audioOutputPosition, _audioOutputPositionError;
|
||||||
|
|
||||||
uint16_t _startScreenAddress, _startLineAddress, _currentScreenAddress;
|
// Display generation.
|
||||||
|
uint16_t _startLineAddress, _currentScreenAddress;
|
||||||
int _currentOutputLine;
|
int _currentOutputLine;
|
||||||
uint8_t *_currentLine;
|
uint8_t *_currentLine;
|
||||||
|
|
||||||
inline void update_display();
|
// Tape.
|
||||||
inline void update_audio();
|
struct Tape {
|
||||||
inline void signal_interrupt(Interrupt interrupt);
|
std::shared_ptr<Storage::Tape> media;
|
||||||
inline void evaluate_interrupts();
|
Storage::Tape::Pulse currentPulse;
|
||||||
|
std::shared_ptr<SignalProcessing::Stepper> pulseStepper;
|
||||||
|
uint32_t time_into_pulse;
|
||||||
|
bool is_running;
|
||||||
|
uint16_t dataRegister;
|
||||||
|
int bits_since_start;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
Long, Short, Unrecognised
|
||||||
|
} crossings[4];
|
||||||
|
} _tape;
|
||||||
|
|
||||||
|
// Outputs.
|
||||||
|
Outputs::CRT *_crt;
|
||||||
|
|
||||||
class Speaker: public ::Outputs::Filter<Speaker> {
|
class Speaker: public ::Outputs::Filter<Speaker> {
|
||||||
public:
|
public:
|
||||||
@ -115,8 +144,6 @@ class Machine: public CPU6502::Processor<Machine> {
|
|||||||
bool _is_enabled;
|
bool _is_enabled;
|
||||||
int16_t _output_level;
|
int16_t _output_level;
|
||||||
|
|
||||||
// FILE *rawStream;
|
|
||||||
|
|
||||||
} _speaker;
|
} _speaker;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -38,14 +38,19 @@ class ElectronDocument: MachineDocument {
|
|||||||
override func readFromURL(url: NSURL, ofType typeName: String) throws {
|
override func readFromURL(url: NSURL, ofType typeName: String) throws {
|
||||||
print(url)
|
print(url)
|
||||||
print(typeName)
|
print(typeName)
|
||||||
switch typeName {
|
|
||||||
case "Electron/BBC Tape Image": // this somewhat implies I've misunderstood the info.plist, doesn't it?
|
if let pathExtension = url.pathExtension {
|
||||||
|
switch pathExtension.lowercaseString {
|
||||||
|
case "uef":
|
||||||
electron.openUEFAtURL(url)
|
electron.openUEFAtURL(url)
|
||||||
default:
|
return
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let fileWrapper = try NSFileWrapper(URL: url, options: NSFileWrapperReadingOptions(rawValue: 0))
|
let fileWrapper = try NSFileWrapper(URL: url, options: NSFileWrapperReadingOptions(rawValue: 0))
|
||||||
try self.readFromFileWrapper(fileWrapper, ofType: typeName)
|
try self.readFromFileWrapper(fileWrapper, ofType: typeName)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
override func readFromData(data: NSData, ofType typeName: String) throws {
|
override func readFromData(data: NSData, ofType typeName: String) throws {
|
||||||
electron.setROM(data, slot: 15)
|
electron.setROM(data, slot: 15)
|
||||||
|
@ -14,7 +14,7 @@
|
|||||||
- (void)setOSROM:(nonnull NSData *)rom;
|
- (void)setOSROM:(nonnull NSData *)rom;
|
||||||
- (void)setBASICROM:(nonnull NSData *)rom;
|
- (void)setBASICROM:(nonnull NSData *)rom;
|
||||||
- (void)setROM:(nonnull NSData *)rom slot:(int)slot;
|
- (void)setROM:(nonnull NSData *)rom slot:(int)slot;
|
||||||
- (void)openUEFAtURL:(NSURL *)URL;
|
- (BOOL)openUEFAtURL:(NSURL *)URL;
|
||||||
|
|
||||||
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed;
|
- (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed;
|
||||||
|
|
||||||
|
@ -48,9 +48,14 @@
|
|||||||
_electron.get_crt()->set_delegate(delegate);
|
_electron.get_crt()->set_delegate(delegate);
|
||||||
}
|
}
|
||||||
|
|
||||||
- (void)openUEFAtURL:(NSURL *)URL {
|
- (BOOL)openUEFAtURL:(NSURL *)URL {
|
||||||
Storage::UEF tape([URL fileSystemRepresentation]);
|
try {
|
||||||
// _electron.
|
std::shared_ptr<Storage::UEF> tape(new Storage::UEF([URL fileSystemRepresentation]));
|
||||||
|
_electron.set_tape(tape);
|
||||||
|
return YES;
|
||||||
|
} catch(int exception) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate {
|
- (BOOL)setSpeakerDelegate:(Outputs::Speaker::Delegate *)delegate sampleRate:(int)sampleRate {
|
||||||
|
@ -107,7 +107,7 @@ template <class T> class Filter: public Speaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// determine how many source samples to step
|
// determine how many source samples to step
|
||||||
uint64_t steps = _stepper->update();
|
uint64_t steps = _stepper->step();
|
||||||
if(steps > 1)
|
if(steps > 1)
|
||||||
static_cast<T *>(this)->skip_samples((unsigned int)(steps-1));
|
static_cast<T *>(this)->skip_samples((unsigned int)(steps-1));
|
||||||
input_cycles -= steps;
|
input_cycles -= steps;
|
||||||
|
@ -16,14 +16,21 @@ namespace SignalProcessing {
|
|||||||
class Stepper
|
class Stepper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Stepper(uint64_t output_rate, uint64_t update_rate)
|
Stepper()
|
||||||
{
|
{
|
||||||
whole_step_ = output_rate / update_rate;
|
Stepper(1, 1);
|
||||||
adjustment_up_ = (int64_t)(output_rate % update_rate) << 1;
|
|
||||||
adjustment_down_ = (int64_t)update_rate << 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint64_t update()
|
Stepper(uint64_t output_rate, uint64_t input_rate)
|
||||||
|
{
|
||||||
|
input_rate_ = input_rate;
|
||||||
|
output_rate_ = output_rate;
|
||||||
|
whole_step_ = output_rate / input_rate;
|
||||||
|
adjustment_up_ = (int64_t)(output_rate % input_rate) << 1;
|
||||||
|
adjustment_down_ = (int64_t)input_rate << 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t step()
|
||||||
{
|
{
|
||||||
uint64_t update = whole_step_;
|
uint64_t update = whole_step_;
|
||||||
accumulated_error_ += adjustment_up_;
|
accumulated_error_ += adjustment_up_;
|
||||||
@ -35,10 +42,21 @@ class Stepper
|
|||||||
return update;
|
return update;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline uint64_t get_output_rate()
|
||||||
|
{
|
||||||
|
return output_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline uint64_t get_input_rate()
|
||||||
|
{
|
||||||
|
return input_rate_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint64_t whole_step_;
|
uint64_t whole_step_;
|
||||||
int64_t adjustment_up_, adjustment_down_;
|
int64_t adjustment_up_, adjustment_down_;
|
||||||
int64_t accumulated_error_;
|
int64_t accumulated_error_;
|
||||||
|
uint64_t input_rate_, output_rate_;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,7 +19,7 @@ Storage::UEF::UEF(const char *file_name) :
|
|||||||
int bytes_read = gzread(_file, identifier, 10);
|
int bytes_read = gzread(_file, identifier, 10);
|
||||||
if(bytes_read < 10 || strcmp(identifier, "UEF File!"))
|
if(bytes_read < 10 || strcmp(identifier, "UEF File!"))
|
||||||
{
|
{
|
||||||
// exception?
|
throw ErrorNotUEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
int minor, major;
|
int minor, major;
|
||||||
@ -28,7 +28,7 @@ Storage::UEF::UEF(const char *file_name) :
|
|||||||
|
|
||||||
if(major > 0 || minor > 10 || major < 0 || minor < 0)
|
if(major > 0 || minor > 10 || major < 0 || minor < 0)
|
||||||
{
|
{
|
||||||
// exception?
|
throw ErrorNotUEF;
|
||||||
}
|
}
|
||||||
|
|
||||||
find_next_tape_chunk();
|
find_next_tape_chunk();
|
||||||
@ -53,8 +53,6 @@ Storage::Tape::Pulse Storage::UEF::get_next_pulse()
|
|||||||
find_next_tape_chunk();
|
find_next_tape_chunk();
|
||||||
}
|
}
|
||||||
|
|
||||||
next_pulse.length.clock_rate = _time_base * 2;
|
|
||||||
|
|
||||||
switch(_chunk_id)
|
switch(_chunk_id)
|
||||||
{
|
{
|
||||||
case 0x0100: case 0x0102:
|
case 0x0100: case 0x0102:
|
||||||
@ -70,12 +68,14 @@ Storage::Tape::Pulse Storage::UEF::get_next_pulse()
|
|||||||
|
|
||||||
next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low;
|
next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low;
|
||||||
next_pulse.length.length = _current_bit ? 1 : 2;
|
next_pulse.length.length = _current_bit ? 1 : 2;
|
||||||
|
next_pulse.length.clock_rate = _time_base * 4;
|
||||||
_bit_position = (_bit_position+1)&(_current_bit ? 3 : 1);
|
_bit_position = (_bit_position+1)&(_current_bit ? 3 : 1);
|
||||||
} break;
|
} break;
|
||||||
|
|
||||||
case 0x0110:
|
case 0x0110:
|
||||||
next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low;
|
next_pulse.type = (_bit_position&1) ? Pulse::High : Pulse::Low;
|
||||||
next_pulse.length.length = 1;
|
next_pulse.length.length = 1;
|
||||||
|
next_pulse.length.clock_rate = _time_base * 4;
|
||||||
_bit_position ^= 1;
|
_bit_position ^= 1;
|
||||||
|
|
||||||
if(!_bit_position) _chunk_position++;
|
if(!_bit_position) _chunk_position++;
|
||||||
@ -84,7 +84,7 @@ Storage::Tape::Pulse Storage::UEF::get_next_pulse()
|
|||||||
case 0x0112:
|
case 0x0112:
|
||||||
case 0x0116:
|
case 0x0116:
|
||||||
next_pulse.type = Pulse::Zero;
|
next_pulse.type = Pulse::Zero;
|
||||||
next_pulse.length.length = _tone_length;
|
next_pulse.length = _chunk_duration;
|
||||||
_chunk_position++;
|
_chunk_position++;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -120,16 +120,24 @@ void Storage::UEF::find_next_tape_chunk()
|
|||||||
switch(_chunk_id)
|
switch(_chunk_id)
|
||||||
{
|
{
|
||||||
case 0x0100: case 0x0102: // implicit and explicit bit patterns
|
case 0x0100: case 0x0102: // implicit and explicit bit patterns
|
||||||
case 0x0112: case 0x0116: // gaps
|
return;
|
||||||
|
|
||||||
|
case 0x0112:
|
||||||
|
_chunk_duration.length = (uint16_t)gzgetc(_file);
|
||||||
|
_chunk_duration.length |= (uint16_t)(gzgetc(_file) << 8);
|
||||||
|
_chunk_duration.clock_rate = _time_base;
|
||||||
|
return;
|
||||||
|
|
||||||
|
case 0x0116: // gaps
|
||||||
return;
|
return;
|
||||||
|
|
||||||
case 0x0110: // carrier tone
|
case 0x0110: // carrier tone
|
||||||
_tone_length = (uint16_t)gzgetc(_file);
|
_chunk_duration.length = (uint16_t)gzgetc(_file);
|
||||||
_tone_length |= (uint16_t)(gzgetc(_file) << 8);
|
_chunk_duration.length |= (uint16_t)(gzgetc(_file) << 8);
|
||||||
gzseek(_file, _chunk_length - 2, SEEK_CUR);
|
gzseek(_file, _chunk_length - 2, SEEK_CUR);
|
||||||
return;
|
return;
|
||||||
case 0x0111: // carrier tone with dummy byte
|
case 0x0111: // carrier tone with dummy byte
|
||||||
// TODO: read length
|
// TODO: read lengths
|
||||||
return;
|
return;
|
||||||
case 0x0114: // security cycles
|
case 0x0114: // security cycles
|
||||||
// TODO: read number, Ps and Ws
|
// TODO: read number, Ps and Ws
|
||||||
@ -151,7 +159,7 @@ bool Storage::UEF::chunk_is_finished()
|
|||||||
{
|
{
|
||||||
case 0x0100: return (_chunk_position / 10) == _chunk_length;
|
case 0x0100: return (_chunk_position / 10) == _chunk_length;
|
||||||
case 0x0102: return (_chunk_position / 8) == _chunk_length;
|
case 0x0102: return (_chunk_position / 8) == _chunk_length;
|
||||||
case 0x0110: return _chunk_position == _tone_length;
|
case 0x0110: return _chunk_position == _chunk_duration.length;
|
||||||
|
|
||||||
case 0x0112:
|
case 0x0112:
|
||||||
case 0x0116: return _chunk_position ? true : false;
|
case 0x0116: return _chunk_position ? true : false;
|
||||||
|
@ -23,6 +23,10 @@ class UEF : public Tape {
|
|||||||
Pulse get_next_pulse();
|
Pulse get_next_pulse();
|
||||||
void reset();
|
void reset();
|
||||||
|
|
||||||
|
enum {
|
||||||
|
ErrorNotUEF
|
||||||
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
gzFile _file;
|
gzFile _file;
|
||||||
unsigned int _time_base;
|
unsigned int _time_base;
|
||||||
@ -36,7 +40,7 @@ class UEF : public Tape {
|
|||||||
bool _current_bit;
|
bool _current_bit;
|
||||||
uint32_t _bit_position;
|
uint32_t _bit_position;
|
||||||
|
|
||||||
uint16_t _tone_length;
|
Time _chunk_duration;
|
||||||
|
|
||||||
void find_next_tape_chunk();
|
void find_next_tape_chunk();
|
||||||
bool get_next_bit();
|
bool get_next_bit();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user