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