我目前正在尝试编写16位实模式启动代码来打印一个字母,然后从软盘加载第二段并跳转到它,然后还会打印一封信。
但是,我对Read Sectors From Drive调用的工作方式感到有些困惑。到目前为止,这是我的代码:
[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技能轻松解决,所以请原谅我
答案 0 :(得分:5)
我强烈建议您查看我在之前的StackOverflow答案中写的General Bootloader Tips。您应该考虑在引导加载程序代码中设置堆栈,设置 DS (数据段),并且当您的 DL 寄存器中应使用BIOS提供的引导驱动器时bootloader已执行。这将允许您从可能不是第一张软盘的驱动器启动。我的第一个提示概述了:
当BIOS跳转到您的代码时,您不能依赖具有有效或预期值的CS,DS,ES,SS,SP寄存器。应在引导加载程序启动时正确设置它们。只能保证您的引导加载程序将从物理地址0x00007c00加载并运行,引导驱动器号将加载到 DL 寄存器中。
主引导记录(磁盘的第一个扇区)由BIOS加载到0x7c00。您的 ORG 指令使用0x7b00。你应该改变:
org 0x7B00
为:
org 0x7C00
我不确定您是否关闭了代码,但是在启动加载程序的最底层,您没有将 0xAA55 魔术值放在扇区的最后一个字中以标记您的引导扇区可引导。你应该在你的引导灯的底部添加这样的东西:
times 510-($-$$) db 0
dw 0xaa55
如果您使用的是链接描述文件(有迹象表明您没有),那么您可以使用它在第一个扇区的末尾插入 0xaa55 。您可能会以其他方式执行此操作,但未在您的问题中显示。如果您以其他方式插入0xAA55,则可以忽略此更改。
Ralph Brown的Interrupt List可能是中断文档的最佳来源(包括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。扇区上的扇区1 0头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
将设置段:偏移CS:IP到CS = 0x0500和IP = 0x0000。您需要 ORG 指令来匹配跳转到的偏移量。您应该使用ORG 0x0000
,因为您将获得远程跳转jmp sect2dest:0
的IP(偏移)= 0x0000。
变化:
org 0x5000
要
org 0x0000
您应该在第二阶段通过将 CS (0x0500)复制到 DS 来设置 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 $