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