Implement printf in the CRT
[monolithium.git] / kernel / src / common.c
1 /*
2  * common.c
3  *
4  * Copyright (C) 2016 Aleksandar Andrejevic <theflash@sdf.lonestar.org>
5  *
6  * This program is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU Affero General Public License as
8  * published by the Free Software Foundation, either version 3 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU Affero General Public License for more details.
15  *
16  * You should have received a copy of the GNU Affero General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include <common.h>
21 #include <heap.h>
22 #include <video.h>
23 #include <thread.h>
24 #include <timer.h>
25
26 static dword_t terminal_row = 0, terminal_col = 0;
27 static byte_t terminal_color = 0x07;
28
29 bool_t video_initialized = FALSE;
30
31 const char *get_error_string(dword_t err_num)
32 {
33     static const char *error_strings[] = {
34         "ERR_SUCCESS",
35         "ERR_NOTFOUND",
36         "ERR_FORBIDDEN",
37         "ERR_INVALID",
38         "ERR_EXISTS",
39         "ERR_NOMEMORY",
40         "ERR_HARDWARE",
41         "ERR_BUSY",
42         "ERR_NOMEDIA",
43         "ERR_NOTRESP",
44         "ERR_WRITEPROT",
45         "ERR_NOSYSCALL",
46         "ERR_TIMEOUT",
47         "ERR_BADPTR",
48         "ERR_CANCELED",
49         "ERR_ISDIR",
50         "ERR_ISNOTDIR",
51         "ERR_DISKFULL",
52         "ERR_MEDIACHG"
53     };
54
55     if (err_num == 0)
56     {
57         return error_strings[0];
58     }
59     else if (err_num >= 0xE0000000)
60     {
61         if (err_num < MAX_ERR) return error_strings[err_num - 0xE0000000];
62     }
63
64     return NULL;
65 }
66
67 void set_text_color(byte_t color)
68 {
69     terminal_color = color;
70 }
71
72 void clearscreen(void)
73 {
74     word_t *video_mem = (word_t*)TEXT_VIDEO_MEMORY;
75     memset(video_mem, 0x00, TEXT_HEIGHT * TEXT_WIDTH * sizeof(word_t));
76 }
77
78 static void scroll(void)
79 {
80     word_t *video_mem = (word_t*)(video_initialized ? VIDEO_MEMORY : TEXT_VIDEO_MEMORY);
81     memcpy(video_mem, &video_mem[TEXT_WIDTH], TEXT_WIDTH * (TEXT_HEIGHT - 1) * 2);
82     memset(&video_mem[(TEXT_HEIGHT - 1) * TEXT_WIDTH], 0, TEXT_WIDTH * 2);
83 }
84
85 void putchar(char character)
86 {
87     if (character == '\n')
88     {
89         if (terminal_row < (TEXT_HEIGHT - 1)) terminal_row++;
90         else scroll();
91
92         terminal_col = 0;
93     }
94     else if (character == '\t')
95     {
96         terminal_col++;
97         while(terminal_col % 8) terminal_col++;
98     }
99     else if (character == '\r')
100     {
101         terminal_col = 0;
102     }
103     else
104     {
105         word_t *video_mem = (word_t*)(video_initialized ? VIDEO_MEMORY : TEXT_VIDEO_MEMORY);
106         video_mem[terminal_row * TEXT_WIDTH + terminal_col] = character | (terminal_color << 8);
107         terminal_col++;
108     }
109
110     if (terminal_col == TEXT_WIDTH)
111     {
112         if (terminal_row < (TEXT_HEIGHT - 1)) terminal_row++;
113         else scroll();
114         terminal_col = 0;
115     }
116
117     if (video_initialized)
118     {
119         video_cursor_location_t params = {
120             .row = terminal_row,
121             .column = terminal_col
122         };
123
124         device_t *video_device = get_char_device("Video0");
125         if (video_device) device_ioctl_internal(video_device, IOCTL_VIDEO_SET_TEXT_CURSOR, &params, sizeof(params), NULL, 0);
126     }
127     else
128     {
129         dword_t index = terminal_row * 80 + terminal_col;
130         outportb(VGA_CRTC_INDEX, 0x0F);
131         outportb(VGA_CRTC_DATA, (byte_t) (index & 0xFF));
132         outportb(VGA_CRTC_INDEX, 0x0E);
133         outportb(VGA_CRTC_DATA, (byte_t) ((index >> 8) & 0xFF));
134     }
135 }
136
137 void puts(const char *string)
138 {
139     while (*string) putchar(*(string++));
140     putchar('\n');
141 }
142
143 int vsnprintf(char *output, dword_t count, const char *format, va_list args)
144 {
145     int ret = 0;
146     const char *ptr;
147     const char *valid_flags = "-+ #0";
148
149     for (ptr = format; *ptr != '\0'; ptr++)
150     {
151         if (*ptr != '%')
152         {
153             if (count <= 1) break;
154             output[ret++] = *ptr;
155             count--;
156             continue;
157         }
158
159         ptr++;
160
161         int i;
162         char *p;
163         char padding_char = ' ', sign = '\0';
164         int padding = 0;
165         bool_t alternative = FALSE;
166         bool_t left_justify = FALSE;
167         bool_t external_width = FALSE;
168         bool_t external_precision = FALSE;
169         int width = 0, precision = -1;
170
171         while (*ptr)
172         {
173             char *flag = strchr(valid_flags, *ptr);
174             if (flag == NULL) break;
175
176             ptr++;
177
178             switch (*flag)
179             {
180             case '+':
181                 sign = '+';
182                 break;
183
184             case '-':
185                 left_justify = TRUE;
186                 break;
187
188             case ' ':
189                 sign = ' ';
190                 break;
191
192             case '#':
193                 alternative = TRUE;
194                 break;
195
196             case '0':
197                 padding_char = '0';
198                 break;
199             }
200         }
201
202         UNUSED_PARAMETER(alternative); // TODO
203
204         if (*ptr == '*') external_width = TRUE;
205         else width = strtol(ptr, (char**)&ptr, 10);
206
207         if (*ptr == '.')
208         {
209             ptr++;
210             if (*ptr == '*') external_precision = TRUE;
211             else precision = strtol(ptr, (char**)&ptr, 10);
212         }
213
214         char specifier = *ptr;
215
216         switch (specifier)
217         {
218         case '%':
219             if (count <= 1) goto done;
220             output[ret++] = '%';
221             count--;
222             break;
223
224         case 'c':
225             if (external_width) width = *va_arg(args, int*);
226             padding = width - 1;
227
228             if (!left_justify)
229             {
230                 for (i = 0; i < padding; i++)
231                 {
232                     if (count <= 1) goto done;
233                     output[ret++] = padding_char;
234                     count--;
235                 }
236             }
237
238             if (count <= 1) goto done;
239             output[ret++] = va_arg(args, int);
240             count--;
241
242             if (left_justify)
243             {
244                 for (i = 0; i < padding; i++)
245                 {
246                     if (count <= 1) goto done;
247                     output[ret++] = padding_char;
248                     count--;
249                 }
250             }
251
252             break;
253
254         case 'p':
255             padding_char = '0';
256             width = 8;
257             specifier = 'X';
258
259         case 'd':
260         case 'u':
261         case 'i':
262         case 'o':
263         case 'x':
264         case 'X':
265         {
266             char num_buffer[256];
267             int number = va_arg(args, int);
268             int number_base;
269
270             if (specifier == 'o') number_base = 8;
271             else if (tolower(specifier) == 'x') number_base = 16;
272             else number_base = 10;
273
274             if ((number > 0) && (sign != 0))
275             {
276                 itoa(number, &num_buffer[1], number_base);
277                 num_buffer[0] = sign;
278             }
279             else
280             {
281                 itoa(number, num_buffer, number_base);
282             }
283
284             if (external_width) width = *va_arg(args, int*);
285             if (external_precision) precision = *va_arg(args, int*);
286
287             if ((int)strlen(num_buffer) < precision)
288             {
289                 int leading_zeros = precision - strlen(num_buffer);
290                 memmove(&num_buffer[leading_zeros], &num_buffer[0], leading_zeros);
291                 for (i = 0; i < leading_zeros; i++) num_buffer[i] = '0';
292             }
293
294             padding = (int)width - (int)strlen(num_buffer);
295
296             if (!left_justify)
297             {
298                 for (i = 0; i < padding; i++)
299                 {
300                     if (count <= 1) goto done;
301                     output[ret++] = padding_char;
302                     count--;
303                 }
304             }
305
306             for (p = num_buffer; *p != '\0'; p++)
307             {
308                 if (count <= 1) goto done;
309                 output[ret++] = *p;
310                 count--;
311             }
312
313             if (left_justify && width)
314             {
315                 for (i = 0; i < padding; i++)
316                 {
317                     if (count <= 1) goto done;
318                     output[ret++] = padding_char;
319                     count--;
320                 }
321             }
322
323             break;
324         }
325
326         case 's':
327         {
328             char *string = va_arg(args, char*);
329             if (external_width) width = *va_arg(args, int*);
330             if (external_precision) precision = *va_arg(args, int*);
331             padding = (int)width - (int)strlen(string);
332
333             if (!left_justify)
334             {
335                 for (i = 0; i < padding; i++)
336                 {
337                     if (count <= 1) goto done;
338                     output[ret++] = padding_char;
339                     count--;
340                 }
341             }
342
343             for (i = 0, p = string; *p != '\0' && (precision < 0 || i < precision); p++, i++)
344             {
345                 if (count <= 1) goto done;
346                 output[ret++] = *p;
347                 count--;
348             }
349
350             if (left_justify)
351             {
352                 for (i = 0; i < padding; i++)
353                 {
354                     if (count <= 1) goto done;
355                     output[ret++] = padding_char;
356                     count--;
357                 }
358             }
359
360             break;
361         }
362
363         case 'f':
364         case 'F':
365         case 'e':
366         case 'E':
367         case 'g':
368         case 'G':
369             // TODO: print decimal
370             UNUSED_PARAMETER(precision);
371             UNUSED_PARAMETER(external_precision);
372             break;
373
374         case 'n':
375         {
376             int *printed_chars = va_arg(args, int*);
377             *printed_chars = ret;
378             break;
379         }
380         }
381     }
382
383 done:
384     output[ret] = '\0';
385     return ret;
386 }
387
388 int snprintf(char *output, dword_t count, const char *format, ...)
389 {
390     va_list args;
391     va_start(args, format);
392     int ret = vsnprintf(output, count, format, args);
393     va_end(args);
394     return ret;
395 }
396
397 int vsprintf(char *output, const char *format, va_list args)
398 {
399     return vsnprintf(output, (dword_t)-1, format, args);
400 }
401
402 int vprintf(const char *format, va_list args)
403 {
404     char buffer[256];
405     int ret = vsnprintf(buffer, 256, format, args);
406
407     char *ptr = &buffer[0];
408     while (*ptr) putchar(*(ptr++));
409     return ret;
410 }
411
412 int sprintf(char *output, const char *format, ...)
413 {
414     va_list args;
415     va_start(args, format);
416     int ret = vsprintf(output, format, args);
417     va_end(args);
418     return ret;
419 }
420
421 int printf(const char *format, ...)
422 {
423     va_list args;
424     va_start(args, format);
425     int ret = vprintf(format, args);
426     va_end(args);
427     return ret;
428 }
429
430 int abs(int x)
431 {
432     return (x < 0) ? -x : x;
433 }