I have just recently gone into Protected Mode when developing an OS from scratch. I have managed to get into C and make functions to print characters to the screen (thanks Michael Petch for helping me reach this stage). Anyway, whenever I try to make a routine that loops through a string literal and prints every character in it, well, there's a bit of a problem. QEMU just goes into a boot loop, restarts again and again, and I am never able to see my beautiful green-on-black video mode. If I move this out of a routine and print it character-by-character in the kmain()
function (that part of which I have removed), everything works fine and dandy. Here's the file where I try to implement a string printing function:
vga.c -
#include <vga.h>
size_t terminal_row;
size_t terminal_column;
uint8_t terminal_color;
uint16_t *terminal_buffer;
volatile uint16_t * const VIDMEM = (volatile uint16_t *) 0xB8000;
size_t strlen(const char *s)
{
size_t len = 0;
while(s[len]) {
len++;
}
return len;
}
void terminal_init(void)
{
terminal_row = 0;
terminal_column = 0;
terminal_color = vga_entry_color(LGREEN, BLACK);
for(size_t y = 0; y < VGA_HEIGHT; y++) {
for(size_t x = 0; x < VGA_WIDTH; x++) {
const size_t index = y * VGA_WIDTH + x;
VIDMEM[index] = vga_entry(' ', terminal_color);
}
}
}
void terminal_putentryat(char c, uint8_t color, size_t x, size_t y)
{
const size_t index = y * VGA_WIDTH + x;
VIDMEM[index] = vga_entry(c, color);
}
void terminal_putchar(char c)
{
terminal_putentryat(c, terminal_color, terminal_column, terminal_row);
if(++terminal_column == VGA_WIDTH) {
terminal_column = 0;
if(++terminal_row == VGA_HEIGHT) {
terminal_row = 0;
}
}
}
void terminal_puts(const char *s)
{
size_t n = strlen(s);
for (size_t i=0; i < n; i++) {
terminal_putchar(s[i]);
}
}
I read my kernel into memory with this bootloader code:
extern kernel_start ; External label for start of kernel
global boot_start ; Make this global to suppress linker warning
bits 16
boot_start:
xor ax, ax ; Set DS to 0. xor register to itselfzeroes register
mov ds, ax
mov ss, ax ; Stack just below bootloader SS:SP=0x0000:0x7c00
mov sp, 0x7c00
mov ah, 0x00
mov al, 0x03
int 0x10
load_kernel:
mov ah, 0x02 ; call function 0x02 of int 13h (read sectors)
mov al, 0x01 ; read one sector (512 bytes)
mov ch, 0x00 ; track 0
mov cl, 0x02 ; sector 2
mov dh, 0x00 ; head 0
; mov dl, 0x00 ; drive 0, floppy 1. Comment out DL passed to bootloader
xor bx, bx ; segment 0x0000
mov es, bx ; segments must be loaded from non immediate data
mov bx, 0x7E00 ; load the kernel right after the bootloader in memory
.readsector:
int 13h ; call int 13h
jc .readsector ; error? try again
jmp 0x0000:kernel_start ; jump to the kernel at 0x0000:0x7e00
I have an assembly stub at the start of my kernel that enters protected mode, zeroes the BSS section, issues a CLD and calls into my C code:
; These symbols are defined by the linker. We use them to zero BSS section
extern __bss_start
extern __bss_sizel
; Export kernel entry point
global kernel_start
; This is the C entry point defined in kmain.c
extern kmain ; kmain is C entry point
bits 16
section .text
kernel_start:
cli
in al, 0x92
or al, 2
out 0x92, al
lgdt[toc]
mov eax, cr0
or eax, 1
mov cr0, eax
jmp 0x08:start32 ; The FAR JMP is simplified since our segment is 0
section .rodata
gdt32:
dd 0
dd 0
dw 0x0FFFF
dw 0
db 0
db 0x9A
db 0xCF
db 0
dw 0x0FFFF
dw 0
db 0
db 0x92
db 0xCF
db 0
gdt_end:
toc:
dw gdt_end - gdt32 - 1
dd gdt32 ; The GDT base is simplified since our segment is now 0
bits 32
section .text
start32:
mov ax, 0x10
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ss, ax
mov esp, 0x9c000 ; Set the stack to grow down from area under BDA/Video memory
; We need to zero out the BSS section. We'll do it a DWORD at a time
cld
lea edi, [__bss_start] ; Start address of BSS
lea ecx, [__bss_sizel] ; Lenght of BSS in DWORDS
xor eax, eax ; Set to 0x00000000
rep stosd ; Do clear using string store instruction
call kmain
I have a specialized linker script that places the bootloader at 0x7c00 and the kernel at 0x7e00.
What's the problem and how can I fix it? I've made my git repo available if more information is needed.