5

我正在编写 MBR 并使用QEMU进行测试。

当使用读扇区到内存 ( int 0x13, ah=0x02)时,该int指令似乎阻止了我的程序的执行,并且它继续挂起。我已经用各种打印语句对此进行了测试,以确认它是这个特定的指令阻塞。

什么可以使中断阻塞?我认为这只能通过cli指令来完成,即使那样它也不会阻塞int指令。


对于上下文,这是导致阻塞中断的代码read_sectors_16

        [bits 16]        
        [ORG 0x7C00]
        jmp 0x000:start_16      ; ensure cs == 0x0000

        reset_failure_str db 'error: boot disk reset', 13, 10, 0
        read_failure_str db 'error: boot disk read', 13, 10, 0
        boot_segaddr dw 0x7E00

        read_attempt_str db 'read attempt', 13, 10, 0
        end_read_attempt_str db 'end read attempt', 13, 10, 0

start_16:
        ;; Initialize segment registers
        mov ax, cs
        mov ds, ax
        mov es, ax

        jmp load_bootsector_2            


load_bootsector_2:              ; Read program from disk
        ;; dl set by BIOS to the drive number MBR was loaded from
        mov cx, 0x0002          ; cylinder 0, sector 2
        xor dh, dh              ; head 0
        mov al, 0x01            ; load 1 sector
        mov bx, boot_segaddr    ; destination - load right after the boot loader
        call read_sectors_16
        jnc .success
        mov si, read_failure_str
        call print_string_16        
        jmp halt                ; halt

        .success:        
        jmp boot_segaddr:0000   ; jump to program

这是带有阻塞中断的函数:

;;; read_sectors_16
;;;
;;; Read sectors from disk in memory using BIOS services
;;;
;;; input:      dl      = drive
;;;             ch      = cylinder[7:0]        
;;;             cl[7:6] = cylinder[9:8]
;;;             dh      = head
;;;             cl[5:0] = sector (1-63)
;;;             es:bx   -> destination
;;;             al      = number of sectors
;;;
;;; output:     cf (0 = success, 1 = failure)
read_sectors_16:
        pusha
        mov di, 0x02            ; set attempts (max attempts - 1)

        .attempt:
        mov ah, 0x02            ; read sectors into memory (int 0x13, ah = 0x02)
        int 0x13                ; TODO: this call is not returning!
        jnc .end                ; exit if read succeeded
        dec di                  ; record attempt
        test di, di
        jz .end                 ; end if no more attempts
        xor ah, ah              ; reset disk (int 0x13, ah = 0x00)
        int 0x13
        jnc .attempt            ; retry if reset succeeded, otherwise exit
        jmp .end

        .end:
        popa
        ret
4

1 回答 1

5

突出的是您的细分市场。首先,您的代码定义boot_segaddr为:

boot_segaddr dw 0x7E00

这将在内存中创建一个值为 0x7E00 的 16 位字。然后你有这两条线:

mov bx, boot_segaddr
[snip]
jmp boot_segaddr:0000 

在这两种情况下boot_segaddr,都被用作立即数。您使用的是 的地址boot_segaddr,而不是boot_segaddr指向的值。

我将更boot_segaddr dw 0x7E00改为一个常量值(使用EQU)并将其重命名为:

BOOT_OFFSET EQU 0x7E00

然后你可以修改mov bx, boot_segaddr为:

mov bx, BOOT_OFFSET

这将具有将 0x7E00 移动到BX的效果。对Int 13/AH=2的调用将读取从 ES:BX = 0x0000:0x7E00 开始的扇区,这正是您想要的。

下一个问题是如果我们像这样为FAR JMP重用相同的常量:

jmp BOOT_OFFSET:0000 

这将导致FAR JMP到 0x7E00:0x0000。不幸的是,这是物理地址 (0x7E00<<4)+0x0000 = 0x7E000,这不是您想要跳转的地方。您想跳转到物理地址 0x07E00。你真的想要一个到 0x07E0:0x0000 的FAR JMP,这将是物理地址 (0x07E0<<4)+0x0000 = 0x7E00。为了让FAR JMP正常工作,我们可以BOOT_OFFSET右移 4 位。您可以将行更改为:

jmp (BOOT_OFFSET>>4):0000 

进行这些更改应该可以让您的引导加载程序正常工作。因为原来的代码中有 2 个错误:

  • 使用变量的地址而不是该地址的值
  • 使用错误的段值执行FAR JMP 。

明显的挂起可能是由于读取从boot_segaddr引导加载程序中的内存地址开始的扇区引起的。您可能覆盖了引导加载程序中的所有代码,使其在int 13h最终返回时无法正常工作。


正如 Peter 指出的那样,使用像 BOCHS 这样的仿真器及其内部调试器将允许您单步执行 16 位实模式代码。您可能已经发现了这些问题。

于 2016-09-17T06:05:47.283 回答