xeij/RasterBreakPoint.java
//========================================================================================
// RasterBreakPoint.java
// en:Raster break point -- It stops the MPU at the horizontal front porch just before the specified break raster or the IRQ raster.
// ja:ラスタブレークポイント -- 指定されたブレークラスタまたはIRQラスタの直前の水平フロントポーチでMPUを止めます。
// Copyright (C) 2003-2025 Makoto Kamada
//
// This file is part of the XEiJ (X68000 Emulator in Java).
// You can use, modify and redistribute the XEiJ if the conditions are met.
// Read the XEiJ License for more details.
// https://stdkmd.net/xeij/
//========================================================================================
package xeij;
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
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
import javax.swing.event.*; //CaretListener,ChangeEvent,ChangeListener,DocumentEvent,DocumentListener,ListSelectionListener
public class RasterBreakPoint {
public static final boolean RBP_ON = true; //true=ラスタブレークポイント機能を有効にする
//ラスタブレーク
public static boolean rbpBreakEnabled; //true=指定されたブレークラスタでラスタブレークをかける
public static int rbpBreakRaster; //ブレークラスタ。0~1023
public static int rbpActiveBreakRaster; //使用中のブレークラスタ。rbpBreakEnabled?rbpBreakRaster:-1
public static boolean rbpIRQBreakEnabled; //true=IRQラスタでラスタブレークをかける
public static int rbpCountValue; //回数
public static int rbpThresholdValue; //閾値
//ウインドウ
public static JFrame rbpFrame;
public static JCheckBox rbpEnabledCheckBox;
public static JCheckBox rbpIRQEnabledCheckBox;
public static JLabel rbpStatusLabel;
public static JTextField rbpCurrentRasterTextField;
public static JTextField rbpIRQRasterTextField;
public static SpinnerNumberModel rbpBreakModel;
public static SpinnerNumberModel rbpCountModel;
public static SpinnerNumberModel rbpThresholdModel;
//タイマー
public static final int RBP_INTERVAL = 10;
public static int rbpTimer;
//rbpInit ()
// 初期化
public static void rbpInit () {
//ラスタブレーク
rbpBreakEnabled = false;
rbpBreakRaster = 0;
rbpActiveBreakRaster = -1;
rbpIRQBreakEnabled = false;
rbpCountValue = 0;
rbpThresholdValue = 0;
//ウインドウ
rbpFrame = null;
//タイマー
rbpTimer = 0;
} //rbpInit()
//rbpStart ()
public static void rbpStart () {
if (RestorableFrame.rfmGetOpened (Settings.SGS_RBP_FRAME_KEY)) {
rbpOpen ();
}
} //rbpStart()
//rbpOpen ()
// ラスタブレークポイントウインドウを開く
public static void rbpOpen () {
if (rbpFrame == null) {
rbpMakeFrame ();
} else {
rbpUpdateFrame ();
}
XEiJ.dbgVisibleMask |= XEiJ.DBG_RBP_VISIBLE_MASK;
XEiJ.pnlExitFullScreen (false);
rbpFrame.setVisible (true);
} //rbpOpen()
//rbpMakeFrame ()
// ラスタブレークポイントウインドウを作る
// ここでは開かない
public static void rbpMakeFrame () {
//アクションリスナー
ActionListener listener = new ActionListener () {
@Override public void actionPerformed (ActionEvent ae) {
Object source = ae.getSource ();
switch (ae.getActionCommand ()) {
case "Fixed raster ": //固定ラスタ
rbpSetBreakEnabled (((JCheckBox) source).isSelected ()); //指定されたブレークラスタでラスタブレークをかけるかどうか
break;
case "IRQ raster ": //IRQ ラスタ
rbpSetIRQEnabled (((JCheckBox) source).isSelected ()); //IRQラスタでラスタブレークをかけるかどうか
break;
case "Run to next raster": //次のラスタまで実行
if (XEiJ.mpuTask == null) {
//一旦無効にする
rbpSetBreakEnabled (false);
//ブレークラスタをインクリメントする
// rbpSetBreakRaster()で指定してからrbpBreakModelを更新するとrbpSetBreakRaster()が2回呼び出されることになるが、
// rbpBreakModel経由だけにするとrbpBreakRasterの更新が遅れる可能性がある
rbpSetBreakRaster (CRTC.crtRasterNumber < CRTC.crtR04VFrontEndCurr ? CRTC.crtRasterNumber + 1 : 0);
rbpBreakModel.setValue (Integer.valueOf (rbpBreakRaster));
//有効にする
rbpSetBreakEnabled (true);
rbpEnabledCheckBox.setSelected (true);
//現在の値を表示する
rbpUpdateFrame ();
//実行する
XEiJ.mpuStart ();
}
break;
case "Run": //実行
if (XEiJ.mpuTask == null) {
//実行する
XEiJ.mpuStart ();
//if (!rbpBreakEnabled) { //無効になっているときは閉じる
// rbpFrame.setVisible (false);
//}
}
break;
}
}
};
//ウインドウ
rbpFrame = Multilingual.mlnTitle (
ComponentFactory.createRestorableSubFrame (
Settings.SGS_RBP_FRAME_KEY,
"Raster break point",
null,
ComponentFactory.createVerticalBox (
Box.createVerticalStrut (4),
ComponentFactory.createHorizontalBox (
Box.createHorizontalStrut (12),
Box.createHorizontalGlue (),
rbpStatusLabel = ComponentFactory.createLabel (rbpMakeStatusText ()),
Box.createHorizontalGlue (),
Box.createHorizontalStrut (12)
),
Box.createVerticalStrut (4),
ComponentFactory.createHorizontalBox (
Box.createHorizontalStrut (12),
Box.createHorizontalGlue (),
Multilingual.mlnText (ComponentFactory.createLabel ("Current raster "), "ja", "現在のラスタ "),
rbpCurrentRasterTextField = ComponentFactory.setEditable (
ComponentFactory.createNumberField (String.valueOf (CRTC.crtRasterNumber), 5),
false),
Box.createHorizontalStrut (12),
Box.createHorizontalGlue (),
rbpEnabledCheckBox = Multilingual.mlnText (ComponentFactory.createCheckBox (rbpBreakEnabled, "Fixed raster ", listener), "ja", "固定ラスタ "),
ComponentFactory.createNumberSpinner (rbpBreakModel = new SpinnerNumberModel (rbpBreakRaster, 0, 1023, 1), 5, new ChangeListener () {
@Override public void stateChanged (ChangeEvent ce) {
rbpSetBreakRaster (rbpBreakModel.getNumber ().intValue ()); //ブレークラスタ
}
}),
Box.createHorizontalStrut (12),
Box.createHorizontalGlue (),
rbpIRQEnabledCheckBox = Multilingual.mlnText (ComponentFactory.createCheckBox (rbpIRQBreakEnabled, "IRQ raster ", listener), "ja", "IRQ ラスタ "),
rbpIRQRasterTextField = ComponentFactory.setEditable (
ComponentFactory.createNumberField (String.valueOf (CRTC.crtR09IRQRasterCurr), 5),
false),
Box.createHorizontalGlue (),
Box.createHorizontalStrut (12)
),
Box.createVerticalStrut (4),
ComponentFactory.createHorizontalBox (
Box.createHorizontalStrut (12),
Box.createHorizontalGlue (),
Multilingual.mlnText (ComponentFactory.createLabel ("Count "), "ja", "回数 "),
ComponentFactory.createNumberSpinner (rbpCountModel = new SpinnerNumberModel (rbpCountValue, 0, 99999999, 1), 8, new ChangeListener () {
@Override public void stateChanged (ChangeEvent ce) {
rbpCountValue = rbpCountModel.getNumber ().intValue ();
}
}),
Box.createHorizontalStrut (12),
Multilingual.mlnText (ComponentFactory.createLabel ("Threshold "), "ja", "閾値 "),
ComponentFactory.createNumberSpinner (rbpThresholdModel = new SpinnerNumberModel (rbpThresholdValue, 0, 99999999, 1), 8, new ChangeListener () {
@Override public void stateChanged (ChangeEvent ce) {
rbpThresholdValue = rbpThresholdModel.getNumber ().intValue ();
}
}),
Box.createHorizontalStrut (12),
Box.createHorizontalGlue (),
XEiJ.mpuAddButtonStopped (Multilingual.mlnText (ComponentFactory.createButton ("Run to next raster", listener), "ja", "次のラスタまで実行")),
Box.createHorizontalStrut (12),
XEiJ.mpuAddButtonStopped (Multilingual.mlnText (ComponentFactory.createButton ("Run", listener), "ja", "実行")),
Box.createHorizontalGlue (),
Box.createHorizontalStrut (12)
),
Box.createVerticalStrut (4)
)
),
"ja", "ラスタブレークポイント");
// ウインドウリスナー
ComponentFactory.addListener (
rbpFrame,
new WindowAdapter () {
@Override public void windowClosing (WindowEvent we) {
XEiJ.dbgVisibleMask &= ~XEiJ.DBG_RBP_VISIBLE_MASK;
}
});
} //rbpMakeFrame()
//rbpMakeStatusText ()
public static String rbpMakeStatusText () {
StringBuilder sb = new StringBuilder ();
return (Multilingual.mlnJapanese ? sb.
append ("帰線期間 0-").append (CRTC.crtR05VSyncEndCurr).
append (" バックポーチ ").append (CRTC.crtR05VSyncEndCurr + 1).append ("-").append (CRTC.crtR06VBackEndCurr).
append (" 映像期間 ").append (CRTC.crtR06VBackEndCurr + 1).append ("-").append (CRTC.crtR07VDispEndCurr).
append (" フロントポーチ ").append (CRTC.crtR07VDispEndCurr + 1).append ("-").append (CRTC.crtR04VFrontEndCurr)
: sb.
append ("Blanking period 0-").append (CRTC.crtR05VSyncEndCurr).
append (" Back porch ").append (CRTC.crtR05VSyncEndCurr + 1).append ("-").append (CRTC.crtR06VBackEndCurr).
append (" Video period ").append (CRTC.crtR06VBackEndCurr + 1).append ("-").append (CRTC.crtR07VDispEndCurr).
append (" Front porch ").append (CRTC.crtR07VDispEndCurr + 1).append ("-").append (CRTC.crtR04VFrontEndCurr)
).toString ();
} //rbpMakeStatusText ()
//rbpUpdateFrame ()
// ラスタブレークポイントウインドウを更新する
// ウインドウが構築済みであることを確認してから呼び出すこと
public static void rbpUpdateFrame () {
rbpStatusLabel.setText (rbpMakeStatusText ());
rbpCurrentRasterTextField.setText (String.valueOf (CRTC.crtRasterNumber));
rbpIRQRasterTextField.setText (String.valueOf (CRTC.crtR09IRQRasterCurr));
rbpTimer = RBP_INTERVAL;
} //rbpUpdateFrame()
//rbpSetBreakEnabled (enabled)
// 指定されたブレークラスタでラスタブレークをかけるかどうかを設定する
public static void rbpSetBreakEnabled (boolean enabled) {
rbpBreakEnabled = enabled;
rbpActiveBreakRaster = rbpBreakEnabled ? rbpBreakRaster : -1;
if (CRTC.CRT_RASTER_HASH_ON) {
CRTC.crtUpdateRasterHash ();
}
} //rbpSetBreakEnabled(boolean)
//rbpSetBreakRaster (breakRaster)
// ラスタブレークをかけるラスタ番号を設定する
public static void rbpSetBreakRaster (int raster) {
rbpBreakRaster = raster;
rbpActiveBreakRaster = rbpBreakEnabled ? rbpBreakRaster : -1;
if (CRTC.CRT_RASTER_HASH_ON) {
CRTC.crtUpdateRasterHash ();
}
} //rbpSetBreakRaster(int)
//rbpSetIRQEnabled (atInterrupt)
// IRQラスタでラスタブレークをかけるかどうかを設定する
public static void rbpSetIRQEnabled (boolean enabled) {
rbpIRQBreakEnabled = enabled;
} //rbpSetIRQEnabled(boolean)
//rbpCheckIRQ ()
// IRQを確認する
public static void rbpCheckIRQ () {
int irq = CRTC.crtRasterNumber == CRTC.crtR09IRQRasterCurr ? 0 : MC68901.MFP_GPIP_RINT_MASK; //IRQ信号を更新
if (irq != MC68901.mfpGpipRint) { //IRQ信号が変化したとき
if (irq == 0) { //IRQ信号が0になったとき
if (RBP_ON) {
if (rbpIRQBreakEnabled) { //IRQラスタでラスタブレークをかけるとき
rbpFire (); //ラスタブレークをかける
}
}
MC68901.mfpRintFall (); //IRQ開始
} else { //IRQ信号が0でなくなったとき
MC68901.mfpRintRise (); //IRQ終了
}
}
} //rbpCheckIRQ()
//rbpFire ()
// ラスタブレークをかける
// ブレークラスタの水平フロントポーチで呼び出す
public static void rbpFire () {
if (XEiJ.mpuTask == null) { //既に停止している(ブレークラスタとIRQラスタが同じとき2回呼び出されることがある)
return;
}
//回数を数える
rbpCountValue++;
//ウインドウが表示されていたら更新する
if ((XEiJ.dbgVisibleMask & XEiJ.DBG_RBP_VISIBLE_MASK) != 0) {
rbpUpdateFrame ();
rbpCountModel.setValue (Integer.valueOf (rbpCountValue));
}
//回数が閾値に達していたらラスタブレークをかける
if (rbpThresholdValue <= rbpCountValue) {
if (!XEiJ.PNL_USE_THREAD) {
//スクリーンを更新する
if (CRTC.crtDirtyY0 >= 0) { //更新されたラスタがある
int dirtyY0 = CRTC.crtDirtyY0;
CRTC.crtUpdateScreen (); //スクリーンを更新する
CRTC.crtDirtyY0 = dirtyY0; //CRTC.crtDirtyY0を更新しない
}
}
//逆アセンブルリストウインドウが開いていなかったら開く
if ((XEiJ.dbgVisibleMask & XEiJ.DBG_DDP_VISIBLE_MASK) == 0) {
DisassembleList.ddpOpen (-1, -1, false);
}
//ラスタブレークをかける
XEiJ.mpuStop (null);
}
} //rbpFire()
public static void rbpTrace () {
System.out.println (new StringBuilder ().
append ("backEnd=").append (CRTC.crtR06VBackEndCurr).append (",").
append ("dispEnd=").append (CRTC.crtR07VDispEndCurr).append (",").
append ("frontEnd=").append (CRTC.crtR04VFrontEndCurr).append (",").
append ("IRQ=").append (CRTC.crtR09IRQRasterCurr).append (",").
append ("break=").append (rbpBreakRaster).append (",").
append ("raster=").append (CRTC.crtRasterNumber).toString ());
} //rbpTrace()
} //class RasterBreakPoint