2021-03-18 12:57 — asano
カテゴリー:
長らくお待たせしました、MC68000でのブレーク処理です。
MC68000でブレークに使いやすい命令は何かを考えてみます。命令を置き換えて埋め込むので1ワード長のもの、ユーザモードで実行中でもスーパバイザモードに戻ってくるためには例外を発生させるものが良さそうです。もちろん無条件で実行される必要があります。
これらの条件を満たすものとして以下が考えられます。
TRAP
命令- 特権命令
- 未実装命令(Line-1010, Line-1111)
- 不当命令
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については原因となったアクセスの詳細情報の表示も書けるはずです。そのうちやりたいですね。
コメント
$4AFC、懐かしいですね。
$4AFC、懐かしいですね。
確か OS-9/68000 のモジュールヘッダの先頭に使われてたと思いました。
不当命令だったんですね。
Re: $4AFC、懐かしいですね。
マニュアルには「M68000ファミリと互換性を持つマイクロプロセッサでは$4AFA,$4AFBおよび$4AFCの3ビット・パターンは常に不当命令トラップを発生します. ...」とあります。
モジュールヘッダの先頭ということは前のモジュールからのフォールスルーに対する防護を兼ねたマジックですかね。
モジュールヘッダ~本体~CRC の構造なので
モジュールヘッダ~本体~CRC の構造なので、前のモジュールからは流れて来ない想定です。
ROM内のモジュール登録の際にサーチをするのですが、目印として、命令コードとしては一番
出現頻度の低いものを選んでいるんだと思います。
OS-9/6809 では $87,$CD でした。$CD は HCF なので、命令コードとしては絶対出ないです。
末尾にCRCあるならそうですね。
末尾にCRCあるならそうですね。
6800のHCF $9D,$DDは有名でしたが、6809にもあって$CDですか。しかし例外を発生する68Kの$4AFCはともかく、HCFは間違って実行した時のリスクが高いような...
コメントを追加