PC-9821ハードディスクIPL組み込みプログラム作成支援ソフト
IPLware 第3.93版 プログラム組み込み仕様説明書
                Copyright(C) 2001-2005,2019-2023 まりも

操作説明書のほうを読む
ローダ部のソースプログラムをダウンロードする

【1】プログラム開発の目的 

 CPUや周辺デバイスの設定は、必ずしも本体のBIOSによって適切あるいは最適に行なわれるわけではありません。とくに、本体が知らないような新しいハードウェアを入れた場合には、何も処理されません。Windowsなどの新しいOSでは、こうしたデバイスをドライバ自身が設定するので問題ありませんが、CPUの設定などは何も行なわれない場合がありますし、PCIデバイスについては、前もってBIOSがきちんと設定していないと、使用できないことがあります。

 このような場合、諸設定はOS起動より前に行なっておかなくてはなりませんが、それが可能なのは、ハードディスク(などの起動デバイス)のIPLプログラムの実行の段階です。これまでにも、こうした目的のためにIPLに組み込む方式のソフトウェアはいくつも存在していました。

 しかし、アセンブリプログラミングは何とかできるけれども、ハードディスクのIPLに組み込むような形のものは作成できないという人がかなり多いのではないかと思います。また統一された組み込み仕様というのがないため、別々の作者が作ったソフトウェアを複数組み込むことは、一般的に難しく、場合によっては既存のプログラムを破壊しかねないという問題があります。

 IPLへの組み込みに関するAPI(アプリケーションインターフェイス)でも存在していれば簡単ですが、そういうものはありませんし、そもそもIPLのようなファイルシステム外の場所というのは、ユーザが勝手にいじることが推奨される場所ではありません。

 そうしたことから、独自に仕様を定めて、プログラムモジュールの組み込み・取り外しを行なうプログラム「IPLware」を作成しました。IPLwareとは、IPLware.exeそのものというよりは、組み込み方式の名称と考えて下さい。

 これにより、別々の作者が作った複数のIPL組み込みソフトを、競合することなく組み込むことができるようになります。今後IPL組み込みプログラムを作る方々に、大いに活用していただければと思います。また商用ソフトでそれを目指す場合にも、IPLwareの存在を考慮していただけたら幸いです。

   なお、「IPLへの組み込み」と述べていますが、正確には「固定ディスク
  起動メニュー」プログラムが走る直前の位置にパッチをあてています。
  コード自体は固定ディスク起動メニューの続き(余り部分)に格納します。

 IPLwareの特徴は、

などとなっています。

 なお、IPLware.exeは、NEC PC-9800シリーズ専用ソフトで、固定ディスク起動メニューのバージョンが 2.70 であり、他の仕様の組み込みソフトが既に存在していない場合に動作します。PC/ATでは全く使えません。PC-98を21世紀も使う人々のための支援ソフトです。


【2】IPLwareプログラムの使い方、組み込みに関する注意

 IPLware.html または IPLware2.txtを参照して下さい。ここでは述べません。

 なおIPLwareが使用できる条件は、
・PC-9801,9821シリーズであること
・CPUが80386以上であること(80286機に80386以上のCPUアクセラレータは可)
・インストールされている固定ディスク起動メニューのバージョンが2.70であること
・固定ディスクのBIOSセクタサイズが512バイトであること(256バイトは不可)
となっています。


【3】IPLwareとして組み込み可能なユーザプログラムモジュールの仕様

 8086のバイナリプログラムコードであれば何でもよいというわけではありません。IPLwareとして組み込み可能なのは、DOS上のアセンブラで、COMファイルイメージとして作られたプログラムに、後述の条件を満たしたものに限ります。IPLwareでは、任意の開始番地を持つプログラムではいけません。開始番地は 0100hでなければなりません。これはIPLwareのローダがロードする番地が、絶対番地で 6000:0100hからとなっているためです。ほとんどのCOMプログラムは0100から開始する構造ですので、特に不都合はないと思います。

● IPLwareのプログラムモジュール仕様

まず、十分動作チェックを行なってある(バグのない)DOSのCOMプログラムファイルを作成・用意しておきますが、下記のような仕様にしてください。

(1) プログラムの終了は   RETF (far return )であるか、RET(near return)であること。

通常のCOMファイル形式プログラムでは、MS-DOSの終了ファンクション(AH=4Ch)を 使うか、RET 命令でプログラムから抜けるはずです。しかしIPLware中では MS-DOSファンクションによる終了は使えません。そこで、ユーザプログラムから戻る際は 次のいずれかで戻る必要があります。
■RETF で戻る(従来からのモジュールタイプ)
以前からのIPLwareでの方式です。IPLwareのユーザプログラムの呼び出しは FAR CALLを用いているため、RETFで戻るようにしておけば、戻り番地などをユーザが考慮する必要はありません。
■RET で戻る(モジュールタイプ2)
RETでの終了は、通常のCOMファイル形式プログラムとして実行可能です。これをわざわざfar RETに書き換えないとIPLwareとしてのプログラムにできないのは不便であるということから、near RETでもよいことにしました。ただしファイル名が *.comの場合に限ってタイプ2と判断するようにしています。プログラムの内容では判断していません(できません)。

なお、プログラムの抜けだし口はいくつあってもよく、最後のバイトが実行されなければならないなどという制約はありません。要は、戻るときに、スタックの状態が、呼ばれたときと同じように積まれていることだけが条件です。終了の際のRETFあるいはRET実行時のスタックのネスト状態だけ注意しておけばOKです。
  

(2) プログラムの開始時点のレジスタ状態
  セグメントレジスタはCS=DS=ES=SS=6000hになっています。これにより、通常のCOMプログラムイメージのまま使用できます。スタック(SPレジスタ)は   64KBのほぼ最大限となっています。起動メニューに戻る際に全てのレジスタは   保存/退避されるので、ユーザプログラムモジュール内では、レジスタの内容を破壊したまま戻っても構いません。

汎用レジスタとSIレジスタについては、プログラム開始時で次のように定義されています。ただしこの機能が正常に動作するのは、ローダバージョン3.33からです。それ以前のバージョンでは汎用レジスタの値は不定となっています。
  
  
レジスタ 初期値の意味
AL 呼び出されたアプリケーションプログラムが存在する装置のDA/UA(最上位bitはゼロ)  
AH 呼び出されたアプリケーションプログラムのモジュールタイプビット  
DX:CX 呼び出されたアプリケーションプログラムが存在するディスクアドレス(32bit)
BX 呼び出されたアプリケーションプログラムのサイズ(セクタサイズ512の倍数)  
SI IPLwareのローダのバージョンで上位下位8bitごとのBCDコード   3.93版ならば 0393h
DI 必ず0 であり、そのとき SIの値は正しいことを示す。
BP CS,DS,ES,SSと同じ値 (現バージョンでは 6000h)

これらのレジスタ値を使うことで、IPLwareアプリケーションプログラムは、自分自身の存在するディスク上の位置を知ることができます。これにより、例えば起動時にプログラム自身の一部を書き換えてディスクに書き戻すということが可能になります。プログラム内にパラメータテーブルを置いて、そこを更新することで、次回の実行条件を変更する、ということに応用できます。AL,BX,DX:CXはそのまま INT 1Bhコールに使用できますので、あとはAHで読み書きなどのファンクションを指定すればよいようになっています。

なお、そのような仕様のプログラムを作る際には、SIレジスタの値でバージョンを調べ、0333h以上であることと、DIが0000hであることの両方を確認するようにして下さい。古いバージョンのIPLwareでは汎用レジスタの値が不定ですので、レジスタの値どおりにデータをディスクに書き込むと大変なことになります。


(3) DOSファンクションコールは使用できない

言うまでもありませんが、DOS起動前に実行されるプログラムですから、DOSやその他OSのファンクション,APIは使用できません。BIOSファンクションコールのみ使用可能です。画面に文字を書くにも、TEXT-VRAMへの直接描画が必要です。
(4) プログラムコード(バイナリファイル)の最大は32KBまで
組み込みプログラムIPLware.exeの都合により、ユーザプログラムのバイナリファイルの最大は32KBまでとしています。しかしユーザが作成する非初期化変数や、スタック変数は、64Kバイトの空間を使用できます。エントリポイントの 0100h番地より前も使おうと思えば使えます(org指定などで)。絶対番地も使用できますが、1060:0000以下のところは使用しないほうがよいと思われます。

 IPLへのパッチでは、汎用/インデクス/セグメント/フラグの全ての16bitレジスタを保持しておかないといけませんが、これらの保持はIPLwareのローダが行ないますので、ユーザプログラム側で保存と復帰(push,pop)をする必要はありません。ただしセグメントレジスタの保存は、dsとesしか行なっていないので、その他のものを変更する場合は元に戻しておいてください。また32bitレジスタの上位16bitぶんについてもIPLwareのローダでは保存しませんので、念のためアプリケーション側で終了時に復旧しておいてください(固定ディスク起動メニューでは32bitレジスタが使用されていないので保存せずとも問題ないかもしれませんが)。


【4】 IPL組み込みのメカニズムと、IPLwareの登録システムの仕様(技術資料)

 この章はIPLwareの技術的側面について記述しています。IPLwareを単に使うだけの場合は読み飛ばして構いません。

● IPL組み込みのメカニズム

 組み込みのメカニズムの理解のために、ハードディスクのデータ格納構造とブートシーケンスに関しても、あわせて説明をしておきます。なおここで「セクタ番号」と言っているのは、論理ブロックアドレス(LBA)のセクタ番号です。

 ハードディスクのセクタ番号 0 には、IPL(初期プログラムローダ)(*1)が格納されていて、起動優先順位先頭のハードディスクの場合、まずこれが読み込まれて実行されます。ついで、起動メニュー(*3)がすぐに実行されます。起動メニューが実行されると、パーティションテーブル(*2)がスキャンされ、指定したパーティションのOSのIPLが次に実行されます。そしてOSの起動となります。

 IPLwareのローダは、起動メニューの空き領域(*4)(512バイトある)に追加されて格納されます。ここで組み込み時に、起動メニューの入り口のジャンプ先を変更して、IPLwareのローダに実行を移します。固定ディスク起動メニューの先頭には、nearジャンプ命令(E9 xx xx)がありますが、これの飛び先をIPLwareのローダ部(起動メニュー先頭からのオフセット1900h)に変更しておきます。そして、IPLwareのローダ実行終了後には、もとの飛び先(オフセット 02D4h)に戻るようにしておきます。

 IPLwareのローダは、IPLwareのプログラムモジュールの登録テーブル(*5)(起動メニュー先頭からのオフセット 1B00h〜)をスキャンし、ここに記述された内容に従って、プログラムモジュールを所定ディスク番地からメモリにロードして、そこに far call します。プログラムモジュールは RETFで戻ります。そしてローダはテーブルの次の項目をスキャンします。テーブルは16バイト×8個ぶん用意されているので、ユーザプログラムモジュールは8個まで登録できます。テーブルのスキャンの結果、もう実行すべきプログラムがないと判断された場合は、レジスタを全部退避した上で、起動メニューの実行に戻ります。

 IPLware.exeでは、テーブルを参照の上、空いているセクタ番地にユーザプログラムモジュールを格納して、テーブルにその情報を書き入れます。このテーブルは一種のディレクトリであり、簡易なファイルシステムを構成しているとも言えます。
  

● ハードディスクのシステム領域の内容とそのセクタ位置
  
(*1)
セクタ 0
マスターブートレコードのIPLコード。PC-98では、Nearジャンプ命令(EB xx)のあと、"IPL1"という識別文字列が書かれていて、そのあとにIPLのコードが格納されている。
(*2)
セクタ 1  
パーティションテーブル 16 個分の領域の開始終了番地がある。
(*3)
セクタ 2〜14  
NEC固定ディスク起動メニュー 2.70の意味ある部分(コードがあるのはセクタ14の半分までであるが、実際はセクタ15までメモリにロードされる)
(*4,  *5)
セクタ 14〜15
IPLwareのローダが置かれる所で、起動メニューと共にロードされる。セクタ15の後半1/2(128バイト)が、プログラムの登録テーブル。最後の128バイトは、将来のための予約とした(V2.02から)。
セクタ 16〜23   空き領域なのであるが、どうやら Windows 2000で無断で使用されるため、ここにプログラムやデータを置くと破壊される。IPLwareでも使用しないようにしている。
セクタ 24〜135 IPLwareのユーザプログラムモジュールが置かれる所。最大は BIOSシリンダ番号1となるセクタ番号まで使用可能。最もそれが小さい内蔵IDEで、セクタ番号136未満まで使える。上限は組み込み時にチェックされる。

  

● ユーザプログラムモジュールを登録する「ディレクトリ」の構造

 場所はセクタ 15の後半 256バイトにあります。下記のような16バイトの情報が 8個分格納されています。何も格納情報がないときは、値ゼロが詰められています(ゼロ行進の場合には何もないとローダが判断しています)。
  
「ディレクトリ」のバイトオフセットの内容
0〜7 IDname プログラムを登録したときの識別名 8文字(以内)。
8 組み込み時のユーザプログラムモジュールのバイトサム(総和)が格納される。この値と、ロード実行時に検査した値が異なっていた場合 CHECKSUM不正として、実行を回避する。プログラム本体が破壊された場合の対策として用意した。
9 実行制御ビット
ビット 7 1のときチェックサム検査を行わない。(Ver. 3.60で機能復活)
ビット6 プログラムモジュールタイプ2であることの識別。タイプ2(RET命令で終了するCOMファイル形式プログラム)のときは1、従来からのモジュールタイプの場合は0。
ビット5 1のとき動作を無効にする。(バージョン3.50で新設)
10〜11 プログラムモジュールのバイト数。占有セクタ数はこれから算出される。
12〜15 ユーザプログラムモジュールを格納するディスクセクタ番地。
4バイトあって、ディスクのLBA(リニアな論理ブロックアドレス)です。ということは、ディスク上のどこに置いてもよいということですが、通常はBIOSシリンダ0内のセクタに置かないと、破壊されてしまいます。

 --------------------------------------------------------------------------- 

(例)        バ イ ト 並 び 
 00 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 
 'E' '1' '0' 'P' 'A' 'T' ' ' ' ' xx ww (yyyy) (  zzzzzzzz  ) 
(説明)
"E10PAT  " :プログラムIDname(一例),8文字未満の余白は空白文字(NULL文字ではない) 
xx     :チェックサムバイト値 
ww     :実行制御ビット、通常は00h 
yyyy    :プログラムモジュールのバイト数 
zzzzzzzz  :プログラムモジュールの格納先頭ディスクアドレス
---------------------------------------------------------------------------