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