Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Source/Teensy/MinimalBoot/Common/Common_Defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
//synch with win app:
//all commands must start with 0x64
#define SetColorToken 0x6422
#define SetRTCToken 0x6474 //dt
#define LaunchFileToken 0x6444
#define PingToken 0x6455
#define PauseSIDToken 0x6466 //df
Expand Down
3 changes: 3 additions & 0 deletions Source/Teensy/MinimalBoot/Min_SerUSBIO.ino
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ FLASHMEM void ServiceSerial(Stream *ThisCmdChannel)
SendU16(FWMinimalToken);
return;
}
//left unread, the time bytes would each run as a command of their own: a 0x64 can reset or launch
uint32_t RefusedTime;
if (inVal == SetRTCToken) GetUInt(&RefusedTime, 4);
SendU16(FailToken);
CmdChannel->print("Busy!\n");
return;
Expand Down
16 changes: 16 additions & 0 deletions Source/Teensy/RemoteControl.ino
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,22 @@ FLASHMEM bool SetColorRef()
return true;
}

// Command:
// Set the TR's real-time clock, which the TR+'s battery keeps through power-off
//
// Workflow:
// Receive <-- SetRTCToken Token 0x6474
// Receive <-- UTC time, seconds since 1970 (4 bytes, big endian)
// Send --> AckToken 0x64CC or FailToken 0x9B7F
FLASHMEM bool SetRTC()
{
uint32_t SecsSince1970;
if (!GetUInt(&SecsSince1970, 4)) return false;

Teensy3Clock.set(SecsSince1970);
return true;
}

// Command:
// Set sub-song number of currently loaded SID
//
Expand Down
7 changes: 7 additions & 0 deletions Source/Teensy/SerUSBIO.ino
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,9 @@ FLASHMEM void ProcessCommand()

if (CurrentIOHandler != IOH_TeensyROM && !ASIDAllowed)
{
//left unread, the time bytes would each run as a command of their own: 'e' resets EEPROM
uint32_t RefusedTime;
if (TokenVal == SetRTCToken) GetUInt(&RefusedTime, 4);
SendU16(FailToken);
CmdChannel->print("Busy!\n");
return;
Expand Down Expand Up @@ -661,6 +664,10 @@ FLASHMEM void ProcessCommand()
if(SetColorRef()) SendU16(AckToken);
else SendU16(FailToken);
break;
case SetRTCToken: //Set the real-time clock
if(SetRTC()) SendU16(AckToken);
else SendU16(FailToken);
break;
case DebugToken: //'dg'Test/debug
//for (int a=0; a<256; a++) CmdChannel->printf("\n%3d, // %3d '%c'", ToPETSCII(a), a, a);
//Printf_dbg("isFab2x: %d\n", isFab2x());
Expand Down
15 changes: 15 additions & 0 deletions docs/ControlComms.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ A C64 reset the TeensyROM doesn't see (reset detect disabled in Settings) doesn'
|GetDirectoryToken | 0x64DD | List directory contents (JSON format - deprecated) |
|GetDirNDJSONToken | 0x64DE | List directory contents (NDJSON format) |
|SetColorToken | 0x6422 | Set a TR UI color value |
|SetRTCToken | 0x6474 | Set the real-time clock |
|PauseSIDToken | 0x6466 | Pause SID playback |
|SetSIDSongToken | 0x6488 | Set sub-song number of currently loaded SID |
|SIDSpeedLinToken | 0x6499 | Set SID playback speed (linear equation) |
Expand Down Expand Up @@ -348,6 +349,20 @@ Sets one of the colors in the TR UI. Color is stored in EEPROM and IO space; the

---

### Set Real-Time Clock
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.

**Workflow:**
| Direction | Data |
|---|---|
| Receive | `SetRTCToken` — `0x6474` |
| Receive | UTC time in seconds since 1970-01-01 (4 bytes, big endian) |
| Send | `AckToken 0x64CC` or `FailToken 0x9B7F` |

**Handler:** `SetRTC()`

---

### Pause SID
Pauses/Resumes SID playback by sending an interrupt request to the C64.

Expand Down
1 change: 1 addition & 0 deletions tools/bench/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ whatever node appeared beside it.
| `probe.py` | Which image is running -- main, minimal or silent -- and its build banner. |
| `ls.py [path] [drive]` | List a directory, to see that a push landed where it was aimed (first 1000 entries). |
| `reset.py` | Reset the C64 to the menu, and the board out of the minimal image. |
| `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. |
| `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()`. |
| `trlink.py` | The shared library the above are built on. |
| `protocol.py` | Every value that goes on the wire, named once. |
Expand Down
2 changes: 2 additions & 0 deletions tools/bench/protocol.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
POST_FILE = 0x64BB
DELETE_FILE = 0x64CF
GET_DIR_NDJSON = 0x64DE
SET_RTC = 0x6474

# Replies.
ACK = 0x64CC
Expand Down Expand Up @@ -49,6 +50,7 @@
'PostFileToken': POST_FILE,
'DeleteFileToken': DELETE_FILE,
'GetDirNDJSONToken': GET_DIR_NDJSON,
'SetRTCToken': SET_RTC,
'AckToken': ACK,
'FailToken': FAIL,
'FWFullToken': FW_FULL,
Expand Down
27 changes: 27 additions & 0 deletions tools/bench/setclock.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/usr/bin/env python3
# SPDX-License-Identifier: MIT
"""Set the TR's real-time clock. setclock.py [UTC time]

The time is UTC, to the second: this computer's clock, or an ISO 8601 time
such as 2026-10-16T17:00:00. The board refuses it with "Busy!" unless it is
sitting at its own menu.
"""
import datetime, math, sys
from protocol import SET_RTC, to_board
from trlink import Link

if len(sys.argv) > 1:
when = datetime.datetime.fromisoformat(sys.argv[1])
if when.tzinfo is None:
when = when.replace(tzinfo=datetime.timezone.utc)
when = when.astimezone(datetime.timezone.utc)
else:
when = datetime.datetime.now(datetime.timezone.utc)
secs = math.floor(when.timestamp())
if not 0 <= secs < 1 << 32:
raise SystemExit(f'{when:%Y-%m-%d %H:%M:%S} UTC is outside 1970-2106, the range four bytes of seconds hold')
with Link() as tr:
tr.drain(0.3)
tr.wr(to_board(SET_RTC) + to_board(secs, 4))
tr.ack('set clock')
print('clock set to', when.strftime('%Y-%m-%d %H:%M:%S UTC'))
Loading