HDMedia.java
     1: //========================================================================================
     2: //  HDMedia.java
     3: //    en:SASI hard disk media
     4: //    ja:SASIハードディスクメディア
     5: //  Copyright (C) 2003-2019 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: //SASIハードディスクのメディアの種類
    15: //
    16: //  アドレス
    17: //  00000000  SASIディスクIPL
    18: //            IPLROMが$2000.wに読み込んで実行する
    19: //            起動パーティションを自動または手動で選択し、選択されたパーティションのSASIパーティションIPLを$2400.wに読み込んで実行する
    20: //
    21: //  00000400  パーティションテーブル
    22: //                 10MB        20MB        40MB
    23: //  00000400    0x5836384b  0x5836384b  0x5836384b  X68Kマジック
    24: //  00000404    0x00009f54  0x00013c98  0x00027930  レコード数(ディスクイメージではこのサイズで保存する)
    25: //  00000408    0x00009f54  0x00013c98  0x00027930  代替レコード
    26: //  0000040c    0x0000af50  0x00015660  0x0002acc0  ランプレコード
    27: //  00000410  第1パーティションの情報
    28: //                 10MB        20MB        40MB
    29: //  00000410    0x48756d61  0x48756d61  0x48756d61  Humaマジック
    30: //  00000414    0x6e36386b  0x6e36386b  0x6e36386b  n68kマジック
    31: //  00000418    0x00000021  0x00000021  0x00000021  開始レコード
    32: //  0000041c    0x00009f2e  0x00013c68  0x000278f8  レコード数
    33: //  00000420  第2パーティションの情報
    34: //      :
    35: //  000004f0  第15パーティションの情報
    36: //  00000500  空き
    37: //      :
    38: //
    39: //  第1パーティション(10MBの装置に最大サイズのパーティションを確保した場合)
    40: //  アドレス   セクタ
    41: //  00002100  00000000  SASIパーティションIPL
    42: //                      SASIディスクIPLが$2400.wに読み込んで実行する
    43: //                      ルートディレクトリにあるHUMAN.SYSを$6800.wに読み込んで実行する
    44: //  00002500  00000001  第1FAT
    45: //  00007500  00000015  第2FAT
    46: //  0000c500  00000029  ルートディレクトリ
    47: //  00010500  00000039  データ領域
    48: //
    49: //  ドライブ情報(最大サイズのパーティションを確保した場合)
    50: //     10MB   20MB   40MB
    51: //     1024   1024   1024  バイト/セクタ
    52: //        1      1      1  セクタ/クラスタ
    53: //    10132  20155  40335  データ領域のクラスタ数+2
    54: //        1      1      1  予約領域のセクタ数
    55: //       20     40     80  1個のFAT領域に使用するセクタ数
    56: //       41     81    161  ルートディレクトリの先頭セクタ番号
    57: //      512    512    512  ルートディレクトリに入るエントリ数
    58: //       57     97    177  データ領域の先頭セクタ番号
    59: //
    60: //  X68000のSASIハードディスクの物理的な構成に関する考察
    61: //    FORMAT.XのSASIハードディスクの装置初期化で送られてくるFormatBlockコマンドのレコード番号は33ずつ増えている
    62: //    フロッピーディスクと同様にトラック単位で初期化していると考えられる
    63: //      00ff9858 hdcDoCommand() [0x06,0x00,0x00,0x00,0x01,0x00]
    64: //      00ff9858 hdcDoCommand() [0x06,0x00,0x00,0x21,0x01,0x00]
    65: //      00ff9858 hdcDoCommand() [0x06,0x00,0x00,0x42,0x01,0x00]
    66: //                                 :
    67: //      00ff9858 hdcDoCommand() [0x06,0x02,0x78,0xcd,0x01,0x00]
    68: //      00ff9858 hdcDoCommand() [0x06,0x02,0x78,0xee,0x01,0x00]
    69: //      00ff9858 hdcDoCommand() [0x06,0x02,0x79,0x0f,0x01,0x00]
    70: //    実際、X68000で利用できるSASIハードディスクのレコード数はすべて33で割り切れる
    71: //    IBM PC/AT(1984年)の20MBのハードディスクは512バイト/レコード*17レコード/トラック*4トラック/シリンダ*614シリンダという情報を参考にすると、
    72: //    X68000 ACE-HD(1988年)の20MBのハードディスクは256バイト/レコード*33レコード/トラック*4トラック/シリンダ*614シリンダと考えると調度よい
    73: //      https://en.wikipedia.org/wiki/Timeline_of_DOS_operating_systems
    74: //    ドライブパラメータの詳細は不明だが4~5バイト目が10MB=[$03,$01],20MB=[$03,$02],40MB=[$07,$02]となっており、
    75: //    10MB→20MBと20MB→40MBでは2倍になった項目が異なる可能性がある
    76: //----------------------------------------------------------------------------------------
    77: 
    78: package xeij;
    79: 
    80: import java.lang.*;  //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
    81: import java.util.*;  //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap
    82: 
    83: public class HDMedia extends HumanMedia {
    84: 
    85:   public static final HDMedia[] HDM_ARRAY = {
    86:     //                                         開始        終了        全体       ランプ
    87:     new HDMedia (0, "SASI HDD (10MB)", 256, 0x00000021, 0x00009f4f, 0x00009f54, 0x0000af50),  //10441728=256*33*4*309
    88:     new HDMedia (1, "SASI HDD (20MB)", 256, 0x00000021, 0x00013c89, 0x00013c98, 0x00015660),  //20748288=256*33*4*614
    89:     new HDMedia (2, "SASI HDD (40MB)", 256, 0x00000021, 0x00027919, 0x00027930, 0x0002acc0),  //41496576=256*33*8*614
    90:   };
    91:   public static final HDMedia HDM_10MB = HDM_ARRAY[0];
    92:   public static final HDMedia HDM_20MB = HDM_ARRAY[1];
    93:   public static final HDMedia HDM_40MB = HDM_ARRAY[2];
    94:   public static final int HDM_MAX_BYTES_PER_DISK = (int) HDM_40MB.humDiskEndByte;  //HDM_ARRAYにあるhumDiskEndByteの最大値
    95: 
    96:   //media = hdmLengthToMedia (longLength)
    97:   //  ディスクイメージファイルのサイズに合うメディアを返す
    98:   //  同じサイズでフォーマットの異なるメディアが複数あるときは最初に見つかったものを返す
    99:   public static HDMedia hdmLengthToMedia (long longLength) {
   100:     for (HDMedia media : HDM_ARRAY) {
   101:       if (media.humDiskEndByte == longLength) {
   102:         return media;
   103:       }
   104:     }
   105:     return null;
   106:   }  //hdmLengthToMedia(long)
   107: 
   108:   //media = hdmPathToMedia (length, bb)
   109:   //  パスに合うメディアを返す。null=見つからない
   110:   //  バッファを指定したときはそこにコピーする
   111:   public static HDMedia hdmPathToMedia (String path, byte[] bb) {
   112:     byte[] array = XEiJ.rscGetFile (path);
   113:     if (array == null) {  //読み込めない
   114:       return null;
   115:     }
   116:     HDMedia media = hdmLengthToMedia ((long) array.length);  //メディアの種類
   117:     if (media == null) {  //不明
   118:       System.out.println (Multilingual.mlnJapanese ?
   119:                           path + " は SASI ハードディスクのイメージファイルではありません" :
   120:                           path + " is not a SASI hard disk image file");
   121:       return null;
   122:     }
   123:     System.out.println (Multilingual.mlnJapanese ?
   124:                         path + " は " + media.hdmName + " です" :
   125:                         path + " is " + media.hdmName);
   126: /*
   127:       if (ByteArray.byaRls (hduImage, 0x00000400) != 0x5836384b) {  //X68K
   128:         //装置初期化されていない
   129:         return true;
   130:       }
   131:       if (!(ByteArray.byaRls (hduImage, 0x00000410) == 0x48756d61 &&
   132:             ByteArray.byaRls (hduImage, 0x00000414) == 0x6e36386b &&
   133:             ByteArray.byaRls (hduImage, 0x00000418) == 0x00000021 &&
   134:             ByteArray.byaRls (hduImage, 0x0000041c) == 0x000278f8)) {
   135:         //先頭のパーティションがHuman68kの40MBパーティションでない
   136:         return true;
   137:       }
   138:       if (false) {
   139:         //パーティションテーブル
   140:         ByteArray.byaWl (hduImage, 0x00000404, 0x00013c98);  //最大セクタ
   141:         ByteArray.byaWl (hduImage, 0x00000408, 0x00013c98);  //代替セクタ
   142:         ByteArray.byaWl (hduImage, 0x0000040c, 0x00015660);  //シッピングゾーン
   143:         ByteArray.byaWl (hduImage, 0x0000041c, 0x00013c68);  //先頭のパーティションのブロック数
   144:         //IPL内のBPBテーブル
   145:         ByteArray.byaWl (hduImage, 0x00002112, 0x0400_01_02);  //バイト/セクタ,セクタ/クラスタ,FAT領域の個数
   146:         ByteArray.byaWl (hduImage, 0x00002116, 0x0001_0200);  //予約領域のセクタ数,ルートディレクトリのエントリ数
   147:         ByteArray.byaWl (hduImage, 0x0000211a, 0x4f1a_f8_28);  //総クラスタ数+2,FATID,1個のFAT領域のセクタ数
   148:         //FATの20MBを超える部分に使用中の領域がないか確認してからルートディレクトリ以降を1024*80バイト手前にずらす
   149:         //  後で読み込み方法を変更して使用中の領域が20MB以下であれば読み込めるようにしたい
   150:         boolean error = false;
   151:         for (int i = 256 * (33 + 4 * 1) + 4; i < 256 * (33 + 4 * 1) + 4 + 2 * (20155 - 2); i += 2) {
   152:           int fat = (char) (hduImage[i] << 8 | hduImage[i + 1] & 255);
   153:           error |= fat >= 20155 && fat < 40335;
   154:         }
   155:         for (int i = 256 * (33 + 4 * 1) + 4 + 2 * (20155 - 2); i < 256 * (33 + 4 * 1) + 4 + 2 * (40335 - 2); i += 2) {
   156:           int fat = (char) (hduImage[i] << 8 | hduImage[i + 1] & 255);
   157:           error |= fat != 0;
   158:           hduImage[i] = 0;
   159:           hduImage[i + 1] = 0;
   160:         }
   161:         for (int i = 256 * (33 + 4 * 161); i < 256 * (33 + 4 * (97 + 20155 - 2)); i++) {
   162:           hduImage[i - 1024 * 80] = hduImage[i];
   163:         }
   164:         if (error) {
   165:           System.out.println (Multilingual.mlnJapanese ?
   166:                               "40MB から 20MB へ変換できませんでした" :
   167:                               "Failed to convert from 40MB to 20MB");
   168:         }
   169:       }
   170: */
   171:     if (bb != null) {
   172:       System.arraycopy (array, 0, bb, 0, array.length);
   173:     }
   174:     return media;
   175:   }  //hdmPathToMedia(String,byte[])
   176: 
   177: 
   178:   public int hdmNumber;  //番号
   179:   public String hdmName;  //名前
   180:   public int hdmBytesShiftRecord;  //1レコードあたりのバイト数のシフトカウント
   181:   public int hdmBytesPerRecord;  //1レコードあたりのバイト数(2の累乗)
   182:   public int hdmPartitionStartRecord;  //ディスクの先頭からパーティションの先頭までのレコード数
   183:   public int hdmPartitionEndRecord;  //ディスクの先頭からパーティションの末尾までのレコード数
   184:   public int hdmDiskEndRecord;  //ディスクのレコード数
   185:   public int hdmRampRecord;  //ディスクの先頭からランプレコードまでのレコード数
   186: 
   187:   public HDMedia (int number, String name,
   188:                    int bytesPerRecord, int partitionStartRecord, int partitionEndRecord, int diskEndRecord, int rampRecord) {
   189:     super (1024,  //bytesPerSector
   190:            -1,    //sectorsPerCluster
   191:            2,     //fatCount
   192:            1,     //reservedSectors
   193:            512,   //rootEntries
   194:            0xf8,  //mediaByte
   195:            0xf8,  //fatID
   196:            -1,    //fatSectors
   197:            (long) bytesPerRecord * diskEndRecord,         //diskEndByte
   198:            (long) bytesPerRecord * partitionStartRecord,  //partitionStartByte
   199:            (long) bytesPerRecord * partitionEndRecord);   //partitionEndByte
   200:     //定数で与えるもの
   201:     hdmNumber = number;  //番号
   202:     hdmName = name;  //名前
   203:     hdmBytesPerRecord = bytesPerRecord;  //1レコードあたりのバイト数(2の累乗)
   204:     hdmPartitionStartRecord = partitionStartRecord;  //ディスクの先頭からパーティションの先頭までのレコード数
   205:     hdmPartitionEndRecord = partitionEndRecord;  //ディスクの先頭からパーティションの先頭までのレコード数
   206:     hdmDiskEndRecord = diskEndRecord;  //ディスクのレコード数
   207:     hdmRampRecord = rampRecord;  //ディスクの先頭からランプレコードまでのレコード数
   208:     //定数から求まるもの
   209:     hdmBytesShiftRecord = Integer.numberOfTrailingZeros (hdmBytesPerRecord);  //1レコードあたりのバイト数のシフトカウント
   210:   }  //HDMedia
   211: 
   212:   //success = media.hdmMakeFormatData (bb, copySystemFiles)
   213:   //  SASIディスクのフォーマットデータを作る
   214:   public boolean hdmMakeFormatData (byte[] bb, boolean copySystemFiles) {
   215:     Arrays.fill (bb, (byte) 0);
   216:     //SASIディスクIPLを書き込む
   217:     System.arraycopy (HDC.HDC_DISK_IPL, 0, bb, 0x00000000, HDC.HDC_DISK_IPL.length);
   218:     //パーティションテーブルを作る
   219:     ByteArray.byaWl (bb, 0x00000400, 'X' << 24 | '6' << 16 | '8' << 8 | 'K');  //X68Kマジック
   220:     ByteArray.byaWl (bb, 0x00000404, hdmDiskEndRecord);  //ディスクのレコード数。ディスクの先頭から確保されている領域の末尾までのレコード数ではない
   221:     ByteArray.byaWl (bb, 0x00000408, hdmDiskEndRecord);  //ディスクのレコード数
   222:     ByteArray.byaWl (bb, 0x0000040c, hdmRampRecord);  //ランプレコード
   223:     ByteArray.byaWl (bb, 0x00000410, 'H' << 24 | 'u' << 16 | 'm' << 8 | 'a');  //Humaマジック
   224:     ByteArray.byaWl (bb, 0x00000414, 'n' << 24 | '6' << 16 | '8' << 8 | 'k');  //n68kマジック。n68kの代わりにn/16などと書くと1クラスタが16セクタになったりする
   225:     ByteArray.byaWl (bb, 0x00000418, hdmPartitionStartRecord);  //(0=自動起動,1=使用不可,2=使用可能)<<24|ディスクの先頭からパーティションの先頭までのレコード数
   226:     ByteArray.byaWl (bb, 0x0000041c, hdmPartitionEndRecord - hdmPartitionStartRecord);  //パーティションのレコード数
   227:     //SASIパーティションIPLを書き込む
   228:     System.arraycopy (HDC.HDC_PARTITION_IPL, 0, bb, 0x00002100, HDC.HDC_PARTITION_IPL.length);
   229:     //SASIパーティションIPLにBPBを埋め込む
   230:     ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x12, humBytesPerSector);        //1138  0000.w  1セクタあたりのバイト数(2の累乗)
   231:     ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x14, humSectorsPerCluster);     //113a  0002.b  1クラスタあたりのセクタ数(2の累乗)
   232:     ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x15, humFatCount);              //113b  0003.b  FAT領域の個数
   233:     ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x16, humReservedSectors);       //113c  0004.w  予約領域のセクタ数(FAT領域の先頭セクタ番号)
   234:     ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x18, humRootEntries);           //113e  0006.w  ルートディレクトリに入るエントリ数
   235:     ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x1a, humPartitionSectors);      //1140  0008.w  パーティションのセクタ数
   236:     ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x1c, humFatID);                 //1142  000a.b  FATID
   237:     ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x1d, humFatSectors);            //1143  000b.b  1個のFAT領域に使用するセクタ数
   238:     ByteArray.byaWl (bb, (int) humPartitionStartByte + 0x1e, hdmPartitionStartRecord);  //1144  000c.l  ディスクの先頭からパーティションの先頭までのレコード数
   239:     //FAT領域の先頭にFATマーカーを書き込む
   240:     humWriteFatMarker (bb);
   241:     //システムファイルを転送する
   242:     if (copySystemFiles) {
   243:       if (!humCopyHumanSys (bb) ||
   244:           !humCopyCommandX (bb)) {
   245:         return false;
   246:       }
   247:     }
   248:     if (false) {
   249:       humDumpFat (bb);
   250:     }
   251:     return true;
   252:   }  //media.hdmMakeFormatData(byte[],boolean)
   253: 
   254: }  //class HDMedia
   255: 
   256: 
   257: