/* Windows port for the microcore: stdin/stdout a byte at a time, the syscalls, diagnostics on stderr. same behaviour as io_win.c plus core.c's hostcall. 1..6 are files, getenv and unix time. 7 exits the process, 8 is a counter, 9 reports the machine size. */ #include #include #include #include #include #include #include #include #include "micro_port.h" #include "bops.h" #ifndef O_BINARY #define O_BINARY _O_BINARY #endif #if defined(_MSC_VER) #define open _open #define close _close #define read _read #define write _write #endif /* the machine's memory: a block that grows by realloc, zero in the new part. the base can move on a grow, which micro.c takes into account (it re-reads m). */ static Cell *lmem; static UCell lcap; Cell *micro_mem_alloc(UCell words) { Cell *n; if (words <= lcap && lmem) return lmem; if (words > ((UCell)-1) / sizeof(Cell)) return NULL; n = realloc(lmem, words * sizeof(Cell)); if (!n) return NULL; memset(n + lcap, 0, (words - lcap) * sizeof(Cell)); lmem = n; lcap = words; return n; } int micro_getc(void) { int c = _fgetc_nolock(stdin); return c == EOF ? -1 : c; } int micro_eof(void) { int c = _fgetc_nolock(stdin); if (c == EOF) return 1; _ungetc_nolock(c, stdin); return 0; } void micro_putc(int c) { _fputc_nolock(c & 255, stdout); } void micro_msg(const char *s) { fputs(s, stderr); } static int zcstr(Cell *mem, Cell addr, char *dst, int max) { int i = 0, c; if (max < 1) return -1; if ((UCell)addr >= (UCell)micro_mem_words()) { dst[0] = 0; return -1; } while (i < max - 1 && (UCell)(addr + i) < (UCell)micro_mem_words()) { c = (int)mem[addr + i]; if (!c) break; dst[i++] = (char)c; } dst[i] = 0; return i; } static int inmem(Cell a) { return (UCell)a < (UCell)micro_mem_words(); } /* the command line and the environment, kept for ops 21 to 24: the same contract as src/core/core.c. a program read from stdin has no file name, so argv[0] is . */ static int zy_argc; static char **zy_argv; static int zy_envc; static char **zy_envp; static void zy_args(int argc, char **argv) { char **e = _environ; int n = 0; while (e && e[n]) n++; zy_envp = e; zy_envc = n; zy_argv = argv; zy_argc = argc > 0 ? argc : 1; } /* copy entry a of a char ** into vm words, one character per word, terminated like SYS_GETENV. a zero buffer asks for the length only. lead, when given, replaces entry zero. */ static Cell vecstr(Cell *mem, char **v, int count, Cell a, Cell b, Cell c, const char *lead) { char *e; int i = 0; if (a < 0 || a >= (Cell)count) return -1; e = (a == 0 && lead) ? (char *)lead : v[a]; if (!e) return -1; if (!b) { while (e[i]) i++; return i; } if (c < 1 || !inmem(b)) return -1; while (e[i] && i < c - 1 && inmem(b + i)) { mem[b + i] = (unsigned char)e[i]; i++; } if (inmem(b + i)) mem[b + i] = 0; return i; } static Cell host_rand(void) { static uint64_t s; if (!s) s = (uint64_t)time(0) ^ 0x9E3779B97F4A7C15ull; if (!s) s = 0x9E3779B97F4A7C15ull; s ^= s << 13; s ^= s >> 7; s ^= s << 17; if ((Cell)s == -1) s ^= 0x100; return (Cell)s; } static int memory_words(const char *name, UCell fallback, UCell *words) { const char *s = getenv(name); char *end; unsigned long long value; if (!s) { *words = fallback; return 1; } errno = 0; value = strtoull(s, &end, 10); if (errno || end == s || *end || !value || value > 0x7fffffffULL) return 0; *words = (UCell)value; return 1; } Cell micro_syscall(Cell *mem, Cell op, Cell a, Cell b, Cell c) { char path[1024], tmp[512]; int n, i, got; switch (op) { case 1: if (zcstr(mem, a, path, 1024) < 0) return -1; if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); return open(path, O_RDONLY | O_BINARY); case 2: return close((int)a); case 3: if (c < 0 || !inmem(b)) return -1; n = 0; while (n < c) { i = (int)(c - n); if (i > 512) i = 512; if (!inmem(b + n)) break; if (!inmem(b + n + i - 1)) i = (int)(micro_mem_words() - (b + n)); if (i <= 0) break; got = (int)read((int)a, tmp, (size_t)i); if (got <= 0) break; for (i = 0; i < got; i++) mem[b + n + i] = (unsigned char)tmp[i]; n += got; } return n; case 4: if (c < 0 || !inmem(b)) return -1; n = 0; while (n < c) { i = (int)(c - n); if (i > 512) i = 512; if (!inmem(b + n)) break; if (!inmem(b + n + i - 1)) i = (int)(micro_mem_words() - (b + n)); if (i <= 0) break; for (got = 0; got < i; got++) tmp[got] = (char)mem[b + n + got]; got = (int)write((int)a, tmp, (size_t)i); if (got <= 0) break; n += got; } return n; case 5: { char *e; if (zcstr(mem, a, path, 1024) < 0) return -1; e = getenv(path); if (!e) return -1; if (c < 1 || !inmem(b)) return -1; i = 0; while (e[i] && i < c - 1 && inmem(b + i)) { mem[b + i] = (unsigned char)e[i]; i++; } if (inmem(b + i)) mem[b + i] = 0; return i; } case 6: return (Cell)time(0); case 7: exit((int)a); case 8: return host_rand(); case 9: if (a == 0) return micro_mem_words(); if (a == 1) return HEAP_LO; if (a == 2) return HEAP_HI; if (a == 3) return (Cell)micro_mem_words() * 8; return -1; case 10: if (bop_cstr(mem, a, path, 1024, micro_mem_words()) < 0) return -1; if (b == 1) return open(path, O_WRONLY | O_CREAT | O_TRUNC | O_BINARY, 0644); return open(path, O_RDONLY | O_BINARY); case 11: if (c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; n = 0; while (n < c) { i = (int)(c - n); if (i > 512) i = 512; i = bop_span(b + n, i, micro_mem_words()); if (i <= 0) break; got = (int)read((int)a, tmp, (size_t)i); if (got <= 0) break; for (i = 0; i < got; i++) bop_put1(mem, b + n + i, (unsigned char)tmp[i]); n += got; } return n; case 12: if (c < 0 || (c > 0 && !bop_ok(b, 1, micro_mem_words()))) return -1; n = 0; while (n < c) { i = (int)(c - n); if (i > 512) i = 512; i = bop_span(b + n, i, micro_mem_words()); if (i <= 0) break; for (got = 0; got < i; got++) tmp[got] = (char)bop_load(mem, b + n + got, 1); got = (int)write((int)a, tmp, (size_t)i); if (got <= 0) break; n += got; } return n; case 13: { char *e; if (bop_cstr(mem, a, path, 1024, micro_mem_words()) < 0) return -1; e = getenv(path); if (!e) return -1; if (c < 1 || !bop_ok(b, 1, micro_mem_words())) return -1; i = 0; while (e[i] && i < c - 1 && bop_ok(b + i, 1, micro_mem_words())) { bop_put1(mem, b + i, (unsigned char)e[i]); i++; } if (bop_ok(b + i, 1, micro_mem_words())) bop_put1(mem, b + i, 0); return i; } case 21: return (Cell)zy_argc; case 22: return vecstr(mem, zy_argv, zy_argc, a, b, c, ""); case 23: return (Cell)zy_envc; case 24: return vecstr(mem, zy_envp, zy_envc, a, b, c, 0); default: return -1; } } int main(int argc, char **argv) { UCell initial, limit; _setmode(_fileno(stdin), _O_BINARY); _setmode(_fileno(stdout), _O_BINARY); zy_args(argc, argv); if (!memory_words("TINYVM_MEMORY_WORDS", MICRO_MW_INIT, &initial) || !memory_words("TINYVM_MAX_MEMORY_WORDS", MICRO_MW_LIMIT, &limit) || initial > limit || !micro_memory_config(initial, limit)) { fputs("micro: invalid memory configuration\n", stderr); return 1; } return micro_run(); }