まて、の引数て、す。対象となる引数は Table 1 スタックが使われます。 きい番地 ) になります。 て、示されるレジスタに割り当てられます。 関数のリターン値は , 大きさが 1 バイトな 呼び出された関数は SP レジスタを破壊し レジスタに割り当てられなかった引数は らば A レジスタに , 2 バイトならば HL レジス てはいけません。それ以外のレジスタは呼 スタックにつまれて渡されます。 タに , 3 バイト以上ならばスタックにつまれ んだ関数が必要なレジスタを待避するのて、 , また , printf() 関数のような可変パラメタ て返されます。関数値がスタックにつまれ 破壊してもかまいません。したがって , ア 関数の場合には引数の受け渡しにはすべて る場合 , その場所はすべての引数より下 ( 大 センプリ言語のプログラムから C の関数を呼 び出す場合には必要なレジスタを待避して から , C の関数を呼び出さなければいけませ ん。 ところて、 , コンパイラは関数や外部変数 〃 ( アンダースコア ) を付加 の名前の後に します。したがって , アセンプリ言語て、関 Fig. 2 List4 をコンバイルした結果 CSEG PUB い C table ta b 1 e ・ 66 DW DB DW DB DW DB DW DB DW DB DW DB DW DB DW lSt tblentr•y 1 : typedef 2 : 3 : 5 : 6 : TBLENTRY 7 : 8 : 9 : 14 : 16 : TBLENTRY struct Char int TB し ENTRY; table[] 0 1 2 3 4 5 6 7 っ乙《 0 ー 8 0 ^ 0 1 よ CD ワレれ 0 っ行ー行ー戸 0 ーれ 0 ー *tblp; ・呼び出す側 (C) asm (char, long) : int, abc; int / * プロトタイプ宣言 * / int int VO i d DSEG PUBLI C tblp- 2 DS tblp- ・ , 1 , 0X12345678 ) : . 3 List7 から生成されたコード Fig putch PUSH PSW 0 ANI 1 JZ 2 POP PSW OUT 1 RET ・呼び出される側 ( アセンプリ言語 ) : C 言語で asm ( ) を呼び出せるように宣言。 pub lic asm : C 言語で定義した外部変数を参照することを宣言。 abc extrn long l); asm(char C, int i, int 引数 c は A レジスタに i は DE レジスタに ーはスタック CSP 十 2... SP 十 5 ] にセットされて渡される。 ret urn 値は H しにセットしなければならない。 asm TabIe 1 : SP 以外のレジスタは破壊してもよい。 ; return 値を H しにセットして ret する。 1 番目 2 番目 3 番目 A E DE BC 大きさ 1 バイト 2 バイト ret 特集 ROM 化プログラミング考察 73
な振舞いをします。このことは , 十分に注 ルした結果は Fig. 2 のように , table はコー 割り込み処理 意する必要があります。 ドセグメント ( ROM ) におかれ , tb ゆはデータ ROM プログラムにおけるイニシャライザ セグメント ( RAM ) におかれます。 LSIC て、は , 割り込み処理もて、きます。た の役割りは「定数テープルを実現する」こと だし , 「割り込み手続き宣言」などは用意さ て、あり , 変数を初期化することて、はありま アセンプラと C のリンク れていません。て、すから , コンパイラは割 せん。変数の初期化は , 明示的に代入文て、 り込みの入り口て、の全レジスタの待避 , 出 行う必要があります。また , 文字列は「記憶 関数引数の受け渡しにはレジスタ A, ロて、のレジスタの回復 , そして割り込みマ クラス static て、ある初期化された文字の配列」 HL, DE, BC とスタックが使われます。レ スクの解除といったシーケンスは生成しな て、あり , コードセグメントに配置されるた ジスタが使われるため , 関数呼び出しのオ いのて、 , こういった処理はアセンプリ言語 めに , ROM に書き込んだ場合にはその値を ーバヘッドは非常に軽くなっています。 て、書かなくてはなりません。 変えることはて、きません。 レジスタに割り当てられる対象となるの C プログラムの中て、 , 割り込みを禁止した 例を示します。 List 4 のソースをコンパイ は , データの大きさが 2 バイト以下て、 3 番目 り , 解除したりという処理が必要な場合は , ヘッダファイル machine. h の中て、定義されて いる di マクロと ei マクロを使用します。 また , 割り込み処理と , 通常の処理の両 方から呼ばれる関数がある場合 , その関数 は recursive にしておく必要があります。再 帰呼び出しはしていなくても , 再入の可能 性が要求される場合は , recursive にしなけ ればいけません。 Z80 の割り込み処理も行うことがて、きま す。次の例は , CTC を使った Z80 の MODE 2 の割り込み処理記述例て、す ( List 3 ) 。 List 3 のようにザイログニーモニックて、書 かれているアセンプリ言語は LSI C ー 80 のア センプラ r80 て、はアセンプルて、きません。岩 崎技研工業の PROASM-II または Microsoft 社の M80 て、アセンプルします。 定数テープレの扱い イニシャライズされている変数のうち , 己憶クラスが static と extern のものはコード セグメント ( CSEG ) に配置されます。 CP/M 環境下のように RAM 上て、プログラムを動か す場合には , これらの変数の値を変更する ことがて、きます。しかし , そのプログラム を ROM に書き込んだ場合にはどうなるて、し ようか ? これらの変数はコードセグメント に置かれるのて、値を書き換えることがて、き なくなり , あたかも「定数」て、あるかのよう 72 CMAGAZINE 1990 11 lSt アセンプリ言語 : EXTRN CSEG i n t0 ma 1 n : 初期設定を行う。 START : SP, スタックのアドレス A,HIGH INTVEC 2 : CTC の割り込みべクタの設定を行う ( し 0 W I NTV EC の設定 ) JR ma 1 n : 割り込みべクタテープル ページバウンダリーにする。 INTVEC: CTCINTO CTCINTI CTCINT2 CTCINT3 : 割り込みルーチン CTCINTO: PUSH AF PUSH BC PUSH DE PUSH H し CA しし int0 POP H し POP DE POP POP AF RETI : C 言語の関数を呼び出す。 一三ロ 語 int0() VO i d
ROM 化 考察 プログラミン モデルが考えられる。 る。これらの 8086 と異なる点は , 各セグメ ないリテラルデータ , たとえば文字列定数 まず , 全ページレジスタを同じ値にして ントの一部がほかのセグメントと重なるこ や多くの CPU て、の doub 形の定数 , そして おけば , ニマムモードのとき , 同じく 8080 とはありえないということて、ある。 型修飾子 const がつけられた静的変数 ( 定 モデルになる。 数 ? ) などはすべてリテラルになる。 メップの考え方 そして , CP レジスタと DP レジスタの値を データは , 実行前にある特定の内容に初 変えることによって , プログラムとデータ 期化される静的変数て、ある。 のアドレス空間を別々に取ることがて、きる 0 初期化データは , 初期値として 0 をもつ ようになる。ページレジスタはアドレッシ ROM 化可能なプログラムの書き方は前述 静的変数て、ある。 ングの際に 24 ビット長て、表される全アドレ したとおりて、あるが , それを実際に ROM に そしてこれらのデータを 3 種類の記憶領域 焼き付けるためには , 静的変数を具体的に ス空間の上位 8 ビットになり , 下位 16 ビット ( テキスト領域 , データ領域 , BSS 領域 ) に が汎用レジスタまたはプログラムカウンタ メモリのどこへ割り当てるかを決定する必 任意に割り当てる。これらは物理的な記憶 ( 以下 PC レジスタ ) や絶対アドレス指定の値 要がある。 WSL C て、はそれらについて柔軟 領域にそのまま対応する。 に指定することがて、きる。 テキスト領域は書き換えられない領域て、 になるから , アドレス空間は 64K バイト単位 のセグメントに分割される。そこて、 , 8086 WSL C て、のデータは , 関数 , リテラル , ある。書き換えられることのないデータ , データ , 0 初期化データの 4 種類に分類され すなわち関数や定数のために使用て、きる。 と同じようにスモールモデル ( プログラム 1 セグメント , データ 1 セグメント ) , ミディ この領域は書き換えられないのて、 ROM に割 アムモデル ( プログラム複数セグメント , デ 関数とはその名のとおりプログラムの実 り当てることがて、きる。 ータ 1 セグメント ) , コンパクトモデル ( プロ 行部分本体て、あり , 残り 3 つが実データにな データ領域は実行前にある特定の内容に グラム 1 セグメント , データ複数セグメン 初期化される領域てある。定数や静的変数 る。 ト ) , ラージモデル ( プログラム複数セグメ リテラルとは定数のことて、ある。プログ のために使用て、きる。この領域には初期値 ント , データ複数セグメント ) が考えられ ラム中のインストラクション中に展開て、き があり , そしてそれらは書き換えることが て、きる。したがって , C プログラムの ROM マナーの悪いプログラム例 化の際に , この領域を単純に ROM か RAM のいずれに割り当てるとはいえない。この 間題に対して , 何通りかの方法が考えられ ① この領域にデータを出さない。つま り , めんどうなこの領域をプログラ ム中からなくしてしまう ② この領域を ROM に割り当て , プログ ラム中で書き換えを行わないように コーディングする ③ この領域を RAM に割り当て , 初期値 をプログラムの実行前または入口で ROM からロードする ③の方法はもっとも汎用性が高く , ROM 化にあたっても , 通常のオペレーティング システム上て、のプログラミングとなんら変 わらないのて、 , プログラミングの上て、はよ いのて、あるが , 実現のために仕掛けが必要 なことと , ROM と RAM の両方を消費しメ モリが無駄て、あるなどの問題がある。 特集 ROM 化プログラミング考察 57 lSt ィー 4 ワっ 0 4 ・ ^. 0 っー char *P : ” abC ” : P p [ 2 ] Fig 1 C プロクラムのメモリへのマッピンク、 ・関数 ・テキスト領域 初期値あり 書き換えなし ・テータ領域 初期値あり 書き換えあり —x6 ROM ・リテラル const int X = 3 ; abc ・テータ inty=4, ・セロ初期化テータ int2=O; —x4 RAM ・ BSS 領域 初期値は 0 のみ 書き換えあり
フラグをつけることによって , —x フラグて、 て、は , 基本的に①②の方法を考えることに メーデレによる データが指定されたのとデータと同じ領域 するが , 場合によっては , ③以外の方法が ツ違い へ割り振ることがて、きる。 使えない場合もあるのて、 , ③についても最 後に簡単に触れることにする。 —x フラグはそのあとに数値をとり , それ ニマムモードては , 前述したとおり , ①②は , 最初に説明した , ROM 化可能な が指定になる。その数値の意味は , 関数ビ 8080 モデルになる。この状態て、は関数とデ プログラムをどう書くかという問題になる。 ット ( 04 ) , リテラルビット ( 02 ) , データビ ット ( 01 ) のそれぞれが 1 ならばテキスト領 すなわち , 0 以外の値て、初期化される静的変 ータは同一のアドレス空間上にあるのて、 , 数は書き換えてはいけないということて、あ 域 , 0 ならばデータ領域という意味になる。 アドレス空間を気にすることなくマップす る。そのうえて、 , 後述するように , コンパ 通常 , ー x6 かー x4 が用いられる。 ることがて、きる。 イル時の指定などて定数や変数をそれぞれ ー x6 は関数とリテラルのビットが立って そこて、通常は , 関数とリテラルをテキス いるから , 関数とリテラルがテキスト領域 テキスト領域に割り振るかデータ領域に割 ト領域に , データをデータ領域に , 0 初期化 り振るかを指定てきるようになっている。 へ , データがデータ領域へマップされる。 データを BSS 領域に割り当てる。 マキシマムモードて、は , 8080 モデルて、あ これはミニマムモードて、用いられる。 その一方て、 ( これも後述するが ) , マキシ マムモードて、は , ①の方法は一般には不可 ー x4 て、は関数ビットのみが立っているの る場合を除いて , 関数とデータは異なるア ドレス空間上にあることになる。すなわち , て、 , 関数のみテキスト領域へ , リテラルと 能になる。 BSS 領域は , 実行前にすべて 0 に初期化さ 関数は CP レジスタに示されるセグメン・ト データはデータ領域へマップされる。これ はマキシマムモードて、用いられる。 れる領域て、ある。初期値として 0 をもつ静的 に , データは DP レジスタに示されるセグメ 変数や , 初期値をとくに指定されていない ントに配置する必要がある。 静的変数に使用てきる。この領域も書き換 通常は , 関数をテキスト領域に , リテラ えられるのて、 RAM に割り当てられる必要が ルとデータをデブタ領域に , 0 初期化データ あるのだが , 実行前または実行時のいちば を BSS 領域に割り当てる。 こうすることに 実行形式オプジェクトは , スタートアッ ん最初にすべて 0 にクリアされる必要があ よって , リテラルについてもほかのデータ プ部 , プログラムをコンパイルしたオプジ る。 ROM 化にあたっても , この領域を使用 と同じようにアクセスて、きるようになる。 ェクト , 実行時ライプラリの 3 つをリンカて、 することにとくに問題はないが , 0 にクリア これを 8080 モデルの場合と同じようにリ リンクすることによってて、きあがる。実行 するという作業は必要て、ある。 テラルもテキスト領域にマップすると , リ 時ライプラリは C の標準的なライプラリて、あ テラルにアクセスするたびに , 一時的に DP なお , BSS 領域に似たものにスタックヒ レジスタまたは EP レジスタを CP レジスタと り , これを使わないて、プログラムを書くこ ープ領域がある。その名のとおり , スタッ 同じ値にする必要があり , コードサイズお ともて、きるが , 非生産的て、ある。 クがここに置かれる。 auto 変数は基本的にスタック上に確保さ よび実行速度の両面て、不利になる。また , そして , リンク時に各記憶領域のマップ されるアドレスも決定される。そのときに れる。また , ma Ⅱ oc ( ) 関数などの標準関数 リテラルへのポインタとデータへのポイン て、確保される動的メモリもこの領域に確保 タの間の整合性が保証て、きなくなり , 正常 ROM 領域と RAM 領域が最終決定されるわ されることがある。 にアクセスて、きなくなる場合もある。 けて、あるが , 最初に述べた ROM 化可能なプ この領域は当然 RAM に割り当てられ , 初 この場合 , リテラルをデータ領域にマッ ログラムて、あれば , テキスト領域は ROM, 期化の必要もない。プログラムの実行前ま プしているのて、 , データ領域は ROM て、ある BSS 領域は RAM に割り当てることがて、きる たは実行時の入口て、スタックポインタ ( H8 / 必要がある。 て、あろう。 WSL C のコードジェネレータ ( p2. h85 ) に 500 ては R7 レジスタがスタックポインタにな データ領域はコードジェネレータ (p2. h85 ) る。以下 SP レジスタ ) をこの領域のもっとも は , オプションフラグとして「一 x 」というも へのフラグが一 x4 の場合は ROM て、ある必要 大きいアドレスにセットする必要がある。 のがある。これは関数 , リテラル , データ があるが , ー x6 の場合はプログラム中て初期 をテキスト領域 , データ領域のどちらに割 以上 , 4 種類のデータを 3 種類の記憶領域 値のあるものにすべて const をつけてリテラ にマップするのてあるが , H8 / 500 て、は CPU り振るかを指定するものて、ある。 ルにしてあればなくなってしまうはずてあ のモードによって多少考え方が変わる ( Fig. 0 初期化データについては必ず BSS 領域に 割り振られるが , 「—bss 」というオプション スタートアップ部ては , 今まて、出てきた アップ 1 参照 ) 。 58 CMAGAZINE 1990 11
int C というように , 変数 a , b, c が宣言されたと する。すると , a は定数 , b は変数 , c は初期 値のある変数となる。 定数 a は , ROM に割り当てる。プログラ ム中て、 , 10 ; a のような代入を書くと , コンパイラはこれ をエラーにする。つまり , 定数 a の値は変わ ることがありえない。このような変数は ROM に割り当てることがて、きる。 一方 , 変数 b は , RAM に割り当てる。 WSL C は変数の定義と参照が比較的はっきりして いる C 処理系て、 , 変数 b のような宣言は , int b と同じ扱いになる。このような 0 に初期化さ れている変数 b は , b に何かを代入をする前 などとすると x の値は 0 になるが , のような代入も許される。つまり , 変数 b の 値は , 最初は 0 て、 , その後 , 値を変えること がて、きる。このような , 初期値が 0 て、ある変 数は RAM に割り当てることがて、きる。 変数 c は , c へ何かを代入する前に としたとき , x の値は 2 にならなければなら 20 : C としても , コンパイラはエラーにしない こうすることによっ int C ; このような場合 , 変数宣言を , このような変数は使えない がて、きない。したがって , ROM 化するとき 変数 c は ROM にも RAM にも割り当てること 後て、値を変えることもて、きる。このような つまり , 最初に 0 以外の初期値があり , その としておき , 変数 c を使うよりも前に関数内 56 CMAGAZINE 1990 11 と初期化すればよい て , て , 変数 c は初期化の必要のない変数とな り , RAM に割り当てることがて、きる。その 後て、あれば , 10 ; C も , なんら間題はなくなる。 しかし , もし , 10 ; C のような変数 c への代入がプログラム中に存 在しないのて、あれば , 変数 c を最初から ROM に割り当てることがて、きる。これもひとつ の解決方法て、ある。 しかしながら , 本来 , 静的変数は書き換 え可能て、あるから , コンパイラはプログラ ム中に , 万が一 , 上のような代入式が存在 していても , 工ラーとして検出することが て、きない これを解決するには , 定数 a のように const C と , 変数宣言をすれば , 工ラーとして検出 て、きる。プログラムの書き方としては , のほうが望ましいといえる。 結局 , ROM 化可能なプログラムとは , int C の形に代表される初期値の必要な静的変数 を ROM に割り付けてしまっても問題のない ようなプログラムて、あるといえる。また , 逆に , 初期値の必要な静的変数のないプロ グラムは ROM 化可能て、あるともいえる。 悪しプロクラム ある。コーディングの際には , そのような に検出することが不可能になっているのて、 の不完全さによって , コンパイラが自動的 ポインタの扱いに起因する型チェック機構 そして , それらの大部分は , C 言語における にもかかわらず , 「悪い」プログラムもある。 が , その一方て、 , その原則が守られている 変数宣言については以上のとおりて、ある プログラムにならないように注意する必要 がある。 たとえば , マナーの悪いプログラム (List 1 参照 ) はエラーにならす , しかもリテラル て、ある文字列定数、、 abc 〃を書き換えようと する操作がある。これは , 基本的に全メモ リ空間が RAM て、あるパソコンや評価ポード などの上て、は実行可能て、あり , ROM に焼き 付けたとたんに実行不可能になる。 これをコンパイラがエラーとして検出て、 きるようにするためには 3 行目の宣言部を , const char * p , とすると , 6 行目て、コンパイル時にエラーに なる。すなわち , p は 5 行目て、文字列定数へ のポインタ値を代入しているのて、あり , p の 宣言に const をつけて , はじめて記憶クラス も含めた型の整合性が保たれることになる のて、ある。 H8 / 500 のメモリモテル H8 / 500 はそのアーキティクチャから 8086 のようないくつかのメモリモデルが考えら れる。これらは CPU のハード的なモード設 定によるミニマムモードとマキシマムモー ドにもよる。 ニマムモードて、は , ページレジスタは すべて無効になる。したがって , コードベ ージレジスタ ( 以下 CP レジスタ ) とデータベ ージレジスタ ( 以下 DP レジスタ ) , 工クステ ンドベージレジスタ ( 以下 EP レジスタ ) , ス タックベージレジスタ ( 以下 TP レジスタ ) は すべて同じ値て、ある , という状態になる。 この状態て、は , プログラムとデータは同一 のアドレス空間上にあり , 全体て、 64K バイト のアドレス空間があることになる。これは いわゆる 8080 モデルに相当する。 ニマム モードて、はこれ以外のメモリモデルは考え られない マキシマムモードて、はペーシレジスタの 使い方て、 8086 と同じような数種類のメモリ
fo 「 mation from ( 0 mak 5 する必要がありますが , プロシー ンタのように 64K バイトのメモリセ ジャコールが使用される関数を完 グメントの参照を可能にしました。 全に管理てきます。 based ポインタをサポートする新し いキーワードは以下のふたってす。 segment 0 変更されたオプティマイジン segment は , 新しいデータタ グについて教えてください。 イプてす。各メモリセグメント に , それぞれ異なった識別子を A MS-C 6.0 は , より高速て品質 つけるものてす。ちょうど , 8086 の高いコードを生成するために以 系 CPU のセグメントレジスタの 下のような最適化の技法を取り入 役割をします。 れました。 based ・グローバルな最適化の実現 based は , 新しいポインタタ MS-C 6.0 は , MS-C 5.1 て、行 イプてす。このポインタが指す このため , 特定の関数を思い通 640K バイト (MS-DOS)0 空き われていたループレベルクて メモリセグメントは , 上記の メモリ IM バイト以上 ( OS / 2 ) りのレベルて最適化することが の最適化から , 関数全体に対し segment< 指されているセグメ ・必要ディスク装置 て、きます。 て第グローバルなク最適化を実 ントになります。ちょうど , 8086 たとえば , ポインタアリアス IM バイト容量の FDD と 現するように進化しました。っ 系 CPU のインデックスレジスタ を使用する関数ての最適化にア HDDI 台以上。 HDD の空き容量 まり , 重複する部分式をひとつ の役割をします。 は最低約 6M バイト以上 , 最大 20 リアスチェックの緩和を行った にまとめる処理の対象を , ルー 場合 , 従来のコンパイラては正 M バイト ( PWB を使用する場合 プから関数全体に拡大したわけ しいコードの生成がて、きません には約 7M バイト必要 (SETUP てす。 また , 変数を自動的にてきる てした。このような場合に , プ 時に , DOS のみを選択して , 全 だけレジスタ変数に割り当てる 設定を行うと約 12.5M バイト必 ラグマを使い , その関数のみ最 ようにしています。もちろん , 要。 OS / 2 のみを選択して , 全設 適化を行わないようにすれば , 従来と同様のループレベル〃 コードの品質を保った上ての最 定を行うと約 18M バイト必要 ) ) の最適化も同時に行えます。 適化が可能になります。 ・推奨システム IM バイト以上の LIM 4.0 対 ・ローカルコードの最適化の実現 応の EMS メモリ (MS-DOS)0 IM MS-C 6.0 は , プログラマに代 MS-C 6.0 を使用するのに必要 バイト以上のキャッシュディス わり数式を手作業て最適化する なハードウェアを教えてください。 ク (MS-DOS)0 マウス ときの経験則〃を導入しまし ・使用可能な日本語入力システム た。これにより , shift 式や switch 以下のとおりてす MS-KANJI API 仕様の日本 ステートメントを有効に最適化 語入力システム (ATOK6, してくれます。 ードウェア ATOK7 の場合 , 付属のドライバ 付録ディスクには , List 1 のサ 80826 , 80386 を CPU にもつ PC て対応可能 ) ンプルプログラムを MS-C 5.1 と ー 9800 シリーズ , または PC-H98 MS-C 6.0 てコンパイルした結 シリーズ ( ただし , ハイレゾモー ~ 果が収録されています。 ドを除く ) ・最適化レベルの管理の強化 ・ OS MS-C 6.0 は , コンパイル時の MS-DOS Ver. 3.1 以上もし 指定以外にプラグマによる最適 くは , OS / 2 Ver. 1. IA 以上 化の管理が可能になっています。 ・必要メモリ サンプルプログム switch(c) { case docmd(l) : case 'b' docmd ( 2 ) : case case y case Z docmd(3) : 1 LiSt 1 っ乙 CO -4 戸 0 6 7 8 9 0 新しいプロシージャコール fa stca 日について教えてください。 A 新しいプロシーシャコール fa stca Ⅱは , 関数間の引数の受け渡し の際にスタックを利用せず , 直接 , レジスタを利用することて , 実行 速度を大幅に向上させます。 MS- C 6.0 は , この fastc 訓を管理する 2 通りの方法を用意しています。 ひとつは , オプジェクトモジュ ールレベルての管理てす。これは , モジュール全体について必要が生 じるたびに関数の引数をレジスタ て渡すようにコンパイラに指示す るものてす。この方法ては , MS- C 5.1 て作成したソースコードを変 更する必要はありません。 ふたつめは , 関数レベルての管 理てす。この方法ては , MS ー C 5.1 て作成したソースコードを手直し 0 A lnformation from Compiler Makers 149
領域のデータの操作を記述するのにたいへ 空間と IO 空間が別々なのて , 直接 in / out 命令 ん便利て、ある。 に展開て、きるというよさがある。 ポート変数はファイル内てしか有効て、な 劃り関数 く , 外部変数にはならないのて、 , インクル ードへッダファイル中て、 # define マクロと同 じように使用するのが便利て、ある。 割り込み処理のための関数は通常の関数 と違って , 指定変数 つ ①割り込みが起こったときの全レジス タの内容の保存 ②「 et 命令ではなく「 te 命令でリターンす ポート変数に似たものにアドレス指定変 る 数がある。 のふたつの処理が必要て、ある。 ポート変数とアドレス指定変数の違いは , これを関数宣言の前に@PO 「 t を指定して , ポート変数が IO 空間に割り付けられるのに 割り込み関数て、あることを指示て、きるよう 対して , アドレス指定変数はメモリ空間に になっている。 割り付けられる。 たとえば , これは , 特定のメモリ上に記憶領域を割 @port rxi( ) り当てるのに使われる。 たとえば , char bufC1024] @Oxfb80 ; と書けばメモリ空間上の 0x 市 80 番地から IK バイトの領域に対して , bufC0x200] などという形て、アクセスて、きるようになる。 もちろん , たんに char p3ddr @0xff84 ; というような使い方がて、きる。これらの関 といった形て、もよい。 H8 / 500 て、はメモリ空 数は通常の関数と同じように外部関数にも 間と IO 空間が分離されていないのて、 , これ て、きる。 は前出の , @port char p3ddr @Oxff84 : @port (*vec ロ ) ( ) と同じ意味になる。 これもポート変数と同じく外部変数にて、 きないのて、 , インクルードへッダファイル リンク時の記憶領域 などを用いる。また , のマップの影響をまったく受けず , 直接 RAM Fig. 2 リンクとモトローラ S レコードへの変換 ( ⅱ nk ) Fig. 3 メモリマップ 0X0000 OxOObf 0X00C0 内 蔵 0 領 域 割り込みべクタテープル テキスト領域 ( 関数とリテラル ) 0X2b5 0X2b6 Ox7fff Oxfb80 BSS 領域 ( セロ初期化テータ ) Oxfbc7 内 Oxfbc8 Oxfcc7 Oxfcc8 領 域 Oxff7f Ox 幵 80 スタック領域 内部レジスタ領域 @po 「 t txi( ) Oxffff というようにして割り込みべクタテーフル をつくることがて、きる。割り込みべクタテ ープルはリンク時に割り込みべクタテープ ルアドレスにマップされるように指定する 必要がある。 , 評価ポードや ICE などて、はアドレス 指定変数を用いて , @port (*vrxi)( ) @0x6a : @port (*vtxi)( ) @0x6c ; init( ) txi, V 「刈 VtXi link 十 h ー 0 eb. xeq ー b0X100 + text + bss -b0xfb80 \ ebvec. 0 Crts. h85 ebmain. 0 liba. h85 libc. h85 libb. h85 libm. h85 hex ー 0 eb. sa -s -m eb eb. xeq txi ・ というように初期化することもて、きる。 60 CMAGAZINE 1990 11
bo C のヒュージモデルに対応しているかど ②割り込みべクタテープルの設定 ( 1 ) スタックポインタの設定 ことを行います。 スタートアップルーチンは以下のような ズタートアップレーチン MS<eg や Turb0 C の うかは不明て、す ) 。 Fig. 18 一般的な EXE ファイルの構成 タテープルを設定します。 例外やハードウェア割り込みのためのべク 除算ェラー例外 , オーパフロー例外などの ( 3 ) 初期値をもたない領域のゼロクリア 初期値をもたない領域は通常の処理系の スタートアップルーチンて、はゼロクリアを 行っていますから , ROM 化の場合て、もゼロ ( 7 ) セグメントレジスタのセットアップ ( 6 ) ハードウェア割り込みの許可 ( 5 ) ハードウェアの初期化 RAM へ転送します。 実際は DGROUP の初期値を ROM から RAM へ転送 ④初期値をもつ領域の初期値を ROM から クリアしておきます。 通常の処理系て、は DS レジスタは DGROUP EXE ファイルヘッダ ( Tab 厄 1 ) プログラム領域 初期値をもった変数領域 シンホル情報 一般的な EXE ファイルは EX E ファイルヘッダ プログラム領域 初期値をもった変数領域 ロードモジュールのサイズ ↓ からなり , CodeView 用のシンホル情報が追加される場合には , EXE ファイルの最後に追加される。 lSt 13 : 8 : 7 : 3 : 2 : スタートアップルーチン例 スタート・アッフ・・トチン 4 : MSC 1 equ 5 : TURBOC equ 2 6 : C-Compi ler equ MSC RAMSIZB equ 800h ROMSEG equ 0C000h 9 : STKSIZ equ 800h ( Microsoft C / TurbO C ラーシ・・モテ Microsoft C Compi ler : スタックハ・イト数 : ROM セメント : RAM ハ・ラク・ラフ数 ( X16 ハ・イト : Turbo C 2. 0 ← 1000H ) ハ・ワー ON リセット処理 i f C-Compiler eq MSC publi c acrtused acrtused 1 equ edata:byte ;extrn : MS-C 用ハ・アリック宣言 : end of DATA ( start Of BSS ROM 化 考察 を指している必要があるため , セグメント レジスタをセットアップします。 ⑧メインルーチン main の呼び出し DOS 上のプログラムて、は main 関数からリ ターンしてプログラムを終了しますが , ROM 化システムて、は main 関数から戻ってくるこ とはないため , サプルーチンコールて、なく main 関数へのジャンプて、もかまいません。 スタトアドレス FFFF0h の割り付け法 8086 は IM ノヾイトの空間の最上位ノヾラグラ フの FFFF0h 番地から実行を開始します。し たがって , 86 系て、 ROM 化を行うには FFFFO h 番地に fa 「ジャンプ命令て、スタートアップル ーチンにジャンプするようなコードを書き 込まなくてはいけません (Fig. 17 ) 。 ROM 化ツール自体がこの処理を独自にサ ポートしているものもあるようて、すが , 般的には以下のような方法て、対処可能て、す。 ( 1 ) AT 疑似命令でセグメントを FFFF0h に割り 付ける (List 9 ① ) この方法は OBJ ファイルをリンクして HEX フォーマットに変換する ROM 化ツールの場 合に使用可能な方法て、す。 EXE ファイルか ら HEX フォーマットに変換する場合は AT て、割り付けたセグメントが EXE ファイル内 に書き込まれないため , この方法は適応て、 きません。 ( 2 ) END 疑似命令で開始アドレスをセットす る (List 9 ② ) MS ー DOS 上のプログラムは END 疑似命令 て、開始アドレスを設定することがて、きます。 たとえば END STARTUP とすればプログラムは STARTUP から開始さ れます。 一般的な EXE ファイルは Fig. 18 のような 構成になっており , ファイルの先頭に EXE ファイルヘッダ ( Table 2 ) があります OEND 疑似命令て、指定した開始アドレスは , この 特集 ROM 化プログラミング考察 51
を作りました。ⅶ tExit を使って , 初期化と 終了のメソッドを起動すると , それが INIT E 刈 T メソッドを定義しているすべてのオプ ジェクト型の同じメソッドを起動し , 現在 のプログラムにリンクされます。終了メソ ッドを起動した後 , tExit は求められた方 法て、終了します。常駐終了を求めたならば , InitExit は , 自動的に , . COM または . EXE 型 のファイルのプログラム初期化部分を開放 します。 なデータ成分をサポートするために必要な メソッドの宣言は , 定義したオプジェク •SET OBJECT TYPE . メモリをオプ 制御が提供されるのて、す。ほかのファイル ト型のデータ成分を操作する手続きを定義 ジェクトの型に設定します。 のオプジェクトを USE OBJECTS マクロて、 します。非バーチャルメソッドは , 直接コ •IJSE OBJECTS . 現在のプログラムに ールて、起動され , バーチャルメソッドは , インクルードしたときは , public と宣言され オプジェクトファイルをインクルード ている項目の名前だけが定義されます。 間接コールて、起動され , そしてインライン します。 初期化と終了のメソッドは , コンストラ メソッドは , 自分のコードを実行の流れの •ODB, ODW, ODD : データ成分の定義 クタやデストラクタとは違います。これら なかに直接におくためのマクロを必要とし です。 のメソッドはただ , プログラムの初期化時 ます。 •$GET OBJ REG PARMS . インライ と , 終了前に起動されるだけて、す。これら ンメソッドをサポートするために , パ このオプジェクト定義のシンタックスを は , 割り込みべクタへのアクセスが必要だ ラメータをレジスタに読み込みます。 使うと , 前の P0int オプジェクトは , 次のよ ったり , メモリを事前に初期化することが オプジェクトの標準的な定義を , List 1 に うに定義て、きます。 必要な場合 ( たとえばタイマや通信のオプジ 示します。オプジェクトのデータ項目をす OBJECT Point べて最初に宣言し , その後のオプションて、 ェクト ) には便利て、す。プログラムは終了メ 〇 DW X 0 そのオプジェクト型の初期化と終了のメソ ソッドもサポートするのて、 , メインプログ ODW Y 0 ッド , そしてさらにその後に , そのオプジ ラムにコードを追加することなく , 割り込 METHOD Move, V 旧 TUAL ェクト型のメソッドを書く , という形式て、 みべクタを外すことがて、きます。終了メソ OBJECT END ッドは初期化メソッドの逆順に起動され , オプジェクトの定義に加えて , Move メソ す。 ッドのコードを書き , それらがオプジェク 終了タイプを定義しているコードを渡され データ項目のマクロは , アセンプリ言語 ます。 ト型を完成させなければなりません。イン が普通サポートしている , スカラーのデー ラインて、はないメソッドを定義するたびに 初期化と終了のメソッドをアクティベー タ型をすべてサポートします。 DB, DW, そのための手続きを書かなければなりませ トするために , lnitExit というオプジェクト DD などを使わずに , マクロを使うと ,private LiSt OBJECT object_name [ , ancestor-name] [ODB data-element-name value [ , PUBLIC]] [IN IT_EXIT type_init_method, type_exit_method] [METHOD method_name [ , virtual ー OBJECT_END LiSt List ッドは点を X, Y 両方向に移動する . このメソ ノヾラメ ータ : ー Po i nt のインスタンスへのポインタ ds : d i ニ X 方向移動量 ニ Y 方向移動量 bx : 帰値 : 移動した Po i nt レジスタ使用 : FLA GS ( 起動により引が使われる ) Point PROC Move : X に加算 [d i ]. X , ax add [di]. Y,bx : Y に加算 add ; そしてリターンする ret Point Move ENDP ッドは点を X, Y, および Z 方向に移動する . このメソ パラメータ : Po i nt のインスタンスへのポインタ d s : d i = X 方向移動量 = Y 方向移動量 bx = Z 方向移動量 CX ; 帰値 : 移動した Po i nt レジスタ使用 : FLAGS ( 起動により引が使われる ) Point3D_Move PROC add [ d i ] . Z, cx : Z に加算 Point3D Move-A [di] : 祖先のメソッドに渡す リターンする ret Point3D_Move ENDP 23 21 世紀のアセンプラ
static const int nea 「 fstatdat=10 ; これ以外の書式て、は , const を付加しても 、、 DATA" や、、 FAR DATA 〃のクラスになります。 このセグメントは DGROUP に属するため , 初期値を ROM に置いてスタートアップルー チンて、 RAM に転送する必要があります。 BSS セグメント 初期値をもたない near データのセグメン トて、す。以下のようなコーディングを行う とこのセグメントが生成されます。 int near fpub ; static int near fstat : このセグメントは RAM 上に置くことにな ります。 Turbo C 2.0 の標準的なセグメントの配 置を Fig. 12—Fig. 16 に示します。基本的に は MS-C 5.1 から FAR DATA セグメント HUGE BSS セグメント FAR BSS セグメント CONST セグメント を取り除いたような感じになります。 Turbo C て、は far 配列や huge 配列などが宣言て、きな いため , このようになります。 メモリモデルのなかて、ヒュージモデル ( Fig. 16 ) が特別て、す。ほかのメモリモデルて、は DATA セグメントと BSS セグメントが生成 され , 初期値をもったデータと初期値をも たないデータが区別されますが , ヒュージ モデルてはこれらが同一のセグメントに生 成されます ( その代わりに DGROUP がな い ) 。したがってヒュージモデルて、は 初期値をもったデータ 文字列 などを使用すると初期値をもたないデータ と区別がてきなくなるため , ROM 化がて、き なくなります ( 市販の ROM 化ツールが Tur 50 CMAGAZINE 1990 11 生成されます。 00h ~ 01 h 02h ~ 03h 04h ~ 05h 06h ~ 07h 08h ~ 09h OAh—OBh OCh—ODh 14h ~ 15h 1 6 h ~ 1 7 h 1 8 h— 1 9 h 1 Ah ~ 1 B h TabIe 2 EXE ファイルヘッダ オフセット Fig. 17 スタ 内 4Dh, 5Ah : EXE 形式識別マーク 最後のページに入っているノヾイト数 512 バイト単位のファイルの大きさ リロケーションテープルの項目数 ヘッダのバラグラフサイズ ロードするプログラムの他に必要とされるノヾラグラフの最小数 ロードするプログラムの他に必要とされるノヾラグラフの最大数 スタックセグメント ( SS ) のロードモジュール先頭からの相対アドレス 実行直前に SP レジスタにわたされる値 リード補数チェックサム 実行直前ロ P レジスタにわたされる値 コードセグメントのロードモジュール先頭からの相対アドレス ファイル内の先頭のリロケーション項目のオフセット オーノヾレイ番号 ートアドレス FFFFOh の割り付け RAM startup ROM FFFFOh JMP FAR PTR startup