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