I'm making an OS that runs snake, and I'm in 32 bit protected mode, But I can't draw a pixel to the screen. I switch to Mode 0x13 (320x200x256) in real mode and the screen blanks. After entering protected mode the kernel runs and the pixel I am plotting doesn't appear.
I am drawing inspiration from this OSDev Article that switches to protected mode and plots pixels to the video display.
Here's kernel.c
#include "display.h" // Include Display Drivers
#include "constants.h" // Include constants that C doesn't have included
#include "keyboard.h" // Include the custom keyboard driver
void _start(){} // Remove LD Warning
DisplayDetails globalDisplayDetails; // The display details.
int main(){
// Init the display details
DisplayDetails details = display_init();
globalDisplayDetails = details;
bool running = true;
while(running){
// This is the OS loop
putpixel(100, 100, 0x00FFFFFF);
}
// return 0;
}
And Here's display.h
(The actual display driver)
#define VRAM 0xA0000
typedef struct DisplayModeDetails {
int width; // Width of one line of pixels
int height; // Height of display in pixels
int colors; // How many colors are supported
int pitch; // How many bytes of VRAM to skip when going 1 pixel down
int pixelWidth; // How many bytes of VRAM to skip when going 1 pixel right
} DisplayDetails;
struct DisplayModeDetails display_init(){
struct DisplayModeDetails details; // Setup display mode details
details.width = 640;
details.height = 480;
details.colors = 16;
details.pitch = 1;
details.pixelWidth = 1;
return details;
}
void putpixel(int pos_x, int pos_y, unsigned long VGA_COLOR)
{
unsigned char* location = (unsigned char*)0xA0000 + 320 * pos_y + pos_x;
*location = VGA_COLOR;
}
boot_sect.asm
:
[bits 16]
[org 0x7C00]
mov [BOOTDRIVE], dl
; call clear_screen ; Clear screen
call load_kernel
load_kernel:
mov bx, KERNELOFFSET
mov dh, 0x05
mov dl, [BOOTDRIVE]
; mov dl, 0x80
call clear_screen ; Clear Screen
call disk_load ; Load From Disk
mov ah, 0x00 ; Start setting video mode
mov al, 0x13 ; 320x200 256 color graphics
int 0x10
cli ; Disable Interrupts
lgdt [gdt_descriptor] ; GDT start address
mov eax, cr0
or eax, 1
mov cr0, eax ; Jump to Protected 32 bit mode
jmp CODESEG:start_protected_mode
jmp $
clear_screen:
pusha
mov ah, 0x07 ; Scroll al lines; 0 = all
mov bh, 0x0f ; white on black
mov cx, 0x00 ; row=0, col=0
mov dx, 0x184f ; row = 24, col = 79
int 0x10 ; Call interrupt
mov ah, 0x02
mov dh, 0x00
mov dl, 0x00
mov bh, 0x00
int 0x10
popa
ret
disk_load:
pusha
push dx
mov ah, 0x02 ; read mode
mov al, dh ; read dh number of sects
mov cl, 0x02 ; read from sect 2 (1 = boot)
mov ch, 0x00 ; cylinder 0
mov dh, 0x00 ; head 0
int 0x13
jc disk_error
pop dx
cmp al, dh
jne sectors_error
popa
ret
disk_error:
mov ah, '1' ; Error Code
jmp err_loop
sectors_error:
mov ah, '2' ; Error Code
jmp err_loop
err_loop:
call clear_screen
mov dh, ah ; Print Error Message
mov ah, 0x0e
mov al, 'E'
int 0x10
mov al, 'r'
int 0x10
int 0x10
mov al, ' '
int 0x10
mov al, dh ; Print Error Code
int 0x10
jmp $ ; create infinite loop
; Constants
KERNELOFFSET equ 0x1000
CODESEG equ gdt_code - gdt_start
DATASEG equ gdt_data - gdt_start
gdt_start:
dq 0x0
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
dd gdt_start
[bits 32]
start_protected_mode:
; Load the kernel
mov ax, DATASEG
mov dx, ax
mov ss, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ebp, 0x9000
mov esp, ebp
call KERNELOFFSET
jmp $
BOOTDRIVE db 0
; Marking as bootable.
times 510-($-$$) db 0
dw 0xaa55
kernel_entry.asm
:
[bits 32]
[extern main]
call main
jmp $
I build and test with this script:
gcc -m32 -fno-pie -ffreestanding -g -c kernel.c -o obj/main.o
nasm -f elf kernel_entry.asm -o obj/entry.o
nasm -f bin boot_sect.asm -o obj/boot.bin
ld -m elf_i386 -o obj/kernel.bin -Ttext 0x1000 obj/main.o obj/entry.o --oformat binary
cat obj/boot.bin obj/kernel.bin > bin/os.bin
qemu-system-x86_64 -drive format=raw,file=bin/os.bin -display sdl