读写从INT 13H硬盘的扇区(Read a write a sector from hard dr

2019-08-17 18:07发布

我有一个简单的程序。 它必须读取硬盘驱动器(MBR不)第一个扇区,并将其写入0扇区(MBR)。 但它不工作。 我认为这是错误的与DAP相连。 谢谢。

    [bits   16]
    [org    0x7c00]

;clear screen
start:
    mov     ax, 0x3
    int     0x10

;reset the hard drive
    xor     ah, ah
    mov     dl, 0x80
    int     0x13
    jnz     error

;read the second sector
    mov     si, DAP
    mov     ah, 0x42
    int     0x13

    mov     si, data
    call    print_string
    jmp     $

DAP:
    db      0x10    ;size of DAP
    db      0x0     ;zero
    db      0x1     ;number of sectors to read
    db      0x0     ;zero
;point to memory
    dw      0x0     ;offset
    dw      0x0     ;segment
    dq      0x1     ;disk address

DAP2:
    db      0x10
    db      0x0
    db      0x1
    db      0x0
    dw      0x0
    dw      0x0
    dd      0x0
    dd      0x0            

print_string:
    mov     ax, 0xb800
    mov     es, ax
    xor     di, di
    mov     cx, 8
    rep     movsw
    ret
data: db 'H',2,'e',2,'l',2,'l',2
error:db 'E',2,'r',2,'r',2
    times   510 - ($ - $$) db 0
    dw      0xaa55   

UPD:新代码

    [bits   16]
    [org    0x7c00]

;clear screen
start:
;    mov     ah, 0
;    push    ax
;    pop     ds
    mov     ax, 0x3
    int     0x10

;reset the hard drive
    xor     ah, ah
    mov     dl, 0x80
    int     0x13
    jc      error

;read the second sector
    mov     si, DAP
    mov     ah, 0x42
    int     0x13

    mov     si, data
    call    print_string
    jmp     $

DAP:
    db      0x10    ;size of DAP
    db      0x0     ;zero
    db      0x1     ;number of sectors to read
    db      0x0     ;zero
;point to memory
    dw      0x0     ;offset
    dw      0x8c00  ;segment
    dq      0x1     ;disk address

DAP2:
    db      0x10
    db      0x0
    db      0x1
    db      0x0
    dw      0x0
    dw      0x8c00
    dq      0x2            

print_string:
    mov     ax, 0xb800
    mov     es, ax
    xor     di, di
    mov     si, 0x8c00
    mov     cx, 8
    rep     movsw
    ret

data: db 'H',2,'e',2,'l',2,'l',2
error:db 'E',2,'r',2,'r',2
endp:
    times   510 - ($ - $$) db 0
    dw      0xaa55 

PS我使用Bochs的。

Answer 1:

奸尸的位; 希望您的装配技能,在​​此期间有所改善。 但是为了以防万一......

引述@Alexey伏龙芝“你需要注意你在做什么”。 除了在其他答案详细的错误,这里是我的一些意见:


你的模拟器是太不厚道

  • 您的代码似乎是一个引导程序。 您将承担BIOS会在加载代码0x0000:0x7C00 ,但你不能确保它不会在实际上在加载0x07C0:0000 ,或任何其他等效的地址。 阅读上的分割 。

  • 您无法初始化任何段寄存器。 你可能逃脱它,因为你的模拟器是一种,正确初始化csdses0x0000

您可以解决这两个问题是这样的:

[bits 16]
[org 0x7C00]

    jmp 0x0000:start_16 ; ensure cs == 0x0000

start_16:
    ; initialise essential segment registers
    xor ax, ax
    mov ds, ax
    mov es, ax


基本的误解

  • 在一个错误的情况下,您可以直接跳转到一个字符串,而不是可执行代码。 上帝只知道如果出现这种情况,计算机将做什么。

  • 您检查驱动器复位的返回值(CF),而不是阅读本身。 在读的情况下失败,您应该重置驱动器,再次尝试读取。 在几次尝试(比如3)的情况下,驱动器打嗝一个循环做到这一点。 如果驱动器复位失败,很可能更严重是不对的,你应该保释。


更安全的方法

我会建议使用int 0x13, ah = 0x02 。 您使用的可能不是在所有系统上支持扩展BIOS功能(模拟器支持可能是古怪,更不用说一些现代硬件中的懒惰BIOS实现)。 你在实模式 - 你不需要做任何幻想。 这将是最好的进入保护模式写一个PM驱动程序来处理磁盘I / O的长期目标。

只要你留在实模式,这里是一个独立的函数,将读取使用简单的BIOS功能,从磁盘中的一个或多个部门。 如果你不提前知道哪个部门()你需要,你将不得不增加额外的检查,以照顾多轨读取 。

; read_sectors_16
;
; Reads sectors from disk into 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 si, 0x02    ; maximum attempts - 1
.top:
    mov ah, 0x02    ; read sectors into memory (int 0x13, ah = 0x02)
    int 0x13
    jnc .end        ; exit if read succeeded
    dec si          ; decrement remaining attempts
    jc  .end        ; exit if maximum attempts exceeded
    xor ah, ah      ; reset disk system (int 0x13, ah = 0x00)
    int 0x13
    jnc .top        ; retry if reset succeeded, otherwise exit
.end:
    popa
    retn


您的打印功能呈现在彩色显示器(通过0xB8000写入显存)。 同样,你在实模式。 把事情简单化。 使用BIOS服务:

; print_string_16
;
; Prints a string using BIOS services
;
; input:    ds:si -> string

print_string_16:
    pusha
    mov  ah, 0x0E    ; teletype output (int 0x10, ah = 0x0E)
    mov  bx, 0x0007  ; bh = page number (0), bl = foreground colour (light grey)
.print_char:
    lodsb            ; al = [ds:si]++
    test al, al
    jz   .end        ; exit if null-terminator found
    int  0x10        ; print character
    jmp  .print_char ; repeat for next character
.end:
    popa
    retn


用法示例

load_sector_2:
    mov  al, 0x01           ; load 1 sector
    mov  bx, 0x7E00         ; destination (might as well load it right after your bootloader)
    mov  cx, 0x0002         ; cylinder 0, sector 2
    mov  dl, [BootDrv]      ; boot drive
    xor  dh, dh             ; head 0
    call read_sectors_16
    jnc  .success           ; if carry flag is set, either the disk system wouldn't reset, or we exceeded our maximum attempts and the disk is probably shagged
    mov  si, read_failure_str
    call print_string_16
    jmp halt                ; jump to a hang routine to prevent further execution
.success:
    ; do whatever (maybe jmp 0x7E00?)


read_failure_str db 'Boot disk read failure!', 13, 10, 0

halt:
    cli
    hlt
    jmp halt


最后但并非最不重要的...

引导程序不设置栈。 我提供的代码使用了堆栈来防止寄存器捣毁。 有几乎30KiB引导程序(<0x7C00)之前,所以你可以简单地做这个地方你的引导附近开始:

xor ax, ax
cli         ; disable interrupts to update ss:sp atomically (AFAICT, only required for <= 286)
mov ss, ax
mov sp, 0x7C00
sti


唷! 大量消化。 请注意,我试图保持独立的功能灵活,所以在其他16位实模式程序可以重新使用它们。 我建议你尝试写更多模块化的代码,并坚持这种做法,直到你更有经验。

例如,如果你在使用扩展读取功能死心塌地,也许你应该写一个接受DAP,或指向一个,堆栈上的功能。 当然,你会浪费代码空间推数据有摆在首位,但一旦它的存在,你可以简单地调整必要的字段为后续读取,而不是大量的DAP的占用内存。 堆栈空间可以稍后收回。

不要气馁,组装需要时间,而可怕的对细节的关注......在工作中扑这个东西出来不容易的时候,所以有可能是在我的代码中的错误! :)



Answer 2:

首先,你需要检查cf而不是zf ,看是否BIOS调用成功。 纠正你的jnz error

其次,你似乎是依靠ds等于0。它不能保证为0将其设置为0。

对于同上flags.df ,它不能保证为0它设置为0检查的文件repmovs*cld

第三,你问BIOS读取部门和它在内存中写入到物理地址0。 通过这样做,你覆盖中断向量表(即从那里开始,占用1KB)和破坏系统,需要重新启动。 选择一个更好的地址。 最好是在内存中的启动扇区结束之后。 但是,你想也需要确保堆栈不存在,所以你需要堆栈设置为一个已知的位置为好。

你需要注意你在做什么。



Answer 3:

加载一个扇区的代码并跳转到它,没有错误检查和DAP最小NASM BIOS例如:

use16
org 0x7C00

    ; For greater portability you should
    ; do further initializations here like setup the stack and segments. 

    ; Load stage 2 to memory.
    mov ah, 0x02
    mov al, 1
    ; This may not be necessary as many BIOS setup is as an initial state.
    mov dl, 0x80
    mov ch, 0
    mov dh, 0
    mov cl, 2
    mov bx, stage2
    int 0x13

    jmp stage2

    ; Magic bytes.    
    times ((0x200 - 2) - ($ - $$)) db 0x00
    dw 0xAA55

stage2:

    ; Print 'a'.
    mov ax, 0x0E61
    int 0x10

    cli
    hlt

    ; Pad image to multiple of 512 bytes.
    times ((0x400) - ($ - $$)) db 0x00

编译并运行:

nasm -f bin -o main.img main.asm
qemu-system-i386 main.img

预期成果: a被打印到屏幕上,然后程序停止。

经测试在Ubuntu 14.04。

使用链接脚本和更正确的初始化(段寄存器,堆栈)理智GAS例如在我的GitHub 。



Answer 4:

load:
; Load sectors routine : bootdrv-drive , snum-sectors to load
;ES:BX where to load ex:  mov ax,0000h mov es,ax  mov bx, 7c00h
        push bx
        push ds
        mov verify,0h
.reset:
        cmp verify,5h
        je Err1
        add verify,1h
        mov ax, 0               ; Reset Disk
        mov dl, [BootDrv]       ; Drive to reset
        int 13h                 ;
        jc .reset               ; Failed -> Try again

        pop ds
        pop bx
        mov verify,0h
.read:
        cmp verify,5h
        je Err1
        add verify,1h
        mov ah, 2               ; Interrupt 13h,2 => Read disk sectors
        mov al, snum            ; how many sectors to read
        mov cx, fsect           ; cl-first sector to be r/w ch-track containing sector
        mov dh, head               ; head=0
        mov dl, [BootDrv]       ; Drive=boot drive
        int 13h                 ; Read! ES:BX = data from disk
        jc .read                ; failed -> Try again
        retn


文章来源: Read a write a sector from hard drive with int 13h