|
1 | 1 | /** |
2 | | - * @file printf.h |
3 | | - * @author Craig Edwards (craigedwards@brainbox.cc) |
4 | | - * @copyright Copyright (c) 2012-2025 |
| 2 | + * @file printf.c |
| 3 | + * @author Craig Edwards |
| 4 | + * @copyright 2012–2025 |
| 5 | + * |
| 6 | + * @brief Minimal printf implementation for Retro Rocket |
| 7 | + * |
| 8 | + * @section provenance Provenance |
| 9 | + * This implementation originated from a public-domain printf clone |
| 10 | + * commonly circulated in early OSDev / hobbyist C runtimes. The original |
| 11 | + * code dates back to the 1990s, likely adapted from 16‑bit DOS/Minix |
| 12 | + * environments where: |
| 13 | + * - `PR_16` meant "use 16‑bit short", |
| 14 | + * - `PR_32` meant "use 32‑bit long", |
| 15 | + * - `PR_FP` supported "far pointers", |
| 16 | + * and short identifiers were required because some compilers only |
| 17 | + * guaranteed 8‑character identifier uniqueness. |
| 18 | + * |
| 19 | + * This version has since been heavily modernised: |
| 20 | + * - All 16‑bit and far pointer cruft removed. |
| 21 | + * - Flags renamed for clarity (`PR_INT32`, `PR_INT64`, `PR_WAS_NEG`). |
| 22 | + * - Goto-based control flow replaced with structured `switch`/`if`. |
| 23 | + * - Doxygen-style documentation added. |
| 24 | + * - Confirmed fit for 32‑/64‑bit protected mode systems. |
| 25 | + * |
| 26 | + * While the skeleton came from public domain code, the current |
| 27 | + * implementation is effectively a clean-room rework and is considered |
| 28 | + * part of the Retro Rocket codebase. |
| 29 | + * |
| 30 | + * @section limitations Limitations |
| 31 | + * - Floating point formats (%f, %e, %g) are not implemented. |
| 32 | + * - Width/precision parsing is minimal. |
| 33 | + * - Thread safety relies on external spinlocks around vprintf/dprintf. |
| 34 | + * |
| 35 | + * @section intent Intent |
| 36 | + * The purpose of this implementation is to provide a lightweight, |
| 37 | + * dependency-free printf family for kernel and low-level userland code, |
| 38 | + * suitable for use in kernel code where full libc is not available. |
5 | 39 | */ |
| 40 | + |
6 | 41 | #pragma once |
7 | 42 |
|
8 | 43 | #include "kernel.h" |
9 | 44 |
|
| 45 | +/** |
| 46 | + * @brief Variadic argument support |
| 47 | + * GCC/Clang builtins are used for variadic argument handling, |
| 48 | + * ensuring this remains freestanding without requiring stdarg.h. |
| 49 | + */ |
10 | 50 | typedef __builtin_va_list va_list; |
11 | 51 | #define va_start(ap, X) __builtin_va_start(ap, X) |
12 | 52 | #define va_arg(ap, type) __builtin_va_arg(ap, type) |
13 | 53 | #define va_end(ap) __builtin_va_end(ap) |
14 | 54 |
|
15 | | -/* flags used in processing format string */ |
16 | | -#define PR_LJ 0x001 /* left justify */ |
17 | | -#define PR_CA 0x002 /* use A-F instead of a-f for hex */ |
18 | | -#define PR_SG 0x004 /* signed numeric conversion (%d vs. %u) */ |
19 | | -#define PR_32 0x008 /* long (32-bit) numeric conversion */ |
20 | | -#define PR_16 0x010 /* short (16-bit) numeric conversion */ |
21 | | -#define PR_WS 0x020 /* PR_SG set and num was < 0 */ |
22 | | -#define PR_LZ 0x040 /* pad left with '0' instead of ' ' */ |
23 | | -#define PR_FP 0x080 /* pointers are far */ |
24 | | -#define PR_64 0x100 /* long (32-bit) numeric conversion */ |
25 | | -/* largest number handled is 2^64-1, lowest radix handled is 8. |
26 | | -2^64-1 in base 8 has 21 digits (add 5 for trailing NUL and for slop) */ |
27 | | -#define PR_BUFLEN 26 |
| 55 | +/** |
| 56 | + * @brief Format Flags |
| 57 | + * |
| 58 | + * These flags are set during format string parsing and affect the |
| 59 | + * behaviour of string/number emission. They may be combined. |
| 60 | + */ |
| 61 | +#define PR_LEFT_JUSTIFY 0x001 /**< Left justify within field width (%-d). */ |
| 62 | +#define PR_UPPER_HEX 0x002 /**< Use A–F instead of a–f for hex digits (%X). */ |
| 63 | +#define PR_SIGNED 0x004 /**< Signed numeric conversion (%d vs. %u). */ |
| 64 | +#define PR_INT64 0x008 /**< Treat numeric as 32-bit long. */ |
| 65 | +#define PR_INT32 0x010 /**< Treat numeric as 16-bit short. */ |
| 66 | +#define PR_WAS_NEG 0x020 /**< Write sign ('-') because PR_SG was set and number < 0. */ |
| 67 | +#define PR_LEFT_ZEROES 0x040 /**< Pad left with zeroes instead of spaces. */ |
28 | 68 |
|
29 | | -typedef int (*fnptr_t)(unsigned c, void **helper, const void* end); |
| 69 | +/** |
| 70 | + * @brief Maximum temporary buffer length for number-to-string conversion. |
| 71 | + * |
| 72 | + * Largest number handled is 2^64–1. In base 8, this is 21 digits. |
| 73 | + * Add 1 for sign, 1 for NUL terminator, and a few for slop. |
| 74 | + */ |
| 75 | +#define PR_BUFLEN 26 |
30 | 76 |
|
31 | | -int vsprintf(char *buf, const char *fmt, va_list args); |
| 77 | +/** |
| 78 | + * @brief Output Function Type |
| 79 | + * |
| 80 | + * Each printf variant passes characters to a callback function |
| 81 | + * of this type. This decouples formatting from the output device. |
| 82 | + * |
| 83 | + * @param c Character to output |
| 84 | + * @param helper Opaque pointer (e.g. buffer write position) |
| 85 | + * @param end End-of-buffer guard (if bounded) |
| 86 | + * @return 0 on success, nonzero if buffer/device is full |
| 87 | + */ |
| 88 | +typedef int (*fnptr_t)(unsigned c, void **helper, const void* end); |
32 | 89 |
|
| 90 | +/** |
| 91 | + * @brief Write formatted output to a string buffer with max size. |
| 92 | + * @param buf Output buffer |
| 93 | + * @param max Maximum number of characters (including NUL terminator) |
| 94 | + * @param fmt Format string |
| 95 | + * @param args Variadic argument list |
| 96 | + * @return Number of characters written (excluding NUL terminator) |
| 97 | + */ |
33 | 98 | int vsnprintf(char *buf, size_t max, const char *fmt, va_list args); |
34 | 99 |
|
35 | | -int sprintf(char *buf, const char *fmt, ...); |
36 | | - |
| 100 | +/** |
| 101 | + * @brief Write formatted output to a string buffer with max size. |
| 102 | + * @param buf Output buffer |
| 103 | + * @param max Maximum number of characters (including NUL terminator) |
| 104 | + * @param fmt Format string |
| 105 | + * @param ... Format arguments |
| 106 | + * @return Number of characters written (excluding NUL terminator) |
| 107 | + */ |
37 | 108 | int snprintf(char *buf, size_t max, const char *fmt, ...); |
38 | 109 |
|
| 110 | +/** |
| 111 | + * @brief Write formatted output to the active console. |
| 112 | + * Thread-safe via spinlocks. |
| 113 | + * @param fmt Format string |
| 114 | + * @param args Variadic argument list |
| 115 | + * @return Number of characters written |
| 116 | + */ |
39 | 117 | int vprintf(const char *fmt, va_list args); |
40 | 118 |
|
| 119 | +/** |
| 120 | + * @brief Write formatted output to the active console. |
| 121 | + * Thread-safe via spinlocks. |
| 122 | + * @param fmt Format string |
| 123 | + * @param ... Format arguments |
| 124 | + * @return Number of characters written |
| 125 | + */ |
41 | 126 | int printf(const char *fmt, ...); |
42 | 127 |
|
43 | 128 | /** |
44 | | - * @brief Debug only to int 0xE9 |
45 | | - * |
46 | | - * @param fmt format specifier |
47 | | - * @param ... format args |
48 | | - * @return int number of printed characters |
| 129 | + * @brief Debug output only (I/O port 0xE9). |
| 130 | + * |
| 131 | + * Characters are written to the Bochs/QEMU debug console, |
| 132 | + * prefixed with a timestamp (kernel tick count). |
| 133 | + * |
| 134 | + * @param fmt Format string |
| 135 | + * @param ... Format arguments |
| 136 | + * @return Number of characters written |
49 | 137 | */ |
50 | 138 | int dprintf(const char *fmt, ...); |
0 commit comments