/* test_host_rom_nucleus.c -- the proof: the ROM rebuilds today's nucleus, word for word. * * Plan Task 8; MACHINE.md section 6, step 1. Node A is built as * v4/tools/mkimage.c builds the nucleus: text.c assembles v4/capsule/ *.v4, * then the switch-on state is set in C. Node B is the machine with its ROM, * fed the system's blobs -- capsules/v4/sys/map-.f18, the fourteen * .f18 files made from the .v4 ones, and end.f18 -- through the console. * When the ROM starts the system, every word of B's RAM must be A's, except * the ROM's own state area and the image record (which A does not have, and * which must hold A's addresses). It also reports what the assembly cost: * instructions retired and seconds (Captain Bob: watch the startup cost). */ #include "rom_harness.h" #include "v4/text.h" #include #include static int failures = 0, checks = 0; #define CHECK(c,...) do{checks++; if(!(c)){failures++; printf("FAIL %s:%d: ",__FILE__,__LINE__); printf(__VA_ARGS__); printf("\n");}}while(0) static v4_node a, b; static v4_text tx; static const char *const files[] = { "core", "input", "dict", "codegen", "compile", "quit", "forth", "numout", "words", "system", "qmath", "blocks", "log", "acl" }; /* node A: mkimage.c's main, as it is */ static int build_a(void) { static const char *const v4files[] = { "core.v4", "input.v4", "dict.v4", "codegen.v4", "compile.v4", "quit.v4", "forth.v4", "numout.v4", "words.v4", "system.v4", "qmath.v4", "blocks.v4", "log.v4", "acl.v4" }; v4_cell k; if (!host_load(&tx, &a, v4files, 14) || !v4_text_finish(&tx)) return 0; a.mem[DP] = DICT_W * 4; a.mem[LATEST] = v4_text_latest(&tx); a.mem[STATE] = 0; a.mem[CFP] = CFS_W; a.mem[BASE] = 10; a.mem[FENCE] = DICT_W; a.mem[LOG_LEVEL] = 2; a.mem[CONTEXT] = LATEST; a.mem[CURRENT] = LATEST; a.mem[SRC] = TIB; a.mem[BOOT_CELLS] = a.mem[DP]; a.mem[BOOT_CELLS + 1] = a.mem[LATEST]; a.mem[FENCE] = (a.mem[DP] + 3) / 4; a.mem[NODE_ERROR] = 0; a.mem[BLK] = 0; a.mem[SCR] = 0; a.mem[SRC] = TIB; a.mem[SRC_HOOK] = 0; for (k = BVARS; k < BVARS + 6; k++) a.mem[k] = 0; for (k = BUF0_W; k < BUF0_W + (v4_cell)(V4_BLOCK_SLOTS * V4_BLOCK_CELLS); k++) a.mem[k] = 0; for (k = TIB_W; k < TIB_W + 260; k++) a.mem[k] = 0; for (k = WBUF_W; k < WBUF_W + WBUF_CELLS; k++) a.mem[k] = 0; for (k = PAD_W; k < PAD_W + 21; k++) a.mem[k] = 0; a.mem[TO_IN] = 0; a.mem[SPAN] = 0; a.mem[LINE_STATUS] = 1; HOST_MESSAGES_EMPTY(&a); return 1; } /* the ROM's own cells, and the image record: not compared */ static int excluded(v4_cell w) { return (w >= OUT_W - 144 && w < OUT_W - 64) || (w >= BUF0_W - 32 && w < BUF0_W - 22) || (w >= BUF0_W - 122 && w < BUF0_W - 119) || (w >= ROM_IMG && w < ROM_IMG + 4); } static char *slurp(const char *name, size_t *len) { char path[512]; FILE *f; char *t; long n; snprintf(path, sizeof path, "%s/%s", V4_SYS_DIR, name); f = fopen(path, "rb"); if (!f) { printf(" cannot read %s\n", path); return NULL; } fseek(f, 0, SEEK_END); n = ftell(f); fseek(f, 0, SEEK_SET); t = malloc((size_t)n + 2); if (fread(t, 1, (size_t)n, f) != (size_t)n) { fclose(f); free(t); return NULL; } fclose(f); t[n] = '\n'; t[n + 1] = 0; /* a blob that does not end with a new line still ends a token */ *len = (size_t)n + 1; return t; } int main(void) { static char *text; size_t total = 0, cap = 1u << 20, len, fed = 0; char name[32]; unsigned i; long words = 0; clock_t t0, t1; v4_cell w, idle_a; CHECK(build_a(), "node A builds as mkimage.c builds it: %s", v4_text_error(&tx)); idle_a = v4_text_word(&tx, "(IDLE)"); /* the system's source, as the loader will feed it: map, the fourteen, end, EOT */ text = malloc(cap); snprintf(name, sizeof name, "map-%d.f18", V4_CELL_BITS); for (i = 0; i < 16; i++) { char *t; if (i == 0) t = slurp(name, &len); else if (i <= 14) { char f18[32]; snprintf(f18, sizeof f18, "%s.f18", files[i - 1]); t = slurp(f18, &len); } else t = slurp("end.f18", &len); if (!t) { CHECK(0, "a blob is missing"); return 1; } memcpy(text + total, t, len); total += len; free(t); } text[total++] = 4; rom_boot(&b); v4_node_stack_regs_attach(&b, DSTACK_REG, RSTACK_REG); b.p = V4_ROM_BASE + 12; t0 = clock(); while (b.p >= V4_ROM_BASE && words < 400000000L && !b.stopped) { if (fed < total) fed += v4_node_console_feed(&b, text + fed, (unsigned)(total - fed)); (void)v4_exec_step_word(&b, &rom_es, &rom_h); words++; if (words % 1000 == 0 && rom_in(&b, "(ROM-FAILED)")) break; } t1 = clock(); CHECK(b.p < V4_ROM_BASE, "the ROM started the system: P %ld, failed %ld at line %ld", (long)b.p, (long)b.mem[ROM_FAILED], (long)b.mem[ROM_ERRLINE] + 1); printf(" the ROM assembled the system: %lu instructions, %lu instruction words, %.2f s (%d-bit cells)\n", (unsigned long)rom_es.anticlock, (unsigned long)words, (double)(t1 - t0) / CLOCKS_PER_SEC, V4_CELL_BITS); CHECK(b.p == idle_a, "it starts at (IDLE): %ld, mkimage's %ld", (long)b.p, (long)idle_a); CHECK(b.mem[ROM_IMG] == idle_a && b.mem[ROM_IMG + 1] == v4_text_word(&tx, "(FAULTS)") && b.mem[ROM_IMG + 2] == v4_text_word(&tx, "KEY") && b.mem[ROM_IMG + 3] == v4_text_word(&tx, "CR"), "the image record holds mkimage's addresses"); for (w = 0; w < (v4_cell)V4_NODE_WORDS; w++) { if (excluded(w) || a.mem[w] == b.mem[w]) continue; CHECK(0, "RAM word %ld: mkimage %lx, ROM %lx", (long)w, (unsigned long)a.mem[w], (unsigned long)b.mem[w]); break; } CHECK(w == (v4_cell)V4_NODE_WORDS, "every word of RAM is the same"); printf("test_host_rom_nucleus: %d checks, %d failures\n", checks, failures); return failures != 0; }