BranchLog.java
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43: package xeij;
44:
45: import java.awt.*;
46: import java.awt.event.*;
47: import java.lang.*;
48: import java.util.*;
49: import javax.swing.*;
50: import javax.swing.event.*;
51:
52: public class BranchLog {
53:
54: public static final boolean BLG_ON = true;
55:
56:
57:
58:
59: public static final int BLG_RECORD_SHIFT = 5;
60: public static final int[] blgArray = new int[65536 << BLG_RECORD_SHIFT];
61: public static long blgNewestRecord;
62: public static int blgPrevHeadSuper;
63: public static int blgPrevTailInt;
64: public static int blgHead;
65: public static int blgSuper;
66: public static int blgInt;
67: public static final int[] blgRegs = new int[16];
68:
69:
70: public static JFrame blgFrame;
71:
72:
73: public static SpinnerNumberModel blgModel;
74: public static JSpinner blgSpinner;
75:
76:
77: public static final int BLG_TEXT_AREA_WIDTH = 400;
78: public static final int BLG_TEXT_AREA_HEIGHT = 400;
79: public static ScrollTextArea blgScrollTextArea;
80: public static JTextArea blgTextArea;
81: public static boolean blgLock;
82:
83:
84: public static final long BLG_SELECT_OLDEST = -3L;
85: public static final long BLG_SELECT_NEWEST = -2L;
86: public static final long BLG_SELECT_NONE = -1L;
87: public static final int BLG_RECORDS_PER_PAGE = 1000;
88: public static int blgNumberOfRecords;
89: public static long blgFirstRecord;
90: public static long blgLastRecord;
91: public static long blgSelectedRecord;
92: public static int blgNumberOfItems;
93: public static int blgSelectedItem;
94: public static final long[] blgRecordArray = new long[BLG_RECORDS_PER_PAGE + 2];
95: public static final int[] blgPositionArray = new int[BLG_RECORDS_PER_PAGE + 3];
96:
97:
98: public static boolean blgShowUser;
99: public static boolean blgShowSupervisor;
100: public static boolean blgShowNormal;
101: public static boolean blgShowInterrupt;
102: public static boolean blgShowRegs;
103:
104:
105:
106: public static void blgInit () {
107:
108:
109:
110: blgNewestRecord = 0L;
111: blgPrevHeadSuper = 0;
112: blgPrevTailInt = 0;
113: blgHead = 0;
114: blgSuper = 0;
115: blgInt = 0;
116:
117:
118:
119: blgFrame = null;
120:
121:
122:
123:
124: blgModel = null;
125: blgSpinner = null;
126:
127:
128: blgScrollTextArea = null;
129: blgTextArea = null;
130: blgLock = false;
131:
132:
133: blgNumberOfRecords = 0;
134: blgFirstRecord = -1L;
135: blgLastRecord = -1L;
136: blgSelectedRecord = -1L;
137: blgNumberOfItems = 0;
138: blgSelectedItem = -1;
139:
140:
141:
142:
143: blgShowUser = true;
144: blgShowSupervisor = true;
145: blgShowNormal = true;
146: blgShowInterrupt = true;
147: blgShowRegs = false;
148:
149: }
150:
151:
152:
153:
154: public static void blgReset () {
155:
156: blgNewestRecord = 0L;
157: blgPrevHeadSuper = 0;
158: blgPrevTailInt = 0;
159: blgHead = XEiJ.regPC;
160: blgSuper = XEiJ.regSRS >>> 13;
161: blgInt = XEiJ.mpuISR == 0 ? 0 : 1;
162: System.arraycopy (XEiJ.regRn, 0, blgRegs, 0, 16);
163:
164: blgNumberOfRecords = 0;
165: blgFirstRecord = -1L;
166: blgLastRecord = -1L;
167: blgSelectedRecord = -1L;
168: blgNumberOfItems = 0;
169: blgSelectedItem = -1;
170:
171: DisassembleList.ddpBacktraceRecord = -1L;
172:
173: }
174:
175:
176:
177:
178:
179: public static void blgStop () {
180: int i = (char) blgNewestRecord << BLG_RECORD_SHIFT;
181: blgArray[i] = blgHead | blgSuper;
182: blgArray[i + 1] = XEiJ.regPC | blgInt;
183: System.arraycopy (blgRegs, 0, blgArray, i + 2, 16);
184: }
185:
186:
187:
188: public static void blgJump (int a) {
189: if (blgPrevHeadSuper != (blgHead | blgSuper) ||
190: blgPrevTailInt != (XEiJ.regPC0 | blgInt)) {
191: int i = (char) blgNewestRecord++ << BLG_RECORD_SHIFT;
192: blgArray[i] = blgPrevHeadSuper = blgHead | blgSuper;
193: blgArray[i + 1] = blgPrevTailInt = XEiJ.regPC0 | blgInt;
194: System.arraycopy (blgRegs, 0, blgArray, i + 2, 16);
195: }
196: blgHead = XEiJ.regPC = a;
197: blgSuper = XEiJ.regSRS >>> 13;
198: blgInt = XEiJ.mpuISR == 0 ? 0 : 1;
199: System.arraycopy (XEiJ.regRn, 0, blgRegs, 0, 16);
200: }
201:
202:
203:
204:
205: public static void blgMakeFrame () {
206:
207:
208: blgScrollTextArea = ComponentFactory.setPreferredSize (
209: ComponentFactory.setFont (new ScrollTextArea (), LnF.lnfMonospacedFont),
210: BLG_TEXT_AREA_WIDTH, BLG_TEXT_AREA_HEIGHT);
211: blgScrollTextArea.setMargin (new Insets (2, 4, 2, 4));
212: blgScrollTextArea.setHighlightCursorOn (true);
213: blgTextArea = blgScrollTextArea.getTextArea ();
214: blgTextArea.setEditable (false);
215: blgTextArea.setText (Multilingual.mlnJapanese ? "MPU が動作中です" : "MPU is running");
216: blgTextArea.setCaretPosition (0);
217: blgTextArea.addMouseWheelListener ((mwe) -> {
218: int n = mwe.getWheelRotation ();
219: JViewport v = blgScrollTextArea.getViewport ();
220: Point p = v.getViewPosition ();
221: v.setViewPosition (new Point (p.x,
222: Math.max (0,
223: Math.min (blgTextArea.getSize ().height - v.getExtentSize ().height,
224: p.y + n * blgTextArea.getFont ().getSize () * 5))));
225: });
226:
227:
228: ComponentFactory.addListener (
229: blgTextArea,
230: new MouseAdapter () {
231: @Override public void mousePressed (MouseEvent me) {
232: if (XEiJ.mpuTask == null && me.isPopupTrigger ()) {
233: XEiJ.dbgShowPopup (me, blgTextArea, false);
234: }
235: }
236: @Override public void mouseReleased (MouseEvent me) {
237: if (XEiJ.mpuTask == null && me.isPopupTrigger ()) {
238: XEiJ.dbgShowPopup (me, blgTextArea, false);
239: }
240: }
241: });
242:
243:
244:
245: ComponentFactory.addListener (
246: blgTextArea,
247: new CaretListener () {
248: @Override public void caretUpdate (CaretEvent ce) {
249: if (blgSelectedRecord >= 0 && !blgLock &&
250: XEiJ.dbgEventMask == 0) {
251: int p = ce.getDot ();
252: if (p == ce.getMark ()) {
253: int item = Arrays.binarySearch (blgPositionArray, 1, blgNumberOfItems, p + 1);
254: item = (item >> 31 ^ item) - 1;
255: if (blgSelectedItem != item) {
256: if (item == 0) {
257: blgUpdate (Math.max (0L, blgFirstRecord - 1L));
258: } else if (item <= blgNumberOfRecords) {
259: blgLock = true;
260: long record = blgRecordArray[item];
261: blgSelectedRecord = record;
262: blgSelectedItem = item;
263: if (blgModel.getNumber ().longValue () != record) {
264: blgModel.setValue (Long.valueOf (record));
265: }
266: blgLock = false;
267: } else {
268: blgUpdate (blgLastRecord + 1L);
269: }
270: }
271: }
272: }
273: }
274: });
275:
276:
277: blgModel = new ReverseLongModel (0L, 0L, Long.MAX_VALUE, 1L);
278: blgSpinner = ComponentFactory.createNumberSpinner (blgModel, 10, new ChangeListener () {
279: @Override public void stateChanged (ChangeEvent ce) {
280: if (!blgLock) {
281: blgUpdate (blgModel.getNumber ().longValue ());
282: }
283: }
284: });
285:
286:
287: ActionListener listener = new ActionListener () {
288: @Override public void actionPerformed (ActionEvent ae) {
289: Object source = ae.getSource ();
290: switch (ae.getActionCommand ()) {
291: case "Clear":
292: blgClear ();
293: blgUpdate (BLG_SELECT_NEWEST);
294: break;
295: case "Oldest record":
296: if (blgSelectedRecord >= 0) {
297: blgUpdate (BLG_SELECT_OLDEST);
298: }
299: break;
300: case "Previous page":
301: if (blgSelectedRecord >= 0) {
302: blgUpdate (blgFirstRecord < blgSelectedRecord ? blgFirstRecord :
303: Math.max (0L, blgFirstRecord - BLG_RECORDS_PER_PAGE));
304: }
305: break;
306: case "Previous record":
307: if (blgSelectedRecord > 0) {
308: if (blgTextArea.getCaretPosition () != blgPositionArray[blgSelectedItem]) {
309: blgTextArea.setCaretPosition (blgPositionArray[blgSelectedItem]);
310: } else {
311: blgUpdate (blgSelectedRecord - 1);
312: }
313: }
314: break;
315: case "Next record":
316: if (blgSelectedRecord >= 0) {
317: blgUpdate (blgSelectedRecord + 1);
318: }
319: break;
320: case "Next page":
321: if (blgSelectedRecord >= 0) {
322: blgUpdate (blgSelectedRecord < blgLastRecord ? blgLastRecord :
323: blgLastRecord + BLG_RECORDS_PER_PAGE);
324: }
325: break;
326: case "Newest record":
327: if (blgSelectedRecord >= 0) {
328: blgUpdate (BLG_SELECT_NEWEST);
329: }
330: break;
331:
332: case "User":
333: blgShowUser = ((JCheckBox) source).isSelected ();
334: blgUpdate (BLG_SELECT_NONE);
335: break;
336: case "Supervisor":
337: blgShowSupervisor = ((JCheckBox) source).isSelected ();
338: blgUpdate (BLG_SELECT_NONE);
339: break;
340: case "Normal":
341: blgShowNormal = ((JCheckBox) source).isSelected ();
342: blgUpdate (BLG_SELECT_NONE);
343: break;
344: case "Interrupt":
345: blgShowInterrupt = ((JCheckBox) source).isSelected ();
346: blgUpdate (BLG_SELECT_NONE);
347: break;
348: case "Regs":
349: blgShowRegs = ((JCheckBox) source).isSelected ();
350: blgUpdate (BLG_SELECT_NONE);
351: break;
352:
353: }
354: }
355: };
356:
357:
358: JButton clearButton = XEiJ.mpuAddButtonStopped (
359: Multilingual.mlnToolTipText (
360: ComponentFactory.createImageButton (
361: LnF.LNF_CLEAR_IMAGE,
362: LnF.LNF_CLEAR_DISABLED_IMAGE,
363: "Clear", listener),
364: "ja", "クリア")
365: );
366: JButton oldestRecordButton = XEiJ.mpuAddButtonStopped (
367: Multilingual.mlnToolTipText (
368: ComponentFactory.createImageButton (
369: LnF.LNF_OLDEST_IMAGE,
370: LnF.LNF_OLDEST_DISABLED_IMAGE,
371: "Oldest record", listener),
372: "ja", "最古のレコード")
373: );
374: JButton previousPageButton = XEiJ.mpuAddButtonStopped (
375: Multilingual.mlnToolTipText (
376: ComponentFactory.createImageButton (
377: LnF.LNF_OLDER_IMAGE,
378: LnF.LNF_OLDER_DISABLED_IMAGE,
379: "Previous page", listener),
380: "ja", "前のページ")
381: );
382: JButton previousRecordButton = XEiJ.mpuAddButtonStopped (
383: Multilingual.mlnToolTipText (
384: ComponentFactory.createImageButton (
385: LnF.LNF_PREVIOUS_IMAGE,
386: LnF.LNF_PREVIOUS_DISABLED_IMAGE,
387: "Previous record", listener),
388: "ja", "前のレコード")
389: );
390: JButton nextRecordButton = XEiJ.mpuAddButtonStopped (
391: Multilingual.mlnToolTipText (
392: ComponentFactory.createImageButton (
393: LnF.LNF_NEXT_IMAGE,
394: LnF.LNF_NEXT_DISABLED_IMAGE,
395: "Next record", listener),
396: "ja", "次のレコード")
397: );
398: JButton nextPageButton = XEiJ.mpuAddButtonStopped (
399: Multilingual.mlnToolTipText (
400: ComponentFactory.createImageButton (
401: LnF.LNF_NEWER_IMAGE,
402: LnF.LNF_NEWER_DISABLED_IMAGE,
403: "Next page", listener),
404: "ja", "次のページ")
405: );
406: JButton newestRecordButton = XEiJ.mpuAddButtonStopped (
407: Multilingual.mlnToolTipText (
408: ComponentFactory.createImageButton (
409: LnF.LNF_NEWEST_IMAGE,
410: LnF.LNF_NEWEST_DISABLED_IMAGE,
411: "Newest record", listener),
412: "ja", "最新のレコード")
413: );
414: JCheckBox userCheckBox = ComponentFactory.createIconCheckBox (
415: blgShowUser,
416: LnF.LNF_USER_IMAGE,
417: LnF.LNF_USER_SELECTED_IMAGE,
418: "User", listener);
419: JCheckBox supervisorCheckBox = ComponentFactory.createIconCheckBox (
420: blgShowSupervisor,
421: LnF.LNF_SUPERVISOR_IMAGE,
422: LnF.LNF_SUPERVISOR_SELECTED_IMAGE,
423: "Supervisor", listener);
424: JCheckBox normalCheckBox = ComponentFactory.createIconCheckBox (
425: blgShowNormal,
426: LnF.LNF_NORMAL_IMAGE,
427: LnF.LNF_NORMAL_SELECTED_IMAGE,
428: "Normal", listener);
429: JCheckBox interruptCheckBox = ComponentFactory.createIconCheckBox (
430: blgShowInterrupt,
431: LnF.LNF_INTERRUPT_IMAGE,
432: LnF.LNF_INTERRUPT_SELECTED_IMAGE,
433: "Interrupt", listener);
434: JCheckBox regsCheckBox = ComponentFactory.createIconCheckBox (
435: blgShowRegs,
436: LnF.LNF_REGS_IMAGE,
437: LnF.LNF_REGS_SELECTED_IMAGE,
438: "Regs", listener);
439:
440:
441: blgFrame = Multilingual.mlnTitle (
442: ComponentFactory.createRestorableSubFrame (
443: Settings.SGS_BLG_FRAME_KEY,
444: "Branch log",
445: null,
446: ComponentFactory.createBorderPanel (
447:
448: blgScrollTextArea,
449:
450: ComponentFactory.createVerticalBox (
451:
452: ComponentFactory.createHorizontalBox (
453: clearButton,
454: Box.createHorizontalGlue (),
455: Box.createHorizontalStrut (12),
456: XEiJ.mpuMakeOriIllegalCheckBox (),
457: XEiJ.mpuMakeStopOnErrorCheckBox (),
458: XEiJ.mpuMakeStopAtStartCheckBox (),
459: Box.createHorizontalStrut (12),
460: XEiJ.mpuMakeBreakButton (),
461: XEiJ.mpuMakeTraceButton (),
462: XEiJ.mpuMakeTrace10Button (),
463: XEiJ.mpuMakeTrace100Button (),
464: XEiJ.mpuMakeStepButton (),
465: XEiJ.mpuMakeStep10Button (),
466: XEiJ.mpuMakeStep100Button (),
467: XEiJ.mpuMakeReturnButton (),
468: XEiJ.mpuMakeRunButton ()
469: ),
470:
471: ComponentFactory.createHorizontalBox (
472: blgSpinner,
473: Box.createHorizontalStrut (12),
474: oldestRecordButton,
475: previousPageButton,
476: previousRecordButton,
477: nextRecordButton,
478: nextPageButton,
479: newestRecordButton,
480: Box.createHorizontalStrut (12),
481: userCheckBox,
482: supervisorCheckBox,
483: normalCheckBox,
484: interruptCheckBox,
485: regsCheckBox,
486: Box.createHorizontalGlue ()
487: )
488: )
489: )
490: ),
491: "ja", "分岐ログ");
492:
493: ComponentFactory.addListener (
494: blgFrame,
495: new WindowAdapter () {
496: @Override public void windowClosing (WindowEvent we) {
497: XEiJ.dbgVisibleMask &= ~XEiJ.DBG_BLG_VISIBLE_MASK;
498: }
499: });
500:
501: }
502:
503:
504:
505: public static void blgClear () {
506: blgReset ();
507: blgArray[0] = XEiJ.regPC | XEiJ.regSRS >>> 13;
508: blgArray[1] = XEiJ.regPC | (XEiJ.mpuISR == 0 ? 0 : 1);
509: System.arraycopy (XEiJ.regRn, 0, blgArray, 2, 16);
510: }
511:
512:
513: public static void blgStart () {
514: if (RestorableFrame.rfmGetOpened (Settings.SGS_BLG_FRAME_KEY)) {
515: blgOpen (BLG_SELECT_NONE);
516: }
517: }
518:
519:
520:
521: public static void blgOpen (long selectedRecord) {
522: if (blgFrame == null) {
523: blgMakeFrame ();
524: }
525: XEiJ.dbgVisibleMask |= XEiJ.DBG_BLG_VISIBLE_MASK;
526: blgUpdate (selectedRecord);
527: XEiJ.pnlExitFullScreen (false);
528: blgFrame.setVisible (true);
529: }
530:
531:
532:
533:
534: public static void blgUpdate (long selectedRecord) {
535: if (XEiJ.mpuTask != null) {
536: blgLock = true;
537: blgTextArea.setText (Multilingual.mlnJapanese ? "MPU が動作中です" : "MPU is running");
538: blgTextArea.setCaretPosition (0);
539: blgLock = false;
540: return;
541: }
542: if (blgLock) {
543: return;
544: }
545: blgLock = true;
546: blgStop ();
547: long newestRecord = blgNewestRecord;
548: long oldestRecord = Math.max (0L, newestRecord - 65535);
549: if (selectedRecord < 0L) {
550: if (selectedRecord == BLG_SELECT_NONE) {
551: selectedRecord = blgSelectedRecord < 0L ? newestRecord : blgSelectedRecord;
552: } else if (selectedRecord == BLG_SELECT_NEWEST) {
553: selectedRecord = newestRecord;
554: } else if (selectedRecord == BLG_SELECT_OLDEST) {
555: selectedRecord = oldestRecord;
556: }
557: }
558: if (selectedRecord < oldestRecord) {
559: selectedRecord = oldestRecord;
560: } else if (selectedRecord > newestRecord) {
561: selectedRecord = newestRecord;
562: }
563: long firstRecord = selectedRecord / BLG_RECORDS_PER_PAGE * BLG_RECORDS_PER_PAGE;
564: long lastRecord = firstRecord + (long) (BLG_RECORDS_PER_PAGE - 1);
565: if (firstRecord < oldestRecord) {
566: firstRecord = oldestRecord;
567: }
568: if (lastRecord > newestRecord) {
569: lastRecord = newestRecord;
570: }
571:
572: if (blgFirstRecord != firstRecord || blgLastRecord != lastRecord ||
573: blgLastRecord == blgNewestRecord) {
574:
575:
576:
577:
578: blgFirstRecord = firstRecord;
579: blgLastRecord = lastRecord;
580: blgSelectedRecord = selectedRecord;
581:
582: int pcPosition = -1;
583:
584:
585:
586:
587:
588: blgRecordArray[0] = firstRecord - 1;
589: blgPositionArray[0] = 0;
590: StringBuilder sb = new StringBuilder (
591: firstRecord == oldestRecord ?
592: Multilingual.mlnJapanese ? "───── 分岐ログの先頭 ─────\n" : "───── Top of the branch log ─────\n" :
593: Multilingual.mlnJapanese ? "↑↑↑↑↑ 手前のページ ↑↑↑↑↑\n" : "↑↑↑↑↑ Previous page ↑↑↑↑↑\n");
594:
595:
596: int itemNumber = 1;
597: long itemRecord = firstRecord;
598: while (itemRecord <= lastRecord) {
599: int i = (char) itemRecord << BLG_RECORD_SHIFT;
600: int headAddress = blgArray[i] & ~1;
601: int supervisor = blgArray[i] & 1;
602: int tailAddress = blgArray[i + 1] & ~1;
603: int interrupt = blgArray[i + 1] & 1;
604:
605:
606: if (itemRecord == selectedRecord) {
607: blgSelectedItem = itemNumber;
608: }
609: blgRecordArray[itemNumber] = itemRecord;
610: blgPositionArray[itemNumber] = sb.length ();
611: sb.append (itemRecord);
612: sb.append (supervisor == 0 ? "[U" : "[S");
613: sb.append (interrupt == 0 ? "N] " : "I] ");
614: XEiJ.fmtHex8 (sb, headAddress);
615: LabeledAddress.lblSearch (sb, headAddress);
616: sb.append ('\n');
617:
618:
619: if ((supervisor == 0 ? blgShowUser : blgShowSupervisor) &&
620: (interrupt == 0 ? blgShowNormal : blgShowInterrupt)) {
621:
622:
623: if (blgShowRegs) {
624: for (int n = 0; n < 16; n++) {
625: if (n == 0) {
626: sb.append ('D');
627: } else if (n == 8) {
628: sb.append ('A');
629: } else if ((n & 7) == 4) {
630: sb.append (' ');
631: }
632: XEiJ.fmtHex8 (sb.append (' '), blgArray[i + 2 + n]);
633: if ((n & 7) == 7) {
634: sb.append ('\n');
635: }
636: }
637: }
638:
639: for (Disassembler.disPC = headAddress; Disassembler.disPC <= tailAddress; ) {
640: if (itemRecord == selectedRecord &&
641: Disassembler.disPC == XEiJ.regPC) {
642: pcPosition = sb.length ();
643: }
644: Disassembler.disDisassemble (XEiJ.fmtHex8 (sb.append (" "),
645: Disassembler.disPC).
646: append (" "),
647: Disassembler.disPC, supervisor).
648: append ('\n');
649: if ((Disassembler.disStatus & Disassembler.DIS_ALWAYS_BRANCH) != 0) {
650: sb.append ('\n');
651: }
652: }
653: }
654:
655:
656: itemNumber++;
657: itemRecord++;
658:
659: }
660:
661:
662: blgRecordArray[itemNumber] = lastRecord + 1;
663: blgPositionArray[itemNumber] = sb.length ();
664: sb.append (
665: lastRecord == newestRecord ?
666: Multilingual.mlnJapanese ? "───── 分岐ログの末尾 ─────" : "───── Bottom of the branch log ─────" :
667: Multilingual.mlnJapanese ? "↓↓↓↓↓ 次のページ ↓↓↓↓↓" : "↓↓↓↓↓ Next page ↓↓↓↓↓");
668: itemNumber++;
669: blgPositionArray[itemNumber] = sb.length ();
670: blgNumberOfRecords = itemNumber - 2;
671: blgNumberOfItems = itemNumber;
672:
673:
674: blgTextArea.setText (sb.toString ());
675: blgTextArea.setCaretPosition (pcPosition >= 0 ? pcPosition :
676: blgPositionArray[blgSelectedItem]);
677:
678: } else if (blgSelectedRecord != selectedRecord) {
679:
680: blgSelectedRecord = selectedRecord;
681: blgSelectedItem = (int) (blgSelectedRecord - blgFirstRecord) + 1;
682: blgTextArea.setCaretPosition (blgPositionArray[blgSelectedItem]);
683:
684: }
685:
686:
687:
688:
689: if (blgModel.getNumber ().longValue () != selectedRecord) {
690: blgModel.setValue (Long.valueOf (selectedRecord));
691: }
692:
693: blgLock = false;
694: }
695:
696: }
697:
698:
699: