x86 程序集:int13h 似乎无法加载程序

时间:2021-03-21 14:30:52

标签: assembly x86-16 bootloader

最近我一直在尝试制作一个有趣的操作系统,我从引导加载程序开始。首先,我想首先说我昨天问了另一个关于相同副作用(程序未运行)的问题,但结果证明原因可能与我最初想到的不同。所以,这是引导加载程序代码:

start: jmp boot

boot:
    cli                               ; Disable interrupts
    cld                               ; Clear direction flags


    mov al, 2                         ; Read 2 sectors
    mov ch, 0                         ; Track 0
    mov cl, 2                         ; Read 2nd sector (1st sector is bootloader)
    mov dh, 0                         ; Head number
    mov dl, 0                         ; Drive number (0 = floppy drive)

    ; Specify memory address to read floppy to
    mov cx, 0x5000
    mov bx, cx
    mov es, bx
    xor bx, bx

    mov ah, 0x2                       ; INT 0x13 with AH=0x2 means read sector
    int 0x13                          ; Call BIOS to read sector
    jmp cx                            ; Jump to sector

  ; Must be 512 bytes
  times 510 - ($-$$) db 0
  dw 0xAA55                           ; Boot Signature

但是,示例程序我从来没有运行过。在 QEMU 上运行它,我以前认为问题是 jmp 转到了错误的地址,但现在在获得内存转储并进行更多调试后,我看到 jmp 发生并且它转到正确的地址,但地址 0x5000 全为零,并且绝对没有,附近也没有任何东西。这可能是 GDB/QEMU 不适合分段或实模式的问题吗?我读了一些关于那个的东西。或者我用错了 int 0x13?也许分割不是这样工作的?由于我使用寄存器的值作为地址开始存储数据,然后还使用它跳转到那里,所以它不应该是完全相同的地址,因此应该是正确的吗?我在互联网上找不到任何帮助解决此问题的内容,我完全感到困惑。请赐教。感谢所有帮助,谢谢!

构建磁盘映像所采取的步骤:

首先,我使用以下命令构建引导加载程序:

nasm -f elf bootloader.asm -F dwarf -g -o ../build/bootloader/bootloader.o
ld -m elf_i386 -T bootloader.lds ../build/bootloader/bootloader.o -o ../build/bootloader/bootloader.o.elf
objcopy -O binary  ../build/bootloader/bootloader.o.elf ../build/bootloader/bootloader.o

链接脚本:

OUTPUT(bootloader);

PHDRS
{
  headers PT_NULL;
  text PT_LOAD FILEHDR PHDRS ;
  data PT_LOAD ;
}


SECTIONS
{
  . = SIZEOF_HEADERS;
  .text 0x7c00:  {  *(.text)  } :text
  .data :  {  *(.data)  } :data
}

为了使用调试信息创建它,以便更轻松地将其与 GDB 一起使用。

然后,这是我使用的示例程序:

start: jmp MovCursor

MovCursor:
    cld
    mov ah, 0x2
    mov bh, 0
    mov dh, 12
    mov dl, 0
    int 0x10
    jmp PutChar

PutChar:
    mov ah, 0xA
    mov al, 0x49
    mov bh, 0
    mov cx, 1
    int 0x10
    hlt
    jmp Print

Print:
    mov si, msg          ; Load start address of the message into SI
    jmp printstring

printstring:
    xor ax, ax
    mov ds, ax
    lodsb                ; Load byte at DS into AL, increment SI
    or al, al            ; Check if AL is 0 (and set flags)
    jz exitloop          ; If zero jump to end
    mov ah, 0xE          ; INT 0x10/AH=0xE is teletype output
    int 0x10             ; Call BIOS to print
    jmp printstring      ; Repeat for next character

exitloop:
    hlt

msg db "Welcome to kOS!", 0ah, 0dh, 0h

使用 nasm -f bin os/io.asm -o os/io 编译为原始二进制文件。

最后,构建镜像:

dd if=/dev/zero of=disk.img bs=512 count=2880
dd if=build/bootloader/bootloader.o of=disk.img bs=512 seek=0
dd if=os/io of=disk.img bs=512 seek=1

QEMU 命令:qemu-system-i386 -machine q35 -fda disk.img -gdb tcp::26000 -S

1 个答案:

答案 0 :(得分:4)

您正在将 bootloader.asmnasm -f elf 组合在一起,默认情况下会使其组合成 32 位代码。因此,当在 16 位实模式下运行时,您会得到无法正确执行操作的机器代码。

您可以通过将 bits 16 放在 bootloader.asm 文件的顶部来解决这个问题。但是 ELF 目标文件格式一开始并不是为 16 位代码设计的,试图将它用于这么小的代码片段是相当荒谬的。相反,我建议只使用

构建引导扇区
nasm -f bin -o bootloader.bin bootloader.asm

因为 -f bin 默认使用 16 位模式。然后跳过 ldobjcopy 行,并使用 bootloader.bin 代替您之前所说的 bootloader.o(这不是一个好的文件名,因为 .o 通常表示可重定位的目标文件,而不是二进制图像)。

你不会有调试信息,但对于像引导扇区这样短的代码来说,这无论如何都是不必要的。只需让您的调试器在您逐步执行指令时反汇编指令,并与您的源代码进行比较。

修复此问题和注释中提到的错误(覆盖 cx 中的轨道和扇区号,并且需要 jmp 0x5000:0000)后,代码成功启动、加载和运行扇区。它在屏幕上显示 I 并停止,就像您告诉它的那样。