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