RS232CTerminal.java
     1: //========================================================================================
     2: //  RS232CTerminal.java
     3: //    en:RS-232C settings and terminal
     4: //    ja:RS-232C設定とターミナル
     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.datatransfer.*;  //DataFlavor
    16: import java.awt.event.*;  //ActionListener
    17: import java.io.*;  //UnsupportedEncodingException
    18: import java.net.*;  //URLEncoder
    19: import java.nio.charset.*;  //Charset
    20: import java.util.*;  //HashSet
    21: import java.util.zip.*;  //CRC32
    22: import javax.swing.*;
    23: import javax.swing.event.*;  //DocumentListener
    24: 
    25: import com.fazecast.jSerialComm.*;  //SerialPort
    26: 
    27: public class RS232CTerminal {
    28: 
    29:   public static final int TRM_MAX_OUTPUT_LENGTH = 1024 * 256;  //出力の上限を256KBとする
    30:   public static final int TRM_CUT_OUTPUT_LENGTH = TRM_MAX_OUTPUT_LENGTH + 1024 * 16;  //出力が上限よりも16KB以上長くなったら上限でカットする
    31: 
    32:   //コンポーネント
    33:   public static JFrame trmFrame;  //ウインドウ
    34:   public static ScrollTextArea trmBoard;  //テキストエリア
    35:   public static JPopupMenu trmPopupMenu;  //ポップアップメニュー
    36:   public static JMenuItem trmPopupCutMenuItem;  //切り取り
    37:   public static JMenuItem trmPopupCopyMenuItem;  //コピー
    38:   public static JMenuItem trmPopupPasteMenuItem;  //貼り付け
    39:   public static JMenuItem trmPopupSelectAllMenuItem;  //すべて選択
    40:   public static JMenuItem trmPopupSendCtrlCMenuItem;  //^C送信
    41:   public static StringBuilder trmOutputBuilder;  //ターミナルを最初に開くまでに出力された文字列を貯めておくバッファ
    42:   public static int trmOutputEnd;  //出力された文字列の末尾。リターンキーが押されたらこれ以降に書かれた文字列をまとめて入力する
    43:   public static int trmOutputSJIS1;  //出力するときに繰り越したSJISの1バイト目
    44: 
    45:   //SerialPort
    46:   public static SerialPort[] trmPortArray;  //[row-1]=SerialPort。SerialPortの配列。じょいぽーとU君とすかじーU君改を含まない
    47:   public static int trmNumberOfPorts;  //SerialPortの数
    48:   //行
    49:   //  Terminal,SerialPort,SerialPort,…
    50:   public static int trmRows;  //行数。1+trmNumberOfPorts
    51:   public static String[] trmRowName;  //行の名前
    52:   public static int[] trmRowToCol;  //行に接続している列。なければ-1
    53:   //AUX*
    54:   public static int trmNumberOfAUXs;  //AUX*の数
    55:   //列
    56:   //  Terminal,AUX,AUX2,…
    57:   public static int trmCols;  //列数。1+trmNumberOfAUXs
    58:   public static String[] trmColName;  //列の名前
    59:   public static int[] trmColToRow;  //列に接続している行。なければ-1
    60: 
    61: 
    62: 
    63:   //接続
    64:   //  Terminalから送信するとき。TerminalでENTERキーが押されてそれまでに入力/貼り付けされた文字列を送信するとき
    65:   //    KeyListenerでENTERキーが押されたとき
    66:   //      それまでに入力/貼り付けされた文字列を取り出す(trmEnter)
    67:   //        SJISに変換してキューに書き込む(trmSendString)
    68:   //  Terminalが受信するとき。入力/貼り付け以外でTerminalに文字列を表示するとき
    69:   //    スレッドでキューをポーリングする
    70:   //      取り出してSJIS逆変換してテキストエリアへ書き込む
    71:   //      テキストエリアが大きくなりすぎたときは先頭を削って短くする
    72:   //  AUXから送信するとき。SCCのデータレジスタへライトされたとき
    73:   //    キューに書き込むだけ
    74:   //    OUT232CはTx Buffer Emptyが1になるのを待ってからデータポートへライトするが、
    75:   //    Tx Buffer Emptyが1になるのを待たずにライトしても送信される
    76:   //    Tx Buffer Emptyの動作についてはtrmAUXSendTickerを参照
    77:   //  AUXが受信するとき。SCCのデータレジスタからリードされたとき
    78:   //    レジスタのデータを返すだけ
    79:   //    ポーリング間隔またはボーレートなどから決めた受信間隔でティッカーを繰り返す
    80:   //    (受信間隔は短くてもよいがデバッガの誤動作をどうにかしなければならない)
    81:   //      キューが空でなくReset Highest IUSコマンド(WR0=$38)によりIUS(Interrupt Under Service)がリセットされているとき
    82:   //        キューからデータを取り出してレジスタを更新して割り込みを要求する
    83:   //  SerialPortから送信するとき。jSerialCommが受信するとき
    84:   //    SerialPortDataListenerでSerialPortからreadBytesしてキューへ書き込む
    85:   //    キューが満杯になることは事実上ないのでreadBytesが長く滞ることはないはず
    86:   //  SerialPortが受信するとき。jSerialCommが送信するとき
    87:   //    スレッドでキューをポーリングする
    88:   //      データがあれば取り出してwriteBytesでjSerialCommに渡す
    89:   //      writeBytesがブロックするとポーリングが止まる
    90:   static class Connection implements SerialPortDataListener {
    91:     int row;  //行。Terminal,SerialPort,SerialPort,…
    92:     int col;  //列。Terminal,AUX,AUX2,…。row==0&&col==0は不可
    93:     int index;  //trmConnectionArrayとtrmConnectionBoxでのインデックス。trmCols*row+col-1
    94:     String text;  //checkBoxのtext兼actionCommand
    95:     boolean connected;  //接続している
    96:     JCheckBox checkBox;  //接続チェックボックス
    97:     Box box;  //checkBoxを入れる箱
    98: 
    99:     //キュー
   100:     ByteQueue row2colQueue;  //Terminal/SerialPort→Terminal/AUX
   101:     ByteQueue col2rowQueue;  //Terminal/AUX→Terminal/SerialPort
   102:     boolean row2colReset;  //true=row2colQueueをクリアする
   103:     boolean col2rowReset;  //true=col2rowQueueをクリアする
   104: 
   105:     //シリアルポートデータリスナー。row2col。SerialPort→?
   106:     @Override public int getListeningEvents () {
   107:       return SerialPort.LISTENING_EVENT_DATA_AVAILABLE;
   108:     }
   109:     @Override public void serialEvent (SerialPortEvent spe) {
   110:       if (spe.getEventType () != SerialPort.LISTENING_EVENT_DATA_AVAILABLE) {
   111:         return;
   112:       }
   113:       SerialPort port = spe.getSerialPort ();
   114:       for (;;) {
   115:         int k = Math.min (port.bytesAvailable (),  //ポートから読み出せるバイト数と
   116:                           row2colQueue.unused ());  //キューへ書き込めるバイト数の小さい方
   117:         if (k == 0) {
   118:           break;
   119:         }
   120:         byte[] b = new byte[k];
   121:         port.readBytes (b, k);  //ポートから読み出す
   122:         row2colQueue.write (b, 0, k);  //キューへ書き込む
   123:       }
   124:     }
   125: 
   126:     //ポーリングスレッド
   127:     boolean polling;  //false=ポーリング終了
   128:     Thread row2colThread;  //Terminal/SerialPort→Terminal/AUX。TerminalThread
   129:     Thread col2rowThread;  //Terminal/AUX→Terminal/SerialPort。TerminalThreadまたはSerialPortThread
   130: 
   131:     //  ?→Terminalポーリングスレッド
   132:     class TerminalThread extends Thread {
   133:       @Override public void run () {
   134:         ByteQueue queue = (col == 0 ? row2colQueue : col2rowQueue);
   135:         while (polling) {
   136:           if (col == 0 ? row2colReset : col2rowReset) {
   137:             if (col == 0) {
   138:               row2colReset = false;
   139:             } else {
   140:               col2rowReset = false;
   141:             }
   142:             queue.clear ();
   143:           } else {
   144:             for (int k = queue.used (); k != 0; k = queue.used ()) {  //キューが空になるまでスリープしない
   145:               byte[] b = new byte[k];
   146:               queue.read (b, 0, k);  //キューから読み出して
   147:               for (int i = 0; i < k; i++) {
   148:                 trmPrintSJIS (b[i] & 0xff);  //ターミナルへ書き込む
   149:               }
   150:             }
   151:           }
   152:           try {
   153:             Thread.sleep (100);
   154:           } catch (InterruptedException ie) {
   155:           }
   156:         }  //while polling
   157:       }  //run
   158:     }  //class TerminalThread
   159: 
   160:     //  ?→SerialPortポーリングスレッド
   161:     class SerialPortThread extends Thread {
   162:       @Override public void run () {
   163:         SerialPort port = trmPortArray[row - 1];
   164:         ByteQueue queue = col2rowQueue;
   165:         while (polling) {
   166:           if (col2rowReset) {
   167:             col2rowReset = false;
   168:             queue.clear ();
   169:             port.flushIOBuffers ();
   170:           } else {
   171:             for (int k = queue.used (); k != 0; k = queue.used ()) {  //キューが空になるまでスリープしない
   172:               byte[] b = new byte[k];
   173:               queue.read (b, 0, k);  //キューから読み出して
   174:               port.writeBytes (b, k);  //シリアルポートへ書き込む。ブロックすることがある
   175:             }
   176:           }
   177:           try {
   178:             Thread.sleep (10);
   179:           } catch (InterruptedException ie) {
   180:           }
   181:         }  //while polling
   182:       }  //run
   183:     }  //class SerialPortThread
   184: 
   185:   }  //class Connection
   186: 
   187:   //AUXフロー制御
   188:   //  X68000のRS-232Cのフロー制御は通信ドライバが処理している
   189:   //  プログラムが連続的に動作する実機と違い、間欠的に動作するエミュレータではフロー制御が適切なタイミングで行われない
   190:   //  SerialPort側のフロー制御をjSerialCommに任せ、通信ドライバ側のフロー制御をSCCで完結させることで、取りこぼしを防ぐ
   191:   //  フロー制御がXONでバッファの3/4が埋まって通信ドライバがXOFF$13を送信したとき
   192:   //  フロー制御がRTSでバッファの3/4が埋まって通信ドライバがWR5 bit1 0=/RTS is Highにしたとき
   193:   //    直ちにSCCの受信を停止する。瞬時に止まるのでどんなに速く通信していても通信ドライバの受信バッファは溢れない
   194:   //  フロー制御がXONでバッファの3/4が空いて通信ドライバがXON$11を送信したとき
   195:   //  フロー制御がRTSでバッファの3/4が空いて通信ドライバがWR5 bit1 1=/RTS is Lowにしたとき
   196:   //    直ちにSCCの受信を再開する。
   197:   //!!! 反応が速すぎると誤動作するドライバがあるかも?
   198:   //  フロー制御の設定はどこにも接続されていないときも有効でなければならない
   199:   public static boolean trmAUXFlowControlRTS;  //true=AUXのフロー制御はRTS。接続するときに使うので接続していなくても有効
   200:   public static boolean trmAUXFlowControlXON;  //true=AUXのフロー制御はXON。接続するときに使うので接続していなくても有効
   201:   public static boolean trmAUXNotReceiving;  //false=受信可($11=XONまたは1=/RTS is Low),true=受信不可($13=XOFFまたは0=/RTS is High)
   202: 
   203:   //AUX受信データバッファ
   204:   public static int trmAUXDataBuffer;  //0xffでマスクしておくこと
   205:   public static boolean trmAUXDataAvailable;  //ReadCommandのbit0:Rx Character Available。DataBufferの更新でセット、ReadDataでクリア
   206: 
   207:   //data = trmAUXReadData ()
   208:   //  AUXリードデータ
   209:   public static int trmAUXReadData () {
   210:     trmAUXDataAvailable = false;
   211:     return trmAUXDataBuffer;
   212:   }  //trmAUXReadData
   213: 
   214:   //trmAUXWriteData (data)
   215:   //  AUXライトデータ
   216:   public static void trmAUXWriteData (int data) {
   217:     int col = 1;
   218:     int row = trmColToRow[col];
   219:     if (row < 0) {
   220:       return;
   221:     }
   222:     data &= 0xff;  //!!! ビット長
   223:     if (trmAUXFlowControlXON) {
   224:       if (data == 0x11) {  //$11=XON
   225:         trmAUXNotReceiving = false;  //受信可
   226:         return;
   227:       } else if (data == 0x13) {  //$13=XOFF
   228:         trmAUXNotReceiving = true;  //受信不可
   229:         return;
   230:       }
   231:     }
   232:     trmConnectionArray[trmCols * row + col - 1].col2rowQueue.write (data);  //AUX→Terminal/SerialPort
   233:     //AUX送信割り込み
   234:     trmAUXSendEmpty = false;  //送信バッファ空フラグをクリア
   235:     TickerQueue.tkqAdd (trmAUXSendTicker, XEiJ.mpuClockTime + Z8530.scc1aInterval);  //現在時刻+間隔の時刻に送信ティッカーを設定
   236:   }  //trmAUXWriteData
   237: 
   238:   //trmAUXSetNotReceiving (notReceiving)
   239:   //  notReceiving  false  WR5 bit1 1=/RTS is Low   受信可
   240:   //                true   WR5 bit1 0=/RTS is High  受信不可
   241:   public static void trmAUXSetNotReceiving (boolean notReceiving) {
   242:     trmAUXNotReceiving = notReceiving;
   243:   }  //trmAUXSetNotReceiving
   244: 
   245:   //command = trmAUXReadCommand ()
   246:   //  リードコマンド
   247:   //  bit5  CTS 送信許可
   248:   //  bit3  DCD キャリア検出
   249:   //  bit2  Tx Buffer Empty
   250:   //  bit0  Rx Character Available
   251:   public static int trmAUXReadCommand () {
   252:     return (trmAUXConnection == null ?
   253:             (0 << 5 |
   254:              0 << 3 |
   255:              0 << 2 |
   256:              0 << 0) :
   257:             (1 << 5 |  //CTS。キューは容量無制限なので常に1
   258:              1 << 3 |  //DCD。常に1
   259:              (trmAUXSendEmpty ? 1 << 2 : 0 << 2) |  //Tx Buffer Empty
   260:              (trmAUXDataAvailable ? 1 << 0 : 0 << 0)));  //Rx Character Available
   261:   }  //trmAUXReadCommand
   262: 
   263:   //AUX送信ティッカー
   264:   //  送信
   265:   //    データをキューに追加
   266:   //    送信ティッカーを消去
   267:   //    送信バッファ空フラグをクリア
   268:   //    現在時刻+間隔の時刻に送信ティッカーを設定
   269:   //  送信ティッカー
   270:   //    送信バッファ空フラグをセット
   271:   //    送信割り込みが許可されているとき
   272:   //      送信割り込み発生
   273:   public static boolean trmAUXSendEmpty;  //true=送信バッファ空
   274:   public static final TickerQueue.Ticker trmAUXSendTicker = new TickerQueue.Ticker () {
   275:     @Override protected void tick () {
   276:       trmAUXSendEmpty = true;  //送信バッファ空フラグをセット
   277:       if (Z8530.scc1aSendMask != 0) {  //送信割り込みが許可されているとき
   278:         Z8530.scc1aSendRR3 = Z8530.SCC_1A_SEND_MASK;  //送信割り込み発生
   279:         Z8530.scc1aSendRequest = Z8530.SCC_1A_SEND_MASK;
   280:         XEiJ.mpuIRR |= XEiJ.MPU_SCC_INTERRUPT_MASK;
   281:       }
   282:     }  //tick
   283:   };  //trmAUXSendTicker
   284: 
   285:   //AUXポーリングティッカー
   286:   public static Connection trmAUXConnection;  //?→AUXの接続。null=なし
   287:   public static final TickerQueue.Ticker trmAUXTicker = new TickerQueue.Ticker () {
   288:     @Override protected void tick () {
   289:       long interval = XEiJ.TMR_FREQ / 1000 * 1;  //1ms
   290:       ByteQueue queue = trmAUXConnection.row2colQueue;  //?→AUX キュー
   291:       if (trmAUXConnection.row2colReset) {  //?→AUX リセット
   292:         trmAUXConnection.row2colReset = false;
   293:         queue.clear ();
   294:       } else if (!trmAUXNotReceiving &&  //受信可で
   295:                  queue.used () != 0) {  //キューが空でないとき
   296:         trmAUXDataBuffer = queue.read ();  //キューから読み出してDataBufferへ書き込む
   297:         trmAUXDataAvailable = true;  //DataBufferは有効
   298:         if (Z8530.scc1aReceiveMask != 0 &&  //割り込みが許可されていて
   299:             Z8530.scc1aReceiveRR3 == 0) {  //割り込み発生からIUSリセットまでではないとき
   300:           Z8530.scc1aReceiveRR3 = Z8530.SCC_1A_RECEIVE_MASK;  //割り込み発生
   301:           Z8530.scc1aReceiveRequest = Z8530.SCC_1A_RECEIVE_MASK;
   302:           XEiJ.mpuIRR |= XEiJ.MPU_SCC_INTERRUPT_MASK;
   303:         }
   304:         interval = Z8530.scc1aInterval;
   305:       }
   306:       TickerQueue.tkqAdd (trmAUXTicker, XEiJ.mpuClockTime + interval);
   307:     }  //tick
   308:   };  //trmAUXTicker
   309: 
   310:   public static int trmRSDRV202Head;  //RSDRV.SYS 2.02の先頭アドレス
   311:   public static int trmTMSIO031Head;  //tmsio.x 0.31の先頭アドレス
   312:   public static int trmBSIO021Head;  //bsio.x 0.21の先頭アドレス
   313: 
   314:   //trmAUXFlowControlTicker
   315:   //  フロー制御をSerialPortに反映させる
   316:   //  ボーレートジェネレータが動き始めるとき通信設定をSerialPortに反映させるが、
   317:   //  その時点でワークエリアが更新されていないためフロー制御を反映させることができない
   318:   //  フロー制御だけ少し遅れて反映させることにする
   319:   //  遅らせすぎると最初のデータに間に合わない可能性がある
   320:   //  バッファが一杯になるまではフロー制御は行われないと仮定すれば間に合わなくても問題ないかも知れない
   321:   public static final TickerQueue.Ticker trmAUXFlowControlTicker = new TickerQueue.Ticker () {
   322:     @Override protected void tick () {
   323:       int set232c = MC68060.mmuPeekLongData (0x04c0, 1);  //IOCS _SET232Cベクタ
   324:       int modeAddress = (0x00fc0000 <= set232c && set232c < 0x01000000 ? 0x0926 :  //IPLROM
   325:                          set232c == trmRSDRV202Head + 0x03ba ? trmRSDRV202Head + 0x0ab2 :  //RSDRV.SYS 2.02
   326:                          set232c == trmTMSIO031Head + 0x0210 ? trmTMSIO031Head + 0x0a42 :  //tmsio.x 0.31
   327:                          set232c == trmBSIO021Head + 0x013A ? trmBSIO021Head + 0x074a :  //bsio.x 0.21
   328:                          0);  //不明
   329:       if (modeAddress == 0) {
   330:         return;
   331:       }
   332:       int mode = MC68060.mmuPeekWordZeroData (modeAddress, 1);  //通信設定
   333:       if (mode == 0x0000 || mode == 0xffff) {
   334:         return;
   335:       }
   336:       boolean rts = (mode & 0x0080) != 0;
   337:       boolean xon = !rts && (mode & 0x0200) != 0;
   338:       if (trmAUXFlowControlRTS == rts &&
   339:           trmAUXFlowControlXON == xon) {
   340:         return;
   341:       }
   342:       trmAUXFlowControlRTS = rts;
   343:       trmAUXFlowControlXON = xon;
   344:       if (false) {
   345:         System.out.printf ("flowcontrol=%s\n", rts ? "rts" : xon ? "xon" : "none");
   346:       }
   347:       int row = trmColToRow[1];
   348:       SerialPort port = row < 1 ? null : trmPortArray[row - 1];
   349:       if (port != null) {
   350:         port.setFlowControl (rts ? (SerialPort.FLOW_CONTROL_RTS_ENABLED |
   351:                                     SerialPort.FLOW_CONTROL_CTS_ENABLED) :
   352:                              xon ? (SerialPort.FLOW_CONTROL_XONXOFF_IN_ENABLED |
   353:                                     SerialPort.FLOW_CONTROL_XONXOFF_OUT_ENABLED) :
   354:                              SerialPort.FLOW_CONTROL_DISABLED);
   355:       }
   356:     }  //tick
   357:   };  //trmAUXFlowControlTicker
   358: 
   359:   //trmAUXReset ()
   360:   //  AUXリセット
   361:   public static void trmAUXReset () {
   362:     if (trmAUXConnection != null) {  //接続しているとき
   363:       TickerQueue.tkqRemove (trmAUXFlowControlTicker);
   364:       //キューをクリアする
   365:       trmAUXConnection.row2colReset = true;
   366:       trmAUXConnection.col2rowReset = true;
   367:     }
   368:   } //trmAUXReset
   369: 
   370: 
   371:   public static Connection[] trmConnectionArray;  //Connectionの配列
   372:   public static Box trmConnectionBox;  //Connectionのboxを入れる箱
   373:   public static ActionListener trmConnectionListener;  //ConnectionのcheckBoxのactionListener
   374:   public static boolean trmRefreshEnabled;  //更新ボタンは有効か
   375:   public static JButton trmRefreshButton;  //更新ボタン
   376:   //通信設定
   377:   public static boolean trmSettingsEnabled;  //通信設定は有効か
   378:   public static String[] trmBaudRateArray;  //ボーレートの選択肢
   379:   public static int trmBaudRateIndex;  //ボーレートのインデックス
   380:   public static String[] trmDataBitsArray;  //データビットの選択肢
   381:   public static int trmDataBitsIndex;  //データビットのインデックス
   382:   public static String[] trmParityArray;  //パリティの選択肢
   383:   public static int trmParityIndex;  //パリティのインデックス
   384:   public static String[] trmStopBitsArray;  //ストップビットの選択肢
   385:   public static int trmStopBitsIndex;  //ストップビットのインデックス
   386:   public static String[] trmFlowControlArray;  //フロー制御の選択肢
   387:   public static int trmFlowControlIndex;  //フロー制御のインデックス
   388:   public static JComboBox<String> trmBaudRateComboBox;  //ボーレート
   389:   public static JComboBox<String> trmDataBitsComboBox;  //データビット
   390:   public static JComboBox<String> trmParityComboBox;  //パリティ
   391:   public static JComboBox<String> trmStopBitsComboBox;  //ストップビット
   392:   public static JComboBox<String> trmFlowControlComboBox;  //フロー制御
   393:   //ファイル転送
   394:   public static boolean trmSendEnabled;  //送信ボタンは有効か
   395:   public static JButton trmSendButton;  //送信ボタン
   396:   public static JFileChooser trmSendFileChooser;  //送信ダイアログのファイルチューザー
   397:   public static JDialog trmSendDialog;  //送信ダイアログ
   398:   public static SendThread trmSendThread;  //送信スレッド
   399:   //追加ポート
   400:   public static JTextField trmAdditionalTextField;
   401: 
   402:   //trmInitConnection ()
   403:   //  接続を初期化する
   404:   public static void trmInitConnection () {
   405:     //通信設定
   406:     trmBaudRateArray = new String[] { "75", "150", "300", "600", "1200", "2400", "4800", "9600", "19200", "31250", "38400", "57600", "76800", "115200" };
   407:     trmBaudRateIndex = 10;
   408:     trmDataBitsArray = new String[] { "B5", "B6", "B7", "B8" };
   409:     trmDataBitsIndex = 3;
   410:     trmParityArray = new String[] { "PN", "PO", "PE" };
   411:     trmParityIndex = 0;
   412:     trmStopBitsArray = new String[] { "S1", "S1.5", "S2" };
   413:     trmStopBitsIndex = 0;
   414:     trmFlowControlArray = new String[] { "NONE", "XON", "RTS" };
   415:     trmFlowControlIndex = 2;
   416:     trmBaudRateComboBox = null;
   417:     trmDataBitsComboBox = null;
   418:     trmParityComboBox = null;
   419:     trmStopBitsComboBox = null;
   420:     trmFlowControlComboBox = null;
   421:     //通信設定を復元する
   422:     for (String keyword : Settings.sgsGetString ("terminalsettings").split ("/")) {
   423:       for (int i = 0; i < trmBaudRateArray.length; i++) {
   424:         if (trmBaudRateArray[i].equals (keyword)) {
   425:           trmBaudRateIndex = i;
   426:           break;
   427:         }
   428:       }
   429:       for (int i = 0; i < trmDataBitsArray.length; i++) {
   430:         if (trmDataBitsArray[i].equals (keyword)) {
   431:           trmDataBitsIndex = i;
   432:           break;
   433:         }
   434:       }
   435:       for (int i = 0; i < trmParityArray.length; i++) {
   436:         if (trmParityArray[i].equals (keyword)) {
   437:           trmParityIndex = i;
   438:           break;
   439:         }
   440:       }
   441:       for (int i = 0; i < trmStopBitsArray.length; i++) {
   442:         if (trmStopBitsArray[i].equals (keyword)) {
   443:           trmStopBitsIndex = i;
   444:           break;
   445:         }
   446:       }
   447:       for (int i = 0; i < trmFlowControlArray.length; i++) {
   448:         if (trmFlowControlArray[i].equals (keyword)) {
   449:           trmFlowControlIndex = i;
   450:           break;
   451:         }
   452:       }
   453:     }  //for keyword
   454:     //ファイル転送
   455:     trmSendButton = null;
   456:     trmSendFileChooser = null;
   457:     trmSendDialog = null;
   458:     trmSendThread = null;
   459:     //SerialPort
   460:     trmPortArray = new SerialPort[0];
   461:     trmNumberOfPorts = 0;
   462:     //行
   463:     trmRows = 1;
   464:     trmRowName = new String[1];
   465:     trmRowName[0] = "Terminal";
   466:     trmRowToCol = new int[1];
   467:     Arrays.fill (trmRowToCol, -1);
   468:     //AUX*
   469:     trmNumberOfAUXs = 0;
   470:     //列
   471:     trmCols = 1;
   472:     trmColName = new String[1];
   473:     trmColName[0] = "Terminal";
   474:     trmColToRow = new int[1];
   475:     Arrays.fill (trmColToRow, -1);
   476:     //追加ポート
   477:     {
   478:       String text = Settings.sgsGetString ("additionalport");
   479:       try {
   480:         text = URLDecoder.decode (text, "UTF-8");
   481:       } catch (UnsupportedEncodingException uee) {
   482:         text = "";
   483:       }
   484:       trmAdditionalTextField = ComponentFactory.createTextField (text, 30);
   485:     }
   486:     //接続
   487:     trmConnectionArray = new Connection[0];
   488:     trmConnectionBox = ComponentFactory.createVerticalBox (
   489:       Box.createVerticalGlue (),
   490:       ComponentFactory.createHorizontalBox (
   491:         Multilingual.mlnText (
   492:           ComponentFactory.createLabel ("Additional port "),
   493:           "ja", "追加ポート "
   494:           ),
   495:         trmAdditionalTextField,
   496:         Box.createHorizontalGlue ()
   497:         )
   498:       );
   499:     trmConnectionListener = new ActionListener () {
   500:       @Override public void actionPerformed (ActionEvent ae) {
   501:         String command = ae.getActionCommand ();
   502:         for (Connection connection : trmConnectionArray) {
   503:           if (connection.text.equals (command)) {
   504:             if (connection.connected) {
   505:               trmDisconnect (connection);
   506:             } else {
   507:               trmConnect (connection);
   508:             }
   509:             break;
   510:           }
   511:         }
   512:       }
   513:     };
   514:     trmRefreshEnabled = false;
   515:     trmRefreshButton = null;
   516:     trmSettingsEnabled = false;
   517:     trmSendEnabled = false;
   518:     //接続を更新する
   519:     trmUpdateConnection ();
   520:     //接続を復元する
   521:     HashSet<String> map = new HashSet<String> ();
   522:     for (String encodedText : Settings.sgsGetString ("rs232cconnection").split ("/")) {
   523:       try {
   524:         map.add (URLDecoder.decode (encodedText, "UTF-8"));
   525:       } catch (UnsupportedEncodingException uee) {
   526:       }
   527:     }
   528:     for (Connection connection : trmConnectionArray) {
   529:       if (map.contains (connection.text)) {
   530:         trmConnect (connection);
   531:       }
   532:     }
   533:   }  //trmInitConnection
   534: 
   535:   //trmTiniConnection ()
   536:   //  接続を後始末する
   537:   public static void trmTiniConnection () {
   538:     //接続を保存する
   539:     {
   540:       StringBuilder sb = new StringBuilder ();
   541:       for (Connection connection : trmConnectionArray) {
   542:         if (connection.connected) {  //接続している
   543:           if (sb.length () != 0) {
   544:             sb.append ('/');
   545:           }
   546:           try {
   547:             sb.append (URLEncoder.encode (connection.text, "UTF-8"));
   548:           } catch (UnsupportedEncodingException uee) {
   549:           }
   550:         }
   551:       }
   552:       Settings.sgsPutString ("rs232cconnection", sb.toString ());
   553:     }
   554:     //追加ポート
   555:     {
   556:       String text = trmAdditionalTextField.getText ();
   557:       try {
   558:         text = URLEncoder.encode (text, "UTF-8");
   559:       } catch (UnsupportedEncodingException uee) {
   560:         text = "";
   561:       }
   562:       Settings.sgsPutString ("additionalport", text);
   563:     }
   564:     //通信設定を保存する
   565:     {
   566:       StringBuilder sb = new StringBuilder ();
   567:       sb.append (trmBaudRateArray[trmBaudRateIndex]);
   568:       sb.append ('/');
   569:       sb.append (trmDataBitsArray[trmDataBitsIndex]);
   570:       sb.append ('/');
   571:       sb.append (trmParityArray[trmParityIndex]);
   572:       sb.append ('/');
   573:       sb.append (trmStopBitsArray[trmStopBitsIndex]);
   574:       sb.append ('/');
   575:       sb.append (trmFlowControlArray[trmFlowControlIndex]);
   576:       Settings.sgsPutString ("terminalsettings", sb.toString ());
   577:     }
   578:     //すべて切断する
   579:     for (Connection connection : trmConnectionArray) {
   580:       trmDisconnect (connection);
   581:     }
   582:   }  //trmTiniConnection
   583: 
   584:   //trmIsConnectionUpdatable ()
   585:   //  接続を更新できるか
   586:   public static boolean trmIsConnectionUpdatable () {
   587:     for (Connection connection : trmConnectionArray) {
   588:       if (connection.row != 0 &&  //SerialPortに接続している
   589:           connection.connected) {  //接続している
   590:         return false;  //更新できない
   591:       }
   592:     }
   593:     return true;  //更新できる
   594:   }  //trmIsConnectionUpdatable
   595: 
   596:   //trmUpdateConnection ()
   597:   //  接続を更新する
   598:   public static void trmUpdateConnection () {
   599:     //更新できないときは何もしない
   600:     if (!trmIsConnectionUpdatable ()) {
   601:       return;
   602:     }
   603:     //SerialPort
   604:     ArrayList<SerialPort> portList = new ArrayList<SerialPort> ();
   605:     for (SerialPort port : SerialPort.getCommPorts ()) {
   606:       int vid = port.getVendorID ();
   607:       int pid = port.getProductID ();
   608:       if (!(vid == PPI.PPI_UKUN_VID && pid == PPI.PPI_UKUN_PID) &&  //じょいぽーとU君ではない
   609:           !(vid == SUK.SUK_VID && pid == SUK.SUK_PID)) {  //すかじーU君改ではない
   610:         portList.add (port);
   611:       }
   612:     }
   613:     for (String descriptor : trmAdditionalTextField.getText ().split (",")) {  //追加ポート
   614:       descriptor = descriptor.trim ();
   615:       if (!descriptor.equals ("")) {
   616:         try {
   617:           SerialPort port = SerialPort.getCommPort (descriptor);
   618:           if (port != null) {
   619:             if (false) {  //既にリストにあるポートを追加できないようにする。getDescriptivePortName()はおそらく適切でない
   620:               for (SerialPort anotherPort : portList) {
   621:                 if (port.getDescriptivePortName ().equals (anotherPort.getDescriptivePortName ())) {  //port.equals(anotherPort)は不可
   622:                   port = null;
   623:                   break;
   624:                 }
   625:               }
   626:             }
   627:             if (port != null) {
   628:               portList.add (port);
   629:             }
   630:           } else {
   631:             System.out.println (descriptor + " not found");
   632:           }
   633:         } catch (SerialPortInvalidPortException spipe) {
   634:           System.out.println (spipe.toString ());
   635:         }
   636:       }
   637:     }
   638:     trmNumberOfPorts = portList.size ();
   639:     trmPortArray = portList.toArray (new SerialPort[trmNumberOfPorts]);
   640:     //行
   641:     trmRows = 1 + trmNumberOfPorts;
   642:     trmRowName = new String[trmRows];
   643:     trmRowName[0] = "Terminal";
   644:     for (int row = 1; row < trmRows; row++) {
   645:       SerialPort port = trmPortArray[row - 1];
   646:       trmRowName[row] = port.getSystemPortName () + "(" + port.getPortDescription () + ")";
   647:     }
   648:     trmRowToCol = new int[trmRows];
   649:     Arrays.fill (trmRowToCol, -1);
   650:     //AUX*
   651:     trmNumberOfAUXs = 1;
   652:     //列
   653:     trmCols = 1 + trmNumberOfAUXs;
   654:     trmColName = new String[trmCols];
   655:     trmColName[0] = "Terminal";
   656:     for (int col = 1; col < trmCols; col++) {
   657:       trmColName[col] = col == 1 ? "AUX" : "AUX" + col;
   658:     }
   659:     trmColToRow = new int[trmCols];
   660:     Arrays.fill (trmColToRow, -1);
   661:     //接続
   662:     for (int index = trmConnectionArray.length - 1; 0 <= index; index--) {
   663:       trmConnectionBox.remove (index);
   664:     }
   665:     trmConnectionArray = new Connection[trmCols * trmRows - 1];
   666:     for (int row = 0; row < trmRows; row++) {
   667:       for (int col = 0; col < trmCols; col++) {
   668:         if (col == 0 && row == 0) {
   669:           continue;
   670:         }
   671:         Connection connection = new Connection ();
   672:         connection.row = row;
   673:         connection.col = col;
   674:         connection.index = trmCols * row + col - 1;
   675:         connection.text = trmRowName[row] + " ⇔ " + trmColName[col];
   676:         connection.connected = false;
   677:         connection.checkBox =
   678:           ComponentFactory.createCheckBox (connection.connected, connection.text, trmConnectionListener);
   679:         connection.box =
   680:           ComponentFactory.createHorizontalBox (
   681:             connection.checkBox,
   682:             Box.createHorizontalGlue ());
   683:         trmConnectionArray[connection.index] = connection;
   684:         trmConnectionBox.add (connection.box, connection.index);
   685:       }  //for col
   686:     }  //for row
   687:     trmUpdateComponents ();
   688:     trmConnectionBox.validate ();
   689:   }  //trmUpdateConnection
   690: 
   691:   //trmUpdateComponents ()
   692:   //  接続できないConnectionのcheckBoxを無効にする
   693:   //  SerialPortに接続しているConnectionがあるとき更新ボタンを無効にする
   694:   //  Terminal-SerialPortがないとき通信設定を表示しない
   695:   public static void trmUpdateComponents () {
   696:     boolean updatable = true;  //更新できる
   697:     boolean configurable = false;  //設定できない
   698:     boolean transferable = false;  //転送できない
   699:     for (Connection connection : trmConnectionArray) {
   700:       if (connection.connected) {  //接続している
   701:         connection.checkBox.setEnabled (true);  //切断できる
   702:         if (0 < connection.row) {  //SerialPortを接続している
   703:           updatable = false;  //更新できない
   704:           if (connection.col == 0) {  //TerminalとSerialPortを接続している
   705:             configurable = true;  //設定できる
   706:           }
   707:         }
   708:         if (connection.row == 0 ||
   709:             connection.col == 0) {  //Terminalを接続している
   710:           transferable = true;  //転送できる
   711:         }
   712:       } else {  //接続していない
   713:         connection.checkBox.setEnabled (trmIsConnectable (connection));  //接続できるときだけ有効
   714:       }
   715:     }
   716:     trmRefreshEnabled = updatable;
   717:     if (trmRefreshButton != null) {
   718:       trmRefreshButton.setEnabled (updatable);
   719:     }
   720:     trmSettingsEnabled = configurable;
   721:     trmSendEnabled = transferable;
   722:     if (trmBaudRateComboBox != null) {
   723:       trmBaudRateComboBox.setEnabled (configurable);
   724:       trmDataBitsComboBox.setEnabled (configurable);
   725:       trmParityComboBox.setEnabled (configurable);
   726:       trmStopBitsComboBox.setEnabled (configurable);
   727:       trmFlowControlComboBox.setEnabled (configurable);
   728:       trmSendButton.setEnabled (transferable);
   729:     }
   730:   }  //trmUpdateComponents
   731: 
   732:   //connectable = trmIsConnectable (connection)
   733:   //  接続できるか
   734:   public static boolean trmIsConnectable (Connection connection) {
   735:     if (!connection.connected) {  //自分が接続していないとき
   736:       for (Connection connection2 : trmConnectionArray) {
   737:         if (connection != connection2 &&  //自分以外で
   738:             connection2.connected &&  //接続していて
   739:             (((connection.row == 0 || connection.col == 0) &&
   740:               (connection2.row == 0 || connection2.col == 0)) ||  //Terminalが衝突しているまたは
   741:              connection.row == connection2.row ||  //SerialPortが衝突しているまたは
   742:              connection.col == connection2.col)) {  //AUX*が衝突しているとき
   743:           return false;  //接続できない
   744:         }
   745:       }
   746:     }
   747:     return true;  //接続できる
   748:   }  //trmIsConnectable
   749: 
   750:   //trmConnect (connection)
   751:   //  接続する
   752:   public static void trmConnect (Connection connection) {
   753:     //接続しているか接続できないときは何もしない
   754:     if (connection.connected ||  //接続している
   755:         !trmIsConnectable (connection)) {  //接続できない
   756:       return;
   757:     }
   758:     //接続する
   759:     connection.connected = true;
   760:     connection.checkBox.setSelected (true);
   761:     trmRowToCol[connection.row] = connection.col;
   762:     trmColToRow[connection.col] = connection.row;
   763:     trmUpdateComponents ();
   764:     //キューを作る
   765:     connection.row2colQueue = new ByteQueue ();
   766:     connection.col2rowQueue = new ByteQueue ();
   767:     if (0 < connection.row) {  //SerialPort→?
   768:       //シリアルポートを開く
   769:       SerialPort port = trmPortArray[connection.row - 1];
   770:       port.openPort ();
   771:       System.out.println (Multilingual.mlnJapanese ?
   772:                           connection.text + " を開きました" :
   773:                           connection.text + " opened");
   774:       port.setComPortTimeouts (SerialPort.TIMEOUT_WRITE_BLOCKING | SerialPort.TIMEOUT_READ_BLOCKING, 0, 0);
   775:       port.setFlowControl (SerialPort.FLOW_CONTROL_DISABLED);
   776:       //通信設定をSerialPortに反映させる
   777:       trmReflectSettings (connection.col);
   778:       //シリアルポートデータリスナーを設定する
   779:       port.addDataListener (connection);
   780:     }
   781:     //ポーリングスレッドを開始する
   782:     connection.polling = true;
   783:     connection.row2colThread = (connection.col == 0 ? connection.new TerminalThread () :  //?→Terminal
   784:                                 null);  //?→AUX
   785:     connection.col2rowThread = (connection.row == 0 ? connection.new TerminalThread () :  //Terminal→?
   786:                                 connection.new SerialPortThread ());  //SerialPort→?
   787:     for (int i = 0; i < 2; i++) {
   788:       Thread thread = (i == 0 ? connection.row2colThread : connection.col2rowThread);
   789:       if (thread != null) {
   790:         thread.start ();
   791:       }
   792:     }
   793:     //ポーリングティッカーを開始する
   794:     if (connection.col == 1) {  //?→AUX
   795:       trmAUXNotReceiving = false;  //受信可
   796:       trmAUXDataBuffer = 0;
   797:       trmAUXDataAvailable = false;
   798:       trmAUXConnection = connection;
   799:       TickerQueue.tkqAdd (trmAUXTicker, XEiJ.mpuClockTime + XEiJ.TMR_FREQ / 1000 * 1);  //1ms
   800:       //AUX送信割り込み
   801:       trmAUXSendEmpty = true;  //送信バッファ空フラグをセット
   802:     }
   803:   }  //trmConnect
   804: 
   805:   //trmDisconnect (connection)
   806:   //  切断する
   807:   public static void trmDisconnect (Connection connection) {
   808:     //接続していないときは何もしない
   809:     if (!connection.connected) {
   810:       return;
   811:     }
   812:     //切断する
   813:     connection.connected = false;
   814:     connection.checkBox.setSelected (connection.connected);
   815:     trmRowToCol[connection.row] = -1;
   816:     trmColToRow[connection.col] = -1;
   817:     trmUpdateComponents ();
   818:     //ポーリングティッカーを終了する
   819:     if (trmAUXConnection != null) {
   820:       TickerQueue.tkqRemove (trmAUXTicker);
   821:       trmAUXConnection = null;
   822:     }
   823:     //ポーリングスレッドを停止する
   824:     connection.polling = false;
   825:     for (int i = 0; i < 2; i++) {
   826:       Thread thread = (i == 0 ? connection.row2colThread : connection.col2rowThread);
   827:       if (thread != null) {
   828:         connection.row2colThread = null;
   829:         if (thread.isAlive ()) {  //スレッドがある
   830:           thread.interrupt ();  //割り込む
   831:           try {
   832:             thread.join ();  //止まるまで待つ
   833:           } catch (InterruptedException ie) {
   834:           }
   835:         }
   836:       }
   837:     }
   838:     //AUX送信割り込み
   839:     if (connection.col == 1) {  //?→AUX
   840:       TickerQueue.tkqRemove (trmAUXSendTicker);  //送信ティッカーを消去
   841:     }
   842:     if (0 < connection.row) {  //SerialPort
   843:       SerialPort port = trmPortArray[connection.row - 1];
   844:       //シリアルポートデータリスナーを削除する
   845:       port.removeDataListener ();
   846:       //シリアルポートを閉じる
   847:       port.closePort ();
   848:       System.out.println (Multilingual.mlnJapanese ?
   849:                           connection.text + " を閉じました" :
   850:                           connection.text + " closed");
   851:     }
   852:     //キューを消す
   853:     connection.row2colQueue.clear ();
   854:     connection.col2rowQueue.clear ();
   855:     connection.row2colQueue = null;
   856:     connection.col2rowQueue = null;
   857:   }  //trmDisconnect
   858: 
   859:   //trmSetBaudRate (index)
   860:   //  ボーレートを設定する
   861:   public static void trmSetBaudRate (int index) {
   862:     if (0 <= index && index < trmBaudRateArray.length) {
   863:       trmBaudRateIndex = index;
   864:       trmReflectSettings (0);
   865:     }
   866:   }  //trmSetBaudRate
   867: 
   868:   //trmSetDataBits (index)
   869:   //  データビットを設定する
   870:   public static void trmSetDataBits (int index) {
   871:     if (0 <= index && index < trmDataBitsArray.length) {
   872:       trmDataBitsIndex = index;
   873:       trmReflectSettings (0);
   874:     }
   875:   }  //trmSetDataBits
   876: 
   877:   //trmSetParity (index)
   878:   //  パリティを設定する
   879:   public static void trmSetParity (int index) {
   880:     if (0 <= index && index < trmParityArray.length) {
   881:       trmParityIndex = index;
   882:       trmReflectSettings (0);
   883:     }
   884:   }  //trmSetParity
   885: 
   886:   //trmSetStopBits (index)
   887:   //  ストップビットを設定する
   888:   public static void trmSetStopBits (int index) {
   889:     if (0 <= index && index < trmStopBitsArray.length) {
   890:       trmStopBitsIndex = index;
   891:       trmReflectSettings (0);
   892:     }
   893:   }  //trmSetStopBits
   894: 
   895:   //trmSetFlowControl (index)
   896:   //  フロー制御を設定する
   897:   public static void trmSetFlowControl (int index) {
   898:     if (0 <= index && index < trmFlowControlArray.length) {
   899:       trmFlowControlIndex = index;
   900:       trmReflectSettings (0);
   901:     }
   902:   }  //trmSetFlowControl
   903: 
   904:   //trmReflectSettings (col)
   905:   //  通信設定をSerialPortに反映させる
   906:   public static void trmReflectSettings (int col) {
   907:     int row = trmColToRow[col];
   908:     SerialPort port = row <= 0 ? null : trmPortArray[row - 1];
   909:     if (col == 0) {  //Terminal
   910:       String baudRate = trmBaudRateArray[trmBaudRateIndex];
   911:       String dataBits = trmDataBitsArray[trmDataBitsIndex];
   912:       String stopBits = trmStopBitsArray[trmStopBitsIndex];
   913:       String parity = trmParityArray[trmParityIndex];
   914:       if (port != null) {
   915:         port.setComPortParameters (Integer.parseInt (baudRate, 10),
   916:                                    Integer.parseInt (dataBits.substring (1), 10),
   917:                                    stopBits.equals ("S1.5") ? SerialPort.ONE_POINT_FIVE_STOP_BITS :
   918:                                    stopBits.equals ("S2") ? SerialPort.TWO_STOP_BITS : SerialPort.ONE_STOP_BIT,
   919:                                    parity.equals ("PO") ? SerialPort.ODD_PARITY :
   920:                                    parity.equals ("PE") ? SerialPort.EVEN_PARITY : SerialPort.NO_PARITY);
   921:       }
   922:     } else if (col == 1) {  //AUX
   923:       double rate = Z8530.sccFreq / (double) ((Z8530.scc1aBaudRateGen + 2) << (Z8530.scc1aClockModeShift + 1));
   924:       double bits = (1.0 +  //start
   925:                      (Z8530.scc1aRxBits == 0 ? 5.0 :
   926:                       Z8530.scc1aRxBits == 1 ? 7.0 :
   927:                       Z8530.scc1aRxBits == 2 ? 6.0 : 8.0) +  //data
   928:                      ((Z8530.scc1aParity & 1) == 0 ? 0.0 : 1.0) +  //parity
   929:                      (Z8530.scc1aStop == 0 ? 0.0 :
   930:                       Z8530.scc1aStop == 1 ? 1.0 :
   931:                       Z8530.scc1aStop == 2 ? 1.5 : 2.0));  //stop
   932:       double interval = bits / rate;
   933:       if (false) {
   934:         System.out.printf ("%08x baudrate=%.3fbps interval=%.3fus\n", XEiJ.regPC0, rate, interval * 1e+6);
   935:       }
   936:       Z8530.scc1aInterval = Math.round (interval * (double) XEiJ.TMR_FREQ);
   937:       //
   938:       if (port != null) {
   939:         port.setComPortParameters ((int) Math.round (rate),
   940:                                    Z8530.scc1aRxBits == 0b00 ? 5 :
   941:                                    Z8530.scc1aRxBits == 0b01 ? 7 :
   942:                                    Z8530.scc1aRxBits == 0b10 ? 6 : 8,
   943:                                    Z8530.scc1aStop == 0b10 ? SerialPort.ONE_POINT_FIVE_STOP_BITS :
   944:                                    Z8530.scc1aStop == 0b11 ? SerialPort.TWO_STOP_BITS : SerialPort.ONE_STOP_BIT,
   945:                                    Z8530.scc1aParity == 0b01 ? SerialPort.ODD_PARITY :
   946:                                    Z8530.scc1aParity == 0b11 ? SerialPort.EVEN_PARITY : SerialPort.NO_PARITY);
   947:       }
   948:       //
   949:       //フロー制御をSerialPortに反映させる
   950:       TickerQueue.tkqAdd (trmAUXFlowControlTicker, XEiJ.mpuClockTime + XEiJ.TMR_FREQ * 500 / 1000000);  //500us後
   951:     } else {  //AUX2~
   952:       //!!! 未対応
   953:     }
   954:   }  //trmReflectSettings
   955: 
   956: 
   957:   //trmInit ()
   958:   //  ターミナルウインドウを初期化する
   959:   public static void trmInit () {
   960:     trmFrame = null;
   961:     trmBoard = null;
   962:     trmPopupMenu = null;
   963:     trmPopupCutMenuItem = null;
   964:     trmPopupCopyMenuItem = null;
   965:     trmPopupPasteMenuItem = null;
   966:     trmPopupSelectAllMenuItem = null;
   967:     trmPopupSendCtrlCMenuItem = null;
   968:     trmOutputBuilder = new StringBuilder ();
   969:     trmOutputEnd = 0;
   970:     trmOutputSJIS1 = 0;
   971:     //
   972:     trmInitConnection ();
   973:     trmReset ();
   974:   }
   975: 
   976:   public static void trmReset () {
   977:     trmRSDRV202Head = 0;
   978:     trmTMSIO031Head = 0;
   979:     trmBSIO021Head = 0;
   980:     trmAUXFlowControlXON = false;
   981:     trmAUXFlowControlRTS = false;
   982:   }
   983: 
   984:   //trmTini ()
   985:   //  後始末
   986:   public static void trmTini () {
   987:     trmTiniConnection ();
   988:   }  //trmTini
   989: 
   990:   //trmMake ()
   991:   //  ターミナルウインドウを作る
   992:   //  ここでは開かない
   993:   public static void trmMake () {
   994: 
   995:     //テキストエリア
   996:     trmBoard = ComponentFactory.createScrollTextArea (
   997:       trmOutputBuilder.toString (),  //作る前に出力されていた文字列を設定する
   998:       650, 350,
   999:       true);
  1000:     trmOutputBuilder = null;  //これはもういらない
  1001:     trmBoard.setUnderlineCursorOn (true);
  1002:     trmBoard.setLineWrap (true);  //行を折り返す
  1003:     trmBoard.addDocumentListener (new DocumentListener () {
  1004:       @Override public void changedUpdate (DocumentEvent de) {
  1005:       }
  1006:       @Override public void insertUpdate (DocumentEvent de) {
  1007:         if (de.getOffset () < trmOutputEnd) {
  1008:           trmOutputEnd += de.getLength ();  //出力された文字列の末尾を調整する
  1009:         }
  1010:       }
  1011:       @Override public void removeUpdate (DocumentEvent de) {
  1012:         if (de.getOffset () < trmOutputEnd) {
  1013:           trmOutputEnd -= Math.min (de.getLength (), trmOutputEnd - de.getOffset ());  //出力された文字列の末尾を調整する
  1014:         }
  1015:       }
  1016:     });
  1017:     trmBoard.addKeyListener (new KeyAdapter () {
  1018:       @Override public void keyPressed (KeyEvent ke) {
  1019:         int keyCode = ke.getKeyCode ();
  1020:         if (keyCode == KeyEvent.VK_ENTER) {  //ENTERキーが押された
  1021:           ke.consume ();  //ENTERキーをキャンセルする
  1022:           trmEnter ();  //ENTERキーを処理する
  1023:         } else if (keyCode == KeyEvent.VK_PAUSE) {  //Pauseキーが押された
  1024:           ke.consume ();  //Pauseキーをキャンセルする
  1025:           trmSendString ("\u0003");  //^Cを送信する
  1026:         }
  1027:       }
  1028:     });
  1029: 
  1030:     //ポップアップメニュー
  1031:     ActionListener popupActionListener = new ActionListener () {
  1032:       @Override public void actionPerformed (ActionEvent ae) {
  1033:         switch (ae.getActionCommand ()) {
  1034:         case "Cut":  //切り取り
  1035:           trmCut ();
  1036:           break;
  1037:         case "Copy":  //コピー
  1038:           trmCopy ();
  1039:           break;
  1040:         case "Paste":  //貼り付け
  1041:           trmPaste ();
  1042:           break;
  1043:         case "Select All":  //すべて選択
  1044:           trmSelectAll ();
  1045:           break;
  1046:         case "Send ^C":  //^C 送信
  1047:           trmSendString ("\u0003");  //^Cを送信する
  1048:           break;
  1049:         }
  1050:       }
  1051:     };
  1052:     trmPopupMenu = ComponentFactory.createPopupMenu (
  1053:       trmPopupCutMenuItem = Multilingual.mlnText (
  1054:         ComponentFactory.createMenuItem ("Cut", 'T', popupActionListener),
  1055:         "ja", "切り取り"),
  1056:       trmPopupCopyMenuItem = Multilingual.mlnText (
  1057:         ComponentFactory.createMenuItem ("Copy", 'C', popupActionListener),
  1058:         "ja", "コピー"),
  1059:       trmPopupPasteMenuItem = Multilingual.mlnText (
  1060:         ComponentFactory.createMenuItem ("Paste", 'P', popupActionListener),
  1061:         "ja", "貼り付け"),
  1062:       ComponentFactory.createHorizontalSeparator (),
  1063:       trmPopupSelectAllMenuItem = Multilingual.mlnText (
  1064:         ComponentFactory.createMenuItem ("Select All", 'A', popupActionListener),
  1065:         "ja", "すべて選択"),
  1066:       ComponentFactory.createHorizontalSeparator (),
  1067:       trmPopupSendCtrlCMenuItem = Multilingual.mlnText (
  1068:         ComponentFactory.createMenuItem ("Send ^C", popupActionListener),
  1069:         "ja", "^C 送信")
  1070:       );
  1071:     trmBoard.addMouseListener (new MouseAdapter () {
  1072:       @Override public void mousePressed (MouseEvent me) {
  1073:         trmShowPopup (me);
  1074:       }
  1075:       @Override public void mouseReleased (MouseEvent me) {
  1076:         trmShowPopup (me);
  1077:       }
  1078:     });
  1079: 
  1080:     //アクションリスナー
  1081:     ActionListener listener = new ActionListener () {
  1082:       @Override public void actionPerformed (ActionEvent ae) {
  1083:         Object source = ae.getSource ();
  1084:         String command = ae.getActionCommand ();
  1085:         switch (command) {
  1086:         case "Refresh":
  1087:           trmUpdateConnection ();
  1088:           break;
  1089:         case "Baud rate":
  1090:           trmSetBaudRate (((JComboBox) source).getSelectedIndex ());
  1091:           break;
  1092:         case "Data bits":
  1093:           trmSetDataBits (((JComboBox) source).getSelectedIndex ());
  1094:           break;
  1095:         case "Parity":
  1096:           trmSetParity (((JComboBox) source).getSelectedIndex ());
  1097:           break;
  1098:         case "Stop bits":
  1099:           trmSetStopBits (((JComboBox) source).getSelectedIndex ());
  1100:           break;
  1101:         case "Flow control":
  1102:           trmSetFlowControl (((JComboBox) source).getSelectedIndex ());
  1103:           break;
  1104:         case "Send file":
  1105:           trmSendFile ();
  1106:           break;
  1107:         case "7.3728MHz":
  1108:           Z8530.sccFreq = ((JCheckBox) source).isSelected () ? 7372800.0 : 5000000.0;
  1109:           break;
  1110:         default:
  1111:           System.out.println ("unknown action command " + command);
  1112:         }
  1113:       }
  1114:     };
  1115: 
  1116:     //ボタンとコンボボックス
  1117:     trmRefreshButton =
  1118:       ComponentFactory.setEnabled (
  1119:         Multilingual.mlnText (
  1120:           ComponentFactory.createButton ("Refresh", listener),
  1121:           "ja", "更新"),
  1122:         trmRefreshEnabled);
  1123:     trmBaudRateComboBox =
  1124:       ComponentFactory.setEnabled (
  1125:         Multilingual.mlnToolTipText (
  1126:           ComponentFactory.createComboBox (
  1127:             trmBaudRateIndex, "Baud rate", listener, trmBaudRateArray),
  1128:           "ja", "ボーレート"),
  1129:         trmSettingsEnabled);
  1130:     trmDataBitsComboBox =
  1131:       ComponentFactory.setEnabled (
  1132:         Multilingual.mlnToolTipText (
  1133:           ComponentFactory.createComboBox (
  1134:             trmDataBitsIndex, "Data bits", listener, trmDataBitsArray),
  1135:           "ja", "データビット"),
  1136:         trmSettingsEnabled);
  1137:     trmParityComboBox =
  1138:       ComponentFactory.setEnabled (
  1139:         Multilingual.mlnToolTipText (
  1140:           ComponentFactory.createComboBox (
  1141:             trmParityIndex, "Parity", listener, trmParityArray),
  1142:           "ja", "パリティ"),
  1143:         trmSettingsEnabled);
  1144:     trmStopBitsComboBox =
  1145:       ComponentFactory.setEnabled (
  1146:         Multilingual.mlnToolTipText (
  1147:           ComponentFactory.createComboBox (
  1148:             trmStopBitsIndex, "Stop bits", listener, trmStopBitsArray),
  1149:           "ja", "ストップビット"),
  1150:         trmSettingsEnabled);
  1151:     trmFlowControlComboBox =
  1152:       ComponentFactory.setEnabled (
  1153:         Multilingual.mlnToolTipText (
  1154:           ComponentFactory.createComboBox (
  1155:             trmFlowControlIndex, "Flow control", listener, trmFlowControlArray),
  1156:           "ja", "フロー制御"),
  1157:         trmSettingsEnabled);
  1158:     trmSendButton =
  1159:       ComponentFactory.setEnabled (
  1160:         Multilingual.mlnText (
  1161:           ComponentFactory.createButton ("Send file", listener),
  1162:           "ja", "ファイル送信"),
  1163:         trmSendEnabled);
  1164: 
  1165:     //ウインドウ
  1166:     trmFrame = Multilingual.mlnTitle (
  1167:       ComponentFactory.createRestorableSubFrame (
  1168:         Settings.SGS_TRM_FRAME_KEY,
  1169:         "RS-232C and terminal",
  1170:         null,
  1171:         ComponentFactory.createVerticalSplitPane (
  1172:           ComponentFactory.createVerticalBox (
  1173:             Multilingual.mlnTitledBorder (
  1174:               ComponentFactory.setTitledLineBorder (
  1175:                 ComponentFactory.createHorizontalBox (
  1176:                   Box.createHorizontalStrut (5),
  1177:                   ComponentFactory.createVerticalBox (
  1178:                     Box.createVerticalGlue (),
  1179:                     trmRefreshButton,
  1180:                     Box.createVerticalGlue ()
  1181:                     ),
  1182:                   Box.createHorizontalStrut (10),
  1183:                   trmConnectionBox,
  1184:                   Box.createHorizontalGlue ()
  1185:                   ),
  1186:                 "Connection"),
  1187:               "ja", "接続"),
  1188:             ComponentFactory.createHorizontalBox (
  1189:               Multilingual.mlnTitledBorder (
  1190:                 ComponentFactory.setTitledLineBorder (
  1191:                   ComponentFactory.createHorizontalBox (
  1192:                     Box.createHorizontalStrut (5),
  1193:                     trmBaudRateComboBox,
  1194:                     trmDataBitsComboBox,
  1195:                     trmParityComboBox,
  1196:                     trmStopBitsComboBox,
  1197:                     trmFlowControlComboBox,
  1198:                     Box.createHorizontalStrut (5)
  1199:                     ),  //createHorizontalBox
  1200:                   "Communication Settings"),  //setTitledLineBorder
  1201:                 "ja", "通信設定"),  //mlnTitledBorder
  1202:               Multilingual.mlnTitledBorder (
  1203:                 ComponentFactory.setTitledLineBorder (
  1204:                   ComponentFactory.createHorizontalBox (
  1205:                     Box.createHorizontalStrut (5),
  1206:                     trmSendButton,
  1207:                     Box.createHorizontalStrut (5)
  1208:                     ),  //createHorizontalBox
  1209:                   "Transfer"),  //setTitledLineBorder
  1210:                 "ja", "転送"),  //mlnTitledBorder
  1211:               Box.createHorizontalGlue (),
  1212:               Multilingual.mlnTitledBorder (
  1213:                 ComponentFactory.setTitledLineBorder (
  1214:                   ComponentFactory.createHorizontalBox (
  1215:                     Box.createHorizontalStrut (5),
  1216:                     ComponentFactory.createCheckBox (Z8530.sccFreq == 7372800.0, "7.3728MHz", listener),
  1217:                     Box.createHorizontalStrut (5)
  1218:                     ),  //createHorizontalBox
  1219:                   "Modification"),  //setTitledLineBorder
  1220:                 "ja", "改造")  //mlnTitledBorder
  1221:               )  //createHorizontalBox
  1222:             ),  //createVerticalBox
  1223:           ComponentFactory.createVerticalBox (
  1224:             Multilingual.mlnTitledBorder (
  1225:               ComponentFactory.setTitledLineBorder (trmBoard, "Terminal"),
  1226:               "ja", "ターミナル")
  1227:             )  //createVerticalBox
  1228:           )  //createVerticalSplitPane
  1229:         ),  //createRestorableSubFrame
  1230:       "ja", "RS-232C とターミナル");
  1231: 
  1232:   }  //trmMake()
  1233: 
  1234:   //trmShowPopup (me)
  1235:   //  ポップアップメニューを表示する
  1236:   //  テキストエリアのマウスリスナーが呼び出す
  1237:   public static void trmShowPopup (MouseEvent me) {
  1238:     if (me.isPopupTrigger ()) {
  1239:       //選択範囲があれば切り取りとコピーが有効
  1240:       boolean enableCutAndCopy = XEiJ.clpClipboard != null && trmBoard.getSelectionStart () != trmBoard.getSelectionEnd ();
  1241:       ComponentFactory.setEnabled (trmPopupCutMenuItem, enableCutAndCopy);
  1242:       ComponentFactory.setEnabled (trmPopupCopyMenuItem, enableCutAndCopy);
  1243:       //クリップボードに文字列があれば貼り付けが有効
  1244:       ComponentFactory.setEnabled (trmPopupPasteMenuItem, XEiJ.clpClipboard != null && XEiJ.clpClipboard.isDataFlavorAvailable (DataFlavor.stringFlavor));
  1245:       //クリップボードがあればすべて選択が有効
  1246:       ComponentFactory.setEnabled (trmPopupSelectAllMenuItem, XEiJ.clpClipboard != null);
  1247:       //Terminalが接続していれば^C送信が有効
  1248:       ComponentFactory.setEnabled (trmPopupSendCtrlCMenuItem,
  1249:                                    trmRowToCol[0] == 1 ||  //Terminal→AUX
  1250:                                    0 < trmColToRow[0]);  //Terminal→SerialPort
  1251:       //ポップアップメニューを表示する
  1252:       trmPopupMenu.show (me.getComponent (), me.getX (), me.getY ());
  1253:     }
  1254:   }  //trmShowPopup(MouseEvent)
  1255: 
  1256:   //trmCut ()
  1257:   //  切り取り
  1258:   public static void trmCut () {
  1259:     if (XEiJ.clpClipboard != null) {
  1260:       //選択範囲の文字列をコピーする
  1261:       XEiJ.clpClipboardString = trmBoard.getSelectedText ();
  1262:       try {
  1263:         XEiJ.clpClipboard.setContents (XEiJ.clpStringContents, XEiJ.clpClipboardOwner);
  1264:         XEiJ.clpIsClipboardOwner = true;  //自分がコピーした
  1265:       } catch (Exception e) {
  1266:         return;
  1267:       }
  1268:       //選択範囲の文字列を削除する
  1269:       trmBoard.replaceRange ("", trmBoard.getSelectionStart (), trmBoard.getSelectionEnd ());
  1270:     }
  1271:   }  //trmCut()
  1272: 
  1273:   //trmCopy ()
  1274:   //  コピー
  1275:   public static void trmCopy () {
  1276:     if (XEiJ.clpClipboard != null) {
  1277:       //選択範囲の文字列をコピーする
  1278:       String selectedText = trmBoard.getSelectedText ();
  1279:       if (selectedText != null) {
  1280:         XEiJ.clpClipboardString = selectedText;
  1281:         try {
  1282:           XEiJ.clpClipboard.setContents (XEiJ.clpStringContents, XEiJ.clpClipboardOwner);
  1283:           XEiJ.clpIsClipboardOwner = true;  //自分がコピーした
  1284:         } catch (Exception e) {
  1285:           return;
  1286:         }
  1287:       }
  1288:     }
  1289:   }  //trmCopy()
  1290: 
  1291:   //trmPaste ()
  1292:   //  貼り付け
  1293:   public static void trmPaste () {
  1294:     if (XEiJ.clpClipboard != null) {
  1295:       //クリップボードから文字列を取り出す
  1296:       String string = null;
  1297:       try {
  1298:         string = (String) XEiJ.clpClipboard.getData (DataFlavor.stringFlavor);
  1299:       } catch (Exception e) {
  1300:         return;
  1301:       }
  1302:       //選択範囲の文字列を置換する
  1303:       trmBoard.replaceRange (string, trmBoard.getSelectionStart (), trmBoard.getSelectionEnd ());
  1304:     }
  1305:   }  //trmPaste()
  1306: 
  1307:   //trmSelectAll ()
  1308:   //  すべて選択
  1309:   public static void trmSelectAll () {
  1310:     if (XEiJ.clpClipboard != null) {
  1311:       //すべて選択する
  1312:       trmBoard.selectAll ();
  1313:     }
  1314:   }  //trmSelectAll()
  1315: 
  1316:   //trmStart ()
  1317:   public static void trmStart () {
  1318:     if (RestorableFrame.rfmGetOpened (Settings.SGS_TRM_FRAME_KEY)) {
  1319:       trmOpen ();
  1320:     }
  1321:   }  //trmStart()
  1322: 
  1323:   //trmOpen ()
  1324:   //  ターミナルウインドウを開く
  1325:   public static void trmOpen () {
  1326:     if (trmFrame == null) {
  1327:       trmMake ();
  1328:     }
  1329:     XEiJ.pnlExitFullScreen (false);
  1330:     trmFrame.setVisible (true);
  1331:   }  //trmOpen()
  1332: 
  1333:   //trmPrintSJIS (d)
  1334:   //  SJISで1バイト追加する
  1335:   //  SJISの1バイト目は繰り越して2バイト目が来たときに表示する
  1336:   public static void trmPrintSJIS (int d) {
  1337:     d &= 0xff;
  1338:     if (trmOutputSJIS1 != 0) {  //前回SJISの1バイト目を繰り越した
  1339:       if (0x40 <= d && d != 0x7f && d <= 0xfc) {  //SJISの2バイト目が来た
  1340:         int c = CharacterCode.chrSJISToChar[trmOutputSJIS1 << 8 | d];  //2バイトで変換する
  1341:         if (c != 0) {  //2バイトで変換できた
  1342:           trmPrintChar (c);  //1文字表示する
  1343:         } else {  //2バイトで変換できなかった
  1344:           //2バイトで変換できなかったがSJISの1バイト目と2バイト目であることはわかっているので2バイト分のコードを表示する
  1345:           trmPrintChar ('[');
  1346:           trmPrintChar (XEiJ.fmtHexc (trmOutputSJIS1 >> 4));
  1347:           trmPrintChar (XEiJ.fmtHexc (trmOutputSJIS1 & 15));
  1348:           trmPrintChar (XEiJ.fmtHexc (d >> 4));
  1349:           trmPrintChar (XEiJ.fmtHexc (d & 15));
  1350:           trmPrintChar (']');
  1351:         }
  1352:         trmOutputSJIS1 = 0;
  1353:         return;
  1354:       }
  1355:       //SJISの2バイト目が来なかった
  1356:       //前回繰り越したSJISの1バイト目を吐き出す
  1357:       trmPrintChar ('[');
  1358:       trmPrintChar (XEiJ.fmtHexc (trmOutputSJIS1 >> 4));
  1359:       trmPrintChar (XEiJ.fmtHexc (trmOutputSJIS1 & 15));
  1360:       trmPrintChar (']');
  1361:       trmOutputSJIS1 = 0;
  1362:     }
  1363:     if (0x81 <= d && d <= 0x9f || 0xe0 <= d && d <= 0xef) {  //SJISの1バイト目が来た
  1364:       trmOutputSJIS1 = d;  //次回に繰り越す
  1365:     } else {  //SJISの1バイト目が来なかった
  1366:       int c = CharacterCode.chrSJISToChar[d];  //1バイトで変換する
  1367:       if (c != 0) {  //1バイトで変換できた
  1368:         trmPrintChar (c);  //1文字表示する
  1369:       } else {  //1バイトで変換できなかった
  1370:         //1バイトで変換できなかったがSJISの1バイト目でないことはわかっているので1バイト分のコードを表示する
  1371:         trmPrintChar ('[');
  1372:         trmPrintChar (XEiJ.fmtHexc (d >> 4));
  1373:         trmPrintChar (XEiJ.fmtHexc (d & 15));
  1374:         trmPrintChar (']');
  1375:       }
  1376:     }
  1377:   }  //trmPrintSJIS(int)
  1378: 
  1379:   //trmPrintChar (c)
  1380:   //  末尾に1文字追加する
  1381:   public static void trmPrintChar (int c) {
  1382:     if (c == 0x08) {  //バックスペース
  1383:       if (trmOutputEnd > 0) {
  1384:         if (trmBoard != null) {
  1385:           trmBoard.replaceRange ("", trmOutputEnd - 1, trmOutputEnd);  //1文字削除
  1386:           trmOutputEnd--;
  1387:           trmBoard.setCaretPosition (trmOutputEnd);
  1388:         } else {
  1389:           trmOutputBuilder.delete (trmOutputEnd - 1, trmOutputEnd);  //1文字削除
  1390:           trmOutputEnd--;
  1391:         }
  1392:       }
  1393:     } else if (c >= 0x20 && c != 0x7f || c == 0x09 || c == 0x0a) {  //タブと改行以外の制御コードを除く
  1394:       if (trmBoard != null) {
  1395:         trmBoard.insert (String.valueOf ((char) c), trmOutputEnd);  //1文字追加
  1396:         trmOutputEnd++;
  1397:         if (trmOutputEnd >= TRM_CUT_OUTPUT_LENGTH) {
  1398:           trmBoard.replaceRange ("", 0, trmOutputEnd - TRM_MAX_OUTPUT_LENGTH);  //先頭を削って短くする
  1399:           trmOutputEnd = TRM_MAX_OUTPUT_LENGTH;
  1400:         }
  1401:         trmBoard.setCaretPosition (trmOutputEnd);
  1402:       } else {
  1403:         trmOutputBuilder.append ((char) c);  //1文字追加
  1404:         trmOutputEnd++;
  1405:         if (trmOutputEnd >= TRM_CUT_OUTPUT_LENGTH) {
  1406:           trmOutputBuilder.delete (0, trmOutputEnd - TRM_MAX_OUTPUT_LENGTH);  //先頭を削って短くする
  1407:           trmOutputEnd = TRM_MAX_OUTPUT_LENGTH;
  1408:         }
  1409:       }
  1410:     }
  1411:   }  //trmPrintChar(int)
  1412: 
  1413:   //trmPrint (s)
  1414:   //  末尾に文字列を追加する
  1415:   //  情報表示用
  1416:   //  制御コードを処理しないのでタブと改行以外の制御コードを含めないこと
  1417:   public static void trmPrint (String s) {
  1418:     if (s == null) {
  1419:       return;
  1420:     }
  1421:     if (trmFrame != null) {
  1422:       trmBoard.insert (s, trmOutputEnd);  //文字列追加
  1423:       trmOutputEnd += s.length ();
  1424:       if (trmOutputEnd >= TRM_CUT_OUTPUT_LENGTH) {
  1425:         trmBoard.replaceRange ("", 0, trmOutputEnd - TRM_MAX_OUTPUT_LENGTH);  //先頭を削って短くする
  1426:         trmOutputEnd = TRM_MAX_OUTPUT_LENGTH;
  1427:       }
  1428:       trmBoard.setCaretPosition (trmOutputEnd);
  1429:     } else {
  1430:       trmOutputBuilder.append (s);  //文字列追加
  1431:       trmOutputEnd += s.length ();
  1432:       if (trmOutputEnd >= TRM_CUT_OUTPUT_LENGTH) {
  1433:         trmOutputBuilder.delete (0, trmOutputEnd - TRM_MAX_OUTPUT_LENGTH);  //先頭を削って短くする
  1434:         trmOutputEnd = TRM_MAX_OUTPUT_LENGTH;
  1435:       }
  1436:     }
  1437:   }  //trmPrint(String)
  1438: 
  1439:   //trmPrintln (s)
  1440:   //  末尾に文字列と改行を追加する
  1441:   //  情報表示用
  1442:   //  制御コードを処理しないのでタブと改行以外の制御コードを含めないこと
  1443:   public static void trmPrintln (String s) {
  1444:     trmPrint (s);
  1445:     trmPrintChar ('\n');
  1446:   }  //trmPrintln(String)
  1447: 
  1448:   //trmEnter ()
  1449:   //  ENTERキーを処理する
  1450:   public static void trmEnter () {
  1451:     String text = trmBoard.getText ();  //テキスト全体
  1452:     int length = text.length ();  //テキスト全体の長さ
  1453:     int outputLineStart = text.lastIndexOf ('\n', trmOutputEnd - 1) + 1;  //出力の末尾の行の先頭。プロンプトの先頭
  1454:     int caretLineStart = text.lastIndexOf ('\n', trmBoard.getCaretPosition () - 1) + 1;  //キャレットがある行の先頭
  1455:     if (outputLineStart <= caretLineStart) {  //出力の末尾の行の先頭以降でENTERキーが押された
  1456:       trmBoard.replaceRange ("", trmOutputEnd, length);  //入力された文字列を一旦削除する
  1457:       trmSendString (text.substring (trmOutputEnd, length) + "\r");  //入力された文字列を送信する
  1458:     } else if (outputLineStart < trmOutputEnd) {  //出力の末尾の行の先頭よりも手前でENTERキーが押されて、出力の末尾の行にプロンプトがあるとき
  1459:       String prompt = text.substring (outputLineStart, trmOutputEnd);  //出力の末尾の行のプロンプト
  1460:       int caretLineEnd = text.indexOf ('\n', caretLineStart);  //キャレットがある行の末尾
  1461:       if (caretLineEnd == -1) {
  1462:         caretLineEnd = length;
  1463:       }
  1464:       String line = text.substring (caretLineStart, caretLineEnd);  //キャレットがある行
  1465:       int start = line.indexOf (prompt);  //キャレットがある行のプロンプトの先頭
  1466:       if (start >= 0) {  //キャレットがある行にプロンプトがあるとき
  1467:         trmOutputEnd = length;  //入力された文字列を無効化する
  1468:         if (text.charAt (trmOutputEnd - 1) != '\n') {  //改行で終わっていないとき
  1469:           trmBoard.insert ("\n", trmOutputEnd);  //末尾にENTERを追加する
  1470:           trmOutputEnd++;
  1471:           if (trmOutputEnd >= TRM_CUT_OUTPUT_LENGTH) {
  1472:             trmBoard.replaceRange ("", 0, trmOutputEnd - TRM_MAX_OUTPUT_LENGTH);  //先頭を削って短くする
  1473:             trmOutputEnd = TRM_MAX_OUTPUT_LENGTH;
  1474:           }
  1475:         }
  1476:         trmBoard.setCaretPosition (trmOutputEnd);
  1477:         trmSendString (line.substring (start + prompt.length ()) + "\r");  //プロンプトの後ろから行の末尾までを送信する
  1478:       }
  1479:     }
  1480:   }  //trmEnter()
  1481: 
  1482:   //trmSendString (s)
  1483:   //  文字列をSJISに変換してAUXまたはSerialPortへ送信する
  1484:   public static void trmSendString (String s) {
  1485:     int l = s.length ();
  1486:     if (l == 0) {
  1487:       return;
  1488:     }
  1489:     byte[] b = new byte[l * 2];
  1490:     int k = 0;
  1491:     for (int i = 0; i < l; i++) {
  1492:       int c = CharacterCode.chrCharToSJIS[s.charAt (i)];
  1493:       if (0x00ff < c) {
  1494:         b[k++] = (byte) (c >> 8);
  1495:       }
  1496:       b[k++] = (byte) c;
  1497:     }
  1498:     if (trmRowToCol[0] == 1) {  //Terminal→AUX。row2col
  1499:       int row = 0;
  1500:       int col = 1;
  1501:       trmConnectionArray[trmCols * row + col - 1].row2colQueue.write (b, 0, k);
  1502:     } else if (0 < trmColToRow[0]) {  //Terminal→SerialPort。col2row
  1503:       int row = trmColToRow[0];
  1504:       int col = 0;
  1505:       trmConnectionArray[trmCols * row + col - 1].col2rowQueue.write (b, 0, k);
  1506:     }
  1507:   }  //trmSendString
  1508: 
  1509: 
  1510: 
  1511:   //trmSendFile ()
  1512:   //  ファイル送信ボタンが押された
  1513:   public static void trmSendFile () {
  1514:     if (trmSendDialog == null) {
  1515:       ActionListener listener = new ActionListener () {
  1516:         @Override public void actionPerformed (ActionEvent ae) {
  1517:           switch (ae.getActionCommand ()) {
  1518:           case JFileChooser.APPROVE_SELECTION:
  1519:           case "Send":  //送信
  1520:             trmSendDialog.setVisible (false);
  1521:             trmSendThread = new SendThread (trmSendFileChooser.getSelectedFile ());
  1522:             trmSendThread.start ();
  1523:             break;
  1524:           case JFileChooser.CANCEL_SELECTION:
  1525:           case "Cancel":  //キャンセル
  1526:             trmSendDialog.setVisible (false);
  1527:             break;
  1528:           }  //switch
  1529:         }  //actionPerformed
  1530:       };  //ActionListener
  1531:       trmSendFileChooser = new JFileChooser (new File ("a.txt"));
  1532:       trmSendFileChooser.setMultiSelectionEnabled (false);  //複数選択不可
  1533:       trmSendFileChooser.setControlButtonsAreShown (false);  //デフォルトのボタンを消す
  1534:       trmSendFileChooser.addActionListener (listener);
  1535:       trmSendDialog =
  1536:         Multilingual.mlnTitle (
  1537:           ComponentFactory.createModalDialog (
  1538:             trmFrame,
  1539:             "Send file",
  1540:             ComponentFactory.createBorderPanel (
  1541:               0, 0,
  1542:               ComponentFactory.createVerticalBox (
  1543:                 trmSendFileChooser,
  1544:                 ComponentFactory.createHorizontalBox (
  1545:                   Box.createHorizontalStrut (12),
  1546:                   Box.createHorizontalGlue (),
  1547:                   Multilingual.mlnText (
  1548:                     ComponentFactory.createLabel (
  1549:                       "Prepare COMMAND.X with CTTY AUX"),
  1550:                     "ja", "COMMAND.X を CTTY AUX で準備して"),
  1551:                   Box.createHorizontalStrut (12),
  1552:                   Multilingual.mlnText (
  1553:                     ComponentFactory.createButton ("Send", KeyEvent.VK_S, listener),
  1554:                     "ja", "送信"),
  1555:                   Box.createHorizontalStrut (12),
  1556:                   Multilingual.mlnText (
  1557:                     ComponentFactory.createButton ("Cancel", KeyEvent.VK_C, listener),
  1558:                     "ja", "キャンセル"),
  1559:                   Box.createHorizontalStrut (12)
  1560:                   ),  //createHorizontalBox
  1561:                 Box.createVerticalStrut (12)
  1562:                 )  //createVerticalBox
  1563:               )  //createBorderPanel
  1564:             ),  //createModalDialog
  1565:           "ja", "ファイル送信");  //mlnTitle
  1566:     }  //if
  1567:     XEiJ.pnlExitFullScreen (true);
  1568:     trmSendDialog.setVisible (true);
  1569:   }  //trmSendFile
  1570: 
  1571:   //class SendThread
  1572:   //  送信スレッド
  1573:   //  ファイルをa.rに変換する
  1574:   //    a.rは実行ファイルだが終端の$1A以外に$00~$1Fを含まない
  1575:   //  a.rをcopy aux a.rとa.rで挟んで送信する
  1576:   //  X68000でa.rが作られて実行されてファイルが復元される
  1577:   static class SendThread extends Thread {
  1578:     File file;
  1579:     SendThread (File file) {
  1580:       this.file = file;
  1581:     }
  1582:     @Override public void run () {
  1583:       trmSendProcess (file);
  1584:     }
  1585:   }  //class SendThread
  1586: 
  1587:   //buf = load (file)
  1588:   //  ファイルを読み込む
  1589:   static byte[] load (File file) {
  1590:     if (!file.isFile ()) {
  1591:       return null;
  1592:     }
  1593:     int len = (int) file.length ();
  1594:     if (len == 0) {
  1595:       return null;
  1596:     }
  1597:     byte[] buf = new byte[len];
  1598:     try (BufferedInputStream bis = new BufferedInputStream (new FileInputStream (file))) {
  1599:       if (bis.read (buf) != len) {
  1600:         return null;
  1601:       }
  1602:     } catch (IOException ioe) {
  1603:       return null;
  1604:     }
  1605:     return buf;
  1606:   }  //load
  1607: 
  1608:   //class BitWriter
  1609:   //  ビットライタ
  1610:   static class BitWriter {
  1611: 
  1612:     byte[] buffer;  //バッファ
  1613:     int byteIndex;  //バイト書き込み位置=現在の長さ
  1614:     int bitIndex;  //ビット書き込み位置
  1615:     int bitCount;  //[bitIndex]の残りビット数。上位から書く。下位bitCountビットが空いている
  1616: 
  1617:     //new BitWriter ()
  1618:     //  コンストラクタ
  1619:     BitWriter () {
  1620:       buffer = new byte[16];
  1621:       byteIndex = 0;
  1622:       bitIndex = -1;
  1623:       bitCount = 0;
  1624:     }  //BitWriter
  1625: 
  1626:     //writeByte (data)
  1627:     //  バイト書き込み
  1628:     //  data  データ。下位8ビットだけ使う
  1629:     void writeByte (int data) {
  1630:       if (byteIndex == buffer.length) {  //bufferが満杯なので長さを2倍にする
  1631:         byte[] temporary = new byte[byteIndex * 2];
  1632:         System.arraycopy (buffer, 0,  //from
  1633:                           temporary, 0,  //to
  1634:                           byteIndex);  //length
  1635:         buffer = temporary;
  1636:       }
  1637:       buffer[byteIndex++] = (byte) data;
  1638:     }  //writeByte
  1639: 
  1640:     //writeBits (width, data)
  1641:     //  ビット書き込み
  1642:     //  width  ビット数。0~32
  1643:     //  data   データ。下位widthビットだけ使う
  1644:     void writeBits (int width, int data) {
  1645:       while (width != 0) {  //書き込む残りビット数
  1646:         if (bitCount == 0) {  //[bitIndex]が満杯なので新しい[bitIndex]を用意する
  1647:           if (byteIndex == buffer.length) {  //bufferが満杯なので長さを2倍にする
  1648:             byte[] temporary = new byte[byteIndex * 2];
  1649:             System.arraycopy (buffer, 0,  //from
  1650:                               temporary, 0,  //to
  1651:                               byteIndex);  //length
  1652:             buffer = temporary;
  1653:           }
  1654:           bitIndex = byteIndex;
  1655:           bitCount = 8;
  1656:           buffer[byteIndex++] = 0;
  1657:         }
  1658:         data &= (1 << width) - 1;  //dataのゴミを消す
  1659:         int n = Math.min (bitCount, width);  //今回書き込むビット数
  1660:         bitCount -= n;  //今回書き込んだ後の[bitIndex]の残りビット数
  1661:         width -= n;  //今回書き込んだ後の書き込む残りビット数
  1662:         buffer[bitIndex] |= (byte) ((data >>> width) << bitCount);  //符号なし右シフト
  1663:       }  //while
  1664:     }  //writeBits
  1665: 
  1666:     //getBuffer ()
  1667:     //  バッファを返す
  1668:     byte[] getBuffer () {
  1669:       return buffer;
  1670:     }  //getBuffer
  1671: 
  1672:     //getLength ()
  1673:     //  現在の長さを返す
  1674:     int getLength () {
  1675:       return byteIndex;
  1676:     }  //getLength
  1677: 
  1678:   }  //class BitWriter
  1679: 
  1680:   static class DIC {
  1681:     int ptr;  //開始位置
  1682:     int len;  //長さ
  1683:   }
  1684: 
  1685:   //outbuf = compress (inpbuf, dicbit)
  1686:   //  圧縮
  1687:   //  inpbuf  入力バッファ
  1688:   //  dicbit  辞書のページ数のビット数。1~15
  1689:   static byte[] compress (byte[] inpbuf, int dicbit) {
  1690:     int dicsiz = 1 << dicbit;  //辞書のページ数
  1691:     //辞書を初期化する
  1692:     DIC[] dicbuf = new DIC[dicsiz];  //辞書
  1693:     for (int pag = 0; pag < dicsiz; pag++) {
  1694:       dicbuf[pag] = new DIC ();
  1695:       dicbuf[pag].ptr = 0;
  1696:       dicbuf[pag].len = 0;
  1697:     }
  1698:     int dicent = 0;  //辞書エントリ
  1699:     //入力長さ
  1700:     int inplen = inpbuf.length;
  1701:     BitWriter bw = new BitWriter ();
  1702:     bw.writeBits (24, inplen);
  1703:     //辞書のページ数のビット数
  1704:     bw.writeBits (4, dicbit);
  1705:     //圧縮ループ
  1706:     int inpptr = 0;  //入力位置
  1707:     while (inpptr < inplen) {
  1708:       //辞書から探す
  1709:       int dicpag = -1;  //辞書にある単語のページ番号
  1710:       int dicptr = -1;  //辞書にある単語の開始位置
  1711:       int diclen = 0;  //辞書にある単語の長さ
  1712:       int fstchr = inpbuf[inpptr] & 255;  //1文字目
  1713:       for (int pag = 0; pag < dicsiz; pag++) {
  1714:         int len = dicbuf[pag].len;  //辞書にある単語の長さ
  1715:         if (diclen < len &&  //これまでより長い
  1716:             inpptr + len + 1 <= inplen) {  //1文字伸ばしてもはみ出さない
  1717:           int ptr = dicbuf[pag].ptr;  //辞書にある単語の開始位置
  1718:         cmp:
  1719:           if (fstchr == (inpbuf[ptr] & 255)) {
  1720:             for (int i = 1; i < len; i++) {
  1721:               if (inpbuf[inpptr + i] != inpbuf[ptr + i]) {
  1722:                 break cmp;
  1723:               }
  1724:             }
  1725:             dicpag = pag;
  1726:             dicptr = ptr;
  1727:             diclen = len;
  1728:           }
  1729:         }
  1730:       }  //for pag
  1731:       if (diclen == 0) {  //辞書にない
  1732:         bw.writeBits (1, 0);
  1733:       } else {  //辞書にある
  1734:         bw.writeBits (1, 1);
  1735:         bw.writeBits (dicbit, dicpag);
  1736:       }
  1737:       int chr = inpbuf[inpptr + diclen] & 255;  //今回の文字
  1738:       //文字を出力する
  1739:       bw.writeByte (chr);
  1740:       //1文字伸ばす
  1741:       diclen++;
  1742:       //新しい単語を辞書に登録する
  1743:       dicbuf[dicent].ptr = inpptr;
  1744:       dicbuf[dicent].len = diclen;
  1745:       dicent++;
  1746:       if (dicent == dicsiz) {
  1747:         dicent = 0;
  1748:       }
  1749:       //次の文字へ
  1750:       inpptr += diclen;
  1751:     }  //while
  1752:     //出力バッファを返す
  1753:     byte[] outbuf = bw.getBuffer ();
  1754:     int outlen = bw.getLength ();
  1755:     if (outbuf.length != outlen) {
  1756:       byte[] tmpbuf = new byte[outlen];
  1757:       System.arraycopy (outbuf, 0, tmpbuf, 0, outlen);
  1758:       outbuf = tmpbuf;
  1759:     }
  1760:     return outbuf;
  1761:   }  //compress
  1762: 
  1763:   //dst = encode (src)
  1764:   //  エンコード
  1765:   //    Pxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  1766:   //    Qxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  1767:   //    Rxxxxxxxxxxxxxxx
  1768:   //      ↓
  1769:   //    0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  1770:   //    0xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
  1771:   //    0xxxxxxxxxxxxxxxPQR0000000000000
  1772:   public static byte[] encode (byte[] src) {
  1773:     int r = src.length;
  1774:     int q = r / 62;
  1775:     r -= 62 * q;
  1776:     if (r != 0) {  //62で割り切れないとき
  1777:       r = ((r + 2 + 3) & -4) - 2;  //余りを4*n+2に切り上げる
  1778:       if (src.length < 62 * q + r) {
  1779:         byte[] tmp = new byte[62 * q + r];
  1780:         System.arraycopy (src, 0, tmp, 0, src.length);
  1781:         src = tmp;
  1782:       }
  1783:     }
  1784:     byte[] dst = new byte[64 * q + (r == 0 ? 0 : r + 2)];
  1785:     int[] w = new int[16];
  1786:     for (int i = 0; 64 * i < dst.length; i++) {  //ブロックの番号
  1787:       int n = Math.min (64, dst.length - 64 * i) / 4;  //ブロックに含まれるintの数
  1788:       //intを集める
  1789:       for (int k = 0; k < n - 1; k++) {
  1790:         w[k] = ((src[62 * i + 4 * k + 0] & 255) << 24 |
  1791:                 (src[62 * i + 4 * k + 1] & 255) << 16 |
  1792:                 (src[62 * i + 4 * k + 2] & 255) << 8 |
  1793:                 (src[62 * i + 4 * k + 3] & 255));
  1794:       }
  1795:       w[n - 1] = ((src[62 * i + 4 * (n - 1) + 0] & 255) << 24 |
  1796:                   (src[62 * i + 4 * (n - 1) + 1] & 255) << 16);
  1797:       //intの最上位ビットを最後のintの下位2バイトに移して31ビット整数にする
  1798:       for (int k = 0; k < n; k++) {
  1799:         if ((w[k] & 0x80000000) != 0) {
  1800:           w[k] &= 0x7fffffff;
  1801:           w[n - 1] |= 1 << (15 - k);
  1802:         }
  1803:       }
  1804:       //31ビット整数を4桁の224進数に変換して各桁に32を加えて出力する
  1805:       for (int k = 0; k < n; k++) {
  1806:         int t3 = w[k];
  1807:         int t2 = t3 / 224;
  1808:         t3 -= 224 * t2;
  1809:         int t1 = t2 / 224;
  1810:         t2 -= 224 * t1;
  1811:         int t0 = t1 / 224;
  1812:         t1 -= 224 * t0;
  1813:         dst[64 * i + 4 * k + 0] = (byte) (32 + t0);
  1814:         dst[64 * i + 4 * k + 1] = (byte) (32 + t1);
  1815:         dst[64 * i + 4 * k + 2] = (byte) (32 + t2);
  1816:         dst[64 * i + 4 * k + 3] = (byte) (32 + t3);
  1817:       }
  1818:     }
  1819:     return dst;
  1820:   }  //encode
  1821: 
  1822:   //trmSendProcess (file)
  1823:   //  送信処理
  1824:   public static void trmSendProcess (File file) {
  1825:     //ファイルを読み込む
  1826:     byte[] fileData = load (file);  //本体
  1827:     if (fileData == null) {
  1828:       return;
  1829:     }
  1830:     if (0x00ffff00 < fileData.length) {
  1831:       return;
  1832:     }
  1833:     //------------------------------------------------
  1834:     //step3_data
  1835:     //byte[] adotr3 = load ("adotr3.r");
  1836:     byte[] step3Data = new byte[adotr3.length +  //adotr3
  1837:                                 24 +  //ファイル名
  1838:                                 4 +  //日時
  1839:                                 4 +  //本体のCRC32
  1840:                                 4 +  //本体の長さ
  1841:                                 fileData.length];  //本体
  1842:     int index3 = 0;
  1843:     //adotr3
  1844:     System.arraycopy (adotr3, 0,
  1845:                       step3Data, index3,
  1846:                       adotr3.length);
  1847:     index3 += adotr3.length;
  1848:     //ファイル名
  1849:     //  ・SJISに変換できない
  1850:     //  ・SJISに変換できたが、Human68kの標準のファイル名に使えない文字がある
  1851:     //  ・SJISに変換できたが、/^[^\.]{1,18}(?:\.[^\.]{1,3})?$/にマッチしない
  1852:     //  はすべてエラー
  1853:     String name = file.getName ();
  1854:     {
  1855:       byte[] b = new byte[2 * name.length ()];
  1856:       int k = 0;
  1857:       int p = -1;
  1858:       for (int i = 0; i < name.length (); i++) {
  1859:         int c = CharacterCode.chrCharToSJIS[name.charAt (i)];
  1860:         if (c == 0) {  //変換できない文字がある
  1861:           return;
  1862:         }
  1863:         if (c <= ' ' || c == ':' || c == '*' || c == '?' ||
  1864:             c == '\\' || c == '/' || (c == '-' && k == 0) ||
  1865:             c == '"' || c == '\'' || c == '+' || c == ';' ||
  1866:             c == '<' || c == '=' || c == '>' ||
  1867:             c == '[' || c == ']' || c == '|') {  //Human68kの標準のファイル名に使えない文字がある
  1868:           return;
  1869:         }
  1870:         if (c == '.') {  //'.'
  1871:           if (p < 0) {  //初回
  1872:             p = k;
  1873:           } else {  //'.'が2つある
  1874:             return;
  1875:           }
  1876:         }
  1877:         if (0x00ff < c) {  //2バイト
  1878:           b[k++] = (byte) (c >> 8);
  1879:         }
  1880:         b[k++] = (byte) c;
  1881:         if (p < 0 ? 18 < k : p + 1 + 3 < k) {  //18+3文字に収まっていない
  1882:           return;
  1883:         }
  1884:       }
  1885:       for (int i = 0; i < k; i++) {
  1886:         step3Data[index3++] = b[i];
  1887:       }
  1888:       for (int i = k; i < 24; i++) {
  1889:         step3Data[index3++] = 0;
  1890:       }
  1891:     }
  1892:     //日時
  1893:     {
  1894:       long dttm = DnT.dntDttmCmil (file.lastModified () + RP5C15.rtcCmilGap);  //西暦年<<42|月<<38|月通日<<32|時<<22|分<<16|秒<<10|ミリ秒。FCBの日時はRTCの日時なのでオフセットを加える
  1895:       int date = DnT.dntYearDttm (dttm) - 1980 << 9 | DnT.dntMontDttm (dttm) << 5 | DnT.dntMdayDttm (dttm);  //(西暦年-1980)<<9|月<<5|月通日
  1896:       int time = DnT.dntHourDttm (dttm) << 11 | DnT.dntMinuDttm (dttm) << 5 | DnT.dntSecoDttm (dttm) >> 1;  //時<<11|分<<5|秒/2
  1897:       step3Data[index3++] = (byte) (date >> 8);
  1898:       step3Data[index3++] = (byte) date;
  1899:       step3Data[index3++] = (byte) (time >> 8);
  1900:       step3Data[index3++] = (byte) time;
  1901:     }
  1902:     //本体のCRC32
  1903:     {
  1904:       CRC32 crc32 = new CRC32 ();
  1905:       crc32.update (fileData, 0, fileData.length);
  1906:       int t = (int) crc32.getValue ();
  1907:       step3Data[index3++] = (byte) (t >> 24);
  1908:       step3Data[index3++] = (byte) (t >> 16);
  1909:       step3Data[index3++] = (byte) (t >> 8);
  1910:       step3Data[index3++] = (byte) (t);
  1911:     }
  1912:     //本体の長さ
  1913:     {
  1914:       int t = fileData.length;
  1915:       step3Data[index3++] = (byte) (t >> 24);
  1916:       step3Data[index3++] = (byte) (t >> 16);
  1917:       step3Data[index3++] = (byte) (t >> 8);
  1918:       step3Data[index3++] = (byte) (t);
  1919:     }
  1920:     //本体
  1921:     System.arraycopy (fileData, 0,
  1922:                       step3Data, index3,
  1923:                       fileData.length);
  1924:     index3 += fileData.length;
  1925:     //------------------------------------------------
  1926:     //step2_data
  1927:     //byte[] adotr2 = load ("adotr2.r");
  1928:     byte[] step3DataCompressed = null;
  1929:     int dicbit = 0;
  1930:     //辞書のサイズのビット数
  1931:     //  圧縮しにくいときは小さい方がよいが縮まらず捨てられるビット数を試しても意味がない
  1932:     //  圧縮しやすいときは大きい方がよいが圧縮に時間がかかる
  1933:     //  9に固定してもよい
  1934:     for (int i = 8; i <= 10; i++) {
  1935:       byte[] t = compress (step3Data, i);
  1936:       if (step3DataCompressed == null ||
  1937:           t.length < step3DataCompressed.length) {
  1938:         step3DataCompressed = t;
  1939:         dicbit = i;
  1940:       }
  1941:     }
  1942:     byte[] step2Data = new byte[adotr2.length + step3DataCompressed.length];
  1943:     int index2 = 0;
  1944:     //adotr2
  1945:     System.arraycopy (adotr2, 0,
  1946:                       step2Data, index2,
  1947:                       adotr2.length);
  1948:     index2 += adotr2.length;
  1949:     //本体
  1950:     System.arraycopy (step3DataCompressed, 0,
  1951:                       step2Data, index2,
  1952:                       step3DataCompressed.length);
  1953:     index2 += step3DataCompressed.length;
  1954:     //------------------------------------------------
  1955:     //step1_data
  1956:     //byte[] adotr1 = load ("adotr1.r");
  1957:     byte[] step3DataEncoded = encode (step3Data);  //圧縮なし
  1958:     byte[] step2DataEncoded = encode (step2Data);  //圧縮あり
  1959:     byte[] step1Data;
  1960:     if (step3DataEncoded.length <= step2DataEncoded.length) {  //圧縮効果なし
  1961:       step1Data = new byte[adotr1.length + step3DataEncoded.length + 1];
  1962:       int index1 = 0;
  1963:       System.arraycopy (adotr1, 0,
  1964:                         step1Data, index1,
  1965:                         adotr1.length);
  1966:       index1 += adotr1.length;
  1967:       System.arraycopy (step3DataEncoded, 0,
  1968:                         step1Data, index1,
  1969:                         step3DataEncoded.length);
  1970:       index1 += step3DataEncoded.length;
  1971:       step1Data[index1++] = 0x1a;
  1972:       if (false) {
  1973:         System.out.printf ("                 original: %d bytes (%.1f%%)\n",
  1974:                            fileData.length,
  1975:                            100.0);
  1976:         System.out.printf ("   generator concatenated: %d bytes (%.1f%%)\n",
  1977:                            step3Data.length,
  1978:                            100.0 * (double) step3Data.length / (double) fileData.length);
  1979:         System.out.printf ("                  encoded: %d bytes (%.1f%%)\n",
  1980:                            step3DataEncoded.length,
  1981:                            100.0 * (double) step3DataEncoded.length / (double) fileData.length);
  1982:         System.out.printf ("     decoder concatenated: %d bytes (%.1f%%)\n",
  1983:                            step1Data.length,
  1984:                            100.0 * (double) step1Data.length / (double) fileData.length);
  1985:       }
  1986:     } else {  //圧縮効果あり
  1987:       step1Data = new byte[adotr1.length + step2DataEncoded.length + 1];
  1988:       int index1 = 0;
  1989:       System.arraycopy (adotr1, 0,
  1990:                         step1Data, index1,
  1991:                         adotr1.length);
  1992:       index1 += adotr1.length;
  1993:       System.arraycopy (step2DataEncoded, 0,
  1994:                         step1Data, index1,
  1995:                         step2DataEncoded.length);
  1996:       index1 += step2DataEncoded.length;
  1997:       step1Data[index1++] = 0x1a;
  1998:       if (false) {
  1999:         System.out.printf ("                 original: %d bytes (%.1f%%)\n",
  2000:                            fileData.length,
  2001:                            100.0);
  2002:         System.out.printf ("   generator concatenated: %d bytes (%.1f%%)\n",
  2003:                            step3Data.length,
  2004:                            100.0 * (double) step3Data.length / (double) fileData.length);
  2005:         System.out.printf ("               compressed: %d bytes (%.1f%%) (%d bits dictionary)\n",
  2006:                            step3DataCompressed.length,
  2007:                            100.0 * (double) step3DataCompressed.length / (double) fileData.length,
  2008:                            dicbit);
  2009:         System.out.printf ("decompressor concatenated: %d bytes (%.1f%%)\n",
  2010:                            step2Data.length,
  2011:                            100.0 * (double) step2Data.length / (double) fileData.length);
  2012:         System.out.printf ("                  encoded: %d bytes (%.1f%%)\n",
  2013:                            step2DataEncoded.length,
  2014:                            100.0 * (double) step2DataEncoded.length / (double) fileData.length);
  2015:         System.out.printf ("     decoder concatenated: %d bytes (%.1f%%)\n",
  2016:                            step1Data.length,
  2017:                            100.0 * (double) step1Data.length / (double) fileData.length);
  2018:       }
  2019:     }
  2020:     //copy aux a.rとa.rで挟んで送信する
  2021:     trmSendString (String.format (
  2022:       "rem a.r/%s %d/%d %.1f%%\rcopy aux a.r\r",
  2023:       name, step1Data.length, fileData.length,
  2024:       100.0 * (double) step1Data.length / (double) fileData.length));
  2025:     try {
  2026:       Thread.sleep (1000);
  2027:     } catch (InterruptedException ie) {
  2028:     }
  2029:     if (trmRowToCol[0] == 1) {  //Terminal→AUX。row2col
  2030:       int row = 0;
  2031:       int col = 1;
  2032:       trmConnectionArray[trmCols * row + col - 1].row2colQueue.write (step1Data, 0, step1Data.length);
  2033:     } else if (0 < trmColToRow[0]) {  //Terminal→SerialPort。col2row
  2034:       int row = trmColToRow[0];
  2035:       int col = 0;
  2036:       trmConnectionArray[trmCols * row + col - 1].col2rowQueue.write (step1Data, 0, step1Data.length);
  2037:     }
  2038:     try {
  2039:       Thread.sleep (1000);
  2040:     } catch (InterruptedException ie) {
  2041:     }
  2042:     trmSendString ("a.r\r");
  2043:   }  //trmSendProcess
  2044: 
  2045:   //perl misc/ftob.pl adotr1 misc/adotr1.r
  2046:   public static final byte[] adotr1 = "B\202G\373 h\324\211\304|\377\374(B\"Kx@\330\213B\200\320\214\220\204\223\201\300\201\330\200$I\"<\337\337\337\340\322\233B\200v( @\347\210\220\210\353\210\341\231\320\201Q\301\220\201\342Kd\354\"\300\267\304e\3322!B\200\322A\342\220\321\232\265\311e\364\267\314e\266A\372\377\370t\370N\360\" BAVAp\254NO".getBytes (XEiJ.ISO_8859_1);
  2047:   //perl misc/ftob.pl adotr2 misc/adotr2.r
  2048:   public static final byte[] adotr2 = "p\1\320\211\300|\377\376\"@|\0~\0E\372\0\372r\30a\0\0\322&\0\326\211p\1\320\203\300|\377\376*@r\4a\0\0\276g\0\0\246\30\0z\b\351\255\332\215\377\201\"\5\222\200/\1/\0\377JP\217J\200ktp\0 M \300 \300\261\305e\370(I,M\265\311dvr\1a\0\0\206f\b,\314p\1,\300`0\22\4av\347\210 u\b\0 5\b\4gVR\200\"\0\322\214\262\203bL,\314,\300U\200H@H@\30\330Q\310\377\374H@Q\310\377\364\30\332\275\305f\2,M\271\303e\260p\372N\373\2\16r\3p\254NO IN\320\377\t\377\0Hz\0\4`\366out of memory\r\n\0Hz\0\4`\340data error\r\n\0\0p\0J\7f\4\34\32P\7\24\1\264\7c\2\24\7\20\6\345\250\345.\236\2\222\2f\344\340\210Nu".getBytes (XEiJ.ISO_8859_1);
  2049:   //perl misc/ftob.pl adotr3 misc/adotr3.r
  2050:   public static final byte[] adotr3 = "E\372\1\2&*\0 G\352\0$ \13\320\203R\200\300|\377\376(@K\354\4\0\377\201\"\r\222\200/\1/\0\377JP\217J\200k\0\0\230 Lr\0 \1t\7\342\210d\6\n\200\355\270\203 Q\312\377\364 \300R\1d\350\"Lp\0\"\3 KF\200`\22HAt\0\24\30\261\2\340\210\345J$1(\0\265\200Q\311\377\356HAQ\311\377\346F\200\260\252\0\34f`?<\0 /\n\377<\\\217J\200kd/\3/\13?\0\377@\"\0/j\0\30\0\2\377\207\377>O\357\0\n\262\203g\b/\n\377AX\217`@/\n\377\tHz\0\6\377\t\377\0 created\r\n\0\0Hz\0\4`\352out of memory\r\n\0Hz\0\4`\324crc error\r\n\0Hz\0\4`\302cannot write\r\n\0\0".getBytes (XEiJ.ISO_8859_1);
  2051: 
  2052: }  //class RS232CTerminal