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 boolean SUK_ON = false;  //true=すかじーU君改に対応する
    25:   public static final boolean SUK_DEBUG = true;  //true=デバッグ表示あり
    26:   public static boolean sukOnRequest;  //true=リセット後すかじーU君改を使う
    27:   public static boolean sukOn;  //true=すかじーU君改を使う
    28:   public static boolean sukExpansionRequest;  //true=リセット後拡張,false=リセット後内蔵
    29:   public static boolean sukExpansion;  //true=拡張,false=内蔵
    30: 
    31:   //ポート
    32:   public static final int SUK_VID = 0x04d8;  //ベンダーID
    33:   public static final int SUK_PID = 0xe6b2;  //プロダクトID
    34:   public static final int SUK_TRANSMISSION_QUEUE_SIZE = 65535;  //送信バッファサイズ
    35:   public static final int SUK_RECEIVE_QUEUE_SIZE = 65535;  //受信バッファサイズ
    36:   public static SerialPort sukPort1;  //ポート1
    37:   public static SerialPort sukPort2;  //ポート2
    38:   public static final byte[] sukCoreTemporary = new byte[65536];  //テンポラリバッファ(コアスレッド)
    39:   public static final byte[] sukReceivingTemporary = new byte[65536];  //テンポラリバッファ(受信スレッド)
    40: 
    41:   //メニュー
    42:   public static JMenu sukMenu;  //すかじーU君改メニュー
    43:   public static JCheckBoxMenuItem sukOnCheckBox;  //接続チェックボックス
    44:   public static JCheckBoxMenuItem sukExpansionCheckBox;  //拡張チェックボックス
    45: 
    46:   //レジスタ
    47:   public static final int SPC_BDID          = 0x01;
    48:   public static final int SPC_SCTL          = 0x03;
    49:   public static final int SPC_SCTL_RD       = 0b10000000;
    50:   public static final int SPC_SCTL_CR       = 0b01000000;
    51:   public static final int SPC_SCTL_DM       = 0b00100000;
    52:   public static final int SPC_SCTL_AE       = 0b00010000;
    53:   public static final int SPC_SCTL_PE       = 0b00001000;
    54:   public static final int SPC_SCTL_SE       = 0b00000100;
    55:   public static final int SPC_SCTL_RE       = 0b00000010;
    56:   public static final int SPC_SCTL_IE       = 0b00000001;
    57:   public static final int SPC_SCMD          = 0x05;
    58:   public static final int SPC_SCMD_CC       = 0b11100000;
    59:   public static final int SPC_SCMD_CC_BR    = 0b00000000;
    60:   public static final int SPC_SCMD_CC_SL    = 0b00100000;
    61:   public static final int SPC_SCMD_CC_RA    = 0b01000000;
    62:   public static final int SPC_SCMD_CC_SA    = 0b01100000;
    63:   public static final int SPC_SCMD_CC_TR    = 0b10000000;
    64:   public static final int SPC_SCMD_CC_TP    = 0b10100000;
    65:   public static final int SPC_SCMD_CC_RR    = 0b11000000;
    66:   public static final int SPC_SCMD_CC_SR    = 0b11100000;
    67:   public static final int SPC_SCMD_RO       = 0b00010000;
    68:   public static final int SPC_SCMD_IT       = 0b00001000;
    69:   public static final int SPC_SCMD_PT       = 0b00000100;
    70:   public static final int SPC_SCMD_TM       = 0b00000001;
    71:   public static final int SPC_INTS          = 0x09;
    72:   public static final int SPC_INTS_SL       = 0b10000000;
    73:   public static final int SPC_INTS_RS       = 0b01000000;
    74:   public static final int SPC_INTS_DC       = 0b00100000;
    75:   public static final int SPC_INTS_CC       = 0b00010000;
    76:   public static final int SPC_INTS_SR       = 0b00001000;
    77:   public static final int SPC_INTS_TO       = 0b00000100;
    78:   public static final int SPC_INTS_HE       = 0b00000010;
    79:   public static final int SPC_INTS_RC       = 0b00000001;
    80:   public static final int SPC_PSNS          = 0x0b;  //read
    81:   public static final int SPC_PSNS_REQ      = 0b10000000;
    82:   public static final int SPC_PSNS_ACK      = 0b01000000;
    83:   public static final int SPC_PSNS_ATN      = 0b00100000;
    84:   public static final int SPC_PSNS_SEL      = 0b00010000;
    85:   public static final int SPC_PSNS_BSY      = 0b00001000;
    86:   public static final int SPC_SDGC          = 0x0b;  //write
    87:   public static final int SPC_SDGC_REQ      = 0b10000000;
    88:   public static final int SPC_SDGC_ACK      = 0b01000000;
    89:   public static final int SPC_SDGC_XFER     = 0b00100000;
    90:   public static final int SPC_SDGC_BSY      = 0b00001000;
    91:   public static final int SPC_SDGC_MSG      = 0b00000100;
    92:   public static final int SPC_SDGC_CD       = 0b00000010;
    93:   public static final int SPC_SDGC_IO       = 0b00000001;
    94:   public static final int SPC_SSTS          = 0x0d;
    95:   public static final int SPC_SSTS_INIT     = 0b10000000;
    96:   public static final int SPC_SSTS_TARG     = 0b01000000;
    97:   public static final int SPC_SSTS_BUSY     = 0b00100000;
    98:   public static final int SPC_SSTS_TRIP     = 0b00010000;
    99:   public static final int SPC_SSTS_RSIN     = 0b00001000;
   100:   public static final int SPC_SSTS_TC0      = 0b00000100;
   101:   public static final int SPC_SSTS_DF       = 0b00000010;
   102:   public static final int SPC_SSTS_DE       = 0b00000001;
   103:   public static final int SPC_SERR          = 0x0f;
   104:   public static final int SPC_SERR_DI       = 0b10000000;
   105:   public static final int SPC_SERR_DO       = 0b01000000;
   106:   public static final int SPC_SERR_XO       = 0b00100000;
   107:   public static final int SPC_SERR_PE       = 0b00001000;
   108:   public static final int SPC_SERR_ST       = 0b00000010;
   109:   public static final int SPC_PCTL          = 0x11;
   110:   public static final int SPC_PCTL_IE       = 0b10000000;
   111:   public static final int SPC_PCTL_SR       = 0b00000001;
   112:   public static final int SPC_PCTL_SR_R     = 0b00000001;
   113:   public static final int SPC_PCTL_SR_S     = 0b00000000;
   114:   public static final int SPC_MBC           = 0x13;
   115:   public static final int SPC_DREG          = 0x15;
   116:   public static final int SPC_TEMP          = 0x17;
   117:   public static final int SPC_TCH           = 0x19;
   118:   public static final int SPC_TCM           = 0x1b;
   119:   public static final int SPC_TCL           = 0x1d;
   120:   public static final int SPC_BYPASS        = 0x1f;
   121:   public static final int SPC_PHASE_MASK    = 0b00000111;
   122:   public static final int SPC_DATAOUT_PHASE = 0b00000000;
   123:   public static final int SPC_DATAIN_PHASE  = 0b00000001;
   124:   public static final int SPC_CMDOUT_PHASE  = 0b00000010;
   125:   public static final int SPC_STSIN_PHASE   = 0b00000011;
   126:   public static final int SPC_MSGOUT_PHASE  = 0b00000110;
   127:   public static final int SPC_MSGIN_PHASE   = 0b00000111;
   128:   public static final byte[] sukRegister = new byte[32];  //最後に読み出されたか書き込まれたレジスタの値
   129: 
   130:   //受信キュー
   131:   //  コアとは別のスレッドで受信したデータは直ちに受信キューに移される
   132:   //  受信キューは溢れてはならない
   133:   public static final int SUK_QUEUE_SIZE = 1048576;  //キューの長さ。1MB
   134:   public static final byte[] sukQueueArray = new byte[SUK_QUEUE_SIZE];  //キューの配列
   135:   public static volatile int sukQueueWrite;  //書き込んだ長さ
   136:   public static volatile int sukQueueRead;  //読み出した長さ
   137: 
   138:   //受信スレッド
   139:   public static volatile boolean sukRunning;  //true=受信スレッド動作中
   140:   public static Thread sukThread;  //受信スレッド
   141:   public static boolean sukWaiting;  //true=リードコマンド送信済み
   142: 
   143:   //データインフェーズ受信
   144:   public static final int SUK_DATAIN_MODE = 0;  //0=常に0x41を使う,1=未受信データ数が512以上のときだけ0x41を使う,2=0x41を使わない
   145:   public static volatile int sukDatainStage;  //0=データインフェーズ受信中ではない,1=スタンバイ,2=データインフェーズ受信中
   146:   public static volatile int sukDatainTotal;  //総データ数
   147:   public static volatile int sukDatainNotReceived;  //未受信データ数
   148:   public static volatile int sukDatainBlockNotReceived;  //ブロック未受信データ数
   149:   public static int sukDatainNotRead;  //未リードデータ数
   150: 
   151:   //遅延送信
   152:   //  送信データを送信プールに溜めて一定時間送信がないか受信を行うときにまとめて送信する
   153:   //  送信と受信の間隔が足りなくなる場合は個別に対処する
   154:   //  ティッカーはコアと同じスレッドで命令の後に呼び出されるので追加や削除で衝突する心配はない
   155:   public static final int SUK_POOL_SIZE = 65536;  //送信プールのサイズ
   156:   public static final long SUK_POOL_SPAN = XEiJ.TMR_FREQ / 1000;  //一定時間。1ms
   157:   public static final byte[] sukPoolArray = new byte[SUK_POOL_SIZE];  //送信プール
   158:   public static int sukPoolIndex;  //送信プールのインデックス
   159:   public static final TickerQueue.Ticker sukPoolTicker = new TickerQueue.Ticker () {
   160:     @Override protected void tick () {
   161:       sukPoolFlush ();
   162:     }
   163:   };  //ティッカー
   164: 
   165:   //バイパス送信
   166:   //  0x9f,データの長さ下位,データの長さ上位,データ,…
   167:   //  バイパス送信中に送信プールが満杯になってはならない
   168:   //  送信プールから一気に送信するとき送信バッファフルでブロックさせてはならない
   169:   //  バイパス未送信数
   170:   //    0x00000000  バイパス送信中ではない
   171:   //    0xffff00??  バイパス送信中。次はデータの長さ上位を送信する
   172:   //    0x0000????  バイパス送信中。次はデータを送信する
   173:   public static int sukBypassNotTransmitted;  //バイパス未送信数
   174: 
   175:   //バイパス受信
   176:   //  データの長さ下位,データの長さ上位,データ,…
   177:   //  バイパス未受信数
   178:   //    0x00000000  バイパス受信中ではない
   179:   //    0xffffffff  バイパス受信中。次はデータの長さ下位を受信する
   180:   //    0xffff00??  バイパス受信中。次はデータの長さ上位を受信する
   181:   //    0x0000????  バイパス受信中。次はデータを受信する
   182:   public static int sukBypassNotReceived;  //バイパス未受信数
   183:   public static int sukBypassNotRead;  //バイパス未リード数
   184: 
   185:   //間隔保証
   186:   //  仮想時間で間隔を空ける命令列(Timer-Cを使う方法を含む)は実時間で間隔を空けることができない
   187:   //  ポートに書き込んでから次に読み書きするまでに実時間で間隔を空ける必要があるとき、最初の書き込みの直後に待機ポイントを設置する
   188:   //  最初の書き込みだけでは間隔が必要かどうか分からない場合は余分に間隔が空く場合がある
   189:   //  待機ポイントで間隔を空けるのでその後に実行される命令列と合わせると必要以上に間隔が空く場合がある
   190:   //  指定されたナノ秒時刻になるまで待機する
   191:   public static long sukWaitTimeNano;
   192:   public static int sukWaitTimeCounter;
   193:   public static final WaitInstruction sukWaitTimeInstruction = new WaitInstruction () {
   194:     @Override public boolean terminate () {
   195:       return ((sukWaitTimeCounter++ & 15) == 0 &&
   196:               0 <= (System.nanoTime () - sukWaitTimeNano));
   197:     }
   198:   };
   199: 
   200:   //割り込み保証
   201:   //  ポートがリードされたときデータの準備ができるまでコアを止めると割り込みを含めてエミュレータ全体の動きが止まってしまう
   202:   //  待機例外、待機ポイント、待機命令を用いてデータの準備ができるまで割り込みを止めずに待つ
   203:   //  受信キュー内のデータの数がMTC以上になるまで待機する
   204:   public static int sukWaitQueueMTC;
   205:   public static int sukWaitQueueCounter;
   206:   public static final WaitInstruction sukWaitQueueInstruction = new WaitInstruction () {
   207:     @Override public boolean terminate () {
   208:       return ((sukWaitQueueCounter++ & 15) == 0 &&
   209:               sukWaitQueueMTC <= (sukQueueWrite - sukQueueRead));
   210:     }
   211:   };
   212: 
   213:   //sukInit ()
   214:   //  初期化
   215:   public static void sukInit () {
   216:     sukPort1 = null;
   217:     sukPort2 = null;
   218:     sukOnRequest = Settings.sgsGetOnOff ("suk");
   219:     sukOn = false;
   220:     sukExpansionRequest = Settings.sgsGetOnOff ("sukex");
   221:     sukExpansion = false;
   222:     //sukRegister = new byte[32];
   223:   }  //sukInit
   224: 
   225:   //sukTini ()
   226:   //  後始末
   227:   public static void sukTini () {
   228:     Settings.sgsPutOnOff ("suk", sukOnRequest);
   229:     Settings.sgsPutOnOff ("sukex", sukExpansionRequest);
   230:     if (sukOn) {
   231:       sukDisconnect ();
   232:       sukOn = false;
   233:     }
   234:   }  //sukTini
   235: 
   236:   //sukGetMenu ()
   237:   //  メニューを返す
   238:   public static JMenu sukGetMenu () {
   239:     if (sukMenu == null) {
   240:       sukMenu = Multilingual.mlnText (
   241:         ComponentFactory.createMenu (
   242:           "SCSI Ukun Kai (experimental)",
   243:           sukOnCheckBox = Multilingual.mlnText (
   244:             ComponentFactory.createCheckBoxMenuItem (
   245:               sukOnRequest,
   246:               "Connect",
   247:               new ActionListener () {
   248:                 @Override public void actionPerformed (ActionEvent ae) {
   249:                   sukOnRequest = sukOnCheckBox.isSelected ();
   250:                 }
   251:               }),
   252:             "ja", "接続"),
   253:           sukExpansionCheckBox = Multilingual.mlnText (
   254:             ComponentFactory.createCheckBoxMenuItem (
   255:               sukExpansionRequest,
   256:               "Expansion",
   257:               new ActionListener () {
   258:                 @Override public void actionPerformed (ActionEvent ae) {
   259:                   sukExpansionRequest = sukExpansionCheckBox.isSelected ();
   260:                 }
   261:               }),
   262:             "ja", "拡張")
   263:           ),
   264:         "ja", "すかじー U 君改 (実験中)");
   265:     }
   266:     return sukMenu;
   267:   }  //sukGetMenu
   268: 
   269:   //sukReset ()
   270:   //  リセット
   271:   public static void sukReset () {
   272:     if (sukOnRequest) {
   273:       if (!sukConnect ()) {
   274:         sukOnRequest = false;
   275:         sukOnCheckBox.setSelected (false);
   276:       }
   277:     } else {
   278:       sukDisconnect ();
   279:     }
   280:     sukOn = sukOnRequest;
   281:     sukExpansion = sukExpansionRequest;
   282:   }  //sukReset
   283: 
   284:   //sukConnect ()
   285:   //  接続する
   286:   public static boolean sukConnect () {
   287:     if (sukPort1 != null) {  //接続している
   288:       return true;
   289:     }
   290:     //すかじーU君改のポートを探す
   291:     SerialPort port1 = null;
   292:     SerialPort port2 = null;
   293:     for (SerialPort port : SerialPort.getCommPorts ()) {  //すべてのシリアル通信ポートについて
   294:       if (port.getVendorID () == SUK_VID &&  //ベンダーIDが一致
   295:           port.getProductID () == SUK_PID) {  //プロダクトIDが一致
   296:         if (port1 == null) {  //1個目
   297:           port1 = port;  //ポート1(仮)
   298:         } else if (port2 == null) {  //2個目
   299:           port2 = port;  //ポート2(仮)
   300:         } else {  //3個目
   301:           //すかじーU君改が複数接続されている
   302:           //ポート1とポート2の組み合わせが分からないので「先に見つかった方」は採用できない
   303:           System.out.println (Multilingual.mlnJapanese ?
   304:                               "すかじー U 君改のポートが多すぎます" :
   305:                               "Too many MB89352 bridger");
   306:           return false;
   307:         }
   308:       }
   309:     }
   310:     if (port2 == null) {  //見つからない
   311:       System.out.println (Multilingual.mlnJapanese ?
   312:                           "すかじー U 君改のポートが見つかりません" :
   313:                           "MB89352 bridger not found");
   314:       return false;
   315:     }
   316:     //開く
   317:     if (!port1.openPort (0, SUK_TRANSMISSION_QUEUE_SIZE, SUK_RECEIVE_QUEUE_SIZE)) {  //ポート1(仮)を開けない
   318:       System.out.println (Multilingual.mlnJapanese ?
   319:                           "すかじー U 君改のポートを開けません" :
   320:                           "Cannot open MB89352 bridger");
   321:       return false;
   322:     }
   323:     if (!port2.openPort (0, SUK_TRANSMISSION_QUEUE_SIZE, SUK_RECEIVE_QUEUE_SIZE)) {  //ポート2(仮)を開けない
   324:       port1.closePort ();
   325:       System.out.println (Multilingual.mlnJapanese ?
   326:                           "すかじー U 君改のポートを開けません" :
   327:                           "Cannot open MB89352 bridger");
   328:       return false;
   329:     }
   330:     //パラメータを設定する
   331:     //  9600bpsに設定されているので480Mbpsに変更する
   332:     port1.setComPortParameters (480000000, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
   333:     port2.setComPortParameters (480000000, 8, SerialPort.ONE_STOP_BIT, SerialPort.NO_PARITY);
   334:     //タイムアウトを設定する
   335:     //  readBytesは50msを上限として少なくとも1バイト受信するまでブロックする
   336:     //  writeBytesは送信バッファが一杯になるまでブロックしない
   337:     port1.setComPortTimeouts (SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 50, 0);
   338:     port2.setComPortTimeouts (SerialPort.TIMEOUT_READ_SEMI_BLOCKING, 50, 0);
   339:     //ポート1とポート2を判別する
   340:     //  50ms以内に応答がないと失敗する
   341:     sukCoreTemporary[0] = 0x00;
   342:     sukCoreTemporary[1] = 0x00;
   343:     port1.writeBytes (sukCoreTemporary, 1, 0);  //ポート1(仮)へ0x00を送信する
   344:     port1.readBytes (sukCoreTemporary, 1, 0);  //ポート1(仮)から1バイト受信する
   345:     port2.writeBytes (sukCoreTemporary, 1, 1);  //ポート2(仮)へ0x00を送信する
   346:     port2.readBytes (sukCoreTemporary, 1, 1);  //ポート2(仮)から1バイト受信する
   347:     if (sukCoreTemporary[0] == 's' && sukCoreTemporary[1] == 'S') {  //(仮)は合っている
   348:     } else if (sukCoreTemporary[0] == 'S' && sukCoreTemporary[1] == 's') {  //(仮)は逆
   349:       //ポート1とポート2を入れ替える
   350:       SerialPort port = port1;
   351:       port1 = port2;
   352:       port2 = port;
   353:     } else {  //不明
   354:       port1.closePort ();
   355:       port2.closePort ();
   356:       System.out.println (Multilingual.mlnJapanese ?
   357:                           "すかじー U 君改のポートを判別できません" :
   358:                           "Unable to identify MB89352 bridger");
   359:       return false;
   360:     }
   361:     //初期化する
   362:     sukCoreTemporary[0] = (byte) 0x80;
   363:     sukCoreTemporary[1] = (byte) 0xfe;
   364:     port1.writeBytes (sukCoreTemporary, 2);  //ポート1へ0x80,0xfeを送信する(エスケープなし)
   365:     //完了
   366:     sukPort1 = port1;
   367:     sukPort2 = port2;
   368:     Arrays.fill (sukRegister, (byte) 0);
   369:     System.out.printf (Multilingual.mlnJapanese ?
   370:                        "すかじー U 君改 (%s,%s) に接続しました\n" :
   371:                        "MB89352 bridger (%s,%s) connected\n",
   372:                        sukPort1.getSystemPortName (),
   373:                        sukPort2.getSystemPortName ());
   374:     //受信キューを作る
   375:     //sukQueueArray = new byte[SUK_QUEUE_SIZE];
   376:     sukQueueWrite = 0;
   377:     sukQueueRead = 0;
   378:     //受信スレッドを開始する
   379:     //  以降の受信はすべて受信キューを介して行う
   380:     sukRunning = true;
   381:     sukThread = new Thread () {
   382:       @Override public void run () {
   383:         while (sukRunning) {
   384:           if (sukDatainStage == 2 && sukDatainBlockNotReceived < 0) {  //ブロック未受信データ数
   385:             if (((short) sukDatainBlockNotReceived) < 0) {  //1バイト目(下位バイト)
   386:               int n = sukPort1.readBytes (sukReceivingTemporary, 2, 0);
   387:               if (n == 2) {
   388:                 sukDatainBlockNotReceived = (0xff & sukReceivingTemporary[1]) << 8 | (0xff & sukReceivingTemporary[0]);
   389:               } else if (n == 1) {
   390:                 sukDatainBlockNotReceived = 0xffff0000 + (0xff & sukReceivingTemporary[0]);
   391:               }
   392:             } else {  //2バイト目(上位バイト)
   393:               int n = sukPort1.readBytes (sukReceivingTemporary, 1, 1);
   394:               if (n == 1) {
   395:                 sukDatainBlockNotReceived = (0xff & sukReceivingTemporary[1]) << 8 | (0xff & sukDatainBlockNotReceived);
   396:               }
   397:             }
   398:           } else {  //データ
   399:             int w = sukQueueWrite;  //書き込んだ長さ
   400:             int f = SUK_QUEUE_SIZE - (w - sukQueueRead);  //未使用の長さ
   401:             if (f == 0) {  //満杯。あってはならない
   402:               System.out.printf ("%08x queue is full\n", XEiJ.regPC0);
   403:               break;
   404:             }
   405:             int i = w & (SUK_QUEUE_SIZE - 1);  //次に書き込む位置
   406:             int n = sukPort1.readBytes (sukQueueArray, Math.min (f, SUK_QUEUE_SIZE - i), i);  //受信できるだけ受信する
   407:             if (0 < n) {
   408:               w += n;
   409:               sukQueueWrite = w;  //受信データキューに追加する
   410:               sukDatainNotReceived -= n;  //未受信データ数から受信したバイト数を引く
   411:               if (sukDatainNotReceived < 0) {  //未受信データ数が負。あってはならない
   412:                 System.out.printf ("%08x not received is negative\n", XEiJ.regPC0);
   413:                 break;
   414:               }
   415:               sukDatainBlockNotReceived -= n;  //ブロック未受信データ数から受信したバイト数を引く
   416:               if (sukDatainBlockNotReceived < 0) {  //ブロック未受信データ数が負。あってはならない
   417:                 System.out.printf ("%08x block not received is negative\n", XEiJ.regPC0);
   418:                 break;
   419:               }
   420:               if (sukDatainNotReceived != 0 && sukDatainBlockNotReceived == 0) {  //未受信データ数が0でなくブロック未受信データ数が0になったとき
   421:                 if (SUK_DATAIN_MODE == 0) {  //常に0x41を使う場合
   422:                   sukDatainBlockNotReceived = 0xffffffff;  //次はブロック未受信データ数の下位
   423:                   sukReceivingTemporary[0] = 0x41;  //0x41を送信する
   424:                   sukPort1.writeBytes (sukReceivingTemporary, 1);
   425:                 } else if (SUK_DATAIN_MODE == 1) {  //未受信データ数が512以上のときだけ0x41を使う場合
   426:                   if (sukDatainNotReceived < 512) {  //未受信データ数が512未満のとき
   427:                     sukDatainBlockNotReceived = sukDatainNotReceived;  //ブロック未受信データ数を未受信データ数とする
   428:                     Arrays.fill (sukReceivingTemporary, 0, sukDatainBlockNotReceived, (byte) SPC_DREG);  //0x15をブロック未受信データ数だけ送信する
   429:                     sukPort1.writeBytes (sukReceivingTemporary, sukDatainBlockNotReceived);
   430:                   } else {  //未受信データ数が512以上のとき
   431:                     sukDatainBlockNotReceived = 0xffffffff;  //次はブロック未受信データ数の下位
   432:                     sukReceivingTemporary[0] = 0x41;  //0x41を送信する
   433:                     sukPort1.writeBytes (sukReceivingTemporary, 1);
   434:                   }
   435:                 } else {  //0x41を使わない場合
   436:                   sukDatainBlockNotReceived = Math.min (512, sukDatainNotReceived);  //ブロック未受信データ数
   437:                   Arrays.fill (sukReceivingTemporary, 0, sukDatainBlockNotReceived, (byte) SPC_DREG);  //0x15をブロック未受信データ数だけ送信する
   438:                   sukPort1.writeBytes (sukReceivingTemporary, sukDatainBlockNotReceived);
   439:                 }
   440:               }
   441:             }
   442:           }
   443:           while (sukPort2.bytesAvailable () != 0) {
   444:             sukReceivingTemporary[0] = 0x00;
   445:             if (sukPort2.readBytes (sukReceivingTemporary, 1) != 0 &&
   446:                 sukReceivingTemporary[0] == 'I') {  //割り込み要求
   447:               if (sukExpansion) {
   448:                 XEiJ.eb2Interrupt (XEiJ.EB2_SPC_REQUEST);
   449:               } else {
   450:                 IOInterrupt.ioiSpcFall ();
   451:                 IOInterrupt.ioiSpcRise ();
   452:               }
   453:             }
   454:           }
   455:         }
   456:       }
   457:     };
   458:     sukThread.start ();
   459:     sukWaiting = false;
   460:     //データインフェーズ受信中ではない
   461:     sukDatainStage = 0;
   462:     //遅延送信
   463:     //  以降の送信はすべて送信プールを介して行う
   464:     //sukPoolArray = new byte[SUK_POOL_SIZE];
   465:     sukPoolIndex = 0;
   466:     //バイパス送信
   467:     sukBypassNotTransmitted = 0;
   468:     //バイパス受信
   469:     sukBypassNotReceived = 0;
   470:     sukBypassNotRead = 0;
   471:     return true;
   472:   }  //sukConnect
   473: 
   474:   //sukDisconnect ()
   475:   //  切断する
   476:   public static void sukDisconnect () {
   477:     if (sukPort1 == null) {  //接続していない
   478:       return;
   479:     }
   480:     //閉じる
   481:     sukPort1.closePort ();
   482:     sukPort2.closePort ();
   483:     System.out.printf (Multilingual.mlnJapanese ?
   484:                        "すかじー U 君改 (%s,%s) を切り離しました\n" :
   485:                        "MB89352 bridger (%s,%s) disconnected\n",
   486:                        sukPort1.getSystemPortName (),
   487:                        sukPort2.getSystemPortName ());
   488:     sukPort1 = null;
   489:     sukPort2 = null;
   490:   }  //sukDisconnect
   491: 
   492:   //sukPeek (a)
   493:   //  ピーク
   494:   public static int sukPeek (int a) {
   495:     a &= 0x1f;
   496:     return 0xff & sukRegister[a];
   497:   }  //sukPeek
   498: 
   499:   //sukRead (a)
   500:   //  リード
   501:   public static int sukRead (int a) throws M68kException {
   502:     a &= 0x1f;
   503:     if (sukDatainStage == 2) {  //データインフェーズ受信中
   504:       if (a == SPC_DREG) {  //DREG。受信キューから取り出す
   505:         if ((sukQueueWrite - sukQueueRead) == 0) {  //受信キューが空。あってはならない
   506:           System.out.printf ("%08x receiving queue is empty\n", XEiJ.regPC0);
   507:         } else {  //受信キューが空ではない
   508:           //受信キューからデータを取り出す
   509:           int r = sukQueueRead;
   510:           sukRegister[a] = sukQueueArray[r++ & (SUK_QUEUE_SIZE - 1)];
   511:           sukQueueRead = r;
   512:           //未リードデータ数をデクリメントする
   513:           sukDatainNotRead--;
   514:           //未リードデータ数が0になったら終了
   515:           if (sukDatainNotRead == 0) {  //未リードデータ数が0になった
   516:             if (SUK_DEBUG) {
   517:               System.out.printf ("%08x receiving in data-in-phase is complete\n", XEiJ.regPC0);
   518:             }
   519:             sukDatainStage = 0;
   520:           }
   521:         }
   522:       } else if (a == SPC_INTS) {  //INTS。常に0x00
   523:         sukRegister[a] = 0x00;
   524:       } else if (a == SPC_SSTS) {  //SSTS。DEは常に0、DFは未リードデータ数が8未満のとき0、さもなくば1
   525:         sukRegister[a] = (byte) (SPC_SSTS_INIT | SPC_SSTS_BUSY | SPC_SSTS_TRIP | (sukDatainNotRead < 8 ? 0 : SPC_SSTS_DF));
   526:       }
   527:       return 0xff & sukRegister[a];
   528:     }
   529:     if (sukDatainStage == 1 &&  //スタンバイで
   530:         (XEiJ.busWaitTime == XEiJ.mpuWaitTime || XEiJ.busWaitTime == XEiJ.mpuNoWaitTime) &&  //MPUが
   531:         a == SPC_DREG) {  //DREGをリードしようとしたとき
   532:       if (SUK_DEBUG) {
   533:         System.out.printf ("%08x MPU has started receiving\n", XEiJ.regPC0);
   534:       }
   535:       sukDatainStage2 ();  //データインフェーズ受信中に移行する
   536:       int n = Math.min (512, sukDatainNotRead);  //受信キューに用意するデータ数
   537:       if (n <= (sukQueueWrite - sukQueueRead)) {  //足りている
   538:         int r = sukQueueRead;
   539:         sukRegister[a] = sukQueueArray[r++ & (SUK_QUEUE_SIZE - 1)];
   540:         sukQueueRead = r;
   541:         sukDatainNotRead--;
   542:         if (sukDatainNotRead == 0) {
   543:           if (SUK_DEBUG) {
   544:             System.out.printf ("%08x receiving in data-in-phase is complete\n", XEiJ.regPC0);
   545:           }
   546:           sukDatainStage = 0;
   547:         }
   548:         return 0xff & sukRegister[a];
   549:       }
   550:       sukWaitQueueMTC = n;  //リードしようとした命令に待機ポイントを設置する
   551:       sukWaitQueueCounter = 0;
   552:       InstructionBreakPoint.ibpAddWaitPoint (XEiJ.regPC0, XEiJ.regSRS, sukWaitQueueInstruction);
   553:       M68kException.m6eNumber = M68kException.M6E_WAIT_EXCEPTION;  //待機例外をスローしてデータの準備ができてからリードし直す
   554:       throw M68kException.m6eSignal;
   555:     }
   556:     //データインフェーズ受信中ではない
   557:     if (XEiJ.busWaitTime == XEiJ.dmaWaitTime || XEiJ.busWaitTime == XEiJ.dmaNoWaitTime) {  //DMACによるリード。あってはならない
   558:       System.out.printf ("%08x DMAC attempt to read while not in data-in-phase\n", XEiJ.regPC0);
   559:       return 0xff & sukRegister[a];
   560:     }
   561:     //MPUによるリード
   562:     if (sukBypassNotTransmitted != 0) {  //バイパス送信中にリードしようとした。あってはならない
   563:       System.out.printf ("%08x MPU attempt to read during transmitting bypass\n", XEiJ.regPC0);
   564:       return 0xff & sukRegister[a];
   565:     }
   566:     if (a == SPC_BYPASS) {  //バイパス
   567:       if (sukBypassNotReceived == 0) {  //バイパス受信開始
   568:         sukBypassNotReceived = 0xffffffff;
   569:         sukBypassNotRead = 0xffffffff;
   570:       } else {  //バイパス受信中
   571:         //!!!
   572:       }
   573:     } else {  //バイパスではない
   574:       if (sukBypassNotReceived != 0) {  //バイパス受信中にバイパス以外のレジスタをリードしようとした。あってはならない
   575:         System.out.printf ("%08x MPU attempt to read a register other than bypass while receiving bypass\n", XEiJ.regPC0);
   576:         return 0xff & sukRegister[a];
   577:       }
   578:       //!!!
   579:     }
   580:     //!!!
   581:     if (1 <= (sukQueueWrite - sukQueueRead)) {  //データがある
   582:       sukWaiting = false;
   583:       int r = sukQueueRead;
   584:       sukRegister[a] = sukQueueArray[r++ & (SUK_QUEUE_SIZE - 1)];
   585:       sukQueueRead = r;
   586:       return 0xff & sukRegister[a];
   587:     }
   588:     if (!sukWaiting) {  //リードコマンド未送信
   589:       sukWaiting = true;
   590:       TickerQueue.tkqRemove (sukPoolTicker);
   591:       if (SUK_POOL_SIZE - 1 < sukPoolIndex) {
   592:         sukPoolFlush ();
   593:       }
   594:       sukPoolArray[sukPoolIndex++] = (byte) a;  //リードコマンドを送信する
   595:       sukPoolFlush ();
   596:     }
   597:     //リードしようとした命令に待機ポイントを設置する
   598:     sukWaitQueueMTC = 1;
   599:     sukWaitQueueCounter = 0;
   600:     InstructionBreakPoint.ibpAddWaitPoint (XEiJ.regPC0, XEiJ.regSRS, sukWaitQueueInstruction);
   601:     M68kException.m6eNumber = M68kException.M6E_WAIT_EXCEPTION;  //待機例外をスローしてデータの準備ができてからリードし直す
   602:     throw M68kException.m6eSignal;
   603:   }  //sukRead
   604: 
   605:   //sukWrite (a, d)
   606:   //  ライト
   607:   public static void sukWrite (int a, int d) {
   608:     a &= 0x1f;
   609:     d &= 0xff;
   610:     if (a == SPC_BYPASS) {  //バイパス
   611:       if (sukBypassNotTransmitted == 0) {  //バイパス送信開始
   612:         sukPoolArray[sukPoolIndex++] = (byte) (0x80 + a);  //ライトコマンド
   613:         sukPoolArray[sukPoolIndex++] = (byte) d;  //データの長さ下位
   614:         sukBypassNotTransmitted = 0xffff0000 + d;
   615:       } else if (sukBypassNotTransmitted < 0) {
   616:         sukPoolArray[sukPoolIndex++] = (byte) d;  //データの長さ上位
   617:         sukBypassNotTransmitted = d << 8 | (0xff & sukBypassNotTransmitted);
   618:       } else {  //データ
   619:         sukPoolArray[sukPoolIndex++] = (byte) d;  //データ
   620:         sukBypassNotTransmitted--;  //0で終了
   621:       }
   622:     } else {  //バイパス以外
   623:       if (sukBypassNotTransmitted != 0) {  //バイパス送信中。あってはならない
   624:         return;
   625:       }
   626:       if (SUK_POOL_SIZE - 2 < sukPoolIndex) {
   627:         sukPoolFlush ();
   628:       }
   629:       sukPoolArray[sukPoolIndex++] = (byte) (0x80 + a);  //ライトコマンド
   630:       sukPoolArray[sukPoolIndex++] = (byte) d;  //データ
   631:     }
   632:     //遅延送信
   633:     TickerQueue.tkqAdd (sukPoolTicker, XEiJ.mpuClockTime + SUK_POOL_SPAN);
   634:     sukRegister[a] = (byte) d;
   635:     //間隔保証
   636:     int us = (a == SPC_SCMD && (d & SPC_SCMD_RO) != 0 ? 250 :  //SCSIバスリセット開始
   637:               a == SPC_SCMD && (d & SPC_SCMD_CC) == SPC_SCMD_CC_SL ? 50 :  //セレクション開始
   638:               a == SPC_INTS && (d & SPC_INTS_TO) != 0 ? 50 :  //セレクションタイムアウトクリア
   639:               0);
   640:     if (us != 0) {  //実時間で間隔を空ける必要があるとき
   641:       sukPoolFlush ();
   642:       //直後の命令に待機ポイントを設置する
   643:       sukWaitTimeNano = System.nanoTime () + us * 1000;
   644:       sukWaitTimeCounter = 0;
   645:       InstructionBreakPoint.ibpAddWaitPoint (XEiJ.regPC, XEiJ.regSRS, sukWaitTimeInstruction);
   646:     }
   647:     //データインフェーズ転送
   648:     if ((sukRegister[SPC_PCTL] & SPC_PHASE_MASK) == SPC_DATAIN_PHASE &&  //データインフェーズで
   649:         a == SPC_SCMD && (d & SPC_SCMD_CC) == SPC_SCMD_CC_TR) {  //転送を開始したとき
   650:       if (SUK_DEBUG) {
   651:         System.out.printf ("%08x datain SPC start\n", XEiJ.regPC0);
   652:       }
   653:       sukDatainStage = 1;  //スタンバイ
   654:     }
   655:   }  //sukWrite
   656: 
   657:   //sukPoolFlush ()
   658:   //  送信プールから送信する
   659:   public static void sukPoolFlush () {
   660:     if (0 < sukPoolIndex) {
   661:       sukPort1.writeBytes (sukPoolArray, sukPoolIndex);
   662:       sukPoolIndex = 0;
   663:     }
   664:   }
   665: 
   666:   //sukDatainStage2 ()
   667:   //  データインフェーズ受信中に移行する
   668:   //  スタンバイから最初に
   669:   //    MPUがDREGをリードしたとき
   670:   //    または
   671:   //    DAR1がDREGかつOCR1のDIRがDEVICE_TO_MEMORYでCCR1にSTRが書き込まれたとき
   672:   public static void sukDatainStage2 () {
   673:     sukDatainTotal = ((0xff & sukRegister[SPC_TCH]) << 16 |
   674:                      (0xff & sukRegister[SPC_TCM]) <<  8 |
   675:                      (0xff & sukRegister[SPC_TCL]));  //総データ数はTC*
   676:     if (SUK.SUK_DEBUG) {
   677:       System.out.printf ("%08x receiving %d bytes in data-in-phase has started\n", XEiJ.regPC0, sukDatainTotal);
   678:     }
   679:     if (sukDatainTotal == 0) {  //受信するデータがない
   680:       sukDatainStage = 0;
   681:       return;
   682:     }
   683:     sukDatainStage = 2;  //受信中
   684:     sukDatainNotReceived = sukDatainTotal;  //未受信データ数は総データ数
   685:     sukDatainNotRead = sukDatainTotal;  //未リードデータ数は総データ数
   686:     sukQueueRead = sukQueueWrite;  //受信データキューを空にする
   687:     if (SUK_DATAIN_MODE == 0) {  //常に0x41を使う場合
   688:       sukDatainBlockNotReceived = 0xffffffff;  //次はブロック未受信データ数の下位
   689:       sukCoreTemporary[0] = 0x41;  //0x41を送信する
   690:       sukPort1.writeBytes (sukCoreTemporary, 1);
   691:     } else if (SUK_DATAIN_MODE == 1) {  //未受信データ数が512以上のときだけ0x41を使う場合
   692:       if (sukDatainNotReceived < 512) {  //未受信データ数が512未満のとき
   693:         sukDatainBlockNotReceived = sukDatainNotReceived;  //ブロック未受信データ数を未受信データ数とする
   694:         Arrays.fill (sukCoreTemporary, 0, sukDatainBlockNotReceived, (byte) SPC_DREG);  //0x15をブロック未受信データ数だけ送信する
   695:         sukPort1.writeBytes (sukCoreTemporary, sukDatainBlockNotReceived);
   696:       } else {  //未受信データ数が512以上のとき
   697:         sukDatainBlockNotReceived = 0xffffffff;  //次はブロック未受信データ数の下位
   698:         sukCoreTemporary[0] = 0x41;  //0x41を送信する
   699:         sukPort1.writeBytes (sukCoreTemporary, 1);
   700:       }
   701:     } else {  //0x41を使わない場合
   702:       sukDatainBlockNotReceived = Math.min (512, sukDatainNotReceived);  //ブロック未受信データ数
   703:       Arrays.fill (sukCoreTemporary, 0, sukDatainBlockNotReceived, (byte) SPC_DREG);  //0x15をブロック未受信データ数だけ送信する
   704:       sukPort1.writeBytes (sukCoreTemporary, sukDatainBlockNotReceived);
   705:     }
   706:   }  //sukDatainStage2
   707: 
   708: }  //class SUK