用 int13h 从软盘加载段
Load segment from floppy with int13h
我目前正在尝试编写打印一个字母的 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 技能轻松解决,所以请原谅
我强烈建议您看一下我在之前的 Whosebug 回答中写的 。您应该考虑在您的引导加载程序代码中设置一个堆栈,设置 DS(数据段)并且您应该使用 BIOS 在 DL 在执行引导加载程序时注册。这将允许您从可能不是第一张软盘的驱动器引导。我的第一个提示概述了:
When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register.
引导加载程序问题
主引导记录(磁盘的第一个扇区)由 BIOS 加载到 0x7c00。您的 ORG 指令使用 0x7b00。你应该改变:
org 0x7B00
至:
org 0x7C00
我不确定你是否切断了你的代码,但在你的引导加载程序的最底部,你没有将 0xAA55 魔法值放在将您的引导扇区标记为可引导的扇区。你应该在你的引导加载器底部添加这样的东西:
times 510-($-$$) db 0
dw 0xaa55
如果您使用的是链接描述文件(有迹象表明您没有),那么您可以使用它在第一个扇区的末尾插入 0xaa55。您可能会以问题中未显示的其他方式执行此操作。如果您以其他方式插入 0xAA55,则可以忽略此更改。
Ralph Brown 的 Interrupt List is probably the best source of interrupt documentation (including BIOS interrupts). In particular the 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传过来的boot盘号,可以去掉这一行,因为DL已经是我们想要的值了:
mov dl, 0 ; Drive 0 (Floppy)
此更改允许我们从 BIOS 实际使用的引导驱动器中读取扇区,这意味着代码可以用于从软盘 B (DL =0x01) 或软盘引导硬盘驱动器(DL =0x80 等)
CL 中开始读取的扇区号是 1 到 63 之间的值。扇区号(CHS 格式)不常见,因为它们基于 1 而不是 0 . Sector 1 on Cylinder 0 Head 0 是引导扇区。 Cylinder 0 Head 0 上的扇区 2 是引导记录之后的扇区。鉴于你的问题,你的意思似乎是这条线:
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:offsetCS:IP设置为CS=0x0500和IP=0x0000。您需要 ORG 指令来匹配您跳转到的偏移量。你应该使用 ORG 0x0000
因为你会得到一个 IP (offset)=0x0000 和远跳 jmp sect2dest:0
.
变化:
org 0x5000
到
org 0x0000
您应该在第二阶段设置 DS 数据段,方法是将 CS (0x0500) 复制到 DS 。您可以在第二阶段将其添加到代码的顶部:
mov ax, cs
mov ds, ax
这有效地使 DS=CS。您不需要为显示的示例代码执行此操作,但如果您添加 .data
部分,您将需要它来正确访问您的变量。
更改后的代码
bootloader汇编代码:
[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 $
我目前正在尝试编写打印一个字母的 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 技能轻松解决,所以请原谅
我强烈建议您看一下我在之前的 Whosebug 回答中写的
When the BIOS jumps to your code you can't rely on CS,DS,ES,SS,SP registers having valid or expected values. They should be set up appropriately when your bootloader starts. You can only be guaranteed that your bootloader will be loaded and run from physical address 0x00007c00 and that the boot drive number is loaded into the DL register.
引导加载程序问题
主引导记录(磁盘的第一个扇区)由 BIOS 加载到 0x7c00。您的 ORG 指令使用 0x7b00。你应该改变:
org 0x7B00
至:
org 0x7C00
我不确定你是否切断了你的代码,但在你的引导加载程序的最底部,你没有将 0xAA55 魔法值放在将您的引导扇区标记为可引导的扇区。你应该在你的引导加载器底部添加这样的东西:
times 510-($-$$) db 0
dw 0xaa55
如果您使用的是链接描述文件(有迹象表明您没有),那么您可以使用它在第一个扇区的末尾插入 0xaa55。您可能会以问题中未显示的其他方式执行此操作。如果您以其他方式插入 0xAA55,则可以忽略此更改。
Ralph Brown 的 Interrupt List is probably the best source of interrupt documentation (including BIOS interrupts). In particular the 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传过来的boot盘号,可以去掉这一行,因为DL已经是我们想要的值了:
mov dl, 0 ; Drive 0 (Floppy)
此更改允许我们从 BIOS 实际使用的引导驱动器中读取扇区,这意味着代码可以用于从软盘 B (DL =0x01) 或软盘引导硬盘驱动器(DL =0x80 等)
CL 中开始读取的扇区号是 1 到 63 之间的值。扇区号(CHS 格式)不常见,因为它们基于 1 而不是 0 . Sector 1 on Cylinder 0 Head 0 是引导扇区。 Cylinder 0 Head 0 上的扇区 2 是引导记录之后的扇区。鉴于你的问题,你的意思似乎是这条线:
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:offsetCS:IP设置为CS=0x0500和IP=0x0000。您需要 ORG 指令来匹配您跳转到的偏移量。你应该使用 ORG 0x0000
因为你会得到一个 IP (offset)=0x0000 和远跳 jmp sect2dest:0
.
变化:
org 0x5000
到
org 0x0000
您应该在第二阶段设置 DS 数据段,方法是将 CS (0x0500) 复制到 DS 。您可以在第二阶段将其添加到代码的顶部:
mov ax, cs
mov ds, ax
这有效地使 DS=CS。您不需要为显示的示例代码执行此操作,但如果您添加 .data
部分,您将需要它来正确访问您的变量。
更改后的代码
bootloader汇编代码:
[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 $