自定义打印功能有不良行为

Custom print function has undesired behaviour

我正在制作我自己的 x86 OS 使用 i386-elf 交叉编译器和链接器 nasm 来编译 asm 文件。 OS 本身与 qemu 一起运行。话虽如此,我制作了自定义打印功能,但 运行 出现了问题。每次我访问内存(通过 [] 运算符或通过取消引用指针)并随后调用我的打印函数时,它都会留下 8 个空格,然后正常打印。 打印代码:

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 != '[=10=]'; 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] = '[=10=]';
                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, '[=10=]', 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);
}

调用示例:

    printv("Starting PonchOS!\n");
    char str[12];
    for (int i = 0; i < 11; i++)
    {
        str[i] = 'a' + i;
    }
    str[11] = '[=11=]';
    
    printv("Testtesttesttesttest");

输出:

如您所见,它在任何内存访问之前打印良好,但在那之后,它会留下那些空白。关于为什么会发生这种情况的任何想法?

编辑:
实施@chqrlie 的更改,一些问题已得到修复,但间距问题仍然存在。
代码:

printv("Starting PonchOS!\n");
printv("%c\n", 'C');
printv("%i", 128);
printv("%s", "string");

输出:

问题是由于您在递归调用 printv 时没有一致地更新游标变量。此外,此调用会导致未定义的行为:printv("%s", "%s").

您应该将函数拆分为处理格式设置的高级函数和将字符串绘制到屏幕的低级函数。

这是修改后的版本:

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 != '[=10=]'; ptr++) {
        if (*ptr == '%' && ptr[1] != '[=10=]') {
            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);
}