From bfe94eb268cc2ae0b6e4ce90bbaa6bbaf5855403 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 16 Jan 2023 20:11:42 -0500 Subject: [PATCH 01/12] Seed date and time with current. --- Components/RP5C01/RP5C01.cpp | 21 +++++++++++++++++++-- Components/RP5C01/RP5C01.hpp | 2 +- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 88d8d2001..65cea00f0 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -8,9 +8,26 @@ #include "RP5C01.hpp" +#include + using namespace Ricoh::RP5C01; -RP5C01::RP5C01(HalfCycles clock_rate) : clock_rate_(clock_rate) {} +RP5C01::RP5C01(HalfCycles clock_rate) : clock_rate_(clock_rate) { + // Seed internal clock. + std::time_t now = time(NULL); + std::tm *time_date = localtime(&now); + + seconds_ = + time_date->tm_sec + + time_date->tm_min * 60 + + time_date->tm_hour * 60 * 60; + + day_of_the_week_ = time_date->tm_wday; + day_ = time_date->tm_mday; + month_ = time_date->tm_mon; + year_ = time_date->tm_year % 100; + leap_year_ = time_date->tm_year % 4; +} void RP5C01::run_for(HalfCycles cycles) { sub_seconds_ += cycles; @@ -68,7 +85,7 @@ void RP5C01::run_for(HalfCycles cycles) { if(month_ == 12) { month_ = 0; - ++year_; + year_ = (year_ + 1) % 100; leap_year_ = (leap_year_ + 1) & 3; } } diff --git a/Components/RP5C01/RP5C01.hpp b/Components/RP5C01/RP5C01.hpp index 24c1ad38b..198b3e7a5 100644 --- a/Components/RP5C01/RP5C01.hpp +++ b/Components/RP5C01/RP5C01.hpp @@ -43,7 +43,7 @@ class RP5C01 { int day_of_the_week_ = 0; int day_ = 0; int month_ = 0; - int year_ = 1988; + int year_ = 0; int leap_year_ = 0; // Other flags. From 32b29bd63bf1f1aad6a181f15b621782d0d50818 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 16 Jan 2023 20:26:27 -0500 Subject: [PATCH 02/12] Transcribe all missing registers. --- Components/RP5C01/RP5C01.cpp | 113 ++++++++++++++++++++++++++--------- 1 file changed, 85 insertions(+), 28 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 65cea00f0..7fc3f6429 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -91,43 +91,100 @@ void RP5C01::run_for(HalfCycles cycles) { } } +namespace { + +constexpr int Reg(int mode, int address) { + return address | mode << 4; +} + +} + /// Performs a write of @c value to @c address. void RP5C01::write(int address, uint8_t value) { address &= 0xf; + value &= 0xf; - // Registers D–F don't depend on the mode. - if(address >= 0xd) { - switch(address) { - default: break; - case 0xd: - timer_enabled_ = value & 0x8; - alarm_enabled_ = value & 0x4; - mode_ = value & 0x3; - break; - case 0xe: - // Test register; unclear what is supposed to happen. - break; - case 0xf: - one_hz_on_ = !(value & 0x8); - sixteen_hz_on_ = !(value & 0x4); - // TODO: timer reset on bit 1, alarm reset on bit 0 - break; - } - + // Handle potential RAM accesses. + if(address < 0xd && mode_ >= 2) { + address += mode_ == 3 ? 13 : 0; + ram_[size_t(address)] = value & 0xf; return; } - switch(mode_) { - case 3: - address += 13; - [[fallthrough]]; - case 2: - ram_[size_t(address)] = value & 0xf; - return; + switch(Reg(mode_, address)) { + default: break; + + // Seconds. + case Reg(0, 0x00): + seconds_ = seconds_ - (seconds_ % 10) + (value % 10); + break; + case Reg(0, 0x01): + seconds_ = (seconds_ % 10) + ((value % 6) * 10); + break; + + // TODO: minutes. + case Reg(0, 0x02): + case Reg(0, 0x03): break; + + // TODO: hours. + case Reg(0, 0x04): + case Reg(0, 0x05): break; + + // TODO: day-of-the-week counter. + case Reg(0, 0x06): break; + + // TODO: day counter. + case Reg(0, 0x07): + case Reg(0, 0x08): break; + + // TODO: month counter. + case Reg(0, 0x09): + case Reg(0, 0x0a): break; + + // TODO: year counter. + case Reg(0, 0x0b): + case Reg(0, 0x0c): break; + + // TODO: alarm minutes. + case Reg(1, 0x02): + case Reg(1, 0x03): break; + + // TODO: alarm hours. + case Reg(1, 0x04): + case Reg(1, 0x05): break; + + // TODO: alarm day-of-the-week. + case Reg(1, 0x06): break; + + // TODO: alarm day. + case Reg(1, 0x07): + case Reg(1, 0x08): break; + + // TODO: 12/24 hour select. + case Reg(1, 0x0a): break; + + // TODO: leap-year counter. + case Reg(1, 0x0b): break; + + // + // Registers D–F don't depend on the mode. + // + + case Reg(0, 0xd): case Reg(1, 0xd): case Reg(2, 0xd): case Reg(3, 0xd): + timer_enabled_ = value & 0x8; + alarm_enabled_ = value & 0x4; + mode_ = value & 0x3; + break; + case Reg(0, 0xe): case Reg(1, 0xe): case Reg(2, 0xe): case Reg(3, 0xe): + // Test register; unclear what is supposed to happen. + break; + case Reg(0, 0xf): case Reg(1, 0xf): case Reg(2, 0xf): case Reg(3, 0xf): + one_hz_on_ = !(value & 0x8); + sixteen_hz_on_ = !(value & 0x4); + // TODO: b0 = alarm reset; b1 = timer reset. + break; } - // TODO. - printf("RP-5C01 write of %d to %d in mode %d\n", value, address & 0xf, mode_); } uint8_t RP5C01::read(int address) { From f0db676a10aa933257f3222bc607cb2cfc9fddc7 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 16 Jan 2023 20:29:32 -0500 Subject: [PATCH 03/12] Be consistent in use of C parts. --- Components/RP5C01/RP5C01.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 7fc3f6429..1e28a7c39 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -14,8 +14,8 @@ using namespace Ricoh::RP5C01; RP5C01::RP5C01(HalfCycles clock_rate) : clock_rate_(clock_rate) { // Seed internal clock. - std::time_t now = time(NULL); - std::tm *time_date = localtime(&now); + std::time_t now = std::time(NULL); + std::tm *time_date = std::localtime(&now); seconds_ = time_date->tm_sec + From 55e73cb812fd475baa344d1b042d6816fb012425 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 16 Jan 2023 22:25:20 -0500 Subject: [PATCH 04/12] Implement most of reading. --- Components/RP5C01/RP5C01.cpp | 102 ++++++++++++++++++++++++++++++----- Components/RP5C01/RP5C01.hpp | 1 + 2 files changed, 90 insertions(+), 13 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 1e28a7c39..9d0289a48 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -160,8 +160,9 @@ void RP5C01::write(int address, uint8_t value) { case Reg(1, 0x07): case Reg(1, 0x08): break; - // TODO: 12/24 hour select. - case Reg(1, 0x0a): break; + case Reg(1, 0x0a): + twentyfour_hour_clock_ = value & 1; + break; // TODO: leap-year counter. case Reg(1, 0x0b): break; @@ -190,17 +191,92 @@ void RP5C01::write(int address, uint8_t value) { uint8_t RP5C01::read(int address) { address &= 0xf; - if(address < 0xd) { - switch(mode_) { - case 3: - address += 13; - [[fallthrough]]; - case 2: - return 0xf0 | ram_[size_t(address)]; - } + if(address < 0xd && mode_ >= 2) { + address += mode_ == 3 ? 13 : 0; + return 0xf0 | ram_[size_t(address)]; } - // TODO. - printf("RP-5C01 read from %d in mode %d\n", address & 0xf, mode_); - return 0xff; + int value = 0xf; + switch(Reg(mode_, address)) { + // Second. + case Reg(0, 0x00): value = seconds_ % 10; break; + case Reg(0, 0x01): value = (seconds_ / 10) % 6; break; + + // Minute. + case Reg(0, 0x02): value = (seconds_ / 60) % 10; break; + case Reg(0, 0x03): value = (seconds_ / 600) % 6; break; + + // Hour. + case Reg(0, 0x04): + if(twentyfour_hour_clock_) { + value = (seconds_ / 3600) % 10; + } else { + value = ((seconds_ / 3600) % 12) % 10; + } + break; + case Reg(0, 0x05): + if(twentyfour_hour_clock_) { + value = (seconds_ / 36000); + } else { + value = ((seconds_ / 3600) / 12) + (seconds_ >= 12*60*60 ? 2 : 0); + } + break; + + // Day-of-the-week. + case Reg(0, 0x06): value = day_of_the_week_; break; + + // Day. + case Reg(0, 0x07): value = day_ % 10; break; + case Reg(0, 0x08): value = day_ / 10; break; + + // Month. + case Reg(0, 0x09): value = month_ % 10; break; + case Reg(0, 0x0a): value = month_ / 10; break; + + // Year; + case Reg(0, 0x0b): value = year_ % 10; break; + case Reg(0, 0x0c): value = year_ / 10; break; + + // TODO: alarm minutes. + case Reg(1, 0x02): + case Reg(1, 0x03): break; + + // TODO: alarm hours. + case Reg(1, 0x04): + case Reg(1, 0x05): break; + + // TODO: alarm day-of-the-week. + case Reg(1, 0x06): break; + + // TODO: alarm day. + case Reg(1, 0x07): + case Reg(1, 0x08): break; + + // 12/24-hour clock. + case Reg(1, 0x0a): value = twentyfour_hour_clock_; break; + + // Leap year. + case Reg(1, 0x0b): value = leap_year_; break; + + // + // Registers D–F don't depend on the mode. + // + + case Reg(0, 0xd): case Reg(1, 0xd): case Reg(2, 0xd): case Reg(3, 0xd): + value = + (timer_enabled_ ? 0x8 : 0x0) | + (alarm_enabled_ ? 0x4 : 0x0) | + mode_; + break; + case Reg(0, 0xe): case Reg(1, 0xe): case Reg(2, 0xe): case Reg(3, 0xe): + // Test register; unclear what is supposed to happen. + break; + case Reg(0, 0xf): case Reg(1, 0xf): case Reg(2, 0xf): case Reg(3, 0xf): + value = + (one_hz_on_ ? 0x0 : 0x8) | + (sixteen_hz_on_ ? 0x0 : 0x4); + break; + } + + return uint8_t(0xf0 | value); } diff --git a/Components/RP5C01/RP5C01.hpp b/Components/RP5C01/RP5C01.hpp index 198b3e7a5..ba7e5fe71 100644 --- a/Components/RP5C01/RP5C01.hpp +++ b/Components/RP5C01/RP5C01.hpp @@ -52,6 +52,7 @@ class RP5C01 { int mode_ = 0; bool one_hz_on_ = false; bool sixteen_hz_on_ = false; + bool twentyfour_hour_clock_ = true; }; } From bb6ceafe0eac4aa22438a59efa339a4f14743291 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Mon, 16 Jan 2023 22:31:03 -0500 Subject: [PATCH 05/12] Implement the easy writes. --- Components/RP5C01/RP5C01.cpp | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 9d0289a48..099ab4ef3 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -62,6 +62,7 @@ void RP5C01::run_for(HalfCycles cycles) { while(true) { int month_length = 1; switch(month_) { + default: case 0: month_length = 31; break; case 1: month_length = 28 + !leap_year_; break; case 2: month_length = 31; break; @@ -119,7 +120,8 @@ void RP5C01::write(int address, uint8_t value) { seconds_ = seconds_ - (seconds_ % 10) + (value % 10); break; case Reg(0, 0x01): - seconds_ = (seconds_ % 10) + ((value % 6) * 10); + // TODO. +// seconds_ = (seconds_ % 10) + ((value & 7) * 10); break; // TODO: minutes. @@ -130,20 +132,20 @@ void RP5C01::write(int address, uint8_t value) { case Reg(0, 0x04): case Reg(0, 0x05): break; - // TODO: day-of-the-week counter. - case Reg(0, 0x06): break; + // Day of the week. + case Reg(0, 0x06): day_of_the_week_ = value % 7; break; - // TODO: day counter. - case Reg(0, 0x07): - case Reg(0, 0x08): break; + // Day. + case Reg(0, 0x07): day_ = day_ - (day_ % 10) + value; break; + case Reg(0, 0x08): day_ = (day_ % 10) + ((value & 3) * 10); break; - // TODO: month counter. - case Reg(0, 0x09): - case Reg(0, 0x0a): break; + // Month. + case Reg(0, 0x09): month_ = month_ - (month_ % 10) + value; break; + case Reg(0, 0x0a): month_ = (month_ % 10) + ((value & 1) * 10); break; - // TODO: year counter. - case Reg(0, 0x0b): - case Reg(0, 0x0c): break; + // Year. + case Reg(0, 0x0b): year_ = year_ - (year_ % 10) + value; break; + case Reg(0, 0x0c): year_ = (year_ % 10) + (value * 10); break; // TODO: alarm minutes. case Reg(1, 0x02): @@ -160,12 +162,15 @@ void RP5C01::write(int address, uint8_t value) { case Reg(1, 0x07): case Reg(1, 0x08): break; + // 24/12-hour clock. case Reg(1, 0x0a): twentyfour_hour_clock_ = value & 1; break; - // TODO: leap-year counter. - case Reg(1, 0x0b): break; + // Lead-year counter. + case Reg(1, 0x0b): + leap_year_ = value & 3; + break; // // Registers D–F don't depend on the mode. From f6e601daff95be36ec0b6143aac21028fb567a76 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 17 Jan 2023 13:26:11 -0500 Subject: [PATCH 06/12] Introduce a template for numeric coding. --- Numeric/NumericCoder.hpp | 64 +++++++++++++++++++ .../Clock Signal.xcodeproj/project.pbxproj | 2 + 2 files changed, 66 insertions(+) create mode 100644 Numeric/NumericCoder.hpp diff --git a/Numeric/NumericCoder.hpp b/Numeric/NumericCoder.hpp new file mode 100644 index 000000000..26f906baa --- /dev/null +++ b/Numeric/NumericCoder.hpp @@ -0,0 +1,64 @@ +// +// NumericCoder.hpp +// Clock Signal +// +// Created by Thomas Harte on 17/01/2023. +// Copyright © 2023 Thomas Harte. All rights reserved. +// + +#ifndef NumericCoder_hpp +#define NumericCoder_hpp + +namespace Numeric { + +/// Stores and retrieves an arbitrary number of fields into an int with arbitrary modulos. +/// +/// E.g. NumericEncoder<8, 3, 14> establishes an encoder and decoder for three fields, +/// the first is modulo 8, the second is modulo 3, the third is modulo 14. +/// +/// NumericEncoder<8, 3, 14>::encode<2>(v, 9) will mutate v so that the third field +/// (i.e. field 2) has value 9. +template class NumericCoder { + public: + /// Modifies @c target to hold @c value at @c index. + template static void encode(int &target, int value) { + static_assert(index < sizeof...(Sizes), "Index must be within range"); + NumericEncoderImp::template encode(target, value); + } + /// @returns The value from @c source at @c index. + template static int decode(int source) { + static_assert(index < sizeof...(Sizes), "Index must be within range"); + return NumericEncoderImp::template decode(source); + } + + private: + + template + struct NumericEncoderImp { + template static void encode(int &target, int value) { + if constexpr (i == index) { + const int suffix = target % divider; + target /= divider; + target -= target % size; + target += value % size; + target *= divider; + target += suffix; + } else { + NumericEncoderImp::template encode(target, value); + } + } + + template static int decode(int source) { + if constexpr (i == index) { + return (source / divider) % size; + } else { + return NumericEncoderImp::template decode(source); + } + } + + }; +}; + +} + +#endif /* NumericCoder_hpp */ diff --git a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj index 3844079a6..b807c9c8b 100644 --- a/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj +++ b/OSBindings/Mac/Clock Signal.xcodeproj/project.pbxproj @@ -1430,6 +1430,7 @@ 4B643F3E1D77B88000D431D6 /* DocumentController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DocumentController.swift; sourceTree = ""; }; 4B644ED023F0FB55006C0CC5 /* ScanSynchroniser.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = ScanSynchroniser.hpp; sourceTree = ""; }; 4B65085F22F4CF8D009C1100 /* Keyboard.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = Keyboard.cpp; sourceTree = ""; }; + 4B66E1A8297719270057ED0F /* NumericCoder.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = NumericCoder.hpp; sourceTree = ""; }; 4B670A832401CB8400D4E002 /* z80memptr.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80memptr.tap; sourceTree = ""; }; 4B670A852401CB8400D4E002 /* z80ccf.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80ccf.tap; sourceTree = ""; }; 4B670A872401CB8400D4E002 /* z80flags.tap */ = {isa = PBXFileReference; lastKnownFileType = file; path = z80flags.tap; sourceTree = ""; }; @@ -3304,6 +3305,7 @@ 4BD155312716362A00410C6E /* BitSpread.hpp */, 4B7BA03E23D55E7900B98D9E /* CRC.hpp */, 4B7BA03F23D55E7900B98D9E /* LFSR.hpp */, + 4B66E1A8297719270057ED0F /* NumericCoder.hpp */, 4BB5B995281B1D3E00522DA9 /* RegisterSizes.hpp */, 4BFEA2F12682A90200EBF94C /* Sizes.hpp */, ); From 83cf4497dd5397e95c1069d458bc799fc673ab89 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 17 Jan 2023 17:33:52 -0500 Subject: [PATCH 07/12] Split encode and decode for clearer naming. --- Numeric/NumericCoder.hpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/Numeric/NumericCoder.hpp b/Numeric/NumericCoder.hpp index 26f906baa..a473eaa2f 100644 --- a/Numeric/NumericCoder.hpp +++ b/Numeric/NumericCoder.hpp @@ -23,18 +23,18 @@ template class NumericCoder { /// Modifies @c target to hold @c value at @c index. template static void encode(int &target, int value) { static_assert(index < sizeof...(Sizes), "Index must be within range"); - NumericEncoderImp::template encode(target, value); + NumericEncoder::template encode(target, value); } /// @returns The value from @c source at @c index. template static int decode(int source) { static_assert(index < sizeof...(Sizes), "Index must be within range"); - return NumericEncoderImp::template decode(source); + return NumericDecoder::template decode(source); } private: template - struct NumericEncoderImp { + struct NumericEncoder { template static void encode(int &target, int value) { if constexpr (i == index) { const int suffix = target % divider; @@ -44,18 +44,20 @@ template class NumericCoder { target *= divider; target += suffix; } else { - NumericEncoderImp::template encode(target, value); + NumericEncoder::template encode(target, value); } } + }; + template + struct NumericDecoder { template static int decode(int source) { if constexpr (i == index) { return (source / divider) % size; } else { - return NumericEncoderImp::template decode(source); + return NumericDecoder::template decode(source); } } - }; }; From eb51ed9ae864001d16c659150d87aec72fb25333 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 17 Jan 2023 17:36:15 -0500 Subject: [PATCH 08/12] Shift ownership of initial values. --- Numeric/NumericCoder.hpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Numeric/NumericCoder.hpp b/Numeric/NumericCoder.hpp index a473eaa2f..ab8d48ad8 100644 --- a/Numeric/NumericCoder.hpp +++ b/Numeric/NumericCoder.hpp @@ -23,19 +23,20 @@ template class NumericCoder { /// Modifies @c target to hold @c value at @c index. template static void encode(int &target, int value) { static_assert(index < sizeof...(Sizes), "Index must be within range"); - NumericEncoder::template encode(target, value); + NumericEncoder::template encode(target, value); } + /// @returns The value from @c source at @c index. template static int decode(int source) { static_assert(index < sizeof...(Sizes), "Index must be within range"); - return NumericDecoder::template decode(source); + return NumericDecoder::template decode(source); } private: template struct NumericEncoder { - template static void encode(int &target, int value) { + template static void encode(int &target, int value) { if constexpr (i == index) { const int suffix = target % divider; target /= divider; @@ -51,7 +52,7 @@ template class NumericCoder { template struct NumericDecoder { - template static int decode(int source) { + template static int decode(int source) { if constexpr (i == index) { return (source / divider) % size; } else { From 6f973fc605d6c9502a6ba2040df88ea75a38e927 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 17 Jan 2023 18:53:26 -0500 Subject: [PATCH 09/12] Attempt some use of NumericCoder. --- Components/RP5C01/RP5C01.cpp | 43 ++++++++++++++++++++---------------- 1 file changed, 24 insertions(+), 19 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 099ab4ef3..9f0a9402c 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -8,6 +8,8 @@ #include "RP5C01.hpp" +#include "../../Numeric/NumericCoder.hpp" + #include using namespace Ricoh::RP5C01; @@ -112,40 +114,43 @@ void RP5C01::write(int address, uint8_t value) { return; } + using SecondEncoder = Numeric::NumericCoder< + 10, 6, // Seconds. + 10, 6, // Minutes. + 10, 3 // Hours + >; + using TwoDigitEncoder = Numeric::NumericCoder<10, 10>; + switch(Reg(mode_, address)) { default: break; // Seconds. - case Reg(0, 0x00): - seconds_ = seconds_ - (seconds_ % 10) + (value % 10); - break; - case Reg(0, 0x01): - // TODO. -// seconds_ = (seconds_ % 10) + ((value & 7) * 10); - break; + case Reg(0, 0x00): SecondEncoder::encode<0>(seconds_, value); break; + case Reg(0, 0x01): SecondEncoder::encode<1>(seconds_, value); break; - // TODO: minutes. - case Reg(0, 0x02): - case Reg(0, 0x03): break; + // Minutes. + case Reg(0, 0x02): SecondEncoder::encode<2>(seconds_, value); break; + case Reg(0, 0x03): SecondEncoder::encode<3>(seconds_, value); break; - // TODO: hours. - case Reg(0, 0x04): - case Reg(0, 0x05): break; + // Hours. + // TODO: this isn't correct if the 12-hour clock is selected. + case Reg(0, 0x04): SecondEncoder::encode<4>(seconds_, value); break; + case Reg(0, 0x05): SecondEncoder::encode<5>(seconds_, value); break; // Day of the week. case Reg(0, 0x06): day_of_the_week_ = value % 7; break; // Day. - case Reg(0, 0x07): day_ = day_ - (day_ % 10) + value; break; - case Reg(0, 0x08): day_ = (day_ % 10) + ((value & 3) * 10); break; + case Reg(0, 0x07): TwoDigitEncoder::encode<0>(day_, value); break; + case Reg(0, 0x08): TwoDigitEncoder::encode<1>(day_, value & 3); break; // Month. - case Reg(0, 0x09): month_ = month_ - (month_ % 10) + value; break; - case Reg(0, 0x0a): month_ = (month_ % 10) + ((value & 1) * 10); break; + case Reg(0, 0x09): TwoDigitEncoder::encode<0>(month_, value); break; + case Reg(0, 0x0a): TwoDigitEncoder::encode<1>(month_, value & 1); break; // Year. - case Reg(0, 0x0b): year_ = year_ - (year_ % 10) + value; break; - case Reg(0, 0x0c): year_ = (year_ % 10) + (value * 10); break; + case Reg(0, 0x0b): TwoDigitEncoder::encode<0>(year_, value); break; + case Reg(0, 0x0c): TwoDigitEncoder::encode<1>(year_, value); break; // TODO: alarm minutes. case Reg(1, 0x02): From 0951c50e405a962d4961bb2ab5bff42abd526b9b Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 17 Jan 2023 20:14:32 -0500 Subject: [PATCH 10/12] Further explain. --- Numeric/NumericCoder.hpp | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Numeric/NumericCoder.hpp b/Numeric/NumericCoder.hpp index ab8d48ad8..368838c39 100644 --- a/Numeric/NumericCoder.hpp +++ b/Numeric/NumericCoder.hpp @@ -13,11 +13,21 @@ namespace Numeric { /// Stores and retrieves an arbitrary number of fields into an int with arbitrary modulos. /// -/// E.g. NumericEncoder<8, 3, 14> establishes an encoder and decoder for three fields, +/// E.g. NumericCoder<8, 3, 14> establishes an encoder and decoder for three fields, /// the first is modulo 8, the second is modulo 3, the third is modulo 14. /// -/// NumericEncoder<8, 3, 14>::encode<2>(v, 9) will mutate v so that the third field +/// NumericCoder<8, 3, 14>::encode<2>(v, 9) will mutate v so that the third field /// (i.e. field 2) has value 9. +/// +/// The first given field will occupy the least significant part of the target int; e.g. +/// a NumericCoder<3, 4, 6> with ::encode<0>(v, 2), ::encode<1>(v, 1) and +/// ::encode<2>(v, 5) will have a final value of: +/// +/// [value] 2 + +/// [value] 1 * [product of previous field sizes] 3 + +/// 5 * 12 +/// = 65 +/// template class NumericCoder { public: /// Modifies @c target to hold @c value at @c index. From 194b5bc36aed699278530cab18eacc0ad8210981 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 17 Jan 2023 21:12:00 -0500 Subject: [PATCH 11/12] Attempt to deal with hours correctly. --- Components/RP5C01/RP5C01.cpp | 41 ++++++++++++++++++++++++++++++++---- 1 file changed, 37 insertions(+), 4 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index 9f0a9402c..ee7802419 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -100,6 +100,26 @@ constexpr int Reg(int mode, int address) { return address | mode << 4; } +constexpr int PM = 1 << 4; + +constexpr int twenty_four_to_twelve(int hours) { + switch(hours) { + default: return (hours % 12) + (hours > 12 ? PM : 0); + case 0: return 12; + case 12: return 12 | PM; + } +} + +constexpr int twelve_to_twenty_four(int hours) { + hours = (hours & 0xf) + (hours & PM ? 12 : 0); + switch(hours) { + default: break; + case 24: return 12; + case 12: return 0; + } + return hours; +} + } /// Performs a write of @c value to @c address. @@ -117,7 +137,7 @@ void RP5C01::write(int address, uint8_t value) { using SecondEncoder = Numeric::NumericCoder< 10, 6, // Seconds. 10, 6, // Minutes. - 10, 3 // Hours + 24 // Hours >; using TwoDigitEncoder = Numeric::NumericCoder<10, 10>; @@ -133,9 +153,22 @@ void RP5C01::write(int address, uint8_t value) { case Reg(0, 0x03): SecondEncoder::encode<3>(seconds_, value); break; // Hours. - // TODO: this isn't correct if the 12-hour clock is selected. - case Reg(0, 0x04): SecondEncoder::encode<4>(seconds_, value); break; - case Reg(0, 0x05): SecondEncoder::encode<5>(seconds_, value); break; + case Reg(0, 0x04): + case Reg(0, 0x05): { + int hours = SecondEncoder::decode<4>(seconds_); + if(!twentyfour_hour_clock_) { + hours = twenty_four_to_twelve(hours); + } + if(address == 0x4) { + hours = hours - (hours % 10) + value; + } else { + hours = (hours % 10) + ((value & 3) * 10); + } + if(!twentyfour_hour_clock_) { + hours = twelve_to_twenty_four(hours); + } + SecondEncoder::encode<4>(seconds_, hours); + } break; // Day of the week. case Reg(0, 0x06): day_of_the_week_ = value % 7; break; From 7b25fe5f61edafd00e45acc0f34c05a9b6832614 Mon Sep 17 00:00:00 2001 From: Thomas Harte Date: Tue, 17 Jan 2023 21:18:56 -0500 Subject: [PATCH 12/12] Make `read` consistent. --- Components/RP5C01/RP5C01.cpp | 56 +++++++++++++++++------------------- 1 file changed, 27 insertions(+), 29 deletions(-) diff --git a/Components/RP5C01/RP5C01.cpp b/Components/RP5C01/RP5C01.cpp index ee7802419..8521f7989 100644 --- a/Components/RP5C01/RP5C01.cpp +++ b/Components/RP5C01/RP5C01.cpp @@ -120,6 +120,13 @@ constexpr int twelve_to_twenty_four(int hours) { return hours; } +using SecondEncoder = Numeric::NumericCoder< + 10, 6, // Seconds. + 10, 6, // Minutes. + 24 // Hours +>; +using TwoDigitEncoder = Numeric::NumericCoder<10, 10>; + } /// Performs a write of @c value to @c address. @@ -134,13 +141,6 @@ void RP5C01::write(int address, uint8_t value) { return; } - using SecondEncoder = Numeric::NumericCoder< - 10, 6, // Seconds. - 10, 6, // Minutes. - 24 // Hours - >; - using TwoDigitEncoder = Numeric::NumericCoder<10, 10>; - switch(Reg(mode_, address)) { default: break; @@ -160,9 +160,9 @@ void RP5C01::write(int address, uint8_t value) { hours = twenty_four_to_twelve(hours); } if(address == 0x4) { - hours = hours - (hours % 10) + value; + TwoDigitEncoder::encode<0>(hours, value); } else { - hours = (hours % 10) + ((value & 3) * 10); + TwoDigitEncoder::encode<1>(hours, value & 3); } if(!twentyfour_hour_clock_) { hours = twelve_to_twenty_four(hours); @@ -242,43 +242,41 @@ uint8_t RP5C01::read(int address) { int value = 0xf; switch(Reg(mode_, address)) { // Second. - case Reg(0, 0x00): value = seconds_ % 10; break; - case Reg(0, 0x01): value = (seconds_ / 10) % 6; break; + case Reg(0, 0x00): value = SecondEncoder::decode<0>(seconds_); break; + case Reg(0, 0x01): value = SecondEncoder::decode<1>(seconds_); break; // Minute. - case Reg(0, 0x02): value = (seconds_ / 60) % 10; break; - case Reg(0, 0x03): value = (seconds_ / 600) % 6; break; + case Reg(0, 0x02): value = SecondEncoder::decode<2>(seconds_); break; + case Reg(0, 0x03): value = SecondEncoder::decode<3>(seconds_); break; // Hour. case Reg(0, 0x04): - if(twentyfour_hour_clock_) { - value = (seconds_ / 3600) % 10; - } else { - value = ((seconds_ / 3600) % 12) % 10; + case Reg(0, 0x05): { + int hours = SecondEncoder::decode<4>(seconds_); + if(!twentyfour_hour_clock_) { + hours = twenty_four_to_twelve(hours); } - break; - case Reg(0, 0x05): - if(twentyfour_hour_clock_) { - value = (seconds_ / 36000); + if(address == 0x4) { + value = TwoDigitEncoder::decode<0>(hours); } else { - value = ((seconds_ / 3600) / 12) + (seconds_ >= 12*60*60 ? 2 : 0); + value = TwoDigitEncoder::decode<1>(hours); } - break; + } break; // Day-of-the-week. case Reg(0, 0x06): value = day_of_the_week_; break; // Day. - case Reg(0, 0x07): value = day_ % 10; break; - case Reg(0, 0x08): value = day_ / 10; break; + case Reg(0, 0x07): value = TwoDigitEncoder::decode<0>(day_); break; + case Reg(0, 0x08): value = TwoDigitEncoder::decode<1>(day_); break; // Month. - case Reg(0, 0x09): value = month_ % 10; break; - case Reg(0, 0x0a): value = month_ / 10; break; + case Reg(0, 0x09): value = TwoDigitEncoder::decode<0>(month_); break; + case Reg(0, 0x0a): value = TwoDigitEncoder::decode<1>(month_); break; // Year; - case Reg(0, 0x0b): value = year_ % 10; break; - case Reg(0, 0x0c): value = year_ / 10; break; + case Reg(0, 0x0b): value = TwoDigitEncoder::decode<0>(year_); break; + case Reg(0, 0x0c): value = TwoDigitEncoder::decode<1>(year_); break; // TODO: alarm minutes. case Reg(1, 0x02):