とりあえず使ってみる
OpenDOS については、ソースを追い掛けるのを止めて、とりあえず、使ってみる事にした。
この場合、正確には、DR-DOSって事になるのかもしれないが、Ver 7.03 を選択。
QEMU 0.13.0 上に、20M のHDDイメージを用意して、DR-DOS をインストール。
割り当てメモリは、4M。(でも十分過ぎる。笑)
試行錯誤の末、なんとか、インストール終了。
さて、ここから、HDD イメージ上の MBR を覗いてみる。
80 01 01 00 04 0F 3F 26 3F 00 00 00 51 99 00 00
いきなり、パーティションテーブル。
それも、1〜3は、空で、4番目のパーティションの中身。
順番に、
80: ブート可
01 01 00:パーティションの最初のセクタ C=0,H=1,S=1
04: パーティション識別子 → FAT16(32MB以下)
0F 3F 26:パーティションの最後のセクタ C=26,H=0F,S=3F
3F 00 00 00:パーティションの最初のセクタ LBA=3F(本当?)
51 99 00 00:パーティションの全セクタ数 39249 → 19MiBくらい?
=27h*10h*3Fh - 3F(1トラック分)
seg000:0000 cli seg000:0001 sub ax, ax seg000:0003 mov ss, ax seg000:0005 mov ax, 7C00h seg000:0008 mov sp, ax seg000:000A sti seg000:000B cld seg000:000C sub ax, ax seg000:000E mov ds, ax seg000:0010 mov si, 7C00h seg000:0013 mov ax, 60h ; '`' seg000:0016 mov es, ax seg000:0018 assume es:nothing seg000:0018 sub di, di seg000:001A mov cx, 100h seg000:001D rep movsw seg000:001F jmp far ptr 60h:24h
こっちは、MBR の ブートストラップローダの先頭部分。
自分自身を、0060:0000にコピーしてから、次の命令があるアドレスへ far ジャンプしています。
seg000:0024 mov ax, cs seg000:0026 mov ds, ax seg000:0028 assume ds:nothing seg000:0028 mov cx, 4 seg000:002B mov di, 1BEh seg000:002E seg000:002E loc_7C02E: ; CODE XREF: seg000:003D^Yj seg000:002E mov dx, [di] seg000:0030 cmp dl, 80h ; '€' seg000:0033 jz short loc_7C041 seg000:0035 cmp dl, 0 seg000:0038 jnz short loc_7C08A seg000:003A add di, 10h seg000:003D loop loc_7C02E seg000:003F int 18h ; TRANSFER TO ROM BASIC seg000:003F ; causes transfer to ROM-based BASIC (IBM-PC) seg000:003F ; often reboots a compatible; often has no effect at all
DSレジスタをジャンプ先の0060に設定。
CXレジスタをカウンタとして以降の処理を4回繰り返す。DIレジスタの初期値は1BEh。
DIレジスタがポイントするアドレスの内容をDXレジスタに転送。
DLレジスタと即値:80hとを比較。
一致すれば、loc_7C041へジャンプ
DLレジスタと即値:00hとを比較。
一致しなければ、loc_7C08Aへジャンプ
DIレジスタに即値:10hを加算
繰り返し
ここでは、パーティションテーブルのフラグをチェックしています。
80はブート可、00はブート不可、それ以外は不正なコードとみなしているようです。
int 18h は、ROM-BASIC 呼び出しですが、最近のPCでは、何も無しですかね?
seg000:008A loc_7C08A: ; CODE XREF: seg000:0038^Xj seg000:008A ; seg000:004C^Xj ... seg000:008A mov si, 9Dh ; '・ seg000:008D seg000:008D loc_7C08D: ; CODE XREF: seg000:0075^Xj seg000:008D ; seg000:0088^Xj ... seg000:008D lodsb seg000:008E cmp al, 24h ; '$' seg000:0090 seg000:0090 loc_7C090: ; CODE XREF: seg000:loc_7C090^Yj seg000:0090 jz short loc_7C090 seg000:0092 push si seg000:0093 mov bx, 7 seg000:0096 mov ah, 0Eh seg000:0098 int 10h ; - VIDEO - WRITE CHARACTER AND ADVANCE CURSOR (TTY WRITE) seg000:0098 ; AL = character, BH = display page (alpha modes) seg000:0098 ; BL = foreground color (graphics modes) seg000:009A pop si seg000:009B jmp short loc_7C08D
とりあえず、不正なコードに遭遇した時の処理。
SIレジスタに、即値:9Dhを転送。
9Dhには、エラーメッセージが配置されています。
seg000:009D aInvalidPartiti db 'Invalid partition table$'
lodsb は、
MOV AL, [SI] INC SI ; DF=0の場合
1バイトのコードで、上記と同等の処理を行ってくれるのは便利なんですが、
これが、Intelマジックと言うか、混乱の元になっているような気もするなぁ...
'$'は、DOS形式の文字列終端子です。
もし、ALにセットされた文字が、'$'であれば、ここで無限ループします。
表示すべき文字であれば、BIOSサービスを利用して、一文字ずつ表示すると。
BIOSサービスをコールする前に、SIレジスタを退避しているけど、
int 10h って、SI レジスタの内容を破壊しちゃうのかな?
seg000:0041 loc_7C041: ; CODE XREF: seg000:0033^Xj seg000:0041 mov si, di seg000:0043 jmp short loc_7C052 seg000:0045 ; トトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトトト seg000:0045 seg000:0045 loc_7C045: ; CODE XREF: seg000:loc_7C052^Yj seg000:0045 add di, 10h seg000:0048 mov ax, [di] seg000:004A cmp al, 80h ; '€' seg000:004C jz short loc_7C08A seg000:004E cmp al, 0 seg000:0050 jnz short loc_7C08A seg000:0052 seg000:0052 loc_7C052: ; CODE XREF: seg000:0043^Xj seg000:0052 loop loc_7C045
最初にブート可のパーティションに遭遇した後の処理。
DIレジスタの内容をSIレジスタに保存してloc_7C052へジャンプ。
まだ、繰り返し処理は続いている。(アセンブラ的な流れやな〜)
こっちの繰り返しでは、80hのパーティションが見付かったら不正とするようですね。
ブート可のパーティションが複数あったらダメって事ですな。
ブート可のパーティションが見付かったら、さっさと起動すれば良いように思うのですが、
世の中、そんなに、甘いもんじゃない... ってわけでもないだろうに。
seg000:0054 mov cx, 5 seg000:0057 seg000:0057 loc_7C057: ; CODE XREF: seg000:0070^Yj seg000:0057 mov ax, 0 seg000:005A mov es, ax seg000:005C assume es:nothing seg000:005C mov bx, 7C00h seg000:005F mov ah, 2 seg000:0061 mov al, 1 seg000:0063 push cx seg000:0064 mov cx, [si+2] seg000:0067 int 13h ; DISK - READ SECTORS INTO MEMORY seg000:0067 ; AL = number of sectors to read, CH = track, CL = sector seg000:0067 ; DH = head, DL = drive, ES:BX -> buffer to fill seg000:0067 ; Return: CF set on error, AH = status, AL = number of sectors read seg000:0069 pop cx seg000:006A jnb short loc_7C077 seg000:006C mov ah, 0 seg000:006E int 13h ; DISK - RESET DISK SYSTEM seg000:006E ; DL = drive (if bit 7 is set both hard disks and floppy disks reset) seg000:0070 loop loc_7C057 seg000:0072 mov si, 0C9h ; 'ノ' seg000:0075 jmp short loc_7C08D
こちらは、正常系の処理。
CXレジスタに5を設定しているのは、リトライ回数だと思う。
ES:BXは、0000:7C00に設定。
DXは、既に設定済み、CXの内容は、SI+2から転送。
なるほど...
パーティションテーブルの先頭4バイトは、そのまま、BIOSサービスのDX,CXの内容になるわけですな。
カウンタとして使用しているCXレジスタの内容は、当然、
BIOSサービスの呼び出し前後で、PUSH,POP されると。
BIOSサービスの呼び出し後、JNBとなっているけど、これは、JNCだと思う。
OPコードは同じなので、逆アセンブラには、どっちだか分からないわけですね。
キャリーフラグがONの場合は、何かしらエラーが発生したので、
ディスクをリセットして、再度、読み込みを行うと。
リトライ回数分繰り返したら、SIレジスタに即値:C9hを転送。
seg000:00C9 aOperatingSyste db 'Operating System load error$'
seg000:0077 loc_7C077: ; CODE XREF: seg000:006A^Xj seg000:0077 cmp word ptr es:[bx+1FEh], 0AA55h seg000:007E jnz short loc_7C085 seg000:0080 jmp far ptr 0:7C00h
読み込みが正常終了したら、読み込んだ内容のブートシグネチャをチェック。
一致しなければ、loc_7C085へジャンプ。
一致した場合は、読み込んだ内容(PBR)の先頭へ、far ジャンプする。
seg000:0085 loc_7C085: ; CODE XREF: seg000:007E^Xj seg000:0085 mov si, 0B5h ; 'オ' seg000:0088 jmp short loc_7C08D
seg000:00B5 aNoOperatingSys db 'No Operating System$'を表示。
MBRについては、多少の差はあっても、同じような処理になるはず。