FDC.java
     1: //========================================================================================
     2: //  FDC.java
     3: //    en:Floppy disk controller
     4: //    ja:フロッピーディスクコントローラ
     5: //  Copyright (C) 2003-2023 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:     fdcFormatDialog.setVisible (true);
  1024:   }  //fdcOpenFormatDialog()
  1025: 
  1026:   //success = fdcFormatFiles (list)
  1027:   //  フロッピーディスクをフォーマットする
  1028:   //  コマンドラインのみ
  1029:   public static boolean fdcFormatFiles (File[] list) {
  1030:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1031:       System.out.printf ("%08x fdcFormatFiles({", XEiJ.regPC0);
  1032:       for (int i = 0; i < list.length; i++) {
  1033:         if (0 < i) {
  1034:           System.out.print (',');
  1035:         }
  1036:         System.out.printf ("\"%s\"", list[i].getPath ());
  1037:       }
  1038:       System.out.println ('}');
  1039:     }
  1040:     boolean success = true;
  1041:   format:
  1042:     {
  1043:       //フローピーディスクのフォーマットデータを作る
  1044:       byte[] diskImage = new byte[fdcFormatMedia.fdmBytesPerDisk];
  1045:       if (!fdcFormatMedia.fdmMakeFormatData (diskImage, fdcFormatCopyHumanSysOn, fdcFormatCopyCommandXOn, fdcFormatX86SafeOn)) {
  1046:         success = false;  //失敗
  1047:         break format;
  1048:       }
  1049:       byte[] dimImage = null;
  1050:       int dimSize = 0;
  1051:       //書き出す
  1052:       int u = 0;
  1053:       for (File file : list) {
  1054:         String path = file.getPath ();
  1055:         String upperPath = path.toUpperCase ();
  1056:         if (upperPath.endsWith (".DIM")) {  // *.DIM
  1057:           if (fdcIsInsertedPath (path)) {  //他のユニットに挿入されている
  1058:             success = false;  //失敗
  1059:             break format;
  1060:           }
  1061:           if (dimImage == null) {
  1062:             dimImage = new byte[256 + fdcFormatMedia.fdmBytesPerDisk];
  1063:             dimSize = fdcFormatMedia.fdmMakeDimImage (dimImage, diskImage);  // *.DIMのイメージを作る
  1064:             if (dimSize < 0) {  // *.DIMのイメージを作れない
  1065:               success = false;  //失敗
  1066:               break format;
  1067:             }
  1068:           }
  1069:           if (!XEiJ.rscPutFile (path, dimImage, 0, dimSize)) {  //書き出せない
  1070:             success = false;  //失敗
  1071:             break format;
  1072:           }
  1073:         } else {  // *.DIM以外
  1074:           boolean extNotSpecified = true;
  1075:           for (String mediaExt : fdcFormatMedia.fdmExtensionArray) {
  1076:             if (upperPath.endsWith (mediaExt)) {  //適切な拡張子が指定されている
  1077:               extNotSpecified = false;
  1078:               break;
  1079:             }
  1080:           }
  1081:           if (extNotSpecified) {  //適切な拡張子が指定されていない
  1082:             if (!path.endsWith (".")) {
  1083:               path += ".";
  1084:             }
  1085:             path += fdcFormatMedia.fdmExtensionArray[0].toLowerCase ();  //拡張子を追加する
  1086:             upperPath = path.toUpperCase ();
  1087:           }
  1088:           if (fdcIsInsertedPath (path)) {  //他のユニットに挿入されている
  1089:             success = false;  //失敗
  1090:             break format;
  1091:           }
  1092:           if (!XEiJ.rscPutFile (path, diskImage, 0, fdcFormatMedia.fdmBytesPerDisk)) {  //書き出せない
  1093:             success = false;  //失敗
  1094:             break format;
  1095:           }
  1096:         }
  1097:         //空いているユニットがあれば挿入する
  1098:         while (u < FDC_MAX_UNITS) {
  1099:           FDUnit unit = fdcUnitArray[u++];  //ユニット
  1100:           if (unit.abuConnected &&  //接続されていて
  1101:               !unit.fduIsInserted () &&  //空いていて
  1102:               unit.insert (path,
  1103:                            false)) {  //挿入できた
  1104:             //フォーマットしたディスクの書き込みを禁止しても意味がないのでここでは書き込みを禁止しない
  1105:             break;
  1106:           }
  1107:         }
  1108:       }
  1109:     }  //format
  1110:     if (success) {  //すべてフォーマットできた
  1111:       fdcAddHistory (list);  //ヒストリに追加する
  1112:     }
  1113:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1114:       System.out.printf ("\tsuccess=%b\n", success);
  1115:     }
  1116:     return success;
  1117:   }  //fdcFormatFiles(File[])
  1118: 
  1119: 
  1120:   //fdcAddHistory (file)
  1121:   //  ファイルをヒストリに追加する
  1122:   public static void fdcAddHistory (File file) {
  1123:     fdcAddHistory (new File[] { file });
  1124:   }
  1125: 
  1126:   //fdcAddHistory (files)
  1127:   //  複数のファイルをヒストリに追加する
  1128:   public static void fdcAddHistory (File[] files) {
  1129:     if (fdcOpenDialog == null) {
  1130:       fdcOpenHistory.add (files);
  1131:     } else {
  1132:       fdcOpenDialog.addHistory (files);
  1133:     }
  1134:     fdcMakeFormatFileChooser ();
  1135:     fdcFormatFileChooser.addHistory (files);
  1136:     fdcFormatFileChooser.selectLastFiles ();
  1137:   }
  1138: 
  1139: 
  1140:   //d = fdcPeekStatus ()
  1141:   //  pbz (0x00e94001)
  1142:   //  FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
  1143:   public static int fdcPeekStatus () {
  1144:     return fdcStatus;
  1145:   }  //fdcPeekStatus()
  1146: 
  1147:   //d = fdcReadStatus ()
  1148:   //  rbz (0x00e94001)
  1149:   //  FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
  1150:   public static int fdcReadStatus () {
  1151:     int d = fdcStatus;
  1152:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1153:       if (fdcLastStatus == d) {
  1154:         System.out.print ('=');
  1155:       } else {
  1156:         fdcLastStatus = d;
  1157:         System.out.printf ("%08x fdcReadStatus(0x00e94001)=%s\n", XEiJ.regPC0, fdcStatusToString (d));
  1158:       }
  1159:     }
  1160:     return d;
  1161:   }  //fdcReadStatus
  1162: 
  1163:   //d = fdcPeekData ()
  1164:   //  pbz (0x00e94003)
  1165:   //  FDC データ/コマンド
  1166:   public static int fdcPeekData () {
  1167:     return (fdcReadHandle == null ? 0 :  //Read中でない
  1168:             fdcReadHandle[fdcIndex] & 255);
  1169:   }  //fdcPeekData
  1170: 
  1171:   //d = fdcReadData ()
  1172:   //  rbz (0x00e94003)
  1173:   //  FDC データ/コマンド
  1174:   public static int fdcReadData () {
  1175:     if (fdcReadHandle == null) {  //Read中でない
  1176:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1177:         System.out.printf ("%08x fdcReadData(0x00e94003=???)=0x00\n", XEiJ.regPC0);
  1178:       }
  1179:       return 0;
  1180:     }
  1181:     int d = fdcReadHandle[fdcIndex] & 255;
  1182:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1183:       if (fdcIndex < fdcStart + 8 || fdcLimit - 8 <= fdcIndex) {  //先頭8バイトまたは末尾8バイト
  1184:         System.out.printf ("%08x fdcReadData(0x00e94003=%s[0x%08x])=0x%02x\n",
  1185:                            XEiJ.regPC0,
  1186:                            fdcReadHandle == fdcResultBuffer ? "fdcResultBuffer" :
  1187:                            fdcReadHandle == fdcTempBuffer ? "fdcTempBuffer" :
  1188:                            "fduImage",
  1189:                            fdcIndex,
  1190:                            d);
  1191:       }
  1192:     }
  1193:     fdcIndex++;
  1194:     if (fdcIndex < fdcLimit) {  //継続
  1195:       if (fdcReadHandle != fdcResultBuffer) {  //E-Phase
  1196:         HD63450.dmaFallPCL (0);  //DMA転送継続
  1197:       } else {  //R-Phase
  1198:         if (fdcIndex == 1) {  //R-PhaseでSE(Seek End)のリザルトステータスの1バイト目のST0が引き取られたとき
  1199:           fdcStatus &= ~(1 << (d & 3));  //ST0に対応するユニットのFDnビジー(DnB)を0にする
  1200:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1201:             System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1202:           }
  1203:         }
  1204:       }
  1205:     } else {  //終了
  1206:       if (fdcReadHandle != fdcResultBuffer) {  //E-Phase
  1207:         fdcEPhaseEnd ();
  1208:       } else {  //R-Phase
  1209:         fdcRPhaseEnd ();
  1210:       }
  1211:     }
  1212:     return d;
  1213:   }  //fdcReadData
  1214: 
  1215:   //d = fdcPeekDriveStatus ()
  1216:   //  pbz (0x00e94005)
  1217:   //  FDD 状態(挿入|誤挿入|000000)/機能(点滅|排出禁止|排出|-|選択####)
  1218:   public static int fdcPeekDriveStatus () {
  1219:     return (fdcDriveLastSelected == null ? 0 :  //ドライブが選択されていない
  1220:             fdcDriveLastSelected.fduDriveStatus ());
  1221:   }  //fdcPeekDriveStatus
  1222: 
  1223:   //d = fdcReadDriveStatus ()
  1224:   //  rbz (0x00e94005)
  1225:   //  FDD 状態(挿入|誤挿入|000000)/機能(点滅|排出禁止|排出|-|選択####)
  1226:   public static int fdcReadDriveStatus () {
  1227:     int d = (fdcDriveLastSelected == null ? 0 :  //ドライブが選択されていない
  1228:              fdcDriveLastSelected.fduDriveStatus ());
  1229:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1230:       System.out.printf ("%08x fdcReadDriveStatus(0x00e94005)=0x%02x(挿入=%d 誤挿入=%d)\n",
  1231:                          XEiJ.regPC0,
  1232:                          d,
  1233:                          d >> 7,
  1234:                          d >> 6 & 1);
  1235:     }
  1236:     return d;
  1237:   }  //fdcReadDriveStatus
  1238: 
  1239:   //fdcWriteCommand (d)
  1240:   //  wb (0x00e94001, d)
  1241:   //  FDC ステータス(RQM|DIO(OUT/IN)|NDM|CB|D3B|D2B|D1B|D0B)/コマンド
  1242:   public static void fdcWriteCommand (int d) {
  1243:     if (fdcWriteHandle != fdcCommandBuffer) {  //C-Phaseでない
  1244:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1245:         System.out.printf ("%08x fdcWriteCommand(0x00e94001=???,0x%02x)\n",
  1246:                            XEiJ.regPC0,
  1247:                            d & 255);
  1248:       }
  1249:       return;
  1250:     }
  1251:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1252:       if (fdcIndex < fdcStart + 8 || fdcLimit - 8 <= fdcIndex) {  //先頭8バイトまたは末尾8バイト
  1253:         System.out.printf ("%08x fdcWriteCommand(0x00e94001=%s[0x%08x],0x%02x)\n",
  1254:                            XEiJ.regPC0,
  1255:                            "fdcCommandBuffer",
  1256:                            fdcIndex,
  1257:                            d & 255);
  1258:       }
  1259:     }
  1260:     fdcWriteHandle[fdcIndex++] = (byte) d;
  1261:     if (fdcLimit <= fdcIndex) {  //C-Phaseが終了した
  1262:       fdcCPhaseEnd ();
  1263:     }  //C-Phaseが終了した
  1264:   }  //fdcWriteCommand(int)
  1265: 
  1266:   //fdcWriteData (d)
  1267:   //  wb (0x00e94003, d)
  1268:   //  FDC データ/コマンド
  1269:   public static void fdcWriteData (int d) {
  1270:     if (fdcWriteHandle == null) {  //Write中でない
  1271:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1272:         System.out.printf ("%08x fdcWriteData(0x00e94003=???,0x%02x)\n",
  1273:                            XEiJ.regPC0,
  1274:                            d & 255);
  1275:       }
  1276:       return;
  1277:     }
  1278:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1279:       if (fdcIndex < fdcStart + 8 || fdcLimit - 8 <= fdcIndex) {  //先頭8バイトまたは末尾8バイト
  1280:         System.out.printf ("%08x fdcWriteData(0x00e94003=%s[0x%08x],0x%02x)\n",
  1281:                            XEiJ.regPC0,
  1282:                            fdcWriteHandle == fdcCommandBuffer ? "fdcCommandBuffer" :
  1283:                            fdcWriteHandle == fdcTempBuffer ? "fdcTempBuffer" :
  1284:                            "fduImage",
  1285:                            fdcIndex,
  1286:                            d & 255);
  1287:       }
  1288:     }
  1289:     fdcWriteHandle[fdcIndex++] = (byte) d;
  1290:     if (fdcIndex < fdcLimit) {  //継続
  1291:       if (fdcWriteHandle != fdcCommandBuffer) {  //E-Phaseのとき
  1292:         HD63450.dmaFallPCL (0);  //DMA転送継続
  1293:       }
  1294:     } else if (fdcWriteHandle == fdcCommandBuffer) {  //C-Phaseが終了した
  1295:       fdcCPhaseEnd ();
  1296:     } else {  //E-Phaseが終了した
  1297:       fdcEPhaseEnd ();
  1298:     }
  1299:   }  //fdcWriteData(int)
  1300: 
  1301: 
  1302:   //fdcCPhase ()
  1303:   //  C-Phaseに戻る
  1304:   public static void fdcCPhase () {
  1305:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1306:       System.out.printf ("%08x fdcCPhase()\n", XEiJ.regPC0);
  1307:     }
  1308:     //コマンドが終了してC-Phaseに戻るとき
  1309:     fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC |  //FDCビジー(CB)を0にする
  1310:                  (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  1311:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1312:       System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1313:     }
  1314:     //C-Phaseに移行する
  1315:     fdcReadHandle = null;
  1316:     fdcWriteHandle = fdcCommandBuffer;  //C-Phase
  1317:     fdcIndex = fdcStart = 0;
  1318:     fdcLimit = 1;  //C-Phaseの1バイト目
  1319:     fdcCommandNumber = -1;  //C-Phaseの1バイト目
  1320:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1321:       System.out.printf ("\tfdcCommandNumber=%d\n", fdcCommandNumber);
  1322:     }
  1323:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1324:       FDUnit unit = fdcUnitArray[u];
  1325:       if (unit.fduSeekStepWaiting) {  //シークステップ待機(seekStepWaiting)が1のとき
  1326:         unit.fduSeekStepWaiting = false;  //シークステップ待機(seekStepWaiting)を0にする
  1327:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1328:           System.out.printf ("\tfduSeekStepWaiting%d=%b\n", u, unit.fduSeekStepWaiting);
  1329:         }
  1330:         unit.fduSeekStep ();  //シークステップ
  1331:       }
  1332:       if ((fdcStatus & 1 << u) != 0) {  //FDnビジー(DnB)が1のとき
  1333:         if (unit.fduSeekEndInterruptWaiting) {  //シーク終了割り込み待機(seekEndInterruptWaiting)が1のとき
  1334:           unit.fduSeekEndInterruptWaiting = false;  //シーク終了割り込み待機(seekEndInterruptWaiting)を0にする
  1335:           unit.fduSeekEndInterruptRequest = true;  //シーク終了割り込み要求(seekEndInterruptRequest)を1にする
  1336:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1337:             System.out.printf ("\tfduSeekEndInterruptWaiting%d=%b\n", u, unit.fduSeekEndInterruptWaiting);
  1338:             System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", u, unit.fduSeekEndInterruptRequest);
  1339:           }
  1340:           IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  1341:         }
  1342:       } else {  //FDnビジー(DnB)が0のとき
  1343:         if (unit.fduAttentionCheckWaiting) {  //状態遷移確認フラグ(attentionCheckWaiting)が1のとき
  1344:           unit.fduAttentionCheckWaiting = false;  //状態遷移確認フラグ(attentionCheckWaiting)を0にする
  1345:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1346:             System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", u, unit.fduAttentionCheckWaiting);
  1347:           }
  1348:           boolean ready = unit.fduIsReady ();
  1349:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1350:             System.out.printf ("\tready%d=%b\n", u, ready);
  1351:           }
  1352:           if (ready != unit.fduSavedReady) {  //レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
  1353:             unit.fduSavedReady = ready;  //レディ信号の状態(isReady)を保存する(isReady→savedReady)
  1354:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1355:               System.out.printf ("\tfduSavedReady%d=%b\n", u, unit.fduSavedReady);
  1356:             }
  1357:             if (!unit.fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が0のとき
  1358:               unit.fduAttentionInterruptRequest = true;  //状態遷移割り込み要求(attentionInterruptRequest)を1にする
  1359:               if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1360:                 System.out.printf ("\tfduAttentionInterruptRequest%d=%b\n", u, unit.fduAttentionInterruptRequest);
  1361:               }
  1362:               IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  1363:             }
  1364:           }
  1365:         }
  1366:       }
  1367:     }  //for u
  1368:   }  //fdcCPhase()
  1369: 
  1370:   //fdcCPhaseEnd ()
  1371:   //  C-Phaseが終了した
  1372:   public static void fdcCPhaseEnd () {
  1373:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1374:       System.out.printf ("%08x fdcCPhaseEnd()\n", XEiJ.regPC0);
  1375:     }
  1376:     if (fdcCommandNumber < 0) {  //C-Phaseの1バイト目のとき
  1377:       fdcCommandNumber = fdcCommandBuffer[0] & 31;
  1378:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1379:         System.out.printf ("\tfdcCommandNumber=%d\n", fdcCommandNumber);
  1380:       }
  1381:       fdcLimit = FDC_COMMAND_LENGTH[fdcCommandNumber];  //コマンドの長さ
  1382:       if (1 < fdcLimit) {  //2バイト以上のコマンドなのでC-Phaseを継続
  1383:         fdcStatus |= FDC_CB;  //2バイト目からCBをセットする
  1384:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1385:           System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1386:         }
  1387:         //外部転送要求モードでDMAの動作を開始してからFDCにコマンドが送られてくるので、
  1388:         //C-PhaseのときDMAに転送要求を出してはならない
  1389:         //HD63450.dmaFallPCL (0);  //DMA転送継続
  1390:         return;
  1391:       }
  1392:       //1バイトのコマンドなのでC-Phaseが終了した
  1393:     }
  1394:     //HD63450.dmaRisePCL (0);
  1395:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1396:       System.out.printf ("********* %s {",
  1397:                          FDC_COMMAND_NAME[fdcCommandNumber]);
  1398:       for (int i = 0; i < fdcLimit; i++) {
  1399:         if (i > 0) {
  1400:           System.out.print (',');
  1401:         }
  1402:         System.out.printf ("0x%02x", fdcCommandBuffer[i] & 255);
  1403:       }
  1404:       System.out.println ("} ********");
  1405:     }
  1406:     FDUnit unit = fdcUnitArray[fdcCommandBuffer[1] & 3];  //ユニットを指定しないコマンドでは無意味だがエラーになることはないので問題ない
  1407:     switch (fdcCommandNumber) {
  1408:       //case 0x02:  //0x02  READ DIAGNOSTIC
  1409:       //  unit.fduCommandReadDiagnostic ();
  1410:       //  break;
  1411:     case 0x03:  //0x03  SPECIFY
  1412:       fdcCommandSpecify ();
  1413:       break;
  1414:     case 0x04:  //0x04  SENSE DEVICE STATUS
  1415:       unit.fduCommandSenseDeviceStatus ();
  1416:       break;
  1417:     case 0x05:  //0x05  WRITE DATA
  1418:       unit.fduCommandWriteData ();
  1419:       break;
  1420:     case 0x06:  //0x06  READ DATA
  1421:       unit.fduCommandReadData ();
  1422:       break;
  1423:     case 0x07:  //0x07  RECALIBRATE
  1424:       unit.fduCommandRecalibrate ();
  1425:       break;
  1426:     case 0x08:  //0x08  SENSE INTERRUPT STATUS
  1427:       fdcCommandSenseInterruptStatus ();
  1428:       break;
  1429:       //case 0x09:  //0x09  WRITE DELETED DATA
  1430:       //  unit.fduCommandWriteDeletedData ();
  1431:       //  break;
  1432:     case 0x0a:  //0x0a  READ ID
  1433:       unit.fduCommandReadId ();
  1434:       break;
  1435:       //case 0x0c:  //0x0c  READ DELETED DATA
  1436:       //  unit.fduCommandReadDeletedData ();
  1437:       //  break;
  1438:     case 0x0d:  //0x0d  WRITE ID
  1439:       unit.fduCommandWriteId ();
  1440:       break;
  1441:     case 0x0f:  //0x0f  SEEK
  1442:       unit.fduCommandSeek ();
  1443:       break;
  1444:     case 0x11:  //0x11  SCAN EQUAL
  1445:       unit.fduCommandScan ();
  1446:       break;
  1447:       //case 0x14:  //0x14  RESET STANDBY
  1448:       //  fdcCommandResetStandby ();
  1449:       //  break;
  1450:       //case 0x15:  //0x15  SET STANDBY
  1451:       //  fdcCommandSetStandby ();
  1452:       //  break;
  1453:     case 0x16:  //0x16  SOFTWARE RESET
  1454:       fdcCommandSoftwareReset ();
  1455:       break;
  1456:     case 0x19:  //0x19  SCAN LOW OR EQUAL
  1457:       unit.fduCommandScan ();
  1458:       break;
  1459:     case 0x1d:  //0x1d  SCAN HIGH OR EQUAL
  1460:       unit.fduCommandScan ();
  1461:       break;
  1462:       //case 0x00:  //0x00  INVALID
  1463:       //case 0x01:  //0x01  INVALID
  1464:       //case 0x0b:  //0x0b  INVALID
  1465:       //case 0x0e:  //0x0e  INVALID
  1466:       //case 0x10:  //0x10  INVALID
  1467:       //case 0x12:  //0x12  INVALID
  1468:       //case 0x13:  //0x13  INVALID
  1469:       //case 0x17:  //0x17  INVALID
  1470:       //case 0x18:  //0x18  INVALID
  1471:       //case 0x1a:  //0x1a  INVALID
  1472:       //case 0x1b:  //0x1b  INVALID
  1473:       //case 0x1c:  //0x1c  INVALID
  1474:       //case 0x1e:  //0x1e  INVALID
  1475:       //case 0x1f:  //0x1f  INVALID
  1476:     default:  //INVALID
  1477:       fdcCommandInvalid ();
  1478:     }
  1479:   }  //fdcCPhaseEnd()
  1480: 
  1481:   //fdcEPhaseEnd ()
  1482:   //  E-Phaseが終了した
  1483:   public static void fdcEPhaseEnd () {
  1484:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1485:       System.out.printf ("%08x fdcEPhaseEnd()\n", XEiJ.regPC0);
  1486:     }
  1487:     FDUnit unit = fdcUnitArray[fdcCommandBuffer[1] & 3];
  1488:     switch (unit.fduCommandNumber) {
  1489:     case 0x05:  //0x05  WRITE DATA
  1490:       unit.fduWriteDataEPhaseEnd ();
  1491:       break;
  1492:     case 0x06:  //0x06  READ DATA
  1493:       unit.fduReadDataEPhaseEnd ();
  1494:       break;
  1495:     case 0x0d:  //0x0d  WRITE ID
  1496:       unit.fduWriteIdEPhaseEnd ();
  1497:       break;
  1498:     case 0x11:  //0x11  SCAN EQUAL
  1499:       unit.fduScanEqualEPhaseEnd ();
  1500:       break;
  1501:     case 0x19:  //0x19  SCAN LOW OR EQUAL
  1502:       unit.fduScanLowOrEqualEPhaseEnd ();
  1503:       break;
  1504:     case 0x1d:  //0x1d  SCAN HIGH OR EQUAL
  1505:       unit.fduScanHighOrEqualEPhaseEnd ();
  1506:       break;
  1507:     }
  1508:   }  //fdcEPhaseEnd()
  1509: 
  1510:   //fdcRPhase (limit)
  1511:   //  R-Phaseに移行する
  1512:   public static void fdcRPhase (int limit) {
  1513:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1514:       System.out.printf ("%08x fdcRPhase({", XEiJ.regPC0);
  1515:       for (int i = 0; i < limit; i++) {
  1516:         if (0 < i) {
  1517:           System.out.print (',');
  1518:         }
  1519:         System.out.printf ("0x%02x", fdcResultBuffer[i] & 255);
  1520:       }
  1521:       System.out.println ("})");
  1522:     }
  1523:     fdcStatus = (FDC_RQM | FDC_FDC_TO_MPU | FDC_CB |
  1524:                  (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  1525:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1526:       System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  1527:     }
  1528:     fdcReadHandle = fdcResultBuffer;  //R-Phase
  1529:     fdcWriteHandle = null;
  1530:     fdcIndex = fdcStart = 0;
  1531:     fdcLimit = limit;
  1532:   }  //fdcRPhase(int)
  1533: 
  1534:   //fdcRPhaseEnd ()
  1535:   //  R-Phaseが終了した
  1536:   public static void fdcRPhaseEnd () {
  1537:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1538:       System.out.printf ("%08x fdcRPhaseEnd()\n", XEiJ.regPC0);
  1539:     }
  1540:     fdcCPhase ();  //C-Phaseに戻る
  1541:     //R-Phaseが終わってC-Phaseに戻るとき
  1542:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1543:       FDUnit unit = fdcUnitArray[u];
  1544:       if (unit.fduSeekEndInterruptRequest) {  //シーク終了割り込み要求(seekEndInterruptRequest)が1のユニットが残っているとき
  1545:         IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  1546:         break;
  1547:       }
  1548:     }
  1549:   }  //fdcRPhaseEnd()
  1550: 
  1551: 
  1552:   //fdcWriteDriveControl (d)
  1553:   //  wb (0x00e94005, d)
  1554:   //  FDD 状態(挿入|誤挿入|------)/機能(点滅|排出禁止|排出|-|選択####)
  1555:   public static void fdcWriteDriveControl (int d) {
  1556:     //0x00e94005にライトすると0x00e9c001のFDD割り込みステータスがクリアされる
  1557:     IOInterrupt.ioiFddFall ();
  1558:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1559:       System.out.printf ("%08x fdcWriteDriveControl(0x00e94005,0x%02x(BLK=%d PRV=%d EJT=%d US3=%d US2=%d US1=%d US0=%d))\n",
  1560:                          XEiJ.regPC0,
  1561:                          d & 255,
  1562:                          d >> 7,
  1563:                          d >> 6 & 1,
  1564:                          d >> 5 & 1,
  1565:                          d >> 3 & 1,
  1566:                          d >> 2 & 1,
  1567:                          d >> 1 & 1,
  1568:                          d & 1);
  1569:     }
  1570:     //bit0-3で選択されたドライブについてbit7=点滅,bit6=排出禁止,bit5=排出
  1571:     int u = Integer.numberOfTrailingZeros (d & 15);  //選択されたドライブの番号。なければ32
  1572:     if (u < 4) {
  1573:       FDUnit unit = fdcUnitArray[u];  //ユニット
  1574:       if (unit.abuConnected) {  //接続されている
  1575:         unit.fduDriveControl (d);
  1576:       }
  1577:       fdcDriveLastSelected = unit;
  1578:     } else {
  1579:       fdcDriveLastSelected = null;
  1580:     }
  1581:   }  //fdcWriteDriveControl
  1582: 
  1583:   //fdcWriteDriveSelect (d)
  1584:   //  wb (0x00e94007, d)
  1585:   //  FDD 選択(モータON|--|2DD|--|ドライブ##)
  1586:   public static void fdcWriteDriveSelect (int d) {
  1587:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1588:       System.out.printf ("%08x fdcWriteDriveSelect(0x00e94007,0x%02x(MT=%d 2DD=%d US=%d))\n",
  1589:                          XEiJ.regPC0,
  1590:                          d & 255,
  1591:                          d >> 7,
  1592:                          d >> 4 & 1,
  1593:                          d & 3);
  1594:     }
  1595:     IOInterrupt.ioiFddFall ();
  1596:     FDUnit unit = fdcUnitArray[d & 3];  //ユニット
  1597:     if (unit.abuConnected &&  //接続されている
  1598:         unit.fduIsInserted ()) {  //挿入されている
  1599:       unit.fduDoubleDensity = d << 31 - 4 < 0;
  1600:       if ((byte) d < 0) {  //モータON
  1601:         unit.fduMotorOn ();
  1602:       } else {  //モータOFF
  1603:         unit.fduMotorOff ();
  1604:       }
  1605:     }
  1606:   }  //fdcWriteDriveSelect
  1607: 
  1608:   //fdcSetEnforcedReady (enforcedReady)
  1609:   //  強制レディ状態(YM2151のCT2が1)の設定
  1610:   public static void fdcSetEnforcedReady (boolean enforcedReady) {
  1611:     fdcEnforcedReady = enforcedReady;
  1612:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1613:       System.out.printf ("%08x fdcSetEnforcedReady(%b)\n", XEiJ.regPC0, enforcedReady);
  1614:     }
  1615:   }  //fdcSetEnforcedReady(boolean)
  1616: 
  1617: 
  1618:   //fdcCommandSpecify ()
  1619:   //  0x03  SPECIFY
  1620:   //    タイマとNon-DMAモードの設定
  1621:   //  C-Phase
  1622:   //    [0]          CMD   Command           0x03=SPECIFY
  1623:   //    [1] bit7-4   SRT   Step Rate Time    Seekコマンドのステップパルス(シリンダ移動)の間隔。標準1ms単位、ミニ2ms単位。16から引く
  1624:   //        bit3-0   HUT   Head Unload Time  Read/Writeコマンド終了後のヘッドアンロードまでの時間。標準16ms単位、ミニ32ms単位。16倍する
  1625:   //    [2] bit7-1   HLT   Head Load Time    ヘッドロード後の安定待ち時間。標準2ms単位、ミニ4ms単位。2倍する
  1626:   //        bit0     ND    Non-DMA Mode      Read/WriteコマンドのE-Phaseについて0=DMAモード,1=Non-DMAモード
  1627:   public static void fdcCommandSpecify () {
  1628:     int srt = fdcCommandBuffer[1] >> 4 & 15;  //SRT
  1629:     int hut = fdcCommandBuffer[1] & 15;  //HUT
  1630:     int hlt = fdcCommandBuffer[2] >> 1 & 127;  //HLT
  1631:     int nd = fdcCommandBuffer[2] & 1;  //ND
  1632:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1633:       System.out.printf ("%08x fdcCommandSpecify(SRT=%d HUT=%d HLT=%d ND=%d)\n", XEiJ.regPC0, srt, hut, hlt, nd);
  1634:     }
  1635:     fdcSRT = srt;
  1636:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1637:       System.out.printf ("\tfdcSRT=%d\n", fdcSRT);
  1638:     }
  1639:     fdcCPhase ();  //C-Phaseに戻る
  1640:   }  //fdcCommandSpecify
  1641: 
  1642:   //fdcCommandSenseInterruptStatus ()
  1643:   //  0x08  SENSE INTERRUPT STATUS
  1644:   //    リザルトステータス0(ST0)の引き取り
  1645:   //  C-Phase
  1646:   //    [0]          CMD   Command          0x08=SENSE INTERRUPT STATUS
  1647:   //    [1]  bit2    HD    Head Address     サイド
  1648:   //         bit1-0  US    Unit Select      ユニット
  1649:   //  R-Phase
  1650:   //    [0]          ST0   Status 0         リザルトステータス0
  1651:   //    [1]          PCN   Present Cylinder Number  現在のシリンダ
  1652:   public static void fdcCommandSenseInterruptStatus () {
  1653:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1654:       System.out.printf ("%08x fdcCommandSenseInterruptStatus()\n", XEiJ.regPC0);
  1655:     }
  1656:     //R-Phaseに移行するとき
  1657:     IOInterrupt.ioiFdcFall ();  //FDC割り込み要求(INT)を0にする
  1658:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1659:       FDUnit unit = fdcUnitArray[u];
  1660:       if ((fdcStatus & 1 << u) != 0) {  //FDnビジー(DnB)が1のとき
  1661:         if (unit.fduSeekEndInterruptWaiting) {  //シーク終了割り込み待機(seekEndInterruptWaiting)が1のとき
  1662:           unit.fduSeekEndInterruptWaiting = false;  //シーク終了割り込み待機(seekEndInterruptWaiting)を0にする
  1663:           unit.fduSeekEndInterruptRequest = true;  //シーク終了割り込み要求(seekEndInterruptRequest)を1にする
  1664:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1665:             System.out.printf ("\tfduSeekEndInterruptWaiting%d=%b\n", u, unit.fduSeekEndInterruptWaiting);
  1666:             System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", u, unit.fduSeekEndInterruptRequest);
  1667:           }
  1668:         } else {  //FDnビジー(DnB)が0のとき
  1669:           if (unit.fduAttentionCheckWaiting) {  //状態遷移確認フラグ(attentionCheckWaiting)が1のとき
  1670:             unit.fduAttentionCheckWaiting = false;  //状態遷移確認フラグ(attentionCheckWaiting)を0にする
  1671:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1672:               System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", u, unit.fduAttentionCheckWaiting);
  1673:             }
  1674:             boolean ready = unit.fduIsReady ();
  1675:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1676:               System.out.printf ("\tready%d=%b\n", u, ready);
  1677:             }
  1678:             if (ready != unit.fduSavedReady) {  //レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
  1679:               unit.fduSavedReady = ready;  //レディ信号の状態(isReady)を保存する(isReady→savedReady)
  1680:               if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1681:                 System.out.printf ("\tfduSavedReady%d=%b\n", u, unit.fduSavedReady);
  1682:               }
  1683:               if (!unit.fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が0のとき
  1684:                 unit.fduAttentionInterruptRequest = true;  //状態遷移割り込み要求(attentionInterruptRequest)を1にする
  1685:                 if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1686:                   System.out.printf ("\tfduAttentionInterruptRequest%d=%b\n", u, unit.fduAttentionInterruptRequest);
  1687:                 }
  1688:               }
  1689:             }
  1690:           }
  1691:         }
  1692:       }
  1693:     }
  1694:     int status = FDC_ST0_IC;
  1695:     for (int u = 0; u <= 3; u++) {  //ユニット0..3について
  1696:       FDUnit unit = fdcUnitArray[u];
  1697:       if (unit.fduSeekEndInterruptRequest) {  //シーク終了割り込み要求(seekEndInterruptRequest)が1のとき
  1698:         unit.fduSeekEndInterruptRequest = false;  //シーク終了割り込み要求(seekEndInterruptRequest)を0にする
  1699:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1700:           System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", u, unit.fduSeekEndInterruptRequest);
  1701:         }
  1702:         status = unit.fduSeekResultStatus | u << 24 | (unit.fduPCN & 255) << 16;
  1703:         break;
  1704:       }
  1705:       if (unit.fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が1のとき
  1706:         unit.fduAttentionInterruptRequest = false;  //状態遷移割り込み要求(attentionInterruptRequest)を0にする
  1707:         status = FDC_ST0_AI | u << 24 | (unit.fduPCN & 255) << 16;  //AI(Attention Interrupt)のリザルトステータスを出力する
  1708:         break;
  1709:       }
  1710:     }
  1711:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1712:       System.out.printf ("\tstatus=%s\n", fdcResultStatusToString (status & 0xff000000));
  1713:     }
  1714:     fdcResultBuffer[0] = (byte) (status >> 24);
  1715:     if (status == FDC_ST0_IC) {
  1716:       fdcRPhase (1);
  1717:     } else {
  1718:       fdcResultBuffer[1] = (byte) (status >> 16);
  1719:       fdcRPhase (2);
  1720:     }
  1721:   }  //fdcCommandSenseInterruptStatus()
  1722: 
  1723:   //fdcCommandResetStandby ()
  1724:   //  0x14  RESET STANDBY
  1725:   //    スタンバイ状態の解除
  1726:   public static void fdcCommandResetStandby () {
  1727:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1728:       System.out.printf ("%08x fdcCommandResetStandby()\n", XEiJ.regPC0);
  1729:     }
  1730:     //何もしない
  1731:     fdcCPhase ();  //C-Phaseに戻る
  1732:   }  //fdcCommandResetStandby()
  1733: 
  1734:   //fdcCommandSetStandby ()
  1735:   //  0x15  SET STANDBY
  1736:   //    スタンバイ状態への移行
  1737:   public static void fdcCommandSetStandby () {
  1738:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1739:       System.out.printf ("%08x fdcCommandSetStandby()\n", XEiJ.regPC0);
  1740:     }
  1741:     //何もしない
  1742:     fdcCPhase ();  //C-Phaseに戻る
  1743:   }  //fdcCommandSetStandby()
  1744: 
  1745:   //fdcCommandSoftwareReset ()
  1746:   //  0x16  SOFTWARE RESET
  1747:   //    FDCの初期化
  1748:   public static void fdcCommandSoftwareReset () {
  1749:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1750:       System.out.printf ("%08x fdcCommandSoftwareReset()\n", XEiJ.regPC0);
  1751:     }
  1752:     //何もしない
  1753:     fdcCPhase ();  //C-Phaseに戻る
  1754:   }  //fdcCommandSoftwareReset()
  1755: 
  1756:   //fdcCommandInvalid ()
  1757:   //  0x00  INVALID
  1758:   //  0x01  INVALID
  1759:   //  0x0b  INVALID
  1760:   //  0x0e  INVALID
  1761:   //  0x10  INVALID
  1762:   //  0x12  INVALID
  1763:   //  0x13  INVALID
  1764:   //  0x17  INVALID
  1765:   //  0x18  INVALID
  1766:   //  0x1a  INVALID
  1767:   //  0x1b  INVALID
  1768:   //  0x1c  INVALID
  1769:   //  0x1e  INVALID
  1770:   //  0x1f  INVALID
  1771:   //    不正なコマンドまたはSENSE INTERRUPT STATUSの不正使用
  1772:   public static void fdcCommandInvalid () {
  1773:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1774:       System.out.printf ("%08x fdcCommandInvalid()\n", XEiJ.regPC0);
  1775:     }
  1776:     int status = FDC_ST0_IC;
  1777:     if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1778:       System.out.printf ("\tstatus=%s\n", fdcResultStatusToString (status));
  1779:     }
  1780:     fdcResultBuffer[0] = (byte) (status >> 24);  //ST0
  1781:     fdcRPhase (1);
  1782:   }  //fdcCommandInvalid
  1783: 
  1784: 
  1785:   public static String fdcResultStatusToString (int status) {
  1786:     int ic = status >> 24 + 6 & 3;
  1787:     return String.format ("0x%08x\n" +
  1788:                           "\t\tST0=0x%02x(IC=%d(%s) SE=%d EC=%d NR=%d HD=%d US=%d)\n" +
  1789:                           "\t\tST1=0x%02x(EN=%d DE=%d OR=%d ND=%d NW=%d MA=%d)\n" +
  1790:                           "\t\tST2=0x%02x(CM=%d DD=%d NC=%d SH=%d SN=%d BC=%d MD=%d)",
  1791:                           status,
  1792:                           status >> 24 & 255,
  1793:                           ic, ic == 0 ? "NT" : ic == 1 ? "AT" : ic == 2 ? "IC" : "AI",  //IC
  1794:                           status >> 24 + 5 & 1,
  1795:                           status >> 24 + 4 & 1,
  1796:                           status >> 24 + 3 & 1,
  1797:                           status >> 24 + 2 & 1,
  1798:                           status >> 24 + 0 & 3,
  1799:                           status >> 16 & 255,
  1800:                           status >> 16 + 7 & 1,
  1801:                           status >> 16 + 5 & 1,
  1802:                           status >> 16 + 4 & 1,
  1803:                           status >> 16 + 2 & 1,
  1804:                           status >> 16 + 1 & 1,
  1805:                           status >> 16 + 0 & 1,
  1806:                           status >> 8 & 255,
  1807:                           status >> 8 + 6 & 1,
  1808:                           status >> 8 + 5 & 1,
  1809:                           status >> 8 + 4 & 1,
  1810:                           status >> 8 + 3 & 1,
  1811:                           status >> 8 + 2 & 1,
  1812:                           status >> 8 + 1 & 1,
  1813:                           status >> 8 + 0 & 1);
  1814:   }  //fdcResultStatusToString(int)
  1815: 
  1816:   //fdcStatusToString (status)
  1817:   public static String fdcStatusToString (int status) {
  1818:     return String.format ("0x%02x(RQM=%d DIO=%d(%s) NDM=%d CB=%d D3B=%d D2B=%d D1B=%d D0B=%d)",
  1819:                           status,
  1820:                           status >> 7,
  1821:                           status >> 6 & 1,
  1822:                           (status >> 6 & 1) == 0 ? "MPU->FDC" : "FDC->MPU",
  1823:                           status >> 5 & 1,
  1824:                           status >> 4 & 1,
  1825:                           status >> 3 & 1,
  1826:                           status >> 2 & 1,
  1827:                           status >> 1 & 1,
  1828:                           status & 1);
  1829:   }  //fdcStatusToString(int)
  1830: 
  1831: 
  1832: 
  1833:   //========================================================================================
  1834:   //$$FDU FDユニット
  1835:   //  フロッピーディスクのユニット
  1836:   //
  1837:   public static class FDUnit extends AbstractUnit {
  1838: 
  1839:     public FDMedia fduMedia;  //メディアの種類
  1840:     public byte[] fduImage;  //イメージ
  1841:     public boolean fduWritten;  //true=書き込みがあった
  1842: 
  1843:     public int fduCommandNumber;  //fdcCommandNumberのコピー。シークを伴うコマンドのE-Phase以降で使う
  1844: 
  1845:     //  (接続フラグ)
  1846:     //    ユニットが接続されているときON、接続されていないときOFF
  1847:     //public boolean fduIsConnected {
  1848:     //  return abuConnected;
  1849:     //}  //fduIsConnected()
  1850: 
  1851:     //  (挿入フラグ)
  1852:     //    ドライブステータス(0x00e94005)のReadのbit7。0=OFF,1=ON
  1853:     //    接続フラグがON、かつ、
  1854:     //    ディスクイメージファイルの読み込みが完了している、かつ、
  1855:     //    メディアの種類の判別が完了している
  1856:     public boolean fduIsInserted () {
  1857:       return abuConnected && abuInserted && fduMedia != null;
  1858:     }  //fduIsInserted()
  1859: 
  1860:     //  (誤挿入フラグ)
  1861:     //    ドライブステータス(0x00e94005)のReadのbit6。0=OFF,1=ON
  1862:     //    使用しない。常にOFF
  1863: 
  1864:     //  点滅設定フラグ
  1865:     //    ドライブコントロール(0x00e94005)のWriteのbit7。0=OFF,1=ON
  1866:     public boolean fduBlinking;  //true=点滅が設定されている
  1867: 
  1868:     //  排出禁止設定フラグ
  1869:     //    ドライブコントロール(0x00e94005)のWriteのbit6。0=OFF,1=ON
  1870:     public boolean fduPrevented;  //true=排出禁止が設定されている
  1871: 
  1872:     //  排出要求フラグ
  1873:     //    ドライブコントロール(0x00e94005)のWriteのbit5。0=OFF,1=ON
  1874: 
  1875:     //  モータON設定フラグ
  1876:     //    ドライブセレクト(0x00e94007)のWriteのbit7。0=OFF,1=ON
  1877: 
  1878:     //  2DD設定フラグ
  1879:     //    ドライブセレクト(0x00e94007)のWriteのbit5。0=OFF,1=ON
  1880:     public boolean fduDoubleDensity;  //true=2DD設定
  1881: 
  1882:     //  書き込み禁止フラグ
  1883:     //    デバイスステータス(ST3)のbit6。0=書き込み許可,1=書き込み禁止
  1884:     public boolean fduIsWriteProtected () {  //true=書き込み禁止
  1885:       return abuWriteProtected;
  1886:     }  //fduIsWriteProtected()
  1887: 
  1888:     //ready = fduIsReady ()
  1889:     //  レディ信号の状態
  1890:     //  デバイスステータス(ST3)のbit5
  1891:     //    0  ノットレディ
  1892:     //    1  レディ。強制レディ状態または接続されていてメディアが挿入されていてモータ動作中またはモータ減速中
  1893:     public boolean fduIsReady () {
  1894:       return (fdcEnforcedReady ||  //強制レディ状態または
  1895:               (abuConnected &&  //接続されていて
  1896:                fduIsInserted () &&  //メディアが挿入されていて
  1897:                (fduMotorStatus == FDU_MOTOR_RUN_SOON ||  //モータ動作前(レディ→割り込み要求)または
  1898:                 fduMotorStatus == FDU_MOTOR_DECELERATING ||  //モータ減速中(レディ→ノットレディ)または
  1899:                 fduMotorStatus == FDU_MOTOR_RUNNING)));  //モータ動作中(レディ)
  1900:     }  //fduIsReady()
  1901: 
  1902:     //  アクセスランプ
  1903:     //    消灯    挿入フラグがOFF、かつ、点滅設定フラグがOFF
  1904:     //    緑点滅  挿入フラグがOFF、かつ、点滅設定フラグがON
  1905:     //    緑点灯  挿入フラグがON、かつ、ユニットビジーフラグがOFF
  1906:     //    赤点灯  挿入フラグがON、かつ、ユニットビジーフラグがON
  1907:     public static final int FDU_ACCESS_OFF            = 0;
  1908:     public static final int FDU_ACCESS_GREEN_BLINKING = 1;
  1909:     public static final int FDU_ACCESS_GREEN_ON       = 2;
  1910:     public static final int FDU_ACCESS_RED_ON         = 3;
  1911: 
  1912:     //  イジェクトランプ
  1913:     //    消灯    挿入フラグがOFF
  1914:     //    消灯    挿入フラグがON、かつ、排出禁止フラグがON
  1915:     //    緑点灯  挿入フラグがON、かつ、排出禁止フラグがOFF
  1916:     public static final int FDU_EJECT_OFF      = 0;
  1917:     public static final int FDU_EJECT_GREEN_ON = 1;
  1918: 
  1919: 
  1920:     //シーク
  1921:     public static final long FDU_SEEK_INTERVAL = XEiJ.TMR_FREQ * 1 / 1000;  //1ms。シークティッカーの動作間隔
  1922:     public int fduNCN;  //NCN 目標シリンダ番号(NCNn)
  1923:     public int fduPCN;  //PCN Present Cylinder Number シリンダ番号(PCNn)。Track0信号=fduPCN==0?1:0
  1924:     public int fduPHN;  //PHN Present Head Number サイド番号。0~1。2HDEは最初のセクタを除いて128~129
  1925:     public int fduPRN;  //PRN Present Record Number セクタ番号。1~1トラックあたりのセクタ数。2HSは最初のセクタを除いて10~18
  1926:     public int fduPNN;  //PNN Present Record Length セクタ長。0~7
  1927:     public int fduEOT;  //End of Track。終了セクタ
  1928:     public int fduSTP;  //Step。1=R++,2=R+=2
  1929:     public int fduSRC;  //ステップレートカウンタ。初期値は16-SRT
  1930:     public boolean fduSeekStepWaiting;  //シークステップ待機(seekStepWaiting)
  1931:     public boolean fduSeekEndInterruptWaiting;  //シーク終了割り込み待機(seekEndInterruptWaiting)
  1932:     public boolean fduSeekEndInterruptRequest;  //シーク終了割り込み要求(seekEndInterruptRequest)
  1933:     public int fduSeekResultStatus;  //シーク終了割り込みでSENSE INTERRUPT STATUSが返すリザルトステータス
  1934: 
  1935:     //fduSeekTicker
  1936:     //  シークティッカー
  1937:     public final TickerQueue.Ticker fduSeekTicker = new TickerQueue.Ticker () {
  1938:       @Override protected void tick () {
  1939:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1940:           System.out.printf ("%08x fduSeekTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  1941:         }
  1942:         if ((fdcStatus & 1 << abuNumber) != 0) {  //FDnビジー(DnB)が1のとき
  1943:           if ((fdcStatus & FDC_CB) != 0) {  //FDCビジー(CB)が1のとき
  1944:             fduSeekStepWaiting = true;  //シークステップ待機(seekStepWaiting)を1にする
  1945:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1946:               System.out.printf ("\tfduSeekStepWaiting%d=%b\n", abuNumber, fduSeekStepWaiting);
  1947:             }
  1948:           } else {  //FDCビジー(CB)が0のとき
  1949:             fduSeekStep ();  //シークステップ
  1950:           }
  1951:         } else {  //FDnビジー(DnB)が0のとき
  1952:           fduSeekStep ();  //シークステップ
  1953:         }
  1954:       }  //tick()
  1955:     };  //fduSeekTicker
  1956: 
  1957:     //fduSeekStep ()
  1958:     //  シークステップ
  1959:     public void fduSeekStep () {
  1960:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1961:         System.out.printf ("%08x fduSeekStep%d()\n", XEiJ.regPC0, abuNumber);
  1962:       }
  1963:       if (fduPCN != fduNCN) {  //シリンダ番号(PCNn)と目標シリンダ番号(NCNn)が違うとき
  1964:         fduSRC--;  //ステップレートカウンタ(SRC)をデクリメントする
  1965:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1966:           System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  1967:         }
  1968:         if (fduSRC == 0) {  //ステップレートカウンタ(SRC)が0になったとき
  1969:           fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  1970:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1971:             System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  1972:           }
  1973:           if (fduPCN < fduNCN) {  //シリンダ番号(PCNn)が目標シリンダ番号(NCNn)よりも小さいとき
  1974:             fduPCN++;  //シリンダ番号(PCNn)をインクリメントする
  1975:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1976:               System.out.printf ("\tfduPCN%d=%d\n", abuNumber, fduPCN);
  1977:             }
  1978:           } else {  //シリンダ番号(PCNn)が目標シリンダ番号(NCNn)よりも大きいとき
  1979:             fduPCN--;  //シリンダ番号(PCNn)をデクリメントする
  1980:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  1981:               System.out.printf ("\tfduPCN%d=%d\n", abuNumber, fduPCN);
  1982:             }
  1983:           }
  1984:         }  //if fduSRC==0
  1985:         TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを1ms後に予約する
  1986:         return;
  1987:       }
  1988:       //シーク終了
  1989:       //E-Phase
  1990:       switch (fduCommandNumber) {
  1991:         //case 0x02:  //READ DIAGNOSTIC
  1992:         //  fduReadDiagnosticEPhase ();
  1993:         //  break;
  1994:       case 0x05:  //WRITE DATA
  1995:         fduWriteDataEPhase ();
  1996:         break;
  1997:       case 0x06:  //READ DATA
  1998:         fduReadDataEPhase ();
  1999:         break;
  2000:       case 0x07:  //RECALIBRATE
  2001:       case 0x0f:  //SEEK
  2002:         //SEEK/RECALIBRATEコマンドのとき
  2003:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2004:         break;
  2005:         //case 0x09:  //WRITE DELETED DATA
  2006:         //  fduWriteDeletedDataEPhase ();
  2007:         //  break;
  2008:         //case 0x0c:  //READ DELETED DATA
  2009:         //  fduReadDeletedDataEPhase ();
  2010:         //  break;
  2011:       case 0x11:  //SCAN EQUAL
  2012:       case 0x19:  //SCAN LOW OR EQUAL
  2013:       case 0x1d:  //SCAN HIGH OR EQUAL
  2014:         fduScanEPhase ();
  2015:         break;
  2016:       }
  2017:     }  //fduSeekStep()
  2018: 
  2019:     //fduSeekEnd ()
  2020:     //  SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2021:     public void fduSeekEnd () {
  2022:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2023:         System.out.printf ("%08x fduSeekEnd%d()\n", XEiJ.regPC0, abuNumber);
  2024:       }
  2025:       if ((fdcStatus & FDC_CB) != 0) {  //FDCビジー(CB)が1のとき
  2026:         fduSeekEndInterruptWaiting = true;  //シーク終了割り込み待機(seekEndInterruptWaiting)を1にする
  2027:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2028:           System.out.printf ("\tfduSeekEndInterruptWaiting%d=%b\n", abuNumber, fduSeekEndInterruptWaiting);
  2029:         }
  2030:       } else {  //FDCビジー(CB)が0のとき
  2031:         if (!fduSeekEndInterruptRequest) {  //シーク終了割り込み要求(seekEndInterruptRequest)が0のとき
  2032:           fduSeekEndInterruptRequest = true;  //シーク終了割り込み要求(seekEndInterruptRequest)を1にする
  2033:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2034:             System.out.printf ("\tfduSeekEndInterruptRequest%d=%b\n", abuNumber, fduSeekEndInterruptRequest);
  2035:           }
  2036:           IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  2037:         }
  2038:       }
  2039:     }  //fduSeekEnd()
  2040: 
  2041: 
  2042:     //モータ
  2043:     //
  2044:     //  モータON/OFFのFDC割り込み
  2045:     //    0x00e94001==0x80
  2046:     //    RQM=1なので0x00e94003=0x08(SENSE INTERRUPT STATUS)
  2047:     //    0x00e94001==0x00  RQM=0,DIO=0(OUT),NDM=0,CB=0,D3B=0,D2B=0,D1B=0,D0B=0 x 2回~3回
  2048:     //    0x00e94001==0x10  RQM=0,DIO=0(OUT),NDM=0,CB=1,D3B=0,D2B=0,D1B=0,D0B=0 x 2回
  2049:     //    0x00e94001==0xd0  RQM=1,DIO=1(IN),NDM=0,CB=1,D3B=0,D2B=0,D1B=0,D0B=0
  2050:     //    0x00e94003==0xc0(モータ動作中のとき)  IC=3(AI状態遷移)
  2051:     //                0xc8(モータ停止中のとき)  IC=3(AI状態遷移),NR=1(ノットレディ)
  2052:     //    0x00e94003==0x00  PCN
  2053:     //    0x00e94001==0x80
  2054:     //
  2055:     //  謎
  2056:     //    X68030実機で2HDディスクを入れたFDD0のモータをONにすると、FDD0だけでなく、メディアが入っていないFDD1と、
  2057:     //    接続されていないFDD2とFDD3もレディ状態になったことを知らせる、4個の状態遷移割り込みステータスが出力される
  2058:     //    OFFも同様
  2059:     //    これはドライブの選択をPEDECが行うのでuPD72065 FDCがREADY信号をポーリングするときドライブを選択できないことが原因
  2060:     //    https://twitter.com/moveccr/status/1518985253049991169
  2061:     //
  2062:     //  モータON
  2063:     //      FDU_MOTOR_SLEEPING  モータ停止中(ノットレディ)
  2064:     //      ↓
  2065:     //    fdcMotorOn  モータON
  2066:     //      FDU_MOTOR_ACCELERATING  モータ加速中(ノットレディ→レディ)
  2067:     //      ↓
  2068:     //      ↓  FDU_MOTOR_ON_DELAY  モータ加速中(ノットレディ→レディ)
  2069:     //      ↓
  2070:     //    fduMotorAcceleratingTicker  モータ加速中ティッカー(ノットレディ→レディ)
  2071:     //      FDU_MOTOR_RUN_SOON  モータ動作前(レディ→割り込み要求)
  2072:     //      READY=1
  2073:     //      ↓
  2074:     //      ↓  FDU_MOTOR_INTERRUPT_DELAY  レディ→割り込み要求
  2075:     //      ↓
  2076:     //    fduMotorRunSoonTicker  モータ動作前ティッカー(レディ→割り込み要求)
  2077:     //      FDU_MOTOR_RUNNING  モータ動作中(レディ)
  2078:     //      状態遷移割り込み要求
  2079:     //
  2080:     //  モータOFF
  2081:     //      FDU_MOTOR_RUNNING  モータ動作中(レディ)
  2082:     //      ↓
  2083:     //    fdcMotorOff  モータOFF
  2084:     //      FDU_MOTOR_DECELERATING  モータ減速中(レディ→ノットレディ)
  2085:     //      ↓
  2086:     //      ↓  FDU_MOTOR_OFF_DELAY  レディ→ノットレディ
  2087:     //      ↓
  2088:     //    fduMotorDeceleratingTicker  モータ減速中ティッカー(レディ→ノットレディ)
  2089:     //      FDU_MOTOR_SLEEP_SOON  モータ停止前(ノットレディ→割り込み要求)
  2090:     //      READY=0
  2091:     //      ↓
  2092:     //      ↓  FDU_MOTOR_INTERRUPT_DELAY  ノットレディ→割り込み要求
  2093:     //      ↓
  2094:     //    fduMotorSleepSoonTicker  モータ停止前ティッカー(ノットレディ→割り込み要求)
  2095:     //      FDU_MOTOR_SLEEPING  モータ停止中(ノットレディ)
  2096:     //      状態遷移割り込み要求
  2097:     //
  2098:     //  メモ
  2099:     //    IOCSはモータONしてからSENSE DEVICE STATUSコマンドを繰り返してST3のREADYが1になるのを待つ
  2100:     //    このとき2バイトのコマンドを出力する間だけ割り込みを禁止している
  2101:     //    ST3のREADYが1になるのと同時に状態遷移割り込みを要求すると、
  2102:     //    2バイトのコマンドを出力している間にFDC割り込みが要求が発生し、
  2103:     //    コマンドの出力が終わって割り込みが許可されるとすぐに割り込み処理が開始されてしまうことがある
  2104:     //    その時点でR-Phaseになっているので割り込みハンドラが状態遷移割り込みを転送終了割り込みと誤認して、
  2105:     //    SENSE DEVICE STATUSコマンドのST3を転送コマンドのST0として回収してしまう
  2106:     //    割り込み処理が終了してSENSE DEVICE STATUSコマンドのST3を読み出そうとしたときにはC-Phaseになっており、
  2107:     //    SENSE DEVICE STATUSコマンドのR-Phaseを待つループから抜けられなくなってハングアップする
  2108:     //      FDC                     MPU
  2109:     //      C-Phase                 割り込み禁止
  2110:     //      状態遷移割り込み要求    SENSE DEVICE STATUSコマンドを出力する
  2111:     //      R-Phase                 割り込み許可
  2112:     //                              割り込み処理開始
  2113:     //                              SENSE DEVICE STATUSコマンドのST3を転送コマンドのST0として回収してしまう
  2114:     //      C-Phase                 割り込み処理終了
  2115:     //                              SENSE DEVICE STATUSコマンドのR-Phaseを待つループから抜けられなくなる
  2116:     //    実機はポーリングでREADYを監視しているので通常はREADYが1になってから割り込みが発生するまで少し間が空く
  2117:     //    最小間隔が0だと実機でも同じ問題が発生するはずだが見たことがないので最小間隔は0よりも大きいと考えられる
  2118:     //
  2119:     //    +------------------+--------------------------------------------------------+
  2120:     //    |  イジェクト許可  |                     イジェクト禁止                     |
  2121:     //    +------------------+------------------+-------------------------------------+
  2122:     //    |             ノットレディ            |                レディ               |
  2123:     //    +-------------------------------------+-------------------------------------+
  2124:     //    |                 ON                 ON                 ON                  |
  2125:     //    |                 →   ACCELERATING  →     RUN_SOON    →                  |
  2126:     //    |     SLEEPING         OFF↓  ↑ON        OFF↓  ↑ON          RUNNING      |
  2127:     //    |                 ←    SLEEP_SOON   ←   DECELERATING  ←                  |
  2128:     //    |                 OFF                OFF                OFF                 |
  2129:     //    +---------------------------------------------------------------------------+
  2130:     //    SLEEPINGからのONでACCELERATINGに移行するが、DECELERATINGからのONでACCELERATINGに移行してしまうと、
  2131:     //    RUNNINGからOFFされてDECELERATINGになった直後にONされたときノットレディの時間ができてしまう
  2132:     //    それではDECELERATINGの時間を設けてノットレディを遅延させている意味がない
  2133:     //    DECELERATINGからのONはRUN_SOONに移行しなければならない
  2134:     //
  2135:     public static final int FDU_MOTOR_SLEEPING     = 0;  //モータ停止中(ノットレディ)。イジェクト可
  2136:     public static final int FDU_MOTOR_ACCELERATING = 1;  //モータ加速中(ノットレディ→レディ)。イジェクト不可
  2137:     public static final int FDU_MOTOR_SLEEP_SOON   = 2;  //モータ停止前(ノットレディ→割り込み要求)。イジェクト不可
  2138:     public static final int FDU_MOTOR_RUN_SOON     = 3;  //モータ動作前(レディ→割り込み要求)。イジェクト不可
  2139:     public static final int FDU_MOTOR_DECELERATING = 4;  //モータ減速中(レディ→ノットレディ)。イジェクト不可
  2140:     public static final int FDU_MOTOR_RUNNING      = 5;  //モータ動作中(レディ)。イジェクト不可
  2141:     public int fduMotorStatus;  //モータの状態
  2142:     public static final long FDU_MOTOR_ON_DELAY        = XEiJ.TMR_FREQ * 100 / 1000000;  //100us。モータ加速中(ノットレディ→レディ)
  2143:     public static final long FDU_MOTOR_OFF_DELAY       = XEiJ.TMR_FREQ * 3;  //3s。モータ減速中(レディ→ノットレディ)
  2144:     public static final long FDU_MOTOR_INTERRUPT_DELAY = XEiJ.TMR_FREQ * 100 / 1000000;  //100us。割り込み待ち
  2145:     public boolean fduSavedReady;  //保存されたレディ信号の状態(savedReady)
  2146:     public boolean fduAttentionCheckWaiting;  //状態遷移確認フラグ(attentionCheckWaiting)
  2147:     public boolean fduAttentionInterruptRequest;  //状態遷移割り込み要求(attentionInterruptRequest)
  2148: 
  2149:     //fduMotorOn ()
  2150:     //  モータON
  2151:     public void fduMotorOn () {
  2152:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2153:         System.out.printf ("%08x fduMotorOn%d()\n", XEiJ.regPC0, abuNumber);
  2154:       }
  2155:       if (!fduIsInserted ()) {  //メディアが挿入されていないとき
  2156:         return;  //何もしない
  2157:       }
  2158:       switch (fduMotorStatus) {
  2159:       case FDU_MOTOR_SLEEPING:  //モータ停止中(ノットレディ)のとき
  2160:         fduMotorStatus = FDU_MOTOR_ACCELERATING;  //モータ加速中(ノットレディ→レディ)にする
  2161:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2162:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2163:         }
  2164:         if (!fduPrevented) {  //排出禁止設定でないとき
  2165:           prevent ();  //排出を禁止する
  2166:         }
  2167:         TickerQueue.tkqAdd (fduMotorAcceleratingTicker, XEiJ.mpuClockTime + FDU_MOTOR_ON_DELAY);  //モータ加速中ティッカー(ノットレディ→レディ)を予約する
  2168:         break;
  2169:       case FDU_MOTOR_SLEEP_SOON:  //モータ停止前(ノットレディ→割り込み要求)のとき
  2170:         TickerQueue.tkqRemove (fduMotorSleepSoonTicker);  //モータ停止前ティッカー(ノットレディ→割り込み要求)を取り消す
  2171:         fduMotorStatus = FDU_MOTOR_ACCELERATING;  //モータ加速中(ノットレディ→レディ)にする
  2172:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2173:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2174:         }
  2175:         TickerQueue.tkqAdd (fduMotorAcceleratingTicker, XEiJ.mpuClockTime + FDU_MOTOR_ON_DELAY);  //モータ加速中ティッカー(ノットレディ→レディ)を予約する
  2176:         break;
  2177:       case FDU_MOTOR_DECELERATING:  //モータ減速中(レディ→ノットレディ)のとき
  2178:         TickerQueue.tkqRemove (fduMotorDeceleratingTicker);  //モータ減速中ティッカー(レディ→ノットレディ)を取り消す
  2179:         fduMotorStatus = FDU_MOTOR_RUN_SOON;  //モータ動作前(レディ→割り込み要求)にする
  2180:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2181:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2182:         }
  2183:         TickerQueue.tkqAdd (fduMotorRunSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ動作前ティッカー(レディ→割り込み要求)を予約する
  2184:         break;
  2185:       }
  2186:     }  //fduMotorOn()
  2187: 
  2188:     //fduMotorAcceleratingTicker
  2189:     //  モータ加速中ティッカー(ノットレディ→レディ)
  2190:     public final TickerQueue.Ticker fduMotorAcceleratingTicker = new TickerQueue.Ticker () {
  2191:       @Override protected void tick () {
  2192:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2193:           System.out.printf ("%08x fduMotorAcceleratingTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2194:         }
  2195:         fduMotorStatus = FDU_MOTOR_RUN_SOON;  //モータ動作前(レディ→割り込み要求)にする
  2196:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2197:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2198:         }
  2199:         //(強制レディ状態でなければここでレディになる)
  2200:         TickerQueue.tkqAdd (fduMotorRunSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ動作前ティッカー(レディ→割り込み要求)を予約する
  2201:       }  //tick()
  2202:     };  //fduMotorAcceleratingTicker
  2203: 
  2204:     //fduMotorRunSoonTicker
  2205:     //  モータ動作前ティッカー(レディ→割り込み要求)
  2206:     public final TickerQueue.Ticker fduMotorRunSoonTicker = new TickerQueue.Ticker () {
  2207:       @Override protected void tick () {
  2208:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2209:           System.out.printf ("%08x fduMotorRunSoonTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2210:         }
  2211:         fduMotorStatus = FDU_MOTOR_RUNNING;  //モータ動作中(レディ)にする
  2212:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2213:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2214:         }
  2215:         fduMotorInterrupt ();  //モータONまたはモータOFFから一定の時間が経ったとき
  2216:       }  //tick()
  2217:     };  //fduMotorRunSoonTicker
  2218: 
  2219:     //fduMotorOff ()
  2220:     //  モータOFF
  2221:     @SuppressWarnings ("fallthrough") public void fduMotorOff () {
  2222:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2223:         System.out.printf ("%08x fduMotorOff%d()\n", XEiJ.regPC0, abuNumber);
  2224:       }
  2225:       if (!fduIsInserted ()) {  //メディアが挿入されていないとき
  2226:         return;  //何もしない
  2227:       }
  2228:       switch (fduMotorStatus) {
  2229:       case FDU_MOTOR_ACCELERATING:  //モータ加速中(ノットレディ→レディ)のとき
  2230:         TickerQueue.tkqRemove (fduMotorAcceleratingTicker);  //モータ加速中ティッカー(ノットレディ→レディ)を取り消す
  2231:         fduMotorStatus = FDU_MOTOR_SLEEP_SOON;  //モータ停止前(ノットレディ→割り込み要求)にする
  2232:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2233:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2234:         }
  2235:         TickerQueue.tkqAdd (fduMotorSleepSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ停止前ティッカー(ノットレディ→割り込み要求)を予約する
  2236:         break;
  2237:       case FDU_MOTOR_RUN_SOON:  //モータ動作前(レディ→割り込み要求)のとき
  2238:         TickerQueue.tkqRemove (fduMotorRunSoonTicker);  //モータ動作前ティッカー(レディ→割り込み要求)を取り消す
  2239:         //fallthrough
  2240:       case FDU_MOTOR_RUNNING:  //モータ動作中(レディ)のとき
  2241:         fduMotorStatus = FDU_MOTOR_DECELERATING;  //モータ減速中(レディ→ノットレディ)にする
  2242:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2243:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2244:         }
  2245:         TickerQueue.tkqAdd (fduMotorDeceleratingTicker, XEiJ.mpuClockTime + FDU_MOTOR_OFF_DELAY);  //モータ減速中ティッカー(レディ→ノットレディ)を予約する
  2246:         break;
  2247:       }
  2248:     }  //fduMotorOff()
  2249: 
  2250:     //fduMotorDeceleratingTicker
  2251:     //  モータ減速中ティッカー(レディ→ノットレディ)
  2252:     public final TickerQueue.Ticker fduMotorDeceleratingTicker = new TickerQueue.Ticker () {
  2253:       @Override protected void tick () {
  2254:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2255:           System.out.printf ("%08x fduMotorDeceleratingTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2256:         }
  2257:         fduMotorStatus = FDU_MOTOR_SLEEP_SOON;  //モータ停止前(ノットレディ→割り込み要求)にする
  2258:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2259:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2260:         }
  2261:         //(強制レディ状態でなければここでノットレディになる)
  2262:         TickerQueue.tkqAdd (fduMotorSleepSoonTicker, XEiJ.mpuClockTime + FDU_MOTOR_INTERRUPT_DELAY);  //モータ停止前ティッカー(ノットレディ→割り込み要求)を予約する
  2263:       }  //tick()
  2264:     };  //fduMotorDeceleratingTicker
  2265: 
  2266:     //fduMotorSleepSoonTicker
  2267:     //  モータ停止前ティッカー(ノットレディ→割り込み要求)
  2268:     public final TickerQueue.Ticker fduMotorSleepSoonTicker = new TickerQueue.Ticker () {
  2269:       @Override protected void tick () {
  2270:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2271:           System.out.printf ("%08x fduMotorSleepSoonTicker%d.tick()\n", XEiJ.regPC0, abuNumber);
  2272:         }
  2273:         fduMotorStatus = FDU_MOTOR_SLEEPING;  //モータ停止中(ノットレディ)にする
  2274:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2275:           System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2276:         }
  2277:         if (!fduPrevented) {  //排出禁止設定でないとき
  2278:           allow ();  //排出を許可する
  2279:         }
  2280:         fduMotorInterrupt ();  //モータONまたはモータOFFから一定の時間が経ったとき
  2281:       }  //tick()
  2282:     };  //fduMotorSleepSoonTicker
  2283: 
  2284:     //fduMotorInterrupt ()
  2285:     //  モータONまたはモータOFFから一定の時間が経ったとき
  2286:     public void fduMotorInterrupt () {
  2287:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2288:         System.out.printf ("%08x fduMotorInterrupt%d()\n", XEiJ.regPC0, abuNumber);
  2289:       }
  2290:       if ((fdcStatus & 1 << abuNumber) != 0) {  //FDnビジー(DnB)が1のとき
  2291:         fduAttentionCheckWaiting = true;  //状態遷移確認フラグ(attentionCheckWaiting)を1にする
  2292:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2293:           System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", abuNumber, fduAttentionCheckWaiting);
  2294:         }
  2295:       } else {  //FDnビジー(DnB)が0のとき
  2296:         if ((fdcStatus & FDC_CB) != 0) {  //FDCビジー(CB)が1のとき
  2297:           fduAttentionCheckWaiting = true;  //状態遷移確認フラグ(attentionCheckWaiting)を1にする
  2298:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2299:             System.out.printf ("\tfduAttentionCheckWaiting%d=%b\n", abuNumber, fduAttentionCheckWaiting);
  2300:           }
  2301:         } else {  //FDCビジー(CB)が0のとき
  2302:           boolean ready = fduIsReady ();
  2303:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2304:             System.out.printf ("\tready%d=%b\n", abuNumber, ready);
  2305:           }
  2306:           if (ready != fduSavedReady) {  //レディ信号の状態(isReady)が保存されたレディ信号の状態(savedReady)と違うとき
  2307:             fduSavedReady = ready;  //レディ信号の状態(isReady)を保存する(isReady→RPYn)
  2308:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2309:               System.out.printf ("\tfduSavedReady%d=%b\n", abuNumber, fduSavedReady);
  2310:             }
  2311:             if (!fduAttentionInterruptRequest) {  //状態遷移割り込み要求(attentionInterruptRequest)が0のとき
  2312:               fduAttentionInterruptRequest = true;  //状態遷移割り込み要求(attentionInterruptRequest)を1にする
  2313:               if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2314:                 System.out.printf ("\tfduAttentionInterruptRequest%d=%b\n", abuNumber, fduAttentionInterruptRequest);
  2315:               }
  2316:               IOInterrupt.ioiFdcRise ();  //FDC割り込み要求(INT)を1にする
  2317:             }
  2318:           }
  2319:         }
  2320:       }
  2321:     }  //fduMotorInterrupt()
  2322: 
  2323: 
  2324:     //コンストラクタ
  2325: 
  2326:     //new FDUnit (number)
  2327:     //  コンストラクタ
  2328:     public FDUnit (int number) {
  2329:       super (number);
  2330: 
  2331:       fduMedia = null;
  2332:       fduImage = null;
  2333:       fduWritten = false;
  2334: 
  2335:       fduBlinking = false;
  2336:       fduPrevented = false;
  2337:       fduDoubleDensity = false;
  2338: 
  2339:       fduNCN = 0;
  2340:       fduPCN = 0;
  2341:       fduPHN = 0;
  2342:       fduPRN = 1;
  2343:       fduPNN = 3;
  2344:       fduEOT = 1;
  2345:       fduSTP = 1;
  2346:       fduSeekStepWaiting = false;
  2347:       fduSeekEndInterruptWaiting = false;
  2348:       fduSeekEndInterruptRequest = false;
  2349:       fduSeekResultStatus = FDC_ST0_IC;
  2350: 
  2351:       fduMotorStatus = FDU_MOTOR_SLEEPING;  //モータ停止中(ノットレディ)
  2352:       fduSavedReady = false;
  2353:       fduAttentionCheckWaiting = false;
  2354:       fduAttentionInterruptRequest = false;
  2355:     }
  2356: 
  2357:     //fduTini ()
  2358:     //  後始末
  2359:     //  イメージファイルに書き出す
  2360:     public void fduTini () {
  2361:       if (fduIsInserted ()) {
  2362:         fduFlush ();
  2363:       }
  2364:     }  //fduTini()
  2365: 
  2366:     //success = unit.fduFlush ()
  2367:     //  イメージファイルに書き出す
  2368:     public boolean fduFlush () {
  2369:       if (!abuConnected ||  //接続されていない
  2370:           !fduIsInserted () ||  //挿入されていない
  2371:           !fduWritten) {  //書き込みがない
  2372:         return true;
  2373:       }
  2374:       if (fduIsWriteProtected ()) {  //書き込みが許可されていない
  2375:         return false;
  2376:       }
  2377:       int dotIndex = abuPath.lastIndexOf ('.');
  2378:       String upperExt = dotIndex < 0 ? "" : abuPath.substring (dotIndex + 1).toUpperCase ();
  2379:       if (upperExt.equals ("DIM")) {  // *.dim
  2380:         byte[] dimImage = new byte[256 + fduMedia.fdmBytesPerDisk];
  2381:         int dimSize = fduMedia.fdmMakeDimImage (dimImage, fduImage);  // *.DIMのイメージを作る
  2382:         if (dimSize < 0) {
  2383:           // *.DIMから読み込んだがFORMAT.XなどでDIMにできない形式に変更されてしまった
  2384:           JOptionPane.showMessageDialog (null,
  2385:                                          Multilingual.mlnJapanese ?
  2386:                                          fduMedia.fdmName + " を *.DIM に変換できません" :
  2387:                                          fduMedia.fdmName + " cannot be converted to *.DIM");
  2388:           return false;
  2389:         }
  2390:         if (!XEiJ.rscPutFile (abuPath, dimImage, 0, dimSize)) {  //保存する
  2391:           return false;
  2392:         }
  2393:       } else {  // *.DIM以外
  2394:         if (!XEiJ.rscPutFile (abuPath, fduImage, 0, fduMedia.fdmBytesPerDisk)) {
  2395:           return false;
  2396:         }
  2397:       }
  2398:       fduWritten = false;
  2399:       return true;
  2400:     }  //fduFlush()
  2401: 
  2402:     //unit.connect (disconnectable)
  2403:     //  接続する
  2404:     @Override protected void connect (boolean disconnectable) {
  2405:       super.connect (disconnectable);
  2406:       fduImage = new byte[FDMedia.FDM_BUFFER_SIZE];
  2407:     }
  2408: 
  2409:     //unit.disconnect ()
  2410:     //  切り離す
  2411:     @Override protected void disconnect () {
  2412:       super.disconnect ();
  2413:       fduImage = null;
  2414:     }
  2415: 
  2416:     //unit.blink ()
  2417:     //  挿入されていないときLEDを点滅させる
  2418:     public void blink () {
  2419:       if (!abuConnected ||  //接続されていない
  2420:           fduBlinking) {  //既にLEDが点滅している
  2421:         return;
  2422:       }
  2423:       fduBlinking = true;
  2424:       //! 表示なし
  2425:     }
  2426: 
  2427:     //unit.darken ()
  2428:     //  挿入されていないときLEDを消す
  2429:     public void darken () {
  2430:       if (!abuConnected ||  //接続されていない
  2431:           !fduBlinking) {  //LEDが点滅していない
  2432:         return;
  2433:       }
  2434:       fduBlinking = false;
  2435:       //! 表示なし
  2436:     }
  2437: 
  2438:     //success = unit.eject ()
  2439:     //  イジェクトする
  2440:     @Override protected boolean eject () {
  2441:       if (!fduFlush ()) {  //イメージファイルに書き出す
  2442:         return false;
  2443:       }
  2444:       fduMotorStatus = FDU_MOTOR_SLEEPING;  //モータ停止中(ノットレディ)にする
  2445:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2446:         System.out.printf ("\tfduMotorStatus%d=%d\n", abuNumber, fduMotorStatus);
  2447:       }
  2448:       boolean inserted = fduIsInserted ();
  2449:       String path = abuPath;  //イジェクトされたイメージファイルのパス。super.eject()を呼び出す前にコピーすること
  2450:       if (!super.eject ()) {  //イジェクトする
  2451:         return false;
  2452:       }
  2453:       if (fduMedia != null) {  //挿入されていたとき
  2454:         fdcAddHistory (new File (path).getAbsoluteFile ());
  2455:         System.out.println (Multilingual.mlnJapanese ?
  2456:                             path + " を fd" + abuNumber + " から取り出しました" :
  2457:                             path + " was ejected from fd" + abuNumber);
  2458:       }
  2459:       fduMedia = null;
  2460:       fduPCN = 0;
  2461:       fduPHN = 0;
  2462:       fduPRN = 1;
  2463:       fduPNN = 3;
  2464:       //イジェクトされたときFDD割り込みを要求する
  2465:       if (inserted) {
  2466:         IOInterrupt.ioiFddFall ();
  2467:         IOInterrupt.ioiFddRise ();
  2468:       }
  2469:       return true;
  2470:     }
  2471: 
  2472:     //success = unit.open ()
  2473:     //  開くダイアログを開く
  2474:     @Override protected boolean open () {
  2475:       if (!super.open ()) {
  2476:         return false;
  2477:       }
  2478:       fdcOpenUnit = abuNumber;
  2479:       if (fdcOpenDialog == null) {
  2480:         fdcOpenDialog = new OpenDialog ();
  2481:         fdcOpenDialog.setReadOnly (Settings.sgsGetOnOff ("fdreadonly"));
  2482:         fdcOpenDialog.setReboot (Settings.sgsGetOnOff ("fdappreboot"));
  2483:         for (File[] files : fdcOpenHistory) {
  2484:           fdcOpenDialog.addHistory (files);
  2485:         }
  2486:         fdcOpenHistory.clear ();
  2487:       }
  2488:       fdcOpenDialog.rescanCurrentDirectory ();  //挿入されているファイルが変わると選択できるファイルも変わるのでリストを作り直す
  2489:       fdcOpenDialog.setVisible (true);
  2490:       return true;
  2491:     }  //unit.open()
  2492: 
  2493:     //success = unit.insert (path, writeProtected)
  2494:     //  挿入する
  2495:     public boolean insert (String path, boolean writeProtected) {
  2496:       if (fdcIsInsertedPath (path)) {  //既に挿入されている
  2497:         return false;
  2498:       }
  2499:       if (!super.insert (path, writeProtected)) {  //挿入できなかった
  2500:         return false;
  2501:       }
  2502:       if (fduMedia != null) {
  2503:         IOInterrupt.ioiFddFall ();
  2504:         IOInterrupt.ioiFddRise ();
  2505:       }
  2506:       return true;
  2507:     }  //unit.insert(String)
  2508: 
  2509:     //loaded = unit.load (path)
  2510:     //  読み込む
  2511:     //  挿入されていない状態で呼び出すこと
  2512:     @Override protected boolean load (String path) {
  2513:       fduMedia = FDMedia.fdmPathToMedia (path, fduImage);
  2514:       if (fduMedia == null) {  //読み込めない
  2515:         return false;
  2516:       }
  2517:       if (abuWriteProtected && !abuUnprotectable) {  //書き込みが許可されることはない
  2518:         fduMedia.fdmReviveFiles (fduImage);  //削除ファイルを復元する
  2519:       }
  2520:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2521:         System.out.println ("media = " + fduMedia.fdmName);
  2522:         System.out.println ("------------------------------------------------------------------------");
  2523:         fduMedia.fdmPrintInfo ();
  2524:         System.out.println ("------------------------------------------------------------------------");
  2525:       }
  2526:       fduWritten = false;
  2527:       fdcAddHistory (new File (path).getAbsoluteFile ());
  2528:       System.out.println (Multilingual.mlnJapanese ?
  2529:                           path + " を fd" + abuNumber + " に挿入しました" :
  2530:                           path + " was inserted in fd" + abuNumber);
  2531:       return true;
  2532:     }  //unit.load(String)
  2533: 
  2534: 
  2535:     //name = unit.getName ()
  2536:     //  pathからnameを作る
  2537:     public String getName () {
  2538:       return abuPath.substring (abuPath.lastIndexOf (File.separatorChar) + 1);
  2539:     }
  2540: 
  2541:     //d = unit.fduDriveStatus ()
  2542:     //  bit7  1=挿入
  2543:     //  bit6  1=誤挿入
  2544:     public int fduDriveStatus () {
  2545:       return fduIsInserted () ? 0x80 : 0;
  2546:     }
  2547: 
  2548:     //fduDriveControl (d)
  2549:     //  bit7  0=消灯する,1=点滅する
  2550:     //  bit6  0=排出を許可する,1=排出を禁止する
  2551:     //  bit5  0=排出しない,1=排出する
  2552:     public void fduDriveControl (int d) {
  2553:       if (d << 31 - 5 < 0) {  //排出する
  2554:         eject ();
  2555:       }
  2556:       if (d << 31 - 6 < 0) {  //排出を禁止する
  2557:         fduPrevented = true;
  2558:         prevent ();
  2559:       } else {  //排出を許可する
  2560:         fduPrevented = false;
  2561:         allow ();
  2562:       }
  2563:       if ((byte) d < 0) {  //点滅する
  2564:         fduBlinking = true;
  2565:         blink ();
  2566:       } else {  //消灯する
  2567:         fduBlinking = false;
  2568:         darken ();
  2569:       }
  2570:     }
  2571: 
  2572:     //fduCommandReadDiagnostic ()
  2573:     //  0x02  READ DIAGNOSTIC
  2574:     //    診断のための読み出し
  2575:     //    セクタ1から開始し、1トラック分のエラーを累積して正常終了する
  2576:     //  C-Phase
  2577:     //    [0]  bit6    MF    MFM Mode         0=FM,1=MFM
  2578:     //         bit4-0  CMD   Command          0x02=READ DIAGNOSTIC
  2579:     //    [1]  bit2    HD    Head Address     サイド
  2580:     //         bit1-0  US    Unit Select      ユニット
  2581:     //    [2]          C     Cylinder Number  シリンダ
  2582:     //    [3]          H     Head Number      サイド
  2583:     //    [4]          R     Record Number    無意味
  2584:     //    [5]          N     Record Length    セクタ長=128<<N
  2585:     //    [6]          EOT   End of Track     終了セクタ
  2586:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  2587:     //    [8]          DTL   Data Length      N==0&&DTL<128のとき128バイト読み出してCRCをチェックするがMPUにはDTLバイトだけ転送する
  2588:     //  E-Phase(FDC→MPU)
  2589:     //    データ
  2590:     //  R-Phase
  2591:     //    [0]          ST0   Status 0         リザルトステータス0
  2592:     //    [1]          ST1   Status 1         リザルトステータス1
  2593:     //    [2]          ST2   Status 2         リザルトステータス2
  2594:     //    [3]          C     Cylinder Number  シリンダ
  2595:     //    [4]          H     Head Number      サイド
  2596:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  2597:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  2598:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  2599:     //                                        エラーのときは異常発生セクタ
  2600:     //    [6]          N     Record Length    セクタ長=128<<N
  2601:     //  リザルトステータス
  2602:     //    正常終了  NT
  2603:     //    ノットレディ  AT|NR
  2604:     //    ID部
  2605:     //      IDAM非検出  AT|MA
  2606:     //      C不一致(終了せず)  NT|ND
  2607:     //      H不一致(終了せず)  NT|ND
  2608:     //      R不一致(終了せず)  NT|ND
  2609:     //      N不一致(終了せず)  NT|ND
  2610:     //      CRC不一致(終了せず)  NT|DE
  2611:     //    データ部
  2612:     //      DAM非検出  AT|MA|MD
  2613:     //      DDAM検出(終了せず)  NT|CM
  2614:     //      CRC不一致(終了せず)  NT|DE|DD
  2615:     //      オーバーラン  AT|OR
  2616:     //    最終セクタで未終了  AT|EN
  2617:     public void fduCommandReadDiagnostic () {
  2618:       //!!!
  2619:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2620:         System.out.printf ("%08x READ DIAGNOSTIC is not implemented\n", XEiJ.regPC0);
  2621:       }
  2622:       fdcCommandInvalid ();
  2623:     }
  2624: 
  2625:     //fduCommandSenseDeviceStatus ()
  2626:     //  0x04  SENSE DEVICE STATUS
  2627:     //    状態信号(ST3)の読み出し
  2628:     //  C-Phase
  2629:     //    [0]          CMD   Command          0x04=SENSE DEVICE STATUS
  2630:     //    [1]  bit2    HD    Head Address     サイド
  2631:     //         bit1-0  US    Unit Select      ユニット
  2632:     //  R-Phase
  2633:     //    [0]          ST3   Status 3         リザルトステータス3
  2634:     public void fduCommandSenseDeviceStatus () {
  2635:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  2636:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2637:         System.out.printf ("%08x fduCommandSenseDeviceStatus%d()\n", XEiJ.regPC0, abuNumber);
  2638:       }
  2639:       int st3 = ((fduIsWriteProtected () ? FDC_ST3_WP : 0) |  //Write-Protected
  2640:                  (fduIsReady () ? FDC_ST3_RY : 0) |  //Ready
  2641:                  (fduIsInserted () && fduIsReady () && fduPCN == 0 ? FDC_ST3_T0 : 0) |  //Track 0
  2642:                  //(fduIsInserted () ? fduMedia.fdmTwoSide : 0) |  //Two Side
  2643:                  (fduPHN & 1) << 2 |  //HD
  2644:                  abuNumber);  //US1,US0
  2645:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2646:         System.out.printf ("\t\tST3=0x%02x(FT=%d WP=%d RY=%d T0=%d TS=%d HD=%d US=%d)\n",
  2647:                            st3,
  2648:                            st3 >> 7,
  2649:                            st3 >> 6 & 1,
  2650:                            st3 >> 5 & 1,
  2651:                            st3 >> 4 & 1,
  2652:                            st3 >> 3 & 1,
  2653:                            st3 >> 2 & 1,
  2654:                            st3 & 3);
  2655:       }
  2656:       fdcResultBuffer[0] = (byte) st3;
  2657:       fdcRPhase (1);
  2658:     }
  2659: 
  2660:     //fduCommandWriteData ()
  2661:     //  0x05  WRITE DATA
  2662:     //    データ(DAM)の書き込み
  2663:     //  C-Phase
  2664:     //    [0]  bit7    MT    Multitrack       マルチトラック
  2665:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  2666:     //         bit4-0  CMD   Command          0x05=WRITE DATA
  2667:     //    [1]  bit2    HD    Head Address     サイド
  2668:     //         bit1-0  US    Unit Select      ユニット
  2669:     //    [2]          C     Cylinder Number  シリンダ
  2670:     //    [3]          H     Head Number      サイド
  2671:     //    [4]          R     Record Number    開始セクタ
  2672:     //    [5]          N     Record Length    セクタ長=128<<N
  2673:     //    [6]          EOT   End of Track     終了セクタ
  2674:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  2675:     //    [8]          DTL   Data Length      N==0&&DTL<128のときMPUからDTLバイトだけ受け取って128バイト書き込む
  2676:     //  E-Phase(MPU→FDC)
  2677:     //    データ
  2678:     //  R-Phase
  2679:     //    [0]          ST0   Status 0         リザルトステータス0
  2680:     //    [1]          ST1   Status 1         リザルトステータス1
  2681:     //    [2]          ST2   Status 2         リザルトステータス2
  2682:     //    [3]          C     Cylinder Number  シリンダ
  2683:     //    [4]          H     Head Number      サイド
  2684:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  2685:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  2686:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  2687:     //                                        エラーのときは異常発生セクタ
  2688:     //    [6]          N     Record Length    セクタ長=128<<N
  2689:     //  リザルトステータス
  2690:     //    正常終了  NT
  2691:     //    ノットレディ  AT|NR
  2692:     //    ライトプロテクト  AT|NW
  2693:     //    ID部
  2694:     //      IDAM非検出  AT|MA
  2695:     //      C不一致(!=0xff)  AT|ND|NC
  2696:     //      C不一致(==0xff)  AT|ND|BC
  2697:     //      H不一致  AT|ND
  2698:     //      R不一致  AT|ND
  2699:     //      N不一致  AT|ND
  2700:     //      CRC不一致  AT|DE
  2701:     //    データ部
  2702:     //      フォールト  AT|EC
  2703:     //      オーバーラン  AT|OR
  2704:     //    最終セクタで未終了  AT|EN
  2705:     public void fduCommandWriteData () {
  2706:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  2707:       int ncn = fdcCommandBuffer[2] & 255;  //NCN
  2708:       int phn = fdcCommandBuffer[3] & 255;  //H
  2709:       int prn = fdcCommandBuffer[4] & 255;  //R
  2710:       int pnn = fdcCommandBuffer[5] & 255;  //N
  2711:       int eot = fdcCommandBuffer[6] & 255;  //EOT
  2712:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2713:         System.out.printf ("%08x fduCommandWriteData%d(NCN=%d H=%d R=%d N=%d EOT=%d)\n",
  2714:                            XEiJ.regPC0, abuNumber, ncn, phn, prn, pnn, eot);
  2715:       }
  2716:       fduCommandNumber = fdcCommandNumber;
  2717:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2718:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  2719:       }
  2720:       if (!fduIsReady ()) {  //ノットレディ
  2721:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2722:         return;
  2723:       }
  2724:       if (fduIsWriteProtected ()) {  //ライトプロテクト
  2725:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_NW | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2726:         return;
  2727:       }
  2728:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  2729:       fduPHN = phn;
  2730:       fduPRN = prn;
  2731:       fduPNN = pnn;
  2732:       fduEOT = eot;
  2733:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  2734:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2735:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  2736:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  2737:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2738:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  2739:         System.out.printf ("\tfduEOT%d=%d\n", abuNumber, fduEOT);
  2740:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  2741:       }
  2742:       fdcStatus = (FDC_MPU_TO_FDC | FDC_CB |  //E-Phase(転送中)に移行する(RQM=0で待機する)
  2743:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2744:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2745:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2746:       }
  2747:       TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  2748:     }  //fduCommandWriteData()
  2749: 
  2750:     public void fduWriteDataEPhase () {
  2751:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2752:         System.out.printf ("%08x fduWriteDataEPhase%d()\n", XEiJ.regPC0, abuNumber);
  2753:       }
  2754:       if (fduPCN != fduNCN) {  //シークが中断されたとき
  2755:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2756:         return;
  2757:       }
  2758:       //E-Phase
  2759:       fduWritten = true;
  2760:       fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC | FDC_CB |
  2761:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2762:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2763:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2764:       }
  2765:       //転送
  2766:       int o = fduCalcOffset ();
  2767:       if (o < 0) {  //セクタが存在しない
  2768:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2769:         return;
  2770:       }
  2771:       fdcReadHandle = null;
  2772:       fdcWriteHandle = fduImage;
  2773:       fdcIndex = fdcStart = o;
  2774:       fdcLimit = o + (128 << fduPNN);
  2775:       HD63450.dmaFallPCL (0);  //DMA転送開始
  2776:     }  //fduWriteDataEPhase()
  2777: 
  2778:     //fduWriteDataEPhaseEnd ()
  2779:     public void fduWriteDataEPhaseEnd () {
  2780:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2781:         System.out.printf ("%08x fduWriteDataEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  2782:       }
  2783:       if (fduPRN == fduEOT) {  //終了
  2784:         HD63450.dmaRisePCL (0);  //DMA転送終了
  2785:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2786:         return;
  2787:       }
  2788:       //継続
  2789:       if (fduMedia == FDMedia.FDM_2HS && fduPCN == 0 && fduPHN == 0 && fduPRN == 1) {
  2790:         fduPRN = 11;
  2791:       } else {
  2792:         fduPRN++;
  2793:       }
  2794:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2795:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2796:       }
  2797:       int o = fduCalcOffset ();
  2798:       if (o < 0) {  //セクタが存在しない
  2799:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | (fduPHN << 2 | abuNumber << 24));
  2800:         return;
  2801:       }
  2802:       fdcIndex = fdcStart = o;
  2803:       fdcLimit = o + (128 << fduPNN);
  2804:     }  //fduWriteDataEPhaseEnd()
  2805: 
  2806:     //fduCommandReadData ()
  2807:     //  0x06  READ DATA
  2808:     //    データ(DAM)の読み出し
  2809:     //  C-Phase
  2810:     //    [0]  bit7    MT    Multitrack       マルチトラック
  2811:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  2812:     //         bit5    SK    Skip             DDAMをスキップ
  2813:     //         bit4-0  CMD   Command          0x06=READ DATA
  2814:     //    [1]  bit2    HD    Head Address     サイド
  2815:     //         bit1-0  US    Unit Select      ユニット
  2816:     //    [2]          C     Cylinder Number  シリンダ
  2817:     //    [3]          H     Head Number      サイド
  2818:     //    [4]          R     Record Number    開始セクタ
  2819:     //    [5]          N     Record Length    セクタ長=128<<N
  2820:     //    [6]          EOT   End of Track     終了セクタ
  2821:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  2822:     //    [8]          DTL   Data Length      N==0&&DTL<128のとき128バイト読み出してCRCをチェックするがMPUにはDTLバイトだけ転送する
  2823:     //  E-Phase(FDC→MPU)
  2824:     //    データ
  2825:     //  R-Phase
  2826:     //    [0]          ST0   Status 0         リザルトステータス0
  2827:     //    [1]          ST1   Status 1         リザルトステータス1
  2828:     //    [2]          ST2   Status 2         リザルトステータス2
  2829:     //    [3]          C     Cylinder Number  シリンダ
  2830:     //    [4]          H     Head Number      サイド
  2831:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  2832:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  2833:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  2834:     //                                        エラーのときは異常発生セクタ
  2835:     //    [6]          N     Record Length    セクタ長=128<<N
  2836:     //  リザルトステータス
  2837:     //    正常終了  NT
  2838:     //    ノットレディ  AT|NR
  2839:     //    ID部
  2840:     //      IDAM非検出  AT|MA
  2841:     //      C不一致(!=0xff)  AT|ND|NC
  2842:     //      C不一致(==0xff)  AT|ND|BC
  2843:     //      H不一致  AT|ND
  2844:     //      R不一致  AT|ND
  2845:     //      N不一致  AT|ND
  2846:     //      CRC不一致  AT|DE
  2847:     //    データ部
  2848:     //      DAM非検出  AT|MA|MD
  2849:     //      DDAM検出  NT|CM
  2850:     //      CRC不一致  AT|DE|DD
  2851:     //      オーバーラン  AT|OR
  2852:     //    最終セクタで未終了  AT|EN
  2853:     public void fduCommandReadData () {
  2854:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  2855:       int ncn = fdcCommandBuffer[2] & 255;  //NCN
  2856:       int phn = fdcCommandBuffer[3] & 255;  //H
  2857:       int prn = fdcCommandBuffer[4] & 255;  //R
  2858:       int pnn = fdcCommandBuffer[5] & 255;  //N
  2859:       int eot = fdcCommandBuffer[6] & 255;  //EOT
  2860:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2861:         System.out.printf ("%08x fduCommandReadData%d(NCN=%d H=%d R=%d N=%d EOT=%d)\n",
  2862:                            XEiJ.regPC0, abuNumber, ncn, phn, prn, pnn, eot);
  2863:       }
  2864:       fduCommandNumber = fdcCommandNumber;
  2865:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2866:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  2867:       }
  2868:       if (!fduIsReady ()) {  //ノットレディ
  2869:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2870:         return;
  2871:       }
  2872:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  2873:       fduPHN = phn;
  2874:       fduPRN = prn;
  2875:       fduPNN = pnn;
  2876:       fduEOT = eot;
  2877:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  2878:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2879:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  2880:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  2881:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2882:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  2883:         System.out.printf ("\tfduEOT%d=%d\n", abuNumber, fduEOT);
  2884:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  2885:       }
  2886:       fdcStatus = (FDC_MPU_TO_FDC | FDC_CB |  //E-Phase(転送中)に移行する(RQM=0で待機する)
  2887:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2888:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2889:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2890:       }
  2891:       fdcReadHandle = null;
  2892:       fdcWriteHandle = null;
  2893:       TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  2894:     }  //fduCommandReadData()
  2895: 
  2896:     public void fduReadDataEPhase () {
  2897:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2898:         System.out.printf ("%08x fduReadDataEPhase%d()\n", XEiJ.regPC0, abuNumber);
  2899:       }
  2900:       if (fduPCN != fduNCN) {  //シークが中断されたとき
  2901:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2902:         return;
  2903:       }
  2904:       //E-Phase
  2905:       fdcStatus = (FDC_RQM | FDC_FDC_TO_MPU | FDC_CB |
  2906:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  2907:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2908:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2909:       }
  2910:       //転送
  2911:       int o = fduCalcOffset ();
  2912:       if (o < 0) {  //セクタが存在しない
  2913:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2914:         return;
  2915:       }
  2916:       fdcReadHandle = fduImage;
  2917:       fdcWriteHandle = null;
  2918:       fdcIndex = fdcStart = o;
  2919:       fdcLimit = o + (128 << fduPNN);
  2920:       HD63450.dmaFallPCL (0);  //DMA転送開始
  2921:     }  //fduReadDataEPhase()
  2922: 
  2923:     //fduReadDataEPhaseEnd ()
  2924:     public void fduReadDataEPhaseEnd () {
  2925:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2926:         System.out.printf ("%08x fduReadDataEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  2927:       }
  2928:       if (fduPRN == fduEOT) {  //終了
  2929:         HD63450.dmaRisePCL (0);  //DMA転送終了
  2930:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2931:         return;
  2932:       }
  2933:       //継続
  2934:       if (fduMedia == FDMedia.FDM_2HS && fduPCN == 0 && fduPHN == 0 && fduPRN == 1) {
  2935:         fduPRN = 11;
  2936:       } else {
  2937:         fduPRN++;
  2938:       }
  2939:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2940:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  2941:       }
  2942:       int o = fduCalcOffset ();
  2943:       if (o < 0) {  //セクタが存在しない
  2944:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  2945:         return;
  2946:       }
  2947:       fdcIndex = fdcStart = o;
  2948:       fdcLimit = o + (128 << fduPNN);
  2949:     }  //fduReadDataEPhaseEnd()
  2950: 
  2951:     //fduCommandRecalibrate ()
  2952:     //  0x07  RECALIBRATE
  2953:     //    トラック0(一番外側)へのシーク
  2954:     //  C-Phase
  2955:     //    [0]          CMD   Command          0x07=RECALIBRATE
  2956:     //    [1]  bit1-0  US    Unit Select      ユニット
  2957:     //  リザルトステータス(SENSE INTERRUPT STATUSで引き取る)
  2958:     //    正常終了  NT|SE
  2959:     //    ノットレディ  AT|SE|NR
  2960:     //    トラック0非検出  AT|SE|EC
  2961:     public void fduCommandRecalibrate () {
  2962:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2963:         System.out.printf ("%08x fduCommandRecalibrate%d()\n", XEiJ.regPC0, abuNumber);
  2964:       }
  2965:       fduCommandNumber = fdcCommandNumber;
  2966:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2967:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  2968:       }
  2969:       fdcStatus |= 1 << abuNumber;  //FDnビジー(DnB)を1にする
  2970:       fduNCN = 0;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  2971:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  2972:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2973:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  2974:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  2975:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  2976:       }
  2977:       if (fdcEnforcedReady && !abuConnected) {  //強制レディ状態で接続されていないとき
  2978:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_EC;  //AT(Abnormal Terminate)+SE(Seek End)+EC(Equipment Check)のリザルトステータスを準備する
  2979:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2980:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  2981:         }
  2982:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  2983:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2984:       } else if (!fduIsReady ()) {  //ノットレディのとき
  2985:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_NR;  //AT(Abnormal Terminate)+SE(Seek End)+NR(Not Ready)のリザルトステータスを準備する
  2986:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2987:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  2988:         }
  2989:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  2990:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  2991:       } else {  //レディのとき
  2992:         fduSeekResultStatus = FDC_ST0_NT | FDC_ST0_SE;  //NT(Normal Terminate)+SE(Seek End)のリザルトステータスを準備する
  2993:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  2994:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  2995:         }
  2996:         TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  2997:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  2998:       }
  2999:     }  //fduCommandRecalibrate()
  3000: 
  3001:     //fduCommandWriteDeletedData ()
  3002:     //  0x09  WRITE DELETED DATA
  3003:     //    削除データ(DDAM)の書き込み
  3004:     //  C-Phase
  3005:     //    [0]  bit7    MT    Multitrack       マルチトラック
  3006:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  3007:     //         bit4-0  CMD   Command          0x09=WRITE DELETED DATA
  3008:     //    [1]  bit2    HD    Head Address     サイド
  3009:     //         bit1-0  US    Unit Select      ユニット
  3010:     //    [2]          C     Cylinder Number  シリンダ
  3011:     //    [3]          H     Head Number      サイド
  3012:     //    [4]          R     Record Number    開始セクタ
  3013:     //    [5]          N     Record Length    セクタ長=128<<N
  3014:     //    [6]          EOT   End of Track     終了セクタ
  3015:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  3016:     //    [8]          DTL   Data Length      N==0&&DTL<128のときMPUからDTLバイトだけ受け取って128バイト書き込む
  3017:     //  E-Phase(MPU→FDC)
  3018:     //    データ
  3019:     //  R-Phase
  3020:     //    [0]          ST0   Status 0         リザルトステータス0
  3021:     //    [1]          ST1   Status 1         リザルトステータス1
  3022:     //    [2]          ST2   Status 2         リザルトステータス2
  3023:     //    [3]          C     Cylinder Number  シリンダ
  3024:     //    [4]          H     Head Number      サイド
  3025:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  3026:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  3027:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  3028:     //                                        エラーのときは異常発生セクタ
  3029:     //    [6]          N     Record Length    セクタ長=128<<N
  3030:     //  リザルトステータス
  3031:     //    正常終了  NT
  3032:     //    ノットレディ  AT|NR
  3033:     //    ライトプロテクト  AT|NW
  3034:     //    ID部
  3035:     //      IDAM非検出  AT|MA
  3036:     //      C不一致(!=0xff)  AT|ND|NC
  3037:     //      C不一致(==0xff)  AT|ND|BC
  3038:     //      H不一致  AT|ND
  3039:     //      R不一致  AT|ND
  3040:     //      N不一致  AT|ND
  3041:     //      CRC不一致  AT|DE
  3042:     //    データ部
  3043:     //      フォールト  AT|EC
  3044:     //      オーバーラン  AT|OR
  3045:     //    最終セクタで未終了  AT|EN
  3046:     public void fduCommandWriteDeletedData () {
  3047:       //!!!
  3048:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3049:         System.out.printf ("%08x WRITE DELETED DATA is not implemented\n", XEiJ.regPC0);
  3050:       }
  3051:       fdcCommandInvalid ();
  3052:     }
  3053: 
  3054:     //fduCommandReadId ()
  3055:     //  0x0a  READ ID
  3056:     //    現在のシリンダの指定されたサイドにあるトラックでヘッドロード後最初に見つけたDEエラーやMAエラーのないセクタのIDを返す
  3057:     //    トラック上のどのセクタのIDが返るかは不定
  3058:     //  C-Phase
  3059:     //    [0]  bit6    MF    MFM Mode         0=FM,1=MFM
  3060:     //         bit4-0  CMD   Command          0x0a=READ ID
  3061:     //    [1]  bit2    HD    Head Address     サイド
  3062:     //         bit1-0  US    Unit Select      ユニット
  3063:     //  R-Phase
  3064:     //    [0]          ST0   Status 0         リザルトステータス0
  3065:     //    [1]          ST1   Status 1         リザルトステータス1
  3066:     //    [2]          ST2   Status 2         リザルトステータス2
  3067:     //    [3]          C     Cylinder Number  シリンダ
  3068:     //    [4]          H     Head Number      サイド
  3069:     //    [5]          R     Record Number    セクタ
  3070:     //    [6]          N     Record Length    セクタ長=128<<N
  3071:     //  リザルトステータス
  3072:     //    正常終了  NT
  3073:     //    ノットレディ  AT|NR
  3074:     //    ID部
  3075:     //      IDAM非検出  AT|MA  インデックスパルスを2回検出するまでにIDAMが見つからない
  3076:     //      CRC不一致  AT|ND  IDAMを見つけたがインデックスパルスを2回検出するまでにCRCエラーのないIDが見つからない
  3077:     public void fduCommandReadId () {
  3078:       int hd = fdcCommandBuffer[1] >> 2 & 1;  //HD
  3079:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3080:         System.out.printf ("%08x fduCommandReadId%d(HD=%d)\n", XEiJ.regPC0, abuNumber, hd);
  3081:       }
  3082:       fduCommandNumber = fdcCommandNumber;
  3083:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3084:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3085:       }
  3086:       fduPHN = hd;
  3087:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3088:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  3089:       }
  3090:       if (!fduIsReady ()) {  //ノットレディ
  3091:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3092:         return;
  3093:       }
  3094:       //セクタ番号
  3095:       //  常に1を返す方法だとREAD IDを繰り返してセクタ数を確認することができない
  3096:       if (fduMedia == FDMedia.FDM_2HS) {  //2HS
  3097:         if (fduPCN == 0 && fduPHN == 0) {  //最初のトラック
  3098:           if (fduPRN == 1) {  //最初のセクタ
  3099:             fduPRN = 11;  //2番目のセクタ
  3100:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3101:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3102:             }
  3103:           } else if (fduPRN == 18) {  //最後のセクタ
  3104:             fduPRN = 1;  //最初のセクタ
  3105:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3106:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3107:             }
  3108:           } else {  //その他のセクタ
  3109:             fduPRN++;  //次のセクタ
  3110:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3111:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3112:             }
  3113:           }
  3114:         } else {  //最初のトラック以外
  3115:           if (fduPRN == 18) {  //最後のセクタ
  3116:             fduPRN = 10;  //最初のセクタ
  3117:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3118:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3119:             }
  3120:           } else {  //その他のセクタ
  3121:             fduPRN++;  //次のセクタ
  3122:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3123:               System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3124:             }
  3125:           }
  3126:         }
  3127:       } else {  //2HS以外
  3128:         if (fduPRN == fduMedia.fdmSectorsPerTrack) {  //最後のセクタ
  3129:           fduPRN = 1;  //最初のセクタ
  3130:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3131:             System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3132:           }
  3133:         } else {  //その他のセクタ
  3134:           fduPRN++;  //次のセクタ
  3135:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3136:             System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3137:           }
  3138:         }
  3139:       }
  3140:       //サイド番号
  3141:       if (fduMedia == FDMedia.FDM_2HDE) {
  3142:         if (!(fduPCN == 0 && fduPHN == 0 && fduPRN == 1)) {  //最初のトラックの最初のセクタ以外
  3143:           fduPHN |= 128;
  3144:           if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3145:             System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  3146:           }
  3147:         }
  3148:       }
  3149:       //セクタスケール
  3150:       fduPNN = fduMedia.fdmSectorScale;
  3151:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3152:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  3153:       }
  3154:       int o = fduCalcOffset ();
  3155:       if (o < 0) {  //セクタが存在しない
  3156:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3157:         return;
  3158:       }
  3159:       fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3160:     }  //fduCommandReadId()
  3161: 
  3162:     //fduCommandReadDeletedData ()
  3163:     //  0x0c  READ DELETED DATA
  3164:     //    削除データ(DDAM)の読み出し
  3165:     //  C-Phase
  3166:     //    [0]  bit7    MT    Multitrack       マルチトラック
  3167:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  3168:     //         bit5    SK    Skip             DAMをスキップ
  3169:     //         bit4-0  CMD   Command          0x0c=READ DELETED DATA
  3170:     //    [1]  bit2    HD    Head Address     サイド
  3171:     //         bit1-0  US    Unit Select      ユニット
  3172:     //    [2]          C     Cylinder Number  シリンダ
  3173:     //    [3]          H     Head Number      サイド
  3174:     //    [4]          R     Record Number    開始セクタ
  3175:     //    [5]          N     Record Length    セクタ長=128<<N
  3176:     //    [6]          EOT   End of Track     終了セクタ
  3177:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  3178:     //    [8]          DTL   Data Length      N==0&&DTL<128のとき128バイト読み出してCRCをチェックしてMPUにDTLバイトだけ渡す
  3179:     //  E-Phase(FDC→MPU)
  3180:     //    データ
  3181:     //  R-Phase
  3182:     //    [0]          ST0   Status 0         リザルトステータス0
  3183:     //    [1]          ST1   Status 1         リザルトステータス1
  3184:     //    [2]          ST2   Status 2         リザルトステータス2
  3185:     //    [3]          C     Cylinder Number  シリンダ
  3186:     //    [4]          H     Head Number      サイド
  3187:     //    [5]          R     Record Number    正常終了のときは実行終了セクタの次のセクタ
  3188:     //                                          R=EOTまで転送するとRが1に戻り、MTでないときC、MTのときHとCに繰り上がる
  3189:     //                                          ただし、NT|CMでSKでないときは実行終了セクタ
  3190:     //                                        エラーのときは異常発生セクタ
  3191:     //    [6]          N     Record Length    セクタ長=128<<N
  3192:     //  リザルトステータス
  3193:     //    正常終了  NT
  3194:     //    ノットレディ  AT|NR
  3195:     //    ID部
  3196:     //      IDAM非検出  AT|MA
  3197:     //      C不一致(!=0xff)  AT|ND|NC
  3198:     //      C不一致(==0xff)  AT|ND|BC
  3199:     //      H不一致  AT|ND
  3200:     //      R不一致  AT|ND
  3201:     //      N不一致  AT|ND
  3202:     //      CRC不一致  AT|DE
  3203:     //    データ部
  3204:     //      DDAM非検出  AT|MA|MD
  3205:     //      DAM検出  NT|CM
  3206:     //      CRC不一致  AT|DE|DD
  3207:     //      オーバーラン  AT|OR
  3208:     //    最終セクタで未終了  AT|EN
  3209:     public void fduCommandReadDeletedData () {
  3210:       //!!!
  3211:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3212:         System.out.printf ("%08x READ DELETED DATA is not implemented\n", XEiJ.regPC0);
  3213:       }
  3214:       fdcCommandInvalid ();
  3215:     }
  3216: 
  3217:     //fduCommandWriteId ()
  3218:     //  0x0d  WRITE ID
  3219:     //    1トラックフォーマットする。別名Format Write
  3220:     //  C-Phase
  3221:     //    [0]  bit6    MF    MFM Mode         0=FM,1=MFM
  3222:     //         bit4-0  CMD   Command          0x0d=WRITE ID
  3223:     //    [1]  bit2    HD    Head Address     サイド
  3224:     //         bit1-0  US    Unit Select      ユニット
  3225:     //    [2]          N     Record Length    セクタ長=128<<N
  3226:     //    [3]          SC    Sector           1トラックあたりのセクタ数
  3227:     //    [4]          GPL   Gap Length       Gap3に書き込む長さ
  3228:     //    [5]          D     Data             データ部に書き込むデータ
  3229:     //  E-Phase
  3230:     //    1トラック分のID情報。4*SCバイト
  3231:     //    [0]          C     Cylinder Number  シリンダ
  3232:     //    [1]          H     Head Number      サイド
  3233:     //    [2]          R     Record Number    開始セクタ
  3234:     //    [3]          N     Record Length    セクタ長=128<<N
  3235:     //    これをSC回繰り返す
  3236:     //  R-Phase
  3237:     //    [0]          ST0   Status 0         リザルトステータス0
  3238:     //    [1]          ST1   Status 1         リザルトステータス1
  3239:     //    [2]          ST2   Status 2         リザルトステータス2
  3240:     //    [3]          C     Cylinder Number  無意味
  3241:     //    [4]          H     Head Number      無意味
  3242:     //    [5]          R     Record Number    無意味
  3243:     //    [6]          N     Record Length    セクタ長=128<<N
  3244:     //  リザルトステータス
  3245:     //    正常終了  NT
  3246:     //    ノットレディ  AT|NR
  3247:     //    ライトプロテクト  AT|NW
  3248:     //    フォールト  AT|EC
  3249:     //    オーバーラン  AT|OR
  3250:     public void fduCommandWriteId () {
  3251:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  3252:       fduCommandNumber = fdcCommandNumber;
  3253:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3254:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3255:       }
  3256:       if (!fduIsReady ()) {  //ノットレディ
  3257:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3258:           System.out.printf ("%08x unit=%d,is not ready\n", XEiJ.regPC0, abuNumber);
  3259:         }
  3260:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3261:         return;
  3262:       }
  3263:       if (fduIsWriteProtected ()) {  //書き込めない
  3264:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3265:           System.out.printf ("%08x unit=%d,is read-only\n", XEiJ.regPC0, abuNumber);
  3266:         }
  3267:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_NW | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3268:         return;
  3269:       }
  3270:       //E-Phase
  3271:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3272:         System.out.printf ("%08x FDC E-Phase\n", XEiJ.regPC0);
  3273:       }
  3274:       fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC | FDC_CB |
  3275:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  3276:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3277:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3278:       }
  3279:       fdcReadHandle = null;
  3280:       fdcWriteHandle = fdcTempBuffer;
  3281:       fdcIndex = fdcStart = 0;
  3282:       fdcLimit = (fdcCommandBuffer[3] & 255) << 2;
  3283:       HD63450.dmaFallPCL (0);  //DMA転送開始
  3284:     }  //fduCommandWriteId()
  3285: 
  3286:     //fduWriteIdEPhaseEnd ()
  3287:     public void fduWriteIdEPhaseEnd () {
  3288:       HD63450.dmaRisePCL (0);  //DMA転送終了
  3289:       //トラック0のフォーマットでメディアを判別する
  3290:       if (fdcTempBuffer[0] == 0 && fdcTempBuffer[1] == 0) {  //pcn1==0&&phn1==0。トラック0
  3291:         int prn1 = fdcTempBuffer[2] & 255;
  3292:         int pnn1 = fdcTempBuffer[3] & 255;
  3293:         int pcn2 = fdcTempBuffer[4] & 255;
  3294:         int phn2 = fdcTempBuffer[5] & 255;
  3295:         int prn2 = fdcTempBuffer[6] & 255;
  3296:         int pnn2 = fdcTempBuffer[7] & 255;
  3297:         int sectors = fdcLimit >> 2;  //1トラックあたりのセクタ数。fdcCommandBuffer[3]&255
  3298:         FDMedia media = null;
  3299:         if (phn2 == 0 && prn1 == 1 && prn2 == 2) {
  3300:           if (!fduDoubleDensity) {  //高密度
  3301:             if (pnn1 == 3 && pnn2 == 3) {  //1024バイト/セクタ
  3302:               if (sectors == 8) {  //8セクタ/トラック
  3303:                 media = FDMedia.FDM_2HD;
  3304:               }
  3305:             } else if (pnn1 == 2 && pnn2 == 2) {  //512バイト/セクタ
  3306:               if (sectors == 15) {  //15セクタ/トラック
  3307:                 media = FDMedia.FDM_2HC;
  3308:               } else if (sectors == 18) {  //18セクタ/トラック
  3309:                 media = FDMedia.FDM_2HQ;
  3310:               }
  3311:             }
  3312:           } else {  //倍密度
  3313:             if (pnn1 == 2 && pnn2 == 2) {  //512バイト/セクタ
  3314:               if (sectors == 8) {  //8セクタ/トラック
  3315:                 media = FDMedia.FDM_2DD8;
  3316:               } else if (sectors == 9) {  //9セクタ/トラック
  3317:                 media = FDMedia.FDM_2DD9;
  3318:               } else if (sectors == 10) {  //10セクタ/トラック
  3319:                 media = FDMedia.FDM_2DD10;
  3320:               }
  3321:             }
  3322:           }
  3323:         } else if (phn2 == 128 && prn1 == 1 && prn2 == 2) {  //2番目のセクタのサイド番号が128
  3324:           if (!fduDoubleDensity) {  //高密度
  3325:             if (pnn1 == 3 && pnn2 == 3) {  //1024バイト/セクタ
  3326:               if (sectors == 9) {  //9セクタ/トラック
  3327:                 media = FDMedia.FDM_2HDE;
  3328:               }
  3329:             }
  3330:           }
  3331:         } else if (phn2 == 0 && prn1 == 1 && prn2 == 11) {  //2番目のセクタのレコード番号が11
  3332:           if (!fduDoubleDensity) {  //高密度
  3333:             if (pnn1 == 3 && pnn2 == 3) {  //1024バイト/セクタ
  3334:               if (sectors == 9) {  //9セクタ/トラック
  3335:                 media = FDMedia.FDM_2HS;
  3336:               }
  3337:             }
  3338:           }
  3339:         }
  3340:         if (media != null) {
  3341:           fduMedia = media;
  3342:         } else {
  3343:           fduMedia = FDMedia.FDM_2HD;
  3344:         }
  3345:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3346:           System.out.println ("media = " + fduMedia.fdmName);
  3347:           System.out.println ("------------------------------------------------------------------------");
  3348:           fduMedia.fdmPrintInfo ();
  3349:           System.out.println ("------------------------------------------------------------------------");
  3350:         }
  3351:       }  //if トラック0
  3352:       for (int i = 0; i < fdcLimit; i += 4) {
  3353:         fduPCN = fdcTempBuffer[i    ] & 255;
  3354:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3355:           System.out.printf ("%08x unit=%d,fduPCN=%d\n", XEiJ.regPC0, abuNumber, fduPCN);
  3356:         }
  3357:         fduPHN = fdcTempBuffer[i + 1] & 255;
  3358:         fduPRN = fdcTempBuffer[i + 2] & 255;
  3359:         fduPNN = fdcTempBuffer[i + 3] & 255;
  3360:         int o = fduCalcOffset ();
  3361:         if (o < 0) {  //セクタが存在しない
  3362:           //FORMAT.Xが1トラック余分にフォーマットしようとするので、シリンダが上限+1のときはエラーにせず無視する
  3363:           if (0 < fduPCN) {
  3364:             fduPCN--;
  3365:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3366:               System.out.printf ("%08x unit=%d,fduPCN=%d\n", XEiJ.regPC0, abuNumber, fduPCN);
  3367:             }
  3368:             o = fduCalcOffset ();
  3369:             fduPCN++;
  3370:             if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3371:               System.out.printf ("%08x unit=%d,fduPCN=%d\n", XEiJ.regPC0, abuNumber, fduPCN);
  3372:             }
  3373:           }
  3374:           if (o < 0) {  //シリンダを1つ減らしたセクタも存在しない
  3375:             fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_EC | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3376:             return;
  3377:           }
  3378:         } else {  //セクタが存在する
  3379:           Arrays.fill (fduImage, o, o + (128 << fduPNN), fdcCommandBuffer[5]);  //初期化データで充填する
  3380:           fduWritten = true;
  3381:         }
  3382:       }  //for i
  3383:       fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3384:     }  //fduWriteIdEPhaseEnd()
  3385: 
  3386:     //fduCommandSeek ()
  3387:     //  0x0f  SEEK
  3388:     //    NCNがPCNと異なるときSPECIFYコマンドで指定されたStep Rate Time間隔でPCNをインクリメントまたはデクリメントすることを繰り返す
  3389:     //    NCNが範囲外でもエラーを出さない
  3390:     //  C-Phase
  3391:     //    [0]          CMD   Command          0x0f=SEEK
  3392:     //    [1]  bit1-0  US    Unit Select      ユニット
  3393:     //    [2]          NCN   New Cylinder Number
  3394:     //  E-Phase
  3395:     //    終了待ち
  3396:     //  リザルトステータス(SENSE INTERRUPT STATUSで引き取る)
  3397:     //    正常終了  NT|SE
  3398:     //    ノットレディ  AT|SE|NR
  3399:     public void fduCommandSeek () {
  3400:       int ncn = fdcCommandBuffer[2] & 255;  //New Cylinder Number
  3401:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3402:         System.out.printf ("%08x fduCommandSeek%d(NCN=%d)\n", XEiJ.regPC0, abuNumber, ncn);
  3403:       }
  3404:       fduCommandNumber = fdcCommandNumber;
  3405:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3406:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3407:       }
  3408:       fdcStatus |= 1 << abuNumber;  //FDnビジー(DnB)を1にする
  3409:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  3410:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  3411:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3412:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3413:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  3414:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  3415:       }
  3416:       if (fdcEnforcedReady && !abuConnected) {  //強制レディ状態で接続されていないとき
  3417:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_EC;  //AT(Abnormal Terminate)+SE(Seek End)+EC(Equipment Check)のリザルトステータスを準備する
  3418:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3419:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  3420:         }
  3421:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  3422:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  3423:       } else if (!fduIsReady ()) {  //ノットレディのとき
  3424:         fduSeekResultStatus = FDC_ST0_AT | FDC_ST0_SE | FDC_ST0_NR;  //AT(Abnormal Terminate)+SE(Seek End)+NR(Not Ready)のリザルトステータスを準備する
  3425:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3426:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  3427:         }
  3428:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  3429:         fduSeekEnd ();  //SEEK/RECALIBRATEコマンドのE-Phase(シーク中)が終了したとき
  3430:       } else {  //レディのとき
  3431:         fduSeekResultStatus = FDC_ST0_NT | FDC_ST0_SE;  //NT(Normal Terminate)+SE(Seek End)のリザルトステータスを準備する
  3432:         if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3433:           System.out.printf ("\tfduSeekResultStatus=%s\n", fdcResultStatusToString (fduSeekResultStatus));
  3434:         }
  3435:         TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  3436:         fdcCPhase ();  //E-Phase(シーク中)に移行する
  3437:       }
  3438:     }  //fduCommandSeek()
  3439: 
  3440:     //fduCommandScan ()
  3441:     //  0x11  SCAN EQUAL
  3442:     //  0x19  SCAN LOW OR EQUAL
  3443:     //  0x1d  SCAN HIGH OR EQUAL
  3444:     //    記録データと目的データを1バイトずつ8bit符号なし数値とみなして比較する(ベリファイ)
  3445:     //  C-Phase
  3446:     //    [0]  bit7    MT    Multitrack       マルチトラック
  3447:     //         bit6    MF    MFM Mode         0=FM,1=MFM
  3448:     //         bit5    SK    Skip             DDAMをスキップ
  3449:     //         bit4-0  CMD   Command          0x11=SCAN EQUAL,0x19=SCAN LOW OR EQUAL,0x1d=SCAN HIGH OR EQUAL
  3450:     //    [1]  bit2    HD    Head Address     サイド
  3451:     //         bit1-0  US    Unit Select      ユニット
  3452:     //    [2]          C     Cylinder Number  シリンダ
  3453:     //    [3]          H     Head Number      サイド
  3454:     //    [4]          R     Record Number    開始セクタ
  3455:     //    [5]          N     Record Length    セクタ長=128<<N
  3456:     //    [6]          EOT   End of Track     終了セクタ。STP==2のときはRとEOTの奇偶が同じであること
  3457:     //    [7]          GSL   Gap Skip Length  マルチセクタのときGap3の不連続部分を読み飛ばす長さ
  3458:     //    [8]          STP   Step             条件不成立のとき1=次のセクタに進む(R++),2=次の次のセクタに進む(R+=2)
  3459:     //  E-Phase(MPU→FDC)
  3460:     //    (EOT-R)/STP+1セクタ分の目的データ
  3461:     //    条件
  3462:     //      EQUAL          目的データ==0xff||記録データ==目的データ
  3463:     //      LOW OR EQUAL   目的データ==0xff||記録データ<=目的データ
  3464:     //      HIGH OR EQUAL  目的データ==0xff||記録データ>=目的データ
  3465:     //    1セクタ分のデータがすべて条件成立したとき
  3466:     //      条件成立で終了
  3467:     //    1セクタの中に条件成立しないデータがあったとき
  3468:     //      終了セクタではないとき
  3469:     //        STPに従って次または次の次のセクタに進む
  3470:     //      終了セクタまでに1セクタ分のデータがすべて条件成立するセクタが見つからなかったとき
  3471:     //        条件不成立で終了
  3472:     //  R-Phase
  3473:     //    [0]          ST0   Status 0         リザルトステータス0
  3474:     //    [1]          ST1   Status 1         リザルトステータス1
  3475:     //    [2]          ST2   Status 2         リザルトステータス2
  3476:     //    [3]          C     Cylinder Number  シリンダ
  3477:     //    [4]          H     Head Number      サイド
  3478:     //    [5]          R     Record Number    最後に比較したセクタ
  3479:     //    [6]          N     Record Length    セクタ長=128<<N
  3480:     //  リザルトステータス
  3481:     //    条件成立  EQUAL          NT|SH
  3482:     //              LOW OR EQUAL   NT
  3483:     //              HIGH OR EQUAL  NT
  3484:     //    条件不成立  NT|SN
  3485:     //    ノットレディ  AT|NR
  3486:     //    ID部
  3487:     //      IDAM非検出  AT|MA
  3488:     //      C不一致(!=0xff)  AT|ND|NC
  3489:     //      C不一致(==0xff)  AT|ND|BC
  3490:     //      H不一致  AT|ND
  3491:     //      R不一致  AT|ND
  3492:     //      N不一致  AT|ND
  3493:     //      CRC不一致  AT|DE
  3494:     //    データ部
  3495:     //      DAM非検出  AT|MA|MD
  3496:     //      DDAM検出  NT|CM
  3497:     //      CRC不一致  AT|DE|DD
  3498:     //      オーバーラン  AT|OR
  3499:     public void fduCommandScan () {
  3500:       //fduPHN = fdcCommandBuffer[1] >> 2 & 1;  //HD
  3501:       int ncn = fdcCommandBuffer[2] & 255;  //NCN
  3502:       int phn = fdcCommandBuffer[3] & 255;  //H
  3503:       int prn = fdcCommandBuffer[4] & 255;  //R
  3504:       int pnn = fdcCommandBuffer[5] & 255;  //N
  3505:       int eot = fdcCommandBuffer[6] & 255;  //EOT
  3506:       int stp = fdcCommandBuffer[8] & 3;  //STP
  3507:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3508:         System.out.printf ("%08x fduCommandScan%d(NCN=%d H=%d R=%d N=%d EOT=%d STP=%d)\n",
  3509:                            XEiJ.regPC0, abuNumber, ncn, phn, prn, pnn, eot, stp);
  3510:       }
  3511:       fduCommandNumber = fdcCommandNumber;
  3512:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3513:         System.out.printf ("\tfduCommandNumber%d=0x%02x\n", abuNumber, fduCommandNumber);
  3514:       }
  3515:       if (!fduIsReady ()) {  //ノットレディ
  3516:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST0_NR | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3517:         return;
  3518:       }
  3519:       fduNCN = ncn;  //目標シリンダ番号(NCNn)を設定する(既に設定されているときは上書きする)
  3520:       fduPHN = phn;
  3521:       fduPRN = prn;
  3522:       fduPNN = pnn;
  3523:       fduEOT = eot;
  3524:       fduSTP = stp;
  3525:       fduSRC = 16 - fdcSRT;  //ステップレートカウンタ(SRC)を16-SRTにする
  3526:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3527:         System.out.printf ("\tfduNCN%d=%d\n", abuNumber, fduNCN);
  3528:         System.out.printf ("\tfduPHN%d=%d\n", abuNumber, fduPHN);
  3529:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3530:         System.out.printf ("\tfduPNN%d=%d\n", abuNumber, fduPNN);
  3531:         System.out.printf ("\tfduEOT%d=%d\n", abuNumber, fduEOT);
  3532:         System.out.printf ("\tfduSTP%d=%d\n", abuNumber, fduSTP);
  3533:         System.out.printf ("\tfduSRC%d=%d\n", abuNumber, fduSRC);
  3534:       }
  3535:       fdcStatus = (FDC_MPU_TO_FDC | FDC_CB |  //E-Phase(転送中)に移行する(RQM=0で待機する)
  3536:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  3537:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3538:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3539:       }
  3540:       fdcReadHandle = null;
  3541:       fdcWriteHandle = null;
  3542:       TickerQueue.tkqAdd (fduSeekTicker, XEiJ.mpuClockTime + FDU_SEEK_INTERVAL);  //シークティッカーを(あれば取り消してから)1ms後に予約する
  3543:     }  //fduCommandScan()
  3544: 
  3545:     public void fduScanEPhase () {
  3546:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3547:         System.out.printf ("%08x fduScanEPhase%d()\n", XEiJ.regPC0, abuNumber);
  3548:       }
  3549:       //E-Phase
  3550:       fdcStatus = (FDC_RQM | FDC_MPU_TO_FDC | FDC_CB |
  3551:                    (fdcStatus & (FDC_D3B | FDC_D2B | FDC_D1B | FDC_D0B)));
  3552:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3553:         System.out.printf ("\tfdcStatus=%s\n", fdcStatusToString (fdcStatus));
  3554:       }
  3555:       //転送
  3556:       fdcReadHandle = null;
  3557:       fdcWriteHandle = fdcTempBuffer;
  3558:       fdcIndex = fdcStart = 0;
  3559:       fdcLimit = 128 << fduPNN;
  3560:       HD63450.dmaFallPCL (0);  //DMA転送開始
  3561:     }  //fduScanEPhase()
  3562: 
  3563:     //fduScanEqualEPhaseEnd ()
  3564:     public void fduScanEqualEPhaseEnd () {
  3565:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3566:         System.out.printf ("%08x fduScanEqualEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  3567:       }
  3568:       int o = fduCalcOffset ();
  3569:       if (o < 0) {  //セクタが存在しない
  3570:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3571:         return;
  3572:       }
  3573:       //比較
  3574:     scan:
  3575:       {
  3576:         int l = 128 << fduPNN;
  3577:         for (int i = 0; i < l; i++) {
  3578:           int d = fdcTempBuffer[i] & 255;
  3579:           if (d != 0xff && (fduImage[o + i] & 255) != d) {
  3580:             break scan;
  3581:           }
  3582:         }
  3583:         //条件成立
  3584:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3585:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SH | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3586:         return;
  3587:       }
  3588:       if (fduPRN == fduEOT) {  //条件不成立
  3589:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3590:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SN | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3591:         return;
  3592:       }
  3593:       //継続
  3594:       fduPRN += fduSTP;
  3595:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3596:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3597:       }
  3598:       fdcIndex = fdcStart = 0;
  3599:       //fdcLimit = 128 << fduPNN;
  3600:     }  //fduScanEqualEPhaseEnd()
  3601: 
  3602:     //fduScanLowOrEqualEPhaseEnd ()
  3603:     public void fduScanLowOrEqualEPhaseEnd () {
  3604:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3605:         System.out.printf ("%08x fduScanLowOrEqualEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  3606:       }
  3607:       int o = fduCalcOffset ();
  3608:       if (o < 0) {  //セクタが存在しない
  3609:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3610:         return;
  3611:       }
  3612:     scan:
  3613:       {
  3614:         int l = 128 << fduPNN;
  3615:         for (int i = 0; i < l; i++) {
  3616:           int d = fdcTempBuffer[i] & 255;
  3617:           if (d != 0xff && (fduImage[o + i] & 255) > d) {
  3618:             break scan;
  3619:           }
  3620:         }
  3621:         //条件成立
  3622:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3623:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3624:         return;
  3625:       }
  3626:       if (fduPRN == fduEOT) {  //条件不成立
  3627:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3628:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SN | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3629:         return;
  3630:       }
  3631:       //継続
  3632:       fduPRN += fduSTP;
  3633:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3634:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3635:       }
  3636:       fdcIndex = fdcStart = 0;
  3637:       fdcLimit = 128 << fduPNN;
  3638:     }  //fduScanLowOrEqualEPhaseEnd()
  3639: 
  3640:     //fduScanHighOrEqualEPhaseEnd ()
  3641:     public void fduScanHighOrEqualEPhaseEnd () {
  3642:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3643:         System.out.printf ("%08x fduScanHighOrEqualEPhaseEnd%d()\n", XEiJ.regPC0, abuNumber);
  3644:       }
  3645:       int o = fduCalcOffset ();
  3646:       if (o < 0) {  //セクタが存在しない
  3647:         fduEPhaseEnd (FDC_ST0_AT | FDC_ST1_MA | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3648:         return;
  3649:       }
  3650:     scan:
  3651:       {
  3652:         int l = 128 << fduPNN;
  3653:         for (int i = 0; i < l; i++) {
  3654:           int d = fdcTempBuffer[i] & 255;
  3655:           if (d != 0xff && (fduImage[o + i] & 255) < d) {
  3656:             break scan;
  3657:           }
  3658:         }
  3659:         //条件成立
  3660:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3661:         fduEPhaseEnd (FDC_ST0_NT | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3662:         return;
  3663:       }
  3664:       if (fduPRN == fduEOT) {  //条件不成立
  3665:         HD63450.dmaRisePCL (0);  //DMA転送終了
  3666:         fduEPhaseEnd (FDC_ST0_NT | FDC_ST2_SN | ((fduPHN & 1) << 2 | abuNumber) << 24);
  3667:         return;
  3668:       }
  3669:       //継続
  3670:       fduPRN += fduSTP;
  3671:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3672:         System.out.printf ("\tfduPRN%d=%d\n", abuNumber, fduPRN);
  3673:       }
  3674:       fdcIndex = fdcStart = 0;
  3675:       fdcLimit = 128 << fduPNN;
  3676:     }  //fduScanHighOrEqualEPhaseEnd()
  3677: 
  3678:     //offset = fduCalcOffset ()
  3679:     //  指定されたセクタのオフセットを計算する。-1=範囲外
  3680:     public int fduCalcOffset () {
  3681:       int o = (fduMedia == null ? -1 :
  3682:                fduDoubleDensity == fduMedia.fdmDoubleDensity &&
  3683:                fduPNN == fduMedia.fdmSectorScale &&
  3684:                fduPCN < fduMedia.fdmCylindersPerDisk &&
  3685:                (fduMedia == FDMedia.FDM_2HDE ?
  3686:                 fduPHN == (fduPCN == 0 && fduPRN == 1 ? 0 : 128) || fduPHN == 129 :
  3687:                 fduPHN < fduMedia.fdmTracksPerCylinder) &&
  3688:                (fduMedia == FDMedia.FDM_2HS ?
  3689:                 fduPCN == 0 && fduPHN == 0 ? fduPRN == 1 || (11 <= fduPRN && fduPRN <= 18) : 10 <= fduPRN && fduPRN <= 18 :
  3690:                 1 <= fduPRN && fduPRN <= fduMedia.fdmSectorsPerTrack) ?
  3691:                fduMedia.fdmBytesPerSector * (
  3692:                  (fduPRN <= fduMedia.fdmSectorsPerTrack ? fduPRN : fduPRN - fduMedia.fdmSectorsPerTrack) - 1 +
  3693:                  fduMedia.fdmSectorsPerTrack * (
  3694:                    ((fduPHN & 1) + fduMedia.fdmTracksPerCylinder * fduPCN))) :
  3695:                -1);
  3696:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3697:         System.out.printf ("\tfduCalcOffset(C=%d H=%d R=%d N=%d)=%d\n", fduPCN, fduPHN, fduPRN, fduPNN, o);
  3698:       }
  3699:       return o;
  3700:     }  //fduCalcOffset()
  3701: 
  3702:     //fduEPhaseEnd (status)
  3703:     //  E-Phaseを終了してR-Phaseに移行する
  3704:     public void fduEPhaseEnd (int status) {
  3705:       if (FDC_DEBUG_TRACE && fdcDebugLogOn) {
  3706:         System.out.printf ("%08x fduEPhaseEnd%d(%s)\n", XEiJ.regPC0, abuNumber, fdcResultStatusToString (status));
  3707:       }
  3708:       //READ DATAやWRITE DATAなどの転送コマンドのE-Phase(転送中)が終了したとき
  3709:       //リザルトステータス(ST0,ST1,ST2,C,H,R,N)を作る
  3710:       fdcResultBuffer[0] = (byte) (status >> 24);  //ST0
  3711:       fdcResultBuffer[1] = (byte) (status >> 16);  //ST1
  3712:       fdcResultBuffer[2] = (byte) (status >> 8);  //ST2
  3713:       fdcResultBuffer[3] = (byte) fduPCN;  //C
  3714:       fdcResultBuffer[4] = (byte) fduPHN;  //H
  3715:       fdcResultBuffer[5] = (byte) fduPRN;  //R
  3716:       fdcResultBuffer[6] = (byte) fduPNN;  //N
  3717:       //R-Phase(RQM=1,DIO=1(FDC→MPU))に移行する
  3718:       fdcRPhase (7);
  3719:       //FDC割り込み要求(INT)を1にする
  3720:       IOInterrupt.ioiFdcRise ();
  3721:     }  //fduEPhaseEnd(int)
  3722: 
  3723: 
  3724:   }  //class FDUnit
  3725: 
  3726: 
  3727: 
  3728: }  //class FDC
  3729: 
  3730: 
  3731: