Skip to content

Commit a4e24c2

Browse files
Merge pull request #64 from kfox/rtc-serial
firmware: a serial command sets the real-time clock
2 parents c388b82 + e110008 commit a4e24c2

8 files changed

Lines changed: 72 additions & 0 deletions

File tree

‎Source/Teensy/MinimalBoot/Common/Common_Defs.h‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
//synch with win app:
5252
//all commands must start with 0x64
5353
#define SetColorToken 0x6422
54+
#define SetRTCToken 0x6474 //dt
5455
#define LaunchFileToken 0x6444
5556
#define PingToken 0x6455
5657
#define PauseSIDToken 0x6466 //df

‎Source/Teensy/MinimalBoot/Min_SerUSBIO.ino‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ FLASHMEM void ServiceSerial(Stream *ThisCmdChannel)
5252
SendU16(FWMinimalToken);
5353
return;
5454
}
55+
//left unread, the time bytes would each run as a command of their own: a 0x64 can reset or launch
56+
uint32_t RefusedTime;
57+
if (inVal == SetRTCToken) GetUInt(&RefusedTime, 4);
5558
SendU16(FailToken);
5659
CmdChannel->print("Busy!\n");
5760
return;

‎Source/Teensy/RemoteControl.ino‎

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,22 @@ FLASHMEM bool SetColorRef()
111111
return true;
112112
}
113113

114+
// Command:
115+
// Set the TR's real-time clock, which the TR+'s battery keeps through power-off
116+
//
117+
// Workflow:
118+
// Receive <-- SetRTCToken Token 0x6474
119+
// Receive <-- UTC time, seconds since 1970 (4 bytes, big endian)
120+
// Send --> AckToken 0x64CC or FailToken 0x9B7F
121+
FLASHMEM bool SetRTC()
122+
{
123+
uint32_t SecsSince1970;
124+
if (!GetUInt(&SecsSince1970, 4)) return false;
125+
126+
Teensy3Clock.set(SecsSince1970);
127+
return true;
128+
}
129+
114130
// Command:
115131
// Set sub-song number of currently loaded SID
116132
//

‎Source/Teensy/SerUSBIO.ino‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,9 @@ FLASHMEM void ProcessCommand()
617617

618618
if (CurrentIOHandler != IOH_TeensyROM && !ASIDAllowed)
619619
{
620+
//left unread, the time bytes would each run as a command of their own: 'e' resets EEPROM
621+
uint32_t RefusedTime;
622+
if (TokenVal == SetRTCToken) GetUInt(&RefusedTime, 4);
620623
SendU16(FailToken);
621624
CmdChannel->print("Busy!\n");
622625
return;
@@ -666,6 +669,10 @@ FLASHMEM void ProcessCommand()
666669
if(SetColorRef()) SendU16(AckToken);
667670
else SendU16(FailToken);
668671
break;
672+
case SetRTCToken: //Set the real-time clock
673+
if(SetRTC()) SendU16(AckToken);
674+
else SendU16(FailToken);
675+
break;
669676
case DebugToken: //'dg'Test/debug
670677
//for (int a=0; a<256; a++) CmdChannel->printf("\n%3d, // %3d '%c'", ToPETSCII(a), a, a);
671678
//Printf_dbg("isFab2x: %d\n", isFab2x());

‎docs/ControlComms.md‎

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ A C64 reset the TeensyROM doesn't see (reset detect disabled in Settings) doesn'
118118
|GetDirectoryToken | 0x64DD | List directory contents (JSON format - deprecated) |
119119
|GetDirNDJSONToken | 0x64DE | List directory contents (NDJSON format) |
120120
|SetColorToken | 0x6422 | Set a TR UI color value |
121+
|SetRTCToken | 0x6474 | Set the real-time clock |
121122
|PauseSIDToken | 0x6466 | Pause SID playback |
122123
|SetSIDSongToken | 0x6488 | Set sub-song number of currently loaded SID |
123124
|SIDSpeedLinToken | 0x6499 | Set SID playback speed (linear equation) |
@@ -348,6 +349,20 @@ Sets one of the colors in the TR UI. Color is stored in EEPROM and IO space; the
348349

349350
---
350351

352+
### Set Real-Time Clock
353+
Sets the TR's real-time clock. The time is UTC; the TR applies its time zone setting when it shows or passes on the time. On a TR+ with a battery, the clock keeps the time through power-off. The menu's clock shows the new time after its next reset.
354+
355+
**Workflow:**
356+
| Direction | Data |
357+
|---|---|
358+
| Receive | `SetRTCToken` — `0x6474` |
359+
| Receive | UTC time in seconds since 1970-01-01 (4 bytes, big endian) |
360+
| Send | `AckToken 0x64CC` or `FailToken 0x9B7F` |
361+
362+
**Handler:** `SetRTC()`
363+
364+
---
365+
351366
### Pause SID
352367
Pauses/Resumes SID playback by sending an interrupt request to the C64.
353368

‎tools/bench/README.md‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ whatever node appeared beside it.
5656
| `probe.py` | Which image is running -- main, minimal or silent -- and its build banner. |
5757
| `ls.py [path] [drive]` | List a directory, to see that a push landed where it was aimed (first 1000 entries). |
5858
| `reset.py` | Reset the C64 to the menu, and the board out of the minimal image. |
59+
| `setclock.py [UTC time]` | Set the TR's real-time clock, to this computer's clock or an ISO 8601 UTC time such as `2026-10-16T17:00:00`. Only at the TR menu. |
5960
| `hostops.py` | Installing and removing a host, and `run_step` -- driving anything that ends in a reboot and reading which of the two shapes came back. Also the phrases a reboot is judged by: `INSTALLED`, `REMOVED` and `FINISHED_NORMALLY`. Shared by the `host*.py` scripts and by `exttest.py`, whose exit status is `finished_normally()`'s answer, so a phrase edited here moves that verdict; and by `test_bounds.py`, which patches its timeouts to test them, and `test_protocol.py`, which pins the phrases against the firmware's own `VmFail::describe()`. |
6061
| `trlink.py` | The shared library the above are built on. |
6162
| `protocol.py` | Every value that goes on the wire, named once. |

‎tools/bench/protocol.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
POST_FILE = 0x64BB
2222
DELETE_FILE = 0x64CF
2323
GET_DIR_NDJSON = 0x64DE
24+
SET_RTC = 0x6474
2425

2526
# Replies.
2627
ACK = 0x64CC
@@ -49,6 +50,7 @@
4950
'PostFileToken': POST_FILE,
5051
'DeleteFileToken': DELETE_FILE,
5152
'GetDirNDJSONToken': GET_DIR_NDJSON,
53+
'SetRTCToken': SET_RTC,
5254
'AckToken': ACK,
5355
'FailToken': FAIL,
5456
'FWFullToken': FW_FULL,

‎tools/bench/setclock.py‎

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
#!/usr/bin/env python3
2+
# SPDX-License-Identifier: MIT
3+
"""Set the TR's real-time clock. setclock.py [UTC time]
4+
5+
The time is UTC, to the second: this computer's clock, or an ISO 8601 time
6+
such as 2026-10-16T17:00:00. The board refuses it with "Busy!" unless it is
7+
sitting at its own menu.
8+
"""
9+
import datetime, math, sys
10+
from protocol import SET_RTC, to_board
11+
from trlink import Link
12+
13+
if len(sys.argv) > 1:
14+
when = datetime.datetime.fromisoformat(sys.argv[1])
15+
if when.tzinfo is None:
16+
when = when.replace(tzinfo=datetime.timezone.utc)
17+
when = when.astimezone(datetime.timezone.utc)
18+
else:
19+
when = datetime.datetime.now(datetime.timezone.utc)
20+
secs = math.floor(when.timestamp())
21+
if not 0 <= secs < 1 << 32:
22+
raise SystemExit(f'{when:%Y-%m-%d %H:%M:%S} UTC is outside 1970-2106, the range four bytes of seconds hold')
23+
with Link() as tr:
24+
tr.drain(0.3)
25+
tr.wr(to_board(SET_RTC) + to_board(secs, 4))
26+
tr.ack('set clock')
27+
print('clock set to', when.strftime('%Y-%m-%d %H:%M:%S UTC'))

0 commit comments

Comments
 (0)