DataBreakPoint.java
     1: //========================================================================================
     2: //  DataBreakPoint.java
     3: //    en:Data break point -- It stops the MPU when a data that meets the specified condition is read or written.
     4: //    ja:データブレークポイント -- 指定された条件に合うデータが読み書きされたらMPUを止めます。
     5: //  Copyright (C) 2003-2023 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: //----------------------------------------------------------------------------------------
    14: //  機能
    15: //    指定されたアドレスから指定された条件に合う値が読み出されたら停止する
    16: //    指定されたアドレスに指定された条件に合う値が書き込まれたら停止する
    17: //  情報
    18: //    udm     ユーザデータメモリマップ
    19: //    sdm     スーパーバイザデータメモリマップ
    20: //    mdm     現在のデータメモリマップ
    21: //    データブレークポイント
    22: //      有効フラグ
    23: //      アドレス
    24: //      サイズ
    25: //      マスク
    26: //      下限
    27: //      上限
    28: //      回数
    29: //      閾値
    30: //    最後にヒットしたリードデータブレークポイント
    31: //    最後にヒットしたライトデータブレークポイント
    32: //  条件
    33: //    有効である
    34: //    アドレスが一致している
    35: //    サイズが一致している
    36: //      命令のオペレーションサイズではなくバスアクセスのサイズなので、ワードとロングは分割されている場合がある
    37: //    下限<=上限のとき
    38: //      下限<=マスクされたデータ&&マスクされたデータ<=上限
    39: //    上限<下限のとき
    40: //      マスクされたデータ<=上限||下限<=マスクされたデータ
    41: //    比較は常に符号なしで行われる
    42: //  動作
    43: //    データブレークポイントがあるページから読み出されたとき
    44: //      元のページから読み出す
    45: //        ここでバスエラーが発生して戻ってこなかったときは何もしない
    46: //      リードデータブレークポイントの条件をテストする
    47: //    データブレークポイントがあるページに書き込まれたとき
    48: //      ライトデータブレークポイントの条件をテストする
    49: //      元のページに書き込む
    50: //        ここでバスエラーが発生して戻ってこなかったときは何もしない
    51: //    条件が成立したブレークポイントがあるとき
    52: //      回数を更新する
    53: //      回数が閾値以上のとき
    54: //        コアを停止させる
    55: //        実行中の命令は最後まで実行させなければならない
    56: //        エラーをthrowするのではなくループカウンタをオーバーフローさせた上でタスクを停止させる
    57: //        実行中の命令が終わるまでに複数のデータブレークポイントがヒットする可能性があることに注意する
    58: //  メモリマップ
    59: //    現在はumとsmが大元のメモリマップになっている
    60: //    グラフィックス画面、スーパーバイザ領域設定、RTC、拡張SCSI、CGROMなどはメモリマップを動的に組み替えている
    61: //    メモリマップの組み換えの競合を避けるため、um,smをコピーしてudm,sdmを作る
    62: //    um,smへの変更はデータブレークポイントがないページに限ってudm,sdmにもコピーされる
    63: //    データブレークポイントが設定されるとudm,sdmが変更される
    64: //    データブレークポイントが解除されるとum,smを用いてudm,sdmが復元される
    65: //    mmの代わりにmdmを使う
    66: //      ユーザモードに切り替えるとき
    67: //        mdm = udm;
    68: //      スーパーバイザモードに切り替えるとき
    69: //        mdm = sdm;
    70: //    モード切り替えでmmとmdmの両方を更新するのは負荷が増えるのでmmを廃止する
    71: //      残骸が残っているとデバッグの邪魔になるので一旦削除して完全に移行できたか確認した方がよい
    72: //    MPUのDMAのリードとライトはすべてmdmを経由して行う
    73: //      DMAによるライトも検出できるようにする
    74: //    ピークはリードを経由せずum,smに直接アクセスしなければならない
    75: //      そうしないとメモリダンプでも停止してしまうことになる
    76: //      mmを廃止するのでumとsmのどちらかを使うか毎回選ばなければならない
    77: //  課題
    78: //    MOVEMでメインメモリを特別扱いにする処理が重くなる
    79: //    ディスクアクセスをどうするか
    80: //      セクタ単位のディスクアクセスは途中で止めることができない
    81: //      1つの命令が多数のデータにアクセスしているだけと考えれば問題ない?
    82: //    ページを跨ぐロングワードのデータブレークポイントは設置が困難
    83: //    4の倍数でないアドレスのロングワードのアクセスを常に分割してしまうとロングワードのブレークポイントを設置できる場所が制限される
    84: //    アドレスは一致しているが設定されたサイズと異なるサイズでアクセスされたらどうするか
    85: //      バイトサイズで待っていたらワードサイズでアクセスされたとき
    86: //        データは揃っているのだから上位バイトか下位バイトのどちらかだけテストすればよい
    87: //      ワードサイズで待っていたらバイトサイズでアクセスされたとき
    88: //        ワードサイズのデータの範囲の条件が下限==0x0000&&上限==0xffffまたは下限==上限のとき
    89: //          バイトサイズのデータの範囲の条件も同じなので範囲の条件を満たすかどうか判断できる
    90: //        その他
    91: //          データが不足しているので範囲の条件を満たすかどうか判断できない
    92: //    もっと複雑な条件を与えたい
    93: //      条件を式で与える
    94: //      元の値よりも小さい値が書き込まれたら停止する
    95: //        slt(d,rws(a))
    96: //      アドレスがマッチすることが少なければ条件が複雑でも全体のパフォーマンスにそれほど影響はないはず
    97: //----------------------------------------------------------------------------------------
    98: 
    99: package xeij;
   100: 
   101: 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
   102: 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
   103: import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
   104: import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap
   105: 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
   106: import javax.swing.event.*;  //CaretListener,ChangeEvent,ChangeListener,DocumentEvent,DocumentListener,ListSelectionListener
   107: 
   108: public class DataBreakPoint {
   109: 
   110:   public static final boolean DBP_ON = true;  //true=データブレークポイントの機能を用いる
   111: 
   112:   //サイズ
   113:   public static final int DBP_BYTE  = 0;
   114:   public static final int DBP_WORD  = 1;
   115:   public static final int DBP_LONG  = 2;
   116:   public static final java.util.List<String> DBP_SIZE_LIST_EN = Arrays.asList ("Byte", "Word", "Long");
   117:   public static final java.util.List<String> DBP_SIZE_LIST_JA = Arrays.asList ("バイト", "ワード", "ロング");
   118: 
   119:   //メモリマップ
   120:   public static final MemoryMappedDevice[] dbpUserMap = DBP_ON ? new MemoryMappedDevice[XEiJ.BUS_PAGE_COUNT] : null;  //ユーザデータメモリマップ
   121:   public static final MemoryMappedDevice[] dbpSuperMap = DBP_ON ? new MemoryMappedDevice[XEiJ.BUS_PAGE_COUNT] : null;  //スーパーバイザデータメモリマップ
   122:   public static MemoryMappedDevice[] dbpMemoryMap;  //現在のデータメモリマップ。srS==0?udm:sdm
   123: 
   124:   //リスト
   125:   public static ArrayList<DataBreakRecord> dbpList;  //リスト
   126: 
   127:   //ウインドウ
   128:   public static int dbpLock;  //スピナーのチェンジイベントを抑制するためのフラグ
   129:   public static JFrame dbpFrame;  //フレーム
   130:   public static GridLayout dbpListGridLayout;  //リストパネルのグリッドレイアウト
   131:   public static JPanel dbpListPanel;  //リストパネル
   132: 
   133:   //dbpInit ()
   134:   //  データブレークポイントを初期化する
   135:   public static void dbpInit () {
   136: 
   137:     Arrays.fill (dbpUserMap, MemoryMappedDevice.MMD_NUL);
   138:     Arrays.fill (dbpSuperMap, MemoryMappedDevice.MMD_NUL);
   139:     dbpMemoryMap = dbpSuperMap;
   140: 
   141:     //リスト
   142:     dbpList = new ArrayList<DataBreakRecord> ();
   143: 
   144:   }  //dbpInit()
   145: 
   146:   //dbpStart ()
   147:   public static void dbpStart () {
   148:     if (RestorableFrame.rfmGetOpened (Settings.SGS_DBP_FRAME_KEY)) {
   149:       dbpOpen ();
   150:     }
   151:   }  //dbpStart()
   152: 
   153:   //dbpOpen ()
   154:   //  データブレークポイントウインドウを開く
   155:   public static void dbpOpen () {
   156:     if (dbpFrame == null) {
   157:       dbpMakeFrame ();
   158:     }
   159:     XEiJ.dbgVisibleMask |= XEiJ.DBG_DBP_VISIBLE_MASK;
   160:     dbpFrame.setVisible (true);
   161:   }  //dbpOpen()
   162: 
   163:   //dbpMakeFrame ()
   164:   //  データブレークポイントウインドウを作る
   165:   //  ここでは開かない
   166:   public static void dbpMakeFrame () {
   167:     dbpLock = 0;
   168:     //アクションリスナー
   169:     ActionListener listener = new ActionListener () {
   170:       @Override public void actionPerformed (ActionEvent ae) {
   171:         Object source = ae.getSource ();
   172:         switch (ae.getActionCommand ()) {
   173:         case "Add":  //追加
   174:           new DataBreakRecord (0x00000000, DBP_BYTE, 0xff, 0, 0xff, false, 0, 0);
   175:           break;
   176:         }
   177:       }
   178:     };
   179:     //ウインドウ
   180:     dbpListGridLayout = new GridLayout (1, 9);
   181:     dbpListPanel = ComponentFactory.addComponents (
   182:       new JPanel (dbpListGridLayout),
   183:       Multilingual.mlnText (ComponentFactory.createLabel ("Enabled"), "ja", "有効"),
   184:       Multilingual.mlnText (ComponentFactory.createLabel ("Address"), "ja", "アドレス"),
   185:       Multilingual.mlnText (ComponentFactory.createLabel ("Size"), "ja", "サイズ"),
   186:       Multilingual.mlnText (ComponentFactory.createLabel ("Mask"), "ja", "マスク"),
   187:       Multilingual.mlnText (ComponentFactory.createLabel ("Lower"), "ja", "下限"),
   188:       Multilingual.mlnText (ComponentFactory.createLabel ("Upper"), "ja", "上限"),
   189:       Multilingual.mlnText (ComponentFactory.createLabel ("Count"), "ja", "回数"),
   190:       Multilingual.mlnText (ComponentFactory.createLabel ("Threshold"), "ja", "閾値"),
   191:       Multilingual.mlnText (ComponentFactory.createLabel ("Remove"), "ja", "削除")
   192:       );
   193:     dbpFrame = Multilingual.mlnTitle (
   194:       ComponentFactory.createRestorableSubFrame (
   195:         Settings.SGS_DBP_FRAME_KEY,
   196:         "Data break point",
   197:         null,
   198:         ComponentFactory.setPreferredSize (
   199:           ComponentFactory.createBorderPanel (
   200:             //CENTER
   201:             ComponentFactory.createScrollPane (dbpListPanel),
   202:             //NORTH
   203:             null,
   204:             //WEST
   205:             null,
   206:             //SOUTH
   207:             ComponentFactory.createFlowPanel (
   208:               FlowLayout.CENTER,
   209:               Multilingual.mlnText (ComponentFactory.createButton ("Add", listener), "ja", "追加")
   210:               )
   211:             //EAST
   212:             ),
   213:           750, 100)
   214:         ),
   215:       "ja", "データブレークポイント");
   216:     //  ウインドウリスナー
   217:     ComponentFactory.addListener (
   218:       dbpFrame,
   219:       new WindowAdapter () {
   220:         @Override public void windowClosing (WindowEvent we) {
   221:           XEiJ.dbgVisibleMask &= ~XEiJ.DBG_DBP_VISIBLE_MASK;
   222:         }
   223:       });
   224:   }  //dbpMakeFrame()
   225: 
   226:   //dbpBreak (type, address, data)
   227:   //  条件に合うブレークポイントを探して見つかったら回数を増やす。閾値に達したらコアを停止させる
   228:   //  バイトアクセスしたときバイトサイズのブレークポイントがマッチする
   229:   //  ワードアクセスしたときバイトサイズとワードサイズのブレークポイントがマッチする
   230:   //  ロングワードアクセスしたときバイトサイズとワードサイズとロングワードサイズのブレークポイントがマッチする
   231:   public static void dbpBreak (int size, int address, int data) {
   232:     if (size == DBP_BYTE) {  //バイトアクセス
   233:       for (DataBreakRecord r : dbpList) {
   234:         if (r.dbrEnabled) {  //ブレークポイントが有効
   235:           if (r.dbrSize == DBP_BYTE) {  //ブレークポイントがバイトサイズ
   236:             if (address == r.dbrAddress) {  //バイトアクセスの1バイト目にバイトサイズのブレークポイントがある
   237:               int masked = data & r.dbrMask;  //マスクされたバイトデータの1バイト目
   238:               if (r.dbrLower <= r.dbrUpper ?
   239:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   240:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   241:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   242:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達している
   243:                   XEiJ.mpuStop (null);  //コアを停止させる
   244:                 }
   245:               }  //データ
   246:             }  //アドレス
   247:           }  //ブレークポイントのサイズ
   248:         }  //ブレークポイントが有効
   249:       }  //for r
   250:     } else if (size == DBP_WORD) {  //ワードアクセス
   251:       for (DataBreakRecord r : dbpList) {
   252:         if (r.dbrEnabled) {  //ブレークポイントが有効
   253:           if (r.dbrSize == DBP_BYTE) {  //ブレークポイントがバイトサイズ
   254:             if (address == r.dbrAddress) {  //ワードアクセスの1バイト目にバイトサイズのブレークポイントがある
   255:               int masked = data >>> 8 & r.dbrMask;  //マスクされたワードデータの1バイト目
   256:               if (r.dbrLower <= r.dbrUpper ?
   257:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   258:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   259:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   260:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   261:                   XEiJ.mpuStop (null);  //コアを停止させる
   262:                 }
   263:               }  //条件
   264:             } else if (address + 1 == r.dbrAddress) {  //ワードアクセスの2バイト目にバイトサイズのブレークポイントがある
   265:               int masked = data & r.dbrMask;  //マスクされたワードデータの2バイト目
   266:               if (r.dbrLower <= r.dbrUpper ?
   267:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   268:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   269:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   270:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   271:                   XEiJ.mpuStop (null);  //コアを停止させる
   272:                 }
   273:               }  //条件
   274:             }  //アドレス
   275:           } else if (r.dbrSize == DBP_WORD) {  //ブレークポイントがワードサイズ
   276:             if (address == r.dbrAddress) {  //ワードアクセスの1ワード目にワードサイズのブレークポイントがある
   277:               int masked = data & r.dbrMask;  //マスクされたワードデータの1ワード目
   278:               if (r.dbrLower <= r.dbrUpper ?
   279:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   280:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   281:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   282:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   283:                   XEiJ.mpuStop (null);  //コアを停止させる
   284:                 }
   285:               }  //条件
   286:             }  //アドレス
   287:           }  //ブレークポイントのサイズ
   288:         }  //ブレークポイントが有効
   289:       }  //for r
   290:     } else if (size == DBP_LONG) {  //ロングワードアクセス
   291:       for (DataBreakRecord r : dbpList) {
   292:         if (r.dbrEnabled) {  //ブレークポイントが有効
   293:           if (r.dbrSize == DBP_BYTE) {  //ブレークポイントがバイトサイズ
   294:             if (address == r.dbrAddress) {  //ロングワードアクセスの1バイト目にバイトサイズのブレークポイントがある
   295:               int masked = data >>> 24 & r.dbrMask;  //マスクされたロングワードデータの1バイト目
   296:               if (r.dbrLower <= r.dbrUpper ?
   297:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   298:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   299:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   300:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   301:                   XEiJ.mpuStop (null);  //コアを停止させる
   302:                 }
   303:               }  //条件
   304:             } else if (address + 1 == r.dbrAddress) {  //ロングワードアクセスの2バイト目にバイトサイズのブレークポイントがある
   305:               int masked = data >>> 16 & r.dbrMask;  //マスクされたロングワードデータの2バイト目
   306:               if (r.dbrLower <= r.dbrUpper ?
   307:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   308:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   309:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   310:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   311:                   XEiJ.mpuStop (null);  //コアを停止させる
   312:                 }
   313:               }  //条件
   314:             } else if (address + 2 == r.dbrAddress) {  //ロングワードアクセスの3バイト目にバイトサイズのブレークポイントがある
   315:               int masked = data >>> 8 & r.dbrMask;  //マスクされたロングワードデータの3バイト目
   316:               if (r.dbrLower <= r.dbrUpper ?
   317:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   318:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   319:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   320:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   321:                   XEiJ.mpuStop (null);  //コアを停止させる
   322:                 }
   323:               }  //条件
   324:             } else if (address + 3 == r.dbrAddress) {  //ロングワードアクセスの4バイト目にバイトサイズのブレークポイントがある
   325:               int masked = data & r.dbrMask;  //マスクされたロングワードデータの4バイト目
   326:               if (r.dbrLower <= r.dbrUpper ?
   327:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   328:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   329:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   330:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   331:                   XEiJ.mpuStop (null);  //コアを停止させる
   332:                 }
   333:               }  //条件
   334:             }  //アドレス
   335:           } else if (r.dbrSize == DBP_WORD) {  //ブレークポイントがワードサイズ
   336:             if (address == r.dbrAddress) {  //ロングワードアクセスの1ワード目にワードサイズのブレークポイントがある
   337:               int masked = data >> 16 & r.dbrMask;  //マスクされたロングワードデータの1ワード目
   338:               if (r.dbrLower <= r.dbrUpper ?
   339:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   340:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   341:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   342:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   343:                   XEiJ.mpuStop (null);  //コアを停止させる
   344:                 }
   345:               }  //条件
   346:             } else if (address + 2 == r.dbrAddress) {  //ロングワードアクセスの2ワード目にワードサイズのブレークポイントがある
   347:               int masked = data & r.dbrMask;  //マスクされたロングワードデータの2ワード目
   348:               if (r.dbrLower <= r.dbrUpper ?
   349:                   r.dbrLower <= masked && masked <= r.dbrUpper :
   350:                   masked <= r.dbrUpper || r.dbrLower <= masked) {  //データが範囲内
   351:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   352:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   353:                   XEiJ.mpuStop (null);  //コアを停止させる
   354:                 }
   355:               }  //条件
   356:             }  //アドレス
   357:           } else if (r.dbrSize == DBP_LONG) {  //ブレークポイントがロングワードサイズ
   358:             if (address == r.dbrAddress) {  //ロングワードアクセスの1ロングワード目にロングワードサイズのブレークポイントがある
   359:               int masked = data & r.dbrMask;  //マスクされたロングワードデータの1ロングワード目
   360:               if (Integer.compareUnsigned (r.dbrLower, r.dbrUpper) <= 0 ?
   361:                   Integer.compareUnsigned (r.dbrLower, masked) <= 0 && Integer.compareUnsigned (masked, r.dbrUpper) <= 0 :
   362:                   Integer.compareUnsigned (masked, r.dbrUpper) <= 0 || Integer.compareUnsigned (r.dbrLower, masked) <= 0) {  //データが範囲内
   363:                 r.dbrSetCount (r.dbrCount + 1);  //回数を増やす
   364:                 if (r.dbrThreshold <= r.dbrCount) {  //閾値に達しているとき
   365:                   XEiJ.mpuStop (null);  //コアを停止させる
   366:                 }
   367:               }  //条件
   368:             }  //アドレス
   369:           }  //ブレークポイントのサイズ
   370:         }  //ブレークポイントが有効
   371:       }  //for r
   372:     }  //アクセスサイズ
   373:   }  //dbpBreak(int,int,int)
   374: 
   375: 
   376: 
   377:   public static class DataBreakRecord implements Comparable<DataBreakRecord> {
   378: 
   379:     //設定
   380:     public int dbrAddress;  //アドレス
   381:     public int dbrSize;  //サイズ。DBP_BYTE=バイト,DBP_WORD=ワード,DBP_LONG=ロング
   382:     public int dbrMask;  //マスク。バイトのときは0xff、ワードのときは0xffffを超えないこと
   383:     public int dbrLower;  //下限。符号なし比較。下限の方が大きいときは範囲外を意味する
   384:     public int dbrUpper;  //上限。符号なし比較。lower<=upper?lower<=data&&data<=upper:data<=upper||lower<=data
   385: 
   386:     //状態
   387:     public boolean dbrEnabled;  //true=有効
   388:     public int dbrCount;  //回数
   389:     public int dbrThreshold;  //閾値
   390: 
   391:     //パネル
   392:     public Box dbrEnabledBox;
   393:     public JCheckBox dbrEnabledCheckBox;
   394:     public Hex8Spinner dbrAddressSpinner;
   395:     public JSpinner dbrSizeSpinner;
   396:     public JLabel[] dbrSizeLabels;
   397:     public Hex8Spinner dbrMaskSpinner;
   398:     public Hex8Spinner dbrLowerSpinner;
   399:     public Hex8Spinner dbrUpperSpinner;
   400:     public JSpinner dbrCountSpinner;
   401:     public SpinnerNumberModel dbrCountModel;
   402:     public JSpinner dbrThresholdSpinner;
   403:     public SpinnerNumberModel dbrThresholdModel;
   404:     public Box dbrRemoveBox;
   405:     public JButton dbrRemoveButton;
   406: 
   407:     //コンパレータ
   408:     @Override public int compareTo (DataBreakRecord r) {
   409:       int t = Integer.compareUnsigned (dbrAddress, r.dbrAddress);
   410:       if (t == 0) {
   411:         t = Integer.compareUnsigned (dbrSize, r.dbrSize);
   412:         if (t == 0) {
   413:           t = Integer.compareUnsigned (dbrMask, r.dbrMask);
   414:           if (t == 0) {
   415:             t = Integer.compareUnsigned (dbrLower, r.dbrLower);
   416:             if (t == 0) {
   417:               t = Integer.compareUnsigned (dbrUpper, r.dbrUpper);
   418:             }
   419:           }
   420:         }
   421:       }
   422:       return t;
   423:     }
   424: 
   425:     //new DataBreakRecord (address, size, mask, lower, upper, enabled, count, threshold)
   426:     //  コンストラクタ
   427:     @SuppressWarnings ("this-escape") public DataBreakRecord (int address, int size, int mask, int lower, int upper, boolean enabled, int count, int threshold) {
   428: 
   429:       //サイズによるマスク
   430:       mask &= size == DBP_BYTE ? 0xff : size == DBP_WORD ? 0xffff : 0xffffffff;
   431:       lower &= mask;
   432:       upper &= mask;
   433: 
   434:       //設定
   435:       dbrAddress = address;  //アドレス
   436:       dbrSize = size;  //サイズ
   437:       dbrMask = mask;  //マスク
   438:       dbrLower = lower;  //下限
   439:       dbrUpper = upper;  //上限
   440: 
   441:       //状態
   442:       dbrEnabled = enabled;  //true=有効
   443:       dbrCount = count;  //回数
   444:       dbrThreshold = threshold;  //閾値
   445: 
   446:       //アクションリスナー
   447:       ActionListener listener = new ActionListener () {
   448:         @Override public void actionPerformed (ActionEvent ae) {
   449:           Object source = ae.getSource ();
   450:           switch (ae.getActionCommand ()) {
   451:           case "Enabled":  //有効
   452:             dbrSetEnabled (((JCheckBox) source).isSelected ());
   453:             break;
   454:           case "Remove":  //削除
   455:             dbrRemove ();
   456:             break;
   457:           }
   458:         }
   459:       };
   460: 
   461:       //有効
   462:       dbrEnabledBox = ComponentFactory.createGlueBox (
   463:         dbrEnabledCheckBox = Multilingual.mlnText (ComponentFactory.createCheckBox (dbrEnabled, "Enabled", listener), "ja", "有効")
   464:         );
   465: 
   466:       //アドレス
   467:       dbrAddressSpinner = ComponentFactory.createHex8Spinner (dbrAddress, 0xffffffff, false, new ChangeListener () {
   468:         @Override public void stateChanged (ChangeEvent ce) {
   469:           if (dbpLock == 0) {
   470:             dbrSetAddress (((Hex8Spinner) ce.getSource ()).getIntValue ());  //構築中はdbrAddressSpinnerを参照できないことに注意
   471:           }
   472:         }
   473:       });
   474: 
   475:       //サイズ
   476:       dbrSizeSpinner = Multilingual.mlnList (
   477:         ComponentFactory.createListSpinner (
   478:           DBP_SIZE_LIST_EN,
   479:           DBP_SIZE_LIST_EN.get (dbrSize),
   480:           new ChangeListener () {
   481:             @Override public void stateChanged (ChangeEvent ce) {
   482:               if (dbpLock == 0) {
   483:                 SpinnerListModel model = (SpinnerListModel) ((JSpinner) ce.getSource ()).getModel ();  //構築中はdbrSizeSpinnerを参照できないことに注意
   484:                 dbrSetSize (model.getList ().indexOf (model.getValue ()));
   485:               }
   486:             }
   487:           }),
   488:         "ja", DBP_SIZE_LIST_JA);
   489: 
   490:       //マスク
   491:       dbrMaskSpinner = ComponentFactory.createHex8Spinner (dbrMask, -1, false, new ChangeListener () {
   492:         @Override public void stateChanged (ChangeEvent ce) {
   493:           if (dbpLock == 0) {
   494:             dbrSetMask (((Hex8Spinner) ce.getSource ()).getIntValue ());  //構築中はdbrMaskSpinnerを参照できないことに注意
   495:           }
   496:         }
   497:       });
   498: 
   499:       //下限
   500:       dbrLowerSpinner = ComponentFactory.createHex8Spinner (dbrLower, -1, false, new ChangeListener () {
   501:         @Override public void stateChanged (ChangeEvent ce) {
   502:           if (dbpLock == 0) {
   503:             dbrSetLower (((Hex8Spinner) ce.getSource ()).getIntValue ());  //構築中はdbrLowerSpinnerを参照できないことに注意
   504:           }
   505:         }
   506:       });
   507: 
   508:       //上限
   509:       dbrUpperSpinner = ComponentFactory.createHex8Spinner (dbrUpper, -1, false, new ChangeListener () {
   510:         @Override public void stateChanged (ChangeEvent ce) {
   511:           if (dbpLock == 0) {
   512:             dbrSetUpper (((Hex8Spinner) ce.getSource ()).getIntValue ());  //構築中はdbrUpperSpinnerを参照できないことに注意
   513:           }
   514:         }
   515:       });
   516: 
   517:       //回数
   518:       dbrCountSpinner = ComponentFactory.createNumberSpinner (
   519:         dbrCountModel = new SpinnerNumberModel (dbrCount, 0, 99999999, 1),
   520:         8,
   521:         new ChangeListener () {
   522:           @Override public void stateChanged (ChangeEvent ce) {
   523:             if (dbpLock == 0) {
   524:               dbrSetCount (dbrCountModel.getNumber ().intValue ());
   525:             }
   526:           }
   527:         });
   528: 
   529:       //閾値
   530:       dbrThresholdSpinner = ComponentFactory.createNumberSpinner (
   531:         dbrThresholdModel = new SpinnerNumberModel (dbrThreshold, 0, 99999999, 1),
   532:         8,
   533:         new ChangeListener () {
   534:           @Override public void stateChanged (ChangeEvent ce) {
   535:             if (dbpLock == 0) {
   536:               dbrSetThreshold (dbrThresholdModel.getNumber ().intValue ());
   537:             }
   538:           }
   539:         });
   540: 
   541:       //削除
   542:       dbrRemoveBox = ComponentFactory.createGlueBox (
   543:         dbrRemoveButton = Multilingual.mlnText (ComponentFactory.createButton ("Remove", listener), "ja", "削除")
   544:         );
   545: 
   546:       //データブレークポイントのリストに加える
   547:       dbpList.add (this);  //[this-escape]
   548: 
   549:       //パネルに加える
   550:       dbpListGridLayout.setRows (dbpListGridLayout.getRows () + 1);
   551:       ComponentFactory.addComponents (
   552:         dbpListPanel,
   553:         dbrEnabledBox,  //有効
   554:         dbrAddressSpinner,  //アドレス
   555:         dbrSizeSpinner,  //サイズ
   556:         dbrMaskSpinner,  //マスク
   557:         dbrLowerSpinner,  //下限
   558:         dbrUpperSpinner,  //上限
   559:         dbrCountSpinner,  //回数
   560:         dbrThresholdSpinner,  //閾値
   561:         dbrRemoveBox
   562:         );
   563:       dbpListPanel.validate ();
   564:       dbpListPanel.repaint ();
   565: 
   566:       //このデータブレークポイントが有効かどうかでページのデータブレークポイントを有効にするかどうか決める
   567:       dbrSetEnabled (dbrEnabled);
   568: 
   569:     }  //new DataBreakRecord(int,int,int,int,int,boolean,int,int)
   570: 
   571:     //dbr.dbrRemove ()
   572:     //  データブレークポイントを取り除く
   573:     public void dbrRemove () {
   574: 
   575:       //このデータブレークポイントを無効にして必要ならばページのデータブレークポイントを無効にする
   576:       dbrSetEnabled (false);
   577: 
   578:       //パネルから取り除く
   579:       ComponentFactory.removeComponents (
   580:         dbpListPanel,
   581:         dbrEnabledBox,  //有効
   582:         dbrAddressSpinner,  //アドレス
   583:         dbrSizeSpinner,  //サイズ
   584:         dbrMaskSpinner,  //マスク
   585:         dbrLowerSpinner,  //下限
   586:         dbrUpperSpinner,  //上限
   587:         dbrCountSpinner,  //回数
   588:         dbrThresholdSpinner,  //閾値
   589:         dbrRemoveBox
   590:         );
   591:       dbpListGridLayout.setRows (dbpListGridLayout.getRows () - 1);
   592:       dbpListPanel.validate ();
   593:       dbpListPanel.repaint ();
   594: 
   595:       //リストから取り除く
   596:       dbpList.remove (this);
   597: 
   598:     }  //dbr.dbrRemove()
   599: 
   600:     //dbr.dbrSetAddress (address)
   601:     //  アドレスを設定する
   602:     public void dbrSetAddress (int address) {
   603:       if (dbrAddress != address) {
   604:         dbrAddress = address;
   605:         if (dbrAddressSpinner != null) {
   606:           dbpLock++;
   607:           dbrAddressSpinner.setIntValue (address);
   608:           dbpLock--;
   609:         }
   610:       }
   611:     }  //dbr.dbrSetAddress(int)
   612: 
   613:     //dbr.dbrSetSize (size)
   614:     //  サイズを設定する
   615:     public void dbrSetSize (int size) {
   616:       if (dbrSize != size) {
   617:         dbrSize = size;
   618:         if (dbrSizeSpinner != null) {
   619:           dbpLock++;
   620:           dbrSizeSpinner.setValue (((SpinnerListModel) dbrSizeSpinner.getModel ()).getList ().get (size));
   621:           dbpLock--;
   622:         }
   623:         dbrSetMask (dbrMask & (dbrSize == DBP_BYTE ? 0xff : dbrSize == DBP_WORD ? 0xffff : 0xffffffff));
   624:       }
   625:     }  //dbr.dbrSetSize(int)
   626: 
   627:     //dbr.dbrSetMask (mask)
   628:     //  マスクを設定する
   629:     public void dbrSetMask (int mask) {
   630:       mask &= dbrSize == DBP_BYTE ? 0xff : dbrSize == DBP_WORD ? 0xffff : 0xffffffff;
   631:       if (dbrMask != mask) {
   632:         dbrMask = mask;
   633:         if (dbrMaskSpinner != null) {
   634:           dbpLock++;
   635:           dbrMaskSpinner.setIntValue (mask);
   636:           dbpLock--;
   637:         }
   638:         dbrSetLower (dbrLower);
   639:         dbrSetUpper (dbrUpper);
   640:       }
   641:     }  //dbr.dbrSetMask(int)
   642: 
   643:     //dbr.dbrSetLower (lower)
   644:     //  下限を設定する
   645:     public void dbrSetLower (int lower) {
   646:       lower &= dbrMask;
   647:       if (dbrLower != lower) {
   648:         dbrLower = lower;
   649:         if (dbrLowerSpinner != null) {
   650:           dbpLock++;
   651:           dbrLowerSpinner.setIntValue (lower);
   652:           dbpLock--;
   653:         }
   654:       }
   655:     }  //dbr.dbrSetLower(int)
   656: 
   657:     //dbr.dbrSetUpper (upper)
   658:     //  上限を設定する
   659:     public void dbrSetUpper (int upper) {
   660:       upper &= dbrMask;
   661:       if (dbrUpper != upper) {
   662:         dbrUpper = upper;
   663:         if (dbrUpperSpinner != null) {
   664:           dbpLock++;
   665:           dbrUpperSpinner.setIntValue (upper);
   666:           dbpLock--;
   667:         }
   668:       }
   669:     }  //dbr.dbrSetUpper(int)
   670: 
   671:     //dbr.dbrSetEnabled (enabled)
   672:     //  有効/無効を設定する
   673:     public void dbrSetEnabled (boolean enabled) {
   674:       if (dbrEnabled != enabled) {
   675:         dbrEnabled = enabled;
   676:         dbrEnabledCheckBox.setSelected (enabled);
   677:       }
   678:       int p = dbrAddress >>> XEiJ.BUS_PAGE_BITS;  //ページ番号
   679:       if (dbrEnabled) {  //有効
   680:         //ページのデータブレークポイントを有効にする
   681:         dbpUserMap[p] = dbpSuperMap[p] = MemoryMappedDevice.MMD_DBP;
   682:       } else {  //無効
   683:         //同じページに他にデータブレークポイントがなければページのデータブレークポイントを無効にする
   684:         boolean exists = false;
   685:         for (DataBreakRecord r : dbpList) {
   686:           if (r.dbrAddress >>> XEiJ.BUS_PAGE_BITS == p) {  //同じページに他のデータブレークポイントがある
   687:             exists = true;
   688:             break;
   689:           }
   690:         }
   691:         dbpUserMap[p] = exists ? MemoryMappedDevice.MMD_DBP : XEiJ.busUserMap[p];
   692:         dbpSuperMap[p] = exists ? MemoryMappedDevice.MMD_DBP : XEiJ.busSuperMap[p];
   693:       }
   694:     }  //dbr.dbrSetEnabled(boolean)
   695: 
   696:     //dbr.dbrSetCount (count)
   697:     //  回数を設定する
   698:     public void dbrSetCount (int count) {
   699:       if (dbrCount != count) {
   700:         dbrCount = count;
   701:         if (dbrCountSpinner != null) {
   702:           dbpLock++;
   703:           dbrCountSpinner.setValue (Integer.valueOf (count));
   704:           dbpLock--;
   705:         }
   706:       }
   707:     }  //dbr.dbrSetCount(int)
   708: 
   709:     //dbr.dbrSetThreshold (threshold)
   710:     //  閾値を設定する
   711:     public void dbrSetThreshold (int threshold) {
   712:       if (dbrThreshold != threshold) {
   713:         dbrThreshold = threshold;
   714:         if (dbrThresholdSpinner != null) {
   715:           dbpLock++;
   716:           dbrThresholdSpinner.setValue (Integer.valueOf (threshold));
   717:           dbpLock--;
   718:         }
   719:       }
   720:     }  //dbr.dbrSetThreshold(int)
   721: 
   722:   }  //class DataBreakRecord
   723: 
   724: 
   725: 
   726: }  //class DataBreakPoint
   727: 
   728: 
   729: