Checkpoint.
This commit is contained in:
parent
1622053eb2
commit
e805911e18
8 changed files with 518 additions and 100 deletions
50
STATUS.md
50
STATUS.md
|
|
@ -49,11 +49,14 @@ which runs correctly under MAME (apple2gs).
|
||||||
strcspn, atol, llabs (kept in their own translation unit so
|
strcspn, atol, llabs (kept in their own translation unit so
|
||||||
vprintf's branch layout doesn't shift).
|
vprintf's branch layout doesn't shift).
|
||||||
- `<math.h>`: fabs, floor, ceil, fmod, copysign, sqrt, pow,
|
- `<math.h>`: fabs, floor, ceil, fmod, copysign, sqrt, pow,
|
||||||
sin, cos, exp, log (and float variants). Bit-twiddling for
|
sin, cos, exp, log, atan, atan2, asin, acos, sinh, cosh, tanh
|
||||||
fabs/floor/ceil/copysign; Newton iteration for sqrt;
|
(and float variants). Bit-twiddling for fabs/floor/ceil/copysign;
|
||||||
range-reduction + Taylor for the transcendentals. Accuracy
|
Newton iteration for sqrt; range-reduction + Taylor for sin/cos/
|
||||||
is in the ~1e-6 range — good enough for typical numeric work,
|
exp/log/atan; identities for asin/acos/atan2/sinh/cosh/tanh.
|
||||||
far short of glibc-quality.
|
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.
|
- `setjmp` / `longjmp` from libgcc.s.
|
||||||
- Static constructors via crt0's init_array walk.
|
- Static constructors via crt0's init_array walk.
|
||||||
|
|
||||||
|
|
@ -82,20 +85,25 @@ which runs correctly under MAME (apple2gs).
|
||||||
|
|
||||||
## In flight
|
## In flight
|
||||||
|
|
||||||
Only one tracked task is open: `strtok` continuation calls
|
Nothing tracked is open. Runtime now exposes a ~complete C99
|
||||||
(`strtok(NULL, ...)`) return NULL even though the internal save
|
subset: sprintf/snprintf with correct %.Nf precision, qsort/bsearch,
|
||||||
pointer is correctly populated by the first call. Three rewrites
|
the full string.h family (strcat/strncat/strpbrk/strspn/strcspn/
|
||||||
chased the same backend mishap; the other string-extras helpers
|
strtok/strtok_r), math.h with the eleven common transcendentals
|
||||||
(strpbrk, strspn, strcspn) all work, so users who need a tokenizer
|
(sqrt/pow/sin/cos/exp/log/atan/atan2/asin/acos/sinh/cosh/tanh),
|
||||||
can roll one against those for now. Tracked but not blocking.
|
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
|
`strtok` / `strtok_r` live in their own TU built at `-O0` — the
|
||||||
including sqrt/pow/sin/cos/exp/log), and the small string/stdlib
|
`-O2` codegen for the str==NULL continuation path miscompiles
|
||||||
gaps (strcat, strncat, strpbrk, strspn, strcspn, atol, llabs).
|
(documented as the same backend-fragility class as #70 / qsort,
|
||||||
sprintf/snprintf was the most invasive — eight independent W65816
|
both mitigated by reaching for fast regalloc per-TU). Multi-call
|
||||||
backend workarounds documented in the file banner, including the
|
strtok over "a,b,,c" works end-to-end in smoke.
|
||||||
%.Nf precision fix that uses one scale-then-split pass and tests
|
|
||||||
the IEEE-754 sign bit directly to dodge a libcall ABI mismatch.
|
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
|
The **DWARF sidecar** (`link816 --debug-out FILE`) now applies
|
||||||
text/rodata/bss/init_array relocations to every `.debug_*` section
|
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
|
- **More of the C standard library**: real `<stdio.h>` file I/O
|
||||||
(`fopen`, `fread`, `fwrite`, `fseek` are currently stubs
|
(`fopen`, `fread`, `fwrite`, `fseek` are currently stubs
|
||||||
returning success/zero), additional `<math.h>` (atan, asin,
|
returning success/zero) — would need a memory-backed FS or a
|
||||||
acos, hyperbolic forms), and `<locale.h>` / `<wchar.h>` if any
|
MAME hook; `<locale.h>` / `<wchar.h>` if any real-world code
|
||||||
real-world code needs them.
|
needs them.
|
||||||
|
|
||||||
- **C++ runtime support**: vtable layout for multiple inheritance,
|
- **C++ runtime support**: vtable layout for multiple inheritance,
|
||||||
RTTI, exceptions (or a documented `-fno-exceptions` requirement).
|
RTTI, exceptions (or a documented `-fno-exceptions` requirement).
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,12 @@ cc "$SRC/strtol.c"
|
||||||
cc "$SRC/snprintf.c"
|
cc "$SRC/snprintf.c"
|
||||||
cc "$SRC/qsort.c"
|
cc "$SRC/qsort.c"
|
||||||
cc "$SRC/extras.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/math.c"
|
||||||
cc "$SRC/softFloat.c"
|
cc "$SRC/softFloat.c"
|
||||||
# softDouble.c needs -regalloc=fast: __muldf3's 64x64 -> 128 mul +
|
# softDouble.c needs -regalloc=fast: __muldf3's 64x64 -> 128 mul +
|
||||||
|
|
|
||||||
|
|
@ -23,5 +23,19 @@ double exp (double x);
|
||||||
float expf (float x);
|
float expf (float x);
|
||||||
double log (double x);
|
double log (double x);
|
||||||
float logf (float 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
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -23,6 +23,7 @@ char *strpbrk(const char *s, const char *accept);
|
||||||
size_t strspn (const char *s, const char *accept);
|
size_t strspn (const char *s, const char *accept);
|
||||||
size_t strcspn(const char *s, const char *reject);
|
size_t strcspn(const char *s, const char *reject);
|
||||||
char *strtok (char *str, const char *delim);
|
char *strtok (char *str, const char *delim);
|
||||||
|
char *strtok_r(char *str, const char *delim, char **saveptr);
|
||||||
|
|
||||||
char *strerror(int err);
|
char *strerror(int err);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,77 +109,5 @@ size_t strcspn(const char *s, const char *reject) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// strtok: stateful tokenizer. First call passes the string; subsequent
|
// strtok / strtok_r are in runtime/src/strtok.c (built at -O0 to dodge
|
||||||
// calls pass NULL and resume from the saved cursor. Single-threaded
|
// a backend miscompile of the str==NULL continuation path; #84).
|
||||||
// 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++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
||||||
|
|
@ -344,3 +344,137 @@ double log(double x) {
|
||||||
float logf(float x) {
|
float logf(float x) {
|
||||||
return (float)log((double)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
75
runtime/src/strtok.c
Normal 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);
|
||||||
|
}
|
||||||
|
|
@ -1403,6 +1403,7 @@ EOF
|
||||||
oSnprintfF="$(mktemp --suffix=.o)"
|
oSnprintfF="$(mktemp --suffix=.o)"
|
||||||
oQsortF="$(mktemp --suffix=.o)"
|
oQsortF="$(mktemp --suffix=.o)"
|
||||||
oExtrasF="$(mktemp --suffix=.o)"
|
oExtrasF="$(mktemp --suffix=.o)"
|
||||||
|
oStrtokF="$(mktemp --suffix=.o)"
|
||||||
oMathF="$(mktemp --suffix=.o)"
|
oMathF="$(mktemp --suffix=.o)"
|
||||||
oSfF="$(mktemp --suffix=.o)"
|
oSfF="$(mktemp --suffix=.o)"
|
||||||
oSdF="$(mktemp --suffix=.o)"
|
oSdF="$(mktemp --suffix=.o)"
|
||||||
|
|
@ -1416,6 +1417,9 @@ EOF
|
||||||
-c "$PROJECT_ROOT/runtime/src/qsort.c" -o "$oQsortF"
|
-c "$PROJECT_ROOT/runtime/src/qsort.c" -o "$oQsortF"
|
||||||
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
-c "$PROJECT_ROOT/runtime/src/extras.c" -o "$oExtrasF"
|
-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 \
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
-c "$PROJECT_ROOT/runtime/src/math.c" -o "$oMathF"
|
-c "$PROJECT_ROOT/runtime/src/math.c" -o "$oMathF"
|
||||||
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
|
|
@ -2160,7 +2164,7 @@ int main(void) {
|
||||||
r = sprintf(buf, "[%c%c%%]", 'A', 'B');
|
r = sprintf(buf, "[%c%c%%]", 'A', 'B');
|
||||||
if (r == 5 && eq(buf, "[AB%]")) ok |= 0x20;
|
if (r == 5 && eq(buf, "[AB%]")) ok |= 0x20;
|
||||||
switchToBank2();
|
switchToBank2();
|
||||||
*(volatile unsigned char *)0x5000 = ok;
|
*(volatile unsigned short *)0x5000 = (unsigned short)ok;
|
||||||
while (1) {}
|
while (1) {}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -2263,7 +2267,7 @@ int main(void) {
|
||||||
if (dEq(ceil(2.3), 3.0)) ok |= 0x40;
|
if (dEq(ceil(2.3), 3.0)) ok |= 0x40;
|
||||||
if (dEq(copysign(1.0, -2.0), -1.0)) ok |= 0x80;
|
if (dEq(copysign(1.0, -2.0), -1.0)) ok |= 0x80;
|
||||||
switchToBank2();
|
switchToBank2();
|
||||||
*(volatile unsigned char *)0x5000 = ok;
|
*(volatile unsigned short *)0x5000 = (unsigned short)ok;
|
||||||
while (1) {}
|
while (1) {}
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
@ -2271,7 +2275,7 @@ EOF
|
||||||
"$cExFile" -o "$oExFile"
|
"$cExFile" -o "$oExFile"
|
||||||
"$PROJECT_ROOT/tools/link816" -o "$binExFile" --text-base 0x1000 \
|
"$PROJECT_ROOT/tools/link816" -o "$binExFile" --text-base 0x1000 \
|
||||||
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
||||||
"$oExtrasF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oExFile" \
|
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oExFile" \
|
||||||
>/dev/null 2>&1
|
>/dev/null 2>&1
|
||||||
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binExFile" --check \
|
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binExFile" --check \
|
||||||
0x025000=00ff >/dev/null 2>&1; then
|
0x025000=00ff >/dev/null 2>&1; then
|
||||||
|
|
@ -2279,6 +2283,254 @@ EOF
|
||||||
fi
|
fi
|
||||||
rm -f "$cExFile" "$oExFile" "$binExFile"
|
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)"
|
log "check: MAME runs sqrt/pow + sin/cos/exp/log + strpbrk/spn/cspn (#81 + #82 + #83)"
|
||||||
cTrFile="$(mktemp --suffix=.c)"
|
cTrFile="$(mktemp --suffix=.c)"
|
||||||
oTrFile="$(mktemp --suffix=.o)"
|
oTrFile="$(mktemp --suffix=.o)"
|
||||||
|
|
@ -2327,7 +2579,7 @@ EOF
|
||||||
"$cTrFile" -o "$oTrFile"
|
"$cTrFile" -o "$oTrFile"
|
||||||
"$PROJECT_ROOT/tools/link816" -o "$binTrFile" --text-base 0x1000 \
|
"$PROJECT_ROOT/tools/link816" -o "$binTrFile" --text-base 0x1000 \
|
||||||
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
||||||
"$oExtrasF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oTrFile" \
|
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oLibgccFile" "$oTrFile" \
|
||||||
>/dev/null 2>&1
|
>/dev/null 2>&1
|
||||||
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binTrFile" --check \
|
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binTrFile" --check \
|
||||||
0x025000=0fff >/dev/null 2>&1; then
|
0x025000=0fff >/dev/null 2>&1; then
|
||||||
|
|
@ -2617,7 +2869,7 @@ EOF
|
||||||
rm -f "$cDmaFile" "$oDmaFile" "$binDmaFile"
|
rm -f "$cDmaFile" "$oDmaFile" "$binDmaFile"
|
||||||
|
|
||||||
rm -f "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
rm -f "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
||||||
"$oExtrasF" "$oMathF" "$oSfF" "$oSdF" "$oCrt0F"
|
"$oExtrasF" "$oStrtokF" "$oMathF" "$oSfF" "$oSdF" "$oCrt0F"
|
||||||
else
|
else
|
||||||
warn "MAME or apple2gs ROMs not installed; skipping end-to-end test"
|
warn "MAME or apple2gs ROMs not installed; skipping end-to-end test"
|
||||||
fi
|
fi
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue