現在地

MC68000でブレーク


カテゴリー:

長らくお待たせしました、MC68000でのブレーク処理です。

MC68000でブレークに使いやすい命令は何かを考えてみます。命令を置き換えて埋め込むので1ワード長のもの、ユーザモードで実行中でもスーパバイザモードに戻ってくるためには例外を発生させるものが良さそうです。もちろん無条件で実行される必要があります。

これらの条件を満たすものとして以下が考えられます。

  1. TRAP命令
  2. 特権命令
  3. 未実装命令(Line-1010, Line-1111)
  4. 不当命令

TRAP命令はAPI的に使われることが多いのでできれば残しておきたいところです。

スーパバイザモードでブレークしたくなることも考えると特権命令も避けたいですね。

未実装命令もMC68020+MC68881などでは使われます。X68000がIOCSコールに未実装命令を使用していたのですが、X68030開発時にコプロセッサ命令との衝突がありIOCSコールの番号を変更せざるを得なかったという事例があります。

不当命令も将来使われるかもしれないではないか、やはりTRAP命令を使うしかないとなってしまいそうですね。

ところがモトローラが将来にわたって不当命令であることを保障したコードが存在します。それが$4AFC(他に$4AFA,$4AFBもありますがモトローラのために予約されています)です。

さらにMC68010以上では$4848~$484Fまでがブレークポイント不当命令として定義されています。これはMC68000でも不当命令例外を発生しますので問題なく使用可能です。MC68010だと特別なバスサイクルを発生させる(DTACK,VPA,BERRで応答しないといけない)ので却って使いにくいかもしれません。そういう場合は$4AFCがお勧めです。

今さら新たな(しかも命令が拡張されるような)デバイスが登場するとは考えにくいので「将来」を心配することもないですが...

そこで今回は$4AFC,$4848~$484Fの9つをブレークポイントとして扱うことにします。

例外ハンドラはMC68010の判別での判定のあと以下のようになっています。

    1006/  3007E0 :                     ILLINS0:
    1007/  3007E0 : =>TRUE              	IF USE_REGCMD
    1008/  3007E0 : 48F9 7FFF 0030      	MOVEM.L	D0-D7/A0-A6,REGD0
           3007E6 : 07E0             
    1009/  3007E8 : =>FALSE             	ELSE
    1010/  3007E8 :                     	MOVEM.L	D0-D3,REGD0
    1011/  3007E8 :                     	MOVEM.L	A0-A3,REGA0
    1012/  3007E8 : [1007]              	ENDIF
    1013/  3007E8 :                     
    1014/  3007E8 : 23FC 0030 07E8      	MOVE.L	#ILLINS_M,EXMSG
           3007EE : 0030 07E8          
    1015/  3007F2 : 33FC 0001 0030      	MOVE	#1,EXGRP	; Exception Group 1
           3007F8 : 07F2             
    1016/  3007FA :                     
    1017/  3007FA :                     	;; Check BREAK
    1018/  3007FA : 206F 0002           	MOVE.L	2(A7),A0	; Get PC
    1019/  3007FE : 3010                	MOVE	(A0),D0		; Read OP-code
    1020/  300800 : B07C 4AFC           	CMP	#$4AFC,D0
    1021/  300804 : 67FE                	BEQ	BREAK
    1022/  300806 : C07C FFF8           	AND	#$FFF8,D0
    1023/  30080A : B07C 4848           	CMP	#$4848,D0
    1024/  30080E : 66FE                	BNE	COMM_H0
    1025/  300810 :                     BREAK:
    1026/  300810 : 23FC 0030 0810      	MOVE.L	#BREAK_M,EXMSG
           300816 : 0030 0810          
    1027/  30081A : 33FC 0101 0030      	MOVE	#$0101,EXGRP	; BREAK
           300820 : 081A             
    1028/  300822 : 60FE                	BRA	COMM_H0

1007~1012行ではレジスタを保存しておきます。

1014行ではメッセージを一旦「Illegal Instruction」(不当命令)に設定しておきます。

1018行では例外発生時にスタックに積まれたPCを取り出します。不当命令例外の場合PCは原因となった命令を指していますので、1019行でその命令のオペコードを取得します。

その後オペコードが$4AFC,$4848~$484Fの場合だけメッセージを「BREAK」に変更します。不当命令例外でスタックに積まれるPCは原因となった命令を指しているので特に補正する必要はありません。

COMM_H0は共通の例外処理ルーチンです。

MC68000系の例外処理ルーチンはほぼ共通なので、表示するメッセージなど最小限の設定をした後は共通ルーチンに任せるようになっています。現状では個別に処理を行なっているのはこの不当命令例外くらいです。

それでは共通ルーチンを見ていきましょう。長いので分割しています。

    1198/  300A04 :                     	;; 
    1199/  300A04 :                     	;; Common handler
    1200/  300A04 :                     	;; 
    1201/  300A04 :                     COMM_H:
    1202/  300A04 :                     	;; Save registers
    1203/  300A04 : =>TRUE              	IF USE_REGCMD
    1204/  300A04 : 48F9 7FFF 0030      	MOVEM.L	D0-D7/A0-A6,REGD0
           300A0A : 0A04             
    1205/  300A0C : =>FALSE             	ELSE
    1206/  300A0C :                     	MOVEM.L	D0-D3,REGD0
    1207/  300A0C :                     	MOVEM.L	A0-A3,REGA0
    1208/  300A0C : [1203]              	ENDIF
    1209/  300A0C :                     
    1210/  300A0C :                     COMM_H0:
    1211/  300A0C :                     	

レジスタを保存します。R(egister)コマンドはメモリ節約のためにオプションになっています。Rコマンドが有効な場合はすべてのレジスタを保存しますが、無効な場合は一部のみの保存です。今のところ例外から戻ることはないのでRコマンド無効の場合は保存しなくても困りませんが...

ほとんどの場合COMM_Hから入ってきますが、不当命令例外の場合は既に保存済みですのでスキップしてCOMM_H0からになります。


    1212/  300A0C : =>TRUE              	IF USE_REGCMD
    1213/  300A0C :                     
    1214/  300A0C : 4A79 0030 0A0C      	TST	EXGRP
    1215/  300A12 : 66FE                	BNE	CH0		; not Group 0
    1216/  300A14 : 4A39 0030 0A14      	TST.B	PSPEC
    1217/  300A1A : 66FE                	BNE	CH0		; not MC68000/8
    1218/  300A1C :                     
    1219/  300A1C :                     	;; MC68000/8 : Group 0 (Bus/address error)
    1220/  300A1C : 41F9 0030 0A1C      	LEA	GR0BUF,A0
    1221/  300A22 : 30DF                	MOVE	(A7)+,(A0)+
    1222/  300A24 : 20DF                	MOVE.L	(A7)+,(A0)+	; Access address
    1223/  300A26 : 30DF                	MOVE	(A7)+,(A0)+	; Instruction register
    1224/  300A28 :                     CH0:
    1225/  300A28 :                     	;; Common
    1226/  300A28 : 33DF 0030 0A28      	MOVE	(A7)+,REGSR
    1227/  300A2E : 23DF 0030 0A2E      	MOVE.L	(A7)+,REGPC
    1228/  300A34 :                     
    1229/  300A34 : 4E68                	MOVE	USP,A0
    1230/  300A36 : 23C8 0030 0A36      	MOVE.L	A0,REGA7
    1231/  300A3C :                     	

MC68000/MC68008でグループ0(アドレスエラー・バスエラー)の場合、スタックトップに原因となった命令コードやアクセスしようとしたアドレスが積まれているので退避エリアに移動します。

これでスタックトップはプロセッサの種類・例外の種類に関係なくSRとPCになるので取り出して保存します。

USP(User Stack Pointer)も保存しておきます。

    1232/  300A3C : 4A39 0030 0A3C      	TST.B	PSPEC
    1233/  300A42 : 67FE                	BEQ	CH2
    1234/  300A44 :                     
    1235/  300A44 :                     	;; MC68010
    1236/  300A44 :                     	SAVE
    1237/  300A44 :                     	CPU	68010
    1238/  300A44 : 4E7A 0801           	MOVEC	VBR,D0
    1239/  300A48 : 23C0 0030 0A48      	MOVE.L	D0,REGVBR
    1240/  300A4E : 4E7A 0000           	MOVEC	SFC,D0
    1241/  300A52 : 13C0 0030 0A52      	MOVE.B	D0,REGSFC
    1242/  300A58 : 4E7A 0000           	MOVEC	SFC,D0
    1243/  300A5C : 13C0 0030 0A5C      	MOVE.B	D0,REGDFC
    1244/  300A62 : ALL                 	RESTORE
    1245/  300A62 :                     
    1246/  300A62 : 341F                	MOVE	(A7)+,D2	; Format / Vector offset
    1247/  300A64 : 33C2 0030 0A64      	MOVE	D2,REGFV
    1248/  300A6A : C47C F000           	AND	#$F000,D2
    1249/  300A6E : 4A42                	TST	D2
    1250/  300A70 : 67FE                	BEQ	CH2
    1251/  300A72 :                     	;; Format:1000 (29 Word)
    1252/  300A72 : 41F9 0030 0A72      	LEA	GR0BUF,A0
    1253/  300A78 : 7418                	MOVEQ	#25-1,D2
    1254/  300A7A :                     CH1:
    1255/  300A7A : 30DF                	MOVE	(A7)+,(A0)+
    1256/  300A7C : 51CA FFFC           	DBF	D2,CH1
    1257/  300A80 :                     	
    1258/  300A80 :                     CH2:

ここはMC68010の場合です。

1238~1243行では追加されたVBR,SFC,DFCの各レジスタを保存します。

MC68010ではまだスタックに情報が残っているのでトップの1ワードを取り出します。このワードの上位4ビットにあと何がスタックにあるかの情報があるので分岐します。

1000の場合は29ワード形式(アドレスエラー・バスエラー)です。既に4ワード読んでいるので残りの25ワードを取り出し保存しておきます。この中には命令の実行を継続するのに必要な情報が含まれていますが大半はブラックボックス、一部エラーの原因となったアクセス先などもあるので表示してみたいですね。今は保存しているだけで利用はしていません。

0000の場合は4ワード形式で残りはありません。

68020以降ではさらに別のパターンもあるので分岐を増やす必要があります。

    1259/  300A80 : 23CF 0030 0A80      	MOVE.L	A7,REGSSP
    1260/  300A86 :                     

例外によってスタックに積まれたものはすべて取り出したのでA7には例外発生直前のSSPが入っています。これも保存しておきます。

    1261/  300A86 : =>FALSE             	ELSE
    1262/  300A86 :                     	;; USE_REGCMD == 0
    1263/  300A86 :                     
    1264/  300A86 :                     	TST.B	PSPEC
    1265/  300A86 :                     	BNE	CH1
    1266/  300A86 :                     
    1267/  300A86 :                     	;; MC68000/8
    1268/  300A86 :                     	TST	D2
    1269/  300A86 :                     	BNE	CH0
    1270/  300A86 :                     
    1271/  300A86 :                     	;; Group 0
    1272/  300A86 :                     	ADD.L	#7*2,A7
    1273/  300A86 :                     	BRA	CH3
    1274/  300A86 :                     
    1275/  300A86 :                     CH0:
    1276/  300A86 :                     	;; Group 1-3
    1277/  300A86 :                     	ADD.L	#3*2,A7
    1278/  300A86 :                     	BRA	CH3
    1279/  300A86 :                     	
    1280/  300A86 :                     CH1:
    1281/  300A86 :                     	;; MC68010
    1282/  300A86 :                     	MOVE	6(A7),D2
    1283/  300A86 :                     	AND	#$F000,D2
    1284/  300A86 :                     	BNE	CH2
    1285/  300A86 :                     
    1286/  300A86 :                     	;; Format:0000
    1287/  300A86 :                     	ADD	#4*2,A7
    1288/  300A86 :                     	BRA	CH3
    1289/  300A86 :                     
    1290/  300A86 :                     CH2:
    1291/  300A86 :                     	;; Format:1000
    1292/  300A86 :                     	ADD	#29*2,A7
    1293/  300A86 :                     
    1294/  300A86 :                     CH3:	
    1295/  300A86 : [1212]              	ENDIF
    1296/  300A86 :                     

Rコマンド無効の場合は例外によってスタックに積まれたものをすべて捨てます。

    1297/  300A86 : 2079 0030 0A86      	MOVE.L	EXMSG,A0
    1298/  300A8C : 6100 FBF0           	BSR	STROUT
    1299/  300A90 : 6100 FC56           	BSR	CRLF
    1300/  300A94 : 6100 FBAC           	BSR	RDUMP
    1301/  300A98 :                     
    1302/  300A98 : 6000 F6CA           	BRA	WSTART

最後に例外の種類を示すメッセージを表示し、保存してあるレジスタも表示します。

なんかブレークについて書くといっておきながら例外処理一般の話になってしまいました。でもグループ0関連の処理以外はブレークに必要なものですから。

グループ0については原因となったアクセスの詳細情報の表示も書けるはずです。そのうちやりたいですね。

参考文献・関連図書: 
Motorola『M68000マイクロプロセッサ ユーザーズ・マニュアル 4th edition』, CQ出版社.

コメント

$4AFC、懐かしいですね。
確か OS-9/68000 のモジュールヘッダの先頭に使われてたと思いました。
不当命令だったんですね。

マニュアルには「M68000ファミリと互換性を持つマイクロプロセッサでは$4AFA,$4AFBおよび$4AFCの3ビット・パターンは常に不当命令トラップを発生します. ...」とあります。
モジュールヘッダの先頭ということは前のモジュールからのフォールスルーに対する防護を兼ねたマジックですかね。

モジュールヘッダ~本体~CRC の構造なので、前のモジュールからは流れて来ない想定です。
ROM内のモジュール登録の際にサーチをするのですが、目印として、命令コードとしては一番
出現頻度の低いものを選んでいるんだと思います。
OS-9/6809 では $87,$CD でした。$CD は HCF なので、命令コードとしては絶対出ないです。

末尾にCRCあるならそうですね。
6800のHCF $9D,$DDは有名でしたが、6809にもあって$CDですか。しかし例外を発生する68Kの$4AFCはともかく、HCFは間違って実行した時のリスクが高いような...

コメントを追加

Plain text

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