cx86i386

Custom print function has undesired behaviour


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:

enter image description here

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

Output: Undesired spacing


Solution

  • 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);
    }