操作系统的引导程序无法工作

4
我是一个自定义操作系统的制作者。我有两个nasm文件:

boot.asm:

[BITS 16]   ;tell the assembler that its a 16 bit code
[ORG 0x7C00]    ;Origin, tell the assembler that where the code will
;be in memory after it is been loaded

INT 0x13

JMP $       ;infinite loop

TIMES 510 - ($ - $$) db 0   ;fill the rest of sector with 0
DW 0xAA55           ; add boot signature

start.asm:

[BITS 16]
MOV AL, 72
CALL PrintCharacter
MOV AL, 101
CALL PrintCharacter
MOV AL, 108
CALL PrintCharacter
MOV AL, 108
CALL PrintCharacter
MOV AL, 111
CALL PrintCharacter
MOV AL, 44
CALL PrintCharacter
MOV AL, 32
CALL PrintCharacter

MOV AL, 87
CALL PrintCharacter
MOV AL, 111
CALL PrintCharacter
MOV AL, 114
CALL PrintCharacter
MOV AL, 108
CALL PrintCharacter
MOV AL, 100
CALL PrintCharacter
MOV AL, 33
CALL PrintCharacter

PrintCharacter: 
MOV AH, 0x0E
MOV BH, 0x00
MOV BL, 0x07
INT 0x10
RET

TIMES 512 - ($ - $$) db 0

我使用以下命令将它们编译成 .bin 文件:

nasm boot.asm -f bin -o boot.bin
nasm start.asm -f bin -o start.bin

然后使用以下命令将它们添加到软盘映像中:

dd if=boot.bin bs=512 of=MyOS.img count=1
dd if=start.bin bs=512 of=MyOS.img count=2

当我在VirtualBox中从软盘映像启动时,它显示两个感叹号而不是一个,并且甚至在QEmu(Q.app)中也无法启动。我对操作系统开发很新,所以如果有人能告诉我我做错了什么,并给我一些指针来更好地设置我的操作系统,那就太好了。
2个回答

5
当然它会打印出两个感叹号。让我们看一下你的代码:
...
MOV AL, 33
CALL PrintCharacter    ;   |1
                       ;   |          ^     |4
PrintCharacter:        ;   v    |2    |     |
MOV AH, 0x0E           ;        |     |     |
MOV BH, 0x00           ;        |     |     |
MOV BL, 0x07           ;        |     |     |
INT 0x10               ;        |     |     |     5
RET                    ;        v     |3    v     ----> off to la-la land

注意:我添加了一些箭头来说明程序执行的过程。

前两行代码负责在输出 Hello, World 后打印最终的 !。这是通过调用你的 PrintCharacter 子过程实现的(箭头 12)。当 PrintCharacter 返回时(箭头 3),你的程序会直接继续执行(箭头 4)... 然后下一行代码恰好又是 PrintCharacter 的开头。由于寄存器 AL 仍然包含 33(即 ! 的 ANSI 代码),所以会再次打印一个感叹号。

然后,执行再次到达 RET,但这一次,由于你并没有实际上调用 PrintCharacter,所以没有定义的返回位置,因此它返回到...某个未定义的位置,很可能是操作系统停止继续启动过程的时刻(箭头 5)。

结论:在你的代码输出 Hello, World! 后,它应该做些其他的事情(至少应该 停止),否则当你遇到未定义的行为或挂起时,不要感到惊讶...


谢谢。我把无限循环移到了start.asm,现在它可以工作了。但是它仍然无法在QEmu中启动。 - None
我不了解QEMU。我只能猜测:也许你的第一条指令(INT 0x13h)是罪魁祸首。可能QEMU在启动时设置了寄存器,中断调用没有达到你想要的效果。尝试显式地设置寄存器(AH = 02,AL = 01,...)。或者QEMU没有仿真软盘驱动器,因此无法加载引导程序。--我必须承认我不太理解你在boot.asm中具体在做什么。难道你不应该将start.asm加载到内存中,然后跳转到它吗? - stakx - no longer contributing
当我将start.asm的主要内容放入boot.asm中时,它可以正常工作。 - None
以下是您需要做的事情:将代码放入boot.asm中,从而将包含在start.asm中的代码扇区加载到内存中(例如通过调用INT 13h函数)。将其加载到某个固定地址(在start.asm中使用ORG指定该地址)。然后,在加载该代码之后,进行远跳转。 - stakx - no longer contributing

1

我不知道VirtualBox如何启动您的代码,但我非常确定qemu不会,因为您正在错误地设置磁盘映像上的二进制文件。当您在磁盘映像上使用“dd”时,需要传递一个选项,以便它不会截断磁盘,类似于这样:

dd if=boot.bin of=MyOS.img bs=512 count=1 conv=notrunc status=noxfer
dd if=start.bin of=MyOS.img bs=512 count=1 conv=notrunc seek=1 status=noxfer

conv=notrunc告诉'dd'不要截断磁盘,也就是说,不要删除它并用你的二进制文件覆盖它,status=noxfer使其更少冗长,seek=1将start.bin写入磁盘的第二个扇区(从0开始)。

为了验证我的说法,尝试创建一个1MB的磁盘映像,并使用你之前使用的命令(即dd),你会发现磁盘映像被缩小为你的二进制文件的副本。

因此,在最后一个'dd'命令之后,你可以使用start.bin作为磁盘映像来调用qemu(MyOS.img成为它的副本),由于你没有在start.bin末尾使用引导标志,所以qemu BIOS认为你的磁盘不可引导。


“start.bin” 可以在 VirtualBox 中运行,因为它不执行磁盘签名检查,而 QEMU 则会执行。 - Michael Petch

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接