summaryrefslogtreecommitdiff
path: root/kc/core/kprint.c
blob: 596d11026c64eec69e117756ab2d5e062741f7c0 (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
#include "kprint.h"
#include "serial.h"

#include <kc/lib.h>

#include <stdint.h>

__attribute__((optimize("O3")))
int kputchar(int c)
{
    return serial_putchar(c);
}

__attribute__((optimize("O3")))
int kputs(const char *s)
{
    while (*s)
    {
        kputchar(*s++);
    }

    return 0;
}

static char *convert_scalar(
        uint64_t value,
        unsigned base, 
        char *buffer, 
        size_t bufsz)
{
    static const char *stringdigits = "0123456789abcdef";

    // the string will be built from the lower-to-higher value, and the
    // result pointer will point to the first character of the string in
    // the supplied buffer

    // gotta zero the buffer
    memset(buffer, 0, bufsz);

    // the string is being built backwards, so the pointer needs to be
    // at the last byte of the string
    char *result = buffer + bufsz - 1;

    // result >= buffer condition ensures we don't overflow
    
    while (value > 0 && result >= buffer)
    {
        *--result = stringdigits[value % base];
        value /= base;
    }

    return result;
}

int kvprintf(const char *restrict format, va_list arguments)
{
    int count = 0;

    while (*format)
    {
        if (*format != '%')
        {
            kputchar(*format++);
            count++;
            continue;
        }

        switch (*++format)
        {
            case 0:
                return count;
            case '%':
                count++;
                kputchar('%');
                break;
            case 's':
                {
                    const char *s = va_arg(arguments, const char *);
                    count += strlen(s);
                    kputs(s);
                    break;
                }

            case 'x':
                {
                    char buffer[65];
                    char *s = convert_scalar(
                            va_arg(arguments, uint64_t),
                            16,
                            buffer,
                            sizeof(buffer));
                    count += strlen(s);
                    kputs(s);
                    break;
                }
            case 'd':
                {
                    char buffer[65];
                    char *s = convert_scalar(
                            va_arg(arguments, uint64_t),
                            10,
                            buffer,
                            sizeof(buffer));
                    count += strlen(s);
                    kputs(s);
                    break;
                }
            case 'o':
                {
                    char buffer[65];
                    char *s = convert_scalar(
                            va_arg(arguments, uint64_t),
                            8,
                            buffer,
                            sizeof(buffer));
                    count += strlen(s);
                    kputs(s);
                    break;
                }
            case 'b':
                {
                    char buffer[65];
                    char *s = convert_scalar(
                            va_arg(arguments, uint64_t),
                            2,
                            buffer,
                            sizeof(buffer));
                    count += strlen(s);
                    kputs(s);
                    break;
                }

            default:
                break;
        }

        format++;
    }

    va_end(arguments);
    return count;
}

int kprintf(const char *restrict format, ...)
{
    va_list arguments;
    va_start (arguments, format);

    return kvprintf(format, arguments);
}