HFS.java
     1: //========================================================================================
     2: //  HFS.java
     3: //    en:Host file system interface -- It makes an arbitrary directory of the host machine into the boot drive of the Human68k.
     4: //    ja:ホストファイルシステムインタフェイス -- ホストマシンの任意のディレクトリをHuman68kの起動ドライブにします。
     5: //  Copyright (C) 2003-2017 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: //  http://stdkmd.com/xeij/
    11: //========================================================================================
    12: 
    13: //----------------------------------------------------------------------------------------
    14: //  0x00e9f020  HFS ホストファイルシステムインタフェイス
    15: //
    16: //  種類
    17: //    仮想拡張ボード
    18: //
    19: //  機能
    20: //    ホストマシンの任意のディレクトリをHuman68kのリモートデバイスにする
    21: //    ホストマシンの任意のディレクトリからHuman68kを起動する
    22: //
    23: //  組み込み
    24: //    SWITCH BOOT=ROM$E9F020
    25: //
    26: //----------------------------------------------------------------------------------------
    27: 
    28: package xeij;
    29: 
    30: 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
    31: import java.io.*;  //BufferedInputStream,BufferedOutputStream,BufferedReader,BufferedWriter,File,FileInputStream,FileNotFoundException,FileReader,InputStream,InputStreamReader,IOException,OutputStreamWriter,RandomAccessFile
    32: import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,InterruptedException,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
    33: import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap
    34: import javax.swing.*;  //AbstractSpinnerModel,Box,ButtonGroup,DefaultListModel,ImageIcon,JApplet,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
    35: 
    36: public class HFS {
    37: 
    38:   public static final boolean HFS_DEBUG_TRACE = false;
    39:   public static final boolean HFS_DEBUG_FILE_INFO = false;
    40: 
    41:   //コマンドトレース
    42:   public static final boolean HFS_COMMAND_TRACE = false;
    43:   public static boolean hfsCommandTraceOn;
    44: 
    45:   //スレッド
    46:   public static final boolean HFS_USE_THREAD = true;  //true=ホストマシンのファイルを操作するスレッドをコアから分離する
    47:   public static final long HFS_THREAD_DELAY = 0L;
    48:   public static java.util.Timer hfsTimer;  //Timerだけだとjavax.swing.Timerと紛らわしい
    49:   //  状態
    50:   //
    51:   //                   Call
    52:   //    IDLE  →  X68K  →  DONE  →  IDLE
    53:   //
    54:   //                   Call                Host
    55:   //    IDLE  →  X68K  →  HOST  →  BUSY  →  DONE  →  IDLE
    56:   //
    57:   //                   Call                Host      X68k
    58:   //    IDLE  →  X68K  →  HOST  →  BUSY  →  X68K  →  DONE  →  IDLE
    59:   //
    60:   //                   Call                Host      X68k                Host
    61:   //    IDLE  →  X68K  →  HOST  →  BUSY  →  X68K  →  HOST  →  BUSY  →  DONE  →  IDLE
    62:   //
    63:   //                   Call                Host      X68k                Host      X68k
    64:   //    IDLE  →  X68K  →  HOST  →  BUSY  →  X68K  →  HOST  →  BUSY  →  X68K  →  DONE  →  IDLE
    65:   //
    66:   //  Call,X68k
    67:   //    X68000側の処理。X68Kで呼び出され、HOSTまたはDONEで終了する
    68:   //  Host
    69:   //    ホスト側の処理。BUSYで呼び出され、X68KまたはDONEで終了する
    70:   //
    71:   //  複数のデバイスコマンドが並列に動作することはない
    72:   //  BUSYのとき割り込みを受け付けるがディスクアクセス中はスーパーバイザモードなのでTimer-DでHuman68kのスレッドが切り替わることはない
    73:   //
    74:   //  DOSコールのパラメータに存在しないアドレスを指定するとX68000側の処理中にバスエラーが発生する可能性がある
    75:   //  バスエラーが発生したときはhfsState=HFS_STATE_IDLEとして強制的に次のコマンドを受け付けられるようにする
    76:   //  X68000側の処理中はホスト側のタスクは存在しないか終了直前にhfsStateを書き換えた後なのでhfsStateの書き換えが衝突することはない
    77:   //
    78:   public static final int HFS_STATE_IDLE = 0;  //何もしていない
    79:   public static final int HFS_STATE_X68K = 1;  //X68000側の処理中
    80:   public static final int HFS_STATE_HOST = 2;  //ホスト側のタスクの起動中
    81:   public static final int HFS_STATE_BUSY = 3;  //ホスト側の処理中
    82:   public static final int HFS_STATE_DONE = 4;  //コマンド終了
    83:   public static int hfsState;  //状態
    84: 
    85:   //先読み・遅延書き込みバッファ
    86:   public static final boolean HFS_BUFFER_TRACE = false;
    87:   public static final int HFS_BUFFER_SIZE = 16384;  //先読み・遅延書き込みバッファのサイズ
    88: 
    89:   //ROM
    90:   //    +0   l  +20      IPL起動ハンドル
    91:   //                       IPL起動ルーチンのアドレス
    92:   //                       IPL起動ハンドルのアドレスでROM起動するとIPLROMがIPL起動ルーチンを呼び出してくれる
    93:   //                       IPL起動ハンドルよりもIPL起動ルーチンのほうが後ろ(大きいアドレス)に配置されていなければならない
    94:   //                       IPLROMはチェックしていないがHuman68kがこの条件でIPL起動ハンドルの有効性を確認している
    95:   //                       (RAMディスクドライバでは使用しない)
    96:   //    +4   l  +24      デバイスドライバ組み込みハンドル
    97:   //                       デバイスドライバ組み込みルーチンのアドレス
    98:   //                       Human68kが(IPL起動ルーチンのアドレス-16).lから取り出す
    99:   //    +8   l  0        デバイスドライバ組み込みパラメータ
   100:   //                       デバイスドライバ組み込みルーチンを呼び出すときにd0に入れておく値
   101:   //                       Human68kがIPL起動ルーチンのアドレス-12から取り出す
   102:   //                       SCSIボードの場合はサービスルーチン(IOCS _SCSIDRV)のベクタが入っている
   103:   //                       (RAMディスクドライバでは使用しない)
   104:   //    +12  l  'Huma'   Human68kマジック
   105:   //         l  'n68k'     'Human68k'
   106:   //                       Human68kが(IPL起動ルーチンのアドレス-8).l[2]を見てデバイスドライバ組み込みルーチンの存在を確認している
   107:   //    +20  w  HFSBOOT    IPL起動ルーチン
   108:   //         w  RTS        ROM起動なので先頭は0x60でなくてよい(RAM起動のときは0x60で始まっていなければならない)
   109:   //                       (RAMディスクドライバでは使用しない)
   110:   //    +24  w  HFSINST  デバイスドライバ組み込みルーチン
   111:   //         w  RTS
   112:   //    +28  l  'JHFS'   HFSマジック
   113:   //    +32
   114:   public static final int HFS_ADDRESS            = 0x00e9f020;
   115:   public static final int HFS_BOOT_HANDLE        = HFS_ADDRESS + 0;  //IPL起動ハンドル
   116:   public static final int HFS_INSTALL_HANDLE     = HFS_ADDRESS + 4;  //デバイスドライバ組み込みハンドル
   117:   public static final int HFS_INSTALL_PARAMETER  = HFS_ADDRESS + 8;  //デバイスドライバ組み込みパラメータ
   118:   public static final int HFS_HUMAN68K_MAGIC     = HFS_ADDRESS + 12;  //Human68kマジック
   119:   public static final int HFS_BOOT_ROUTINE       = HFS_ADDRESS + 20;  //IPL起動ルーチン
   120:   public static final int HFS_INSTALL_ROUTINE    = HFS_ADDRESS + 24;  //デバイスドライバ組み込みルーチン
   121:   public static final int HFS_MAGIC              = HFS_ADDRESS + 28;  //HFSマジック
   122:   public static final int HFS_ROM_SIZE           = 32;  //ROMサイズ
   123: 
   124:   //デバイスドライバ
   125:   //    +0   l  -1       ネクストデバイスドライバハンドル
   126:   //    +4   w  0x0000   デバイスタイプ
   127:   //    +6   l  +26      ストラテジハンドル
   128:   //    +10  l  +24      インタラプトハンドル
   129:   //    +14  l  '\1HFS'  デバイス名
   130:   //         l  '    '
   131:   //    +22  w  0        ドライブ番号
   132:   //    +24  w  HFSSTR   ストラテジルーチン
   133:   //    +26  w  RTS
   134:   //    +28  w  HFSINT   インタラプトルーチン
   135:   //    +30  w  RTS
   136:   //    +32
   137:   public static final int HFS_NEXT_DEVICE        = 0;  //ネクストデバイスドライバハンドル
   138:   public static final int HFS_DEVICE_TYPE        = 4;  //デバイスタイプ
   139:   public static final int HFS_STRATEGY_HANDLE    = 6;  //ストラテジハンドル
   140:   public static final int HFS_INTERRUPT_HANDLE   = 10;  //インタラプトハンドル
   141:   public static final int HFS_DEVICE_NAME        = 14;  //デバイス名
   142:   public static final int HFS_DRIVE_NUMBER       = 22;  //ドライブ番号
   143:   public static final int HFS_STRATEGY_ROUTINE   = 24;  //ストラテジルーチン
   144:   public static final int HFS_INTERRUPT_ROUTINE  = 28;  //インタラプトルーチン
   145:   public static final int HFS_DEVICE_SIZE        = 32;  //デバイスサイズ
   146: 
   147:   //ユニット
   148:   public static final int HFS_MIN_UNITS = 1;  //最小ユニット数
   149:   public static final int HFS_MAX_UNITS = 16;  //最大ユニット数
   150:   public static final String HFS_DUMMY_UNIT_NAME = "*HFS*";
   151:   public static final HFUnit[] hfsUnitArray = new HFUnit[HFS_MAX_UNITS];  //ユニットの配列
   152:   public static int hfsBootUnit;  //起動ユニット番号
   153:   public static final int[] hfsDeviceUnitArray = new int[HFS_MAX_UNITS];  //ユニット番号の変換表。リクエストヘッダのユニット番号→本来のユニット番号
   154:   public static int hfsDeviceUnitCount;  //起動時に接続されていたユニットの数
   155: 
   156:   //メニュー
   157:   public static JMenu hfsMenu;
   158: 
   159:   //ダイアログ
   160:   public static JDialog hfsOpenDialog;  //ファイルチューザーダイアログ
   161:   public static JFileChooser2 hfsOpenFileChooser;  //ファイルチューザー
   162:   public static int hfsOpenUnit;  //開くユニットの番号
   163:   public static boolean hfsOpenWriteProtect;  //true=ライトプロテクトモードで開く
   164:   public static javax.swing.filechooser.FileFilter hfsOpenFileFilter;  //ファイルフィルタ
   165: 
   166:   public static File hfsLastFile;  //最後にアクセスしたファイル=次にファイルチューザーを開いたときに表示するディレクトリ。コマンドラインのみ
   167: 
   168:   //デバイスドライバ
   169:   public static int hfsDeviceHeader;  //デバイスヘッダのアドレス
   170:   public static int hfsRequestHeader;  //a5  実行中のコマンドのリクエストヘッダのアドレス
   171:   public static int hfsRequest1Number;  //<(a5+1).b:リクエストヘッダのユニット番号
   172:   public static int hfsRequest2Command;  //<(a5+2).b:コマンドコード
   173:   public static int hfsRequest13Mode;  //<(a5+13).b:モードなど
   174:   public static int hfsRequest14Namests;  //<(a5+14).l:_NAMESTS形式のファイル名など
   175:   public static int hfsRequest18Param;  //<(a5+18).l:追加のパラメータ
   176:   public static int hfsRequest22Fcb;  //<(a5+22).l:FCBテーブルのアドレスなど
   177:   public static int hfsRequest3Error;  //>(a5+3).w:エラーコード
   178:   public static int hfsRequest18Result;  //>(a5+18).l:リザルトステータス
   179:   public static HFUnit hfsRequestUnit;  //コマンドを実行するユニット
   180: 
   181:   //TwentyOne.x
   182:   //!!! 工事中
   183:   public static final boolean HFS_USE_TWENTY_ONE = false;  //true=TwentyOne.xのオプションによって動作を変更する
   184:   public static final int HFS_TW_VERBOSE_MODE         = 1 << 31;  //+V バーボーズモード
   185:   public static final int HFS_TW_CASE_SENSITIVE       = 1 << 30;  //+C 大文字と小文字を区別する
   186:   public static final int HFS_TW_SPECIAL_CHARACTER    = 1 << 29;  //+S 特殊文字が使える
   187:   public static final int HFS_TW_MULTI_PERIOD         = 1 << 28;  //+P ピリオドが複数使える
   188:   public static final int HFS_TW_NOT_TWENTY_ONE       = 1 << 27;  //-T 21バイト比較しない
   189:   public static final int HFS_TW_DISABLE_PRINTER_ECHO = 1 << 26;  //+D プリンタエコーを無効化する
   190:   public static final int HFS_TW_USE_SYSROOT          = 1 << 25;  //+R $SYSROOTを使う
   191:   public static final int HFS_TW_WARN_CASE_MISMATCH   = 1 << 24;  //+W +Cのとき大文字と小文字だけが違う名前を警告する
   192:   public static final int HFS_TW_USE_STRONG_SYSROOT   = 1 << 23;  //+r $SYSROOTを使う。'\\'で始まる名前でも使う
   193:   //                                                    bit22-16       予約
   194:   //                                                    bit15-0     -B バッファ数
   195:   public static int hfsTwentyOneOption;  //TwentyOne.xのオプション
   196: 
   197:   //hfsInit ()
   198:   //  ホストファイルシステムインタフェイスを初期化する
   199:   public static void hfsInit () {
   200: 
   201:     //コマンドトレース
   202:     if (HFS_COMMAND_TRACE) {
   203:       hfsCommandTraceOn = false;
   204:     }
   205: 
   206:     //スレッド
   207:     if (HFS_USE_THREAD) {
   208:       hfsTimer = new java.util.Timer ();  //Timerだけだとjavax.swing.Timerと紛らわしい
   209:     }
   210: 
   211:     //TwentyOne.x
   212:     if (HFS_USE_TWENTY_ONE) {
   213:       hfsTwentyOneOption = 0;
   214:     }
   215: 
   216:     //ユニット
   217:     //hfsUnitArray = new HFUnit[HFS_MAX_UNITS];
   218:     hfsBootUnit = 0;
   219:     //hfsDeviceUnitArray = new int[HFS_MAX_UNITS];
   220:     hfsDeviceUnitCount = 0;
   221:     for (int u = 0; u < HFS_MAX_UNITS; u++) {
   222:       HFUnit unit = hfsUnitArray[u] = new HFUnit (u);
   223:       if (u < HFS_MIN_UNITS) {
   224:         unit.connect (false);  //ドライブ0は最初から接続されていて切り離せない
   225:       }
   226:     }
   227: 
   228:     //HFメニュー
   229:     hfsMenu = ComponentFactory.createMenu ("HFS");  //横に長いとサブメニューを開きにくいので短くする
   230: 
   231:     //ローカルでないときは使用不可
   232:     if (!XEiJ.prgIsLocal) {  //ローカルでないとき
   233:       ComponentFactory.setEnabled (hfsMenu, false);
   234:       return;
   235:     }
   236: 
   237:     //パラメータ
   238:     hfsLastFile = new File (".");  //カレントディレクトリ
   239:     for (int u = 0; u < HFS_MAX_UNITS; u++) {
   240:       HFUnit unit = hfsUnitArray[u];
   241:       String path = Settings.sgsCurrentMap.get ("hf" + u);
   242:       boolean userWriteProtect = false;
   243:       if (path.toUpperCase ().endsWith (":R")) {  //書き込み禁止モードで開く
   244:         path = path.substring (0, path.length () - 2);
   245:         userWriteProtect = true;
   246:       }
   247:       boolean hostWriteProtect = !new File (path).canWrite ();
   248:       if (path.length () != 0) {
   249:         unit.connect (true);  //接続されていなければ接続する
   250:         if (unit.insert (path)) {  //挿入できた
   251:           if (userWriteProtect || hostWriteProtect) {  //書き込みを禁止する
   252:             unit.protect (false);  //開くときに書き込みを禁止した場合はイジェクトするまで書き込みを許可できない
   253:           }
   254:           hfsLastFile = new File (path);
   255:         }
   256:       }
   257:     }
   258: 
   259:     //メニュー
   260:     ComponentFactory.addComponents (
   261:       hfsMenu,
   262:       ComponentFactory.createHorizontalBox (
   263:         Multilingual.mlnText (ComponentFactory.createLabel ("Host File System"),
   264:                               "ja", "ホストファイルシステム")),
   265:       ComponentFactory.createHorizontalSeparator ()
   266:       );
   267:     for (HFUnit unit : hfsUnitArray) {
   268:       hfsMenu.add (unit.getMenuBox ());
   269:     }
   270:     if (HFS_COMMAND_TRACE) {
   271:       ComponentFactory.addComponents (
   272:         hfsMenu,
   273:         ComponentFactory.createHorizontalSeparator (),
   274:         Multilingual.mlnText (
   275:           ComponentFactory.createCheckBoxMenuItem (hfsCommandTraceOn, "HFS Command Trace", new ActionListener () {
   276:             @Override public void actionPerformed (ActionEvent ae) {
   277:               hfsCommandTraceOn = ((JCheckBoxMenuItem) ae.getSource ()).isSelected ();
   278:             }
   279:           }),
   280:           "ja", "HFS コマンドトレース")
   281:         );
   282:     }
   283: 
   284:     //ROM
   285:     MainMemory.mmrWl (HFS_BOOT_HANDLE,        HFS_BOOT_ROUTINE);  //IPL起動ハンドル
   286:     MainMemory.mmrWl (HFS_INSTALL_HANDLE,     HFS_INSTALL_ROUTINE);  //デバイスドライバ組み込みハンドル
   287:     MainMemory.mmrWl (HFS_INSTALL_PARAMETER,  0);  //デバイスドライバ組み込みパラメータ
   288:     MainMemory.mmrWl (HFS_HUMAN68K_MAGIC,     'H' << 24 | 'u' << 16 | 'm' << 8 | 'a');  //Human68kマジック
   289:     MainMemory.mmrWl (HFS_HUMAN68K_MAGIC + 4, 'n' << 24 | '6' << 16 | '8' << 8 | 'k');
   290:     MainMemory.mmrWl (HFS_BOOT_ROUTINE,       XEiJ.EMX_OPCODE_HFSBOOT << 16 | 0x4e75);  //IPL起動ルーチン
   291:     MainMemory.mmrWl (HFS_INSTALL_ROUTINE,    XEiJ.EMX_OPCODE_HFSINST << 16 | 0x4e75);  //デバイスドライバ組み込みルーチン
   292:     MainMemory.mmrWl (HFS_MAGIC,              'J' << 24 | 'H' << 16 | 'F' << 8 | 'S');  //HFSマジック
   293: 
   294:     //開くダイアログ
   295:     hfsOpenDialog = null;
   296:     hfsOpenFileChooser = null;
   297:     hfsOpenUnit = 0;
   298:     hfsOpenWriteProtect = false;
   299:     hfsOpenFileFilter = new javax.swing.filechooser.FileFilter () {  //java.io.FileFilterと紛らわしい
   300:       @Override public boolean accept (File file) {
   301:         if (file.isDirectory ()) {
   302:           return true;
   303:         }
   304:         String path = file.getPath ();
   305:         if (hfsIsInserted (path)) {  //既に挿入されている
   306:           return false;
   307:         }
   308:         return file.getName ().toUpperCase ().equals ("HUMAN.SYS");
   309:       }
   310:       @Override public String getDescription () {
   311:         return Multilingual.mlnJapanese ? "ディレクトリまたは HUMAN.SYS" : "Directories or HUMAN.SYS";
   312:       }
   313:     };
   314: 
   315:     hfsState = HFS_STATE_IDLE;
   316: 
   317:   }  //hfsInit()
   318: 
   319:   //hfsTini ()
   320:   //  ホストファイルシステムインタフェイスの後始末
   321:   //  開いたままのファイルがあれば閉じる
   322:   //  スレッドを停止させる
   323:   public static void hfsTini () {
   324:     if (HFS_USE_THREAD) {
   325:       if (hfsTimer != null) {
   326:         hfsTimer.schedule (new TimerTask () {
   327:           @Override public void run () {
   328:             for (HFUnit unit : hfsUnitArray) {
   329:               unit.hfuTini ();
   330:             }
   331:             hfsTimer.cancel ();
   332:           }
   333:         }, HFS_THREAD_DELAY);
   334:       }
   335:     } else {
   336:       for (HFUnit unit : hfsUnitArray) {
   337:         unit.hfuTini ();
   338:       }
   339:     }
   340:   }  //hfsTini()
   341: 
   342:   //inserted = hfsIsInserted (path)
   343:   //  パスで指定したファイルが既に挿入されているか調べる
   344:   public static boolean hfsIsInserted (String path) {
   345:     for (HFUnit unit : hfsUnitArray) {
   346:       if (unit != null &&
   347:           unit.abuConnected &&  //接続されている
   348:           unit.abuInserted &&  //挿入されている
   349:           unit.abuPath.equals (path)) {  //パスが一致している
   350:         return true;  //既に挿入されている
   351:       }
   352:     }
   353:     return false;  //まだ挿入されていない
   354:   }  //hfsIsInserted(String)
   355: 
   356:   //hfsReset ()
   357:   //  HFSのリセット
   358:   //  開いたままのファイルがあれば閉じる
   359:   public static void hfsReset () {
   360:     for (HFUnit unit : hfsUnitArray) {
   361:       unit.hfuTini ();
   362:     }
   363:   }  //hfsReset()
   364: 
   365:   //success = hfsIPLBoot ()
   366:   //  IPL起動ルーチン
   367:   public static boolean hfsIPLBoot () {
   368:     return hfsUnitArray[hfsBootUnit].hfuIPLBoot ();
   369:   }  //hfsIPLBoot()
   370: 
   371:   //hfsInstall ()
   372:   //  デバイスドライバ組み込みルーチン
   373:   //  Human68kがデバイスドライバを組み込むときに呼び出す
   374:   //  メインメモリにデバイスヘッダを構築する
   375:   //  d2=-1を返すまで組み込みルーチンと初期化コマンドが繰り返して呼び出される
   376:   //  1つのインタフェイスで種類の異なる複数のデバイスドライバを組み込むことができる
   377:   //  <d0.l  デバイスドライバ組み込みパラメータ
   378:   //  <d2.l  フラグ。初回は0。2回目以降は前回返したd2の値がそのまま入っている
   379:   //         SCSIドライバの場合
   380:   //           次に接続を確認するSCSI-ID
   381:   //  <a1.l  デバイスヘッダを構築するアドレス
   382:   //  >d2.l  フラグ。-1=終了
   383:   //         SCSIドライバの場合
   384:   //           入力されたd2以上のSCSI-IDを持つ認識可能なSCSI機器が接続されているとき
   385:   //             そのSCSI機器のSCSI-ID+1
   386:   //           入力されたd2以上のSCSI-IDを持つ認識可能なSCSI機器が接続されていないとき
   387:   //             -1
   388:   public static void hfsInstall () throws M68kException {
   389:     if (XEiJ.regRn[2] != 0) {
   390:       XEiJ.regRn[2] = -1;
   391:     } else {
   392:       XEiJ.regRn[2] = 1;
   393:       int a1 = XEiJ.regRn[9];
   394:       hfsDeviceHeader = a1;
   395:       MC68060.mmuWriteLongData (a1 + HFS_NEXT_DEVICE,       -1, XEiJ.regSRS);  //ネクストデバイスドライバハンドル
   396:       MC68060.mmuWriteWordData (a1 + HFS_DEVICE_TYPE,       0x2000, XEiJ.regSRS);  //デバイスタイプ。リモートデバイス、IOCTRL不可
   397:       MC68060.mmuWriteLongData (a1 + HFS_STRATEGY_HANDLE,   a1 + HFS_STRATEGY_ROUTINE, XEiJ.regSRS);  //ストラテジハンドル
   398:       MC68060.mmuWriteLongData (a1 + HFS_INTERRUPT_HANDLE,  a1 + HFS_INTERRUPT_ROUTINE, XEiJ.regSRS);  //インタラプトハンドル
   399:       MC68060.mmuWriteLongData (a1 + HFS_DEVICE_NAME,       0x01 << 24 | 'X' << 16 | 'E' << 8 | 'I', XEiJ.regSRS);  //デバイス名
   400:       MC68060.mmuWriteLongData (a1 + HFS_DEVICE_NAME + 4,   'J' << 24 | 'H' << 16 | 'F' << 8 | 'S', XEiJ.regSRS);
   401:       MC68060.mmuWriteLongData (a1 + HFS_STRATEGY_ROUTINE,  XEiJ.EMX_OPCODE_HFSSTR << 16 | 0x4e75, XEiJ.regSRS);  //ストラテジルーチン
   402:       MC68060.mmuWriteLongData (a1 + HFS_INTERRUPT_ROUTINE, XEiJ.EMX_OPCODE_HFSINT << 16 | 0x4e75, XEiJ.regSRS);  //インタラプトルーチン
   403:     }
   404:   }  //hfsInstall()
   405: 
   406:   //hfsStrategy ()
   407:   //  デバイスドライバのストラテジルーチン
   408:   public static void hfsStrategy () throws M68kException {
   409:     hfsRequestHeader = XEiJ.regRn[13];  //リクエストヘッダのアドレス
   410: 
   411:     if (false && HFS_DEBUG_TRACE) {
   412:       System.out.printf ("hfsStrategy\n");
   413:       System.out.printf ("  hfsRequestHeader = %08x\n", hfsRequestHeader);
   414:     }
   415: 
   416:   }  //hfsStrategy()
   417: 
   418:   //wait = hfsInterrupt ()
   419:   //  デバイスドライバのインタラプトルーチン
   420:   //  trueを返したときWAIT命令を繰り返す
   421:   public static boolean hfsInterrupt () throws M68kException {
   422: 
   423:     if (false && HFS_DEBUG_TRACE) {
   424:       System.out.printf ("hfsInterrupt\n");
   425:       System.out.printf ("  hfsRequestHeader = %08x\n", hfsRequestHeader);
   426:       System.out.printf ("  hfsState = %d\n", hfsState);
   427:     }
   428: 
   429:     int a5 = hfsRequestHeader;  //リクエストヘッダのアドレス。インタラプトルーチンが呼び出された時点でa5に入っているとは限らない
   430: 
   431:     if (hfsState == HFS_STATE_IDLE) {  //新たなコマンド
   432: 
   433:       if (HFS_DEBUG_TRACE) {
   434:         int number = MC68060.mmuPeekByteZeroData (a5 + 1, XEiJ.regSRS);
   435:         int command = MC68060.mmuPeekByteZeroData (a5 + 2, XEiJ.regSRS);
   436:         int mode = MC68060.mmuPeekByteZeroData (a5 + 13, XEiJ.regSRS);
   437:         int namests = MC68060.mmuPeekLongData (a5 + 14, XEiJ.regSRS);
   438:         int param = MC68060.mmuPeekLongData (a5 + 18, XEiJ.regSRS);
   439:         int fcb = MC68060.mmuPeekLongData (a5 + 22, XEiJ.regSRS);
   440:         if (!(hfsRequest2Command == 0x57 ||  //mediacheck
   441:               hfsRequest2Command == 0x4c && param == 1)) {  //read(1バイト)
   442:           for (int i = 0; i < 26; i++) {
   443:             System.out.printf (" %02x", MC68060.mmuPeekByteZeroData (a5 + i, XEiJ.regSRS));
   444:           }
   445:           System.out.println ();
   446:           System.out.printf ("  Number: %02x\n", number);
   447:           System.out.printf ("  Command: %02x\n", command);
   448:           System.out.printf ("  Mode: %02x\n", mode);
   449:           System.out.printf ("  Namests: %08x:", namests);
   450:           for (int i = 0; i < 88; i++) {
   451:             System.out.printf (" %02x", MC68060.mmuPeekByteZeroData (namests + i, XEiJ.regSRS));
   452:           }
   453:           System.out.println ();
   454:           System.out.printf ("  Param: %08x\n", param);
   455:           System.out.printf ("  Fcb: %08x:", fcb);
   456:           for (int i = 0; i < 96; i++) {
   457:             System.out.printf (" %02x", MC68060.mmuPeekByteZeroData (fcb + i, XEiJ.regSRS));
   458:           }
   459:           System.out.println ();
   460:         }
   461:       }
   462: 
   463:       //  アドレスを配列のインデックスやマップのキーとして使うときはマスクするのを忘れないこと
   464:       //  主にアドレスとして使うパラメータでもそれ以外の使い方をする場合があるのでここではマスクしない
   465:       hfsRequest1Number = MC68060.mmuReadByteZeroData (a5 + 1, XEiJ.regSRS);  //リクエストヘッダのユニット番号
   466:       hfsRequest2Command = MC68060.mmuReadByteZeroData (a5 + 2, XEiJ.regSRS) & 0x7f;  //コマンドコード。ベリファイフラグは無視する
   467:       hfsRequest13Mode = MC68060.mmuReadByteZeroData (a5 + 13, XEiJ.regSRS);  //モードなど
   468:       hfsRequest14Namests = MC68060.mmuReadLongData (a5 + 14, XEiJ.regSRS);  //_NAMESTS形式のファイル名など
   469:       hfsRequest18Param = MC68060.mmuReadLongData (a5 + 18, XEiJ.regSRS);  //追加のパラメータ
   470:       hfsRequest22Fcb = MC68060.mmuReadLongData (a5 + 22, XEiJ.regSRS);  //FCBテーブルのアドレスなど
   471:       hfsRequest3Error = 0;
   472:       hfsRequest18Result = 0;
   473: 
   474:       if (hfsRequest2Command == 0x40) {  //初期化コマンド
   475: 
   476:         //0x40 initialize 初期化
   477:         //  リクエストヘッダ
   478:         //       0.b  i   22
   479:         //       2.b  i   コマンドコード。0x40
   480:         //       3.b  o   エラーコード下位
   481:         //       4.b  o   エラーコード上位
   482:         //      13.b  o   ユニット数
   483:         //      14.l  o   デバイスドライバの末尾
   484:         //      18.l  i   CONFIG.SYSに書かれた引数のアドレス。デバイス名,0,引数1,0,…,引数n,0,0。起動デバイスのときは0,0のみ
   485:         //      22.b  i   内部ドライブ番号(1=A:)
   486:         //            o   起動ユニット番号(1~)
   487:         //    (23バイト)
   488:         //  ユニット数の条件
   489:         //    ユニット数は1以上でなければならない
   490:         //    ユニット数が0になるときはエラーコード下位で0以外の値を返すこと
   491:         //  デバイスドライバの長さの条件
   492:         //    ストラテジルーチンとインタラプトルーチンがデバイスドライバの中に書かれている必要はないが、
   493:         //    デバイスドライバの長さはデバイスヘッダの22バイトを含めて34バイト以上でなければならない
   494:         //  Human68kをリモートデバイスから起動しようとすると暴走するバグの解説と対策
   495:         //    解説
   496:         //      RAMまたはROMから起動したとき、Human68kは起動ドライブのデバイスドライバを初期化した後にDISK2HDを初期化する
   497:         //      Human68kはDISK2HDを初期化するときリクエストヘッダの領域を再利用するが、初期化コマンドを設定し直すことを忘れている
   498:         //      DISK2HDなどのブロックデバイスの初期化コマンドは0x00だが、リモートデバイスの初期化コマンドは0x40である
   499:         //      すなわち、リモートデバイスから起動するとDISK2HDに0x40というブロックデバイスには存在しないコマンドが渡される
   500:         //      DISK2HDはコマンドの範囲をチェックしておらず、範囲外の0x40が渡されるとジャンプテーブルから外れて暴走する
   501:         //      human302の場合は0x000109a4+0x40*4=0x00010aa4にある命令列MOVE.B (A6),D1;BEQ.S *+4のコード0x12166702にジャンプする
   502:         //      2MB以上積んでいれば0x00166702にはRAMがあり、アドレスも偶数なので即エラーとならず、スーパーバイザモードで突っ走る
   503:         //    対策
   504:         //      リモートデバイスを起動可能にするときは初期化コマンドでリクエストヘッダのコマンドを0x00に書き換える
   505:         //      起動デバイスの初期化コマンドは複数回呼ばれる場合があって最後の1回だけ書き換えれば良いのだが、
   506:         //      毎回0x40が設定されてから呼び出されるので毎回0x00に書き換えてしまっても問題ない
   507:         //      最後に呼び出されたときに書き込んだ0x00がDISK2HDデバイスドライバの初期化コマンドとして使用される
   508:         if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
   509:           System.out.printf ("%08x initialize(internaldrive=0x%02,param=0x%08x)\n", XEiJ.regPC0, hfsRequest22Fcb >>> 24, hfsRequest18Param);
   510:         }
   511:         MC68060.mmuWriteByteData (a5 + 2, 0x00, XEiJ.regSRS);  //DISK2HDの初期化コマンド
   512:         //起動時に接続されていたユニットだけをドライブとして登録する
   513:         //  ユニット番号がずれるのでユニット番号の変換表を作る
   514:         //  起動ユニット番号も変換する
   515:         hfsDeviceUnitCount = 0;
   516:         int bootUnit = -1;
   517:         for (int u = 0; u < HFS_MAX_UNITS; u++) {
   518:           if (hfsUnitArray[u].isConnected ()) {
   519:             if (u == hfsBootUnit) {
   520:               bootUnit = hfsDeviceUnitCount;
   521:             }
   522:             hfsDeviceUnitArray[hfsDeviceUnitCount++] = u;
   523:           }
   524:         }
   525:         if (hfsDeviceUnitCount > 0 && bootUnit >= 0) {  //起動ユニットが接続されている
   526:           MC68060.mmuWriteByteData (a5 + 13, hfsDeviceUnitCount, XEiJ.regSRS);  //ユニット数
   527:           MC68060.mmuWriteLongData (a5 + 14, hfsDeviceHeader + 34, XEiJ.regSRS);  //デバイスドライバの末尾
   528:           MC68060.mmuWriteByteData (a5 + 22, 1 + bootUnit, XEiJ.regSRS);  //起動ユニット番号(1~)
   529:         } else {  //起動ユニットが接続されていない
   530:           hfsRequest3Error = HFUnit.DEV_ABORT | HFUnit.DEV_INVALID_UNIT_NUMBER;  //ユニットを増やすにはリセットする必要があるので中止のみ
   531:           hfsRequest18Result = -1;
   532:         }
   533:         hfsState = HFS_STATE_DONE;
   534: 
   535:       } else {  //初期化コマンド以外のコマンド
   536: 
   537:         if (hfsRequest1Number < hfsDeviceUnitCount) {  //ユニットがある
   538:           //リクエストヘッダのユニット番号をHFSのユニット番号に変換する
   539:           hfsRequestUnit = hfsUnitArray[hfsDeviceUnitArray[hfsRequest1Number]];
   540:           hfsState = HFS_STATE_X68K;  //X68000側の処理中
   541:           hfsRequestUnit.hfuCall ();
   542:         } else {  //ユニットがない
   543:           hfsRequest3Error = HFUnit.DEV_ABORT | HFUnit.DEV_INVALID_UNIT_NUMBER;  //ユニットを増やすにはリセットする必要があるので中止のみ
   544:           hfsRequest18Result = -1;
   545:           hfsState = HFS_STATE_DONE;
   546:         }
   547: 
   548:       }
   549: 
   550:     }  //if hfsState==HFS_STATE_IDLE
   551: 
   552:     if (HFS_USE_THREAD) {
   553:       while (hfsState != HFS_STATE_DONE) {
   554:         while (hfsState == HFS_STATE_X68K) {  //X68000側の処理中
   555:           hfsRequestUnit.hfuCallX68k ();
   556:         }
   557:         if (hfsState == HFS_STATE_HOST) {  //ホスト側のタスクの起動中
   558:           hfsState = HFS_STATE_BUSY;  //ホスト側の処理中
   559:           hfsTimer.schedule (new TimerTask () {
   560:             @Override public void run () {
   561:               hfsRequestUnit.hfuCallHost ();
   562:             }
   563:           }, HFS_THREAD_DELAY);
   564:         }
   565:         //ここでホスト側の処理が一瞬で終わってhfsState==HFS_STATE_X68Kになっている場合がある
   566:         if (hfsState == HFS_STATE_BUSY) {  //ホスト側の処理中
   567:           return true;  //WAITあり
   568:         }
   569:       }
   570:     } else {
   571:       while (hfsState != HFS_STATE_DONE) {
   572:         while (hfsState == HFS_STATE_X68K) {  //X68000側の処理中
   573:           hfsRequestUnit.hfuCallX68k ();
   574:         }
   575:         if (hfsState == HFS_STATE_HOST) {  //ホスト側のタスクの起動中
   576:           hfsState = HFS_STATE_BUSY;  //ホスト側の処理中
   577:         }
   578:         if (hfsState == HFS_STATE_BUSY) {  //ホスト側の処理中
   579:           hfsRequestUnit.hfuCallHost ();
   580:         }
   581:       }
   582:     }
   583: 
   584:     if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
   585:       System.out.printf ("\terror=0x%04x,result=0x%08x\n", hfsRequest3Error, hfsRequest18Result);
   586:     }
   587: 
   588:     MC68060.mmuWriteByteData (a5 +  3, hfsRequest3Error, XEiJ.regSRS);  //エラーコード下位
   589:     MC68060.mmuWriteByteData (a5 +  4, hfsRequest3Error >> 8, XEiJ.regSRS);  //エラーコード上位
   590:     MC68060.mmuWriteLongData (a5 + 18, hfsRequest18Result, XEiJ.regSRS);  //リザルトステータス
   591: 
   592:     hfsState = HFS_STATE_IDLE;
   593:     return false;  //WAITなし
   594:   }  //hfsInterrupt()
   595: 
   596:   //hfsMakeOpenDialog ()
   597:   //  開くダイアログを作る
   598:   //  コマンドラインのみ
   599:   public static void hfsMakeOpenDialog () {
   600:     ActionListener listener = new ActionListener () {
   601:       @Override public void actionPerformed (ActionEvent ae) {
   602:         switch (ae.getActionCommand ()) {
   603:         case JFileChooser.APPROVE_SELECTION:
   604:         case "Reboot from it":  //ここから再起動
   605:           {
   606:             File[] list = hfsOpenFileChooser.getSelectedFiles2 ();
   607:             if (list.length == 0) {
   608:               list = new File[] { hfsOpenFileChooser.getCurrentDirectory () };
   609:             }
   610:             if (list.length > 0) {
   611:               hfsOpenFiles (list, true);
   612:               hfsOpenDialog.setVisible (false);
   613:             }
   614:           }
   615:           break;
   616:         case "Open":  //開く
   617:           {
   618:             File[] list = hfsOpenFileChooser.getSelectedFiles2 ();
   619:             if (list.length == 0) {
   620:               list = new File[] { hfsOpenFileChooser.getCurrentDirectory () };
   621:             }
   622:             if (list.length > 0) {
   623:               hfsOpenFiles (list, false);
   624:               hfsOpenDialog.setVisible (false);
   625:             }
   626:           }
   627:           break;
   628:         case JFileChooser.CANCEL_SELECTION:
   629:         case "Cancel":  //キャンセル
   630:           hfsOpenDialog.setVisible (false);
   631:           break;
   632:         case "Write-Protect":  //書き込み禁止
   633:           hfsOpenWriteProtect = ((JCheckBox) ae.getSource ()).isSelected ();
   634:           break;
   635:         }
   636:       }
   637:     };
   638:     hfsOpenFileChooser = new JFileChooser2 (hfsLastFile);
   639:     hfsOpenFileChooser.setFileFilter (hfsOpenFileFilter);
   640:     hfsOpenFileChooser.setMultiSelectionEnabled (true);  //複数選択可能
   641:     hfsOpenFileChooser.setControlButtonsAreShown (false);  //デフォルトのボタンを消す
   642:     hfsOpenFileChooser.addActionListener (listener);
   643:     hfsOpenDialog = Multilingual.mlnTitle (
   644:       ComponentFactory.createModalDialog (
   645:         XEiJ.frmFrame,
   646:         "Open directories to mount on drives of Human68k",
   647:         ComponentFactory.createBorderPanel (
   648:           0, 0,
   649:           ComponentFactory.createVerticalBox (
   650:             hfsOpenFileChooser,
   651:             ComponentFactory.createHorizontalBox (
   652:               Box.createHorizontalStrut (12),
   653:               Box.createHorizontalGlue (),
   654:               Multilingual.mlnText (ComponentFactory.createCheckBox (hfsOpenWriteProtect, "Write-Protect", listener), "ja", "書き込み禁止"),
   655:               Box.createHorizontalGlue (),
   656:               Multilingual.mlnText (ComponentFactory.createButton ("Reboot from it", KeyEvent.VK_R, listener), "ja", "ここから再起動"),
   657:               Box.createHorizontalStrut (12),
   658:               Multilingual.mlnText (ComponentFactory.createButton ("Open", KeyEvent.VK_O, listener), "ja", "開く"),
   659:               Box.createHorizontalStrut (12),
   660:               Multilingual.mlnText (ComponentFactory.createButton ("Cancel", KeyEvent.VK_C, listener), "ja", "キャンセル"),
   661:               Box.createHorizontalStrut (12)
   662:               ),
   663:             Box.createVerticalStrut (12)
   664:             )
   665:           )
   666:         ),
   667:       "ja", "Human68k のドライブに割り当てるディレクトリを開く");
   668:   }  //hfsMakeOpenDialog()
   669: 
   670:   //hfsOpenFiles (list, reset)
   671:   //  開くダイアログで選択されたファイルを開く
   672:   //  コマンドラインのみ
   673:   public static void hfsOpenFiles (File[] list, boolean reset) {
   674:     for (int u = hfsOpenUnit, k = 0; k < list.length; ) {
   675:       if (u >= HFS_MAX_UNITS) {
   676:         reset = false;  //ユニットが足りないときはリセットをキャンセルする
   677:         break;
   678:       }
   679:       HFUnit unit = hfsUnitArray[u];  //ユニット
   680:       if (!unit.abuConnected) {  //接続されていない
   681:         u++;
   682:         continue;
   683:       }
   684:       File file = list[k++];  //ディレクトリまたはHUMAN.SYS
   685:       if (!(file.isDirectory () || file.isFile ())) {  //ディレクトリまたはファイルが存在しない
   686:         reset = false;  //ディレクトリまたはファイルが存在しないときはリセットをキャンセルする
   687:         continue;
   688:       }
   689:       if (unit.insert (file.getPath ())) {  //挿入できた
   690:         if (hfsOpenWriteProtect || !file.canWrite ()) {  //書き込みを禁止する
   691:           unit.protect (false);  //開くときに書き込みを禁止した場合はイジェクトするまで書き込みを許可できない
   692:         }
   693:         hfsLastFile = file;
   694:         u++;
   695:       } else {
   696:         reset = false;  //挿入できないファイルがあったときはリセットをキャンセルする
   697:       }
   698:     }
   699:     if (reset) {
   700:       hfsBootUnit = hfsOpenUnit;
   701:       XEiJ.mpuReset (0xa000, HFS_BOOT_HANDLE);
   702:     }
   703:   }  //hfsOpenFiles(File[],boolean)
   704: 
   705:   //hfsCheckTwentyOneOption ()
   706:   //  TwentyOne.xのオプションを確認する
   707:   //  hfsTwentyOneOptionを更新する
   708:   public void hfsCheckTwentyOneOption () {
   709:     hfsTwentyOneOption = 0;
   710:     if (HFS_USE_TWENTY_ONE) {
   711:       int a = MainMemory.mmrTwentyOneOptionAddress;
   712:       if (0 < a) {
   713:         hfsTwentyOneOption = MC68060.mmuPeekLongData (a, 1);
   714:       }
   715:     }
   716:   }  //hfsCheckTwentyOneOption()
   717: 
   718: 
   719: 
   720:   //========================================================================================
   721:   //$$HFU HFSユニット
   722:   //
   723:   //  _NAMESTS形式のファイル名
   724:   //    0000     0.b      フラグまたはパスの長さ
   725:   //    0001     1.b      内部ドライブ番号(0=A:)
   726:   //    0002     2.b[65]  パス(区切りは'\'または$09)
   727:   //    0043    67.b[8]   ファイル名1
   728:   //    004B    75.b[3]   拡張子
   729:   //    004E    78.b[10]  ファイル名2
   730:   //    0058    88バイト
   731:   //
   732:   //  FCBテーブル
   733:   //                      $0000B520の設定値
   734:   //    0000     0.b      1               FCBテーブルの参照数
   735:   //                                      ハンドラFCB変換テーブルの何箇所から参照されているか(0ならば未使用)
   736:   //    0001     1.b      FCBフラグ       FCBフラグ
   737:   //                                      ブロックデバイス,特殊デバイス    キャラクタデバイス
   738:   //               bit7   0               0                                1
   739:   //               bit6   0               1=_CLOSEしたとき日時を更新する   EOFのON/OFF
   740:   //               bit5   0/1             1=特殊デバイスドライバ           0=COOKEDモード,1=RAWモード
   741:   //               bit4   }               }                                未使用
   742:   //               bit3   }               }                                1=CLOCK
   743:   //               bit2   }               }内部ドライブ番号(0=A:)          1=NUL
   744:   //               bit1   }               }                                1=標準出力
   745:   //               bit0   }               }                                1=標準入力
   746:   //    0002     2.l                      内部DPBテーブル                  デバイスヘッダ
   747:   //    0006     6.l      0               現在のシーク位置
   748:   //    000A    10.l                      シェア管理テーブルのアドレス
   749:   //    000E    14.b      オープンモード  オープンモード
   750:   //                                      bit0~3(アクセスモード)
   751:   //                                              0       読み込み
   752:   //                                              1       書き込み
   753:   //                                              2       読み書き
   754:   //                                              3       _CHMOD,_DELETE,_RENAME
   755:   //                                              4
   756:   //    000F    15バイト
   757:   //    特殊デバイスドライバでは以下の領域を自由に使用してよい
   758:   //    000F    15.b      0               セクタ内で何番目のエントリか。ブロックデバイスのとき$0000C1B4で設定
   759:   //    0010    16.b      0               アクセス中のクラスタ内でのセクタ位置
   760:   //    0011    17.b      0               FAT先頭からのセクタオフセット
   761:   //    0012    18.w      0               FAT先頭からの(現在アクセスしているFATへの)セクタオフセット
   762:   //    0014    20.l      0               現在のデータのセクタ位置
   763:   //    0018    24.l      0               現在のデータのバッファアドレス
   764:   //    001C    28.l      0               ディレクトリのセクタ位置。ブロックデバイスのとき$0000C1B4で設定
   765:   //    0020    32.l      0               次のFCBテーブル
   766:   //    ここからディレクトリエントリ
   767:   //      ブロックデバイスのとき$0000C1B4の中の$0000C1FEでディスクからコピーされる
   768:   //      時刻、日付、先頭クラスタ番号、ファイルサイズはディスク上はリトルエンディアンで、
   769:   //      FCBテーブルにコピーされるときにビッグエンディアンに変換されている
   770:   //    0024    36.b[8]   ファイル名1     デバイス名またはファイル名1
   771:   //    002C    44.b[3]   拡張子          拡張子
   772:   //    002F    47.b      0x20            ファイル属性
   773:   //    0030    48.b[10]  ファイル名2     ファイル名2
   774:   //    003A    58.w      現在時刻        時刻。時<<11|分<<5|秒/2
   775:   //    003C    60.w      現在日付        日付。(西暦年-1980)<<9|月<<5|月通日
   776:   //    003E    62.w      0               このファイルの最初のクラスタ番号
   777:   //    0040    64.l      0               ファイルサイズ
   778:   //    ここまでディレクトリエントリ
   779:   //    0044    68.w                      ディスク内クラスタ番号1
   780:   //    0046    70.w                      ファイル内クラスタ番号1
   781:   //    0048    72.w                      ディスク内クラスタ番号2
   782:   //    004A    74.w                      ファイル内クラスタ番号2
   783:   //    004C    76.w                      ディスク内クラスタ番号3
   784:   //    004E    78.w                      ファイル内クラスタ番号3
   785:   //    0050    80.w                      ディスク内クラスタ番号4
   786:   //    0052    82.w                      ファイル内クラスタ番号4
   787:   //    0054    84.w                      ディスク内クラスタ番号5
   788:   //    0056    86.w                      ファイル内クラスタ番号5
   789:   //    0058    88.w                      ディスク内クラスタ番号6
   790:   //    005A    90.w                      ファイル内クラスタ番号6
   791:   //    005C    92.w                      ディスク内クラスタ番号7
   792:   //    005E    94.w                      ファイル内クラスタ番号7
   793:   //    0060    96バイト
   794:   //
   795:   public static class HFUnit extends AbstractUnit {
   796: 
   797:     //デバイスエラー
   798:     //  主に装置、メディア、管理領域などの問題
   799:     //  状況によってリトライできる場合がある
   800:     //  上位バイト
   801:     public static final int DEV_IGNORE                 = 0x4000;  //無視(I)
   802:     public static final int DEV_RETRY                  = 0x2000;  //再実行(R)
   803:     public static final int DEV_ABORT                  = 0x1000;  //中止(A)
   804:     //  下位バイト
   805:     public static final int DEV_INVALID_UNIT_NUMBER    = 0x0001;  //無効なユニット番号を指定しました
   806:     public static final int DEV_INSERT_MEDIA           = 0x0002;  //ディスクが入っていません、入れてください
   807:     public static final int DEV_UNKNOWN_COMMAND        = 0x0003;  //デバイスドライバに無効なコマンドを指定しました
   808:     public static final int DEV_CRC_ERROR              = 0x0004;  //CRCエラー
   809:     public static final int DEV_MANEGEMENT_AREA_BROKEN = 0x0005;  //ディスクの管理領域が破壊されています、使用不能です
   810:     public static final int DEV_SEEK_ERROR             = 0x0006;  //シークエラー
   811:     public static final int DEV_INVALID_MEDIA          = 0x0007;  //無効なメディアを使用しました
   812:     public static final int DEV_SECTOR_NOT_FOUND       = 0x0008;  //セクタが見つかりません
   813:     public static final int DEV_PRINTER_NOT_CONNECTED  = 0x0009;  //プリンタがつながっていません
   814:     public static final int DEV_WRITE_ERROR            = 0x000a;  //書き込みエラー
   815:     public static final int DEV_READ_ERROR             = 0x000b;  //読み込みエラー
   816:     public static final int DEV_MISCELLANEOUS_ERROR    = 0x000c;  //エラーが発生しました
   817:     public static final int DEV_UNPROTECT_MEDIA        = 0x000d;  //プロテクトをはずして、同じディスクを入れてください
   818:     public static final int DEV_CANNOT_WRITE           = 0x000e;  //書き込み不可能です
   819:     public static final int DEV_FILE_SHARING_VIOLATION = 0x000f;  //ファイル共有違反です。現在使用できません。
   820: 
   821:     //DOSコールエラー
   822:     //  主にファイルシステムの中の問題
   823:     public static final int DOS_INVALID_FUNCTION      =  -1;  //無効なファンクションコード
   824:     public static final int DOS_FILE_NOT_FOUND        =  -2;  //ファイルが見つからない
   825:     public static final int DOS_DIRECTORY_NOT_FOUND   =  -3;  //ディレクトリが見つからない
   826:     public static final int DOS_TOO_MANY_HANDLES      =  -4;  //オープンしているファイルが多すぎる
   827:     public static final int DOS_NOT_A_FILE            =  -5;  //ディレクトリやボリュームラベルをアクセスしようとした
   828:     public static final int DOS_HANDLE_IS_NOT_OPENED  =  -6;  //指定したハンドラがオープンされていない
   829:     public static final int DOS_BROKEN_MEMORY_CHAIN   =  -7;  //メモリ管理領域が壊れている(実際に-7が返されることはない)
   830:     public static final int DOS_NOT_ENOUGH_MEMORY     =  -8;  //メモリが足りない
   831:     public static final int DOS_INVALID_MEMORY_CHAIN  =  -9;  //無効なメモリ管理テーブルを指定した
   832:     public static final int DOS_INVALID_ENVIRONMENT   = -10;  //不正な環境を指定した(実際に-10が返されることはない)
   833:     public static final int DOS_ABNORMAL_X_FILE       = -11;  //実行ファイルのフォーマットが異常
   834:     public static final int DOS_INVALID_ACCESS_MODE   = -12;  //オープンのアクセスモードが異常
   835:     public static final int DOS_ILLEGAL_FILE_NAME     = -13;  //ファイル名の指定が間違っている
   836:     public static final int DOS_INVALID_PARAMETER     = -14;  //パラメータが無効
   837:     public static final int DOS_ILLEGAL_DRIVE_NUMBER  = -15;  //ドライブの指定が間違っている
   838:     public static final int DOS_CURRENT_DIRECTORY     = -16;  //カレントディレクトリを削除しようとした
   839:     public static final int DOS_CANNOT_IOCTRL         = -17;  //_IOCTRLできないデバイス
   840:     public static final int DOS_NO_MORE_FILES         = -18;  //該当するファイルがもうない(_FILES,_NFILES)
   841:     public static final int DOS_CANNOT_WRITE          = -19;  //ファイルに書き込めない(主に属性R,Sのファイルに対する書き込みや削除)
   842:     public static final int DOS_DIRECTORY_EXISTS      = -20;  //同一名のディレクトリを作ろうとした
   843:     public static final int DOS_RM_NONEMPTY_DIRECTORY = -21;  //空でないディレクトリを削除しようとした
   844:     public static final int DOS_MV_NONEMPTY_DIRECTORY = -22;  //空でないディレクトリを移動しようとした
   845:     public static final int DOS_DISK_FULL             = -23;  //ディスクフル
   846:     public static final int DOS_DIRECTORY_FULL        = -24;  //ディレクトリフル
   847:     public static final int DOS_SEEK_OVER_EOF         = -25;  //EOFを越えてシークしようとした
   848:     public static final int DOS_ALREADY_SUPERVISOR    = -26;  //既にスーパーバイザ状態になっている
   849:     public static final int DOS_THREAD_EXISTS         = -27;  //同じスレッド名が存在する
   850:     public static final int DOS_COMMUNICATION_FAILED  = -28;  //スレッド間通信バッファに書き込めない(ビジーまたはオーバーフロー)
   851:     public static final int DOS_TOO_MANY_THREADS      = -29;  //これ以上バックグラウンドでスレッドを起動できない
   852:     public static final int DOS_NOT_ENOUGH_LOCK_AREA  = -32;  //ロック領域が足りない
   853:     public static final int DOS_FILE_IS_LOCKED        = -33;  //ロックされていてアクセスできない
   854:     public static final int DOS_OPENED_HANDLE_EXISTS  = -34;  //指定のドライブはハンドラがオープンされている
   855:     public static final int DOS_FILE_EXISTS           = -80;  //ファイルが存在している(_NEWFILE,_MAKETMP)
   856:     //8200000?  メモリが完全に確保できない(下位4bitは不定)
   857:     //81??????  メモリが確保できない(下位24bitは確保できる最大のサイズ)
   858: 
   859:     private static final int HFU_FILES_MAGIC = '*' << 24 | 'H' << 16 | 'F' << 8 | 'S';  //_FILESのバッファに書き込むマジック
   860: 
   861:     public String hfuRootPath;  //ルートディレクトリのパス。末尾の'/'を含まない
   862: 
   863:     //_FILESのバッファ
   864:     public HashMap<Integer,ArrayDeque<byte[]>> hfuFilesBufferToArrayDeque;  //_FILESのバッファの通し番号→ファイルの一覧のArrayDeque
   865:     public int hfuFilesBufferCounter;  //_FILESのバッファの通し番号
   866: 
   867:     //ハンドル
   868:     public class HFHandle {
   869:       public int hfhFcb;  //FCBのアドレス
   870:       public File hfhFile;  //ホストにある実体のFile
   871:       public RandomAccessFile hfhRaf;  //ホストにある実体のRandomAccessFile
   872:       public byte[] hfhBuffer;  //先読み・遅延書き込みバッファ
   873:       public long hfhStart;  //バッファの先頭のシーク位置
   874:       public long hfhEnd;  //バッファのデータの末尾のシーク位置
   875:       public boolean hfhDirty;  //true=ダーティデータがある
   876:       public HFHandle (int fcb, File file, RandomAccessFile raf) {
   877:         hfhFcb = fcb;
   878:         hfhFile = file;
   879:         hfhRaf = raf;
   880:         hfhBuffer = new byte[HFS_BUFFER_SIZE];
   881:         hfhStart = 0L;
   882:         hfhEnd = 0L;
   883:         hfhDirty = false;
   884:       }
   885:       @Override public String toString () {
   886:         return String.format ("HFHandle{fcb:0x%08x,file:\"%s\",start:%d,end:%d,dirty:%b}", hfhFcb, hfhFile.toString (), hfhStart, hfhEnd, hfhDirty);
   887:       }
   888:     }
   889:     public HashMap<Integer,HFHandle> hfuFcbToHandle;  //オープンしているファイルのFCBのアドレス→ハンドル
   890:     public LinkedList<HFHandle> hfuClosedHandle;  //クローズされたハンドル。次にオープンするとき再利用する
   891:     public HFHandle hfuNewHandle (int fcb, File file, RandomAccessFile raf) {
   892:       HFHandle handle = hfuClosedHandle.pollFirst ();  //先頭の要素を取り除いて返す。空のときはnull
   893:       if (handle == null) {
   894:         return new HFHandle (fcb, file, raf);
   895:       }
   896:       handle.hfhFcb = fcb;
   897:       handle.hfhFile = file;
   898:       handle.hfhRaf = raf;
   899:       Arrays.fill (handle.hfhBuffer, (byte) 0);
   900:       handle.hfhStart = 0L;
   901:       handle.hfhEnd = 0L;
   902:       handle.hfhDirty = false;
   903:       return handle;
   904:     }
   905:     public void hfuRecycleHandle (HFHandle handle) {
   906:       hfuClosedHandle.push (handle);
   907:     }
   908: 
   909:     //コマンドのワークエリア
   910:     public final byte[] hfuTargetNameArray1 = new byte[88];  //ワークエリア
   911:     public final byte[] hfuTargetNameArray2 = new byte[88];  //ワークエリア
   912:     public String hfuTargetName1;  //hfuNamestsToPath(hfsRequest14Namests,~)
   913:     public String hfuTargetName2;  //hfuNamestsToPath(hfsRequest18Param,~)
   914:     public long hfuTargetLastModified;  //最終更新時刻
   915:     public int hfuTargetOpenMode;  //<(fcb+14).b:オープンモード。0=読み出し,1=書き込み,2=読み書き
   916:     public HFHandle hfuTargetHandle;  //ハンドル
   917:     public long hfuTargetPosition;  //シーク位置
   918:     public long hfuTargetFileSize;  //ファイルサイズ
   919:     public long hfuTargetLength;  //転送する長さの初期値
   920:     public long hfuTargetAddress;  //転送するアドレスの初期値
   921:     public long hfuTargetTransferred;  //これまでに転送した長さ
   922:     public long hfuTargetTotalSpace;
   923:     public long hfuTargetFreeSpace;
   924: 
   925:     //unit = new HFUnit (number)
   926:     //  コンストラクタ
   927:     public HFUnit (int number) {
   928:       super (number);
   929:       hfuRootPath = null;
   930:       hfuFilesBufferToArrayDeque = new HashMap<Integer,ArrayDeque<byte[]>> ();
   931:       hfuFilesBufferCounter = 0;
   932:       hfuFcbToHandle = new HashMap<Integer,HFHandle> ();
   933:       hfuClosedHandle = new LinkedList<HFHandle> ();
   934:     }
   935: 
   936:     //hfuTini ()
   937:     //  HFUnitの後始末
   938:     //  開いたままのファイルがあれば閉じる
   939:     public void hfuTini () {
   940:       if (XEiJ.prgIsLocal) {  //ローカルのとき
   941:         for (HFHandle handle : hfuFcbToHandle.values ()) {
   942:           RandomAccessFile raf = handle.hfhRaf;
   943:           //バッファをフラッシュする
   944:           if (handle.hfhDirty) {  //ダーティデータが残っている
   945:             if (HFS_BUFFER_TRACE) {
   946:               System.out.printf ("delaywrite(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
   947:             }
   948:             try {
   949:               raf.seek (handle.hfhStart);
   950:             } catch (IOException ioe) {
   951:               XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
   952:             }
   953:             try {
   954:               raf.write (handle.hfhBuffer, 0, (int) (handle.hfhEnd - handle.hfhStart));  //RandomAccessFileのwriteは返却値がない
   955:             } catch (IOException ioe) {
   956:               XEiJ.prgMessage ((Multilingual.mlnJapanese ? "遅延書き込みに失敗しました: " : "Delayed write failed: ") + handle.toString ());
   957:             }
   958:             handle.hfhDirty = false;
   959:           }  //if handle.hfhDirty
   960:           try {
   961:             raf.close ();
   962:           } catch (IOException ioe) {
   963:             XEiJ.prgMessage ((Multilingual.mlnJapanese ? "クローズエラー: " : "Close error: ") + handle.toString ());
   964:           }
   965:         }  //for handle
   966:         hfuFcbToHandle.clear ();
   967:       }
   968:     }
   969: 
   970:     //success = unit.hfuIPLBoot ()
   971:     //  このユニットからIPL起動する
   972:     //    ルートディレクトリからHUMAN.SYSをロードする
   973:     //    bss+comm+stackをクリアしてから実行開始位置にジャンプする
   974:     //  HUMAN.SYSはX形式実行ファイルでリロケートテーブルも付いているがベースアドレスにロードするのでリロケートする必要はない
   975:     //  参考にしたもの
   976:     //    FORMAT.Xがハードディスクに書き込むIPL起動ルーチン
   977:     public boolean hfuIPLBoot () {
   978:       if (!abuConnected) {  //接続されていないとき
   979:         return false;  //失敗
   980:       }
   981:       //InputStream in = XEiJ.ismOpen (hfuRootPath + "/HUMAN.SYS");  //ルートディレクトリにあるHUMAN.SYSを開く
   982:       byte[] rr = XEiJ.ismGetResource ("HUMAN.SYS");  //HUMAN.SYSをリソースから読み込む
   983:       if (rr == null ||  //読み込めないか
   984:           ByteArray.byaRwz (rr, 0x00) != ('H' << 8 | 'U') ||  //X形式実行ファイルのマジックがないか
   985:           ByteArray.byaRls (rr, 0x04) != 0x00006800 ||  //ベースアドレスが違うか
   986:           ByteArray.byaRls (rr, 0x08) != 0x00006800) {  //実行開始位置が違うとき
   987:         return false;  //失敗
   988:       }
   989:       int textData = ByteArray.byaRls (rr, 0x0c) + ByteArray.byaRls (rr, 0x10);  //text+dataのサイズ
   990:       int bssCommStack = ByteArray.byaRls (rr, 0x14);  //bss+comm+stackのサイズ
   991:       System.arraycopy (rr, 0x40, MainMemory.mmrM8, 0x00006800, textData);  //text+dataをメモリに書き込む
   992:       Arrays.fill (MainMemory.mmrM8, 0x00006800 + textData, 0x00006800 + textData + bssCommStack, (byte) 0);  //bss+comm+stackをクリアする
   993:       return true;  //成功
   994:     }
   995: 
   996:     //unit.open ()
   997:     //  openダイアログを開く
   998:     @Override protected boolean open () {
   999:       if (!super.open ()) {
  1000:         return false;
  1001:       }
  1002:       if (XEiJ.prgIsLocal) {  //ローカルのとき
  1003:         hfsOpenUnit = abuNumber;
  1004:         if (hfsOpenDialog == null) {
  1005:           hfsMakeOpenDialog ();
  1006:         }
  1007:         //hfsOpenFileChooser.setSelectedFile (hfsLastFile);  //最後にアクセスしたファイルを設定する
  1008:         hfsOpenFileChooser.setSelectedFile (hfsLastFile.isDirectory () ? new File (hfsLastFile, ".") : hfsLastFile);  //最後にアクセスしたファイルを設定する。ディレクトリのとき"/."を付けないと親ディレクトリが開いてしまう
  1009:         hfsOpenFileChooser.rescanCurrentDirectory ();  //挿入されているファイルが変わると選択できるファイルも変わるのでリストを作り直す
  1010:         hfsOpenDialog.setVisible (true);
  1011:       }
  1012:       return true;
  1013:     }  //unit.open()
  1014: 
  1015:     //success = load (path)
  1016:     //  読み込む
  1017:     @Override protected boolean load (String path) {
  1018:       if (XEiJ.prgIsLocal) {  //ローカルのとき
  1019:         File file = new File (path).getAbsoluteFile ();
  1020:         if (file.isFile ()) {  //ファイルのとき
  1021:           file = file.getParentFile ();  //親ディレクトリ
  1022:           if (file == null) {  //親ディレクトリが存在しない
  1023:             return false;
  1024:           }
  1025:         }
  1026:         if (!file.isDirectory ()) {  //ディレクトリが存在しない
  1027:           return false;
  1028:         }
  1029:         hfuRootPath = file.getAbsolutePath ();
  1030:       }
  1031:       //XEiJ.prgMessage ("HF" + abuNumber + ": " + path);
  1032:       return true;
  1033:     }
  1034: 
  1035:     //unit.hfuCall ()
  1036:     //  デバイスコマンド
  1037:     public void hfuCall () throws M68kException {
  1038:       switch (hfsRequest2Command) {
  1039:       case 0x41:
  1040:         hfuCallChdir ();
  1041:         break;
  1042:       case 0x42:
  1043:         hfuCallMkdir ();
  1044:         break;
  1045:       case 0x43:
  1046:         hfuCallRmdir ();
  1047:         break;
  1048:       case 0x44:
  1049:         hfuCallRename ();
  1050:         break;
  1051:       case 0x45:
  1052:         hfuCallDelete ();
  1053:         break;
  1054:       case 0x46:
  1055:         hfuCallChmod ();
  1056:         break;
  1057:       case 0x47:
  1058:         hfuCallFiles ();
  1059:         break;
  1060:       case 0x48:
  1061:         hfuCallNfiles ();
  1062:         break;
  1063:       case 0x49:
  1064:         hfuCallCreateNewfile ();
  1065:         break;
  1066:       case 0x4a:
  1067:         hfuCallOpen ();
  1068:         break;
  1069:       case 0x4b:
  1070:         hfuCallClose ();
  1071:         break;
  1072:       case 0x4c:
  1073:         hfuCallRead ();
  1074:         break;
  1075:       case 0x4d:
  1076:         hfuCallWrite ();
  1077:         break;
  1078:       case 0x4e:
  1079:         hfuCallSeek ();
  1080:         break;
  1081:       case 0x4f:
  1082:         hfuCallFiledate ();
  1083:         break;
  1084:       case 0x50:
  1085:         hfuCallDskfre ();
  1086:         break;
  1087:       case 0x51:
  1088:         hfuCallDrvctrl ();
  1089:         break;
  1090:       case 0x52:
  1091:         hfuCallGetdpb ();
  1092:         break;
  1093:       case 0x53:
  1094:         hfuCallDiskred ();
  1095:         break;
  1096:       case 0x54:
  1097:         hfuCallDiskwrt ();
  1098:         break;
  1099:       case 0x55:
  1100:         hfuCallIoctrl ();
  1101:         break;
  1102:       case 0x56:
  1103:         hfuCallFflush ();
  1104:         break;
  1105:       case 0x57:
  1106:         hfuCallMediacheck ();
  1107:         break;
  1108:       case 0x58:
  1109:         hfuCallLock ();
  1110:         break;
  1111:       default:
  1112:         hfsRequest3Error = DEV_ABORT | DEV_UNKNOWN_COMMAND;  //デバイスドライバに無効なコマンドを指定しました
  1113:         hfsRequest18Result = -1;
  1114:         hfsState = HFS_STATE_DONE;
  1115:       }
  1116:     }  //unit.hfuCall()
  1117: 
  1118:     //unit.hfuCallX68k ()
  1119:     //  X68000側の処理
  1120:     public void hfuCallX68k () throws M68kException {
  1121:       switch (hfsRequest2Command) {
  1122: /*
  1123:       case 0x41:
  1124:         hfuCallChdirX68k ();
  1125:         break;
  1126:       case 0x42:
  1127:         hfuCallMkdirX68k ();
  1128:         break;
  1129:       case 0x43:
  1130:         hfuCallRmdirX68k ();
  1131:         break;
  1132:       case 0x44:
  1133:         hfuCallRenameX68k ();
  1134:         break;
  1135:       case 0x45:
  1136:         hfuCallDeleteX68k ();
  1137:         break;
  1138:       case 0x46:
  1139:         hfuCallChmodX68k ();
  1140:         break;
  1141: */
  1142:       case 0x47:
  1143:         hfuCallFilesX68k ();
  1144:         break;
  1145: /*
  1146:       case 0x48:
  1147:         hfuCallNfilesX68k ();
  1148:         break;
  1149: */
  1150:       case 0x49:
  1151:         hfuCallCreateNewfileX68k ();
  1152:         break;
  1153:       case 0x4a:
  1154:         hfuCallOpenX68k ();
  1155:         break;
  1156:       case 0x4b:
  1157:         hfuCallCloseX68k ();
  1158:         break;
  1159:       case 0x4c:
  1160:         hfuCallReadX68k ();
  1161:         break;
  1162:       case 0x4d:
  1163:         hfuCallWriteX68k ();
  1164:         break;
  1165: /*
  1166:       case 0x4e:
  1167:         hfuCallSeekX68k ();
  1168:         break;
  1169:       case 0x4f:
  1170:         hfuCallFiledateX68k ();
  1171:         break;
  1172: */
  1173:       case 0x50:
  1174:         hfuCallDskfreX68k ();
  1175:         break;
  1176: /*
  1177:       case 0x51:
  1178:         hfuCallDrvctrlX68k ();
  1179:         break;
  1180:       case 0x52:
  1181:         hfuCallGetdpbX68k ();
  1182:         break;
  1183:       case 0x53:
  1184:         hfuCallDiskredX68k ();
  1185:         break;
  1186:       case 0x54:
  1187:         hfuCallDiskwrtX68k ();
  1188:         break;
  1189:       case 0x55:
  1190:         hfuCallIoctrlX68k ();
  1191:         break;
  1192:       case 0x56:
  1193:         hfuCallFflushX68k ();
  1194:         break;
  1195:       case 0x57:
  1196:         hfuCallMediacheckX68k ();
  1197:         break;
  1198:       case 0x58:
  1199:         hfuCallLockX68k ();
  1200:         break;
  1201: */
  1202:       }
  1203:     }  //unit.hfuCallX68k()
  1204: 
  1205:     //unit.hfuCallHost ()
  1206:     //  ホスト側の処理
  1207:     public void hfuCallHost () {
  1208:       switch (hfsRequest2Command) {
  1209:       case 0x41:
  1210:         hfuCallChdirHost ();
  1211:         break;
  1212:       case 0x42:
  1213:         hfuCallMkdirHost ();
  1214:         break;
  1215:       case 0x43:
  1216:         hfuCallRmdirHost ();
  1217:         break;
  1218:       case 0x44:
  1219:         hfuCallRenameHost ();
  1220:         break;
  1221:       case 0x45:
  1222:         hfuCallDeleteHost ();
  1223:         break;
  1224:       case 0x46:
  1225:         hfuCallChmodHost ();
  1226:         break;
  1227:       case 0x47:
  1228:         hfuCallFilesHost ();
  1229:         break;
  1230: /*
  1231:       case 0x48:
  1232:         hfuCallNfilesHost ();
  1233:         break;
  1234: */
  1235:       case 0x49:
  1236:         hfuCallCreateNewfileHost ();
  1237:         break;
  1238:       case 0x4a:
  1239:         hfuCallOpenHost ();
  1240:         break;
  1241:       case 0x4b:
  1242:         hfuCallCloseHost ();
  1243:         break;
  1244:       case 0x4c:
  1245:         hfuCallReadHost ();
  1246:         break;
  1247:       case 0x4d:
  1248:         hfuCallWriteHost ();
  1249:         break;
  1250: /*
  1251:       case 0x4e:
  1252:         hfuCallSeekHost ();
  1253:         break;
  1254:       case 0x4f:
  1255:         hfuCallFiledateHost ();
  1256:         break;
  1257: */
  1258:       case 0x50:
  1259:         hfuCallDskfreHost ();
  1260:         break;
  1261: /*
  1262:       case 0x51:
  1263:         hfuCallDrvctrlHost ();
  1264:         break;
  1265:       case 0x52:
  1266:         hfuCallGetdpbHost ();
  1267:         break;
  1268:       case 0x53:
  1269:         hfuCallDiskredHost ();
  1270:         break;
  1271:       case 0x54:
  1272:         hfuCallDiskwrtHost ();
  1273:         break;
  1274:       case 0x55:
  1275:         hfuCallIoctrlHost ();
  1276:         break;
  1277: */
  1278:       case 0x56:
  1279:         hfuCallFflushHost ();
  1280:         break;
  1281: /*
  1282:       case 0x57:
  1283:         hfuCallMediacheckHost ();
  1284:         break;
  1285:       case 0x58:
  1286:         hfuCallLockHost ();
  1287:         break;
  1288: */
  1289:       }
  1290:     }  //unit.hfuCallHost()
  1291: 
  1292:     //unit.hfuCallChdir ()
  1293:     //  0x41 FF3B _CHDIR カレントディレクトリの設定
  1294:     //  カレントディレクトリにしようとしているディレクトリが存在していることを確認する
  1295:     //  カレントディレクトリの情報はHumanが管理しているのでドライバは記憶しなくてよい
  1296:     //  リクエストヘッダ
  1297:     //       0.b  i   26
  1298:     //       1.b  i   ユニット番号
  1299:     //       2.b  i   コマンドコード。0x41/0xc1
  1300:     //       3.b  o   エラーコード下位
  1301:     //       4.b  o   エラーコード上位
  1302:     //      13.b  i   0
  1303:     //      14.l  i   カレントディレクトリにするディレクトリ名。_NAMESTS形式
  1304:     //      18.l  i   0
  1305:     //            o   リザルトステータス
  1306:     //      22.l  i   0
  1307:     //    (26バイト)
  1308:     public void hfuCallChdir () throws M68kException {
  1309:       hfuTargetName1 = hfuNamestsToPath (hfsRequest14Namests, false);  //主ファイル名は使わない
  1310:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1311:         System.out.printf ("%08x chdir(name=\"%s\")\n", XEiJ.regPC0, hfuTargetName1);
  1312:       }
  1313:       if (!abuInserted) {  //挿入されていない
  1314:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1315:         hfsRequest18Result = -1;
  1316:         hfsState = HFS_STATE_DONE;
  1317:         return;
  1318:       }
  1319:       hfsRequest18Result = 0;
  1320:       hfsState = HFS_STATE_HOST;
  1321:     }  //unit.hfuCallChdir()
  1322: 
  1323:     //unit.hfuCallChdirHost ()
  1324:     public void hfuCallChdirHost () {
  1325:       File file1 = new File (hfuTargetName1);
  1326:       hfsRequest18Result = (!file1.isDirectory () ? DOS_DIRECTORY_NOT_FOUND :  //ディレクトリがない
  1327:                             0);  //成功
  1328:       hfsState = HFS_STATE_DONE;
  1329:     }  //unit.hfuCallChdirHost()
  1330: 
  1331:     //unit.hfuCallMkdir ()
  1332:     //  0x42 FF39 _MKDIR ディレクトリの作成
  1333:     //  既にあるときはエラー
  1334:     //  リクエストヘッダ
  1335:     //       0.b  i   26
  1336:     //       1.b  i   ユニット番号
  1337:     //       2.b  i   コマンドコード。0x42/0xc2
  1338:     //       3.b  o   エラーコード下位
  1339:     //       4.b  o   エラーコード上位
  1340:     //      13.b  i   0
  1341:     //      14.l  i   作成するディレクトリ名。_NAMESTS形式
  1342:     //      18.l  i   0
  1343:     //            o   リザルトステータス
  1344:     //      22.l  i   0
  1345:     //    (26バイト)
  1346:     public void hfuCallMkdir () throws M68kException {
  1347:       hfuTargetName1 = hfuNamestsToPath (hfsRequest14Namests);
  1348:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1349:         System.out.printf ("%08x mkdir(name=\"%s\")\n", XEiJ.regPC0, hfuTargetName1);
  1350:       }
  1351:       if (!abuInserted) {  //挿入されていない
  1352:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1353:         hfsRequest18Result = -1;
  1354:         hfsState = HFS_STATE_DONE;
  1355:         return;
  1356:       }
  1357:       if (abuWriteProtected) {  //書き込みが禁止されている
  1358:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  1359:         hfsRequest18Result = -1;
  1360:         hfsState = HFS_STATE_DONE;
  1361:         return;
  1362:       }
  1363:       hfsRequest18Result = 0;
  1364:       hfsState = HFS_STATE_HOST;
  1365:     }  //unit.hfuCallMkdir ()
  1366: 
  1367:     //unit.hfuCallMkdirHost ()
  1368:     public void hfuCallMkdirHost () {
  1369:       try {
  1370:         File file1 = new File (hfuTargetName1);
  1371:         hfsRequest18Result = (!file1.mkdir () ? DOS_DIRECTORY_EXISTS :  //ディレクトリが既にある
  1372:                               0);  //成功
  1373:       } catch (Exception e) {  //セキュリティなどのエラー
  1374:         hfsRequest18Result = DOS_CANNOT_WRITE;
  1375:       }
  1376:       hfsState = HFS_STATE_DONE;
  1377:     }  //unit.hfuCallMkdirHost()
  1378: 
  1379:     //unit.hfuCallRmdir ()
  1380:     //  0x43 FF3A _RMDIR ディレクトリの削除
  1381:     //  ディレクトリが空でないときはエラー
  1382:     //  リクエストヘッダ
  1383:     //       0.b  i   26
  1384:     //       1.b  i   ユニット番号
  1385:     //       2.b  i   コマンドコード。0x43/0xc3
  1386:     //       3.b  o   エラーコード下位
  1387:     //       4.b  o   エラーコード上位
  1388:     //      13.b  i   0
  1389:     //      14.l  i   削除するディレクトリ名。_NAMESTS形式
  1390:     //      18.l  i   0
  1391:     //            o   リザルトステータス
  1392:     //      22.l  i   0
  1393:     //    (26バイト)
  1394:     public void hfuCallRmdir () throws M68kException {
  1395:       hfuTargetName1 = hfuNamestsToPath (hfsRequest14Namests);
  1396:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1397:         System.out.printf ("%08x rmdir(name=\"%s\")\n", XEiJ.regPC0, hfuTargetName1);
  1398:       }
  1399:       if (!abuInserted) {  //挿入されていない
  1400:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1401:         hfsRequest18Result = -1;
  1402:         hfsState = HFS_STATE_DONE;
  1403:         return;
  1404:       }
  1405:       if (abuWriteProtected) {  //書き込みが禁止されている
  1406:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  1407:         hfsRequest18Result = -1;
  1408:         hfsState = HFS_STATE_DONE;
  1409:         return;
  1410:       }
  1411:       hfsRequest18Result = 0;
  1412:       hfsState = HFS_STATE_HOST;
  1413:     }  //unit.hfuCallRmdir ()
  1414: 
  1415:     //unit.hfuCallRmdirHost ()
  1416:     public void hfuCallRmdirHost () {
  1417:       try {
  1418:         File file1 = new File (hfuTargetName1);
  1419:         hfsRequest18Result = (!file1.isDirectory () ? DOS_DIRECTORY_NOT_FOUND :  //ディレクトリがない
  1420:                               !file1.canWrite () ? DOS_CANNOT_WRITE :  //ディレクトリがあるが書き込めない
  1421:                               !file1.delete () ? DOS_RM_NONEMPTY_DIRECTORY :  //削除できない
  1422:                               0);  //成功
  1423:       } catch (Exception e) {  //セキュリティなどのエラー
  1424:         hfsRequest18Result = DOS_CANNOT_WRITE;
  1425:       }
  1426:       hfsState = HFS_STATE_DONE;
  1427:     }  //unit.hfuCallRmdirHost()
  1428: 
  1429:     //unit.hfuCallRename ()
  1430:     //  0x44 FF86 _RENAME ファイル名またはディレクトリ名の変更およびファイルの移動
  1431:     //  パスが違うときはファイルを移動する
  1432:     //  変更後のファイル名が既にあるときはエラー
  1433:     //  リクエストヘッダ
  1434:     //       0.b  i   26
  1435:     //       1.b  i   ユニット番号
  1436:     //       2.b  i   コマンドコード。0x44/0xc4
  1437:     //       3.b  o   エラーコード下位
  1438:     //       4.b  o   エラーコード上位
  1439:     //      13.b  i   0
  1440:     //      14.l  i   変更前のファイル名。_NAMESTS形式
  1441:     //      18.l  i   変更後のファイル名。_NAMESTS形式
  1442:     //            o   リザルトステータス
  1443:     //      22.l  i   0
  1444:     //    (26バイト)
  1445:     public void hfuCallRename () throws M68kException {
  1446:       hfuTargetName1 = hfuNamestsToPath (hfsRequest14Namests);
  1447:       hfuTargetName2 = hfuNamestsToPath (hfsRequest18Param);
  1448:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1449:         System.out.printf ("%08x rename(from=\"%s\",to=\"%s\")\n", XEiJ.regPC0, hfuTargetName1, hfuTargetName2);
  1450:       }
  1451:       if (!abuInserted) {  //挿入されていない
  1452:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1453:         hfsRequest18Result = -1;
  1454:         hfsState = HFS_STATE_DONE;
  1455:         return;
  1456:       }
  1457:       if (abuWriteProtected) {  //書き込みが禁止されている
  1458:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  1459:         hfsRequest18Result = -1;
  1460:         hfsState = HFS_STATE_DONE;
  1461:         return;
  1462:       }
  1463:       hfsRequest18Result = 0;
  1464:       hfsState = HFS_STATE_HOST;
  1465:     }  //unit.hfuCallRename ()
  1466: 
  1467:     //unit.hfuCallRenameHost ()
  1468:     public void hfuCallRenameHost () {
  1469:       try {
  1470:         File file1 = new File (hfuTargetName1);
  1471:         File file2 = new File (hfuTargetName2);
  1472:         hfsRequest18Result = (!file1.exists () ? DOS_FILE_NOT_FOUND :  //ファイルまたはディレクトリがない
  1473:                               !file1.renameTo (file2) ? file1.isFile () ? DOS_CANNOT_WRITE : DOS_MV_NONEMPTY_DIRECTORY :  //変更できない
  1474:                               0);  //成功
  1475:       } catch (Exception e) {  //セキュリティなどのエラー
  1476:         hfsRequest18Result = DOS_CANNOT_WRITE;
  1477:       }
  1478:       hfsState = HFS_STATE_DONE;
  1479:     }  //unit.hfuCallRenameHost()
  1480: 
  1481:     //unit.hfuCallDelete ()
  1482:     //  0x45 FF41 _DELETE ファイルの削除
  1483:     //  リクエストヘッダ
  1484:     //       0.b  i   26
  1485:     //       1.b  i   ユニット番号
  1486:     //       2.b  i   コマンドコード。0x45/0xc5
  1487:     //       3.b  o   エラーコード下位
  1488:     //       4.b  o   エラーコード上位
  1489:     //      13.b  i   0
  1490:     //      14.l  i   削除するファイル名。_NAMESTS形式
  1491:     //      18.l  i   0
  1492:     //      22.l  i   0
  1493:     //            o   リザルトステータス
  1494:     //    (26バイト)
  1495:     public void hfuCallDelete () throws M68kException {
  1496:       hfuTargetName1 = hfuNamestsToPath (hfsRequest14Namests);
  1497:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1498:         System.out.printf ("%08x delete(name=\"%s\")\n", XEiJ.regPC0, hfuTargetName1);
  1499:       }
  1500:       if (!abuInserted) {  //挿入されていない
  1501:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1502:         hfsRequest18Result = -1;
  1503:         hfsState = HFS_STATE_DONE;
  1504:         return;
  1505:       }
  1506:       if (abuWriteProtected) {  //書き込みが禁止されている
  1507:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  1508:         hfsRequest18Result = -1;
  1509:         hfsState = HFS_STATE_DONE;
  1510:         return;
  1511:       }
  1512:       hfsRequest18Result = 0;
  1513:       hfsState = HFS_STATE_HOST;
  1514:     }  //unit.hfuCallDelete()
  1515: 
  1516:     //unit.hfuCallDeleteHost ()
  1517:     public void hfuCallDeleteHost () {
  1518:       try {
  1519:         File file1 = new File (hfuTargetName1);
  1520:         hfsRequest18Result = (!file1.isFile () ? DOS_FILE_NOT_FOUND :  //ファイルがない
  1521:                               !file1.canWrite () ? DOS_CANNOT_WRITE :  //ファイルがあるが書き込めない
  1522:                               !file1.delete () ? DOS_CANNOT_WRITE :  //削除できない
  1523:                               0);  //成功
  1524:       } catch (Exception e) {  //セキュリティなどのエラー
  1525:         hfsRequest18Result = DOS_CANNOT_WRITE;
  1526:       }
  1527:       hfsState = HFS_STATE_DONE;
  1528:     }  //unit.hfuCallDeleteHost()
  1529: 
  1530:     //unit.hfuCallChmod ()
  1531:     //  0x46 FF43 _CHMOD ファイルまたはディレクトリの属性の読み込みと設定
  1532:     //  リクエストヘッダ
  1533:     //       0.b  i   26
  1534:     //       1.b  i   ユニット番号
  1535:     //       2.b  i   コマンドコード。0x46/0xc6
  1536:     //       3.b  o   エラーコード下位
  1537:     //       4.b  o   エラーコード上位
  1538:     //      13.b  i   新しい属性。-1=読み出し
  1539:     //      14.l  i   属性を変更するファイル名。_NAMESTS形式
  1540:     //      18.l  i   0
  1541:     //            o   属性/リザルトステータス
  1542:     //      22.l  i   0
  1543:     //    (26バイト)
  1544:     public void hfuCallChmod () throws M68kException {
  1545:       hfuTargetName1 = hfuNamestsToPath (hfsRequest14Namests);
  1546:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1547:         System.out.printf ("%08x chmod(name=\"%s\",mode=0x%02x)\n", XEiJ.regPC0, hfuTargetName1, hfsRequest13Mode);
  1548:       }
  1549:       if (!abuInserted) {  //挿入されていない
  1550:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1551:         hfsRequest18Result = -1;
  1552:         hfsState = HFS_STATE_DONE;
  1553:         return;
  1554:       }
  1555:       if (hfsRequest13Mode != 255 && abuWriteProtected) {  //モードが設定で書き込みが禁止されている
  1556:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  1557:         hfsRequest18Result = -1;
  1558:         hfsState = HFS_STATE_DONE;
  1559:         return;
  1560:       }
  1561:       hfsRequest18Result = 0;
  1562:       hfsState = HFS_STATE_HOST;
  1563:     }  //unit.hfuCallChmod ()
  1564: 
  1565:     //unit.hfuCallChmodHost ()
  1566:     public void hfuCallChmodHost () {
  1567:       //! 設定は行わない
  1568:       File file1 = new File (hfuTargetName1);
  1569:       hfsRequest18Result = (!file1.exists () ? DOS_FILE_NOT_FOUND :  //ファイルまたはディレクトリがない
  1570:                             (file1.isFile () ? HumanMedia.HUM_ARCHIVE : 0) |
  1571:                             (file1.isDirectory () ? HumanMedia.HUM_DIRECTORY : 0) |
  1572:                             (file1.isHidden () ? HumanMedia.HUM_HIDDEN : 0) |
  1573:                             (!file1.canWrite () ? HumanMedia.HUM_READONLY : 0));
  1574:       hfsState = HFS_STATE_DONE;
  1575:     }  //unit.unit.hfuCallChmodHost()
  1576: 
  1577:     //unit.hfuCallFiles ()
  1578:     //  0x47 FF4E _FILES ディレクトリエントリの検索(最初)
  1579:     //  リクエストヘッダ
  1580:     //       0.b  i   26
  1581:     //       1.b  i   ユニット番号
  1582:     //       2.b  i   コマンドコード。0x47/0xc7
  1583:     //       3.b  o   エラーコード下位
  1584:     //       4.b  o   エラーコード上位
  1585:     //      13.b  i   検索する属性
  1586:     //      14.l  i   検索するファイル名。_NAMESTS形式
  1587:     //      18.l  i   _FILESのバッファ
  1588:     //                     0.b      i   検索する属性                Humanによって設定済み
  1589:     //                     1.b      i   内部ドライブ番号(0=A:)      Humanによって設定済み
  1590:     //                     2.l[2]   io  ワークエリア
  1591:     //                    10.b[8]   i   検索するファイル名          Humanによって設定済み
  1592:     //                    18.b[3]   i   検索する拡張子              Humanによって設定済み
  1593:     //                    21.b      o   属性
  1594:     //                    22.w      o   時刻
  1595:     //                    24.w      o   日付
  1596:     //                    26.l      o   ファイルサイズ
  1597:     //                    30.b[23]  o   ファイル名
  1598:     //                  (53バイト)
  1599:     //                  (以降は_FILESのバッファのアドレスのbit31を1にしたとき有効)
  1600:     //                    53.b[2]   i   'A:'        内部ドライブ名(A:~)
  1601:     //                    55.b[65]  i   '\dir\',0   パス(区切りは'\')
  1602:     //                   120.b[8]   i   'file    '  ファイル名1(残りは$20または'?')
  1603:     //                   128.b[3]   i   '   '       拡張子(残りは$20または'?')
  1604:     //                   131.b[10]  i   0           ファイル名2(残りは0)
  1605:     //                  (141バイト)
  1606:     //            o   リザルトステータス
  1607:     //      22.l  i   0
  1608:     //    (26バイト)
  1609:     //  ファイル名の大文字と小文字を区別しない
  1610:     //    ホストに大文字と小文字だけが異なるファイルが複数あると同じ名前にマッチするファイルが複数出力されることがある
  1611:     //  ファイル名にSJISに変換できない文字が含まれるものはマッチしない
  1612:     //  ファイル名がSJISに変換したとき18+3バイトに収まらないものはマッチしない
  1613:     //
  1614:     //  ルートディレクトリのとき
  1615:     //    .と..があれば削除する
  1616:     //      -d----                               .
  1617:     //      -d----                               ..
  1618:     //    HUMAN.SYSがあればシステム属性を追加する
  1619:     //      a--s--                               HUMAN.SYS
  1620:     //! 以下は未対応
  1621:     //    ボリューム名を追加する
  1622:     //      ボリューム名はSJISで18バイトまで
  1623:     //      advshr
  1624:     //      --v---    [ホストから取得]        0  [hfuRootPath]  n
  1625:     //    HUMAN.SYSとCOMMAND.Xがなければ追加する
  1626:     //      a--s--  12:00:00  1993-09-15  58496  HUMAN.SYS      n+1
  1627:     //      a-----  12:00:00  1993-02-25  28382  COMMAND.X      n+2
  1628:     public void hfuCallFiles () throws M68kException {
  1629:       //検索するディレクトリ名をホストのディレクトリ名に変換する
  1630:       byte[] w = hfuTargetNameArray1;  //ワークエリア
  1631:       MC68060.mmuReadByteArray (hfsRequest14Namests, w, 0, 88, XEiJ.regSRS);
  1632:       String dirName = hfuTargetName1 = hfuNamestsToPath (w, false);  //ホストのディレクトリ名
  1633:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1634:         System.out.printf ("%08x files(name=\"%s\",mode=0x%02x)\n", XEiJ.regPC0, dirName, hfsRequest13Mode);
  1635:       }
  1636:       if (!abuInserted) {  //挿入されていない
  1637:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1638:         hfsRequest18Result = -1;
  1639:         hfsState = HFS_STATE_DONE;
  1640:         return;
  1641:       }
  1642:       hfsRequest18Result = 0;
  1643:       hfsState = HFS_STATE_HOST;
  1644:     }  //unit.hfuCallFiles()
  1645: 
  1646:     //unit.hfuCallFilesHost ()
  1647:     public void hfuCallFilesHost () {
  1648:       byte[] w = hfuTargetNameArray1;  //ワークエリア
  1649:       String dirName = hfuTargetName1;  //ホストのディレクトリ名
  1650:       File parent = new File (dirName);  //ホストのディレクトリ
  1651:       //検索するディレクトリの一覧を取得する
  1652:       String[] children = parent.list ();
  1653:       if (children == null) {  //ディレクトリがない
  1654:         hfsRequest18Result = DOS_DIRECTORY_NOT_FOUND;
  1655:         hfsState = HFS_STATE_DONE;
  1656:         return;
  1657:       }
  1658:       //検索するファイル名の順序を入れ替える
  1659:       //  主ファイル名1の末尾が'?'で主ファイル名2の先頭が'\0'のときは主ファイル名2を'?'で充填する
  1660:       //    1234567?.Xと1234567*.Xが同じになってしまうのは仕様
  1661:       //    TwentyOne.x +Tのときは*.*で主ファイル名2も'?'で充填されている
  1662:       //  ソース
  1663:       //    w[67..87]  検索するファイル名(_NAMESTS形式)
  1664:       //      w[67..74]  主ファイル名1。残りは' '
  1665:       //      w[75..77]  拡張子。残りは' '
  1666:       //      w[78..87]  主ファイル名2。残りは'\0'
  1667:       //  デスティネーション
  1668:       //    w[21..41]  検索するファイル名
  1669:       //      w[21..28]  主ファイル名1。残りは'\0'
  1670:       //      w[29..38]  主ファイル名2。残りは'\0'
  1671:       //      w[39..41]  拡張子。残りは'\0'
  1672:       for (int i = 21; i <= 28; i++) {  //主ファイル名1
  1673:         w[i] = w[67 - 21 + i];
  1674:       }
  1675:       if (w[74] == '?' && w[78] == '\0') {  //主ファイル名1の末尾が'?'で主ファイル名2の先頭が'\0'
  1676:         for (int i = 29; i <= 38; i++) {  //主ファイル名2
  1677:           w[i] = '?';
  1678:         }
  1679:       } else {
  1680:         for (int i = 29; i <= 38; i++) {  //主ファイル名2
  1681:           w[i] = w[78 - 29 + i];
  1682:         }
  1683:       }
  1684:       for (int i = 38; i >= 21 && (w[i] == '\0' || w[i] == ' '); i--) {  //主ファイル名1+主ファイル名2の空き
  1685:         w[i] = '\0';
  1686:       }
  1687:       for (int i = 39; i <= 41; i++) {  //拡張子
  1688:         w[i] = w[75 - 39 + i];
  1689:       }
  1690:       for (int i = 41; i >= 39 && (w[i] == '\0' || w[i] == ' '); i--) {  //拡張子の空き
  1691:         w[i] = '\0';
  1692:       }
  1693:       //検索するファイル名を小文字化する
  1694:       for (int i = 21; i <= 41; i++) {
  1695:         int c = w[i] & 255;
  1696:         if ('A' <= c && c <= 'Z') {  //大文字
  1697:           w[i] = (byte) (c | 0x20);  //小文字化する
  1698:         } else if (0x81 <= c && c <= 0x9f || 0xe0 <= c && c <= 0xef) {  //SJISの1バイト目
  1699:           i++;
  1700:         }
  1701:       }
  1702:       //ディレクトリの一覧から属性とファイル名の条件に合うものを選ぶ
  1703:       boolean isRoot = dirName.equals (hfuRootPath);  //true=ルートディレクトリ
  1704:       boolean humansysRequired = isRoot;  //true=HUMAN.SYSを追加する必要がある
  1705:       boolean commandxRequired = isRoot;  //true=COMMAND.Xを追加する必要がある
  1706:       ArrayDeque<byte[]> deque = new ArrayDeque<byte[]> ();  //リスト
  1707:       if (isRoot) {  //ルートディレクトリのとき
  1708:         if ((hfsRequest13Mode & HumanMedia.HUM_VOLUME) != 0 &&  //ボリューム名が必要なとき
  1709:             w[21] == '?' && w[39] == '?') {  //検索するファイル名が*.*のとき
  1710:           //ボリューム名を作る
  1711:           int l = dirName.length ();  //UTF-16のボリューム名の文字数
  1712:           byte[] b = new byte[32];  //バッファ
  1713:           //  b[0]      21.b      属性。eladvshr
  1714:           //  b[1..2]   22.w      時刻。時<<11|分<<5|秒/2
  1715:           //  b[3..4]   24.w      日付。(西暦年-1980)<<9|月<<5|月通日
  1716:           //  b[5..8]   26.l      ファイルサイズ
  1717:           //  b[9..31]  30.b[23]  ファイル名
  1718:           hfuFileInfo (parent, b);  //ディレクトリの更新日時をボリューム名の更新日時にする
  1719:           b[0] = HumanMedia.HUM_VOLUME;  //属性をディレクトリからボリューム名に変更する
  1720:           b[5] = b[6] = b[7] = b[8] = 0;  //ファイルサイズは0
  1721:           int k = 9;
  1722:           for (int i = 0; i < l; i++) {
  1723:             int c = CharacterCode.chrCharToSJIS[dirName.charAt (i)];  //UTF-16→SJIS変換
  1724:             if (c < 0x0100) {
  1725:               if (k >= 31) {  //長すぎる
  1726:                 break;
  1727:               }
  1728:               b[k++] = (byte) c;
  1729:             } else {
  1730:               if (k >= 30) {  //長すぎる
  1731:                 break;
  1732:               }
  1733:               b[k++] = (byte) (c >> 8);
  1734:               b[k++] = (byte) c;
  1735:             }
  1736:           }
  1737:           for (int i = k; i <= 31; i++) {
  1738:             b[i] = '\0';
  1739:           }
  1740:           if (HFS_DEBUG_FILE_INFO) {
  1741:             System.out.print ("FILES   ");
  1742:             hfuPrintFileInfo (b);
  1743:           }
  1744:           //リストに追加する
  1745:           deque.addLast (b);
  1746:         }  //ボリューム名が必要なとき
  1747:       }  //ルートディレクトリのとき
  1748:     childrenLoop:
  1749:       for (String childName : children) {  //UTF-16のファイル名
  1750:         int l = childName.length ();  //UTF-16のファイル名の文字数
  1751:         if (l == 0) {  //念のため
  1752:           continue childrenLoop;
  1753:         }
  1754:         //ルートディレクトリの処理
  1755:         boolean isHumansys = false;  //true=このエントリはルートディレクトリのHUMAN.SYSである
  1756:         boolean isCommandx = false;  //true=このエントリはルートディレクトリのCOMMAND.Xである
  1757:         if (isRoot) {  //ルートディレクトリのとき
  1758:           if (childName.equals (".") || childName.equals ("..")) {  //.と..を除く
  1759:             continue childrenLoop;
  1760:           }
  1761:           isHumansys = childName.equalsIgnoreCase ("HUMAN.SYS");
  1762:           if (isHumansys) {
  1763:             humansysRequired = false;  //HUMAN.SYSを追加する必要はない
  1764:           }
  1765:           isCommandx = childName.equalsIgnoreCase ("COMMAND.X");
  1766:           if (isCommandx) {
  1767:             commandxRequired = false;  //COMMAND.Xを追加する必要はない
  1768:           }
  1769:         }
  1770:         //ファイル名をSJISに変換する
  1771:         //  ソース
  1772:         //    childName
  1773:         //  デスティネーション
  1774:         //    b[9..31]  ファイル名(主ファイル名+'.'+拡張子+'\0')
  1775:         byte[] b = new byte[32];  //バッファ
  1776:         //  b[0]      21.b      属性。eladvshr
  1777:         //  b[1..2]   22.w      時刻。時<<11|分<<5|秒/2
  1778:         //  b[3..4]   24.w      日付。(西暦年-1980)<<9|月<<5|月通日
  1779:         //  b[5..8]   26.l      ファイルサイズ
  1780:         //  b[9..31]  30.b[23]  ファイル名
  1781:         int k = 9;
  1782:         for (int i = 0; i < l; i++) {
  1783:           int c = CharacterCode.chrCharToSJIS[childName.charAt (i)];  //UTF-16→SJIS変換
  1784:           if (c <= 0x1f ||  //変換できない文字または制御コード
  1785:               c == '/' || c == '\\' ||  //ディレクトリ名の区切り
  1786:               (c == '-' && i == 0) ||  //ファイル名の先頭に使えない文字
  1787:               c == '"' || c == '\'' ||
  1788:               //c == '+' ||  //hlk.rがフォルダ名hlk-3.01+14に使っている
  1789:               c == ',' ||
  1790:               c == ';' || c == '<' || c == '=' || c == '>' ||
  1791:               c == '[' || c == ']' ||
  1792:               c == '|') {  //ファイル名に使えない文字
  1793:             continue childrenLoop;
  1794:           }
  1795:           if (c < 0x0100) {
  1796:             if (k >= 31) {  //長すぎる
  1797:               continue childrenLoop;
  1798:             }
  1799:             b[k++] = (byte) c;
  1800:           } else {
  1801:             if (k >= 30) {  //長すぎる
  1802:               continue childrenLoop;
  1803:             }
  1804:             b[k++] = (byte) (c >> 8);
  1805:             b[k++] = (byte) c;
  1806:           }
  1807:         }
  1808:         for (int i = k; i <= 31; i++) {
  1809:           b[i] = '\0';
  1810:         }
  1811:         //ファイル名を分解する
  1812:         //  ソース
  1813:         //    b[9..k-1]  ファイル名(主ファイル名+'.'+拡張子)
  1814:         //  デスティネーション
  1815:         //    w[0..20]  ファイル名
  1816:         //      w[0..7]  主ファイル名1。残りは'\0'
  1817:         //      w[8..17]  主ファイル名2。残りは'\0'
  1818:         //      w[18..20]  拡張子。残りは'\0'
  1819:         int m = (b[k - 1] == '.' ? k :  //name.
  1820:                  k >= 9 + 3 && b[k - 2] == '.' ? k - 2 :  //name.e
  1821:                  k >= 9 + 4 && b[k - 3] == '.' ? k - 3 :  //name.ex
  1822:                  k >= 9 + 5 && b[k - 4] == '.' ? k - 4 :  //name.ext
  1823:                  k);  //主ファイル名の直後。拡張子があるときは'.'の位置、ないときはk
  1824:         if (m > 9 + 18) {  //主ファイル名が長すぎる
  1825:           continue childrenLoop;
  1826:         }
  1827:         {
  1828:           int i = 0;
  1829:           for (int j = 9; j < m; j++) {  //主ファイル名
  1830:             w[i++] = b[j];
  1831:           }
  1832:           while (i <= 17) {  //主ファイル名の残り
  1833:             w[i++] = '\0';
  1834:           }
  1835:           for (int j = m + 1; j < k; j++) {  //拡張子
  1836:             w[i++] = b[j];
  1837:           }
  1838:           while (i <= 20) {  //拡張子の残り
  1839:             w[i++] = '\0';
  1840:           }
  1841:         }
  1842:         //ファイル名を比較する
  1843:         //  ソース
  1844:         //    w[0..20]  ファイル名
  1845:         //      w[0..7]  主ファイル名1。残りは'\0'
  1846:         //      w[8..17]  主ファイル名2。残りは'\0'
  1847:         //      w[18..20]  拡張子。残りは'\0'
  1848:         //  デスティネーション
  1849:         //    w[21..41]  検索するファイル名
  1850:         //      w[21..28]  主ファイル名1。残りは'\0'
  1851:         //      w[29..38]  主ファイル名2。残りは'\0'
  1852:         //      w[39..41]  拡張子。残りは'\0'
  1853:         {
  1854:           int f = 0x20;  //0x00=次のバイトはSJISの2バイト目,0x20=次のバイトはSJISの2バイト目ではない
  1855:           for (int i = 0; i <= 20; i++) {
  1856:             int c = w[i] & 255;
  1857:             int d = w[21 + i] & 255;
  1858:             if (d != '?' && ('A' <= c && c <= 'Z' ? c | f : c) != d) {  //検索するファイル名の'?'以外の部分がマッチしない。SJISの2バイト目でなければ小文字化してから比較する
  1859:               continue childrenLoop;
  1860:             }
  1861:             f = f != 0x00 && (0x81 <= c && c <= 0x9f || 0xe0 <= c && c <= 0xef) ? 0x00 : 0x20;  //このバイトがSJISの2バイト目ではなくてSJISの1バイト目ならば次のバイトはSJISの2バイト目
  1862:           }
  1863:         }
  1864:         //属性、時刻、日付、ファイルサイズを取得する
  1865:         File file = new File (parent, childName);
  1866:         if (0xffffffffL < file.length ()) {  //4GB以上のファイルは検索できないことにする
  1867:           continue childrenLoop;
  1868:         }
  1869:         hfuFileInfo (file, b);
  1870:         if (isHumansys) {  //HUMAN.SYSにシステム属性を追加する
  1871:           b[0] |= HumanMedia.HUM_SYSTEM;
  1872:         }
  1873:         if (HFS_DEBUG_FILE_INFO) {
  1874:           System.out.print ("FILES   ");
  1875:           hfuPrintFileInfo (b);
  1876:         }
  1877:         if ((b[0] & hfsRequest13Mode) == 0) {  //属性がマッチしない
  1878:           continue childrenLoop;
  1879:         }
  1880:         //リストに追加する
  1881:         deque.addLast (b);
  1882:       }
  1883:       if (false) {
  1884:         if (isRoot) {
  1885:           if (humansysRequired) {
  1886:             //リストの先頭にHUMAN.SYSを追加する
  1887:           }
  1888:           if (commandxRequired) {
  1889:             //リストの先頭にCOMMAND.Xを追加する
  1890:           }
  1891:         }
  1892:       }
  1893:       if (deque.isEmpty ()) {  //1つもなかった
  1894:         hfsRequest18Result = DOS_NO_MORE_FILES;
  1895:         hfsState = HFS_STATE_DONE;
  1896:         return;
  1897:       }
  1898:       hfuFilesBufferCounter++;
  1899:       hfuFilesBufferToArrayDeque.put (hfuFilesBufferCounter, deque);
  1900:       hfsRequest18Result = 0;
  1901:       hfsState = HFS_STATE_X68K;
  1902:     }  //unit.hfuCallFilesHost()
  1903: 
  1904:     //unit.hfuCallFilesX68k ()
  1905:     public void hfuCallFilesX68k () throws M68kException {
  1906:       MC68060.mmuWriteLongData (hfsRequest18Param + 2, HFU_FILES_MAGIC, XEiJ.regSRS);
  1907:       MC68060.mmuWriteLongData (hfsRequest18Param + 6, hfuFilesBufferCounter, XEiJ.regSRS);
  1908:       //hfuCallNfiles ();
  1909:       //int key = MC68060.mmuReadLongData (hfsRequest18Param + 6, XEiJ.regSRS);
  1910:       int key = hfuFilesBufferCounter;
  1911:       ArrayDeque<byte[]> deque = hfuFilesBufferToArrayDeque.get (key);
  1912:       if (deque == null) {
  1913:         hfsRequest18Result = DOS_NO_MORE_FILES;
  1914:         hfsState = HFS_STATE_DONE;
  1915:         return;
  1916:       }
  1917:       byte[] b = deque.pollFirst ();
  1918:       MC68060.mmuWriteByteArray (hfsRequest18Param + 21, b, 0, 32, XEiJ.regSRS);
  1919:       if (deque.isEmpty ()) {  //終わり
  1920:         MC68060.mmuWriteLongData (hfsRequest18Param + 2, 0, XEiJ.regSRS);
  1921:         MC68060.mmuWriteLongData (hfsRequest18Param + 6, 0, XEiJ.regSRS);
  1922:         hfuFilesBufferToArrayDeque.remove (key);
  1923:       }
  1924:       hfsRequest18Result = 0;
  1925:       hfsState = HFS_STATE_DONE;
  1926:     }  //unit.hfuCallFilesX68k()
  1927: 
  1928:     //unit.hfuCallNfiles ()
  1929:     //  0x48 FF4F _NFILES ディレクトリエントリの検索(次)
  1930:     //  リクエストヘッダ
  1931:     //       0.b  i   26
  1932:     //       1.b  i   ユニット番号
  1933:     //       2.b  i   コマンドコード。0x48/0xc8
  1934:     //       3.b  o   エラーコード下位
  1935:     //       4.b  o   エラーコード上位
  1936:     //      13.b  i   0
  1937:     //      14.l  i   0
  1938:     //      18.l  i   _FILESのバッファ
  1939:     //            o   リザルトステータス
  1940:     //      22.l  i   0
  1941:     //    (26バイト)
  1942:     public void hfuCallNfiles () throws M68kException {
  1943:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1944:         System.out.printf ("%08x nfiles()\n", XEiJ.regPC0);
  1945:       }
  1946:       if (!abuInserted) {  //挿入されていない
  1947:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  1948:         hfsRequest18Result = -1;
  1949:         hfsState = HFS_STATE_DONE;
  1950:         return;
  1951:       }
  1952:       if (MC68060.mmuReadLongData (hfsRequest18Param + 2, XEiJ.regSRS) != HFU_FILES_MAGIC) {  //マジックがない
  1953:         hfsRequest18Result = DOS_NO_MORE_FILES;
  1954:         hfsState = HFS_STATE_DONE;
  1955:         return;
  1956:       }
  1957:       int key = MC68060.mmuReadLongData (hfsRequest18Param + 6, XEiJ.regSRS);
  1958:       ArrayDeque<byte[]> deque = hfuFilesBufferToArrayDeque.get (key);
  1959:       if (deque == null) {
  1960:         hfsRequest18Result = DOS_NO_MORE_FILES;
  1961:         hfsState = HFS_STATE_DONE;
  1962:         return;
  1963:       }
  1964:       byte[] b = deque.pollFirst ();
  1965:       MC68060.mmuWriteByteArray (hfsRequest18Param + 21, b, 0, 32, XEiJ.regSRS);
  1966:       if (deque.isEmpty ()) {  //終わり
  1967:         MC68060.mmuWriteLongData (hfsRequest18Param + 2, 0, XEiJ.regSRS);
  1968:         MC68060.mmuWriteLongData (hfsRequest18Param + 6, 0, XEiJ.regSRS);
  1969:         hfuFilesBufferToArrayDeque.remove (key);
  1970:       }
  1971:       hfsRequest18Result = 0;
  1972:       hfsState = HFS_STATE_DONE;
  1973:     }  //unit.hfuCallNfiles()
  1974: 
  1975:     //unit.hfuCallCreateNewfile ()
  1976:     //  0x49 FF3C _CREATE 新規ファイルの作成
  1977:     //       FF8B _NEWFILE 新規ファイルの作成(非破壊)
  1978:     //  _CREATEは既にあるファイルを削除してから作成
  1979:     //  _NEWFILEは既にファイルがあるときはエラー
  1980:     //  リクエストヘッダ
  1981:     //       0.b  i   26
  1982:     //       1.b  i   ユニット番号
  1983:     //       2.b  i   コマンドコード。0x49/0xc9
  1984:     //       3.b  o   エラーコード下位
  1985:     //       4.b  o   エラーコード上位
  1986:     //      13.b  i   作成する属性
  1987:     //      14.l  i   作成するファイル名。_NAMESTS形式
  1988:     //      18.l  i   0=_NEWFILE,1=_CREATE
  1989:     //            o   リザルトステータス
  1990:     //      22.l  i   FCBテーブルのアドレス
  1991:     //    (26バイト)
  1992:     public void hfuCallCreateNewfile () throws M68kException {
  1993:       byte[] w = hfuTargetNameArray1;  //ワークエリア
  1994:       MC68060.mmuReadByteArray (hfsRequest14Namests, w, 0, 88, XEiJ.regSRS);
  1995:       hfuTargetName1 = hfuNamestsToPath (w, true);  //ファイル名
  1996:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  1997:         System.out.printf ("%08x %s(fcb=0x%08x,name=\"%s\",mode=0x%02x)\n", XEiJ.regPC0, hfsRequest18Param == 0 ? "newfile" : "create", hfsRequest22Fcb, hfuTargetName1, hfsRequest13Mode);
  1998:       }
  1999:       if (!abuInserted) {  //挿入されていない
  2000:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2001:         hfsRequest18Result = -1;
  2002:         hfsState = HFS_STATE_DONE;
  2003:         return;
  2004:       }
  2005:       if (abuWriteProtected) {  //書き込みが禁止されている
  2006:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  2007:         hfsRequest18Result = -1;
  2008:         hfsState = HFS_STATE_DONE;
  2009:         return;
  2010:       }
  2011:       hfsRequest18Result = 0;
  2012:       hfsState = HFS_STATE_HOST;
  2013:     }  //unit.hfuCallCreateNewfile()
  2014: 
  2015:     //unit.hfuCallCreateNewfileHost ()
  2016:     public void hfuCallCreateNewfileHost () {
  2017:       byte[] b = hfuTargetNameArray2;  //ワークエリア
  2018:       File file = new File (hfuTargetName1);
  2019:       if (file.exists ()) {  //同名のファイルまたはディレクトリがある
  2020:         if (hfsRequest18Param == 0) {  //_NEWFILEで同名のファイルまたはディレクトリがある
  2021:           hfsRequest18Result = DOS_FILE_EXISTS;  //ファイルが存在している(_NEWFILE,_MAKETMP)
  2022:           hfsState = HFS_STATE_DONE;
  2023:           return;
  2024:         }
  2025:         //_CREATEで同名のファイルまたはディレクトリがある
  2026:         //  作成日を更新させるためとファイルサイズを0にするために一旦削除する
  2027:         //  file.delete()はディレクトリでも空だと削除しようとするのでディレクトリのときfile.delete()を試みてはならない
  2028:         if (file.isDirectory () ||  //同名のディレクトリがある
  2029:             !file.delete ()) {  //削除できない
  2030:           hfsRequest18Result = DOS_CANNOT_WRITE;
  2031:           hfsState = HFS_STATE_DONE;
  2032:           return;
  2033:         }
  2034:       }
  2035:       hfuFileInfo (file, b);
  2036:       if (HFS_DEBUG_FILE_INFO) {
  2037:         System.out.print ("CREATE  ");
  2038:         hfuPrintFileInfo (b);
  2039:       }
  2040:       RandomAccessFile raf;
  2041:       try {
  2042:         raf = new RandomAccessFile (file, "rw");  //RandomAccessFileに"w"というモードはない
  2043:       } catch (IOException ioe) {
  2044:         hfsRequest18Result = DOS_CANNOT_WRITE;
  2045:         hfsState = HFS_STATE_DONE;
  2046:         return;
  2047:       }
  2048:       if (hfuFcbToHandle.isEmpty ()) {  //このユニットでオープンされているファイルがなかった
  2049:         prevent ();  //イジェクト禁止
  2050:       }
  2051:       int fcb = hfsRequest22Fcb;
  2052:       HFHandle handle = hfuTargetHandle = hfuNewHandle (fcb, file, raf);
  2053:       hfuFcbToHandle.put (fcb, handle);
  2054:       hfsRequest18Result = 0;
  2055:       hfsState = HFS_STATE_X68K;
  2056:     }  //unit.hfuCallCreateNewfileHost()
  2057: 
  2058:     //unit.hfuCallCreateNewfileX68k ()
  2059:     public void hfuCallCreateNewfileX68k () throws M68kException {
  2060:       HFHandle handle = hfuTargetHandle;
  2061:       int fcb = handle.hfhFcb;
  2062:       byte[] w = hfuTargetNameArray1;  //ワークエリア
  2063:       byte[] b = hfuTargetNameArray2;  //ワークエリア
  2064:       //FCBを作る
  2065:       //  ソース
  2066:       //    b[0]       属性。eladvshr
  2067:       //    b[1..2]    時刻。時<<11|分<<5|秒/2
  2068:       //    b[3..4]    日付。(西暦年-1980)<<9|月<<5|月通日
  2069:       //    b[5..8]    ファイルサイズ
  2070:       //    w[67..74]  主ファイル名1。残りは' '
  2071:       //    w[75..77]  拡張子。残りは' '
  2072:       //    w[78..87]  主ファイル名2。残りは'\0'
  2073:       //  デスティネーション
  2074:       //    f[36..43]  主ファイル名1
  2075:       //    f[44..46]  拡張子
  2076:       //    f[47]      属性
  2077:       //    f[48..57]  主ファイル名2
  2078:       //    f[58..59]  時刻。時<<11|分<<5|秒/2
  2079:       //    f[60..61]  日付。(西暦年-1980)<<9|月<<5|月通日
  2080:       //    f[62..63]  このファイルの最初のクラスタ番号
  2081:       //    f[64..67]  ファイルサイズ
  2082:       for (int i = 0; i < 8 + 3; i++) {  //主ファイル名1,拡張子
  2083:         MC68060.mmuWriteByteData (fcb + 36 + i, w[67 + i], XEiJ.regSRS);
  2084:       }
  2085:       MC68060.mmuWriteByteData (fcb + 47, b[0], XEiJ.regSRS);  //属性
  2086:       for (int i = 0; i < 10; i++) {  //主ファイル名2
  2087:         MC68060.mmuWriteByteData (fcb + 48 + i, w[78 + i], XEiJ.regSRS);
  2088:       }
  2089:       MC68060.mmuWriteLongData (fcb + 58, ByteArray.byaRls (b, 1), XEiJ.regSRS);  //時刻,日付
  2090:       MC68060.mmuWriteWordData (fcb + 62, 0, XEiJ.regSRS);  //クラスタ番号
  2091:       MC68060.mmuWriteLongData (fcb + 64, ByteArray.byaRls (b, 5), XEiJ.regSRS);  //ファイルサイズ
  2092:       hfsRequest18Result = 0;
  2093:       hfsState = HFS_STATE_DONE;
  2094:     }  //unit.hfuCallCreateNewfileX68k()
  2095: 
  2096:     //unit.hfuCallOpen ()
  2097:     //  0x4a FF3D _OPEN 存在するファイルのオープン
  2098:     //  リクエストヘッダ
  2099:     //       0.b  i   26
  2100:     //       1.b  i   ユニット番号
  2101:     //       2.b  i   コマンドコード。0x4a/0xca
  2102:     //       3.b  o   エラーコード下位
  2103:     //       4.b  o   エラーコード上位
  2104:     //      13.b  i   0
  2105:     //      14.l  i   オープンするファイル名。_NAMESTS形式
  2106:     //      18.l  i   0
  2107:     //            o   リザルトステータス
  2108:     //      22.l  i   FCBテーブルのアドレス
  2109:     //    (26バイト)
  2110:     public void hfuCallOpen () throws M68kException {
  2111:       byte[] w = hfuTargetNameArray1;  //ワークエリア
  2112:       MC68060.mmuReadByteArray (hfsRequest14Namests, w, 0, 88, XEiJ.regSRS);
  2113:       hfuTargetName1 = hfuNamestsToPath (w, true);  //ファイル名
  2114:       int fcb = hfsRequest22Fcb;
  2115:       hfuTargetOpenMode = MC68060.mmuReadByteZeroData (fcb + 14, XEiJ.regSRS);  //オープンモード。0=読み出し,1=書き込み,2=読み書き
  2116:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  2117:         System.out.printf ("%08x open(fcb=0x%08x,name=\"%s\",mode=0x%02x)\n", XEiJ.regPC0, hfsRequest22Fcb, hfuTargetName1, hfuTargetOpenMode);
  2118:       }
  2119:       if (!abuInserted) {  //挿入されていない
  2120:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2121:         hfsRequest18Result = -1;
  2122:         hfsState = HFS_STATE_DONE;
  2123:         return;
  2124:       }
  2125:       if (hfuTargetOpenMode != 0 && abuWriteProtected) {  //書き込みが禁止されている
  2126:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  2127:         hfsRequest18Result = -1;
  2128:         hfsState = HFS_STATE_DONE;
  2129:         return;
  2130:       }
  2131:       hfsRequest18Result = 0;
  2132:       hfsState = HFS_STATE_HOST;
  2133:     }  //unit.hfuCallOpen()
  2134: 
  2135:     //unit.hfuCallOpenHost ()
  2136:     public void hfuCallOpenHost () {
  2137:       byte[] b = hfuTargetNameArray2;  //ワークエリア
  2138:       File file = new File (hfuTargetName1);
  2139:       //_OPENはファイルが存在しないときモードに関係なくエラーを返さなければならない
  2140:       //  LK.Xはテンポラリファイルを_OPEN(2)してみてエラーが出なければ同名のファイルが存在すると判断し、名前を変えて同じことを繰り返す
  2141:       //  RandomAccessFile("rw")はファイルが存在しなければ作成するので、その前にファイルが存在することを確認しなければならない
  2142:       //  _OPEN(2)→RandomAccessFile("rw")だけではLK.Xを実行した途端にホストのディレクトリが空のテンポラリファイルで埋め尽くされてしまう
  2143:       if (!file.exists ()) {  //ファイルが存在しない
  2144:         hfsRequest18Result = DOS_FILE_NOT_FOUND;
  2145:         hfsState = HFS_STATE_DONE;
  2146:         return;
  2147:       }
  2148:       if (0xffffffffL < file.length ()) {  //4GB以上のファイルはオープンできないことにする
  2149:         hfsRequest18Result = DOS_FILE_NOT_FOUND;
  2150:         hfsState = HFS_STATE_DONE;
  2151:         return;
  2152:       }
  2153:       hfuFileInfo (file, b);
  2154:       if (HFS_DEBUG_FILE_INFO) {
  2155:         System.out.print ("OPEN    ");
  2156:         hfuPrintFileInfo (b);
  2157:       }
  2158:       RandomAccessFile raf;
  2159:       try {
  2160:         raf = new RandomAccessFile (file, hfuTargetOpenMode == 0 ? "r" : "rw");  //RandomAccessFileに"w"というモードはない
  2161:       } catch (IOException ioe) {
  2162:         hfsRequest18Result = DOS_FILE_NOT_FOUND;
  2163:         hfsState = HFS_STATE_DONE;
  2164:         return;
  2165:       }
  2166:       if (hfuFcbToHandle.isEmpty ()) {  //このユニットでオープンされているファイルがなかった
  2167:         prevent ();  //イジェクト禁止
  2168:       }
  2169:       int fcb = hfsRequest22Fcb;
  2170:       HFHandle handle = hfuTargetHandle = hfuNewHandle (fcb, file, raf);
  2171:       hfuFcbToHandle.put (fcb, handle);
  2172:       hfsRequest18Result = 0;
  2173:       hfsState = HFS_STATE_X68K;
  2174:     }  //unit.hfuCallOpenHost()
  2175: 
  2176:     //unit.hfuCallOpenX68k ()
  2177:     public void hfuCallOpenX68k () throws M68kException {
  2178:       HFHandle handle = hfuTargetHandle;
  2179:       int fcb = handle.hfhFcb;
  2180:       byte[] w = hfuTargetNameArray1;  //ワークエリア
  2181:       byte[] b = hfuTargetNameArray2;  //ワークエリア
  2182:       //FCBを作る
  2183:       //  ソース
  2184:       //    b[0]       属性。eladvshr
  2185:       //    b[1..2]    時刻。時<<11|分<<5|秒/2
  2186:       //    b[3..4]    日付。(西暦年-1980)<<9|月<<5|月通日
  2187:       //    b[5..8]    ファイルサイズ
  2188:       //    w[67..74]  主ファイル名1。残りは' '
  2189:       //    w[75..77]  拡張子。残りは' '
  2190:       //    w[78..87]  主ファイル名2。残りは'\0'
  2191:       //  デスティネーション
  2192:       //    f[36..43]  主ファイル名1
  2193:       //    f[44..46]  拡張子
  2194:       //    f[47]      属性
  2195:       //    f[48..57]  主ファイル名2
  2196:       //    f[58..59]  時刻。時<<11|分<<5|秒/2
  2197:       //    f[60..61]  日付。(西暦年-1980)<<9|月<<5|月通日
  2198:       //    f[62..63]  このファイルの最初のクラスタ番号
  2199:       //    f[64..67]  ファイルサイズ
  2200:       for (int i = 0; i < 8 + 3; i++) {  //主ファイル名1,拡張子
  2201:         MC68060.mmuWriteByteData (fcb + 36 + i, w[67 + i], XEiJ.regSRS);
  2202:       }
  2203:       MC68060.mmuWriteByteData (fcb + 47, b[0], XEiJ.regSRS);  //属性
  2204:       for (int i = 0; i < 10; i++) {  //主ファイル名2
  2205:         MC68060.mmuWriteByteData (fcb + 48 + i, w[78 + i], XEiJ.regSRS);
  2206:       }
  2207:       MC68060.mmuWriteLongData (fcb + 58, ByteArray.byaRls (b, 1), XEiJ.regSRS);  //時刻,日付
  2208:       MC68060.mmuWriteWordData (fcb + 62, 0, XEiJ.regSRS);  //クラスタ番号
  2209:       MC68060.mmuWriteLongData (fcb + 64, ByteArray.byaRls (b, 5), XEiJ.regSRS);  //ファイルサイズ
  2210:       hfsRequest18Result = 0;
  2211:       hfsState = HFS_STATE_DONE;
  2212:     }  //unit.hfuCallOpenX68k()
  2213: 
  2214:     //unit.hfuCallClose ()
  2215:     //  0x4b FF3E _CLOSE ハンドラのクローズ
  2216:     //  リクエストヘッダ
  2217:     //       0.b  i   26
  2218:     //       1.b  i   ユニット番号
  2219:     //       2.b  i   コマンドコード。0x4b/0xcb
  2220:     //       3.b  o   エラーコード下位
  2221:     //       4.b  o   エラーコード上位
  2222:     //      13.b  i   0
  2223:     //      14.l  i   0
  2224:     //      18.l  i   0
  2225:     //            o   リザルトステータス
  2226:     //      22.l  i   FCBテーブルのアドレス
  2227:     //    (26バイト)
  2228:     public void hfuCallClose () throws M68kException {
  2229:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  2230:         System.out.printf ("%08x close(fcb=0x%08x)\n", XEiJ.regPC0, hfsRequest22Fcb);
  2231:       }
  2232:       if (!abuInserted) {  //挿入されていない
  2233:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2234:         hfsRequest18Result = -1;
  2235:         hfsState = HFS_STATE_DONE;
  2236:         return;
  2237:       }
  2238:       int fcb = hfsRequest22Fcb;
  2239:       HFHandle handle = hfuTargetHandle = hfuFcbToHandle.remove (fcb);
  2240:       if (handle == null) {  //オープンされていない
  2241:         //既にクローズされているファイルをクローズしようとしたときはエラーにせず無視する
  2242:         hfsRequest18Result = 0;
  2243:         hfsState = HFS_STATE_DONE;
  2244:         return;
  2245:       }
  2246:       hfsRequest18Result = 0;
  2247:       hfsState = HFS_STATE_HOST;
  2248:     }  //unit.hfuCallClose()
  2249: 
  2250:     //unit.hfuCallCloseHost ()
  2251:     public void hfuCallCloseHost () {
  2252:       HFHandle handle = hfuTargetHandle;
  2253:       if (hfsRequest18Result != 0) {  //最終更新日時を設定する
  2254:         File file = handle.hfhFile;
  2255:         if (!file.setLastModified (hfuTargetLastModified)) {  //最終更新日時を設定する
  2256:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "最終更新日時を設定できません: " : "Could not set last modified date and time: ") + handle.toString ());
  2257:         }
  2258:         if (hfuFcbToHandle.isEmpty ()) {  //このユニットでオープンされているファイルがなくなった
  2259:           allow ();  //イジェクト許可
  2260:         }
  2261:         hfuRecycleHandle (handle);
  2262:         handle = hfuTargetHandle = null;
  2263:         hfsRequest18Result = 0;
  2264:         hfsState = HFS_STATE_DONE;
  2265:         return;
  2266:       }
  2267:       RandomAccessFile raf = handle.hfhRaf;
  2268:       //バッファをフラッシュする
  2269:       if (handle.hfhDirty) {  //ダーティデータが残っている
  2270:         if (HFS_BUFFER_TRACE) {
  2271:           System.out.printf ("delaywrite(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
  2272:         }
  2273:         try {
  2274:           handle.hfhRaf.seek (handle.hfhStart);
  2275:         } catch (IOException ioe) {
  2276:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
  2277:         }
  2278:         try {
  2279:           handle.hfhRaf.write (handle.hfhBuffer, 0, (int) (handle.hfhEnd - handle.hfhStart));  //RandomAccessFileのwriteは返却値がない
  2280:         } catch (IOException ioe) {
  2281:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "遅延書き込みに失敗しました: " : "Delayed write failed: ") + handle.toString ());
  2282:         }
  2283:         handle.hfhDirty = false;
  2284:       }  //if handle.hfhDirty
  2285:       try {
  2286:         raf.close ();
  2287:       } catch (IOException ioe) {
  2288:         XEiJ.prgMessage ((Multilingual.mlnJapanese ? "クローズエラー: " : "Close error: ") + handle.toString ());
  2289:       }
  2290:       hfsRequest18Result = 0;
  2291:       hfsState = HFS_STATE_X68K;
  2292:     }  //unit.hfuCallCloseHost()
  2293: 
  2294:     //unit.hfuCallCloseX68k ()
  2295:     public void hfuCallCloseX68k () throws M68kException {
  2296:       HFHandle handle = hfuTargetHandle;
  2297:       int fcb = handle.hfhFcb;
  2298:       if ((MC68060.mmuReadByteZeroData (fcb + 14, XEiJ.regSRS) & 15) == 0) {  //アクセスモードが読み出しのときは終了
  2299:         if (hfuFcbToHandle.isEmpty ()) {  //このユニットでオープンされているファイルがなくなった
  2300:           allow ();  //イジェクト許可
  2301:         }
  2302:         hfuRecycleHandle (handle);
  2303:         handle = hfuTargetHandle = null;
  2304:         hfsRequest18Result = 0;
  2305:         hfsState = HFS_STATE_DONE;
  2306:         return;
  2307:       }
  2308:       if ((MC68060.mmuReadByteZeroData (fcb + 1, XEiJ.regSRS) & 0x40) != 0) {  //_CLOSEしたとき日時を更新する(クローズした日時を書き込む)
  2309:         hfuTargetLastModified = System.currentTimeMillis ();
  2310:       } else {  //_CLOSEしたとき日時を更新しない(FCBの日時を書き込む)
  2311:         int time = MC68060.mmuReadWordZeroData (fcb + 58, XEiJ.regSRS);  //時刻。時<<11|分<<5|秒/2
  2312:         int date = MC68060.mmuReadWordZeroData (fcb + 60, XEiJ.regSRS);  //日付。(西暦年-1980)<<9|月<<5|月通日
  2313:         hfuTargetLastModified = DnT.dntCmilYearMontMdayHourMinuSeco (
  2314:           (date >> 9) + 1980, date >> 5 & 15, date & 31,
  2315:           time >> 11, time >> 5 & 63, (time & 31) << 1) - RP5C15.rtcCmilGap;  //FCBの日時はRTCの日時なのでオフセットを引く
  2316:       }
  2317:       hfsRequest18Result = 1;  //最終更新日時を設定する
  2318:       hfsState = HFS_STATE_HOST;
  2319:     }  //unit.hfuCallCloseX68k ()
  2320: 
  2321:     //unit.hfuCallRead ()
  2322:     //  0x4c FF3F _READ ハンドラから指定されたサイズのデータを読み込む
  2323:     //  リクエストヘッダ
  2324:     //       0.b  i   26
  2325:     //       1.b  i   ユニット番号
  2326:     //       2.b  i   コマンドコード。0x4c/0xcc
  2327:     //       3.b  o   エラーコード下位
  2328:     //       4.b  o   エラーコード上位
  2329:     //      13.b  i   0
  2330:     //      14.l  i   バッファの先頭アドレス
  2331:     //      18.l  i   読み込むバイト数
  2332:     //            o   実際に読み込んだバイト数/リザルトステータス
  2333:     //      22.l  i   FCBテーブルのアドレス
  2334:     //    (26バイト)
  2335:     public void hfuCallRead () throws M68kException {
  2336:       if (HFS_BUFFER_TRACE || (HFS_COMMAND_TRACE && hfsCommandTraceOn)) {
  2337:         System.out.printf ("%08x read(fcb=0x%08x,address=0x%08x,length=0x%08x)\n", XEiJ.regPC0, hfsRequest22Fcb, hfsRequest14Namests, hfsRequest18Param);
  2338:       }
  2339:       if (!abuInserted) {  //挿入されていない。オープンされているファイルがあるときはイジェクト禁止なので挿入されていないということは通常はない。強制イジェクトに対応
  2340:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2341:         hfsRequest18Result = -1;
  2342:         hfsState = HFS_STATE_DONE;
  2343:         return;
  2344:       }
  2345:       int fcb = hfsRequest22Fcb;
  2346:       HFHandle handle = hfuTargetHandle = hfuFcbToHandle.get (fcb);
  2347:       if (handle == null) {  //オープンされていない
  2348:         hfsRequest18Result = DOS_HANDLE_IS_NOT_OPENED;
  2349:         hfsState = HFS_STATE_DONE;
  2350:         return;
  2351:       }
  2352:       hfuTargetLength = hfsRequest18Param & 0xffffffffL;  //読み出す長さ
  2353:       if (HFS_BUFFER_TRACE) {
  2354:         System.out.printf ("length=%d\n", hfuTargetLength);
  2355:       }
  2356:       hfuTargetPosition = MC68060.mmuReadLongData (fcb + 6, XEiJ.regSRS) & 0xffffffffL;  //シーク位置
  2357:       if (HFS_BUFFER_TRACE) {
  2358:         System.out.printf ("position=%d\n", hfuTargetPosition);
  2359:       }
  2360:       hfuTargetFileSize = MC68060.mmuReadLongData (fcb + 64, XEiJ.regSRS) & 0xffffffffL;  //ファイルサイズ
  2361:       if (HFS_BUFFER_TRACE) {
  2362:         System.out.printf ("fileSize=%d\n", hfuTargetFileSize);
  2363:       }
  2364:       hfuTargetLength = Math.min (hfuTargetLength, hfuTargetFileSize - hfuTargetPosition);  //読み出す長さと読み出せる長さの短い方を読み出す長さにする
  2365:       if (HFS_BUFFER_TRACE) {
  2366:         System.out.printf ("length=%d\n", hfuTargetLength);
  2367:       }
  2368:       if (hfuTargetLength == 0L) {  //読み出す長さが0のときは何もしない。エラーにもしない
  2369:         hfsRequest18Result = 0;
  2370:         hfsState = HFS_STATE_DONE;
  2371:         return;
  2372:       }
  2373:       hfuTargetAddress = hfsRequest14Namests & 0xffffffffL;  //アドレス
  2374:       if (HFS_BUFFER_TRACE) {
  2375:         System.out.printf ("address=0x%08x\n", hfuTargetAddress);
  2376:       }
  2377:       hfuTargetTransferred = 0L;  //読み出した長さ
  2378:       if (HFS_BUFFER_TRACE) {
  2379:         System.out.printf ("transferred=%d\n", hfuTargetTransferred);
  2380:       }
  2381:       //  ファイルサイズが0のとき
  2382:       //    バッファは空
  2383:       //    バッファを充填する必要がない
  2384:       //  ファイルサイズが0でないとき
  2385:       //    バッファが空のとき
  2386:       //      オープンした直後かバッファをフラッシュした直後
  2387:       //      バッファを充填する必要がある
  2388:       //    バッファが空でないとき
  2389:       //      バッファのデータはバッファの先頭からバッファの末尾またはファイルの末尾まで有効
  2390:       //      バッファの途中にファイルの末尾がある場合を含めて、
  2391:       //      バッファの範囲内でファイルにあってバッファにないデータは存在しない
  2392:       //      シーク位置がバッファの範囲内のとき
  2393:       //        バッファに読み書きできる部分がある
  2394:       //        バッファを充填する必要がない
  2395:       //      シーク位置がバッファの範囲外のとき
  2396:       //        バッファに読み書きできる部分がない
  2397:       //        バッファを充填する必要がある
  2398:       //  ファイルサイズが0またはバッファが空でなくてシーク位置がバッファの範囲内のときバッファを充填する必要がない
  2399:       //  ファイルサイズが0でなくてバッファが空またはシーク位置がバッファの範囲外のときバッファを充填する必要がある
  2400:       if (hfuTargetFileSize == 0 ||  //ファイルサイズが0または
  2401:           (0 < handle.hfhEnd &&  //バッファが空でなくて
  2402:            handle.hfhStart <= hfuTargetPosition && hfuTargetPosition < handle.hfhStart + HFS_BUFFER_SIZE)) {  //シーク位置がバッファの範囲内のとき
  2403:         //バッファを充填する必要がない
  2404:         hfsRequest18Result = 0;
  2405:         hfsState = HFS_STATE_X68K;
  2406:       } else {  //ファイルサイズが0でなくてバッファが空またはシーク位置がバッファの範囲外のとき
  2407:         //バッファを充填する必要がある
  2408:         hfsRequest18Result = 0;
  2409:         hfsState = HFS_STATE_HOST;
  2410:       }
  2411:     }  //unit.hfuCallRead()
  2412: 
  2413:     //unit.hfuCallReadHost ()
  2414:     public void hfuCallReadHost () {
  2415:       HFHandle handle = hfuTargetHandle;
  2416:       RandomAccessFile raf = handle.hfhRaf;
  2417:       //バッファをフラッシュする
  2418:       if (handle.hfhDirty) {  //ダーティデータが残っている
  2419:         if (HFS_BUFFER_TRACE) {
  2420:           System.out.printf ("delaywrite(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
  2421:         }
  2422:         try {
  2423:           raf.seek (handle.hfhStart);
  2424:         } catch (IOException ioe) {
  2425:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
  2426:         }
  2427:         try {
  2428:           raf.write (handle.hfhBuffer, 0, (int) (handle.hfhEnd - handle.hfhStart));  //RandomAccessFileのwriteは返却値がない
  2429:         } catch (IOException ioe) {
  2430:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "遅延書き込みに失敗しました: " : "Delayed write failed: ") + handle.toString ());
  2431:         }
  2432:         handle.hfhDirty = false;
  2433:       }  //if handle.hfhDirty
  2434:       //バッファを充填する
  2435:       int ll = (int) Math.min (HFS_BUFFER_SIZE,  //バッファの長さ
  2436:                                hfuTargetFileSize - hfuTargetPosition  //シーク位置からファイルの末尾までの長さ
  2437:                                );  //バッファに読み出す長さ
  2438:       int kk = 0;  //バッファに読み出した長さ
  2439:       Arrays.fill (handle.hfhBuffer, (byte) 0);
  2440:       handle.hfhStart = hfuTargetPosition;  //バッファの先頭のシーク位置
  2441:       handle.hfhEnd = hfuTargetPosition + ll;  //バッファのデータの末尾のシーク位置
  2442:       handle.hfhDirty = false;  //ダーティデータなし
  2443:       if (HFS_BUFFER_TRACE) {
  2444:         System.out.printf ("preread(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
  2445:       }
  2446:       if (0 < ll) {
  2447:         try {
  2448:           raf.seek (hfuTargetPosition);
  2449:         } catch (IOException ioe) {
  2450:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
  2451:         }
  2452:         try {
  2453:           while (kk < ll) {
  2454:             int tt = raf.read (handle.hfhBuffer, kk, ll - kk);  //今回読み出した長さ。エラーでなければ1以上
  2455:             if (tt < 0) {  //途中でEOFに達した。FCBのファイルサイズが合っていれば起こらないはずだが念のため
  2456:               //先読みに失敗した
  2457:               hfsRequest18Result = -1;
  2458:               hfsState = HFS_STATE_DONE;
  2459:               return;
  2460:             }
  2461:             kk += tt;
  2462:           }
  2463:         } catch (IOException ioe) {
  2464:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "リードエラー: " : "Read error: ") + handle.toString ());
  2465:           hfsRequest18Result = -1;
  2466:           hfsState = HFS_STATE_DONE;
  2467:           return;
  2468:         }
  2469:       }
  2470:       //バッファから読み出す
  2471:       hfsRequest18Result = 0;
  2472:       hfsState = HFS_STATE_X68K;
  2473:     }  //unit.hfuCallReadHost()
  2474: 
  2475:     //unit.hfuCallReadX68k ()
  2476:     public void hfuCallReadX68k () throws M68kException {
  2477:       HFHandle handle = hfuTargetHandle;
  2478:       int fcb = handle.hfhFcb;
  2479:       //バッファから読み出す
  2480:       long t = Math.min (hfuTargetLength - hfuTargetTransferred,  //転送する残りの長さ
  2481:                          handle.hfhEnd - hfuTargetPosition  //シーク位置からバッファのデータの末尾までの長さ
  2482:                          );  //バッファから読み出せる長さ
  2483:       if (HFS_BUFFER_TRACE) {
  2484:         System.out.printf ("t=%d\n", t);
  2485:       }
  2486:       MC68060.mmuWriteByteArray ((int) (hfuTargetAddress + hfuTargetTransferred),
  2487:                                  handle.hfhBuffer,
  2488:                                  (int) (hfuTargetPosition - handle.hfhStart),  //バッファの先頭からシーク位置までのオフセット
  2489:                                  (int) t,  //読み出す長さ
  2490:                                  XEiJ.regSRS);  //バッファから読み出す
  2491:       hfuTargetPosition += t;  //シーク位置を更新する
  2492:       if (HFS_BUFFER_TRACE) {
  2493:         System.out.printf ("position=%d\n", hfuTargetPosition);
  2494:       }
  2495:       MC68060.mmuWriteLongData (fcb + 6, (int) hfuTargetPosition, XEiJ.regSRS);  //FCBのシーク位置を更新する
  2496:       hfuTargetTransferred += t;  //読み出した長さを更新する
  2497:       if (HFS_BUFFER_TRACE) {
  2498:         System.out.printf ("transferred=%d\n", hfuTargetTransferred);
  2499:       }
  2500:       if (hfuTargetLength <= hfuTargetTransferred) {  //終了
  2501:         hfsRequest18Result = (int) hfuTargetTransferred;  //読み出した長さ
  2502:         hfsState = HFS_STATE_DONE;
  2503:         return;
  2504:       }
  2505:       //バッファを使い切ったので続きはバッファを充填するところから行う
  2506:       hfsRequest18Result = 0;
  2507:       hfsState = HFS_STATE_HOST;
  2508:     }  //unit.hfuCallReadX68k()
  2509: 
  2510:     //unit.hfuCallWrite ()
  2511:     //  0x4d FF40 _WRITE ハンドラへ指定されたサイズのデータを書き込む
  2512:     //  リクエストヘッダ
  2513:     //       0.b  i   26
  2514:     //       1.b  i   ユニット番号
  2515:     //       2.b  i   コマンドコード。0x4d/0xcd
  2516:     //       3.b  o   エラーコード下位
  2517:     //       4.b  o   エラーコード上位
  2518:     //      13.b  i   0
  2519:     //      14.l  i   バッファの先頭アドレス
  2520:     //      18.l  i   書き込むバイト数。0=現在のシーク位置から後ろを切り捨てる
  2521:     //            o   実際に書き込んだバイト数/リザルトステータス
  2522:     //      22.l  i   FCBテーブルのアドレス
  2523:     //    (26バイト)
  2524:     public void hfuCallWrite () throws M68kException {
  2525:       if (HFS_BUFFER_TRACE || (HFS_COMMAND_TRACE && hfsCommandTraceOn)) {
  2526:         System.out.printf ("%08x write(fcb=0x%08x,address=0x%08x,length=0x%08x)\n", XEiJ.regPC0, hfsRequest22Fcb, hfsRequest14Namests, hfsRequest18Param);
  2527:       }
  2528:       if (!abuInserted) {  //挿入されていない。オープンされているファイルがあるときはイジェクト禁止なので挿入されていないということは通常はない。強制イジェクトに対応
  2529:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2530:         hfsRequest18Result = -1;
  2531:         hfsState = HFS_STATE_DONE;
  2532:         return;
  2533:       }
  2534:       if (abuWriteProtected) {  //書き込みが禁止されている。書き込みモードでオープンできたのだからオープンした後に書き込みが禁止されたということ。本来は書き込みモードでオープンされているときは書き込みを禁止できないようにするべき
  2535:         hfsRequest3Error = DEV_IGNORE | DEV_RETRY | DEV_ABORT | DEV_CANNOT_WRITE;  //書き込み不可能です
  2536:         hfsRequest18Result = -1;
  2537:         hfsState = HFS_STATE_DONE;
  2538:         return;
  2539:       }
  2540:       int fcb = hfsRequest22Fcb;
  2541:       HFHandle handle = hfuTargetHandle = hfuFcbToHandle.get (fcb);
  2542:       if (handle == null) {  //オープンされていない
  2543:         hfsRequest18Result = DOS_HANDLE_IS_NOT_OPENED;
  2544:         hfsState = HFS_STATE_DONE;
  2545:         return;
  2546:       }
  2547:       hfuTargetLength = hfsRequest18Param & 0xffffffffL;  //書き込む長さ
  2548:       if (HFS_BUFFER_TRACE) {
  2549:         System.out.printf ("length=%d\n", hfuTargetLength);
  2550:       }
  2551:       hfuTargetPosition = MC68060.mmuReadLongData (fcb + 6, XEiJ.regSRS) & 0xffffffffL;  //シーク位置
  2552:       if (HFS_BUFFER_TRACE) {
  2553:         System.out.printf ("position=%d\n", hfuTargetPosition);
  2554:       }
  2555:       hfuTargetFileSize = MC68060.mmuReadLongData (fcb + 64, XEiJ.regSRS) & 0xffffffffL;  //ファイルサイズ
  2556:       if (HFS_BUFFER_TRACE) {
  2557:         System.out.printf ("fileSize=%d\n", hfuTargetFileSize);
  2558:       }
  2559:       if (hfuTargetLength == 0L) {  //書き込む長さが0のときはシーク位置から後ろを切り捨てる
  2560:         if (hfuTargetFileSize <= hfuTargetPosition) {  //シーク位置がファイルの末尾なので何もしない
  2561:           hfsRequest18Result = 0;
  2562:           hfsState = HFS_STATE_DONE;
  2563:           return;
  2564:         }
  2565:         MC68060.mmuWriteLongData (fcb + 64, (int) hfuTargetPosition, XEiJ.regSRS);  //FCBのファイルサイズを更新する
  2566:         hfsRequest18Result = 1;  //シーク位置から後ろを切り捨てる
  2567:         hfsState = HFS_STATE_HOST;
  2568:         return;
  2569:       }
  2570:       if (0xffffffffL < hfuTargetPosition + hfuTargetLength) {
  2571:         hfuTargetLength = 0xffffffffL - hfuTargetPosition;  //ファイルサイズが4GB以上にならないようにする
  2572:         if (HFS_BUFFER_TRACE) {
  2573:           System.out.printf ("length=%d\n", hfuTargetLength);
  2574:         }
  2575:         if (hfuTargetLength == 0L) {
  2576:           hfsRequest18Result = 0;
  2577:           hfsState = HFS_STATE_DONE;
  2578:           return;
  2579:         }
  2580:       }
  2581:       hfuTargetAddress = hfsRequest14Namests & 0xffffffffL;  //アドレス
  2582:       if (HFS_BUFFER_TRACE) {
  2583:         System.out.printf ("address=0x%08x\n", hfuTargetAddress);
  2584:       }
  2585:       hfuTargetTransferred = 0L;  //書き込んだ長さ
  2586:       if (HFS_BUFFER_TRACE) {
  2587:         System.out.printf ("transferred=%d\n", hfuTargetTransferred);
  2588:       }
  2589:       //  ファイルサイズが0のとき
  2590:       //    バッファは空
  2591:       //    バッファを充填する必要がない
  2592:       //  ファイルサイズが0でないとき
  2593:       //    バッファが空のとき
  2594:       //      オープンした直後かバッファをフラッシュした直後
  2595:       //      バッファを充填する必要がある
  2596:       //    バッファが空でないとき
  2597:       //      バッファのデータはバッファの先頭からバッファの末尾またはファイルの末尾まで有効
  2598:       //      バッファの途中にファイルの末尾がある場合を含めて、
  2599:       //      バッファの範囲内でファイルにあってバッファにないデータは存在しない
  2600:       //      シーク位置がバッファの範囲内のとき
  2601:       //        バッファに読み書きできる部分がある
  2602:       //        バッファを充填する必要がない
  2603:       //      シーク位置がバッファの範囲外のとき
  2604:       //        バッファに読み書きできる部分がない
  2605:       //        バッファを充填する必要がある
  2606:       //  ファイルサイズが0またはバッファが空でなくてシーク位置がバッファの範囲内のときバッファを充填する必要がない
  2607:       //  ファイルサイズが0でなくてバッファが空またはシーク位置がバッファの範囲外のときバッファを充填する必要がある
  2608:       if (hfuTargetFileSize == 0 ||  //ファイルサイズが0または
  2609:           (0 < handle.hfhEnd &&  //バッファが空でなくて
  2610:            handle.hfhStart <= hfuTargetPosition && hfuTargetPosition < handle.hfhStart + HFS_BUFFER_SIZE)) {  //シーク位置がバッファの範囲内のとき
  2611:         //バッファを充填する必要がない
  2612:         hfsRequest18Result = 0;
  2613:         hfsState = HFS_STATE_X68K;
  2614:       } else {  //ファイルサイズが0でなくてバッファが空またはシーク位置がバッファの範囲外のとき
  2615:         //バッファを充填する必要がある
  2616:         hfsRequest18Result = 0;  //シーク位置から後ろを切り捨てない
  2617:         hfsState = HFS_STATE_HOST;
  2618:       }
  2619:     }  //unit.hfuCallWrite()
  2620: 
  2621:     //unit.hfuCallWriteHost ()
  2622:     public void hfuCallWriteHost () {
  2623:       HFHandle handle = hfuTargetHandle;
  2624:       RandomAccessFile raf = handle.hfhRaf;
  2625:       if (hfsRequest18Result != 0) {  //シーク位置から後ろを切り捨てる
  2626:         if (handle.hfhStart <= hfuTargetPosition && hfuTargetPosition < handle.hfhEnd) {  //シーク位置がバッファのデータの範囲内のとき
  2627:           //バッファのデータのシーク位置から後ろを切り捨てる
  2628:           Arrays.fill (handle.hfhBuffer, (int) (hfuTargetPosition - handle.hfhStart), (int) (handle.hfhEnd - handle.hfhStart), (byte) 0);
  2629:           handle.hfhEnd = hfuTargetPosition;
  2630:           if (HFS_BUFFER_TRACE) {
  2631:             System.out.printf ("truncate(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
  2632:           }
  2633:         }
  2634:         try {
  2635:           raf.seek (hfuTargetPosition);  //シーク位置
  2636:         } catch (IOException ioe) {
  2637:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
  2638:         }
  2639:         //  human302  0x0000c6c8
  2640:         try {
  2641:           raf.setLength (hfuTargetPosition);  //シーク位置から後ろを切り捨てる
  2642:         } catch (IOException ioe) {
  2643:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "ファイルの長さを設定できません: " : "Could not set length of file: ") + handle.toString ());
  2644:           hfsRequest18Result = -1;
  2645:           hfsState = HFS_STATE_DONE;
  2646:           return;
  2647:         }
  2648:         hfsRequest18Result = 0;
  2649:         hfsState = HFS_STATE_DONE;
  2650:         return;
  2651:       }
  2652:       //バッファをフラッシュする
  2653:       if (handle.hfhDirty) {  //ダーティデータが残っている
  2654:         if (HFS_BUFFER_TRACE) {
  2655:           System.out.printf ("delaywrite(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
  2656:         }
  2657:         try {
  2658:           raf.seek (handle.hfhStart);
  2659:         } catch (IOException ioe) {
  2660:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
  2661:         }
  2662:         try {
  2663:           raf.write (handle.hfhBuffer, 0, (int) (handle.hfhEnd - handle.hfhStart));  //RandomAccessFileのwriteは返却値がない
  2664:         } catch (IOException ioe) {
  2665:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "遅延書き込みに失敗しました: " : "Delayed write failed: ") + handle.toString ());
  2666:         }
  2667:         handle.hfhDirty = false;
  2668:       }  //if handle.hfhDirty
  2669:       //バッファを充填する
  2670:       int ll = (int) Math.min (HFS_BUFFER_SIZE,  //バッファの長さ
  2671:                                hfuTargetFileSize - hfuTargetPosition  //シーク位置からファイルの末尾までの長さ
  2672:                                );  //バッファに読み出す長さ。末尾に書き込むときは0
  2673:       int kk = 0;  //バッファに読み出した長さ
  2674:       Arrays.fill (handle.hfhBuffer, (byte) 0);
  2675:       handle.hfhStart = hfuTargetPosition;  //バッファの先頭のシーク位置
  2676:       handle.hfhEnd = hfuTargetPosition + ll;  //バッファのデータの末尾のシーク位置
  2677:       handle.hfhDirty = false;  //ダーティデータなし
  2678:       if (HFS_BUFFER_TRACE) {
  2679:         System.out.printf ("preread(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
  2680:       }
  2681:       if (0 < ll) {
  2682:         try {
  2683:           raf.seek (hfuTargetPosition);
  2684:         } catch (IOException ioe) {
  2685:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
  2686:         }
  2687:         try {
  2688:           while (kk < ll) {
  2689:             int tt = raf.read (handle.hfhBuffer, kk, ll - kk);  //今回読み出した長さ。エラーでなければ1以上
  2690:             if (tt < 0) {  //途中でEOFに達した。FCBのファイルサイズが合っていれば起こらないはずだが念のため
  2691:               //先読みに失敗した
  2692:               hfsRequest18Result = -1;
  2693:               hfsState = HFS_STATE_DONE;
  2694:               return;
  2695:             }
  2696:             kk += tt;
  2697:           }
  2698:         } catch (IOException ioe) {
  2699:           XEiJ.prgMessage ((Multilingual.mlnJapanese ? "リードエラー: " : "Read error: ") + handle.toString ());
  2700:           hfsRequest18Result = -1;
  2701:           hfsState = HFS_STATE_DONE;
  2702:           return;
  2703:         }
  2704:       }
  2705:       //バッファに書き込む
  2706:       hfsRequest18Result = 0;
  2707:       hfsState = HFS_STATE_X68K;
  2708:     }  //unit.hfuCallWriteHost()
  2709: 
  2710:     //unit.hfuCallWriteX68k ()
  2711:     public void hfuCallWriteX68k () throws M68kException {
  2712:       HFHandle handle = hfuTargetHandle;
  2713:       int fcb = handle.hfhFcb;
  2714:       //バッファに書き込む
  2715:       long t = Math.min (hfuTargetLength - hfuTargetTransferred,  //転送する残りの長さ
  2716:                          handle.hfhStart + HFS_BUFFER_SIZE - hfuTargetPosition  //シーク位置からバッファの末尾までの長さ
  2717:                          );  //バッファに書き込める長さ
  2718:       if (HFS_BUFFER_TRACE) {
  2719:         System.out.printf ("t=%d\n", t);
  2720:       }
  2721:       MC68060.mmuReadByteArray ((int) (hfuTargetAddress + hfuTargetTransferred),
  2722:                                 handle.hfhBuffer,
  2723:                                 (int) (hfuTargetPosition - handle.hfhStart),  //バッファの先頭からシーク位置までのオフセット
  2724:                                 (int) t,  //書き込む長さ
  2725:                                 XEiJ.regSRS);  //バッファに書き込む
  2726:       handle.hfhEnd = Math.max (handle.hfhEnd, hfuTargetPosition + t);  //バッファのデータが長くなる
  2727:       handle.hfhDirty = true;  //ダーティデータあり
  2728:       if (HFS_BUFFER_TRACE) {
  2729:         System.out.printf ("written(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x,dirty=%b)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd, handle.hfhDirty);
  2730:       }
  2731:       hfuTargetPosition += t;  //シーク位置を更新する
  2732:       if (HFS_BUFFER_TRACE) {
  2733:         System.out.printf ("position=%d\n", hfuTargetPosition);
  2734:       }
  2735:       MC68060.mmuWriteLongData (fcb + 6, (int) hfuTargetPosition, XEiJ.regSRS);  //FCBのシーク位置を更新する
  2736:       if (hfuTargetFileSize < hfuTargetPosition) {  //ファイルが長くなった
  2737:         hfuTargetFileSize = hfuTargetPosition;  //ファイルサイズを更新する
  2738:         if (HFS_BUFFER_TRACE) {
  2739:           System.out.printf ("fileSize=%d\n", hfuTargetFileSize);
  2740:         }
  2741:         MC68060.mmuWriteLongData (fcb + 64, (int) hfuTargetFileSize, XEiJ.regSRS);  //FCBのファイルサイズを更新する
  2742:       }
  2743:       hfuTargetTransferred += t;  //書き込んだ長さを更新する
  2744:       if (HFS_BUFFER_TRACE) {
  2745:         System.out.printf ("transferred=%d\n", hfuTargetTransferred);
  2746:       }
  2747:       if (hfuTargetLength <= hfuTargetTransferred) {  //終了
  2748:         hfsRequest18Result = (int) hfuTargetTransferred;  //書き込んだ長さ
  2749:         hfsState = HFS_STATE_DONE;
  2750:         return;
  2751:       }
  2752:       //バッファを使い切ったので続きはバッファを充填するところから行う
  2753:       hfsRequest18Result = 0;  //シーク位置から後ろを切り捨てない
  2754:       hfsState = HFS_STATE_HOST;
  2755:     }  //unit.hfuCallWriteX68k()
  2756: 
  2757:     //unit.hfuCallSeek ()
  2758:     //  0x4e FF42 _SEEK ハンドラのシーク位置の変更
  2759:     //  リクエストヘッダ
  2760:     //       0.b  i   26
  2761:     //       1.b  i   ユニット番号
  2762:     //       2.b  i   コマンドコード。0x4e/0xce
  2763:     //       3.b  o   エラーコード下位
  2764:     //       4.b  o   エラーコード上位
  2765:     //      13.b  i   シークモード(0=先頭から,1=現在位置から,2=終端から)
  2766:     //      14.l  i   0
  2767:     //      18.l  i   オフセット
  2768:     //            o   現在のシーク位置/リザルトステータス
  2769:     //      22.l  i   FCBテーブルのアドレス
  2770:     //    (26バイト)
  2771:     public void hfuCallSeek () throws M68kException {
  2772:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  2773:         System.out.printf ("%08x seek(fcb=0x%08x,offset=0x%08x,mode=0x%02x)\n", XEiJ.regPC0, hfsRequest22Fcb, hfsRequest18Param, hfsRequest13Mode);
  2774:       }
  2775:       if (!abuInserted) {  //挿入されていない。オープンされているファイルがあるときはイジェクト禁止なので挿入されていないということは通常はない。強制イジェクトに対応
  2776:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2777:         hfsRequest18Result = -1;
  2778:         hfsState = HFS_STATE_DONE;
  2779:         return;
  2780:       }
  2781:       int m = hfsRequest13Mode;  //モード(0=先頭から,1=現在位置から,2=終端から)
  2782:       if (m < 0 || 2 < m) {
  2783:         hfsRequest18Result = DOS_INVALID_PARAMETER;  //パラメータが無効
  2784:         hfsState = HFS_STATE_DONE;
  2785:         return;
  2786:       }
  2787:       int fcb = hfsRequest22Fcb;
  2788:       long o = (long) hfsRequest18Param;  //オフセット
  2789:       long p = MC68060.mmuReadLongData (fcb + 6, XEiJ.regSRS) & 0xffffffffL;  //シーク位置
  2790:       long s = MC68060.mmuReadLongData (fcb + 64, XEiJ.regSRS) & 0xffffffffL;  //ファイルサイズ
  2791:       p = (m == 0 ? 0L : m == 1 ? p : s) + o;  //新しいシーク位置
  2792:       if (p < 0L || s < p) {
  2793:         hfsRequest18Result = DOS_SEEK_OVER_EOF;  //EOFを越えてシークしようとした
  2794:         hfsState = HFS_STATE_DONE;
  2795:         return;
  2796:       }
  2797:       HFHandle handle = hfuFcbToHandle.get (fcb);
  2798:       if (handle == null) {  //オープンされていない
  2799:         hfsRequest18Result = DOS_HANDLE_IS_NOT_OPENED;
  2800:         hfsState = HFS_STATE_DONE;
  2801:         return;
  2802:       }
  2803:       MC68060.mmuWriteLongData (fcb + 6, (int) p, XEiJ.regSRS);  //FCBのシーク位置を更新する
  2804:       hfsRequest18Result = (int) p;
  2805:       hfsState = HFS_STATE_DONE;
  2806:     }  //unit.hfuCallSeek()
  2807: 
  2808:     //unit.hfuCallFiledate ()
  2809:     //  0x4f FF87 _FILEDATE ハンドラの更新日時の取得と設定
  2810:     //  リクエストヘッダ
  2811:     //       0.b  i   26
  2812:     //       1.b  i   ユニット番号
  2813:     //       2.b  i   コマンドコード。0x4f/0xcf
  2814:     //       3.b  o   エラーコード下位
  2815:     //       4.b  o   エラーコード上位
  2816:     //      13.b  i   0
  2817:     //      14.l  i   0
  2818:     //      18.l  i   日付<<16|時刻,0=読み込み
  2819:     //            o   日付<<16|時刻/リザルトステータス
  2820:     //      22.l  i   FCBテーブルのアドレス
  2821:     //    (26バイト)
  2822:     //  ディレクトリはオープンできないので日時を変更できない
  2823:     public void hfuCallFiledate () throws M68kException {
  2824:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  2825:         System.out.printf ("%08x filedate(fcb=0x%08x,datetime=0x%08x)\n", XEiJ.regPC0, hfsRequest22Fcb, hfsRequest18Param);
  2826:       }
  2827:       if (!abuInserted) {  //挿入されていない。オープンされているファイルがあるときはイジェクト禁止なので挿入されていないということは通常はない。強制イジェクトに対応
  2828:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2829:         hfsRequest18Result = -1;
  2830:         hfsState = HFS_STATE_DONE;
  2831:         return;
  2832:       }
  2833:       int fcb = hfsRequest22Fcb;
  2834:       int datetime = hfsRequest18Param;  //日時
  2835:       if (datetime == 0) {  //読み出し
  2836:         datetime = (MC68060.mmuReadWordZeroData (fcb + 60, XEiJ.regSRS) << 16 |  //日付。(西暦年-1980)<<9|月<<5|月通日
  2837:                     MC68060.mmuReadWordZeroData (fcb + 58, XEiJ.regSRS));  //時刻。時<<11|分<<5|秒/2
  2838:       } else {  //設定
  2839:         //アクセスモードとの整合はHumanによって確認済み
  2840:         //  FCBのアクセスモードが読み出しなのに特殊デバイスの_FILEDATEが書き込みで呼び出されることはない
  2841:         //FCBの日時は更新されていないのでここで更新する必要がある
  2842:         int time = datetime & 0xffff;  //時刻。時<<11|分<<5|秒/2
  2843:         int date = datetime >>> 16;  //日付。(西暦年-1980)<<9|月<<5|月通日
  2844:         MC68060.mmuWriteWordData (fcb + 58, time, XEiJ.regSRS);  //FCBの時刻。時<<11|分<<5|秒/2
  2845:         MC68060.mmuWriteWordData (fcb + 60, date, XEiJ.regSRS);  //FCBの日付。(西暦年-1980)<<9|月<<5|月通日
  2846:         //書き込み中のファイルのタイムスタンプを更新してもクローズしたときにクローズした日時が上書きされてしまうのでクローズした後に設定する
  2847:         int type = MC68060.mmuModifyByteSignData (fcb + 1, XEiJ.regSRS);  //デバイスタイプ
  2848:         if ((type & 0x40) != 0) {
  2849:           MC68060.mmuWriteByteData (fcb + 1, type & ~0x40, XEiJ.regSRS);  //_CLOSEするときクローズした日時ではなくFCBの日時を設定する
  2850:         }
  2851:       }
  2852:       hfsRequest18Result = datetime;
  2853:       hfsState = HFS_STATE_DONE;
  2854:     }  //unit.hfuCallFiledate()
  2855: 
  2856:     //unit.hfuCallDskfre ()
  2857:     //  0x50 FF36 _DSKFRE ドライブの空容量の取得
  2858:     //  リクエストヘッダ
  2859:     //       0.b  i   26
  2860:     //       1.b  i   ユニット番号
  2861:     //       2.b  i   コマンドコード。0x50/0xd0
  2862:     //       3.b  o   エラーコード下位
  2863:     //       4.b  o   エラーコード上位
  2864:     //      13.b  i   0
  2865:     //      14.l  i   バッファのアドレス
  2866:     //                     0.w    使用可能なクラスタ数
  2867:     //                     2.w    総クラスタ数(データ領域のセクタ数/1クラスタあたりのセクタ数)
  2868:     //                     4.w    1クラスタあたりのセクタ数
  2869:     //                     6.w    1セクタあたりのバイト数
  2870:     //                  (8バイト)
  2871:     //      18.l  i   0
  2872:     //            o   使用可能なバイト数/リザルトステータス
  2873:     //      22.l  i   0
  2874:     //    (26バイト)
  2875:     public void hfuCallDskfre () throws M68kException {
  2876:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  2877:         System.out.printf ("%08x dskfre(buffer=0x%08x)\n", XEiJ.regPC0, hfsRequest14Namests);
  2878:       }
  2879:       if (!abuInserted) {  //挿入されていない
  2880:         hfsRequest3Error = DEV_RETRY | DEV_ABORT | DEV_INSERT_MEDIA;  //ディスクが入っていません、入れてください
  2881:         hfsRequest18Result = -1;
  2882:         hfsState = HFS_STATE_DONE;
  2883:         return;
  2884:       }
  2885:       hfsRequest18Result = 0;
  2886:       hfsState = HFS_STATE_HOST;
  2887:     }  //unit.hfuCallDskfre()
  2888: 
  2889:     //unit.hfuCallDskfreHost ()
  2890:     public void hfuCallDskfreHost () {
  2891:       File file = new File (hfuRootPath);
  2892:       hfuTargetTotalSpace = file.getTotalSpace ();
  2893:       hfuTargetFreeSpace = file.getFreeSpace ();
  2894:       hfsRequest18Result = 0;
  2895:       hfsState = HFS_STATE_X68K;
  2896:     }  //unit.hfuCallDskfreHost()
  2897: 
  2898:     //unit.hfuCallDskfreX68k ()
  2899:     public void hfuCallDskfreX68k () throws M68kException {
  2900:       int totalSpace = (int) Math.min (0x7fffffffL, hfuTargetTotalSpace);  //2GBを上限とする
  2901:       int freeSpace = (int) Math.min (0x7fffffffL, hfuTargetFreeSpace);  //2GBを上限とする
  2902:       int clusterBit = Math.max (0, 7 - Integer.numberOfLeadingZeros (totalSpace));
  2903:       MC68060.mmuWriteWordData (hfsRequest14Namests, freeSpace >>> clusterBit + 10, XEiJ.regSRS);  //使用可能なクラスタ数
  2904:       MC68060.mmuWriteWordData (hfsRequest14Namests + 2, totalSpace >>> clusterBit + 10, XEiJ.regSRS);  //総クラスタ数
  2905:       MC68060.mmuWriteWordData (hfsRequest14Namests + 4, 1 << clusterBit, XEiJ.regSRS);  //1クラスタあたりのセクタ数
  2906:       MC68060.mmuWriteWordData (hfsRequest14Namests + 6, 1 << 10, XEiJ.regSRS);  //1セクタあたりのバイト数
  2907:       hfsRequest18Result = freeSpace;
  2908:       hfsState = HFS_STATE_DONE;
  2909:       //COMMAND.XのDIRコマンドの容量表示は最大2GBの返却値に頼っている上に(1クラスタあたりのセクタ数*1セクタあたりのバイト数)の上位9ビットを無視するのでまったくあてにならない
  2910:     }  //unit.hfuCallDskfreX68k()
  2911: 
  2912:     //unit.hfuCallDrvctrl ()
  2913:     //  0x51 FF0F _DRVCTRL ドライブコントロール
  2914:     //  リクエストヘッダ
  2915:     //       0.b  i   26
  2916:     //       1.b  i   ユニット番号
  2917:     //       2.b  i   コマンドコード。0x51/0xd1
  2918:     //       3.b  o   エラーコード下位
  2919:     //       4.b  o   エラーコード上位
  2920:     //      13.b  i   モード
  2921:     //                   0    センス
  2922:     //                   1    イジェクト
  2923:     //                   2    イジェクト禁止
  2924:     //                   3    イジェクト許可
  2925:     //                   4    挿入されていないときLED点滅
  2926:     //                   9    カレントディレクトリフラッシュ、サーチFATフラッシュ
  2927:     //                  10    サーチFATフラッシュ
  2928:     //                  16~  拡張モード
  2929:     //            o   センス結果
  2930:     //      14.l  i   _DRVCTRLのパラメータのドライブ番号の次のアドレス
  2931:     //      18.l  i   0
  2932:     //            o   リザルトステータス
  2933:     //      22.l  i   0
  2934:     //    (26バイト)
  2935:     public void hfuCallDrvctrl () throws M68kException {
  2936:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  2937:         System.out.printf ("%08x drvctrl(mode=0x%02x,param=0x%08x)\n", XEiJ.regPC0, hfsRequest13Mode, hfsRequest14Namests);
  2938:       }
  2939:       switch (hfsRequest13Mode) {
  2940:       case 1:  //イジェクト
  2941:         if (hfuFcbToHandle.isEmpty ()) {  //オープンされているファイルがない
  2942:           eject ();
  2943:         }
  2944:         break;
  2945:       case 2:  //イジェクト禁止
  2946:         prevent ();
  2947:         break;
  2948:       case 3:  //イジェクト許可
  2949:         if (hfuFcbToHandle.isEmpty ()) {  //オープンされているファイルがない
  2950:           allow ();
  2951:         }
  2952:         break;
  2953:       }
  2954:       MC68060.mmuWriteByteData (hfsRequestHeader + 13,
  2955:                                 (abuInserted ? ABU_INSERTED : 0) |
  2956:                                 (abuWriteProtected ? ABU_WRITE_PROTECTED : 0) |
  2957:                                 (abuBuffered ? ABU_BUFFERED : 0) |
  2958:                                 (abuEjectPrevented ? ABU_EJECT_PREVENTED : 0), XEiJ.regSRS);
  2959:       hfsRequest18Result = 0;
  2960:       hfsState = HFS_STATE_DONE;
  2961:     }  //unit.hfuCallDrvctrl()
  2962: 
  2963:     //unit.hfuCallGetdpb ()
  2964:     //  0x52 FF32 _GETDPB DPBの取得
  2965:     //  リクエストヘッダ
  2966:     //       0.b  i   26
  2967:     //       1.b  i   ユニット番号
  2968:     //       2.b  i   コマンドコード。0x52/0xd2
  2969:     //       3.b  o   エラーコード下位
  2970:     //       4.b  o   エラーコード上位
  2971:     //      13.b  i   0
  2972:     //      14.l  i   バッファのアドレス(_GETDPBのパラメータ+2)
  2973:     //                                      +呼び出される前に設定される
  2974:     //                   DOS                |  +呼び出された後に上書きされる
  2975:     //                  FF32  0x52          |  |
  2976:     //                    +0        .b      *  -  内部ドライブ番号(0=A:)
  2977:     //                    +1        .b      *  -  ユニット番号
  2978:     //                    +2    +0  .w      -  -  1セクタあたりのバイト数(0=特殊デバイスドライバ)
  2979:     //                    +4    +2  .b      -  -  1クラスタあたりのセクタ数-1
  2980:     //                    +5    +3  .b      -  -  クラスタ数をセクタ数に変換するときのシフトカウント
  2981:     //                                      -  -  bit7=1のとき2バイトFATの上下のバイトを入れ換える
  2982:     //                    +6    +4  .w      -  -  FATの先頭セクタ番号
  2983:     //                    +8    +6  .b      -  -  FAT領域の個数
  2984:     //                    +9    +7  .b      -  -  1個のFAT領域に使用するセクタ数
  2985:     //                   +10    +8  .w      -  -  ルートディレクトリに入るエントリ数
  2986:     //                   +12   +10  .w      -  -  データ部の先頭セクタ番号
  2987:     //                   +14   +12  .w      -  -  総クラスタ数+3
  2988:     //                   +16   +14  .w      -  -  ルートディレクトリの先頭セクタ番号
  2989:     //                        (16バイト)
  2990:     //                   +18        .l      *  -  デバイスヘッダのアドレス
  2991:     //                   +22        .b      -  *  メディアバイト(特殊デバイスのときは内部ドライブ名('a'~))
  2992:     //                   +23        .b      -  0  内部DPBテーブル使用フラグ(-1でアクセスなし)
  2993:     //                   +24        .l      *  -  次の内部DPBテーブル(-1=終わり)
  2994:     //                   +28        .w      -  0  カレントディレクトリのクラスタ番号(0=ルートディレクトリ)
  2995:     //                   +30        .b[64]  -  *  カレントディレクトリ
  2996:     //                  (94バイト)
  2997:     //      18.l  i   0
  2998:     //            o   リザルトステータス
  2999:     //      22.l  i   0
  3000:     //    (26バイト)
  3001:     public void hfuCallGetdpb () throws M68kException {
  3002:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  3003:         System.out.printf ("%08x getdpb(buffer=0x%08x)\n", XEiJ.regPC0, hfsRequest14Namests);
  3004:       }
  3005:       MC68060.mmuWriteLongData (hfsRequest14Namests, 0, XEiJ.regSRS);
  3006:       MC68060.mmuWriteLongData (hfsRequest14Namests + 4, 0, XEiJ.regSRS);
  3007:       MC68060.mmuWriteLongData (hfsRequest14Namests + 8, 0, XEiJ.regSRS);
  3008:       MC68060.mmuWriteLongData (hfsRequest14Namests + 12, 0, XEiJ.regSRS);
  3009:       hfsRequest18Result = 0;
  3010:       hfsState = HFS_STATE_DONE;
  3011:     }  //unit.hfuCallGetdpb()
  3012: 
  3013:     //unit.hfuCallDiskred ()
  3014:     //  0x53 FFF3 _DISKRED ハンドラから直接読み込む
  3015:     //  リクエストヘッダ
  3016:     //       0.b  i   26
  3017:     //       1.b  i   ユニット番号
  3018:     //       2.b  i   コマンドコード。0x53/0xd3
  3019:     //       3.b  o   エラーコード下位
  3020:     //       4.b  o   エラーコード上位
  3021:     //      13.b  i   メディアバイト
  3022:     //      14.l  i   バッファの先頭アドレス
  3023:     //      18.l  i   セクタ数
  3024:     //            o   リザルトステータス
  3025:     //      22.l  i   先頭のセクタ番号
  3026:     //    (26バイト)
  3027:     public void hfuCallDiskred () throws M68kException {
  3028:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  3029:         System.out.printf ("%08x diskred(buffer=0x%08x,start=0x%08x,count=0x%08x,mediabyte=0x%02x)\n", XEiJ.regPC0, hfsRequest14Namests, hfsRequest22Fcb, hfsRequest18Param, hfsRequest13Mode);
  3030:       }
  3031:       hfsRequest18Result = -1;
  3032:       hfsState = HFS_STATE_DONE;
  3033:     }  //unit.hfuCallDiskred()
  3034: 
  3035:     //unit.hfuCallDiskwrt ()
  3036:     //  0x54 FFF4 _DISKWRT ハンドラに直接書き込む
  3037:     //  リクエストヘッダ
  3038:     //       0.b  i   26
  3039:     //       1.b  i   ユニット番号
  3040:     //       2.b  i   コマンドコード。0x54/0xd4
  3041:     //       3.b  o   エラーコード下位
  3042:     //       4.b  o   エラーコード上位
  3043:     //      13.b  i   メディアバイト
  3044:     //      14.l  i   バッファの先頭アドレス
  3045:     //      18.l  i   セクタ数
  3046:     //            o   リザルトステータス
  3047:     //      22.l  i   先頭のセクタ番号
  3048:     //    (26バイト)
  3049:     public void hfuCallDiskwrt () throws M68kException {
  3050:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  3051:         System.out.printf ("%08x diskwrt(buffer=0x%08x,start=0x%08x,count=0x%08x,mediabyte=0x%02x)\n", XEiJ.regPC0, hfsRequest14Namests, hfsRequest22Fcb, hfsRequest18Param, hfsRequest13Mode);
  3052:       }
  3053:       hfsRequest18Result = -1;
  3054:       hfsState = HFS_STATE_DONE;
  3055:     }  //unit.hfuCallDiskwrt()
  3056: 
  3057:     //unit.hfuCallIoctrl ()
  3058:     //  0x55 FF44 _IOCTRL デバイスによるハンドラの直接制御
  3059:     //                  12  ハンドラ番号による特殊コントロール
  3060:     //                  13  ドライブ番号による特殊コントロール
  3061:     //  リクエストヘッダ
  3062:     //       0.b  i   26
  3063:     //       1.b  i   ユニット番号
  3064:     //       2.b  i   コマンドコード。0x55/0xd5
  3065:     //       3.b  o   エラーコード下位
  3066:     //       4.b  o   エラーコード上位
  3067:     //      13.b  i   0
  3068:     //      14.l  i   ポインタ
  3069:     //      18.l  i   上位ワード  コマンド
  3070:     //            o   リザルトステータス
  3071:     //      22.l  i   0
  3072:     //    (26バイト)
  3073:     public void hfuCallIoctrl () throws M68kException {
  3074:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  3075:         System.out.printf ("%08x ioctrl(command=0x%08x,param=0x%08x)\n", XEiJ.regPC0, hfsRequest18Param, hfsRequest14Namests);
  3076:       }
  3077:       hfsRequest18Result = -1;
  3078:       hfsState = HFS_STATE_DONE;
  3079:     }  //unit.hfuCallIoctrl()
  3080: 
  3081:     //unit.hfuCallFflush ()
  3082:     //  0x56 FF0D _FFLUSH バッファフラッシュ
  3083:     //  リクエストヘッダ
  3084:     //       0.b  i   26
  3085:     //       1.b  i   ユニット番号
  3086:     //       2.b  i   コマンドコード。0x56/0xd6
  3087:     //       3.b  o   エラーコード下位
  3088:     //       4.b  o   エラーコード上位
  3089:     //      13.b  i   0
  3090:     //      14.l  i   0
  3091:     //      18.l  i   0
  3092:     //            o   リザルトステータス
  3093:     //      22.l  i   0
  3094:     //    (26バイト)
  3095:     public void hfuCallFflush () throws M68kException {
  3096:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  3097:         System.out.printf ("%08x fflush()\n", XEiJ.regPC0);
  3098:       }
  3099:       hfsRequest18Result = 0;
  3100:       hfsState = HFS_STATE_HOST;
  3101:     }  //unit.hfuCallFflush()
  3102: 
  3103:     //unit.hfuCallFflushHost ()
  3104:     public void hfuCallFflushHost () {
  3105:       for (HFHandle handle : hfuFcbToHandle.values ()) {
  3106:         //バッファをフラッシュする
  3107:         if (handle.hfhDirty) {  //ダーティデータが残っている
  3108:           if (HFS_BUFFER_TRACE) {
  3109:             System.out.printf ("delaywrite(fcb=0x%08x,name=\"%s\",start=0x%08x,end=0x%08x)\n", handle.hfhFcb, handle.hfhFile.toString(), handle.hfhStart, handle.hfhEnd);
  3110:           }
  3111:           RandomAccessFile raf = handle.hfhRaf;
  3112:           try {
  3113:             raf.seek (handle.hfhStart);
  3114:           } catch (IOException ioe) {
  3115:             XEiJ.prgMessage ((Multilingual.mlnJapanese ? "シークエラー: " : "Seek error: ") + handle.toString ());
  3116:           }
  3117:           try {
  3118:             raf.write (handle.hfhBuffer, 0, (int) (handle.hfhEnd - handle.hfhStart));  //RandomAccessFileのwriteは返却値がない
  3119:           } catch (IOException ioe) {
  3120:             XEiJ.prgMessage ((Multilingual.mlnJapanese ? "遅延書き込みに失敗しました: " : "Delayed write failed: ") + handle.toString ());
  3121:           }
  3122:           handle.hfhDirty = false;
  3123:         }  //if handle.hfhDirty
  3124:         handle.hfhStart = 0L;
  3125:         handle.hfhEnd = 0L;
  3126:       }  //for handle
  3127:       hfsRequest18Result = 0;
  3128:       hfsState = HFS_STATE_DONE;
  3129:     }  //unit.hfuCallFflushHost()
  3130: 
  3131:     //unit.hfuCallMediacheck ()
  3132:     //  0x57 mediacheck メディアチェック
  3133:     //  リクエストヘッダ
  3134:     //       0.b  i   26
  3135:     //       1.b  i   ユニット番号
  3136:     //       2.b  i   コマンドコード。0x57/0xd7
  3137:     //       3.b  o   エラーコード下位
  3138:     //       4.b  o   エラーコード上位
  3139:     //      13.b  i   0
  3140:     //      14.l  i   0
  3141:     //      18.l  i   0
  3142:     //            o   最下位バイト  0=メディア交換なし,-1=メディア交換あり
  3143:     //      22.l  i   0
  3144:     //    (26バイト)
  3145:     public void hfuCallMediacheck () throws M68kException {
  3146:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  3147:         System.out.printf ("%08x mediacheck()\n", XEiJ.regPC0);
  3148:       }
  3149:       hfsRequest18Result = hasBeenEjected () ? -1 : 0;
  3150:       hfsState = HFS_STATE_DONE;
  3151:     }  //unit.hfuCallMediacheck()
  3152: 
  3153:     //unit.hfuCallLock ()
  3154:     //  0x58 FF8C _LOCK ハンドラのロックの制御
  3155:     //  リクエストヘッダ
  3156:     //       0.b  i   26
  3157:     //       1.b  i   ユニット番号
  3158:     //       2.b  i   コマンドコード。0x58/0xd8
  3159:     //       3.b  o   エラーコード下位
  3160:     //       4.b  o   エラーコード上位
  3161:     //      13.b  i   0=lock,1=unlock
  3162:     //      14.l  i   lock/unlockするバイト数
  3163:     //      18.l  i   lock/unlockするシーク位置
  3164:     //            o   リザルトステータス
  3165:     //      22.l  i   FCBテーブルのアドレス
  3166:     //    (26バイト)
  3167:     public void hfuCallLock () throws M68kException {
  3168:       if (HFS_COMMAND_TRACE && hfsCommandTraceOn) {
  3169:         System.out.printf ("%08x %s(fcb=0x%08x,offset=0x%08x,length=0x%08x)\n", XEiJ.regPC0, hfsRequest13Mode == 0 ? "lock" : "unlock", hfsRequest22Fcb, hfsRequest18Param, hfsRequest14Namests);
  3170:       }
  3171:       hfsRequest18Result = 0;
  3172:       hfsState = HFS_STATE_DONE;
  3173:     }  //unit.hfuCallLock()
  3174: 
  3175:     //hfuFileInfo (file, b)
  3176:     //  ファイルの情報の取得(ホスト側)
  3177:     //  属性、時刻、日付、ファイルサイズを読み取る
  3178:     //  ファイル名は設定済み
  3179:     //    b[0]     属性。eladvshr
  3180:     //    b[1..2]  時刻。時<<11|分<<5|秒/2
  3181:     //    b[3..4]  日付。(西暦年-1980)<<9|月<<5|月通日
  3182:     //    b[5..8]  ファイルサイズ
  3183:     //    b[9..31]  ファイル名
  3184:     private void hfuFileInfo (File file, byte[] b) {
  3185:       //属性
  3186:       b[0] = (byte) ((file.isFile () ? HumanMedia.HUM_ARCHIVE : 0) |
  3187:                      (file.isDirectory () ? HumanMedia.HUM_DIRECTORY : 0) |
  3188:                      (file.isHidden () ? HumanMedia.HUM_HIDDEN : 0) |
  3189:                      (!file.canWrite () ? HumanMedia.HUM_READONLY : 0));
  3190:       //更新日時
  3191:       long dttm = DnT.dntDttmCmil (file.lastModified () + RP5C15.rtcCmilGap);  //西暦年<<42|月<<38|月通日<<32|時<<22|分<<16|秒<<10|ミリ秒。FCBの日時はRTCの日時なのでオフセットを加える
  3192:       //時刻
  3193:       int time = DnT.dntHourDttm (dttm) << 11 | DnT.dntMinuDttm (dttm) << 5 | DnT.dntSecoDttm (dttm) >> 1;  //時<<11|分<<5|秒/2
  3194:       b[1] = (byte) (time >> 8);
  3195:       b[2] = (byte) time;
  3196:       //日付
  3197:       int date = DnT.dntYearDttm (dttm) - 1980 << 9 | DnT.dntMontDttm (dttm) << 5 | DnT.dntMdayDttm (dttm);  //(西暦年-1980)<<9|月<<5|月通日
  3198:       b[3] = (byte) (date >> 8);
  3199:       b[4] = (byte) date;
  3200:       //ファイルサイズ
  3201:       int size = (int) Math.min (0xffffffffL, file.length ());  //4GB未満。intでマイナスのときは2GB以上
  3202:       b[5] = (byte) (size >> 24);
  3203:       b[6] = (byte) (size >> 16);
  3204:       b[7] = (byte) (size >> 8);
  3205:       b[8] = (byte) size;
  3206:     }  //hfuFileInfo(File,byte[])
  3207: 
  3208:     //hfuPrintFileInfo (b)
  3209:     //  ファイルの情報の表示
  3210:     //    b[0]     属性。eladvshr
  3211:     //    b[1..2]  時刻。時<<11|分<<5|秒/2
  3212:     //    b[3..4]  日付。(西暦年-1980)<<9|月<<5|月通日
  3213:     //    b[5..8]  ファイルサイズ
  3214:     //    b[9..31]  ファイル名
  3215:     public void hfuPrintFileInfo (byte[] b) {
  3216:       StringBuilder sb = new StringBuilder ();
  3217:       //    b[0]     属性。eladvshr
  3218:       int attr = b[0] & 255;
  3219:       sb.append ((attr & HumanMedia.HUM_EXECUTABLE) != 0 ? 'e' : '-');
  3220:       sb.append ((attr & HumanMedia.HUM_LINK      ) != 0 ? 'l' : '-');
  3221:       sb.append ((attr & HumanMedia.HUM_ARCHIVE   ) != 0 ? 'a' : '-');
  3222:       sb.append ((attr & HumanMedia.HUM_DIRECTORY ) != 0 ? 'd' : '-');
  3223:       sb.append ((attr & HumanMedia.HUM_VOLUME    ) != 0 ? 'v' : '-');
  3224:       sb.append ((attr & HumanMedia.HUM_SYSTEM    ) != 0 ? 's' : '-');
  3225:       sb.append ((attr & HumanMedia.HUM_HIDDEN    ) != 0 ? 'h' : '-');
  3226:       sb.append ((attr & HumanMedia.HUM_READONLY  ) != 0 ? 'r' : '-');
  3227:       sb.append ("  ");
  3228:       //    b[3..4]  日付。(西暦年-1980)<<9|月<<5|月通日
  3229:       int date = (char) (b[3] << 8 | b[4] & 255);
  3230:       XEiJ.fmtSB02u (XEiJ.fmtSB02u (XEiJ.fmtSB04u (sb, (date >>> 9) + 1980).append ('-'), date >>> 5 & 15).append ('-'), date & 31);
  3231:       sb.append ("  ");
  3232:       //    b[1..2]  時刻。時<<11|分<<5|秒/2
  3233:       int time = (char) (b[1] << 8 | b[2] & 255);
  3234:       XEiJ.fmtSB02u (XEiJ.fmtSB02u (XEiJ.fmtSB02u (sb, time >>> 11).append (':'), time >>> 5 & 63).append (':'), time << 1 & 63);
  3235:       sb.append ("  ");
  3236:       //    b[5..8]  ファイルサイズ
  3237:       if ((attr & HumanMedia.HUM_DIRECTORY) != 0) {
  3238:         sb.append ("     <dir>");
  3239:       } else if ((attr & HumanMedia.HUM_VOLUME) != 0) {
  3240:         sb.append ("     <vol>");
  3241:       } else {
  3242:         int size = (b[5] << 8 | b[6] & 255) << 16 | (char) (b[7] << 8 | b[8] & 255);
  3243:         XEiJ.fmtSBnd (sb, 10, size);
  3244:       }
  3245:       sb.append ("  ");
  3246:       //    b[9..31]  ファイル名
  3247:       int l = b.length;
  3248:       for (int i = 9; i < l; i++) {
  3249:         int s = b[i] & 255;
  3250:         char c;
  3251:         if (0x81 <= s && s <= 0x9f || 0xe0 <= s && s <= 0xef) {  //SJISの2バイトコードの1バイト目
  3252:           int t = i + 1 < l ? b[i + 1] & 255 : 0;
  3253:           if (0x40 <= t && t != 0x7f && t <= 0xfc) {  //SJISの2バイトコードの2バイト目
  3254:             c = CharacterCode.chrSJISToChar[s << 8 | t];  //2バイトで変換する
  3255:             if (c == 0) {  //対応する文字がない
  3256:               c = '※';
  3257:             }
  3258:             i++;
  3259:           } else {  //SJISの2バイトコードの2バイト目ではない
  3260:             c = '.';  //SJISの2バイトコードの1バイト目ではなかった
  3261:           }
  3262:         } else {  //SJISの2バイトコードの1バイト目ではない
  3263:           c = CharacterCode.chrSJISToChar[s];  //1バイトで変換する
  3264:           if (c < 0x20 || c == 0x7f) {  //対応する文字がないまたは制御コード
  3265:             c = '.';
  3266:           }
  3267:         }
  3268:         sb.append (c);
  3269:       }
  3270:       System.out.println (sb.toString ());
  3271:     }  //hfuPrintFileInfo(byte[]);
  3272: 
  3273:     //path = hfuNamestsToPath (namests)
  3274:     //path = hfuNamestsToPath (namests, full)
  3275:     //path = hfuNamestsToPath (ns, full)
  3276:     //  namestsのパスをホストマシンのパスに変換する(X68000側の処理)
  3277:     //  namests
  3278:     //    +2                                                   +67                   +75            +78                   +88
  3279:     //    0x09,ディレクトリ名,0x09,ディレクトリ名,0x09,0x00,…,主ファイル名1,0x20,…,拡張子,0x20,…,主ファイル名2,0x00,…,
  3280:     //    0x09,ディレクトリ名,0x09,0x00,…                    ,
  3281:     //    0x00,…                                             ,
  3282:     //  0x09(の並び)をディレクトリ名の区切りとみなす
  3283:     //    0x09が2つ以上連続しているときは1つとみなす
  3284:     //    パスの先頭と末尾の0x09はあってもなくても同じ
  3285:     //  0x00または+67をディレクトリ名の末尾とみなす
  3286:     //    最後のディレクトリ名が+67に達しているときはそこで打ち切る
  3287:     //  主ファイル名1と主ファイル名2にSJISの1バイト目と2バイト目が跨る場合がある
  3288:     private String hfuNamestsToPath (int namests) throws M68kException {
  3289:       byte[] w = new byte[88];
  3290:       MC68060.mmuReadByteArray (namests, w, 0, 88, XEiJ.regSRS);
  3291:       return hfuNamestsToPath (w, true);
  3292:     }  //hfuNamestsToPath(int)
  3293:     private String hfuNamestsToPath (int namests, boolean full) throws M68kException {
  3294:       byte[] w = new byte[88];
  3295:       MC68060.mmuReadByteArray (namests, w, 0, 88, XEiJ.regSRS);
  3296:       return hfuNamestsToPath (w, full);
  3297:     }  //hfuNamestsToPath(int,boolean)
  3298:     private String hfuNamestsToPath (byte[] ns, boolean full) throws M68kException {
  3299:       byte[] bb = new byte[88];
  3300:       int k = 0;
  3301:       for (int i = 2; i < 67; ) {
  3302:         for (; i < 67 && ns[i] == 0x09; i++) {  //0x09の並びを読み飛ばす
  3303:         }
  3304:         if (i >= 67 || ns[i] == 0x00) {  //ディレクトリ名がなかった
  3305:           break;
  3306:         }
  3307:         bb[k++] = 0x2f;  //ディレクトリ名の手前の'/'
  3308:         for (; i < 67 && ns[i] != 0x00 && ns[i] != 0x09; i++) {
  3309:           bb[k++] = ns[i];  //ディレクトリ名
  3310:         }
  3311:       }
  3312:       if (full) {  //主ファイル名を展開する
  3313:         bb[k++] = 0x2f;  //主ファイル名の手前の'/'
  3314:         for (int i = 67; i < 75; i++) {
  3315:           bb[k++] = ns[i];  //主ファイル名1
  3316:         }
  3317:         for (int i = 78; i < 88; i++) {
  3318:           bb[k++] = ns[i];  //主ファイル名2
  3319:         }
  3320:         for (; k > 0 && bb[k - 1] == 0x00; k--) {  //主ファイル名2の末尾の0x00を切り捨てる
  3321:         }
  3322:         for (; k > 0 && bb[k - 1] == 0x20; k--) {  //主ファイル名1の末尾の0x20を切り捨てる
  3323:         }
  3324:         bb[k++] = 0x2e;  //拡張子の手前の'.'
  3325:         for (int i = 75; i < 78; i++) {
  3326:           bb[k++] = ns[i];  //拡張子
  3327:         }
  3328:         for (; k > 0 && bb[k - 1] == 0x20; k--) {  //拡張子の末尾の0x20を切り捨てる
  3329:         }
  3330:         for (; k > 0 && bb[k - 1] == 0x2e; k--) {  //主ファイル名の末尾の0x2eを切り捨てる
  3331:         }
  3332:       }
  3333:       StringBuilder sb = new StringBuilder (hfuRootPath);
  3334:       int h = 0x00;  //繰り越した文字。SJISの1バイト目に限る
  3335:       for (int i = 0; i < k; i++) {
  3336:         int l = bb[i] & 255;
  3337:         if (h != 0x00 && 0x40 <= l && l != 0x7f && l <= 0xfc) {  //SJISの1バイト目が繰り越されており、今回の文字はSJISの2バイト目
  3338:           int c = CharacterCode.chrSJISToChar[h << 8 | l];
  3339:           if (c != 0x0000) {  //SJISで変換できる
  3340:             sb.append ((char) c);
  3341:           } else {  //SJISだが変換できない
  3342:             XEiJ.fmtHex2 (XEiJ.fmtHex2 (sb.append ('%'), h).append ('%'), l);  //%XX%XX
  3343:           }
  3344:           h = 0x00;
  3345:         } else {  //SJISの1バイト目が繰り越されていないか、今回の文字はSJISの2バイト目ではない
  3346:           if (h != 0x00) {  //SJISの1バイト目が繰り越されているが、今回の文字はSJISの2バイト目ではない
  3347:             XEiJ.fmtHex2 (sb.append ('%'), h);  //%XX
  3348:             h = 0x00;
  3349:           }
  3350:           if (0x81 <= l && l <= 0x9f || 0xe0 <= l && l <= 0xef) {  //今回の文字はSJISの1バイト目
  3351:             h = l;  //繰り越す
  3352:           } else {  //今回の文字はSJISの1バイト目ではない
  3353:             int c = CharacterCode.chrSJISToChar[l];
  3354:             if (0x20 <= c && c != 0x7f) {  //今回の文字はANK
  3355:               sb.append ((char) c);
  3356:             } else {  //今回の文字はSJISの1バイト目ではなく、ANKでもない
  3357:               XEiJ.fmtHex2 (sb.append ('%'), l);  //%XX
  3358:             }
  3359:           }
  3360:         }
  3361:       }
  3362:       if (h != 0x00) {  //SJISの1バイト目が繰り越されている
  3363:         XEiJ.fmtHex2 (sb.append ('%'), h);  //%XX
  3364:       }
  3365:       return sb.toString ();
  3366:     }  //hfuNamestsToPath(byte[],boolean)
  3367: 
  3368:   }  //class HFUnit
  3369: 
  3370: 
  3371: 
  3372: }  //class HFS
  3373: 
  3374: 
  3375: