summaryrefslogtreecommitdiff
path: root/src/libultra_boot_O2/_Printf.c
blob: cbb803bbdce82313934a08debe403e596564619f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
#include <ultra64.h>
#include <ultra64/printf.h>
#include <stdarg.h>
#include <string.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 = (void*)pfn(arg, fmt, _size);                                                                  \
        if (arg != 0)                                                                                  \
            x.nchar += _size;                                                                        \
        else                                                                                           \
            return x.nchar;                                                                          \
    }
#define _PAD(m, src, extracond)                                                                       \
    if (extracond && m > 0) {                                                                          \
        int i;                                                                                         \
        int j;                                                                                         \
        for (j = m; j > 0; j -= i) {                                                                   \
            if ((u32) j > 32)                                                                          \
                i = 32;                                                                                \
            else                                                                                       \
                i = j;                                                                                 \
            _PROUT(src, i);                                                                       \
        }                                                                                              \
    }

char spaces[] = "                                ";
char zeroes[] = "00000000000000000000000000000000";

void _Putfld(_Pft *, va_list *, u8, u8 *);

s32 _Printf(char *(*pfn)(char *, const char *, size_t), char *arg, const char *fmt, va_list ap)
{
    _Pft x;
    x.nchar = 0;
    while (1) {
        const u8* s;
        u8 c;
        u8* t;

        static const u8 fchar[] = " +-#0";
        static const u32 fbit[] = { FLAGS_SPACE, FLAGS_PLUS, FLAGS_MINUS, FLAGS_HASH, FLAGS_ZERO, 0 };

        u8 ac[0x20];

        s = (u8 *)fmt;
        while ((c = *s) != 0 && c != '%') {
            s++;
        }
        _PROUT(fmt, s - (u8 *)fmt);
        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) {
            x.qual = *s++;
        } else {
            x.qual = 0;
        }

        if (x.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* pap, u8 code, u8* ac) {
    px->n0 = px->nz0 = px->n1 = px->nz1 = px->n2 =
        px->nz2 = 0;

    switch (code) {

        case 'c':
            ac[px->n0++] = va_arg(*pap, u32);
            break;

        case 'd':
        case 'i':
            if (px->qual == 'l') {
                px->v.ll = va_arg(*pap, s32);
            } else if (px->qual == 'L') {
                px->v.ll = va_arg(*pap, s64);
            } else {
                px->v.ll = va_arg(*pap, 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(*pap, s32);
            } else if (px->qual == 'L') {
                px->v.ll = va_arg(*pap, s64);
            } else {
                px->v.ll = va_arg(*pap, 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(*pap, f64) : va_arg(*pap, 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(*pap, u16 *)) = px->nchar;
            } else if (px->qual == 'l') {
                *va_arg(*pap, u32 *) = px->nchar;
            } else if (px->qual == 'L') {
                *va_arg(*pap, u64 *) = px->nchar;
            } else {
                *va_arg(*pap, u32 *) = px->nchar;
            }
            break;

        case 'p':
            px->v.ll = (long) va_arg(*pap, void *);
            px->s = (char *) &ac[px->n0];
            _Litob(px, 'x');
            break;

        case 's':
            px->s = va_arg(*pap, 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;
    }
}