ひとまず仕様をfixさせ、ユーザーズガイドを作成しました。結局構文解析部分は命令毎に規則を書き下す方式にしました。状態遷移表の状態数は740状態、遷移数は3445個です。これくらいなら許容範囲かな?
規則部はこんな感じ。各命令に対応するシンボルから始まり、それぞれの命令で指定可能なアドレシングモードの規則を記述しています。文法違反の場合は、特別なエラー命令(ErrorInstruction)を生成し、パーサから抜けた後に命令リストをスキャンし、まとめてエラー表示する方式とします。
ADCA expression:val {: if (ulen(val) != 1) { warning(String.format("operand out of range: %d(0x%h)", val, val)); val = val & 0xff; } RESULT = new ADCA(Instruction.ADDRESSING_IMMEDIATE, 1, val); next_addr = addr + 2; :} | ADCA LBRACE expression:val RBRACE {: if (ulen(val) != 1) { warning(String.format("operand out of range: %d(0x%h)", val, val)); val = val & 0xff; } RESULT = new ADCA(Instruction.ADDRESSING_DIRECT, 1, val); next_addr = addr + 2; :} | ADCA LBRACKET expression:val RBRACKET {: if (ulen(val) != 1 && ulen(val) != 2) { warning(String.format("operand out of range: %d(0x%h)", val, val)); val = val & 0xffff; } RESULT = new ADCA(Instruction.ADDRESSING_EXTENDED, 2, val); next_addr = addr + 3; :} | ADCA LBRACKET REG_X PLUS expression:val RBRACKET {: if (ulen(val) != 1) { warning(String.format("operand out of range: %d(0x%h)", val, val)); val = val & 0xff; } RESULT = new ADCA(Instruction.ADDRESSING_INDEXED, 1, val); next_addr = addr + 2; :} | ADCA LBRACKET REG_X RBRACKET {: RESULT = new ADCA(Instruction.ADDRESSING_INDEXED, 1, 0); next_addr = addr + 2; :} | ADCA error {: RESULT = new ErrorInstruction(scanner.getLineNumber(), "ADCA immediate|direct|indexed|extended"); :} | 以下続く
ちょっと悩んだのがアドレシングモードのうちDirectモードとExtendedモードの取り扱い。オペランドに[アドレス]
と書くと指定したアドレスの値を参照するのですが、アドレス値が1バイトか2バイトでopcodeが変える必要があるのです。アドレス部分は定数値なので理屈上は自動判定可能です。数値定数で書くなら、0x10なら1バイト、0x0010なら2バイトというように自動判定しても良いのですが、ラベルを含む定数式を書けるようにしたことで問題を難しくしました。ちょっと極端ですが、こんな例を考えます。
.ORG 0xFD ADCA [TABLE] TABLE: .DB 0x10 ; このアドレスは0xFFか0x100か?
ADCA
命令のアドレスは0xFDなので、ADCA
命令が2バイト命令(Directモード)ならTABLE
のアドレスは0xFF、3バイト命令(Extendedモード)なら0x100となります。つまりこのADCA
命令はDirectモードで記述できることが分かります。メモリがふんだんにあるいまどきのマシンならすべてExtendedモードとしてしまえと言いたくなりますが、6800系マシンはメモリ資源が貴重ですから、できるだけプログラムは短くしたい。
さて、これはアセンブラからどう見えるか?
ADCA
命令はアドレスラベルTABLE
を前方参照しているので、TABLE
の指すアドレスが1バイトなのか2バイトなのかは1パス目では決定できず、TABLE
までのすべての命令長を合算し、TABLE
のアドレスを決定する必要があります。この例の場合はTABLE
のアドレスは「0xFD+ADCA
命令のバイト長」です。
しかしADCA
命令のバイト長はTABLE
のアドレスが決まらないと分かりません。……あれ? 循環してしまいました。
ここから言えるのは、DirectモードかExtendedモードかの自動判定にはヒューリスティックを持ち込まないと決定できないということです。
今回はヒューリスティックを持ち込まず、それぞれのモードの書式を変えることで対処しました。つまりユーザ任せです 😉
Directモードなら {アドレス}、Extendeモードなら [アドレス] とします。
コメント