Search code examples
c++assemblyx86bootloadervga

Why won't the pixels I try to draw into VGA memory show up?


I'm working on a little operating system, and I'm running into issues drawing pixels. It seems that no matter what I do, I am unable to get anything to appear. I'm trying to follow along with an article by OSDev Wiki, but so far all that works for me is text outputting.

Here's my assembly bootloader code:

[org 0x7c00]                        
KERNEL_LOCATION equ 0x1000
                                    

mov [BOOT_DISK], dl                 

                                    
xor ax, ax                          
mov es, ax
mov ds, ax
mov bp, 0x8000
mov sp, bp

mov bx, KERNEL_LOCATION
mov dh, 2

mov ah, 0x02
mov al, dh 
mov ch, 0x00
mov dh, 0x00
mov cl, 0x02
mov dl, [BOOT_DISK]
int 0x13

                                    
mov ah, 0x0
mov al, 0x3
int 0x10                ; text mode


CODE_SEG equ GDT_code - GDT_start
DATA_SEG equ GDT_data - GDT_start

cli
lgdt [GDT_descriptor]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp CODE_SEG:start_protected_mode

jmp $
                                    
BOOT_DISK: db 0

GDT_start:
    GDT_null:
        dd 0x0
        dd 0x0

    GDT_code:
        dw 0xffff
        dw 0x0
        db 0x0
        db 0b10011010
        db 0b11001111
        db 0x0

    GDT_data:
        dw 0xffff
        dw 0x0
        db 0x0
        db 0b10010010
        db 0b11001111
        db 0x0

GDT_end:

GDT_descriptor:
    dw GDT_end - GDT_start - 1
    dd GDT_start


[bits 32]
start_protected_mode:
    mov ax, DATA_SEG
    mov ds, ax
    mov ss, ax
    mov es, ax
    mov fs, ax
    mov gs, ax
    
    mov ebp, 0x90000        ; 32 bit stack base pointer
    mov esp, ebp

    jmp KERNEL_LOCATION

                                     
 
times 510-($-$$) db 0              
dw 0xaa55

And here's my C++ kernel code:

void draw_pixel(int pos_x, int pos_y, unsigned char colour)
{
    unsigned char* location = (unsigned char*)0xA0000 + 320 * pos_y + pos_x;
    *location = colour;
}

void write_string( int colour, const char *string )
{
    volatile char *video = (volatile char*)0xB8000;
    while( *string != 0 )
    {
        *video++ = *string++;
        *video++ = colour;
    }
}

extern "C" void main(){
    for(int i = 0; i<10; i++)
    {
    draw_pixel(i,i,15);
    }
    write_string(15, "Hellop World");
    
    return;
}

I'm rather new to Assembly, but I assume the issue lies in some incompatibility between my ASM code and my C++ code.

For a bit more context, I'm trying to output to VGA 320x200 with 8 bit color palette.


Solution

  • The assembly code sets mode 0x03 (80x25 Text), you have even commented it as such.

    Nowhere does it appear that you are setting mode 0x13 (320x200 256 colour).

    Your code is writing to both the text and graphics frame buffer. Only the text framebuffer will be rendered in text mode. You cannot render both text and graphics simultaneously. To render text in a graphics mode, you will have to "draw" it.

    To set the VGA mode 13h:

    mov ah, 0x00
    mov al, 0x13
    int 0x10
    

    To "draw" text to the graphics framebuffer, you will need character bitmaps. The simplest solution to that is to use the font tables used for translating ASCII codes to glyphs in text mode. That is described at https://wiki.osdev.org/VGA_Fonts

    Note that the BIOS call to switch mode (or indeed any BIOS call) must be done before entering protected mode. BIOS calls, or more particularly software interrupts do not work in protected mode.