diff options
| author | user <user@clank> | 2026-09-24 23:08:46 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-24 23:08:46 +0200 |
| commit | 4bfe20f88b93645f9a277c9903620d75f0ddc179 (patch) | |
| tree | ba8af4184f10645fecc59adc9b0b0454658ad278 /usr/time.c | |
| parent | term: ESC[2J, so clear(1) can actually clear (diff) | |
| download | gbos-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.c | 94 |
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(); +} |
