// Validates the C transliteration in chunk5Setup.c against the // fs2trace oracle (which runs the actual chunk5/chunk4 binaries on // a 6502 emulator). #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include "chunk5Setup.h" static int runOracle(const char *cmd, int *out) { FILE *p = popen(cmd, "r"); if (p == NULL) { fprintf(stderr, "popen failed: %s\n", cmd); return -1; } char line[256]; long val = 0; int found = 0; while (fgets(line, sizeof(line), p) != NULL) { char *eq = strrchr(line, '='); if (eq != NULL) { val = strtol(eq + 1, NULL, 10); found = 1; } } pclose(p); if (!found) { return -1; } *out = (int)val; return 0; } static int testScale(int16_t a, int16_t b) { int16_t mine = chunk5ScaleC2ByC4(a, b); char cmd[128]; snprintf(cmd, sizeof(cmd), "/home/scott/claude/flight/port/bin/fs2trace --zpscale %d %d 2>/dev/null", (int)a, (int)b); int oracle; if (runOracle(cmd, &oracle) != 0) { fprintf(stderr, "oracle failed for (%d, %d)\n", (int)a, (int)b); return -1; } if (mine != oracle) { printf(" MISMATCH: ScaleC2ByC4(%6d, %6d) = %6d oracle=%6d delta=%+d\n", (int)a, (int)b, (int)mine, oracle, (int)mine - oracle); return 1; } return 0; } static int testL177B(uint8_t a, uint8_t x) { int16_t mine = chunk5L177B(a, x); char cmd[128]; snprintf(cmd, sizeof(cmd), "/home/scott/claude/flight/port/bin/fs2trace --l177b %d %d 2>/dev/null", (int)a, (int)x); int oracle; if (runOracle(cmd, &oracle) != 0) { fprintf(stderr, "oracle failed for L177B(%d, %d)\n", (int)a, (int)x); return -1; } if (mine != oracle) { printf(" MISMATCH: L177B(%3d, %3d) = %6d oracle=%6d delta=%+d\n", (int)a, (int)x, (int)mine, oracle, (int)mine - oracle); return 1; } return 0; } int main(void) { // Print L177B cos lookup samples for direct inspection. printf("L177B cos lookups (sub=0):\n"); for (int a = 0; a <= 256; a += 32) { int16_t v = chunk5L177B((uint8_t)(a & 0xFF), 0); printf(" L177B(byte=%3d, x=0) = %6d\n", a & 0xFF, v); } int rc = chunk5SetupSelfTest(); printf("\nL177B self-test: %s (case %d)\n", rc == 0 ? "PASS" : "FAIL", -rc); printf("\nScaleC2ByC4 vs fs2trace --zpscale oracle:\n"); struct { int16_t a, b; } cases[] = { { 0, 16384 }, { 16384, 0 }, { 1, 1 }, { -1, -1 }, { 100, 100 }, { -100, 100 }, { 256, 256 }, { 1024, 1024 }, { 4096, 4096 }, { 8192, 8192 }, { 16383, 16383 }, { 16384, 16384 }, { 16383, 32767 }, { 16384, 32767 }, { 32767, 32767 }, { -32768, 32767 }, { 32767, -32768 }, { 1, 32767 }, { 2, 32767 }, { 3, 32767 }, { 10000, 10000 }, { 20000, 30000 }, { -109, 32767 }, { -1234, 5678 }, { 16383, 16384 }, { 16384, 16383 }, }; int fails = 0; for (size_t i = 0; i < sizeof(cases)/sizeof(cases[0]); i++) { if (testScale(cases[i].a, cases[i].b) > 0) { fails++; } } if (fails == 0) { printf(" all %zu cases PASS\n", sizeof(cases)/sizeof(cases[0])); } else { printf(" %d/%zu cases failed\n", fails, sizeof(cases)/sizeof(cases[0])); } // Sweep L177B over byte angles (every 8) and sub-byte values // (every 32). ~256 calls; each is one fs2trace invocation, // total ~5 sec. printf("\nL177B sweep vs fs2trace --l177b oracle:\n"); int l177bFails = 0; int l177bTotal = 0; for (int a = 0; a < 256; a += 8) { for (int x = 0; x < 256; x += 32) { l177bTotal++; if (testL177B((uint8_t)a, (uint8_t)x) > 0) { l177bFails++; } } } if (l177bFails == 0) { printf(" all %d L177B cases PASS\n", l177bTotal); } else { printf(" %d/%d L177B cases failed\n", l177bFails, l177bTotal); } // Sweep the full SetupViewProjection cascade against // fs2trace --matrix. printf("\nSetupViewProjection sweep vs fs2trace --matrix oracle:\n"); struct { int16_t y, p, b; uint8_t vd; } svpCases[] = { { 0, 0, 0, 0 }, { -109, 0, 0, 0 }, { -109, 0, 0, 15 }, { 16384, 0, 0, 0 }, { 0, 16384, 0, 0 }, { 0, 0, 16384, 0 }, { 8000, 4000, 0, 0 }, { 8000, 4000, 0, 3 }, {-12345, 500, 1000, 7 }, }; int svpFails = 0; for (size_t i = 0; i < sizeof(svpCases)/sizeof(svpCases[0]); i++) { int16_t mine[3][3]; chunk5SetupViewProjection(svpCases[i].y, svpCases[i].p, svpCases[i].b, svpCases[i].vd, 0, mine); char cmd[256]; snprintf(cmd, sizeof(cmd), "FS2TRACE_USE_ORIG=1 " "/home/scott/claude/flight/port/bin/fs2trace --matrix " "%d %d %d %d 2>/dev/null", (int)svpCases[i].y, (int)svpCases[i].p, (int)svpCases[i].b, (int)svpCases[i].vd); FILE *p = popen(cmd, "r"); int16_t oracle[3][3] = {{0}}; if (p != NULL) { char buf[256]; int row = 0; while (fgets(buf, sizeof(buf), p) != NULL) { int v0, v1, v2; if (sscanf(buf, " row %*d: %d %d %d", &v0, &v1, &v2) == 3 && row < 3) { oracle[row][0] = (int16_t)v0; oracle[row][1] = (int16_t)v1; oracle[row][2] = (int16_t)v2; row++; } } pclose(p); } bool match = true; for (int r = 0; r < 3; r++) { for (int c = 0; c < 3; c++) { if (mine[r][c] != oracle[r][c]) match = false; } } if (!match) { printf(" MISMATCH (yaw=%d pitch=%d bank=%d vd=%d):\n", svpCases[i].y, svpCases[i].p, svpCases[i].b, svpCases[i].vd); for (int r = 0; r < 3; r++) { printf(" mine row %d: %6d %6d %6d | oracle: %6d %6d %6d\n", r, mine[r][0], mine[r][1], mine[r][2], oracle[r][0], oracle[r][1], oracle[r][2]); } svpFails++; } } if (svpFails == 0) { printf(" all %zu SetupViewProjection cases PASS\n", sizeof(svpCases)/sizeof(svpCases[0])); } else { printf(" %d/%zu SetupViewProjection cases failed\n", svpFails, sizeof(svpCases)/sizeof(svpCases[0])); } return (rc != 0 || fails > 0 || l177bFails > 0 || svpFails > 0) ? 1 : 0; }