DisassembleList.java
     1: //========================================================================================
     2: //  DisassembleList.java
     3: //    en:Disassemble list
     4: //    ja:逆アセンブルリスト
     5: //  Copyright (C) 2003-2025 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:   public static int ddpProgramMode;  //アドレスとオフセット。0=アドレスのみ,1=オフセットのみ,2=両方
    69:   public static RotaryButton ddpProgramButton;  //アドレスとオフセットボタン
    70:   public static JComboBox<String> ddpProgramBox;  //プログラム名コンボボックス
    71:   public static int ddpProgramAge;
    72: 
    73: 
    74:   //ddpInit ()
    75:   //  初期化
    76:   public static void ddpInit () {
    77: 
    78:     ddpItemCount = 0;  //構築前
    79:     ddpItemIndex = 0;
    80:     ddpItemAddress = -1;
    81:     ddpPageAddress = 0;
    82:     ddpSupervisorMode = 1;
    83:     //ddpAddressArray = new int[DDP_MAX_ITEMS];
    84:     //ddpSplitArray = new int[DDP_MAX_ITEMS];
    85:     //ddpCaretArray = new int[DDP_MAX_ITEMS];
    86:     //ddpDCWArray = new boolean[DDP_MAX_ITEMS];
    87: 
    88:     ddpFrame = null;
    89: 
    90:   }  //ddpInit()
    91: 
    92:   //ddpStart ()
    93:   public static void ddpStart () {
    94:     if (RestorableFrame.rfmGetOpened (Settings.SGS_DDP_FRAME_KEY)) {
    95:       ddpOpen (-1, -1, true);
    96:     }
    97:   }  //ddpStart()
    98: 
    99:   //ddpOpen (address, supervisor, forceUpdate)
   100:   //  逆アセンブルリストウインドウを開く
   101:   //  既に開いているときは手前に持ってくる
   102:   public static void ddpOpen (int address, int supervisor, boolean forceUpdate) {
   103:     if (ddpFrame == null) {
   104:       ddpMake ();
   105:     }
   106:     ddpFrame.setVisible (true);
   107:     ddpBacktraceRecord = -1L;  //分岐レコードの選択を解除する
   108:     LabeledAddress.lblClear ();
   109:     ddpUpdate (address, supervisor, forceUpdate);
   110:     XEiJ.pnlExitFullScreen (false);
   111:     XEiJ.dbgVisibleMask |= XEiJ.DBG_DDP_VISIBLE_MASK;
   112:   }  //ddpOpen(int,int,boolean)
   113: 
   114:   //ddpMake ()
   115:   //  逆アセンブルリストウインドウを作る
   116:   public static void ddpMake () {
   117: 
   118:     //スクロールテキストエリア
   119:     ddpBoard = ComponentFactory.setPreferredSize (
   120:       ComponentFactory.setFont (new ScrollTextArea (), LnF.lnfMonospacedFont),
   121:       730, 400);
   122:     ddpBoard.setMargin (new Insets (2, 4, 2, 4));
   123:     ddpBoard.setHighlightCursorOn (true);
   124:     ddpTextArea = ddpBoard.getTextArea ();
   125:     ddpTextArea.setEditable (false);
   126:     ddpTextArea.addMouseWheelListener ((mwe) -> {
   127:       int n = mwe.getWheelRotation ();
   128:       JViewport v = ddpBoard.getViewport ();
   129:       Point p = v.getViewPosition ();
   130:       v.setViewPosition (new Point (p.x,
   131:                                     Math.max (0,
   132:                                               Math.min (ddpTextArea.getSize ().height - v.getExtentSize ().height,
   133:                                                         p.y + n * ddpTextArea.getFont ().getSize () * 5))));
   134:     });
   135: 
   136:     //スピナー
   137:     ddpSpinner = ComponentFactory.createHex8Spinner (ddpPageAddress, DDP_ITEM_MASK, true, new ChangeListener () {
   138:       //スピナーのチェンジリスナー
   139:       //  スピナーが操作されたとき、そのアドレスの行にテキストエリアのキャレットを移動させる
   140:       //  ページの範囲外になったときはテキストエリアを再構築する
   141:       //  ページの構築中に呼び出されたときは何もしない
   142:       @Override public void stateChanged (ChangeEvent ce) {
   143:         if (XEiJ.dbgEventMask == 0) {  //テキストは構築済みでsetTextの中ではない
   144:           ddpUpdate (ddpSpinner.getAbsoluteValue (), ddpSupervisorMode, false);
   145:         }
   146:       }
   147:     });
   148: 
   149:     //テキストエリアのキャレットリスナー
   150:     //  テキストエリアがクリックされてキャレットが動いたとき、その行のアドレスをスピナーに設定する
   151:     //  クリックでテキストエリアに移ってしまったフォーカスをスピナーに戻す
   152:     //  ページの構築中に呼び出されたときは何もしない
   153:     //    setText→キャレットリスナー→スピナーのチェンジリスナー→setTextとなるとsetTextの二重呼び出しでエラーが出る
   154:     ComponentFactory.addListener (
   155:       ddpTextArea,
   156:       new CaretListener () {
   157:         @Override public void caretUpdate (CaretEvent ce) {
   158:           if (XEiJ.dbgEventMask == 0) {  //テキストは構築済みでsetTextの中ではない
   159:             int p = ce.getDot ();  //キャレットの位置
   160:             if (p == ce.getMark ()) {  //選択範囲がない
   161:               int i = Arrays.binarySearch (ddpSplitArray, 1, ddpItemCount, p + 1);  //項目の先頭のときも次の項目を検索してから1つ戻る
   162:               i = (i >> 31 ^ i) - 1;  //キャレットがある位置を含む項目の番号
   163:               ddpSpinner.setHintIndex (i);
   164:               //ddpProgramBox.setSelectedIndex (LabeledAddress.lblGetIndex (ddpAddressArray[i]) + 1);
   165:             }
   166:           }
   167:         }
   168:       });
   169: 
   170:     //テキストエリアのマウスリスナー
   171:     ComponentFactory.addListener (
   172:       ddpTextArea,
   173:       new MouseAdapter () {
   174:         @Override public void mousePressed (MouseEvent me) {
   175:           if (XEiJ.mpuTask == null && me.isPopupTrigger ()) {
   176:             XEiJ.dbgShowPopup (me, ddpTextArea, true);
   177:           }
   178:         }
   179:         @Override public void mouseReleased (MouseEvent me) {
   180:           if (XEiJ.mpuTask == null && me.isPopupTrigger ()) {
   181:             XEiJ.dbgShowPopup (me, ddpTextArea, true);
   182:           }
   183:         }
   184:       });
   185: 
   186:     //ボタンのアクションリスナー
   187:     ActionListener listener = new ActionListener () {
   188:       @Override public void actionPerformed (ActionEvent ae) {
   189:         Object source = ae.getSource ();
   190:         switch (ae.getActionCommand ()) {
   191:         case "Backtrace":
   192:           if (BranchLog.BLG_ON) {
   193:             ddpBacktraceOn = ((JCheckBox) ae.getSource ()).isSelected ();
   194:             if (XEiJ.dbgEventMask == 0) {
   195:               ddpUpdate (ddpAddressArray[ddpItemIndex], ddpSupervisorMode, true);
   196:             }
   197:           }
   198:           break;
   199:         case "User/Supervisor":  //ユーザ/スーパーバイザ
   200:           if (XEiJ.dbgEventMask == 0) {
   201:             ddpUpdate (ddpAddressArray[ddpItemIndex], ((JCheckBox) ae.getSource ()).isSelected () ? 1 : 0, true);
   202:           }
   203:           break;
   204:         case "Address and/or offset":  //アドレスとオフセット
   205:           ddpProgramMode = ddpProgramButton.getIndex ();
   206:           if (XEiJ.dbgEventMask == 0) {
   207:             ddpUpdate (0, ddpSupervisorMode, true);
   208:           }
   209:           break;
   210:         case "Program name":  //プログラム名
   211:           {
   212:             int index = ddpProgramBox.getSelectedIndex ();
   213:             if (0 <= index) {
   214:               if (index == 0) {
   215:                 ddpSpinner.setOffset (0);
   216:               } else {
   217:                 if (ddpProgramAge == LabeledAddress.lblProgramAge) {  //念の為
   218:                   ddpSpinner.setOffset (LabeledAddress.lblProgramHead[index - 1]);
   219:                 }
   220:               }
   221:             }
   222:           }
   223:           break;
   224:         }
   225:       }
   226:     };
   227: 
   228:     //アドレスとオフセットボタン
   229:     ddpProgramMode = 0;
   230:     ddpProgramButton = Multilingual.mlnToolTipText (
   231:       ComponentFactory.createRotaryButton (
   232:         "Address and/or offset",
   233:         listener,
   234:         ddpProgramMode,
   235:         LnF.LNF_ADDRESS_IMAGE,
   236:         LnF.LNF_OFFSET_IMAGE,
   237:         LnF.LNF_BOTH_IMAGE
   238:         ),
   239:       "ja", "アドレスとオフセット");
   240: 
   241:     //プログラム名コンボボックス
   242:     ddpProgramBox = Multilingual.mlnToolTipText (
   243:       ComponentFactory.createComboBox (0, "Program name", listener, 16, ""),
   244:       "ja", "プログラム名");
   245:     ddpProgramBox.setMaximumRowCount (16);
   246:     ddpProgramAge = -1;
   247: 
   248:     //バックトレース
   249:     if (BranchLog.BLG_ON) {
   250:       ddpBacktraceOn = false;
   251:       ddpBacktraceRecord = -1L;  //未選択
   252: 
   253:       //バックトレーススピナー
   254:       ddpBacktraceModel = new ReverseLongModel (0L, 0L, 0L, 1L);
   255:       ddpBacktraceSpinner = ComponentFactory.createNumberSpinner (ddpBacktraceModel, 10, new ChangeListener () {
   256:         @Override public void stateChanged (ChangeEvent ce) {
   257:           if (XEiJ.dbgEventMask == 0 && XEiJ.mpuTask == null) {  //MPU停止中
   258:             long record = ddpBacktraceModel.getNumber ().longValue ();
   259:             int i = (char) record << BranchLog.BLG_RECORD_SHIFT;
   260:             if (//ddpBacktraceRecord < 0L ||  //現在選択されている分岐レコードがない
   261:                 ddpBacktraceRecord < record) {  //後ろへ移動する
   262:               ddpBacktraceRecord = record;
   263:               ddpUpdate (BranchLog.blgArray[i] & ~1, BranchLog.blgArray[i] & 1, false);  //分岐レコードの先頭
   264:             } else if (record < ddpBacktraceRecord) {  //手前へ移動する
   265:               ddpBacktraceRecord = record;
   266:               ddpUpdate (BranchLog.blgArray[i + 1] & ~1, BranchLog.blgArray[i] & 1, false);  //分岐レコードの末尾
   267:             }
   268:           }
   269:         }
   270:       });
   271: 
   272:       //バックトレースチェックボックス
   273:       ddpBacktraceCheckBox =
   274:         Multilingual.mlnToolTipText (
   275:           ComponentFactory.createIconCheckBox (
   276:             ddpBacktraceOn,
   277:             LnF.LNF_BACK_TRACE_IMAGE,
   278:             LnF.LNF_BACK_TRACE_SELECTED_IMAGE,
   279:             "Backtrace", listener),
   280:           "ja", "バックトレース");
   281:     }
   282: 
   283:     //スーパーバイザチェックボックス
   284:     ddpSupervisorCheckBox =
   285:       Multilingual.mlnToolTipText (
   286:         ComponentFactory.createIconCheckBox (
   287:           ddpSupervisorMode != 0,
   288:           LnF.LNF_USER_SELECTED_IMAGE,
   289:           LnF.LNF_SUPERVISOR_SELECTED_IMAGE,
   290:           "User/Supervisor", listener),
   291:         "ja", "ユーザ/スーパーバイザ");
   292: 
   293:     //ウインドウ
   294:     ddpFrame = Multilingual.mlnTitle (
   295:       ComponentFactory.createRestorableSubFrame (
   296:         Settings.SGS_DDP_FRAME_KEY,
   297:         "Disassemble list",
   298:         null,
   299:         ComponentFactory.createBorderPanel (
   300:           //CENTER
   301:           ddpBoard,
   302:           //NORTH
   303:           ComponentFactory.createVerticalBox (
   304:             //1行目
   305:             ComponentFactory.createHorizontalBox (
   306:               Box.createHorizontalGlue (),
   307:               XEiJ.mpuMakeOriIllegalCheckBox (),  //ORI.B #$00,D0を不当命令とみなすチェックボックス
   308:               XEiJ.mpuMakeStopOnErrorCheckBox (),  //エラーで停止するチェックボックス
   309:               XEiJ.mpuMakeStopAtStartCheckBox (),  //実行開始位置で停止するチェックボックス
   310:               Box.createHorizontalStrut (12),
   311:               XEiJ.mpuMakeBreakButton (),  //停止ボタン
   312:               XEiJ.mpuMakeTraceButton (),  //トレース実行ボタン
   313:               XEiJ.mpuMakeTrace10Button (),  //トレース10回ボタン
   314:               XEiJ.mpuMakeTrace100Button (),  //トレース100回ボタン
   315:               XEiJ.mpuMakeStepButton (),  //ステップ実行ボタン
   316:               XEiJ.mpuMakeStep10Button (),  //ステップ10回ボタン
   317:               XEiJ.mpuMakeStep100Button (),  //ステップ100回ボタン
   318:               XEiJ.mpuMakeReturnButton (),  //ステップアンティルリターンボタン
   319:               XEiJ.mpuMakeRunButton ()  //実行ボタン
   320:               ),
   321:             //2行目
   322:             ComponentFactory.createHorizontalBox (
   323:               ddpProgramButton,
   324:               ddpProgramBox,
   325:               ddpSpinner,
   326:               ddpSupervisorCheckBox,
   327:               Box.createHorizontalStrut (12),
   328:               (BranchLog.BLG_ON ?
   329:                ComponentFactory.createHorizontalBox (
   330:                  ddpBacktraceCheckBox,
   331:                  ddpBacktraceSpinner,
   332:                  Box.createHorizontalStrut (12)) :
   333:                null),
   334:               Box.createHorizontalGlue ()
   335:               )
   336:             )
   337:           )
   338:         ),
   339:       "ja", "逆アセンブルリスト");
   340:     ComponentFactory.addListener (
   341:       ddpFrame,
   342:       new WindowAdapter () {
   343:         @Override public void windowClosing (WindowEvent we) {
   344:           XEiJ.dbgVisibleMask &= ~XEiJ.DBG_DDP_VISIBLE_MASK;
   345:         }
   346:       });
   347: 
   348:     ddpStoppedBy = null;
   349:     ddpStoppedAddress = -1;
   350: 
   351:   }  //ddpMake()
   352: 
   353:   //ddpUpdate (address, supervisor, forceUpdate)
   354:   //  逆アセンブルリストウインドウを更新する
   355:   //  address  -1=pcまたはpc0を表示,0=前回と同じアドレスを表示
   356:   public static void ddpUpdate (int address, int supervisor, boolean forceUpdate) {
   357: 
   358:     XEiJ.dbgEventMask++;  //構築開始
   359: 
   360:     if (address == -1) {  //pcまたはpc0を表示
   361:       ddpStoppedAddress = address = ddpStoppedBy == null ? XEiJ.regPC : XEiJ.regPC0;
   362:       forceUpdate = true;
   363:     } else if (address == 0) {  //前回と同じアドレスを表示。同じアドレスで再構築したいときに使う
   364:       address = ddpItemAddress;
   365:     }
   366: 
   367:     if (supervisor == -1) {
   368:       supervisor = XEiJ.regSRS;
   369:       forceUpdate = true;
   370:     }
   371: 
   372:     if ((ddpSupervisorMode != 0) != (supervisor != 0)) {  //ユーザ/スーパーバイザが一致しない
   373:       ddpSupervisorMode = supervisor;
   374:       forceUpdate = true;
   375:       if (ddpSupervisorCheckBox.isSelected () != (supervisor != 0)) {
   376:         ddpSupervisorCheckBox.setSelected (supervisor != 0);
   377:       }
   378:     }
   379: 
   380:     if (forceUpdate) {  //再構築要求
   381:       ddpItemCount = 0;
   382:     }
   383: 
   384:     address &= DDP_ITEM_MASK;  //目的のアドレスを含む項目の先頭アドレス
   385: 
   386:     //バックトレース
   387:     if (BranchLog.BLG_ON) {
   388:       if (XEiJ.mpuTask == null) {  //MPU停止中
   389:         long newestRecord = BranchLog.blgNewestRecord;  //最新のレコードの番号
   390:         long oldestRecord = Math.max (0L, newestRecord - 65535);  //最古のレコードの番号
   391:         if (//ddpBacktraceRecord < 0L ||  //レコードが選択されていない
   392:             ddpBacktraceRecord < oldestRecord || newestRecord < ddpBacktraceRecord) {  //選択されているレコードが存在しない
   393:           ddpBacktraceRecord = newestRecord;  //最新のレコードを選択する
   394:           ddpBacktraceModel.setMaximum (Long.valueOf (newestRecord));
   395:           ddpBacktraceModel.setValue (Long.valueOf (newestRecord));
   396:         }
   397:         if (ddpBacktraceOn) {  //バックトレースモードのとき
   398:           int i = (char) ddpBacktraceRecord << BranchLog.BLG_RECORD_SHIFT;
   399:           if (address >>> 1 < BranchLog.blgArray[i] >>> 1) {  //現在選択されているレコードよりも前
   400:             if (oldestRecord < ddpBacktraceRecord) {  //直前にレコードがある
   401:               ddpBacktraceRecord--;  //直前のレコードを選択する
   402:               ddpBacktraceModel.setValue (Long.valueOf (ddpBacktraceRecord));
   403:               address = BranchLog.blgArray[((char) ddpBacktraceRecord << BranchLog.BLG_RECORD_SHIFT) + 1] & ~1;  //直前のレコードの末尾に移動する
   404:             }
   405:           } else if (BranchLog.blgArray[i + 1] >>> 1 < address >>> 1) {  //現在選択されているレコードよりも後
   406:             if (ddpBacktraceRecord < newestRecord) {  //直後にレコードがある
   407:               ddpBacktraceRecord++;  //直後のレコードを選択する
   408:               ddpBacktraceModel.setValue (Long.valueOf (ddpBacktraceRecord));
   409:               address = BranchLog.blgArray[(char) ddpBacktraceRecord << BranchLog.BLG_RECORD_SHIFT] & ~1;  //直後のレコードの先頭に移動する
   410:             }
   411:           }
   412:         }
   413:       }
   414:     }
   415: 
   416:     if (ddpItemCount != 0) {  //構築前または再構築要求のいずれでもない
   417:       int i = Arrays.binarySearch (ddpAddressArray, 1, ddpItemCount, address + 1);  //項目の先頭のときも次の項目を検索してから1つ戻る
   418:       i = (i >> 31 ^ i) - 1;  //目的のアドレスを含む項目の番号
   419:       if (0 < i && i < ddpItemCount - 1 &&  //ページの内側
   420:           ddpAddressArray[i] == address &&  //項目の先頭
   421:           !ddpDCWArray[i]) {  //.dc.wで出力されていない
   422: 
   423:         //再構築しない
   424: 
   425:         ddpItemAddress = address;
   426:         if (ddpItemIndex != i) {  //キャレットがある項目を変更する必要がある
   427:           ddpItemIndex = i;
   428:           ddpTextArea.setCaretPosition (ddpCaretArray[i]);
   429:         }
   430: 
   431:         //!
   432:         //バックトレースモードのとき分岐レコードの範囲が変わったときはハイライト表示を更新する
   433: 
   434:         XEiJ.dbgEventMask--;  //構築終了
   435:         return;
   436:       }
   437:     }
   438: 
   439:     //再構築する
   440:     ddpItemAddress = address;
   441: 
   442:     //構築前または再構築要求または先頭または末尾の番兵が選択された
   443:     //  0x00000000の境界を跨ぐとき反対側を指すことがあるので先頭と末尾の番兵を区別しない
   444:     ddpPageAddress = address & DDP_PAGE_MASK;  //ページの先頭アドレス
   445:     int pageEndAddress = ddpPageAddress + DDP_PAGE_SIZE;  //ページの末尾アドレス。0になることがある
   446: 
   447:     //幅を決める
   448:     int dataBytes = 10;  //1行あたりのデータのバイト数
   449:     int addressWidth;  //アドレスの幅
   450:     int codeWidth;  //コードの幅
   451:     if (ddpProgramMode == 0) {  //アドレスのみ
   452:       //          1111111111222222222233333333334444444444555555555566666666667777777777
   453:       //01234567890123456789012345678901234567890123456789012345678901234567890123456789
   454:       //aaaaaaaa  dddddddddddddddddddd  cccccccccccccccccccccccccccccccccccc..........
   455:       //          +0+1+2+3+4+5+6+7+8+9                                              ▲
   456:       //          +0+1+2+3+4+5+6+7+8+9                                              ▼
   457:       addressWidth = 8;
   458:       codeWidth = 36;
   459:     } else if (ddpProgramMode == 1) {  //オフセットのみ
   460:       //          1111111111222222222233333333334444444444555555555566666666667777777777
   461:       //01234567890123456789012345678901234567890123456789012345678901234567890123456789
   462:       //Loooooo  dddddddddddddddddddd  ccccccccccccccccccccccccccccccccccccc..........
   463:       //         +0+1+2+3+4+5+6+7+8+9                                               ▲
   464:       //         +0+1+2+3+4+5+6+7+8+9                                               ▼
   465:       addressWidth = 7;
   466:       codeWidth = 37;
   467:     } else {  //両方
   468:       //          111111111122222222223333333333444444444455555555556666666666777777777788888888889999999999
   469:       //0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789
   470:       //aaaaaaaa:Loooooo  dddddddddddddddddddd  cccccccccccccccccccccccccccccccccccccccccccccccc..........
   471:       //                  +0+1+2+3+4+5+6+7+8+9                                                           ▲
   472:       //                  +0+1+2+3+4+5+6+7+8+9                                                           ▼
   473:       addressWidth = 16;
   474:       codeWidth = 48;
   475:     }
   476:     String header = (new StringBuilder ().
   477:                      append (XEiJ.DBG_SPACES, 0, addressWidth + 2).
   478:                      append ("+0+1+2+3+4+5+6+7+8+9").
   479:                      append (XEiJ.DBG_SPACES, 0, 2 + codeWidth + dataBytes - 2).
   480:                      append ("▲\n")).toString ();
   481:     String footer = (new StringBuilder ().
   482:                      append (XEiJ.DBG_SPACES, 0, addressWidth + 2).
   483:                      append ("+0+1+2+3+4+5+6+7+8+9").
   484:                      append (XEiJ.DBG_SPACES, 0, 2 + codeWidth + dataBytes - 2).
   485:                      append ("▼")).toString ();
   486: 
   487:     //先頭の番兵
   488:     ddpAddressArray[0] = ddpPageAddress - DDP_ITEM_SIZE;  //昇順を維持するためマスクしない
   489:     ddpSplitArray[0] = 0;
   490:     ddpCaretArray[0] = 0;
   491:     StringBuilder sb = new StringBuilder (header);
   492:     int itemCount = 1;  //項目数
   493:     int itemAddress = ddpPageAddress;  //項目の先頭アドレス
   494:     int dcwAddress = pageEndAddress;  //.dc.wで出力する範囲
   495:     int dcwEndAddress = pageEndAddress;
   496:     boolean prevBranchFlag = false;  //true=直前が完全分岐命令だった
   497: 
   498:     //ラベル
   499:     if (LabeledAddress.lblProgramCount == 0) {
   500:       LabeledAddress.lblUpdateProgram ();
   501:     }
   502:     if (ddpProgramAge != LabeledAddress.lblProgramAge) {  //更新が必要
   503:       ddpProgramAge = LabeledAddress.lblProgramAge;
   504:       ddpProgramBox.removeAllItems ();
   505:       ddpProgramBox.addItem ("raw address");
   506:       for (int i = 0; i < LabeledAddress.lblProgramCount; i++) {
   507:         ddpProgramBox.addItem (XEiJ.fmtHex8 (new StringBuilder (), LabeledAddress.lblProgramHead[i]).append (' ').append (LabeledAddress.lblProgramName[i]).toString ());
   508:       }
   509:     }
   510: 
   511:     TreeMap<Integer,InstructionBreakPoint.InstructionBreakRecord> pointTable;
   512:     if (InstructionBreakPoint.IBP_ON) {
   513:       pointTable = InstructionBreakPoint.ibpPointTable;
   514:     }
   515: 
   516:   itemLoop:
   517:     do {
   518:       int itemEndAddress;  //項目の末尾アドレス
   519:       String code;  //逆アセンブル結果
   520: 
   521:       //逆アセンブルする
   522:       //  以下のアドレスを跨いでしまうときは逆アセンブルせず1ワードずつ.dc.wまたはmoveqで出力する
   523:       //    目的のアドレス
   524:       //    命令ブレークポイント
   525:       //  途中の行をクリックすることで途中から逆アセンブルし直せるようにするため、複数ワードあっても1行にまとめない
   526:       //  途中に逆アセンブルできる命令があっても、跨いではいけないアドレスまですべて1ワードずつ出力する
   527:       if (dcwAddress <= itemAddress && itemAddress < dcwEndAddress) {  //.dc.wで出力中
   528:         Disassembler.disStatus = 0;  //念のため
   529:         int oc = MC68060.mmuPeekWordZeroCode (itemAddress, supervisor);
   530:         if ((oc & 0xfe00) == 0x7000 && MC68060.mmuPeekWordZeroCode (itemAddress + 2, supervisor) == 0x4e4f) {  //moveq.l #$xx,d0;trap#15
   531:           //pcがIOCSコールのtrap#15を指しているときmoveqが.dc.wになってしまうのを避ける
   532:           XEiJ.fmtHex2 (DDP_MOVEQD0_BASE, 10, oc);
   533:           code = String.valueOf (DDP_MOVEQD0_BASE);
   534:         } else {
   535:           XEiJ.fmtHex4 (DDP_DCW_BASE, 9, oc);
   536:           code = String.valueOf (DDP_DCW_BASE);
   537:         }
   538:         itemEndAddress = itemAddress + 2;
   539:         ddpDCWArray[itemCount] = true;
   540:       } else {  //.dc.wで出力中ではない
   541:         LabeledAddress.lblGetIndex (itemAddress);
   542:         code = Disassembler.disDisassemble (new StringBuilder (), itemAddress, supervisor,
   543:                                             LabeledAddress.lblLastGetHead, LabeledAddress.lblLastGetTail, ddpProgramMode).toString ();  //逆アセンブルする
   544:         for (int t = itemAddress + 2; t < Disassembler.disPC; t += 2) {
   545:           if (t == address ||  //目的のアドレスを跨いでしまった
   546:               InstructionBreakPoint.IBP_ON && pointTable.containsKey (t)) {  //命令ブレークポイントを跨いでしまった
   547:             //!
   548:             //バックトレースモードのとき選択されている分岐レコードの先頭も跨がないようにする
   549:             //  IOCSの_B_READと_B_WRITEでmoveq.l #<data>,d0だけ変更してtrap#15に飛び込んでいるところなど
   550:             dcwAddress = itemAddress;  //.dc.wで出力し直す
   551:             dcwEndAddress = t;
   552:             continue itemLoop;
   553:           }
   554:         }
   555:         itemEndAddress = Disassembler.disPC;
   556:         ddpDCWArray[itemCount] = false;
   557:       }
   558: 
   559:       //完全分岐命令の下に隙間を空けて読みやすくする
   560:       if (prevBranchFlag) {
   561:         sb.append ('\n');
   562:       }
   563: 
   564:       //項目の開始
   565:       if (itemAddress == address) {
   566:         ddpItemIndex = itemCount;  //目的のアドレスを含む項目の番号
   567:       }
   568:       ddpAddressArray[itemCount] = itemAddress;  //項目の先頭アドレス
   569:       ddpSplitArray[itemCount] = sb.length ();  //項目を区切る位置
   570: 
   571:       if (prevBranchFlag) {
   572:         //ラベル
   573:         if (true) {
   574:           int i = sb.length ();
   575:           LabeledAddress.lblSearch (sb, itemAddress);
   576:           if (i < sb.length ()) {
   577:             sb.append ('\n');
   578:           }
   579:         }
   580:       }
   581: 
   582:       //停止理由
   583:       if (itemAddress == ddpStoppedAddress && ddpStoppedBy != null) {
   584:         sb.append (ddpStoppedBy).append ('\n');
   585:       }
   586: 
   587:       ddpCaretArray[itemCount] = sb.length ();  //項目が選択されたときキャレットを移動させる位置
   588: 
   589:       //1行目
   590:       int lineAddress = itemAddress;  //行の開始アドレス
   591:       int lineEndAddress = Math.min (lineAddress + dataBytes, itemEndAddress);  //行の終了アドレス
   592:       {
   593:         //アドレス
   594:         //  アドレスのみまたは両方のとき
   595:         //    アドレスを表示する
   596:         if (ddpProgramMode == 0 ||  //アドレスのみまたは
   597:             ddpProgramMode == 2) {  //両方のとき
   598:           XEiJ.fmtHex8 (sb, lineAddress);  //アドレスを表示する
   599:         }
   600:         //オフセット
   601:         //  オフセットのみまたは両方のとき
   602:         //    プログラムがありかつ先頭が0でないとき
   603:         //      両方のとき
   604:         //        ':'を表示する
   605:         //      'L'とオフセットを表示する
   606:         //    プログラムがないまたは先頭が0のとき
   607:         //      オフセットのみのとき
   608:         //        アドレスを表示する
   609:         //      両方のとき
   610:         //        空白を表示する
   611:         if (ddpProgramMode == 1 ||  //オフセットのみまたは
   612:             ddpProgramMode == 2) {  //両方のとき
   613:           int index = LabeledAddress.lblGetIndex (lineAddress);
   614:           if (index != -1 &&  //プログラムがありかつ
   615:               LabeledAddress.lblLastGetHead != 0) {  //先頭が0でないとき
   616:             if (ddpProgramMode == 2) {  //両方のとき
   617:               sb.append (':');  //':'を表示する
   618:             }
   619:             XEiJ.fmtHex6 (sb.append ('L'), lineAddress - LabeledAddress.lblLastGetHead);  //'L'とオフセットを表示する
   620:           } else {  //プログラムがないまたは先頭が0のとき
   621:             if (ddpProgramMode == 1) {  //オフセットのみのとき
   622:               XEiJ.fmtHex8 (sb, lineAddress);  //アドレスを表示する
   623:             } else {  //両方のとき
   624:               //          Lxxxxxx
   625:               sb.append ("       ");  //空白を表示する
   626:             }
   627:           }
   628:         }
   629:         sb.append ("  ");
   630:       }
   631:       //データ
   632:       for (int a = lineAddress; a < lineEndAddress; a += 2) {
   633:         XEiJ.fmtHex4 (sb, MC68060.mmuPeekWordZeroCode (a, supervisor));
   634:       }
   635:       sb.append (XEiJ.DBG_SPACES, 0, 2 * Math.max (0, lineAddress + dataBytes - lineEndAddress) + 2);
   636:       //逆アセンブル結果
   637:       sb.append (code).append (XEiJ.DBG_SPACES, 0, Math.max (1, codeWidth - code.length ()));
   638:       //キャラクタ
   639:       InstructionBreakPoint.InstructionBreakRecord r = InstructionBreakPoint.IBP_ON ? pointTable.get (itemAddress) : null;
   640:       if (r != null) {  //命令ブレークポイントがある
   641:         if (r.ibrWaitInstruction != null) {  //待機ポイント
   642:           sb.append ("----");
   643:         }
   644:         if (r.ibrThreshold < 0) {  //インスタント
   645:           sb.append ("******");
   646:         } else if (r.ibrThreshold != 0x7fffffff) {
   647:           sb.append (r.ibrValue).append ('/').append (r.ibrThreshold);
   648:         }
   649:       } else {  //命令ブレークポイントがない
   650:         for (int a = lineAddress; a < lineEndAddress; a++) {
   651:           int h = MC68060.mmuPeekByteZeroCode (a, supervisor);
   652:           int c;
   653:           if (0x81 <= h && h <= 0x9f || 0xe0 <= h && h <= 0xef) {  //SJISの2バイトコードの1バイト目
   654:             int l = MC68060.mmuPeekByteZeroCode (a + 1, supervisor);  //これは範囲外になる場合がある
   655:             if (0x40 <= l && l != 0x7f && l <= 0xfc) {  //SJISの2バイトコードの2バイト目
   656:               c = CharacterCode.chrSJISToChar[h << 8 | l];  //2バイトで変換する
   657:               if (c == 0) {  //対応する文字がない
   658:                 c = '※';
   659:               }
   660:               a++;
   661:             } else {  //SJISの2バイトコードの2バイト目ではない
   662:               c = '.';  //SJISの2バイトコードの1バイト目ではなかった
   663:             }
   664:           } else {  //SJISの2バイトコードの1バイト目ではない
   665:             c = CharacterCode.chrSJISToChar[h];  //1バイトで変換する
   666:             if (c < 0x20 || c == 0x7f) {  //対応する文字がないまたは制御コード
   667:               c = '.';
   668:             }
   669:           }
   670:           sb.append ((char) c);
   671:         }  //for a
   672:       }
   673:       sb.append ('\n');
   674: 
   675:       //2行目以降
   676:       while (lineEndAddress < itemEndAddress) {
   677:         lineAddress = lineEndAddress;  //行の開始アドレス
   678:         lineEndAddress = Math.min (lineAddress + dataBytes, itemEndAddress);  //行の終了アドレス
   679:         //アドレス
   680:         //  アドレスのみまたは両方のとき
   681:         //    アドレスを表示する
   682:         if (ddpProgramMode == 0 ||  //アドレスのみまたは
   683:             ddpProgramMode == 2) {  //両方のとき
   684:           XEiJ.fmtHex8 (sb, lineAddress);  //アドレスを表示する
   685:         }
   686:         //オフセット
   687:         //  オフセットのみまたは両方のとき
   688:         //    プログラムがありかつ先頭が0でないとき
   689:         //      両方のとき
   690:         //        ':'を表示する
   691:         //      'L'とオフセットを表示する
   692:         //    プログラムがないまたは先頭が0のとき
   693:         //      オフセットのみのとき
   694:         //        アドレスを表示する
   695:         //      両方のとき
   696:         //        空白を表示する
   697:         if (ddpProgramMode == 1 ||  //オフセットのみまたは
   698:             ddpProgramMode == 2) {  //両方のとき
   699:           int index = LabeledAddress.lblGetIndex (lineAddress);
   700:           if (index != -1 &&  //プログラムがありかつ
   701:               LabeledAddress.lblLastGetHead != 0) {  //先頭が0でないとき
   702:             if (ddpProgramMode == 2) {  //両方のとき
   703:               sb.append (':');  //':'を表示する
   704:             }
   705:             XEiJ.fmtHex6 (sb.append ('L'), lineAddress - LabeledAddress.lblLastGetHead);  //'L'とオフセットを表示する
   706:           } else {  //プログラムがないまたは先頭が0のとき
   707:             if (ddpProgramMode == 1) {  //オフセットのみのとき
   708:               XEiJ.fmtHex8 (sb, lineAddress);  //アドレスを表示する
   709:             } else {  //両方のとき
   710:               //          Lxxxxxx
   711:               sb.append ("       ");  //空白を表示する
   712:             }
   713:           }
   714:         }
   715:         sb.append ("  ");
   716:         //データ
   717:         for (int a = lineAddress; a < lineEndAddress; a += 2) {
   718:           XEiJ.fmtHex4 (sb, MC68060.mmuPeekWordZeroCode (a, supervisor));
   719:         }
   720:         sb.append (XEiJ.DBG_SPACES, 0, 2 * Math.max (0, lineAddress + dataBytes - lineEndAddress) + 2 + codeWidth);
   721:         //キャラクタ
   722:         for (int a = lineAddress; a < lineEndAddress; a++) {
   723:           int h = MC68060.mmuPeekByteZeroCode (a, supervisor);
   724:           int c;
   725:           if (0x81 <= h && h <= 0x9f || 0xe0 <= h && h <= 0xef) {  //SJISの2バイトコードの1バイト目
   726:             int l = MC68060.mmuPeekByteZeroCode (a + 1, supervisor);  //これは範囲外になる場合がある
   727:             if (0x40 <= l && l != 0x7f && l <= 0xfc) {  //SJISの2バイトコードの2バイト目
   728:               c = CharacterCode.chrSJISToChar[h << 8 | l];  //2バイトで変換する
   729:               if (c == 0) {  //対応する文字がない
   730:                 c = '※';
   731:               }
   732:               a++;
   733:             } else {  //SJISの2バイトコードの2バイト目ではない
   734:               c = '.';  //SJISの2バイトコードの1バイト目ではなかった
   735:             }
   736:           } else {  //SJISの2バイトコードの1バイト目ではない
   737:             c = CharacterCode.chrSJISToChar[h];  //1バイトで変換する
   738:             if (c < 0x20 || c == 0x7f) {  //対応する文字がないまたは制御コード
   739:               c = '.';
   740:             }
   741:           }
   742:           sb.append ((char) c);
   743:         }  //for a
   744:         sb.append ('\n');
   745:       }
   746: 
   747:       //項目の終了
   748:       itemCount++;
   749:       itemAddress = itemEndAddress;
   750: 
   751:       //完全分岐命令の下に隙間を空けて読みやすくする
   752:       prevBranchFlag = (Disassembler.disStatus & Disassembler.DIS_ALWAYS_BRANCH) != 0;
   753: 
   754:     } while (itemAddress < pageEndAddress);
   755: 
   756:     //末尾の番兵
   757:     ddpAddressArray[itemCount] = itemAddress;  //昇順を維持するためマスクしない
   758:     ddpSplitArray[itemCount] = sb.length ();
   759:     ddpCaretArray[itemCount] = sb.length ();
   760:     sb.append (footer);
   761:     itemCount++;
   762:     ddpItemCount = itemCount;
   763: 
   764:     //テキスト
   765:     ddpTextArea.setText (sb.toString ());
   766:     ddpTextArea.setCaretPosition (ddpCaretArray[ddpItemIndex]);
   767: 
   768:     //!
   769:     //バックトレースモードのとき選択されている分岐レコードの範囲をハイライト表示する
   770: 
   771:     //スピナー
   772:     ddpSpinner.setHintArray (ddpAddressArray, itemCount);
   773:     ddpSpinner.setHintIndex (ddpItemIndex);
   774: 
   775:     XEiJ.dbgEventMask--;  //構築終了
   776: 
   777:   }  //ddpUpdate(int,int,boolean)
   778: 
   779: }  //class DisassembleList
   780: 
   781: 
   782: