当我尝试从引导加载程序挂起INT 13h时,为什么PC会停止?

时间:2018-02-09 11:21:45

标签: assembly x86 x86-16 bootloader fasm

当我将软件写入MBR时,我的电脑停止运行。我的软件用于挂接INT 13h并在执行INT 13h时执行一些代码。我已经为INT 13h尝试了太多的ISR,但没有成功。这是我的示例来源:

cli
cs
mov si,[4Ch]
cs
mov di,[4Eh]
cs
mov [180h],cx
cs
mov [182h],dx
mov si,0h
mov di,2000h
cs
mov [4Ch],si
cs
mov [4Eh],di
sti
mov si,2000h
mov di,0
mov ds,di
mov es,si
mov si,7C6DH
mov di,0
mov cx,codelastbyte-payload
cld     
repz
movsb

mov ax,201h
push cs
pop es
mov bx,7c00h
mov cx,7
mov dx,80h
int 60h
jmp 0:7C00h

payload:           ;NEW INT13H
sti
INT 60H
iret

codelastbyte:

有什么问题?

1 个答案:

答案 0 :(得分:2)

您没有提供任何背景信息,而且您的代码似乎并不完整。一个明显的错误似乎出现在这些行中:

mov si,[4Ch]
cs
mov di,[4Eh]
cs
mov [180h],cx
cs
mov [182h],dx

看来你真的是指最后4行是 SI DI

cs
mov [180h],si
cs
mov [182h],di

如果chain loading您无法在代码已执行的区域之上加载新的引导加载程序。您可能会遇到不寻常的行为。您需要将引导加载程序复制到其他位置;继续在新位置执行代码;然后将新的引导加载程序加载到物理地址0x07C00的内存中。

为了便于阅读,最好将段覆盖(在这种情况下为 CS )放在内存操作数内。这段代码:

cs
mov [180h],si

可以写成:

mov [cs:180h],si

在FASM中完成实施

以下代码示例:

  • 设置堆栈指针,以便我们知道我们的磁盘读取不会破坏它
  • 保存BIOS通过 DL 传入的引导驱动器号。如果我们不必,则最好不要对引导驱动器号进行硬编码。
  • 将引导加载程序代码和数据复制到0x0060:0x0000,以便在将新引导扇区链式加载到0x07c0:0x0000时不会破坏它。内存位置0x0060:0x0000位于中断表,BIOS数据区和保留内存之上。
  • 使用 FAR JMP
  • 继续在0x0060段中执行代码
  • 将新的Int 13h中断处理程序复制到0x2000:0x0000
  • 安装新的Int 13h中断处理程序
  • 在物理地址0x07c00
  • 上读取原始文件顶部的引导磁盘的第7扇区
  • FAR JMP 到新加载的引导程序0x0000:0x7c00
  • 将装配文件填充到1.44MiB软盘的大小

实现此目的的FASM代码:

RESIDENT_SEG equ 0x2000
BOOT_SEG     equ 0x07c0
BOOTCOPY_SEG equ 0x0060
STACK_OFFSET equ 0x7c00

INT13_OFFSET equ 0x13 * 4

org 0x0000
boot_start:
    ; Set up our own stack just below bootloader @ 0x0000:0x7c00
    xor ax, ax
    mov ss, ax
    mov sp, STACK_OFFSET

    ; Copy the bootloader code and data from 0x07C0:0x0000 to 0x0060:0x0000
    mov ax, BOOT_SEG
    mov ds, ax
    mov [boot_drive], dl
    mov ax, BOOTCOPY_SEG
    mov es, ax
    cld
    xor si, si
    mov di, si
    mov cx, SIZEOF_BOOT+SIZEOF_PAYLOAD
    rep movsb

    ; Transfer control to copy of bootloader relocated to segment 0x0060
    jmp BOOTCOPY_SEG:bootcopy_cont
bootcopy_cont:

    ; Set DS = ES= 0x0060
    push es
    pop  ds

    ; ES = 0 to access IVT that begins at 0x0000:0x0000
    xor  ax, ax
    mov  es, ax

    ; Place the original FAR PTR of Int 13 into BX:AX
    cli
    mov ax, [es:INT13_OFFSET]
    mov bx, [es:INT13_OFFSET+2]

    ; Install our new int 0x13 handler
    mov si, int13
    mov di, RESIDENT_SEG
    mov [es:INT13_OFFSET], si
    mov [es:INT13_OFFSET+2], di

    ; Copy the interrupt handler to 0x2000:0x0000
    mov es, di
    mov si, payload_boot_offset
    xor di, di
    mov cx, SIZEOF_PAYLOAD
    rep movsb

    ; Now that interrupt and interrupt data (payload) is copied to 0x2000 segment
    ; save the orig_int13 to the interrupt data area. AX and BX were set earlier
    mov [es:orig_int13], ax
    mov [es:orig_int13+2], bx

    sti

    ; Read sector 7(CHS=0,0,7) to 0x07c0:0x0000 (phys address 0x07c00)
    mov ax, 201h
    mov bp, BOOT_SEG
    mov es, bp
    xor bx, bx
    mov cx, 7
    xor dx, dx
    mov dl, [boot_drive]
    call do_orig_int13

    ; Jump to newly loaded bootloader
    jmp 0:0x7C00

    ; Function : do_orig_int13
    ; Return   : none
    ; Clobbers : Same as int 0x13
    ;
    ; Description:
    ; Manually call the original Int 0x13 handler. Int instruction pushes
    ; the flags and then calls through the interrupt vector table. We
    ; Do that same process. This avoids having to use Int 0x60. The
    ; Old interrupt vector was saved to RESIDENT_SEG(0x2000):orig_int13
    ; earlier.
do_orig_int13:
    push ds
    push bp
    mov bp, RESIDENT_SEG
    mov ds, bp
    pop bp
    pushf
    call far [ds:orig_int13]
    pop ds
    ret

; Boot data
boot_drive: db 0x00

SIZEOF_BOOT = $-$$

payload_boot_offset:
org 0x0000
int13:           ;NEW INT 0x13

    ; Print MDP to upper left corner of screen in magenta on white
    push ax
    push es
    mov ax, 0xb800
    mov es, ax
    mov word [es:0x0000], (0x75 shl 8) or 'M'
    mov word [es:0x0002], (0x75 shl 8) or 'D'
    mov word [es:0x0004], (0x75 shl 8) or 'P'
    pop es
    pop ax

    ; Do original disk interrupt routine. We jump to it
    ; instead of using the INT instruction because
    ; The Int 0x13 original routine sets the flags and we
    ; want them to be returned to whoever called us.
    jmp far [cs:orig_int13]

    ; The original interrupt handler will do the IRET for us
    ; Will never return to this point

; Insert data used by the interrupt handler here
orig_int13: dd 0x0000          ; Copy of the original Int 13h handler

SIZEOF_PAYLOAD = $-$$

;Pad first sector with 0, add boot signature to last 2 bytes
times 510-(SIZEOF_BOOT+SIZEOF_PAYLOAD) db 0
dw 0xaa55

; Pad out 5 more sectors with 0
times 512 * 5 db 0x00

; Sector 7 test code
org 0x7c00
sector7:
    ; Set DS = 0x0000
    xor ax, ax
    mov ds, ax

    ; Call Int 0x13 to reset disk. Our interrupt handler should also display MDP
    ; in upper left corner of the screen. AX set to zero above. Int 0x13/AH=0 is reset.
    ; DL set by the code that loaded this bootloader just like the BIOS would have done.
    int 0x13

    cli

; Infinite loop to end the code
.endloop:
    hlt
    jmp .endloop

; Pad out remainder of sector 7
times 512-($-$$) db 0x00

; Pad out remaining bytes to create a 1.44MiB floppy image
times (2880-7)*512 db 0x00

您可以使用以下命令将其组合成软盘映像:

fasm bootload.asm disk.img

使用以下命令运行:

qemu-system-i386 -fda disk.img

输出看起来类似于:

enter image description here

注意:

  • FASM和NASM的ORG指令的工作方式不同。
  • 代码不再将原始Int 0x13中断向量保存到Int 0x60。我们通过存储在内存中的地址执行 PUSHF FAR CALL 手动调用旧中断。
  • 代码修改Int 0x13,使其在屏幕左上角显示为MDP白色洋红色,然后将控制传递给原始处理程序。
  • 扇区7中的代码(CHS = 0,0,7)使用Int 0x13 / AH = 2来重置引导驱动器。此代码用于测试目的,但它是您希望链式加载的实际引导加载程序。
  • 此代码假定我们从引导的同一磁盘链式加载不同的引导扇区(@ CHS = 0,0,7)。
相关问题