1
0
mirror of https://github.com/TomHarte/CLK.git synced 2024-09-08 18:55:14 +00:00

Merge pull request #50 from TomHarte/FasterDisk

Introduces a slightly less expensive timed event loop
This commit is contained in:
Thomas Harte 2016-09-18 10:33:16 -04:00 committed by GitHub
commit 656d0c7a69
8 changed files with 112 additions and 128 deletions

View File

@ -1,36 +0,0 @@
//
// Factors.cpp
// Clock Signal
//
// Created by Thomas Harte on 29/07/2016.
// Copyright © 2016 Thomas Harte. All rights reserved.
//
#include "Factors.hpp"
unsigned int NumberTheory::greatest_common_divisor(unsigned int a, unsigned int b)
{
if(a < b)
{
unsigned int swap = b;
b = a;
a = swap;
}
while(1) {
if(!a) return b;
if(!b) return a;
unsigned int remainder = a%b;
a = b;
b = remainder;
}
}
unsigned int NumberTheory::least_common_multiple(unsigned int a, unsigned int b)
{
if(a == b) return a;
unsigned int gcd = greatest_common_divisor(a, b);
return (a / gcd) * (b / gcd) * gcd;
}

View File

@ -13,13 +13,33 @@ namespace NumberTheory {
/*! /*!
@returns The greatest common divisor of @c a and @c b as computed by Euclid's algorithm. @returns The greatest common divisor of @c a and @c b as computed by Euclid's algorithm.
*/ */
unsigned int greatest_common_divisor(unsigned int a, unsigned int b); template<class T> T greatest_common_divisor(T a, T b) {
if(a < b) {
T swap = b;
b = a;
a = swap;
}
while(1) {
if(!a) return b;
if(!b) return a;
T remainder = a%b;
a = b;
b = remainder;
}
}
/*! /*!
@returns The least common multiple of @c a and @c b computed indirectly via Euclid's greatest @returns The least common multiple of @c a and @c b computed indirectly via Euclid's greatest
common divisor algorithm. common divisor algorithm.
*/ */
unsigned int least_common_multiple(unsigned int a, unsigned int b); template<class T> T least_common_multiple(T a, T b) {
if(a == b) return a;
T gcd = greatest_common_divisor<T>(a, b);
return (a / gcd) * (b / gcd) * gcd;
}
} }
#endif /* Factors_hpp */ #endif /* Factors_hpp */

View File

@ -322,7 +322,6 @@
4BB299F71B587D8400A49093 /* txan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EB1B587D8400A49093 /* txan */; }; 4BB299F71B587D8400A49093 /* txan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EB1B587D8400A49093 /* txan */; };
4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; }; 4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; };
4BB299F91B587D8400A49093 /* tyan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298ED1B587D8400A49093 /* tyan */; }; 4BB299F91B587D8400A49093 /* tyan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298ED1B587D8400A49093 /* tyan */; };
4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C51D4B558F00248BDF /* Factors.cpp */; };
4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; }; 4BB697CB1D4B6D3E00248BDF /* TimedEventLoop.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */; };
4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */; }; 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */; };
4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; }; 4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; };
@ -736,7 +735,6 @@
4BB298EB1B587D8400A49093 /* txan */ = {isa = PBXFileReference; lastKnownFileType = file; path = txan; sourceTree = "<group>"; }; 4BB298EB1B587D8400A49093 /* txan */ = {isa = PBXFileReference; lastKnownFileType = file; path = txan; sourceTree = "<group>"; };
4BB298EC1B587D8400A49093 /* txsn */ = {isa = PBXFileReference; lastKnownFileType = file; path = txsn; sourceTree = "<group>"; }; 4BB298EC1B587D8400A49093 /* txsn */ = {isa = PBXFileReference; lastKnownFileType = file; path = txsn; sourceTree = "<group>"; };
4BB298ED1B587D8400A49093 /* tyan */ = {isa = PBXFileReference; lastKnownFileType = file; path = tyan; sourceTree = "<group>"; }; 4BB298ED1B587D8400A49093 /* tyan */ = {isa = PBXFileReference; lastKnownFileType = file; path = tyan; sourceTree = "<group>"; };
4BB697C51D4B558F00248BDF /* Factors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Factors.cpp; path = ../../NumberTheory/Factors.cpp; sourceTree = "<group>"; };
4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.hpp; sourceTree = "<group>"; }; 4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.hpp; sourceTree = "<group>"; };
4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = "<group>"; }; 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = "<group>"; };
4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = "<group>"; }; 4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = "<group>"; };
@ -1389,7 +1387,6 @@
4BB697C81D4B559300248BDF /* NumberTheory */ = { 4BB697C81D4B559300248BDF /* NumberTheory */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
4BB697C51D4B558F00248BDF /* Factors.cpp */,
4BB697C61D4B558F00248BDF /* Factors.hpp */, 4BB697C61D4B558F00248BDF /* Factors.hpp */,
); );
name = NumberTheory; name = NumberTheory;
@ -2063,7 +2060,6 @@
4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */,
4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */,
4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */,
4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */,
4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */, 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */,
4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */,
4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */,

View File

@ -38,7 +38,7 @@ class CommodoreGCRParser: public Storage::Disk::Drive {
@returns a sector if one was found; @c nullptr otherwise. @returns a sector if one was found; @c nullptr otherwise.
*/ */
std::unique_ptr<Sector> get_sector(uint8_t track, uint8_t sector) std::shared_ptr<Sector> get_sector(uint8_t track, uint8_t sector)
{ {
int difference = (int)track - (int)track_; int difference = (int)track - (int)track_;
track_ = track; track_ = track;
@ -65,6 +65,7 @@ class CommodoreGCRParser: public Storage::Disk::Drive {
int index_count_; int index_count_;
int bit_count_; int bit_count_;
uint8_t track_; uint8_t track_;
std::shared_ptr<Sector> sector_cache_[65536];
void process_input_bit(int value, unsigned int cycles_since_index_hole) void process_input_bit(int value, unsigned int cycles_since_index_hole)
{ {
@ -115,23 +116,26 @@ class CommodoreGCRParser: public Storage::Disk::Drive {
index_count_++; index_count_++;
} }
std::unique_ptr<Sector> get_sector(uint8_t sector) std::shared_ptr<Sector> get_sector(uint8_t sector)
{ {
std::unique_ptr<Sector> first_sector = get_next_sector(); uint16_t sector_address = (uint16_t)((track_ << 8) | sector);
if(sector_cache_[sector_address]) return sector_cache_[sector_address];
std::shared_ptr<Sector> first_sector = get_next_sector();
if(!first_sector) return first_sector; if(!first_sector) return first_sector;
if(first_sector->sector == sector) return first_sector; if(first_sector->sector == sector) return first_sector;
while(1) while(1)
{ {
std::unique_ptr<Sector> next_sector = get_next_sector(); std::shared_ptr<Sector> next_sector = get_next_sector();
if(next_sector->sector == first_sector->sector) return nullptr; if(next_sector->sector == first_sector->sector) return nullptr;
if(next_sector->sector == sector) return next_sector; if(next_sector->sector == sector) return next_sector;
} }
} }
std::unique_ptr<Sector> get_next_sector() std::shared_ptr<Sector> get_next_sector()
{ {
std::unique_ptr<Sector> sector(new Sector); std::shared_ptr<Sector> sector(new Sector);
index_count_ = 0; index_count_ = 0;
while(index_count_ < 2) while(index_count_ < 2)
@ -166,7 +170,12 @@ class CommodoreGCRParser: public Storage::Disk::Drive {
checksum ^= sector->data[c]; checksum ^= sector->data[c];
} }
if(checksum == get_next_byte()) return sector; if(checksum == get_next_byte())
{
uint16_t sector_address = (uint16_t)((sector->track << 8) | sector->sector);
sector_cache_[sector_address] = sector;
return sector;
}
} }
return nullptr; return nullptr;
@ -180,7 +189,7 @@ std::list<File> StaticAnalyser::Commodore::GetFiles(const std::shared_ptr<Storag
parser.set_disk(disk); parser.set_disk(disk);
// find any sector whatsoever to establish the current track // find any sector whatsoever to establish the current track
std::unique_ptr<CommodoreGCRParser::Sector> sector; std::shared_ptr<CommodoreGCRParser::Sector> sector;
// assemble directory // assemble directory
std::vector<uint8_t> directory; std::vector<uint8_t> directory;

View File

@ -85,11 +85,14 @@ void Drive::run_for_cycles(int number_of_cycles)
if(has_disk()) if(has_disk())
{ {
number_of_cycles *= _clock_rate_multiplier; number_of_cycles *= _clock_rate_multiplier;
while(number_of_cycles--) while(number_of_cycles)
{ {
_cycles_since_index_hole ++; int cycles_until_next_event = (int)get_cycles_until_next_event();
_pll->run_for_cycles(1); int cycles_to_run_for = std::min(cycles_until_next_event, number_of_cycles);
TimedEventLoop::run_for_cycles(1); _cycles_since_index_hole += (unsigned int)cycles_to_run_for;
number_of_cycles -= cycles_to_run_for;
_pll->run_for_cycles(cycles_to_run_for);
TimedEventLoop::run_for_cycles(cycles_to_run_for);
} }
} }
} }

View File

@ -17,10 +17,7 @@
static float gzgetfloat(gzFile file) static float gzgetfloat(gzFile file)
{ {
uint8_t bytes[4]; uint8_t bytes[4];
bytes[0] = (uint8_t)gzgetc(file); gzread(file, bytes, 4);
bytes[1] = (uint8_t)gzgetc(file);
bytes[2] = (uint8_t)gzgetc(file);
bytes[3] = (uint8_t)gzgetc(file);
/* assume a four byte array named Float exists, where Float[0] /* assume a four byte array named Float exists, where Float[0]
was the first byte read from the UEF, Float[1] the second, etc */ was the first byte read from the UEF, Float[1] the second, etc */
@ -45,25 +42,33 @@ static float gzgetfloat(gzFile file)
return result; return result;
} }
static uint8_t gzget8(gzFile file)
{
// This is a workaround for gzgetc, which seems to be broken in ZLib 1.2.8.
uint8_t result;
gzread(file, &result, 1);
return result;
}
static int gzget16(gzFile file) static int gzget16(gzFile file)
{ {
int result = gzgetc(file); uint8_t bytes[2];
result |= (gzgetc(file) << 8); gzread(file, bytes, 2);
return result; return bytes[0] | (bytes[1] << 8);
} }
static int gzget24(gzFile file) static int gzget24(gzFile file)
{ {
int result = gzget16(file); uint8_t bytes[3];
result |= (gzgetc(file) << 16); gzread(file, bytes, 3);
return result; return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16);
} }
static int gzget32(gzFile file) static int gzget32(gzFile file)
{ {
int result = gzget16(file); uint8_t bytes[4];
result |= (gzget16(file) << 16); gzread(file, bytes, 4);
return result; return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
} }
using namespace Storage::Tape; using namespace Storage::Tape;
@ -83,11 +88,10 @@ UEF::UEF(const char *file_name) :
throw ErrorNotUEF; throw ErrorNotUEF;
} }
int minor, major; uint8_t version[2];
minor = gzgetc(_file); gzread(_file, version, 2);
major = gzgetc(_file);
if(major > 0 || minor > 10 || major < 0 || minor < 0) if(version[1] > 0 || version[0] > 10)
{ {
throw ErrorNotUEF; throw ErrorNotUEF;
} }
@ -199,17 +203,17 @@ void UEF::queue_implicit_bit_pattern(uint32_t length)
{ {
while(length--) while(length--)
{ {
queue_implicit_byte((uint8_t)gzgetc(_file)); queue_implicit_byte(gzget8(_file));
} }
} }
void UEF::queue_explicit_bit_pattern(uint32_t length) void UEF::queue_explicit_bit_pattern(uint32_t length)
{ {
size_t length_in_bits = (length << 3) - (size_t)gzgetc(_file); size_t length_in_bits = (length << 3) - (size_t)gzget8(_file);
uint8_t current_byte = 0; uint8_t current_byte = 0;
for(size_t bit = 0; bit < length_in_bits; bit++) for(size_t bit = 0; bit < length_in_bits; bit++)
{ {
if(!(bit&7)) current_byte = (uint8_t)gzgetc(_file); if(!(bit&7)) current_byte = gzget8(_file);
queue_bit(current_byte&1); queue_bit(current_byte&1);
current_byte >>= 1; current_byte >>= 1;
} }
@ -250,13 +254,13 @@ void UEF::queue_carrier_tone_with_dummy()
void UEF::queue_security_cycles() void UEF::queue_security_cycles()
{ {
int number_of_cycles = gzget24(_file); int number_of_cycles = gzget24(_file);
bool first_is_pulse = gzgetc(_file) == 'P'; bool first_is_pulse = gzget8(_file) == 'P';
bool last_is_pulse = gzgetc(_file) == 'P'; bool last_is_pulse = gzget8(_file) == 'P';
uint8_t current_byte = 0; uint8_t current_byte = 0;
for(int cycle = 0; cycle < number_of_cycles; cycle++) for(int cycle = 0; cycle < number_of_cycles; cycle++)
{ {
if(!(cycle&7)) current_byte = (uint8_t)gzgetc(_file); if(!(cycle&7)) current_byte = gzget8(_file);
int bit = (current_byte >> 7); int bit = (current_byte >> 7);
current_byte <<= 1; current_byte <<= 1;
@ -284,9 +288,9 @@ void UEF::queue_defined_data(uint32_t length)
{ {
if(length < 3) return; if(length < 3) return;
int bits_per_packet = gzgetc(_file); int bits_per_packet = gzget8(_file);
char parity_type = (char)gzgetc(_file); char parity_type = (char)gzget8(_file);
int number_of_stop_bits = gzgetc(_file); int number_of_stop_bits = gzget8(_file);
bool has_extra_stop_wave = (number_of_stop_bits < 0); bool has_extra_stop_wave = (number_of_stop_bits < 0);
number_of_stop_bits = abs(number_of_stop_bits); number_of_stop_bits = abs(number_of_stop_bits);
@ -294,7 +298,7 @@ void UEF::queue_defined_data(uint32_t length)
length -= 3; length -= 3;
while(length--) while(length--)
{ {
uint8_t byte = (uint8_t)gzgetc(_file); uint8_t byte = gzget8(_file);
uint8_t parity_value = byte; uint8_t parity_value = byte;
parity_value ^= (parity_value >> 4); parity_value ^= (parity_value >> 4);

View File

@ -8,6 +8,7 @@
#include "TimedEventLoop.hpp" #include "TimedEventLoop.hpp"
#include "../NumberTheory/Factors.hpp" #include "../NumberTheory/Factors.hpp"
#include <algorithm>
using namespace Storage; using namespace Storage;
@ -16,29 +17,27 @@ TimedEventLoop::TimedEventLoop(unsigned int input_clock_rate) :
void TimedEventLoop::run_for_cycles(int number_of_cycles) void TimedEventLoop::run_for_cycles(int number_of_cycles)
{ {
_time_into_interval += (unsigned int)_stepper->step((uint64_t)number_of_cycles); _cycles_until_event -= number_of_cycles;
while(_time_into_interval >= _event_interval.length) while(_cycles_until_event <= 0)
{ {
process_next_event(); process_next_event();
} }
} }
unsigned int TimedEventLoop::get_cycles_until_next_event()
{
return (unsigned int)std::max(_cycles_until_event, 0);
}
void TimedEventLoop::reset_timer() void TimedEventLoop::reset_timer()
{ {
_time_into_interval = 0; _subcycles_until_event.set_zero();
_stepper.reset(); _cycles_until_event = 0;
} }
void TimedEventLoop::reset_timer_to_offset(Time offset) void TimedEventLoop::reset_timer_to_offset(Time offset)
{ {
unsigned int common_clock_rate = NumberTheory::least_common_multiple(offset.clock_rate, _event_interval.clock_rate); // TODO: apply
_time_into_interval = offset.length * (common_clock_rate / offset.clock_rate);
_event_interval.length *= common_clock_rate / _event_interval.clock_rate;
_event_interval.clock_rate = common_clock_rate;
if(common_clock_rate != _stepper->get_output_rate())
{
_stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate));
}
} }
void TimedEventLoop::jump_to_next_event() void TimedEventLoop::jump_to_next_event()
@ -49,43 +48,27 @@ void TimedEventLoop::jump_to_next_event()
void TimedEventLoop::set_next_event_time_interval(Time interval) void TimedEventLoop::set_next_event_time_interval(Time interval)
{ {
// figure out how much time has been run since the last bit ended // Calculate [interval]*[input clock rate] + [subcycles until this event].
if(_stepper) int64_t denominator = (int64_t)interval.clock_rate * (int64_t)_subcycles_until_event.clock_rate;
{ int64_t numerator =
_time_into_interval -= _event_interval.length; (int64_t)_subcycles_until_event.clock_rate * (int64_t)_input_clock_rate * (int64_t)interval.length +
if(_time_into_interval) (int64_t)interval.clock_rate * (int64_t)_subcycles_until_event.length;
{
// simplify the quotient
unsigned int common_divisor = NumberTheory::greatest_common_divisor(_time_into_interval, _event_interval.clock_rate);
_time_into_interval /= common_divisor;
_event_interval.clock_rate /= common_divisor;
// build a quotient that is the sum of the time overrun plus the incoming time and adjust the time overrun // Simplify now, to prepare for stuffing into possibly 32-bit quantities
// to be in terms of the new quotient int64_t common_divisor = NumberTheory::greatest_common_divisor(numerator % denominator, denominator);
unsigned int denominator = NumberTheory::least_common_multiple(_event_interval.clock_rate, interval.clock_rate); denominator /= common_divisor;
interval.length *= denominator / interval.clock_rate; numerator /= common_divisor;
interval.clock_rate = denominator;
_time_into_interval *= denominator / _event_interval.clock_rate;
}
}
else
{
_time_into_interval = 0;
}
// store new interval // So this event will fire in the integral number of cycles from now, putting us at the remainder
_event_interval = interval; // number of subcycles
_cycles_until_event = (int)(numerator / denominator);
// adjust stepper if required _subcycles_until_event.length = (unsigned int)(numerator % denominator);
if(!_stepper || _event_interval.clock_rate != _stepper->get_output_rate()) _subcycles_until_event.clock_rate = (unsigned int)denominator;
{
_stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate));
}
} }
Time TimedEventLoop::get_time_into_next_event() Time TimedEventLoop::get_time_into_next_event()
{ {
Time result = _event_interval; // TODO: calculate, presumably as [length of interval] - ([cycles left] + [subcycles left])
result.length = _time_into_interval; Time zero;
return result; return zero;
} }

View File

@ -48,6 +48,11 @@ namespace Storage {
*/ */
void run_for_cycles(int number_of_cycles); void run_for_cycles(int number_of_cycles);
/*!
@returns the number of whole cycles remaining until the next event is triggered.
*/
unsigned int get_cycles_until_next_event();
protected: protected:
/*! /*!
Sets the time interval, as a proportion of a second, until the next event should be triggered. Sets the time interval, as a proportion of a second, until the next event should be triggered.
@ -86,9 +91,9 @@ namespace Storage {
private: private:
unsigned int _input_clock_rate; unsigned int _input_clock_rate;
int _cycles_until_event;
Time _subcycles_until_event;
Time _event_interval; Time _event_interval;
std::unique_ptr<SignalProcessing::Stepper> _stepper;
uint32_t _time_into_interval;
}; };
} }