最近我一直在尝试制作一个有趣的操作系统,我从引导加载程序开始。首先,我想首先说我昨天问了另一个关于相同副作用(程序未运行)的问题,但结果证明原因可能与我最初想到的不同。所以,这是引导加载程序代码:
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
答案 0 :(得分:4)
您正在将 bootloader.asm
与 nasm -f elf
组合在一起,默认情况下会使其组合成 32 位代码。因此,当在 16 位实模式下运行时,您会得到无法正确执行操作的机器代码。
您可以通过将 bits 16
放在 bootloader.asm
文件的顶部来解决这个问题。但是 ELF 目标文件格式一开始并不是为 16 位代码设计的,试图将它用于这么小的代码片段是相当荒谬的。相反,我建议只使用
nasm -f bin -o bootloader.bin bootloader.asm
因为 -f bin
默认使用 16 位模式。然后跳过 ld
和 objcopy
行,并使用 bootloader.bin
代替您之前所说的 bootloader.o
(这不是一个好的文件名,因为 .o
通常表示可重定位的目标文件,而不是二进制图像)。
你不会有调试信息,但对于像引导扇区这样短的代码来说,这无论如何都是不必要的。只需让您的调试器在您逐步执行指令时反汇编指令,并与您的源代码进行比较。
修复此问题和注释中提到的错误(覆盖 cx
中的轨道和扇区号,并且需要 jmp 0x5000:0000
)后,代码成功启动、加载和运行扇区。它在屏幕上显示 I
并停止,就像您告诉它的那样。