2

マルチステージ ブートローダーを構築しようとしていますが、第 2 ステージをメモリに読み込むことになっている第 1 ステージ コードでスタックしています。このコードはint 13h、仮想フロッピー ディスク (.img ファイル) からセクターを読み取るために使用します。コードは次のとおりです(MASM構文):

        .286
        .model tiny
        .data
    org 07c00h
    driveNumber db ?
        .code
main:  jmp short start
       nop
start: mov driveNumber,dl  ;storing booting drive number
       cli
       mov ax,cs
       mov ds,ax
       mov es,ax
       mov ss,ax
       sti
reset: mov ah,0h              ;resetting the drive to the first sector
       mov dl,driveNumber
       int 13h
       js reset
read:  mov ax,1000h           ;reading sectors into memory address 0x1000:0
       mov es,ax
       xor bx,bx
       mov ah,02h
       mov al,01h             ;reading 1 sector
       mov ch,01h             ;form cylinder #1
       mov cl,02h             ;starting from sector #2
       mov dh,01h             ;using head #1
       mov dl,driveNumber     ;on booting drive
       int 13h
       jc read

       push 1000h             ;pushing the memory address into stack
       push 0h                ;pushing the offset
       retf

end main

このコードは、最後の 2 バイトの 0x55AA 署名とともに仮想ディスクの最初のセクターに置かれ、第 2 段階のコードは次のセクターに置かれます。

そして、私がここにいるので、うまくいきませんでした!

私は vmware と bochs の両方で試しましたが、どちらも同じ結果になりました:何もありません!

だから私はいくつかのテストを実行しました:

  1. 問題は、シリンダー、ヘッド、およびセクターのインデックス付け方法にあるのではないかと思いました。そこで、シリンダー、ヘッド、およびセクター番号のさまざまな組み合わせを試しましたが、うまくいきませんでした。
  2. の戻り値を確認したint 13hところ、ステータス コード ( ah==00h -> 成功)、実際のセクター読み取りカウント ( al= 01h -> 1 セクターが実際に読み取られた) が得られました。
  3. 読み取りプロセスの前に、何らかの値を入力してes:bxから読み取りプロセスを実行しました。終了後、で値を確認したes:bxところ、フォームを読み取る必要がある値ではなく、以前に入力した値のままであることがわかりましたセクター。

したがって、セクターが実際に読み取られたことを示すテストと、メモリに何も読み取られていないことを示すテストがあります...したがって、行き詰まっています!

何かご意見は?

4

2 に答える 2

4

主な問題は、ディスク上の間違った場所から読み取っていることです。ブート セクターの直後のディスクの 2 番目のセクターから開始する 2 番目のステージを配置すると、シリンダー/ヘッド/セクター (CHS) = (0,0,2) になります。ブート セクタは (0,0,1) です。セクター番号は 1 から始まり、シリンダーとヘッドの番号付けは 0 から始まります。

その他の潜在的な問題は次のとおりです (その多くは、私の一般的なブートローダーのヒントで見つけることができます):

  • コードは、 CSが 0000h に設定されているという事実に依存しています (7c00h の ORG を使用しているため)。DS=ES=SS=CSを設定します。DLのドライブ番号を除いて、セグメント レジスタまたは汎用レジスタの状態を仮定しないでください。DSのようなセグメント レジスタを 0000h に設定する必要がある場合は、 0 に設定します。

  • DSセグメントを設定する前に、 DLのドライブ番号をメモリアドレスに書き込みます。ブート ドライブを 1 つのセグメントに書き込み、後で間違ったセグメントから読み取ることができます。DLをメモリに保存する必要がある場合は、 DSセグメントを設定した後に行ってください。参照時に暗黙的にDSを使用します(つまり、.driveNumbermov driveNumber, dldriveNumbermov [ds:driveNumber], dl

  • コードで実際にSPを設定することはありません。SSのみを更新します。SPがどこを指しているのか誰にもわかりません。SS:SPの組み合わせによって、現在のスタック ポインタが決まります。SS:SPを 0000h:7c00h に設定することで、スタックがブートローダーの下に成長するように設定できます。これは、stage2 の 1000h:0000h へのロードを妨げません。

  • セグメント レジスタの更新の周りに CLI/STI を配置する必要はありません。アトミックでなければならない 1 つの場所は、 SS:SPを更新するときです。SSに書き込むと、CPU はの命令が実行されるまで割り込みを無効にします。SSの直後にSPを更新すると、アトミック操作と見なすことができ、 CLI / STIは必要ありません。これは、1980 年代初頭に製造された一部の欠陥のある 8088 を除いて、ほぼすべてのプロセッサに当てはまります。そのようなシステムで起動する可能性がある場合は、SS:SPを更新するコードの周りにCLI / STIを配置することを検討してください。

  • js resetディスクのリセットを試みた後です。jcサインフラグではなく、キャリーフラグ(CF)をチェックするために使用するつもりだったと思います。通常、リセットの失敗を確認する必要はありません。リセットを実行してから、ドライブ アクセス コマンド (つまり、Disk Read) を再発行し、そこでドライブ エラーをキャプチャします。実際のハードウェアでは、通常、あきらめて中止する前に操作を 3 回再試行します。

  • .286このコードがコンパイルされるように、命令セットを有効にしたようです。

    push 1000h             ; pushing the memory address into stack
    push 0h                ; pushing the offset
    retf
    

    retfFAR JMP に相当するものを実行していましたが、MASM の初期のバージョンでは JMP 構文がサポートされていませんでした。.186あなたのコードは正しいですが、Intel 8088/8086 プロセッサはエンコーディングをサポートしていないため、少なくともディレクティブPUSH imm8が必要ですPUSH imm16。これは80186 で追加されました。コードを 8088/8086 で実行したい場合は、次のようにすることができます。

    ; Version that uses a FAR RET to do same as FAR JMP that works on 8086
    mov ax, 1000h              ; On 8086 push imm16 doesn't exist
    push ax                    ; Push the code segment (1000h) to execute
    xor ax, ax                 ; Zero AX
    push ax                    ; Push the offset (0) of code to execute
    retf                       ; MASM may not understand a FAR JMP, do RETF instead
    

    その解決策は機能しますが、かなり長いエンコーディングです。次のコードを使用して、 FAR JMP (オペコード 0EAh) を手動で発行できます。

    ; Early versions of MASM don't support FAR JMP syntax like 'jmp 1000h:0000h'
    ; Manually encode the FAR JMP instruction
    db 0eah                    ; 0EAh is opcode for a FAR JMP
    dw 0000h, 1000h            ; 0000h = offset, 1000h segment of the FAR JMP
    
  • 0aa55hすべてのコードとデータを.codeセグメントORGに配置し、ブート署名のパディングと配置を行うために使用することで、ブート署名を発行し、ブート コードを 512 バイトにパディングできます。


上記の問題を修正するには、コードを次のようにします。

.8086
.model tiny

.code
org 7c00h

main PROC
    jmp short start
    nop

start:
    xor ax, ax
    mov ds, ax                 ; DS=0
    cli                        ; Only need STI/CLI around SS:SP change on buggy 8088
    mov ss, ax                 ; SS:SP = 0000h:7c00h grow down from beneath bootloader
    mov sp, 7c00h
    sti
    mov driveNumber, dl        ; Storing booting drive number
    jmp read                   ; Jump to reading (don't need reset first time)

reset:
    mov ah, 0h                 ; Reset the drive before retrying operation
    mov dl, driveNumber
    int 13h

read:
    mov ax, 1000h              ; Reading sectors into memory address 0x1000:0
    mov es, ax
    xor bx, bx
    mov ah, 02h
    mov al, 01h                ; Reading 1 sector
    mov ch, 00h                ; Form cylinder #0
    mov cl, 02h                ; Dtarting from sector #2
    mov dh, 00h                ; Using head #0
    mov dl, driveNumber        ; On boot drive
    int 13h
    jc reset

    ; Early versions of MASM don't support FAR JMP syntax like 'jmp 1000h:0000h'
    ; Manually encode the FAR JMP instruction
    db 0eah                    ; 0EAh is opcode for a FAR JMP
    dw 0000h, 1000h            ; 0000h = offset, 1000h segment of the FAR JMP

; Error - end with HLT loop or you could use 'jmp $' as an infinite loop
error:
    cli
endloop:
    hlt
    jmp endloop

main ENDP

; Boot sector data between code and boot signature.
; Don't put in data section as the linker will place that section after boot sig
driveNumber db ?

org 7c00h+510                  ; Pad out boot sector up to the boot sig
dw 0aa55h                      ; Add boot signature

END main

その他の観察

  • Int 13h/AH=2 (読み取り) およびInt 13h/AH=0 (リセット) は、AX レジスタ ( AH/AL ) のみを消去します。ディスク障害後に別の読み取りを行うためにすべてのパラメータを設定する必要はありません。

  • 前述のように、ディスク操作を 3 回再試行することは、実際のハードウェアでは一般的でした。SIはディスクの読み取りおよび BIOS のリセット呼び出しでは使用されないため、ディスク操作の再試行回数としてSIを使用できます。

  • 以下から始める必要はありません。

    main:  jmp short start
           nop
    start:
    

    ボリューム ブート レコード (VBR) として使用するために BIOS パラメータ ブロック (BPB) を挿入する場合を除きます。Floppy Drive Emulation (FDD) を使用して USB デバイスで起動する場合、実際のハードウェアでBPB を使用することをお勧めします。

  • 次のように、16 ビット レジスタの上位および下位 8 ビット レジスタを更新する場合:

    mov ah,02h
    mov al,01h
    

    次のようにして、それらを 1 つの命令に組み合わせることができます。

    mov ax, 0201h
    

追加の観察で特定されたものを実装すると、コードは次のようになります。

boot.asm :

DISK_RETRIES EQU 3

.8086
.model tiny

IFDEF WITH_BPB
    include bpb.inc
ENDIF

.code
org 7c00h

main PROC

IFDEF WITH_BPB
    jmp short start
    nop
    bpb bpb_s<>
ENDIF

start:
    xor ax, ax
    mov ds, ax                 ; DS=0
;   cli                        ; Only need STI/CLI around SS:SP change on buggy 8088
    mov ss, ax                 ; SS:SP = 0000h:7c00h
    mov sp, 7c00h
;   sti

    mov ax, 1000h              ; Reading sectors into memory address (ES:BX) 1000h:0000h
    mov es, ax                 ; ES=1000h
    xor bx, bx                 ; BX=0000h
    mov cx, 0002               ; From cylinder #0
                               ; Starting from sector #2
    mov dh, 00h                ; Using head #0
    mov si, DISK_RETRIES+1     ; Retry count
    jmp read                   ; Jump to reading (don't need reset first time)

reset:
    dec si                     ; Decrement retry count
    jz error                   ; If zero we reached the retry limit, goto error
    mov ah, 0h                 ; If not, reset the drive before retrying operation
    int 13h

read:
    mov ax, 0201h              ; BIOS disk read function
                               ; Reading 1 sector
    int 13h                    ; BIOS disk read call
                               ;     This call only clobbers AX
    jc reset                   ; If error reset drive and try again

    ; Early versions of MASM don't support FAR JMP syntax like 'jmp 1000h:0000h'
    ; Manually encode the FAR JMP instruction
    db 0eah                    ; 0EAh is opcode for a FAR JMP
    dw 0000h, 1000h            ; 0000h = offset, 1000h segment of the FAR JMP

; Error - end with HLT loop or you could use 'jmp $' as an infinite loop
error:
    cli
endloop:
    hlt
    jmp endloop

main ENDP

; Boot sector data between code and boot signature.
; Don't put in data section as the linker will place that section after boot sig

org 7c00h+510                  ; Pad out boot sector up to the boot sig
dw 0aa55h                      ; Add boot signature

END main

株式会社ビーピービー

bpb_s STRUCT
    ; Dos 4.0 EBPB 1.44MB floppy
    OEMname            db    "mkfs.fat"  ; mkfs.fat is what OEMname mkdosfs uses
    bytesPerSector     dw    512
    sectPerCluster     db    1
    reservedSectors    dw    1
    numFAT             db    2
    numRootDirEntries  dw    224
    numSectors         dw    2880
    mediaType          db    0f0h
    numFATsectors      dw    9
    sectorsPerTrack    dw    18
    numHeads           dw    2
    numHiddenSectors   dd    0
    numSectorsHuge     dd    0
    driveNum           db    0
    reserved           db    0
    signature          db    29h
    volumeID           dd    2d7e5a1ah
    volumeLabel        db    "NO NAME    "
    fileSysType        db    "FAT12   "
bpb_s ENDS

実行時に文字列を表示するstage2.asmの例:

.8086
.model tiny
.data
msg_str db "Running stage2 code...", 0

.code
org 0000h

main PROC
    mov ax, cs
    mov ds, ax
    mov es, ax
    cld

    mov si, offset msg_str
    call print_string

    ; End with a HLT loop
    cli
endloop:
    hlt
    jmp endloop
main ENDP

; Function: print_string
;           Display a string to the console on display page 0
;
; Inputs:   SI = Offset of address to print
; Clobbers: AX, BX, SI

print_string PROC
    mov ah, 0eh                ; BIOS tty Print
    xor bx, bx                 ; Set display page to 0 (BL)
    jmp getch
chloop:
    int 10h                    ; print character
getch:
    lodsb                      ; Get character from string
    test al,al                 ; Have we reached end of string?
    jnz chloop                 ;     if not process next character

    ret
print_string ENDP

END main

MASM32 SDKの ML.EXE および LINK16.EXE を使用している場合、コードをアセンブルしてリンクし、ディスク イメージを作成するには、次のコマンドを使用できます。

ml.exe /Fe boot.bin /Bl link16.exe boot.asm
ml.exe /Fe stage2.bin /Bl link16.exe stage2.asm
copy /b boot.bin+stage2.bin disk.img

BPB を含めたい場合は、次の方法でアセンブルしてリンクできます。

ml.exe /DWITH_BPB /Fe boot.bin /Bl link16.exe boot.asm
ml.exe /Fe stage2.bin /Bl link16.exe stage2.asm
copy /b boot.bin+stage2.bin disk.img

どちらの方法でも、 というディスク イメージが作成されdisk.imgます。をBOCHSdisk.imgで起動すると、次のように表示されます。

ここに画像の説明を入力

于 2019-08-27T00:16:00.750 に答える