もしかしたら!!

どうにも、こうにも、怪しいと思った箇所をいくら眺めてみても、原因が分からない。
こういう時に相談できる相手がいたら、どんなに心強い事か...
そんな事を考えたりもしたけど、相談できる相手が都合良く現れる訳もなく...
とにかく一人で悩み続けるしかないし、悩んだ分だけ自分の力になると信じて、
折れかけている気持ちを、なんとか奮い立たせる。おっしゃ〜!!!!!!


と言うわけで、前置きはこのぐらいにして... (^^;;;
いや、実際、途方に暮れてたんですけど、
インテルの資料やインターネットで関連しそうな情報を調べ倒しているうちに、
ふと、ある事に気がつきました。
セグメント違ってね?


これまで CS=DS=SS=ES=0 を前提にしてきたんだけれど、
正確に言うと、CS=0 は、暗黙の前提であって、
明確に初期化を実行しているのは、DS=SS=ES=0 のみ。
何故かと言うと、CSレジスタって、勝手に変更出来ない訳ですよ。
MOV CS, xx とか、一応、エンコードは可能なんですけど、実行すると即例外です。
CSレジスタの変更は、far JMP, far CALL, RETF によって行われると。


さて、じゃぁ、誰がCSレジスタの値を設定しているかと言うと...
現状では、BIOSと言う事になります。
BIOSの仕様では、ブートデバイスの先頭512バイト分を 0000:7C00 以降にロードし、
その後、同アドレスに far JMP する事により、制御を渡すという事になっています。
ここで、JMP 0000:7C00 であれば、CS=0000, IP=7C00 となります。
基本、これが前提となっています。
ところが、仕様的には、実効アドレス 07C00(20bit) であれば良いらしいので、
JMP 07C0:0000 でも、仕様的には、何ら問題がなかったりする訳です!!(マヂですか!!)
もし、QEMUが前者で、実機が後者であれば、CSレジスタの値が違うわけですから、
IPに同じ値を突っ込めば、ジャンプ先が違ってしまうわけですね。
じゃ、何故、今までうまく動いていたかと言うと、全部相対ジャンプだったからです!!
ほら、辻褄は合うでしょう?


それでは、確認してみましょう。
とりあえず、システム本体の起動直後に、CSレジスタとIPレジスタの内容を表示してみる。

		; CS:IP をチェック
		MOV		AX, CS
		CALL	puthex
		CALL	putcrlf
		CALL	putip
		CALL	putcrlf

IPレジスタについては、直接的に取り出す方法がないので、
CALLを利用して、サブルーチン側でスタックから取り出します。

putip:
		MOV		BP, SP
		MOV		AX, [BP]
		CALL	puthex
		RET

こんな感じです。
本当は、AXレジスタやBPレジスタの内容を、PUSH&POPして保護してあげたいけど、
今回に関しては、とりあえずの対応なので、ひとまず気にしない。


それでは、検証してみましょう。
結果(QEMU) → 0000:8026
結果(実機) → 07C0:0426
ビンゴです!!
実機で、この事実を確認した瞬間、ガッツポーズですよ!!(笑)


さて、原因が分かったところで、どう対応するかですが、
現在、ROSe の IPLは、単純に2セクタ以降の数セクタ分を読み込み、
0x8000 に near ジャンプしています。
この辺は、はりぼてOSを参考にしたんですが、
この箇所を far ジャンプに変更すれば、とりあえずは動くんじゃないかな?
と言う訳で...

system_start:
		; システム開始
		JMP		0x8000

1stloader の上記の箇所を、

system_start:
		; システム開始
		JMP		0x0000:0x8000

とっとと、far ジャンプに書き換えてみます。
結果 → 大成功!!!!!!!!!!


すっきりです!!


ところで、セグメントが[07C0]の時に、どこへジャンプしていたのか、
そして、それが、何故、再起動に繋がったのか、ちょっと気になる。
そこで、ジャンプ先のアドレスを計算してみた。
07C0:829D → 07C00 + 829D = 0FE9D
該当アドレスの中身を確認すると、見事に[00]が埋まっている。
Z80とかの時代だと、00=NOP ってイメージがあるんだけど、
x86だと、00 00 で、ADD [BX+SI], AL になる。
って事は、延々と加算を繰り返して、セグメントの境界まで行ったのかな?
仮にそうだとして、オフセットがラップアラウンドして[0000]に戻れば、
IPLの先頭アドレスに戻る事になる。
その手前で、何かしらの例外が発生したとしても、
ベクタテーブルを眺めた限り、ほとんどが、CF = IRET なんで、
何もしないで戻ってくる可能性が高そう。
後で、セグメントの末尾近辺にコードを置いて、先頭に戻るか、検証してみようか。
とりあえず、今回は、恐らく末尾まで行って先頭に戻った、とか思い込んでおく。(^^;;;