From 142254a478956771fc4c34c084647990fb04c8c9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Sep 2016 18:11:58 -0400 Subject: [PATCH 01/11] Resolved failure to open disks that arrive through analysis. --- Machines/Commodore/Vic-20/Vic20.cpp | 68 +++++++++++++++++------------ Machines/Commodore/Vic-20/Vic20.hpp | 6 +-- 2 files changed, 43 insertions(+), 31 deletions(-) diff --git a/Machines/Commodore/Vic-20/Vic20.cpp b/Machines/Commodore/Vic-20/Vic20.cpp index 918b44f77..f9defb267 100644 --- a/Machines/Commodore/Vic-20/Vic20.cpp +++ b/Machines/Commodore/Vic-20/Vic20.cpp @@ -272,29 +272,26 @@ void Machine::set_rom(ROMSlot slot, size_t length, const uint8_t *data) } } -void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) -{ - // TEST! - StaticAnalyser::GetTargets(file_name); - - if(length > 2) - { - _rom_address = (uint16_t)(data[0] | (data[1] << 8)); - _rom_length = (uint16_t)(length - 2); - - // install in the ROM area if this looks like a ROM; otherwise put on tape and throw into that mechanism - if(_rom_address == 0xa000) - { - _rom = new uint8_t[0x2000]; - memcpy(_rom, &data[2], length - 2); - write_to_map(_processorReadMemoryMap, _rom, _rom_address, 0x2000); - } - else - { - set_tape(std::shared_ptr(new Storage::Tape::PRG(file_name))); - } - } -} +//void Machine::set_prg(const char *file_name, size_t length, const uint8_t *data) +//{ +// if(length > 2) +// { +// _rom_address = (uint16_t)(data[0] | (data[1] << 8)); +// _rom_length = (uint16_t)(length - 2); +// +// // install in the ROM area if this looks like a ROM; otherwise put on tape and throw into that mechanism +// if(_rom_address == 0xa000) +// { +// _rom = new uint8_t[0x2000]; +// memcpy(_rom, &data[2], length - 2); +// write_to_map(_processorReadMemoryMap, _rom, _rom_address, 0x2000); +// } +// else +// { +// set_tape(std::shared_ptr(new Storage::Tape::PRG(file_name))); +// } +// } +//} #pragma mar - Tape @@ -305,6 +302,21 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) _tape.set_tape(target.tapes.front()); } + if(target.disks.size()) + { + // construct the 1540 + _c1540.reset(new ::Commodore::C1540::Machine); + + // attach it to the serial bus + _c1540->set_serial_bus(_serialBus); + + // hand it the disk + _c1540->set_disk(target.disks.front()); + + // install the ROM if it was previously set + install_disk_rom(); + } + if(_should_automatically_load_media) { if(target.loadingCommand.length()) // TODO: and automatic loading option enabled @@ -327,11 +339,11 @@ void Machine::configure_as_target(const StaticAnalyser::Target &target) } } -void Machine::set_tape(std::shared_ptr tape) -{ +//void Machine::set_tape(std::shared_ptr tape) +//{ // _tape.set_tape(tape); // if(_should_automatically_load_media) set_typer_for_string("LOAD\nRUN\n"); -} +//} void Machine::tape_did_change_input(Tape *tape) { @@ -340,7 +352,7 @@ void Machine::tape_did_change_input(Tape *tape) #pragma mark - Disc -void Machine::set_disk(std::shared_ptr disk) +/*void Machine::set_disk(std::shared_ptr disk) { // construct the 1540 _c1540.reset(new ::Commodore::C1540::Machine); @@ -355,7 +367,7 @@ void Machine::set_disk(std::shared_ptr disk) install_disk_rom(); if(_should_automatically_load_media) set_typer_for_string("LOAD\"*\",8,1\nRUN\n"); -} +}*/ void Machine::install_disk_rom() { diff --git a/Machines/Commodore/Vic-20/Vic20.hpp b/Machines/Commodore/Vic-20/Vic20.hpp index 7893dea7d..eb2bcabb5 100644 --- a/Machines/Commodore/Vic-20/Vic20.hpp +++ b/Machines/Commodore/Vic-20/Vic20.hpp @@ -263,9 +263,9 @@ class Machine: void set_rom(ROMSlot slot, size_t length, const uint8_t *data); void configure_as_target(const StaticAnalyser::Target &target); - void set_prg(const char *file_name, size_t length, const uint8_t *data); - void set_tape(std::shared_ptr tape); - void set_disk(std::shared_ptr disk); +// void set_prg(const char *file_name, size_t length, const uint8_t *data); +// void set_tape(std::shared_ptr tape); +// void set_disk(std::shared_ptr disk); void set_key_state(Key key, bool isPressed) { _keyboardVIA->set_key_state(key, isPressed); } void clear_all_keys() { _keyboardVIA->clear_all_keys(); } From a98374995ed1ff3f4b16c22f85618488b38f90fd Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Sep 2016 18:19:32 -0400 Subject: [PATCH 02/11] Cleaned up pre-analysis code. --- .../Clock Signal.xcodeproj/project.pbxproj | 10 ++-- .../Clock Signal/Machine/Wrappers/CSVic20.h | 5 -- .../Clock Signal/Machine/Wrappers/CSVic20.mm | 48 ------------------- 3 files changed, 5 insertions(+), 58 deletions(-) diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 08acf0b6d..049d5b3d8 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1487,13 +1487,13 @@ 4BB73EDC1B587CA500552FC2 /* Machines */ = { isa = PBXGroup; children = ( - 4B4DC81D1D2C2425003C5BF8 /* Commodore */, - 4B046DC31CFE651500E9E45E /* CRTMachine.hpp */, - 4B2E2D961C3A06EC00138695 /* Atari2600 */, - 4B2E2D9E1C3A070900138695 /* Electron */, 4B1E85731D170228001EF87D /* Typer.cpp */, - 4B1E85741D170228001EF87D /* Typer.hpp */, 4BA9C3CF1D8164A9002DDB61 /* ConfigurationTarget.hpp */, + 4B046DC31CFE651500E9E45E /* CRTMachine.hpp */, + 4B1E85741D170228001EF87D /* Typer.hpp */, + 4B2E2D961C3A06EC00138695 /* Atari2600 */, + 4B4DC81D1D2C2425003C5BF8 /* Commodore */, + 4B2E2D9E1C3A070900138695 /* Electron */, ); name = Machines; path = ../../Machines; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h index c2e17fe61..42d0aaee0 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.h @@ -30,11 +30,6 @@ typedef NS_ENUM(NSInteger, CSVic20MemorySize) - (void)setCharactersROM:(nonnull NSData *)rom; - (void)setDriveROM:(nonnull NSData *)rom; -- (BOOL)openPRGAtURL:(nonnull NSURL *)URL; -- (BOOL)openTAPAtURL:(nonnull NSURL *)URL; -- (BOOL)openG64AtURL:(nonnull NSURL *)URL; -- (BOOL)openD64AtURL:(nonnull NSURL *)URL; - @property (nonatomic, assign) BOOL useFastLoadingHack; @property (nonatomic, assign) BOOL shouldLoadAutomatically; @property (nonatomic, assign) CSVic20Region region; diff --git a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm index 7198aafe7..41aed0e6b 100644 --- a/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm +++ b/OSBindings/Mac/Clock Signal/Machine/Wrappers/CSVic20.mm @@ -46,54 +46,6 @@ using namespace Commodore::Vic20; [self setROM:rom slot:Drive]; } -- (BOOL)openTAPAtURL:(NSURL *)URL { - @synchronized(self) { - try { - std::shared_ptr tape(new Storage::Tape::CommodoreTAP([URL fileSystemRepresentation])); - _vic20.set_tape(tape); - return YES; - } catch(...) { - return NO; - } - } -} - -- (BOOL)openG64AtURL:(NSURL *)URL { - return [self openDisk:^std::shared_ptr{ - return std::shared_ptr(new Storage::Disk::G64([URL fileSystemRepresentation])); - }]; -} - -- (BOOL)openD64AtURL:(NSURL *)URL { - return [self openDisk:^std::shared_ptr{ - return std::shared_ptr(new Storage::Disk::D64([URL fileSystemRepresentation])); - }]; -} - -- (BOOL)openDisk:(std::shared_ptr (^)())opener { - @synchronized(self) { - try { - std::shared_ptr disk = opener(); - _vic20.set_disk(disk); - return YES; - } catch(...) { - return NO; - } - } -} - -- (BOOL)openPRGAtURL:(NSURL *)URL { - NSData *prg = [NSData dataWithContentsOfURL:URL]; - @synchronized(self) { - try { - _vic20.set_prg(URL.fileSystemRepresentation, prg.length, (const uint8_t *)prg.bytes); - return YES; - } catch(...) { - return NO; - } - } -} - - (void)setKey:(uint16_t)key isPressed:(BOOL)isPressed { static NSDictionary *vicKeysByKeys = @{ @(VK_ANSI_1): @(Key::Key1), @(VK_ANSI_2): @(Key::Key2), From 14a9edcf5dc277812f19f8cd280192350e0dd5b9 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Sep 2016 19:52:27 -0400 Subject: [PATCH 03/11] Made an attempt to do the time base conversion upfront, saving a lot of hassle and allowing greater prediction. --- Storage/TimedEventLoop.cpp | 58 +++++++++++++------------------------- Storage/TimedEventLoop.hpp | 6 ++-- 2 files changed, 24 insertions(+), 40 deletions(-) diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index 4a35527bd..ed102042b 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,34 @@ 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(); + _error.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); +/* 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)); - } + }*/ } void TimedEventLoop::jump_to_next_event() @@ -49,43 +55,19 @@ 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; + unsigned int common_divisor = NumberTheory::greatest_common_divisor(_error.clock_rate, interval.clock_rate); + uint64_t denominator = (interval.clock_rate * _error.clock_rate) / common_divisor; + uint64_t numerator = (_error.clock_rate / common_divisor) * _input_clock_rate * interval.length - (interval.clock_rate / common_divisor) * _error.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; - } - - // 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)); - } + _cycles_until_event = (int)(numerator / denominator); + _error.length = (unsigned int)(numerator % denominator); + _error.clock_rate = (unsigned int)denominator; + _error.simplify(); } Time TimedEventLoop::get_time_into_next_event() { Time result = _event_interval; - result.length = _time_into_interval; +// result.length = _time_into_interval; return result; } diff --git a/Storage/TimedEventLoop.hpp b/Storage/TimedEventLoop.hpp index 431464853..f8442a6c8 100644 --- a/Storage/TimedEventLoop.hpp +++ b/Storage/TimedEventLoop.hpp @@ -48,6 +48,8 @@ namespace Storage { */ void run_for_cycles(int number_of_cycles); + 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 +88,9 @@ namespace Storage { private: unsigned int _input_clock_rate; + int _cycles_until_event; Time _event_interval; - std::unique_ptr _stepper; - uint32_t _time_into_interval; + Time _error; }; } From dbb758aaf1c3e06e7c33da876ccf65e0c50931af Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Sep 2016 22:01:25 -0400 Subject: [PATCH 04/11] Workaround for a weird bug that suddenly appears to manifest: gzgetc is returning the file name, not bytes from the file. Seems to be related to improper initialisation of the `next` field within the gzFile header. I can't immediately see where ZLib intends to do that so it's a bit mysterious. But the larger-than-8 readers could probably save time by reading in blocks anyway. --- Storage/Tape/Formats/TapeUEF.cpp | 58 +++++++++++++++++--------------- 1 file changed, 31 insertions(+), 27 deletions(-) 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); From bfed9585b8a033ee17cf012ef71e058c2d825895 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Sep 2016 22:01:54 -0400 Subject: [PATCH 05/11] Added a sector cache to avoid having to re-decode things five times over. --- StaticAnalyser/Commodore/Disk.cpp | 25 +++++++++++++++++-------- 1 file changed, 17 insertions(+), 8 deletions(-) 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; From 79ef38b123924c7b9745d7fbc7db4952f7ed5445 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sat, 17 Sep 2016 22:02:38 -0400 Subject: [PATCH 06/11] Attempted to use the new `get_cycles_until_next_event` method to take some repetition outside of the PLL and drive event loops. --- Storage/Disk/DiskDrive.cpp | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) 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); } } } From 48317f3b771ce32e05395f61a6881655fc6ad4c5 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Sep 2016 10:23:15 -0400 Subject: [PATCH 07/11] Templated these two methods, so as no longer to put a constraint on precision. --- NumberTheory/Factors.cpp | 36 ------------------------------------ NumberTheory/Factors.hpp | 24 ++++++++++++++++++++++-- 2 files changed, 22 insertions(+), 38 deletions(-) delete mode 100644 NumberTheory/Factors.cpp 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 */ From 6f0b9adc3d8d64f98ce473bf6e1c764ddd0a0585 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Sep 2016 10:23:34 -0400 Subject: [PATCH 08/11] Factors.cpp is dead. --- OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj | 4 ---- 1 file changed, 4 deletions(-) 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 */, From 8c2bf099ad0d0cb0d2a02c02707d0bbcd935dd0f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Sep 2016 10:24:09 -0400 Subject: [PATCH 09/11] Cut down to one GCD and clarified variable names, getting more explicit about what's going on. --- Storage/TimedEventLoop.cpp | 37 +++++++++++++++++++------------------ Storage/TimedEventLoop.hpp | 2 +- 2 files changed, 20 insertions(+), 19 deletions(-) diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index ed102042b..8aa3bc3a6 100644 --- a/Storage/TimedEventLoop.cpp +++ b/Storage/TimedEventLoop.cpp @@ -31,20 +31,13 @@ unsigned int TimedEventLoop::get_cycles_until_next_event() void TimedEventLoop::reset_timer() { - _error.set_zero(); + _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() @@ -55,19 +48,27 @@ void TimedEventLoop::jump_to_next_event() void TimedEventLoop::set_next_event_time_interval(Time interval) { - unsigned int common_divisor = NumberTheory::greatest_common_divisor(_error.clock_rate, interval.clock_rate); - uint64_t denominator = (interval.clock_rate * _error.clock_rate) / common_divisor; - uint64_t numerator = (_error.clock_rate / common_divisor) * _input_clock_rate * interval.length - (interval.clock_rate / common_divisor) * _error.length; + // 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; + // 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; + + // 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); - _error.length = (unsigned int)(numerator % denominator); - _error.clock_rate = (unsigned int)denominator; - _error.simplify(); + _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] + Time zero; + return zero; } diff --git a/Storage/TimedEventLoop.hpp b/Storage/TimedEventLoop.hpp index f8442a6c8..53ac7c07a 100644 --- a/Storage/TimedEventLoop.hpp +++ b/Storage/TimedEventLoop.hpp @@ -89,8 +89,8 @@ namespace Storage { private: unsigned int _input_clock_rate; int _cycles_until_event; + Time _subcycles_until_event; Time _event_interval; - Time _error; }; } From 65b568003d4097e9b7175d21a0ea1abcf410b48f Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Sep 2016 10:29:45 -0400 Subject: [PATCH 10/11] Clarified TODO. --- Storage/TimedEventLoop.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Storage/TimedEventLoop.cpp b/Storage/TimedEventLoop.cpp index 8aa3bc3a6..d420fd082 100644 --- a/Storage/TimedEventLoop.cpp +++ b/Storage/TimedEventLoop.cpp @@ -68,7 +68,7 @@ void TimedEventLoop::set_next_event_time_interval(Time interval) Time TimedEventLoop::get_time_into_next_event() { - // TODO: calculate, presumably as [length of interval] - [cycles left] - [subcycles] + // TODO: calculate, presumably as [length of interval] - ([cycles left] + [subcycles left]) Time zero; return zero; } From 6454508db861fb44634eab43180f26c176686f03 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Sun, 18 Sep 2016 10:30:52 -0400 Subject: [PATCH 11/11] Added a quick bit of documentation. --- Storage/TimedEventLoop.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Storage/TimedEventLoop.hpp b/Storage/TimedEventLoop.hpp index 53ac7c07a..c939a7827 100644 --- a/Storage/TimedEventLoop.hpp +++ b/Storage/TimedEventLoop.hpp @@ -48,6 +48,9 @@ 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: