主な問題は、ディスク上の間違った場所から読み取っていることです。ブート セクターの直後のディスクの 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を使用します(つまり、.driveNumber
mov driveNumber, dl
driveNumber
mov [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
retf
FAR 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
で起動すると、次のように表示されます。