I'm making my own x86 OS using the i386-elf cross-compiler and linker and nasm to compile asm files. The OS itself runs with qemu. That being said, I made a custom print function but ran into a problem. Every time I access memory (either through the [] operator or by dereferencing a pointer) and call my print function afterwards, it leaves 8 blank spaces and then prints normally. Print code:
void printv(char *str, ...)
{
unsigned int tmp_cursor = get_cursor_position();
cursor_position.x = (unsigned short)(tmp_cursor >> 16);
cursor_position.y = (unsigned short)tmp_cursor;
char buffer[12];
va_list list_ptr;
va_start(list_ptr, str);
unsigned int i = 0;
for (char *ptr = str; *ptr != '\0'; ptr++)
{
switch (*ptr)
{
case '%':
cursor_position.y += (cursor_position.x + i) / 80;
cursor_position.x = (cursor_position.x + i) % 80;
update_cursor(cursor_position.x, cursor_position.y);
i = 0;
switch (*(ptr + 1))
{
case 'c':
buffer[0] = (char)va_arg(list_ptr, int);
buffer[1] = '\0';
printv(buffer);
ptr++;
break;
case 's':
printv(va_arg(list_ptr, char *));
ptr++;
break;
case 'i':
case 'd':
int_to_str(va_arg(list_ptr, int), buffer, 10);
printv(buffer);
ptr++;
break;
default:
*(char*)(0xb8000 + (cursor_position.x + i + cursor_position.y * 80) * 2) = *ptr;
i++;
break;
}
break;
case '\n':
i = 0;
cursor_position.x = 0;
cursor_position.y++;
break;
case '\t':
cursor_position.y += (cursor_position.x + i) / 80;
cursor_position.x = (cursor_position.x + i) % 80;
update_cursor(cursor_position.x, cursor_position.y);
i = 0;
cursor_position.x += TAB_SPACE - cursor_position.x % TAB_SPACE - 1;
break;
default:
*(char *)(0xb8000 +(cursor_position.x + i + cursor_position.y * 80) * 2) = *ptr;
i++;
break;
}
}
va_end(list_ptr);
memset(buffer, '\0', 12);
cursor_position.y += (cursor_position.x + i) / 80;
cursor_position.x = (cursor_position.x + i) % 80;
update_cursor(cursor_position.x, cursor_position.y);
}
Call example:
printv("Starting PonchOS!\n");
char str[12];
for (int i = 0; i < 11; i++)
{
str[i] = 'a' + i;
}
str[11] = '\0';
printv("Testtesttesttesttest");
Output:
As you can see, it prints fine before any memory access, but after that, it leaves those white spaces. Any ideas as to why this happens?
Edit:
Implementing @chqrlie 's changes, some issues have been fixed, although spacing problems persist.
Code:
printv("Starting PonchOS!\n");
printv("%c\n", 'C');
printv("%i", 128);
printv("%s", "string");
The problem comes from your not updating the cursor variables consistently when calling printv
recursively. Furthermore you would get undefined behavior for this call: printv("%s", "%s")
.
You should split the function into a high level one that handles the formatting and a low level one that draws a string to the screen.
Here is a modified version:
void putstr(const char *str, size_t n) {
if (n > 0) {
unsigned int tmp_cursor = get_cursor_position();
int x = (unsigned short)(tmp_cursor >> 16);
int y = (unsigned short)tmp_cursor;
size_t i;
for (i = 0; i < n; i++) {
switch (str[i]) {
case '\n':
y += x / 80 + 1;
x = 0;
break;
case '\r':
y += x / 80;
x = 0;
break;
case '\t':
x = (x + TAB_SPACE) / TAB_SPACE * TAB_SPACE;
y += x / 80;
x %= 80;
break;
default:
*(char *)(0xb8000 + (y * 80 + x) * 2) = str[i];
x++;
break;
}
}
update_cursor(x, y);
}
}
void printv(const char *str, ...) {
char buffer[32];
char *p;
const char *ptr;
va_list list_ptr;
va_start(list_ptr, str);
for (ptr = str; *ptr != '\0'; ptr++) {
if (*ptr == '%' && ptr[1] != '\0') {
putstr(str, ptr - str);
str = ptr;
ptr++;
switch (*ptr) {
case 'c':
buffer[0] = (char)va_arg(list_ptr, int);
putstr(buffer, 1);
str += 2; // skip the format
break;
case 's':
p = va_arg(list_ptr, char *);
putstr(p, strlen(p));
str += 2; // skip the format
break;
case 'i':
case 'd':
int_to_str(va_arg(list_ptr, int), buffer, 10);
putstr(buffer, strlen(buffer));
str += 2; // skip the format
break;
case '%':
str += 1; // skip the initial %
break;
}
}
}
putstr(str, ptr - str);
va_end(list_ptr);
}