FDC.java
     1: //========================================================================================
     2: //  FDC.java
     3: //    en:Floppy disk controller
     4: //    ja:フロッピーディスクコントローラ
     5: //  Copyright (C) 2003-2024 Makoto Kamada
     6: //
     7: //  This file is part of the XEiJ (X68000 Emulator in Java).
     8: //  You can use, modify and redistribute the XEiJ if the conditions are met.
     9: //  Read the XEiJ License for more details.
    10: //  https://stdkmd.net/xeij/
    11: //========================================================================================
    12: 
    13: package xeij;
    14: 
    15: import java.awt.event.*;  //ActionEvent,ActionListener,ComponentAdapter,ComponentEvent,ComponentListener,FocusAdapter,FocusEvent,FocusListener,InputEvent,KeyAdapter,KeyEvent,KeyListener,MouseAdapter,MouseEvent,MouseListener,MouseMotionAdapter,MouseWheelEvent,WindowAdapter,WindowEvent,WindowListener,WindowStateListener
    16: import java.io.*;  //BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter,File,FileInputStream,FileNotFoundException,FileReader,InputStream,InputStreamReader,IOException,OutputStreamWriter,RandomAccessFile
    17: import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
    18: import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap
    19: import javax.swing.*;  //AbstractSpinnerModel,Box,ButtonGroup,DefaultListModel,ImageIcon,JButton,JCheckBox,JCheckBoxMenuItem,JDialog,JFileChooser,JFrame,JLabel,JList,JMenu,JMenuBar,JMenuItem,JPanel,JRadioButton,JScrollPane,JSpinner,JTextArea,JTextField,JTextPane,JViewport,ScrollPaneConstants,SpinnerListModel,SpinnerNumberModel,SwingConstants,SwingUtilities,UIManager,UIDefaults,UnsupportedLookAndFeelException
    20: import javax.swing.filechooser.*;  //FileFilter,FileNameExtensionFilter
    21: 
    22: public class FDC {
    23: 
    24:   public static final boolean FDC_DEBUG_TRACE = false;
    25:   public static final boolean FDC_DEBUG_DEFAULT = true;  //true=起動時からデバッグログを有効にする
    26:   public static boolean fdcDebugLogOn;  //true=デバッグログを出力する
    27: 
    28:   //ポート
    29:   public static final int FDC_STATUS_PORT  = 0x00e94001;  //FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
    30:   public static final int FDC_DATA_PORT    = 0x00e94003;  //FDC データ/コマンド
    31:   public static final int FDC_DRIVE_STATUS = 0x00e94005;  //FDD 状態(挿入|誤挿入|------)/機能(点滅|排出禁止|排出|-|選択####)
    32:   public static final int FDC_DRIVE_SELECT = 0x00e94007;  //FDD $FF/選択(モータON|--|2DD|--|ドライブ##)
    33: 
    34:   //ユニット数
    35:   public static final int FDC_MIN_UNITS = 2;  //最小ユニット数
    36:   public static final int FDC_MAX_UNITS = 4;  //最大ユニット数
    37: 
    38:   public static FDUnit[] fdcUnitArray;  //ユニットの配列
    39: 
    40:   //メニュー
    41:   public static JMenu fdcMenu;
    42: 
    43:   //ファイルフィルタ
    44:   public static javax.swing.filechooser.FileFilter fdcFileFilter;  //java.io.FileFilterと紛らわしい
    45: 
    46:   //開くダイアログ
    47:   public static OpenDialog fdcOpenDialog;  //開くダイアログ。null=作る前
    48:   public static int fdcOpenUnit;  //開くユニットの番号
    49:   public static ArrayList<File[]> fdcOpenHistory;  //作る前に追加されたヒストリ
    50: 
    51:   //フォーマットダイアログ
    52:   public static JDialog fdcFormatDialog;  //ダイアログ
    53:   public static JCheckBox fdcFormatX86SafeCheckBox;  //x86セーフチェックボックス
    54:   public static JFileChooser2 fdcFormatFileChooser;  //ファイルチューザー
    55:   public static FDMedia fdcFormatMedia;  //フォーマットするメディアの種類
    56:   //public static boolean fdcFormatCopySystemFiles;  //true=システムファイルを転送する
    57:   public static boolean fdcFormatX86SafeOn;  //true=x86セーフ
    58:   public static JCheckBox fdcFormatCopyHumanSysCheckBox;  //HUMAN.SYSチェックボックス
    59:   public static JCheckBox fdcFormatCopyCommandXCheckBox;  //COMMAND.Xチェックボックス
    60:   public static boolean fdcFormatCopyHumanSysOn;  //true=HUMAN.SYSを書き込む
    61:   public static boolean fdcFormatCopyCommandXOn;  //true=COMMAND.Xを書き込む
    62: 
    63: 
    64:   //FDCステータス
    65:   //  FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
    66:   //    bit7  RQM  Request for Master  転送準備完了
    67:   //                                     DIO=0(MPU→FDC)のときMPUはRQM=1を待ってから書き込む
    68:   //                                     DIO=1(FDC→MPU)のときMPUはRQM=1を待ってから読み出す
    69:   //    bit6  DIO  Data Input/Output   転送方向
    70:   //                                     0  MPU→FDC。C-PhaseまたはE-Phase
    71:   //                                     1  FDC→MPU。R-PhaseまたはE-Phase
    72:   //    bit5  NDM  Non-DMA Mode        Non-DMAモード
    73:   //                                     1  E-Phase(Non-DMA Modeで転送中)
    74:   //    bit4  CB   FDC Busy            FDCビジー(CB)
    75:   //                                     0  コマンド実行中ではない
    76:   //                                        コマンドが終了してC-Phaseに戻るとき0にする
    77:   //                                        SEEK/RECALIBRATEコマンドでC-PhaseからE-Phase(シーク中)に移行するとき0にする
    78:   //                                        新たなコマンドを入力できる
    79:   //                                        ただし、FDnビジー(DnB)が1のとき転送コマンドを入力してはならない
    80:   //                                     1  コマンド実行中
    81:   //                                        C-PhaseまたはE-Phase(シーク中)でコマンドの1バイト目が入力されたとき1にする
    82:   //                                        新たなコマンドを入力できない
    83:   //    bit3  D3B  FD3 Busy            FDnビジー(DnB)
    84:   //    bit2  D2B  FD2 Busy              0  シーク実行中ではない
    85:   //    bit1  D1B  FD1 Busy                 SESNE INTERRUPT STATUSコマンドのR-Phaseで、
    86:   //    bit0  D0B  FD0 Busy                 シーク終了のリザルトステータスの1バイト目のST0が引き取られたとき0にする
    87:   //                                        FDCビジー(CB)が0ならば転送コマンドを入力できる
    88:   //                                     1  シーク実行中
    89:   //                                        SEEK/RECALIBRATEコマンドでC-PhaseからE-Phase(シーク中)に移行するとき1にする
    90:   //                                        FDCビジー(CB)が0でも転送コマンドを入力してはならない
    91:   //
    92:   //  C-PhaseまたはE-Phase(シーク中)のコマンドの1バイト目
    93:   //    0x00e94001=0x80(RQM=1,DIO=0(MPU→FDC),NDM=0,CB=0)を待って
    94:   //    0x00e94003にライトする
    95:   //
    96:   //  C-PhaseまたはE-Phase(シーク中)のコマンドの2バイト目
    97:   //    0x00e94001=0x00(RQM=0,DIO=0(MPU→FDC),NDM=0,CB=0)ではなくて
    98:   //    0x00e94001=0x10(RQM=0,DIO=0(MPU→FDC),NDM=0,CB=1)ではなくて
    99:   //    0x00e94001=0x90(RQM=1,DIO=0(MPU→FDC),NDM=0,CB=1)を待って
   100:   //    0x00e94003にライトする
   101:   //
   102:   //  R-Phaseのリザルトステータス
   103:   //    0x00e94001=0x10(RQM=0,DIO=0(MPU→FDC),NDM=0,CB=1)ではなくて
   104:   //    0x00e94001=0x50(RQM=0,DIO=1(FDC→MPU),NDM=0,CB=1)ではなくて
   105:   //    0x00e94001=0xd0(RQM=1,DIO=1(FDC→MPU),NDM=0,CB=1)を待って
   106:   //    0x00e94003からリードする
   107:   //
   108:   public static final int FDC_RQM        = 0x80;  //RQM
   109:   public static final int FDC_MPU_TO_FDC = 0x00;  //DIO=0 OUT(MPU→FDC)
   110:   public static final int FDC_FDC_TO_MPU = 0x40;  //DIO=1 IN(FDC→MPU)
   111:   public static final int FDC_NDM        = 0x20;  //NDM
   112:   public static final int FDC_CB         = 0x10;  //CB
   113:   public static final int FDC_D3B        = 0x08;  //D3B
   114:   public static final int FDC_D2B        = 0x04;  //D2B
   115:   public static final int FDC_D1B        = 0x02;  //D1B
   116:   public static final int FDC_D0B        = 0x01;  //D0B
   117:   public static int fdcStatus;  //FDCステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)
   118:   public static int fdcLastStatus;  //最後に表示したFDCステータス
   119: 
   120: 
   121:   //リザルトステータス
   122:   //  リザルトステータスとデバイスエラーの関係はHuman302の0x00010ceeを参照
   123:   //    FDC_ST0_NR  「ディスクが入っていません、入れてください」
   124:   //    FDC_ST1_NW  「プロテクトをはずして、同じディスクを入れてください」
   125:   //    FDC_ST1_DE  「CRCエラー」
   126:   //    FDC_ST2_DD  「CRCエラー」
   127:   //    FDC_ST2_SN  「読み込みエラー」
   128:   //    FDC_ST0_AT  「無効なメディアを使用しました」
   129:   //  ST0
   130:   //    bit6-7  IC   Interrupt Code 割り込みの発生要因
   131:   //                 00  NT  Normal Terminate コマンドの正常終了
   132:   //                 01  AT  Abnormal Terminate コマンドの異常終了
   133:   //                         「無効なメディアを使用しました」
   134:   //                 10  IC  Invalid Command 無効なコマンド
   135:   //                 11  AI  Attention Interrupt デバイスに状態遷移があった
   136:   //    bit5    SE   Seek End
   137:   //                 1  SEEKコマンドまたはRECALIBRATEコマンドのシーク動作が正常終了または異常終了した
   138:   //                    ディスクがないときもセット
   139:   //    bit4    EC   Equipment Check
   140:   //                 1  デバイスからFault信号を受け取った
   141:   //                    RECALIBRATEコマンドでTrack 0信号を一定時間検出できなかった
   142:   //                    _B_RECALIの強制レディチェックでドライブがないときセット
   143:   //    bit3    NR   Not Ready
   144:   //                 1  デバイスがReady状態でない
   145:   //                    「ディスクが入っていません、入れてください」
   146:   //    bit2    HD   Head Address 割り込み発生時のヘッドの状態
   147:   //                 Sense Interrupt Statusコマンドでは常に0
   148:   //    bit1    US1  Unit Select 1
   149:   //    bit0    US0  Unit Select 0
   150:   //                 割り込み発生時のデバイス番号
   151:   public static final int FDC_ST0_NT = 0x00 << 24;
   152:   public static final int FDC_ST0_AT = 0x40 << 24;
   153:   public static final int FDC_ST0_IC = 0x80 << 24;
   154:   public static final int FDC_ST0_AI = 0xc0 << 24;
   155:   public static final int FDC_ST0_SE = 0x20 << 24;
   156:   public static final int FDC_ST0_EC = 0x10 << 24;
   157:   public static final int FDC_ST0_NR = 0x08 << 24;
   158:   //  ST1
   159:   //    bit7    EN   End of Cylinder
   160:   //                 1  EOTで指定した最終セクタを超えてリードまたはライトを続けようとした
   161:   //    bit6    -    常に0
   162:   //    bit5    DE   Data Error
   163:   //                 1  ディスク上のIDまたはデータのCRCエラーを検出した(READ IDコマンドを除く)
   164:   //                    (IDとデータの区別はST2のDDによる)
   165:   //                    「CRCエラー」
   166:   //    bit4    OR   Overrun
   167:   //                 1  MPUまたはDMAが規定時間内にデータ転送を行わなかった
   168:   //    bit3    -    常に0
   169:   //    bit2    ND   No Data
   170:   //                 1  以下のコマンドでIDRで指定したセクタがトラック上に検出できなかった(このときST2のNCもセット)
   171:   //                      READ DATA
   172:   //                      READ DELETED DATA
   173:   //                      WRITE DATA
   174:   //                      WRITE DELETED DATA
   175:   //                      SCAN
   176:   //                    READ IDコマンドでトラック上にCRCエラーのないIDが見つからない
   177:   //                    READ DIAGNOSTICコマンドでセクタIDと指定されたIDRの内容が一致しない
   178:   //    bit1    NW   Not Writable
   179:   //                 1  ライト系コマンドでライトプロテクト信号を検出した
   180:   //                    「プロテクトをはずして、同じディスクを入れてください」
   181:   //    bit0    MA   Missing Address Mark
   182:   //                 1  IDをアクセスするコマンドでインデックスパルスを2回検出するまでにIDAMが見つからなかった
   183:   //                    IDAMが見つかった後、DAMまたはDDAMが見つからなかった(このときST2のMDもセット)
   184:   public static final int FDC_ST1_EN = 0x80 << 16;
   185:   public static final int FDC_ST1_DE = 0x20 << 16;
   186:   public static final int FDC_ST1_OR = 0x10 << 16;
   187:   public static final int FDC_ST1_ND = 0x04 << 16;
   188:   public static final int FDC_ST1_NW = 0x02 << 16;
   189:   public static final int FDC_ST1_MA = 0x01 << 16;
   190:   //  ST2
   191:   //    bit7    -    常に0
   192:   //    bit6    CM   Control Mark
   193:   //                 1  READ DATAコマンドまたはREAD DIAGNOSTICコマンドまたはSCANコマンドでDDAMを検出した
   194:   //                    READ DELETED DATAコマンドでDAMを検出した
   195:   //                    削除データ読み込み時に通常データを読み込もうとしたまたはその逆のときセット
   196:   //    bit5    DD   Data Error in Data Field
   197:   //                 1  CRCエラーが検出された
   198:   //                    「CRCエラー」
   199:   //    bit4    NC   No Cylinder
   200:   //                 1  ST1のNDに付帯して、IDのCバイトが一致せず0xffでもない(READ DIAGNOSTICを除く)
   201:   //                    シリンダが見つからなかったときにセット
   202:   //    bit3    SH   Scan Equal Hit
   203:   //                 1  SCANコマンドでEqual条件を満足した
   204:   //                    ベリファイコマンドで一致したときにセット
   205:   //    bit2    SN   Scan Not Satisfied
   206:   //                 1  SCANコマンドで最終セクタまで条件を満足しなかった
   207:   //                    ベリファイコマンドで不一致があったときにセット
   208:   //                    「読み込みエラー」
   209:   //    bit1    BC   Bad Cylinder
   210:   //                 1  ST1のNDに付帯して、IDのCバイトが0xff(READ DIAGNOSTICを除く)
   211:   //                    シリンダの番号が規定外のときにセット
   212:   //    bit0    MD   Missing Address Mark in Data Field
   213:   //                 1   ST1のMAに付帯して、IDAMが見つかった後、DAMまたはDDAMが見つからなかった
   214:   //                     データフィールドがないときにセット
   215:   public static final int FDC_ST2_CM = 0x40 << 8;
   216:   public static final int FDC_ST2_DD = 0x20 << 8;
   217:   public static final int FDC_ST2_NC = 0x10 << 8;
   218:   public static final int FDC_ST2_SH = 0x08 << 8;
   219:   public static final int FDC_ST2_SN = 0x04 << 8;
   220:   public static final int FDC_ST2_BC = 0x02 << 8;
   221:   public static final int FDC_ST2_MD = 0x01 << 8;
   222:   //  ST3
   223:   //    bit7    FT   Fault
   224:   //                 デバイスからのFault信号の状態
   225:   //    bit6    WP   Write-Protected
   226:   //                 デバイスからのWrite-Protected信号の状態
   227:   //    bit5    RY   Ready
   228:   //                 デバイスからのReady信号の状態
   229:   //                 モータONから372ms後くらいに0→1
   230:   //    bit4    T0   Track 0
   231:   //                 デバイスからのTrack 0信号の状態
   232:   //                 モータONまで0、モータONで0→1
   233:   //    bit3    TS   Two Side
   234:   //                 デバイスからのTwo Side信号の状態
   235:   //                 常に0
   236:   //    bit2    HD   Head Address
   237:   //                 デバイスへのSide Select信号の状態
   238:   //    bit1    US1  Unit Select 1
   239:   //                 デバイスへのUnit Select 1信号の状態
   240:   //    bit0    US0  Unit Select 0
   241:   //                 デバイスへのUnit Select 0信号の状態
   242:   public static final int FDC_ST3_FT = 0x80;
   243:   public static final int FDC_ST3_WP = 0x40;
   244:   public static final int FDC_ST3_RY = 0x20;
   245:   public static final int FDC_ST3_T0 = 0x10;
   246:   public static final int FDC_ST3_TS = 0x08;
   247: 
   248: 
   249:   //コマンド
   250:   public static final String[] FDC_COMMAND_NAME = {  //コマンド名(デバッグ用)
   251:     "INVALID",  //0x00
   252:     "INVALID",  //0x01
   253:     "READ DIAGNOSTIC",  //0x02  トラックのフォーマットを調べる
   254:     "SPECIFY",  //0x03  FDCの動作モードを設定する
   255:     "SENSE DEVICE STATUS",  //0x04  FDDの状態を読み出す
   256:     "WRITE DATA",  //0x05  セクタを指定してデータを書き込む
   257:     "READ DATA",  //0x06  セクタを指定してデータを読み出す
   258:     "RECALIBRATE",  //0x07  ヘッドをシリンダ0(最外周)へ移動させる
   259:     "SENSE INTERRUPT STATUS",  //0x08  FDCの割り込み要因を読み出す
   260:     "WRITE DELETED DATA",  //0x09  セクタを指定して削除データを書き込む
   261:     "READ ID",  //0x0a  セクタのIDを読み出す
   262:     "INVALID",  //0x0b
   263:     "READ DELETED DATA",  //0x0c  セクタを指定して削除データを読み出す
   264:     "WRITE ID",  //0x0d  トラックをフォーマットする
   265:     "INVALID",  //0x0e
   266:     "SEEK",  //0x0f  シリンダを指定してヘッドを移動させる
   267:     "INVALID",  //0x10
   268:     "SCAN EQUAL",  //0x11  条件に合うセクタを探す
   269:     "INVALID",  //0x12
   270:     "INVALID",  //0x13
   271:     "RESET STANDBY",  //0x14  FDCのスタンバイ状態を解除する
   272:     "SET STANDBY",  //0x15  FDCをスタンバイ状態にする
   273:     "SOFTWARE RESET",  //0x16  FDCを初期状態にする
   274:     "INVALID",  //0x17
   275:     "INVALID",  //0x18
   276:     "SCAN LOW OR EQUAL",  //0x19  条件に合うセクタを探す
   277:     "INVALID",  //0x1a
   278:     "INVALID",  //0x1b
   279:     "INVALID",  //0x1c
   280:     "SCAN HIGH OR EQUAL",  //0x1d  条件に合うセクタを探す
   281:     "INVALID",  //0x1e
   282:     "INVALID",  //0x1f
   283:   };
   284: /*
   285:   public static final int[] FDC_COMMAND_LENGTH = {  //コマンドの長さ。INVALIDも含めて1以上
   286:     1,  //0x00 INVALID
   287:     1,  //0x01 INVALID
   288:     9,  //0x02 READ DIAGNOSTIC
   289:     3,  //0x03 SPECIFY
   290:     2,  //0x04 SENSE DEVICE STATUS
   291:     9,  //0x05 WRITE DATA
   292:     9,  //0x06 READ DATA
   293:     2,  //0x07 RECALIBRATE
   294:     1,  //0x08 SENSE INTERRUPT STATUS
   295:     9,  //0x09 WRITE DELETED DATA
   296:     2,  //0x0a READ ID
   297:     1,  //0x0b INVALID
   298:     9,  //0x0c READ DELETED DATA
   299:     6,  //0x0d WRITE ID
   300:     1,  //0x0e INVALID
   301:     3,  //0x0f SEEK
   302:     1,  //0x10 INVALID
   303:     9,  //0x11 SCAN EQUAL
   304:     1,  //0x12 INVALID
   305:     1,  //0x13 INVALID
   306:     1,  //0x14 RESET STANDBY
   307:     1,  //0x15 SET STANDBY
   308:     1,  //0x16 SOFTWARE RESET
   309:     1,  //0x17 INVALID
   310:     1,  //0x18 INVALID
   311:     9,  //0x19 SCAN LOW OR EQUAL
   312:     1,  //0x1a INVALID
   313:     1,  //0x1b INVALID
   314:     1,  //0x1c INVALID
   315:     9,  //0x1d SCAN HIGH OR EQUAL
   316:     1,  //0x1e INVALID
   317:     1,  //0x1f INVALID
   318:   };
   319: */
   320:   //  perl misc/itob.pl xeij/FDC.java FDC_COMMAND_LENGTH
   321:   public static final byte[] FDC_COMMAND_LENGTH = "\1\1\t\3\2\t\t\2\1\t\2\1\t\6\1\3\1\t\1\1\1\1\1\1\1\t\1\1\1\t\1\1".getBytes (XEiJ.ISO_8859_1);
   322:   public static int fdcCommandNumber;  //処理中のコマンド番号。C-Phaseの1バイト目まで-1、2バイト目からR-PhaseまでfdcCommandBuffer[0]&31。シークを伴うコマンドのE-Phase以降はfduCommandNumberにコピーしたものを使う
   323: 
   324:   //バッファ
   325:   public static final byte[] fdcCommandBuffer = new byte[256];  //コマンドバッファ
   326:   public static final byte[] fdcResultBuffer = new byte[256];  //リザルトバッファ
   327:   public static final byte[] fdcTempBuffer = new byte[16384];  //WRITE IDまたはSCANで使用するバッファ
   328:   public static byte[] fdcReadHandle;  //E-Phase(Read)のときfduImageまたはfdcIdBuffer、R-PhaseのときfdcResultBuffer、それ以外はnull
   329:   public static byte[] fdcWriteHandle;  //C-Phase(Write)のときfdcCommandBuffer、E-Phase(Write)のときfduImageまたはfdcIdBuffer、それ以外はnull
   330:   public static int fdcIndex;  //fdcReadHandleまたはfdcWriteHandleの次に読み書きするインデックス
   331:   public static int fdcStart;  //fdcReadHandleまたはfdcWriteHandleの読み書きを開始するインデックス。デバッグ表示用
   332:   public static int fdcLimit;  //fdcReadHandleまたはfdcWriteHandleの読み書きを終了するインデックス
   333: 
   334:   //  強制レディフラグ
   335:   public static boolean fdcEnforcedReady;  //true=強制レディ状態(YM2151のCT2が1)
   336: 
   337:   //  ユニット選択
   338:   //    ドライブコントロール(0x00e94005)のWriteのbit1-0
   339:   //    ドライブステータス(0x00e94005)を読み出すユニットを選択する
   340:   public static FDUnit fdcDriveLastSelected;  //ドライブコントロールで選択されているユニット
   341: 
   342: 
   343:   //ポーリング
   344:   //  実機のFDCはCB==1の間約1ms間隔(3.5インチは約2ms間隔)のポーリングでシークの処理とレディ信号の監視を行っている
   345:   //  しかしポーリングのためのタスクを1個増やすとそれ自体の所要時間だけでなくタスクのスケジューリングの負荷も増えてしまう
   346:   //  使わないときはまったく使わないフロッピーディスクのために全体のパフォーマンスを少しでも低下させたくない
   347:   //  ここではレディ信号の監視にポーリングを使わないことにしてイベントの発生順序をなるべく実機に近付ける
   348:   //
   349: 
   350: 
   351:   //FDC割り込み
   352:   //
   353:   //  転送終了割り込み
   354:   //    READ DATAやWRITE DATAなどの転送コマンドのE-Phase(転送中)が終了したとき
   355:   //      リザルトステータス(ST0,ST1,ST2,C,H,R,N)を作る
   356:   //      R-Phase(RQM=1,DIO=1(FDC→MPU))に移行する
   357:   //      FDC割り込み要求(INT)を1にする
   358:   //    メモ
   359:   //      転送コマンドはFDC全体で同時に1つしか動かず、R-Phaseで発生するFDC割り込みは転送終了割り込みだけなので、
   360:   //      転送終了割り込みが他のFDC割り込みと競合することはない
   361:   //
   362:   //  シーク終了割り込み
   363:   //    SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
   364:   //      FDCビジー(CB)が1のとき
   365:   //        (コマンド実行中。
   366:   //         コマンドは必ず終了するので、コマンドが終了してFDCビジー(CB)が0になったときにFDC割り込み要求(INT)を1にする)
   367:   //        シーク終了割り込み待機(seekEndInterruptWaiting)を1にする
   368:   //      FDCビジー(CB)が0のとき
   369:   //        シーク終了割り込み要求(seekEndInterruptRequest)が0のとき
   370:   //          シーク終了割り込み要求(seekEndInterruptRequest)を1にする
   371:   //          FDC割り込み要求(INT)を1にする
   372:   //
   373:   //  状態遷移割り込み
   374:   //    モータONまたはモータOFFから一定の時間が経ったとき
   375:   //      FDnビジー(DnB)が1のとき
   376:   //        (シーク実行中。
   377:   //         シークは必ず終了するので、シークが終了してFDnビジー(DnB)が0になったときに改めて状態遷移を確認する)
   378:   //        状態遷移確認フラグ(attentionCheckWaiting)を1にする
   379:   //      FDnビジー(DnB)が0のとき
   380:   //        FDCビジー(CB)が1のとき
   381:   //          (コマンド実行中。
   382:   //           コマンドは必ず終了するので、コマンドが終了してFDCビジー(CB)が0になったときに改めて状態遷移を確認する)
   383:   //          状態遷移確認フラグ(attentionCheckWaiting)を1にする
   384:   //        FDCビジー(CB)が0のとき
   385:   //          (シーク実行中でもコマンド実行中でもない。
   386:   //           次のイベントがいつ起きるかわからないのでここでFDC割り込み要求(INT)を1にする。
   387:   //           IOCSはコマンドを出力するとき割り込みを止めていないので、
   388:   //           コマンドの1バイト目を出力した後にFDC割り込みハンドラが呼び出されて誤動作やハングアップする可能性がある)
   389:   //          レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
   390:   //            レディ信号の状態(isReady)を保存する(isReady→RPYn)
   391:   //            状態遷移割り込み要求(attentionInterruptRequest)が0のとき
   392:   //              状態遷移割り込み要求(attentionInterruptRequest)を1にする
   393:   //              FDC割り込み要求(INT)を1にする
   394:   //    メモ
   395:   //      μPD72065ではシーク中のユニットに対してレディ信号の監視が行われない
   396:   //      シーク中に一瞬だけノットレディになってシークが終了する前にレディに戻った場合、その変化は検出されない
   397:   //
   398:   //  FDC割り込み要求(INT)
   399:   //    0  Non-DMAモードのとき転送コマンドのE-Phase(転送中)で1バイト転送されたとき0にする
   400:   //       転送コマンドのR-Phaseで1バイト目のST0が引き取られたとき0にする
   401:   //       SESNE INTERRUPT STATUSコマンドでC-PhaseからR-Phaseに移行するとき0にする
   402:   //    1  Non-DMAモードのとき転送コマンドのE-Phase(転送中)で1バイト転送するとき1にする
   403:   //       READ DATAやWRITE DATAなどの転送コマンドでE-Phase(転送中)からR-Phaseに移行するとき1にする
   404:   //       コマンドが終了してC-Phaseに戻るときシーク終了割り込み待機(seekEndInterruptWaiting)が1のユニットがあれば1にする
   405:   //
   406:   //  コマンドが終了してC-Phaseに戻るとき
   407:   //    FDCビジー(CB)を0にする
   408:   //    C-Phaseに移行する
   409:   //    ユニット0..3について
   410:   //      シークステップ待機(seekStepWaiting)が1のとき
   411:   //        シークステップ待機(seekStepWaiting)を0にする
   412:   //        →シークステップ
   413:   //      FDnビジー(DnB)が1のとき
   414:   //        シーク終了割り込み待機(seekEndInterruptWaiting)が1のとき
   415:   //          シーク終了割り込み待機(seekEndInterruptWaiting)を0にする
   416:   //          シーク終了割り込み要求(seekEndInterruptRequest)を1にする
   417:   //          FDC割り込み要求(INT)を1にする
   418:   //      FDnビジー(DnB)が0のとき
   419:   //        状態遷移確認フラグ(attentionCheckWaiting)が1のとき
   420:   //          状態遷移確認フラグ(attentionCheckWaiting)を0にする
   421:   //          レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
   422:   //            レディ信号の状態(isReady)を保存する(isReady→savedReady)
   423:   //            状態遷移割り込み要求(attentionInterruptRequest)が0のとき
   424:   //              状態遷移割り込み要求(attentionInterruptRequest)を1にする
   425:   //              FDC割り込み要求(INT)を1にする
   426:   //
   427:   //  SEEK/RECALIBRATEコマンド
   428:   //    強制レディ状態で接続されていないとき
   429:   //      AT(Abnormal Terminate)+SE(Seek End)+EC(Equipment Check)のリザルトステータスを準備する
   430:   //      E-Phase(シーク中)に移行する
   431:   //      →SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
   432:   //      メモ
   433:   //        強制レディ状態でRECALIBRATEコマンドを実行してEC(Equipment Check)を返すかどうかでユニットの有無を判断できる
   434:   //        シークティッカーが最初に呼び出された時点で強制レディ状態が解除されてしまっている場合があるので、
   435:   //        RECALIBRATEコマンドは強制レディ状態かどうかを最初に確認しなければならない
   436:   //    ノットレディのとき
   437:   //      AT(Abnormal Terminate)+SE(Seek End)+NR(Not Ready)のリザルトステータスを準備する
   438:   //      E-Phase(シーク中)に移行する
   439:   //      →SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
   440:   //    レディのとき
   441:   //      NT(Normal Terminate)+SE(Seek End)のリザルトステータスを準備する
   442:   //      FDnビジー(DnB)を1にする
   443:   //      目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
   444:   //      ステップレートカウンタ(SRC)を16-SRTにする
   445:   //      シークティッカーを(あれば取り消してから)1ms後に予約する
   446:   //      E-Phase(シーク中)に移行する
   447:   //    メモ
   448:   //      SEEK/RECALIBRATEコマンドは途中のE-Phase(シーク中)の期間FDCビジー(CB)が0になるので、
   449:   //      複数のユニットで並行してSEEK/RECALIBRATEコマンドを実行できる
   450:   //      シーク実行中のユニットの目標シリンダ番号(NCNn)はSEEK/RECALIBRATEコマンドで上書きできる
   451:   //
   452:   //  READ DATAやWRITE DATAなどの転送コマンド
   453:   //    目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
   454:   //    ステップレートカウンタ(SRC)を16-SRTにする
   455:   //    E-Phase(転送中)に移行する(RQM=0で待機する)
   456:   //    シークティッカーを(あれば取り消してから)1ms後に予約する
   457:   //    メモ
   458:   //      READ DATAやWRITE DATAなどの転送コマンドは終わるまでFDCビジー(CB)が1のままなので同時に1つしか動かない
   459:   //      シーク実行中のユニットの目標シリンダ番号(NCNn)をREAD DATAやWRITE DATAなどの転送コマンドで上書きしてはならない
   460:   //      (ここでは上書きして動くようにしている)
   461:   //
   462:   //  シークティッカー
   463:   //    FDCビジー(CB)が1のとき
   464:   //      シークステップ待機(seekStepWaiting)を1にする
   465:   //    FDCビジー(CB)が0のとき
   466:   //      →シークステップ
   467:   //
   468:   //  シークステップ
   469:   //    シリンダ番号(PCNn)と目標シリンダ番号(NCNn)が違うとき
   470:   //      ステップレートカウンタ(SRC)をデクリメントする
   471:   //      ステップレートカウンタ(SRC)が0になったとき
   472:   //        ステップレートカウンタ(SRC)を16-SRTにする
   473:   //        シリンダ番号(PCNn)が目標シリンダ番号(NCNn)よりも小さいとき
   474:   //          シリンダ番号(PCNn)をインクリメントする
   475:   //        シリンダ番号(PCNn)が目標シリンダ番号(NCNn)よりも大きいとき
   476:   //          シリンダ番号(PCNn)をデクリメントする
   477:   //      シークティッカーを1ms後に予約する
   478:   //    シリンダ番号(PCNn)と目標シリンダ番号(NCNn)が同じとき
   479:   //      SEEK/RECALIBRATEコマンドのとき
   480:   //        →SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
   481:   //      READ DATAやWRITE DATAなどの転送コマンドのとき
   482:   //        E-Phase(転送中)に移行する
   483:   //        転送を開始する
   484:   //
   485:   //  SENSE INTERRUPT STATUSコマンド
   486:   //    R-Phaseに移行するとき
   487:   //      FDC割り込み要求(INT)を0にする
   488:   //    ユニット0..3について
   489:   //      FDnビジー(DnB)が1のとき
   490:   //        シーク終了割り込み待機(seekEndInterruptWaiting)が1のとき
   491:   //          シーク終了割り込み待機(seekEndInterruptWaiting)を0にする
   492:   //          シーク終了割り込み要求(seekEndInterruptRequest)を1にする
   493:   //      FDnビジー(DnB)が0のとき
   494:   //        状態遷移確認フラグ(attentionCheckWaiting)が1のとき
   495:   //          状態遷移確認フラグ(attentionCheckWaiting)を0にする
   496:   //          レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
   497:   //            レディ信号の状態(isReady)を保存する(isReady→savedReady)
   498:   //            状態遷移割り込み要求(attentionInterruptRequest)が0のとき
   499:   //              状態遷移割り込み要求(attentionInterruptRequest)を1にする
   500:   //    シーク終了割り込み要求(seekEndInterruptRequest)または状態遷移割り込み要求(attentionInterruptRequest)が1のユニットがないとき
   501:   //      IC(Invalid Command)のリザルトステータスを返す
   502:   //    シーク終了割り込み要求(seekEndInterruptRequest)または状態遷移割り込み要求(attentionInterruptRequest)が1のユニットがあるとき
   503:   //      該当するユニットの中でユニット番号が最小のものについて
   504:   //        シーク終了割り込み要求(seekEndInterruptRequest)が1のとき
   505:   //          シーク終了割り込み要求(seekEndInterruptRequest)を0にする
   506:   //          接続されていないとき
   507:   //            AT(Abnormal Terminate)+SE(Seek End)+EC(Equipment Check)のリザルトステータスを出力する
   508:   //          ノットレディのとき
   509:   //            AT(Abnormal Terminate)+SE(Seek End)+NR(Not Ready)のリザルトステータスを出力する
   510:   //          レディのとき
   511:   //            NT(Normal Terminate)+SE(Seek End)のリザルトステータスを出力する
   512:   //        シーク終了割り込み要求(seekEndInterruptRequest)が0のとき
   513:   //          (状態遷移割り込み要求(attentionInterruptRequest)が1のとき)
   514:   //          状態遷移割り込み要求(attentionInterruptRequest)を0にする
   515:   //          AI(Attention Interrupt)のリザルトステータスを出力する
   516:   //    R-PhaseでSE(Seek End)のリザルトステータスの1バイト目のST0が引き取られたとき
   517:   //      ST0に対応するユニットのFDnビジー(DnB)を0にする
   518:   //    R-Phaseが終わってC-Phaseに戻るとき
   519:   //      シーク終了割り込み要求(seekEndInterruptRequest)が1のユニットが残っているとき
   520:   //        FDC割り込み要求(INT)を1にする
   521:   //        (状態遷移割り込み要求(attentionInterruptRequest)だけが残っている場合はFDC割り込み要求(INT)は1にならない)
   522:   //
   523:   //  FDC割り込みが受け付けられたとき
   524:   //
   525:   //  FDC割り込みハンドラ
   526:   //    R-Phase(RQM=1,DIO=1(FDC→MPU))のとき
   527:   //      リザルトステータス(ST0,ST1,ST2,C,H,R,N)を受け取る
   528:   //      メモ
   529:   //        FDC割り込みが発生したときR-Phase(RQM=1,DIO=1(FDC→MPU))ならば、
   530:   //        それは実行中の転送コマンドの転送が正常終了または異常終了したことを知らせる転送終了割り込みである
   531:   //        MPUがそのまま7バイトのリザルトステータスを引き取れば転送コマンドが終了してC-Phaseに戻る
   532:   //        転送コマンドの実行中に他のユニットでシーク終了割り込み要求や状態遷移割り込み要求があった場合は、
   533:   //        転送コマンドが終了してC-Phaseに戻ったときに改めてFDC割り込みが発生する
   534:   //    C-Phase(RQM=1,DIO=0(MPU→FDC))のとき
   535:   //      SENSE INTERRUPT STATUSコマンドでR-Phase(RQM=1,DIO=1(FDC→MPU))にする
   536:   //      リザルトステータス(ST0,PCN)を受け取る
   537:   //      IC(Invalid Command)でなければSENSE INTERRUPT STATUSコマンドを繰り返す
   538:   //      メモ
   539:   //        C-PhaseのときFDC割り込みハンドラはIC(Invalid Command)が返るまで、
   540:   //        1回の割り込みでSENSE INTERRUPT STATUSコマンドを繰り返してすべてのリザルトステータスを引き取らなければならない
   541:   //        複数のユニットで割り込み要求が発生した場合はユニット番号が最も小さいユニットのリザルトステータスが返る
   542:   //        SENSE INTERRUPT STATUSコマンドが終わってC-Phaseに戻るときシーク終了割り込み要求が残っていると、
   543:   //        改めてFDC割り込み要求が発生する
   544:   //        状態遷移割り込み要求だけが残っているときはFDC割り込み要求は発生しない
   545:   //
   546:   //  FDC割り込みハンドラが終了したとき
   547:   //
   548:   public static int fdcSRT;  //ステップレートタイム。SPECIFYコマンドのSRT
   549: 
   550:   //fdcInit ()
   551:   //  FDCを初期化する
   552:   public static void fdcInit () {
   553:     if (FDC_DEBUG_TRACE) {
   554:       System.out.printf ("%08x fdcInit()\n", XEiJ.regPC0);
   555:       fdcDebugLogOn = FDC_DEBUG_DEFAULT;
   556:     }
   557: 
   558:     //ファイルフィルタ
   559:     //  フロッピーディスクのイメージファイルかどうかを調べる
   560:     //  ファイルチューザーとドロップターゲットで使う
   561:     fdcFileFilter = new javax.swing.filechooser.FileFilter () {  //java.io.FileFilterと紛らわしい
   562:       @Override public boolean accept (File file) {
   563:         if (file.isDirectory ()) {  //ディレクトリがある
   564:           return true;
   565:         }
   566:         if (!file.isFile ()) {  //ファイルがない
   567:           return false;
   568:         }
   569:         String path = file.getPath ();
   570:         if (fdcIsInsertedPath (path)) {  //既に挿入されている
   571:           return false;
   572:         }
   573:         int dotIndex = path.lastIndexOf ('.');
   574:         String upperExt = dotIndex < 0 ? "" : path.substring (dotIndex + 1).toUpperCase ();
   575:         if (upperExt.equals ("DIM")) {  // *.dimはサイズに関係なく通す
   576:           return true;
   577:         }
   578:         long longLength = file.length ();
   579:         if (1024 * 1024 * 16 <= longLength) {  //大きすぎる
   580:           return false;
   581:         }
   582:         int length = (int) longLength;
   583:         for (FDMedia media : FDMedia.FDM_ARRAY) {
   584:           if (media.fdmBytesPerDisk == length) {  //ファイルサイズが一致
   585:             return true;
   586:           }
   587:         }
   588:         return false;
   589:       }
   590:       @Override public String getDescription () {
   591:         return (Multilingual.mlnJapanese ?
   592:                 "フロッピーディスクのイメージファイル (*.XDF,*.2HD,*.2HC,*.DIM,etc.)" :
   593:                 "Floppy disk image files (*.XDF,*.2HD,*.2HC,*.DIM,etc.)");
   594:       }
   595:     };
   596: 
   597:     //開くダイアログ
   598:     fdcOpenDialog = null;
   599:     fdcOpenUnit = 0;
   600:     fdcOpenHistory = new ArrayList<File[]> ();
   601:     for (int i = JFileChooser2.MAXIMUM_HISTORY_COUNT - 1; 0 <= i; i--) {
   602:       fdcAddHistory (JFileChooser2.pathsToFiles (Settings.sgsGetString ("fdhistory" + i)));
   603:     }
   604: 
   605:     //フォーマットダイアログ
   606:     fdcFormatDialog = null;
   607:     fdcFormatX86SafeCheckBox = null;
   608:     fdcFormatFileChooser = null;
   609:     fdcFormatMedia = FDMedia.FDM_2HD;
   610:     //fdcFormatCopySystemFiles = false;
   611:     fdcFormatX86SafeOn = true;
   612:     fdcFormatCopyHumanSysCheckBox = null;
   613:     fdcFormatCopyCommandXCheckBox = null;
   614:     fdcFormatCopyHumanSysOn = true;
   615:     fdcFormatCopyCommandXOn = true;
   616: 
   617:     //パラメータ
   618:     //  ダイアログが書き込み禁止でもパラメータは:Rを付けなければ書き込み禁止にしない
   619:     fdcUnitArray = new FDUnit[FDC_MAX_UNITS];
   620:     for (int u = 0; u < FDC_MAX_UNITS; u++) {
   621:       FDUnit unit = fdcUnitArray[u] = new FDUnit (u);
   622:       if (u < FDC_MIN_UNITS) {
   623:         unit.connect (false);  //ドライブ0とドライブ1は最初から接続されていて切り離せない
   624:       }
   625:       String path = Settings.sgsGetString ("fd" + u);
   626:       boolean userWriteProtect = false;
   627:       if (path.toUpperCase ().endsWith (":R")) {  //書き込み禁止モードで開く
   628:         path = path.substring (0, path.length () - 2);
   629:         userWriteProtect = true;
   630:       }
   631:       boolean hostWriteProtect = !new File (path).canWrite ();
   632:       if (path.length () != 0) {
   633:         unit.connect (true);  //接続されていなければ接続する
   634:         if (unit.insert (path,
   635:                          userWriteProtect || hostWriteProtect)) {  //挿入できた
   636:           fdcAddHistory (new File (path).getAbsoluteFile ());
   637:         }
   638:       }
   639:     }
   640: 
   641:     //fdcCommandBuffer = new byte[256];
   642:     //fdcResultBuffer = new byte[256];
   643:     //fdcTempBuffer = new byte[16384];
   644:     fdcCPhase ();  //C-Phase
   645:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   646:       fdcLastStatus = -1;
   647:     }
   648:     fdcEnforcedReady = false;  //強制レディOFF
   649:     fdcDriveLastSelected = null;  //ドライブが選択されていない
   650:     fdcSRT = 3;  //SRT
   651:   }  //fdcInit()
   652: 
   653:   //fdcReset ()
   654:   public static void fdcReset () {
   655:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   656:       System.out.printf ("%08x fdcReset()\n", XEiJ.regPC0);
   657:     }
   658: 
   659:     //リセットしたときCBを0にしないと
   660:     //    ~FF8EB6:
   661:     //            move.b  (a1),d1                 ;[$00E94001].b:FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
   662:     //            btst.l  #$04,d1
   663:     //            bne.s   ~FF8EB6
   664:     //のループから抜けられなくなる
   665:     fdcCPhase ();  //C-Phase
   666:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   667:       fdcLastStatus = -1;
   668:     }
   669:     fdcEnforcedReady = false;  //強制レディOFF
   670:     fdcDriveLastSelected = null;  //ドライブが選択されていない
   671:     fdcSRT = 3;  //SRT
   672: 
   673:     //メディアが挿入されているユニットがあるときFDD割り込みを要求する
   674:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
   675:       FDUnit unit = fdcUnitArray[u];
   676:       if (unit.fduIsInserted ()) {  //挿入されているとき
   677:         IOInterrupt.ioiFddFall ();
   678:         IOInterrupt.ioiFddRise ();
   679:         break;
   680:       }
   681:     }
   682: 
   683:   }  //fdcReset()
   684: 
   685:   //fdcTini ()
   686:   //  後始末
   687:   //  イメージファイルに書き出す
   688:   public static void fdcTini () {
   689:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   690:       System.out.printf ("%08x fdcTini()\n", XEiJ.regPC0);
   691:     }
   692:     for (FDUnit unit : fdcUnitArray) {
   693:       unit.fduTini ();
   694:     }
   695: 
   696:     //開くダイアログ
   697:     //  開くダイアログを作らなかったときはパラメータを更新しない
   698:     if (fdcOpenDialog != null) {
   699:       Settings.sgsPutOnOff ("fdreadonly", fdcOpenDialog.getReadOnly ());
   700:       Settings.sgsPutOnOff ("fdappreboot", fdcOpenDialog.getReboot ());
   701:       ArrayList<String> pathsList = fdcOpenDialog.getHistory ();
   702:       int n = pathsList.size ();
   703:       for (int i = 0; i < n; i++) {
   704:         Settings.sgsPutString ("fdhistory" + i, pathsList.get (i));
   705:       }
   706:       for (int i = n; i < FDC_MAX_UNITS; i++) {
   707:         Settings.sgsPutString ("fdhistory" + i, "");
   708:       }
   709:     }
   710: 
   711:     //ユニット
   712:     for (int u = 0; u < FDC_MAX_UNITS; u++) {
   713:       AbstractUnit unit = fdcUnitArray[u];
   714:       Settings.sgsPutString (
   715:         "fd" + u,
   716:         unit.abuConnected && unit.abuInserted ?
   717:         unit.abuWriteProtected ? unit.abuPath + ":R" : unit.abuPath :
   718:         "");
   719:     }
   720: 
   721:   }  //fdcTini()
   722: 
   723:   public static void fdcMakeMenu () {
   724: 
   725:     //アクションリスナー
   726:     ActionListener listener = new ActionListener () {
   727:       @Override public void actionPerformed (ActionEvent ae) {
   728:         Object source = ae.getSource ();
   729:         String command = ae.getActionCommand ();
   730:         switch (command) {
   731:         case "Create new floppy disk image files":  //フロッピーディスクのイメージファイルの新規作成
   732:           fdcOpenFormatDialog ();
   733:           break;
   734:         case "FDC debug log":  //FDC デバッグログ
   735:           if (FDC_DEBUG_TRACE) {
   736:             fdcDebugLogOn = ((JCheckBoxMenuItem) source).isSelected ();
   737:           }
   738:           break;
   739:         }
   740:       }
   741:     };
   742: 
   743:     //FDメニュー
   744:     fdcMenu = ComponentFactory.createMenu ("FD");  //横に長いとサブメニューを開きにくいので短くする
   745:     ComponentFactory.addComponents (
   746:       fdcMenu,
   747:       ComponentFactory.createHorizontalBox (
   748:         Multilingual.mlnText (ComponentFactory.createLabel ("Floppy disk"),
   749:                               "ja", "フロッピーディスク")),
   750:       ComponentFactory.createHorizontalSeparator ()
   751:       );
   752:     for (FDUnit unit : fdcUnitArray) {
   753:       fdcMenu.add (unit.getMenuBox ());
   754:     }
   755:     ComponentFactory.addComponents (
   756:       fdcMenu,
   757:       ComponentFactory.createHorizontalSeparator (),
   758:       Multilingual.mlnText (ComponentFactory.createMenuItem ("Create new floppy disk image files", listener),
   759:                             "ja", "フロッピーディスクのイメージファイルの新規作成"),
   760:       !FDC_DEBUG_TRACE ? null :
   761:       Multilingual.mlnText (ComponentFactory.createCheckBoxMenuItem (fdcDebugLogOn, "FDC debug log", listener), "ja", "FDC デバッグログ")
   762:       );
   763: 
   764:   }
   765: 
   766:   //inserted = fdcIsInsertedPath (path)
   767:   //  パスで指定したファイルが既に挿入されているか調べる
   768:   public static boolean fdcIsInsertedPath (String path) {
   769:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   770:       System.out.printf ("%08x fdcIsInsertedPath(\"%s\")\n", XEiJ.regPC0, path);
   771:     }
   772:     boolean inserted = false;
   773:     for (FDUnit unit : fdcUnitArray) {
   774:       if (unit != null &&
   775:           unit.abuConnected &&  //接続されている
   776:           unit.fduIsInserted () &&  //挿入されている
   777:           unit.abuPath.equals (path)) {  //パスが一致している
   778:         inserted = true;  //既に挿入されている
   779:         break;
   780:       }
   781:     }
   782:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   783:       System.out.printf ("\tinserted=%b\n", inserted);
   784:     }
   785:     return inserted;
   786:   }  //fdcIsInsertedPath(String)
   787: 
   788:   static class OpenDialog extends AbstractOpenDialog {
   789:     public OpenDialog () {
   790:       super (XEiJ.frmFrame,
   791:              "Open floppy disk image files",
   792:              "フロッピーディスクのイメージファイルを開く",
   793:              false,  //ファイル
   794:              fdcFileFilter);
   795:     }
   796:     @Override public void openFiles (File[] files, boolean reboot) {
   797:       fdcOpenFiles (files, reboot);
   798:     }
   799:   }  //class OpenDialog
   800: 
   801:   //fdcOpenFiles (list, reset)
   802:   //  開くダイアログで選択されたファイルを開く
   803:   public static void fdcOpenFiles (File[] list, boolean reset) {
   804:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   805:       System.out.printf ("%08x fdcOpenFiles({", XEiJ.regPC0);
   806:       for (int i = 0; i < list.length; i++) {
   807:         if (0 < i) {
   808:           System.out.print (',');
   809:         }
   810:         System.out.printf ("\"%s\"", list[i].getPath ());
   811:       }
   812:       System.out.printf ("},%b)\n", reset);
   813:     }
   814:     boolean success = true;
   815:     for (int u = fdcOpenUnit, k = 0; k < list.length; ) {
   816:       if (FDC_MAX_UNITS <= u) {  //ユニットが足りない
   817:         success = false;  //失敗
   818:         break;
   819:       }
   820:       FDUnit unit = fdcUnitArray[u];  //ユニット
   821:       if (!unit.abuConnected) {  //接続されていない
   822:         u++;
   823:         continue;
   824:       }
   825:       File file = list[k++];  //イメージファイル
   826:       if (!file.isFile ()) {  //イメージファイルが存在しない
   827:         success = false;  //失敗
   828:         continue;
   829:       }
   830:       if (!unit.insert (file.getPath (),
   831:                         fdcOpenDialog.getReadOnly () || !file.canWrite ())) {  //挿入できない
   832:         success = false;  //失敗
   833:         continue;
   834:       }
   835:       u++;
   836:     }
   837:     if (success) {  //すべて挿入できた
   838:       fdcAddHistory (list);  //ヒストリに追加する
   839:       if (reset) {  //ここから再起動
   840:         XEiJ.mpuReset (0x9070 | fdcOpenUnit << 8, -1);
   841:       }
   842:     }
   843:   }  //fdcOpenFiles
   844: 
   845:   //fdcMakeFormatDialog ()
   846:   //  フォーマットダイアログを作る
   847:   //  コマンドラインのみ
   848:   public static void fdcMakeFormatDialog () {
   849: 
   850:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
   851:       System.out.printf ("%08x fdcMakeFormatDialog()\n", XEiJ.regPC0);
   852:     }
   853: 
   854:     //アクションリスナー
   855:     ActionListener listener = new ActionListener () {
   856:       @Override public void actionPerformed (ActionEvent ae) {
   857:         switch (ae.getActionCommand ()) {
   858:         case JFileChooser.APPROVE_SELECTION:
   859:         case "Start formatting":  //フォーマットを開始する
   860:           {
   861:             File[] list = fdcFormatFileChooser.getSelectedFiles ();
   862:             if (list.length > 0) {
   863:               fdcFormatDialog.setVisible (false);
   864:               if (!fdcFormatFiles (list)) {
   865:                 //!!! 失敗
   866:               }
   867:             }
   868:           }
   869:           break;
   870:         case JFileChooser.CANCEL_SELECTION:
   871:         case "Cancel":  //キャンセル
   872:           fdcFormatDialog.setVisible (false);
   873:           break;
   874:         case "2HD (1232KB)":
   875:           fdcFormatMedia = FDMedia.FDM_2HD;
   876:           fdcFormatX86SafeCheckBox.setSelected (false);
   877:           fdcFormatX86SafeCheckBox.setEnabled (false);
   878:           break;
   879:         case "2HC (1200KB)":
   880:           fdcFormatMedia = FDMedia.FDM_2HC;
   881:           fdcFormatX86SafeCheckBox.setSelected (false);
   882:           fdcFormatX86SafeCheckBox.setEnabled (false);
   883:           break;
   884:         case "2DD (640KB)":
   885:           fdcFormatMedia = FDMedia.FDM_2DD8;
   886:           fdcFormatX86SafeCheckBox.setSelected (false);
   887:           fdcFormatX86SafeCheckBox.setEnabled (false);
   888:           break;
   889:         case "2DD (720KB)":
   890:           fdcFormatMedia = FDMedia.FDM_2DD9;
   891:           fdcFormatX86SafeCheckBox.setSelected (false);
   892:           fdcFormatX86SafeCheckBox.setEnabled (false);
   893:           break;
   894:         case "2HQ (1440KB)":
   895:           fdcFormatMedia = FDMedia.FDM_2HQ;
   896:           fdcFormatX86SafeCheckBox.setEnabled (true);
   897:           fdcFormatX86SafeCheckBox.setSelected (fdcFormatX86SafeOn);
   898:           break;
   899:         case "2DD (800KB)":
   900:           fdcFormatMedia = FDMedia.FDM_2DD10;
   901:           fdcFormatX86SafeCheckBox.setSelected (false);
   902:           fdcFormatX86SafeCheckBox.setEnabled (false);
   903:           break;
   904:         case "2HDE(1440KB)":
   905:           fdcFormatMedia = FDMedia.FDM_2HDE;
   906:           fdcFormatX86SafeCheckBox.setSelected (false);
   907:           fdcFormatX86SafeCheckBox.setEnabled (false);
   908:           break;
   909:         case "2HS (1440KB)":
   910:           fdcFormatMedia = FDMedia.FDM_2HS;
   911:           fdcFormatX86SafeCheckBox.setSelected (false);
   912:           fdcFormatX86SafeCheckBox.setEnabled (false);
   913:           break;
   914:           //case "Copy System Files":  //システムファイルを転送する
   915:           //  fdcFormatCopySystemFiles = ((JCheckBox) ae.getSource ()).isSelected ();
   916:           //  break;
   917:         case "HUMAN.SYS":
   918:           fdcFormatCopyHumanSysOn = fdcFormatCopyHumanSysCheckBox.isSelected ();  //HUMAN.SYSを書き込む/書き込まない
   919:           if (fdcFormatCopyHumanSysOn) {  //HUMAN.SYSを書き込む
   920:             fdcFormatCopyCommandXCheckBox.setEnabled (true);  //COMMAND.Xを書き込むかどうか選択できる
   921:             fdcFormatCopyCommandXCheckBox.setSelected (fdcFormatCopyCommandXOn);  //COMMAND.Xを書き込む/書き込まない
   922:           } else {  //HUMAN.SYSを書き込まない
   923:             fdcFormatCopyCommandXCheckBox.setEnabled (false);  //COMMAND.Xを書き込むかどうか選択できない
   924:             fdcFormatCopyCommandXCheckBox.setSelected (false);  //COMMAND.Xを書き込まない
   925:           }
   926:           break;
   927:         case "COMMAND.X":
   928:           fdcFormatCopyCommandXOn = fdcFormatCopyCommandXCheckBox.isSelected ();  //COMMAND.Xを書き込む/書き込まない
   929:           break;
   930:         case "x86-safe":  //x86 セーフ
   931:           fdcFormatX86SafeOn = ((JCheckBox) ae.getSource ()).isSelected ();
   932:           break;
   933:         }
   934:       }
   935:     };
   936: 
   937:     //ファイルチューザー
   938:     fdcMakeFormatFileChooser ();
   939:     fdcFormatFileChooser.setFileFilter (fdcFileFilter);
   940:     fdcFormatFileChooser.addActionListener (listener);
   941: 
   942:     //ダイアログ
   943:     ButtonGroup mediaGroup = new ButtonGroup ();
   944:     fdcFormatDialog = Multilingual.mlnTitle (
   945:       ComponentFactory.createModalDialog (
   946:         XEiJ.frmFrame,
   947:         "Create new floppy disk image files",
   948:         ComponentFactory.createBorderPanel (
   949:           0, 0,
   950:           ComponentFactory.createVerticalBox (
   951:             fdcFormatFileChooser,
   952:             ComponentFactory.createHorizontalBox (
   953:               Box.createHorizontalStrut (12),
   954:               Box.createHorizontalGlue (),
   955:               ComponentFactory.createVerticalBox (
   956:                 ComponentFactory.createHorizontalBox (
   957:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2HD,   "2HD (1232KB)", listener),
   958:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2HC,   "2HC (1200KB)", listener),
   959:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2DD8,  "2DD (640KB)",  listener),
   960:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2DD9,  "2DD (720KB)",  listener),
   961:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2HQ,   "2HQ (1440KB)", listener)
   962:                   ),
   963:                 ComponentFactory.createHorizontalBox (
   964:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2DD10, "2DD (800KB)",  listener),
   965:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2HDE,  "2HDE(1440KB)", listener),
   966:                   ComponentFactory.createRadioButtonMenuItem (mediaGroup, fdcFormatMedia == FDMedia.FDM_2HS,   "2HS (1440KB)", listener),
   967:                   Box.createHorizontalGlue (),
   968:                   fdcFormatX86SafeCheckBox = ComponentFactory.setEnabled (
   969:                     Multilingual.mlnText (ComponentFactory.createCheckBox (fdcFormatMedia == FDMedia.FDM_2HQ && fdcFormatX86SafeOn, "x86-safe", listener), "ja", "x86 セーフ"),
   970:                     fdcFormatMedia == FDMedia.FDM_2HQ),
   971:                   Box.createHorizontalStrut (12)
   972:                   )
   973:                 ),
   974:               Box.createHorizontalGlue (),
   975:               Box.createHorizontalStrut (12)
   976:               ),
   977:             Box.createVerticalStrut (12),
   978:             ComponentFactory.createHorizontalBox (
   979:               Box.createHorizontalStrut (12),
   980:               Box.createHorizontalGlue (),
   981:               //Multilingual.mlnText (ComponentFactory.createCheckBox (fdcFormatCopySystemFiles, "Copy System Files", listener), "ja", "システムファイルを転送する"),
   982:               fdcFormatCopyHumanSysCheckBox = ComponentFactory.createCheckBox (fdcFormatCopyHumanSysOn, "HUMAN.SYS", listener),
   983:               Box.createHorizontalStrut (12),
   984:               fdcFormatCopyCommandXCheckBox = ComponentFactory.setEnabled (
   985:                 ComponentFactory.createCheckBox (fdcFormatCopyHumanSysOn && fdcFormatCopyCommandXOn, "COMMAND.X", listener),
   986:                 fdcFormatCopyHumanSysOn),
   987:               Box.createHorizontalGlue (),
   988:               Box.createHorizontalStrut (12),
   989:               Multilingual.mlnText (ComponentFactory.createButton ("Start formatting", KeyEvent.VK_F, listener), "ja", "フォーマットを開始する"),
   990:               Box.createHorizontalStrut (12),
   991:               Multilingual.mlnText (ComponentFactory.createButton ("Cancel", KeyEvent.VK_C, listener), "ja", "キャンセル"),
   992:               Box.createHorizontalStrut (12)
   993:               ),
   994:             Box.createVerticalStrut (12)
   995:             )
   996:           )
   997:         ),
   998:       "ja", "フロッピーディスクのイメージファイルの新規作成");
   999: 
  1000:   }  //fdcMakeFormatDialog()
  1001: 
  1002:   //fdcMakeFormatFileChooser ()
  1003:   //  フォーマットファイルチューザーを作る
  1004:   public static void fdcMakeFormatFileChooser () {
  1005:     if (fdcFormatFileChooser == null) {
  1006:       fdcFormatFileChooser = new JFileChooser2 ();
  1007:       //fdcFormatFileChooser.setMultiSelectionEnabled (true);  //複数選択可能
  1008:       fdcFormatFileChooser.setControlButtonsAreShown (false);  //デフォルトのボタンを消す
  1009:     }
  1010:   }
  1011: 
  1012:   //fdcOpenFormatDialog ()
  1013:   //  フォーマットダイアログを開く
  1014:   //  コマンドラインのみ
  1015:   public static void fdcOpenFormatDialog () {
  1016:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1017:       System.out.printf ("%08x fdcOpenFormatDialog()\n", XEiJ.regPC0);
  1018:     }
  1019:     if (fdcFormatDialog == null) {
  1020:       fdcMakeFormatDialog ();
  1021:     }
  1022:     fdcFormatFileChooser.rescanCurrentDirectory ();  //挿入されているファイルが変わると選択できるファイルも変わるのでリストを作り直す
  1023:     XEiJ.pnlExitFullScreen (true);
  1024:     fdcFormatDialog.setVisible (true);
  1025:   }  //fdcOpenFormatDialog()
  1026: 
  1027:   //success = fdcFormatFiles (list)
  1028:   //  フロッピーディスクをフォーマットする
  1029:   //  コマンドラインのみ
  1030:   public static boolean fdcFormatFiles (File[] list) {
  1031:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1032:       System.out.printf ("%08x fdcFormatFiles({", XEiJ.regPC0);
  1033:       for (int i = 0; i < list.length; i++) {
  1034:         if (0 < i) {
  1035:           System.out.print (',');
  1036:         }
  1037:         System.out.printf ("\"%s\"", list[i].getPath ());
  1038:       }
  1039:       System.out.println ('}');
  1040:     }
  1041:     boolean success = true;
  1042:   format:
  1043:     {
  1044:       //フローピーディスクのフォーマットデータを作る
  1045:       byte[] diskImage = new byte[fdcFormatMedia.fdmBytesPerDisk];
  1046:       if (!fdcFormatMedia.fdmMakeFormatData (diskImage, fdcFormatCopyHumanSysOn, fdcFormatCopyCommandXOn, fdcFormatX86SafeOn)) {
  1047:         success = false;  //失敗
  1048:         break format;
  1049:       }
  1050:       byte[] dimImage = null;
  1051:       int dimSize = 0;
  1052:       //書き出す
  1053:       int u = 0;
  1054:       for (File file : list) {
  1055:         String path = file.getPath ();
  1056:         String upperPath = path.toUpperCase ();
  1057:         if (upperPath.endsWith (".DIM")) {  // *.DIM
  1058:           if (fdcIsInsertedPath (path)) {  //他のユニットに挿入されている
  1059:             success = false;  //失敗
  1060:             break format;
  1061:           }
  1062:           if (dimImage == null) {
  1063:             dimImage = new byte[256 + fdcFormatMedia.fdmBytesPerDisk];
  1064:             dimSize = fdcFormatMedia.fdmMakeDimImage (dimImage, diskImage);  // *.DIMのイメージを作る
  1065:             if (dimSize < 0) {  // *.DIMのイメージを作れない
  1066:               success = false;  //失敗
  1067:               break format;
  1068:             }
  1069:           }
  1070:           if (!XEiJ.rscPutFile (path, dimImage, 0, dimSize)) {  //書き出せない
  1071:             success = false;  //失敗
  1072:             break format;
  1073:           }
  1074:         } else {  // *.DIM以外
  1075:           boolean extNotSpecified = true;
  1076:           for (String mediaExt : fdcFormatMedia.fdmExtensionArray) {
  1077:             if (upperPath.endsWith (mediaExt)) {  //適切な拡張子が指定されている
  1078:               extNotSpecified = false;
  1079:               break;
  1080:             }
  1081:           }
  1082:           if (extNotSpecified) {  //適切な拡張子が指定されていない
  1083:             if (!path.endsWith (".")) {
  1084:               path += ".";
  1085:             }
  1086:             path += fdcFormatMedia.fdmExtensionArray[0].toLowerCase ();  //拡張子を追加する
  1087:             upperPath = path.toUpperCase ();
  1088:           }
  1089:           if (fdcIsInsertedPath (path)) {  //他のユニットに挿入されている
  1090:             success = false;  //失敗
  1091:             break format;
  1092:           }
  1093:           if (!XEiJ.rscPutFile (path, diskImage, 0, fdcFormatMedia.fdmBytesPerDisk)) {  //書き出せない
  1094:             success = false;  //失敗
  1095:             break format;
  1096:           }
  1097:         }
  1098:         //空いているユニットがあれば挿入する
  1099:         while (u < FDC_MAX_UNITS) {
  1100:           FDUnit unit = fdcUnitArray[u++];  //ユニット
  1101:           if (unit.abuConnected &&  //接続されていて
  1102:               !unit.fduIsInserted () &&  //空いていて
  1103:               unit.insert (path,
  1104:                            false)) {  //挿入できた
  1105:             //フォーマットしたディスクの書き込みを禁止しても意味がないのでここでは書き込みを禁止しない
  1106:             break;
  1107:           }
  1108:         }
  1109:       }
  1110:     }  //format
  1111:     if (success) {  //すべてフォーマットできた
  1112:       fdcAddHistory (list);  //ヒストリに追加する
  1113:     }
  1114:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1115:       System.out.printf ("\tsuccess=%b\n", success);
  1116:     }
  1117:     return success;
  1118:   }  //fdcFormatFiles(File[])
  1119: 
  1120: 
  1121:   //fdcAddHistory (file)
  1122:   //  ファイルをヒストリに追加する
  1123:   public static void fdcAddHistory (File file) {
  1124:     fdcAddHistory (new File[] { file });
  1125:   }
  1126: 
  1127:   //fdcAddHistory (files)
  1128:   //  複数のファイルをヒストリに追加する
  1129:   public static void fdcAddHistory (File[] files) {
  1130:     if (fdcOpenDialog == null) {
  1131:       fdcOpenHistory.add (files);
  1132:     } else {
  1133:       fdcOpenDialog.addHistory (files);
  1134:     }
  1135:     fdcMakeFormatFileChooser ();
  1136:     fdcFormatFileChooser.addHistory (files);
  1137:     fdcFormatFileChooser.selectLastFiles ();
  1138:   }
  1139: 
  1140: 
  1141:   //d = fdcPeekStatus ()
  1142:   //  pbz (0x00e94001)
  1143:   //  FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
  1144:   public static int fdcPeekStatus () {
  1145:     return fdcStatus;
  1146:   }  //fdcPeekStatus()
  1147: 
  1148:   //d = fdcReadStatus ()
  1149:   //  rbz (0x00e94001)
  1150:   //  FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
  1151:   public static int fdcReadStatus () {
  1152:     int d = fdcStatus;
  1153:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1154:       if (fdcLastStatus == d) {
  1155:         System.out.print ('=');
  1156:       } else {
  1157:         fdcLastStatus = d;
  1158:         System.out.printf ("%08x fdcReadStatus(0x00e94001)=%s\n", XEiJ.regPC0, fdcStatusToString (d));
  1159:       }
  1160:     }
  1161:     return d;
  1162:   }  //fdcReadStatus
  1163: 
  1164:   //d = fdcPeekData ()
  1165:   //  pbz (0x00e94003)
  1166:   //  FDC データ/コマンド
  1167:   public static int fdcPeekData () {
  1168:     return (fdcReadHandle == null ? 0 :  //Read中でない
  1169:             fdcReadHandle[fdcIndex] & 255);
  1170:   }  //fdcPeekData
  1171: 
  1172:   //d = fdcReadData ()
  1173:   //  rbz (0x00e94003)
  1174:   //  FDC データ/コマンド
  1175:   public static int fdcReadData () {
  1176:     if (fdcReadHandle == null) {  //Read中でない
  1177:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1178:         System.out.printf ("%08x fdcReadData(0x00e94003=???)=0x00\n", XEiJ.regPC0);
  1179:       }
  1180:       return 0;
  1181:     }
  1182:     int d = fdcReadHandle[fdcIndex] & 255;
  1183:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1184:       if (fdcIndex < fdcStart + 8 || fdcLimit - 8 <= fdcIndex) {  //先頭8バイトまたは末尾8バイト
  1185:         System.out.printf ("%08x fdcReadData(0x00e94003=%s[0x%08x])=0x%02x\n",
  1186:                            XEiJ.regPC0,
  1187:                            fdcReadHandle == fdcResultBuffer ? "fdcResultBuffer" :
  1188:                            fdcReadHandle == fdcTempBuffer ? "fdcTempBuffer" :
  1189:                            "fduImage",
  1190:                            fdcIndex,
  1191:                            d);
  1192:       }
  1193:     }
  1194:     fdcIndex++;
  1195:     if (fdcIndex < fdcLimit) {  //継続
  1196:       if (fdcReadHandle != fdcResultBuffer) {  //E-Phase
  1197:         HD63450.dmaFallPCL (0);  //DMA転送継続
  1198:       } else {  //R-Phase
  1199:         if (fdcIndex == 1) {  //R-PhaseでSE(Seek End)のリザルトステータスの1バイト目のST0が引き取られたとき
  1200:           fdcStatus &= ~(1 << (d & 3));  //ST0に対応するユニットのFDnビジー(DnB)を0にする
  1201:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1202:             System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1203:           }
  1204:         }
  1205:       }
  1206:     } else {  //終了
  1207:       if (fdcReadHandle != fdcResultBuffer) {  //E-Phase
  1208:         fdcEPhaseEnd ();
  1209:       } else {  //R-Phase
  1210:         fdcRPhaseEnd ();
  1211:       }
  1212:     }
  1213:     return d;
  1214:   }  //fdcReadData
  1215: 
  1216:   //d = fdcPeekDriveStatus ()
  1217:   //  pbz (0x00e94005)
  1218:   //  FDD 状態(挿入|誤挿入|000000)/機能(点滅|排出禁止|排出|-|選択####)
  1219:   public static int fdcPeekDriveStatus () {
  1220:     return (fdcDriveLastSelected == null ? 0 :  //ドライブが選択されていない
  1221:             fdcDriveLastSelected.fduDriveStatus ());
  1222:   }  //fdcPeekDriveStatus
  1223: 
  1224:   //d = fdcReadDriveStatus ()
  1225:   //  rbz (0x00e94005)
  1226:   //  FDD 状態(挿入|誤挿入|000000)/機能(点滅|排出禁止|排出|-|選択####)
  1227:   public static int fdcReadDriveStatus () {
  1228:     int d = (fdcDriveLastSelected == null ? 0 :  //ドライブが選択されていない
  1229:              fdcDriveLastSelected.fduDriveStatus ());
  1230:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1231:       System.out.printf ("%08x fdcReadDriveStatus(0x00e94005)=0x%02x(挿入=%d 誤挿入=%d)\n",
  1232:                          XEiJ.regPC0,
  1233:                          d,
  1234:                          d >> 7,
  1235:                          d >> 6 & 1);
  1236:     }
  1237:     return d;
  1238:   }  //fdcReadDriveStatus
  1239: 
  1240:   //fdcWriteCommand (d)
  1241:   //  wb (0x00e94001, d)
  1242:   //  FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
  1243:   public static void fdcWriteCommand (int d) {
  1244:     if (fdcWriteHandle != fdcCommandBuffer) {  //C-Phaseでない
  1245:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1246:         System.out.printf ("%08x fdcWriteCommand(0x00e94001=???,0x%02x)\n",
  1247:                            XEiJ.regPC0,
  1248:                            d & 255);
  1249:       }
  1250:       return;
  1251:     }
  1252:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1253:       if (fdcIndex < fdcStart + 8 || fdcLimit - 8 <= fdcIndex) {  //先頭8バイトまたは末尾8バイト
  1254:         System.out.printf ("%08x fdcWriteCommand(0x00e94001=%s[0x%08x],0x%02x)\n",
  1255:                            XEiJ.regPC0,
  1256:                            "fdcCommandBuffer",
  1257:                            fdcIndex,
  1258:                            d & 255);
  1259:       }
  1260:     }
  1261:     fdcWriteHandle[fdcIndex++] = (byte) d;
  1262:     if (fdcLimit <= fdcIndex) {  //C-Phaseが終了した
  1263:       fdcCPhaseEnd ();
  1264:     }  //C-Phaseが終了した
  1265:   }  //fdcWriteCommand(int)
  1266: 
  1267:   //fdcWriteData (d)
  1268:   //  wb (0x00e94003, d)
  1269:   //  FDC データ/コマンド
  1270:   public static void fdcWriteData (int d) {
  1271:     if (fdcWriteHandle == null) {  //Write中でない
  1272:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1273:         System.out.printf ("%08x fdcWriteData(0x00e94003=???,0x%02x)\n",
  1274:                            XEiJ.regPC0,
  1275:                            d & 255);
  1276:       }
  1277:       return;
  1278:     }
  1279:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1280:       if (fdcIndex < fdcStart + 8 || fdcLimit - 8 <= fdcIndex) {  //先頭8バイトまたは末尾8バイト
  1281:         System.out.printf ("%08x fdcWriteData(0x00e94003=%s[0x%08x],0x%02x)\n",
  1282:                            XEiJ.regPC0,
  1283:                            fdcWriteHandle == fdcCommandBuffer ? "fdcCommandBuffer" :
  1284:                            fdcWriteHandle == fdcTempBuffer ? "fdcTempBuffer" :
  1285:                            "fduImage",
  1286:                            fdcIndex,
  1287:                            d & 255);
  1288:       }
  1289:     }
  1290:     fdcWriteHandle[fdcIndex++] = (byte) d;
  1291:     if (fdcIndex < fdcLimit) {  //継続
  1292:       if (fdcWriteHandle != fdcCommandBuffer) {  //E-Phaseのとき
  1293:         HD63450.dmaFallPCL (0);  //DMA転送継続
  1294:       }
  1295:     } else if (fdcWriteHandle == fdcCommandBuffer) {  //C-Phaseが終了した
  1296:       fdcCPhaseEnd ();
  1297:     } else {  //E-Phaseが終了した
  1298:       fdcEPhaseEnd ();
  1299:     }
  1300:   }  //fdcWriteData(int)
  1301: 
  1302: 
  1303:   //fdcCPhase ()
  1304:   //  C-Phaseに戻る
  1305:   public static void fdcCPhase () {
  1306:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1307:       System.out.printf ("%08x fdcCPhase()\n", XEiJ.regPC0);
  1308:     }
  1309:     //コマンドが終了してC-Phaseに戻るとき
  1310:     fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC |  //FDCビジー(CB)を0にする
  1311:                  (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  1312:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1313:       System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1314:     }
  1315:     //C-Phaseに移行する
  1316:     fdcReadHandle = null;
  1317:     fdcWriteHandle = fdcCommandBuffer;  //C-Phase
  1318:     fdcIndex = fdcStart = 0;
  1319:     fdcLimit = 1;  //C-Phaseの1バイト目
  1320:     fdcCommandNumber = -1;  //C-Phaseの1バイト目
  1321:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1322:       System.out.printf ("\tfdcCommandNumber=%d\n", fdcCommandNumber);
  1323:     }
  1324:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1325:       FDUnit unit = fdcUnitArray[u];
  1326:       if (unit.fduSeekStepWaiting) {  //シークステップ待機(seekStepWaiting)が1のとき
  1327:         unit.fduSeekStepWaiting = false;  //シークステップ待機(seekStepWaiting)を0にする
  1328:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1329:           System.out.printf ("\tfduSeekStepWaiting%d=%b\n", u, unit.fduSeekStepWaiting);
  1330:         }
  1331:         unit.fduSeekStep ();  //シークステップ
  1332:       }
  1333:       if ((fdcStatus & 1 << u) != 0) {  //FDnビジー(DnB)が1のとき
  1334:         if (unit.fduSeekEndInterruptWaiting) {  //シーク終了割り込み待機(seekEndInterruptWaiting)が1のとき
  1335:           unit.fduSeekEndInterruptWaiting = false;  //シーク終了割り込み待機(seekEndInterruptWaiting)を0にする
  1336:           unit.fduSeekEndInterruptRequest = true;  //シーク終了割り込み要求(seekEndInterruptRequest)を1にする
  1337:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1338:             System.out.printf ("\tfduSeekEndInterruptWaiting%d=%b\n", u, unit.fduSeekEndInterruptWaiting);
  1339:             System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", u, unit.fduSeekEndInterruptRequest);
  1340:           }
  1341:           IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  1342:         }
  1343:       } else {  //FDnビジー(DnB)が0のとき
  1344:         if (unit.fduAttentionCheckWaiting) {  //状態遷移確認フラグ(attentionCheckWaiting)が1のとき
  1345:           unit.fduAttentionCheckWaiting = false;  //状態遷移確認フラグ(attentionCheckWaiting)を0にする
  1346:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1347:             System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", u, unit.fduAttentionCheckWaiting);
  1348:           }
  1349:           boolean ready = unit.fduIsReady ();
  1350:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1351:             System.out.printf ("\tready%d=%b\n", u, ready);
  1352:           }
  1353:           if (ready != unit.fduSavedReady) {  //レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
  1354:             unit.fduSavedReady = ready;  //レディ信号の状態(isReady)を保存する(isReady→savedReady)
  1355:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1356:               System.out.printf ("\tfduSavedReady%d=%b\n", u, unit.fduSavedReady);
  1357:             }
  1358:             if (!unit.fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が0のとき
  1359:               unit.fduAttentionInterruptRequest = true;  //状態遷移割り込み要求(attentionInterruptRequest)を1にする
  1360:               if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1361:                 System.out.printf ("\tfduAttentionInterruptRequest%d=%b\n", u, unit.fduAttentionInterruptRequest);
  1362:               }
  1363:               IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  1364:             }
  1365:           }
  1366:         }
  1367:       }
  1368:     }  //for u
  1369:   }  //fdcCPhase()
  1370: 
  1371:   //fdcCPhaseEnd ()
  1372:   //  C-Phaseが終了した
  1373:   public static void fdcCPhaseEnd () {
  1374:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1375:       System.out.printf ("%08x fdcCPhaseEnd()\n", XEiJ.regPC0);
  1376:     }
  1377:     if (fdcCommandNumber < 0) {  //C-Phaseの1バイト目のとき
  1378:       fdcCommandNumber = fdcCommandBuffer[0] & 31;
  1379:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1380:         System.out.printf ("\tfdcCommandNumber=%d\n", fdcCommandNumber);
  1381:       }
  1382:       fdcLimit = FDC_COMMAND_LENGTH[fdcCommandNumber];  //コマンドの長さ
  1383:       if (1 < fdcLimit) {  //2バイト以上のコマンドなのでC-Phaseを継続
  1384:         fdcStatus |= FDC_CB;  //2バイト目からCBをセットする
  1385:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1386:           System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1387:         }
  1388:         //外部転送要求モードでDMAの動作を開始してからFDCにコマンドが送られてくるので、
  1389:         //C-PhaseのときDMAに転送要求を出してはならない
  1390:         //HD63450.dmaFallPCL (0);  //DMA転送継続
  1391:         return;
  1392:       }
  1393:       //1バイトのコマンドなのでC-Phaseが終了した
  1394:     }
  1395:     //HD63450.dmaRisePCL (0);
  1396:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1397:       System.out.printf ("********* %s {",
  1398:                          FDC_COMMAND_NAME[fdcCommandNumber]);
  1399:       for (int i = 0; i < fdcLimit; i++) {
  1400:         if (i > 0) {
  1401:           System.out.print (',');
  1402:         }
  1403:         System.out.printf ("0x%02x", fdcCommandBuffer[i] & 255);
  1404:       }
  1405:       System.out.println ("} ********");
  1406:     }
  1407:     FDUnit unit = fdcUnitArray[fdcCommandBuffer[1] & 3];  //ユニットを指定しないコマンドでは無意味だがエラーになることはないので問題ない
  1408:     switch (fdcCommandNumber) {
  1409:       //case 0x02:  //0x02  READ DIAGNOSTIC
  1410:       //  unit.fduCommandReadDiagnostic ();
  1411:       //  break;
  1412:     case 0x03:  //0x03  SPECIFY
  1413:       fdcCommandSpecify ();
  1414:       break;
  1415:     case 0x04:  //0x04  SENSE DEVICE STATUS
  1416:       unit.fduCommandSenseDeviceStatus ();
  1417:       break;
  1418:     case 0x05:  //0x05  WRITE DATA
  1419:       unit.fduCommandWriteData ();
  1420:       break;
  1421:     case 0x06:  //0x06  READ DATA
  1422:       unit.fduCommandReadData ();
  1423:       break;
  1424:     case 0x07:  //0x07  RECALIBRATE
  1425:       unit.fduCommandRecalibrate ();
  1426:       break;
  1427:     case 0x08:  //0x08  SENSE INTERRUPT STATUS
  1428:       fdcCommandSenseInterruptStatus ();
  1429:       break;
  1430:       //case 0x09:  //0x09  WRITE DELETED DATA
  1431:       //  unit.fduCommandWriteDeletedData ();
  1432:       //  break;
  1433:     case 0x0a:  //0x0a  READ ID
  1434:       unit.fduCommandReadId ();
  1435:       break;
  1436:       //case 0x0c:  //0x0c  READ DELETED DATA
  1437:       //  unit.fduCommandReadDeletedData ();
  1438:       //  break;
  1439:     case 0x0d:  //0x0d  WRITE ID
  1440:       unit.fduCommandWriteId ();
  1441:       break;
  1442:     case 0x0f:  //0x0f  SEEK
  1443:       unit.fduCommandSeek ();
  1444:       break;
  1445:     case 0x11:  //0x11  SCAN EQUAL
  1446:       unit.fduCommandScan ();
  1447:       break;
  1448:       //case 0x14:  //0x14  RESET STANDBY
  1449:       //  fdcCommandResetStandby ();
  1450:       //  break;
  1451:       //case 0x15:  //0x15  SET STANDBY
  1452:       //  fdcCommandSetStandby ();
  1453:       //  break;
  1454:     case 0x16:  //0x16  SOFTWARE RESET
  1455:       fdcCommandSoftwareReset ();
  1456:       break;
  1457:     case 0x19:  //0x19  SCAN LOW OR EQUAL
  1458:       unit.fduCommandScan ();
  1459:       break;
  1460:     case 0x1d:  //0x1d  SCAN HIGH OR EQUAL
  1461:       unit.fduCommandScan ();
  1462:       break;
  1463:       //case 0x00:  //0x00  INVALID
  1464:       //case 0x01:  //0x01  INVALID
  1465:       //case 0x0b:  //0x0b  INVALID
  1466:       //case 0x0e:  //0x0e  INVALID
  1467:       //case 0x10:  //0x10  INVALID
  1468:       //case 0x12:  //0x12  INVALID
  1469:       //case 0x13:  //0x13  INVALID
  1470:       //case 0x17:  //0x17  INVALID
  1471:       //case 0x18:  //0x18  INVALID
  1472:       //case 0x1a:  //0x1a  INVALID
  1473:       //case 0x1b:  //0x1b  INVALID
  1474:       //case 0x1c:  //0x1c  INVALID
  1475:       //case 0x1e:  //0x1e  INVALID
  1476:       //case 0x1f:  //0x1f  INVALID
  1477:     default:  //INVALID
  1478:       fdcCommandInvalid ();
  1479:     }
  1480:   }  //fdcCPhaseEnd()
  1481: 
  1482:   //fdcEPhaseEnd ()
  1483:   //  E-Phaseが終了した
  1484:   public static void fdcEPhaseEnd () {
  1485:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1486:       System.out.printf ("%08x fdcEPhaseEnd()\n", XEiJ.regPC0);
  1487:     }
  1488:     FDUnit unit = fdcUnitArray[fdcCommandBuffer[1] & 3];
  1489:     switch (unit.fduCommandNumber) {
  1490:     case 0x05:  //0x05  WRITE DATA
  1491:       unit.fduWriteDataEPhaseEnd ();
  1492:       break;
  1493:     case 0x06:  //0x06  READ DATA
  1494:       unit.fduReadDataEPhaseEnd ();
  1495:       break;
  1496:     case 0x0d:  //0x0d  WRITE ID
  1497:       unit.fduWriteIdEPhaseEnd ();
  1498:       break;
  1499:     case 0x11:  //0x11  SCAN EQUAL
  1500:       unit.fduScanEqualEPhaseEnd ();
  1501:       break;
  1502:     case 0x19:  //0x19  SCAN LOW OR EQUAL
  1503:       unit.fduScanLowOrEqualEPhaseEnd ();
  1504:       break;
  1505:     case 0x1d:  //0x1d  SCAN HIGH OR EQUAL
  1506:       unit.fduScanHighOrEqualEPhaseEnd ();
  1507:       break;
  1508:     }
  1509:   }  //fdcEPhaseEnd()
  1510: 
  1511:   //fdcRPhase (limit)
  1512:   //  R-Phaseに移行する
  1513:   public static void fdcRPhase (int limit) {
  1514:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1515:       System.out.printf ("%08x fdcRPhase({", XEiJ.regPC0);
  1516:       for (int i = 0; i < limit; i++) {
  1517:         if (0 < i) {
  1518:           System.out.print (',');
  1519:         }
  1520:         System.out.printf ("0x%02x", fdcResultBuffer[i] & 255);
  1521:       }
  1522:       System.out.println ("})");
  1523:     }
  1524:     fdcStatus = (FDC_RQM | FDC_FDC_TO_MPU | FDC_CB |
  1525:                  (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  1526:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1527:       System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1528:     }
  1529:     fdcReadHandle = fdcResultBuffer;  //R-Phase
  1530:     fdcWriteHandle = null;
  1531:     fdcIndex = fdcStart = 0;
  1532:     fdcLimit = limit;
  1533:   }  //fdcRPhase(int)
  1534: 
  1535:   //fdcRPhaseEnd ()
  1536:   //  R-Phaseが終了した
  1537:   public static void fdcRPhaseEnd () {
  1538:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1539:       System.out.printf ("%08x fdcRPhaseEnd()\n", XEiJ.regPC0);
  1540:     }
  1541:     fdcCPhase ();  //C-Phaseに戻る
  1542:     //R-Phaseが終わってC-Phaseに戻るとき
  1543:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1544:       FDUnit unit = fdcUnitArray[u];
  1545:       if (unit.fduSeekEndInterruptRequest) {  //シーク終了割り込み要求(seekEndInterruptRequest)が1のユニットが残っているとき
  1546:         IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  1547:         break;
  1548:       }
  1549:     }
  1550:   }  //fdcRPhaseEnd()
  1551: 
  1552: 
  1553:   //fdcWriteDriveControl (d)
  1554:   //  wb (0x00e94005, d)
  1555:   //  FDD 状態(挿入|誤挿入|------)/機能(点滅|排出禁止|排出|-|選択####)
  1556:   public static void fdcWriteDriveControl (int d) {
  1557:     //0x00e94005にライトすると0x00e9c001のFDD割り込みステータスがクリアされる
  1558:     IOInterrupt.ioiFddFall ();
  1559:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1560:       System.out.printf ("%08x fdcWriteDriveControl(0x00e94005,0x%02x(BLK=%d PRV=%d EJT=%d US3=%d US2=%d US1=%d US0=%d))\n",
  1561:                          XEiJ.regPC0,
  1562:                          d & 255,
  1563:                          d >> 7,
  1564:                          d >> 6 & 1,
  1565:                          d >> 5 & 1,
  1566:                          d >> 3 & 1,
  1567:                          d >> 2 & 1,
  1568:                          d >> 1 & 1,
  1569:                          d & 1);
  1570:     }
  1571:     //bit0-3で選択されたドライブについてbit7=点滅,bit6=排出禁止,bit5=排出
  1572:     int u = Integer.numberOfTrailingZeros (d & 15);  //選択されたドライブの番号。なければ32
  1573:     if (u < 4) {
  1574:       FDUnit unit = fdcUnitArray[u];  //ユニット
  1575:       if (unit.abuConnected) {  //接続されている
  1576:         unit.fduDriveControl (d);
  1577:       }
  1578:       fdcDriveLastSelected = unit;
  1579:     } else {
  1580:       fdcDriveLastSelected = null;
  1581:     }
  1582:   }  //fdcWriteDriveControl
  1583: 
  1584:   //fdcWriteDriveSelect (d)
  1585:   //  wb (0x00e94007, d)
  1586:   //  FDD 選択(モータON|--|2DD|--|ドライブ##)
  1587:   public static void fdcWriteDriveSelect (int d) {
  1588:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1589:       System.out.printf ("%08x fdcWriteDriveSelect(0x00e94007,0x%02x(MT=%d 2DD=%d US=%d))\n",
  1590:                          XEiJ.regPC0,
  1591:                          d & 255,
  1592:                          d >> 7,
  1593:                          d >> 4 & 1,
  1594:                          d & 3);
  1595:     }
  1596:     IOInterrupt.ioiFddFall ();
  1597:     FDUnit unit = fdcUnitArray[d & 3];  //ユニット
  1598:     if (unit.abuConnected &&  //接続されている
  1599:         unit.fduIsInserted ()) {  //挿入されている
  1600:       unit.fduDoubleDensity = d << 31 - 4 < 0;
  1601:       if ((byte) d < 0) {  //モータON
  1602:         unit.fduMotorOn ();
  1603:       } else {  //モータOFF
  1604:         unit.fduMotorOff ();
  1605:       }
  1606:     }
  1607:   }  //fdcWriteDriveSelect
  1608: 
  1609:   //fdcSetEnforcedReady (enforcedReady)
  1610:   //  強制レディ状態(YM2151のCT2が1)の設定
  1611:   public static void fdcSetEnforcedReady (boolean enforcedReady) {
  1612:     fdcEnforcedReady = enforcedReady;
  1613:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1614:       System.out.printf ("%08x fdcSetEnforcedReady(%b)\n", XEiJ.regPC0, enforcedReady);
  1615:     }
  1616:   }  //fdcSetEnforcedReady(boolean)
  1617: 
  1618: 
  1619:   //fdcCommandSpecify ()
  1620:   //  0x03  SPECIFY
  1621:   //    タイマとNon-DMAモードの設定
  1622:   //  C-Phase
  1623:   //    [0]          CMD   Command           0x03=SPECIFY
  1624:   //    [1] bit7-4   SRT   Step Rate Time    Seekコマンドのステップパルス(シリンダ移動)の間隔。標準1ms単位、ミニ2ms単位。16から引く
  1625:   //        bit3-0   HUT   Head Unload Time  Read/Writeコマンド終了後のヘッドアンロードまでの時間。標準16ms単位、ミニ32ms単位。16倍する
  1626:   //    [2] bit7-1   HLT   Head Load Time    ヘッドロード後の安定待ち時間。標準2ms単位、ミニ4ms単位。2倍する
  1627:   //        bit0     ND    Non-DMA Mode      Read/WriteコマンドのE-Phaseについて0=DMAモード,1=Non-DMAモード
  1628:   public static void fdcCommandSpecify () {
  1629:     int srt = fdcCommandBuffer[1] >> 4 & 15;  //SRT
  1630:     int hut = fdcCommandBuffer[1] & 15;  //HUT
  1631:     int hlt = fdcCommandBuffer[2] >> 1 & 127;  //HLT
  1632:     int nd = fdcCommandBuffer[2] & 1;  //ND
  1633:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1634:       System.out.printf ("%08x fdcCommandSpecify(SRT=%d HUT=%d HLT=%d ND=%d)\n", XEiJ.regPC0, srt, hut, hlt, nd);
  1635:     }
  1636:     fdcSRT = srt;
  1637:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1638:       System.out.printf ("\tfdcSRT=%d\n", fdcSRT);
  1639:     }
  1640:     fdcCPhase ();  //C-Phaseに戻る
  1641:   }  //fdcCommandSpecify
  1642: 
  1643:   //fdcCommandSenseInterruptStatus ()
  1644:   //  0x08  SENSE INTERRUPT STATUS
  1645:   //    リザルトステータス0(ST0)の引き取り
  1646:   //  C-Phase
  1647:   //    [0]          CMD   Command          0x08=SENSE INTERRUPT STATUS
  1648:   //    [1]  bit2    HD    Head Address     サイド
  1649:   //         bit1-0  US    Unit Select      ユニット
  1650:   //  R-Phase
  1651:   //    [0]          ST0   Status 0         リザルトステータス0
  1652:   //    [1]          PCN   Present Cylinder Number  現在のシリンダ
  1653:   public static void fdcCommandSenseInterruptStatus () {
  1654:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1655:       System.out.printf ("%08x fdcCommandSenseInterruptStatus()\n", XEiJ.regPC0);
  1656:     }
  1657:     //R-Phaseに移行するとき
  1658:     IOInterrupt.ioiFdcFall ();  //FDC割り込み要求(INT)を0にする
  1659:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1660:       FDUnit unit = fdcUnitArray[u];
  1661:       if ((fdcStatus & 1 << u) != 0) {  //FDnビジー(DnB)が1のとき
  1662:         if (unit.fduSeekEndInterruptWaiting) {  //シーク終了割り込み待機(seekEndInterruptWaiting)が1のとき
  1663:           unit.fduSeekEndInterruptWaiting = false;  //シーク終了割り込み待機(seekEndInterruptWaiting)を0にする
  1664:           unit.fduSeekEndInterruptRequest = true;  //シーク終了割り込み要求(seekEndInterruptRequest)を1にする
  1665:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1666:             System.out.printf ("\tfduSeekEndInterruptWaiting%d=%b\n", u, unit.fduSeekEndInterruptWaiting);
  1667:             System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", u, unit.fduSeekEndInterruptRequest);
  1668:           }
  1669:         } else {  //FDnビジー(DnB)が0のとき
  1670:           if (unit.fduAttentionCheckWaiting) {  //状態遷移確認フラグ(attentionCheckWaiting)が1のとき
  1671:             unit.fduAttentionCheckWaiting = false;  //状態遷移確認フラグ(attentionCheckWaiting)を0にする
  1672:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1673:               System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", u, unit.fduAttentionCheckWaiting);
  1674:             }
  1675:             boolean ready = unit.fduIsReady ();
  1676:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1677:               System.out.printf ("\tready%d=%b\n", u, ready);
  1678:             }
  1679:             if (ready != unit.fduSavedReady) {  //レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
  1680:               unit.fduSavedReady = ready;  //レディ信号の状態(isReady)を保存する(isReady→savedReady)
  1681:               if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1682:                 System.out.printf ("\tfduSavedReady%d=%b\n", u, unit.fduSavedReady);
  1683:               }
  1684:               if (!unit.fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が0のとき
  1685:                 unit.fduAttentionInterruptRequest = true;  //状態遷移割り込み要求(attentionInterruptRequest)を1にする
  1686:                 if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1687:                   System.out.printf ("\tfduAttentionInterruptRequest%d=%b\n", u, unit.fduAttentionInterruptRequest);
  1688:                 }
  1689:               }
  1690:             }
  1691:           }
  1692:         }
  1693:       }
  1694:     }
  1695:     int status = FDC_ST0_IC;
  1696:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1697:       FDUnit unit = fdcUnitArray[u];
  1698:       if (unit.fduSeekEndInterruptRequest) {  //シーク終了割り込み要求(seekEndInterruptRequest)が1のとき
  1699:         unit.fduSeekEndInterruptRequest = false;  //シーク終了割り込み要求(seekEndInterruptRequest)を0にする
  1700:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1701:           System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", u, unit.fduSeekEndInterruptRequest);
  1702:         }
  1703:         status = unit.fduSeekResultStatus | u << 24 | (unit.fduPCN & 255) << 16;
  1704:         break;
  1705:       }
  1706:       if (unit.fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が1のとき
  1707:         unit.fduAttentionInterruptRequest = false;  //状態遷移割り込み要求(attentionInterruptRequest)を0にする
  1708:         status = FDC_ST0_AI | u << 24 | (unit.fduPCN & 255) << 16;  //AI(Attention Interrupt)のリザルトステータスを出力する
  1709:         break;
  1710:       }
  1711:     }
  1712:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1713:       System.out.printf ("\tstatus=%s\n", fdcResultStatusToString (status & 0xff000000));
  1714:     }
  1715:     fdcResultBuffer[0] = (byte) (status >> 24);
  1716:     if (status == FDC_ST0_IC) {
  1717:       fdcRPhase (1);
  1718:     } else {
  1719:       fdcResultBuffer[1] = (byte) (status >> 16);
  1720:       fdcRPhase (2);
  1721:     }
  1722:   }  //fdcCommandSenseInterruptStatus()
  1723: 
  1724:   //fdcCommandResetStandby ()
  1725:   //  0x14  RESET STANDBY
  1726:   //    スタンバイ状態の解除
  1727:   public static void fdcCommandResetStandby () {
  1728:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1729:       System.out.printf ("%08x fdcCommandResetStandby()\n", XEiJ.regPC0);
  1730:     }
  1731:     //何もしない
  1732:     fdcCPhase ();  //C-Phaseに戻る
  1733:   }  //fdcCommandResetStandby()
  1734: 
  1735:   //fdcCommandSetStandby ()
  1736:   //  0x15  SET STANDBY
  1737:   //    スタンバイ状態への移行
  1738:   public static void fdcCommandSetStandby () {
  1739:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1740:       System.out.printf ("%08x fdcCommandSetStandby()\n", XEiJ.regPC0);
  1741:     }
  1742:     //何もしない
  1743:     fdcCPhase ();  //C-Phaseに戻る
  1744:   }  //fdcCommandSetStandby()
  1745: 
  1746:   //fdcCommandSoftwareReset ()
  1747:   //  0x16  SOFTWARE RESET
  1748:   //    FDCの初期化
  1749:   public static void fdcCommandSoftwareReset () {
  1750:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1751:       System.out.printf ("%08x fdcCommandSoftwareReset()\n", XEiJ.regPC0);
  1752:     }
  1753:     //何もしない
  1754:     fdcCPhase ();  //C-Phaseに戻る
  1755:   }  //fdcCommandSoftwareReset()
  1756: 
  1757:   //fdcCommandInvalid ()
  1758:   //  0x00  INVALID
  1759:   //  0x01  INVALID
  1760:   //  0x0b  INVALID
  1761:   //  0x0e  INVALID
  1762:   //  0x10  INVALID
  1763:   //  0x12  INVALID
  1764:   //  0x13  INVALID
  1765:   //  0x17  INVALID
  1766:   //  0x18  INVALID
  1767:   //  0x1a  INVALID
  1768:   //  0x1b  INVALID
  1769:   //  0x1c  INVALID
  1770:   //  0x1e  INVALID
  1771:   //  0x1f  INVALID
  1772:   //    不正なコマンドまたはSENSE INTERRUPT STATUSの不正使用
  1773:   public static void fdcCommandInvalid () {
  1774:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1775:       System.out.printf ("%08x fdcCommandInvalid()\n", XEiJ.regPC0);
  1776:     }
  1777:     int status = FDC_ST0_IC;
  1778:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1779:       System.out.printf ("\tstatus=%s\n", fdcResultStatusToString (status));
  1780:     }
  1781:     fdcResultBuffer[0] = (byte) (status >> 24);  //ST0
  1782:     fdcRPhase (1);
  1783:   }  //fdcCommandInvalid
  1784: 
  1785: 
  1786:   public static String fdcResultStatusToString (int status) {
  1787:     int ic = status >> 24 + 6 & 3;
  1788:     return String.format ("0x%08x\n" +
  1789:                           "\t\tST0=0x%02x(IC=%d(%s) SE=%d EC=%d NR=%d HD=%d US=%d)\n" +
  1790:                           "\t\tST1=0x%02x(EN=%d DE=%d OR=%d ND=%d NW=%d MA=%d)\n" +
  1791:                           "\t\tST2=0x%02x(CM=%d DD=%d NC=%d SH=%d SN=%d BC=%d MD=%d)",
  1792:                           status,
  1793:                           status >> 24 & 255,
  1794:                           ic, ic == 0 ? "NT" : ic == 1 ? "AT" : ic == 2 ? "IC" : "AI",  //IC
  1795:                           status >> 24 + 5 & 1,
  1796:                           status >> 24 + 4 & 1,
  1797:                           status >> 24 + 3 & 1,
  1798:                           status >> 24 + 2 & 1,
  1799:                           status >> 24 + 0 & 3,
  1800:                           status >> 16 & 255,
  1801:                           status >> 16 + 7 & 1,
  1802:                           status >> 16 + 5 & 1,
  1803:                           status >> 16 + 4 & 1,
  1804:                           status >> 16 + 2 & 1,
  1805:                           status >> 16 + 1 & 1,
  1806:                           status >> 16 + 0 & 1,
  1807:                           status >> 8 & 255,
  1808:                           status >> 8 + 6 & 1,
  1809:                           status >> 8 + 5 & 1,
  1810:                           status >> 8 + 4 & 1,
  1811:                           status >> 8 + 3 & 1,
  1812:                           status >> 8 + 2 & 1,
  1813:                           status >> 8 + 1 & 1,
  1814:                           status >> 8 + 0 & 1);
  1815:   }  //fdcResultStatusToString(int)
  1816: 
  1817:   //fdcStatusToString (status)
  1818:   public static String fdcStatusToString (int status) {
  1819:     return String.format ("0x%02x(RQM=%d DIO=%d(%s) NDM=%d CB=%d D3B=%d D2B=%d D1B=%d D0B=%d)",
  1820:                           status,
  1821:                           status >> 7,
  1822:                           status >> 6 & 1,
  1823:                           (status >> 6 & 1) == 0 ? "MPU->FDC" : "FDC->MPU",
  1824:                           status >> 5 & 1,
  1825:                           status >> 4 & 1,
  1826:                           status >> 3 & 1,
  1827:                           status >> 2 & 1,
  1828:                           status >> 1 & 1,
  1829:                           status & 1);
  1830:   }  //fdcStatusToString(int)
  1831: 
  1832: 
  1833: 
  1834:   //========================================================================================
  1835:   //$$FDU FDユニット
  1836:   //  フロッピーディスクのユニット
  1837:   //
  1838:   public static class FDUnit extends AbstractUnit {
  1839: 
  1840:     public FDMedia fduMedia;  //メディアの種類
  1841:     public byte[] fduImage;  //イメージ
  1842:     public boolean fduWritten;  //true=書き込みがあった
  1843: 
  1844:     public int fduCommandNumber;  //fdcCommandNumberのコピー。シークを伴うコマンドのE-Phase以降で使う
  1845: 
  1846:     //  (接続フラグ)
  1847:     //    ユニットが接続されているときON、接続されていないときOFF
  1848:     //public boolean fduIsConnected {
  1849:     //  return abuConnected;
  1850:     //}  //fduIsConnected()
  1851: 
  1852:     //  (挿入フラグ)
  1853:     //    ドライブステータス(0x00e94005)のReadのbit7。0=OFF,1=ON
  1854:     //    接続フラグがON、かつ、
  1855:     //    ディスクイメージファイルの読み込みが完了している、かつ、
  1856:     //    メディアの種類の判別が完了している
  1857:     public boolean fduIsInserted () {
  1858:       return abuConnected && abuInserted && fduMedia != null;
  1859:     }  //fduIsInserted()
  1860: 
  1861:     //  (誤挿入フラグ)
  1862:     //    ドライブステータス(0x00e94005)のReadのbit6。0=OFF,1=ON
  1863:     //    使用しない。常にOFF
  1864: 
  1865:     //  点滅設定フラグ
  1866:     //    ドライブコントロール(0x00e94005)のWriteのbit7。0=OFF,1=ON
  1867:     public boolean fduBlinking;  //true=点滅が設定されている
  1868: 
  1869:     //  排出禁止設定フラグ
  1870:     //    ドライブコントロール(0x00e94005)のWriteのbit6。0=OFF,1=ON
  1871:     public boolean fduPrevented;  //true=排出禁止が設定されている
  1872: 
  1873:     //  排出要求フラグ
  1874:     //    ドライブコントロール(0x00e94005)のWriteのbit5。0=OFF,1=ON
  1875: 
  1876:     //  モータON設定フラグ
  1877:     //    ドライブセレクト(0x00e94007)のWriteのbit7。0=OFF,1=ON
  1878: 
  1879:     //  2DD設定フラグ
  1880:     //    ドライブセレクト(0x00e94007)のWriteのbit5。0=OFF,1=ON
  1881:     public boolean fduDoubleDensity;  //true=2DD設定
  1882: 
  1883:     //  書き込み禁止フラグ
  1884:     //    デバイスステータス(ST3)のbit6。0=書き込み許可,1=書き込み禁止
  1885:     public boolean fduIsWriteProtected () {  //true=書き込み禁止
  1886:       return abuWriteProtected;
  1887:     }  //fduIsWriteProtected()
  1888: 
  1889:     //ready = fduIsReady ()
  1890:     //  レディ信号の状態
  1891:     //  デバイスステータス(ST3)のbit5
  1892:     //    0  ノットレディ
  1893:     //    1  レディ。強制レディ状態または接続されていてメディアが挿入されていてモータ動作中またはモータ減速中
  1894:     public boolean fduIsReady () {
  1895:       return (fdcEnforcedReady ||  //強制レディ状態または
  1896:               (abuConnected &&  //接続されていて
  1897:                fduIsInserted () &&  //メディアが挿入されていて
  1898:                (fduMotorStatus == FDU_MOTOR_RUN_SOON ||  //モータ動作前(レディ→割り込み要求)または
  1899:                 fduMotorStatus == FDU_MOTOR_DECELERATING ||  //モータ減速中(レディ→ノットレディ)または
  1900:                 fduMotorStatus == FDU_MOTOR_RUNNING)));  //モータ動作中(レディ)
  1901:     }  //fduIsReady()
  1902: 
  1903:     //  アクセスランプ
  1904:     //    消灯    挿入フラグがOFF、かつ、点滅設定フラグがOFF
  1905:     //    緑点滅  挿入フラグがOFF、かつ、点滅設定フラグがON
  1906:     //    緑点灯  挿入フラグがON、かつ、ユニットビジーフラグがOFF
  1907:     //    赤点灯  挿入フラグがON、かつ、ユニットビジーフラグがON
  1908:     public static final int FDU_ACCESS_OFF            = 0;
  1909:     public static final int FDU_ACCESS_GREEN_BLINKING = 1;
  1910:     public static final int FDU_ACCESS_GREEN_ON       = 2;
  1911:     public static final int FDU_ACCESS_RED_ON         = 3;
  1912: 
  1913:     //  イジェクトランプ
  1914:     //    消灯    挿入フラグがOFF
  1915:     //    消灯    挿入フラグがON、かつ、排出禁止フラグがON
  1916:     //    緑点灯  挿入フラグがON、かつ、排出禁止フラグがOFF
  1917:     public static final int FDU_EJECT_OFF      = 0;
  1918:     public static final int FDU_EJECT_GREEN_ON = 1;
  1919: 
  1920: 
  1921:     //シーク
  1922:     public static final long FDU_SEEK_INTERVAL = XEiJ.TMR_FREQ * 1 / 1000;  //1ms。シークティッカーの動作間隔
  1923:     public int fduNCN;  //NCN 目標シリンダ番号(NCNn)
  1924:     public int fduPCN;  //PCN Present Cylinder Number シリンダ番号(PCNn)。Track0信号=fduPCN==0?1:0
  1925:     public int fduPHN;  //PHN Present Head Number サイド番号。0~1。2HDEは最初のセクタを除いて128~129
  1926:     public int fduPRN;  //PRN Present Record Number セクタ番号。1~1トラックあたりのセクタ数。2HSは最初のセクタを除いて10~18
  1927:     public int fduPNN;  //PNN Present Record Length セクタ長。0~7
  1928:     public int fduEOT;  //End of Track。終了セクタ
  1929:     public int fduSTP;  //Step。1=R++,2=R+=2
  1930:     public int fduSRC;  //ステップレートカウンタ。初期値は16-SRT
  1931:     public boolean fduSeekStepWaiting;  //シークステップ待機(seekStepWaiting)
  1932:     public boolean fduSeekEndInterruptWaiting;  //シーク終了割り込み待機(seekEndInterruptWaiting)
  1933:     public boolean fduSeekEndInterruptRequest;  //シーク終了割り込み要求(seekEndInterruptRequest)
  1934:     public int fduSeekResultStatus;  //シーク終了割り込みでSENSE INTERRUPT STATUSが返すリザルトステータス
  1935: 
  1936:     //fduSeekTicker
  1937:     //  シークティッカー
  1938:     public final TickerQueue.Ticker fduSeekTicker = new TickerQueue.Ticker () {
  1939:       @Override protected void tick () {
  1940:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1941:           System.out.printf ("%08x fduSeekTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  1942:         }
  1943:         if ((fdcStatus & 1 << abuNumber) != 0) {  //FDnビジー(DnB)が1のとき
  1944:           if ((fdcStatus & FDC_CB) != 0) {  //FDCビジー(CB)が1のとき
  1945:             fduSeekStepWaiting = true;  //シークステップ待機(seekStepWaiting)を1にする
  1946:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1947:               System.out.printf ("\tfduSeekStepWaiting%d=%b\n", abuNumber, fduSeekStepWaiting);
  1948:             }
  1949:           } else {  //FDCビジー(CB)が0のとき
  1950:             fduSeekStep ();  //シークステップ
  1951:           }
  1952:         } else {  //FDnビジー(DnB)が0のとき
  1953:           fduSeekStep ();  //シークステップ
  1954:         }
  1955:       }  //tick()
  1956:     };  //fduSeekTicker
  1957: 
  1958:     //fduSeekStep ()
  1959:     //  シークステップ
  1960:     public void fduSeekStep () {
  1961:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1962:         System.out.printf ("%08x fduSeekStep%d()\n", XEiJ.regPC0, abuNumber);
  1963:       }
  1964:       if (fduPCN != fduNCN) {  //シリンダ番号(PCNn)と目標シリンダ番号(NCNn)が違うとき
  1965:         fduSRC--;  //ステップレートカウンタ(SRC)をデクリメントする
  1966:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1967:           System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  1968:         }
  1969:         if (fduSRC == 0) {  //ステップレートカウンタ(SRC)が0になったとき
  1970:           fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  1971:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1972:             System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  1973:           }
  1974:           if (fduPCN < fduNCN) {  //シリンダ番号(PCNn)が目標シリンダ番号(NCNn)よりも小さいとき
  1975:             fduPCN++;  //シリンダ番号(PCNn)をインクリメントする
  1976:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1977:               System.out.printf ("\tfduPCN%d=%d\n", abuNumber, fduPCN);
  1978:             }
  1979:           } else {  //シリンダ番号(PCNn)が目標シリンダ番号(NCNn)よりも大きいとき
  1980:             fduPCN--;  //シリンダ番号(PCNn)をデクリメントする
  1981:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1982:               System.out.printf ("\tfduPCN%d=%d\n", abuNumber, fduPCN);
  1983:             }
  1984:           }
  1985:         }  //if fduSRC==0
  1986:         TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを1ms後に予約する
  1987:         return;
  1988:       }
  1989:       //シーク終了
  1990:       //E-Phase
  1991:       switch (fduCommandNumber) {
  1992:         //case 0x02:  //READ DIAGNOSTIC
  1993:         //  fduReadDiagnosticEPhase ();
  1994:         //  break;
  1995:       case 0x05:  //WRITE DATA
  1996:         fduWriteDataEPhase ();
  1997:         break;
  1998:       case 0x06:  //READ DATA
  1999:         fduReadDataEPhase ();
  2000:         break;
  2001:       case 0x07:  //RECALIBRATE
  2002:       case 0x0f:  //SEEK
  2003:         //SEEK/RECALIBRATEコマンドのとき
  2004:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2005:         break;
  2006:         //case 0x09:  //WRITE DELETED DATA
  2007:         //  fduWriteDeletedDataEPhase ();
  2008:         //  break;
  2009:         //case 0x0c:  //READ DELETED DATA
  2010:         //  fduReadDeletedDataEPhase ();
  2011:         //  break;
  2012:       case 0x11:  //SCAN EQUAL
  2013:       case 0x19:  //SCAN LOW OR EQUAL
  2014:       case 0x1d:  //SCAN HIGH OR EQUAL
  2015:         fduScanEPhase ();
  2016:         break;
  2017:       }
  2018:     }  //fduSeekStep()
  2019: 
  2020:     //fduSeekEnd ()
  2021:     //  SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2022:     public void fduSeekEnd () {
  2023:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2024:         System.out.printf ("%08x fduSeekEnd%d()\n", XEiJ.regPC0, abuNumber);
  2025:       }
  2026:       if ((fdcStatus & FDC_CB) != 0) {  //FDCビジー(CB)が1のとき
  2027:         fduSeekEndInterruptWaiting = true;  //シーク終了割り込み待機(seekEndInterruptWaiting)を1にする
  2028:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2029:           System.out.printf ("\tfduSeekEndInterruptWaiting%d=%b\n", abuNumber, fduSeekEndInterruptWaiting);
  2030:         }
  2031:       } else {  //FDCビジー(CB)が0のとき
  2032:         if (!fduSeekEndInterruptRequest) {  //シーク終了割り込み要求(seekEndInterruptRequest)が0のとき
  2033:           fduSeekEndInterruptRequest = true;  //シーク終了割り込み要求(seekEndInterruptRequest)を1にする
  2034:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2035:             System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", abuNumber, fduSeekEndInterruptRequest);
  2036:           }
  2037:           IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  2038:         }
  2039:       }
  2040:     }  //fduSeekEnd()
  2041: 
  2042: 
  2043:     //モータ
  2044:     //
  2045:     //  モータON/OFFのFDC割り込み
  2046:     //    0x00e94001==0x80
  2047:     //    RQM=1なので0x00e94003=0x08(SENSE INTERRUPT STATUS)
  2048:     //    0x00e94001==0x00  RQM=0,DIO=0(OUT),NDM=0,CB=0,D3B=0,D2B=0,D1B=0,D0B=0 x 2回~3回
  2049:     //    0x00e94001==0x10  RQM=0,DIO=0(OUT),NDM=0,CB=1,D3B=0,D2B=0,D1B=0,D0B=0 x 2回
  2050:     //    0x00e94001==0xd0  RQM=1,DIO=1(IN),NDM=0,CB=1,D3B=0,D2B=0,D1B=0,D0B=0
  2051:     //    0x00e94003==0xc0(モータ動作中のとき)  IC=3(AI状態遷移)
  2052:     //                0xc8(モータ停止中のとき)  IC=3(AI状態遷移),NR=1(ノットレディ)
  2053:     //    0x00e94003==0x00  PCN
  2054:     //    0x00e94001==0x80
  2055:     //
  2056:     //  謎
  2057:     //    X68030実機で2HDディスクを入れたFDD0のモータをONにすると、FDD0だけでなく、メディアが入っていないFDD1と、
  2058:     //    接続されていないFDD2とFDD3もレディ状態になったことを知らせる、4個の状態遷移割り込みステータスが出力される
  2059:     //    OFFも同様
  2060:     //    これはドライブの選択をPEDECが行うのでuPD72065 FDCがREADY信号をポーリングするときドライブを選択できないことが原因
  2061:     //    https://twitter.com/moveccr/status/1518985253049991169
  2062:     //
  2063:     //  モータON
  2064:     //      FDU_MOTOR_SLEEPING  モータ停止中(ノットレディ)
  2065:     //      ↓
  2066:     //    fdcMotorOn  モータON
  2067:     //      FDU_MOTOR_ACCELERATING  モータ加速中(ノットレディ→レディ)
  2068:     //      ↓
  2069:     //      ↓  FDU_MOTOR_ON_DELAY  モータ加速中(ノットレディ→レディ)
  2070:     //      ↓
  2071:     //    fduMotorAcceleratingTicker  モータ加速中ティッカー(ノットレディ→レディ)
  2072:     //      FDU_MOTOR_RUN_SOON  モータ動作前(レディ→割り込み要求)
  2073:     //      READY=1
  2074:     //      ↓
  2075:     //      ↓  FDU_MOTOR_INTERRUPT_DELAY  レディ→割り込み要求
  2076:     //      ↓
  2077:     //    fduMotorRunSoonTicker  モータ動作前ティッカー(レディ→割り込み要求)
  2078:     //      FDU_MOTOR_RUNNING  モータ動作中(レディ)
  2079:     //      状態遷移割り込み要求
  2080:     //
  2081:     //  モータOFF
  2082:     //      FDU_MOTOR_RUNNING  モータ動作中(レディ)
  2083:     //      ↓
  2084:     //    fdcMotorOff  モータOFF
  2085:     //      FDU_MOTOR_DECELERATING  モータ減速中(レディ→ノットレディ)
  2086:     //      ↓
  2087:     //      ↓  FDU_MOTOR_OFF_DELAY  レディ→ノットレディ
  2088:     //      ↓
  2089:     //    fduMotorDeceleratingTicker  モータ減速中ティッカー(レディ→ノットレディ)
  2090:     //      FDU_MOTOR_SLEEP_SOON  モータ停止前(ノットレディ→割り込み要求)
  2091:     //      READY=0
  2092:     //      ↓
  2093:     //      ↓  FDU_MOTOR_INTERRUPT_DELAY  ノットレディ→割り込み要求
  2094:     //      ↓
  2095:     //    fduMotorSleepSoonTicker  モータ停止前ティッカー(ノットレディ→割り込み要求)
  2096:     //      FDU_MOTOR_SLEEPING  モータ停止中(ノットレディ)
  2097:     //      状態遷移割り込み要求
  2098:     //
  2099:     //  メモ
  2100:     //    IOCSはモータONしてからSENSE DEVICE STATUSコマンドを繰り返してST3のREADYが1になるのを待つ
  2101:     //    このとき2バイトのコマンドを出力する間だけ割り込みを禁止している
  2102:     //    ST3のREADYが1になるのと同時に状態遷移割り込みを要求すると、
  2103:     //    2バイトのコマンドを出力している間にFDC割り込みが要求が発生し、
  2104:     //    コマンドの出力が終わって割り込みが許可されるとすぐに割り込み処理が開始されてしまうことがある
  2105:     //    その時点でR-Phaseになっているので割り込みハンドラが状態遷移割り込みを転送終了割り込みと誤認して、
  2106:     //    SENSE DEVICE STATUSコマンドのST3を転送コマンドのST0として回収してしまう
  2107:     //    割り込み処理が終了してSENSE DEVICE STATUSコマンドのST3を読み出そうとしたときにはC-Phaseになっており、
  2108:     //    SENSE DEVICE STATUSコマンドのR-Phaseを待つループから抜けられなくなってハングアップする
  2109:     //      FDC                     MPU
  2110:     //      C-Phase                 割り込み禁止
  2111:     //      状態遷移割り込み要求    SENSE DEVICE STATUSコマンドを出力する
  2112:     //      R-Phase                 割り込み許可
  2113:     //                              割り込み処理開始
  2114:     //                              SENSE DEVICE STATUSコマンドのST3を転送コマンドのST0として回収してしまう
  2115:     //      C-Phase                 割り込み処理終了
  2116:     //                              SENSE DEVICE STATUSコマンドのR-Phaseを待つループから抜けられなくなる
  2117:     //    実機はポーリングでREADYを監視しているので通常はREADYが1になってから割り込みが発生するまで少し間が空く
  2118:     //    最小間隔が0だと実機でも同じ問題が発生するはずだが見たことがないので最小間隔は0よりも大きいと考えられる
  2119:     //
  2120:     //    +------------------+--------------------------------------------------------+
  2121:     //    |  イジェクト許可  |                     イジェクト禁止                     |
  2122:     //    +------------------+------------------+-------------------------------------+
  2123:     //    |             ノットレディ            |                レディ               |
  2124:     //    +-------------------------------------+-------------------------------------+
  2125:     //    |                 ON                 ON                 ON                  |
  2126:     //    |                 →   ACCELERATING  →     RUN_SOON    →                  |
  2127:     //    |     SLEEPING         OFF↓  ↑ON        OFF↓  ↑ON          RUNNING      |
  2128:     //    |                 ←    SLEEP_SOON   ←   DECELERATING  ←                  |
  2129:     //    |                 OFF                OFF                OFF                 |
  2130:     //    +---------------------------------------------------------------------------+
  2131:     //    SLEEPINGからのONでACCELERATINGに移行するが、DECELERATINGからのONでACCELERATINGに移行してしまうと、
  2132:     //    RUNNINGからOFFされてDECELERATINGになった直後にONされたときノットレディの時間ができてしまう
  2133:     //    それではDECELERATINGの時間を設けてノットレディを遅延させている意味がない
  2134:     //    DECELERATINGからのONはRUN_SOONに移行しなければならない
  2135:     //
  2136:     public static final int FDU_MOTOR_SLEEPING     = 0;  //モータ停止中(ノットレディ)。イジェクト可
  2137:     public static final int FDU_MOTOR_ACCELERATING = 1;  //モータ加速中(ノットレディ→レディ)。イジェクト不可
  2138:     public static final int FDU_MOTOR_SLEEP_SOON   = 2;  //モータ停止前(ノットレディ→割り込み要求)。イジェクト不可
  2139:     public static final int FDU_MOTOR_RUN_SOON     = 3;  //モータ動作前(レディ→割り込み要求)。イジェクト不可
  2140:     public static final int FDU_MOTOR_DECELERATING = 4;  //モータ減速中(レディ→ノットレディ)。イジェクト不可
  2141:     public static final int FDU_MOTOR_RUNNING      = 5;  //モータ動作中(レディ)。イジェクト不可
  2142:     public int fduMotorStatus;  //モータの状態
  2143:     public static final long FDU_MOTOR_ON_DELAY        = XEiJ.TMR_FREQ * 100 / 1000000;  //100us。モータ加速中(ノットレディ→レディ)
  2144:     public static final long FDU_MOTOR_OFF_DELAY       = XEiJ.TMR_FREQ * 3;  //3s。モータ減速中(レディ→ノットレディ)
  2145:     public static final long FDU_MOTOR_INTERRUPT_DELAY = XEiJ.TMR_FREQ * 100 / 1000000;  //100us。割り込み待ち
  2146:     public boolean fduSavedReady;  //保存されたレディ信号の状態(savedReady)
  2147:     public boolean fduAttentionCheckWaiting;  //状態遷移確認フラグ(attentionCheckWaiting)
  2148:     public boolean fduAttentionInterruptRequest;  //状態遷移割り込み要求(attentionInterruptRequest)
  2149: 
  2150:     //fduMotorOn ()
  2151:     //  モータON
  2152:     public void fduMotorOn () {
  2153:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2154:         System.out.printf ("%08x fduMotorOn%d()\n", XEiJ.regPC0, abuNumber);
  2155:       }
  2156:       if (!fduIsInserted ()) {  //メディアが挿入されていないとき
  2157:         return;  //何もしない
  2158:       }
  2159:       switch (fduMotorStatus) {
  2160:       case FDU_MOTOR_SLEEPING:  //モータ停止中(ノットレディ)のとき
  2161:         fduMotorStatus = FDU_MOTOR_ACCELERATING;  //モータ加速中(ノットレディ→レディ)にする
  2162:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2163:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2164:         }
  2165:         if (!fduPrevented) {  //排出禁止設定でないとき
  2166:           prevent ();  //排出を禁止する
  2167:         }
  2168:         TickerQueue.tkqAdd (fduMotorAcceleratingTicker, XEiJ.mpuClockTime + FDU_MOTOR_ON_DELAY);  //モータ加速中ティッカー(ノットレディ→レディ)を予約する
  2169:         break;
  2170:       case FDU_MOTOR_SLEEP_SOON:  //モータ停止前(ノットレディ→割り込み要求)のとき
  2171:         TickerQueue.tkqRemove (fduMotorSleepSoonTicker);  //モータ停止前ティッカー(ノットレディ→割り込み要求)を取り消す
  2172:         fduMotorStatus = FDU_MOTOR_ACCELERATING;  //モータ加速中(ノットレディ→レディ)にする
  2173:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2174:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2175:         }
  2176:         TickerQueue.tkqAdd (fduMotorAcceleratingTicker, XEiJ.mpuClockTime + FDU_MOTOR_ON_DELAY);  //モータ加速中ティッカー(ノットレディ→レディ)を予約する
  2177:         break;
  2178:       case FDU_MOTOR_DECELERATING:  //モータ減速中(レディ→ノットレディ)のとき
  2179:         TickerQueue.tkqRemove (fduMotorDeceleratingTicker);  //モータ減速中ティッカー(レディ→ノットレディ)を取り消す
  2180:         fduMotorStatus = FDU_MOTOR_RUN_SOON;  //モータ動作前(レディ→割り込み要求)にする
  2181:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2182:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2183:         }
  2184:         TickerQueue.tkqAdd (fduMotorRunSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ動作前ティッカー(レディ→割り込み要求)を予約する
  2185:         break;
  2186:       }
  2187:     }  //fduMotorOn()
  2188: 
  2189:     //fduMotorAcceleratingTicker
  2190:     //  モータ加速中ティッカー(ノットレディ→レディ)
  2191:     public final TickerQueue.Ticker fduMotorAcceleratingTicker = new TickerQueue.Ticker () {
  2192:       @Override protected void tick () {
  2193:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2194:           System.out.printf ("%08x fduMotorAcceleratingTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2195:         }
  2196:         fduMotorStatus = FDU_MOTOR_RUN_SOON;  //モータ動作前(レディ→割り込み要求)にする
  2197:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2198:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2199:         }
  2200:         //(強制レディ状態でなければここでレディになる)
  2201:         TickerQueue.tkqAdd (fduMotorRunSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ動作前ティッカー(レディ→割り込み要求)を予約する
  2202:       }  //tick()
  2203:     };  //fduMotorAcceleratingTicker
  2204: 
  2205:     //fduMotorRunSoonTicker
  2206:     //  モータ動作前ティッカー(レディ→割り込み要求)
  2207:     public final TickerQueue.Ticker fduMotorRunSoonTicker = new TickerQueue.Ticker () {
  2208:       @Override protected void tick () {
  2209:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2210:           System.out.printf ("%08x fduMotorRunSoonTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2211:         }
  2212:         fduMotorStatus = FDU_MOTOR_RUNNING;  //モータ動作中(レディ)にする
  2213:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2214:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2215:         }
  2216:         fduMotorInterrupt ();  //モータONまたはモータOFFから一定の時間が経ったとき
  2217:       }  //tick()
  2218:     };  //fduMotorRunSoonTicker
  2219: 
  2220:     //fduMotorOff ()
  2221:     //  モータOFF
  2222:     @SuppressWarnings ("fallthrough") public void fduMotorOff () {
  2223:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2224:         System.out.printf ("%08x fduMotorOff%d()\n", XEiJ.regPC0, abuNumber);
  2225:       }
  2226:       if (!fduIsInserted ()) {  //メディアが挿入されていないとき
  2227:         return;  //何もしない
  2228:       }
  2229:       switch (fduMotorStatus) {
  2230:       case FDU_MOTOR_ACCELERATING:  //モータ加速中(ノットレディ→レディ)のとき
  2231:         TickerQueue.tkqRemove (fduMotorAcceleratingTicker);  //モータ加速中ティッカー(ノットレディ→レディ)を取り消す
  2232:         fduMotorStatus = FDU_MOTOR_SLEEP_SOON;  //モータ停止前(ノットレディ→割り込み要求)にする
  2233:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2234:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2235:         }
  2236:         TickerQueue.tkqAdd (fduMotorSleepSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ停止前ティッカー(ノットレディ→割り込み要求)を予約する
  2237:         break;
  2238:       case FDU_MOTOR_RUN_SOON:  //モータ動作前(レディ→割り込み要求)のとき
  2239:         TickerQueue.tkqRemove (fduMotorRunSoonTicker);  //モータ動作前ティッカー(レディ→割り込み要求)を取り消す
  2240:         //fallthrough
  2241:       case FDU_MOTOR_RUNNING:  //モータ動作中(レディ)のとき
  2242:         fduMotorStatus = FDU_MOTOR_DECELERATING;  //モータ減速中(レディ→ノットレディ)にする
  2243:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2244:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2245:         }
  2246:         TickerQueue.tkqAdd (fduMotorDeceleratingTicker, XEiJ.mpuClockTime + FDU_MOTOR_OFF_DELAY);  //モータ減速中ティッカー(レディ→ノットレディ)を予約する
  2247:         break;
  2248:       }
  2249:     }  //fduMotorOff()
  2250: 
  2251:     //fduMotorDeceleratingTicker
  2252:     //  モータ減速中ティッカー(レディ→ノットレディ)
  2253:     public final TickerQueue.Ticker fduMotorDeceleratingTicker = new TickerQueue.Ticker () {
  2254:       @Override protected void tick () {
  2255:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2256:           System.out.printf ("%08x fduMotorDeceleratingTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2257:         }
  2258:         fduMotorStatus = FDU_MOTOR_SLEEP_SOON;  //モータ停止前(ノットレディ→割り込み要求)にする
  2259:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2260:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2261:         }
  2262:         //(強制レディ状態でなければここでノットレディになる)
  2263:         TickerQueue.tkqAdd (fduMotorSleepSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ停止前ティッカー(ノットレディ→割り込み要求)を予約する
  2264:       }  //tick()
  2265:     };  //fduMotorDeceleratingTicker
  2266: 
  2267:     //fduMotorSleepSoonTicker
  2268:     //  モータ停止前ティッカー(ノットレディ→割り込み要求)
  2269:     public final TickerQueue.Ticker fduMotorSleepSoonTicker = new TickerQueue.Ticker () {
  2270:       @Override protected void tick () {
  2271:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2272:           System.out.printf ("%08x fduMotorSleepSoonTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2273:         }
  2274:         fduMotorStatus = FDU_MOTOR_SLEEPING;  //モータ停止中(ノットレディ)にする
  2275:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2276:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2277:         }
  2278:         if (!fduPrevented) {  //排出禁止設定でないとき
  2279:           allow ();  //排出を許可する
  2280:         }
  2281:         fduMotorInterrupt ();  //モータONまたはモータOFFから一定の時間が経ったとき
  2282:       }  //tick()
  2283:     };  //fduMotorSleepSoonTicker
  2284: 
  2285:     //fduMotorInterrupt ()
  2286:     //  モータONまたはモータOFFから一定の時間が経ったとき
  2287:     public void fduMotorInterrupt () {
  2288:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2289:         System.out.printf ("%08x fduMotorInterrupt%d()\n", XEiJ.regPC0, abuNumber);
  2290:       }
  2291:       if ((fdcStatus & 1 << abuNumber) != 0) {  //FDnビジー(DnB)が1のとき
  2292:         fduAttentionCheckWaiting = true;  //状態遷移確認フラグ(attentionCheckWaiting)を1にする
  2293:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2294:           System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", abuNumber, fduAttentionCheckWaiting);
  2295:         }
  2296:       } else {  //FDnビジー(DnB)が0のとき
  2297:         if ((fdcStatus & FDC_CB) != 0) {  //FDCビジー(CB)が1のとき
  2298:           fduAttentionCheckWaiting = true;  //状態遷移確認フラグ(attentionCheckWaiting)を1にする
  2299:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2300:             System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", abuNumber, fduAttentionCheckWaiting);
  2301:           }
  2302:         } else {  //FDCビジー(CB)が0のとき
  2303:           boolean ready = fduIsReady ();
  2304:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2305:             System.out.printf ("\tready%d=%b\n", abuNumber, ready);
  2306:           }
  2307:           if (ready != fduSavedReady) {  //レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
  2308:             fduSavedReady = ready;  //レディ信号の状態(isReady)を保存する(isReady→RPYn)
  2309:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2310:               System.out.printf ("\tfduSavedReady%d=%b\n", abuNumber, fduSavedReady);
  2311:             }
  2312:             if (!fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が0のとき
  2313:               fduAttentionInterruptRequest = true;  //状態遷移割り込み要求(attentionInterruptRequest)を1にする
  2314:               if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2315:                 System.out.printf ("\tfduAttentionInterruptRequest%d=%b\n", abuNumber, fduAttentionInterruptRequest);
  2316:               }
  2317:               IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  2318:             }
  2319:           }
  2320:         }
  2321:       }
  2322:     }  //fduMotorInterrupt()
  2323: 
  2324: 
  2325:     //コンストラクタ
  2326: 
  2327:     //new FDUnit (number)
  2328:     //  コンストラクタ
  2329:     public FDUnit (int number) {
  2330:       super (number);
  2331: 
  2332:       fduMedia = null;
  2333:       fduImage = null;
  2334:       fduWritten = false;
  2335: 
  2336:       fduBlinking = false;
  2337:       fduPrevented = false;
  2338:       fduDoubleDensity = false;
  2339: 
  2340:       fduNCN = 0;
  2341:       fduPCN = 0;
  2342:       fduPHN = 0;
  2343:       fduPRN = 1;
  2344:       fduPNN = 3;
  2345:       fduEOT = 1;
  2346:       fduSTP = 1;
  2347:       fduSeekStepWaiting = false;
  2348:       fduSeekEndInterruptWaiting = false;
  2349:       fduSeekEndInterruptRequest = false;
  2350:       fduSeekResultStatus = FDC_ST0_IC;
  2351: 
  2352:       fduMotorStatus = FDU_MOTOR_SLEEPING;  //モータ停止中(ノットレディ)
  2353:       fduSavedReady = false;
  2354:       fduAttentionCheckWaiting = false;
  2355:       fduAttentionInterruptRequest = false;
  2356:     }
  2357: 
  2358:     //fduTini ()
  2359:     //  後始末
  2360:     //  イメージファイルに書き出す
  2361:     public void fduTini () {
  2362:       if (fduIsInserted ()) {
  2363:         fduFlush ();
  2364:       }
  2365:     }  //fduTini()
  2366: 
  2367:     //success = unit.fduFlush ()
  2368:     //  イメージファイルに書き出す
  2369:     public boolean fduFlush () {
  2370:       if (!abuConnected ||  //接続されていない
  2371:           !fduIsInserted () ||  //挿入されていない
  2372:           !fduWritten) {  //書き込みがない
  2373:         return true;
  2374:       }
  2375:       if (fduIsWriteProtected ()) {  //書き込みが許可されていない
  2376:         return false;
  2377:       }
  2378:       int dotIndex = abuPath.lastIndexOf ('.');
  2379:       String upperExt = dotIndex < 0 ? "" : abuPath.substring (dotIndex + 1).toUpperCase ();
  2380:       if (upperExt.equals ("DIM")) {  // *.dim
  2381:         byte[] dimImage = new byte[256 + fduMedia.fdmBytesPerDisk];
  2382:         int dimSize = fduMedia.fdmMakeDimImage (dimImage, fduImage);  // *.DIMのイメージを作る
  2383:         if (dimSize < 0) {
  2384:           // *.DIMから読み込んだがFORMAT.XなどでDIMにできない形式に変更されてしまった
  2385:           XEiJ.pnlExitFullScreen (true);
  2386:           JOptionPane.showMessageDialog (null,
  2387:                                          Multilingual.mlnJapanese ?
  2388:                                          fduMedia.fdmName + " を *.DIM に変換できません" :
  2389:                                          fduMedia.fdmName + " cannot be converted to *.DIM");
  2390:           return false;
  2391:         }
  2392:         if (!XEiJ.rscPutFile (abuPath, dimImage, 0, dimSize)) {  //保存する
  2393:           return false;
  2394:         }
  2395:       } else {  // *.DIM以外
  2396:         if (!XEiJ.rscPutFile (abuPath, fduImage, 0, fduMedia.fdmBytesPerDisk)) {
  2397:           return false;
  2398:         }
  2399:       }
  2400:       fduWritten = false;
  2401:       return true;
  2402:     }  //fduFlush()
  2403: 
  2404:     //unit.connect (disconnectable)
  2405:     //  接続する
  2406:     @Override protected void connect (boolean disconnectable) {
  2407:       super.connect (disconnectable);
  2408:       fduImage = new byte[FDMedia.FDM_BUFFER_SIZE];
  2409:     }
  2410: 
  2411:     //unit.disconnect ()
  2412:     //  切り離す
  2413:     @Override protected void disconnect () {
  2414:       super.disconnect ();
  2415:       fduImage = null;
  2416:     }
  2417: 
  2418:     //unit.blink ()
  2419:     //  挿入されていないときLEDを点滅させる
  2420:     public void blink () {
  2421:       if (!abuConnected ||  //接続されていない
  2422:           fduBlinking) {  //既にLEDが点滅している
  2423:         return;
  2424:       }
  2425:       fduBlinking = true;
  2426:       //! 表示なし
  2427:     }
  2428: 
  2429:     //unit.darken ()
  2430:     //  挿入されていないときLEDを消す
  2431:     public void darken () {
  2432:       if (!abuConnected ||  //接続されていない
  2433:           !fduBlinking) {  //LEDが点滅していない
  2434:         return;
  2435:       }
  2436:       fduBlinking = false;
  2437:       //! 表示なし
  2438:     }
  2439: 
  2440:     //success = unit.eject ()
  2441:     //  イジェクトする
  2442:     @Override protected boolean eject () {
  2443:       if (!fduFlush ()) {  //イメージファイルに書き出す
  2444:         return false;
  2445:       }
  2446:       fduMotorStatus = FDU_MOTOR_SLEEPING;  //モータ停止中(ノットレディ)にする
  2447:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2448:         System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2449:       }
  2450:       boolean inserted = fduIsInserted ();
  2451:       String path = abuPath;  //イジェクトされたイメージファイルのパス。super.eject()を呼び出す前にコピーすること
  2452:       if (!super.eject ()) {  //イジェクトする
  2453:         return false;
  2454:       }
  2455:       if (fduMedia != null) {  //挿入されていたとき
  2456:         fdcAddHistory (new File (path).getAbsoluteFile ());
  2457:         System.out.println (Multilingual.mlnJapanese ?
  2458:                             path + " を fd" + abuNumber + " から取り出しました" :
  2459:                             path + " was ejected from fd" + abuNumber);
  2460:       }
  2461:       fduMedia = null;
  2462:       fduPCN = 0;
  2463:       fduPHN = 0;
  2464:       fduPRN = 1;
  2465:       fduPNN = 3;
  2466:       //イジェクトされたときFDD割り込みを要求する
  2467:       if (inserted) {
  2468:         IOInterrupt.ioiFddFall ();
  2469:         IOInterrupt.ioiFddRise ();
  2470:       }
  2471:       return true;
  2472:     }
  2473: 
  2474:     //success = unit.open ()
  2475:     //  開くダイアログを開く
  2476:     @Override protected boolean open () {
  2477:       if (!super.open ()) {
  2478:         return false;
  2479:       }
  2480:       fdcOpenUnit = abuNumber;
  2481:       if (fdcOpenDialog == null) {
  2482:         fdcOpenDialog = new OpenDialog ();
  2483:         fdcOpenDialog.setReadOnly (Settings.sgsGetOnOff ("fdreadonly"));
  2484:         fdcOpenDialog.setReboot (Settings.sgsGetOnOff ("fdappreboot"));
  2485:         for (File[] files : fdcOpenHistory) {
  2486:           fdcOpenDialog.addHistory (files);
  2487:         }
  2488:         fdcOpenHistory.clear ();
  2489:       }
  2490:       fdcOpenDialog.rescanCurrentDirectory ();  //挿入されているファイルが変わると選択できるファイルも変わるのでリストを作り直す
  2491:       XEiJ.pnlExitFullScreen (true);
  2492:       fdcOpenDialog.setVisible (true);
  2493:       return true;
  2494:     }  //unit.open()
  2495: 
  2496:     //success = unit.insert (path, writeProtected)
  2497:     //  挿入する
  2498:     public boolean insert (String path, boolean writeProtected) {
  2499:       if (fdcIsInsertedPath (path)) {  //既に挿入されている
  2500:         return false;
  2501:       }
  2502:       if (!super.insert (path, writeProtected)) {  //挿入できなかった
  2503:         return false;
  2504:       }
  2505:       if (fduMedia != null) {
  2506:         IOInterrupt.ioiFddFall ();
  2507:         IOInterrupt.ioiFddRise ();
  2508:       }
  2509:       return true;
  2510:     }  //unit.insert(String)
  2511: 
  2512:     //loaded = unit.load (path)
  2513:     //  読み込む
  2514:     //  挿入されていない状態で呼び出すこと
  2515:     @Override protected boolean load (String path) {
  2516:       fduMedia = FDMedia.fdmPathToMedia (path, fduImage);
  2517:       if (fduMedia == null) {  //読み込めない
  2518:         return false;
  2519:       }
  2520:       if (abuWriteProtected && !abuUnprotectable) {  //書き込みが許可されることはない
  2521:         fduMedia.fdmReviveFiles (fduImage);  //削除ファイルを復元する
  2522:       }
  2523:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2524:         System.out.println ("media = " + fduMedia.fdmName);
  2525:         System.out.println ("------------------------------------------------------------------------");
  2526:         fduMedia.fdmPrintInfo ();
  2527:         System.out.println ("------------------------------------------------------------------------");
  2528:       }
  2529:       fduWritten = false;
  2530:       fdcAddHistory (new File (path).getAbsoluteFile ());
  2531:       System.out.println (Multilingual.mlnJapanese ?
  2532:                           path + " を fd" + abuNumber + " に挿入しました" :
  2533:                           path + " was inserted in fd" + abuNumber);
  2534:       return true;
  2535:     }  //unit.load(String)
  2536: 
  2537: 
  2538:     //name = unit.getName ()
  2539:     //  pathからnameを作る
  2540:     public String getName () {
  2541:       return abuPath.substring (abuPath.lastIndexOf (File.separatorChar) + 1);
  2542:     }
  2543: 
  2544:     //d = unit.fduDriveStatus ()
  2545:     //  bit7  1=挿入
  2546:     //  bit6  1=誤挿入
  2547:     public int fduDriveStatus () {
  2548:       return fduIsInserted () ? 0x80 : 0;
  2549:     }
  2550: 
  2551:     //fduDriveControl (d)
  2552:     //  bit7  0=消灯する,1=点滅する
  2553:     //  bit6  0=排出を許可する,1=排出を禁止する
  2554:     //  bit5  0=排出しない,1=排出する
  2555:     public void fduDriveControl (int d) {
  2556:       if (d << 31 - 5 < 0) {  //排出する
  2557:         eject ();
  2558:       }
  2559:       if (d << 31 - 6 < 0) {  //排出を禁止する
  2560:         fduPrevented = true;
  2561:         prevent ();
  2562:       } else {  //排出を許可する
  2563:         fduPrevented = false;
  2564:         allow ();
  2565:       }
  2566:       if ((byte) d < 0) {  //点滅する
  2567:         fduBlinking = true;
  2568:         blink ();
  2569:       } else {  //消灯する
  2570:         fduBlinking = false;
  2571:         darken ();
  2572:       }
  2573:     }
  2574: 
  2575:     //fduCommandReadDiagnostic ()
  2576:     //  0x02  READ DIAGNOSTIC
  2577:     //    診断のための読み出し
  2578:     //    セクタ1から開始し、1トラック分のエラーを累積して正常終了する
  2579:     //  C-Phase
  2580:     //    [0]  bit6    MF    MFM Mode         0=FM,1=MFM
  2581:     //         bit4-0  CMD   Command          0x02=READ DIAGNOSTIC
  2582:     //    [1]  bit2    HD    Head Address     サイド
  2583:     //         bit1-0  US    Unit Select      ユニット
  2584:     //    [2]          C     Cylinder Number  シリンダ
  2585:     //    [3]          H     Head Number      サイド
  2586:     //    [4]          R     Record Number    無意味
  2587:     //    [5]          N     Record Length    セクタ長=128<<N
  2588:     //    [6]          EOT   End of Track     終了セクタ
  2589:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  2590:     //    [8]          DTL   Data Length      N==0&&DTL<128のとき128バイト読み出してCRCをチェックするがMPUにはDTLバイトだけ転送する
  2591:     //  E-Phase(FDC→MPU)
  2592:     //    データ
  2593:     //  R-Phase
  2594:     //    [0]          ST0   Status 0         リザルトステータス0
  2595:     //    [1]          ST1   Status 1         リザルトステータス1
  2596:     //    [2]          ST2   Status 2         リザルトステータス2
  2597:     //    [3]          C     Cylinder Number  シリンダ
  2598:     //    [4]          H     Head Number      サイド
  2599:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  2600:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  2601:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  2602:     //                                        エラーのときは異常発生セクタ
  2603:     //    [6]          N     Record Length    セクタ長=128<<N
  2604:     //  リザルトステータス
  2605:     //    正常終了  NT
  2606:     //    ノットレディ  AT|NR
  2607:     //    ID部
  2608:     //      IDAM非検出  AT|MA
  2609:     //      C不一致(終了せず)  NT|ND
  2610:     //      H不一致(終了せず)  NT|ND
  2611:     //      R不一致(終了せず)  NT|ND
  2612:     //      N不一致(終了せず)  NT|ND
  2613:     //      CRC不一致(終了せず)  NT|DE
  2614:     //    データ部
  2615:     //      DAM非検出  AT|MA|MD
  2616:     //      DDAM検出(終了せず)  NT|CM
  2617:     //      CRC不一致(終了せず)  NT|DE|DD
  2618:     //      オーバーラン  AT|OR
  2619:     //    最終セクタで未終了  AT|EN
  2620:     public void fduCommandReadDiagnostic () {
  2621:       //!!!
  2622:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2623:         System.out.printf ("%08x READ DIAGNOSTIC is not implemented\n", XEiJ.regPC0);
  2624:       }
  2625:       fdcCommandInvalid ();
  2626:     }
  2627: 
  2628:     //fduCommandSenseDeviceStatus ()
  2629:     //  0x04  SENSE DEVICE STATUS
  2630:     //    状態信号(ST3)の読み出し
  2631:     //  C-Phase
  2632:     //    [0]          CMD   Command          0x04=SENSE DEVICE STATUS
  2633:     //    [1]  bit2    HD    Head Address     サイド
  2634:     //         bit1-0  US    Unit Select      ユニット
  2635:     //  R-Phase
  2636:     //    [0]          ST3   Status 3         リザルトステータス3
  2637:     public void fduCommandSenseDeviceStatus () {
  2638:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  2639:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2640:         System.out.printf ("%08x fduCommandSenseDeviceStatus%d()\n", XEiJ.regPC0, abuNumber);
  2641:       }
  2642:       int st3 = ((fduIsWriteProtected () ? FDC_ST3_WP : 0) |  //Write-Protected
  2643:                  (fduIsReady () ? FDC_ST3_RY : 0) |  //Ready
  2644:                  (fduIsInserted () && fduIsReady () && fduPCN == 0 ? FDC_ST3_T0 : 0) |  //Track 0
  2645:                  //(fduIsInserted () ? fduMedia.fdmTwoSide : 0) |  //Two Side
  2646:                  (fduPHN & 1) << 2 |  //HD
  2647:                  abuNumber);  //US1,US0
  2648:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2649:         System.out.printf ("\t\tST3=0x%02x(FT=%d WP=%d RY=%d T0=%d TS=%d HD=%d US=%d)\n",
  2650:                            st3,
  2651:                            st3 >> 7,
  2652:                            st3 >> 6 & 1,
  2653:                            st3 >> 5 & 1,
  2654:                            st3 >> 4 & 1,
  2655:                            st3 >> 3 & 1,
  2656:                            st3 >> 2 & 1,
  2657:                            st3 & 3);
  2658:       }
  2659:       fdcResultBuffer[0] = (byte) st3;
  2660:       fdcRPhase (1);
  2661:     }
  2662: 
  2663:     //fduCommandWriteData ()
  2664:     //  0x05  WRITE DATA
  2665:     //    データ(DAM)の書き込み
  2666:     //  C-Phase
  2667:     //    [0]  bit7    MT    Multitrack       マルチトラック
  2668:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  2669:     //         bit4-0  CMD   Command          0x05=WRITE DATA
  2670:     //    [1]  bit2    HD    Head Address     サイド
  2671:     //         bit1-0  US    Unit Select      ユニット
  2672:     //    [2]          C     Cylinder Number  シリンダ
  2673:     //    [3]          H     Head Number      サイド
  2674:     //    [4]          R     Record Number    開始セクタ
  2675:     //    [5]          N     Record Length    セクタ長=128<<N
  2676:     //    [6]          EOT   End of Track     終了セクタ
  2677:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  2678:     //    [8]          DTL   Data Length      N==0&&DTL<128のときMPUからDTLバイトだけ受け取って128バイト書き込む
  2679:     //  E-Phase(MPU→FDC)
  2680:     //    データ
  2681:     //  R-Phase
  2682:     //    [0]          ST0   Status 0         リザルトステータス0
  2683:     //    [1]          ST1   Status 1         リザルトステータス1
  2684:     //    [2]          ST2   Status 2         リザルトステータス2
  2685:     //    [3]          C     Cylinder Number  シリンダ
  2686:     //    [4]          H     Head Number      サイド
  2687:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  2688:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  2689:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  2690:     //                                        エラーのときは異常発生セクタ
  2691:     //    [6]          N     Record Length    セクタ長=128<<N
  2692:     //  リザルトステータス
  2693:     //    正常終了  NT
  2694:     //    ノットレディ  AT|NR
  2695:     //    ライトプロテクト  AT|NW
  2696:     //    ID部
  2697:     //      IDAM非検出  AT|MA
  2698:     //      C不一致(!=0xff)  AT|ND|NC
  2699:     //      C不一致(==0xff)  AT|ND|BC
  2700:     //      H不一致  AT|ND
  2701:     //      R不一致  AT|ND
  2702:     //      N不一致  AT|ND
  2703:     //      CRC不一致  AT|DE
  2704:     //    データ部
  2705:     //      フォールト  AT|EC
  2706:     //      オーバーラン  AT|OR
  2707:     //    最終セクタで未終了  AT|EN
  2708:     public void fduCommandWriteData () {
  2709:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  2710:       int ncn = fdcCommandBuffer[2] & 255;  //NCN
  2711:       int phn = fdcCommandBuffer[3] & 255;  //H
  2712:       int prn = fdcCommandBuffer[4] & 255;  //R
  2713:       int pnn = fdcCommandBuffer[5] & 255;  //N
  2714:       int eot = fdcCommandBuffer[6] & 255;  //EOT
  2715:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2716:         System.out.printf ("%08x fduCommandWriteData%d(NCN=%d H=%d R=%d N=%d EOT=%d)\n",
  2717:                            XEiJ.regPC0, abuNumber, ncn, phn, prn, pnn, eot);
  2718:       }
  2719:       fduCommandNumber = fdcCommandNumber;
  2720:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2721:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  2722:       }
  2723:       if (!fduIsReady ()) {  //ノットレディ
  2724:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2725:         return;
  2726:       }
  2727:       if (fduIsWriteProtected ()) {  //ライトプロテクト
  2728:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_NW | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2729:         return;
  2730:       }
  2731:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  2732:       fduPHN = phn;
  2733:       fduPRN = prn;
  2734:       fduPNN = pnn;
  2735:       fduEOT = eot;
  2736:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  2737:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2738:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  2739:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  2740:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2741:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  2742:         System.out.printf ("\tfduEOT%d=%d\n", abuNumber, fduEOT);
  2743:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  2744:       }
  2745:       fdcStatus = (FDC_MPU_TO_FDC | FDC_CB |  //E-Phase(転送中)に移行する(RQM=0で待機する)
  2746:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2747:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2748:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2749:       }
  2750:       TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  2751:     }  //fduCommandWriteData()
  2752: 
  2753:     public void fduWriteDataEPhase () {
  2754:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2755:         System.out.printf ("%08x fduWriteDataEPhase%d()\n", XEiJ.regPC0, abuNumber);
  2756:       }
  2757:       if (fduPCN != fduNCN) {  //シークが中断されたとき
  2758:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2759:         return;
  2760:       }
  2761:       //E-Phase
  2762:       fduWritten = true;
  2763:       fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC | FDC_CB |
  2764:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2765:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2766:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2767:       }
  2768:       //転送
  2769:       int o = fduCalcOffset ();
  2770:       if (o < 0) {  //セクタが存在しない
  2771:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2772:         return;
  2773:       }
  2774:       fdcReadHandle = null;
  2775:       fdcWriteHandle = fduImage;
  2776:       fdcIndex = fdcStart = o;
  2777:       fdcLimit = o + (128 << fduPNN);
  2778:       HD63450.dmaFallPCL (0);  //DMA転送開始
  2779:     }  //fduWriteDataEPhase()
  2780: 
  2781:     //fduWriteDataEPhaseEnd ()
  2782:     public void fduWriteDataEPhaseEnd () {
  2783:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2784:         System.out.printf ("%08x fduWriteDataEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  2785:       }
  2786:       if (fduPRN == fduEOT) {  //終了
  2787:         HD63450.dmaRisePCL (0);  //DMA転送終了
  2788:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2789:         return;
  2790:       }
  2791:       //継続
  2792:       if (fduMedia == FDMedia.FDM_2HS && fduPCN == 0 && fduPHN == 0 && fduPRN == 1) {
  2793:         fduPRN = 11;
  2794:       } else {
  2795:         fduPRN++;
  2796:       }
  2797:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2798:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2799:       }
  2800:       int o = fduCalcOffset ();
  2801:       if (o < 0) {  //セクタが存在しない
  2802:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | (fduPHN << 2 | abuNumber << 24));
  2803:         return;
  2804:       }
  2805:       fdcIndex = fdcStart = o;
  2806:       fdcLimit = o + (128 << fduPNN);
  2807:     }  //fduWriteDataEPhaseEnd()
  2808: 
  2809:     //fduCommandReadData ()
  2810:     //  0x06  READ DATA
  2811:     //    データ(DAM)の読み出し
  2812:     //  C-Phase
  2813:     //    [0]  bit7    MT    Multitrack       マルチトラック
  2814:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  2815:     //         bit5    SK    Skip             DDAMをスキップ
  2816:     //         bit4-0  CMD   Command          0x06=READ DATA
  2817:     //    [1]  bit2    HD    Head Address     サイド
  2818:     //         bit1-0  US    Unit Select      ユニット
  2819:     //    [2]          C     Cylinder Number  シリンダ
  2820:     //    [3]          H     Head Number      サイド
  2821:     //    [4]          R     Record Number    開始セクタ
  2822:     //    [5]          N     Record Length    セクタ長=128<<N
  2823:     //    [6]          EOT   End of Track     終了セクタ
  2824:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  2825:     //    [8]          DTL   Data Length      N==0&&DTL<128のとき128バイト読み出してCRCをチェックするがMPUにはDTLバイトだけ転送する
  2826:     //  E-Phase(FDC→MPU)
  2827:     //    データ
  2828:     //  R-Phase
  2829:     //    [0]          ST0   Status 0         リザルトステータス0
  2830:     //    [1]          ST1   Status 1         リザルトステータス1
  2831:     //    [2]          ST2   Status 2         リザルトステータス2
  2832:     //    [3]          C     Cylinder Number  シリンダ
  2833:     //    [4]          H     Head Number      サイド
  2834:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  2835:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  2836:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  2837:     //                                        エラーのときは異常発生セクタ
  2838:     //    [6]          N     Record Length    セクタ長=128<<N
  2839:     //  リザルトステータス
  2840:     //    正常終了  NT
  2841:     //    ノットレディ  AT|NR
  2842:     //    ID部
  2843:     //      IDAM非検出  AT|MA
  2844:     //      C不一致(!=0xff)  AT|ND|NC
  2845:     //      C不一致(==0xff)  AT|ND|BC
  2846:     //      H不一致  AT|ND
  2847:     //      R不一致  AT|ND
  2848:     //      N不一致  AT|ND
  2849:     //      CRC不一致  AT|DE
  2850:     //    データ部
  2851:     //      DAM非検出  AT|MA|MD
  2852:     //      DDAM検出  NT|CM
  2853:     //      CRC不一致  AT|DE|DD
  2854:     //      オーバーラン  AT|OR
  2855:     //    最終セクタで未終了  AT|EN
  2856:     public void fduCommandReadData () {
  2857:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  2858:       int ncn = fdcCommandBuffer[2] & 255;  //NCN
  2859:       int phn = fdcCommandBuffer[3] & 255;  //H
  2860:       int prn = fdcCommandBuffer[4] & 255;  //R
  2861:       int pnn = fdcCommandBuffer[5] & 255;  //N
  2862:       int eot = fdcCommandBuffer[6] & 255;  //EOT
  2863:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2864:         System.out.printf ("%08x fduCommandReadData%d(NCN=%d H=%d R=%d N=%d EOT=%d)\n",
  2865:                            XEiJ.regPC0, abuNumber, ncn, phn, prn, pnn, eot);
  2866:       }
  2867:       fduCommandNumber = fdcCommandNumber;
  2868:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2869:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  2870:       }
  2871:       if (!fduIsReady ()) {  //ノットレディ
  2872:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2873:         return;
  2874:       }
  2875:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  2876:       fduPHN = phn;
  2877:       fduPRN = prn;
  2878:       fduPNN = pnn;
  2879:       fduEOT = eot;
  2880:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  2881:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2882:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  2883:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  2884:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2885:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  2886:         System.out.printf ("\tfduEOT%d=%d\n", abuNumber, fduEOT);
  2887:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  2888:       }
  2889:       fdcStatus = (FDC_MPU_TO_FDC | FDC_CB |  //E-Phase(転送中)に移行する(RQM=0で待機する)
  2890:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2891:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2892:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2893:       }
  2894:       fdcReadHandle = null;
  2895:       fdcWriteHandle = null;
  2896:       TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  2897:     }  //fduCommandReadData()
  2898: 
  2899:     public void fduReadDataEPhase () {
  2900:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2901:         System.out.printf ("%08x fduReadDataEPhase%d()\n", XEiJ.regPC0, abuNumber);
  2902:       }
  2903:       if (fduPCN != fduNCN) {  //シークが中断されたとき
  2904:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2905:         return;
  2906:       }
  2907:       //E-Phase
  2908:       fdcStatus = (FDC_RQM | FDC_FDC_TO_MPU | FDC_CB |
  2909:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2910:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2911:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2912:       }
  2913:       //転送
  2914:       int o = fduCalcOffset ();
  2915:       if (o < 0) {  //セクタが存在しない
  2916:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2917:         return;
  2918:       }
  2919:       fdcReadHandle = fduImage;
  2920:       fdcWriteHandle = null;
  2921:       fdcIndex = fdcStart = o;
  2922:       fdcLimit = o + (128 << fduPNN);
  2923:       HD63450.dmaFallPCL (0);  //DMA転送開始
  2924:     }  //fduReadDataEPhase()
  2925: 
  2926:     //fduReadDataEPhaseEnd ()
  2927:     public void fduReadDataEPhaseEnd () {
  2928:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2929:         System.out.printf ("%08x fduReadDataEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  2930:       }
  2931:       if (fduPRN == fduEOT) {  //終了
  2932:         HD63450.dmaRisePCL (0);  //DMA転送終了
  2933:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2934:         return;
  2935:       }
  2936:       //継続
  2937:       if (fduMedia == FDMedia.FDM_2HS && fduPCN == 0 && fduPHN == 0 && fduPRN == 1) {
  2938:         fduPRN = 11;
  2939:       } else {
  2940:         fduPRN++;
  2941:       }
  2942:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2943:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2944:       }
  2945:       int o = fduCalcOffset ();
  2946:       if (o < 0) {  //セクタが存在しない
  2947:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2948:         return;
  2949:       }
  2950:       fdcIndex = fdcStart = o;
  2951:       fdcLimit = o + (128 << fduPNN);
  2952:     }  //fduReadDataEPhaseEnd()
  2953: 
  2954:     //fduCommandRecalibrate ()
  2955:     //  0x07  RECALIBRATE
  2956:     //    トラック0(一番外側)へのシーク
  2957:     //  C-Phase
  2958:     //    [0]          CMD   Command          0x07=RECALIBRATE
  2959:     //    [1]  bit1-0  US    Unit Select      ユニット
  2960:     //  リザルトステータス(SENSE INTERRUPT STATUSで引き取る)
  2961:     //    正常終了  NT|SE
  2962:     //    ノットレディ  AT|SE|NR
  2963:     //    トラック0非検出  AT|SE|EC
  2964:     public void fduCommandRecalibrate () {
  2965:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2966:         System.out.printf ("%08x fduCommandRecalibrate%d()\n", XEiJ.regPC0, abuNumber);
  2967:       }
  2968:       fduCommandNumber = fdcCommandNumber;
  2969:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2970:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  2971:       }
  2972:       fdcStatus |= 1 << abuNumber;  //FDnビジー(DnB)を1にする
  2973:       fduNCN = 0;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  2974:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  2975:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2976:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2977:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  2978:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  2979:       }
  2980:       if (fdcEnforcedReady && !abuConnected) {  //強制レディ状態で接続されていないとき
  2981:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_EC;  //AT(Abnormal Terminate)+SE(Seek End)+EC(Equipment Check)のリザルトステータスを準備する
  2982:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2983:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  2984:         }
  2985:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  2986:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2987:       } else if (!fduIsReady ()) {  //ノットレディのとき
  2988:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_NR;  //AT(Abnormal Terminate)+SE(Seek End)+NR(Not Ready)のリザルトステータスを準備する
  2989:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2990:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  2991:         }
  2992:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  2993:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2994:       } else {  //レディのとき
  2995:         fduSeekResultStatus = FDC_ST0_NT | FDC_ST0_SE;  //NT(Normal Terminate)+SE(Seek End)のリザルトステータスを準備する
  2996:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2997:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  2998:         }
  2999:         TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  3000:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  3001:       }
  3002:     }  //fduCommandRecalibrate()
  3003: 
  3004:     //fduCommandWriteDeletedData ()
  3005:     //  0x09  WRITE DELETED DATA
  3006:     //    削除データ(DDAM)の書き込み
  3007:     //  C-Phase
  3008:     //    [0]  bit7    MT    Multitrack       マルチトラック
  3009:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  3010:     //         bit4-0  CMD   Command          0x09=WRITE DELETED DATA
  3011:     //    [1]  bit2    HD    Head Address     サイド
  3012:     //         bit1-0  US    Unit Select      ユニット
  3013:     //    [2]          C     Cylinder Number  シリンダ
  3014:     //    [3]          H     Head Number      サイド
  3015:     //    [4]          R     Record Number    開始セクタ
  3016:     //    [5]          N     Record Length    セクタ長=128<<N
  3017:     //    [6]          EOT   End of Track     終了セクタ
  3018:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  3019:     //    [8]          DTL   Data Length      N==0&&DTL<128のときMPUからDTLバイトだけ受け取って128バイト書き込む
  3020:     //  E-Phase(MPU→FDC)
  3021:     //    データ
  3022:     //  R-Phase
  3023:     //    [0]          ST0   Status 0         リザルトステータス0
  3024:     //    [1]          ST1   Status 1         リザルトステータス1
  3025:     //    [2]          ST2   Status 2         リザルトステータス2
  3026:     //    [3]          C     Cylinder Number  シリンダ
  3027:     //    [4]          H     Head Number      サイド
  3028:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  3029:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  3030:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  3031:     //                                        エラーのときは異常発生セクタ
  3032:     //    [6]          N     Record Length    セクタ長=128<<N
  3033:     //  リザルトステータス
  3034:     //    正常終了  NT
  3035:     //    ノットレディ  AT|NR
  3036:     //    ライトプロテクト  AT|NW
  3037:     //    ID部
  3038:     //      IDAM非検出  AT|MA
  3039:     //      C不一致(!=0xff)  AT|ND|NC
  3040:     //      C不一致(==0xff)  AT|ND|BC
  3041:     //      H不一致  AT|ND
  3042:     //      R不一致  AT|ND
  3043:     //      N不一致  AT|ND
  3044:     //      CRC不一致  AT|DE
  3045:     //    データ部
  3046:     //      フォールト  AT|EC
  3047:     //      オーバーラン  AT|OR
  3048:     //    最終セクタで未終了  AT|EN
  3049:     public void fduCommandWriteDeletedData () {
  3050:       //!!!
  3051:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3052:         System.out.printf ("%08x WRITE DELETED DATA is not implemented\n", XEiJ.regPC0);
  3053:       }
  3054:       fdcCommandInvalid ();
  3055:     }
  3056: 
  3057:     //fduCommandReadId ()
  3058:     //  0x0a  READ ID
  3059:     //    現在のシリンダの指定されたサイドにあるトラックでヘッドロード後最初に見つけたDEエラーやMAエラーのないセクタのIDを返す
  3060:     //    トラック上のどのセクタのIDが返るかは不定
  3061:     //  C-Phase
  3062:     //    [0]  bit6    MF    MFM Mode         0=FM,1=MFM
  3063:     //         bit4-0  CMD   Command          0x0a=READ ID
  3064:     //    [1]  bit2    HD    Head Address     サイド
  3065:     //         bit1-0  US    Unit Select      ユニット
  3066:     //  R-Phase
  3067:     //    [0]          ST0   Status 0         リザルトステータス0
  3068:     //    [1]          ST1   Status 1         リザルトステータス1
  3069:     //    [2]          ST2   Status 2         リザルトステータス2
  3070:     //    [3]          C     Cylinder Number  シリンダ
  3071:     //    [4]          H     Head Number      サイド
  3072:     //    [5]          R     Record Number    セクタ
  3073:     //    [6]          N     Record Length    セクタ長=128<<N
  3074:     //  リザルトステータス
  3075:     //    正常終了  NT
  3076:     //    ノットレディ  AT|NR
  3077:     //    ID部
  3078:     //      IDAM非検出  AT|MA  インデックスパルスを2回検出するまでにIDAMが見つからない
  3079:     //      CRC不一致  AT|ND  IDAMを見つけたがインデックスパルスを2回検出するまでにCRCエラーのないIDが見つからない
  3080:     public void fduCommandReadId () {
  3081:       int hd = fdcCommandBuffer[1] >> 2 & 1;  //HD
  3082:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3083:         System.out.printf ("%08x fduCommandReadId%d(HD=%d)\n", XEiJ.regPC0, abuNumber, hd);
  3084:       }
  3085:       fduCommandNumber = fdcCommandNumber;
  3086:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3087:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3088:       }
  3089:       fduPHN = hd;
  3090:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3091:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  3092:       }
  3093:       if (!fduIsReady ()) {  //ノットレディ
  3094:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3095:         return;
  3096:       }
  3097:       //セクタ番号
  3098:       //  常に1を返す方法だとREAD IDを繰り返してセクタ数を確認することができない
  3099:       if (fduMedia == FDMedia.FDM_2HS) {  //2HS
  3100:         if (fduPCN == 0 && fduPHN == 0) {  //最初のトラック
  3101:           if (fduPRN == 1) {  //最初のセクタ
  3102:             fduPRN = 11;  //2番目のセクタ
  3103:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3104:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3105:             }
  3106:           } else if (fduPRN == 18) {  //最後のセクタ
  3107:             fduPRN = 1;  //最初のセクタ
  3108:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3109:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3110:             }
  3111:           } else {  //その他のセクタ
  3112:             fduPRN++;  //次のセクタ
  3113:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3114:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3115:             }
  3116:           }
  3117:         } else {  //最初のトラック以外
  3118:           if (fduPRN == 18) {  //最後のセクタ
  3119:             fduPRN = 10;  //最初のセクタ
  3120:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3121:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3122:             }
  3123:           } else {  //その他のセクタ
  3124:             fduPRN++;  //次のセクタ
  3125:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3126:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3127:             }
  3128:           }
  3129:         }
  3130:       } else {  //2HS以外
  3131:         if (fduPRN == fduMedia.fdmSectorsPerTrack) {  //最後のセクタ
  3132:           fduPRN = 1;  //最初のセクタ
  3133:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3134:             System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3135:           }
  3136:         } else {  //その他のセクタ
  3137:           fduPRN++;  //次のセクタ
  3138:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3139:             System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3140:           }
  3141:         }
  3142:       }
  3143:       //サイド番号
  3144:       if (fduMedia == FDMedia.FDM_2HDE) {
  3145:         if (!(fduPCN == 0 && fduPHN == 0 && fduPRN == 1)) {  //最初のトラックの最初のセクタ以外
  3146:           fduPHN |= 128;
  3147:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3148:             System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  3149:           }
  3150:         }
  3151:       }
  3152:       //セクタスケール
  3153:       fduPNN = fduMedia.fdmSectorScale;
  3154:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3155:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  3156:       }
  3157:       int o = fduCalcOffset ();
  3158:       if (o < 0) {  //セクタが存在しない
  3159:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3160:         return;
  3161:       }
  3162:       fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3163:     }  //fduCommandReadId()
  3164: 
  3165:     //fduCommandReadDeletedData ()
  3166:     //  0x0c  READ DELETED DATA
  3167:     //    削除データ(DDAM)の読み出し
  3168:     //  C-Phase
  3169:     //    [0]  bit7    MT    Multitrack       マルチトラック
  3170:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  3171:     //         bit5    SK    Skip             DAMをスキップ
  3172:     //         bit4-0  CMD   Command          0x0c=READ DELETED DATA
  3173:     //    [1]  bit2    HD    Head Address     サイド
  3174:     //         bit1-0  US    Unit Select      ユニット
  3175:     //    [2]          C     Cylinder Number  シリンダ
  3176:     //    [3]          H     Head Number      サイド
  3177:     //    [4]          R     Record Number    開始セクタ
  3178:     //    [5]          N     Record Length    セクタ長=128<<N
  3179:     //    [6]          EOT   End of Track     終了セクタ
  3180:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  3181:     //    [8]          DTL   Data Length      N==0&&DTL<128のとき128バイト読み出してCRCをチェックしてMPUにDTLバイトだけ渡す
  3182:     //  E-Phase(FDC→MPU)
  3183:     //    データ
  3184:     //  R-Phase
  3185:     //    [0]          ST0   Status 0         リザルトステータス0
  3186:     //    [1]          ST1   Status 1         リザルトステータス1
  3187:     //    [2]          ST2   Status 2         リザルトステータス2
  3188:     //    [3]          C     Cylinder Number  シリンダ
  3189:     //    [4]          H     Head Number      サイド
  3190:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  3191:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  3192:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  3193:     //                                        エラーのときは異常発生セクタ
  3194:     //    [6]          N     Record Length    セクタ長=128<<N
  3195:     //  リザルトステータス
  3196:     //    正常終了  NT
  3197:     //    ノットレディ  AT|NR
  3198:     //    ID部
  3199:     //      IDAM非検出  AT|MA
  3200:     //      C不一致(!=0xff)  AT|ND|NC
  3201:     //      C不一致(==0xff)  AT|ND|BC
  3202:     //      H不一致  AT|ND
  3203:     //      R不一致  AT|ND
  3204:     //      N不一致  AT|ND
  3205:     //      CRC不一致  AT|DE
  3206:     //    データ部
  3207:     //      DDAM非検出  AT|MA|MD
  3208:     //      DAM検出  NT|CM
  3209:     //      CRC不一致  AT|DE|DD
  3210:     //      オーバーラン  AT|OR
  3211:     //    最終セクタで未終了  AT|EN
  3212:     public void fduCommandReadDeletedData () {
  3213:       //!!!
  3214:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3215:         System.out.printf ("%08x READ DELETED DATA is not implemented\n", XEiJ.regPC0);
  3216:       }
  3217:       fdcCommandInvalid ();
  3218:     }
  3219: 
  3220:     //fduCommandWriteId ()
  3221:     //  0x0d  WRITE ID
  3222:     //    1トラックフォーマットする。別名Format Write
  3223:     //  C-Phase
  3224:     //    [0]  bit6    MF    MFM Mode         0=FM,1=MFM
  3225:     //         bit4-0  CMD   Command          0x0d=WRITE ID
  3226:     //    [1]  bit2    HD    Head Address     サイド
  3227:     //         bit1-0  US    Unit Select      ユニット
  3228:     //    [2]          N     Record Length    セクタ長=128<<N
  3229:     //    [3]          SC    Sector           1トラックあたりのセクタ数
  3230:     //    [4]          GPL   Gap Length       Gap3に書き込む長さ
  3231:     //    [5]          D     Data             データ部に書き込むデータ
  3232:     //  E-Phase
  3233:     //    1トラック分のID情報。4*SCバイト
  3234:     //    [0]          C     Cylinder Number  シリンダ
  3235:     //    [1]          H     Head Number      サイド
  3236:     //    [2]          R     Record Number    開始セクタ
  3237:     //    [3]          N     Record Length    セクタ長=128<<N
  3238:     //    これをSC回繰り返す
  3239:     //  R-Phase
  3240:     //    [0]          ST0   Status 0         リザルトステータス0
  3241:     //    [1]          ST1   Status 1         リザルトステータス1
  3242:     //    [2]          ST2   Status 2         リザルトステータス2
  3243:     //    [3]          C     Cylinder Number  無意味
  3244:     //    [4]          H     Head Number      無意味
  3245:     //    [5]          R     Record Number    無意味
  3246:     //    [6]          N     Record Length    セクタ長=128<<N
  3247:     //  リザルトステータス
  3248:     //    正常終了  NT
  3249:     //    ノットレディ  AT|NR
  3250:     //    ライトプロテクト  AT|NW
  3251:     //    フォールト  AT|EC
  3252:     //    オーバーラン  AT|OR
  3253:     public void fduCommandWriteId () {
  3254:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  3255:       fduCommandNumber = fdcCommandNumber;
  3256:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3257:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3258:       }
  3259:       if (!fduIsReady ()) {  //ノットレディ
  3260:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3261:           System.out.printf ("%08x unit=%d,is not ready\n", XEiJ.regPC0, abuNumber);
  3262:         }
  3263:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3264:         return;
  3265:       }
  3266:       if (fduIsWriteProtected ()) {  //書き込めない
  3267:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3268:           System.out.printf ("%08x unit=%d,is read-only\n", XEiJ.regPC0, abuNumber);
  3269:         }
  3270:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_NW | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3271:         return;
  3272:       }
  3273:       //E-Phase
  3274:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3275:         System.out.printf ("%08x FDC E-Phase\n", XEiJ.regPC0);
  3276:       }
  3277:       fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC | FDC_CB |
  3278:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  3279:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3280:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3281:       }
  3282:       fdcReadHandle = null;
  3283:       fdcWriteHandle = fdcTempBuffer;
  3284:       fdcIndex = fdcStart = 0;
  3285:       fdcLimit = (fdcCommandBuffer[3] & 255) << 2;
  3286:       HD63450.dmaFallPCL (0);  //DMA転送開始
  3287:     }  //fduCommandWriteId()
  3288: 
  3289:     //fduWriteIdEPhaseEnd ()
  3290:     public void fduWriteIdEPhaseEnd () {
  3291:       HD63450.dmaRisePCL (0);  //DMA転送終了
  3292:       //トラック0のフォーマットでメディアを判別する
  3293:       if (fdcTempBuffer[0] == 0 && fdcTempBuffer[1] == 0) {  //pcn1==0&&phn1==0。トラック0
  3294:         int prn1 = fdcTempBuffer[2] & 255;
  3295:         int pnn1 = fdcTempBuffer[3] & 255;
  3296:         int pcn2 = fdcTempBuffer[4] & 255;
  3297:         int phn2 = fdcTempBuffer[5] & 255;
  3298:         int prn2 = fdcTempBuffer[6] & 255;
  3299:         int pnn2 = fdcTempBuffer[7] & 255;
  3300:         int sectors = fdcLimit >> 2;  //1トラックあたりのセクタ数。fdcCommandBuffer[3]&255
  3301:         FDMedia media = null;
  3302:         if (phn2 == 0 && prn1 == 1 && prn2 == 2) {
  3303:           if (!fduDoubleDensity) {  //高密度
  3304:             if (pnn1 == 3 && pnn2 == 3) {  //1024バイト/セクタ
  3305:               if (sectors == 8) {  //8セクタ/トラック
  3306:                 media = FDMedia.FDM_2HD;
  3307:               }
  3308:             } else if (pnn1 == 2 && pnn2 == 2) {  //512バイト/セクタ
  3309:               if (sectors == 15) {  //15セクタ/トラック
  3310:                 media = FDMedia.FDM_2HC;
  3311:               } else if (sectors == 18) {  //18セクタ/トラック
  3312:                 media = FDMedia.FDM_2HQ;
  3313:               }
  3314:             }
  3315:           } else {  //倍密度
  3316:             if (pnn1 == 2 && pnn2 == 2) {  //512バイト/セクタ
  3317:               if (sectors == 8) {  //8セクタ/トラック
  3318:                 media = FDMedia.FDM_2DD8;
  3319:               } else if (sectors == 9) {  //9セクタ/トラック
  3320:                 media = FDMedia.FDM_2DD9;
  3321:               } else if (sectors == 10) {  //10セクタ/トラック
  3322:                 media = FDMedia.FDM_2DD10;
  3323:               }
  3324:             }
  3325:           }
  3326:         } else if (phn2 == 128 && prn1 == 1 && prn2 == 2) {  //2番目のセクタのサイド番号が128
  3327:           if (!fduDoubleDensity) {  //高密度
  3328:             if (pnn1 == 3 && pnn2 == 3) {  //1024バイト/セクタ
  3329:               if (sectors == 9) {  //9セクタ/トラック
  3330:                 media = FDMedia.FDM_2HDE;
  3331:               }
  3332:             }
  3333:           }
  3334:         } else if (phn2 == 0 && prn1 == 1 && prn2 == 11) {  //2番目のセクタのレコード番号が11
  3335:           if (!fduDoubleDensity) {  //高密度
  3336:             if (pnn1 == 3 && pnn2 == 3) {  //1024バイト/セクタ
  3337:               if (sectors == 9) {  //9セクタ/トラック
  3338:                 media = FDMedia.FDM_2HS;
  3339:               }
  3340:             }
  3341:           }
  3342:         }
  3343:         if (media != null) {
  3344:           fduMedia = media;
  3345:         } else {
  3346:           fduMedia = FDMedia.FDM_2HD;
  3347:         }
  3348:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3349:           System.out.println ("media = " + fduMedia.fdmName);
  3350:           System.out.println ("------------------------------------------------------------------------");
  3351:           fduMedia.fdmPrintInfo ();
  3352:           System.out.println ("------------------------------------------------------------------------");
  3353:         }
  3354:       }  //if トラック0
  3355:       for (int i = 0; i < fdcLimit; i += 4) {
  3356:         fduPCN = fdcTempBuffer[i    ] & 255;
  3357:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3358:           System.out.printf ("%08x unit=%d,fduPCN=%d\n", XEiJ.regPC0, abuNumber, fduPCN);
  3359:         }
  3360:         fduPHN = fdcTempBuffer[i + 1] & 255;
  3361:         fduPRN = fdcTempBuffer[i + 2] & 255;
  3362:         fduPNN = fdcTempBuffer[i + 3] & 255;
  3363:         int o = fduCalcOffset ();
  3364:         if (o < 0) {  //セクタが存在しない
  3365:           //FORMAT.Xが1トラック余分にフォーマットしようとするので、シリンダが上限+1のときはエラーにせず無視する
  3366:           if (0 < fduPCN) {
  3367:             fduPCN--;
  3368:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3369:               System.out.printf ("%08x unit=%d,fduPCN=%d\n", XEiJ.regPC0, abuNumber, fduPCN);
  3370:             }
  3371:             o = fduCalcOffset ();
  3372:             fduPCN++;
  3373:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3374:               System.out.printf ("%08x unit=%d,fduPCN=%d\n", XEiJ.regPC0, abuNumber, fduPCN);
  3375:             }
  3376:           }
  3377:           if (o < 0) {  //シリンダを1つ減らしたセクタも存在しない
  3378:             fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_EC | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3379:             return;
  3380:           }
  3381:         } else {  //セクタが存在する
  3382:           Arrays.fill (fduImage, o, o + (128 << fduPNN), fdcCommandBuffer[5]);  //初期化データで充填する
  3383:           fduWritten = true;
  3384:         }
  3385:       }  //for i
  3386:       fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3387:     }  //fduWriteIdEPhaseEnd()
  3388: 
  3389:     //fduCommandSeek ()
  3390:     //  0x0f  SEEK
  3391:     //    NCNがPCNと異なるときSPECIFYコマンドで指定されたStep Rate Time間隔でPCNをインクリメントまたはデクリメントすることを繰り返す
  3392:     //    NCNが範囲外でもエラーを出さない
  3393:     //  C-Phase
  3394:     //    [0]          CMD   Command          0x0f=SEEK
  3395:     //    [1]  bit1-0  US    Unit Select      ユニット
  3396:     //    [2]          NCN   New Cylinder Number
  3397:     //  E-Phase
  3398:     //    終了待ち
  3399:     //  リザルトステータス(SENSE INTERRUPT STATUSで引き取る)
  3400:     //    正常終了  NT|SE
  3401:     //    ノットレディ  AT|SE|NR
  3402:     public void fduCommandSeek () {
  3403:       int ncn = fdcCommandBuffer[2] & 255;  //New Cylinder Number
  3404:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3405:         System.out.printf ("%08x fduCommandSeek%d(NCN=%d)\n", XEiJ.regPC0, abuNumber, ncn);
  3406:       }
  3407:       fduCommandNumber = fdcCommandNumber;
  3408:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3409:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3410:       }
  3411:       fdcStatus |= 1 << abuNumber;  //FDnビジー(DnB)を1にする
  3412:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  3413:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  3414:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3415:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3416:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  3417:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  3418:       }
  3419:       if (fdcEnforcedReady && !abuConnected) {  //強制レディ状態で接続されていないとき
  3420:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_EC;  //AT(Abnormal Terminate)+SE(Seek End)+EC(Equipment Check)のリザルトステータスを準備する
  3421:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3422:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  3423:         }
  3424:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  3425:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  3426:       } else if (!fduIsReady ()) {  //ノットレディのとき
  3427:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_NR;  //AT(Abnormal Terminate)+SE(Seek End)+NR(Not Ready)のリザルトステータスを準備する
  3428:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3429:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  3430:         }
  3431:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  3432:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  3433:       } else {  //レディのとき
  3434:         fduSeekResultStatus = FDC_ST0_NT | FDC_ST0_SE;  //NT(Normal Terminate)+SE(Seek End)のリザルトステータスを準備する
  3435:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3436:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  3437:         }
  3438:         TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  3439:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  3440:       }
  3441:     }  //fduCommandSeek()
  3442: 
  3443:     //fduCommandScan ()
  3444:     //  0x11  SCAN EQUAL
  3445:     //  0x19  SCAN LOW OR EQUAL
  3446:     //  0x1d  SCAN HIGH OR EQUAL
  3447:     //    記録データと目的データを1バイトずつ8bit符号なし数値とみなして比較する(ベリファイ)
  3448:     //  C-Phase
  3449:     //    [0]  bit7    MT    Multitrack       マルチトラック
  3450:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  3451:     //         bit5    SK    Skip             DDAMをスキップ
  3452:     //         bit4-0  CMD   Command          0x11=SCAN EQUAL,0x19=SCAN LOW OR EQUAL,0x1d=SCAN HIGH OR EQUAL
  3453:     //    [1]  bit2    HD    Head Address     サイド
  3454:     //         bit1-0  US    Unit Select      ユニット
  3455:     //    [2]          C     Cylinder Number  シリンダ
  3456:     //    [3]          H     Head Number      サイド
  3457:     //    [4]          R     Record Number    開始セクタ
  3458:     //    [5]          N     Record Length    セクタ長=128<<N
  3459:     //    [6]          EOT   End of Track     終了セクタ。STP==2のときはRとEOTの奇偶が同じであること
  3460:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  3461:     //    [8]          STP   Step             条件不成立のとき1=次のセクタに進む(R++),2=次の次のセクタに進む(R+=2)
  3462:     //  E-Phase(MPU→FDC)
  3463:     //    (EOT-R)/STP+1セクタ分の目的データ
  3464:     //    条件
  3465:     //      EQUAL          目的データ==0xff||記録データ==目的データ
  3466:     //      LOW OR EQUAL   目的データ==0xff||記録データ<=目的データ
  3467:     //      HIGH OR EQUAL  目的データ==0xff||記録データ>=目的データ
  3468:     //    1セクタ分のデータがすべて条件成立したとき
  3469:     //      条件成立で終了
  3470:     //    1セクタの中に条件成立しないデータがあったとき
  3471:     //      終了セクタではないとき
  3472:     //        STPに従って次または次の次のセクタに進む
  3473:     //      終了セクタまでに1セクタ分のデータがすべて条件成立するセクタが見つからなかったとき
  3474:     //        条件不成立で終了
  3475:     //  R-Phase
  3476:     //    [0]          ST0   Status 0         リザルトステータス0
  3477:     //    [1]          ST1   Status 1         リザルトステータス1
  3478:     //    [2]          ST2   Status 2         リザルトステータス2
  3479:     //    [3]          C     Cylinder Number  シリンダ
  3480:     //    [4]          H     Head Number      サイド
  3481:     //    [5]          R     Record Number    最後に比較したセクタ
  3482:     //    [6]          N     Record Length    セクタ長=128<<N
  3483:     //  リザルトステータス
  3484:     //    条件成立  EQUAL          NT|SH
  3485:     //              LOW OR EQUAL   NT
  3486:     //              HIGH OR EQUAL  NT
  3487:     //    条件不成立  NT|SN
  3488:     //    ノットレディ  AT|NR
  3489:     //    ID部
  3490:     //      IDAM非検出  AT|MA
  3491:     //      C不一致(!=0xff)  AT|ND|NC
  3492:     //      C不一致(==0xff)  AT|ND|BC
  3493:     //      H不一致  AT|ND
  3494:     //      R不一致  AT|ND
  3495:     //      N不一致  AT|ND
  3496:     //      CRC不一致  AT|DE
  3497:     //    データ部
  3498:     //      DAM非検出  AT|MA|MD
  3499:     //      DDAM検出  NT|CM
  3500:     //      CRC不一致  AT|DE|DD
  3501:     //      オーバーラン  AT|OR
  3502:     public void fduCommandScan () {
  3503:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  3504:       int ncn = fdcCommandBuffer[2] & 255;  //NCN
  3505:       int phn = fdcCommandBuffer[3] & 255;  //H
  3506:       int prn = fdcCommandBuffer[4] & 255;  //R
  3507:       int pnn = fdcCommandBuffer[5] & 255;  //N
  3508:       int eot = fdcCommandBuffer[6] & 255;  //EOT
  3509:       int stp = fdcCommandBuffer[8] & 3;  //STP
  3510:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3511:         System.out.printf ("%08x fduCommandScan%d(NCN=%d H=%d R=%d N=%d EOT=%d STP=%d)\n",
  3512:                            XEiJ.regPC0, abuNumber, ncn, phn, prn, pnn, eot, stp);
  3513:       }
  3514:       fduCommandNumber = fdcCommandNumber;
  3515:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3516:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3517:       }
  3518:       if (!fduIsReady ()) {  //ノットレディ
  3519:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3520:         return;
  3521:       }
  3522:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  3523:       fduPHN = phn;
  3524:       fduPRN = prn;
  3525:       fduPNN = pnn;
  3526:       fduEOT = eot;
  3527:       fduSTP = stp;
  3528:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  3529:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3530:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  3531:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  3532:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3533:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  3534:         System.out.printf ("\tfduEOT%d=%d\n", abuNumber, fduEOT);
  3535:         System.out.printf ("\tfduSTP%d=%d\n", abuNumber, fduSTP);
  3536:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  3537:       }
  3538:       fdcStatus = (FDC_MPU_TO_FDC | FDC_CB |  //E-Phase(転送中)に移行する(RQM=0で待機する)
  3539:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  3540:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3541:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3542:       }
  3543:       fdcReadHandle = null;
  3544:       fdcWriteHandle = null;
  3545:       TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  3546:     }  //fduCommandScan()
  3547: 
  3548:     public void fduScanEPhase () {
  3549:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3550:         System.out.printf ("%08x fduScanEPhase%d()\n", XEiJ.regPC0, abuNumber);
  3551:       }
  3552:       //E-Phase
  3553:       fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC | FDC_CB |
  3554:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  3555:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3556:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3557:       }
  3558:       //転送
  3559:       fdcReadHandle = null;
  3560:       fdcWriteHandle = fdcTempBuffer;
  3561:       fdcIndex = fdcStart = 0;
  3562:       fdcLimit = 128 << fduPNN;
  3563:       HD63450.dmaFallPCL (0);  //DMA転送開始
  3564:     }  //fduScanEPhase()
  3565: 
  3566:     //fduScanEqualEPhaseEnd ()
  3567:     public void fduScanEqualEPhaseEnd () {
  3568:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3569:         System.out.printf ("%08x fduScanEqualEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  3570:       }
  3571:       int o = fduCalcOffset ();
  3572:       if (o < 0) {  //セクタが存在しない
  3573:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3574:         return;
  3575:       }
  3576:       //比較
  3577:     scan:
  3578:       {
  3579:         int l = 128 << fduPNN;
  3580:         for (int i = 0; i < l; i++) {
  3581:           int d = fdcTempBuffer[i] & 255;
  3582:           if (d != 0xff && (fduImage[o + i] & 255) != d) {
  3583:             break scan;
  3584:           }
  3585:         }
  3586:         //条件成立
  3587:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3588:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SH | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3589:         return;
  3590:       }
  3591:       if (fduPRN == fduEOT) {  //条件不成立
  3592:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3593:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SN | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3594:         return;
  3595:       }
  3596:       //継続
  3597:       fduPRN += fduSTP;
  3598:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3599:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3600:       }
  3601:       fdcIndex = fdcStart = 0;
  3602:       //fdcLimit = 128 << fduPNN;
  3603:     }  //fduScanEqualEPhaseEnd()
  3604: 
  3605:     //fduScanLowOrEqualEPhaseEnd ()
  3606:     public void fduScanLowOrEqualEPhaseEnd () {
  3607:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3608:         System.out.printf ("%08x fduScanLowOrEqualEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  3609:       }
  3610:       int o = fduCalcOffset ();
  3611:       if (o < 0) {  //セクタが存在しない
  3612:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3613:         return;
  3614:       }
  3615:     scan:
  3616:       {
  3617:         int l = 128 << fduPNN;
  3618:         for (int i = 0; i < l; i++) {
  3619:           int d = fdcTempBuffer[i] & 255;
  3620:           if (d != 0xff && (fduImage[o + i] & 255) > d) {
  3621:             break scan;
  3622:           }
  3623:         }
  3624:         //条件成立
  3625:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3626:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3627:         return;
  3628:       }
  3629:       if (fduPRN == fduEOT) {  //条件不成立
  3630:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3631:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SN | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3632:         return;
  3633:       }
  3634:       //継続
  3635:       fduPRN += fduSTP;
  3636:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3637:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3638:       }
  3639:       fdcIndex = fdcStart = 0;
  3640:       fdcLimit = 128 << fduPNN;
  3641:     }  //fduScanLowOrEqualEPhaseEnd()
  3642: 
  3643:     //fduScanHighOrEqualEPhaseEnd ()
  3644:     public void fduScanHighOrEqualEPhaseEnd () {
  3645:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3646:         System.out.printf ("%08x fduScanHighOrEqualEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  3647:       }
  3648:       int o = fduCalcOffset ();
  3649:       if (o < 0) {  //セクタが存在しない
  3650:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3651:         return;
  3652:       }
  3653:     scan:
  3654:       {
  3655:         int l = 128 << fduPNN;
  3656:         for (int i = 0; i < l; i++) {
  3657:           int d = fdcTempBuffer[i] & 255;
  3658:           if (d != 0xff && (fduImage[o + i] & 255) < d) {
  3659:             break scan;
  3660:           }
  3661:         }
  3662:         //条件成立
  3663:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3664:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3665:         return;
  3666:       }
  3667:       if (fduPRN == fduEOT) {  //条件不成立
  3668:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3669:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SN | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3670:         return;
  3671:       }
  3672:       //継続
  3673:       fduPRN += fduSTP;
  3674:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3675:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3676:       }
  3677:       fdcIndex = fdcStart = 0;
  3678:       fdcLimit = 128 << fduPNN;
  3679:     }  //fduScanHighOrEqualEPhaseEnd()
  3680: 
  3681:     //offset = fduCalcOffset ()
  3682:     //  指定されたセクタのオフセットを計算する。-1=範囲外
  3683:     public int fduCalcOffset () {
  3684:       int o = (fduMedia == null ? -1 :
  3685:                fduDoubleDensity == fduMedia.fdmDoubleDensity &&
  3686:                fduPNN == fduMedia.fdmSectorScale &&
  3687:                fduPCN < fduMedia.fdmCylindersPerDisk &&
  3688:                (fduMedia == FDMedia.FDM_2HDE ?
  3689:                 fduPHN == (fduPCN == 0 && fduPRN == 1 ? 0 : 128) || fduPHN == 129 :
  3690:                 fduPHN < fduMedia.fdmTracksPerCylinder) &&
  3691:                (fduMedia == FDMedia.FDM_2HS ?
  3692:                 fduPCN == 0 && fduPHN == 0 ? fduPRN == 1 || (11 <= fduPRN && fduPRN <= 18) : 10 <= fduPRN && fduPRN <= 18 :
  3693:                 1 <= fduPRN && fduPRN <= fduMedia.fdmSectorsPerTrack) ?
  3694:                fduMedia.fdmBytesPerSector * (
  3695:                  (fduPRN <= fduMedia.fdmSectorsPerTrack ? fduPRN : fduPRN - fduMedia.fdmSectorsPerTrack) - 1 +
  3696:                  fduMedia.fdmSectorsPerTrack * (
  3697:                    ((fduPHN & 1) + fduMedia.fdmTracksPerCylinder * fduPCN))) :
  3698:                -1);
  3699:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3700:         System.out.printf ("\tfduCalcOffset(C=%d H=%d R=%d N=%d)=%d\n", fduPCN, fduPHN, fduPRN, fduPNN, o);
  3701:       }
  3702:       return o;
  3703:     }  //fduCalcOffset()
  3704: 
  3705:     //fduEPhaseEnd (status)
  3706:     //  E-Phaseを終了してR-Phaseに移行する
  3707:     public void fduEPhaseEnd (int status) {
  3708:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3709:         System.out.printf ("%08x fduEPhaseEnd%d(%s)\n", XEiJ.regPC0, abuNumber, fdcResultStatusToString (status));
  3710:       }
  3711:       //READ DATAやWRITE DATAなどの転送コマンドのE-Phase(転送中)が終了したとき
  3712:       //リザルトステータス(ST0,ST1,ST2,C,H,R,N)を作る
  3713:       fdcResultBuffer[0] = (byte) (status >> 24);  //ST0
  3714:       fdcResultBuffer[1] = (byte) (status >> 16);  //ST1
  3715:       fdcResultBuffer[2] = (byte) (status >> 8);  //ST2
  3716:       fdcResultBuffer[3] = (byte) fduPCN;  //C
  3717:       fdcResultBuffer[4] = (byte) fduPHN;  //H
  3718:       fdcResultBuffer[5] = (byte) fduPRN;  //R
  3719:       fdcResultBuffer[6] = (byte) fduPNN;  //N
  3720:       //R-Phase(RQM=1,DIO=1(FDC→MPU))に移行する
  3721:       fdcRPhase (7);
  3722:       //FDC割り込み要求(INT)を1にする
  3723:       IOInterrupt.ioiFdcRise ();
  3724:     }  //fduEPhaseEnd(int)
  3725: 
  3726: 
  3727:   }  //class FDUnit
  3728: 
  3729: 
  3730: 
  3731: }  //class FDC
  3732: 
  3733: 
  3734: