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