Files
pokediamond/arm9/lib/src/OS_printf.c
T
2020-06-15 22:02:59 +01:00

493 lines
14 KiB
C

#include "global.h"
#include "OS_printf.h"
typedef struct printfStr
{
s32 spaceLeft;
s8 *stringEnd;
s8 *stringStart;
} printfStr;
void string_put_char(struct printfStr *dest, s8 value);
void string_fill_char(struct printfStr *dest, s8 value, s32 count);
void string_put_string(struct printfStr *dest, const s8 *src, s32 count);
ARM_FUNC void string_put_char(struct printfStr *dest, s8 value)
{
if (dest->spaceLeft != 0)
{
dest->stringEnd[0] = value;
dest->spaceLeft--;
}
dest->stringEnd++;
}
ARM_FUNC void string_fill_char(struct printfStr *dest, s8 value, s32 count)
{
if (count <= 0)
return;
u32 written = 0;
u32 spaceLeft = (u32)dest->spaceLeft;
u32 toWrite = spaceLeft > (u32)count ? count : spaceLeft;
while (written < toWrite)
{
dest->stringEnd[written] = value;
written++;
}
dest->spaceLeft -= toWrite;
dest->stringEnd += count; // this is wrong but matching...
}
ARM_FUNC void string_put_string(struct printfStr *dest, const s8 *src, s32 count)
{
if (count <= 0)
return;
u32 written = 0;
u32 spaceLeft = (u32)dest->spaceLeft;
u32 toWrite = spaceLeft > (u32)count ? count : spaceLeft;
while (written < toWrite)
{
dest->stringEnd[written] = src[written];
written++;
}
dest->spaceLeft -= toWrite;
dest->stringEnd += count; // this is wrong but matching...
}
ARM_FUNC s32 OS_SPrintf(s8 *buffer, const s8 *format, ...)
{
void *args = (void *)((u32 *)((u32)&format & ~0x3) + 1); // hack since mwccarm doesn't have <stdarg.h> apparently
return OS_VSPrintf(buffer, format, args);
}
ARM_FUNC s32 OS_VSPrintf(s8 *buffer, const s8 *format, void *args)
{
return OS_VSNPrintf(buffer, 0x7FFFFFFF, format, args);
}
ARM_FUNC s32 OS_SNPrintf(s8 *buffer, s32 bufsz, const s8 *format, ...)
{
void *args = (void *)((u32 *)((u32)&format & ~0x3) + 1); // hack since mwccarm doesn't have <stdarg.h> apparently
return OS_VSNPrintf(buffer, bufsz, format, args);
}
#define va_arg(list, ty) *(ty *)((u32 *)(list = (void *)((u32 *)(list) + 1)) - 1)
#define va_arg_64(list, sgn) *((sgn##64 *)(list = (void *)((sgn##64 *)(list) + 1)) - 1)
ARM_FUNC s32 OS_VSNPrintf(s8 *buffer, s32 bufsz, const s8 *format, void *args)
{
s8 buf[24];
s32 n_buf;
s8 prefix[2];
s32 n_prefix;
const s8 *s = format;
printfStr str;
str.spaceLeft = bufsz;
str.stringStart = buffer;
str.stringEnd = buffer;
while (*s)
{
// matches:
// binary range (hex range) [dec range]
// 1000 0001-1001 1111 (0x81-0x9F) [129-159]
// 1110 0000-1111 1100 (0xE0-0xFC) [224-252]
if ((u32)(((u8)*s ^ 0x20) - 0xa1) < 0x3c)
{
string_put_char(&str, *s++);
if (*s)
{
string_put_char(&str, *s++);
}
}
else if (*s != '%')
{
string_put_char(&str, *s++);
}
else
{
enum {
blank = 000001,
plus = 000002,
sharp = 000004,
minus = 000010,
zero = 000020,
l1 = 000040,
h1 = 000100,
l2 = 000200,
h2 = 000400,
usigned = 010000,
end
};
s32 flag = 0, width = 0, precision = -1, radix = 10;
s8 hex = 'a' - 10;
const s8 *p_start = s;
for (;;)
{
switch (*++s)
{
case '+':
if (s[-1] != ' ')
break;
flag |= plus;
continue;
case ' ':
flag |= blank;
continue;
case '-':
flag |= minus;
continue;
case '0':
flag |= zero;
continue;
}
break;
}
if (*s == '*')
{
++s, width = va_arg(args, s32);
if (width < 0)
{
width = -width, flag |= minus;
}
}
else
{
while ((*s >= '0') && (*s <= '9'))
{
width = (width * 10) + *s++ - '0';
}
}
if (*s == '.')
{
++s, precision = 0;
if (*s == '*')
{
++s, precision = va_arg(args, s32);
if (precision < 0)
{
precision = -1;
}
}
else
{
while ((*s >= '0') && (*s <= '9'))
{
precision = (precision * 10) + *s++ - '0';
}
}
}
switch (*s)
{
case 'h':
if (*++s != 'h') {
flag |= h1;
}
else {
++s, flag |= h2;
}
break;
case 'l':
if (*++s != 'l') {
flag |= l1;
}
else {
++s, flag |= l2;
}
break;
}
switch (*s)
{
case 'd':
case 'i':
goto put_int;
case 'o':
radix = 8;
flag |= usigned;
goto put_int;
case 'u':
flag |= usigned;
goto put_int;
case 'X':
hex = 'A' - 10;
goto put_hex;
case 'x':
goto put_hex;
case 'p':
flag |= sharp;
precision = 8;
goto put_hex;
case 'c':
if (precision >= 0)
goto put_invalid;
{
s32 v = va_arg(args, s32);
width -= 1;
if (flag & minus)
{
string_put_char(&str, (s8)v);
string_fill_char(&str, ' ', width);
}
else
{
s8 pad = (s8)((flag & zero) ? '0' : ' ');
string_fill_char(&str, pad, width);
string_put_char(&str, (s8)v);
}
++s;
}
break;
case 's':
{
s32 n_bufs = 0;
const s8 *p_bufs = va_arg(args, const s8 *);
if (precision < 0)
{
while (p_bufs[n_bufs])
{
++n_bufs;
}
}
else
{
while (n_bufs < precision && p_bufs[n_bufs])
{
++n_bufs;
}
}
width -= n_bufs;
if (flag & minus)
{
string_put_string(&str, p_bufs, n_bufs);
string_fill_char(&str, ' ', width);
}
else
{
s8 pad = (s8)((flag & zero) ? '0' : ' ');
string_fill_char(&str, pad, width);
string_put_string(&str, p_bufs, n_bufs);
}
++s;
}
break;
case 'n':
{
s32 count = str.stringEnd - str.stringStart;
if (!(flag & h2))
{
if (flag & h1)
{
*va_arg(args, s16 *) = (s16)count;
}
else if (flag & l2)
{
*va_arg(args, u64 *) = (u64)count;
}
else
{
*va_arg(args, s32 *) = count;
}
}
}
++s;
break;
case '%':
if (p_start + 1 != s)
goto put_invalid;
string_put_char(&str, *s++);
break;
default:
goto put_invalid;
put_invalid:
string_put_string(&str, p_start, s - p_start);
break;
put_hex:
radix = 16;
flag |= usigned;
put_int:
{
u64 value = 0;
n_prefix = 0;
if (flag & minus)
{
flag &= ~zero;
}
if (precision < 0)
{
precision = 1;
}
else
{
flag &= ~zero;
}
if (flag & usigned)
{
if (flag & h2)
{
value = va_arg(args, u8);
}
else if (flag & h1)
{
value = va_arg(args, u16);
}
else if (flag & l2)
{
value = va_arg_64(args, u);
}
else
{
value = va_arg(args, u32);
}
flag &= ~(plus | blank);
if (flag & sharp)
{
if (radix == 16)
{
if (value != 0)
{
prefix[0] = (s8)(hex + (10 + 'x' - 'a'));
prefix[1] = '0';
n_prefix = 2;
}
}
else if (radix == 8)
{
prefix[0] = '0';
n_prefix = 1;
}
}
}
else {
if (flag & h2) {
value = va_arg(args, s8);
} else if (flag & h1) {
value = va_arg(args, s16);
} else if (flag & l2) {
value = va_arg_64(args, u);
} else {
value = va_arg(args, s32);
}
if ((value >> 32) & 0x80000000) {
value = ~value + 1;
prefix[0] = '-';
n_prefix = 1;
} else {
if (value || precision) {
if (flag & plus) {
prefix[0] = '+';
n_prefix = 1;
} else if (flag & blank) {
prefix[0] = ' ';
n_prefix = 1;
}
}
}
}
n_buf = 0;
switch (radix) {
case 8:
while (value != 0) {
s32 octDig = (s32) (value & 0x7);
value >>= 3;
buf[n_buf++] = (s8) (octDig + '0');
}
break;
case 10:
if ((value >> 32) == 0) {
u32 v = (u32) value;
while (v) {
u32 div10 = v / 10;
s32 dig = (s32) (v - (div10 * 10));
v = div10;
buf[n_buf++] = (s8) (dig + '0');
}
} else {
while (value) {
u64 div10 = value / 10;
s32 dig = (s32) (value - (div10 * 10));
value = div10;
buf[n_buf++] = (s8) (dig + '0');
}
}
break;
case 16:
while (value != 0) {
s32 hexDig = (s32) (value & 0xf);
value >>= 4;
buf[n_buf++] = (s8) ((hexDig < 10) ? (hexDig + '0') : (hexDig + hex));
}
break;
}
if (n_prefix > 0 && prefix[0] == '0') {
n_prefix = 0;
buf[n_buf++] = '0';
}
}
goto put_to_stream;
put_to_stream:
{
s32 n_pad = (s32)(precision - n_buf);
if (flag & zero)
{
if (n_pad < width - n_buf - n_prefix)
{
n_pad = (s32)(width - n_buf - n_prefix);
}
}
if (n_pad > 0)
{
width -= n_pad;
}
width -= n_prefix + n_buf;
if (!(flag & minus))
{
string_fill_char(&str, ' ', width);
}
while (n_prefix > 0)
{
string_put_char(&str, prefix[--n_prefix]);
}
string_fill_char(&str, '0', n_pad);
while (n_buf > 0)
{
string_put_char(&str, buf[--n_buf]);
}
if (flag & minus)
{
string_fill_char(&str, ' ', width);
}
++s;
}
break;
}
}
}
if (str.spaceLeft != 0)
{
*str.stringEnd = '\0';
}
else if (bufsz != 0)
{
str.stringStart[bufsz - 1] = '\0';
}
return str.stringEnd - str.stringStart;
}