SUK.java
     1: //========================================================================================
     2: //  SUK.java
     3: //    en:SCSI Ukun Kai
     4: //    ja:すかじーU君改
     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.event.*;  //ActionEvent
    16: import java.util.*;  //Arrays
    17: import javax.swing.*;  //JMenu
    18: 
    19: import com.fazecast.jSerialComm.*;  //SerialPort
    20: 
    21: public class SUK {
    22: 
    23:   //レジスタ定数
    24:   public static final int SPC_BDID          = 0x01;
    25:   public static final int SPC_SCTL          = 0x03;
    26:   public static final int SPC_SCTL_RD       = 0b10000000;
    27:   public static final int SPC_SCTL_CR       = 0b01000000;
    28:   public static final int SPC_SCTL_DM       = 0b00100000;
    29:   public static final int SPC_SCTL_AE       = 0b00010000;
    30:   public static final int SPC_SCTL_PE       = 0b00001000;
    31:   public static final int SPC_SCTL_SE       = 0b00000100;
    32:   public static final int SPC_SCTL_RE       = 0b00000010;
    33:   public static final int SPC_SCTL_IE       = 0b00000001;
    34:   public static final int SPC_SCMD          = 0x05;
    35:   public static final int SPC_SCMD_CC       = 0b11100000;
    36:   public static final int SPC_SCMD_CC_BR    = 0b00000000;
    37:   public static final int SPC_SCMD_CC_SL    = 0b00100000;
    38:   public static final int SPC_SCMD_CC_RA    = 0b01000000;
    39:   public static final int SPC_SCMD_CC_SA    = 0b01100000;
    40:   public static final int SPC_SCMD_CC_TR    = 0b10000000;
    41:   public static final int SPC_SCMD_CC_TP    = 0b10100000;
    42:   public static final int SPC_SCMD_CC_RR    = 0b11000000;
    43:   public static final int SPC_SCMD_CC_SR    = 0b11100000;
    44:   public static final int SPC_SCMD_RO       = 0b00010000;
    45:   public static final int SPC_SCMD_IT       = 0b00001000;
    46:   public static final int SPC_SCMD_PT       = 0b00000100;
    47:   public static final int SPC_SCMD_TM       = 0b00000001;
    48:   public static final int SPC_INTS          = 0x09;
    49:   public static final int SPC_INTS_SL       = 0b10000000;
    50:   public static final int SPC_INTS_RS       = 0b01000000;
    51:   public static final int SPC_INTS_DC       = 0b00100000;
    52:   public static final int SPC_INTS_CC       = 0b00010000;
    53:   public static final int SPC_INTS_SR       = 0b00001000;
    54:   public static final int SPC_INTS_TO       = 0b00000100;
    55:   public static final int SPC_INTS_HE       = 0b00000010;
    56:   public static final int SPC_INTS_RC       = 0b00000001;
    57:   public static final int SPC_PSNS          = 0x0b;  //read
    58:   public static final int SPC_PSNS_REQ      = 0b10000000;
    59:   public static final int SPC_PSNS_ACK      = 0b01000000;
    60:   public static final int SPC_PSNS_ATN      = 0b00100000;
    61:   public static final int SPC_PSNS_SEL      = 0b00010000;
    62:   public static final int SPC_PSNS_BSY      = 0b00001000;
    63:   public static final int SPC_SDGC          = 0x0b;  //write
    64:   public static final int SPC_SDGC_REQ      = 0b10000000;
    65:   public static final int SPC_SDGC_ACK      = 0b01000000;
    66:   public static final int SPC_SDGC_XFER     = 0b00100000;
    67:   public static final int SPC_SDGC_BSY      = 0b00001000;
    68:   public static final int SPC_SDGC_MSG      = 0b00000100;
    69:   public static final int SPC_SDGC_CD       = 0b00000010;
    70:   public static final int SPC_SDGC_IO       = 0b00000001;
    71:   public static final int SPC_SSTS          = 0x0d;
    72:   public static final int SPC_SSTS_INIT     = 0b10000000;
    73:   public static final int SPC_SSTS_TARG     = 0b01000000;
    74:   public static final int SPC_SSTS_BUSY     = 0b00100000;
    75:   public static final int SPC_SSTS_TRIP     = 0b00010000;
    76:   public static final int SPC_SSTS_RSIN     = 0b00001000;
    77:   public static final int SPC_SSTS_TC0      = 0b00000100;
    78:   public static final int SPC_SSTS_DF       = 0b00000010;
    79:   public static final int SPC_SSTS_DE       = 0b00000001;
    80:   public static final int SPC_SERR          = 0x0f;
    81:   public static final int SPC_SERR_DI       = 0b10000000;
    82:   public static final int SPC_SERR_DO       = 0b01000000;
    83:   public static final int SPC_SERR_XO       = 0b00100000;
    84:   public static final int SPC_SERR_PE       = 0b00001000;
    85:   public static final int SPC_SERR_ST       = 0b00000010;
    86:   public static final int SPC_PCTL          = 0x11;
    87:   public static final int SPC_PCTL_IE       = 0b10000000;
    88:   public static final int SPC_PCTL_SR       = 0b00000001;
    89:   public static final int SPC_PCTL_SR_R     = 0b00000001;
    90:   public static final int SPC_PCTL_SR_S     = 0b00000000;
    91:   public static final int SPC_MBC           = 0x13;
    92:   public static final int SPC_DREG          = 0x15;
    93:   public static final int SPC_TEMP          = 0x17;
    94:   public static final int SPC_TCH           = 0x19;
    95:   public static final int SPC_TCM           = 0x1b;
    96:   public static final int SPC_TCL           = 0x1d;
    97:   public static final int SPC_BYPASS        = 0x1f;
    98:   public static final int SPC_PHASE_MASK    = 0b00000111;
    99:   public static final int SPC_DATAOUT_PHASE = 0b00000000;
   100:   public static final int SPC_DATAIN_PHASE  = 0b00000001;
   101:   public static final int SPC_CMDOUT_PHASE  = 0b00000010;
   102:   public static final int SPC_STSIN_PHASE   = 0b00000011;
   103:   public static final int SPC_MSGOUT_PHASE  = 0b00000110;
   104:   public static final int SPC_MSGIN_PHASE   = 0b00000111;
   105: 
   106:   //レジスタ名
   107:   public static final String[] SPC_REG_NAME = {
   108:     "[0x00]", "BDID", "[0x02]", "SCTL", "[0x04]", "SCMD", "[0x06]", "[0x07]",
   109:     "[0x08]", "INTS", "[0x0a]", "PSNS", "[0x0c]", "SSTS", "[0x0e]", "SERR",
   110:     "[0x10]", "PCTL", "[0x12]", "MBC", "[0x14]", "DREG", "[0x16]", "TEMP",
   111:     "[0x18]", "TCH", "[0x1a]", "TCM", "[0x1c]", "TCL", "[0x1e]", "BYPASS",
   112:   };
   113: 
   114:   //フェーズ名
   115:   public static final String[] SPC_PHASE_NAME = {
   116:     "data-out-phase",  //0
   117:     "data-in-phase",  //1
   118:     "command-out-phase",  //2
   119:     "status-in-phase",  //3
   120:     "???",  //4
   121:     "???",  //5
   122:     "message-out-phase",  //6
   123:     "message-in-phase",  //7
   124:   };
   125: 
   126:   //設定
   127:   public static final boolean SUK_ON = true;  //true=すかじーU君改に対応する
   128:   public static boolean sukOnRequest;  //true=リセット後接続する
   129:   public static boolean sukOn;  //true=接続している
   130:   public static boolean sukExpansionRequest;  //true=リセット後拡張,false=リセット後内蔵
   131:   public static boolean sukExpansion;  //true=拡張,false=内蔵
   132: 
   133:   //ポート
   134:   public static final int SUK_VID = 0x04d8;  //ベンダーID
   135:   public static final int SUK_PID = 0xe6b2;  //プロダクトID
   136:   public static SerialPort sukPort1;  //ポート1
   137:   public static SerialPort sukPort2;  //ポート2
   138: 
   139:   //レジスタ
   140:   public static final byte[] sukRegister = new byte[32];  //最後に読み出されたか書き込まれたレジスタの値
   141:   public static boolean sukReading;  //true=読み出し中
   142: 
   143:   //メニュー
   144:   public static JMenu sukMenu;  //すかじーU君改メニュー
   145:   public static JCheckBoxMenuItem sukConnectCheckBox;  //接続チェックボックス
   146:   public static JCheckBoxMenuItem sukExpansionCheckBox;  //拡張チェックボックス
   147:   public static JCheckBoxMenuItem sukDebugCheckBox;  //デバッグ出力チェックボックス
   148:   public static JCheckBoxMenuItem sukDumpCheckBox;  //ダンプ出力チェックボックス
   149: 
   150:   //割り込み保証
   151:   //  ポートがリードされたときデータの準備ができるまでコアを止めると、割り込みを含めてエミュレータ全体の動きが止まってしまう
   152:   //  待機例外、待機ポイント、待機命令を用いてデータの準備ができるまで割り込みを止めずに待つ
   153:   //
   154:   //  MPUがリードしたポートのデータの準備ができていないときは、待機例外(新設、バスエラーの仲間)をスローして命令の実行を中止する
   155:   //  待機例外をキャッチしたらPCとArを巻き戻して、PCの位置に待機ポイント(新設、命令ブレークポイントの仲間)を設置して続行する
   156:   //  待機ポイントは踏まれると本来の命令コードではなく待機命令(新設、エミュレータ拡張命令)の命令コードを返す
   157:   //  待機命令はBRA.S (*)と同様に割り込みを受け付けながら空ループを繰り返し、データの準備ができたら待機ポイントを撤去する
   158:   //  待機ポイントがなくなるとポートをリードした命令がデータの準備ができている状態で再実行される
   159:   //  複数のデータをリードすることがわかっている場合は一度に一定数のデータが揃うまで待つ
   160:   //  バッファが空になったときはリードした命令の直後に待機ポイントを設置して再び一定数のデータが揃うまで待つ
   161:   //  DMACによるリードはその場で待つことができないので、転送を開始した命令の直後に待機ポイントを設置してMTCで指定された数のデータが揃うまで待つ
   162:   //  オートリクエスト最大速度だが、データが揃ってから転送を開始する
   163: 
   164:   //バイパス
   165:   public static final boolean SUK_BYPASS = true;  //true=バイパスレジスタを使う
   166:   public static final int SUK_BYPASS_AHEAD = 256;  //バイパス受信で先読みする長さ
   167:   //  0x00000000  バイパス受信中ではない
   168:   //  0xffffffff  バイパス受信中。次は長さ下位
   169:   //  0x0000HHLL  バイパス受信中。次はデータ
   170:   public static int sukBypassNotRead;
   171:   //  0x00000000  バイパス送信中ではない
   172:   //  0xffff00LL  バイパス送信中。次は長さ上位
   173:   //  0x0000HHLL  バイパス送信中。次はデータ
   174:   public static int sukBypassNotWritten;
   175: 
   176:   //sukInit ()
   177:   //  初期化
   178:   public static void sukInit () {
   179:     sukOnRequest = Settings.sgsGetOnOff ("suk");
   180:     sukOn = false;
   181:     sukExpansionRequest = Settings.sgsGetOnOff ("sukex");
   182:     sukExpansion = false;
   183:     sukPort1 = null;
   184:     sukPort2 = null;
   185:     //sukRegister = new byte[32];
   186:     if (SUK_DEBUG) {
   187:       sukDebugInit ();
   188:     }
   189:     if (SUK_DUMP) {
   190:       sukDumpInit ();
   191:     }
   192:   }  //sukInit
   193: 
   194:   //sukTini ()
   195:   //  後始末
   196:   public static void sukTini () {
   197:     if (sukOn) {
   198:       sukDisconnect ();
   199:       sukOn = false;
   200:     }
   201:     Settings.sgsPutOnOff ("suk", sukOnRequest);
   202:     Settings.sgsPutOnOff ("sukex", sukExpansionRequest);
   203:     if (SUK_DEBUG) {
   204:       sukDebugTini ();
   205:     }
   206:     if (SUK_DUMP) {
   207:       sukDumpTini ();
   208:     }
   209:   }  //sukTini
   210: 
   211:   //sukGetMenu ()
   212:   //  メニューを返す
   213:   public static JMenu sukGetMenu () {
   214:     if (sukMenu != null) {
   215:       return sukMenu;
   216:     }
   217:     //アクションリスナー
   218:     ActionListener actionListener = new ActionListener () {
   219:       @Override public void actionPerformed (ActionEvent ae) {
   220:         Object source = ae.getSource ();
   221:         String command = ae.getActionCommand ();
   222:         switch (command) {
   223:         case "Connect":  //接続
   224:           sukOnRequest = ((JCheckBoxMenuItem) ae.getSource ()).isSelected ();
   225:           break;
   226:         case "Expansion SCSI port":  //拡張 SCSI ポート
   227:           sukExpansionRequest = ((JCheckBoxMenuItem) ae.getSource ()).isSelected ();
   228:           break;
   229:         case "Debug output":  //デバッグ出力
   230:           sukDebugOn = ((JCheckBoxMenuItem) ae.getSource ()).isSelected ();
   231:           break;
   232:         case "Dump output":  //ダンプ出力
   233:           sukDumpOn = ((JCheckBoxMenuItem) ae.getSource ()).isSelected ();
   234:           break;
   235:         default:
   236:           System.out.println ("unknown action command " + command);
   237:         }
   238:       }  //actionPerformed
   239:     };  //actionListener
   240:     //メニュー
   241:     sukMenu = Multilingual.mlnText (
   242:       ComponentFactory.createMenu (
   243:         "SCSI Ukun Kai (experimental)",
   244:         sukConnectCheckBox = Multilingual.mlnText (
   245:           ComponentFactory.createCheckBoxMenuItem (sukOnRequest, "Connect", actionListener),
   246:           "ja", "接続"),
   247:         sukExpansionCheckBox = Multilingual.mlnText (
   248:           ComponentFactory.createCheckBoxMenuItem (sukExpansionRequest, "Expansion SCSI port", actionListener),
   249:           "ja", "拡張 SCSI ポート"),
   250:         !SUK_DEBUG ? null :
   251:         (sukDebugCheckBox = Multilingual.mlnText (
   252:           ComponentFactory.createCheckBoxMenuItem (sukDebugOn, "Debug output", actionListener),
   253:           "ja", "デバッグ出力")),
   254:         !SUK_DUMP ? null :
   255:         (sukDumpCheckBox = Multilingual.mlnText (
   256:           ComponentFactory.createCheckBoxMenuItem (sukDumpOn, "Dump output", actionListener),
   257:           "ja", "ダンプ出力"))
   258:         ),
   259:       "ja", "すかじー U 君改 (実験中)");
   260:     return sukMenu;
   261:   }  //sukGetMenu
   262: 
   263:   //sukReset ()
   264:   //  リセット
   265:   public static void sukReset () {
   266:     if (sukOnRequest) {
   267:       if (!sukConnect ()) {
   268:         sukOnRequest = false;
   269:         if (sukConnectCheckBox != null) {
   270:           sukConnectCheckBox.setSelected (false);
   271:         }
   272:       }
   273:     } else {
   274:       sukDisconnect ();
   275:     }
   276:     sukOn = sukOnRequest;
   277:     sukExpansion = sukExpansionRequest;
   278:   }  //sukReset
   279: 
   280:   //sukConnect ()
   281:   //  接続する
   282:   public static boolean sukConnect () {
   283:     if (sukPort1 != null) {  //接続している
   284:       return true;
   285:     }
   286:     //すかじーU君改のポートを探す
   287:     SerialPort port1 = null;
   288:     SerialPort port2 = null;
   289:     for (SerialPort port : SerialPort.getCommPorts ()) {  //すべてのシリアル通信ポートについて
   290:       if (SUK_DEBUG && sukDebugOn) {
   291:         System.out.printf ("systemPortName=%s vid=0x%04x pid=0x%04x\n",
   292:                            port.getSystemPortName (),
   293:                            port.getVendorID (),
   294:                            port.getProductID ());
   295:       }
   296:       if (port.getVendorID () == SUK_VID &&  //ベンダーIDが一致
   297:           port.getProductID () == SUK_PID) {  //プロダクトIDが一致
   298:         if (port1 == null) {  //1個目
   299:           port1 = port;  //ポート1(仮)
   300:         } else if (port2 == null) {  //2個目
   301:           port2 = port;  //ポート2(仮)
   302:         } else {  //3個目
   303:           //すかじーU君改が複数接続されている
   304:           //ポート1とポート2の組み合わせが分からないので「先に見つかった方」は採用できない
   305:           System.out.println (Multilingual.mlnJapanese ?
   306:                               "すかじー U 君改のポートが多すぎます" :
   307:                               "Too many MB89352 bridger");
   308:           return false;
   309:         }
   310:       }
   311:     }
   312:     if (port2 == null) {  //見つからない
   313:       System.out.println (Multilingual.mlnJapanese ?
   314:                           "すかじー U 君改のポートが見つかりません" :
   315:                           "MB89352 bridger not found");
   316:       return false;
   317:     }
   318:     //ポートを開く
   319:     if (!port1.openPort (0, 65536, 65536)) {  //ポート1(仮)を開けない
   320:       System.out.println (Multilingual.mlnJapanese ?
   321:                           "すかじー U 君改のポートを開けません" :
   322:                           "Cannot open MB89352 bridger");
   323:       return false;
   324:     }
   325:     if (!port2.openPort (0, 65536, 65536)) {  //ポート2(仮)を開けない
   326:       port1.closePort ();
   327:       System.out.println (Multilingual.mlnJapanese ?
   328:                           "すかじー U 君改のポートを開けません" :
   329:                           "Cannot open MB89352 bridger");
   330:       return false;
   331:     }
   332:     //パラメータを設定する
   333:     //  9600bpsに設定されているので480Mbpsに変更する
   334:     port1.setComPortParameters (480000000, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
   335:     port2.setComPortParameters (480000000, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
   336:     //タイムアウトを設定する
   337:     //  readBytesは50msを上限として少なくとも1バイト受信するまでブロックする
   338:     //  writeBytesは送信バッファが一杯になるまでブロックしない
   339:     port1.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 50, 0);
   340:     port2.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 50, 0);
   341:     //ポート1とポート2を判別する
   342:     //  50ms以内に応答がないと失敗する
   343:     byte[] b = new byte[] { 0x00, 0x00 };
   344:     port1.writeBytes (b, 1, 0);  //ポート1(仮)へ0x00を送信する
   345:     port1.readBytes (b, 1, 0);  //ポート1(仮)から1バイト受信する
   346:     port2.writeBytes (b, 1, 1);  //ポート2(仮)へ0x00を送信する
   347:     port2.readBytes (b, 1, 1);  //ポート2(仮)から1バイト受信する
   348:     if (b[0] == 's' && b[1] == 'S') {  //(仮)は合っている
   349:     } else if (b[0] == 'S' && b[1] == 's') {  //(仮)は逆
   350:       //ポート1とポート2を入れ替える
   351:       SerialPort port = port1;
   352:       port1 = port2;
   353:       port2 = port;
   354:     } else {  //不明
   355:       port1.closePort ();
   356:       port2.closePort ();
   357:       System.out.println (Multilingual.mlnJapanese ?
   358:                           "すかじー U 君改のポートを判別できません" :
   359:                           "Unable to identify MB89352 bridger");
   360:       return false;
   361:     }
   362:     //タイムアウトを再設定する
   363:     //  readBytesは指定された長さを受信するまでブロックする。以降はSerialPortEventのgetReceivedDataを使う
   364:     //  writeBytesは送信バッファが一杯になるまでブロックしない
   365:     port1.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
   366:     port2.setComPortTimeouts (SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
   367:     //初期化する
   368:     //  ポート1へ0x80,0xfeを送信する
   369:     //  0xffのエスケープは用いない
   370:     port1.writeBytes (new byte[] { (byte) 0x80 }, 1);
   371:     port1.writeBytes (new byte[] { (byte) 0xfe }, 1);
   372:     //完了
   373:     sukPort1 = port1;
   374:     sukPort2 = port2;
   375:     Arrays.fill (sukRegister, (byte) 0x00);
   376:     sukReading = false;
   377:     System.out.printf (Multilingual.mlnJapanese ?
   378:                        "すかじー U 君改 (%s,%s) に接続しました\n" :
   379:                        "MB89352 bridger (%s,%s) connected\n",
   380:                        sukPort1.getSystemPortName (),
   381:                        sukPort2.getSystemPortName ());
   382:     //受信キュー
   383:     sukQueueInit ();
   384:     //遅延送信
   385:     sukPoolInit ();
   386:     //連続受信
   387:     sukDregInit ();
   388:     if (SUK_BYPASS) {
   389:       //バイパス
   390:       sukBypassNotRead = 0;
   391:       sukBypassNotWritten = 0;
   392:     }
   393:     //シリアルポートデータリスナーを登録する
   394:     sukPort1.addDataListener (sukDataListener1);
   395:     sukPort2.addDataListener (sukDataListener2);
   396:     return true;
   397:   }  //sukConnect
   398: 
   399:   //sukDisconnect ()
   400:   //  切断する
   401:   public static void sukDisconnect () {
   402:     if (sukPort1 == null) {  //接続していない
   403:       return;
   404:     }
   405:     //シリアルポートデータリスナーを解除する
   406:     sukPort1.removeDataListener ();
   407:     sukPort2.removeDataListener ();
   408:     //ポートを閉じる
   409:     System.out.printf (Multilingual.mlnJapanese ?
   410:                        "すかじー U 君改 (%s,%s) を切り離しました\n" :
   411:                        "MB89352 bridger (%s,%s) disconnected\n",
   412:                        sukPort1.getSystemPortName (),
   413:                        sukPort2.getSystemPortName ());
   414:     sukPort1.clearRTS ();
   415:     sukPort2.clearRTS ();
   416:     sukPort1.clearDTR ();
   417:     sukPort2.clearDTR ();
   418:     sukPort1.closePort ();
   419:     sukPort2.closePort ();
   420:     sukPort1 = null;
   421:     sukPort2 = null;
   422:   }  //sukDisconnect
   423: 
   424:   //sukPeek (a)
   425:   //  ピーク
   426:   public static int sukPeek (int a) {
   427:     a &= 0x1f;
   428:     //最後に読み書きした値をそのまま返す
   429:     return 0xff & sukRegister[a];
   430:   }  //sukPeek
   431: 
   432:   //sukRead (a)
   433:   //  リード
   434:   public static int sukRead (int a) throws M68kException {
   435:     a &= 0x1f;
   436:     if (SUK_BYPASS) {
   437:       if (0 < sukBypassNotRead) {  //バイパス受信中
   438:         if (a != SPC_BYPASS) {  //バイパス受信中にバイパス以外のレジスタから読み出そうとした
   439:           System.out.printf ("%08x sukRead() attempted to read from a register other than bypass during receiving bypass\n", XEiJ.regPC0);
   440:         }
   441:         int d = sukQueueData ();
   442:         if (d < 0) {  //受信キューが空。あってはならない
   443:           System.out.printf ("%08x sukRead() queue is empty\n", XEiJ.regPC0);
   444:           d = 0;
   445:         }
   446:         if (SUK_DEBUG && sukDebugOn) {
   447:           System.out.printf ("%08x sukRead() bypass data=0x%02x\n", XEiJ.regPC0, d);
   448:         }
   449:         sukBypassNotRead--;
   450:         if (sukBypassNotRead == 0) {  //バイパス受信が終了した
   451:           if (SUK_DEBUG && sukDebugOn) {
   452:             System.out.printf ("%08x sukRead() sukBypassNotRead=%d\n", XEiJ.regPC0, sukBypassNotRead);
   453:           }
   454:         } else if ((sukQueueWrite - sukQueueRead) == 0) {  //バイパス受信が終了していなくて受信キューが空
   455:           //直後の命令に待機ポイントを設置してデータを先読みする
   456:           sukQueueRequired = Math.min (SUK_BYPASS_AHEAD, sukBypassNotRead);
   457:           TickerQueue.tkqAdd (sukQueueTicker, XEiJ.mpuClockTime);
   458:         }
   459:         return d;
   460:       }
   461:       if (a == SPC_BYPASS) {
   462:         if (sukBypassNotRead == 0) {  //バイパス受信中ではない
   463:           sukBypassNotRead = -1;  //次は長さ下位
   464:           //リードコマンドを送信する
   465:           sukPoolAdd1 (a);  //リードコマンド
   466:           sukPoolFlush ();
   467:           //リードしようとした命令に待機ポイントを設置する
   468:           //  データは少なくとも1バイト必要
   469:           sukQueueRequired = 3;  //長さ下位,長さ上位,データ1バイト目
   470:           InstructionBreakPoint.ibpAddWaitPoint (XEiJ.regPC0, XEiJ.regSRS, sukQueueInstruction);
   471:           if (SUK_DEBUG && sukDebugOn) {
   472:             System.out.printf ("%08x sukRead() queue instruction at pc=0x%08x required=%d\n", XEiJ.regPC0, XEiJ.regPC0, sukQueueRequired);
   473:           }
   474:           //待機例外をスローしてデータの準備ができてからリードし直す
   475:           M68kException.m6eNumber = M68kException.M6E_WAIT_EXCEPTION;
   476:           throw M68kException.m6eSignal;
   477:         }
   478:         if (sukBypassNotRead < 0) {  //バイパス受信中。次は長さ下位
   479:           int l = sukQueueData ();  //長さ下位
   480:           int h = sukQueueData ();  //長さ上位
   481:           int d = sukQueueData ();  //データ1バイト目
   482:           if (d < 0) {  //受信キューが空。あってはならない
   483:             System.out.printf ("%08x sukRead() queue is empty\n", XEiJ.regPC0);
   484:             l = 1;
   485:             h = 0;
   486:             d = 0;
   487:           }
   488:           sukBypassNotRead = h << 8 | l;
   489:           if (SUK_DEBUG && sukDebugOn) {
   490:             System.out.printf ("%08x sukRead() bypass lower=0x%02x\n", XEiJ.regPC0, l);
   491:             System.out.printf ("%08x sukRead() bypass upper=0x%02x\n", XEiJ.regPC0, h);
   492:             System.out.printf ("%08x sukRead() sukBypassNotRead=0x%02x\n", XEiJ.regPC0, sukBypassNotRead);
   493:             System.out.printf ("%08x sukRead() bypass data=0x%02x\n", XEiJ.regPC0, d);
   494:           }
   495:           sukBypassNotRead--;
   496:           if (sukBypassNotRead == 0) {  //バイパス受信が終了した
   497:             if (SUK_DEBUG && sukDebugOn) {
   498:               System.out.printf ("%08x sukRead() sukBypassNotRead=%d\n", XEiJ.regPC0, sukBypassNotRead);
   499:             }
   500:           } else if ((sukQueueWrite - sukQueueRead) == 0) {  //バイパス受信が終了していなくて受信キューが空
   501:             //直後の命令に待機ポイントを設置してデータを先読みする
   502:             sukQueueRequired = Math.min (SUK_BYPASS_AHEAD, sukBypassNotRead);
   503:             TickerQueue.tkqAdd (sukQueueTicker, XEiJ.mpuClockTime);
   504:           }
   505:           return d;
   506:         }
   507:         //次はデータ
   508:       }
   509:     }
   510:     if ((sukDregLength - sukDregRead) != 0) {  //連続リード中
   511:       if (a == SPC_DREG) {  //DREG
   512:         //バッファからデータを読み出す
   513:         if ((sukDregWrite - sukDregRead) == 0) {  //バッファが空。あってはならない
   514:           System.out.printf ("%08x sukRead() buffer is empty\n", XEiJ.regPC0);
   515:           return 0xff & sukRegister[a];
   516:         }
   517:         int d = 0xff & sukDregBuffer[sukDregRead++];
   518:         if ((sukDregLength - sukDregRead) == 0) {  //連続リードが終了した
   519:           //TCH,TCM,TCLを0にする
   520:           sukRegister[SPC_TCH] = 0x00;
   521:           sukRegister[SPC_TCM] = 0x00;
   522:           sukRegister[SPC_TCL] = 0x00;
   523:           if (SUK_DEBUG && sukDebugOn) {
   524:             sukDebugRegister (true, SPC_TCH);
   525:             sukDebugRegister (true, SPC_TCM);
   526:             sukDebugRegister (true, SPC_TCL);
   527:             System.out.printf ("%08x sukRead() sukDregRead=%d\n", XEiJ.regPC0, sukDregRead);
   528:           }
   529:         }
   530:         sukRegister[a] = (byte) d;
   531:         //次のデータを準備する
   532:         if ((XEiJ.busWaitTime == XEiJ.mpuWaitTime ||
   533:              XEiJ.busWaitTime == XEiJ.mpuNoWaitTime) &&  //MPUによるリードで
   534:             (sukDregLength - sukDregRead) != 0 &&  //連続リードが終了しておらず
   535:             (sukDregWrite - sukDregRead) == 0) {  //バッファから読み出せる長さが0のとき
   536:           //直後の命令に必要な長さのデータが揃うまで待つ待機ポイントを設置する
   537:           //  MPUからリードされた時点でPCが直後の命令を指しているとは限らないのでティッカーを挟んで間接的に待機ポイントを設置する
   538:           sukDregRequired = Math.min (SUK_MPU_AHEAD, sukDregLength - sukDregRead);
   539:           sukDregCounter = 0;
   540:           TickerQueue.tkqAdd (sukDregMPUTicker, XEiJ.mpuClockTime);
   541:         }
   542:         if (SUK_DUMP && sukDumpOn) {
   543:           sukDumpAdd (d);
   544:         }
   545:       } else if (a == SPC_INTS) {  //INTSは常に0x00
   546:         sukRegister[a] = 0x00;
   547:       } else if (a == SPC_SSTS) {  //SSTSのTC0とDEは0、DFは未リードデータ数が8未満のとき0、さもなくば1
   548:         sukRegister[a] = (byte) (SPC_SSTS_INIT | SPC_SSTS_BUSY | SPC_SSTS_TRIP | ((sukDregLength - sukDregRead) < 8 ? 0 : SPC_SSTS_DF));
   549:       } else if (a == SPC_TCH) {  //TCH,TCM,TCLは未リードデータ数
   550:         sukRegister[a] = (byte) ((sukDregLength - sukDregRead) >> 16);
   551:       } else if (a == SPC_TCM) {
   552:         sukRegister[a] = (byte) ((sukDregLength - sukDregRead) >> 8);
   553:       } else if (a == SPC_TCL) {
   554:         sukRegister[a] = (byte) (sukDregLength - sukDregRead);
   555:       }
   556:       if (SUK_DEBUG && sukDebugOn) {
   557:         sukDebugRegister (true, a);
   558:       }
   559:       return 0xff & sukRegister[a];
   560:     }
   561:     //連続リード中ではない
   562:     if (XEiJ.busWaitTime == XEiJ.dmaWaitTime ||
   563:         XEiJ.busWaitTime == XEiJ.dmaNoWaitTime) {  //DMACによるリード。あってはならない
   564:       System.out.printf ("%08x sukRead() DMAC attempted to read while not in data-in-phase\n", XEiJ.regPC0);
   565:       return 0xff & sukRegister[a];
   566:     }
   567:     //MPUによるリード
   568:     if (a == SPC_DREG) {  //DREGのとき
   569:       if (!sukDregStandby) {  //連続リード以外の方法でDREGをリードしようとした。あってはならない
   570:         System.out.printf ("%08x sukRead() MPU attempted to read while not in data-in-phase\n", XEiJ.regPC0);
   571:       }
   572:       //連続受信を開始する
   573:       sukDregStart ();
   574:       //MPUによる連続受信を開始する
   575:       int d = sukDregMPUStart ();  //データがないので待機例外をスローするはず
   576:       sukRegister[a] = (byte) d;
   577:       if (SUK_DEBUG && sukDebugOn) {
   578:         sukDebugRegister (true, a);
   579:       }
   580:       if (SUK_DUMP && sukDumpOn) {
   581:         sukDumpAdd (d);
   582:       }
   583:       return d;
   584:     }
   585:     if (!sukReading) {  //読み出し中ではない
   586:       sukReading = true;  //読み出し開始
   587:       //リードコマンドを送信する
   588:       sukPoolAdd1 (a);  //リードコマンド
   589:       sukPoolFlush ();
   590:       //リードしようとした命令に待機ポイントを設置する
   591:       sukQueueRequired = 1;
   592:       InstructionBreakPoint.ibpAddWaitPoint (XEiJ.regPC0, XEiJ.regSRS, sukQueueInstruction);
   593:       if (false) {
   594:         if (SUK_DEBUG && sukDebugOn) {
   595:           System.out.printf ("%08x sukRead() queue instruction at pc=0x%08x required=%d\n", XEiJ.regPC0, XEiJ.regPC0, sukQueueRequired);
   596:         }
   597:       }
   598:       //待機例外をスローしてデータの準備ができてからリードし直す
   599:       M68kException.m6eNumber = M68kException.M6E_WAIT_EXCEPTION;
   600:       throw M68kException.m6eSignal;
   601:     }
   602:     //読み出し中
   603:     //受信キューからデータを読み出す
   604:     int d = sukQueueData ();
   605:     if (d < 0) {  //受信キューが空。あってはならない
   606:       System.out.printf ("%08x sukRead() queue is empty\n", XEiJ.regPC0);
   607:       return 0xff & sukRegister[a];
   608:     }
   609:     //SSTSが0x00のとき0x05または0x01に読み替える
   610:     //  SSTS(0x00e9602d)はSASI内蔵機とSCSI内蔵機の判別に使われる
   611:     //  DFとDEは同時にセットされないのでSSTSは0xffにならない
   612:     //  BUSYでないときも(TC0と)DEはセットされるのでSSTSは0x00にならない
   613:     //  IPLROM 1.6はSSTSを見て実機の0xffとX68000_MiSTerの0x00をSASI内蔵機、さもなくばSCSI内蔵機とみなす
   614:     //  すかじーU君改はSSTSが0x00を返すのでSASI内蔵機とみなされ、SCSIINROMがあるので構成に誤りがあると判断されて起動できない
   615:     if (a == SPC_SSTS && d == 0x00) {
   616:       d = ((sukRegister[SPC_TCH] | sukRegister[SPC_TCM] | sukRegister[SPC_TCL]) == 0 ? SPC_SSTS_TC0 : 0) | SPC_SSTS_DE;
   617:     }
   618:     sukRegister[a] = (byte) d;
   619:     if (SUK_DEBUG && sukDebugOn) {
   620:       sukDebugRegister (true, a);
   621:     }
   622:     if (SUK_DUMP && sukDumpOn) {
   623:       if (a == SPC_TEMP) {
   624:         int psns = 0xff & sukRegister[SPC_PSNS];
   625:         if (psns == (SPC_PSNS_ACK | SPC_PSNS_BSY | SPC_DATAIN_PHASE) ||
   626:             psns == (SPC_PSNS_ACK | SPC_PSNS_BSY | SPC_STSIN_PHASE) ||
   627:             psns == (SPC_PSNS_ACK | SPC_PSNS_BSY | SPC_MSGIN_PHASE)) {
   628:           sukDumpAdd (0xff & sukRegister[a]);
   629:         }
   630:       }
   631:     }
   632:     sukReading = false;  //読み出し終了
   633:     return d;
   634:   }  //sukRead
   635: 
   636:   //sukWrite (a, d)
   637:   //  ライト
   638:   public static void sukWrite (int a, int d) {
   639:     a &= 0x1f;
   640:     d &= 0xff;
   641:     if (SUK_BYPASS) {
   642:       if (sukBypassNotWritten != 0) {  //バイパス送信中
   643:         if (a != SPC_BYPASS) {  //バイパス送信中にバイパス以外のレジスタに書き込もうとした
   644:           System.out.printf ("%08x sukWrite() attempted to write to a register other than bypass during bypass transmission\n", XEiJ.regPC0);
   645:         }
   646:         sukPoolAdd1 (d);
   647:         if (sukBypassNotWritten < 0) {  //次は長さ上位
   648:           sukBypassNotWritten = d << 8 | (0xff & sukBypassNotWritten);  //長さ
   649:           if (SUK_DEBUG && sukDebugOn) {
   650:             System.out.printf ("%08x sukWrite() bypass upper=0x%02x\n", XEiJ.regPC0, d);
   651:             System.out.printf ("%08x sukWrite() sukBypassNotWritten=%d\n", XEiJ.regPC0, sukBypassNotWritten);
   652:           }
   653:         } else {  //次はデータ
   654:           if (SUK_DEBUG && sukDebugOn) {
   655:             System.out.printf ("%08x sukWrite() bypass data=0x%02x\n", XEiJ.regPC0, d);
   656:           }
   657:           sukBypassNotWritten--;
   658:           if (sukBypassNotWritten == 0) {  //バイパス送信が終了した
   659:             if (SUK_DEBUG && sukDebugOn) {
   660:               System.out.printf ("%08x sukWrite() sukBypassNotWritten=%d\n", XEiJ.regPC0, sukBypassNotWritten);
   661:             }
   662:           }
   663:         }
   664:         return;
   665:       }
   666:     }
   667:     sukRegister[a] = (byte) d;
   668:     if (SUK_DEBUG && sukDebugOn) {
   669:       sukDebugRegister (false, a);
   670:     }
   671:     //ライトコマンドを送信する
   672:     sukPoolAdd2 (0x80 + a, d);  //ライトコマンドとデータ
   673:     if (SUK_BYPASS) {
   674:       if (a == SPC_BYPASS) {  //バイパス送信開始
   675:         sukBypassNotWritten = 0xffff0000 + d;  //長さ下位
   676:         if (SUK_DEBUG && sukDebugOn) {
   677:           System.out.printf ("%08x sukWrite() bypass lower=0x%02x\n", XEiJ.regPC0, d);
   678:         }
   679:       }
   680:     }
   681:     if (SUK_DUMP && sukDumpOn) {
   682:       if (a == SPC_DREG) {
   683:         sukDumpAdd (d);
   684:       } else if (a == SPC_TEMP) {
   685:         int psns = 0xff & sukRegister[SPC_PSNS];
   686:         if (psns == (SPC_PSNS_REQ | SPC_PSNS_BSY | SPC_DATAOUT_PHASE) ||
   687:             psns == (SPC_PSNS_REQ | SPC_PSNS_BSY | SPC_CMDOUT_PHASE) ||
   688:             psns == (SPC_PSNS_REQ | SPC_PSNS_BSY | SPC_MSGOUT_PHASE)) {
   689:           sukDumpAdd (d);
   690:         }
   691:       } else if (a == SPC_PCTL) {
   692:         sukDumpDump (d & SPC_PHASE_MASK);
   693:       }
   694:     }
   695:     //間隔保証
   696:     int us = (a == SPC_SCMD && (d & SPC_SCMD_RO) != 0 ? 250 :  //SCSIバスリセット開始
   697:               a == SPC_SCMD && (d & SPC_SCMD_CC) == SPC_SCMD_CC_SL ? 50 :  //セレクション開始
   698:               a == SPC_INTS && (d & SPC_INTS_TO) != 0 ? 50 :  //セレクションタイムアウトクリア
   699:               0);
   700:     if (us != 0) {  //実時間で間隔を空ける必要があるとき
   701:       sukPoolFlush ();
   702:       //直後の命令に目標のナノ秒時刻になるまで待つ待機ポイントを設置する
   703:       sukIntervalTime = System.nanoTime () + us * 1000;
   704:       sukIntervalCounter = 0;
   705:       InstructionBreakPoint.ibpAddWaitPoint (XEiJ.regPC, XEiJ.regSRS, sukIntervalInstruction);
   706:       if (SUK_DEBUG && sukDebugOn) {
   707:         System.out.printf ("%08x sukWrite() interval instruction at pc=0x%08x time=%d\n", XEiJ.regPC0, XEiJ.regPC, sukIntervalTime);
   708:       }
   709:     }
   710:     //連続受信
   711:     if ((sukRegister[SPC_PCTL] & SPC_PHASE_MASK) == SPC_DATAIN_PHASE &&  //データインフェーズで
   712:         a == SPC_SCMD && (d & SPC_SCMD_CC) == SPC_SCMD_CC_TR) {  //受信を開始したとき
   713:       sukDregStandby = true;  //スタンバイ開始
   714:       if (SUK_DEBUG && sukDebugOn) {
   715:         System.out.printf ("%08x sukWrite() SPC has started receiving\n", XEiJ.regPC0);
   716:       }
   717:     }
   718:   }  //sukWrite
   719: 
   720: 
   721: 
   722:   //データリスナー
   723: 
   724:   //sukDataListener1
   725:   //  ポート1データリスナー
   726:   //  getReceivedData()について
   727:   //    LISTENING_EVENT_DATA_AVAILABLEのときgetReceivedData()はnullを返す
   728:   //    LISTENING_EVENT_DATA_RECEIVEDのときgetReceivedData()は受信バッファを先読みして返す
   729:   //    受信バッファを先読みするだけで取り除かないのでこれだけでは2回目のイベントが発生しない
   730:   public static final SerialPortDataListener sukDataListener1 = new SerialPortDataListener () {
   731:     @Override public int getListeningEvents () {
   732:       return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
   733:     }  //getListeningEvents
   734:     @Override public void serialEvent (SerialPortEvent spe) {
   735:       if (spe.getEventType () != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
   736:         return;
   737:       }
   738:       //データを受信する
   739:       int n = sukPort1.bytesAvailable ();
   740:       byte[] b = new byte[n];
   741:       sukPort1.readBytes (b, n);
   742:       int w = sukQueueWrite;  //受信キューに書き込んだ長さ
   743:       int r = sukQueueRead;  //受信キューから読み出した長さ
   744:       //受信した長さが受信キューに書き込める長さを超えていてはならない
   745:       if ((SUK_QUEUE_SIZE - (w - r)) < n) {
   746:         System.out.printf ("%08x sukDataListener1.serialEvent() queue is full\n", XEiJ.regPC0);
   747:         return;
   748:       }
   749:       //受信したデータを受信キューに移す
   750:       int i = w & (SUK_QUEUE_SIZE - 1);  //受信キューに書き込む位置
   751:       int k = Math.min (n, SUK_QUEUE_SIZE - i);  //1回目に書き込む長さ
   752:       System.arraycopy (b, 0,  //from
   753:                         sukQueueArray, i,  //to
   754:                         k);  //length
   755:       if (k < n) {  //受信キューの末尾と先頭を跨ぐとき
   756:         System.arraycopy (b, k,  //from
   757:                           sukQueueArray, 0,  //to
   758:                           n - k);  //length
   759:       }
   760:       sukQueueWrite = w += n;
   761:     }  //serialEvent
   762:   };  //sukDataListener1
   763: 
   764:   //sukDataListener2
   765:   //  ポート2データリスナー
   766:   public static final SerialPortDataListener sukDataListener2 = new SerialPortDataListener () {
   767:     @Override public int getListeningEvents () {
   768:       return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
   769:     }  //getListeningEvents
   770:     @Override public void serialEvent (SerialPortEvent spe) {
   771:       if (spe.getEventType () != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
   772:         return;
   773:       }
   774:       //データを受信する
   775:       byte[] b = spe.getReceivedData ();
   776:       if (b == null) {
   777:         return;
   778:       }
   779:       int n = b.length;  //受信した長さ
   780:       for (int i = 0; i < n; i++) {
   781:         if (b[i] == 'I') {  //割り込み要求
   782:           if (SUK_DEBUG && sukDebugOn) {
   783:             System.out.printf ("%08x serialEvent() interrupt\n", XEiJ.regPC0);
   784:           }
   785:           if (sukExpansion) {
   786:             XEiJ.eb2Interrupt (XEiJ.EB2_SPC_REQUEST);
   787:           } else {
   788:             IOInterrupt.ioiSpcFall ();
   789:             IOInterrupt.ioiSpcRise ();
   790:           }
   791:         }
   792:       }
   793:     }  //serialEvent
   794:   };  //sukDataListener2
   795: 
   796: 
   797: 
   798:   //受信キュー
   799:   //  ポート1から受信したデータは直ちに受信キューに移される
   800:   //  受信キューは溢れてはならない
   801:   public static final int SUK_QUEUE_SIZE = 1048576;  //キューの長さ。1MB
   802:   public static final byte[] sukQueueArray = new byte[SUK_QUEUE_SIZE];  //キューの配列
   803:   public static volatile int sukQueueWrite;  //書き込んだ長さ
   804:   public static volatile int sukQueueRead;  //読み出した長さ
   805:   public static int sukQueueRequired;  //今回必要な長さ
   806: 
   807:   //sukQueueInit ()
   808:   //  初期化
   809:   public static void sukQueueInit () {
   810:     //sukQueueArray = new byte[SUK_QUEUE_SIZE];
   811:     sukQueueWrite = 0;
   812:     sukQueueRead = 0;
   813:     sukQueueRequired = 0;
   814:   }  //sukQueueInit
   815: 
   816:   //d = sukQueueData ()
   817:   //  受信キューからデータを読み出す
   818:   //  d  読み出したデータ。-1=受信キューが空
   819:   public static int sukQueueData () {
   820:     int w = sukQueueWrite;
   821:     int r = sukQueueRead;
   822:     if ((w - r) == 0) {  //受信キューが空
   823:       return -1;
   824:     }
   825:     int d = 0xff & sukQueueArray[r++ & (SUK_QUEUE_SIZE - 1)];
   826:     sukQueueRead = r;
   827:     return d;
   828:   }  //sukQueueData
   829: 
   830:   //sukQueueTicker
   831:   //  直後の命令に必要な長さのデータが揃うまで待つ待機ポイントを設置するティッカー
   832:   public static final TickerQueue.Ticker sukQueueTicker = new TickerQueue.Ticker () {
   833:     @Override protected void tick () {
   834:       //sukQueueRequiredは設定済み
   835:       //ティッカーが動く前に割り込み要因が生じている可能性があるが割り込み受け付けは後なのでPCは直後の命令を指している
   836:       int pc = XEiJ.regPC;
   837:       InstructionBreakPoint.ibpAddWaitPoint (pc, XEiJ.regSRS, sukQueueInstruction);
   838:       if (SUK_DEBUG && sukDebugOn) {
   839:         int required = sukQueueRequired;
   840:         System.out.printf ("%08x sukQueueTicker.tick() queue instruction at pc=0x%08x required=%d\n", XEiJ.regPC0, pc, required);
   841:       }
   842:     }  //tick
   843:   };  //sukQueueTicker
   844: 
   845:   //sukQueueInstruction
   846:   //  今回必要な長さのデータを受信するまで待つ待機命令
   847:   public static final WaitInstruction sukQueueInstruction = new WaitInstruction () {
   848:     @Override public boolean terminate () {
   849:       return sukQueueRequired <= (sukQueueWrite - sukQueueRead);
   850:     }  //terminate
   851:   };  //sukQueueInstruction
   852: 
   853: 
   854: 
   855:   //連続受信と連続リード
   856:   //  0x41によるDREGの連続受信と連続リード
   857:   //  連続受信と同時に連続リードを開始する
   858:   //  連続受信が終了した後もバッファが空になるまで連続リードは続く
   859:   public static boolean sukDregStandby;  //true=スタンバイ。SPCの転送開始からMPUまたはDMACの受信開始まで
   860:   public static int sukDregLength;  //全体の長さ
   861:   public static int sukDregMajor;  //全体の残りの長さ
   862:   public static int sukDregMinor;  //ブロックの残りの長さ。-1=ブロックの長さを受信中。sukDregMinor!=0のとき連続受信中
   863:   public static byte[] sukDregBuffer;  //バッファ
   864:   public static int sukDregWrite;  //バッファに書き込んだ長さ
   865:   public static int sukDregRead;  //バッファから読み出した長さ。(sukDregLength-sukDregRead)!=0のとき連続リード中
   866:   public static int sukDregRequired;  //今回必要な長さ
   867:   public static int sukDregCounter;  //バッファから読み出せる長さを求める頻度を下げるためのカウンタ
   868: 
   869:   //sukDregInit ()
   870:   //  初期化
   871:   public static void sukDregInit () {
   872:     sukDregStandby = false;
   873:     sukDregLength = 0;
   874:     sukDregMajor = 0;
   875:     sukDregMinor = 0;
   876:     sukDregBuffer = new byte[0];
   877:     sukDregWrite = 0;
   878:     sukDregRead = 0;
   879:     sukDregRequired = 0;
   880:     sukDregCounter = 0;
   881:   }  //sukDregInit
   882: 
   883:   //sukDregStart ()
   884:   //  連続受信を開始する
   885:   //  スタンバイから最初に
   886:   //    MPUがDREGをリードしたとき
   887:   //    または
   888:   //    DAR1がDREGかつOCR1のDIRがDEVICE_TO_MEMORYでCCR1にSTRが書き込まれたとき
   889:   //  開始
   890:   //    スタンバイ終了
   891:   //    全体の長さはTCH<<16|TCM<<8|TCL
   892:   //    全体の残りの長さは全体の長さ、ブロックの残りの長さは0
   893:   //    全体の長さのバッファを確保する
   894:   //    バッファに書き込んだ長さとバッファから読み出した長さを0にする
   895:   //    全体の長さが0でないとき
   896:   //      ブロックの残りの長さを-1にする
   897:   //      0x41を送信する
   898:   public static void sukDregStart () {
   899:     //スタンバイ終了
   900:     sukDregStandby = false;
   901:     //全体の長さはTCH<<16|TCM<<8|TCL
   902:     sukDregLength = ((0xff & sukRegister[SPC_TCH]) << 16 |
   903:                      (0xff & sukRegister[SPC_TCM]) << 8 |
   904:                      (0xff & sukRegister[SPC_TCL]));
   905:     //全体の残りの長さは全体の長さ、ブロックの残りの長さは0
   906:     sukDregMajor = sukDregLength;
   907:     sukDregMinor = 0;
   908:     if (SUK_DEBUG && sukDebugOn) {
   909:       System.out.printf ("%08x sukDregStart() sukDregLength=%d\n", XEiJ.regPC0, sukDregLength);
   910:       System.out.printf ("%08x sukDregStart() sukDregMajor=%d\n", XEiJ.regPC0, sukDregMajor);
   911:       System.out.printf ("%08x sukDregStart() sukDregMinor=%d\n", XEiJ.regPC0, sukDregMinor);
   912:     }
   913:     //全体の長さのバッファを確保する
   914:     if (sukDregBuffer.length < sukDregLength) {  //入り切らないとき
   915:       sukDregBuffer = new byte[sukDregLength];  //新しく作る
   916:     }
   917:     //バッファに書き込んだ長さとバッファから読み出した長さを0にする
   918:     sukDregWrite = 0;
   919:     sukDregRead = 0;
   920:     if (SUK_DEBUG && sukDebugOn) {
   921:       System.out.printf ("%08x sukDregStart() sukDregWrite=%d\n", XEiJ.regPC0, sukDregWrite);
   922:       System.out.printf ("%08x sukDregStart() sukDregRead=%d\n", XEiJ.regPC0, sukDregRead);
   923:     }
   924:     //全体の長さが0でないとき
   925:     if (sukDregLength != 0) {
   926:       //ブロックの残りの長さを-1にする
   927:       sukDregMinor = -1;
   928:       if (SUK_DEBUG && sukDebugOn) {
   929:         System.out.printf ("%08x sukDregStart() sukDregMinor=%d\n", XEiJ.regPC0, sukDregMinor);
   930:       }
   931:       //0x41を送信する
   932:       sukPort1.writeBytes (new byte[] { 0x41 }, 1);
   933:     }
   934:   }  //sukDregStart
   935: 
   936:   //available = sukDregAvailable ()
   937:   //  バッファを更新してバッファから読み出せる長さを求める
   938:   //    ブロックの残りの長さが-1かつ受信キューから読み出せる長さが2以上のとき
   939:   //      受信キューからブロックの長さを読み出す
   940:   //      ブロックの長さが全体の残りの長さを超えていてはならない
   941:   //      全体の残りの長さからブロックの長さを引く
   942:   //    ブロックの残りの長さが1以上かつ受信キューから読み出せる長さが1以上のとき
   943:   //      受信キューから読み出せる長さがブロックの残りの長さを超えていてはならない
   944:   //      受信キューから読み出せる長さのデータを読み出してバッファに移す
   945:   //      移した長さを受信キューから読み出した長さに加える
   946:   //      移した長さをブロックの残りの長さから引く
   947:   //      移した長さをバッファに書き込んだ長さに加える
   948:   //    ブロックの残りの長さが0かつ全体の残りの長さが0でないとき
   949:   //      ブロックの残りの長さを-1にする
   950:   //      0x41を送信する
   951:   //  available  バッファから読み出せる長さ
   952:   public static int sukDregAvailable () {
   953:     int w = sukQueueWrite;  //受信キューに書き込んだ長さ
   954:     int r = sukQueueRead;  //受信キューから読み出した長さ
   955:     //ブロックの残りの長さが-1かつ受信キューから読み出せる長さが2以上のとき
   956:     if (sukDregMinor == -1 && 2 <= (w - r)) {
   957:       //受信キューからブロックの長さを読み出す
   958:       int l = 0xff & sukQueueArray[r++ & (SUK_QUEUE_SIZE - 1)];
   959:       int h = 0xff & sukQueueArray[r++ & (SUK_QUEUE_SIZE - 1)];
   960:       sukQueueRead = r;
   961:       sukDregMinor = h << 8 | l;
   962:       if (SUK_DEBUG && sukDebugOn) {
   963:         System.out.printf ("%08x sukDregAvailable() sukDregMinor=%d\n", XEiJ.regPC0, sukDregMinor);
   964:       }
   965:       //ブロックの長さが全体の残りの長さを超えていてはならない
   966:       if (sukDregMajor < sukDregMinor) {
   967:         System.out.printf ("%08x sukDregAvailable() total overrun\n", XEiJ.regPC0);
   968:         sukDregMinor = sukDregMajor;  //!!!
   969:         if (SUK_DEBUG && sukDebugOn) {
   970:           System.out.printf ("%08x sukDregAvailable() sukDregMinor=%d\n", XEiJ.regPC0, sukDregMinor);
   971:         }
   972:       }
   973:       //全体の残りの長さからブロックの長さを引く
   974:       sukDregMajor -= sukDregMinor;
   975:       if (SUK_DEBUG && sukDebugOn) {
   976:         System.out.printf ("%08x sukDregAvailable() sukDregMajor=%d\n", XEiJ.regPC0, sukDregMajor);
   977:       }
   978:     }
   979:     int n = w - r;  //受信キューから読み出せる長さ
   980:     //ブロックの残りの長さが1以上かつ受信キューから読み出せる長さが1以上のとき
   981:     if (1 <= sukDregMinor && 1 <= n) {
   982:       //受信キューから読み出せる長さがブロックの残りの長さを超えていてはならない
   983:       if (sukDregMinor < n) {
   984:         System.out.printf ("%08x sukDregAvailable() block overrun\n", XEiJ.regPC0);
   985:         n = sukDregMinor;  //!!!
   986:       }
   987:       //受信キューから読み出せる長さのデータを読み出してバッファに移す
   988:       int i = r & (SUK_QUEUE_SIZE - 1);  //受信キューから読み出す位置
   989:       int k = Math.min (n, SUK_QUEUE_SIZE - i);  //1回目に読み出す長さ
   990:       System.arraycopy (sukQueueArray, i,  //from
   991:                         sukDregBuffer, sukDregWrite,  //to
   992:                         k);  //length
   993:       if (k < n) {  //受信キューの末尾と先頭を跨ぐとき
   994:         System.arraycopy (sukQueueArray, 0,  //from
   995:                           sukDregBuffer, sukDregWrite + k,  //to
   996:                           n - k);  //length
   997:       }
   998:       //移した長さを受信キューから読み出した長さに加える
   999:       sukQueueRead = r += n;
  1000:       //移した長さをブロックの残りの長さから引く
  1001:       sukDregMinor -= n;
  1002:       if (SUK_DEBUG && sukDebugOn) {
  1003:         System.out.printf ("%08x sukDregAvailable() sukDregMinor=%d\n", XEiJ.regPC0, sukDregMinor);
  1004:       }
  1005:       //移した長さをバッファに書き込んだ長さに加える
  1006:       sukDregWrite += n;
  1007:       if ((sukDregLength - sukDregWrite) == 0) {  //連続受信が終了した
  1008:         if (SUK_DEBUG && sukDebugOn) {
  1009:           System.out.printf ("%08x sukDregAvailable() sukDregWrite=%d\n", XEiJ.regPC0, sukDregWrite);
  1010:         }
  1011:       }
  1012:     }
  1013:     //ブロックの残りの長さが0かつ全体の残りの長さが0でないとき
  1014:     if (sukDregMinor == 0 && sukDregMajor != 0) {
  1015:       //ブロックの残りの長さを-1にする
  1016:       sukDregMinor = -1;
  1017:       if (SUK_DEBUG && sukDebugOn) {
  1018:         System.out.printf ("%08x sukDregAvailable() sukDregMinor=%d\n", XEiJ.regPC0, sukDregMinor);
  1019:       }
  1020:       //0x41を送信する
  1021:       sukPort1.writeBytes (new byte[] { 0x41 }, 1);
  1022:     }
  1023:     return sukDregWrite - sukDregRead;
  1024:   }  //sukDregAvailable
  1025: 
  1026: 
  1027: 
  1028:   //MPUによる連続受信
  1029:   public static final int SUK_MPU_AHEAD = 256;  //MPUによる連続受信で先読みする長さ
  1030: 
  1031:   //d = sukDregMPUStart ()
  1032:   //  MPUによる連続受信を開始する
  1033:   public static int sukDregMPUStart () throws M68kException {
  1034:     if (SUK_DEBUG && sukDebugOn) {
  1035:       System.out.printf ("%08x sukDregMPUStart() MPU has started receiving\n", XEiJ.regPC0);
  1036:     }
  1037:     int required = Math.min (SUK_MPU_AHEAD, sukDregLength - sukDregRead);
  1038:     if (required <= (sukDregWrite - sukDregRead)) {  //足りている。ないはず
  1039:       int d = 0xff & sukDregBuffer[sukDregRead++];
  1040:       if ((sukDregLength - sukDregRead) == 0) {  //連続リードが終了した
  1041:         if (SUK_DEBUG && sukDebugOn) {
  1042:           System.out.printf ("%08x sukDregMPUStart() sukDregRead=%d\n", XEiJ.regPC0, sukDregRead);
  1043:         }
  1044:       }
  1045:       return d;
  1046:     }
  1047:     //リードしようとした命令に待機ポイントを設置する
  1048:     sukDregRequired = required;
  1049:     sukDregCounter = 0;
  1050:     InstructionBreakPoint.ibpAddWaitPoint (XEiJ.regPC0, XEiJ.regSRS, sukDregMPUInstruction);
  1051:     if (SUK_DEBUG && sukDebugOn) {
  1052:       System.out.printf ("%08x sukDregMPUStart() pc=0x%08x required=%d\n", XEiJ.regPC0, XEiJ.regPC0, required);
  1053:     }
  1054:     //待機例外をスローしてデータの準備ができてからリードし直す
  1055:     M68kException.m6eNumber = M68kException.M6E_WAIT_EXCEPTION;
  1056:     throw M68kException.m6eSignal;
  1057:   }  //sukDregMPUStart
  1058: 
  1059:   //sukDregMPUTicker
  1060:   //  直後の命令に必要な長さのデータが揃うまで待つ待機ポイントを設置するティッカー
  1061:   public static final TickerQueue.Ticker sukDregMPUTicker = new TickerQueue.Ticker () {
  1062:     @Override protected void tick () {
  1063:       //sukDregRequiredは設定済み
  1064:       //ティッカーが動く前に割り込み要因が生じている可能性があるが割り込み受け付けは後なのでPCは直後の命令を指している
  1065:       int pc = XEiJ.regPC;
  1066:       InstructionBreakPoint.ibpAddWaitPoint (pc, XEiJ.regSRS, sukDregMPUInstruction);
  1067:       if (SUK_DEBUG && sukDebugOn) {
  1068:         int required = sukDregRequired;
  1069:         System.out.printf ("%08x sukDregMPUTicker.tick() dreg mpu instruction at pc=0x%08x required=%d\n", XEiJ.regPC0, pc, required);
  1070:       }
  1071:     }  //tick
  1072:   };  //sukDregMPUTicker
  1073: 
  1074:   //sukDregMPUInstruction
  1075:   //  今回必要な長さのデータが揃うまで待つ待機命令
  1076:   public static final WaitInstruction sukDregMPUInstruction = new WaitInstruction () {
  1077:     @Override public boolean terminate () {
  1078:       return ((++sukDregCounter & 15) == 0 &&
  1079:               sukDregRequired <= sukDregAvailable ());
  1080:     }  //terminate
  1081:   };  //sukDregMPUInstruction
  1082: 
  1083: 
  1084: 
  1085:   //DMACによる連続受信
  1086:   public static final int SUK_DREG_CHANNEL = 1;  //使用するDMACのチャンネル
  1087: 
  1088:   //sukDregDMACStart ()
  1089:   //  DMACによる連続受信を開始または継続する
  1090:   public static void sukDregDMACStart () {
  1091:     if (sukDregStandby) {  //スタンバイのとき
  1092:       if (SUK_DEBUG && sukDebugOn) {
  1093:         System.out.printf ("%08x sukDregDMACStart() DMAC has started receiving\n", XEiJ.regPC0);
  1094:       }
  1095:       //連続受信を開始する
  1096:       sukDregStart ();
  1097:     }
  1098:     if ((sukDregLength - sukDregRead) == 0) {  //DMACが連続リード中でないときにリードしようとした。あってはならない
  1099:       System.out.printf ("%08x sukDregDMACStart() DMAC attempted to read while not in data-in-phase\n", XEiJ.regPC0);
  1100:       //!!!
  1101:     }
  1102:     int required = HD63450.dmaMTC[SUK_DREG_CHANNEL];  //今回必要な長さ
  1103:     if (required <= (sukDregWrite - sukDregRead)) {  //足りている
  1104:       HD63450.dmaTransfer (SUK_DREG_CHANNEL);  //最初のデータを転送する
  1105:     } else {  //足りていない
  1106:       //直後の命令に待機ポイントを設置する
  1107:       int pc = XEiJ.regPC;
  1108:       sukDregRequired = required;
  1109:       sukDregCounter = 0;
  1110:       InstructionBreakPoint.ibpAddWaitPoint (pc, XEiJ.regSRS, sukDregDMACInstruction);
  1111:       if (SUK_DEBUG && sukDebugOn) {
  1112:         System.out.printf ("%08x sukDregDMACStart() dreg dmac instruction at pc=0x%08x required=%d\n", XEiJ.regPC0, XEiJ.regPC, required);
  1113:       }
  1114:     }
  1115:   }  //sukDregDMACStart
  1116: 
  1117:   //sukDregDMACInstruction
  1118:   //  今回必要な長さのデータが揃うまで待ってから最初のデータを転送する待機命令
  1119:   public static final WaitInstruction sukDregDMACInstruction = new WaitInstruction () {
  1120:     @Override public boolean terminate () {
  1121:       boolean b = ((++sukDregCounter & 15) == 0 &&
  1122:                    sukDregRequired <= sukDregAvailable ());
  1123:       if (b) {
  1124:         HD63450.dmaTransfer (SUK_DREG_CHANNEL);
  1125:       }
  1126:       return b;
  1127:     }  //terminate
  1128:   };  //sukDregDMACInstruction
  1129: 
  1130: 
  1131: 
  1132:   //遅延送信
  1133:   //  送信データを送信プールに溜めて一定時間送信がないか受信を行うときにまとめて送信する
  1134:   //  送信と受信の間隔が足りなくなる場合は個別に間隔保証を行うこと
  1135:   //  ティッカーはコアと同じスレッドで命令の後に呼び出されるので追加や削除で衝突する心配はない
  1136:   public static final int SUK_POOL_SIZE = 511;  //送信プールのサイズ。511以下
  1137:   public static final long SUK_POOL_SPAN = XEiJ.TMR_FREQ / 1000;  //一定時間。1ms
  1138:   public static final byte[] sukPoolBuffer = new byte[SUK_POOL_SIZE];  //送信プール
  1139:   public static int sukPoolLength;  //送信プールにあるデータの長さ
  1140: 
  1141:   //sukPoolInit ()
  1142:   //  初期化
  1143:   public static void sukPoolInit () {
  1144:     //sukPoolBuffer = new byte[SUK_POOL_SIZE];
  1145:     sukPoolLength = 0;
  1146:   }  //sukPoolInit
  1147: 
  1148:   //sukPoolAdd1 (d)
  1149:   //  送信プールにデータを1個加える
  1150:   public static void sukPoolAdd1 (int d1) {
  1151:     if (SUK_POOL_SIZE - 1 < sukPoolLength) {
  1152:       sukPoolFlush ();
  1153:     }
  1154:     sukPoolBuffer[sukPoolLength++] = (byte) d1;
  1155:     TickerQueue.tkqAdd (sukPoolTicker, XEiJ.mpuClockTime + SUK_POOL_SPAN);
  1156:   }  //sukPoolAdd1
  1157: 
  1158:   //sukPoolAdd2 (d1, d2)
  1159:   //  送信プールにデータを2個加える
  1160:   public static void sukPoolAdd2 (int d1, int d2) {
  1161:     if (SUK_POOL_SIZE - 2 < sukPoolLength) {
  1162:       sukPoolFlush ();
  1163:     }
  1164:     sukPoolBuffer[sukPoolLength++] = (byte) d1;
  1165:     sukPoolBuffer[sukPoolLength++] = (byte) d2;
  1166:     TickerQueue.tkqAdd (sukPoolTicker, XEiJ.mpuClockTime + SUK_POOL_SPAN);
  1167:   }  //sukPoolAdd2
  1168: 
  1169:   //sukPoolTicker
  1170:   //  最後の送信から一定時間後に送信プールから送信するティッカー
  1171:   public static final TickerQueue.Ticker sukPoolTicker = new TickerQueue.Ticker () {
  1172:     @Override protected void tick () {
  1173:       //送信プールから送信する
  1174:       sukPoolFlush ();
  1175:     }  //tick
  1176:   };  //sukPoolTicker
  1177: 
  1178:   //sukPoolFlush ()
  1179:   //  送信プールから送信する
  1180:   public static void sukPoolFlush () {
  1181:     if (0 < sukPoolLength) {
  1182:       sukPort1.writeBytes (sukPoolBuffer, sukPoolLength);
  1183:       sukPoolLength = 0;
  1184:     }
  1185:   }  //sukPoolFlush
  1186: 
  1187: 
  1188: 
  1189:   //間隔保証
  1190:   //  仮想時間で間隔を空ける命令列(Timer-Cを使う方法を含む)は実時間で間隔を空けることができない
  1191:   //  ポートに書き込んでから次に読み書きするまでに実時間で間隔を空ける必要があるとき、最初の書き込みの直後に待機ポイントを設置する
  1192:   //  最初の書き込みだけでは間隔が必要かどうか分からない場合は余分に間隔が空く場合がある
  1193:   //  待機ポイントで間隔を空けるのでその後に実行される命令列と合わせると必要以上に間隔が空く場合がある
  1194:   public static long sukIntervalTime;  //目標のナノ秒時刻
  1195:   public static int sukIntervalCounter;  //ナノ秒時刻を確認する頻度を下げるためのカウンタ
  1196: 
  1197:   //sukIntervalInstruction
  1198:   //  目標のナノ秒時刻になるまで待つ待機命令
  1199:   public static final WaitInstruction sukIntervalInstruction = new WaitInstruction () {
  1200:     @Override public boolean terminate () {
  1201:       return ((++sukIntervalCounter & 15) == 0 &&
  1202:               0 <= (System.nanoTime () - sukIntervalTime));
  1203:     }  //terminate
  1204:   };  //sukIntervalInstruction
  1205: 
  1206: 
  1207: 
  1208:   //デバッグ出力
  1209:   public static final boolean SUK_DEBUG = true;  //true=デバッグ出力あり
  1210:   public static boolean sukDebugOn;  //true=デバッグ出力あり
  1211:   public static final byte[] sukDebugLastRegister = new byte[32];  //最後に表示したレジスタの値
  1212: 
  1213:   //sukDebugInit ()
  1214:   //  初期化
  1215:   public static void sukDebugInit () {
  1216:     sukDebugOn = Settings.sgsGetOnOff ("sukdebug");
  1217:     //sukDebugLastRegister = new byte[32]
  1218:   }  //sukDebugInit
  1219: 
  1220:   //sukDebugTini ()
  1221:   //  後始末
  1222:   public static void sukDebugTini () {
  1223:     Settings.sgsPutOnOff ("sukdebug", sukDebugOn);
  1224:   }  //sukDebugTini
  1225: 
  1226:   //sukDebugRegister (read, a)
  1227:   //  レジスタを表示する
  1228:   public static void sukDebugRegister (boolean read, int a) {
  1229:     if (a != SPC_DREG && a != SPC_BYPASS &&  //DREG/BYPASS以外
  1230:         sukDebugLastRegister[a] != sukRegister[a]) {  //最後に表示した値と異なる
  1231:       System.out.printf ("%08x suk%s() %s=0x%02x\n", XEiJ.regPC0, read ? "Read" : "Write", SPC_REG_NAME[a], 0xff & sukRegister[a]);
  1232:       sukDebugLastRegister[a] = sukRegister[a];
  1233:     }
  1234:   }  //sukDebugRegister
  1235: 
  1236: 
  1237: 
  1238:   //ダンプ出力
  1239:   //  転送データをダンプする
  1240:   public static final boolean SUK_DUMP = true;  //true=ダンプ出力あり
  1241:   public static boolean sukDumpOn;  //true=ダンプ出力あり
  1242:   public static final int SUK_DUMP_LIMIT = 4096;  //ダンプするデータの長さの上限
  1243:   public static final byte[] sukDumpBuffer = new byte[SUK_DUMP_LIMIT];  //ダンプするデータのバッファ
  1244:   public static int sukDumpLength;  //ダンプするデータの長さ
  1245:   public static int sukDataPhase;  //ダンプするデータのフェーズ
  1246: 
  1247:   //sukDumpInit ()
  1248:   //  初期化
  1249:   public static void sukDumpInit () {
  1250:     sukDumpOn = Settings.sgsGetOnOff ("sukdump");
  1251:     //sukDumpBuffer = new byte[4096];
  1252:     sukDumpLength = 0;
  1253:     sukDataPhase = -1;
  1254:   }  //sukDumpInit
  1255: 
  1256:   //sukDumpTini ()
  1257:   //  後始末
  1258:   public static void sukDumpTini () {
  1259:     Settings.sgsPutOnOff ("sukdump", sukDumpOn);
  1260:   }  //sukDumpTini
  1261: 
  1262:   //sukDumpAdd (d)
  1263:   //  データをバッファに追加する
  1264:   public static void sukDumpAdd (int d) {
  1265:     sukDumpDump (sukRegister[SPC_PSNS] & SPC_PHASE_MASK);
  1266:     if (sukDumpLength < SUK_DUMP_LIMIT) {
  1267:       sukDumpBuffer[sukDumpLength++] = (byte) d;
  1268:     }
  1269:   }  //sukDumpAdd
  1270: 
  1271:   //sukDumpDump (phase)
  1272:   //  フェーズが変化したらデータをダンプする
  1273:   public static void sukDumpDump (int phase) {
  1274:     if (sukDataPhase != phase) {
  1275:       if (0 <= sukDataPhase) {
  1276:         System.out.printf ("%08x %s\n", XEiJ.regPC0, SPC_PHASE_NAME[sukDataPhase]);
  1277:         int m = (sukDumpLength + 15) & -16;
  1278:         for (int i = 0; i < m; i += 16) {
  1279:           System.out.printf ("%08x ", i);
  1280:           for (int j = 0; j < 16; j++) {
  1281:             if (i + j < sukDumpLength) {
  1282:               System.out.printf ("%02x ", 0xff & sukDumpBuffer[i + j]);
  1283:             } else {
  1284:               System.out.print ("   ");
  1285:             }
  1286:           }
  1287:           for (int j = 0; j < 16; j++) {
  1288:             if (i + j < sukDumpLength) {
  1289:               if (0x20 <= sukDumpBuffer[i + j] && sukDumpBuffer[i + j] <= 0x7e) {
  1290:                 System.out.printf ("%c", 0xff & sukDumpBuffer[i + j]);
  1291:               } else {
  1292:                 System.out.print (".");
  1293:               }
  1294:             } else {
  1295:               System.out.print (" ");
  1296:             }
  1297:           }
  1298:           System.out.println ();
  1299:         }
  1300:       }
  1301:       if (sukDataPhase == SPC_MSGIN_PHASE && phase == SPC_DATAOUT_PHASE) {
  1302:         sukDataPhase = -1;
  1303:       } else {
  1304:         sukDataPhase = phase;
  1305:       }
  1306:       sukDumpLength = 0;
  1307:     }
  1308:   }  //sukDumpDump
  1309: 
  1310: 
  1311: 
  1312: }  //class SUK