【问题标题】:BIOS INT 10, AH=0E odd behavior in second stageBIOS INT 10, AH=0E 第二阶段的奇怪行为
【发布时间】:2018-02-25 15:38:12
【问题描述】:

我已经开发内核大约一年了,只是使用 GRUB 作为引导加载程序。但是,现在我想开始为我的内核开发引导加载程序。

尽管我很努力,但我似乎无法解决加载程序第二阶段的一个奇怪问题。

生成文件:

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

我希望这只会打印 Loading... 并停止系统。但是,它会打印:☺????↕Loading... 为了解决这个问题,我将loading_message 的声明移到print_string 之前。令我惊讶的是,它产生了不同的输出:link

我最初的想法是该字符串以某种方式作为代码运行,但这是不可能的,因为我正在跳过它。但是,我在字符串中添加了换行符,并且在 print_string (link) 之前声明时,它似乎切换了视频模式(?) 当在代码 sn-p 中的位置声明时,它什么也不输出。所以它必须以某种方式将其作为代码执行?

关于为什么会发生这种情况的任何想法?

(如果您想查看任何其他文件或反汇编,请随时询问!)

【问题讨论】:

  • 这通常意味着ds段寄存器值和org不匹配。如果您的数据正在执行,则意味着您错误地跳到了第 2 阶段。在这两种情况下,请使用调试器。
  • 提供包括第一阶段在内的所有代码以及如何构建磁盘映像会很有帮助。
  • @MichaelPetch 我有一个第一阶段,它使用 BIOS 将第二阶段从磁盘加载到地址 0x0000:0x0500 编辑:我将上传第一阶段;没有看到你的评论。
  • @MichaelPetch 我使用 QEMU
  • @JonathonReinhart :我不建议将 QEMU/GDB 用于实模式调试,尤其是在调试任何带有软件中断的东西时。 QEMU 在该领域严重缺乏支持,因为它不理解 20 位段:偏移寻址。使用 0x0000 以外的段会完全混淆 GDB。我建议简单地使用 Bochs,它实际上有一个可以理解实模式并对其提供适当调试支持的调试器。如果您了解 QEMU/GDB 的缺陷,这是可能的(我写了一个关于该主题的长度 SO 答案)

标签: assembly x86-16 bootloader osdev sanity-check


【解决方案1】:

您的错误是stage1_address dw 0x500 将其声明为内存中的数据字,但您随后继续将其用作符号。将其更改为 stage1_address equ 0x500

学习使用反汇编器和调试器。

【讨论】:

  • 感谢您的帮助,非常感谢!
猜你喜欢
  • 2016-03-09
  • 2012-01-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-02-10
  • 1970-01-01
  • 2016-10-30
  • 2021-06-05
相关资源
最近更新 更多