Skip to content

Commit acb28d4

Browse files
fix up printf functions at last, and remove sprintf
1 parent e6bd009 commit acb28d4

14 files changed

Lines changed: 305 additions & 268 deletions

File tree

include/acpi.h

Lines changed: 0 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -130,20 +130,6 @@ typedef struct pci_irq_route {
130130
*/
131131
void init_acpi();
132132

133-
/**
134-
* @brief Get total number of CPUs detected via ACPI.
135-
*
136-
* @return Number of CPUs detected.
137-
*/
138-
uint16_t get_cpu_count();
139-
140-
/**
141-
* @brief Retrieve the physical address of the Local APIC MMIO.
142-
*
143-
* @return 64-bit physical address of LAPIC base.
144-
*/
145-
uint64_t get_local_apic();
146-
147133
/**
148134
* @brief Retrieve the IOAPIC structure by index.
149135
*

include/devicename.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ typedef struct devname_prefix_t {
1212
uint8_t increment;
1313
} devname_prefix_t;
1414

15-
bool make_unique_device_name(const char* prefix, char* buffer);
15+
bool make_unique_device_name(const char* prefix, char* buffer, size_t buffer_len);
1616

1717
void init_devicenames();
1818

include/printf.h

Lines changed: 113 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,138 @@
11
/**
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.
539
*/
40+
641
#pragma once
742

843
#include "kernel.h"
944

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+
*/
1050
typedef __builtin_va_list va_list;
1151
#define va_start(ap, X) __builtin_va_start(ap, X)
1252
#define va_arg(ap, type) __builtin_va_arg(ap, type)
1353
#define va_end(ap) __builtin_va_end(ap)
1454

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. */
2868

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
3076

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);
3289

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+
*/
3398
int vsnprintf(char *buf, size_t max, const char *fmt, va_list args);
3499

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+
*/
37108
int snprintf(char *buf, size_t max, const char *fmt, ...);
38109

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+
*/
39117
int vprintf(const char *fmt, va_list args);
40118

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+
*/
41126
int printf(const char *fmt, ...);
42127

43128
/**
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
49137
*/
50138
int dprintf(const char *fmt, ...);

src/ahci.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ void probe_port(ahci_hba_mem_t *abar, pci_dev_t dev)
205205
sd->blockread = storage_device_ahci_block_read;
206206
sd->blockwrite = storage_device_ahci_block_write;
207207
sd->size = dt == AHCI_DEV_SATA ? ahci_read_size(&abar->ports[i], abar) : SIZE_MAX;
208-
make_unique_device_name(dt == AHCI_DEV_SATA ? "hd" : "cd", sd->name);
208+
make_unique_device_name(dt == AHCI_DEV_SATA ? "hd" : "cd", sd->name, sizeof(sd->name));
209209
sd->block_size = dt == AHCI_DEV_SATA ? HDD_SECTOR_SIZE : ATAPI_SECTOR_SIZE;
210210
register_storage_device(sd);
211211
}

src/basic/main.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -532,7 +532,7 @@ bool accept(int token, struct basic_ctx* ctx)
532532
if (token != tokenizer_token(ctx)) {
533533
char err[MAX_STRINGLEN];
534534
GENERATE_ENUM_STRING_NAMES(TOKEN, token_names)
535-
sprintf(err, "Expected %s got %s", token_names[token], token_names[tokenizer_token(ctx)]);
535+
snprintf(err, MAX_STRINGLEN, "Expected %s got %s", token_names[token], token_names[tokenizer_token(ctx)]);
536536
tokenizer_error_print(ctx, err);
537537
return false;
538538
}

src/basic/string.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,7 @@ char* basic_itoa(struct basic_ctx* ctx)
294294
PARAMS_GET_ITEM(BIP_INT);
295295
int64_t radix = intval;
296296
PARAMS_END("RADIX$", "");
297-
char buffer[128] = {0};
297+
char buffer[MAX_STRINGLEN] = {0};
298298
int err;
299299
if ((err = do_itoa(target, buffer, radix)) >= 0) {
300300
return gc_strdup(buffer);
@@ -304,7 +304,7 @@ char* basic_itoa(struct basic_ctx* ctx)
304304
tokenizer_error_print(ctx, "Invalid radix (not in range between 2 and 36)");
305305
return "";
306306
}
307-
sprintf(buffer, "Unknown `do_itoa` error: %d", -err);
307+
snprintf(buffer, MAX_STRINGLEN, "Unknown `do_itoa` error: %d", -err);
308308
tokenizer_error_print(ctx, buffer);
309309
return "";
310310
}

src/basic/variable.c

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -418,8 +418,8 @@ const char* basic_get_string_variable(const char* var, struct basic_ctx* ctx)
418418
}
419419
}
420420

421-
char err[1024];
422-
sprintf(err, "No such string variable '%s'", var);
421+
char err[MAX_STRINGLEN];
422+
snprintf(err, MAX_STRINGLEN, "No such string variable '%s'", var);
423423
tokenizer_error_print(ctx, err);
424424
return "";
425425
}
@@ -566,9 +566,9 @@ bool basic_get_double_variable(const char* var, struct basic_ctx* ctx, double* r
566566
}
567567

568568

569-
char err[1024];
569+
char err[MAX_STRINGLEN];
570570
if (var[strlen(var) - 1] == '#') {
571-
sprintf(err, "No such real variable '%s'", var);
571+
snprintf(err, MAX_STRINGLEN, "No such real variable '%s'", var);
572572
tokenizer_error_print(ctx, err);
573573
}
574574
*res = 0.0; /* No such variable */

src/devicename.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ uint64_t string_hash(const void *item, uint64_t seed0, uint64_t seed1) {
1414
return hashmap_sip(a->prefix, strlen(a->prefix), seed0, seed1);
1515
}
1616

17-
bool make_unique_device_name(const char* prefix, char* buffer)
17+
bool make_unique_device_name(const char* prefix, char* buffer, size_t buffer_len)
1818
{
1919
if (prefix == NULL || buffer == NULL) {
2020
return false;
@@ -31,7 +31,7 @@ bool make_unique_device_name(const char* prefix, char* buffer)
3131
hashmap_set(prefix_hash, &find);
3232
exists = &find;
3333
}
34-
sprintf(buffer, "%s%d", prefix, prefix_increment);
34+
snprintf(buffer, buffer_len, "%s%d", prefix, prefix_increment);
3535
dprintf("Registered new device name: %s\n", buffer);
3636
add_random_entropy((uint64_t)buffer); // NOTE: *address* used as entropy!
3737
hashmap_set(prefix_hash, exists);

src/e1000.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,7 @@ bool e1000_start(pci_dev_t *pci_device) {
376376
}
377377
net->opaque = NULL;
378378
net->deviceid = (INTEL_VEND << 16) | e1000_device_id;
379-
make_unique_device_name("net", net->name);
379+
make_unique_device_name("net", net->name, sizeof(net->name));
380380
net->description = "Intel e1000 Gigabit";
381381
net->flags = CONNECTED;
382382
net->mtu = 0;

src/keyboard.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,7 @@ void init_keyboard()
175175
char devname[16];
176176
buffer_write_ptr = 0;
177177
buffer_read_ptr = 0;
178-
make_unique_device_name("kb", devname);
178+
make_unique_device_name("kb", devname, sizeof(devname));
179179
register_interrupt_handler(IRQ1, keyboard_handler, dev_zero, NULL);
180180
proc_register_idle(keyboard_repeat_tick, IDLE_BACKGROUND);
181181
}

0 commit comments

Comments
 (0)