2020-10-18 23:48 — asano
カテゴリー:
以前[UniMon] 固定エントリポイントで機能ごとに固定したエントリポイントを用意することでユーザプログラムから内部ルーチンを使えるようにする方法を用意しました。
でもよく考えるとこれはあまりよい方法ではなかった気がしてきました。
MC68000を考えるとサブルーチンコールではなくTRAP
を使いたいですし、MC68000のTRAP
命令は16通りしかありません。今のところ足りるとはいえ、できればエントリポイントは1つにしたいところです。
また世のOSなどを見ても機能コードを引数にしてエントリポイントは1つというのが主流に思います。かなり古いCP/Mですら、BDOSコールはCレジスタに機能コードを入れてCALL 0005H
(BIOSは機能ごとのエントリですが)でした。MS-DOSもAHレジスタに機能コードを入れてINT 21H
ですし、NetBSD(x86)のシステムコールはEAXに機能コードを入れてINT 80H
です。
そういえばX68000のIOCSコールもそうだったかな、と思って調べたら未実装命令例外使っていました。機能コードは命令の中に埋め込まれていますが、例外ベクタは1つなのでエントリポイントは1つです。
ということで手始めに8080, Z80版に追加してみました。
Cレジスタに機能コード(0からCSTART, WSTART, CONOUT, ...)を入れてRST 30H
(8080ではRST 6
、以後Z80ニーモニックのみ書くので8080には置き換えて読んでください)です。入出力のパラメータは固定エントリポイントから変更はありません。
これまで3バイトのCALL
命令だったのが、2バイトのLD C,n
と1バイトのRST 30H
になるので呼び出しコストはあまり変わりません。Cレジスタの保存が必要だとちょっと面倒ですが...
追加したコードはこれだけ(他に0030HにJP RST30H
があるだけ)です。
1387/ 835 : ;;;
1388/ 835 : ;;; RST 30H Handler
1389/ 835 : ;;;
1390/ 835 :
1391/ 835 : RST30H:
1392/ 835 : =>TRUE IF USE_NEWAPI
1393/ 835 :
1394/ 835 : E5 PUSH HL
1395/ 836 : C5 PUSH BC
1396/ 837 : 21 45 08 LD HL,APITBL
1397/ 83A : 06 00 LD B,0
1398/ 83C : 09 ADD HL,BC
1399/ 83D : 09 ADD HL,BC
1400/ 83E : 46 LD B,(HL)
1401/ 83F : 23 INC HL
1402/ 840 : 66 LD H,(HL)
1403/ 841 : 68 LD L,B
1404/ 842 : C1 POP BC
1405/ 843 : E3 EX (SP),HL ; Restore HL, jump address on stack top
1406/ 844 : C9 RET
1407/ 845 :
1408/ 845 : APITBL:
1409/ 845 : 53 08 DW API00 ; 00: CSTART
1410/ 847 : 58 08 DW API01 ; 01: WSTART
1411/ 849 : 8D 0B DW CONOUT ; 02: CONOUT
1412/ 84B : 49 07 DW STROUT ; 03: STROUT
1413/ 84D : 7F 0B DW CONIN ; 04: CONIN
1414/ 84F : 88 0B DW CONST ; 05: CONST
1415/ 851 : 5C 08 DW API06 ; 06: PSPEC
1416/ 853 :
1417/ 853 : ;; CSTART
1418/ 853 : API00:
1419/ 853 : E1 POP HL ; Drop return address
1420/ 854 : F3 DI
1421/ 855 : C3 00 01 JP CSTART
1422/ 858 :
1423/ 858 : ;; WSTART
1424/ 858 : API01:
1425/ 858 : E1 POP HL ; Drop return address
1426/ 859 : C3 64 01 JP WSTART
1427/ 85C :
1428/ 85C : ;; PSPEC
1429/ 85C : API06:
1430/ 85C : 3A 1B FF LD A,(PSPEC)
1431/ 85F : C9 RET
1432/ 860 :
1433/ 860 : =>FALSE ELSE ; USE_NEWAPI
1434/ 860 :
1435/ 860 : RET
1436/ 860 :
1437/ 860 : [1392] ENDIF ; USE_NEWAPI
まず1394,1395行目でレジスタを保存しておきます。HLはSTROUTの引数なので必ず保存しなくてはなりません。
1396~1399行目まででHL=テーブル先頭アドレス+C×2を求めます。BCを2倍するより2度足すほうが短く速いはず。あと本来はCのレンジチェックをすべきですがAレジスタの保存が必要になったり結構面倒なので省略しています。
1400~1403行でテーブルから飛び先アドレスを取得します。
1404行でBCレジスタを復帰させます。この時点でスタックトップには保存したHLレジスタの内容、その下にはユーザプログラムへの戻りアドレスがあります。
1405行でスタックトップとHLレジスタを交換、これで飛び先がスタックトップに置かれHLレジスタを復帰できました。
1406行のRET
は元のルーチンへの復帰ではなく、処理ルーチンへのジャンプです。HLレジスタに任意の値を入れてテーブルジャンプするときの定石ですね。
1408行からは各処理ルーチンへのジャンプテーブルです。
1417~1421行はCSTARTの処理、RST 30H
命令を使う都合上どうしても戻りアドレスがスタックにつまれてしまうのですが戻る必要はないので捨てています。
1423~1426行も同様にWSTARTの処理です。
1428~1431行、プロセッサ判別の結果を取得する機能を追加してみました。
次は他のプロセッサへの展開なのですが、まだちょっと考えがまとまらないので...
コメントを追加