If you are still using the old URL (datacrystal.romhacking.net), please update your bookmarks! The old URL may stop working soon.
The current URL is datacrystal.tcrf.net.
The current URL is datacrystal.tcrf.net.
X68k/BASICFUNC
Jump to navigation
Jump to search
- X-BASIC EXTERNAL FUNCTION MANUAL - ============================================================================== ・外部関数の組み込み手順 1.外部関数をまとめたファイルを作成する. ・C 言語やアセンブリ言語などで、外部関数の機能単位モジュールを作成する. ・アセンブラで「インフォメーションテーブル」を作成し、外部関数の機能単位モ ジュールをまとめてインフォメーションテーブルの後につける. 2.(1)で作成した外部関数ファイルのファイル名の拡張子".X"を、".FNC"に変更する. 3.BASIC.CNF に FUNC = 主ファイル名(拡張子は省略) と書き込む. 4.BASIC.X、BASIC.CNF、外部関数ファイル(.FNC)を同一ディレクトリに格納し、X-BASIC を起動する. "*.FNC"ファイルには、複数個の外部関数を入れることが可能である. これは、外部 関数一つについて、一つずつファイルが必要になっては繁雑だという理由からである. そこで、一つのファイルに複数個の外部関数を書けるよう、各 FNC ファイルの先頭に 「インフォメーションテーブル」という名前のヘッダをつけなければならない. * Procedure for inserting external functions 1. Create a file containing the external function. *Using C, Assembly, etc. create a function module for the external function. *In Assembly, create an information table, include the function module(s), and place them after the table. 2. Change the extension of the file created in step 1 from ".X" to ".FNC". 3. Write FUNC = main file name (extension is omitted) in BASIC.CNF. 4. Place BASIC.X, BASIC.CNF, external function file(.FNC) into the same directory, and start up X-BASIC. It's possible to insert a multitude of functions into the "*.FNC" file. This is because it would be too complex if a separate file were necessary for each external function. You must include the header "Information Table" at the top of each FNC file in order to write a multitude of external functions. ============================================================================== ・インフォメーションテーブル 外部関数ファイルの先頭には、その外部関数ファイルに含まれる関数についての情報 を並べた「インフォメーションテーブル」がある. X-BASIC インタプリタは、その内容 を見て呼び出された外部関数がどの外部関数ファイルに含まれているのか、その関数の パラメータはいくつか、パラメータの型は何か、各外部関数の本体の実行アドレスはど こかを知る. インフォメーションテーブルは64バイトからなり、最初の44バイトには様々なルーチ ンやテーブルのアドレスが入っている. 後の20バイトは予備として 0 を入れておく. offset size 0 1.l BASIC 起動時の初期化ルーチンのアドレス 4 1.l RUN 時に実行されるサブルーチンのアドレス 8 1.l END 〃 12 1.l SYSTEM コマンド実行時や EXIT 命令による OS 復帰時に実行される サブルーチンのアドレス 16 1.l BREAK や CTRL+C によるプログラムの中断時に呼ばれるサブルーチン のアドレス 20 1.l 一行入力中の CTRL+D の押し下げ時に呼ばれるサブルーチンのアドレス 24 1.l (予備) 28 1.l ( 〃 ) 32 1.l トークン テーブルの先頭アドレス 36 1.l パラメータ 〃 40 1.l 実行アドレス〃 44 5.l (予備:0 を入れておく) 0~23バイトは、BASIC 上で外部関数を使っている時に起きる可能性のある重大な操 作(BREAK または CTRL+C を押すなど)が行なわれた場合、この外部関数ではどのような 処置をとるのかを書いておくアドレスである. ・BASIC 起動時の初期化ルーチンのアドレス(0~3バイト目) X-BASIC を起動した時に、この外部関数がどのような処理をするかを記述したプロ グラムの実行番地を指す. もしここが指すアドレスに rts 命令が書かれていれば、 BASIC 起動時には特別なことは何もしない. ・RUN 時に実行されるアドレス(4~7バイト目) RUN 命令が X-BASIC インタプリタ上で実行された時、この外部関数がどのような 動作をするかを記述したプログラムのアドレスを指す. ・END 時に実行されるアドレス(8~11バイト目) END 命令が X-BASIC インタプリタ上で実行された時、この外部関数がどのような 動作をするかを記述したプログラムのアドレスを指す. ・SYSTEM/EXIT 時に実行されるアドレス(12~15バイト目) X-BASIC インタプリタでは、ダイレクトモードで SYSTEM 命令が実行されると X-BASIC を抜けて親プロセスに戻る. このような場合に、この外部関数がどのような 動作をするかを記述したプログラムのアドレスを指す. ・BREAK または CTRL+C が押された時に実行されるアドレス(16~19バイト目) X-BASIC インタプリタが外部関数を実行中に BREAK または CTRL+C が押された(ブ レークされた)時、どのような動作をするかを記述したプログラムのアドレスを指す. ・X-BASIC の一行入力中に CTRL+D が押された時に実行されるアドレス(20~23バイト目) X-BASIC インタプリタがコマンド待ちの時に CTRL+D が押されたら、どのような動 作をするかを記述したプログラムのアドレスを指す. X-BASIC ではコマンド待ち時に CTRL+D が押されると外部関数をイニシャライズする. したがって、CTRL+D で関数を リセットしたい時などに、こおにリセットルーチンのアドレスを書いておく. ・予備(24~31バイト目) この後は(予備)のアドレスが2ロングワード続く. ここは将来の拡張に備えた予備 用の領域になっている. 将来の互換性を考え、rts 命令が入ったアドレスを指してお くこと. ・トークンテーブルの先頭アドレス(32~35バイト目) 定義した外部関数の名前を並べたテーブルのアドレスを指す. トークンとは外部関数の名前のことで、X-BASIC インタプリタが外部関数を呼び出 す時にはここに書かれた名前を使う. トークンテーブルには、この外部関数ファイル で定義する外部関数の全ての名前が格納されている. 関数名は、64文字までの英数字 でなければならない. ・パラメータテーブルの先頭アドレス(36~39バイト目) 個々の外部関数のパラメータに関するテーブルのアドレスを指す. 外部関数は当然ながらパラメータ付きで呼び出すことが出来るが、個々の関数がパ ラメータ(引数)を必要としているかどうか、必要ならその型と数、及びパラメータの 順序などの情報を記述したテーブルを「パラメータテーブル」と呼ぶ. X-BASIC イン タプリタが外部関数を呼び出す時には、このテーブルが示す場所の情報を使う(ただ しパラメータは10個まで). 注意しなければならないのは、ここに書かれているアドレスが示す先には各外部関 数のパラメータ情報が直接書かれているわけではないということである. ここにはパ ラメータテーブルの先頭があり、パラメータテーブルには各外部関数のパラメータに 関する情報が書かれた「パラメータIDテーブル」を指すアドレスが書かれている. 各 外部関数のパラメータに関する情報は、このパラメータIDテーブルに書かれている. ・実行アドレステーブルの先頭アドレス(40~43バイト目) 各トークン(関数名)ごとの実行アドレスを指す. 実行アドレスはトークンテーブルと同じ順序に並んでいる. このアドレスから先が実際の外部関数処理ルーチンである. ============================================================================== ・トークンテーブルの構造 個々の外部関数名はトークンテーブルに書かれていて、X-BASIC インタプリタはこの テーブルに書かれた関数名で外部関数を呼び出す. また、この後のパラメータテーブル、実行アドレステーブルもこのトークンテーブル に書かれた順序をもとに並んでいる. 関数名には最大の64文字までの英数字が使用できる. 関数名の最後はヌル文字(0)を 書き、これが一つのトークンとトークンの区切り(ターミネイタ)と見なされる. このような関数名(トークン)を複数個(一つでもよい)並べ、トークンテーブルが構成 されている. また、トークンテーブルの最後にはテーブルの終わりを示すエンドコード として 0 を置く. したがって、トークンテーブルの最後には 0 が二つ続く. 同じ関数名が複数個同じテーブルに定義されている場合は、先に登録された関数名が 有効となり、後で定義された関数名は無視される. (例)sample1、sample2、sample3 という関数名を登録する. token: .dc.b 'sample1',0 .dc.b 'sample2',0 .dc.b 'sample3',0 .dc.b 0 ============================================================================== ・パラメータテーブルの構造 パラメータテーブルは、個々の外部関数とパラメータ(引数)と戻り値に関する情報( パラメータの数(10個まで)、パラメータ・戻り値の型や順序)を管理するテーブルである. パラメータテーブルには実際のパラメータに関する情報を書いておくわけではなく、 ここには単に個々の関数をパラメータIDテーブルのアドレスが書かれている. そして、 パラメータIDテーブルに始めてパラメータと戻り値の情報(パラメータIDという)が並ん でいる. (例) param: .dc.l SAMPLE1_PAR .dc.l SAMPLE2_PAR .dc.l SAMPLE3_PAR このような複雑な方法でパラメータを管理しているのは、外部関数によってパラメー タの数や大きさ、戻り値の大きさが異なっているからである. というのは、X-BASIC イ ンタプリタが実行しようとする外部関数のパラメータの有無、呼び出された時のパラメ ータの型の可否を知るには、インフォメーションテーブルを見るしか方法がない. パラ メータの数や戻り値の有無は外部関数の設計によって千差万別であるから、これをイン フォメーションテーブルにそのまま書いていたのでは、個々の関数ごとにテーブルの大 きさが可変になってしまい、次の関数の検索が非常に面倒である. つまり、上図のパラメータIDテーブルの様に、'SAMPLE1_PAR'と'SAMPLE2_PAR'の間の 距離と、'SAMPLE2_PAR'と'SAMPLE3_PAR'との間の距離が異なってしまう. ところがこのような方法だと、パラメータテーブルの'SAMPLE1_PAR'から'SAMPLE2_PAR' に進むには単に4バイト加えるだけで済む(ロングワードのアドレスが4バイトであるこ とに注意). こうしてパラメータテーブルは、X-BASIC インタプリタからのアクセスを高速化して いる. パラメータ及び返り値は、パラメータIDテーブルに「パラメータID」という形で格納 されている. パラメータIDは長さ1ワードで、次表に示す内容である. パラメータ ┌────┬──────────────────────┬────────┐ │パラメータID│ 意 味 │fdef.hによる名前│ ├────┼──────────────────────┼────────┤ │ $0001 │8バイト浮動小数点型実数 │float_val │ │ $0002 │4バイト符号付き整数 │int_val │ │ $0004 │1バイト符号無し整数 │char_val │ │ $0008 │文字列 │str_val │ ├────┼──────────────────────┼────────┤ │ $0011 │浮動小数点型のデータ部のポインタ │float_vp │ │ $0012 │int型変数 〃 │int_vp │ │ $0014 │char型変数 〃 │char_vp │ │ $0018 │文字列型変数 〃 │str_vp │ ├────┼──────────────────────┼────────┤ │ $003f │1次元配列(全ての型) │ary1 │ │ $0032 │ 〃 (int型) │ary1_i │ │ $0037 │ 〃 (浮動小数点型、int型、char型) │ary1_fic │ │ $0034 │ 〃 (char型) │ary1_c │ │ $0054 │2次元配列( 〃 ) │ary2_c │ ├────┼──────────────────────┼────────┤ │ $0081 │省略可能な8バイト浮動小数点型実数 │float_omt │ │ $0082 │ 〃 4バイト符号付き整数 │int_omt │ │ $0084 │ 〃 1バイト符号無し整数 │char_omt │ │ $0088 │ 〃 文字列 │str_omt │ └────┴──────────────────────┴────────┘ 戻り値 ┌────┬──────────────────────┬────────┐ │パラメータID│ 意 味 │fdef.hによる名前│ ├────┼──────────────────────┼────────┤ │ $8000 │8バイト浮動小数点型実数 │float_ret │ │ $8001 │4バイト符号付き整数 │int_ret │ │ $8003 │文字列 │char_ret │ │ $ffff │戻り値なし │void_ret │ └────┴──────────────────────┴────────┘ ※char型は戻り値としては存在しない. このようにパラメータIDの値には意味に伴う規則性があり、まとめると以下の様になる. ・ビット15が、 1:戻り値 0:パラメータ ・ビット7が、 1:省略可能 0: 不可 ・ビット5と6が、d6 d5 0 0 :単純変数 0 1 :1次元変数 1 0 :2次元変数 ・ビット4が、 1:ポインタ 0:数値 ・ビット3が、 1:文字列型 0:それ以外 ・ビット2が、 1:char型(1バイト符号無し整数) 0:それ以外 ・ビット1が、 1:intr型(4バイト符号付き整数) 0:それ以外 ・ビット0が、 1:float型(8バイト浮動小数点型実数) 0:それ以外 (例)次のようなパラメータIDテーブルを持つ外部関数は、 ①パラメータは三つ. ②第一引数はint型数値、第二引数はfloat型数値、第三引数は省略可能でfloat型数値. ③戻り値なし. ということを表す. param: .dc.w $0002 .dc.w $0001 .dc.w $0081 .dc.w $ffff ※"fdef.h"について パラメータIDは数値であるから、そのままプログラムに使用すると可読性が悪くなる. 本ツールキット(XC)には"fdef.h"というヘッダファイルがあり、あらかじめそれぞれの IDに名前が付けられている. 外部関数プログラムの始めに"fdef.h"をインクルードしておくと誰にも分りやすく便 利である. 前の例を"fdef.h"を使用して書き直すと、 .include fdef.h ... param: .dc.w int_val .dc.w float_val .dc.w float_omt .dc.w void_ret となる. ============================================================================== ・パラメータの受け渡し (1)パラメータの受け渡し ここでは、実際に X-BASIC インタプリタから外部関数が呼ばれた時のパラメータの 受け渡し方法について説明する. X-BASIC インタプリタと外部関数の間では、パラメータはスタック経由で受け渡され る. 次の表はその状態を示す. ちなみに「オフセット」は、外部関数が呼び出される直前のレジスタ a7 の値(スタ ックポインタのアドレス)からのオフセットを表す. ┌─────┬─────┬───────────┐ │オフセット│サイズ(バイト)│ 内 容 │ ├─────┼─────┼───────────┤ │ 4 │ 2 │パラメータの総個数 │ │ 6 │10 │パラメータ1 │ │ 16 │10 │ 〃 2 │ │ 26 │10 │ 〃 3 │ │ … │… │ … │ │(n-1)x10+6│10 │ 〃 n │ └─────┴─────┴───────────┘ なお、パラメータテーブルで指定した以上のパラメータは無効. また、パラメータは最大10個まで. (2)パラメータのフォーマット スタックを介して引き渡されるパラメータは各10バイトあるが、その内訳は次の通り. ┌───┬─────┬─────────┐ │オフセット │サイズ(バイト)│ 内 容 │ ├───┼─────┼─────────┤ │ 0 │ 2 │パラメータの型 │ │ 2 │ 4 │値の上位4バイト │ │ 6 │ 4 │ 〃 下位〃 │ └───┴─────┴─────────┘ ・パラメータの型 X-BASIC インタプリタから外部関数に渡されるパラメータは、その値を見ただけでは どのような種類のものか区別できない. そこで、X-BASIC インタプリタは外部関数を呼 び出してパラメータをスタックにプッシュする際に、そのパラメータの型を値とともに 伝える. このことは、型によって意味のある値のバイト数が異なることを考えれば重要 である. ┌─────┬─────────────────┐ │型を示す値│ パラメータの型 │ ├─────┼─────────────────┤ │ 0 │8バイトの浮動小数点型小数(float型)│ │ 1 │4バイトの符号付き整数(int型) │ │ 2 │1バイトの符号無し整数(char型) │ │ 3 │文字列型(str型) │ │ $ffff │省略された引数(void型) │ └─────┴─────────────────┘ X-BASIC インタプリタは、パラメータを外部関数に渡す為にパラメータ一つあたり10 バイトのスタック領域を使う. このうち最初の 2 バイトは渡すパラメータの型を示す 上の表の値である. 残りの 8 バイトは(原則として)そのパラメータの実際の値である. この 8 バイトを FAC(Floating ACumulator)という. しかし、型によってパラメータの実効バイト数が異なる. すなわち、次のようになる. ・float型の場合、8 バイト全てを使用する. ・int型の場合、8 バイトのうち下位 4 バイトを使用する. 上位 4 バイトには全て 0 が入る. ・char型の場合、8 バイトのうち下位 1 バイトだけを使用し、上位 7 バイトには全て 0 が入る. ・文字列型の場合、8 バイトのうち下位 4 バイトを使用し、上位 4 バイトには全て 0 が入る. 下位 4 バイトには文字列そのものが入るのではなく、文字列型のデータ部へのポイ ンタとしてメモリアドレスが入る. ・ポインタの場合、文字列型と同じく 8 バイトのうち下位 4 バイトだけを使用し、上 位 4 バイトには全て 0 が入る. 下位 4 バイトにはデータ部へのポインタとしてメモリアドレスが入る. ・配列の場合、文字列型と同じく 8 バイトのうち下位 4 バイトだけを使用し、上位 4 バイトには全て 0 が入る. 下位 4 バイトには配列へのポインタとしてメモリアドレスが入る. 型の指定を除く 8 バイトにどのように値が格納されているかを知るためにも、最初 の 2 バイトの型の指定は重要である. 同様に、どんな値が X-BASIC インタプリタから 渡されるか分らないような場合にも、この型の指定を見れば判断できる. <FAC の構造> 上位 4 バイト 下位 4 バイト ┌───────────────┐ float型 │ データ │ ├───────┬───────┤ int型 │ 0 │ データ │ ├───────┴────┬──┤ char型 │ 0 │データ│ ├───────┬────┴──┤ 文字列型,ポインタ,配列 │ 0 │ ポインタ│ └───────┴───────┘ ※FAC……Floating ACumulator:引数などの値を格納する場所のこと. <パラメータの構造> ┌───────────┐←a7(スタック) ├ リターンアドレス ┤ ├───────────┤ │ パラメータの総個数 │+4 ┐ ├───────────┤ │ │ パラメータの型 │+6 │ ├───────────┤ │パラメータ1 ├ FAC 上位 4 バイト ┤+8 │ ├───────────┤ │ ├ FAC 下位 4 バイト ┤+12 ┘ ├───────────┤ │ パラメータの型 │+16 ┐ ├───────────┤ │ ├ FAC 上位 4 バイト ┤+18 │パラメータ2 ├───────────┤ │ <配列ポインタの構造> ┌───┬──────┬────────────────┐ │オフセット │ サイズ(バイト) │ 内 容 │ ├───┼──────┼────────────────┤ │ 0 │ 4 │スキップオフセット │ │ 4 │ 2 │次元数-1 │ │ 6 │ 2 │データのサイズ(1,4,8) │ │ 8 │ 2 │添字の大きさ(0~65535) │ │(10) │ (2) │(2次元目の添字の大きさ) │ │(10) │ (4) │(2次元の場合(?,0)の先頭のオフセット │ │10(16)│各データのサイズ│データ領域 │ └───┴──────┴────────────────┘ ※括弧内は二次元配列の場合 (例)1次元配列の場合のデータの引き渡し par1 .equ 6 move.l (par1+6,sp),a1 move (6,a1),d1 データのサイズ(1,4,8) move (8,a1),d0 添字の大きさ(0~65535) addq #1,d0 0でも1個あり mulu d1,d0 データ部の総数 lea (10,a1),a1 データ部の先頭ポインタ * a1 から d1 バイト単位で総数 d0 バイトが配列のデータ部 例として、"S_ASCII"という名前の外部関数を、 S_ASCII(sharp,x68,tech) というパラメータで呼んだとし、スタックを見てみる. ただし"sharp"は浮動小数点型数値、"x68"は文字列型データ部でのポインタ、"tech" は省略可能なchar型数値とし、戻り値は符号付き整数である. このような場合、呼び出された直後のスタックの状態は次のようになっているはずで ある. ┌───────────────────────┐←a7(スタック) ├ (外部関数実行完了時に戻るリターンアドレス) ┤ ├───────────────────────┤ │ 3(パラメータの総個数) │+4 ├───────────────────────┤ │ 0(1番目のパラメータ"sharp"の型) │+6 ├───────────────────────┤ ├ "sharp"の値の上位 4 バイト ┤+8 ├───────────────────────┤ ├ "sharp"の値の下位 4 バイト ┤+12 ├───────────────────────┤ │ 3(2番目のパラメータ"x68"の型) │+16 ├───────────────────────┤ ├ 0("x68"が文字列型なので、ここは使用しない) ┤+18 ├───────────────────────┤ ├ ("x68"のデータ部へのポインタ ┤+22 ├───────────────────────┤ │ 2(3番目のパラメータ"tech"の型) │+26 ├───────────────────────┤ ├ 0("tech"がchar型なので、ここは使われない) ┤+28 ├ ┤ ├ ┌───────┤ │ │("tech"の値) │+35 ├───────────────┴───────┤ ============================================================================== ・戻り値の受け渡し パラメータとは異なり、外部関数から X-BASIC インタプリタへの戻り値は一つ(また は 0)しかない. そこで、戻り値はレジスタを介して引き渡される. d0.l エラーコード(0 はエラーなし) a0.l 戻り値を格納したエリアへのポインタ a1.l エラーメッセージへのポインタ(エラー時のみ有効) 外部関数は、その実行が正常に終了したかどうかを X-BASIC インタプリタに知らせ るのに、d0 レジスタを使う. 外部関数の処理結果がエラーなら、エラーコードを d0.l に入れ表示したいエラーメッセージを格納したエリアのアドレスを a1.l に入れてリタ ーンする. X-BASIC インタプリタは、d0.l が 0 でなかったら指定されたエラーメッセ ージを表示してプログラムの実行を中断する. エラーでなければ(正常終了なら)、d0.l を 0 にし、戻り値を格納したエリアのアド レスを a0.l に入れてリターンする. 戻り値を格納するエリアは10バイト必要である. 格納の方法は、外部関数にパラメー タを引き渡す時のスタックの状態に似ていて、次のようにする. ┌───┬─────┬─────────────┐ │オフセット │サイズ(バイト)│ 内 容 │ ├───┼─────┼─────────────┤ │ 0 │ 2 │ワードで 0 を入れておく │ │ 2 │ 4 │返り値の上位ロングワード │ │ 6 │ 4 │ 〃 下位 〃 │ └───┴─────┴─────────────┘ 値を返さない外部関数を実行した場合、X-BASIC インタプリタは a0 レジスタを無視 するので、何が入っていても構わない(エラーの場合も同様). ==============================================================================