DisassembleList.java
     1: //========================================================================================
     2: //  DisassembleList.java
     3: //    en:Disassemble list
     4: //    ja:逆アセンブルリスト
     5: //  Copyright (C) 2003-2024 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.awt.*;  //BasicStroke,BorderLayout,BoxLayout,Color,Component,Container,Cursor,Desktop,Dimension,Font,FlowLayout,Frame,Graphics,Graphics2D,GraphicsDevice,GraphicsEnvironment,GridLayout,Image,Insets,Paint,Point,Rectangle,RenderingHints,Robot,Shape,Stroke,TexturePaint,Toolkit
    16: import java.awt.event.*;  //ActionEvent,ActionListener,ComponentAdapter,ComponentEvent,ComponentListener,FocusAdapter,FocusEvent,FocusListener,InputEvent,KeyAdapter,KeyEvent,KeyListener,MouseAdapter,MouseEvent,MouseListener,MouseMotionAdapter,MouseWheelEvent,WindowAdapter,WindowEvent,WindowListener,WindowStateListener
    17: import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
    18: import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,TimeZone,Timer,TimerTask,TreeMap
    19: import javax.swing.*;  //AbstractSpinnerModel,Box,ButtonGroup,DefaultListModel,ImageIcon,JApplet,JButton,JCheckBox,JCheckBoxMenuItem,JDialog,JFileChooser,JFrame,JLabel,JList,JMenu,JMenuBar,JMenuItem,JPanel,JRadioButton,JScrollPane,JSpinner,JTextArea,JTextField,JTextPane,JViewport,ScrollPaneConstants,SpinnerListModel,SpinnerNumberModel,SwingConstants,SwingUtilities,UIManager,UIDefaults,UnsupportedLookAndFeelException
    20: import javax.swing.event.*;  //CaretEvent,CaretListener,ChangeEvent,ChangeListener,DocumentEvent,DocumentListener,ListSelectionListener
    21: 
    22: public class DisassembleList {
    23: 
    24:   public static final int DDP_ITEM_SIZE = 0x00000002;  //行の最小サイズ
    25:   public static final int DDP_PAGE_SIZE = 0x00000400;  //ページのサイズ
    26:   public static final int DDP_ITEM_MASK = -DDP_ITEM_SIZE;
    27:   public static final int DDP_PAGE_MASK = -DDP_PAGE_SIZE;
    28:   public static final int DDP_MAX_ITEMS = DDP_PAGE_SIZE / DDP_ITEM_SIZE + 2;  //ページの最大項目数。先頭と末尾の番兵を含む
    29: 
    30:   public static final char[] DDP_MOVEQD0_BASE = (
    31:     //         11111111112222222222333333333344444444445555555555666666666677777777778
    32:     //12345678901234567890123456789012345678901234567890123456789012345678901234567890
    33:     "moveq.l #$xx,d0").toCharArray ();
    34:   public static final char[] DDP_DCW_BASE = (
    35:     //         11111111112222222222333333333344444444445555555555666666666677777777778
    36:     //12345678901234567890123456789012345678901234567890123456789012345678901234567890
    37:     ".dc.w   $xxxx").toCharArray ();
    38: 
    39:   public static int ddpItemCount;  //ページに含まれる項目の数。先頭と末尾の番兵を含む。0=構築前または再構築要求
    40:   public static int ddpItemIndex;  //キャレットがある項目の番号
    41:   public static int ddpItemAddress;  //キャレットがある項目の先頭アドレス
    42:   public static int ddpPageAddress;  //ページの先頭アドレス
    43:   public static final int[] ddpAddressArray = new int[DDP_MAX_ITEMS];  //項目の先頭アドレスの配列。先頭は前のページの末尾、末尾は次のページの先頭。スピナーのヒント
    44:   public static final int[] ddpSplitArray = new int[DDP_MAX_ITEMS];  //項目を区切る位置の配列。先頭は0
    45:   public static final int[] ddpCaretArray = new int[DDP_MAX_ITEMS];  //項目が選択されたときキャレットを移動させる位置の配列。行の手前にヘッダやラベルなどを挿入しないときはddpSplitArrayと同じ
    46:   public static final boolean[] ddpDCWArray = new boolean[DDP_MAX_ITEMS];  //項目毎の.dc.wで出力したかどうかのフラグの配列。true=数ワード後にある表示しなければならないアドレスを跨がないために逆アセンブルせず.dc.wで出力した。この行がクリックされたとき逆アセンブルし直す
    47: 
    48:   public static JFrame ddpFrame;  //ウインドウ
    49:   public static ScrollTextArea ddpBoard;  //スクロールテキストエリア
    50:   public static JTextArea ddpTextArea;  //テキストエリア
    51: 
    52:   public static Hex8Spinner ddpSpinner;  //スピナー
    53: 
    54:   public static int ddpPopupAddress;  //クリックされた行のアドレス
    55: 
    56:   public static boolean ddpBacktraceOn;  //true=バックトレース
    57:   public static long ddpBacktraceRecord;  //現在選択されている分岐レコードの通し番号。-1L=未選択
    58:   public static SpinnerNumberModel ddpBacktraceModel;  //バックトレーススピナーのモデル。値はLong
    59:   public static JSpinner ddpBacktraceSpinner;  //バックトレーススピナー
    60:   public static JCheckBox ddpBacktraceCheckBox;  //バックトレースチェックボックス
    61: 
    62:   public static String ddpStoppedBy;  //停止理由
    63:   public static int ddpStoppedAddress;  //停止位置
    64: 
    65:   public static int ddpSupervisorMode;  //0=ユーザモード,0以外=スーパーバイザモード
    66:   public static JCheckBox ddpSupervisorCheckBox;  //ユーザ/スーパーバイザチェックボックス
    67: 
    68:   //ddpInit ()
    69:   //  初期化
    70:   public static void ddpInit () {
    71: 
    72:     ddpItemCount = 0;  //構築前
    73:     ddpItemIndex = 0;
    74:     ddpItemAddress = -1;
    75:     ddpPageAddress = 0;
    76:     ddpSupervisorMode = 1;
    77:     //ddpAddressArray = new int[DDP_MAX_ITEMS];
    78:     //ddpSplitArray = new int[DDP_MAX_ITEMS];
    79:     //ddpCaretArray = new int[DDP_MAX_ITEMS];
    80:     //ddpDCWArray = new boolean[DDP_MAX_ITEMS];
    81: 
    82:     ddpFrame = null;
    83: 
    84:   }  //ddpInit()
    85: 
    86:   //ddpStart ()
    87:   public static void ddpStart () {
    88:     if (RestorableFrame.rfmGetOpened (Settings.SGS_DDP_FRAME_KEY)) {
    89:       ddpOpen (-1, -1, true);
    90:     }
    91:   }  //ddpStart()
    92: 
    93:   //ddpOpen (address, supervisor, forceUpdate)
    94:   //  逆アセンブルリストウインドウを開く
    95:   //  既に開いているときは手前に持ってくる
    96:   public static void ddpOpen (int address, int supervisor, boolean forceUpdate) {
    97:     if (ddpFrame == null) {
    98:       ddpMake ();
    99:     }
   100:     ddpBacktraceRecord = -1L;  //分岐レコードの選択を解除する
   101:     ddpUpdate (address, supervisor, forceUpdate);
   102:     ddpFrame.setVisible (true);
   103:     XEiJ.dbgVisibleMask |= XEiJ.DBG_DDP_VISIBLE_MASK;
   104:   }  //ddpOpen(int,int,boolean)
   105: 
   106:   //ddpMake ()
   107:   //  逆アセンブルリストウインドウを作る
   108:   public static void ddpMake () {
   109: 
   110:     //スクロールテキストエリア
   111:     ddpBoard = ComponentFactory.setPreferredSize (
   112:       ComponentFactory.setFont (new ScrollTextArea (), LnF.lnfMonospacedFont),
   113:       500, 400);
   114:     ddpBoard.setMargin (new Insets (2, 4, 2, 4));
   115:     ddpBoard.setHighlightCursorOn (true);
   116:     ddpTextArea = ddpBoard.getTextArea ();
   117:     ddpTextArea.setEditable (false);
   118: 
   119:     //スピナー
   120:     ddpSpinner = ComponentFactory.createHex8Spinner (ddpPageAddress, DDP_ITEM_MASK, true, new ChangeListener () {
   121:       //スピナーのチェンジリスナー
   122:       //  スピナーが操作されたとき、そのアドレスの行にテキストエリアのキャレットを移動させる
   123:       //  ページの範囲外になったときはテキストエリアを再構築する
   124:       //  ページの構築中に呼び出されたときは何もしない
   125:       @Override public void stateChanged (ChangeEvent ce) {
   126:         if (XEiJ.dbgEventMask == 0) {  //テキストは構築済みでsetTextの中ではない
   127:           ddpUpdate (ddpSpinner.getIntValue (), ddpSupervisorMode, false);
   128:         }
   129:       }
   130:     });
   131: 
   132:     //テキストエリアのキャレットリスナー
   133:     //  テキストエリアがクリックされてキャレットが動いたとき、その行のアドレスをスピナーに設定する
   134:     //  クリックでテキストエリアに移ってしまったフォーカスをスピナーに戻す
   135:     //  ページの構築中に呼び出されたときは何もしない
   136:     //    setText→キャレットリスナー→スピナーのチェンジリスナー→setTextとなるとsetTextの二重呼び出しでエラーが出る
   137:     ComponentFactory.addListener (
   138:       ddpTextArea,
   139:       new CaretListener () {
   140:         @Override public void caretUpdate (CaretEvent ce) {
   141:           if (XEiJ.dbgEventMask == 0) {  //テキストは構築済みでsetTextの中ではない
   142:             int p = ce.getDot ();  //キャレットの位置
   143:             if (p == ce.getMark ()) {  //選択範囲がない
   144:               int i = Arrays.binarySearch (ddpSplitArray, 1, ddpItemCount, p + 1);  //項目の先頭のときも次の項目を検索してから1つ戻る
   145:               i = (i >> 31 ^ i) - 1;  //キャレットがある位置を含む項目の番号
   146:               ddpSpinner.setHintIndex (i);
   147:             }
   148:           }
   149:         }
   150:       });
   151: 
   152:     //テキストエリアのマウスリスナー
   153:     ComponentFactory.addListener (
   154:       ddpTextArea,
   155:       new MouseAdapter () {
   156:         @Override public void mousePressed (MouseEvent me) {
   157:           if (XEiJ.mpuTask == null && me.isPopupTrigger ()) {
   158:             XEiJ.dbgShowPopup (me, ddpTextArea, true);
   159:           }
   160:         }
   161:         @Override public void mouseReleased (MouseEvent me) {
   162:           if (XEiJ.mpuTask == null && me.isPopupTrigger ()) {
   163:             XEiJ.dbgShowPopup (me, ddpTextArea, true);
   164:           }
   165:         }
   166:       });
   167: 
   168:     //ボタンのアクションリスナー
   169:     ActionListener listener = new ActionListener () {
   170:       @Override public void actionPerformed (ActionEvent ae) {
   171:         Object source = ae.getSource ();
   172:         switch (ae.getActionCommand ()) {
   173:         case "Backtrace":
   174:           if (BranchLog.BLG_ON) {
   175:             ddpBacktraceOn = ((JCheckBox) ae.getSource ()).isSelected ();
   176:             if (XEiJ.dbgEventMask == 0) {
   177:               ddpUpdate (ddpAddressArray[ddpItemIndex], ddpSupervisorMode, true);
   178:             }
   179:           }
   180:           break;
   181:         case "User/Supervisor":  //ユーザ/スーパーバイザ
   182:           if (XEiJ.dbgEventMask == 0) {
   183:             ddpUpdate (ddpAddressArray[ddpItemIndex], ((JCheckBox) ae.getSource ()).isSelected () ? 1 : 0, true);
   184:           }
   185:           break;
   186:         }
   187:       }
   188:     };
   189: 
   190:     //バックトレース
   191:     if (BranchLog.BLG_ON) {
   192:       ddpBacktraceOn = false;
   193:       ddpBacktraceRecord = -1L;  //未選択
   194: 
   195:       //バックトレーススピナー
   196:       ddpBacktraceModel = new ReverseLongModel (0L, 0L, 0L, 1L);
   197:       ddpBacktraceSpinner = ComponentFactory.createNumberSpinner (ddpBacktraceModel, 15, new ChangeListener () {
   198:         @Override public void stateChanged (ChangeEvent ce) {
   199:           if (XEiJ.dbgEventMask == 0 && XEiJ.mpuTask == null) {  //MPU停止中
   200:             long record = ddpBacktraceModel.getNumber ().longValue ();
   201:             int i = (char) record << BranchLog.BLG_RECORD_SHIFT;
   202:             if (//ddpBacktraceRecord < 0L ||  //現在選択されている分岐レコードがない
   203:                 ddpBacktraceRecord < record) {  //後ろへ移動する
   204:               ddpBacktraceRecord = record;
   205:               ddpUpdate (BranchLog.blgArray[i] & ~1, BranchLog.blgArray[i] & 1, false);  //分岐レコードの先頭
   206:             } else if (record < ddpBacktraceRecord) {  //手前へ移動する
   207:               ddpBacktraceRecord = record;
   208:               ddpUpdate (BranchLog.blgArray[i + 1], BranchLog.blgArray[i] & 1, false);  //分岐レコードの末尾
   209:             }
   210:           }
   211:         }
   212:       });
   213: 
   214:       //バックトレースチェックボックス
   215:       ddpBacktraceCheckBox =
   216:         Multilingual.mlnToolTipText (
   217:           ComponentFactory.createIconCheckBox (
   218:             ddpBacktraceOn,
   219:             XEiJ.createImage (
   220:               20, 14,
   221:               "22222222222222222222" +
   222:               "2..................2" +
   223:               "2.......1..........2" +
   224:               "2......1.1.........2" +
   225:               "2.....1...1........2" +
   226:               "2....111.111.......2" +
   227:               "2......1.1.........2" +
   228:               "2......1.111111....2" +
   229:               "2......1......1....2" +
   230:               "2......111111.1....2" +
   231:               "2...........1.1....2" +
   232:               "2...........111....2" +
   233:               "2..................2" +
   234:               "22222222222222222222",
   235:               LnF.lnfRGB[0],
   236:               LnF.lnfRGB[6],
   237:               LnF.lnfRGB[12]),
   238:             XEiJ.createImage (
   239:               20, 14,
   240:               "22222222222222222222" +
   241:               "2..................2" +
   242:               "2.......1..........2" +
   243:               "2......1.1.........2" +
   244:               "2.....1...1........2" +
   245:               "2....111.111.......2" +
   246:               "2......1.1.........2" +
   247:               "2......1.111111....2" +
   248:               "2......1......1....2" +
   249:               "2......111111.1....2" +
   250:               "2...........1.1....2" +
   251:               "2...........111....2" +
   252:               "2..................2" +
   253:               "22222222222222222222",
   254:               LnF.lnfRGB[0],
   255:               LnF.lnfRGB[12],
   256:               LnF.lnfRGB[12]),
   257:             "Backtrace", listener),
   258:           "ja", "バックトレース");
   259:     }
   260: 
   261:     //スーパーバイザチェックボックス
   262:     ddpSupervisorCheckBox =
   263:       Multilingual.mlnToolTipText (
   264:         ComponentFactory.createIconCheckBox (
   265:           ddpSupervisorMode != 0,
   266:           XEiJ.createImage (
   267:             20, 14,
   268:             "22222222222222222222" +
   269:             "2..................2" +
   270:             "2..................2" +
   271:             "2.....11....11.....2" +
   272:             "2.....11....11.....2" +
   273:             "2.....11....11.....2" +
   274:             "2.....11....11.....2" +
   275:             "2.....11....11.....2" +
   276:             "2.....11....11.....2" +
   277:             "2.....11111111.....2" +
   278:             "2.....11111111.....2" +
   279:             "2..................2" +
   280:             "2..................2" +
   281:             "22222222222222222222",
   282:             LnF.lnfRGB[0],
   283:             LnF.lnfRGB[12],
   284:             LnF.lnfRGB[12]),
   285:           XEiJ.createImage (
   286:             20, 14,
   287:             "22222222222222222222" +
   288:             "2..................2" +
   289:             "2..................2" +
   290:             "2.....11111111.....2" +
   291:             "2.....11111111.....2" +
   292:             "2.....11...........2" +
   293:             "2.....11111111.....2" +
   294:             "2.....11111111.....2" +
   295:             "2...........11.....2" +
   296:             "2.....11111111.....2" +
   297:             "2.....11111111.....2" +
   298:             "2..................2" +
   299:             "2..................2" +
   300:             "22222222222222222222",
   301:             LnF.lnfRGB[0],
   302:             LnF.lnfRGB[12],
   303:             LnF.lnfRGB[12]),
   304:           "User/Supervisor", listener),
   305:         "ja", "ユーザ/スーパーバイザ");
   306: 
   307:     //ウインドウ
   308:     ddpFrame = Multilingual.mlnTitle (
   309:       ComponentFactory.createRestorableSubFrame (
   310:         Settings.SGS_DDP_FRAME_KEY,
   311:         "Disassemble list",
   312:         null,
   313:         ComponentFactory.createBorderPanel (
   314:           ddpBoard,
   315:           ComponentFactory.createHorizontalBox (
   316:             ddpSpinner,
   317:             ddpSupervisorCheckBox,
   318:             Box.createHorizontalStrut (12),
   319:             (BranchLog.BLG_ON ?
   320:              ComponentFactory.createHorizontalBox (
   321:                ddpBacktraceCheckBox,
   322:                ddpBacktraceSpinner,
   323:                Box.createHorizontalStrut (12)) :
   324:              null),
   325:             Box.createHorizontalGlue (),
   326:             XEiJ.mpuMakeOriIllegalCheckBox (),  //ORI.B #$00,D0を不当命令とみなすチェックボックス
   327:             XEiJ.mpuMakeStopOnErrorCheckBox (),  //エラーで停止するチェックボックス
   328:             XEiJ.mpuMakeStopAtStartCheckBox (),  //実行開始位置で停止するチェックボックス
   329:             Box.createHorizontalStrut (12),
   330:             XEiJ.mpuMakeBreakButton (),  //停止ボタン
   331:             XEiJ.mpuMakeTraceButton (),  //トレース実行ボタン
   332:             XEiJ.mpuMakeTrace10Button (),  //トレース10回ボタン
   333:             XEiJ.mpuMakeTrace100Button (),  //トレース100回ボタン
   334:             XEiJ.mpuMakeStepButton (),  //ステップ実行ボタン
   335:             XEiJ.mpuMakeStep10Button (),  //ステップ10回ボタン
   336:             XEiJ.mpuMakeStep100Button (),  //ステップ100回ボタン
   337:             XEiJ.mpuMakeReturnButton (),  //ステップアンティルリターンボタン
   338:             XEiJ.mpuMakeRunButton ()  //実行ボタン
   339:             )
   340:           )
   341:         ),
   342:       "ja", "逆アセンブルリスト");
   343:     ComponentFactory.addListener (
   344:       ddpFrame,
   345:       new WindowAdapter () {
   346:         @Override public void windowClosing (WindowEvent we) {
   347:           XEiJ.dbgVisibleMask &= ~XEiJ.DBG_DDP_VISIBLE_MASK;
   348:         }
   349:       });
   350: 
   351:     ddpStoppedBy = null;
   352:     ddpStoppedAddress = -1;
   353: 
   354:   }  //ddpMake()
   355: 
   356:   //ddpUpdate (address, supervisor, forceUpdate)
   357:   //  逆アセンブルリストウインドウを更新する
   358:   //  address  -1=pcまたはpc0を表示,0=前回と同じアドレスを表示
   359:   public static void ddpUpdate (int address, int supervisor, boolean forceUpdate) {
   360: 
   361:     XEiJ.dbgEventMask++;  //構築開始
   362: 
   363:     if (address == -1) {  //pcまたはpc0を表示
   364:       ddpStoppedAddress = address = ddpStoppedBy == null ? XEiJ.regPC : XEiJ.regPC0;
   365:       forceUpdate = true;
   366:     } else if (address == 0) {  //前回と同じアドレスを表示。同じアドレスで再構築したいときに使う
   367:       address = ddpItemAddress;
   368:     }
   369: 
   370:     if (supervisor == -1) {
   371:       supervisor = XEiJ.regSRS;
   372:       forceUpdate = true;
   373:     }
   374: 
   375:     if ((ddpSupervisorMode != 0) != (supervisor != 0)) {  //ユーザ/スーパーバイザが一致しない
   376:       ddpSupervisorMode = supervisor;
   377:       forceUpdate = true;
   378:       if (ddpSupervisorCheckBox.isSelected () != (supervisor != 0)) {
   379:         ddpSupervisorCheckBox.setSelected (supervisor != 0);
   380:       }
   381:     }
   382: 
   383:     if (forceUpdate) {  //再構築要求
   384:       ddpItemCount = 0;
   385:     }
   386: 
   387:     address &= DDP_ITEM_MASK;  //目的のアドレスを含む項目の先頭アドレス
   388: 
   389:     //バックトレース
   390:     if (BranchLog.BLG_ON) {
   391:       if (XEiJ.mpuTask == null) {  //MPU停止中
   392:         long newestRecord = BranchLog.blgNewestRecord;  //最新のレコードの番号
   393:         long oldestRecord = Math.max (0L, newestRecord - 65535);  //最古のレコードの番号
   394:         if (//ddpBacktraceRecord < 0L ||  //レコードが選択されていない
   395:             ddpBacktraceRecord < oldestRecord || newestRecord < ddpBacktraceRecord) {  //選択されているレコードが存在しない
   396:           ddpBacktraceRecord = newestRecord;  //最新のレコードを選択する
   397:           ddpBacktraceModel.setMaximum (Long.valueOf (newestRecord));
   398:           ddpBacktraceModel.setValue (Long.valueOf (newestRecord));
   399:         }
   400:         if (ddpBacktraceOn) {  //バックトレースモードのとき
   401:           int i = (char) ddpBacktraceRecord << BranchLog.BLG_RECORD_SHIFT;
   402:           if (address >>> 1 < BranchLog.blgArray[i] >>> 1) {  //現在選択されているレコードよりも前
   403:             if (oldestRecord < ddpBacktraceRecord) {  //直前にレコードがある
   404:               ddpBacktraceRecord--;  //直前のレコードを選択する
   405:               ddpBacktraceModel.setValue (Long.valueOf (ddpBacktraceRecord));
   406:               address = BranchLog.blgArray[((char) ddpBacktraceRecord << BranchLog.BLG_RECORD_SHIFT) + 1] & ~1;  //直前のレコードの末尾に移動する
   407:             }
   408:           } else if (BranchLog.blgArray[i + 1] >>> 1 < address >>> 1) {  //現在選択されているレコードよりも後
   409:             if (ddpBacktraceRecord < newestRecord) {  //直後にレコードがある
   410:               ddpBacktraceRecord++;  //直後のレコードを選択する
   411:               ddpBacktraceModel.setValue (Long.valueOf (ddpBacktraceRecord));
   412:               address = BranchLog.blgArray[(char) ddpBacktraceRecord << BranchLog.BLG_RECORD_SHIFT] & ~1;  //直後のレコードの先頭に移動する
   413:             }
   414:           }
   415:         }
   416:       }
   417:     }
   418: 
   419:     if (ddpItemCount != 0) {  //構築前または再構築要求のいずれでもない
   420:       int i = Arrays.binarySearch (ddpAddressArray, 1, ddpItemCount, address + 1);  //項目の先頭のときも次の項目を検索してから1つ戻る
   421:       i = (i >> 31 ^ i) - 1;  //目的のアドレスを含む項目の番号
   422:       if (0 < i && i < ddpItemCount - 1 &&  //ページの内側
   423:           ddpAddressArray[i] == address &&  //項目の先頭
   424:           !ddpDCWArray[i]) {  //.dc.wで出力されていない
   425: 
   426:         //再構築しない
   427: 
   428:         ddpItemAddress = address;
   429:         if (ddpItemIndex != i) {  //キャレットがある項目を変更する必要がある
   430:           ddpItemIndex = i;
   431:           ddpTextArea.setCaretPosition (ddpCaretArray[i]);
   432:         }
   433: 
   434:         //!
   435:         //バックトレースモードのとき分岐レコードの範囲が変わったときはハイライト表示を更新する
   436: 
   437:         XEiJ.dbgEventMask--;  //構築終了
   438:         return;
   439:       }
   440:     }
   441: 
   442:     //再構築する
   443:     ddpItemAddress = address;
   444: 
   445:     //構築前または再構築要求または先頭または末尾の番兵が選択された
   446:     //  0x00000000の境界を跨ぐとき反対側を指すことがあるので先頭と末尾の番兵を区別しない
   447:     ddpPageAddress = address & DDP_PAGE_MASK;  //ページの先頭アドレス
   448:     int pageEndAddress = ddpPageAddress + DDP_PAGE_SIZE;  //ページの末尾アドレス。0になることがある
   449: 
   450:     //先頭の番兵
   451:     ddpAddressArray[0] = ddpPageAddress - DDP_ITEM_SIZE;  //昇順を維持するためマスクしない
   452:     ddpSplitArray[0] = 0;
   453:     ddpCaretArray[0] = 0;
   454:     StringBuilder sb = new StringBuilder (
   455:       //         1111111111222222222233333333334444444444555555555566666666667777777777
   456:       //1234567890123456789012345678901234567890123456789012345678901234567890123456789
   457:       //xxxxxxx  xxxxxxxxxxxxxxxxxxxx  ssssssssssssssssssssssssssssssssssss..........
   458:       //                               move.b  100(a0,d0.l),100(a0,d0.l)
   459:       "          +0+1+2+3+4+5+6+7+8+9                                              ▲\n");
   460:     int itemCount = 1;  //項目数
   461:     int itemAddress = ddpPageAddress;  //項目の先頭アドレス
   462:     int dcwAddress = pageEndAddress;  //.dc.wで出力する範囲
   463:     int dcwEndAddress = pageEndAddress;
   464:     boolean prevBranchFlag = false;  //true=直前が完全分岐命令だった
   465: 
   466:     //ラベル
   467:     //LabeledAddress.lblUpdateProgram ();
   468: 
   469:     TreeMap<Integer,InstructionBreakPoint.InstructionBreakRecord> pointTable;
   470:     if (InstructionBreakPoint.IBP_ON) {
   471:       pointTable = supervisor != 0 ? InstructionBreakPoint.ibpSuperPointTable : InstructionBreakPoint.ibpUserPointTable;
   472:     }
   473: 
   474:   itemLoop:
   475:     do {
   476:       int itemEndAddress;  //項目の末尾アドレス
   477:       String code;  //逆アセンブル結果
   478: 
   479:       //逆アセンブルする
   480:       //  以下のアドレスを跨いでしまうときは逆アセンブルせず1ワードずつ.dc.wまたはmoveqで出力する
   481:       //    目的のアドレス
   482:       //    命令ブレークポイント
   483:       //  途中の行をクリックすることで途中から逆アセンブルし直せるようにするため、複数ワードあっても1行にまとめない
   484:       //  途中に逆アセンブルできる命令があっても、跨いではいけないアドレスまですべて1ワードずつ出力する
   485:       if (dcwAddress <= itemAddress && itemAddress < dcwEndAddress) {  //.dc.wで出力中
   486:         Disassembler.disStatus = 0;  //念のため
   487:         int oc = MC68060.mmuPeekWordZeroCode (itemAddress, supervisor);
   488:         if ((oc & 0xfe00) == 0x7000 && MC68060.mmuPeekWordZeroCode (itemAddress + 2, supervisor) == 0x4e4f) {  //moveq.l #$xx,d0;trap#15
   489:           //pcがIOCSコールのtrap#15を指しているときmoveqが.dc.wになってしまうのを避ける
   490:           XEiJ.fmtHex2 (DDP_MOVEQD0_BASE, 10, oc);
   491:           code = String.valueOf (DDP_MOVEQD0_BASE);
   492:         } else {
   493:           XEiJ.fmtHex4 (DDP_DCW_BASE, 9, oc);
   494:           code = String.valueOf (DDP_DCW_BASE);
   495:         }
   496:         itemEndAddress = itemAddress + 2;
   497:         ddpDCWArray[itemCount] = true;
   498:       } else {  //.dc.wで出力中ではない
   499:         code = Disassembler.disDisassemble (new StringBuilder (), itemAddress, supervisor).toString ();  //逆アセンブルする
   500:         for (int t = itemAddress + 2; t < Disassembler.disPC; t += 2) {
   501:           if (t == address ||  //目的のアドレスを跨いでしまった
   502:               InstructionBreakPoint.IBP_ON && pointTable.containsKey (t)) {  //命令ブレークポイントを跨いでしまった
   503:             //!
   504:             //バックトレースモードのとき選択されている分岐レコードの先頭も跨がないようにする
   505:             //  IOCSの_B_READと_B_WRITEでmoveq.l #<data>,d0だけ変更してtrap#15に飛び込んでいるところなど
   506:             dcwAddress = itemAddress;  //.dc.wで出力し直す
   507:             dcwEndAddress = t;
   508:             continue itemLoop;
   509:           }
   510:         }
   511:         itemEndAddress = Disassembler.disPC;
   512:         ddpDCWArray[itemCount] = false;
   513:       }
   514: 
   515:       //完全分岐命令の下に隙間を空けて読みやすくする
   516:       if (prevBranchFlag) {
   517:         sb.append ('\n');
   518:       }
   519: 
   520:       //項目の開始
   521:       if (itemAddress == address) {
   522:         ddpItemIndex = itemCount;  //目的のアドレスを含む項目の番号
   523:       }
   524:       ddpAddressArray[itemCount] = itemAddress;  //項目の先頭アドレス
   525:       ddpSplitArray[itemCount] = sb.length ();  //項目を区切る位置
   526: 
   527:       if (prevBranchFlag) {
   528:         //ラベル
   529:         if (true) {
   530:           int i = sb.length ();
   531:           LabeledAddress.lblSearch (sb, itemAddress);
   532:           if (i < sb.length ()) {
   533:             sb.append ('\n');
   534:           }
   535:         }
   536:       }
   537: 
   538:       //停止理由
   539:       if (itemAddress == ddpStoppedAddress && ddpStoppedBy != null) {
   540:         sb.append (ddpStoppedBy).append ('\n');
   541:       }
   542: 
   543:       ddpCaretArray[itemCount] = sb.length ();  //項目が選択されたときキャレットを移動させる位置
   544: 
   545:       //1行目
   546:       int lineAddress = itemAddress;  //行の開始アドレス
   547:       int lineEndAddress = Math.min (lineAddress + 10, itemEndAddress);  //行の終了アドレス
   548:       //アドレス
   549:       XEiJ.fmtHex8 (sb, lineAddress).append ("  ");
   550:       //データ
   551:       for (int a = lineAddress; a < lineEndAddress; a += 2) {
   552:         XEiJ.fmtHex4 (sb, MC68060.mmuPeekWordZeroCode (a, supervisor));
   553:       }
   554:       sb.append (XEiJ.DBG_SPACES, 0, 2 * Math.max (0, lineAddress + 10 - lineEndAddress) + 2);
   555:       //逆アセンブル結果
   556:       sb.append (code).append (XEiJ.DBG_SPACES, 0, Math.max (1, 68 - 32 - code.length ()));
   557:       //キャラクタ
   558:       InstructionBreakPoint.InstructionBreakRecord r = InstructionBreakPoint.IBP_ON ? pointTable.get (itemAddress) : null;
   559:       if (r != null) {  //命令ブレークポイントがある
   560:         if (r.ibrThreshold < 0) {  //インスタント
   561:           sb.append ("**********");
   562:         } else {
   563:           sb.append (r.ibrValue).append ('/').append (r.ibrThreshold);
   564:         }
   565:       } else {  //命令ブレークポイントがない
   566:         for (int a = lineAddress; a < lineEndAddress; a++) {
   567:           int h = MC68060.mmuPeekByteZeroCode (a, supervisor);
   568:           int c;
   569:           if (0x81 <= h && h <= 0x9f || 0xe0 <= h && h <= 0xef) {  //SJISの2バイトコードの1バイト目
   570:             int l = MC68060.mmuPeekByteZeroCode (a + 1, supervisor);  //これは範囲外になる場合がある
   571:             if (0x40 <= l && l != 0x7f && l <= 0xfc) {  //SJISの2バイトコードの2バイト目
   572:               c = CharacterCode.chrSJISToChar[h << 8 | l];  //2バイトで変換する
   573:               if (c == 0) {  //対応する文字がない
   574:                 c = '※';
   575:               }
   576:               a++;
   577:             } else {  //SJISの2バイトコードの2バイト目ではない
   578:               c = '.';  //SJISの2バイトコードの1バイト目ではなかった
   579:             }
   580:           } else {  //SJISの2バイトコードの1バイト目ではない
   581:             c = CharacterCode.chrSJISToChar[h];  //1バイトで変換する
   582:             if (c < 0x20 || c == 0x7f) {  //対応する文字がないまたは制御コード
   583:               c = '.';
   584:             }
   585:           }
   586:           sb.append ((char) c);
   587:         }  //for a
   588:       }
   589:       sb.append ('\n');
   590: 
   591:       //2行目以降
   592:       while (lineEndAddress < itemEndAddress) {
   593:         lineAddress = lineEndAddress;  //行の開始アドレス
   594:         lineEndAddress = Math.min (lineAddress + 10, itemEndAddress);  //行の終了アドレス
   595:         //アドレス
   596:         XEiJ.fmtHex8 (sb, lineAddress).append ("  ");
   597:         //データ
   598:         for (int a = lineAddress; a < lineEndAddress; a += 2) {
   599:           XEiJ.fmtHex4 (sb, MC68060.mmuPeekWordZeroCode (a, supervisor));
   600:         }
   601:         sb.append (XEiJ.DBG_SPACES, 0, 2 * Math.max (0, lineAddress + 10 - lineEndAddress) + (2 + 68 - 32));
   602:         //キャラクタ
   603:         for (int a = lineAddress; a < lineEndAddress; a++) {
   604:           int h = MC68060.mmuPeekByteZeroCode (a, supervisor);
   605:           int c;
   606:           if (0x81 <= h && h <= 0x9f || 0xe0 <= h && h <= 0xef) {  //SJISの2バイトコードの1バイト目
   607:             int l = MC68060.mmuPeekByteZeroCode (a + 1, supervisor);  //これは範囲外になる場合がある
   608:             if (0x40 <= l && l != 0x7f && l <= 0xfc) {  //SJISの2バイトコードの2バイト目
   609:               c = CharacterCode.chrSJISToChar[h << 8 | l];  //2バイトで変換する
   610:               if (c == 0) {  //対応する文字がない
   611:                 c = '※';
   612:               }
   613:               a++;
   614:             } else {  //SJISの2バイトコードの2バイト目ではない
   615:               c = '.';  //SJISの2バイトコードの1バイト目ではなかった
   616:             }
   617:           } else {  //SJISの2バイトコードの1バイト目ではない
   618:             c = CharacterCode.chrSJISToChar[h];  //1バイトで変換する
   619:             if (c < 0x20 || c == 0x7f) {  //対応する文字がないまたは制御コード
   620:               c = '.';
   621:             }
   622:           }
   623:           sb.append ((char) c);
   624:         }  //for a
   625:         sb.append ('\n');
   626:       }
   627: 
   628:       //項目の終了
   629:       itemCount++;
   630:       itemAddress = itemEndAddress;
   631: 
   632:       //完全分岐命令の下に隙間を空けて読みやすくする
   633:       prevBranchFlag = (Disassembler.disStatus & Disassembler.DIS_ALWAYS_BRANCH) != 0;
   634: 
   635:     } while (itemAddress < pageEndAddress);
   636: 
   637:     //末尾の番兵
   638:     ddpAddressArray[itemCount] = itemAddress;  //昇順を維持するためマスクしない
   639:     ddpSplitArray[itemCount] = sb.length ();
   640:     ddpCaretArray[itemCount] = sb.length ();
   641:     sb.append (
   642:       //         1111111111222222222233333333334444444444555555555566666666667777777777
   643:       //1234567890123456789012345678901234567890123456789012345678901234567890123456789
   644:       "          +0+1+2+3+4+5+6+7+8+9                                              ▼");
   645:     itemCount++;
   646:     ddpItemCount = itemCount;
   647: 
   648:     //テキスト
   649:     ddpTextArea.setText (sb.toString ());
   650:     ddpTextArea.setCaretPosition (ddpCaretArray[ddpItemIndex]);
   651: 
   652:     //!
   653:     //バックトレースモードのとき選択されている分岐レコードの範囲をハイライト表示する
   654: 
   655:     //スピナー
   656:     ddpSpinner.setHintArray (ddpAddressArray, itemCount);
   657:     ddpSpinner.setHintIndex (ddpItemIndex);
   658: 
   659:     XEiJ.dbgEventMask--;  //構築終了
   660: 
   661:   }  //ddpUpdate(int,int,boolean)
   662: 
   663: }  //class DisassembleList
   664: 
   665: 
   666: