diff --git a/config/dol_slices.yml b/config/dol_slices.yml index 26bb29ea..3f0da760 100644 --- a/config/dol_slices.yml +++ b/config/dol_slices.yml @@ -6,6 +6,20 @@ libforest/ReconfigBATs.c: .text: [0x8005adac, 0x8005aed4] libc64/aprintf.c: .text: [0x8005cbdc, 0x8005cc14] +libultra/xldtob.c: + .text: [0x8005e918, 0x8005f2a0] + .rodata: [0x800ab110, 0x800ab158] + .sdata: [0x80217df8, 0x80217e08] + sdata2: [0x80219210, 0x80219230] +libultra/xlitob.c: + .text: [0x8005f2a0, 0x8005f4cc] + .data: [0x800ddb60, 0x800ddb88] +libultra/xprintf.c: + .text: [0x8005f4cc, 0x8005ff74] + .rodata: [0x800ab158, 0x800ab170] + .data: [0x800ddb88, 0x800ddd20] + .sdata: [0x80217e08, 0x80217e10] + .sdata2: [0x80219230, 0x80219238] JSystem/JKernel/JKRHeap.cpp: .text: [0x80063748, 0x80064028] .data: [0x800ddf20, 0x800ddf98] diff --git a/include/libultra/xprintf.h b/include/libultra/xprintf.h index 3b464dbe..09673c90 100644 --- a/include/libultra/xprintf.h +++ b/include/libultra/xprintf.h @@ -5,10 +5,40 @@ #include "va_args.h" #ifdef __cplusplus -extern "C"{ +extern "C" { #endif -extern int _Printf(void* (*prout_func)(void*, const char*, int), void* arg, const char* fmt, va_list ap); +typedef struct { + /* 0x0 */ union { + /* 0x0 */ s64 ll; + /* 0x0 */ f64 ld; + } v; + /* 0x8 */ char* s; + /* 0xC */ s32 n0; + /* 0x10 */ s32 nz0; + /* 0x14 */ s32 n1; + /* 0x18 */ s32 nz1; + /* 0x1C */ s32 n2; + /* 0x20 */ s32 nz2; + /* 0x24 */ s32 prec; + /* 0x28 */ s32 width; + /* 0x2C */ u32 nchar; + /* 0x30 */ u32 flags; + /* 0x34 */ u8 qual; +} _Pft; // size = 0x38 + +typedef void* (*PrintCallback)(void*, const char*, int); + +#define FLAGS_SPACE 1 +#define FLAGS_PLUS 2 +#define FLAGS_MINUS 4 +#define FLAGS_HASH 8 +#define FLAGS_ZERO 16 + +static void _Litob(_Pft* args, u8 type); +static void _Ldtob(_Pft* args, u8 type); +extern int _Printf(void* (*prout_func)(void*, const char*, int), void* arg, + const char* fmt, va_list ap); #ifdef __cplusplus } diff --git a/src/libultra/xldtob.c b/src/libultra/xldtob.c new file mode 100644 index 00000000..b29400f7 --- /dev/null +++ b/src/libultra/xldtob.c @@ -0,0 +1,288 @@ +#include /* ldiv & ldiv_t */ +#include /* memcpy */ + +#include "xprintf.h" + +#define BUFF_LEN 0x20 + +s16 _Ldunscale(s16*, _Pft*); +void _Genld(_Pft*, u8, u8*, s16, s16); + +const f64 pows[] = {10e0L, 10e1L, 10e3L, 10e7L, 10e15L, + 10e31L, 10e63L, 10e127L, 10e255L}; + +/* float properties */ +#define _D0 0 +#define _DBIAS 0x3FF +#define _DLONG 1 +#define _DOFF 4 +#define _FBIAS 0x7E +#define _FOFF 7 +#define _FRND 1 +#define _LBIAS 0x3FFE +#define _LOFF 15 +/* integer properties */ +#define _C2 1 +#define _CSIGN 1 +#define _ILONG 0 +#define _MBMAX 8 +#define NAN 2 +#define INF 1 +#define FINITE -1 +#define _DFRAC ((1 << _DOFF) - 1) +#define _DMASK (0x7FFF & ~_DFRAC) +#define _DMAX ((1 << (15 - _DOFF)) - 1) +#define _DNAN (0x8000 | _DMAX << _DOFF | 1 << (_DOFF - 1)) +#define _DSIGN 0x8000 +#if _D0 == 3 +#define _D1 2 /* little-endian order */ +#define _D2 1 +#define _D3 0 +#else +#define _D1 1 /* big-endian order */ +#define _D2 2 +#define _D3 3 +#endif + +#pragma optimize_for_size off +void _Ldtob(_Pft* args, u8 type) { + u8 buff[BUFF_LEN]; + u8* ptr = buff; + u32 sp70; + f64 val = args->v.ld; + /* maybe struct? */ + s16 err; + s16 nsig; + s16 exp; + + int i; + int n; + f64 factor; + int gen; + int j; + int lo; + u8 drop; + int n2; + f64* pows_p; + + if (args->prec < 0) { + args->prec = 6; + } else if (args->prec == 0 && (type == 'g' || type == 'G')) { + args->prec = 1; + } + err = _Ldunscale(&exp, (_Pft*)args); + if (err > 0) { + memcpy(args->s, err == 2 ? "NaN" : "Inf", args->n1 = 3); + return; + } + if (err == 0) { + nsig = 0; + exp = 0; + } else { + if (val < 0) { + val = -val; + } + if ((exp = (exp * 30103) / 100000 - 4) < 0) { + n = (3 - (int)exp) & ~3; + pows_p = (double*)pows; + exp = -n; + for (; n > 0; n >>= 1, pows_p++) { + if ((n & 1) != 0) { + val *= *pows_p; + } + } + } else if (exp > 0) { + factor = 1; + exp &= ~3; + + pows_p = (double*)pows; + for (n = exp; n > 0; n >>= 1, pows_p++) { + if ((n & 1) != 0) { + factor *= *pows_p; + } + } + val /= factor; + } + gen = ((type == 'f') ? exp + 10 : 6) + args->prec; + if (gen > 0x13) { + gen = 0x13; + } + *ptr++ = '0'; + while (gen > 0 && 0 < val) { + lo = val; + if ((gen -= 8) > 0) { + val = (val - lo) * 1.0e8; + } + ptr = ptr + 8; + for (j = 8; lo > 0 && --j >= 0;) { + const ldiv_t qr = ldiv(lo, 10); + *--ptr = qr.rem + '0'; + lo = qr.quot; + } + while (--j >= 0) { + ptr--; + *ptr = '0'; + } + ptr += 8; + } + + gen = ptr - &buff[1]; + for (ptr = &buff[1], exp += 7; *ptr == '0'; ptr++) { + --gen, --exp; + } + + nsig = ((type == 'f') ? exp + 1 : ((type == 'e' || type == 'E') ? 1 : 0)) + + args->prec; + if (gen < nsig) { + nsig = gen; + } + if (nsig > 0) { + char drop = nsig < gen && '5' <= ptr[nsig] ? '9' : '0'; + int n; + + for (n = nsig; ptr[--n] == drop;) { + --nsig; + } + + if (drop == '9') { + ++ptr[n]; + } + + if (n < 0) { + --ptr, ++nsig, ++exp; + } + } + } + _Genld((_Pft*)args, type, ptr, nsig, exp); +} + +s16 _Ldunscale(s16* pex, _Pft* px) { + u16* ps = (u16*)px; + s16 xchar = (ps[_D0] & _DMASK) >> _DOFF; + + if (xchar == _DMAX) { /* NaN or INF */ + *pex = 0; + return (s16)(ps[_D0] & _DFRAC || ps[_D1] || ps[_D2] || ps[_D3] ? NAN : INF); + } else if (0 < xchar) { + ps[_D0] = (ps[_D0] & ~_DMASK) | (_DBIAS << _DOFF); + *pex = xchar - (_DBIAS - 1); + return FINITE; + } + if (0 > xchar) { + return NAN; + } else { + *pex = 0; + return 0; + } +} + +void _Genld(_Pft* px, u8 code, u8* p, s16 nsig, s16 xexp) { + u8 point = '.'; + + if (nsig <= 0) { + nsig = 1, + + p = (u8*)"0"; + } + + if (code == 'f' || ((code == 'g' || code == 'G') && (-4 <= xexp) && + (xexp < px->prec))) { /* 'f' format */ + int x; + + ++xexp; /* change to leading digit count */ + if (code != 'f') { /* fixup for 'g' */ + if (!(px->flags & FLAGS_HASH) && nsig < px->prec) { + px->prec = nsig; + } + if ((px->prec -= xexp) < 0) { + px->prec = 0; + } + } + if (xexp <= 0) { /* digits only to right of point */ + px->s[px->n1++] = '0'; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1++] = point; + } + if (px->prec < -xexp) { + xexp = -px->prec; + } + px->nz1 = -xexp; + px->prec += xexp; + if (px->prec < nsig) { + nsig = px->prec; + } + memcpy(&px->s[px->n1], p, x = px->n2 = nsig); + px->nz2 = px->prec - x; + } else if (nsig < xexp) { /* zeros before point */ + memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = xexp - nsig; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1] = point, ++px->n2; + } + px->nz2 = px->prec; + } else { /* enough digits before point */ + memcpy(&px->s[px->n1], p, xexp); + px->n1 += xexp; + nsig -= xexp; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1++] = point; + } + if (px->prec < nsig) { + nsig = px->prec; + } + memcpy(&px->s[px->n1], p + (int)xexp, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + } else { /* 'e' format */ + if (code == 'g' || code == 'G') { /* fixup for 'g' */ + if (nsig < px->prec) { + px->prec = nsig; + } + if (--px->prec < 0) { + px->prec = 0; + } + code = code == 'g' ? 'e' : 'E'; + } + px->s[px->n1++] = *p++; + if (0 < px->prec || px->flags & FLAGS_HASH) { + px->s[px->n1++] = point; + } + if (0 < px->prec) { /* put fraction digits */ + if (px->prec < --nsig) { + nsig = px->prec; + } + memcpy(&px->s[px->n1], p, nsig); + px->n1 += nsig; + px->nz1 = px->prec - nsig; + } + p = (u8*)&px->s[px->n1]; /* put exponent */ + *p++ = code; + if (0 <= xexp) { + *p++ = '+'; + } else { /* negative exponent */ + *p++ = '-'; + xexp = -xexp; + } + if (100 <= xexp) { /* put oversize exponent */ + if (1000 <= xexp) { + *p++ = xexp / 1000 + '0', xexp %= 1000; + } + *p++ = xexp / 100 + '0', xexp %= 100; + } + *p++ = xexp / 10 + '0', xexp %= 10; + *p++ = xexp + '0'; + px->n2 = p - (u8*)&px->s[px->n1]; + } + if ((px->flags & (FLAGS_ZERO | FLAGS_MINUS)) == + FLAGS_ZERO) { /* pad with leading zeros */ + s32 n = px->n0 + px->n1 + px->nz1 + px->n2 + px->nz2; + + if (n < px->width) { + px->nz0 = px->width - n; + } + } +} + +#pragma optimize_for_size reset diff --git a/src/libultra/xlitob.c b/src/libultra/xlitob.c new file mode 100644 index 00000000..b327099d --- /dev/null +++ b/src/libultra/xlitob.c @@ -0,0 +1,59 @@ +#include /* lldiv & lldiv_t */ +#include /* memcpy */ +#include "libultra/xprintf.h" + +#define BUFF_LEN 0x18 + +u8 ldigs[] = "0123456789abcdef"; +u8 udigs[] = "0123456789ABCDEF"; + +static void _Litob(_Pft* args, u8 type) { + u8* numMap; + int base; + int idx; + u64 num; + lldiv_t test; + char buff[BUFF_LEN]; + + if (type == 'X') { + numMap = udigs; + } else { + numMap = ldigs; + } + + base = (type == 'o') ? 8 : ((type != 'x' && type != 'X') ? 10 : 16); + idx = BUFF_LEN; + num = args->v.ll; + + if ((type == 'd' || type == 'i') && args->v.ll < 0) { + num = -num; + } + + if (num != 0 || args->prec != 0) { + buff[--idx] = numMap[num % base]; + } + + args->v.ll = num / base; + + while (args->v.ll > 0 && idx > 0) { + const lldiv_t quotrem = lldiv(args->v.ll, base); + args->v.ll = quotrem.quot; + buff[--idx] = numMap[quotrem.rem]; + } + + args->n1 = BUFF_LEN - idx; + + memcpy(args->s, buff + idx, args->n1); + + if (args->n1 < args->prec) { + args->nz0 = args->prec - args->n1; + } + + if (args->prec < 0 && + (args->flags & (FLAGS_ZERO | FLAGS_MINUS)) == FLAGS_ZERO) { + idx = args->width - args->n0 - args->nz0 - args->n1; + if (idx > 0) { + args->nz0 += idx; + } + } +} diff --git a/src/libultra/xprint.c b/src/libultra/xprint.c deleted file mode 100644 index 579ecbf1..00000000 --- a/src/libultra/xprint.c +++ /dev/null @@ -1,5 +0,0 @@ -#include "libultra/xprintf.h" - -extern int _Printf(void* (*prout_func)(void*, const char*, int), void* arg, const char* fmt, va_list ap) { - -} diff --git a/src/libultra/xprintf.c b/src/libultra/xprintf.c new file mode 100644 index 00000000..74eafe05 --- /dev/null +++ b/src/libultra/xprintf.c @@ -0,0 +1,240 @@ +#include /* strchr */ + +#include "libultra/xprintf.h" + +#define ATOI(i, a) \ + for (i = 0; *a >= '0' && *a <= '9'; a++) \ + if (i < 999) i = *a + i * 10 - '0'; + +#define _PROUT(fmt, _size) \ + if (_size > 0) { \ + arg = pfn(arg, fmt, _size); \ + if (arg != NULL) \ + x.nchar += _size; \ + else \ + return x.nchar; \ + } +#define _PROUT2(fmt, _size) \ + if ((u32)(_size) != 0) { \ + arg = pfn(arg, fmt, (u32)(_size)); \ + if (arg != NULL) \ + x.nchar += _size; \ + else \ + return x.nchar; \ + } +#define _PAD(m, src, extracond) \ + if (extracond && m > 0) { \ + s32 i; \ + s32 j; \ + for (j = m; j > 0; j -= i) { \ + if (j > 32) \ + i = 32; \ + else \ + i = j; \ + _PROUT(src, i); \ + } \ + } + +static const char spaces[] = " "; +static const char zeroes[] = "00000000000000000000000000000000"; + +void _Putfld(_Pft*, va_list, u8, u8*); + +/* These functions seem to have 04,p enabled */ +#pragma optimize_for_size off +s32 _Printf(void* (*pfn)(void*, const char*, int), void* arg, const char* fmt, + va_list ap) { + /* Unused static variables to match position of locals */ + static int unused0; + static int unused1; + static int unused2; + + static char fchar[] = " +-#0"; + static u32 fbit[6] = {FLAGS_SPACE, FLAGS_PLUS, FLAGS_MINUS, + FLAGS_HASH, FLAGS_ZERO, 0}; + _Pft x; + x.nchar = 0; + + while (TRUE) { + u8 qual; + const u8* s = (u8*)fmt; + u8 c; + const char* t; + u8 ac[0x20]; + u8* temp; + + for (c = *s; c != 0 && c != '%';) { + c = *++s; + } + temp = (u8*)fmt; + _PROUT2(fmt, s - temp); + if (c == 0) { + return x.nchar; + } + fmt = (char*)++s; + x.flags = 0; + for (; (t = strchr(fchar, *s)) != NULL; s++) { + x.flags |= fbit[t - fchar]; + } + if (*s == '*') { + x.width = va_arg(ap, s32); + if (x.width < 0) { + x.width = -x.width; + x.flags |= FLAGS_MINUS; + } + s++; + } else { + ATOI(x.width, s); + } + if (*s != '.') { + x.prec = -1; + } else { + s++; + if (*s == '*') { + x.prec = va_arg(ap, s32); + s++; + } else { + ATOI(x.prec, s); + } + } + if (strchr("hlL", *s) != NULL) { + qual = *s++; + } else { + qual = 0; + } + x.qual = qual; + + if (qual == 'l' && *s == 'l') { + x.qual = 'L'; + s++; + } + _Putfld(&x, ap, *s, ac); + x.width -= x.n0 + x.nz0 + x.n1 + x.nz1 + x.n2 + x.nz2; + _PAD(x.width, spaces, !(x.flags & FLAGS_MINUS)); + _PROUT((char*)ac, x.n0); + _PAD(x.nz0, zeroes, 1); + _PROUT(x.s, x.n1); + _PAD(x.nz1, zeroes, 1); + _PROUT((char*)(&x.s[x.n1]), x.n2) + _PAD(x.nz2, zeroes, 1); + _PAD(x.width, spaces, x.flags & FLAGS_MINUS); + fmt = (char*)s + 1; + } +} + +void _Putfld(_Pft* px, va_list ap, u8 code, u8* ac) { + px->n0 = px->nz0 = px->n1 = px->nz1 = px->n2 = px->nz2 = 0; + + switch ((u8)code) { + case 'c': + ac[px->n0++] = va_arg(ap, u32); + break; + + case 'd': + case 'i': + if (px->qual == 'l') { + px->v.ll = va_arg(ap, s32); + } else if (px->qual == 'L') { + px->v.ll = va_arg(ap, s64); + } else { + px->v.ll = va_arg(ap, s32); + } + + if (px->qual == 'h') { + px->v.ll = (s16)px->v.ll; + } + + if (px->v.ll < 0) { + ac[px->n0++] = '-'; + } else if (px->flags & FLAGS_PLUS) { + ac[px->n0++] = '+'; + } else if (px->flags & FLAGS_SPACE) { + ac[px->n0++] = ' '; + } + + px->s = (char*)&ac[px->n0]; + + _Litob(px, code); + break; + case 'x': + case 'X': + case 'u': + case 'o': + if (px->qual == 'l') { + px->v.ll = va_arg(ap, s32); + } else if (px->qual == 'L') { + px->v.ll = va_arg(ap, s64); + } else { + px->v.ll = va_arg(ap, s32); + } + + if (px->qual == 'h') { + px->v.ll = (u16)px->v.ll; + } else if (px->qual == 0) { + px->v.ll = (u32)px->v.ll; + } + + if (px->flags & FLAGS_HASH) { + ac[px->n0++] = '0'; + if (code == 'x' || code == 'X') { + + ac[px->n0++] = code; + } + } + px->s = (char*)&ac[px->n0]; + _Litob(px, code); + break; + case 'e': + case 'f': + case 'g': + case 'E': + case 'G': + px->v.ld = px->qual == 'L' ? va_arg(ap, f64) : va_arg(ap, f64); + + if (*(u16*)&px->v.ll & 0x8000) { + ac[px->n0++] = '-'; + } else { + if (px->flags & FLAGS_PLUS) { + ac[px->n0++] = '+'; + } else if (px->flags & FLAGS_SPACE) { + ac[px->n0++] = ' '; + } + } + + px->s = (char*)&ac[px->n0]; + _Ldtob(px, code); + break; + case 'n': + if (px->qual == 'h') { + *(va_arg(ap, s16*)) = px->nchar; + } else if (px->qual == 'l') { + *va_arg(ap, s32*) = px->nchar; + } else if (px->qual == 'L') { + *va_arg(ap, s64*) = px->nchar; + } else { + *va_arg(ap, s32*) = px->nchar; + } + break; + + case 'p': + px->v.ll = (s32)va_arg(ap, void*); + px->s = (char*)&ac[px->n0]; + _Litob(px, 'x'); + break; + case 's': + px->s = va_arg(ap, char*); + px->n1 = strlen(px->s); + if (px->prec >= 0 && px->n1 > px->prec) { + px->n1 = px->prec; + } + break; + case '%': + ac[px->n0++] = '%'; + break; + default: + ac[px->n0++] = code; + break; + } +} + +#pragma optimize_for_size reset