I've been developing my kernel for about a year now, just using GRUB as the bootloader. However, now I want to get my feet wet in developing a bootloader for my kernel.
Despite my efforts, I can't seem to fix a bizarre issue with the second stage of the loader.
Makefile:
BOOTLOADER_FOLDER := boot
KERNEL_FOLDER := kernel
BOOTLOADER_SOURCES := $(shell find $(BOOTLOADER_FOLDER) -type f -iname "*.asm")
BOOTLOADER_OBJECTS := $(subst .asm,.bin,$(BOOTLOADER_SOURCES))
BOOTLOADER_IMAGE := $(BOOTLOADER_FOLDER)/boot.img
KERNEL_SOURCES := $(shell find . -type f -iname "*.c")
KERNEL_OBJECTS := $(foreach x,$(basename $(C_SOURCES)),$(x).o)
OS_IMAGE := os.img
ALLFILES := $(BOOTLOADER_SOURCES) $(KERNEL_SOURCES)
NASM := nasm
NASM_FLAGS := -f bin
DD := dd
DD_FLAGS := bs=512
GCC := GCC
GCC_FLAGS := -g -std=gnu99 -Wall -Wextra -pedantic -Wshadow -Wpointer-arith \
-Wcast-align -Wwrite-strings -Wmissing-prototypes -Wmissing-declarations \
-Wredundant-decls -Wnested-externs -Winline -Wno-long-long \
-Wconversion -Wstrict-prototypes
QEMU := qemu-system-i386
QEMU_FLAGS := -fda
all: clean compile todo run
clean:
-rm -r $(BOOTLOADER_OBJECTS) $(KERNEL_OBJECTS) $(BOOTLOADER_IMAGE) $(OS_IMAGE)
compile: $(BOOTLOADER_OBJECTS) $(KERNEL_OBJECTS)
%.bin:%.asm
@$(NASM) $(NASM_FLAGS) $< -o $@
%.o:%.c
@$(GCC) $(GCC_FLAGS) -o $@ $<
$(BOOTLOADER_IMAGE):$(BOOTLOADER_OBJECTS)
@$(foreach file,$(BOOTLOADER_OBJECTS),dd bs=512 if=$(file) >> $(BOOTLOADER_IMAGE);)
$(KERNEL_IMAGE):$(KERNEL_OBJECTS)
$(OS_IMAGE):$(BOOTLOADER_IMAGE) $(KERNEL_IMAGE)
@$(DD) $(DD_FLAGS) if=$(BOOTLOADER_IMAGE) >> $(OS_IMAGE)
run:$(OS_IMAGE)
@DISPLAY=:0 \
$(QEMU) $(QEMU_FLAGS) $(OS_IMAGE);
todo:
-@for file in $(ALLFILES:Makefile=); do fgrep -H -e TODO -e FIXME $$file; done; true
stage1.asm:
[BITS 16]
[ORG 0x7C00]
jmp 0x0:main
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
loading_message db "Loading bootloader...", 0xD, 0x0A, 0x0
stage1_address dw 0x500
reset_disk:
mov ah, 0x0
mov dl, 0x0
int 0x13
jc reset_disk
jmp .done
.done:
ret
read_disk:
mov ah, 0x02
int 0x13
cmp ah, 0x0
jmp .done
cmp ah, 0x80
jmp .try_again
.try_again:
pusha
call reset_disk
popa
jmp read_disk
.done:
ret
main:
cli
xor ax, ax
mov ds, ax
mov es, ax
mov fs, ax
mov gs, ax
mov ax, 0x0000
mov ss, ax
mov sp, 0xFFFF
sti
mov si, loading_message
call print_string
call reset_disk
mov al, 0x1
mov ch, 0x0
mov cl, 0x2
mov dh, 0x0
mov dl, 0x0
mov bx, stage1_address
call read_disk
jmp 0x000:stage1_address
cli
hlt
TIMES 510-($-$$) db 0
db 0x55
db 0xAA
stage2.asm:
[BITS 16]
[ORG 0x500]
jmp main
print_string:
lodsb
or al, al
jz .done
mov ah, 0x0E
int 0x10
jmp print_string
.done:
ret
loading_message db "Loading...", 0x0
main:
mov si, loading_message
call print_string
cli
hlt
I expected this to just print Loading...
and halt the system. However, it prints this:
`☺𝛿↕Loading...
In my efforts to fix this, I moved the declaration of loading_message
to before the `print_string```. To my surprise, it yields a different output:
My initial thought was that the string was somehow being run as code, but that isn't possible since I'm jumping over it. However, I added the newline delimiters to the string, and it seems to switch video modes(?) when declared before print_string
:
)
When declared in the position in the code snippet, it outputs nothing. So it must be executing it as code somehow?
Any ideas as to why this is happening?
(If you want to see any other file or disassemblies, feel free to ask!)
Your mistake is stage1_address dw 0x500
which declares it as a data word in memory but you then proceed to use it as a symbol. Change that to stage1_address equ 0x500
.
Learn to use a disassembler and a debugger.