MC6800 ROMモニタ自作 その11 〜D改善、SWIブレーク、簡易逆アセンブルを入れる〜

MC6800 ROMモニタMC6800 ROMモニタ自作 その11 〜D改善、SWIブレーク、簡易逆アセンブルを入れる〜

前回は、MIKBUG 互換の固定入口を足して、SBC6800 データパックの HELLOMICBAS13 を動かしました。これで、ROMモニタとしては「ロードして実行する」ところまではかなり使えるようになりました。

ただ、実機で小さいプログラムを試していると、次に欲しくなるのは「もう少し中を見たい」という機能です。メモリを見たい。範囲を指定して見たい。途中で止めたい。止まったときのレジスタを見たい。ついでに、今そこにあるコードを少し逆アセンブルして見たい。

今回はそのあたりをまとめて入れました。

今回の追加要件を D 改善、SWIブレーク、簡易逆アセンブルに分けて進めた

今回やりたかったこと

今回やりたかったのは、ROMモニタを「実行するだけ」から「少し追える」方向へ進めることです。

  • D コマンドで範囲指定できるようにする
  • RAM 上のプログラムに SWI ブレークポイントを置けるようにする
  • ブレークで止まったときにレジスタを表示する
  • 簡易逆アセンブル U コマンドを入れる

ついでに、使い方を忘れないように、コマンドリファレンスも作りました。要件書にコマンド一覧はあったのですが、実際に端末からどう打つか、何が出るか、という利用者向けの文書はまだなかったので、ここも今回まとめました。

D コマンドを範囲指定できるようにした

まずは D コマンドです。今までもメモリダンプはできましたが、開始アドレスから見るだけでした。実機で確認していると、「この 32 バイトだけ見たい」とか「前回の続きからもう 64 バイト見たい」という場面がけっこうあります。

`D
D0100
D0100-011F`

D は継続アドレスから 64 バイト表示します。D0100$0100 から 64 バイト表示します。D0100-011F$0100 から $011F までを表示します。終了アドレスは inclusive、つまり表示範囲に含めます。

D0100 と範囲指定でメモリを確認しているところ

実装としては、まず引数に - があるかを見て、単独アドレスか範囲指定かを分けています。範囲指定のときは、開始アドレスが終了アドレスより大きければ ? にします。

`PARSE_DUMP_RANGE:
        tst     ARG2_LEN
        beq     PARSE_DUMP_FAIL
        ...
        ldx     DUMP_ADDR
        jsr     CMP_X_DUMP_END
        bhi     PARSE_DUMP_FAIL
        clc
        rts`

地味に入れておいたのが、$FFFF 付近の扱いです。例えば DFFF0 は 64 バイト表示しようとすると、普通に足し算すると $0000 側へラップしてしまいます。これは見た目にも危ないので、$FFFF までで止めるようにしました。

SWI を使うソフトウェアブレークを入れた

次にブレークポイントです。外部回路を使ったハードウェアブレークではなく、今回はソフトウェアブレークにしました。指定アドレスの 1 バイトを保存して、そこに SWI 命令、つまり $3F を書き込みます。

`] B0105
] G0100
BRK 0105 A=42 B=00 X=0105 SP=1F20 CC=C0
]`

RAM 上のプログラムに SWI ブレークを設定して停止したところ

ブレークポイント v1 は単一箇所のみです。複数ブレークポイントは欲しくなりそうですが、まずは単一で十分です。ROM容量も RAM ワークも無限ではないですし、実機確認用としては、1箇所止められるだけでもかなり便利です。

ROM 領域への B はエラーにしています。この方式は対象アドレスへ $3F を書くので、ROM には置けません。SBC6800 では RAM 上にロードした小さいプログラムを追う用途に絞っています。

止まったときにレジスタを表示する

`BRK 0105 A=12 B=34 X=2000 SP=1F20 CC=C0`

見たいのは、まず「どこで止まったか」です。次に、ABXSPCC がどうなっているかです。

ここで少し気をつけたのが、PC の扱いです。MC6800 の SWI では、スタック上に積まれる復帰 PCSWI 命令の次を指します。でも、ブレークポイント表示として見たいのは SWI の次ではなく、ブレークを置いたアドレスです。

`        ldaa    BRK_SAVE_PC
        staa    BRK_ADDR
        ldaa    BRK_SAVE_PC+1
        staa    BRK_ADDR+1
        ldx     BRK_ADDR
        dex
        stx     BRK_ADDR`

さらに R で再開するときも、元命令から実行し直したいです。そのため、ブレークヒット時に元バイトを復元し、保存しておいた SWI フレームの PC もブレークアドレスへ戻します。

ここはけっこう大事です。もし PCSWI の次のままだと、元命令を飛ばしてしまいます。ブレークポイントで止まったのに、再開したら止めた命令を実行しない、ということになります。

簡易逆アセンブル U コマンド

次は U コマンドです。これは本格的な逆アセンブラではありません。指定アドレスから 8 命令だけ、主要命令を表示します。未対応 opcode は DB $xx として 1 バイト進めます。

`] U0100
0100 86 LDAA #$12
0102 B7 STAA $0120
0105 3F SWI
0106 FF DB $FF
]`

U コマンドで HELLO 付近を簡易逆アセンブルしているところ

対応したのは、今の monitor やデータパック確認でよく見る命令を中心にしています。未対応命令を全部エラーにするのではなく、DB $xx で表示して進めるようにしたのは、実機確認ではその方が便利だからです。

簡易アセンブラは今回は入れていません。ROM の中でアセンブラまで持つより、短いパッチは M、まとまったコードは PC 側でアセンブルして L で流す方が、今の段階では現実的だと思います。

コマンドリファレンスも作った

今回の最後に、コマンドリファレンスも作りました。今までも要件書にはコマンド一覧がありました。ただ、要件書は「何を作るか」を書く場所なので、実際に端末からどう打つか、何が表示されるか、どこに制限があるか、という使い方の説明としては少し弱かったです。

そこで docs/usage/monitor_commands.md を追加しました。

追加した ROMモニタ コマンドリファレンス

この文書には、今回追加したコマンドだけでなく、既存の MGL も含めて、端末操作としての使い方を書いています。

今回のまとめ

  • D コマンドの 64 バイト表示と範囲指定
  • SWI を使う単一ソフトウェアブレークポイント
  • ブレーク停止時のレジスタ表示
  • R による元命令からの再開
  • U コマンドによる簡易逆アセンブル
  • 利用者向けコマンドリファレンス

これで、「ロードして動かす」から「止めて見る」方向へ少し進みました。本格的なデバッガではありませんが、8bit 機の ROMモニタとしてはだいぶ触りやすくなったと思います。

今回の対応はgithubのこちら。

特に BRU が入ると、RAM 上に小さいコードを置いて試すときの見通しがかなり変わります。次はこの機能を使って、BASIC やデータパック側の挙動確認をもう少し進めていけそうです。

MC6800 ROMモニタSBC6800