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