我目前正在尝试编写16位实模式启动代码,该代码打印一个字母,然后从软盘加载第二个段并跳转到该段,然后再打印一个字母。
但是,我对“从云端硬盘读取扇区”呼叫的工作方式有些困惑。到目前为止,这是我的代码:
[BITS 16] org 0x7B00 start: mov ax, 0xB800 ; Video buffer mov es, ax ; Copy address of video buffer to extra segment mov byte [es:0], 'A' ; Move character A to first address mov byte [es:1], 0x17 ; Format for blue background, white foreground mov ah, 0x02 ; Read sectors from drive mov al, 1 ; Read 1 sector mov ch, 0 ; Cylinder 0 mov cl, 0 ; Sector 0 mov dh, 0 ; Head 0 mov dl, 0 ; Drive 0 (Floppy) mov word [es:bx], sect2dest ; <- Completely unsure about this. int 0x13 jmp sect2dest:0 data: sect2dest equ 0x0500
我将第二个汇编文件复制到软盘的第二个扇区,如下所示:
[BITS 16] org 0x5000 sect2: mov ax, 0xB800 mov es, ax mov byte [es:2], 'B' mov byte [es:3], 0x17 jmp $
但是,没有打印任何内容,而是将光标变成了紫色。我知道,这是一个奇怪的问题,可以通过提高ASM技能轻松解决,所以请原谅
我强烈建议您看一下我在上一个StackOverflow答案中编写的General Bootloader提示。您应该考虑在引导加载程序代码中设置一个堆栈,设置DS(数据段),并且应该在执行引导加载程序时使用DL寄存器中BIOS所提供的引导驱动器。这将允许您从可能不是第一张软盘的驱动器启动。我的第一个提示概述了:
当BIOS跳转到您的代码时,您不能依赖具有有效或预期值的CS,DS,ES,SS,SP寄存器。引导加载程序启动时,应适当设置它们。您只能保证将引导加载程序从物理地址0x00007c00加载并运行,并且将引导驱动器号加载到DL寄存器中。
BIOS将主引导记录(磁盘的第一个扇区)加载到0x7c00。您的ORG指令使用0x7b00。您应该更改:
org 0x7B00
至:
org 0x7C00
我不确定是否要切断代码,但是在引导加载程序的最底部,您没有将0xAA55 magic值放在扇区的最后一个字中,以将引导扇区标记为可引导。您应该在引导程序的底部添加以下内容:
times 510-($-$$) db 0 dw 0xaa55
如果您使用的是链接程序脚本(有迹象表明您不是),则可以使用该脚本在第一个扇区的末尾插入0xaa55。您可能以其他方式(问题中未显示)执行此操作。如果以其他方式插入0xAA55,则可以忽略此更改。
Ralph Brown的中断列表可能是中断文档(包括BIOS中断)的最佳来源。特别是int 13h / ah = 02读取扇区记录为:
AH = 02h AL = number of sectors to read (must be nonzero) CH = low eight bits of cylinder number **CL** = sector number 1-63 (bits 0-5) high two bits of cylinder (bits 6-7, hard disk only) DH = head number **DL** = drive number (bit 7 set for hard disk) **ES:BX** -> data buffer
因为DL包含BIOS传递的引导驱动器号,所以您可以删除此行,因为DL已经是我们想要的值:
mov dl, 0 ; Drive 0 (Floppy)
此更改使我们能够从BIOS实际使用的启动驱动器中读取扇区,这意味着该代码可用于从软盘B(DL = 0x01)或硬盘驱动器(DL = 0x80等)启动
从CL开始读取的扇区号是一个介于1和63之间的值。扇区号(采用CHS格式)是不寻常的,因为它们是从1开始而不是从0开始的。圆柱0上的扇区1磁头0是引导扇区。汽缸0上的扇区2磁头0是引导记录之后的扇区。鉴于您的问题,您似乎打算这样做:
mov cl, 0 ; Sector 0
成为:
mov cl, 2 ; Sector 2
您将此代码标记为可能的问题,并且您是正确的:
mov word [es:bx], sect2dest
这会将常量sect2dest移动到内存中从ES:BX开始的单词。BX可能有零(它从未初始化过,所以可能是任何东西),并且由于ES仍指向视频内存,因此您可能将0x0500写入了视频显示的第一个单元格。什么ES:BX手段在文档中是ES和BX应该是段和你想读磁盘扇区成内存偏移。由于您似乎打算将第二阶段加载到0x0500:0x0000,因此需要设置ES = 0x0500和BX = 0x0000。删除此代码:
mov word [es:bx], sect2dest ; <- Completely unsure about this.
并替换为:
mov bx, sect2dest mov es, bx ; ES = 0x0500 xor bx, bx ; BX = 0. So ES:BX=0x0500:0x0000
在第二阶段org 0x5000
应该是org 0x0000
。原因是jmp 0x0500:0x0000
将segment:offset CS:IP设置为CS = 0x0500和IP = 0x0000。您需要您的ORG指令以匹配您要跳转到的偏移量。您应该使用它,ORG 0x0000
因为随着远跳将获得IP(偏移)= 0x0000 jmp sect2dest:0
。
更改:
org 0x5000
至
org 0x0000
您应该在第二阶段通过将CS(0x0500)复制到DS来设置DS数据段。您可以在第二阶段将其添加到代码的顶部:
mov ax, cs mov ds, ax
这有效地使DS = CS。您无需为所示的示例代码执行此操作,但是如果添加了.data
一部分,则需要它来正确访问变量。
引导程序汇编代码:
[BITS 16] org 0x7C00 start: ; This section of code is added based on Michael Petch's bootloader tips xor ax,ax ; We want a segment of 0 for DS for this question mov ds,ax ; Set AX to appropriate segment value for your situation mov bx,0x8000 ; Stack segment can be any usable memory cli ; Disable interrupts to circumvent bug on early 8088 CPUs mov ss,bx ; Top of the stack @ 0x80000. mov sp,ax ; Set SP=0 so the bottom of stack will be just below 0x90000 sti ; Re-enable interrupts cld ; Set the direction flag to be positive direction mov ax, 0xB800 ; Video buffer mov es, ax ; Copy address of video buffer to extra segment mov byte [es:0], 'A' ; Move character A to first address mov byte [es:1], 0x17 ; Format for blue background, white foreground mov ah, 0x02 ; Read sectors from drive mov al, 1 ; Read 1 sector mov ch, 0 ; Cylinder 0 mov cl, 2 ; Sector 2 mov dh, 0 ; Head 0 mov bx, sect2dest mov es, bx ; ES = sect2dest xor bx, bx ; BX = 0 int 0x13 jmp sect2dest:0 data: sect2dest equ 0x0500 times 510-($-$$) db 0 ; Create padding to fill out to 510 bytes dw 0xaa55 ; Magic number in the trailer of a boot sector
第二阶段汇编代码:
[BITS 16] org 0x0000 sect2: mov ax, cs mov ds, ax ; Set CS=DS. CS=0x0500, therefore DS=0x500 ; If variables are added to this code then this ; will be required to properly reference them ; in memory mov ax, 0xB800 mov es, ax mov byte [es:2], 'B' mov byte [es:3], 0x17 jmp $