209 lines
8.2 KiB
C
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;
|
|
}
|