aboutsummaryrefslogtreecommitdiffstats
path: root/usr/time.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-24 23:08:46 +0200
committeruser <user@clank>2026-09-24 23:08:46 +0200
commit4bfe20f88b93645f9a277c9903620d75f0ddc179 (patch)
treeba8af4184f10645fecc59adc9b0b0454658ad278 /usr/time.c
parentterm: ESC[2J, so clear(1) can actually clear (diff)
downloadgbos-4bfe20f88b93645f9a277c9903620d75f0ddc179.tar.gz
gbos-4bfe20f88b93645f9a277c9903620d75f0ddc179.tar.xz
gbos-4bfe20f88b93645f9a277c9903620d75f0ddc179.zip
usr: sort, cmp, strings, du, time
- sort [-r] [-n]: everything in RAM, which bounds it - a task has 8 KiB and a file caps at 3 KiB, so 96 lines of 2400 bytes with room for the stack. Insertion sort over an index; at that size the simple thing is fast and costs far less ROM. `uniq -c f | sort -n -r` finally works. NB the key needs a scratch SLOT of its own: gt() compares table entries, so reusing slot i as scratch overwrites the entries being shifted into it. - cmp: reports the first differing byte, silent when the files match. There was no way to check a copy on the machine - you had to haul both to a host. - strings [-n N]: pairs with xxd now that /bin/<prog> is readable. It finds "gbos sm83 (Game Boy Color)" inside /bin/uname. - du [dir]: 256-byte blocks, the unit df and the filesystem use. Bytes overflow - /bin alone is 52 x 16 KiB, which wrapped a 16-bit counter and printed 32768. Recurses by re-opendir'ing, since the kernel keeps only one "directory being listed" cursor. - time CMD: 1/64 s resolution from the tick counter. It rebuilds the child's command line at $A000 - which is its own argument area, so everything is copied to the stack first.
Diffstat (limited to '')
-rw-r--r--usr/time.c94
1 files changed, 94 insertions, 0 deletions
diff --git a/usr/time.c b/usr/time.c
new file mode 100644
index 0000000..c97fb34
--- /dev/null
+++ b/usr/time.c
@@ -0,0 +1,94 @@
+#include "gbos.h"
+/* time CMD [ARGS]: run a command and report how long it took.
+ *
+ * Reads the kernel's 64 Hz tick before and after, so the resolution is 1/64s.
+ * The child's command line has to be rebuilt at $A000 ("cmd\0args\0", the
+ * shape usr/sh.c leaves) - which is also OUR argument area, so everything is
+ * copied to the stack first. */
+
+#define ARGV0 ((char *)0xA000)
+
+static void two(unsigned int v) {
+ if (v < 10) putc('0');
+ putu(v);
+}
+
+void main(void) {
+ char args[64];
+ char *cmd, *rest;
+ unsigned char t0[4], t1[4], id, i, j;
+ unsigned long ticks;
+ unsigned int sec, hund;
+ union {
+ unsigned long l;
+ unsigned char b[4];
+ } u;
+
+ {
+ char *a = getargs();
+ for (i = 0; a[i] && i < 63; i++) args[i] = a[i];
+ args[i] = 0;
+ }
+ if (!args[0]) {
+ puts("usage: time CMD [ARGS]");
+ nl();
+ sexit(2);
+ }
+
+ cmd = args; /* split "cmd rest..." */
+ i = 0;
+ while (args[i] && args[i] != ' ') i++;
+ rest = args + i;
+ if (args[i]) {
+ args[i] = 0;
+ rest = args + i + 1;
+ }
+
+ id = lookup(cmd);
+ if (id == NOFD) {
+ puts(cmd);
+ puts(": not found");
+ nl();
+ sexit(127);
+ }
+
+ gticks(t0);
+ {
+ char *p = ARGV0; /* rebuild the child's command line */
+ for (j = 0; cmd[j]; j++) *p++ = cmd[j];
+ *p++ = 0;
+ for (j = 0; rest[j]; j++) *p++ = rest[j];
+ *p = 0;
+ }
+ if (fork() == 0) exec(id);
+ wait();
+ gticks(t1);
+
+ /* elapsed ticks, 32-bit, little-endian - no long division (see uptime.c) */
+ u.b[0] = t1[0];
+ u.b[1] = t1[1];
+ u.b[2] = t1[2];
+ u.b[3] = t1[3];
+ ticks = u.l;
+ u.b[0] = t0[0];
+ u.b[1] = t0[1];
+ u.b[2] = t0[2];
+ u.b[3] = t0[3];
+ ticks -= u.l;
+
+ sec = 0;
+ while (ticks >= 64UL) {
+ ticks -= 64UL;
+ sec++;
+ }
+ hund = 0; /* remaining ticks -> hundredths */
+ while (ticks--) hund += 2; /* 1/64 s ~ 0.0156 s ~ 1.56/100 */
+ hund = (unsigned int)(hund - (hund >> 2) - (hund >> 4)); /* ~x1.5625/2 */
+
+ puts("real ");
+ putu(sec);
+ putc('.');
+ two(hund > 99 ? 99 : hund);
+ puts("s");
+ nl();
+}