現在地

INS8073のROMを読む


カテゴリー:

INS8070ボードでROMのBASICの1文字入出力ルーチンを独自のものにしようとしてうまくいかなかったと書きましたが、モニタを移植したことでROM内を読めるようになったので関連箇所を調べてみることにしました。

まずはPコマンドでROMのアドレス範囲(0x0000~0x09FF)をPCに持ってきます。

そう、P(unch)コマンドを実装したのはこのためです。

手頃な逆アセンブラがないかネットを探してみたのですが見当たらなかったので簡単なものを書いてしまいました。

さて、どこから手を付けるかですが、先頭から読んでいくのはしんどいです。

今回調べたいのは文字入出力だけなのでメッセージから追っていくことにします。

メモリダンプを見ていくとASCII文字列がいくつもありますが、多くはBASICの予約語なので今回の目的には使えません。その中で良さそうなものが見つかりました。

0900 : D0 03 26 16 09 1E 85 C3 14 24 FB 04 45 52 52 4F :   &      $  ERRO
0910 : D2 53 54 4F 50 A0 41 D4 52 45 54 59 50 C5 C5 E6 :  STOP A RETYP   

エラーメッセージならかなり高い確率でそのまま表示するはずです。

そこで先頭アドレスである0x090Cを探してみると2箇所ありました。その近辺を逆アセンブルしてみます。

  1. 081D : 1F         CALL    15
    081E : 0C         SR      EA
    081F : 09         LD      T,EA
    0820 : C5 F7      LD      A,0xFFF7
  2. 08CF : 56         PUSH    P2
    08D0 : 26 0C 09   LD      P2,0x090C
    08D3 : 1E         CALL    14
    08D4 : 85 EA      LD      EA,0xFFEA

1.は偶然「0C 09」というバイト列が現れたようですが、2.の方はポインタレジスタに入れてサブルーチン呼び出しを行っています。CALL 14は文字列表示ルーチンの可能性が出てきます。

CALL命令はベクタを利用した(8086系でのINTのような)ものなのでベクタを見てみます。

0020 : 3B 06 EE 06 46 08 79 06 39 08 49 08 02 02 82 09 : ;   F y 9 I     
0030 : 7D 09 4C 05 01 08 13 06 5D 05 94 05 3E 05 CC 08 : } L     ]   >   

CALL 14のベクタアドレスは 0x0020 + 14×2 = 0x003C なので0x053F(SC/MPはフェッチ前にPCをインクリメントするので)から見てみます。

053F : C6 01      LD      A,@+1,P2
0541 : E4 0D      XOR     A,=0x0D
0543 : 6C 05      BZ      0x054A
0545 : E4 0D      XOR     A,=0x0D
0547 : 17         CALL    7
0548 : 64 F5      BP      0x053F
054A : 5C         RET     
  1. アドレスP2から1バイトずつ取り出し(053F, 0540)
  2. 0x0D(CR)だったら終了(0541~0544)
  3. Aに入れてCALL 7(0545~0547)
  4. MSBが立っていなかったら繰り返す(0548, 0549)
  5. 戻る(054A)

たったこれだけのルーチンですが、いろいろと興味深いですね。

  • 文字列の終端は[CR]か最後の文字のMSBを立てる
    そういえば予約語の最後MSB立ててテーブル作るBASICは多いです。(N BASICやPalo Alto Tiny BASICなんかも)
  • 比較にXOR
    これはなるほどです。
    SUBADDでもいいかな。モニタのswitch的な分岐ではEレジスタに退避していますが、元に戻すADDと次の比較のSUBを融合させれば短く(わかりにくく)なるな、とも考えていました。

    でも終端を0x00にしていれば面倒はないのになんで[CR]なのでしょう?

  • CALL 7は1文字出力かな
    出力時にはMSBを無視するらしいこと、MSBを含めてAレジスタは保存されることもわかります。

さてCALL 7のアドレスですが、CALL 14と同様に求めると0x0983からであるとわかります。

097E : C4 0D      LD      A,=0x0D
0980 : 17         CALL    7
0981 : C4 0A      LD      A,=0x0A

0983 : 0A         PUSH    A
0984 : 22 00 FD   PLI     P2,=0xFD00
0987 : C2 00      LD      A,0,P2
0989 : 64 32      BP      0x09BD
098B : 5E         POP     P2
098C : 38         POP     A

ちょっと前の0x097Eから見たらこれ [CR] [LF] の出力ですね。

本題の0x0983から見ていくと0xFD00のMSBで分岐しています。この0xFD00は"INS8073 NSC Tiny BASIC Microinterpreter"に次のように書かれています。

When the INS8073 is initialized, the desired baud rate is automatically selected by reading the contents of memory location X'FD00. To program the baud rate, this location must be decoded by external logic, and the appropriate logic levels supplied on data lines 1, 2, and 7. The baud rate is selected as follow.

D2 D1 Baud Rate
1 1 110
1 0 300
0 1 1200
0 0 4800

If the user-supplied subroutines are to be used for I/O, bit 7 of location FD00 should be zero (the other bits don't matter), and, locations FD01-FD02 contain the address of the character output routine. (Locations FD03-FD04 contain the address of the character input routine.) These address should first be decremented by one, the stored in these locations low byte first.

分岐しない0x098BからはMSBが立っていた場合で標準のソフトウェアUARTのルーチンです。ビット2,3を無視しているので、おそらくボーレートは共通のウエイトルーチンがあるのではないかと思います。

MSBが落ちていた(ユーザ提供ルーチンを使用)場合の0x09BD以降を見てみましょう。

09BD : 82 03      LD      EA,3,P2
09BF : 46         LD      P2,EA
09C0 : 84 C7 09   LD      EA,=0x09C7
09C3 : 08         PUSH    EA
09C4 : C1 04      LD      A,4,SP
09C6 : 76 FF      BRA     -1,P2
09C8 : 5E         POP     P2
09C9 : 38         POP     A
09CA : 5C         RET     
  1. P2にはまだ0xFD00が入ったままなので、0xFD03~0xFD04を読んでP2に入れます。(09BD~09BF)
  2. ユーザルーチンからの戻りアドレスをスタックに積みます。(09C0~09C3)
  3. スタックの奥から保存されているAの値を読みます。(09C4, 09C5)
  4. P2のアドレス(ユーザルーチン)に飛びます。(09C6, 09C7)
  5. 最後にスタックからレジスタ復帰して戻ります。(09C8~09CA)

なんか資料がいろいろ間違っているようです。まず出力ルーチンのアドレスは0xFD01, 0xFD02ではなく、0xFD03, 0xFD04に書き込まなくてはいけないようです。次に資料ではアドレスから1引けとありますが、この補正は上記4.で行われているのでそのままの値で良いはずです。下位バイトを先にはあっています。

続いて1文字入力ですが、メッセージ出力のようなわかりやすい手がかりがありません。

1文字出力ルーチンは最初に0xFD00の値で分岐していたので1文字入力も同様なのではないかと推測して0xFD00を探してみます。すると3箇所見つかりました。うち1箇所はもちろん上記の1文字出力ルーチンです。

もう一つは0x09E6です。

09E5 : 22 00 FD   PLI     P2,=0xFD00
09E8 : 85 EA      LD      EA,0xFFEA
09EA : C2 00      LD      A,0,P2
09EC : D4 06      AND     A,=0x06
09EE : B4 F8 09   ADD     EA,=0x09F8
09F1 : 46         LD      P2,EA
09F2 : 82 00      LD      EA,0,P2
09F4 : 8D EC      ST      EA,0xFFEC
09F6 : 5E         POP     P2
09F7 : 5C         RET     

0xFFEAに何が入っているのか未確認ですが、0xFD00のビット2,3でテーブルを引いているのでおそらくボーレートごとのループ回数か何かを切り替えているのではないかと思います。

残るは0x092Cです。

092B : 22 00 FD   PLI     P2,=0xFD00
092E : C2 00      LD      A,0,P2
0930 : 64 43      BP      0x0975
0932 : 5E         POP     P2

1文字出力とパターンが似ていますね。

ユーザルーチン使用の場合の0x0975以下を見てみます。

0975 : 84 F5 09   LD      EA,=0x09F5
0978 : 08         PUSH    EA
0979 : 82 01      LD      EA,1,P2
097B : BD E8      SUB     EA,0xFFE8
097D : 44         LD      PC,EA
  1. 戻りアドレスをスタックに積む。(0975~0978)
  2. 0xFD01からアドレスを読んで0xFFE8の値を引いてジャンプする。(0979~097D)

戻り先は前述のボーレートのところを共有していますね。アセンブリではよく使われるテクニックです。

0xFFE8には1が入っていると辻褄が合いますが、1文字出力のときのBRA -1,P2では駄目なのでしょうか? BRAの方が短いのですが。

0xFFE8は気になったので書き込んでいるところを探してみました。

00BC : 84 00 00   LD      EA,=0x0000
00BF : 8D EA      ST      EA,0xFFEA
00C1 : CD C2      ST      A,0xFFC2
00C3 : C4 01      LD      A,=0x01
00C5 : 8D E8      ST      EA,0xFFE8

0xFFEAには0、0xFFE8には1が固定で入れてあるようです。よく使う値はイミディエートで3バイト命令にするよりダイレクトで2バイト命令にするということでしょう。

ボーレートのところではE=0にするのに使っていますね。

(続く)

参考文献・関連図書: 
"INS8073 NSC Tiny BASIC Microinterpreter", National Semiconductor.

コメントを追加

Plain text

  • HTMLタグは利用できません。
  • ウェブページアドレスとメールアドレスは、自動的にハイパーリンクに変換されます。
  • 行と段落は自動的に折り返されます。
※ コメントは原則公開です。個別のご相談などは「ご意見・ご要望」からお願いします。