Checkpoint.
This commit is contained in:
parent
a702f4a970
commit
d6c9fc8252
11 changed files with 820 additions and 5 deletions
31
STATUS.md
31
STATUS.md
|
|
@ -38,6 +38,18 @@ which runs correctly under MAME (apple2gs).
|
||||||
- C++ minimal: clang++ compiles a class with virtual + non-trivial
|
- C++ minimal: clang++ compiles a class with virtual + non-trivial
|
||||||
ctor (vtable + RTTI omitted; no exceptions).
|
ctor (vtable + RTTI omitted; no exceptions).
|
||||||
- printf with `%d %x %s %c %p` and width/precision specifiers.
|
- printf with `%d %x %s %c %p` and width/precision specifiers.
|
||||||
|
- sprintf / snprintf / vsprintf / vsnprintf with the same format
|
||||||
|
coverage as printf (`%d %u %x %ld %lu %s %c %f %p %%` + width).
|
||||||
|
C99 truncation semantics for snprintf.
|
||||||
|
- qsort + bsearch over arbitrary element size with a user `cmp`
|
||||||
|
callback (insertion-sort variant — sidesteps the greedy regalloc
|
||||||
|
bug in the recursive iterative-qsort form).
|
||||||
|
- Standard string/stdlib glue: strcat, strncat, atol, llabs (added
|
||||||
|
in their own translation unit so vprintf's branch layout doesn't
|
||||||
|
shift).
|
||||||
|
- `<math.h>` basics: fabs, floor, ceil, fmod, copysign (and the
|
||||||
|
float variants). All implemented via direct IEEE-754 bit
|
||||||
|
manipulation, no transcendentals.
|
||||||
- `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.
|
||||||
|
|
||||||
|
|
@ -69,6 +81,14 @@ which runs correctly under MAME (apple2gs).
|
||||||
Nothing currently in flight. All tracked tasks are closed; remaining
|
Nothing currently in flight. All tracked tasks are closed; remaining
|
||||||
items are listed under "What's still needed" below.
|
items are listed under "What's still needed" below.
|
||||||
|
|
||||||
|
Runtime grew sprintf/snprintf, qsort/bsearch, math.h basics, and
|
||||||
|
the small string/stdlib gaps (strcat, strncat, atol, llabs).
|
||||||
|
sprintf/snprintf was the most invasive — it tripped three independent
|
||||||
|
W65816 backend miscompiles (struct-pointer mis-addressing, fmt-as-arg1
|
||||||
|
loop-local uninit, `buf+0xFFFE` lowered to `dec a`) plus a fourth
|
||||||
|
codegen bug in countdown loops; each workaround is documented in the
|
||||||
|
file banner so a future cleanup pass doesn't undo them.
|
||||||
|
|
||||||
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
|
||||||
before writing it. PC values in `.debug_addr` and `.debug_line` end
|
before writing it. PC values in `.debug_addr` and `.debug_line` end
|
||||||
|
|
@ -120,10 +140,13 @@ sidecar bytes.
|
||||||
`softDouble.c` and unblocks pattern-rich code that currently
|
`softDouble.c` and unblocks pattern-rich code that currently
|
||||||
must be compiled at `-O0` for correctness.
|
must be compiled at `-O0` for correctness.
|
||||||
|
|
||||||
- **More of the C standard library**: `<math.h>` transcendental
|
- **More of the C standard library**: `<math.h>` transcendentals
|
||||||
functions (sin, cos, exp, log, pow), `<string.h>` beyond what's
|
(sin, cos, exp, log, pow), real `<stdio.h>` file I/O (`fopen`,
|
||||||
hand-coded, `<stdio.h>` file I/O (`fopen`, `fread`, `fwrite`,
|
`fread`, `fwrite`, `fseek` are currently stubs returning
|
||||||
`fseek`).
|
success/zero), and full snprintf %f fractional precision (today
|
||||||
|
only the integer part of `%f` is reliable — same caveat as
|
||||||
|
libc's writeDouble, the soft-double `(long)(frac * mul)` step
|
||||||
|
loses precision).
|
||||||
|
|
||||||
- **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).
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,10 @@ asm "$SRC/crt0.s"
|
||||||
asm "$SRC/libgcc.s"
|
asm "$SRC/libgcc.s"
|
||||||
cc "$SRC/libc.c"
|
cc "$SRC/libc.c"
|
||||||
cc "$SRC/strtol.c"
|
cc "$SRC/strtol.c"
|
||||||
|
cc "$SRC/snprintf.c"
|
||||||
|
cc "$SRC/qsort.c"
|
||||||
|
cc "$SRC/extras.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 +
|
||||||
# inlined alignment shifts overflows the greedy allocator on the
|
# inlined alignment shifts overflows the greedy allocator on the
|
||||||
|
|
|
||||||
15
runtime/include/math.h
Normal file
15
runtime/include/math.h
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
#ifndef _MATH_H
|
||||||
|
#define _MATH_H
|
||||||
|
|
||||||
|
double fabs (double x);
|
||||||
|
float fabsf (float x);
|
||||||
|
double floor (double x);
|
||||||
|
float floorf (float x);
|
||||||
|
double ceil (double x);
|
||||||
|
float ceilf (float x);
|
||||||
|
double fmod (double x, double y);
|
||||||
|
float fmodf (float x, float y);
|
||||||
|
double copysign (double x, double y);
|
||||||
|
float copysignf(float x, float y);
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
@ -14,6 +14,10 @@ int putchar(int c);
|
||||||
int puts(const char *s);
|
int puts(const char *s);
|
||||||
int printf(const char *fmt, ...);
|
int printf(const char *fmt, ...);
|
||||||
int vprintf(const char *fmt, va_list ap);
|
int vprintf(const char *fmt, va_list ap);
|
||||||
|
int sprintf(char *buf, const char *fmt, ...);
|
||||||
|
int snprintf(char *buf, size_t n, const char *fmt, ...);
|
||||||
|
int vsprintf(char *buf, const char *fmt, va_list ap);
|
||||||
|
int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap);
|
||||||
int fprintf(FILE *stream, const char *fmt, ...);
|
int fprintf(FILE *stream, const char *fmt, ...);
|
||||||
int fputc(int c, FILE *stream);
|
int fputc(int c, FILE *stream);
|
||||||
int fputs(const char *s, FILE *stream);
|
int fputs(const char *s, FILE *stream);
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,16 @@ void free(void *p);
|
||||||
int abs(int n);
|
int abs(int n);
|
||||||
long labs(long n);
|
long labs(long n);
|
||||||
int atoi(const char *s);
|
int atoi(const char *s);
|
||||||
|
long atol(const char *s);
|
||||||
|
long long llabs(long long n);
|
||||||
|
|
||||||
|
long strtol (const char *nptr, char **endptr, int base);
|
||||||
|
unsigned long strtoul(const char *nptr, char **endptr, int base);
|
||||||
|
|
||||||
|
typedef int (*__cmp_fn)(const void *, const void *);
|
||||||
|
void qsort (void *base, size_t nmemb, size_t size, __cmp_fn cmp);
|
||||||
|
void *bsearch(const void *key, const void *base, size_t nmemb,
|
||||||
|
size_t size, __cmp_fn cmp);
|
||||||
|
|
||||||
void exit(int code) __attribute__((noreturn));
|
void exit(int code) __attribute__((noreturn));
|
||||||
void abort(void) __attribute__((noreturn));
|
void abort(void) __attribute__((noreturn));
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,8 @@ int strncmp(const char *a, const char *b, size_t n);
|
||||||
char *strchr(const char *s, int c);
|
char *strchr(const char *s, int c);
|
||||||
char *strrchr(const char *s, int c);
|
char *strrchr(const char *s, int c);
|
||||||
char *strstr(const char *haystack, const char *needle);
|
char *strstr(const char *haystack, const char *needle);
|
||||||
|
char *strcat(char *dst, const char *src);
|
||||||
|
char *strncat(char *dst, const char *src, size_t n);
|
||||||
|
|
||||||
char *strerror(int err);
|
char *strerror(int err);
|
||||||
|
|
||||||
|
|
|
||||||
67
runtime/src/extras.c
Normal file
67
runtime/src/extras.c
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
// Tiny string.h / stdlib.h helpers — kept out of libc.c because
|
||||||
|
// adding to that translation unit shifts vprintf's internal branch
|
||||||
|
// distances and randomly breaks BranchExpand (same precedent as
|
||||||
|
// strtol.c and snprintf.c).
|
||||||
|
//
|
||||||
|
// Functions:
|
||||||
|
// string.h: strcat, strncat
|
||||||
|
// stdlib.h: atol, llabs
|
||||||
|
|
||||||
|
typedef unsigned int size_t;
|
||||||
|
|
||||||
|
|
||||||
|
char *strcat(char *dst, const char *src) {
|
||||||
|
char *d = dst;
|
||||||
|
while (*d) {
|
||||||
|
d++;
|
||||||
|
}
|
||||||
|
while ((*d = *src)) {
|
||||||
|
d++;
|
||||||
|
src++;
|
||||||
|
}
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
char *strncat(char *dst, const char *src, size_t n) {
|
||||||
|
char *d = dst;
|
||||||
|
while (*d) {
|
||||||
|
d++;
|
||||||
|
}
|
||||||
|
while (n && *src) {
|
||||||
|
*d = *src;
|
||||||
|
d++;
|
||||||
|
src++;
|
||||||
|
n--;
|
||||||
|
}
|
||||||
|
*d = 0;
|
||||||
|
return dst;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
extern int isspace(int);
|
||||||
|
|
||||||
|
|
||||||
|
long atol(const char *s) {
|
||||||
|
while (isspace(*s)) {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
int sign = 1;
|
||||||
|
if (*s == '-') {
|
||||||
|
sign = -1;
|
||||||
|
s++;
|
||||||
|
} else if (*s == '+') {
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
long n = 0;
|
||||||
|
while (*s >= '0' && *s <= '9') {
|
||||||
|
n = n * 10 + (*s - '0');
|
||||||
|
s++;
|
||||||
|
}
|
||||||
|
return sign < 0 ? -n : n;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
long long llabs(long long n) {
|
||||||
|
return n < 0 ? -n : n;
|
||||||
|
}
|
||||||
132
runtime/src/math.c
Normal file
132
runtime/src/math.c
Normal file
|
|
@ -0,0 +1,132 @@
|
||||||
|
// Minimal math.h subset for the W65816 runtime.
|
||||||
|
//
|
||||||
|
// All operations work directly on the IEEE-754 bit representation
|
||||||
|
// rather than going through long sequences of soft-float libcalls.
|
||||||
|
// fabs/copysign/floor/ceil/fmod cover the common needs of routine
|
||||||
|
// numeric code; transcendentals are intentionally out of scope.
|
||||||
|
//
|
||||||
|
// Layout assumed:
|
||||||
|
// double = sign(1) | exp(11) | frac(52) stored little-endian
|
||||||
|
// (high word at +6, low word at +0)
|
||||||
|
// float = sign(1) | exp(8) | frac(23)
|
||||||
|
//
|
||||||
|
// We type-pun via __builtin_memcpy + uint64_t / uint32_t to stay
|
||||||
|
// strict-aliasing-clean.
|
||||||
|
|
||||||
|
typedef unsigned long uint32_t;
|
||||||
|
typedef unsigned long long uint64_t;
|
||||||
|
typedef long long int64_t;
|
||||||
|
|
||||||
|
|
||||||
|
static uint64_t dToBits(double v) {
|
||||||
|
uint64_t b;
|
||||||
|
__builtin_memcpy(&b, &v, sizeof(b));
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static double dFromBits(uint64_t b) {
|
||||||
|
double v;
|
||||||
|
__builtin_memcpy(&v, &b, sizeof(v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static uint32_t fToBits(float v) {
|
||||||
|
uint32_t b;
|
||||||
|
__builtin_memcpy(&b, &v, sizeof(b));
|
||||||
|
return b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
static float fFromBits(uint32_t b) {
|
||||||
|
float v;
|
||||||
|
__builtin_memcpy(&v, &b, sizeof(v));
|
||||||
|
return v;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double fabs(double x) {
|
||||||
|
return dFromBits(dToBits(x) & ~((uint64_t)1 << 63));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float fabsf(float x) {
|
||||||
|
return fFromBits(fToBits(x) & ~((uint32_t)1 << 31));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double copysign(double x, double y) {
|
||||||
|
uint64_t mask = (uint64_t)1 << 63;
|
||||||
|
return dFromBits((dToBits(x) & ~mask) | (dToBits(y) & mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float copysignf(float x, float y) {
|
||||||
|
uint32_t mask = (uint32_t)1 << 31;
|
||||||
|
return fFromBits((fToBits(x) & ~mask) | (fToBits(y) & mask));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// floor(x) = round toward -inf. Truncate the fraction bits below
|
||||||
|
// the integer position; if any non-zero bit was discarded for a
|
||||||
|
// negative value, subtract one (round more negative).
|
||||||
|
double floor(double x) {
|
||||||
|
uint64_t b = dToBits(x);
|
||||||
|
int e = (int)((b >> 52) & 0x7FF) - 1023;
|
||||||
|
if (e < 0) {
|
||||||
|
// |x| < 1: floor(positive) = 0, floor(negative) = -1.
|
||||||
|
return (b >> 63) ? -1.0 : 0.0;
|
||||||
|
}
|
||||||
|
if (e >= 52) {
|
||||||
|
return x; // already integer (or NaN/Inf)
|
||||||
|
}
|
||||||
|
uint64_t fracMask = ((uint64_t)1 << (52 - e)) - 1;
|
||||||
|
if ((b & fracMask) == 0) {
|
||||||
|
return x; // already integer
|
||||||
|
}
|
||||||
|
uint64_t truncBits = b & ~fracMask;
|
||||||
|
double truncVal = dFromBits(truncBits);
|
||||||
|
// For negatives with a non-zero discarded fraction, subtract 1.
|
||||||
|
if (b >> 63) {
|
||||||
|
truncVal = truncVal - 1.0;
|
||||||
|
}
|
||||||
|
return truncVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
double ceil(double x) {
|
||||||
|
return -floor(-x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float floorf(float x) {
|
||||||
|
return (float)floor((double)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float ceilf(float x) {
|
||||||
|
return (float)ceil((double)x);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fmod(x, y) = x - trunc(x / y) * y. trunc(q) = round-toward-zero.
|
||||||
|
double fmod(double x, double y) {
|
||||||
|
if (y == 0.0) {
|
||||||
|
return 0.0; // implementation-defined; we return 0
|
||||||
|
}
|
||||||
|
double q = x / y;
|
||||||
|
// trunc: floor for positives, ceil for negatives.
|
||||||
|
double t;
|
||||||
|
if (q < 0.0) {
|
||||||
|
t = -floor(-q);
|
||||||
|
} else {
|
||||||
|
t = floor(q);
|
||||||
|
}
|
||||||
|
return x - t * y;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
float fmodf(float x, float y) {
|
||||||
|
return (float)fmod((double)x, (double)y);
|
||||||
|
}
|
||||||
67
runtime/src/qsort.c
Normal file
67
runtime/src/qsort.c
Normal file
|
|
@ -0,0 +1,67 @@
|
||||||
|
// qsort + bsearch — own translation unit so the indirect cmp call
|
||||||
|
// and the byte-swap inner loop don't perturb other libc code.
|
||||||
|
//
|
||||||
|
// qsort uses insertion sort (O(n^2)) rather than recursion-driven
|
||||||
|
// quicksort; the W65816 backend's greedy regalloc still mis-orders
|
||||||
|
// spills in iterative quicksort with if/else recursion (#70), and
|
||||||
|
// for the small arrays this runtime targets (typical IIgs C
|
||||||
|
// program: dozens of items, not thousands) the constant-factor win
|
||||||
|
// of insertion sort over recursive quicksort is meaningful.
|
||||||
|
|
||||||
|
typedef unsigned int size_t;
|
||||||
|
typedef int (*CmpFnT)(const void *, const void *);
|
||||||
|
|
||||||
|
|
||||||
|
// Swap `size` bytes between a and b using a 1-byte scratch. Tight
|
||||||
|
// forward loop is what the backend handles best on this target.
|
||||||
|
static void byteSwap(unsigned char *a, unsigned char *b, size_t size) {
|
||||||
|
for (size_t i = 0; i < size; i++) {
|
||||||
|
unsigned char t = a[i];
|
||||||
|
a[i] = b[i];
|
||||||
|
b[i] = t;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void *bsearch(const void *key, const void *base, size_t nmemb,
|
||||||
|
size_t size, CmpFnT cmp) {
|
||||||
|
const unsigned char *baseP = (const unsigned char *)base;
|
||||||
|
size_t lo = 0;
|
||||||
|
size_t hi = nmemb;
|
||||||
|
while (lo < hi) {
|
||||||
|
size_t mid = lo + (hi - lo) / 2;
|
||||||
|
const unsigned char *p = baseP + mid * size;
|
||||||
|
int r = cmp(key, p);
|
||||||
|
if (r == 0) {
|
||||||
|
return (void *)p;
|
||||||
|
}
|
||||||
|
if (r < 0) {
|
||||||
|
hi = mid;
|
||||||
|
} else {
|
||||||
|
lo = mid + 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return (void *)0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void qsort(void *base, size_t nmemb, size_t size, CmpFnT cmp) {
|
||||||
|
if (nmemb < 2 || size == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
unsigned char *baseP = (unsigned char *)base;
|
||||||
|
// Insertion sort: for each i in 1..n-1, walk left swapping until
|
||||||
|
// the slot at j-1 is <= the slot at j. Stable, in-place.
|
||||||
|
for (size_t i = 1; i < nmemb; i++) {
|
||||||
|
size_t j = i;
|
||||||
|
while (j > 0) {
|
||||||
|
unsigned char *cur = baseP + j * size;
|
||||||
|
unsigned char *prev = cur - size;
|
||||||
|
if (cmp(prev, cur) <= 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
byteSwap(prev, cur, size);
|
||||||
|
j--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
331
runtime/src/snprintf.c
Normal file
331
runtime/src/snprintf.c
Normal file
|
|
@ -0,0 +1,331 @@
|
||||||
|
// Buffer-formatting siblings of printf — kept in their own translation
|
||||||
|
// unit so the shared writeXxx helpers don't have to take a function-
|
||||||
|
// pointer sink (indirect call cost on this target) and so adding the
|
||||||
|
// formatter to libc.c can't shift vprintf's branch distances out of
|
||||||
|
// range (per the strtol.c precedent).
|
||||||
|
//
|
||||||
|
// Functions:
|
||||||
|
// int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap);
|
||||||
|
// int snprintf (char *buf, size_t n, const char *fmt, ...);
|
||||||
|
// int vsprintf (char *buf, const char *fmt, va_list ap);
|
||||||
|
// int sprintf (char *buf, const char *fmt, ...);
|
||||||
|
//
|
||||||
|
// Format support matches vprintf: %d %i %u %x %X %c %s %p %f and the
|
||||||
|
// `l` length modifier (%ld %lu). Width is honoured for %x. %f
|
||||||
|
// precision is capped at 9 fractional digits.
|
||||||
|
//
|
||||||
|
// Return value: number of characters that would have been written had
|
||||||
|
// the buffer been unbounded (C99 vsnprintf semantics), not just the
|
||||||
|
// number actually written. This lets callers detect truncation.
|
||||||
|
//
|
||||||
|
// **Sink state lives in file-static globals** instead of an explicit
|
||||||
|
// struct passed by pointer. Two W65816 backend bugs forced this:
|
||||||
|
// (1) The address of a stack-resident struct is computed wrong
|
||||||
|
// (&s came out as SP+5 = address of s.end instead of SP+3).
|
||||||
|
// emit() then read garbage cur/end values, the cur >= end branch
|
||||||
|
// skipped every write, and snprintf returned the right length
|
||||||
|
// with an empty buffer.
|
||||||
|
// (2) Functions taking fmt as arg1 (stack) didn't initialize the
|
||||||
|
// fmt local before the loop body — first char came from the
|
||||||
|
// arg slot but the loop's fmt++ ran on uninitialized memory.
|
||||||
|
// Fixed by making fmt always arg0 (A reg).
|
||||||
|
// Single-threaded use only, but that matches the rest of this runtime.
|
||||||
|
|
||||||
|
typedef unsigned int size_t;
|
||||||
|
typedef __builtin_va_list va_list;
|
||||||
|
#define va_start(ap, last) __builtin_va_start(ap, last)
|
||||||
|
#define va_arg(ap, ty) __builtin_va_arg(ap, ty)
|
||||||
|
#define va_end(ap) __builtin_va_end(ap)
|
||||||
|
|
||||||
|
|
||||||
|
static char *gCur;
|
||||||
|
static char *gEnd;
|
||||||
|
static size_t gTotal;
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emit(char c) {
|
||||||
|
if (gCur < gEnd) {
|
||||||
|
*gCur++ = c;
|
||||||
|
}
|
||||||
|
gTotal++;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emitStr(const char *p) {
|
||||||
|
if (!p) {
|
||||||
|
p = "(null)";
|
||||||
|
}
|
||||||
|
while (*p) {
|
||||||
|
emit(*p++);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emitUDec(unsigned int n) {
|
||||||
|
char buf[6];
|
||||||
|
int i = 0;
|
||||||
|
if (n == 0) {
|
||||||
|
emit('0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (n > 0) {
|
||||||
|
buf[i++] = '0' + (n % 10);
|
||||||
|
n /= 10;
|
||||||
|
}
|
||||||
|
// Reverse-emit using forward index arithmetic. The natural
|
||||||
|
// countdown forms (`while (i > 0) emit(buf[--i])`,
|
||||||
|
// `while (i > 0) { i--; emit(buf[i]); }`,
|
||||||
|
// `for (j = i - 1; j >= 0; j--) emit(buf[j])`) all lower to a
|
||||||
|
// do-while whose `dec a; bpl` exit condition runs the loop one
|
||||||
|
// extra time on this backend, leaking a buf[-1] read. The forward
|
||||||
|
// count + index-arithmetic form below avoids the bad lowering.
|
||||||
|
int top = i;
|
||||||
|
for (int j = 0; j < top; j++) {
|
||||||
|
emit(buf[top - 1 - j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emitDec(int n) {
|
||||||
|
if (n < 0) {
|
||||||
|
emit('-');
|
||||||
|
emitUDec((unsigned int)(-n));
|
||||||
|
} else {
|
||||||
|
emitUDec((unsigned int)n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emitULong(unsigned long n) {
|
||||||
|
char buf[11];
|
||||||
|
int i = 0;
|
||||||
|
if (n == 0) {
|
||||||
|
emit('0');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
while (n > 0) {
|
||||||
|
buf[i++] = '0' + (n % 10);
|
||||||
|
n /= 10;
|
||||||
|
}
|
||||||
|
// Reverse-emit using forward index arithmetic. The natural
|
||||||
|
// countdown forms (`while (i > 0) emit(buf[--i])`,
|
||||||
|
// `while (i > 0) { i--; emit(buf[i]); }`,
|
||||||
|
// `for (j = i - 1; j >= 0; j--) emit(buf[j])`) all lower to a
|
||||||
|
// do-while whose `dec a; bpl` exit condition runs the loop one
|
||||||
|
// extra time on this backend, leaking a buf[-1] read. The forward
|
||||||
|
// count + index-arithmetic form below avoids the bad lowering.
|
||||||
|
int top = i;
|
||||||
|
for (int j = 0; j < top; j++) {
|
||||||
|
emit(buf[top - 1 - j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emitSignedLong(long n) {
|
||||||
|
if (n < 0) {
|
||||||
|
emit('-');
|
||||||
|
emitULong((unsigned long)(-n));
|
||||||
|
} else {
|
||||||
|
emitULong((unsigned long)n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emitHex(unsigned int n, int width) {
|
||||||
|
static const char digits[] = "0123456789abcdef";
|
||||||
|
char buf[5];
|
||||||
|
int i = 0;
|
||||||
|
if (n == 0) {
|
||||||
|
buf[i++] = '0';
|
||||||
|
}
|
||||||
|
while (n > 0) {
|
||||||
|
buf[i++] = digits[n & 0xF];
|
||||||
|
n >>= 4;
|
||||||
|
}
|
||||||
|
while (i < width) {
|
||||||
|
buf[i++] = '0';
|
||||||
|
}
|
||||||
|
// Reverse-emit using forward index arithmetic. The natural
|
||||||
|
// countdown forms (`while (i > 0) emit(buf[--i])`,
|
||||||
|
// `while (i > 0) { i--; emit(buf[i]); }`,
|
||||||
|
// `for (j = i - 1; j >= 0; j--) emit(buf[j])`) all lower to a
|
||||||
|
// do-while whose `dec a; bpl` exit condition runs the loop one
|
||||||
|
// extra time on this backend, leaking a buf[-1] read. The forward
|
||||||
|
// count + index-arithmetic form below avoids the bad lowering.
|
||||||
|
int top = i;
|
||||||
|
for (int j = 0; j < top; j++) {
|
||||||
|
emit(buf[top - 1 - j]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
__attribute__((noinline))
|
||||||
|
static void emitDouble(double v, int prec) {
|
||||||
|
if (prec < 0) {
|
||||||
|
prec = 6;
|
||||||
|
}
|
||||||
|
if (prec > 9) {
|
||||||
|
prec = 9;
|
||||||
|
}
|
||||||
|
if (v < 0) {
|
||||||
|
emit('-');
|
||||||
|
v = -v;
|
||||||
|
}
|
||||||
|
long ipart = (long)v;
|
||||||
|
emitULong((unsigned long)ipart);
|
||||||
|
if (prec == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
emit('.');
|
||||||
|
double frac = v - (double)ipart;
|
||||||
|
long mul = 1;
|
||||||
|
for (int i = 0; i < prec; i++) {
|
||||||
|
mul *= 10;
|
||||||
|
}
|
||||||
|
long fdigits = (long)(frac * (double)mul);
|
||||||
|
if (fdigits < 0) {
|
||||||
|
fdigits = -fdigits;
|
||||||
|
}
|
||||||
|
char buf[10];
|
||||||
|
int n = 0;
|
||||||
|
long scale = mul / 10;
|
||||||
|
while (n < prec) {
|
||||||
|
if (scale == 0) {
|
||||||
|
scale = 1;
|
||||||
|
}
|
||||||
|
long d = fdigits / scale;
|
||||||
|
buf[n++] = '0' + (char)(d % 10);
|
||||||
|
scale /= 10;
|
||||||
|
if (scale == 0) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
while (n < prec) {
|
||||||
|
buf[n++] = '0';
|
||||||
|
}
|
||||||
|
for (int i = 0; i < n; i++) {
|
||||||
|
emit(buf[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// fmt is arg0 (A register); see banner comment for why the order matters.
|
||||||
|
static int format(const char *fmt, va_list ap) {
|
||||||
|
while (*fmt) {
|
||||||
|
char c = *fmt++;
|
||||||
|
if (c != '%') {
|
||||||
|
emit(c);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int width = 0;
|
||||||
|
while (*fmt >= '0' && *fmt <= '9') {
|
||||||
|
width = width * 10 + (*fmt - '0');
|
||||||
|
fmt++;
|
||||||
|
}
|
||||||
|
int prec = -1;
|
||||||
|
if (*fmt == '.') {
|
||||||
|
fmt++;
|
||||||
|
prec = 0;
|
||||||
|
while (*fmt >= '0' && *fmt <= '9') {
|
||||||
|
prec = prec * 10 + (*fmt - '0');
|
||||||
|
fmt++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
int isLong = 0;
|
||||||
|
if (*fmt == 'l') {
|
||||||
|
isLong = 1;
|
||||||
|
fmt++;
|
||||||
|
}
|
||||||
|
char spec = *fmt++;
|
||||||
|
if (spec == 'd' || spec == 'i') {
|
||||||
|
if (isLong) {
|
||||||
|
emitSignedLong(va_arg(ap, long));
|
||||||
|
} else {
|
||||||
|
emitDec(va_arg(ap, int));
|
||||||
|
}
|
||||||
|
} else if (spec == 'u') {
|
||||||
|
if (isLong) {
|
||||||
|
emitULong(va_arg(ap, unsigned long));
|
||||||
|
} else {
|
||||||
|
emitUDec(va_arg(ap, unsigned int));
|
||||||
|
}
|
||||||
|
} else if (spec == 'x' || spec == 'X') {
|
||||||
|
emitHex(va_arg(ap, unsigned int), width);
|
||||||
|
} else if (spec == 'c') {
|
||||||
|
emit((char)va_arg(ap, int));
|
||||||
|
} else if (spec == 's') {
|
||||||
|
emitStr(va_arg(ap, const char *));
|
||||||
|
} else if (spec == 'f' || spec == 'F' ||
|
||||||
|
spec == 'g' || spec == 'G' ||
|
||||||
|
spec == 'e' || spec == 'E') {
|
||||||
|
emitDouble(va_arg(ap, double), prec);
|
||||||
|
} else if (spec == 'p') {
|
||||||
|
emit('0');
|
||||||
|
emit('x');
|
||||||
|
emitHex(va_arg(ap, unsigned int), 4);
|
||||||
|
} else if (spec == '%') {
|
||||||
|
emit('%');
|
||||||
|
} else {
|
||||||
|
emit('%');
|
||||||
|
emit(spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (gCur < gEnd) {
|
||||||
|
*gCur = '\0';
|
||||||
|
} else if (gEnd > (char *)0) {
|
||||||
|
gEnd[-1] = '\0';
|
||||||
|
}
|
||||||
|
return (int)gTotal;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int snprintf(char *buf, size_t n, const char *fmt, ...) {
|
||||||
|
gCur = buf;
|
||||||
|
gEnd = buf + (n ? n : 0);
|
||||||
|
gTotal = 0;
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
int r = format(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int sprintf(char *buf, const char *fmt, ...) {
|
||||||
|
gCur = buf;
|
||||||
|
// sprintf is unbounded. Setting gEnd = buf + 0xFFFE looks innocuous
|
||||||
|
// but clang lowers the +0xFFFE to a `dec a; dec a` peephole (since
|
||||||
|
// 0xFFFE is -2 in 16-bit), giving gEnd = buf - 2 — and then the
|
||||||
|
// emit() bounds test `cur < end` is always false, so nothing gets
|
||||||
|
// written. Use the absolute top-of-bank sentinel instead.
|
||||||
|
gEnd = (char *)0xFFFF;
|
||||||
|
gTotal = 0;
|
||||||
|
va_list ap;
|
||||||
|
va_start(ap, fmt);
|
||||||
|
int r = format(fmt, ap);
|
||||||
|
va_end(ap);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int vsnprintf(char *buf, size_t n, const char *fmt, va_list ap) {
|
||||||
|
gCur = buf;
|
||||||
|
gEnd = buf + (n ? n : 0);
|
||||||
|
gTotal = 0;
|
||||||
|
return format(fmt, ap);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
int vsprintf(char *buf, const char *fmt, va_list ap) {
|
||||||
|
gCur = buf;
|
||||||
|
gEnd = (char *)0xFFFF;
|
||||||
|
gTotal = 0;
|
||||||
|
return format(fmt, ap);
|
||||||
|
}
|
||||||
|
|
@ -1400,12 +1400,24 @@ EOF
|
||||||
"$cFactFile" -o "$oFactFile"
|
"$cFactFile" -o "$oFactFile"
|
||||||
oLibcF="$(mktemp --suffix=.o)"
|
oLibcF="$(mktemp --suffix=.o)"
|
||||||
oStrtolF="$(mktemp --suffix=.o)"
|
oStrtolF="$(mktemp --suffix=.o)"
|
||||||
|
oSnprintfF="$(mktemp --suffix=.o)"
|
||||||
|
oQsortF="$(mktemp --suffix=.o)"
|
||||||
|
oExtrasF="$(mktemp --suffix=.o)"
|
||||||
|
oMathF="$(mktemp --suffix=.o)"
|
||||||
oSfF="$(mktemp --suffix=.o)"
|
oSfF="$(mktemp --suffix=.o)"
|
||||||
oSdF="$(mktemp --suffix=.o)"
|
oSdF="$(mktemp --suffix=.o)"
|
||||||
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
-c "$PROJECT_ROOT/runtime/src/libc.c" -o "$oLibcF"
|
-c "$PROJECT_ROOT/runtime/src/libc.c" -o "$oLibcF"
|
||||||
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
-c "$PROJECT_ROOT/runtime/src/strtol.c" -o "$oStrtolF"
|
-c "$PROJECT_ROOT/runtime/src/strtol.c" -o "$oStrtolF"
|
||||||
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
|
-c "$PROJECT_ROOT/runtime/src/snprintf.c" -o "$oSnprintfF"
|
||||||
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
|
-c "$PROJECT_ROOT/runtime/src/qsort.c" -o "$oQsortF"
|
||||||
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
|
-c "$PROJECT_ROOT/runtime/src/extras.c" -o "$oExtrasF"
|
||||||
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
|
-c "$PROJECT_ROOT/runtime/src/math.c" -o "$oMathF"
|
||||||
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
"$CLANG" --target=w65816 -O2 -ffunction-sections \
|
||||||
-c "$PROJECT_ROOT/runtime/src/softFloat.c" -o "$oSfF"
|
-c "$PROJECT_ROOT/runtime/src/softFloat.c" -o "$oSfF"
|
||||||
"$CLANG" --target=w65816 -O2 -ffunction-sections -mllvm -regalloc=fast \
|
"$CLANG" --target=w65816 -O2 -ffunction-sections -mllvm -regalloc=fast \
|
||||||
|
|
@ -2120,6 +2132,153 @@ EOF
|
||||||
fi
|
fi
|
||||||
rm -f "$cStFile" "$oStFile" "$binStFile"
|
rm -f "$cStFile" "$oStFile" "$binStFile"
|
||||||
|
|
||||||
|
log "check: MAME runs sprintf/snprintf format coverage (#76)"
|
||||||
|
cSpFile="$(mktemp --suffix=.c)"
|
||||||
|
oSpFile="$(mktemp --suffix=.o)"
|
||||||
|
binSpFile="$(mktemp --suffix=.bin)"
|
||||||
|
cat > "$cSpFile" <<'EOF'
|
||||||
|
extern int sprintf(char *buf, const char *fmt, ...);
|
||||||
|
extern int snprintf(char *buf, unsigned int n, const char *fmt, ...);
|
||||||
|
extern int strcmp(const char *a, const char *b);
|
||||||
|
__attribute__((noinline)) void switchToBank2(void) {
|
||||||
|
__asm__ volatile ("sep #0x20\n.byte 0xa9,0x02\npha\nplb\nrep #0x20\n");
|
||||||
|
}
|
||||||
|
static int eq(const char *a, const char *b) { return strcmp(a, b) == 0; }
|
||||||
|
int main(void) {
|
||||||
|
char buf[32];
|
||||||
|
unsigned char ok = 0;
|
||||||
|
int r = sprintf(buf, "ok-%d", 7);
|
||||||
|
if (r == 4 && eq(buf, "ok-7")) ok |= 0x01;
|
||||||
|
r = sprintf(buf, "n=%d s=%s", -42, "hi");
|
||||||
|
if (r == 10 && eq(buf, "n=-42 s=hi")) ok |= 0x02;
|
||||||
|
r = sprintf(buf, "%04x %lu", 0xC, (unsigned long)123456);
|
||||||
|
if (r == 11 && eq(buf, "000c 123456")) ok |= 0x04;
|
||||||
|
r = snprintf(buf, 6, "abcdefghij");
|
||||||
|
if (r == 10 && eq(buf, "abcde")) ok |= 0x08;
|
||||||
|
r = sprintf(buf, "%.0f", 42.0);
|
||||||
|
if (r == 2 && eq(buf, "42")) ok |= 0x10;
|
||||||
|
r = sprintf(buf, "[%c%c%%]", 'A', 'B');
|
||||||
|
if (r == 5 && eq(buf, "[AB%]")) ok |= 0x20;
|
||||||
|
switchToBank2();
|
||||||
|
*(volatile unsigned char *)0x5000 = ok;
|
||||||
|
while (1) {}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
"$CLANG" --target=w65816 -O2 -ffunction-sections -c \
|
||||||
|
"$cSpFile" -o "$oSpFile"
|
||||||
|
"$PROJECT_ROOT/tools/link816" -o "$binSpFile" --text-base 0x1000 \
|
||||||
|
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oSfF" "$oSdF" \
|
||||||
|
"$oLibgccFile" "$oSpFile" >/dev/null 2>&1
|
||||||
|
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binSpFile" --check \
|
||||||
|
0x025000=003f >/dev/null 2>&1; then
|
||||||
|
die "MAME: sprintf/snprintf format-coverage bitmap != 0x3f"
|
||||||
|
fi
|
||||||
|
rm -f "$cSpFile" "$oSpFile" "$binSpFile"
|
||||||
|
|
||||||
|
log "check: MAME runs qsort([3,1,4,1,5]) + bsearch (#77)"
|
||||||
|
cQbFile="$(mktemp --suffix=.c)"
|
||||||
|
oQbFile="$(mktemp --suffix=.o)"
|
||||||
|
binQbFile="$(mktemp --suffix=.bin)"
|
||||||
|
cat > "$cQbFile" <<'EOF'
|
||||||
|
extern void qsort(void *, unsigned int, unsigned int,
|
||||||
|
int (*)(const void *, const void *));
|
||||||
|
extern void *bsearch(const void *, const void *, unsigned int, unsigned int,
|
||||||
|
int (*)(const void *, const void *));
|
||||||
|
__attribute__((noinline)) void switchToBank2(void) {
|
||||||
|
__asm__ volatile ("sep #0x20\n.byte 0xa9,0x02\npha\nplb\nrep #0x20\n");
|
||||||
|
}
|
||||||
|
static int cmpInt(const void *a, const void *b) {
|
||||||
|
int x = *(const int *)a, y = *(const int *)b;
|
||||||
|
return x < y ? -1 : (x > y ? 1 : 0);
|
||||||
|
}
|
||||||
|
int main(void) {
|
||||||
|
int arr[5] = {3, 1, 4, 1, 5};
|
||||||
|
qsort(arr, 5, sizeof(int), cmpInt);
|
||||||
|
int key = 4;
|
||||||
|
int *hit = (int *)bsearch(&key, arr, 5, sizeof(int), cmpInt);
|
||||||
|
int idx = hit ? (int)(hit - arr) : -1;
|
||||||
|
int miss = 99;
|
||||||
|
int *m = (int *)bsearch(&miss, arr, 5, sizeof(int), cmpInt);
|
||||||
|
short missIdx = m ? 0 : -1;
|
||||||
|
switchToBank2();
|
||||||
|
*(volatile unsigned short *)0x5000 = (unsigned short)arr[0];
|
||||||
|
*(volatile unsigned short *)0x5002 = (unsigned short)arr[1];
|
||||||
|
*(volatile unsigned short *)0x5004 = (unsigned short)arr[2];
|
||||||
|
*(volatile unsigned short *)0x5006 = (unsigned short)arr[3];
|
||||||
|
*(volatile unsigned short *)0x5008 = (unsigned short)arr[4];
|
||||||
|
*(volatile unsigned short *)0x500a = (unsigned short)idx;
|
||||||
|
*(volatile unsigned short *)0x500c = (unsigned short)missIdx;
|
||||||
|
while (1) {}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
"$CLANG" --target=w65816 -O2 -ffunction-sections -c \
|
||||||
|
"$cQbFile" -o "$oQbFile"
|
||||||
|
"$PROJECT_ROOT/tools/link816" -o "$binQbFile" --text-base 0x1000 \
|
||||||
|
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
||||||
|
"$oSfF" "$oSdF" "$oLibgccFile" "$oQbFile" >/dev/null 2>&1
|
||||||
|
if ! bash "$PROJECT_ROOT/scripts/runInMame.sh" "$binQbFile" --check \
|
||||||
|
0x025000=0001 0x025002=0001 0x025004=0003 \
|
||||||
|
0x025006=0004 0x025008=0005 \
|
||||||
|
0x02500a=0003 0x02500c=ffff >/dev/null 2>&1; then
|
||||||
|
die "MAME: qsort/bsearch wrong"
|
||||||
|
fi
|
||||||
|
rm -f "$cQbFile" "$oQbFile" "$binQbFile"
|
||||||
|
|
||||||
|
log "check: MAME runs strcat/atol/llabs + math fabs/floor/ceil/copysign (#78 + #79)"
|
||||||
|
cExFile="$(mktemp --suffix=.c)"
|
||||||
|
oExFile="$(mktemp --suffix=.o)"
|
||||||
|
binExFile="$(mktemp --suffix=.bin)"
|
||||||
|
cat > "$cExFile" <<'EOF'
|
||||||
|
extern char *strcat(char *, const char *);
|
||||||
|
extern char *strncat(char *, const char *, unsigned int);
|
||||||
|
extern int strcmp(const char *, const char *);
|
||||||
|
extern long atol(const char *);
|
||||||
|
extern long long llabs(long long);
|
||||||
|
extern double fabs(double);
|
||||||
|
extern double floor(double);
|
||||||
|
extern double ceil(double);
|
||||||
|
extern double copysign(double, double);
|
||||||
|
__attribute__((noinline)) void switchToBank2(void) {
|
||||||
|
__asm__ volatile ("sep #0x20\n.byte 0xa9,0x02\npha\nplb\nrep #0x20\n");
|
||||||
|
}
|
||||||
|
static int eq(const char *a, const char *b) { return strcmp(a, b) == 0; }
|
||||||
|
static int dEq(double a, double b) {
|
||||||
|
unsigned long long x, y;
|
||||||
|
__builtin_memcpy(&x, &a, sizeof(x));
|
||||||
|
__builtin_memcpy(&y, &b, sizeof(y));
|
||||||
|
return x == y;
|
||||||
|
}
|
||||||
|
int main(void) {
|
||||||
|
char buf[16];
|
||||||
|
unsigned char ok = 0;
|
||||||
|
buf[0] = 0; strcat(buf, "ab"); strcat(buf, "cd");
|
||||||
|
if (eq(buf, "abcd")) ok |= 0x01;
|
||||||
|
buf[0]='h'; buf[1]='i'; buf[2]=0;
|
||||||
|
strncat(buf, "world", 3);
|
||||||
|
if (eq(buf, "hiwor")) ok |= 0x02;
|
||||||
|
if (atol(" -12345 garbage") == -12345) ok |= 0x04;
|
||||||
|
if (llabs(-(long long)1000) == 1000) ok |= 0x08;
|
||||||
|
if (dEq(fabs(-1.5), 1.5)) ok |= 0x10;
|
||||||
|
if (dEq(floor(2.7), 2.0)) ok |= 0x20;
|
||||||
|
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;
|
||||||
|
while (1) {}
|
||||||
|
}
|
||||||
|
EOF
|
||||||
|
"$CLANG" --target=w65816 -O2 -ffunction-sections -c \
|
||||||
|
"$cExFile" -o "$oExFile"
|
||||||
|
"$PROJECT_ROOT/tools/link816" -o "$binExFile" --text-base 0x1000 \
|
||||||
|
"$oCrt0F" "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
||||||
|
"$oExtrasF" "$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
|
||||||
|
die "MAME: extras + math basics bitmap != 0xff"
|
||||||
|
fi
|
||||||
|
rm -f "$cExFile" "$oExFile" "$binExFile"
|
||||||
|
|
||||||
log "check: MAME runs udivmod(0x123...DEF, 0x10000, &m) → q=0x12345_6789AB m=0xCDEF (#69)"
|
log "check: MAME runs udivmod(0x123...DEF, 0x10000, &m) → q=0x12345_6789AB m=0xCDEF (#69)"
|
||||||
cUdmFile="$(mktemp --suffix=.c)"
|
cUdmFile="$(mktemp --suffix=.c)"
|
||||||
oUdmFile="$(mktemp --suffix=.o)"
|
oUdmFile="$(mktemp --suffix=.o)"
|
||||||
|
|
@ -2401,7 +2560,8 @@ EOF
|
||||||
fi
|
fi
|
||||||
rm -f "$cDmaFile" "$oDmaFile" "$binDmaFile"
|
rm -f "$cDmaFile" "$oDmaFile" "$binDmaFile"
|
||||||
|
|
||||||
rm -f "$oLibcF" "$oStrtolF" "$oSfF" "$oSdF" "$oCrt0F"
|
rm -f "$oLibcF" "$oStrtolF" "$oSnprintfF" "$oQsortF" \
|
||||||
|
"$oExtrasF" "$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