aboutsummaryrefslogtreecommitdiffstats
path: root/usr/date.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-24 23:08:54 +0200
committeruser <user@clank>2026-09-24 23:08:54 +0200
commitbe1a8f74e48d4ed2ae3a0b78a7137b4bc41a2969 (patch)
tree25477cc16331b6e919deab918eecfcc4edebc0cb /usr/date.c
parentusr: sort, cmp, strings, du, time (diff)
downloadgbos-master.tar.gz
gbos-master.tar.xz
gbos-master.zip
usr: ntpdate + date - the Game Boy knows what day it isHEADmaster
An MBC5 cart has no RTC, so SNTP is the only route to a wall clock. ntpdate does one 48-byte UDP exchange on port 123 and hands the stamp to the kernel; SYS_SETTIME keeps it with the tick it arrived at, and SYS_GETTIME adds the elapsed ticks back, so date(1) is just formatting. /# ntpdate ntpdate: clock set from pool.ntp.org /# date Sep 24 21:01:23 UTC 2026 (host said 21:01:26) The clock does not survive a power cycle - which is exactly what /rc is for: `ntpdate` there and the machine boots knowing the time. All of it is bounded subtraction, no long division or shifts: SDCC pulls those from sm83.lib, whose modules link into the $A000 RAM window. The month tables are string literals for the same family of reason - an initialized array goes to _INITIALIZED at $A000 and the image overflows its bank ("buffer too small" out of makebin), which is how this first failed to link.
Diffstat (limited to 'usr/date.c')
-rw-r--r--usr/date.c87
1 files changed, 87 insertions, 0 deletions
diff --git a/usr/date.c b/usr/date.c
new file mode 100644
index 0000000..57108ab
--- /dev/null
+++ b/usr/date.c
@@ -0,0 +1,87 @@
+#include "gbos.h"
+/* date: print the wall clock, UTC.
+ *
+ * The kernel hands back unix seconds (SYS_GETTIME); turning that into a date
+ * is all bounded subtraction - no long division or shifts, which SDCC would
+ * pull out of sm83.lib and link into the $A000 RAM window (see usr/uptime.c
+ * and usr/build.sh). ~20k rounds for the day loop is a fraction of a second.
+ *
+ * Says so plainly when the clock has never been set: run ntpdate first. */
+
+/* No initialized arrays: SDCC puts those in _INITIALIZED at $A000, the RAM
+ window, and the image then overflows its bank ("buffer too small" out of
+ makebin - see usr/ansi.c and usr/build.sh). String literals live in _CODE,
+ so the tables are literals indexed by hand. */
+#define MDAYS "\x1f\x1c\x1f\x1e\x1f\x1e\x1f\x1f\x1e\x1f\x1e\x1f"
+#define MNAME "JanFebMarAprMayJunJulAugSepOctNovDec"
+
+static void two(unsigned int v) {
+ if (v < 10) putc('0');
+ putu(v);
+}
+static unsigned char leap(unsigned int y) {
+ if (y & 3) return 0;
+ if (y == 2100) return 0; /* good enough until then */
+ return 1;
+}
+
+void main(void) {
+ unsigned char t[4];
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+ unsigned long sec;
+ unsigned int days = 0, year = 1970, h, m, i, dim;
+
+ if (!gettime(t)) {
+ puts("date: clock not set (run ntpdate)");
+ nl();
+ sexit(1);
+ }
+ u.b[0] = t[0];
+ u.b[1] = t[1];
+ u.b[2] = t[2];
+ u.b[3] = t[3];
+ sec = u.l;
+
+ while (sec >= 86400UL) {
+ sec -= 86400UL;
+ days++;
+ }
+ h = 0;
+ while (sec >= 3600UL) {
+ sec -= 3600UL;
+ h++;
+ }
+ m = 0;
+ while (sec >= 60UL) {
+ sec -= 60UL;
+ m++;
+ }
+
+ for (;;) { /* years, then months */
+ unsigned int len = leap(year) ? 366 : 365;
+ if (days < len) break;
+ days -= len;
+ year++;
+ }
+ for (i = 0; i < 12; i++) {
+ dim = (unsigned char)MDAYS[i];
+ if (i == 1 && leap(year)) dim = 29;
+ if (days < dim) break;
+ days -= dim;
+ }
+ writes(MNAME + i * 3, 3);
+ putc(' ');
+ putu(days + 1);
+ putc(' ');
+ two(h);
+ putc(':');
+ two(m);
+ putc(':');
+ two((unsigned int)sec);
+ puts(" UTC ");
+ putu(year);
+ nl();
+}