xeij/Z8530.java
//========================================================================================
//  Z8530.java
//    en:SCC -- Serial Communication Controller
//    ja:SCC -- シリアルコミュニケーションコントローラ
//  Copyright (C) 2003-2026 Makoto Kamada
//
//  This file is part of the XEiJ (X68000 Emulator in Java).
//  You can use, modify and redistribute the XEiJ if the conditions are met.
//  Read the XEiJ License for more details.
//  https://stdkmd.net/xeij/
//========================================================================================

//----------------------------------------------------------------------------------------
//  マウス
//    マウスイベントとマウスモーションイベントで取得したデータを返す
//  RS-232C
//    ターミナルに接続
//----------------------------------------------------------------------------------------

package xeij;

import java.awt.event.*;
import java.util.*;
import javax.swing.*;

public class Z8530 {

  public static final boolean SCC_DEBUG_ON = false;
  public static int sccDebugOn;  //1=Port0B,2=Port1A,4=Interrupt

  //設定
  public static double sccFreq = 5000000.0;  //SCCの動作周波数

  //メニュー
  public static JMenu sccMenu;

  //ポート
  public static final int SCC_0B_COMMAND = 0x00e98001;
  public static final int SCC_0B_DATA    = 0x00e98003;
  public static final int SCC_1A_COMMAND = 0x00e98005;
  public static final int SCC_1A_DATA    = 0x00e98007;

  //割り込み
  //  ベクタ
  //    0  ポート0B送信バッファ空(マウス送信)
  //    1  ポート0B外部/ステータス変化
  //    2  ポート0B受信バッファフル(マウス受信)
  //    3  ポート0B特別受信条件
  //    4  ポート1A送信バッファ空(RS-232C送信)
  //    5  ポート1A外部/ステータス変化
  //    6  ポート1A受信バッファフル(RS-232C受信)
  //    7  ポート1A特別受信条件
  //  マスク
  //    0x80  常に0
  //    0x40  常に0
  //    0x20  ポート1A受信バッファフル(RS-232C受信)
  //    0x10  ポート1A送信バッファ空(RS-232C送信)
  //    0x08  ポート1A外部/ステータス変化
  //    0x04  ポート0B受信バッファフル(マウス受信)
  //    0x02  ポート0B送信バッファ空(マウス送信)
  //    0x01  ポート0B外部/ステータス変化
  //  優先順位
  //    高い  1A受信バッファフル(RS-232C受信)
  //          1A送信バッファ空(RS-232C送信)
  //          1A外部/ステータス変化
  //          0B受信バッファフル(マウス受信)
  //          0B送信バッファ空(マウス送信)
  //    低い  0B外部/ステータス変化
  //!!! マウス送信、外部/ステータス変化、特別受信条件の割り込みは未実装
  public static int sccInterruptVector;  //非修飾ベクタ。WR2
  public static int sccVectorInclude;  //WR9&0x11
  public static int sccMIE;  //Master Interrupt Enable。(WR9&0x08)!=0?-1:0
  //  マウス受信割り込み
  public static final int SCC_0B_RECEIVE_VECTOR = 2;
  public static final int SCC_0B_RECEIVE_MASK = 0x04;
  public static int scc0bReceiveMask;  //マスク
  public static int scc0bReceiveRR3;  //RR3のペンディングビット。割り込み発生でセット
  public static int scc0bReceiveRequest;  //リクエスト。割り込み発生でセット、受け付けでクリア
  public static int scc0bReceiveVector;  //修飾ベクタ
  //  RS-232C受信割り込み
  public static final int SCC_1A_RECEIVE_VECTOR = 6;
  public static final int SCC_1A_RECEIVE_MASK = 0x20;
  public static int scc1aReceiveMask;  //マスク
  public static int scc1aReceiveRR3;  //RR3のペンディングビット。割り込み発生でセット、IUSリセットでクリア
  public static int scc1aReceiveRequest;  //リクエスト。割り込み発生でセット、受け付けでクリア
  public static int scc1aReceiveVector;  //修飾ベクタ
  //  RS-232C送信割り込み
  public static final int SCC_1A_SEND_VECTOR = 4;
  public static final int SCC_1A_SEND_MASK = 0x10;
  public static int scc1aSendMask;  //マスク
  public static int scc1aSendRR3;  //RR3のペンディングビット。割り込み発生でセット
  public static int scc1aSendRequest;  //リクエスト。割り込み発生でセット、受け付けでクリア
  public static int scc1aSendVector;  //修飾ベクタ

  //ポート0B マウス
  public static int scc0bRegisterNumber;
  public static int scc0bRts;  //RTS(0または1)
  public static int scc0bBaudRateGen;  //WR13<<8|WR12
  public static int scc0bInputCounter;  //マウスデータのカウンタ。0~2
  public static int scc0bData1;
  public static int scc0bData2;

  public static final boolean SCC_FSX_MOUSE = true;  //true=SX-Windowをシームレスにする
  public static int sccFSXMouseHook;  //FSX.Xのマウス受信データ処理ルーチンのアドレス
  public static int sccFSXMouseWork;  //FSX.Xのマウスワークのアドレス

  //ポート1A RS-232C
  public static final int SCC_1A_INPUT_BITS = 16;
  public static final int SCC_1A_INPUT_SIZE = 1 << SCC_1A_INPUT_BITS;
  public static final int SCC_1A_INPUT_MASK = SCC_1A_INPUT_SIZE - 1;
  public static int scc1aRegisterNumber;
  public static final int[] scc1aInputBuffer = new int[SCC_1A_INPUT_SIZE];  //RS-232C受信バッファ。データはゼロ拡張済み
  public static int scc1aInputRead;  //RS-232C受信バッファから次に読み出すデータの位置。read==writeのときバッファエンプティ
  public static int scc1aInputWrite;  //RS-232C受信バッファに次に書き込むデータの位置。(write+1&SCC_1A_INPUT_MASK)==readのときバッファフル
  //  ボーレート
  public static int scc1aBRGEnable;  //WR14のbit0。true=ボーレートジェネレータ動作
  public static int scc1aClockModeShift;  //WR4のbit6-7。0=2^0,1=2^4,2=2^5,3=2^6
  public static int scc1aBaudRateGen;  //WR13<<8|WR12
  public static long scc1aInterval;  //転送間隔(XEiJ.TMR_FREQ単位)
  //  通信設定
  public static int scc1aRxBits;  //WR3のbit7-6。00=5bit,01=7bit,10=6bit,11=8bit。RR8の未使用ビットは1
  public static int scc1aRxEnable;  //WR3のbit0。1=enable
  public static int scc1aStop;  //WR4のbit3-2。01=s1,10=s1.5,11=s2
  public static int scc1aParity;  //WR4のbit1-0。x0=pn,01=po,11=pe
  public static int scc1aTxBits;  //WR5のbit6-5。00=1-5bit,01=7bit,10=6bit,11=8bit。1-5bitのとき1111000x,111000xx,11000xxx,1000xxxx,000xxxxx
  public static int scc1aTxEnable;  //WR5のbit3。1=enable


  //sccInit ()
  //  初期化
  public static void sccInit () {

    sccFreq = 5000000 < Settings.sgsGetInt ("sccfreq") ? 7372800.0 : 5000000.0;

    if (SCC_DEBUG_ON) {
      sccDebugOn = 2;
    }

    //scc1aInputBuffer = new int[SCC_1A_INPUT_SIZE];

    sccReset ();

  }  //sccInit()


  //sccTini ()
  //  後始末
  public static void sccTini () {
    Settings.sgsPutInt ("sccfreq", (int) sccFreq);
  }


  //sccMakeMenu ()
  public static JMenu sccMakeMenu () {
    ActionListener listener = new ActionListener () {
      @Override public void actionPerformed (ActionEvent ae) {
        Object source = ae.getSource ();
        String command = ae.getActionCommand ();
        switch (command) {
        case "5MHz":
          sccFreq = 5000000.0;
          break;
        case "7.3728MHz":
          sccFreq = 7372800.0;
          break;
        case "Port 0B":
          if (SCC_DEBUG_ON) {
            sccDebugOn = ((sccDebugOn & ~1) |
                          (((JCheckBoxMenuItem) source).isSelected () ? 1 : 0));
          }
          break;
        case "Port 1A":
          if (SCC_DEBUG_ON) {
            sccDebugOn = ((sccDebugOn & ~2) |
                          (((JCheckBoxMenuItem) source).isSelected () ? 2 : 0));
          }
          break;
        case "Interrupt":
          if (SCC_DEBUG_ON) {
            sccDebugOn = ((sccDebugOn & ~4) |
                          (((JCheckBoxMenuItem) source).isSelected () ? 4 : 0));
          }
          break;
        default:
          System.out.println ("unknown action command " + command);
        }
      }
    };  //ActionListener
    ButtonGroup freqGroup = new ButtonGroup ();
    return ComponentFactory.createMenu (
      "SCC",
      ComponentFactory.createRadioButtonMenuItem (
        freqGroup,
        sccFreq == 5000000.0,
        "5MHz",
        listener),
      ComponentFactory.createRadioButtonMenuItem (
        freqGroup,
        sccFreq == 7372800.0,
        "7.3728MHz",
        listener),
      !SCC_DEBUG_ON ? null :
      ComponentFactory.createHorizontalSeparator (),
      !SCC_DEBUG_ON ? null :
      ComponentFactory.createMenu (
        "Debug",
        ComponentFactory.createCheckBoxMenuItem (
          (sccDebugOn & 1) != 0, "Port 0B", listener),
        ComponentFactory.createCheckBoxMenuItem (
          (sccDebugOn & 2) != 0, "Port 1A", listener),
        ComponentFactory.createCheckBoxMenuItem (
          (sccDebugOn & 4) != 0, "Interrupt", listener)
        )  //createMenu
      );  //createMenu
  }  //sccMakeMenu


  //リセット
  public static void sccReset () {
    //割り込み
    sccInterruptVector = 0x00;
    sccVectorInclude = 0x00;
    sccMIE = 0;
    scc0bReceiveMask = 0;
    scc0bReceiveRR3 = 0;
    scc0bReceiveRequest = 0;
    scc0bReceiveVector = 0;
    scc1aReceiveMask = 0;
    scc1aReceiveRR3 = 0;
    scc1aReceiveRequest = 0;
    scc1aReceiveVector = 0;
    scc1aSendMask = 0;
    scc1aSendRR3 = 0;
    scc1aSendRequest = 0;
    scc1aSendVector = 0;
    //マウス
    scc0bRegisterNumber = 0;
    scc0bRts = 0;
    scc0bBaudRateGen = 31;  //4800bps。(5000000/2/16)/4800-2=30.552。(5000000/2/16)/(31+2)=4734.848=4800*0.986
    scc0bInputCounter = 0;
    scc0bData1 = 0;
    scc0bData2 = 0;
    if (SCC_FSX_MOUSE) {
      sccFSXMouseHook = 0;
      sccFSXMouseWork = 0;
    }
    //RS-232C
    scc1aRegisterNumber = 0;
    Arrays.fill (scc1aInputBuffer, 0);
    scc1aInputRead = 0;
    scc1aInputWrite = 0;
    scc1aRxBits = 3;  //b8
    scc1aRxEnable = 0;
    scc1aStop = 1;  //s1
    scc1aParity = 0;  //pn
    scc1aTxBits = 3;
    scc1aTxEnable = 0;
    scc1aBRGEnable = 0;
    scc1aClockModeShift = 4;  //1/16
    scc1aBaudRateGen = 14;  //9600bps
    double rate = sccFreq / (double) ((scc1aBaudRateGen + 2) << (scc1aClockModeShift + 1));
    double bits = (1.0 +  //start
                   (scc1aRxBits == 0 ? 5.0 :
                    scc1aRxBits == 1 ? 7.0 :
                    scc1aRxBits == 2 ? 6.0 : 8.0) +  //data
                   ((scc1aParity & 1) == 0 ? 0.0 : 1.0) +  //parity
                   (scc1aStop == 0 ? 0.0 :
                    scc1aStop == 1 ? 1.0 :
                    scc1aStop == 2 ? 1.5 : 2.0));  //stop
    double interval = bits / rate;
    scc1aInterval = Math.round (interval * (double) XEiJ.TMR_FREQ);
    //
    RS232CTerminal.trmAUXReset ();
  }  //sccReset()

  //割り込み受付
  //  コアが割り込み要求を受け付けたときに呼び出す
  //  割り込みベクタ番号を返す
  //  割り込み要求を取り下げる場合は0を返す
  //  オートベクタを使用するデバイスはオートベクタの番号を返すこと
  public static int sccAcknowledge () {
    int d = 0;
    //優先順位は固定
    if (scc1aReceiveRequest != 0) {  //1A受信バッファフル(RS-232C受信)
      scc1aReceiveRequest = 0;
      d = scc1aReceiveVector;
    } else if (scc1aSendRequest != 0) {  //1A送信バッファ空(RS-232C送信)
      scc1aSendRequest = 0;
      d = scc1aSendVector;
    } else if (scc0bReceiveRequest != 0) {  //0B受信バッファフル(マウス受信)
      scc0bReceiveRequest = 0;
      d = scc0bReceiveVector;
    }
    if (SCC_DEBUG_ON && (sccDebugOn & 4) != 0) {
      System.out.printf ("%08x sccAcknowledge()=0x%02x\n", XEiJ.regPC0, d);
    }
    return d;
  }  //sccAcknowledge()

  //割り込み終了
  //  コアが割り込み処理を終了したときに呼び出す
  //  まだ処理されていない割り込みが残っていたら再度割り込み要求を出す
  public static void sccDone () {
    if (SCC_DEBUG_ON && (sccDebugOn & 4) != 0) {
      System.out.printf ("%08x sccDone()\n", XEiJ.regPC0);
    }
    if ((scc1aReceiveRequest | scc1aSendRequest | scc0bReceiveRequest) != 0) {
      XEiJ.mpuIRR |= XEiJ.MPU_SCC_INTERRUPT_MASK;
    }
  }  //sccDone()

  //sccUpdateVector ()
  //  scc0bReceiveVector,scc1aReceiveVector,scc1aSendVectorを更新する
  //  sccInterruptVector,sccVectorIncludeを更新したら呼び出す
  public static void sccUpdateVector () {
    if (sccVectorInclude == 0x00) {  //(WR9&0x01)==0x00
      scc0bReceiveVector = sccInterruptVector;
      scc1aReceiveVector = sccInterruptVector;
      scc1aSendVector    = sccInterruptVector;
    } else if (sccVectorInclude == 0x01) {  //(WR9&0x11)==0x01
      int t = sccInterruptVector & 0b11110001;
      scc0bReceiveVector = t | SCC_0B_RECEIVE_VECTOR << 1;
      scc1aReceiveVector = t | SCC_1A_RECEIVE_VECTOR << 1;
      scc1aSendVector    = t | SCC_1A_SEND_VECTOR << 1;
    } else {  //(WR9&0x11)==0x11
      int t = sccInterruptVector & 0b10001111;
      scc0bReceiveVector = t | SCC_0B_RECEIVE_VECTOR << 4;
      scc1aReceiveVector = t | SCC_1A_RECEIVE_VECTOR << 4;
      scc1aSendVector    = t | SCC_1A_SEND_VECTOR << 4;
    }
    if (SCC_DEBUG_ON && (sccDebugOn & 4) != 0) {
      System.out.printf ("scc0bReceiveVector=0x%02x\n", scc0bReceiveVector);
      System.out.printf ("scc1aReceiveVector=0x%02x\n", scc1aReceiveVector);
      System.out.printf ("scc1aSendVector=0x%02x\n", scc1aSendVector);
    }
  }  //sccUpdateVector()

  //d = sccReadByte (a, peek)
  //  リードバイト
  public static int sccReadByte (int a, boolean peek) {
    XEiJ.mpuClockTime += XEiJ.busWaitTime.scc;
    int d = 0;
    switch (a & 7) {
      //------------------------------------------------
    case SCC_0B_COMMAND & 7:  //ポート0Bコマンド読み出し
      switch (scc0bRegisterNumber) {
      case 0:  //RR0
        //  0x80  ブレークまたはアボート
        //  0x40  送信アンダーラン
        //  0x20  CTS(0=送信禁止,1=送信許可)
        //  0x10  SYNC
        //  0x08  DCD
        //  0x04  送信バッファ空
        //  0x02  ボーレートカウント0
        //  0x01  受信バッファフル
        d = (RS232CTerminal.trmMUSReadRR0 () |  //CTS/DCD
             1 << 2 |  //Tx Buffer Empty
             (scc0bInputCounter < 3 ? 1 << 0 : 0 << 0));  //Rx Character Available
        break;
      case 2:  //RR2
        //修飾割り込みベクタ
        //  ポート0BのRR2はWR2に設定したベクタを割り込み要求で加工して返す
        d = (scc1aReceiveRequest != 0 ? scc1aReceiveVector :  //1A受信バッファフル(RS-232C受信)
             scc1aSendRequest != 0 ? scc1aSendVector :  //1A送信バッファ空(RS-232C送信)
             scc0bReceiveRequest != 0 ? scc0bReceiveVector :  //0B受信バッファフル(マウス受信)
             sccInterruptVector);
        break;
      case 3:  //RR3
        //ポート0BのRR3は常に0
        //  ポート0Bの割り込みペンディングを見るときはポート1AのRR3を参照する
        //d = 0;
        break;
      case 12:  //RR12
        d = scc0bBaudRateGen & 0xff;
        break;
      case 13:  //RR13
        d = scc0bBaudRateGen >> 8 & 0xff;
        break;
      default:
        if (SCC_DEBUG_ON && sccDebugOn != 0) {
          System.out.println ("unimplemented register");
        }
      }
      if (peek) {
        break;
      }
      scc0bRegisterNumber = 0;
      break;
      //------------------------------------------------
    case SCC_0B_DATA & 7:  //ポート0Bデータ読み出し(マウス受信)
      if (scc0bInputCounter == 0) {  //1バイト目
        d = Mouse.musExtraData;
        if (XEiJ.mpuClockTime < Mouse.musWheelReleaseTime) {
          d |= Mouse.musWheelButton;
        }
        if (peek) {
          break;
        }
        Mouse.musExtraData = Mouse.musData;
        if (!Mouse.musOnScreen) {  //ホストのマウスカーソルがスクリーン上にない
          //Mouse.musShow ();
          if (Mouse.musCursorNumber != 1 && Mouse.musCursorAvailable) {
            Mouse.musCursorNumber = 1;
            XEiJ.pnlPanel.setCursor (Mouse.musCursorArray[1]);  //ホストのマウスカーソルを表示する
          }
          scc0bData1 = scc0bData2 = 0;
        } else if (Mouse.musSeamlessOn) {  //シームレス
          int on, dx, dy, coeff = 256;
          if (XEiJ.currentMPU < Model.MPU_MC68LC040) {  //MMUなし
            if (SCC_FSX_MOUSE &&
                sccFSXMouseHook != 0 &&  //FSX.Xが常駐している
                MainMemory.mmrRls (0x0938) == sccFSXMouseHook) {  //マウス受信データ処理ルーチンがFSX.Xを指している。SX-Windowが動作中
              on = MainMemory.mmrRws (sccFSXMouseWork + 0x2e) == 0 ? 1 : 0;  //SX-Windowのマウスカーソルの表示状態。Obscureのときは表示されているとみなす
              int xy = MainMemory.mmrRls (sccFSXMouseWork + 0x0a);
              dx = (xy >> 16) - CRTC.crtR10TxXPort;  //SX-Windowのマウスカーソルの見かけのX座標
              dy = (short) xy - CRTC.crtR11TxYPort;  //SX-Windowのマウスカーソルの見かけのY座標
              coeff = MainMemory.mmrRwz (sccFSXMouseWork + 0x04);  //ポインタの移動量。係数*256
            } else {  //SX-Windowが動作中ではない
              on = MainMemory.mmrRbs (0x0aa2);  //IOCSのマウスカーソルの表示状態
              int xy = MainMemory.mmrRls (0x0ace);
              dx = xy >> 16;  //IOCSのマウスカーソルのX座標
              dy = (short) xy;  //IOCSのマウスカーソルのY座標
              if (Mouse.musFollowScrollOn) {
                dx -= CRTC.crtR10TxXPort;
                dy -= CRTC.crtR11TxYPort;
              }
            }
          } else {  //MMUあり
            if (SCC_FSX_MOUSE &&
                sccFSXMouseHook != 0 &&  //FSX.Xが常駐している
                MC68060.mmuPeekLongData (0x0938, 1) == sccFSXMouseHook) {  //マウス受信データ処理ルーチンがFSX.Xを指している。SX-Windowが動作中
              on = MC68060.mmuPeekWordSignData (sccFSXMouseWork + 0x2e, 1) == 0 ? 1 : 0;  //SX-Windowのマウスカーソルの表示状態。Obscureのときは表示されているとみなす
              int xy = MC68060.mmuPeekLongData (sccFSXMouseWork + 0x0a, 1);
              dx = (xy >> 16) - CRTC.crtR10TxXPort;  //SX-Windowのマウスカーソルの見かけのX座標
              dy = (short) xy - CRTC.crtR11TxYPort;  //SX-Windowのマウスカーソルの見かけのY座標
              coeff = MC68060.mmuPeekWordZeroData (sccFSXMouseWork + 0x04, 1);  //ポインタの移動量。係数*256
            } else {  //SX-Windowが動作中ではない
              on = MC68060.mmuPeekByteSignData (0x0aa2, 1);  //IOCSのマウスカーソルの表示状態
              int xy = MC68060.mmuPeekLongData (0x0ace, 1);
              dx = xy >> 16;  //IOCSのマウスカーソルのX座標
              dy = (short) xy;  //IOCSのマウスカーソルのY座標
              if (Mouse.musFollowScrollOn) {
                dx -= CRTC.crtR10TxXPort;
                dy -= CRTC.crtR11TxYPort;
              }
            }
          }
          dx = Mouse.musScreenX - dx;  //X方向の移動量
          dy = Mouse.musScreenY - dy;  //Y方向の移動量
          if (Mouse.musEdgeAccelerationOn) {  //縁部加速を行う
            final int range = 10;  //加速領域の幅
            final int speed = 10;  //移動速度
            if (Mouse.musScreenX < range) {
              dx = -speed;  //左へ
            } else if (XEiJ.pnlScreenWidth - range <= Mouse.musScreenX) {
              dx = speed;  //右へ
            }
            if (Mouse.musScreenY < range) {
              dy = -speed;  //上へ
            } else if (XEiJ.pnlScreenHeight - range <= Mouse.musScreenY) {
              dy = speed;  //下へ
            }
          }
          if (on != 0) {  //X68000のマウスカーソルが表示されている
            //Mouse.musHide ();
            if (Mouse.musCursorNumber != 0 && Mouse.musCursorAvailable) {
              Mouse.musCursorNumber = 0;
              XEiJ.pnlPanel.setCursor (Mouse.musCursorArray[0]);  //ホストのマウスカーソルを消す
            }
          } else {  //X68000のマウスカーソルが表示されていない
            //Mouse.musShow ();
            if (Mouse.musCursorNumber != 1 && Mouse.musCursorAvailable) {
              Mouse.musCursorNumber = 1;
              XEiJ.pnlPanel.setCursor (Mouse.musCursorArray[1]);  //ホストのマウスカーソルを表示する
            }
          }
          if (coeff != 256 && coeff != 0) {
            //SX-Windowのポインタの移動量の補正
            dx = (dx << 8) / coeff;
            dy = (dy << 8) / coeff;
          }
          //  Mouse.MUS_DEACCELERATION_TABLEの値が127を越えることはないのでシームレスでオーバーフローフラグがセットされることはない
          //  rbzで返すので負数のときのゼロ拡張を忘れないこと
          scc0bData1 = (dx == 0 ? 0 : dx >= 0 ?
                        Mouse.MUS_DEACCELERATION_TABLE[Math.min (1024, dx)] :
                        -Mouse.MUS_DEACCELERATION_TABLE[Math.min (1024, -dx)] & 0xff);
          scc0bData2 = (dy == 0 ? 0 : dy >= 0 ?
                        Mouse.MUS_DEACCELERATION_TABLE[Math.min (1024, dy)] :
                        -Mouse.MUS_DEACCELERATION_TABLE[Math.min (1024, -dy)] & 0xff);
        } else if (!XEiJ.frmIsActive) {  //エクスクルーシブだがフォーカスがない
          //Mouse.musShow ();
          if (Mouse.musCursorNumber != 1 && Mouse.musCursorAvailable) {
            Mouse.musCursorNumber = 1;
            XEiJ.pnlPanel.setCursor (Mouse.musCursorArray[1]);  //ホストのマウスカーソルを表示する
          }
          scc0bData1 = scc0bData2 = 0;
          Mouse.musExclusiveStart = true;  //フォーカスを得たときエクスクルーシブに切り替えた直後のように振る舞う
        } else {  //エクスクルーシブ
          //Mouse.musHide ();
          if (Mouse.musCursorNumber != 0 && Mouse.musCursorAvailable) {
            Mouse.musCursorNumber = 0;
            XEiJ.pnlPanel.setCursor (Mouse.musCursorArray[0]);  //ホストのマウスカーソルを消す
          }
          int dx, dy;
          if (XEiJ.PNL_ROTATION_ON) {
            int ox = (XEiJ.pnlScreenX1 + XEiJ.pnlScreenX2) >> 1;  //左スクリーンの中央のパネル座標
            int oy = (XEiJ.pnlScreenY1 + XEiJ.pnlScreenY2) >> 1;
            int tx = Mouse.musPanelX - ox;  //マウスカーソルの左スクリーンの中央からのパネル方向ベクトル
            int ty = Mouse.musPanelY - oy;
            XEiJ.rbtRobot.mouseMove (XEiJ.pnlGlobalX + ox,
                                     XEiJ.pnlGlobalY + oy);  //マウスカーソルを左スクリーンの中央に戻す
            dx = (int) Math.round (XEiJ.pnlInverseL00 * (double) tx + XEiJ.pnlInverseL01 * (double) ty);  //パネル方向ベクトルをスクリーン方向ベクトルに変換する
            dy = (int) Math.round (XEiJ.pnlInverseL10 * (double) tx + XEiJ.pnlInverseL11 * (double) ty);
          } else {
            int ox = XEiJ.pnlScreenX1 + (XEiJ.pnlZoomWidth >> 1);  //画面の中央
            int oy = XEiJ.pnlScreenY1 + (XEiJ.pnlZoomHeight >> 1);
            XEiJ.rbtRobot.mouseMove (XEiJ.pnlGlobalX + ox, XEiJ.pnlGlobalY + oy);  //マウスカーソルを画面の中央に戻す
            dx = Mouse.musPanelX - ox;
            dy = Mouse.musPanelY - oy;
          }
          if (Mouse.musExclusiveStart) {  //エクスクルーシブに切り替えた直後
            //エクスクルーシブに切り替えた直後の1回だけ相対位置を無視する
            //  エクスクルーシブに切り替える直前にマウスカーソルが画面の中央から離れていると切り替えた瞬間に画面の端に飛んでしまう
            dx = 0;
            dy = 0;
            Mouse.musExclusiveStart = false;
          }
          //  上下左右のレスポンスが非対称になると気持ち悪いので冗長に書く
          //  rbzで返すので負数のときのゼロ拡張を忘れないこと
          if (dx != 0) {
            if (dx >= 0) {
              //dx = dx * Mouse.musSpeedRatioX + 32768 >> 16;
              dx = dx * Mouse.musSpeedRatioX >> 16;
              if (127 < dx) {
                d |= 0x10;
                dx = 127;
              }
            } else {
              //dx = -(-dx * Mouse.musSpeedRatioX + 32768 >> 16);
              dx = -(-dx * Mouse.musSpeedRatioX >> 16);
              if (dx < -128) {
                d |= 0x20;
                dx = -128;
              }
              dx &= 0xff;
            }
          }
          if (dy != 0) {
            if (dy >= 0) {
              //dy = dy * Mouse.musSpeedRatioY + 32768 >> 16;
              dy = dy * Mouse.musSpeedRatioY >> 16;
              if (127 < dy) {
                d |= 0x40;
                dy = 127;
              }
            } else {
              //dy = -(-dy * Mouse.musSpeedRatioY + 32768 >> 16);
              dy = -(-dy * Mouse.musSpeedRatioY >> 16);
              if (dy < -128) {
                d |= 0x80;
                dy = -128;
              }
              dy &= 0xff;
            }
          }
          scc0bData1 = dx;
          scc0bData2 = dy;
        }  //if シームレス else エクスクルーシブ
        scc0bInputCounter = 1;
      } else if (scc0bInputCounter == 1) {  //2バイト目
        d = scc0bData1;
        if (peek) {
          break;
        }
        scc0bInputCounter = 2;
      } else if (scc0bInputCounter == 2) {  //3バイト目
        d = scc0bData2;
        if (peek) {
          break;
        }
        scc0bInputCounter = 3;
      }
      break;
      //------------------------------------------------
    case SCC_1A_COMMAND & 7:  //ポート1Aコマンド読み出し
      switch (scc1aRegisterNumber) {
      case 0:  //RR0
        d = RS232CTerminal.trmAUXReadRR0 ();
        break;
      case 2:  //RR2
        //非修飾割り込みベクタ
        //  ポート1AのRR2はWR2に設定したベクタをそのまま返す
        d = sccInterruptVector;
        break;
      case 3:  //RR3
        //割り込みペンディング
        //  RR3リクエストからインサービスまでの間セットされている
        //  許可されていない割り込みのビットはセットされない
        d = scc1aReceiveRR3 | scc1aSendRR3 | scc0bReceiveRR3;
        break;
      case 12:  //RR12
        d = scc1aBaudRateGen & 0xff;
        break;
      case 13:  //RR13
        d = scc1aBaudRateGen >> 8 & 0xff;
        break;
      default:
        if (SCC_DEBUG_ON && sccDebugOn != 0) {
          System.out.println ("unimplemented register");
        }
      }
      if (peek) {
        break;
      }
      scc1aRegisterNumber = 0;
      break;
      //------------------------------------------------
    case SCC_1A_DATA & 7:  //ポート1Aデータ読み出し(RS-232C受信)
      d = RS232CTerminal.trmAUXReadData ();
      break;
      //------------------------------------------------
    default:
      d = 0xff;
    }
    if (SCC_DEBUG_ON && ((a & 4) == 0 ? (sccDebugOn & 1) != 0 : (sccDebugOn & 2) != 0)) {
      System.out.printf ("%08x sccRead(0x%08x)=0x%02x\n", XEiJ.regPC0, a, d);
    }
    return d;
  }  //sccReadByte



  //sccWriteByte (a, d)
  //  ライトバイト
  public static void sccWriteByte (int a, int d) {
    XEiJ.mpuClockTime += XEiJ.busWaitTime.scc;
    d &= 0xff;
    if (SCC_DEBUG_ON && ((a & 4) == 0 ? (sccDebugOn & 1) != 0 : (sccDebugOn & 2) != 0)) {
      System.out.printf ("%08x sccWrite(0x%08x,0x%02x)\n", XEiJ.regPC0, a, d);
    }
    switch (a & 7) {
      //------------------------------------------------
    case SCC_0B_COMMAND & 7:  //ポート0Bコマンド書き込み
      switch (scc0bRegisterNumber) {
      case 0:  //WR0
        if ((d & 0xf0) == 0) {  //レジスタ選択
          scc0bRegisterNumber = d;
        } else if (d == 0x38) {  //IUSリセット。割り込み処理が終了し、次の割り込みを受け付ける
          if (scc0bReceiveRR3 != 0) {
            scc0bReceiveRR3 = 0;
            if (scc0bInputCounter < 3) {  //3バイト受信するまで割り込み要求を続ける
              if (scc0bReceiveMask != 0) {
                scc0bReceiveRR3 = SCC_0B_RECEIVE_MASK;
                scc0bReceiveRequest = SCC_0B_RECEIVE_MASK;
                XEiJ.mpuIRR |= XEiJ.MPU_SCC_INTERRUPT_MASK;
              }
            }
          }
        } else if (d == 0x10) {  //ステータス割り込みリセット
        } else if (d == 0x30) {  //エラーリセット
        } else if (d == 0x80) {  //送信CRCジェネレータリセット
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented command");
          }
        }
        return;
      case 1:  //WR1
        scc0bReceiveMask = (d & 0x18) != 0 ? SCC_0B_RECEIVE_MASK : 0;
        if ((d & 0xec) != 0x00) {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented interrupt mode");
          }
        }
        break;
      case 2:  //WR2
        sccInterruptVector = d;  //割り込みベクタ
        if (SCC_DEBUG_ON && sccDebugOn != 0) {
          System.out.printf ("sccInterruptVector=0x%02x\n", sccInterruptVector);
        }
        sccUpdateVector ();
        break;
      case 3:  //WR3
        if (d == 0xc0) {  //受信禁止
        } else if (d == 0xc1) {  //受信許可
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented receiver configuration");
          }
        }
        break;
      case 4:  //WR4
        break;
      case 5:  //WR5
        //  0x80  DTR
        //  0x60  ビット長(0x00=5bit,0x20=7bit,0x40=6bit,0x60=8bit)
        //  0x10  ブレーク
        //  0x08  送信許可
        //  0x04  CRC-16
        //  0x02  RTS
        //  0x01  送信CRC
        {
          int rts = d >> 1 & 1;
          if ((~scc0bRts & rts) != 0) {  //RTS=0→1。MSCTRL=H→Lとなってマウスに送信要求が出される
            scc0bInputCounter = 0;
            //マウスデータ受信開始
            if ((sccMIE & scc0bReceiveMask) != 0) {
              scc0bReceiveRR3 = SCC_0B_RECEIVE_MASK;
              scc0bReceiveRequest = SCC_0B_RECEIVE_MASK;
              XEiJ.mpuIRR |= XEiJ.MPU_SCC_INTERRUPT_MASK;
            }
          }
          scc0bRts = rts;
          if ((d & 0x75) == 0x60) {
          } else {
            if (SCC_DEBUG_ON && sccDebugOn != 0) {
              System.out.println ("unimplemented sender configuration");
            }
          }
        }
        break;
      case 6:  //WR6
        break;
      case 7:  //WR7
        break;
      case 9:  //WR9
        sccWriteWR9 (d);
        break;
      case 10:  //WR10
        if (d == 0x00) {
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented SDLC configuration");
          }
        }
        break;
      case 11:  //WR11
        if (d == 0x50) {  //TRxCは入力
        } else if (d == 0x56) {  //TRxCからボーレートジェネレータを出力
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented clock control");
          }
        }
        break;
      case 12:  //WR12
        scc0bBaudRateGen = (scc0bBaudRateGen & ~0xff) | d;
        break;
      case 13:  //WR13
        scc0bBaudRateGen = d << 8 | (scc0bBaudRateGen & 0xff);
        break;
      case 14:  //WR14
        if (d == 0x02) {  //ボーレートジェネレータ停止
        } else if (d == 0x03) {  //ボーレートジェネレータ動作
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented DPLL configuration");
          }
        }
        break;
      case 15:  //WR15
        if (d == 0x00) {
        } else if (d == 0x80) {
        } else if (d == 0x88) {
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented status interrupt configuration");
          }
        }
        break;
      default:
        if (SCC_DEBUG_ON && sccDebugOn != 0) {
          System.out.println ("unimplemented register");
        }
      }
      scc0bRegisterNumber = 0;
      return;
      //------------------------------------------------
    case SCC_0B_DATA & 7:  //ポート0Bデータ書き込み(マウス送信)
      return;
      //------------------------------------------------
    case SCC_1A_COMMAND & 7:  //ポート1Aコマンド書き込み
      switch (scc1aRegisterNumber) {
      case 0:  //WR0
        if ((d & 0xf0) == 0) {  //レジスタ選択
          scc1aRegisterNumber = d;
        } else if (d == 0x38) {  //IUSリセット。割り込み処理が終了し、次の割り込みを受け付ける
          if (scc1aReceiveRR3 != 0) {
            scc1aReceiveRR3 = 0;
          } else if (scc1aSendRR3 != 0) {
            scc1aSendRR3 = 0;
          }
        } else if (d == 0x10) {  //ステータス割り込みリセット
        } else if (d == 0x30) {  //エラーリセット
        } else if (d == 0x80) {  //送信CRCジェネレータリセット
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented command");
          }
        }
        return;
      case 1:  //WR1
        //  Z85C30UM 5-4
        //  WR1
        //    bit7    WAIT/DMA Request Enable
        //    bit6    /WAIT/DMA Request Function
        //    bit5    WAIT/DMA Request On Receive/Transmit
        //    bit4-3  00=Rx Int Disable
        //            01=Rx Int On First Character or Special Condition
        //            10=Int On All Rx Characters or Special Condition
        //            11=Rx Int On Special Condition Only
        //    bit2    Parity is Special Condition
        //    bit1    Tx Int Enable
        //    bit0    Ext Int Enable
        scc1aReceiveMask = (d & 0x18) != 0 ? SCC_1A_RECEIVE_MASK : 0;  //Rx Int Enable
        scc1aSendMask = (d & 0x02) != 0 ? SCC_1A_SEND_MASK : 0;  //Tx Int Enable
        if ((d & 0xe3) != 0x00) {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented interrupt mode");
          }
        }
        break;
      case 2:  //WR2
        sccInterruptVector = d;  //割り込みベクタ
        if (SCC_DEBUG_ON && sccDebugOn != 0) {
          System.out.printf ("sccInterruptVector=0x%02x\n", sccInterruptVector);
        }
        sccUpdateVector ();
        break;
      case 3:  //WR3
        //  Z85C30UM 5-7
        //  WR3
        //    bit7-6  00=Rx 5 Bits/Character
        //            01=Rx 7 Bits/Character
        //            10=Rx 6 Bits/Character
        //            10=Rx 8 Bits/Character
        //    bit5    1=Auto Enables
        //    bit4    1=Enter Hunt Mode
        //    bit3    1=Rx CRC Enable
        //    bit2    1=Address Search Mode (SDLC)
        //    bit1    1=Sync Characster Load Inhibit
        //    bit0    1=Rx Enable
        if ((d & 0x3e) == 0x00) {
          scc1aRxBits = d >> 6;
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented receiver configuration");
          }
        }
        break;
      case 4:  //WR4
        //  Z85C30UM 5-8
        //  WR4
        //    bit7-6  00=X1 Clock Mode
        //            01=X16 Clock Mode
        //            10=X32 Clock Mode
        //            11=X64 Clock Mode
        //    bit5-4  00=8-Bit Sync Character
        //            01=16-Bit Sync Character
        //            10=SDLC Mode (01111110 Flag)
        //            11=External Sync Mode
        //    bit3-2  00=Sync Modes Enable
        //            01=1 Stop Bit/Character
        //            10=1 1/2 Stop Bits/Character
        //            11=2 Stop Bits/Character
        //    bit1    0=Parity ODD
        //            1=Parity EVEN
        //    bit0    1=Parity Enable
        scc1aClockModeShift = d >> 6 == 0 ? 0 : (d >> 6) + 3;  //0=2^0,1=2^4,2=2^5,3=2^6
        scc1aStop = d >> 2 & 3;
        scc1aParity = d & 3;
        break;
      case 5:  //WR5
        //  Z85C30UM 5-9
        //  WR5
        //    bit7    0=/DTR is High
        //            1=/DTR is Low(準備完了)
        //    bit6-5  00=Tx 5 Bits(Or Less)/Character
        //            01=Tx 7 Bits/Character
        //            10=Tx 6 Bits/Character
        //            11=Tx 8 Bits/Character
        //    bit4    1=Send Break
        //    bit3    1=Tx Enable
        //    bit2    0=SDLC
        //            1=CRC-16
        //    bit1    0=/RTS is High  自分が受信バッファに余裕がない(3/4)から送るのやめてと言う
        //            1=/RTS is Low   自分が受信バッファに余裕がある(1/4)から送っていいよと言う
        //    bit0    1=Tx CRC Enable
        RS232CTerminal.trmAUXSetNotReceiving ((d & 0x02) == 0);
        scc1aTxBits = d >> 5 & 3;
        scc1aTxEnable = d >> 3 & 1;
        RS232CTerminal.trmAUXSetDTR ((d & 0x80) != 0);
        if ((d & 0x15) != 0x00) {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented sender configuration");
          }
        }
        break;
      case 6:  //WR6
        break;
      case 7:  //WR7
        break;
      case 9:  //WR9
        sccWriteWR9 (d);
        break;
      case 10:  //WR10
        if (d == 0x00) {
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented SDLC configuration");
          }
        }
        break;
      case 11:  //WR11
        if (d == 0x50) {  //TRxCは入力
        } else if (d == 0x56) {  //TRxCからボーレートジェネレータを出力
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented clock control");
          }
        }
        break;
      case 12:  //WR12
        //  Z85C30UM 5-18
        //  WR12
        //    bit7-0  Lower Byte Of Time Constant
        //
        //                                   5000000
        //      WR13-WR12                Clock Frequency
        //    Time Constant = -------------------------------------- - 2
        //                    2 x (Desired Rate) x (BR Clock Period)
        //                                            WR4 bit7-6
        //
        //    5000000/(2*(0+2)*16)=78125  76800*1.017
        //    5000000/(2*(2+2)*16)=39062.5  38400*1.017
        //    5000000/(2*(3+2)*16)=31250
        //    5000000/(2*(6+2)*16)=19531.25  19200*1.017
        //    5000000/(2*(14+2)*16)=9765.625  9600*1.017
        //    5000000/(2*(30+2)*16)=4882.8125  4800*1.017
        scc1aBaudRateGen = (scc1aBaudRateGen & ~0xff) | d;
        break;
      case 13:  //WR13
        //  Z85C30UM 5-19
        //  WR13
        //    bit7-0  Upper Byte Of Time Constant
        scc1aBaudRateGen = d << 8 | (scc1aBaudRateGen & 0xff);
        break;
      case 14:  //WR14
        //  Z85C30UM 5-19
        //  WR14
        //    bit7-5  000=Null Command
        //            001=Enter Search Mode
        //            010=Reset Missing Clock
        //            011=Disable DPLL
        //            100=Set Source = BR Generator
        //            101=Set Source = /RTxC
        //            110=Set FM Mode
        //            111=Set NRZI Mode
        //    bit4    1=Local Loopback
        //    bit3    1=Auto Echo
        //    bit2    1=/DTR/Request Function
        //    bit1    BR Generator Source
        //            0=/RTxC pin or XTAL oscillator
        //            1=SCC's PCLK input
        //    bit0    1=BR Generator Enable
        if ((d & 0xfe) == 0x02) {
          scc1aBRGEnable = d & 1;
          if (scc1aBRGEnable != 0) {  //1が書き込まれたとき
            //本来は、ボーレートジェネレータのカウンタの初期値はゼロカウントまたはリセットでロードされる
            //一般的に、
            //動作間隔を決めるカウンタの初期値が上位と下位に分かれていて、ゼロカウントで初期値がロードされる仕組みのとき、
            //上位または下位のどちらか一方を書き換えた直後にゼロカウントになると予期しない初期値がロードされてしまうので、
            //カウンタを止めてから初期値を書き換えるのが作法である
            //それならばボーレートジェネレータの再開をボーレート変更完了の合図として使えるはずだが、
            //tmsio.xとbsio.xがカウンタを止めずにボーレートを変更してしまっているため、その方法が使えない
            //ここではBaud Rate Generator Enableに1が書き込まれたら必ず設定を更新する
            RS232CTerminal.trmReflectSettings (1);
          }
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented DPLL configuration");
          }
        }
        break;
      case 15:  //WR15
        //  Z85C30UM 5-20
        //  WR15
        //    bit7  Break/Abort IE
        //    bit6  Tx Underrun/EOM IE
        //    bit5  CTS IE
        //      SCC 21 CTSA ← RS 5 CTS
        //    bit4  Sync/Hunt IE
        //      SCC 12 SYNCA ← VCC
        //      SCC 25 CTSB ← RS 22 CI Call Indicator (RI Ring Indicator)
        //    bit3  DCD IE
        //      SCC 22 DCDA ← RS 6 DSR Data Set Ready
        //      SCC 24 DCDB ← RS 8 CD Carrier Detect
        //    bit2  SDLC FIFO Enable (Reserved on NMOS)
        //    bit1  Zero Count IE
        //    bit0  WR7' SDLC Feature Enable (Reserved on NMOS/CMOS)
        if (d == 0x00) {
        } else if (d == 0x80) {
        } else if (d == 0x88) {
        } else {
          if (SCC_DEBUG_ON && sccDebugOn != 0) {
            System.out.println ("unimplemented status interrupt configuration");
          }
        }
        break;
      default:
        if (SCC_DEBUG_ON && sccDebugOn != 0) {
          System.out.println ("unimplemented register");
        }
      }
      scc1aRegisterNumber = 0;
      return;
      //------------------------------------------------
    case SCC_1A_DATA & 7:  //ポート1Aデータ書き込み(RS-232C送信)
      RS232CTerminal.trmAUXWriteData (d);
      return;
      //------------------------------------------------
    default:
      ;
    }
  }  //sccWriteByte


  //sccWriteWR9 (d)
  //  Z85C30UM 5-9
  //  WR9
  //    bit7-6  00 No Reset
  //            01 Channel Reset B
  //            10 Channel Reset A
  //            11 Force Hardware Reset
  //    bit5    Software INTACK Enable
  //    bit4    0 Status Low V3 V2 V1
  //            1 Status High V6 V5 V4
  //    bit3    MIE Master Interrupt Enable
  //    bit2    DLC Disable Lower Chain
  //    bit1    NV No Vector
  //    bit0    VIS Vector Includes Status
  public static void sccWriteWR9 (int d) {
    //リセット
    switch (d & 0xc0) {
    case 0x40:  //Channel Reset B
      scc0bRts = 0;
      break;
    case 0x80:  //Channel Reset A
      scc1aBRGEnable = 0;
      scc1aRxEnable = 0;
      scc1aTxEnable = 0;
      RS232CTerminal.trmAUXReset ();
      break;
    case 0xc0:  //Force Hardware Reset
      scc0bRts = 0;
      scc1aBRGEnable = 0;
      scc1aRxEnable = 0;
      scc1aTxEnable = 0;
      RS232CTerminal.trmAUXReset ();
      break;
    }
    //割り込み
    sccVectorInclude = d & 0x11;
    if (SCC_DEBUG_ON && sccDebugOn != 0) {
      System.out.printf ("sccVectorInclude=0x%02x\n", sccVectorInclude);
    }
    sccUpdateVector ();
    sccMIE = (d & 0x08) != 0 ? -1 : 0;
    if ((d & 0x26) != 0) {
      if (SCC_DEBUG_ON && sccDebugOn != 0) {
        System.out.println ("unimplemented interrupt configuration");
      }
    }
  }  //sccWriteWR9


}  //class Z8530