MC6800 ROMモニタ自作 その6 〜基本機能(D/M/Gコマンド)を作ってみた〜

前回はフェーズ2の16進入力処理を入れて、コマンドラインの土台を固めました。今回は、いよいよ ROM モニタの「モニタ」の基本機能を追加していきます。
具体的には、お馴染みの D(Dump)、M(Modify)、G(Go) の3つのコマンドを作ります。結論から言うと、いくつかの実装の落とし穴はありましたが、最終的に手打ちでマシン語を入れて走らせるところまでちゃんと成功しました。やったー。
今回やりたかったこと
ROM モニタとして最低限必要なのは、メモリの中身を見て、書き換えて、そこに処理を飛ばすことです。そのため、次のような仕様で機能を実装することにしました。
Dコマンド: 指定アドレスから16バイトのメモリを表示。右端には ASCII 文字も印字する。Mコマンド: アドレスの中身を表示して、新しい Hex 入力を待つ。空エンターでスキップ、.(ピリオド)で終了。Gコマンド: 指定アドレスへjmpしてユーザーコードを実行。
実は 2KB ROM に収めるという制約があるため、今回は「大文字のみ」を受け付けるシンプルなパーサとディスパッチャとして実装しました。
思わぬ落とし穴: jump distance too big エラー
コマンドごとの処理をガリガリとアセンブリで書いて、「さあビルド!」と make を叩いたところ、予期せぬエラーに遭遇しました。

あれ、そういえば MC6800 って相対分岐(bra や beq など)のジャンプ先が、現在地から -128 〜 +127 バイトの範囲に限られているんですよね。機能が増えてきたせいで、エラーハンドリング用のラベルまで届かなくなってしまったようです。
仕方がないので、分岐条件を逆転(beq ではなく bne でスキップさせる)させて、絶対ジャンプ(jmp)に繋ぐ処理を入れることで無事解決しました。このへんの制約を意識しながらコーディングするのは、地味に楽しいところです。
ユーザーコードからの「復帰」をどうするか
もう一つ重要だったのが、Gコマンドで飛ばしたあとの帰り道です。試しにプログラムを書いて実行したら、モニタのプロンプトに戻って来なくなってしまいました。
原因は、モトローラのシステムで伝統的に使われる SWI(Software Interrupt)のベクタ設定でした。割り込み先を単なるダミーの rti 命令に向けていたため、無限ループのようになって暴走していたのです。
今回は、最も確実かつシンプルな方法として、VEC_SWI をモニタの開始位置(RESET)へ向けるように修正しました。これで、ユーザー側で 3F (SWI) を置いておけば安全に ] へ戻ってきてくれます。
そして実際に動かしてみる
ということで、実際に動かしてみます。Aレジスタに $55 をロードして、$0110 番地に保存し、モニタへ SWI で戻るというプログラムを M コマンドで $0100 から打ち込んでみました。
そして、おもむろに G0100 を入力して実行! 一瞬で * が印字され、プロンプトに戻ってきました。続いて D0110 でダンプしてみると……。

無事 $0110 番地に 55 が格納されていました!
手打ちした数バイトのコードが狙い通りに動いて、狙い通りに戻ってくる。この瞬間は何度やっても感動です。
GitHub 側にも反映しました
今回実装した基本機能のコードは、Issue と PR としてまとめて GitHub 側に反映済みです。
今回のまとめ
- D(ダンプ)、M(変更)、G(実行) の三種の神器を実装した
jump distance too bigエラーを絶対ジャンプで回避したVEC_SWIをRESETに向け、ユーザーコードからの確実な復帰を実現した- 実際に簡単なコードを手打ちして動作することを確認した
今後の予定
次は、手打ちがつらくなってくるので「Hexローダー」を作りたいと思います。S-Recordフォーマットと Intel HEX フォーマットの両方を受け付けられるようにする予定です。一気に実用的になりそうで楽しみです!