Search code examples
assemblyx86-16bootloaderosdev

BIOS INT 10, AH=0E odd behavior in second stage


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...

☺𝛿↕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:

link

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:

link)

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!)


Solution

  • 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.