Linux勉強 パート6 GRUB後半 コードを別のメモリアドレスへ移し替える続き

Linux

copy_bufferルーチンでメモリを移し替える

ソースコード

ここを参照しながら読んでみてください。
GRUB前半コード
ソースコード
MBRに読み出されるファイルはdiskboot.Sというファイルです。
2セクター目に配置されています。

copy_buffer

続きから。
このルーチンでメモリを移し替えます。

LOCAL(copy_buffer):
movw	10(%di), %es
popw	%ax
shlw	$5, %ax	
addw	%ax, 10(%di)
pusha
pushw	%ds
shlw	$3, %ax
movw	%ax, %cx
xorw	%di, %di
xorw	%si, %si
movw	%bx, %ds
cld
rep
movsw
popw	%ds
MSG(notification_step)
popa
cmpw	$0, 8(%di)
jne	LOCAL(setup_sectors)
subw	$GRUB_BOOT_MACHINE_LIST_SIZE, %di
jmp	LOCAL(bootloop)

movw 10(%di), %esでコピーするセグメントアドレスを設定、popw %axで退避しておいた読み込みセクター数を復帰。
ディスクから読み込んだデータの単位セクター=(512バイト)で表現しますが、メモリはバイトで表現されます。
1セクターを読み込んだので、次のループでは読み込んだ分変換してアドレスを進めなければなりません。

セグメントベースの復習

shlw $5, %ax
shlw命令はオペランドをシフトさせる命令です。
axの値を5桁シフトします。

これを説明する前に復習。
セグメントベース+オフセットで任意のアドレスを表現すると説明しました。
ここから新しい知識なんですが、セグメント方式ではアドレス変換回路というハードウェアを通してアドレスを認識します。
その時自動的にセグメントベースを16進数で1桁、2進数なら4桁シフトされています。
なのでセグメントベースを1増やすとさらに横にシフトされるので16倍されます。

掛け算とシフト演算は同じ

512=16*32です。
16バイトはは勝手にシフトされますので、5ビットシフトさせれば512になります。
これで1セクターをアドレスに変換できました。
addw %ax, 10(%di)命令でその変換したaxの値を現在のアドレスに加算して次の読み込みのためのアドレスを作成します。

shlw $3, %ax
movw %ax, %cx
先程5ビットシフトしたaxをさらに3ビットシフトします。
つまりセクター数を合計で256倍しています。
そしてその値をcxにコピーします。
CXにコピーする理由は次のコードで分かります。

xorw %di, %di
xorw %si, %si
movw %bx, %ds
まずレジスターをクリア。
ここで理由がわかります。

cld
rep
movsw
repはcxの値が0になるまで次の命令をループします。
movswはDS:DIの値をES:DIにコピーする命令です。
転送したあとはEFLAGSレジスタのDFフラグの値に従ってインクリメント、デクリメントします。
要するにメモリの下に向かってコピーするか、上に向かってコピーするかはEFLAGSで指定するということです。
この場合は上にコピーしていきます。

3ビットシフトした理由を説明します。
メモリは1バイトづつ増えていきます。
なので1セクター読み込んでコピーする場合DS:SIの値をES:DIにデーターを転送したあとメモリ番地をインクリメントする。
この処理を256回繰り返します。
axは既に5回(16ビットはアドレス変換回路でシフトされるので現状は5回)シフトされているので、加えて3回シフトするだけでOKです。

つまりセクター数を合計で8ビットシフト(256)倍すればループ回数ということです。
説明が難しい…

残りのセクターをチェック

popw %ds
MSG(notification_step)
popa

MSG(notification_step)は画面に文字を表示し、popw %dsとpopa命令で次に読み込むセクタのデータをスタックから取り出します。

cmpw $0, 8(%di)
jne LOCAL(setup_sectors)
subw $GRUB_BOOT_MACHINE_LIST_SIZE,%di
jmp LOCAL(bootloop)

diの値が0でない(読み込むセクタがまだある場合)時setup_sectorsルーチンに戻って次のセクタを読み込む処理を準備し次のセクタを読み込みます。
セクタをすべて読み込むまでbootloopを繰り返し、読み込んだ時点でループから抜けます。

subw $GRUB_BOOT_MACHINE_LIST_SIZE,%di

この命令はは読み込むセクタが保存されているfirstlistの値をデクリメントします。
update position to load fromとあるので別のセクターからデータを読み出すために読み込みセクタの位置を更新するものみたいです。

全てのセクターの読み込みを完了したらbootitルーチンにジャンプします。

プログラミングおすすめ書籍

おすすめ書籍

なぜプログラムが動くのか。
平易な言葉で学べますが、初心者には難しいかもしれません。
でも読み終わった頃にはなんとなく理解しているでしょう。
初心者には家にあって損はありません。

UNIXの思想を学べます。
高尚なタイトルですが、コードはおろか難しい言葉も殆どありません。取っ掛かりに最適でその後のプログラミングに対する理解を一変させます。
なぜならこの思想はプログラミングそのものだからです。
一読の価値ありで、これは良書なので今後紹介したいと思っています。

まだまだありますがまたの機会に紹介したいと思います。

Linux コンピューター
スポンサーリンク
himazinをフォローする
私の頭の上の消しゴム

コメント

タイトルとURLをコピーしました