fs2port/port/tools/chunk5SetupTest.c
2026-05-13 21:32:05 -05:00

209 lines
8.2 KiB
C

// 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 <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#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;
}