|
|
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 "m88100.h"
1.1.1.7 ! root 13: #include "mpu64180.h"
1.1.1.5 root 14: #include "vectortable.h"
1.1 root 15:
16: // コンストラクタ
1.1.1.7 ! root 17: BranchHistory::BranchHistory(int objid_)
! 18: : inherited(objid_)
1.1 root 19: {
1.1.1.7 ! root 20: int monitor_id;
! 21:
! 22: switch (GetId()) {
! 23: case OBJ_MPU_BRHIST:
! 24: is_exhist = false;
! 25: monitor_id = ID_SUBWIN_BRHIST;
! 26: break;
! 27: case OBJ_MPU_EXHIST:
! 28: is_exhist = true;
! 29: monitor_id = ID_SUBWIN_EXHIST;
! 30: break;
! 31: case OBJ_XP_BRHIST:
! 32: is_exhist = false;
! 33: monitor_id = ID_SUBWIN_XPBRHIST;
! 34: break;
! 35: case OBJ_XP_EXHIST:
! 36: is_exhist = true;
! 37: monitor_id = ID_SUBWIN_XPEXHIST;
! 38: break;
! 39: default:
! 40: PANIC("unknown id %s", GetIdStr());
! 41: }
1.1.1.4 root 42:
1.1.1.5 root 43: // ログは不要
44: ClearAlias();
45:
1.1.1.7 ! root 46: vectortable = GetVectorTable();
! 47:
1.1.1.5 root 48: monitor.func = ToMonitorCallback(&BranchHistory::MonitorUpdate);
1.1.1.6 root 49: monitor.SetSize(55, 1 + 32); // ヘッダ1行とエントリ数
50: monitor.SetMaxHeight(1 + 256);
1.1.1.7 ! root 51: monitor.Regist(monitor_id);
! 52:
! 53: // hd64180 はこれを上書きする
! 54: x_count = 45;
1.1 root 55: }
56:
57: // デストラクタ
58: BranchHistory::~BranchHistory()
59: {
60: }
61:
62: // 初期化
63: void
64: BranchHistory::Clear()
65: {
66: top = 0;
67: for (int i = 0; i < countof(entry); i++) {
68: BranchEntry& e = entry[i];
69: e.count = 0;
70: e.from = 0xffffffff;
71: e.to = 0xffffffff;
72: e.inst = 0;
73: }
74: }
75:
76: // 使用中のエントリ数を取得する
77: int
78: BranchHistory::GetUsed() const
79: {
80: int i = 0;
81:
82: // top の位置から一巡するまで。有効エントリは count > 0。
83: for (; i < 256; i++) {
84: if (entry[(256 + top - i) % 256].count == 0) {
85: break;
86: }
87: }
88: return i;
89: }
90:
91: // モニタ更新
92: void
1.1.1.4 root 93: BranchHistory::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 94: {
95: uint8 t;
96: int y;
97: int ydelta;
98: int row;
99:
100: // userdata は表示開始位置と各種フラグ。
101: // 下位 32bit が表示開始位置で、0 なら先頭のエントリから、1 なら先頭の
1.1.1.4 root 102: // 一つ次のエントリから、…を表す (表示行数は screen の高さ分)。
1.1 root 103: // BottomToTop が %1 なら並び順を下から上に変える (コンソール用)。
1.1.1.4 root 104: bool bottom_to_top = (screen.userdata & BottomToTop);
105: int pos = (int)screen.userdata;
1.1 root 106:
107: // 0 1 2 3 4 5 6
108: // 0123456789012345678901234567890123456789012345678901234567890123456789
109: // No. FromAddr Instruction ToAddr Iteration
110: // 001 U:01234567(01234567:bcnd.n) -> $01234567 x123456789
111: // 001 U:01234567(0123:trap ) -> $01234567 x123456789
112: // 002 S:01234567< 2> Bus Error
113: // 002 S:01234567< 69> MFP Timer-C
114: // 01234567890123456789012
115:
1.1.1.4 root 116: screen.Clear();
117: row = screen.GetRow();
1.1 root 118:
119: // 最初の1行は常にヘッダ
1.1.1.7 ! root 120: std::string header = FormatHeader();
! 121: screen.Puts(0, 0, header.c_str());
1.1 root 122:
123: // pos は最新を 0 とした通し番号。(スクロールしてたら開始が 0 とは限らない)
124: // t が entry[] 上の現在位置。
125: // y は表示座標。(上から表示か下から表示かが変わる)
126: t = top - pos;
127: int pos_end = pos + row - 1;
128: if (bottom_to_top == false) {
129: y = 1;
130: ydelta = 1;
131: } else {
132: y = row - 1;
133: ydelta = -1;
134: }
135: for (; pos < pos_end; pos++, y += ydelta) {
136: BranchEntry& e = entry[t--];
137: if (e.count == 0) {
138: continue;
139: }
140: std::string str = FormatEntry(e);
1.1.1.4 root 141: screen.Print(0, y, "%3d %s", pos, str.c_str());
1.1 root 142: if (e.count > 1) {
1.1.1.7 ! root 143: screen.Print(x_count, y, "x%9u", e.count);
1.1 root 144: }
145: }
146: }
147:
1.1.1.7 ! root 148: // ヘッダ文字列を返す。(m68k, m88k 共通)
! 149: std::string
! 150: BranchHistory::FormatHeader() const
! 151: {
! 152: if (is_exhist) {
! 153: // 012345678901234567890123456789012345678901234567890123456789
! 154: return "No. FromAddr Vec. Exception Iteration";
! 155: } else {
! 156: // 012345678901234567890123456789012345678901234567890123456789
! 157: return "No. FromAddr Instruction ToAddr Iteration";
! 158: }
! 159: }
1.1 root 160:
161:
162: //
163: // m680x0 ブランチ履歴 (どこに置くのがよいか)
164: //
165:
166: // m680x0 ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによる
167: // ジャンプの3つを区別する。
168: //
169: // 通常の分岐:
1.1.1.3 root 170: // from は上位31ビットが分岐元 PC、最下位ビットが S/U、
171: // to は32ビット全体が分岐先 PC、
172: // inst の上位16ビットに命令の1ワード目。
1.1 root 173: // inst の $8000 が立っていないことで区別する。
174: //
175: // 例外発生:
1.1.1.3 root 176: // from は上位31ビットが例外発生時の PC、最下位ビットが S/U、
177: // to は不問(通常 0 をセットすること)、
1.1 root 178: // inst は $00008LVV で $8000 が例外フラグ、VV がベクタ番号である。
179: // ベクタ番号が $00 だと次項(ベクタによる分岐)と区別つかなくなってしまう
180: // ため、リセット例外 (ベクタ番号0) は V=$01 で記録する。
181: // L は割り込みなら割り込みレベル。
182: // inst に $8000 が立っていて、かつ $8000 と一致しないことで区別する。
183: //
184: // 例外ベクタによる分岐 (例外処理の最後に起きる):
1.1.1.3 root 185: // from はベクタアドレス、
186: // to は分岐先 (0 も起こりえる)、
187: // inst は $00008000。
1.1 root 188: // inst が $00008000 と一致することで区別する (ビットが立っているではない)。
189:
190: // コンストラクタ
1.1.1.7 ! root 191: BranchHistory_m680x0::BranchHistory_m680x0(int objid_)
! 192: : inherited(objid_)
1.1 root 193: {
194: }
195:
196: // デストラクタ
197: BranchHistory_m680x0::~BranchHistory_m680x0()
198: {
199: }
200:
201: // 1エントリ分の表示内容作成
202: std::string
203: BranchHistory_m680x0::FormatEntry(const BranchEntry& e)
204: {
205: // m68k では from の最下位1ビットを S/U として使う
206: std::string desc = string_format("%c:%08x",
207: (e.from & 1) ? 'S' : 'U', (e.from & ~1));
208:
209: if ((e.inst & 0x8000)) {
210: if ((e.inst & 0x7fff) != 0) {
211: // 例外発生記録
212: // 発生アドレスとベクタ(と割り込みレベル)を表示
213: uint32 vector = e.inst & 0xff;
214: if (vector == 1) {
215: vector = 0;
216: }
217: // とりあえず
1.1.1.7 ! root 218: const char *name = vectortable->GetExceptionName(vector);
1.1.1.5 root 219: desc += string_format("<%3d> %s", vector, name ?: "");
1.1 root 220: // XXX TODO 割り込みレベル
221: } else {
222: // 例外ベクタフェッチしてジャンプ
223: desc += string_format("(vector fetch) -> $%08x", e.to);
224: }
225: } else {
226: // ブランチ
227: const char *mnemonic;
228: uint32 inst = e.inst >> 16;
229: if (inst == 0x4e73) {
230: mnemonic = ":rte";
231: } else if (inst == 0x4e75) {
232: mnemonic = ":rts";
233: } else if (inst == 0x4e77) {
234: mnemonic = ":rtr";
235: } else if ((inst & 0xffc0) == 0x4e80) {
236: mnemonic = ":jsr";
237: } else if ((inst & 0xffc0) == 0x4ec0) {
238: mnemonic = ":jmp";
239: } else if ((inst & 0xfff8) == 0x51c8) {
240: mnemonic = ":dbra";
241: } else if ((inst & 0xf0f8) == 0x50c8) {
242: mnemonic = ":dbcc";
243: } else if ((inst & 0xff00) == 0x6000) {
244: mnemonic = ":bra";
245: } else if ((inst & 0xff00) == 0x6100) {
246: mnemonic = ":bsr";
247: } else if ((inst & 0xf000) == 0x6000) {
248: mnemonic = ":bcc";
249: } else if ((inst & 0xfff8) == 0xf248) {
250: mnemonic = ":fdbcc";
251: } else if ((inst & 0xff80) == 0xf280) {
252: mnemonic = ":fbcc";
253: } else {
254: mnemonic = "";
255: }
256: desc += string_format("(%04x%-6s) -> $%08x", inst, mnemonic, e.to);
257: }
258: return desc;
259: }
260:
261:
262: //
263: // m88xx0 ブランチ履歴 (どこに置くのがよいか)
264: //
265:
266: // m88xx0 ブランチ履歴では、通常ブランチ、例外発生の2つを区別する。
267: // m68k は例外ベクタに書いてあるアドレスに飛ぶのでもう1種類分けてあったが、
268: // m88k は例外ベクタを直接実行するのでそれは不要。
269: //
270: // 通常の分岐:
1.1.1.3 root 271: // from は上位30ビットが分岐元 XIP、最下位ビットが S/U、
272: // to は32ビット全体が分岐先アドレス、
273: // inst は命令ワード。
1.1 root 274: //
275: // 例外発生:
1.1.1.3 root 276: // from は上位30ビットが例外発生時の XIP、最下位ビットが S/U、
277: // to は 0。
278: // inst は $fc000VVV で VVV (9ビット)がベクタ番号。
279: // 命令ワードとは衝突しない (instruction.txt 参照)。
1.1 root 280:
281: // コンストラクタ
1.1.1.7 ! root 282: BranchHistory_m88xx0::BranchHistory_m88xx0(int objid_)
! 283: : inherited(objid_)
1.1 root 284: {
285: }
286:
287: // デストラクタ
288: BranchHistory_m88xx0::~BranchHistory_m88xx0()
289: {
290: }
291:
292: // 1エントリ分の表示内容作成
293: std::string
294: BranchHistory_m88xx0::FormatEntry(const BranchEntry& e)
295: {
296: // m88k では from の最下位1ビットを S/U として使う
297: std::string desc = string_format("%c:%08x",
298: (e.from & 1) ? 'S' : 'U', (e.from & ~1));
299:
300: if (e.inst >= 0xfc000000 && e.to == 0) {
301: // 例外発生記録
302: // 発生アドレスとベクタを表示
303: uint32 vector = e.inst & 0x1ff;
1.1.1.7 ! root 304: const char *name = vectortable->GetExceptionName(vector);
1.1 root 305: desc += string_format("<%3d> %s", vector, name ?: "");
1.1.1.7 ! root 306: if (vector == 128 || vector == 450) {
! 307: // 450 なら OpenBSD のシステムコール番号を追加。
! 308: // 非公式 NetBSD/luna88k は(今の所?) 128 を使ってるようだ。
1.1 root 309: desc += string_format("(%d)", (e.inst >> 12) & 0xfff);
310: }
311: } else if (e.inst >= 0xc0000000) {
312: // ブランチ
313: const char *mnemonic = "";
314: uint32 up4 = (e.inst >> 26) & 0xf;
315: switch (up4) {
316: case 0: mnemonic = ":br"; break;
317: case 1: mnemonic = ":br.n"; break;
318: case 2: mnemonic = ":bsr"; break;
319: case 3: mnemonic = ":bsr.n"; break;
320: case 4: mnemonic = ":bb0"; break;
321: case 5: mnemonic = ":bb0.n"; break;
1.1.1.2 root 322: case 6: mnemonic = ":bb1"; break;
1.1 root 323: case 7: mnemonic = ":bb1.n"; break;
324:
325: case 0xa: mnemonic = ":bcnd"; break;
326: case 0xb: mnemonic = ":bcnd.n"; break;
327: case 0xc: {
328: uint32 lo6 = (e.inst >> 10) & 0x3f;
329: if (lo6 == 0x34) {
330: mnemonic = ":tb0";
331: } else if (lo6 == 0x36) {
332: mnemonic = ":tb1";
333: } else if (lo6 == 0x3a) {
334: mnemonic = ":tcnd";
335: }
336: break;
337: }
338: case 0xd: {
339: uint32 lo6 = (e.inst >> 10) & 0x3f;
340: if (lo6 == 0x30) {
341: if ((e.inst & 0x1f) == 1) {
342: mnemonic = ":jmp r1";
343: } else {
344: mnemonic = ":jmp";
345: }
346: } else if (lo6 == 0x31) {
347: mnemonic = ":jmp.n";
348: } else if (lo6 == 0x32) {
349: mnemonic = ":jsr";
350: } else if (lo6 == 0x33) {
351: mnemonic = ":jsr.n";
352: } else if (lo6 == 0x3f) {
353: mnemonic = ":rte";
354: }
355: break;
356: }
357: case 0xe: mnemonic = ":tbnd"; break;
358: default:
359: break;
360: }
361: desc += string_format("(%08x%-7s) -> $%08x", e.inst, mnemonic, e.to);
362: }
363: return desc;
364: }
1.1.1.7 ! root 365:
! 366:
! 367: //
! 368: // XP ブランチ履歴
! 369: //
! 370:
! 371: // XP ブランチ履歴では、通常ブランチ、例外発生、例外ベクタによるジャンプの
! 372: // 3つを区別する。
! 373: //
! 374: // 通常の分岐
! 375: // from は下位 16 ビットが分岐元 PC
! 376: // to は下位 16 ビットが分岐先 PC
! 377: // inst の最上位ビットが %0 で区別する。
! 378: // inst の下位 16 ビットに命令を上詰めで。例えば
! 379: // CALL (1バイト命令 CD nn nn) なら $0000'cd00、
! 380: // RETI (2バイト命令 ED 4D) なら $0000'ed4d。
! 381: //
! 382: // 例外発生
! 383: // from は下位 16 ビットが例外発生時の PC
! 384: // to は $ffffffff ならこのエントリはベクタ方式の例外発生時側を示す、
! 385: // そうでなければダイレクト方式で下位 16 ビットが分岐先 PC
! 386: // inst の最上位ビットが %1 で inst が $ffffffff でなければ
! 387: // 下位に優先度(0-15)。
! 388: //
! 389: // 例外ベクタジャンプの場合
! 390: // from はベクタアドレス
! 391: // to は分岐先 PC、
! 392: // inst が $ffffffff で区別する。
! 393:
! 394: // コンストラクタ
! 395: BranchHistory_hd64180::BranchHistory_hd64180(int objid_)
! 396: : inherited(objid_)
! 397: {
! 398: monitor.SetSize(48, 1 + 32); // ヘッダ1行とエントリ数
! 399: x_count = 38;
! 400: }
! 401:
! 402: // デストラクタ
! 403: BranchHistory_hd64180::~BranchHistory_hd64180()
! 404: {
! 405: }
! 406:
! 407: // ヘッダ文字列を返す。
! 408: std::string
! 409: BranchHistory_hd64180::FormatHeader() const
! 410: {
! 411: if (is_exhist) {
! 412: // 01234567890123456789012345678901234567890123456789
! 413: return "No. From Exception Iteration";
! 414: } else {
! 415: // 01234567890123456789012345678901234567890123456789
! 416: return "No. From Instruction ToAddr Iteration";
! 417: }
! 418: }
! 419:
! 420: // 1エントリ分の表示内容作成
! 421: std::string
! 422: BranchHistory_hd64180::FormatEntry(const BranchEntry& e)
! 423: {
! 424: // 0 1 2 3 4 5
! 425: // 012345678901234567890123456789012345678901234567890
! 426: // No. From Instruction ToAddr Iteration
! 427: // 001 0123(0000:CALL NZ) -> $0234 x123456899
! 428: // No. From Exception
! 429: // 002 0123<00> Timer Overflow
! 430: // 003 0038(vector fetch) -> $0000
! 431:
! 432: std::string desc = strhex(e.from, 4);
! 433:
! 434: if (e.inst == 0xffffffff) {
! 435: // 例外ベクタフェッチしてジャンプ
! 436: desc += string_format("(vector fetch) -> $%04x", e.to);
! 437: } else if ((int32)e.inst < 0) {
! 438: // 例外発生
! 439: uint32 vector = e.inst & 0xff;
! 440: desc += string_format("<%2d>%-15s",
! 441: vector, MPU64180Device::InterruptName[vector]);
! 442: // 例外履歴には宛先は出さないほうに統一。
! 443: // ブランチ履歴でも、ベクタ方式なら宛先不要。
! 444: if (is_exhist == false && e.to != -1) {
! 445: desc += string_format(" -> $%04x", e.to);
! 446: }
! 447: } else {
! 448: // ブランチ
! 449: std::string mnemonic;
! 450: uint32 inst = e.inst & 0xffff;
! 451: if (inst == 0x1000) {
! 452: mnemonic = ":DJNZ";
! 453: } else if (inst == 0x1800) {
! 454: mnemonic = ":JR";
! 455: } else if (inst == 0xc300) {
! 456: mnemonic = ":JP";
! 457: } else if (inst == 0xc900) {
! 458: mnemonic = ":RET";
! 459: } else if (inst == 0xcd00) {
! 460: mnemonic = ":CALL";
! 461: } else if (inst == 0xe900) {
! 462: mnemonic = ":JP (HL)";
! 463: } else if (inst == 0xed45) {
! 464: mnemonic = ":RETN";
! 465: } else if (inst == 0xed4d) {
! 466: mnemonic = ":RETI";
! 467: } else if (inst == 0xdde9) {
! 468: mnemonic = ":JP (IX)";
! 469: } else if (inst == 0xfde9) {
! 470: mnemonic = ":JP (IY)";
! 471: } else if ((inst & 0xe700) == 0x2000) {
! 472: mnemonic = ":JR " + fstr((inst >> 11) & 3);
! 473: } else if ((inst & 0xc700) == 0xc000) {
! 474: mnemonic = ":RET " + fstr((inst >> 11) & 7);
! 475: } else if ((inst & 0xc700) == 0xc200) {
! 476: mnemonic = ":JP " + fstr((inst >> 11) & 7);
! 477: } else if ((inst & 0xc700) == 0xc400) {
! 478: mnemonic = ":CALL " + fstr((inst >> 11) & 7);
! 479: } else if ((inst & 0xc700) == 0xc700) {
! 480: mnemonic = string_format(":RST %02xH", (inst >> 8) & 0x38);
! 481: }
! 482: desc += string_format("(%04x%-8s) -> $%04x",
! 483: inst, mnemonic.c_str(), e.to);
! 484: }
! 485: return desc;
! 486: }
! 487:
! 488: /*static*/ std::string
! 489: BranchHistory_hd64180::fstr(int f)
! 490: {
! 491: static const char fff[] =
! 492: "NZ\0\0"
! 493: "Z\0\0\0"
! 494: "NC\0\0"
! 495: "C\0\0\0"
! 496: "PO\0\0"
! 497: "PE\0\0"
! 498: "P\0\0\0"
! 499: "M";
! 500:
! 501: return &fff[f * 4];
! 502: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.