8

私は簡単なプログラムを持っています。ハード ドライブ (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を使用しています。

4

4 に答える 4

25

少しネクロフィリア。それまでの間に、組み立てスキルが向上していることを願っています。しかし、念のため...

@Alexey Frunze の言葉を引用すると、「自分がしていることに注意を払う必要があります」。他の回答で詳述されている間違いに加えて、ここにいくつかの私の観察があります。


エミュレーターが優しすぎる

  • あなたのコードはブートローダーのようです。BIOS が でコードをロードすると仮定しますが、実際に でロードしないか、または他の同等のアドレス0x0000:0x7C00でロードしないかどうかはわかりません。セグメンテーション0x07C0:0000を読んでください。

  • セグメント レジスタの初期化に失敗しました。あなたのエミュレータは親切で、 、 、および を正しく初期化するので、おそらくそれでcsうまくdsいきesます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

根本的な誤解

  • エラーが発生した場合は、実行可能コードではなく、文字列に直接ジャンプします。主は、それが起こった場合にコンピューターが何をするかを知っているだけです。

  • ドライブのリセットの戻り値 (CF) を確認しますが、読み取り自体は確認しません。読み取りに失敗した場合は、ドライブをリセットして、読み取りを再試行する必要があります。ドライブがしゃっくりしている場合に備えて、これをループで数回 (たとえば 3 回) 実行します。ドライブのリセットに失敗した場合は、もっと深刻な問題が発生している可能性が高いため、問題を解決する必要があります。


より安全なアプローチ

を使用することをお勧めしint 0x13, ah = 0x02ます。すべてのシステムでサポートされていない可能性のある拡張 BIOS 機能を使用しています (最近の一部のハードウェアに見られる遅延 BIOS 実装は言うまでもなく、エミュレーターのサポートは不安定な場合があります)。あなたはリアルモードです - 特別なことをする必要はありません。ディスク I/O を処理する PM ドライバーを作成するという長期的な目標を持って、保護モードに入ることをお勧めします。

リアル モードを使用している限り、単純な BIOS 機能を使用してディスクから 1 つまたは複数のセクターを読み取るスタンドアロン機能があります。必要なセクターが事前にわからない場合は、マルチトラック読み取りを処理するために追加のチェックを追加する必要があります。

; 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 がメモリを占有するのではなく、後続の読み取りに必要なフィールドを調整するだけで済みます。スタック領域は後で再利用できます。

がっかりしないでください、組み立てには時間がかかり、細部への途方もない注意が必要です...仕事でこれを打ち負かすのは簡単ではないので、私のコードにエラーがあるかもしれません! :)

于 2014-07-23T10:03:50.553 に答える
8

まず、BIOS 呼び出しが成功したかどうかcfを確認する必要があります。zfを修正しますjnz error

次に、0 に等しいことに依存しているようですds。0 であるとは限りません。0 に設定してください。

についてもflags.df同様で、0 であるとは限りません。0 に設定してください。 repmovs*およびのドキュメントを確認してくださいcld

3 番目に、BIOS にセクターを読み取り、メモリ内の物理アドレス 0 に書き込むように指示します。そうすることで、割り込みベクトル テーブル (そこから開始され、1KB を占める) が上書きされ、システムが損傷し、再起動が必要になります。より適切な住所を選択してください。最適なのは、メモリ内のブートセクタの終了直後です。ただし、スタックがそこにないことも確認する必要があるため、スタックを既知の場所に設定する必要もあります。

あなたがしていることに注意を払う必要があります。

于 2013-03-19T11:37:52.907 に答える