【发布时间】:2017-03-05 07:26:35
【问题描述】:
我编写了一个启动代码,它在屏幕上打印一些内容,然后重新定位并加载下一个启动代码 (VBR)。 我使用 vmware 在虚拟机上测试了代码,它可以正常工作,我看到了 vbr 代码的正确输出。 Click here查看代码输出。
我希望能够调试我的代码,由于 vmware 没有此功能,我想使用可与 IDA 一起使用的 bochs。 在bochs上运行代码后,我得到了错误:
>>PANIC<< IO write(0x01f0): current command is 20h
调用处理扩展读取的中断后发生错误(int 13h,函数 42)。 我检查了扩展并支持它们,所以我编写了另一个实模式程序,它使用相同的 bios 功能仅加载 1 个扇区,并且由于某种原因它起作用了。 代码之间只有很小的差异。在引导代码中,我用获取参数的函数包装了加载扇区的代码,而在第二个代码中,它没有用函数包装。我将在这里发布代码。
启动代码(仅相关功能,完整版click here):
%define AdjustAddress(addr) addr - BASE_ADDRESS + NEW_ADDRESS
%define DAP(obj, member) [obj + DiskAddressPacket. %+ member]
NEW_ADDRESS equ 0x0600
BASE_ADDRESS equ 0x7C00
DAP_SIZE equ DiskAddressPacket_size
struc DiskAddressPacket
.size resb 1
.resv resb 1
.num_of_sectors resb 2
.offset resb 2
.segment resb 2
.lba_start resb 8
endstruc
main:
; code
; ....
; Read the VBR
push 0x01
mov si, [AdjustAddress(parti_pointer)]
push dword PE(si, starting_lba)
push BASE_ADDRESS
push 0x00
call ReadSectors
; code
; ....
; void __stdcall ReadSectors(WORD segment, WORD offset, DWORD lba, WORD count)
ReadSectors:
push bp ; Save bp register value
mov bp, sp ; Setting up stack frame
sub sp, DAP_SIZE ; Allocate a buffer for the DAP data
; Zero out DAP buffer
std ; Set direction flag (decrease di)
mov di, bp
xor al, al
mov cx, DAP_SIZE
repnz stosb ; di = DAP buffer at the end of this operation
; Initialize DAP with correct data
mov byte DAP(di, size), DAP_SIZE
mov bx, word [bp + 0x0C] ; count parameter
mov word DAP(di, num_of_sectors), bx
mov bx, word [bp + 0x04] ; segment parameter
mov word DAP(di, segment), bx
mov bx, word [bp + 0x06] ; offset parameter
mov word DAP(di, offset), bx
mov bx, word [bp + 0x08] ; Low word of LBA parameter
mov word DAP(di, lba_start), bx
mov bx, word [bp + 0x0A] ; High word of LBA parameter
mov word DAP(di, lba_start + 0x02), bx
; Prepare parameters
mov ax, 0x4200 ; Extended read sectors function of int 0x13
mov dx, word [AdjustAddress(drive_index)] ; Drive index
mov si, di ; si = DAP buffer
int 0x13 ; Read the sectors from the disk
jc CheckLBASupport.no_lba_support
mov sp, bp ; Clear the allocated memory
pop bp ; Restore bp register value
ret 0x0A ; Clean the stack and return to the calling code
第二个代码:
cli ; Cancel interrupts
; Set up segments registers
xor ax, ax
mov ds, ax
mov es, ax
mov ss, ax
mov sp, 0x7c00
mov ax, 0x4100
mov bx, 0x55aa
int 0x13
; Checking returned value with the debugger
sub sp, DiskAddressPacket_size
mov bp, sp
mov byte [bp], DiskAddressPacket_size
mov byte [bp + 0x01], 0x00
mov word [bp + 0x02], 0x01
mov word [bp + 0x04], 0x7C00
mov word [bp + 0x06], 0x00
mov dword [bp + 0x08], 0x80
mov dword [bp + 0x0c], 0x00
mov ax, 0x4200
mov si, bp
int 0x13
我在内存中检查了两个代码的 DAP 缓冲区,结果是一样的。我无法弄清楚为什么在启动代码上我得到错误而在第二个代码上我没有。有什么我想念的吗?
【问题讨论】:
-
repnz stosb将在 ES:DI 中存储第一个值 (0)。假设您正确设置了 ES,DI 指向 BP。 BP 实际上指向存储在堆栈中的 BP 的第一个字节(使用push bp),这意味着数据在堆栈上被清零,您不想设置为 0。在我看来,您有一个错误关闭将导致 DI 比您在repnz stosb完成时的预期高一。在我看来,您想在repnz stosb之前将 DI 减一吗?可能不是您的问题的原因,但似乎是一个错误。 -
@MichaelPetch 确实你是对的,虽然它不能解决问题。谢谢你指出来。
-
由于您没有发布所有代码以便我们进行测试(这不是一个最小的完整可验证示例)我很好奇如果您将
repnz stosb更改为dec di@ 会发生什么987654333@inc di。之后的inc di必须针对最后一个stosb将DI 减一这一事实进行调整。 -
@MichaelPetch 我记得把
inc di放在repnz stosb之后。由于代码有点大,放在原始帖子上,我不想全部复制,这样更容易集中精力解决问题,但我会放一个链接到粘贴 bin 的完整代码(@987654323 @) -
我最好的猜测是问题与堆栈有关。扩展的 int 13h/ah=42 可能需要比重新定位在 0x600 和 0x7c00 附近的代码末尾之间可用的堆栈更多。如果你把你的堆栈移到别处会发生什么。作为一个实验,您可以尝试将 SS:SP 设置为 0x8c00:0x0000 之类的值,这会将堆栈放置在视频内存和 EBDA 下方的 0x8c00:0x0000 (0x8c000) 和 0x9c00:0000 (0x9c000) 之间。
标签: assembly x86 nasm bootloader bochs