我正在尝试为x86机器创建一个小型操作系统,并开始为相当小的引导程序编写代码.我创建的引导加载程序非常简单,它从主引导记录之后的扇区加载一个小的第二个引导加载程序并跳转到该代码.主引导记录中的引导加载程序代码似乎运行正常,当它尝试跳转到第二阶段引导加载程序时会出现问题.这个第二阶段的引导加载程序应该输出一个表示成功的字母(字母S),这样我就能告诉代码正在执行.问题是屏幕上没有出现任何问题,所以我怀疑第二阶段的引导程序从未执行过.我使用的代码如下:
主引导记录中的Bootloader:
[BITS 16] ; 16 bit mode [ORG 0x7C00] ; Boot loader start address Boot: ; Initial, dl contains drive number ; Set data segment to code segment mov ax, cs mov ds, ax mov es, ax ; Set the stack segment to 0xA000 add ax, 0xA000 mov ss, ax mov sp, 0x00 ; Reset the drive, dl contains drive number mov ah, 0x00 int 0x13 ; Read from drive, dl contains drive number ; Set up output location to 0x7E00: 0x00 mov ax, 0x7E00 mov es, ax ; Load to 0x7E00 : 0x00 mov bx, 0x00 ReadDrive: mov ah, 0x02 mov al, 0x01 ; Read 1 sector mov ch, 0x00 ; Read on cylinder 0 mov cl, 0x02 ; Read sector 2 mov dh, 0x00 ; Head number 0 int 0x13 jnc Success ; Print error (character F) mov al, 0x46 call PrintChar jmp ReadDrive ; Retry PrintChar: ; Prints a single character pusha mov ah, 0x09 mov bh, 0x00 mov bl, 0x0F mov cx, 0x01 int 0x10 popa ret Success: jmp 0x7E00:0x00 ; Jump to 2nd stage bootloader TIMES 510 - ($ - $$) db 0 DW 0xAA55 ; Boot signature
第二阶段bootloader的代码:
[BITS 16] [ORG 0x7E00] Boot2: ; Prints the character S to the screen mov al, 0x53 mov ah, 0x09 mov bh, 0x00 mov bl, 0x0F mov cx, 0x01 int 0x10 jmp $ ; Loop forever TIMES 512 - ($ - $$) db 0 ; Fill rest of block
使用以下代码编译并将此代码写入驱动器:
nasm boot.asm -o boot.bin -f bin nasm boot2.asm -o boot2.bin -f bin dd if=boot.bin of=/dev/sd3 bs=512 dd if=boot2.bin of=/dev/sd3 bs=512 seek=1
这段代码编写的设备是一个16GB的USB驱动器.我用来启动此代码的计算机支持从USB启动并像其他任何硬盘一样启动它们.代码似乎没有执行的原因是什么?
您的代码中似乎存在许多问题.我会尝试找出其中一些.在我为Stackoveflow编写的一些答案中可以找到一些有用的参考资料.
常规引导加载程序提示,提供您不希望在引导加载程序中进行的一般准则和假设
有关未正确设置DS并在访问内存变量时获取垃圾的缺陷的信息.这适用于你的第二阶段
与您的相似问题的答案也可以提供一些有用的信息.
您确实设置了堆栈,但它可能与视频内存重叠.虽然这可能与您的问题无关,但这是一个潜在的问题.使用此代码:
add ax, 0xA000 mov ss, ax mov sp, 0x00
您设置SS = 0xa000,SP = 0x0000.这会设置堆栈,但不幸的是,堆栈上的第一个值将是0xa000:(0x0000-2)= 0xa000:0xfffe.0xa000:0xfffe可能属于视频内存.也许你打算做ss = 0x9000,所以堆栈上的第一个值将是0x9000:0xfffe.那里也有一个障碍.该扩展BIOS数据区域(EBDA)可以在该地区.某些BIOS错误地为此区域返回了错误的大小.在大多数情况下,它在物理地址0xa0000之下的大小为0k到4k.如果你考虑到最糟糕的情况,我会选择低于它的堆栈.
add ax, 0x9000 mov ss, ax mov sp, 0xF000 ; Bottom of stack at 0x9000:0xF000
这里有两个问题.在您的问题中,您建议您尝试将第二阶段读入引导加载程序正上方的区域.那将是物理地址0x7e00.您的代码执行此操作:
; Read from drive, dl contains drive number ; Set up output location to 0x7E00: 0x00 mov ax, 0x7E00 mov es, ax ; Load to 0x7E00 : 0x00 mov bx, 0x00
16位段:偏移对使用此计算映射到物理存储器地址:(段<< 4)+偏移(<< 4与乘以16相同).这意味着0x7E00:0x00是物理内存地址(0x7E00 << 4)+ 0 = 0x7e000.这显然是错的.我相信你的意图是这样的:
mov ax, 0x07E0 mov es, ax ; Load to 0x07E0:0x00 mov bx, 0x00
0x07E0:0x00是物理内存地址(0x07E0 << 4)+ 0 = 0x7e00.这是在物理地址0x7c00处加载到内存中的引导加载程序正上方的区域.使用此代码将FAR JMP发送到第二阶段时会出现类似的问题:
jmp 0x7E00:0x00 ; Jump to 2nd stage bootloader
应该:
jmp 0x07E0:0x00 ; Jump to 2nd stage bootloader
如果您进行jmp 0x07E0:0x00
前面提到的建议更改(),则FAR JMP将CS:IP更改为CS = 0x07E0(段),IP = 0x0000(偏移量)并继续执行.您需要ORG指令来匹配从第一阶段跳转到的偏移量(IP).由于偏移量(IP)为0x0000,因此ORG指令应匹配:
[ORG 0x0000]
您还需要确保在第二阶段开始加载时DS也设置为匹配.实现此目的的一种方法是明确地将代码段CS复制到数据段DS.这可以通过第二阶段顶部的代码完成,如下所示:
mov ax, cs mov ds, ax
如果没有正确设置数据段DS,对变量的所有引用都将使用错误的段,并且可能不会指向它们在内存中的实际位置.您的代码目前没有变量,因此您不会注意到该问题.
在本答案的序言中提到的一般Bootloader提示中,提示#1非常重要:
当BIOS跳转到您的代码时,您不能依赖具有有效或预期值的CS,DS,ES,SS,SP寄存器.应在引导加载程序启动时正确设置它们.您只能保证将从物理地址0x00007c00加载并运行引导加载程序,并将引导驱动器号加载到DL寄存器中.
在你的代码中你的bootloader有这个:
[BITS 16] ; 16 bit mode [ORG 0x7C00] ; Boot loader start address Boot: ; Initial, dl contains drive number ; Set data segment to code segment mov ax, cs mov ds, ax mov es, ax
[ORG 0x7C00]
很好,但是假设CS段到达我们的引导加载程序时设置为0x0000.然后我们设置DS = CS.对于天真的引导程序,传统的智慧是BIOS跳转到0x0000:0x7c00(CS:IP).ORG 应匹配偏移量(在本例中为IP).问题是,实际上BIOS会跳转到物理地址0x00007c00,但它可以通过各种CS:IP对来实现.
BIOS可以使用FAR JMP(或等效)代码jmp 0x07c0:0x0000
,并且一些仿真器和真实硬件就是这样做的.0x07c0:0x0000是(0x07c0 << 4)+ 0 = 0x7c00的物理地址.这很好,但请注意IP = 0x0000.我们已经确定了[ORG 0x7c00]
.这将是一个不匹配!如果我们实际上不知道BIOS调用我们的CS:IP对,我们如何解决这个问题?简单 - 不要在引导加载程序的第一阶段将CS复制到DS.由于我们需要0x7c00的偏移量,DS需要为0x0000才能工作.我们应该在数据段(DS)中明确地放置0x0000 .代码可能如下所示:
[BITS 16] ; 16 bit mode [ORG 0x7C00] ; Boot loader start address Boot: ; Initial, dl contains drive number ; Set data segment to code segment xor ax, ax ; AX=0 mov ds, ax ; DS=0 mov es, ax ; ES=0