xeij/HDMedia.java
//========================================================================================
// HDMedia.java
// en:SASI hard disk media
// ja:SASIハードディスクメディア
// Copyright (C) 2003-2019 Makoto Kamada
//
// This file is part of the XEiJ (X68000 Emulator in Java).
// You can use, modify and redistribute the XEiJ if the conditions are met.
// Read the XEiJ License for more details.
// https://stdkmd.net/xeij/
//========================================================================================
//----------------------------------------------------------------------------------------
//SASIハードディスクのメディアの種類
//
// アドレス
// 00000000 SASIディスクIPL
// IPLROMが$2000.wに読み込んで実行する
// 起動パーティションを自動または手動で選択し、選択されたパーティションのSASIパーティションIPLを$2400.wに読み込んで実行する
//
// 00000400 パーティションテーブル
// 10MB 20MB 40MB
// 00000400 0x5836384b 0x5836384b 0x5836384b X68Kマジック
// 00000404 0x00009f54 0x00013c98 0x00027930 レコード数(ディスクイメージではこのサイズで保存する)
// 00000408 0x00009f54 0x00013c98 0x00027930 代替レコード
// 0000040c 0x0000af50 0x00015660 0x0002acc0 ランプレコード
// 00000410 第1パーティションの情報
// 10MB 20MB 40MB
// 00000410 0x48756d61 0x48756d61 0x48756d61 Humaマジック
// 00000414 0x6e36386b 0x6e36386b 0x6e36386b n68kマジック
// 00000418 0x00000021 0x00000021 0x00000021 開始レコード
// 0000041c 0x00009f2e 0x00013c68 0x000278f8 レコード数
// 00000420 第2パーティションの情報
// :
// 000004f0 第15パーティションの情報
// 00000500 空き
// :
//
// 第1パーティション(10MBの装置に最大サイズのパーティションを確保した場合)
// アドレス セクタ
// 00002100 00000000 SASIパーティションIPL
// SASIディスクIPLが$2400.wに読み込んで実行する
// ルートディレクトリにあるHUMAN.SYSを$6800.wに読み込んで実行する
// 00002500 00000001 第1FAT
// 00007500 00000015 第2FAT
// 0000c500 00000029 ルートディレクトリ
// 00010500 00000039 データ領域
//
// ドライブ情報(最大サイズのパーティションを確保した場合)
// 10MB 20MB 40MB
// 1024 1024 1024 バイト/セクタ
// 1 1 1 セクタ/クラスタ
// 10132 20155 40335 データ領域のクラスタ数+2
// 1 1 1 予約領域のセクタ数
// 20 40 80 1個のFAT領域に使用するセクタ数
// 41 81 161 ルートディレクトリの先頭セクタ番号
// 512 512 512 ルートディレクトリに入るエントリ数
// 57 97 177 データ領域の先頭セクタ番号
//
// X68000のSASIハードディスクの物理的な構成に関する考察
// FORMAT.XのSASIハードディスクの装置初期化で送られてくるFormatBlockコマンドのレコード番号は33ずつ増えている
// フロッピーディスクと同様にトラック単位で初期化していると考えられる
// 00ff9858 hdcDoCommand() [0x06,0x00,0x00,0x00,0x01,0x00]
// 00ff9858 hdcDoCommand() [0x06,0x00,0x00,0x21,0x01,0x00]
// 00ff9858 hdcDoCommand() [0x06,0x00,0x00,0x42,0x01,0x00]
// :
// 00ff9858 hdcDoCommand() [0x06,0x02,0x78,0xcd,0x01,0x00]
// 00ff9858 hdcDoCommand() [0x06,0x02,0x78,0xee,0x01,0x00]
// 00ff9858 hdcDoCommand() [0x06,0x02,0x79,0x0f,0x01,0x00]
// 実際、X68000で利用できるSASIハードディスクのレコード数はすべて33で割り切れる
// IBM PC/AT(1984年)の20MBのハードディスクは512バイト/レコード*17レコード/トラック*4トラック/シリンダ*614シリンダという情報を参考にすると、
// X68000 ACE-HD(1988年)の20MBのハードディスクは256バイト/レコード*33レコード/トラック*4トラック/シリンダ*614シリンダと考えると調度よい
// https://en.wikipedia.org/wiki/Timeline_of_DOS_operating_systems
// ドライブパラメータの詳細は不明だが4~5バイト目が10MB=[$03,$01],20MB=[$03,$02],40MB=[$07,$02]となっており、
// 10MB→20MBと20MB→40MBでは2倍になった項目が異なる可能性がある
//----------------------------------------------------------------------------------------
package xeij;
import java.lang.*; //Boolean,Character,Class,Comparable,Double,Exception,Float,IllegalArgumentException,Integer,Long,Math,Number,Object,Runnable,SecurityException,String,StringBuilder,System
import java.util.*; //ArrayList,Arrays,Calendar,GregorianCalendar,HashMap,Map,Map.Entry,Timer,TimerTask,TreeMap
public class HDMedia extends HumanMedia {
public static final HDMedia[] HDM_ARRAY = {
// 開始 終了 全体 ランプ
new HDMedia (0, "SASI HDD (10MB)", 256, 0x00000021, 0x00009f4f, 0x00009f54, 0x0000af50), //10441728=256*33*4*309
new HDMedia (1, "SASI HDD (20MB)", 256, 0x00000021, 0x00013c89, 0x00013c98, 0x00015660), //20748288=256*33*4*614
new HDMedia (2, "SASI HDD (40MB)", 256, 0x00000021, 0x00027919, 0x00027930, 0x0002acc0), //41496576=256*33*8*614
};
public static final HDMedia HDM_10MB = HDM_ARRAY[0];
public static final HDMedia HDM_20MB = HDM_ARRAY[1];
public static final HDMedia HDM_40MB = HDM_ARRAY[2];
public static final int HDM_MAX_BYTES_PER_DISK = (int) HDM_40MB.humDiskEndByte; //HDM_ARRAYにあるhumDiskEndByteの最大値
//media = hdmLengthToMedia (longLength)
// ディスクイメージファイルのサイズに合うメディアを返す
// 同じサイズでフォーマットの異なるメディアが複数あるときは最初に見つかったものを返す
public static HDMedia hdmLengthToMedia (long longLength) {
for (HDMedia media : HDM_ARRAY) {
if (media.humDiskEndByte == longLength) {
return media;
}
}
return null;
} //hdmLengthToMedia(long)
//media = hdmPathToMedia (length, bb)
// パスに合うメディアを返す。null=見つからない
// バッファを指定したときはそこにコピーする
public static HDMedia hdmPathToMedia (String path, byte[] bb) {
byte[] array = XEiJ.rscGetFile (path);
if (array == null) { //読み込めない
return null;
}
HDMedia media = hdmLengthToMedia ((long) array.length); //メディアの種類
if (media == null) { //不明
System.out.println (Multilingual.mlnJapanese ?
path + " は SASI ハードディスクのイメージファイルではありません" :
path + " is not a SASI hard disk image file");
return null;
}
System.out.println (Multilingual.mlnJapanese ?
path + " は " + media.hdmName + " です" :
path + " is " + media.hdmName);
/*
if (ByteArray.byaRls (hduImage, 0x00000400) != 0x5836384b) { //X68K
//装置初期化されていない
return true;
}
if (!(ByteArray.byaRls (hduImage, 0x00000410) == 0x48756d61 &&
ByteArray.byaRls (hduImage, 0x00000414) == 0x6e36386b &&
ByteArray.byaRls (hduImage, 0x00000418) == 0x00000021 &&
ByteArray.byaRls (hduImage, 0x0000041c) == 0x000278f8)) {
//先頭のパーティションがHuman68kの40MBパーティションでない
return true;
}
if (false) {
//パーティションテーブル
ByteArray.byaWl (hduImage, 0x00000404, 0x00013c98); //最大セクタ
ByteArray.byaWl (hduImage, 0x00000408, 0x00013c98); //代替セクタ
ByteArray.byaWl (hduImage, 0x0000040c, 0x00015660); //シッピングゾーン
ByteArray.byaWl (hduImage, 0x0000041c, 0x00013c68); //先頭のパーティションのブロック数
//IPL内のBPBテーブル
ByteArray.byaWl (hduImage, 0x00002112, 0x0400_01_02); //バイト/セクタ,セクタ/クラスタ,FAT領域の個数
ByteArray.byaWl (hduImage, 0x00002116, 0x0001_0200); //予約領域のセクタ数,ルートディレクトリのエントリ数
ByteArray.byaWl (hduImage, 0x0000211a, 0x4f1a_f8_28); //総クラスタ数+2,FATID,1個のFAT領域のセクタ数
//FATの20MBを超える部分に使用中の領域がないか確認してからルートディレクトリ以降を1024*80バイト手前にずらす
// 後で読み込み方法を変更して使用中の領域が20MB以下であれば読み込めるようにしたい
boolean error = false;
for (int i = 256 * (33 + 4 * 1) + 4; i < 256 * (33 + 4 * 1) + 4 + 2 * (20155 - 2); i += 2) {
int fat = (char) (hduImage[i] << 8 | hduImage[i + 1] & 255);
error |= fat >= 20155 && fat < 40335;
}
for (int i = 256 * (33 + 4 * 1) + 4 + 2 * (20155 - 2); i < 256 * (33 + 4 * 1) + 4 + 2 * (40335 - 2); i += 2) {
int fat = (char) (hduImage[i] << 8 | hduImage[i + 1] & 255);
error |= fat != 0;
hduImage[i] = 0;
hduImage[i + 1] = 0;
}
for (int i = 256 * (33 + 4 * 161); i < 256 * (33 + 4 * (97 + 20155 - 2)); i++) {
hduImage[i - 1024 * 80] = hduImage[i];
}
if (error) {
System.out.println (Multilingual.mlnJapanese ?
"40MB から 20MB へ変換できませんでした" :
"Failed to convert from 40MB to 20MB");
}
}
*/
if (bb != null) {
System.arraycopy (array, 0, bb, 0, array.length);
}
return media;
} //hdmPathToMedia(String,byte[])
public int hdmNumber; //番号
public String hdmName; //名前
public int hdmBytesShiftRecord; //1レコードあたりのバイト数のシフトカウント
public int hdmBytesPerRecord; //1レコードあたりのバイト数(2の累乗)
public int hdmPartitionStartRecord; //ディスクの先頭からパーティションの先頭までのレコード数
public int hdmPartitionEndRecord; //ディスクの先頭からパーティションの末尾までのレコード数
public int hdmDiskEndRecord; //ディスクのレコード数
public int hdmRampRecord; //ディスクの先頭からランプレコードまでのレコード数
public HDMedia (int number, String name,
int bytesPerRecord, int partitionStartRecord, int partitionEndRecord, int diskEndRecord, int rampRecord) {
super (1024, //bytesPerSector
-1, //sectorsPerCluster
2, //fatCount
1, //reservedSectors
512, //rootEntries
0xf8, //mediaByte
0xf8, //fatID
-1, //fatSectors
(long) bytesPerRecord * diskEndRecord, //diskEndByte
(long) bytesPerRecord * partitionStartRecord, //partitionStartByte
(long) bytesPerRecord * partitionEndRecord); //partitionEndByte
//定数で与えるもの
hdmNumber = number; //番号
hdmName = name; //名前
hdmBytesPerRecord = bytesPerRecord; //1レコードあたりのバイト数(2の累乗)
hdmPartitionStartRecord = partitionStartRecord; //ディスクの先頭からパーティションの先頭までのレコード数
hdmPartitionEndRecord = partitionEndRecord; //ディスクの先頭からパーティションの先頭までのレコード数
hdmDiskEndRecord = diskEndRecord; //ディスクのレコード数
hdmRampRecord = rampRecord; //ディスクの先頭からランプレコードまでのレコード数
//定数から求まるもの
hdmBytesShiftRecord = Integer.numberOfTrailingZeros (hdmBytesPerRecord); //1レコードあたりのバイト数のシフトカウント
} //HDMedia
//success = media.hdmMakeFormatData (bb, copySystemFiles)
// SASIディスクのフォーマットデータを作る
public boolean hdmMakeFormatData (byte[] bb, boolean copySystemFiles) {
Arrays.fill (bb, (byte) 0);
//SASIディスクIPLを書き込む
System.arraycopy (HDC.HDC_DISK_IPL, 0, bb, 0x00000000, HDC.HDC_DISK_IPL.length);
//パーティションテーブルを作る
ByteArray.byaWl (bb, 0x00000400, 'X' << 24 | '6' << 16 | '8' << 8 | 'K'); //X68Kマジック
ByteArray.byaWl (bb, 0x00000404, hdmDiskEndRecord); //ディスクのレコード数。ディスクの先頭から確保されている領域の末尾までのレコード数ではない
ByteArray.byaWl (bb, 0x00000408, hdmDiskEndRecord); //ディスクのレコード数
ByteArray.byaWl (bb, 0x0000040c, hdmRampRecord); //ランプレコード
ByteArray.byaWl (bb, 0x00000410, 'H' << 24 | 'u' << 16 | 'm' << 8 | 'a'); //Humaマジック
ByteArray.byaWl (bb, 0x00000414, 'n' << 24 | '6' << 16 | '8' << 8 | 'k'); //n68kマジック。n68kの代わりにn/16などと書くと1クラスタが16セクタになったりする
ByteArray.byaWl (bb, 0x00000418, hdmPartitionStartRecord); //(0=自動起動,1=使用不可,2=使用可能)<<24|ディスクの先頭からパーティションの先頭までのレコード数
ByteArray.byaWl (bb, 0x0000041c, hdmPartitionEndRecord - hdmPartitionStartRecord); //パーティションのレコード数
//SASIパーティションIPLを書き込む
System.arraycopy (HDC.HDC_PARTITION_IPL, 0, bb, 0x00002100, HDC.HDC_PARTITION_IPL.length);
//SASIパーティションIPLにBPBを埋め込む
ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x12, humBytesPerSector); //1138 0000.w 1セクタあたりのバイト数(2の累乗)
ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x14, humSectorsPerCluster); //113a 0002.b 1クラスタあたりのセクタ数(2の累乗)
ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x15, humFatCount); //113b 0003.b FAT領域の個数
ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x16, humReservedSectors); //113c 0004.w 予約領域のセクタ数(FAT領域の先頭セクタ番号)
ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x18, humRootEntries); //113e 0006.w ルートディレクトリに入るエントリ数
ByteArray.byaWw (bb, (int) humPartitionStartByte + 0x1a, humPartitionSectors); //1140 0008.w パーティションのセクタ数
ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x1c, humFatID); //1142 000a.b FATID
ByteArray.byaWb (bb, (int) humPartitionStartByte + 0x1d, humFatSectors); //1143 000b.b 1個のFAT領域に使用するセクタ数
ByteArray.byaWl (bb, (int) humPartitionStartByte + 0x1e, hdmPartitionStartRecord); //1144 000c.l ディスクの先頭からパーティションの先頭までのレコード数
//FAT領域の先頭にFATマーカーを書き込む
humWriteFatMarker (bb);
//システムファイルを転送する
if (copySystemFiles) {
if (!humCopyHumanSys (bb) ||
!humCopyCommandX (bb)) {
return false;
}
}
if (false) {
humDumpFat (bb);
}
return true;
} //media.hdmMakeFormatData(byte[],boolean)
} //class HDMedia