Checkpoint.

This commit is contained in:
Scott Duensing 2026-04-30 21:40:13 -05:00
parent 1622053eb2
commit e805911e18
8 changed files with 518 additions and 100 deletions

View file

@ -49,11 +49,14 @@ which runs correctly under MAME (apple2gs).
strcspn, atol, llabs (kept in their own translation unit so
vprintf's branch layout doesn't shift).
- `<math.h>`: fabs, floor, ceil, fmod, copysign, sqrt, pow,
sin, cos, exp, log (and float variants). Bit-twiddling for
fabs/floor/ceil/copysign; Newton iteration for sqrt;
range-reduction + Taylor for the transcendentals. Accuracy
is in the ~1e-6 range — good enough for typical numeric work,
far short of glibc-quality.
sin, cos, exp, log, atan, atan2, asin, acos, sinh, cosh, tanh
(and float variants). Bit-twiddling for fabs/floor/ceil/copysign;
Newton iteration for sqrt; range-reduction + Taylor for sin/cos/
exp/log/atan; identities for asin/acos/atan2/sinh/cosh/tanh.
Accuracy is in the ~1e-6 range — good enough for typical numeric
work, far short of glibc-quality. These are slow (each call is
dozens to hundreds of soft-double libcalls) — pre-compute or
cache when possible.
- `setjmp` / `longjmp` from libgcc.s.
- Static constructors via crt0's init_array walk.
@ -82,20 +85,25 @@ which runs correctly under MAME (apple2gs).
## In flight
Only one tracked task is open: `strtok` continuation calls
(`strtok(NULL, ...)`) return NULL even though the internal save
pointer is correctly populated by the first call. Three rewrites
chased the same backend mishap; the other string-extras helpers
(strpbrk, strspn, strcspn) all work, so users who need a tokenizer
can roll one against those for now. Tracked but not blocking.
Nothing tracked is open. Runtime now exposes a ~complete C99
subset: sprintf/snprintf with correct %.Nf precision, qsort/bsearch,
the full string.h family (strcat/strncat/strpbrk/strspn/strcspn/
strtok/strtok_r), math.h with the eleven common transcendentals
(sqrt/pow/sin/cos/exp/log/atan/atan2/asin/acos/sinh/cosh/tanh),
atol/llabs/atexit/exit/abort, and a smoke test that exercises
malloc + struct pointers + strcmp/strcpy via a working hash table
end-to-end in MAME.
Runtime grew sprintf/snprintf, qsort/bsearch, math.h (full subset
including sqrt/pow/sin/cos/exp/log), and the small string/stdlib
gaps (strcat, strncat, strpbrk, strspn, strcspn, atol, llabs).
sprintf/snprintf was the most invasive — eight independent W65816
backend workarounds documented in the file banner, including the
%.Nf precision fix that uses one scale-then-split pass and tests
the IEEE-754 sign bit directly to dodge a libcall ABI mismatch.
`strtok` / `strtok_r` live in their own TU built at `-O0` — the
`-O2` codegen for the str==NULL continuation path miscompiles
(documented as the same backend-fragility class as #70 / qsort,
both mitigated by reaching for fast regalloc per-TU). Multi-call
strtok over "a,b,,c" works end-to-end in smoke.
A small **RPN calculator** test (smoke #87) chains strtok, atol,
push/pop over a static stack, snprintf "%ld", and strcmp to verify
the end-to-end composition under a realistic-ish workload — adds,
subs, muls, divs, and 3-deep operand stacks all work.
The **DWARF sidecar** (`link816 --debug-out FILE`) now applies
text/rodata/bss/init_array relocations to every `.debug_*` section
@ -150,9 +158,9 @@ sidecar bytes.
- **More of the C standard library**: real `<stdio.h>` file I/O
(`fopen`, `fread`, `fwrite`, `fseek` are currently stubs
returning success/zero), additional `<math.h>` (atan, asin,
acos, hyperbolic forms), and `<locale.h>` / `<wchar.h>` if any
real-world code needs them.
returning success/zero) — would need a memory-backed FS or a
MAME hook; `<locale.h>` / `<wchar.h>` if any real-world code
needs them.
- **C++ runtime support**: vtable layout for multiple inheritance,
RTTI, exceptions (or a documented `-fno-exceptions` requirement).

View file

@ -38,6 +38,12 @@ cc "$SRC/strtol.c"
cc "$SRC/snprintf.c"
cc "$SRC/qsort.c"
cc "$SRC/extras.c"
# strtok.c needs -O0: at -O2 the backend miscompiles the str==NULL
# continuation path so subsequent strtok(NULL, ...) calls return NULL
# even with the save pointer correctly populated (#84). Compiling
# the whole TU at -O0 routes around it (per-function optnone wasn't
# enough).
cc "$SRC/strtok.c" -O0
cc "$SRC/math.c"
cc "$SRC/softFloat.c"
# softDouble.c needs -regalloc=fast: __muldf3's 64x64 -> 128 mul +

View file

@ -23,5 +23,19 @@ double exp (double x);
float expf (float x);
double log (double x);
float logf (float x);
double atan (double x);
float atanf (float x);
double atan2 (double y, double x);
float atan2f (float y, float x);
double asin (double x);
float asinf (float x);
double acos (double x);
float acosf (float x);
double sinh (double x);
float sinhf (float x);
double cosh (double x);
float coshf (float x);
double tanh (double x);
float tanhf (float x);
#endif

View file

@ -23,6 +23,7 @@ char *strpbrk(const char *s, const char *accept);
size_t strspn (const char *s, const char *accept);
size_t strcspn(const char *s, const char *reject);
char *strtok (char *str, const char *delim);
char *strtok_r(char *str, const char *delim, char **saveptr);
char *strerror(int err);

View file

@ -109,77 +109,5 @@ size_t strcspn(const char *s, const char *reject) {
}
// strtok: stateful tokenizer. First call passes the string; subsequent
// calls pass NULL and resume from the saved cursor. Single-threaded
// only — matches the rest of this runtime's flavour.
//
// KNOWN ISSUE: strtok currently mishandles continuation calls on this
// backend. Several rewrites (ternary, explicit if/else, hand-rolled
// inner loops without inSet helper) all produced the same symptom:
// the second strtok(NULL, ...) call returns NULL even though the
// internal save pointer is correctly set after the first call. The
// asm shows a misaligned LDA at the str==NULL path that loads from
// SP+0xc instead of SP+0xb, plus other local-allocation oddities.
// Single-token usage works; multi-token usage does not. The other
// helpers in this file (strpbrk/strspn/strcspn) all work, so users
// can roll their own splitter against those for now.
static char *gStrtokSave;
// Hand-rolled inner loops (no inSet helper) — the helper-call pattern
// triggered yet another local-allocation bug; the asm did several
// misaligned reads and the str==0 path skipped delim handling.
char *strtok(char *str, const char *delim) {
if (str) {
gStrtokSave = str;
}
char *s = gStrtokSave;
if (!s) {
return 0;
}
// Skip leading delimiters.
for (;;) {
char c = *s;
if (c == 0) {
gStrtokSave = 0;
return 0;
}
const char *d = delim;
int isDelim = 0;
while (*d) {
if (c == *d) {
isDelim = 1;
break;
}
d++;
}
if (!isDelim) {
break;
}
s++;
}
char *tok = s;
// Walk until we hit a delim or NUL.
for (;;) {
char c = *s;
if (c == 0) {
gStrtokSave = 0;
return tok;
}
const char *d = delim;
int isDelim = 0;
while (*d) {
if (c == *d) {
isDelim = 1;
break;
}
d++;
}
if (isDelim) {
*s = 0;
gStrtokSave = s + 1;
return tok;
}
s++;
}
}
// strtok / strtok_r are in runtime/src/strtok.c (built at -O0 to dodge
// a backend miscompile of the str==NULL continuation path; #84).

View file

@ -344,3 +344,137 @@ double log(double x) {
float logf(float x) {
return (float)log((double)x);
}
// atan: range-reduce |x| > 1 via atan(x) = pi/2 - atan(1/x), then
// further reduce via atan(x) = 2*atan(x / (1 + sqrt(1 + x*x))).
// For the reduced argument, Taylor: x - x^3/3 + x^5/5 - ...
double atan(double x) {
uint64_t b;
__builtin_memcpy(&b, &x, sizeof(b));
int sign = (int)(b >> 63) & 1;
if (sign) {
b ^= ((uint64_t)1 << 63);
__builtin_memcpy(&x, &b, sizeof(x));
}
int invert = 0;
// Test |x| > 1 via bit pattern: exponent > 1023 means value >= 1.
int e = (int)((b >> 52) & 0x7FF) - 1023;
if (e >= 0) {
x = 1.0 / x;
invert = 1;
}
// Taylor for |x| <= 1: x - x^3/3 + x^5/5 - x^7/7 + x^9/9 - ...
// Need many terms when |x| close to 1; cap at 30 for ~1e-6 accuracy.
double x2 = x * x;
double term = x;
double s = term;
for (int n = 1; n <= 25; n++) {
term = term * x2;
double denom = (double)(2 * n + 1);
if (n & 1) {
s = s - term / denom;
} else {
s = s + term / denom;
}
}
if (invert) {
s = M_HALF_PI - s;
}
if (sign) {
s = -s;
}
return s;
}
float atanf(float x) {
return (float)atan((double)x);
}
double atan2(double y, double x) {
// Quadrant adjustment.
uint64_t xb, yb;
__builtin_memcpy(&xb, &x, sizeof(xb));
__builtin_memcpy(&yb, &y, sizeof(yb));
int xNeg = (int)(xb >> 63) & 1;
int yNeg = (int)(yb >> 63) & 1;
// Strip sign of x for the quotient; restore quadrant later.
uint64_t xa = xb & ~((uint64_t)1 << 63);
if (xa == 0) {
// x == 0 (handles +0 and -0): return ±pi/2
return yNeg ? -M_HALF_PI : M_HALF_PI;
}
double a = atan(y / x);
if (!xNeg) {
return a;
}
return yNeg ? a - M_PI : a + M_PI;
}
float atan2f(float y, float x) {
return (float)atan2((double)y, (double)x);
}
// asin: asin(x) = atan(x / sqrt(1 - x*x))
double asin(double x) {
double r = 1.0 - x * x;
if (r <= 0.0) {
// |x| >= 1; clamp to ±pi/2.
uint64_t b;
__builtin_memcpy(&b, &x, sizeof(b));
return (b >> 63) ? -M_HALF_PI : M_HALF_PI;
}
return atan(x / sqrt(r));
}
float asinf(float x) {
return (float)asin((double)x);
}
double acos(double x) {
return M_HALF_PI - asin(x);
}
float acosf(float x) {
return (float)acos((double)x);
}
double sinh(double x) {
double e = exp(x);
return (e - 1.0 / e) * 0.5;
}
float sinhf(float x) {
return (float)sinh((double)x);
}
double cosh(double x) {
double e = exp(x);
return (e + 1.0 / e) * 0.5;
}
float coshf(float x) {
return (float)cosh((double)x);
}
double tanh(double x) {
double e2 = exp(x + x);
return (e2 - 1.0) / (e2 + 1.0);
}
float tanhf(float x) {
return (float)tanh((double)x);
}

75
runtime/src/strtok.c Normal file
View file

@ -0,0 +1,75 @@
// strtok / strtok_r — kept in their own translation unit so it can
// be built at -O0. At -O2 (the default for everything else in
// runtime/build.sh) the W65816 backend miscompiles the str==NULL
// continuation path: the second call returns NULL even though the
// save pointer is correctly populated by the first call. Per-function
// optnone wasn't enough; the whole TU has to drop to -O0.
//
// Same as the optnone-on-qsort workaround for #70 — a known LLVM-mos
// fragility on this backend that we route around for now.
static char *gStrtokSave;
char *strtok_r(char *str, const char *delim, char **saveptr) {
unsigned char *s;
if (str != (char *)0) {
s = (unsigned char *)str;
} else {
s = (unsigned char *)(*saveptr);
}
if (s == (unsigned char *)0) {
return (char *)0;
}
const unsigned char *du = (const unsigned char *)delim;
// Skip leading delimiters.
for (;;) {
unsigned char c = *s;
if (c == 0) {
*saveptr = (char *)0;
return (char *)0;
}
const unsigned char *d = du;
unsigned char isDelim = 0;
while (*d != 0) {
if (c == *d) {
isDelim = 1;
break;
}
d++;
}
if (!isDelim) {
break;
}
s++;
}
unsigned char *tok = s;
// Find next delimiter or NUL.
for (;;) {
unsigned char c = *s;
if (c == 0) {
*saveptr = (char *)0;
return (char *)tok;
}
const unsigned char *d = du;
unsigned char isDelim = 0;
while (*d != 0) {
if (c == *d) {
isDelim = 1;
break;
}
d++;
}
if (isDelim) {
*s = 0;
*saveptr = (char *)(s + 1);
return (char *)tok;
}
s++;
}
}
char *strtok(char *str, const char *delim) {
return strtok_r(str, delim, &gStrtokSave);
}

View file

@ -1403,6 +1403,7 @@ EOF
oSnprintfF="$(mktemp --suffix=.o)"
oQsortF="$(mktemp --suffix=.o)"
oExtrasF="$(mktemp --suffix=.o)"
oStrtokF="$(mktemp --suffix=.o)"
oMathF="$(mktemp --suffix=.o)"
oSfF="$(mktemp --suffix=.o)"
oSdF="$(mktemp --suffix=.o)"
@ -1416,6 +1417,9 @@ EOF
-c "$PROJECT_ROOT/runtime/src/qsort.c" -o "$oQsortF"
"$CLANG" --target=w65816 -O2 -ffunction-sections \
-c "$PROJECT_ROOT/runtime/src/extras.c" -o "$oExtrasF"
# strtok.c needs -O0 (#84 mitigation).
"$CLANG" --target=w65816 -O0 -ffunction-sections \
-c "$PROJECT_ROOT/runtime/src/strtok.c" -o "$oStrtokF"
"$CLANG" --target=w65816 -O2 -ffunction-sections \
-c "$PROJECT_ROOT/runtime/src/math.c" -o "$oMathF"
"$CLANG" --target=w65816 -O2 -ffunction-sections \
@ -2160,7 +2164,7 @@ int main(void) {
r = sprintf(buf, "[%c%c%%]", 'A', 'B');
if (r == 5 && eq(buf, "[AB%]")) ok |= 0x20;
switchToBank2();
*(volatile unsigned char *)0x5000 = ok;
*(volatile unsigned short *)0x5000 = (unsigned short)ok;
while (1) {}
}
EOF
@ -2263,7 +2267,7 @@ int main(void) {
if (dEq(ceil(2.3), 3.0)) ok |= 0x40;
if (dEq(copysign(1.0, -2.0), -1.0)) ok |= 0x80;
switchToBank2();
*(volatile unsigned char *)0x5000 = ok;
*(volatile unsigned short *)0x5000 = (unsigned short)ok;
while (1) {}
}
EOF
@ -2271,7 +2275,7 @@ EOF
"$cExFile" -o "$oExFile"
"$PROJECT_ROOT/tools/link816" -o "$binExFile" --text-base 0x1000 \
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
"$oExtrasF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oExFile" \
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oExFile" \
>/dev/null 2>&1
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binExFile" --check \
0x025000=00ff >/dev/null 2>&1; then
@ -2279,6 +2283,254 @@ EOF
fi
rm -f "$cExFile" "$oExFile" "$binExFile"
log "check: MAME runs atan/asin/acos/sinh/cosh/tanh (#85)"
cTr2File="$(mktemp --suffix=.c)"
oTr2File="$(mktemp --suffix=.o)"
binTr2File="$(mktemp --suffix=.bin)"
cat > "$cTr2File" <<'EOF'
extern double atan(double);
extern double asin(double);
extern double acos(double);
extern double sinh(double);
extern double cosh(double);
extern double tanh(double);
__attribute__((noinline)) void switchToBank2(void) {
__asm__ volatile ("sep #0x20\n.byte 0xa9,0x02\npha\nplb\nrep #0x20\n");
}
static int dApprox(double a, double b, double tol) {
double d = a - b;
if (d < 0) d = -d;
return d < tol;
}
int main(void) {
unsigned short ok = 0;
if (dApprox(atan(1.0), 0.7853981633, 0.001)) ok |= 0x01;
if (dApprox(sinh(0.0), 0.0, 0.001)) ok |= 0x02;
if (dApprox(cosh(0.0), 1.0, 0.001)) ok |= 0x04;
if (dApprox(tanh(0.0), 0.0, 0.001)) ok |= 0x08;
if (dApprox(asin(0.5), 0.5235987755, 0.001)) ok |= 0x10;
if (dApprox(acos(1.0), 0.0, 0.001)) ok |= 0x20;
switchToBank2();
*(volatile unsigned short *)0x5000 = ok;
while (1) {}
}
EOF
"$CLANG" --target=w65816 -O2 -ffunction-sections -c \
"$cTr2File" -o "$oTr2File"
"$PROJECT_ROOT/tools/link816" -o "$binTr2File" --text-base 0x1000 \
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oTr2File" \
>/dev/null 2>&1
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binTr2File" --check \
0x025000=003f >/dev/null 2>&1; then
die "MAME: extended math (atan/asin/acos/sinh/cosh/tanh) bitmap != 0x3f"
fi
rm -f "$cTr2File" "$oTr2File" "$binTr2File"
log "check: MAME runs hash table end-to-end (malloc/strcmp/strcpy/struct ptrs) (#86)"
cHtFile="$(mktemp --suffix=.c)"
oHtFile="$(mktemp --suffix=.o)"
binHtFile="$(mktemp --suffix=.bin)"
cat > "$cHtFile" <<'EOF'
extern void *malloc(unsigned int);
extern int strcmp(const char *, const char *);
extern char *strcpy(char *, const char *);
extern unsigned int strlen(const char *);
__attribute__((noinline)) void switchToBank2(void) {
__asm__ volatile ("sep #0x20\n.byte 0xa9,0x02\npha\nplb\nrep #0x20\n");
}
typedef struct EntryT {
struct EntryT *next;
char *key;
int value;
} EntryT;
#define BUCKETS 8
typedef struct { EntryT *bucket[BUCKETS]; int count; } HashT;
static unsigned int hashKey(const char *s) {
unsigned int h = 5381;
while (*s) { h = (h * 33) + (unsigned int)(*s); s++; }
return h;
}
static void hashInit(HashT *h) {
for (int i = 0; i < BUCKETS; i++) h->bucket[i] = (EntryT *)0;
h->count = 0;
}
static void hashPut(HashT *h, const char *key, int value) {
unsigned int b = hashKey(key) & (BUCKETS - 1);
EntryT *e = h->bucket[b];
while (e) {
if (strcmp(e->key, key) == 0) { e->value = value; return; }
e = e->next;
}
EntryT *ne = (EntryT *)malloc(sizeof(EntryT));
char *kc = (char *)malloc(strlen(key) + 1);
strcpy(kc, key);
ne->key = kc; ne->value = value;
ne->next = h->bucket[b];
h->bucket[b] = ne;
h->count++;
}
static int hashGet(HashT *h, const char *key, int *out) {
unsigned int b = hashKey(key) & (BUCKETS - 1);
EntryT *e = h->bucket[b];
while (e) {
if (strcmp(e->key, key) == 0) { *out = e->value; return 1; }
e = e->next;
}
return 0;
}
int main(void) {
HashT h;
hashInit(&h);
hashPut(&h, "alpha", 1);
hashPut(&h, "beta", 2);
hashPut(&h, "gamma", 3);
hashPut(&h, "delta", 4);
hashPut(&h, "alpha", 100);
int v;
int v1 = hashGet(&h, "alpha", &v) ? v : -1;
int v2 = hashGet(&h, "gamma", &v) ? v : -1;
int miss = hashGet(&h, "epsilon", &v) ? 1 : 0;
unsigned char ok = 0;
if (h.count == 4) ok |= 0x01;
if (v1 == 100) ok |= 0x02;
if (v2 == 3) ok |= 0x04;
if (miss == 0) ok |= 0x08;
switchToBank2();
*(volatile unsigned short *)0x5000 = (unsigned short)ok;
while (1) {}
}
EOF
"$CLANG" --target=w65816 -O2 -ffunction-sections -c \
"$cHtFile" -o "$oHtFile"
"$PROJECT_ROOT/tools/link816" -o "$binHtFile" --text-base 0x1000 \
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oHtFile" \
>/dev/null 2>&1
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binHtFile" --check \
0x025000=000f >/dev/null 2>&1; then
die "MAME: hash table coverage bitmap != 0x0f"
fi
rm -f "$cHtFile" "$oHtFile" "$binHtFile"
log "check: MAME runs strtok 'a,b,,c' continuation (#84 mitigation: -O0 strtok.o)"
cTkFile="$(mktemp --suffix=.c)"
oTkFile="$(mktemp --suffix=.o)"
binTkFile="$(mktemp --suffix=.bin)"
cat > "$cTkFile" <<'EOF'
extern char *strtok(char *, const char *);
extern int strcmp(const char *, const char *);
__attribute__((noinline)) void switchToBank2(void) {
__asm__ volatile ("sep #0x20\n.byte 0xa9,0x02\npha\nplb\nrep #0x20\n");
}
int main(void) {
char buf[8];
buf[0]='a'; buf[1]=','; buf[2]='b'; buf[3]=','; buf[4]=','; buf[5]='c'; buf[6]=0;
char *t1 = strtok(buf, ",");
char *t2 = strtok((char *)0, ",");
char *t3 = strtok((char *)0, ",");
char *t4 = strtok((char *)0, ",");
unsigned short ok = 0;
if (t1 && strcmp(t1, "a") == 0) ok |= 0x01;
if (t2 && strcmp(t2, "b") == 0) ok |= 0x02;
if (t3 && strcmp(t3, "c") == 0) ok |= 0x04;
if (t4 == (char *)0) ok |= 0x08;
switchToBank2();
*(volatile unsigned short *)0x5000 = ok;
while (1) {}
}
EOF
"$CLANG" --target=w65816 -O2 -ffunction-sections -c \
"$cTkFile" -o "$oTkFile"
"$PROJECT_ROOT/tools/link816" -o "$binTkFile" --text-base 0x1000 \
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" \
"$oLibgccFile" "$oTkFile" >/dev/null 2>&1
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binTkFile" --check \
0x025000=000f >/dev/null 2>&1; then
die "MAME: strtok 4-call continuation bitmap != 0x0f"
fi
rm -f "$cTkFile" "$oTkFile" "$binTkFile"
log "check: MAME runs RPN calculator '3 4 +', '2 3 4 + *', etc. (#87)"
cRpFile="$(mktemp --suffix=.c)"
oRpFile="$(mktemp --suffix=.o)"
binRpFile="$(mktemp --suffix=.bin)"
cat > "$cRpFile" <<'EOF'
extern char *strtok(char *, const char *);
extern long atol(const char *);
extern int snprintf(char *, unsigned int, const char *, ...);
extern int strcmp(const char *, const char *);
__attribute__((noinline)) void switchToBank2(void) {
__asm__ volatile ("sep #0x20\n.byte 0xa9,0x02\npha\nplb\nrep #0x20\n");
}
#define STACK_SIZE 16
static long g_stack[STACK_SIZE];
static int g_top;
static void push(long v) {
if (g_top < STACK_SIZE) g_stack[g_top++] = v;
}
static long pop(void) {
return g_top > 0 ? g_stack[--g_top] : 0;
}
static long evalRpn(char *expr) {
g_top = 0;
char *tok = strtok(expr, " ");
while (tok) {
if (tok[0] == '+' && tok[1] == 0) {
long b = pop(); long a = pop(); push(a + b);
} else if (tok[0] == '-' && tok[1] == 0) {
long b = pop(); long a = pop(); push(a - b);
} else if (tok[0] == '*' && tok[1] == 0) {
long b = pop(); long a = pop(); push(a * b);
} else if (tok[0] == '/' && tok[1] == 0) {
long b = pop(); long a = pop(); push(b != 0 ? a / b : 0);
} else {
push(atol(tok));
}
tok = strtok((char *)0, " ");
}
return pop();
}
static long g_r1, g_r2, g_r3, g_r4;
__attribute__((noinline,optnone))
static void runAll(void) {
char e1[] = "3 4 +";
char e2[] = "2 3 4 + *";
char e3[] = "100 25 /";
char e4[] = "10 2 - 5 *";
g_r1 = evalRpn(e1);
g_r2 = evalRpn(e2);
g_r3 = evalRpn(e3);
g_r4 = evalRpn(e4);
}
int main(void) {
runAll();
char buf[16];
snprintf(buf, 16, "%ld", g_r2);
unsigned short ok = 0;
if (g_r1 == 7) ok |= 0x01;
if (g_r2 == 14) ok |= 0x02;
if (g_r3 == 4) ok |= 0x04;
if (g_r4 == 40) ok |= 0x08;
if (strcmp(buf, "14") == 0) ok |= 0x10;
switchToBank2();
*(volatile unsigned short *)0x5000 = ok;
while (1) {}
}
EOF
"$CLANG" --target=w65816 -O2 -ffunction-sections -c \
"$cRpFile" -o "$oRpFile"
"$PROJECT_ROOT/tools/link816" -o "$binRpFile" --text-base 0x1000 \
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" \
"$oLibgccFile" "$oRpFile" >/dev/null 2>&1
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binRpFile" --check \
0x025000=001f >/dev/null 2>&1; then
die "MAME: RPN calculator bitmap != 0x1f"
fi
rm -f "$cRpFile" "$oRpFile" "$binRpFile"
log "check: MAME runs sqrt/pow + sin/cos/exp/log + strpbrk/spn/cspn (#81 + #82 + #83)"
cTrFile="$(mktemp --suffix=.c)"
oTrFile="$(mktemp --suffix=.o)"
@ -2327,7 +2579,7 @@ EOF
"$cTrFile" -o "$oTrFile"
"$PROJECT_ROOT/tools/link816" -o "$binTrFile" --text-base 0x1000 \
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
"$oExtrasF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oTrFile" \
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oTrFile" \
>/dev/null 2>&1
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binTrFile" --check \
0x025000=0fff >/dev/null 2>&1; then
@ -2617,7 +2869,7 @@ EOF
rm -f "$cDmaFile" "$oDmaFile" "$binDmaFile"
rm -f "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
"$oExtrasF" "$oMathF" "$oSfF" "$oSdF" "$oCrt0F"
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oCrt0F"
else
warn "MAME or apple2gs ROMs not installed; skipping end-to-end test"
fi