diff --git a/NumberTheory/Factors.cpp b/NumberTheory/Factors.cpp deleted file mode 100644 index d383efc79..000000000 --- a/NumberTheory/Factors.cpp +++ /dev/null @@ -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; -} diff --git a/NumberTheory/Factors.hpp b/NumberTheory/Factors.hpp index d2c280a11..03ffc8593 100644 --- a/NumberTheory/Factors.hpp +++ b/NumberTheory/Factors.hpp @@ -13,13 +13,33 @@ namespace NumberTheory { /*! @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 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 common divisor algorithm. */ - unsigned int least_common_multiple(unsigned int a, unsigned int b); + template T least_common_multiple(T a, T b) { + if(a == b) return a; + + T gcd = greatest_common_divisor(a, b); + return (a / gcd) * (b / gcd) * gcd; + } } #endif /* Factors_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 049d5b3d8..d05506a5e 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -322,7 +322,6 @@ 4BB299F71B587D8400A49093 /* txan in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EB1B587D8400A49093 /* txan */; }; 4BB299F81B587D8400A49093 /* txsn in Resources */ = {isa = PBXBuildFile; fileRef = 4BB298EC1B587D8400A49093 /* txsn */; }; 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 */; }; 4BB697CE1D4BA44400248BDF /* CommodoreGCR.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 4BB697CC1D4BA44400248BDF /* CommodoreGCR.cpp */; }; 4BB73EA21B587A5100552FC2 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4BB73EA11B587A5100552FC2 /* AppDelegate.swift */; }; @@ -736,7 +735,6 @@ 4BB298EB1B587D8400A49093 /* txan */ = {isa = PBXFileReference; lastKnownFileType = file; path = txan; sourceTree = ""; }; 4BB298EC1B587D8400A49093 /* txsn */ = {isa = PBXFileReference; lastKnownFileType = file; path = txsn; sourceTree = ""; }; 4BB298ED1B587D8400A49093 /* tyan */ = {isa = PBXFileReference; lastKnownFileType = file; path = tyan; sourceTree = ""; }; - 4BB697C51D4B558F00248BDF /* Factors.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = Factors.cpp; path = ../../NumberTheory/Factors.cpp; sourceTree = ""; }; 4BB697C61D4B558F00248BDF /* Factors.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = Factors.hpp; path = ../../NumberTheory/Factors.hpp; sourceTree = ""; }; 4BB697C91D4B6D3E00248BDF /* TimedEventLoop.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = TimedEventLoop.cpp; sourceTree = ""; }; 4BB697CA1D4B6D3E00248BDF /* TimedEventLoop.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; path = TimedEventLoop.hpp; sourceTree = ""; }; @@ -1389,7 +1387,6 @@ 4BB697C81D4B559300248BDF /* NumberTheory */ = { isa = PBXGroup; children = ( - 4BB697C51D4B558F00248BDF /* Factors.cpp */, 4BB697C61D4B558F00248BDF /* Factors.hpp */, ); name = NumberTheory; @@ -2063,7 +2060,6 @@ 4BBF99151C8FBA6F0075DAFB /* CRTOpenGL.cpp in Sources */, 4B0CCC451C62D0B3001CAC5F /* CRT.cpp in Sources */, 4B643F3F1D77B88000D431D6 /* DocumentController.swift in Sources */, - 4BB697C71D4B558F00248BDF /* Factors.cpp in Sources */, 4BA799951D8B656E0045123D /* StaticAnalyser.cpp in Sources */, 4B55CE591C3B7D360093A61B /* ElectronDocument.swift in Sources */, 4B4DC82B1D2C27A4003C5BF8 /* SerialBus.cpp in Sources */, diff --git a/StaticAnalyser/Commodore/Disk.cpp b/StaticAnalyser/Commodore/Disk.cpp index df987d691..ee7e8b457 100644 --- a/StaticAnalyser/Commodore/Disk.cpp +++ b/StaticAnalyser/Commodore/Disk.cpp @@ -38,7 +38,7 @@ class CommodoreGCRParser: public Storage::Disk::Drive { @returns a sector if one was found; @c nullptr otherwise. */ - std::unique_ptr get_sector(uint8_t track, uint8_t sector) + std::shared_ptr get_sector(uint8_t track, uint8_t sector) { int difference = (int)track - (int)track_; track_ = track; @@ -65,6 +65,7 @@ class CommodoreGCRParser: public Storage::Disk::Drive { int index_count_; int bit_count_; uint8_t track_; + std::shared_ptr sector_cache_[65536]; void process_input_bit(int value, unsigned int cycles_since_index_hole) { @@ -115,23 +116,26 @@ class CommodoreGCRParser: public Storage::Disk::Drive { index_count_++; } - std::unique_ptr get_sector(uint8_t sector) + std::shared_ptr get_sector(uint8_t sector) { - std::unique_ptr 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 first_sector = get_next_sector(); if(!first_sector) return first_sector; if(first_sector->sector == sector) return first_sector; while(1) { - std::unique_ptr next_sector = get_next_sector(); + std::shared_ptr next_sector = get_next_sector(); if(next_sector->sector == first_sector->sector) return nullptr; if(next_sector->sector == sector) return next_sector; } } - std::unique_ptr get_next_sector() + std::shared_ptr get_next_sector() { - std::unique_ptr sector(new Sector); + std::shared_ptr sector(new Sector); index_count_ = 0; while(index_count_ < 2) @@ -166,7 +170,12 @@ class CommodoreGCRParser: public Storage::Disk::Drive { 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; @@ -180,7 +189,7 @@ std::list StaticAnalyser::Commodore::GetFiles(const std::shared_ptr sector; + std::shared_ptr sector; // assemble directory std::vector directory; diff --git a/Storage/Disk/DiskDrive.cpp b/Storage/Disk/DiskDrive.cpp index 29f9cb4eb..681a9e94d 100644 --- a/Storage/Disk/DiskDrive.cpp +++ b/Storage/Disk/DiskDrive.cpp @@ -85,11 +85,14 @@ void Drive::run_for_cycles(int number_of_cycles) if(has_disk()) { number_of_cycles *= _clock_rate_multiplier; - while(number_of_cycles--) + while(number_of_cycles) { - _cycles_since_index_hole ++; - _pll->run_for_cycles(1); - TimedEventLoop::run_for_cycles(1); + int cycles_until_next_event = (int)get_cycles_until_next_event(); + int cycles_to_run_for = std::min(cycles_until_next_event, number_of_cycles); + _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); } } } diff --git a/Storage/Tape/Formats/TapeUEF.cpp b/Storage/Tape/Formats/TapeUEF.cpp index bd5e3075c..3fe5d32cc 100644 --- a/Storage/Tape/Formats/TapeUEF.cpp +++ b/Storage/Tape/Formats/TapeUEF.cpp @@ -17,10 +17,7 @@ static float gzgetfloat(gzFile file) { uint8_t bytes[4]; - bytes[0] = (uint8_t)gzgetc(file); - bytes[1] = (uint8_t)gzgetc(file); - bytes[2] = (uint8_t)gzgetc(file); - bytes[3] = (uint8_t)gzgetc(file); + gzread(file, bytes, 4); /* assume a four byte array named Float exists, where Float[0] was the first byte read from the UEF, Float[1] the second, etc */ @@ -45,25 +42,33 @@ static float gzgetfloat(gzFile file) 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) { - int result = gzgetc(file); - result |= (gzgetc(file) << 8); - return result; + uint8_t bytes[2]; + gzread(file, bytes, 2); + return bytes[0] | (bytes[1] << 8); } static int gzget24(gzFile file) { - int result = gzget16(file); - result |= (gzgetc(file) << 16); - return result; + uint8_t bytes[3]; + gzread(file, bytes, 3); + return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16); } static int gzget32(gzFile file) { - int result = gzget16(file); - result |= (gzget16(file) << 16); - return result; + uint8_t bytes[4]; + gzread(file, bytes, 4); + return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24); } using namespace Storage::Tape; @@ -83,11 +88,10 @@ UEF::UEF(const char *file_name) : throw ErrorNotUEF; } - int minor, major; - minor = gzgetc(_file); - major = gzgetc(_file); + uint8_t version[2]; + gzread(_file, version, 2); - if(major > 0 || minor > 10 || major < 0 || minor < 0) + if(version[1] > 0 || version[0] > 10) { throw ErrorNotUEF; } @@ -199,17 +203,17 @@ void UEF::queue_implicit_bit_pattern(uint32_t length) { while(length--) { - queue_implicit_byte((uint8_t)gzgetc(_file)); + queue_implicit_byte(gzget8(_file)); } } 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; 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); current_byte >>= 1; } @@ -250,13 +254,13 @@ void UEF::queue_carrier_tone_with_dummy() void UEF::queue_security_cycles() { int number_of_cycles = gzget24(_file); - bool first_is_pulse = gzgetc(_file) == 'P'; - bool last_is_pulse = gzgetc(_file) == 'P'; + bool first_is_pulse = gzget8(_file) == 'P'; + bool last_is_pulse = gzget8(_file) == 'P'; uint8_t current_byte = 0; 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); current_byte <<= 1; @@ -284,9 +288,9 @@ void UEF::queue_defined_data(uint32_t length) { if(length < 3) return; - int bits_per_packet = gzgetc(_file); - char parity_type = (char)gzgetc(_file); - int number_of_stop_bits = gzgetc(_file); + int bits_per_packet = gzget8(_file); + char parity_type = (char)gzget8(_file); + int number_of_stop_bits = gzget8(_file); bool has_extra_stop_wave = (number_of_stop_bits < 0); number_of_stop_bits = abs(number_of_stop_bits); @@ -294,7 +298,7 @@ void UEF::queue_defined_data(uint32_t length) length -= 3; while(length--) { - uint8_t byte = (uint8_t)gzgetc(_file); + uint8_t byte = gzget8(_file); uint8_t parity_value = byte; parity_value ^= (parity_value >> 4); diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index 4a35527bd..d420fd082 100644 --- a/Storage/TimedEventLoop.cpp +++ b/Storage/TimedEventLoop.cpp @@ -8,6 +8,7 @@ #include "TimedEventLoop.hpp" #include "../NumberTheory/Factors.hpp" +#include using namespace Storage; @@ -16,29 +17,27 @@ TimedEventLoop::TimedEventLoop(unsigned int input_clock_rate) : void TimedEventLoop::run_for_cycles(int number_of_cycles) { - _time_into_interval += (unsigned int)_stepper->step((uint64_t)number_of_cycles); - while(_time_into_interval >= _event_interval.length) + _cycles_until_event -= number_of_cycles; + while(_cycles_until_event <= 0) { process_next_event(); } } +unsigned int TimedEventLoop::get_cycles_until_next_event() +{ + return (unsigned int)std::max(_cycles_until_event, 0); +} + void TimedEventLoop::reset_timer() { - _time_into_interval = 0; - _stepper.reset(); + _subcycles_until_event.set_zero(); + _cycles_until_event = 0; } void TimedEventLoop::reset_timer_to_offset(Time offset) { - unsigned int common_clock_rate = NumberTheory::least_common_multiple(offset.clock_rate, _event_interval.clock_rate); - _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)); - } + // TODO: apply } 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) { - // figure out how much time has been run since the last bit ended - if(_stepper) - { - _time_into_interval -= _event_interval.length; - if(_time_into_interval) - { - // 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; + // Calculate [interval]*[input clock rate] + [subcycles until this event]. + int64_t denominator = (int64_t)interval.clock_rate * (int64_t)_subcycles_until_event.clock_rate; + int64_t numerator = + (int64_t)_subcycles_until_event.clock_rate * (int64_t)_input_clock_rate * (int64_t)interval.length + + (int64_t)interval.clock_rate * (int64_t)_subcycles_until_event.length; - // build a quotient that is the sum of the time overrun plus the incoming time and adjust the time overrun - // to be in terms of the new quotient - unsigned int denominator = NumberTheory::least_common_multiple(_event_interval.clock_rate, interval.clock_rate); - interval.length *= denominator / interval.clock_rate; - interval.clock_rate = denominator; - _time_into_interval *= denominator / _event_interval.clock_rate; - } - } - else - { - _time_into_interval = 0; - } + // Simplify now, to prepare for stuffing into possibly 32-bit quantities + int64_t common_divisor = NumberTheory::greatest_common_divisor(numerator % denominator, denominator); + denominator /= common_divisor; + numerator /= common_divisor; - // store new interval - _event_interval = interval; - - // adjust stepper if required - if(!_stepper || _event_interval.clock_rate != _stepper->get_output_rate()) - { - _stepper.reset(new SignalProcessing::Stepper(_event_interval.clock_rate, _input_clock_rate)); - } + // So this event will fire in the integral number of cycles from now, putting us at the remainder + // number of subcycles + _cycles_until_event = (int)(numerator / denominator); + _subcycles_until_event.length = (unsigned int)(numerator % denominator); + _subcycles_until_event.clock_rate = (unsigned int)denominator; } Time TimedEventLoop::get_time_into_next_event() { - Time result = _event_interval; - result.length = _time_into_interval; - return result; + // TODO: calculate, presumably as [length of interval] - ([cycles left] + [subcycles left]) + Time zero; + return zero; } diff --git a/Storage/TimedEventLoop.hpp b/Storage/TimedEventLoop.hpp index 431464853..c939a7827 100644 --- a/Storage/TimedEventLoop.hpp +++ b/Storage/TimedEventLoop.hpp @@ -48,6 +48,11 @@ namespace Storage { */ 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: /*! Sets the time interval, as a proportion of a second, until the next event should be triggered. @@ -86,9 +91,9 @@ namespace Storage { private: unsigned int _input_clock_rate; + int _cycles_until_event; + Time _subcycles_until_event; Time _event_interval; - std::unique_ptr _stepper; - uint32_t _time_into_interval; }; }