|
|
1.1 root 1: //
2: // nono
3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
5: //
6:
1.1.1.5 root 7: //
1.1 root 8: // ブランチ履歴
1.1.1.5 root 9: //
1.1 root 10:
11: #include "branchhistory.h"
12: #include "m68030core.h"
13: #include "m88100.h"
1.1.1.5 root 14: #include "vectortable.h"
1.1 root 15:
16: // コンストラクタ
1.1.1.4 root 17: BranchHistory::BranchHistory(bool is_exhist_)
1.1.1.5 root 18: : inherited(is_exhist_ ? "(ExceptionHistory)" : "(BranchHistory)")
1.1 root 19: {
1.1.1.4 root 20: is_exhist = is_exhist_;
21:
1.1.1.5 root 22: // ログは不要
23: ClearAlias();
24:
25: monitor.func = ToMonitorCallback(&BranchHistory::MonitorUpdate);
1.1.1.6 ! root 26: monitor.SetSize(55, 1 + 32); // ヘッダ1行とエントリ数
! 27: monitor.SetMaxHeight(1 + 256);
1.1.1.4 root 28: if (is_exhist) {
29: monitor.Regist(ID_SUBWIN_EXHIST);
30: } else {
31: monitor.Regist(ID_SUBWIN_BRHIST);
32: }
1.1 root 33: }
34:
35: // デストラクタ
36: BranchHistory::~BranchHistory()
37: {
38: }
39:
40: // 初期化
41: void
42: BranchHistory::Clear()
43: {
44: top = 0;
45: for (int i = 0; i < countof(entry); i++) {
46: BranchEntry& e = entry[i];
47: e.count = 0;
48: e.from = 0xffffffff;
49: e.to = 0xffffffff;
50: e.inst = 0;
51: }
52: }
53:
54: // 使用中のエントリ数を取得する
55: int
56: BranchHistory::GetUsed() const
57: {
58: int i = 0;
59:
60: // top の位置から一巡するまで。有効エントリは count > 0。
61: for (; i < 256; i++) {
62: if (entry[(256 + top - i) % 256].count == 0) {
63: break;
64: }
65: }
66: return i;
67: }
68:
69: // モニタ更新
70: void
1.1.1.4 root 71: BranchHistory::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 72: {
73: uint8 t;
74: int y;
75: int ydelta;
76: int row;
77:
78: // userdata は表示開始位置と各種フラグ。
79: // 下位 32bit が表示開始位置で、0 なら先頭のエントリから、1 なら先頭の
1.1.1.4 root 80: // 一つ次のエントリから、…を表す (表示行数は screen の高さ分)。
1.1 root 81: // BottomToTop が %1 なら並び順を下から上に変える (コンソール用)。
1.1.1.4 root 82: bool bottom_to_top = (screen.userdata & BottomToTop);
83: int pos = (int)screen.userdata;
1.1 root 84:
85: // 0 1 2 3 4 5 6
86: // 0123456789012345678901234567890123456789012345678901234567890123456789
87: // No. FromAddr Instruction ToAddr Iteration
88: // 001 U:01234567(01234567:bcnd.n) -> $01234567 x123456789
89: // 001 U:01234567(0123:trap ) -> $01234567 x123456789
90: // 002 S:01234567< 2> Bus Error
91: // 002 S:01234567< 69> MFP Timer-C
92: // 01234567890123456789012
93:
1.1.1.4 root 94: screen.Clear();
95: row = screen.GetRow();
1.1 root 96:
97: // 最初の1行は常にヘッダ
98: if (is_exhist) {
1.1.1.4 root 99: screen.Print(0, 0, "No. FromAddr Vec. Exception");
100: screen.Print(45, 0, "Iteration");
1.1 root 101: } else {
1.1.1.4 root 102: screen.Print(0, 0, "No. FromAddr Instruction");
103: screen.Print(35, 0, "ToAddr Iteration");
1.1 root 104: }
105:
106: // pos は最新を 0 とした通し番号。(スクロールしてたら開始が 0 とは限らない)
107: // t が entry[] 上の現在位置。
108: // y は表示座標。(上から表示か下から表示かが変わる)
109: t = top - pos;
110: int pos_end = pos + row - 1;
111: if (bottom_to_top == false) {
112: y = 1;
113: ydelta = 1;
114: } else {
115: y = row - 1;
116: ydelta = -1;
117: }
118: for (; pos < pos_end; pos++, y += ydelta) {
119: BranchEntry& e = entry[t--];
120: if (e.count == 0) {
121: continue;
122: }
123: std::string str = FormatEntry(e);
1.1.1.4 root 124: screen.Print(0, y, "%3d %s", pos, str.c_str());
1.1 root 125: if (e.count > 1) {
1.1.1.4 root 126: screen.Print(45, y, "x%9u", e.count);
1.1 root 127: }
128: }
129: }
130:
131:
132:
133: //
134: // m680x0 ブランチ履歴 (どこに置くのがよいか)
135: //
136:
137: // m680x0 ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによる
138: // ジャンプの3つを区別する。
139: //
140: // 通常の分岐:
1.1.1.3 root 141: // from は上位31ビットが分岐元 PC、最下位ビットが S/U、
142: // to は32ビット全体が分岐先 PC、
143: // inst の上位16ビットに命令の1ワード目。
1.1 root 144: // inst の $8000 が立っていないことで区別する。
145: //
146: // 例外発生:
1.1.1.3 root 147: // from は上位31ビットが例外発生時の PC、最下位ビットが S/U、
148: // to は不問(通常 0 をセットすること)、
1.1 root 149: // inst は $00008LVV で $8000 が例外フラグ、VV がベクタ番号である。
150: // ベクタ番号が $00 だと次項(ベクタによる分岐)と区別つかなくなってしまう
151: // ため、リセット例外 (ベクタ番号0) は V=$01 で記録する。
152: // L は割り込みなら割り込みレベル。
153: // inst に $8000 が立っていて、かつ $8000 と一致しないことで区別する。
154: //
155: // 例外ベクタによる分岐 (例外処理の最後に起きる):
1.1.1.3 root 156: // from はベクタアドレス、
157: // to は分岐先 (0 も起こりえる)、
158: // inst は $00008000。
1.1 root 159: // inst が $00008000 と一致することで区別する (ビットが立っているではない)。
160:
161: // コンストラクタ
1.1.1.4 root 162: BranchHistory_m680x0::BranchHistory_m680x0(bool mode_)
163: : inherited(mode_)
1.1 root 164: {
165: }
166:
167: // デストラクタ
168: BranchHistory_m680x0::~BranchHistory_m680x0()
169: {
170: }
171:
172: // 1エントリ分の表示内容作成
173: std::string
174: BranchHistory_m680x0::FormatEntry(const BranchEntry& e)
175: {
176: // m68k では from の最下位1ビットを S/U として使う
177: std::string desc = string_format("%c:%08x",
178: (e.from & 1) ? 'S' : 'U', (e.from & ~1));
179:
180: if ((e.inst & 0x8000)) {
181: if ((e.inst & 0x7fff) != 0) {
182: // 例外発生記録
183: // 発生アドレスとベクタ(と割り込みレベル)を表示
184: uint32 vector = e.inst & 0xff;
185: if (vector == 1) {
186: vector = 0;
187: }
188: // とりあえず
1.1.1.5 root 189: const char *name = gVectorTable->GetExceptionName(vector);
190: desc += string_format("<%3d> %s", vector, name ?: "");
1.1 root 191: // XXX TODO 割り込みレベル
192: } else {
193: // 例外ベクタフェッチしてジャンプ
194: desc += string_format("(vector fetch) -> $%08x", e.to);
195: }
196: } else {
197: // ブランチ
198: const char *mnemonic;
199: uint32 inst = e.inst >> 16;
200: if (inst == 0x4e73) {
201: mnemonic = ":rte";
202: } else if (inst == 0x4e75) {
203: mnemonic = ":rts";
204: } else if (inst == 0x4e77) {
205: mnemonic = ":rtr";
206: } else if ((inst & 0xffc0) == 0x4e80) {
207: mnemonic = ":jsr";
208: } else if ((inst & 0xffc0) == 0x4ec0) {
209: mnemonic = ":jmp";
210: } else if ((inst & 0xfff8) == 0x51c8) {
211: mnemonic = ":dbra";
212: } else if ((inst & 0xf0f8) == 0x50c8) {
213: mnemonic = ":dbcc";
214: } else if ((inst & 0xff00) == 0x6000) {
215: mnemonic = ":bra";
216: } else if ((inst & 0xff00) == 0x6100) {
217: mnemonic = ":bsr";
218: } else if ((inst & 0xf000) == 0x6000) {
219: mnemonic = ":bcc";
220: } else if ((inst & 0xfff8) == 0xf248) {
221: mnemonic = ":fdbcc";
222: } else if ((inst & 0xff80) == 0xf280) {
223: mnemonic = ":fbcc";
224: } else {
225: mnemonic = "";
226: }
227: desc += string_format("(%04x%-6s) -> $%08x", inst, mnemonic, e.to);
228: }
229: return desc;
230: }
231:
232:
233: //
234: // m88xx0 ブランチ履歴 (どこに置くのがよいか)
235: //
236:
237: // m88xx0 ブランチ履歴では、通常ブランチ、例外発生の2つを区別する。
238: // m68k は例外ベクタに書いてあるアドレスに飛ぶのでもう1種類分けてあったが、
239: // m88k は例外ベクタを直接実行するのでそれは不要。
240: //
241: // 通常の分岐:
1.1.1.3 root 242: // from は上位30ビットが分岐元 XIP、最下位ビットが S/U、
243: // to は32ビット全体が分岐先アドレス、
244: // inst は命令ワード。
1.1 root 245: //
246: // 例外発生:
1.1.1.3 root 247: // from は上位30ビットが例外発生時の XIP、最下位ビットが S/U、
248: // to は 0。
249: // inst は $fc000VVV で VVV (9ビット)がベクタ番号。
250: // 命令ワードとは衝突しない (instruction.txt 参照)。
1.1 root 251:
252: // コンストラクタ
1.1.1.4 root 253: BranchHistory_m88xx0::BranchHistory_m88xx0(bool mode_)
254: : inherited(mode_)
1.1 root 255: {
256: }
257:
258: // デストラクタ
259: BranchHistory_m88xx0::~BranchHistory_m88xx0()
260: {
261: }
262:
263: // 1エントリ分の表示内容作成
264: std::string
265: BranchHistory_m88xx0::FormatEntry(const BranchEntry& e)
266: {
267: // m88k では from の最下位1ビットを S/U として使う
268: std::string desc = string_format("%c:%08x",
269: (e.from & 1) ? 'S' : 'U', (e.from & ~1));
270:
271: if (e.inst >= 0xfc000000 && e.to == 0) {
272: // 例外発生記録
273: // 発生アドレスとベクタを表示
274: uint32 vector = e.inst & 0x1ff;
1.1.1.5 root 275: const char *name = gVectorTable->GetExceptionName(vector);
1.1 root 276: desc += string_format("<%3d> %s", vector, name ?: "");
277: if (vector == 450) {
278: // OpenBSD のシステムコール番号
279: desc += string_format("(%d)", (e.inst >> 12) & 0xfff);
280: }
281: } else if (e.inst >= 0xc0000000) {
282: // ブランチ
283: const char *mnemonic = "";
284: uint32 up4 = (e.inst >> 26) & 0xf;
285: switch (up4) {
286: case 0: mnemonic = ":br"; break;
287: case 1: mnemonic = ":br.n"; break;
288: case 2: mnemonic = ":bsr"; break;
289: case 3: mnemonic = ":bsr.n"; break;
290: case 4: mnemonic = ":bb0"; break;
291: case 5: mnemonic = ":bb0.n"; break;
1.1.1.2 root 292: case 6: mnemonic = ":bb1"; break;
1.1 root 293: case 7: mnemonic = ":bb1.n"; break;
294:
295: case 0xa: mnemonic = ":bcnd"; break;
296: case 0xb: mnemonic = ":bcnd.n"; break;
297: case 0xc: {
298: uint32 lo6 = (e.inst >> 10) & 0x3f;
299: if (lo6 == 0x34) {
300: mnemonic = ":tb0";
301: } else if (lo6 == 0x36) {
302: mnemonic = ":tb1";
303: } else if (lo6 == 0x3a) {
304: mnemonic = ":tcnd";
305: }
306: break;
307: }
308: case 0xd: {
309: uint32 lo6 = (e.inst >> 10) & 0x3f;
310: if (lo6 == 0x30) {
311: if ((e.inst & 0x1f) == 1) {
312: mnemonic = ":jmp r1";
313: } else {
314: mnemonic = ":jmp";
315: }
316: } else if (lo6 == 0x31) {
317: mnemonic = ":jmp.n";
318: } else if (lo6 == 0x32) {
319: mnemonic = ":jsr";
320: } else if (lo6 == 0x33) {
321: mnemonic = ":jsr.n";
322: } else if (lo6 == 0x3f) {
323: mnemonic = ":rte";
324: }
325: break;
326: }
327: case 0xe: mnemonic = ":tbnd"; break;
328: default:
329: break;
330: }
331: desc += string_format("(%08x%-7s) -> $%08x", e.inst, mnemonic, e.to);
332: }
333: return desc;
334: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.