PPI.java
     1: //========================================================================================
     2: //  PPI.java
     3: //    en:8255 PPI -- It emulates joystick ports.
     4: //    ja:8255 PPI -- ジョイスティックポートをエミュレートします。
     5: //  Copyright (C) 2003-2025 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.*;
    16: import java.awt.event.*;
    17: import java.io.*;
    18: import java.util.*;
    19: import javax.swing.*;
    20: import javax.swing.event.*;
    21: 
    22: public class PPI {
    23: 
    24:   //ポート
    25:   //  0x00e9a000-0x00e9bfffは下位3ビットだけデコードされる
    26:   //  偶数アドレスをバイトサイズでリード/ライトするとバスエラーになる
    27:   //  偶数アドレスをワードサイズでリードすると上位バイトは0xffになる
    28:   //
    29:   //  0x00e9a001  PPIポートA  ジョイスティック1
    30:   //         7        6        5        4        3        2        1        0
    31:   //    +--------+--------+--------+--------+--------+--------+--------+--------+
    32:   //    |   PA7  |   PA6  |   PA5  |   PA4  |   PA3  |   PA2  |   PA1  |   PA0  |
    33:   //    |    1   |   JS7  |   JS6  |    1   |   JS4  |   JS3  |   JS2  |   JS1  |
    34:   //    |        |    B   |    A   |        |   →   |   ←   |   ↓   |   ↑   |
    35:   //    +--------+--------+--------+--------+--------+--------+--------+--------+
    36:   //    bit7    PA7  1
    37:   //    bit6    PA6  JS7入力。0=Bが押されている
    38:   //    bit5    PA5  JS6入力。0=Aが押されている
    39:   //    bit4    PA4  1
    40:   //    bit3    PA3  JS4入力。0=→が押されている
    41:   //    bit2    PA2  JS3入力。0=←が押されている
    42:   //    bit1    PA1  JS2入力。0=↓が押されている
    43:   //    bit0    PA0  JS1入力。0=↑が押されている
    44:   //
    45:   //  0x00e9a003  PPIポートB  ジョイスティック2
    46:   //         7        6        5        4        3        2        1        0
    47:   //    +--------+--------+--------+--------+--------+--------+--------+--------+
    48:   //    |   PB7  |   PB6  |   PB5  |   PB4  |   PB3  |   PB2  |   PB1  |   PB0  |
    49:   //    |    1   |   JT7  |   JT6  |    1   |   JT4  |   JT3  |   JT2  |   JT1  |
    50:   //    |        |    B   |    A   |        |   →   |   ←   |   ↓   |   ↑   |
    51:   //    +--------+--------+--------+--------+--------+--------+--------+--------+
    52:   //    bit7    PB7  1
    53:   //    bit6    PB6  JT7入力。0=Bが押されている
    54:   //    bit5    PB5  JT6入力。0=Aが押されている
    55:   //    bit4    PB4  1
    56:   //    bit3    PB3  JT4入力。0=→が押されている
    57:   //    bit2    PB2  JT3入力。0=←が押されている
    58:   //    bit1    PB1  JT2入力。0=↓が押されている
    59:   //    bit0    PB0  JT1入力。0=↑が押されている
    60:   //
    61:   //  0x00e9a005  PPIポートC  ADPCMコントロール。初期値は0x0b
    62:   //         7        6        5        4        3        2        1        0
    63:   //    +--------+--------+--------+--------+--------+--------+--------+--------+
    64:   //    |   PC7  |   PC6  |   PC5  |   PC4  |   PC3  |   PC2  |   PC1  |   PC0  |
    65:   //    |   JS7  |   JS6  |   JT8  |   JS8  |      RATIO      |  LEFT  |  RIGHT |
    66:   //    +--------+--------+--------+--------+--------+--------+--------+--------+
    67:   //    bit7    PC7    JS7出力(負論理)
    68:   //    bit6    PC6    JS6出力(負論理)
    69:   //    bit5    PC5    JT8出力
    70:   //    bit4    PC4    JS8出力
    71:   //    bit3-2  PC3-2  ADPCM分周比。00=1/1024,01=1/768,10=1/512,11=inhibited
    72:   //    bit1    PC1    ADPCM出力LEFT。0=出力する,1=出力しない
    73:   //    bit0    PC0    ADPCM出力RIGHT。0=出力する,1=出力しない
    74:   //
    75:   //  0x00e9a007  PPIコントロール
    76:   //    bit7=0  ポートCで出力に設定されているビットの操作
    77:   //      bit3-1  ビット番号
    78:   //      bit0    設定値
    79:   //    bit7=1  モードの設定。0x92に固定
    80:   //      bit6-5  グループA(ポートAとポートCの上位)のモード(0=モード0,1=モード1,2/3=モード2)。モード0に固定
    81:   //      bit4    ポートAの方向(0=出力,1=入力)。入力に固定
    82:   //      bit3    ポートCの上位の方向(0=出力,1=入力)。出力に固定
    83:   //      bit2    グループB(ポートBとポートCの下位)のモード(0=モード0,1=モード1)。モード0に固定
    84:   //      bit1    ポートBの方向(0=出力,1=入力)。入力に固定
    85:   //      bit0    ポートCの下位の方向(0=出力,1=入力)。出力に固定
    86:   //
    87:   //  ボタンのマスク
    88:   //    上と下または左と右のキーが同時に押された場合は両方キャンセルする
    89:   //    SELECTボタンは上下同時押し、RUNボタンは左右同時押しに割り当てられる
    90:   //
    91:   //  bit4とbit7は入力できない
    92:   //    8255の足がプルアップされているので実機ではどうすることもできない(外付けの回路でどうにかなるものではない)
    93:   //    エミュレータで入力できるようにするのは簡単だか対応しているソフトは存在しないだろう
    94:   //
    95:   //  参考
    96:   //    電脳倶楽部67号  B/MDPAD/M6PAD_AN.DOC
    97:   //    電脳倶楽部77号  B/6B/T_EXPAD.DOC
    98:   //
    99:   public static final int PPI_PORT_A  = 0x00e9a001;   //PPIポートA
   100:   public static final int PPI_PORT_B  = 0x00e9a003;   //PPIポートB
   101:   public static final int PPI_PORT_C  = 0x00e9a005;   //PPIポートC
   102:   public static final int PPI_CONTROL = 0x00e9a007;   //PPIコントロール
   103: 
   104:   //ジョイスティック
   105:   public static Joystick[] ppiJoysticks;
   106:   public static Joystick ppiJoystick1;
   107:   public static Joystick ppiJoystick2;
   108: 
   109:   //モード
   110:   public static boolean ppiJoyKey;  //true=キーボードの一部をジョイスティックとみなす
   111:   public static boolean ppiJoyAuto;  //true=ポートが繰り返し読み出されている間だけ有効
   112:   public static boolean ppiJoyBlock;  //true=ジョイスティック操作として処理されたキー入力データを取り除く
   113: 
   114: 
   115:   //ポートの値
   116:   public static int ppiPortCData;
   117: 
   118:   //自動切り替え
   119:   //  PPIのポートが参照されてから一定時間だけJOYKEYを有効にする
   120:   public static final long PPI_CONTINUOUS_ACCESS_SPAN = XEiJ.TMR_FREQ / 10;  //自動切り替えの有効期間(TMR_FREQ単位)。1/10秒
   121:   public static long ppiLastAccessTime;  //最後にPPIのポートがアクセスされた時刻
   122: 
   123:   //XInput
   124:   public static final boolean PPI_XINPUT_ON = true;  //false=使わない,true=使う
   125:   public static boolean ppiXInputOn;  //false=使わない,true=使う
   126:   public static XInput ppiXInput;  //XInput。nullでなければ動作中
   127:   public static int ppiXInputLastButtons;
   128: 
   129:   //ウインドウ
   130:   public static JFrame ppiFrame;
   131: 
   132:   public static JScrollPane ppiConfigurationScrollPane;
   133: 
   134:   //ppiInit ()
   135:   //  PPIを初期化する
   136:   public static void ppiInit () {
   137:     ppiJoyKey = Settings.sgsGetOnOff ("joykey");
   138:     ppiJoyAuto = Settings.sgsGetOnOff ("joyauto");
   139:     ppiJoyBlock = Settings.sgsGetOnOff ("joyblock");
   140: 
   141:     ppiJoysticks = new Joystick[] {
   142:       new DummyPad (),
   143:       new Normal2ButtonPad (1),
   144:       new Normal2ButtonPad (2),
   145:       new MegaDrive3ButtonPad (1),
   146:       new MegaDrive3ButtonPad (2),
   147:       new MegaDrive6ButtonPad (1),
   148:       new MegaDrive6ButtonPad (2),
   149:       new CyberStickAnalog (1),
   150:       new CyberStickAnalog (2),
   151:       new CyberStickDigital (1),
   152:       new CyberStickDigital (2),
   153:       new Shiromadokun (1),
   154:       new Shiromadokun (2),
   155:     };
   156: 
   157:     String id1 = Settings.sgsGetString ("joystick1");  //ジョイスティックポート1に接続するデバイス
   158:     ppiJoystick1 = ppiJoysticks[0];  //DummyPad
   159:     for (Joystick joystick : ppiJoysticks) {
   160:       if (joystick.getId ().equalsIgnoreCase (id1)) {
   161:         ppiJoystick1 = joystick;
   162:       }
   163:     }
   164: 
   165:     String id2 = Settings.sgsGetString ("joystick2");  //ジョイスティックポート2に接続するデバイス
   166:     ppiJoystick2 = ppiJoysticks[0];  //DummyPad
   167:     for (Joystick joystick : ppiJoysticks) {
   168:       if (joystick.getId ().equalsIgnoreCase (id2)) {
   169:         ppiJoystick2 = joystick;
   170:       }
   171:     }
   172: 
   173:     if (PPI_XINPUT_ON) {
   174:       ppiXInputOn = XEiJ.prgIsWindows && Settings.sgsGetOnOff ("xinput");
   175:       if (ppiXInputOn) {
   176:         ppiXInputStart ();
   177:       }
   178:       ppiXInputLastButtons = 0;
   179:     }
   180: 
   181:     ppiReset ();
   182:   }  //ppiInit
   183: 
   184:   //ppiTini ()
   185:   //  後始末
   186:   public static void ppiTini () {
   187:     Settings.sgsPutOnOff ("joykey", ppiJoyKey);
   188:     Settings.sgsPutOnOff ("joyauto", ppiJoyAuto);
   189:     Settings.sgsPutOnOff ("joyblock", ppiJoyBlock);
   190:     for (Joystick joystick : ppiJoysticks) {
   191:       joystick.tini ();
   192:     }
   193:     Settings.sgsPutString ("joystick1", ppiJoystick1.getId ());
   194:     Settings.sgsPutString ("joystick2", ppiJoystick2.getId ());
   195:     //XInput
   196:     if (PPI_XINPUT_ON) {
   197:       Settings.sgsPutOnOff ("xinput", ppiXInputOn);
   198:       if (ppiXInputOn) {
   199:         ppiXInputOn = false;
   200:         ppiXInputEnd ();
   201:       }
   202:     }
   203:   }  //ppiTini
   204: 
   205:   //ppiReset ()
   206:   //  リセット
   207:   public static void ppiReset () {
   208:     ppiPortCData = 0;
   209:     ppiLastAccessTime = 0L;
   210:   }
   211: 
   212: 
   213:   public static void ppiXInputStart () {
   214:     if (ppiXInput == null) {
   215:       System.out.println (Multilingual.mlnJapanese ?
   216:                           "XInput のポーリングを開始します" :
   217:                           "Starts polling XInput");
   218:       ppiXInput = new XInput ();
   219:     }
   220:   }
   221: 
   222:   public static void ppiXInputEnd () {
   223:     if (ppiXInput != null) {
   224:       System.out.println (Multilingual.mlnJapanese ?
   225:                           "XInput のポーリングを終了します" :
   226:                           "Ends polling XInput");
   227:       ppiXInput.end ();
   228:       ppiXInput = null;
   229:     }
   230:   }
   231: 
   232: 
   233:   //ppiStart ()
   234:   public static void ppiStart () {
   235:     if (RestorableFrame.rfmGetOpened (Settings.SGS_PPI_FRAME_KEY)) {
   236:       ppiOpen ();
   237:     }
   238:   }
   239: 
   240:   //ppiOpen ()
   241:   //  ジョイスティックの設定ウインドウを開く
   242:   public static void ppiOpen () {
   243:     if (ppiFrame == null) {
   244:       ppiMakeFrame ();
   245:     }
   246:     XEiJ.pnlExitFullScreen (false);
   247:     ppiFrame.setVisible (true);
   248:   }
   249: 
   250:   //ppiMakeFrame ()
   251:   //  ジョイスティックポートの設定ウインドウを作る
   252:   //  ここでは開かない
   253:   public static void ppiMakeFrame () {
   254: 
   255:     //アクションリスナー
   256:     ActionListener actionListener = new ActionListener () {
   257:       @Override public void actionPerformed (ActionEvent ae) {
   258:         Object source = ae.getSource ();
   259:         String command = ae.getActionCommand ();
   260:         switch (command) {
   261:         case "Consider part of keyboard as joystick":
   262:           //キーボードの一部をジョイスティックとみなす
   263:           ppiJoyKey = ((JCheckBox) ae.getSource ()).isSelected ();
   264:           break;
   265:         case "Enabled only while the port is read repeatedly":
   266:           //ポートが繰り返し読み出されている間だけ有効
   267:           ppiJoyAuto = ((JCheckBox) ae.getSource ()).isSelected ();
   268:           break;
   269:         case "Remove key input data processed as a joystick operation":
   270:           //ジョイスティック操作として処理されたキー入力データを取り除く
   271:           ppiJoyBlock = ((JCheckBox) ae.getSource ()).isSelected ();
   272:           break;
   273:           //
   274:         case "XInput":
   275:           if (PPI_XINPUT_ON) {
   276:             if (((JCheckBox) ae.getSource ()).isSelected ()) {  //on
   277:               if (!ppiXInputOn) {  //off→on
   278:                 ppiXInputOn = true;
   279:                 ppiXInputStart ();
   280:               }
   281:             } else {  //off
   282:               if (ppiXInputOn) {  //on→off
   283:                 ppiXInputOn = false;
   284:                 ppiXInputEnd ();
   285:               }
   286:             }
   287:           }
   288:         }
   289:       }
   290:     };
   291: 
   292:     //ジョイスティックポートのメニュー
   293:     //       0   1  2
   294:     //   0  ポート  接続
   295:     //   1   1   2
   296:     //   2  -------------------------
   297:     //   3  RB  RB  なし
   298:     //   4  RB  RB  ノーマル2ボタンパッド#1
   299:     //   5  RB  RB  ノーマル2ボタンパッド#2
   300:     //   6  RB  RB  メガドラ3ボタンパッド#1
   301:     //   7  RB  RB  メガドラ3ボタンパッド#2
   302:     //   8  RB  RB  メガドラ6ボタンパッド#1
   303:     //   9  RB  RB  メガドラ6ボタンパッド#2
   304:     //  10  RB  RB  サイバースティック(デジタルモード)#1
   305:     //  12  RB  RB  サイバースティック(デジタルモード)#2
   306:     //  13  RB  RB  サイバースティック(アナログモード)#1
   307:     //  14  RB  RB  サイバースティック(アナログモード)#2
   308:     //  15  RB  RB  白窓君#1
   309:     //  16  RB  RB  白窓君#2
   310:     ButtonGroup port1Group = new ButtonGroup ();
   311:     ButtonGroup port2Group = new ButtonGroup ();
   312:     ActionListener port1Listener = new ActionListener () {
   313:       @Override public void actionPerformed (ActionEvent ae) {
   314:         Joystick joyStick = ppiJoysticks[Integer.parseInt (ae.getActionCommand ())];
   315:         if (ppiJoystick1 != joyStick) {
   316:           ppiJoystick1.reset ();
   317:           ppiJoystick1 = joyStick;
   318:         }
   319:         ppiConfigurationScrollPane.setViewportView (joyStick.getConfigurationPanel ());
   320:       }
   321:     };
   322:     ActionListener port2Listener = new ActionListener () {
   323:       @Override public void actionPerformed (ActionEvent ae) {
   324:         Joystick joyStick = ppiJoysticks[Integer.parseInt (ae.getActionCommand ())];
   325:         if (ppiJoystick2 != joyStick) {
   326:           ppiJoystick2.reset ();
   327:           ppiJoystick2 = joyStick;
   328:         }
   329:         ppiConfigurationScrollPane.setViewportView (joyStick.getConfigurationPanel ());
   330:       }
   331:     };
   332:     ArrayList<Object> cellList = new ArrayList<Object> ();
   333:     cellList.add (Multilingual.mlnText (ComponentFactory.createLabel ("Port"), "ja", "ポート"));  //(0,0)-(1,0)
   334:     cellList.add (Multilingual.mlnText (ComponentFactory.createLabel ("Connect to"), "ja", "接続"));  //(2,0)-(2,1)
   335:     cellList.add ("1");  //(0,1)
   336:     cellList.add ("2");  //(1,1)
   337:     cellList.add (ComponentFactory.createHorizontalSeparator ());  //(0,2)-(2,2)
   338:     for (int i = 0; i < ppiJoysticks.length; i++) {
   339:       Joystick joyStick = ppiJoysticks[i];
   340:       cellList.add (ComponentFactory.setText (ComponentFactory.createRadioButton (
   341:         port1Group, joyStick == ppiJoystick1, String.valueOf (i), port1Listener), ""));  //(0,3+i)
   342:       cellList.add (ComponentFactory.setText (ComponentFactory.createRadioButton (
   343:         port2Group, joyStick == ppiJoystick2, String.valueOf (i), port2Listener), ""));  //(1,3+i)
   344:       cellList.add (Multilingual.mlnText (ComponentFactory.createLabel (joyStick.getNameEn ()), "ja", joyStick.getNameJa ()));  //(3,3)
   345:     }
   346:     JScrollPane portMenuPanel = new JScrollPane (
   347:       ComponentFactory.createGridPanel (
   348:         3, 3 + ppiJoysticks.length,
   349:         "paddingLeft=3,paddingRight=3,center",   //gridStyles
   350:         "",  //colStyles
   351:         "italic;italic;colSpan=3,widen",  //rowStyles
   352:         "colSpan=2;rowSpan=2",  //cellStyles
   353:         cellList.toArray (new Object[0])));
   354: 
   355:     //個々のジョイスティックの設定パネル
   356:     ppiConfigurationScrollPane = new JScrollPane ((((ppiJoystick1 instanceof DummyPad) &&
   357:                                                     !(ppiJoystick2 instanceof DummyPad)) ||  //2だけ接続されている
   358:                                                    (!(ppiJoystick1 instanceof Shiromadokun) &&
   359:                                                     (ppiJoystick2 instanceof Shiromadokun))  //2だけ白窓君
   360:                                                    ? ppiJoystick2 : ppiJoystick1).getConfigurationPanel ());
   361: 
   362:     //ウインドウ
   363:     ppiFrame = Multilingual.mlnTitle (
   364:       ComponentFactory.createRestorableSubFrame (
   365:         Settings.SGS_PPI_FRAME_KEY,
   366:         "Joystick port settings",
   367:         null,
   368:         ComponentFactory.setEmptyBorder (
   369:           ComponentFactory.createVerticalBox (
   370:             //
   371:             !(PPI_XINPUT_ON && XEiJ.prgIsWindows) ? null :
   372:             ComponentFactory.createFlowPanel (
   373:               ComponentFactory.createCheckBox (ppiXInputOn, "XInput", actionListener)
   374:               ),
   375:             !(PPI_XINPUT_ON && XEiJ.prgIsWindows) ? null : ComponentFactory.createHorizontalSeparator (),
   376:             //
   377:             ComponentFactory.createFlowPanel (
   378:               Multilingual.mlnText (
   379:                 ComponentFactory.createCheckBox (
   380:                   ppiJoyKey,
   381:                   "Consider part of keyboard as joystick",
   382:                   actionListener),
   383:                 "ja", "キーボードの一部をジョイスティックとみなす")
   384:               ),
   385:             ComponentFactory.createFlowPanel (
   386:               Multilingual.mlnText (
   387:                 ComponentFactory.createCheckBox (
   388:                   ppiJoyAuto,
   389:                   "Enabled only while the port is read repeatedly",
   390:                   actionListener),
   391:                 "ja", "ポートが繰り返し読み出されている間だけ有効")
   392:               ),
   393:             ComponentFactory.createFlowPanel (
   394:               Multilingual.mlnText (
   395:                 ComponentFactory.createCheckBox (
   396:                   ppiJoyBlock,
   397:                   "Remove key input data processed as a joystick operation",
   398:                   actionListener),
   399:                 "ja", "ジョイスティック操作として処理されたキー入力データを取り除く")
   400:               ),
   401:             Box.createVerticalStrut (5),
   402:             ComponentFactory.createHorizontalBox (
   403:               ComponentFactory.createHorizontalSplitPane (
   404:                 portMenuPanel,
   405:                 ppiConfigurationScrollPane)
   406:               )
   407:             ),
   408:           5, 5, 5, 5)),
   409:       "ja", "ジョイスティックポート設定");
   410:   }  //ppiMakeFrame()
   411: 
   412:   //consume = ppiInput (ke, pressed)
   413:   //  JOYKEYの処理
   414:   //  consume  true=入力をキーボードに渡さない
   415:   public static boolean ppiInput (KeyEvent ke, boolean pressed) {
   416:     boolean consume = false;
   417:     if (ppiJoyKey && (!ppiJoyAuto || XEiJ.mpuClockTime < ppiLastAccessTime + PPI_CONTINUOUS_ACCESS_SPAN)) {
   418:       if (ppiJoystick1.input (ke, pressed) ||
   419:           ppiJoystick2.input (ke, pressed)) {
   420:         //押されたときだけキーボード入力を取り除く
   421:         //  特に自動有効化のときは、押されている間に有効になって離されたデータだけ取り除かれると、
   422:         //  キーボード側は押されたままになっていると判断してリピートが止まらなくなる
   423:         consume = pressed && ppiJoyBlock;
   424:       }
   425:     }
   426:     return consume;
   427:   }
   428: 
   429: 
   430:   //  リード
   431:   //  FM音源レジスタのアクセスウェイトのためのPPIの空読みは、ジョイスティックのデータを得ることが目的ではないので、
   432:   //  ジョイスティックポートが連続的に読み出されているとみなさない
   433:   public static int ppiReadByte (int a) {
   434:     int d;
   435:     //
   436:     switch (a & 7) {
   437:       //
   438:     case PPI_PORT_A & 7:
   439:       if (XEiJ.regOC >> 6 != 0b0100_101_000) {  //TST.B以外
   440:         ppiLastAccessTime = XEiJ.mpuClockTime;
   441:       }
   442:       d = ppiJoystick1.readByte () & 0xff;
   443:       break;
   444:       //
   445:     case PPI_PORT_B & 7:
   446:       if (XEiJ.regOC >> 6 != 0b0100_101_000) {  //TST.B以外
   447:         ppiLastAccessTime = XEiJ.mpuClockTime;
   448:       }
   449:       d = ppiJoystick2.readByte () & 0xff;
   450:       break;
   451:       //
   452:     case PPI_PORT_C & 7:
   453:       d = ppiPortCData;
   454:       break;
   455:     default:
   456:       d = 0xff;
   457:     }  //switch a&7
   458:     //
   459:     return d;
   460:   }  //ppiReadByte
   461: 
   462:   //  ライト
   463:   public static void ppiWriteByte (int a, int d) {
   464:     d &= 0xff;
   465:     //
   466:     switch (a & 7) {
   467:       //
   468:     case PPI_PORT_A & 7:
   469:       ppiJoystick1.writeByte (d);
   470:       break;
   471:       //
   472:     case PPI_PORT_B & 7:
   473:       ppiJoystick2.writeByte (d);
   474:       break;
   475:       //
   476:     case PPI_PORT_C & 7:
   477:       ppiPortCData = d;
   478:       //下位4ビット
   479:       ADPCM.pcmSetPan (d);  //パン
   480:       ADPCM.pcmDivider = d >> 2 & 3;  //分周比。0=1/1024,1=1/768,2=1/512,3=inhibited
   481:       ADPCM.pcmUpdateRepeatInterval ();
   482:       //上位4ビット
   483:       ppiJoystick1.setPin8 (d >> 4 & 1);
   484:       ppiJoystick2.setPin8 (d >> 5 & 1);
   485:       ppiJoystick1.setPin6 ((d >> 6 & 1) ^ 1);
   486:       ppiJoystick1.setPin7 ((d >> 7 & 1) ^ 1);
   487:       break;
   488:       //
   489:     case PPI_CONTROL & 7:
   490:       if ((d & 0b1000_0000) == 0b0000_0000) {  //0b0..._nnnx
   491:         int n = (d >> 1) & 7;  //ビット番号
   492:         int x = d & 1;  //データ
   493:         ppiPortCData = ppiPortCData & ~(1 << n) | x << n;
   494:         if (n < 4) {  //下位4ビット
   495:           switch (n) {
   496:           case 0:
   497:           case 1:
   498:             ADPCM.pcmSetPan (ppiPortCData & 3);  //パン
   499:             break;
   500:           case 2:
   501:           case 3:
   502:             ADPCM.pcmDivider = ppiPortCData >> 2 & 3;  //分周比。0=1/1024,1=1/768,2=1/512,3=inhibited
   503:             ADPCM.pcmUpdateRepeatInterval ();
   504:             break;
   505:           }
   506:         } else {  //上位4ビット
   507:           switch (n) {
   508:           case 4:
   509:             ppiJoystick1.setPin8 (x);
   510:             break;
   511:           case 5:
   512:             ppiJoystick2.setPin8 (x);
   513:             break;
   514:           case 6:
   515:             ppiJoystick1.setPin6 (x ^ 1);
   516:             break;
   517:           case 7:
   518:             ppiJoystick1.setPin7 (x ^ 1);
   519:             break;
   520:           }
   521:         }  //if 下位/上位
   522:       } else if ((d & 0b1000_0100) == 0b1000_0000){  //0b1..._.0..
   523:         //!!! 未対応
   524:       } else {  //0b1..._.1..
   525:         //!!! 非対応
   526:       }
   527:     }  //switch a&7
   528:     //
   529:   }  //ppiWriteByte
   530: 
   531: }  //class PPI