簡體   English   中英

用int 13h讀取硬盤驅動器寫入扇區

[英]Read a write a sector from hard drive with int 13h

我有一個簡單的程序。 它必須從硬盤驅動器(而不是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。

有點嗜屍; 希望你的裝配技能在此期間得到改善。 但為了以防萬一......

引用@Alexey Frunze,“你需要注意你正在做的事情”。 除了其他答案中詳述的錯誤之外,以下是我的一些觀察:


你的模擬器太善良了

  • 您的代碼似乎是一個引導加載程序。 您假設BIOS將您的代碼加載到0x0000:0x7C00 ,但您無法確定它實際上不會將其加載到0x07C0:0000或任何其他等效地址。 閱讀細分

  • 您無法初始化任何段寄存器。 您可能會因為您的仿真器很友好而正確使用它,並正確地將csdses初始化為0x0000

您可以像這樣解決這兩個問題:

[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


根本的誤解

  • 如果發生錯誤,您可以直接跳轉到字符串,而不是可執行代碼。 Lord只知道如果發生這種情況,計算機會做什么。

  • 檢查驅動器復位的返回值(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


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

您的引導加載程序不會設置堆棧。 我提供的代碼使用堆棧來防止寄存器丟棄。 在引導加載程序(<0x7C00)之前有大約30KiB可用,因此您可以在引導加載程序的開頭附近執行此操作:

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的函數。 當然,你會浪費代碼空間在那里推送數據,但是一旦它存在,你可以簡單地調整后續讀取的必要字段,而不是讓大量的DAP占用內存。 堆棧空間可以在以后回收。

不要灰心,匯編需要時間,並且對細節有着極大的關注......在工作時抨擊這些東西並不容易,所以我的代碼可能會出錯! :)

首先,您需要檢查cf而不是zf以查看BIOS調用是否成功。 糾正你的jnz error

其次,您似乎依賴於ds等於0.它不能保證為0.將其設置為0。

對於同上flags.df ,它不能保證為0它設置為0檢查的文件repmovs*cld

第三,你要求BIOS讀取扇區並將其寫入內存中的物理地址0。 通過這樣做,您將覆蓋中斷向量表(從那里開始並占用1KB)並損壞系統,需要重新啟動。 選擇一個更好的地址。 最好的是在內存中的bootsector結束之后。 但是您還需要確保堆棧不存在,因此您還需要將堆棧設置為已知位置。

你需要注意你正在做的事情。

最小的NASM BIOS示例,它使用代碼加載扇區並跳轉到它,而不進行錯誤檢查和DAP:

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上測試過。

在我的GitHub上使用鏈接描述文件和更正確的初始化(段寄存器,堆棧)的Saner GAS示例。

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

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM