diff options
-rw-r--r-- | src/ahoy/config.h | 13 | ||||
-rw-r--r-- | src/ahoy/emulator.c | 10 | ||||
-rw-r--r-- | src/ahoy/main.c | 55 |
3 files changed, 71 insertions, 7 deletions
diff --git a/src/ahoy/config.h b/src/ahoy/config.h new file mode 100644 index 0000000..9a78f59 --- /dev/null +++ b/src/ahoy/config.h @@ -0,0 +1,13 @@ +#ifndef AHOY_AHOY_CONFIG_H +#define AHOY_AHOY_CONFIG_H + +#include <stdint.h> + +struct config { + /* Valid ranges are 0–UINT16_MAX. >UINT16_MAX is for random seed. */ + uint32_t seed; +}; + +extern struct config cfg; + +#endif /* !AHOY_AHOY_CONFIG_H */ diff --git a/src/ahoy/emulator.c b/src/ahoy/emulator.c index 749e3a2..cffc4a9 100644 --- a/src/ahoy/emulator.c +++ b/src/ahoy/emulator.c @@ -6,6 +6,7 @@ #include <mbstring.h> #include "cerr.h" +#include "config.h" #include "emulator.h" #include "macros.h" @@ -54,9 +55,12 @@ emuinit(struct u8view prog, const char *fn) c8.PC = MEM_RESERVED; memcpy(mem + c8.PC, prog.p, prog.len); - if (clock_gettime(CLOCK_REALTIME, &tp) == -1) - die("clock_gettime"); - srand(tp.tv_sec ^ tp.tv_nsec); + if (cfg.seed > UINT16_MAX) { + if (clock_gettime(CLOCK_REALTIME, &tp) == -1) + die("clock_gettime"); + srand((tp.tv_sec ^ tp.tv_nsec) & UINT16_MAX); + } else + srand(cfg.seed); } void diff --git a/src/ahoy/main.c b/src/ahoy/main.c index e52e6bd..581078b 100644 --- a/src/ahoy/main.c +++ b/src/ahoy/main.c @@ -2,25 +2,41 @@ #include <fcntl.h> #include <getopt.h> -#include <stdint.h> +#include <inttypes.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <builder.h> +#include <mbstring.h> #include <SDL.h> #include "cerr.h" +#include "config.h" #include "emulator.h" #include "gui.h" #include "macros.h" +#if __has_include(<stdckdint.h>) +# include <stdckdint.h> +# warning "stdckdint.h now available; remove manual ckd_*() implementations" +#elifdef __GNUC__ +# define ckd_add(r, a, b) ((bool)__builtin_add_overflow(a, b, r)) +# define ckd_mul(r, a, b) ((bool)__builtin_mul_overflow(a, b, r)) +#else +# error "Platform not supported at the moment" +#endif + #define FPS 60 #define INSTS_PER_SEC 700 [[noreturn]] static void usage(void); static void run(int, const char *); +static uint16_t strtou16(const char8_t *, rune *); +struct config cfg = { + .seed = UINT16_MAX + 1, +}; static const char *argv0; void @@ -35,18 +51,32 @@ main(int argc, char **argv) { int opt; const struct option longopts[] = { - {"help", no_argument, nullptr, 'h'}, - {nullptr, no_argument, nullptr, 0 }, + {"help", no_argument, nullptr, 'h'}, + {"seed", required_argument, nullptr, 's'}, + {nullptr, no_argument, nullptr, 0 }, }; argv0 = argv[0]; cerrinit(*argv); - while ((opt = getopt_long(argc, argv, "h", longopts, nullptr)) != -1) { + while ((opt = getopt_long(argc, argv, "hs:", longopts, nullptr)) != -1) { switch (opt) { case 'h': execlp("man", "man", "1", argv[0], nullptr); die("execlp: man 1 %s", argv[0]); + case 's': { + rune ch; + uint16_t n = strtou16(optarg, &ch); + if (ch >= '0' && ch <= '9') + diex("seed too large; may not exceed %" PRIu16, UINT16_MAX); + else if (ch) { + char8_t buf[U8_LEN_MAX]; + int w = rtou8(buf, ch, sizeof(buf)); + diex("invalid character ‘%.*s’; seed must be numeric", w, buf); + } + cfg.seed = n; + break; + } default: usage(); } @@ -139,3 +169,20 @@ run(int fd, const char *fn) u8strfree(sb); winfree(); } + +uint16_t +strtou16(const char8_t *s, rune *ch) +{ + uint16_t n = 0; + size_t len = strlen(s); + + while (u8next(ch, &s, &len)) { + if (!(*ch >= '0' && *ch <= '9')) + return -1; + if (ckd_mul(&n, n, 10) || ckd_add(&n, n, *ch - '0')) + return -1; + } + + *ch = '\0'; + return n; +} |