From 5d6b5f4ce09e2c8f1b2c660b629aa1710db4cb9c Mon Sep 17 00:00:00 2001 From: Allen Hill Date: Thu, 13 Aug 2026 17:47:31 -0700 Subject: [PATCH] Handle flag for NEGATIVE time; unify time incrementing --- src/avclan/cdchanger.cc | 74 ++++++++++++++++++++++------------------ src/avclan/cdchanger.hpp | 5 +-- 2 files changed, 43 insertions(+), 36 deletions(-) diff --git a/src/avclan/cdchanger.cc b/src/avclan/cdchanger.cc index e451ff0..a20891b 100644 --- a/src/avclan/cdchanger.cc +++ b/src/avclan/cdchanger.cc @@ -167,7 +167,7 @@ void CDChanger::handle(const Frame &in, Frame &out) { track = 1; mins = 0xff; secs = 0x7f; - flags2 = 0xc0; + flags2 &= ~NEGATIVE; generateStatus(out, true, Device::CMD_SW); media_action(MediaAction::Track_Next); out.reaction = r_TrackChange; @@ -175,7 +175,7 @@ void CDChanger::handle(const Frame &in, Frame &out) { case Track_Seek_Down: state = SEEKING_TRACK; // Track down returns to track beginning if in ~middle of song - if (mins == 0 && secs < 5) { + if ((flags2 & NEGATIVE) != 0 || (mins == 0 && secs < 5)) { if (track > 1) --track; else @@ -185,17 +185,13 @@ void CDChanger::handle(const Frame &in, Frame &out) { } mins = 0xff; secs = 0x7f; - flags2 = 0xc0; + flags2 &= ~NEGATIVE; generateStatus(out, true, Device::CMD_SW); out.reaction = r_TrackChange; break; case Track_Fast_Forward: { state |= SEEKING; - secs += TIME_SKIP; - if (secs > 59) { - secs -= 60; - ++mins; - } + incrementTime(TIME_SKIP); generateStatus(out, true, Device::CMD_SW); media_action(MediaAction::Skip_Forward); cdtimer_reset(); // Skipped to a whole/round sec; ensure next tick @@ -205,17 +201,7 @@ void CDChanger::handle(const Frame &in, Frame &out) { } case Track_Rewind: { state |= SEEKING; - if (secs < TIME_SKIP) { - if (mins > 0) { - const uint8_t dif = TIME_SKIP - secs; - secs = 60 - dif; - --mins; - } else { - mins = 0; - secs = 0; - } - } else - secs -= TIME_SKIP; + incrementTime(-TIME_SKIP); generateStatus(out, true, Device::CMD_SW); media_action(MediaAction::Skip_Backward); cdtimer_reset(); // Skipped to a whole/round sec; ensure next tick @@ -268,8 +254,8 @@ void CDChanger::handle(const Frame &in, Frame &out) { #pragma GCC diagnostic pop } -std::unique_ptr CDChanger::react( - expected, detail::SendError> exp) { +std::unique_ptr +CDChanger::react(expected, detail::SendError> exp) { if (!exp) { if (exp.error().reaction == to_underlying(r_StateReport) && @@ -277,7 +263,7 @@ std::unique_ptr CDChanger::react( ++failedStatusReports > 1) { failedStatusReports = 0; stopPlaying(); // Disable periodic updates if e.g. no-one's - // listening (car was turned off?) + // listening (car was turned off?) } } else { auto out = std::move(exp.value()); @@ -348,7 +334,7 @@ void CDChanger::enable(Frame &out) { if (secs > TWODIGIT_MAX) secs = 0; state = SEEKING | SEEKING_TRACK; - flags2 = 0xc0; + flags2 = 0x80; generateStatus(out, false, Device::STATUS); out.reaction = r_StartPlaying; } @@ -399,19 +385,40 @@ void CDChanger::setTime(uint8_t min, uint8_t sec) { secs = sec; } -void CDChanger::incrementTime() { +// Increment the time by inc_sec (REQUIRES |inc_sec| <= 59). +void CDChanger::incrementTime(int8_t inc_sec) { // Sentinel values (>TWODIGIT_MAX) mean "no time"; leave them alone until // setTime() replaces them with a real count. - if (secs > TWODIGIT_MAX) + if (mins > TWODIGIT_MAX) return; - if (secs == 59) { - secs = 0; - if (mins == TWODIGIT_MAX) - mins = 0; - else - mins++; - } else - secs++; + + if ((flags2 & NEGATIVE) != 0) + inc_sec = -inc_sec; // time forward shrinks a negative magnitude + int8_t sum = secs + inc_sec; + + if (sum < 0 && mins == 0) { + // Stepped through zero: the display flips sign and counts away from it. + secs = (uint8_t)-sum; + flags2 ^= NEGATIVE; + return; + } + + if (sum > 59) { + if (mins == TWODIGIT_MAX) { // saturate at 99:59 rather than wrap the hour + secs = 59; + return; + } + sum -= 60; + ++mins; + } else if (sum < 0) { + sum += 60; + --mins; // mins > 0: the mins == 0 borrow was handled above + } + secs = (uint8_t)sum; + + // Zero is neither sign, so it must never display as -00:00. + if ((mins | secs) == 0) + flags2 &= ~NEGATIVE; } // Used for changed status messages @@ -439,7 +446,6 @@ void CDChanger::normalizeState() { secs = 0; state = PLAYBACK; flags &= (uint8_t)~(DISK_SCAN | SCAN); - flags2 = 0x80; } #ifndef NDEBUG diff --git a/src/avclan/cdchanger.hpp b/src/avclan/cdchanger.hpp index 4f8733e..72538e8 100644 --- a/src/avclan/cdchanger.hpp +++ b/src/avclan/cdchanger.hpp @@ -39,6 +39,7 @@ public: REPEAT = 1 << 4, DISK_SCAN = 1 << 5, SCAN = 1 << 6, + NEGATIVE = 1 << 6, }; /// Message state machine @@ -68,7 +69,7 @@ public: void disable(Frame &out); static bool pending(); void emit(Frame &out); - void incrementTime(); + void incrementTime(int8_t inc_sec = 1); bool isPlaying() const; #ifndef NDEBUG void media_action(MediaAction action); @@ -93,7 +94,7 @@ private: uint8_t mins = 0xFF; // Decimal storage; serialize to BCD uint8_t secs = 0x7F; // Decimal storage; serialize to BCD uint8_t flags = 0; - uint8_t flags2 = 0xC0; + uint8_t flags2 = 0x80; }; } // namespace avclan