2022-07-28 23:11 — asano
カテゴリー:
前回の最後に複合命令の処理がイマイチと書いたのは次のようなことです。
SNL MQA
SNL
はグループ2ですから次のMQA
は共通命令テーブルとグループ2の命令テーブルを探すことになります。ところがこれはグループ3なので当然見つかりません。
結果として"unknown instruction"エラーになるわけですが、MQA
は存在する命令でただSNL
とグループが違っているだけです。"invalid instruction combination"エラーの方がより親切というものでしょう。
そこですべてを1つの命令テーブルに入れることにしました。各命令ごとに複合命令にできるか、できるならどのグループかの情報を付加して組み合わせられるか判定します。
ここで1つ問題があります。これまでデコードルーチンに渡される符号なし16ビットのパラメータの、上4ビットにパネルモードや6120のメモリ拡張に関するフラグを入れ、下12ビットにオペコードを入れていたのですが追加情報を入れる空きがもうありません。
仕方ないので次のような配列を作りました。
typedef struct {
char *NName;
InstProc Proc;
Word NCode[3];
Word Flags;
} tInstInfo;
static tInstInfo InstInfo[] = {
/* Memory Reference Instruction */
{"AND", DecodeMR, {00000, 0, 0}, 0},
{"TAD", DecodeMR, {01000, 0, 0}, 0},
{"ISZ", DecodeMR, {02000, 0, 0}, 0},
{"DCA", DecodeMR, {03000, 0, 0}, 0},
{"JMS", DecodeMR, {04000, 0, 0}, 0},
{"JMP", DecodeMR, {05000, 0, 0}, 0},
/* Operate Instruction (Group 1) */
{"NOP", DecodeOP, {07000, 07400, 07401}, F_Combi | F_GP1 | F_GP2 | F_GP3},
{"IAC", DecodeOP, {07001, 0, 0}, F_Combi | F_GP1},
{"RAL", DecodeOP, {07004, 0, 0}, F_Combi | F_GP1},
{"RTL", DecodeOP, {07006, 0, 0}, F_Combi | F_GP1},
{"R3L", DecodeOP, {07014, 0, 0}, F_6120 | F_Combi | F_GP1},
{"RAR", DecodeOP, {07010, 0, 0}, F_Combi | F_GP1},
{"RTR", DecodeOP, {07012, 0, 0}, F_Combi | F_GP1},
.....
InstTable
にはフラグ+オペコードの代わりにこの配列のインデックスを登録します。各デコードルーチンでは渡されたインデックスでこの配列からオペコードや必要な情報を取り出します。
例えばNOP HLT MQL
という複合命令があったとしましょう。
まず1つ目のNOP
を見るとグループ1,2,3すべてに属しそれぞれの場合のオペコードは7000(8), 7400(8), 7401(8)であることがわかります。この時点ではグループは1, 2, 3のいずれであるかは確定しません。
次のHLT
のデータを見てみましょう。
.....
/* Operate Instruction (Group 2) */
{"HLT", DecodeOP, { 0, 07402, 0}, F_Combi | F_GP2},
.....
これはグループ2のみなので、グループ2が確定します。オペコードは7400(8)(NOP
のグループ2の場合のコード)と7402(8)のORです。
続いて最後のMQL
のデータを見てみましょう。
.....
/* Operate Instruction (Group 3) */
{"MQL", DecodeOP, { 0, 0, 07421}, F_Combi | F_GP3},
.....
おっと、これはグループ3ですので組み合わせられないことが判明しました。"invalid instruction combination"エラーです。
もし途中で配列にないものが現れたら"unknown instruction"エラーです。
これで必要な機能は揃ったとHD1-6120ボード用にUniversal Monitorの移植を始めたのですが...
このアーキテクチャ、イミディエイト命令が存在しないので定数の扱いが非常に厄介です。
AC
を0にクリアするならCLA
です。
1にするならCLA IAC
、クリアしてからインクリメント、命令長もクロックもクリアだけと一緒です。
-1ならCLA CMA
、クリアしてからビット反転します。
2, 4はCLA CLL IAC RAL
とかCLA CLL IAC RTL
でできるのかな。データシートによるとこの順序で実行されるらしいからクリアしてインクリメントして左シフトでできると思うのだけど...
BSW
も使えば64も作れるかな。でもこういう特殊な値以外はメモリに置いておいて参照します。
.....
CLA
TAD label1
.....
label1: DC constant
.....
定数を使うたびにDC
疑似命令を書くのはよいとして、毎回ラベルを付けなくてはいけないのは面倒です。同一ページ内にないといけませんし、同じ値なら使いまわしたいところです。
Temporary Symbols も使えないか試したのですが使い勝手が悪く... などと考えていたところSHのLTORG
機能を思い出しました。SHは8ビットまでならイミディエイトが使えますが、それを超えたらメモリに定数を置いて参照するしかありません。あまりに面倒なのでメモリに定数を置いて参照するコードを自動的に生成できるのです。IM6100にも同じ機能を搭載することにしました。
そもそもイミディエイトという概念がないので文法を拡張する必要があります。
TAD L const
のように書くとconstがどこかのメモリに置かれ、それを参照するコードが生成されます。どこに置くかはLTORG
疑似命令で指定します。同一ページ内でないといけないので通常はページ末尾に置くことになるでしょう。同一値はまとめられるので書き換えるISZ
, DCA
ではエラーにしています。
JMS IL const
のような組み合わせも可能でページ外のサブルーチン呼び出しに使えます。Indirectと組み合わせる場合はISZ
命令などもOKです。
これでモニタの移植も捗って基本機能が動くようになり、その過程でASのバグも修正し一区切りついたかなということで先ほどパッチを AS-users ML の方に送りました。
次回はモニタ移植の話を予定しています。
2022-08-07 追記:
AS 1.42bld228 に無事入りました。
公式ソースでは上記命令テーブルの部分は配列の初期化ではなくヒープに確保したメモリを埋めていく方式に変更されています。
私はすっかり忘れていたのですがASはDOSの16ビット環境用にもビルドできるので。
コメントを追加