xeij/HDC.java
//========================================================================================
//  HDC.java
//    en:SASI hard disk controller
//    ja:SASIハードディスクコントローラ
//  Copyright (C) 2003-2026 Makoto Kamada
//
//  This file is part of the XEiJ (X68000 Emulator in Java).
//  You can use, modify and redistribute the XEiJ if the conditions are met.
//  Read the XEiJ License for more details.
//  https://stdkmd.net/xeij/
//========================================================================================

//----------------------------------------------------------------------------------------
//  0x00e96020  HDC SASI HDコントローラ
//  0x00e96020  SPC 内蔵SCSIプロトコルコントローラ
//----------------------------------------------------------------------------------------

package xeij;

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
import java.io.*;  //BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter,File,FileInputStream,FileNotFoundException,FileReader,InputStream,InputStreamReader,IOException,OutputStreamWriter,RandomAccessFile
import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap
import java.util.zip.*;  //CRC32,Deflater,GZIPInputStream,GZIPOutputStream,ZipEntry,ZipInputStream
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

public class HDC {

  public static final boolean HDC_DEBUG_TRACE = false;
  public static final boolean HDC_DEBUG_COMMAND = false;
  public static final boolean HDC_DEBUG_UNIMPLEMENTED_COMMAND = true;

  public static final int HDC_BASE           = 0x00e96000;
  public static final int HDC_DATA_PORT      = 0x00e96001;  //データポート
  public static final int HDC_STATUS_PORT    = 0x00e96003;  //ステータスポート
  public static final int HDC_RESET_PORT     = 0x00e96005;  //リセットポート
  public static final int HDC_SELECTION_PORT = 0x00e96007;  //セレクションポート

  public static final int HDC_STATUS_MESSAGE = 0b10000;
  public static final int HDC_STATUS_C_D     = 0b01000;
  public static final int HDC_STATUS_COMMAND = 0b01000;
  public static final int HDC_STATUS_DATA    = 0b00000;
  public static final int HDC_STATUS_I_O     = 0b00100;
  public static final int HDC_STATUS_OUTPUT  = 0b00000;
  public static final int HDC_STATUS_INPUT   = 0b00100;
  public static final int HDC_STATUS_BUSY    = 0b00010;
  public static final int HDC_STATUS_FREE    = 0b00000;
  public static final int HDC_STATUS_REQUEST = 0b00001;

  public static final String[] HDC_COMMAND_NAME = (
    "TestDriveReady"                + "," +  //0x00
    "Recalibrate"                   + "," +  //0x01
    ""                              + "," +  //0x02
    "RequestSenseStatus"            + "," +  //0x03
    "FormatDrive"                   + "," +  //0x04
    "CheckTrackFormat"              + "," +  //0x05
    "FormatBlock"                   + "," +  //0x06
    "BadTrackFormat"                + "," +  //0x07
    "Read"                          + "," +  //0x08
    ""                              + "," +  //0x09
    "Write"                         + "," +  //0x0a
    "Seek"                          + "," +  //0x0b
    "InitializeDriveCharacteristic" + "," +  //0x0c
    "LastCorrectedBurstLength"      + "," +  //0x0d
    "AssignTrack"                   + "," +  //0x0e
    "WriteSectorBuffer"             + "," +  //0x0f
    "ReadSectorBuffer"              + "," +  //0x10
    //123456789abcdef0123456789abcdef
    ",,,,,,,,,,,,,,,"                     +  //0x11..0x1f
    ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"    +  //0x20..0x3f
    ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"    +  //0x40..0x5f
    ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"    +  //0x60..0x7f
    ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"    +  //0x80..0x9f
    ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"    +  //0xa0..0xbf
    ",,"                                  +  //0xc0..0xc1
    "AssignDrive"                   + "," +  //0xc2
    ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,"       +  //0xc3..0xdf
    ",,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,"        //0xe0..0xff
    ).split (",", 256);  //splitで末尾の空要素が削除されるのでHDC_COMMAND_NAME[cmd]が存在しないとき""とみなす処理が必要

  //転送開始遅延
  //  WRITEコマンドを実行するには、コマンドとデータの両方をデータポートに書き込む。
  //  コマンドをMPUで、データをDMACで書き込むには、その間にDMACを始動しなければならない。
  //  コマンドの6バイト目を受け取った瞬間にデータの1バイト目の外部転送要求を出してしまうと、
  //  DMACがREQの立ち下がりを見逃してしまうので、少し間隔をあける必要がある。
  public static final long HDC_DELAY_TIME = XEiJ.TMR_FREQ * 100 / 1000000;  //100us
  public static final TickerQueue.Ticker hdcDelayTicker = new TickerQueue.Ticker () {
    @Override protected void tick () {
      HD63450.dmaFallREQ (1);
    }
  };


  //メニュー
  public static JMenu hdcMenu;

  //ファイルフィルタ
  public static javax.swing.filechooser.FileFilter hdcFileFilter;  //java.io.FileFilterと紛らわしい

  //開くダイアログ
  public static OpenDialog hdcOpenDialog;  //開くダイアログ。null=作る前
  public static int hdcOpenUnit;  //開くユニットの番号
  public static ArrayList<File[]> hdcOpenHistory;  //作る前に追加されたヒストリ

  //フォーマットダイアログ
  public static JDialog hdcFormatDialog;  //ダイアログ
  public static JFileChooser2 hdcFormatFileChooser;  //ファイルチューザー
  public static HDMedia hdcFormatMedia;  //フォーマットするメディアの種類
  public static boolean hdcFormatCopySystemFiles;  //true=システムファイルを転送する

  //ユニット
  public static final HDUnit[] hdcUnitArray = new HDUnit[16];  //ユニットの配列
  public static int hdcHDMax;  //現在挿入されているユニットのユニット番号の最大値+1。0~16。[0x00ed005a].b=SPC.spcSCSIINOn?0:hdcHDMax

  public static int hdcSelectedID;  //セレクションフェーズで選択されたID。0~7=有効,-1=選択されていない
  public static int hdcSelectedLUN;  //コマンドフェーズで選択されたLUN。0~1=有効,-1=選択されていない
  public static HDUnit hdcTargetUnit;  //バスを占有しているターゲットのユニット。hdcUnitArray[hdcSelectedID<<1|hdcSelectedLUN],null=フリー
  public static int hdcBusStatus;  //バスステータス

  public static byte[] hdcReadHandle;  //転送(ターゲット→イニシエータ)の対象の配列。hdcStatusBuffer,hdcMessageBuffer,hdcSenseBuffer,hduImage
  public static byte[] hdcWriteHandle;  //転送(イニシエータ→ターゲット)の対象の配列。hdcCommandBuffer,hduImage
  public static int hdcIndex;  //次に転送する位置
  public static int hdcLimit;  //転送を終了する位置
  public static final byte[] hdcCommandBuffer = new byte[6];  //コマンドバッファ
  public static final byte[] hdcStatusBuffer = new byte[1];  //ステータスバッファ
  public static final byte[] hdcMessageBuffer = new byte[1];  //メッセージバッファ
  public static final byte[] hdcSenseBuffer = new byte[4];  //センスバッファ
  public static final byte[] hdcAssignDriveBuffer = new byte[10];  //ドライブパラメータバッファ

  public static int hdcLastFormatBlodkEnd;  //最後に実行したFormatBlockコマンドの終了位置(バイト)。-1=最後に実行したコマンドはFormatBlockではない

  public static JCheckBoxMenuItem hdcSASIMenuItem;

  //hdcInit ()
  //  HDCを初期化する
  public static void hdcInit () {

    //ファイルフィルタ
    //  SASIハードディスクのイメージファイルかどうかを調べる
    //  ファイルチューザーとドロップターゲットで使う
    hdcFileFilter = new javax.swing.filechooser.FileFilter () {  //java.io.FileFilterと紛らわしい
      @Override public boolean accept (File file) {
        if (file.isDirectory ()) {  //ディレクトリがある
          return true;
        }
        if (!file.isFile ()) {  //ファイルがない
          return false;
        }
        String path = file.getPath ();
        if (hdcIsInserted (path)) {  //既に挿入されている
          return false;
        }
        long longLength = file.length ();
        for (HDMedia media : HDMedia.HDM_ARRAY) {
          if (media.humDiskEndByte == longLength) {  //ファイルサイズが一致
            return true;
          }
        }
        return false;
      }
      @Override public String getDescription () {
        return (Multilingual.mlnJapanese ?
                "SASI ハードディスクのイメージファイル (*.HDF)" :
                "SASI hard disk image files (*.HDF)");
      }
    };

    //開くダイアログ
    hdcOpenDialog = null;
    hdcOpenUnit = 0;
    hdcOpenHistory = new ArrayList<File[]> ();
    for (int i = JFileChooser2.MAXIMUM_HISTORY_COUNT - 1; 0 <= i; i--) {
      hdcAddHistory (JFileChooser2.pathsToFiles (Settings.sgsGetString ("sahistory" + i)));
    }

    //ユニット
    //  ダイアログが書き込み禁止でもパラメータは:Rを付けなければ書き込み禁止にしない
    //hdcUnitArray = new HDUnit[16];
    hdcHDMax = 0;
    for (int u = 0; u < 16; u++) {
      HDUnit unit = hdcUnitArray[u] = new HDUnit (u);
      if (u == 0) {
        unit.connect (false);  //ID 0は内蔵ドライブとするため切り離せない
      }
      String path = Settings.sgsGetString ("sa" + u);
      String hdN = Settings.sgsGetString ("hd" + u);  //SASIまたはSCSI
      if (!(hdN.equals ("") || hdN.equals ("none"))) {  //hdNが指定されている
        String hdNWithoutR = hdN.endsWith (":R") || hdN.endsWith (":r") ? hdN.substring (0, hdN.length () - 2) : hdN;  //":R"を取り除く
        int dotIndex = hdNWithoutR.lastIndexOf ('.');
        String ext = dotIndex < 0 ? "" : hdNWithoutR.substring (dotIndex + 1);  //最後の'.'の後の文字列、なければ""
        if (ext.equalsIgnoreCase ("HDF") ||
            HDMedia.hdmPathToMedia (hdNWithoutR, null) != null) {  //hdNはSASI HDらしい
          path = hdN;
          Settings.sgsPutString ("hd" + u, "");  //消しておく
        }
      }
      boolean userWriteProtect = false;
      if (path.toUpperCase ().endsWith (":R")) {  //書き込み禁止モードで開く
        path = path.substring (0, path.length () - 2);
        userWriteProtect = true;
      }
      boolean hostWriteProtect = !new File (path).canWrite ();
      if (path.length () != 0) {
        unit.connect (true);  //接続されていなければ接続する
        if (unit.insert (path,
                         userWriteProtect || hostWriteProtect)) {  //挿入できた
          hdcAddHistory (new File (path).getAbsoluteFile ());
        }
      }
    }

    //フォーマットダイアログ
    hdcFormatDialog = null;
    hdcFormatFileChooser = null;
    hdcFormatMedia = HDMedia.HDM_40MB;
    hdcFormatCopySystemFiles = true;

    hdcSelectedID = -1;
    hdcSelectedLUN = -1;
    hdcTargetUnit = null;
    hdcBusStatus = HDC_STATUS_FREE;

    hdcReadHandle = null;
    hdcWriteHandle = null;
    hdcIndex = 0;
    hdcLimit = 0;
    //hdcCommandBuffer = new byte[6];
    //hdcStatusBuffer = new byte[1];
    //hdcMessageBuffer = new byte[1];
    //hdcSenseBuffer = new byte[4];
    //hdcAssignDriveBuffer = new byte[10];

    hdcLastFormatBlodkEnd = -1;

  }  //hdcInit()

  //hdcTini ()
  //  後始末
  public static void hdcTini () {

    //イメージファイルに書き出す
    for (HDUnit unit : hdcUnitArray) {
      unit.hduTini ();
    }

    //開くダイアログ
    //  開くダイアログを作らなかったときはパラメータを更新しない
    if (hdcOpenDialog != null) {
      Settings.sgsPutOnOff ("sareadonly", hdcOpenDialog.getReadOnly ());
      Settings.sgsPutOnOff ("saappreboot", hdcOpenDialog.getReboot ());
      ArrayList<String> pathsList = hdcOpenDialog.getHistory ();
      int n = pathsList.size ();
      for (int i = 0; i < n; i++) {
        Settings.sgsPutString ("sahistory" + i, pathsList.get (i));
      }
      for (int i = n; i < 16; i++) {
        Settings.sgsPutString ("sahistory" + i, "");
      }
    }

    //ユニット
    for (int u = 0; u < 16; u++) {
      AbstractUnit unit = hdcUnitArray[u];
      Settings.sgsPutString (
        "sa" + u,
        unit.abuConnected && unit.abuInserted ?
        unit.abuWriteProtected ? unit.abuPath + ":R" : unit.abuPath :
        "");
    }

  }  //hdcTini()

  public static void hdcMakeMenu () {

    //アクションリスナー
    ActionListener listener = new ActionListener () {
      @Override public void actionPerformed (ActionEvent ae) {
        Object source = ae.getSource ();
        String command = ae.getActionCommand ();
        switch (command) {
        case "Create new SASI hard disk image files":  //SASIハードディスクのイメージファイルの新規作成
          hdcOpenFormatDialog ();
          break;
        }
      }
    };

    //SASIメニュー
    hdcMenu = ComponentFactory.createMenu ("SASI");  //横に長いとサブメニューを開きにくいので短くする
    ComponentFactory.addComponents (
      hdcMenu,
      ComponentFactory.createHorizontalBox (
        Multilingual.mlnText (
          ComponentFactory.createLabel ("SASI hard disk"),
          "ja", "SASI ハードディスク")),
      ComponentFactory.createHorizontalSeparator ()
      );
    for (int u = 0; u < 16; u++) {
      hdcMenu.add (hdcUnitArray[u].getMenuBox ());
    }
    ComponentFactory.addComponents (
      hdcMenu,
      ComponentFactory.createHorizontalSeparator (),
      hdcSASIMenuItem = ComponentFactory.setEnabled (
        Multilingual.mlnText (ComponentFactory.createCheckBoxMenuItem (!XEiJ.currentModel.isSCSI (), "Built-in SASI port", listener), "ja", "内蔵 SASI ポート"),
        false),  //機種の指定で内蔵SASIと内蔵SCSIを切り替えるので操作できないことにする
      ComponentFactory.createHorizontalSeparator (),
      Multilingual.mlnText (ComponentFactory.createMenuItem ("Create new SASI hard disk image files", listener),
                            "ja", "SASI ハードディスクのイメージファイルの新規作成")
      );

  }

  //inserted = hdcIsInserted (path)
  //  パスで指定したファイルが既に挿入されているか調べる
  public static boolean hdcIsInserted (String path) {
    for (HDUnit unit : hdcUnitArray) {
      if (unit != null &&
          unit.abuConnected &&  //接続されている
          unit.abuInserted &&  //挿入されている
          unit.abuPath.equals (path)) {  //パスが一致している
        return true;  //既に挿入されている
      }
    }
    return false;  //まだ挿入されていない
  }  //hdcIsInserted(String)

  static class OpenDialog extends AbstractOpenDialog {
    public OpenDialog () {
      super (XEiJ.frmFrame,
             "Open SASI hard disk image files",
             "SASI ハードディスクのイメージファイルを開く",
             false,  //ファイル
             hdcFileFilter);
    }
    @Override public void openFiles (File[] files, boolean reboot) {
      hdcOpenFiles (files, reboot);
    }
  }  //class OpenDialog

  //hdcOpenFiles (list, reset)
  //  開くダイアログで選択されたファイルを開く
  public static void hdcOpenFiles (File[] list, boolean reset) {
    boolean success = true;
    for (int u = hdcOpenUnit, k = 0; k < list.length; ) {
      if (16 <= u) {  //ユニットが足りない
        success = false;  //失敗
        break;
      }
      HDUnit unit = hdcUnitArray[u];  //ユニット
      if (!unit.abuConnected) {  //接続されていない
        u++;
        continue;
      }
      File file = list[k++];  //イメージファイル
      if (!file.isFile ()) {  //イメージファイルが存在しない
        success = false;  //失敗
        continue;
      }
      if (!unit.insert (file.getPath (),
                        hdcOpenDialog.getReadOnly () || !file.canWrite ())) {  //挿入できない
        success = false;  //失敗
        continue;
      }
      u++;
    }
    if (success) {  //すべて挿入できた
      hdcAddHistory (list);  //ヒストリに追加する
      if (reset) {  //ここから再起動
        XEiJ.mpuReset (0x8000 | hdcOpenUnit << 8, -1);
      }
    }
  }  //hdcOpenFiles(File[],boolean)

  //hdcMakeFormatDialog ()
  //  フォーマットダイアログを作る
  //  コマンドラインのみ
  public static void hdcMakeFormatDialog () {

    //アクションリスナー
    ActionListener listener = new ActionListener () {
      @Override public void actionPerformed (ActionEvent ae) {
        switch (ae.getActionCommand ()) {
        case JFileChooser.APPROVE_SELECTION:
        case "Start formatting":  //フォーマットを開始する
          {
            File[] list = hdcFormatFileChooser.getSelectedFiles ();
            if (list.length > 0) {
              hdcFormatDialog.setVisible (false);
              if (!hdcFormatFiles (list)) {
                //!!! 失敗
              }
            }
          }
          break;
        case JFileChooser.CANCEL_SELECTION:
        case "Cancel":  //キャンセル
          hdcFormatDialog.setVisible (false);
          break;
        case "10MB":
          hdcFormatMedia = HDMedia.HDM_10MB;
          break;
        case "20MB":
          hdcFormatMedia = HDMedia.HDM_20MB;
          break;
        case "40MB":
          hdcFormatMedia = HDMedia.HDM_40MB;
          break;
        case "Copy system files":  //システムファイルを転送する
          hdcFormatCopySystemFiles = ((JCheckBox) ae.getSource ()).isSelected ();
          break;
        }
      }
    };

    //ファイルチューザー
    hdcMakeFormatFileChooser ();
    hdcFormatFileChooser.setFileFilter (hdcFileFilter);
    hdcFormatFileChooser.addActionListener (listener);

    //ダイアログ
    ButtonGroup mediaGroup = new ButtonGroup ();
    hdcFormatDialog = Multilingual.mlnTitle (
      ComponentFactory.createModalDialog (
        XEiJ.frmFrame,
        "Create new SASI hard disk image files",
        ComponentFactory.createBorderPanel (
          0, 0,
          ComponentFactory.createVerticalBox (
            hdcFormatFileChooser,
            ComponentFactory.createHorizontalBox (
              Box.createHorizontalStrut (12),
              Box.createHorizontalGlue (),
              ComponentFactory.createRadioButtonMenuItem (mediaGroup, hdcFormatMedia == HDMedia.HDM_10MB,  "10MB", listener),
              ComponentFactory.createRadioButtonMenuItem (mediaGroup, hdcFormatMedia == HDMedia.HDM_20MB,  "20MB", listener),
              ComponentFactory.createRadioButtonMenuItem (mediaGroup, hdcFormatMedia == HDMedia.HDM_40MB,  "40MB", listener),
              Box.createHorizontalGlue (),
              Box.createHorizontalStrut (12)
              ),
            Box.createVerticalStrut (12),
            ComponentFactory.createHorizontalBox (
              Box.createHorizontalStrut (12),
              Box.createHorizontalGlue (),
              Multilingual.mlnText (ComponentFactory.createCheckBox (hdcFormatCopySystemFiles, "Copy system files", listener), "ja", "システムファイルを転送する"),
              Box.createHorizontalGlue (),
              Box.createHorizontalStrut (12),
              Multilingual.mlnText (ComponentFactory.createButton ("Start formatting", KeyEvent.VK_F, listener), "ja", "フォーマットを開始する"),
              Box.createHorizontalStrut (12),
              Multilingual.mlnText (ComponentFactory.createButton ("Cancel", KeyEvent.VK_C, listener), "ja", "キャンセル"),
              Box.createHorizontalStrut (12)
              ),
            Box.createVerticalStrut (12)
            )
          )
        ),
      "ja", "SASI ハードディスクのイメージファイルの新規作成");

  }  //hdcMakeFormatDialog()

  //hdcMakeFormatFileChooser ()
  //  フォーマットファイルチューザーを作る
  public static void hdcMakeFormatFileChooser () {
    if (hdcFormatFileChooser == null) {
      hdcFormatFileChooser = new JFileChooser2 ();
      //hdcFormatFileChooser.setMultiSelectionEnabled (true);  //複数選択可能
      hdcFormatFileChooser.setControlButtonsAreShown (false);  //デフォルトのボタンを消す
    }
  }

  //hdcOpenFormatDialog ()
  //  フォーマットダイアログを開く
  public static void hdcOpenFormatDialog () {
    if (hdcFormatDialog == null) {
      hdcMakeFormatDialog ();
    }
    XEiJ.pnlExitFullScreen (true);
    hdcFormatDialog.setVisible (true);
  }  //hdcOpenFormatDialog()

  //success = hdcFormatFiles (list)
  //  SASIディスクをフォーマットする
  //  コマンドラインのみ
  public static boolean hdcFormatFiles (File[] list) {
    boolean success = true;
  format:
    {
      //SASIディスクのフォーマットデータを作る
      byte[] bb = new byte[(int) hdcFormatMedia.humDiskEndByte];  //高々40MBなので丸ごと確保する
      if (!hdcFormatMedia.hdmMakeFormatData (bb, hdcFormatCopySystemFiles)) {
        success = false;  //失敗
        break format;
      }
      //書き出す
      int u = 0;
      for (File file : list) {
        String path = file.getPath ();
        if (true) {
          if (!path.toUpperCase ().endsWith (".HDF")) {  //適切な拡張子が指定されていない
            path += path.endsWith (".") ? "hdf" : ".hdf";  //拡張子を付ける
            file = new File (path);
          }
        }
        if (hdcIsInserted (path)) {  //他のユニットに挿入されている
          success = false;  //失敗
          break format;
        }
        if (!XEiJ.rscPutFile (path, bb, 0, bb.length)) {  //書き出せない
          success = false;  //失敗
          break format;
        }
        //空いているユニットがあれば挿入する
        while (u < 16) {
          HDUnit unit = hdcUnitArray[u++];  //ユニット
          if (unit.abuConnected &&  //接続されていて
              !unit.abuInserted &&  //空いていて
              unit.insert (path,
                           false)) {  //挿入できた
            //フォーマットしたディスクの書き込みを禁止しても意味がないのでここでは書き込みを禁止しない
            break;
          }
        }
      }
    }  //format
    if (success) {  //すべてフォーマットできた
      hdcAddHistory (list);  //ヒストリに追加する
    }
    return success;
  }  //hdcFormatFiles(File[])


  //hdcAddHistory (file)
  //  ファイルをヒストリに追加する
  public static void hdcAddHistory (File file) {
    hdcAddHistory (new File[] { file });
  }

  //hdcAddHistory (files)
  //  複数のファイルをヒストリに追加する
  public static void hdcAddHistory (File[] files) {
    if (hdcOpenDialog == null) {
      hdcOpenHistory.add (files);
    } else {
      hdcOpenDialog.addHistory (files);
    }
    hdcMakeFormatFileChooser ();
    hdcFormatFileChooser.addHistory (files);
    hdcFormatFileChooser.selectLastFiles ();
  }


  //入出力
  //
  //     4   3   2   1   0
  //    MSG C/D I/O BSY REQ | ACK SEL | DATA
  //
  //     0   0   0   0   0  |  0   0  |       バスフリーフェーズ
  //
  //     0   0   0   0   0  |  0   1  |  ID   イニシエータがセレクションフェーズを開始
  //     0   0   0   1   0  |  0   1  |       ターゲットがバスを占有
  //     0   0   0   1   0  |  0   0  |       イニシエータがセレクションフェーズを終了
  //
  //     0   1   0   1   0  |  0   0  |       ターゲットがコマンドフェーズ(イニシエータ→ターゲット)を開始
  //     0   1   0   1   1  |  0   0  |       ターゲットがデータの受信を開始
  //     0   1   0   1   1  |  1   0  |  XX   イニシエータがデータの送信を開始
  //     0   1   0   1   0  |  1   0  |       ターゲットがデータの受信を終了
  //     0   1   0   1   0  |  0   0  |       イニシエータがデータの送信を終了
  //    繰り返す
  //
  //    データ転送フェーズが必要なとき
  //     0   0   0   1   0  |  0   0  |       ターゲットがデータ転送フェーズ(イニシエータ→ターゲット)を開始
  //     0   0   0   1   1  |  0   0  |       ターゲットがデータの受信を開始
  //     0   0   0   1   1  |  1   0  |  XX   イニシエータがデータの送信を開始
  //     0   0   0   1   0  |  1   0  |       ターゲットがデータの受信を終了
  //     0   0   0   1   0  |  0   0  |       イニシエータがデータの送信を終了
  //    繰り返す
  //    または
  //     0   0   1   1   0  |  0   0  |       ターゲットがデータ転送フェーズ(ターゲット→イニシエータ)を開始
  //     0   0   1   1   1  |  0   0  |  XX   ターゲットがデータの送信を開始
  //     0   0   1   1   1  |  1   0  |       イニシエータがデータを受信を開始
  //     0   0   1   1   0  |  1   0  |       ターゲットがデータの送信を終了
  //     0   0   1   1   0  |  0   0  |       イニシエータがデータの受信を終了
  //    繰り返す
  //
  //     0   1   1   1   0  |  0   0  |       ターゲットがステータスフェーズ(ターゲット→イニシエータ)を開始
  //     0   1   1   1   1  |  0   0  |  XX   ターゲットがデータの送信を開始
  //     0   1   1   1   1  |  1   0  |       イニシエータがデータを受信を開始
  //     0   1   1   1   0  |  1   0  |       ターゲットがデータの送信を終了
  //     0   1   1   1   0  |  0   0  |       イニシエータがデータの受信を終了
  //
  //     1   1   1   1   0  |  0   0  |       ターゲットがメッセージフェーズ(ターゲット→イニシエータ)を開始
  //     1   1   1   1   1  |  0   0  |  XX   ターゲットがデータの送信を開始
  //     1   1   1   1   1  |  1   0  |       イニシエータがデータを受信を開始
  //     1   1   1   1   0  |  1   0  |       ターゲットがデータの送信を終了
  //     1   1   1   1   0  |  0   0  |       イニシエータがデータの受信を終了
  //
  //     0   0   0   0   0  |  0   0  |       ターゲットがバスを開放
  //
  //  SELはセレクションポートにIDを書き込むと1になり、ステータスポートに任意のデータを書き込むと0に戻る
  //  ACKはREQ=1のときにデータポートを読み書きすると1になり、REQ=0で0に戻る
  //  ACKはプログラムから見えないので厳密に実装する必要はない
  //  DMAのREQ1はSICILIANのHDREQに接続されている。DMAのACK1はどこにも繋がっておらず、SICILIANがACKを作っている

  //d = hdcPeekStatus ()
  //  pbz (0x00e96003)
  public static int hdcPeekStatus () {
    return hdcBusStatus;
  }  //hdcPeekStatus()

  //d = hdcReadStatus ()
  //  rbz (0x00e96003)
  public static int hdcReadStatus () {
    int d = hdcBusStatus;
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcReadStatus(%d,%d)=%02x\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, d);
    }
    return d;
  }  //hdcReadStatus()

  //hdcWriteSelect (d)
  //  wb (0x00e96007, d)
  public static void hdcWriteSelect (int d) {
    d &= 255;
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcWriteSelect(%02x)\n", XEiJ.regPC0, d);
    }
    if (d == 0) {  //選択されていない
      return;
    }
    //セレクションフェーズに移行する
    hdcSelectedID = Integer.numberOfTrailingZeros (d);
    hdcSelectedLUN = -1;
    hdcBusStatus = HDC_STATUS_BUSY;
  }  //hdcWriteSelect(int)

  //hdcWriteCommand (d)
  //  wb (0x00e96003, d)
  public static void hdcWriteCommand (int d) {
    d &= 255;
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcWriteCommand(%d,%d,%02x)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, d);
    }
    //コマンドフェーズに移行する
    hdcWriteHandle = hdcCommandBuffer;
    hdcIndex = 0;
    hdcLimit = 6;
    hdcBusStatus = HDC_STATUS_COMMAND | HDC_STATUS_OUTPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST;
    HD63450.dmaFallREQ (1);
  }  //hdcWriteCommand(int)

  //hdcWriteReset (d)
  //  wb (0x00e96005, d)
  public static void hdcWriteReset (int d) {
    d &= 255;
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcWriteReset(%d,%d,%02x)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, d);
    }
    //バスフリーフェーズに移行する
    hdcBusFreePhase ();
  }  //hdcWriteReset(int)

  //d = hdcPeekData ()
  //  pbz (0x00e96001)
  public static int hdcPeekData () {
    return (hdcReadHandle == null ? 0 :  //転送中でなければ無視する
            hdcReadHandle[hdcIndex] & 255);
  }  //hdcPeekData()

  //d = hdcReadData ()
  //  rbz (0x00e96001)
  public static int hdcReadData () {
    if (hdcReadHandle == null) {  //転送中でなければ無視する
      if (HDC_DEBUG_TRACE) {
        System.out.printf ("%08x hdcReadData(%d,%d)=%02x\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, 0);
      }
      return 0;
    }
    int d = hdcReadHandle[hdcIndex++] & 255;
    HD63450.dmaRiseREQ (1);
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcReadData(%d,%d)=%02x\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, d);
    }
    if (hdcIndex < hdcLimit) {  //継続
      HD63450.dmaFallREQ (1);
    } else if (hdcBusStatus == (HDC_STATUS_INPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST)) {  //データ転送フェーズが終了した
      //ステータスフェーズに移行する
      hdcStatusPhase (0, 0);  //センスデータなし
    } else if (hdcBusStatus == (HDC_STATUS_COMMAND | HDC_STATUS_INPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST)) {  //ステータスフェーズが終了した
      //メッセージフェーズに移行する
      hdcReadHandle = hdcMessageBuffer;
      hdcWriteHandle = null;
      hdcIndex = 0;
      hdcLimit = 1;
      hdcBusStatus = HDC_STATUS_MESSAGE | HDC_STATUS_COMMAND | HDC_STATUS_INPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST;
      HD63450.dmaFallREQ (1);
    } else {  //メッセージフェーズが終了した
      //バスフリーフェーズに移行する
      hdcBusFreePhase ();
      IOInterrupt.ioiHdcFall ();
      IOInterrupt.ioiHdcRise ();
    }
    return d;
  }  //hdcReadData()

  //hdcWriteData (d)
  //  wb (0x00e96001)
  public static void hdcWriteData (int d) {
    d &= 255;
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcWriteData(%d,%d,%02x)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, d);
    }
    if (hdcWriteHandle == null) {  //転送中でなければ無視する
      return;
    }
    hdcWriteHandle[hdcIndex++] = (byte) d;
    HD63450.dmaRiseREQ (1);
    if (hdcIndex < hdcLimit) {  //継続
      HD63450.dmaFallREQ (1);
    } else if (hdcBusStatus == (HDC_STATUS_OUTPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST)) {  //データ転送フェーズが終了した
      if (hdcWriteHandle == hdcAssignDriveBuffer) {  //ASSIGN DRIVEのデータ転送フェーズが終了した
        if (HDC_DEBUG_COMMAND) {
          System.out.printf ("%08x SASI AssignDrive[", XEiJ.regPC0);
          for (int i = 0; i < 10; i++) {
            if (i > 0) {
              System.out.print (',');
            }
            System.out.printf ("0x%02x", hdcAssignDriveBuffer[i] & 255);
          }
          System.out.println (']');
        }
        //!!!
      }
      //ステータスフェーズに移行する
      hdcStatusPhase (0, 0);  //センスデータなし
    } else {  //コマンドフェーズが終了した
      int cmd = hdcCommandBuffer[0] & 255;  //コマンド番号
      if (HDC_DEBUG_COMMAND) {
        String name = cmd < HDC_COMMAND_NAME.length ? HDC_COMMAND_NAME[cmd] : "";
        if (name.length () == 0) {
          name = "???";
        }
        System.out.printf ("%08x SASI %s(", XEiJ.regPC0, name);
        for (int i = 0; i < hdcLimit; i++) {
          if (i > 0) {
            System.out.print (',');
          }
          System.out.printf ("0x%02x", hdcCommandBuffer[i] & 255);
        }
        System.out.println (')');
      }
      hdcSelectedLUN = hdcCommandBuffer[1] >> 5 & 7;
      if (hdcSelectedLUN > 1) {
        //ステータスフェーズに移行する
        hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
        hdcStatusPhase (2, 0);  //センスデータあり
        return;
      }
      hdcTargetUnit = hdcUnitArray[hdcSelectedID << 1 | hdcSelectedLUN];  //ユニット
      if (!hdcTargetUnit.abuInserted) {  //挿入されていない
        //ステータスフェーズに移行する
        hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
        hdcStatusPhase (2, 0);  //センスデータあり
        return;
      }
      if (hdcLastFormatBlodkEnd >= 0 && cmd != 0x06) {  //一連のFormatBlockコマンドが終了した
        HDMedia media = HDMedia.hdmLengthToMedia (hdcLastFormatBlodkEnd);
        if (media != null) {
          hdcTargetUnit.hduMedia = media;  //SASIハードディスクの容量を変更する
          //!!! FORMAT.XはFormatBlockを行う前に指定されたサイズまでアクセスできるか確認しようとするので、容量を減らすことはできるが増やすことはできない
        }
        hdcLastFormatBlodkEnd = -1;
      }
      switch (cmd) {
      case 0x00:  //TEST DRIVE READY
        hdcDoTestDriveReady ();
        break;
      case 0x01:  //RECALIBRATE
        hdcDoRecalibrate ();
        break;
      case 0x03:  //REQUEST SENSE STATUS
        hdcDoRequestSenseStatus ();
        break;
      case 0x04:  //FORMAT DRIVE
        hdcDoFormatDrive ();
        break;
      //case 0x05:  //CHECK TRACK FORMAT
      case 0x06:  //FORMAT BLOCK
        hdcDoFormatBlock ();
        break;
      //case 0x07:  //BAD TRACK FORMAT
      case 0x08:  //READ
        hdcDoRead ();
        break;
      case 0x0a:  //WRITE
        hdcDoWrite ();
        break;
      case 0x0b:  //SEEK
        hdcDoSeek ();
        break;
      //case 0x0c:  //INITIALIZE DRIVE CHARACTERISTICS
      //case 0x0d:  //LAST CORRECTED BURST LENGTH
      //case 0x0e:  //ASSIGN TRACK
      //case 0x0f:  //WRITE SECTOR BUFFER
      //case 0x10:  //READ SECTOR BUFFER
      case 0xc2:  //ASSIGN DRIVE
        hdcDoAssignDrive ();
        break;
      default:
        if (HDC_DEBUG_UNIMPLEMENTED_COMMAND) {  //未実装コマンドを表示する
          StringBuilder sb = new StringBuilder ();
          String name = cmd < HDC_COMMAND_NAME.length ? HDC_COMMAND_NAME[cmd] : "";
          if (name.length () == 0) {
            name = "???";
          }
          sb.append (String.format ("%08x SASI %s(", XEiJ.regPC0, name));
          for (int i = 0; i < hdcLimit; i++) {
            if (i > 0) {
              sb.append (',');
            }
            sb.append (String.format ("0x%02x", hdcCommandBuffer[i] & 255));
          }
          sb.append (')');
          System.out.println (sb.toString ());
        }
        //ステータスフェーズに移行する
        hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
        hdcStatusPhase (2, 0);  //センスデータあり
      }
    }
  }  //hdcWriteData(int)

  //hdcDoTestDriveReady ()
  public static void hdcDoTestDriveReady () {
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoTestDriveReady(%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN);
    }
    //ステータスフェーズに移行する
    hdcStatusPhase (0, 0);  //センスデータなし
  }  //hdcDoTestDriveReady()

  //hdcDoRecalibrate ()
  public static void hdcDoRecalibrate () {
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoRecalibrate(%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN);
    }
    //ステータスフェーズに移行する
    hdcStatusPhase (0, 0);  //センスデータなし
  }  //hdcDoRecalibrate()

  //hdcDoRequestSenseStatus ()
  public static void hdcDoRequestSenseStatus () {
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoRequestSenseStatus(%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN);
    }
    //データ転送フェーズ(ターゲット→イニシエータ)に移行する
    hdcReadHandle = hdcSenseBuffer;
    hdcIndex = 0;
    hdcLimit = 4;
    hdcBusStatus = HDC_STATUS_INPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST;
    if (false) {
      HD63450.dmaFallREQ (1);
    } else {
      TickerQueue.tkqAdd (hdcDelayTicker, XEiJ.mpuClockTime + HDC_DELAY_TIME);
    }
  }  //hdcDoRequestSenseStatus()

  //hdcDoFormatDrive ()
  public static void hdcDoFormatDrive () {
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoFormatDrive(%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN);
    }
    if (!hdcTargetUnit.abuInserted || hdcTargetUnit.hduMedia == null ||  //挿入されていない
        hdcTargetUnit.abuWriteProtected) {  //書き込みが禁止されている
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    hdcTargetUnit.hduWritten = true;
    //ディスクの全体をゼロクリアする
    Arrays.fill (hdcTargetUnit.hduImage, 0, (int) hdcTargetUnit.hduMedia.humDiskEndByte, (byte) 0);
    //ステータスフェーズに移行する
    hdcStatusPhase (0, 0);  //センスデータなし
  }  //hdcDoFormatDrive()

  //hdcDoFormatBlock ()
  //  SASIコマンド$06 FormatBlock
  public static void hdcDoFormatBlock () {
    int i = (hdcCommandBuffer[1] & 31) << 16 | (char) (hdcCommandBuffer[2] << 8 | hdcCommandBuffer[3] & 255);  //開始セクタ
    int n = 33;  //セクタ数
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoFormatBlock(%d,%d,%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, i, n);
    }
    if (!hdcTargetUnit.abuInserted || hdcTargetUnit.hduMedia == null ||  //挿入されていない
        hdcTargetUnit.abuWriteProtected) {  //書き込みが禁止されている
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    int l = i + n;  //終了セクタ+1
    if (hdcTargetUnit.hduMedia.hdmDiskEndRecord < l) {
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x21;  //INVALID SECTOR ADDRESS
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    int begin = hdcTargetUnit.hduMedia.hdmBytesPerRecord * i;
    int end = hdcTargetUnit.hduMedia.hdmBytesPerRecord * l;
    hdcLastFormatBlodkEnd = end;
    hdcTargetUnit.hduWritten = true;
    //指定されたセクタをゼロクリアする
    Arrays.fill (hdcTargetUnit.hduImage, begin, end, (byte) 0);
    //ステータスフェーズに移行する
    hdcStatusPhase (0, 0);  //センスデータなし
  }  //hdcDoFormatBlock()

  //hdcDoRead ()
  public static void hdcDoRead () {
    int i = (hdcCommandBuffer[1] & 31) << 16 | (char) (hdcCommandBuffer[2] << 8 | hdcCommandBuffer[3] & 255);  //開始セクタ
    int n = hdcCommandBuffer[4] & 255;  //セクタ数
    if (n == 0) {
      n = 256;
    }
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoRead(%d,%d,%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, i, n);
    }
    if (!hdcTargetUnit.abuInserted || hdcTargetUnit.hduMedia == null) {  //挿入されていない
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    int l = i + n;  //終了セクタ+1
    if (hdcTargetUnit.hduMedia.hdmDiskEndRecord < l) {
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x21;  //INVALID SECTOR ADDRESS
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    //データ転送フェーズ(ターゲット→イニシエータ)に移行する
    hdcReadHandle = hdcTargetUnit.hduImage;
    hdcIndex = hdcTargetUnit.hduMedia.hdmBytesPerRecord * i;
    hdcLimit = hdcTargetUnit.hduMedia.hdmBytesPerRecord * l;
    hdcBusStatus = HDC_STATUS_INPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST;
    if (false) {
      HD63450.dmaFallREQ (1);
    } else {
      TickerQueue.tkqAdd (hdcDelayTicker, XEiJ.mpuClockTime + HDC_DELAY_TIME);
    }
  }  //hdcDoRead()

  //hdcDoWrite ()
  public static void hdcDoWrite () {
    int i = (hdcCommandBuffer[1] & 31) << 16 | (char) (hdcCommandBuffer[2] << 8 | hdcCommandBuffer[3] & 255);  //開始セクタ
    int n = hdcCommandBuffer[4] & 255;  //セクタ数
    if (n == 0) {
      n = 256;
    }
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoWrite(%d,%d,%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, i, n);
    }
    if (!hdcTargetUnit.abuInserted || hdcTargetUnit.hduMedia == null ||  //挿入されていない
        hdcTargetUnit.abuWriteProtected) {  //書き込みが禁止されている
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    int l = i + n;  //終了セクタ+1
    if (hdcTargetUnit.hduMedia.hdmDiskEndRecord < l) {
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x21;  //INVALID SECTOR ADDRESS
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    hdcTargetUnit.hduWritten = true;
    //データ転送フェーズ(イニシエータ→ターゲット)に移行する
    hdcWriteHandle = hdcTargetUnit.hduImage;
    hdcIndex = hdcTargetUnit.hduMedia.hdmBytesPerRecord * i;
    hdcLimit = hdcTargetUnit.hduMedia.hdmBytesPerRecord * l;
    hdcBusStatus = HDC_STATUS_OUTPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST;
    if (false) {
      HD63450.dmaFallREQ (1);
    } else {
      TickerQueue.tkqAdd (hdcDelayTicker, XEiJ.mpuClockTime + HDC_DELAY_TIME);
    }
  }  //hdcDoWrite()

  //hdcDoSeek ()
  public static void hdcDoSeek () {
    int i = (hdcCommandBuffer[1] & 31) << 16 | (char) (hdcCommandBuffer[2] << 8 | hdcCommandBuffer[3] & 255);  //開始セクタ
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoSeek(%d,%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN, i);
    }
    if (!hdcTargetUnit.abuInserted || hdcTargetUnit.hduMedia == null) {  //挿入されていない
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x20;  //INVALID COMMAND
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    if (hdcTargetUnit.hduMedia.hdmDiskEndRecord <= i) {
      //ステータスフェーズに移行する
      hdcSenseBuffer[0] = 0x21;  //INVALID SECTOR ADDRESS
      hdcStatusPhase (2, 0);  //センスデータあり
      return;
    }
    //ステータスフェーズに移行する
    hdcStatusPhase (0, 0);  //センスデータなし
  }  //hdcDoSeek()

  //hdcDoAssignDrive ()
  public static void hdcDoAssignDrive () {
    if (HDC_DEBUG_TRACE) {
      System.out.printf ("%08x hdcDoAssignDrive(%d,%d)\n", XEiJ.regPC0, hdcSelectedID, hdcSelectedLUN);
    }
    //データ転送フェーズ(イニシエータ→ターゲット)に移行する
    hdcWriteHandle = hdcAssignDriveBuffer;
    hdcIndex = 0;
    hdcLimit = 10;
    hdcBusStatus = HDC_STATUS_OUTPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST;
    if (false) {
      HD63450.dmaFallREQ (1);
    } else {
      TickerQueue.tkqAdd (hdcDelayTicker, XEiJ.mpuClockTime + HDC_DELAY_TIME);
    }
  }  //hdcDoAssignDrive()

  //hdcStatusPhase (status, message)
  //  ステータスフェーズに移行する
  //  status   2=センスデータあり。hdcSenseBufferを設定しておくこと
  //  message  常に0
  public static void hdcStatusPhase (int status, int message) {
    hdcStatusBuffer[0] = (byte) status;
    hdcMessageBuffer[0] = (byte) message;
    hdcReadHandle = hdcStatusBuffer;
    hdcWriteHandle = null;
    hdcIndex = 0;
    hdcLimit = 1;
    hdcBusStatus = HDC_STATUS_COMMAND | HDC_STATUS_INPUT | HDC_STATUS_BUSY | HDC_STATUS_REQUEST;
    HD63450.dmaFallREQ (1);
  }  //hdcStatusPhase(int,int)

  //hdcBusFreePhase ()
  //  バスフリーフェーズに移行する
  public static void hdcBusFreePhase () {
    hdcSelectedID = -1;
    hdcSelectedLUN = -1;
    hdcTargetUnit = null;
    hdcBusStatus = HDC_STATUS_FREE;
    hdcReadHandle = null;
    hdcWriteHandle = null;
    hdcIndex = 0;
    hdcLimit = 0;
    HD63450.dmaRiseREQ (1);
  }  //hdcBusFreePhase()



  //========================================================================================
  //SASIフォーマットデータ
  //  無償公開されたHuman68k version 3.02のシステムディスクに入っているFORMAT.Xから抽出したデータを使う

  //----------------------------------------------------------------------------------------
  //SASIディスクIPL
/*
  public static final int[] HDC_DISK_IPL = {
    //  perl -e "do'sjdump.pl';$p=0;$m=4;$o=0x8aa;$l=0xcaa-$o;open IN,'HUMAN302.XDF'or die;binmode IN;seek IN,1024*592,0;read IN,$b,64;seek IN,1024*592+vec($b,15,32)+32*$m,0;read IN,$b,32;seek IN,1024*592+vec($b,7,32)+64+$o,0;read IN,$b,$l;close IN;sjdumpcode($b,0,$l,$p)"
    0x60,0x00,0x00,0xca,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00000000  `..ハ............
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00000010  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00000020  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00000030  ................
    0x1a,0x1b,0x5b,0x36,0x3b,0x33,0x32,0x48,0x58,0x36,0x38,0x30,0x30,0x30,0x20,0x48,  //00000040  ..[6;32HX68000 H
    0x41,0x52,0x44,0x20,0x44,0x49,0x53,0x4b,0x20,0x49,0x50,0x4c,0x20,0x4d,0x45,0x4e,  //00000050  ARD DISK IPL MEN
    0x55,0x1b,0x5b,0x32,0x35,0x3b,0x32,0x32,0x48,0x83,0x4a,0x81,0x5b,0x83,0x5c,0x83,  //00000060  U.[25;22Hカーソル
    0x8b,0x83,0x4c,0x81,0x5b,0x82,0xc5,0x91,0x49,0x91,0xf0,0x82,0xb5,0x82,0xc4,0x83,  //00000070   キーで選択してリ
    0x8a,0x83,0x5e,0x81,0x5b,0x83,0x93,0x83,0x4c,0x81,0x5b,0x82,0xf0,0x89,0x9f,0x82,  //00000080   ターンキーを押し
    0xb5,0x82,0xc4,0x82,0xad,0x82,0xbe,0x82,0xb3,0x82,0xa2,0x00,0x1b,0x5b,0x32,0x36,  //00000090   てください..[26
    0x3b,0x32,0x38,0x48,0x91,0x49,0x91,0xf0,0x82,0xb5,0x82,0xbd,0x82,0xe0,0x82,0xcc,  //000000a0  ;28H選択したもの
    0x82,0xf0,0x8e,0xa9,0x93,0xae,0x8b,0x4e,0x93,0xae,0x82,0xc6,0x82,0xb5,0x82,0xc4,  //000000b0  を自動起動として
    0x93,0x6f,0x98,0x5e,0x82,0xb5,0x82,0xdc,0x82,0xb7,0x00,0x00,0x4f,0xfa,0xff,0x32,  //000000c0  登録します..O..2
    0x42,0x85,0x20,0x3c,0x00,0x00,0x00,0x8e,0x4e,0x4f,0x1e,0x00,0xe1,0x47,0x74,0x04,  //000000d0  B. <...晒O..瓱t.
    0x26,0x3c,0x00,0x00,0x01,0x00,0x43,0xfa,0x02,0xc6,0x61,0x00,0x02,0x32,0x4a,0x00,  //000000e0  &<....C..ニa..2J.
    0x66,0x00,0x01,0x6a,0x43,0xfa,0x02,0xb8,0x47,0xfa,0xff,0x0a,0x0c,0x91,0x58,0x36,  //000000f0  f..jC..クG....噌6
    0x38,0x4b,0x66,0x00,0x01,0x6c,0x74,0x0e,0x42,0x43,0x42,0x44,0x42,0x86,0x43,0xe9,  //00000100  8Kf..lt.BCBDB.C.
    0x00,0x10,0x4a,0x11,0x67,0x16,0x52,0x46,0x26,0xc9,0x10,0x29,0x00,0x08,0x08,0x00,  //00000110  ..J.g.RF&ノ.)....
    0x00,0x00,0x66,0x08,0x52,0x43,0x4a,0x00,0x66,0x02,0x52,0x44,0x51,0xca,0xff,0xe0,  //00000120  ..f.RCJ.f.RDQハ.濳
    0x4a,0x43,0x67,0x00,0x01,0x42,0x72,0x0a,0x70,0x04,0x4e,0x4f,0x08,0x00,0x00,0x04,  //00000130   Cg..Br.p.NO....
    0x66,0x12,0x4a,0x44,0x67,0x0e,0x53,0x44,0x67,0x1c,0x43,0xfa,0xfe,0xf4,0x61,0x00,  //00000140  f.JDg.SDg.C...a.
    0x01,0xc4,0x60,0x28,0x43,0xfa,0xfe,0xea,0x61,0x00,0x01,0xba,0x43,0xfa,0xff,0x3e,  //00000150  .ト`(C..鸞..コC..>
    0x61,0x00,0x01,0xb2,0x60,0x14,0x47,0xfa,0xfe,0x9c,0x20,0x5b,0x24,0x28,0x00,0x08,  //00000160  a..イ`.G... [$(..
    0x4a,0x28,0x00,0x08,0x66,0xf4,0x60,0x00,0x00,0xbe,0x7a,0x02,0x42,0x43,0x45,0xfa,  //00000170  J(..f.`..セz.BCE.
    0xfe,0x84,0x22,0x52,0x10,0x29,0x00,0x08,0x67,0x0a,0xb0,0x05,0x67,0x06,0x72,0x02,  //00000180  .."R.)..g.ー.g.r.
    0x61,0x00,0x01,0x76,0x61,0x00,0x01,0x16,0x58,0x8a,0x52,0x43,0xb6,0x46,0x65,0xe2,  //00000190  a..va...X崖CカFe秡
    0x60,0x2a,0x61,0x00,0x01,0x06,0x61,0x00,0x00,0xf2,0xb0,0x3c,0x00,0x1d,0x67,0x3a,  //000001a0   *a...a...ー<..g:
    0xb0,0x3c,0x00,0x35,0x67,0x0c,0xb0,0x3c,0x00,0x3c,0x67,0x1a,0xb0,0x3c,0x00,0x3e,  //000001b0  ー<.5g.ー<.<g.ー<.>
    0x66,0xe4,0x61,0x00,0x00,0xe8,0x52,0x43,0xb6,0x46,0x65,0x02,0x42,0x43,0x61,0x00,  //000001c0  f臑..鏗CカFe.BCa.
    0x00,0xb2,0x66,0xf2,0x60,0xcc,0x61,0x00,0x00,0xd4,0x53,0x43,0x6a,0x04,0x36,0x06,  //000001d0  .イf.`フa..ヤSCj.6.
    0x53,0x43,0x61,0x00,0x00,0x9e,0x66,0xf2,0x60,0xb8,0x47,0xfa,0xfe,0x18,0xe5,0x43,  //000001e0  SCa..枅.`クG...蕕
    0x20,0x73,0x30,0x00,0x24,0x28,0x00,0x08,0x4a,0x05,0x67,0x3a,0x43,0xfa,0x01,0xb8,  //000001f0   s0.$(..J.g:C..ク
    0x72,0x0e,0x43,0xe9,0x00,0x10,0x4a,0x29,0xff,0xf8,0x67,0x12,0x20,0x11,0x08,0x00,  //00000200  r.C...J)..g. ...
    0x00,0x18,0x66,0x0a,0x42,0x11,0xb4,0x80,0x67,0x04,0x12,0xbc,0x00,0x02,0x51,0xc9,  //00000210  ..f.B.エ.g..シ..Qノ
    0xff,0xe2,0x2f,0x02,0x74,0x04,0x26,0x3c,0x00,0x00,0x01,0x00,0x43,0xfa,0x01,0x80,  //00000220  ../.t.&<....C...
    0x61,0x00,0x00,0xe8,0x24,0x1f,0xc4,0xbc,0x00,0xff,0xff,0xff,0x26,0x3c,0x00,0x00,  //00000230  a...$.トシ....&<..
    0x04,0x00,0x43,0xfa,0xfd,0xbc,0xd3,0xfc,0x00,0x00,0x04,0x00,0x61,0x00,0x00,0xd0,  //00000240  ..C..シモ.....a..ミ
    0x4a,0x00,0x66,0x08,0x0c,0x11,0x00,0x60,0x66,0x22,0x4e,0xd1,0x45,0xfa,0x00,0xda,  //00000250  J.f....`f"NムE..レ
    0x43,0xfa,0x00,0xcc,0x61,0x00,0x00,0xae,0x22,0x4a,0x61,0x00,0x00,0xa8,0x60,0xfe,  //00000260  C..フa..ョ"Ja..ィ`.
    0x45,0xfa,0x00,0xe3,0x60,0xea,0x45,0xfa,0x00,0xfa,0x60,0xe4,0x45,0xfa,0x01,0x11,  //00000270  E..總鵙...`胼...
    0x60,0xde,0x41,0xfa,0xfd,0x80,0x20,0x03,0xe5,0x40,0x20,0x70,0x00,0x00,0x10,0x28,  //00000280  `゙A... .蕁 p...(
    0x00,0x08,0xb0,0x05,0x67,0x02,0x4a,0x00,0x4e,0x75,0x42,0x80,0x4e,0x4f,0xe0,0x48,  //00000290  ..ー.g.J.NuB.NO潯
    0xb0,0x3c,0x00,0x4e,0x66,0x02,0x70,0x1d,0x4e,0x75,0x61,0x5a,0x43,0xfa,0xfd,0x56,  //000002a0  ー<.Nf.p.NuaZC..V
    0x30,0x03,0xe5,0x40,0x43,0xf1,0x00,0x00,0x72,0x24,0x74,0x09,0xd4,0x43,0x70,0x23,  //000002b0  0.蕁C...r$t.ヤCp#
    0x4e,0x4f,0x22,0x51,0x72,0x28,0x61,0x46,0x24,0x09,0x41,0xfa,0x00,0xe2,0x94,0x88,  //000002c0  NO"Qr(aF$.A..笏郁
    0xe8,0x8a,0x84,0xfc,0x00,0x0a,0xd4,0xbc,0x00,0x30,0x00,0x30,0x72,0x20,0xb4,0x7c,  //000002d0   割...ヤシ.0.0r エ|
    0x00,0x30,0x67,0x02,0x32,0x02,0x61,0x26,0x48,0x42,0x32,0x02,0x61,0x20,0x72,0x29,  //000002e0  .0g.2.a&HB2.a r)
    0x61,0x1c,0x72,0x20,0x61,0x18,0x74,0x07,0x42,0x41,0x12,0x19,0x61,0x10,0x51,0xca,  //000002f0  a.r a.t.BA..a.Qハ
    0xff,0xf8,0x72,0x03,0x60,0x02,0x72,0x0b,0x70,0x22,0x4e,0x4f,0x4e,0x75,0x70,0x20,  //00000300  ..r.`.r.p"NONup 
    0x4e,0x4f,0x4e,0x75,0x70,0x21,0x4e,0x4f,0x4e,0x75,0x70,0x45,0x60,0x02,0x70,0x46,  //00000310  NONup!NONupE`.pF
    0x48,0xe7,0x78,0x40,0x32,0x07,0x4e,0x4f,0x4c,0xdf,0x02,0x1e,0x4e,0x75,0x1a,0x1b,  //00000320  H輾@2.NOL゚..Nu..
    0x5b,0x31,0x36,0x3b,0x33,0x33,0x48,0x00,0x20,0x20,0x83,0x6e,0x81,0x5b,0x83,0x68,  //00000330  [16;33H.  ハード
    0x83,0x66,0x83,0x42,0x83,0x58,0x83,0x4e,0x82,0xaa,0x93,0xc7,0x82,0xdf,0x82,0xdc,  //00000340  ディスクが読めま
    0x82,0xb9,0x82,0xf1,0x00,0x20,0x20,0x8a,0xc7,0x97,0x9d,0x83,0x75,0x83,0x8d,0x83,  //00000350  せん.  管理ブロッ
    0x62,0x83,0x4e,0x82,0xaa,0x89,0xf3,0x82,0xea,0x82,0xc4,0x82,0xa2,0x82,0xdc,0x82,  //00000360   クが壊れています
    0xb7,0x00,0x20,0x20,0x8b,0x4e,0x93,0xae,0x89,0xc2,0x94,0x5c,0x82,0xc8,0x97,0xcc,  //00000370   .  起動可能な領
    0x88,0xe6,0x82,0xaa,0x82,0xa0,0x82,0xe8,0x82,0xdc,0x82,0xb9,0x82,0xf1,0x00,0x82,  //00000380  域がありません.I
    0x68,0x82,0x6f,0x82,0x6b,0x83,0x75,0x83,0x8d,0x83,0x62,0x83,0x4e,0x82,0xcc,0x93,  //00000390   PLブロックの内
    0xe0,0x97,0x65,0x82,0xaa,0x88,0xd9,0x8f,0xed,0x82,0xc5,0x82,0xb7,0x00,0x00,0x00,  //000003a0   容が異常です...
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000003b0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000003c0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000003d0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000003e0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000003f0  ................
  };
*/
  //  perl misc/itob.pl xeij/HDC.java HDC_DISK_IPL
  public static final byte[] HDC_DISK_IPL = "`\0\0\312\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\32\33[6;32HX68000 HARD DISK IPL MENU\33[25;22H\203J\201[\203\\\203\213\203L\201[\202\305\221I\221\360\202\265\202\304\203\212\203^\201[\203\223\203L\201[\202\360\211\237\202\265\202\304\202\255\202\276\202\263\202\242\0\33[26;28H\221I\221\360\202\265\202\275\202\340\202\314\202\360\216\251\223\256\213N\223\256\202\306\202\265\202\304\223o\230^\202\265\202\334\202\267\0\0O\372\3772B\205 <\0\0\0\216NO\36\0\341Gt\4&<\0\0\1\0C\372\2\306a\0\0022J\0f\0\1jC\372\2\270G\372\377\n\f\221X68Kf\0\1lt\16BCBDB\206C\351\0\20J\21g\26RF&\311\20)\0\b\b\0\0\0f\bRCJ\0f\2RDQ\312\377\340JCg\0\1Br\np\4NO\b\0\0\4f\22JDg\16SDg\34C\372\376\364a\0\1\304`(C\372\376\352a\0\1\272C\372\377>a\0\1\262`\24G\372\376\234 [$(\0\bJ(\0\bf\364`\0\0\276z\2BCE\372\376\204\"R\20)\0\bg\n\260\5g\6r\2a\0\1va\0\1\26X\212RC\266Fe\342`*a\0\1\6a\0\0\362\260<\0\35g:\260<\0005g\f\260<\0<g\32\260<\0>f\344a\0\0\350RC\266Fe\2BCa\0\0\262f\362`\314a\0\0\324SCj\0046\6SCa\0\0\236f\362`\270G\372\376\30\345C s0\0$(\0\bJ\5g:C\372\1\270r\16C\351\0\20J)\377\370g\22 \21\b\0\0\30f\nB\21\264\200g\4\22\274\0\2Q\311\377\342/\2t\4&<\0\0\1\0C\372\1\200a\0\0\350$\37\304\274\0\377\377\377&<\0\0\4\0C\372\375\274\323\374\0\0\4\0a\0\0\320J\0f\b\f\21\0`f\"N\321E\372\0\332C\372\0\314a\0\0\256\"Ja\0\0\250`\376E\372\0\343`\352E\372\0\372`\344E\372\1\21`\336A\372\375\200 \3\345@ p\0\0\20(\0\b\260\5g\2J\0NuB\200NO\340H\260<\0Nf\2p\35NuaZC\372\375V0\3\345@C\361\0\0r$t\t\324Cp#NO\"Qr(aF$\tA\372\0\342\224\210\350\212\204\374\0\n\324\274\0000\0000r \264|\0000g\0022\2a&HB2\2a r)a\34r a\30t\7BA\22\31a\20Q\312\377\370r\3`\2r\13p\"NONup NONup!NONupE`\2pFH\347x@2\7NOL\337\2\36Nu\32\33[16;33H\0  \203n\201[\203h\203f\203B\203X\203N\202\252\223\307\202\337\202\334\202\271\202\361\0  \212\307\227\235\203u\203\215\203b\203N\202\252\211\363\202\352\202\304\202\242\202\334\202\267\0  \213N\223\256\211\302\224\\\202\310\227\314\210\346\202\252\202\240\202\350\202\334\202\271\202\361\0\202h\202o\202k\203u\203\215\203b\203N\202\314\223\340\227e\202\252\210\331\217\355\202\305\202\267\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".getBytes (XEiJ.ISO_8859_1);

  //----------------------------------------------------------------------------------------
  //SASIパーティションIPL
/*
  public static final int[] HDC_PARTITION_IPL = {
    //  perl -e "do'sjdump.pl';$p=0x2100;$m=4;$o=0x1126;$l=0x1526-$o;open IN,'HUMAN302.XDF'or die;binmode IN;seek IN,1024*592,0;read IN,$b,64;seek IN,1024*592+vec($b,15,32)+32*$m,0;read IN,$b,32;seek IN,1024*592+vec($b,7,32)+64+$o,0;read IN,$b,$l;close IN;sjdumpcode($b,0,$l,$p)"
    0x60,0x20,0x48,0x75,0x64,0x73,0x6f,0x6e,0x20,0x73,0x6f,0x66,0x74,0x20,0x32,0x2e,  //00002100  ` Hudson soft 2.
    0x30,0x30,0x04,0x00,0x01,0x02,0x00,0x01,0x02,0x00,0x27,0x72,0xf8,0x14,0x00,0x00,  //00002110  00........'r....
    0x00,0x00,0x4f,0xfa,0xff,0xdc,0x43,0xfa,0x01,0x34,0x61,0x00,0x01,0x2a,0x20,0x3c,  //00002120  ..O..ワC..4a..* <
    0x00,0x00,0x00,0x8e,0x4e,0x4f,0x12,0x00,0xe1,0x41,0x42,0x82,0x14,0x3a,0xff,0xdf,  //00002130  ...晒O..瓣B..:.゚
    0x42,0x80,0x10,0x3a,0xff,0xd1,0xc4,0xc0,0x42,0x80,0x30,0x3a,0xff,0xca,0xd4,0x80,  //00002140  B..:.ムトタB.0:.ハヤ.
    0xe5,0x82,0xd4,0xba,0xff,0xca,0x26,0x3c,0x00,0x00,0x04,0x00,0x43,0xfa,0x01,0x9a,  //00002150  蛯ヤコ.ハ&<....C..啾
    0x61,0x00,0x00,0xc8,0x4a,0x00,0x66,0x00,0x00,0xd0,0x43,0xfa,0x01,0x8c,0x3c,0x3c,  //00002160   ..ネJ.f..ミC...<<
    0x00,0x1f,0x24,0x49,0x47,0xfa,0x01,0x75,0x7e,0x0a,0x10,0x1a,0x80,0x3c,0x00,0x20,  //00002170  ..$IG..u~....<. 
    0xb0,0x1b,0x66,0x06,0x51,0xcf,0xff,0xf4,0x60,0x14,0xd3,0xfc,0x00,0x00,0x00,0x20,  //00002180  ー.f.Qマ..`.モ.... 
    0x51,0xce,0xff,0xe0,0x43,0xfa,0x00,0xc8,0x61,0x00,0x00,0xbc,0x60,0xfe,0x42,0x80,  //00002190  Qホ.澆..ネa..シ`.B.
    0x30,0x29,0x00,0x1a,0xe1,0x58,0x55,0x40,0x42,0x87,0x1e,0x3a,0xff,0x68,0xc0,0xc7,  //000021a0  0)..畊U@B..:.hタヌ
    0xe5,0x80,0xd4,0x80,0x42,0x80,0x30,0x3a,0xff,0x60,0xeb,0x80,0xd0,0xbc,0x00,0x00,  //000021b0  蛟ヤ.B.0:.`..ミシ..
    0x03,0xff,0xe0,0x88,0xe4,0x88,0xe5,0x80,0xd4,0x80,0x48,0xe7,0x60,0x00,0x43,0xfa,  //000021c0  ..煦艾蛟ヤ.H軈.C.
    0x01,0x28,0x26,0x3c,0x00,0x00,0x01,0x00,0x61,0x50,0x4c,0xdf,0x00,0x06,0x4a,0x00,  //000021d0  .(&<....aPL゚..J.
    0x66,0x56,0x43,0xfa,0x01,0x14,0x0c,0x59,0x48,0x55,0x66,0x56,0x54,0x89,0x0c,0x99,  //000021e0  fVC....YHUfVT...
    0x00,0x00,0x68,0x00,0x66,0x56,0x2f,0x19,0x26,0x19,0xd6,0x99,0x2f,0x03,0x2f,0x19,  //000021f0  ..h.fV/.&.ヨ././.
    0x22,0x7c,0x00,0x00,0x67,0xc0,0xd6,0xbc,0x00,0x00,0x00,0x40,0x61,0x1c,0x22,0x1f,  //00002200  "|..gタヨシ...@a.".
    0x24,0x1f,0x22,0x5f,0x4a,0x00,0x66,0x20,0x41,0xf9,0x00,0x00,0x68,0x00,0xd1,0xc2,  //00002210  $."_J.f A...h.ムツ
    0x53,0x81,0x65,0x04,0x42,0x18,0x60,0xf8,0x4e,0xd1,0x48,0xe7,0x78,0x40,0x70,0x46,  //00002220  S‘.B.`.NムH輾@pF
    0x4e,0x4f,0x4c,0xdf,0x02,0x1e,0x4e,0x75,0x43,0xfa,0x00,0x48,0x61,0x18,0x60,0x00,  //00002230  NOL゚..NuC..Ha.`.
    0xff,0x5c,0x43,0xfa,0x00,0x5d,0x61,0x0e,0x60,0x00,0xff,0x52,0x43,0xfa,0x00,0x75,  //00002240  .\C..]a.`..RC..u
    0x61,0x04,0x60,0x00,0xff,0x48,0x70,0x21,0x4e,0x4f,0x4e,0x75,0x1a,0x00,0x1b,0x5b,  //00002250  a.`..Hp!NONu...[
    0x31,0x36,0x3b,0x33,0x35,0x48,0x48,0x75,0x6d,0x61,0x6e,0x2e,0x73,0x79,0x73,0x20,  //00002260  16;35HHuman.sys 
    0x82,0xaa,0x20,0x8c,0xa9,0x82,0xc2,0x82,0xa9,0x82,0xe8,0x82,0xdc,0x82,0xb9,0x82,  //00002270  が 見つかりません
    0xf1,0x00,0x1b,0x5b,0x31,0x36,0x3b,0x33,0x38,0x48,0x83,0x66,0x83,0x42,0x83,0x58,  //00002280   ..[16;38Hディス
    0x83,0x4e,0x82,0xaa,0x81,0x40,0x93,0xc7,0x82,0xdf,0x82,0xdc,0x82,0xb9,0x82,0xf1,  //00002290  クが 読めません
    0x00,0x1b,0x5b,0x31,0x36,0x3b,0x33,0x36,0x48,0x48,0x75,0x6d,0x61,0x6e,0x2e,0x73,  //000022a0  ..[16;36HHuman.s
    0x79,0x73,0x20,0x82,0xaa,0x20,0x89,0xf3,0x82,0xea,0x82,0xc4,0x82,0xa2,0x82,0xdc,  //000022b0  ys が 壊れていま
    0x82,0xb7,0x00,0x1b,0x5b,0x31,0x36,0x3b,0x33,0x33,0x48,0x48,0x75,0x6d,0x61,0x6e,  //000022c0  す..[16;33HHuman
    0x2e,0x73,0x79,0x73,0x20,0x82,0xcc,0x20,0x83,0x41,0x83,0x68,0x83,0x8c,0x83,0x58,  //000022d0  .sys の アドレス
    0x82,0xaa,0x88,0xd9,0x8f,0xed,0x82,0xc5,0x82,0xb7,0x00,0x68,0x75,0x6d,0x61,0x6e,  //000022e0  が異常です.human
    0x20,0x20,0x20,0x73,0x79,0x73,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000022f0     sys..........
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002300  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002310  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002320  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002330  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002340  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002350  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002360  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002370  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002380  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002390  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000023a0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000023b0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000023c0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000023d0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000023e0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000023f0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002400  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002410  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002420  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002430  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002440  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002450  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002460  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002470  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002480  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //00002490  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000024a0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000024b0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000024c0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000024d0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000024e0  ................
    0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,  //000024f0  ................
  };
*/
  //  perl misc/itob.pl xeij/HDC.java HDC_PARTITION_IPL
  public static final byte[] HDC_PARTITION_IPL = "` Hudson soft 2.00\4\0\1\2\0\1\2\0\'r\370\24\0\0\0\0O\372\377\334C\372\0014a\0\1* <\0\0\0\216NO\22\0\341AB\202\24:\377\337B\200\20:\377\321\304\300B\2000:\377\312\324\200\345\202\324\272\377\312&<\0\0\4\0C\372\1\232a\0\0\310J\0f\0\0\320C\372\1\214<<\0\37$IG\372\1u~\n\20\32\200<\0 \260\33f\6Q\317\377\364`\24\323\374\0\0\0 Q\316\377\340C\372\0\310a\0\0\274`\376B\2000)\0\32\341XU@B\207\36:\377h\300\307\345\200\324\200B\2000:\377`\353\200\320\274\0\0\3\377\340\210\344\210\345\200\324\200H\347`\0C\372\1(&<\0\0\1\0aPL\337\0\6J\0fVC\372\1\24\fYHUfVT\211\f\231\0\0h\0fV/\31&\31\326\231/\3/\31\"|\0\0g\300\326\274\0\0\0@a\34\"\37$\37\"_J\0f A\371\0\0h\0\321\302S\201e\4B\30`\370N\321H\347x@pFNOL\337\2\36NuC\372\0Ha\30`\0\377\\C\372\0]a\16`\0\377RC\372\0ua\4`\0\377Hp!NONu\32\0\33[16;35HHuman.sys \202\252 \214\251\202\302\202\251\202\350\202\334\202\271\202\361\0\33[16;38H\203f\203B\203X\203N\202\252\201@\223\307\202\337\202\334\202\271\202\361\0\33[16;36HHuman.sys \202\252 \211\363\202\352\202\304\202\242\202\334\202\267\0\33[16;33HHuman.sys \202\314 \203A\203h\203\214\203X\202\252\210\331\217\355\202\305\202\267\0human   sys\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0".getBytes (XEiJ.ISO_8859_1);



  //========================================================================================
  //$$HDU SASI HDユニット
  //  SASIハードディスクのユニット
  public static class HDUnit extends AbstractUnit {

    public HDMedia hduMedia;  //メディアの種類
    public byte[] hduImage;  //ディスクイメージ
    public boolean hduWritten;  //true=書き込みがあった

    //new HDUnit (number)
    //  コンストラクタ
    public HDUnit (int number) {
      super (number);
      hduImage = null;
      hduWritten = false;
    }

    //hduTini ()
    //  後始末
    //  イメージファイルに書き出す
    public void hduTini () {
      if (abuInserted) {
        hduFlush ();
      }
    }  //hduTini()

    //success = unit.hduFlush ()
    //  イメージファイルに書き出す
    public boolean hduFlush () {
      if (!hduWritten) {  //書き込みがない
        return true;
      }
      if (abuWriteProtected) {  //書き込みが許可されていない
        return false;
      }
      if (!XEiJ.rscPutFile (abuPath, hduImage, 0, (int) hduMedia.humDiskEndByte)) {
        return false;
      }
      hduWritten = false;
      return true;
    }  //hduFlush()

    //unit.connect (disconnectable)
    //  接続する
    @Override protected void connect (boolean disconnectable) {
      super.connect (disconnectable);
      hduImage = new byte[HDMedia.HDM_MAX_BYTES_PER_DISK];
      hduWritten = false;
    }

    //unit.disconnect ()
    //  切り離す
    @Override protected void disconnect () {
      super.disconnect ();
      hduImage = null;
    }

    //success = unit.eject ()
    //  イジェクトする
    @Override protected boolean eject () {
      if (!hduFlush ()) {  //イメージファイルに書き出す
        return false;
      }
      String path = abuPath;  //イジェクトされたイメージファイルのパス。super.eject()を呼び出す前にコピーすること
      if (!super.eject ()) {  //イジェクトする
        return false;
      }
      if (hduMedia != null) {  //挿入されていたとき
        hdcAddHistory (new File (path).getAbsoluteFile ());
        System.out.println (Multilingual.mlnJapanese ?
                            path + " を sa" + abuNumber + " から切り離しました" :
                            path + " was removed from sa" + abuNumber);
      }
      hduMedia = null;
      //hdcHDMaxを更新する
      if (hdcHDMax == abuNumber - 1) {
        int u = abuNumber - 1;
        while (u >= 0 && !hdcUnitArray[u].abuInserted) {  //挿入されている最後のユニットまで遡る
          u--;
        }
        hdcHDMax = u + 1;
      }
      return true;
    }

    //success = unit.open ()
    //  開くダイアログを開く
    @Override protected boolean open () {
      if (!super.open ()) {
        return false;
      }
      hdcOpenUnit = abuNumber;
      if (hdcOpenDialog == null) {
        hdcOpenDialog = new OpenDialog ();
        hdcOpenDialog.setReadOnly (Settings.sgsGetOnOff ("sareadonly"));
        hdcOpenDialog.setReboot (Settings.sgsGetOnOff ("saappreboot"));
        for (File[] files : hdcOpenHistory) {
          hdcOpenDialog.addHistory (files);
        }
        hdcOpenHistory.clear ();
      }
      hdcOpenDialog.rescanCurrentDirectory ();  //挿入されているファイルが変わると選択できるファイルも変わるのでリストを作り直す
      XEiJ.pnlExitFullScreen (true);
      hdcOpenDialog.setVisible (true);
      return true;
    }  //unit.open()

    //success = unit.insert (path, writeProtected)
    //  挿入する
    @Override protected boolean insert (String path, boolean writeProtected) {
      if (hdcIsInserted (path)) {  //既に挿入されている
        return false;
      }
      if (!super.insert (path, writeProtected)) {  //挿入できなかった
        return false;
      }
      //hdcHDMaxを更新する
      if (hdcHDMax <= abuNumber) {
        hdcHDMax = abuNumber + 1;
      }
      return true;
    }  //unit.insert(String)

    //loaded = unit.load (path)
    //  読み込む
    @Override protected boolean load (String path) {
      hduMedia = HDMedia.hdmPathToMedia (path, hduImage);
      if (hduMedia == null) {  //読み込めない
        return false;
      }
      hduWritten = false;
      hdcAddHistory (new File (path).getAbsoluteFile ());
      System.out.println (Multilingual.mlnJapanese ?
                          path + " を sa" + abuNumber + " に接続しました" :
                          path + " was connected to sa" + abuNumber);
      return true;
    }

  }  //class HDUnit



}  //class HDC