/* rom_harness.h -- run the ROM's routines on a host node, for tests. * * The ROM (capsules/v4/rom, built by tools/mkrom.c into $(BINDIR)/rom-.c) * is attached to a host node as the machine's ROM. A routine is called * through the ROM's entry table, as a payload calls it (MACHINE.md 2.1): the * table's order is fixed, and is rom_entries[] below. Where a test needs to * know whether P is inside a routine, the ROM's symbols are read from * $(BINDIR)/rom-.sym (V4_ROM_SYM). */ #ifndef V4_TESTS_ROM_HARNESS_H #define V4_TESTS_ROM_HARNESS_H #include "v4/testcode.h" #include "host_map.h" #include "v4/sys_files.h" #include "v4/rom_entries.h" #include #include #include /* A mesh node's ROM (MACHINE.md 2.2): the shared math only. */ #if V4_CELL_BITS == 64 extern const v4_cell v4_mesh_rom_64[]; extern const unsigned v4_mesh_rom_64_words; #define MESH_ROM v4_mesh_rom_64 #define MESH_ROM_WORDS v4_mesh_rom_64_words #else extern const v4_cell v4_mesh_rom_32[]; extern const unsigned v4_mesh_rom_32_words; #define MESH_ROM v4_mesh_rom_32 #define MESH_ROM_WORDS v4_mesh_rom_32_words #endif /* The entry table, in its fixed order: entry i is at V4_ROM_BASE + i. */ #define ROM_MATH_NAME(i, routine, forth, effect) routine, static const char *const rom_entries[] = { V4_ROM_MATH_ENTRIES(ROM_MATH_NAME) "(KEY)", "(TOKEN)", "(NUMBER)", "(FIND)", "(DEFINE)", "(OPCODE)", "(OP)", "(LIT)", "(BRANCH)", "(BRANCH>)", "(RESOLVE)", "(LABEL)", "(ROM)" }; #undef ROM_MATH_NAME #define ROM_ENTRIES (sizeof rom_entries / sizeof rom_entries[0]) static v4_exec_state rom_es; static v4_heat rom_h; /* The address a payload calls for the routine `name`: its entry. */ static v4_cell rom_entry(const char *name) { unsigned i; for (i = 0; i < ROM_ENTRIES; i++) if (strcmp(rom_entries[i], name) == 0) return V4_ROM_BASE + (v4_cell)i; printf("rom_harness: %s is not in the entry table\n", name); return -1; } /* Call the routine through its entry. 1 if it returned within `max_words` * instruction words; 0 if not, and the node is left where it got to, to be * fed and resumed. */ static int rom_call(v4_node *n, const char *name, long max_words) { return v4_test_call(n, &rom_es, &rom_h, rom_entry(name), max_words) >= 0; } /* Go on with a call that had not returned. 1 if it now has. */ static int rom_resume(v4_node *n, long max_words) { long words = 0; while (n->p != V4_TEST_HALT) { if (words++ >= max_words || n->stopped) return 0; (void)v4_exec_step_word(n, &rom_es, &rom_h); } return 1; } /* 1 if P is inside the ROM's word `name`: at or after it, and before the * next word the ROM has. */ static int rom_in(const v4_node *n, const char *name) { FILE *f = fopen(V4_ROM_SYM, "r"); char sym[80]; long addr, start = -1, next = -1; if (!f) { printf("rom_harness: cannot read %s\n", V4_ROM_SYM); return 0; } while (fscanf(f, "%79s %ld", sym, &addr) == 2) if (strcmp(sym, name) == 0) start = addr; rewind(f); while (fscanf(f, "%79s %ld", sym, &addr) == 2) if (start >= 0 && addr > start && (next < 0 || addr < next)) next = addr; fclose(f); if (start < 0) return 0; if (next < 0) next = (long)V4_ROM_BASE + (long)ROM_IMAGE_WORDS; return (long)n->p >= start && (long)n->p < next; } static char *rom_system_text(size_t *len); static int rom_build_system(v4_node *n, long *words); /* Reset `n`, attach the ROM and the console's receive registers. */ static void rom_boot(v4_node *n) { (void)rom_build_system; (void)host_load; /* host_map.h's loader is not used here */ (void)host_branch_slots; (void)rom_call; /* not every test uses every helper */ (void)rom_resume; (void)rom_in; v4_node_reset(n); v4_exec_reset(&rom_es); v4_heat_reset(&rom_h); v4_node_rom_attach(n, ROM_IMAGE, ROM_IMAGE_WORDS); v4_node_console_input_attach(n, CONSOLE_RX, CONSOLE_ST); } /* THE SYSTEM, AS THE MACHINE BUILDS IT (MACHINE.md section 6, step 1). The * system's source, capsules/v4/sys (V4_SYS_DIR): the map, v4/sys_files.h and * the end, each followed by a new line and V4_ROM_BLOB_END, then EOT -- what v4/system/boot.c * feeds the console. rom_system_text gives it whole, *len its length; * rom_build_system has `n`, booted with its ROM, assemble it, and runs it to * its start: 1 if it started, with *words the instruction words it took. */ #define ROM_SYS_FILE(file) #file, static const char *const rom_system_files[] = { V4_SYS_FILES(ROM_SYS_FILE) }; #undef ROM_SYS_FILE #define ROM_SYS_FILES ((unsigned)(sizeof rom_system_files / sizeof rom_system_files[0])) static char *rom_system_text(size_t *len) { size_t total = 0, cap = 1u << 20; char *text = malloc(cap); unsigned i; if (!text) return NULL; for (i = 0; i < ROM_SYS_FILES + 2u; i++) { char path[512]; FILE *f; long n; if (i == 0) snprintf(path, sizeof path, "%s/map-%d.f18", V4_SYS_DIR, V4_CELL_BITS); else if (i <= ROM_SYS_FILES) snprintf(path, sizeof path, "%s/%s.f18", V4_SYS_DIR, rom_system_files[i - 1]); else snprintf(path, sizeof path, "%s/end.f18", V4_SYS_DIR); f = fopen(path, "rb"); if (!f) { printf(" cannot read %s\n", path); free(text); return NULL; } fseek(f, 0, SEEK_END); n = ftell(f); fseek(f, 0, SEEK_SET); if (n < 0 || total + (size_t)n + 3 > cap || fread(text + total, 1, (size_t)n, f) != (size_t)n) { fclose(f); free(text); return NULL; } fclose(f); total += (size_t)n; text[total++] = '\n'; /* a blob that does not end with a new line still ends a token */ text[total++] = V4_ROM_BLOB_END; /* and the blob ends, as a file ends for text.c */ } text[total++] = 4; /* EOT */ *len = total; return text; } static int rom_build_system(v4_node *n, long *words) { size_t total, fed = 0; char *text = rom_system_text(&total); long w = 0; if (!text) return 0; rom_boot(n); v4_node_stack_regs_attach(n, DSTACK_REG, RSTACK_REG); n->p = V4_ROM_BASE + (v4_cell)V4_ROM_START; /* (ROM): where the machine starts */ while (n->p >= V4_ROM_BASE && w < 400000000L && !n->stopped) { if (fed < total && n->input_pos == n->input_len) fed += v4_node_console_feed(n, text + fed, (unsigned)(total - fed)); (void)v4_exec_step_word(n, &rom_es, &rom_h); w++; if (w % 1000 == 0 && n->mem[ROM_FAILED] != 0) break; } free(text); n->input_pos = n->input_len = 0; /* what was not read is the source's: boot.c takes it back */ if (words) *words = w; return n->p < V4_ROM_BASE && n->mem[ROM_FAILED] == 0; } #endif /* V4_TESTS_ROM_HARNESS_H */