//*****************************************************************************
// FAT32フォーマットを作り出すプログラム
// Copyright (C) 2000,2002 by AIG-Soft
//*****************************************************************************

#include <scsi.h>
#include <string.h>
#include <stdlib.h>	// exit()
#include <stdio.h>
#include <conio.h>	// kbhit()/getch()
#include <time.h>	// clock()
#include "defines.h"	// BIT??
#include "doslibm.h"	// GETTIME/GETDATE
#include "acdata.h"	// ltos80()/stoc80()
#include "ascsi.h"
#include "aparam.h"
#include "astdio.h"	// printf2()
#include "fat32.h"

char prog[]="@@> FORMAT32.C : 2002/02/28 THU : COMMAND : FORMAT32 : X680x0 : GCC";
// 2000/12/18	V1.0	初版
// 2002/02/28	V1.1	Bootと言う呼び名をBPBに変更
//			BPBコピーの位置を絶対指定からBPB相対に変更
//			フォーマットシンボルを0x0b(FAT32;CHS)から0x0c(FAT32;LBA)に変更
//			FSINFOもがらだけでも作ろうと思ったが、非常に面倒なので止め(^_^;)

#define	DEBUG	0	// デバッグモード

//*****************************************************************************
/* 現在の制約
・１パーティションのみ。
・設定値／ディスクエラーに対するエラーチェックがいい加減。
・ルートディレクトリは１クラスタ固定
　ＦＡＴ３２では動的延伸がされるのでこれで良い。
*/
//*****************************************************************************
// 設定値
//*****************************************************************************

// Num.. =数
// Len..=長さ
// Sec..=セクター番号
// Clu..=クラスタ番号
// Nm.. =名前
// Flg..=フラグ
// Def..=デフォルト値
// Max..=最大値

// オプション指定値
int	FlgLook;		// 各種数値を表示するかどうか:0=しない,1=する,2=もっとする
unlong	id;			// SCSI ID+LUN
int	NumFAT;			// ＦＡＴの数(本当はunchar)
unlong	FlgNoSecIPL;		// ＩＰＬセクターの有無（ＩＰＬセクターなし=BPBのみ）
int	SecBPB;			// BPBセクターの位置(本当はunshort)
int	SecBPBCopy;		// BPBセクターコピーの位置（＝０：なし）
int	SecFSINFO;		// FSINFOセクターコピーの位置（＝０：なし）
int	NumClu1Sec;		// １クラスタのセクター数
unlong	CluRootDir;		// ルートディレクトリの先頭クラスタ
int	NumBPB2FAT1;		// BPBからFAT1までの間のセクター数(本当はunshort)
unlong	NumDefectSec;		// 交替領域にするセクター数

// オプション指定値のデフォルト
#define	DefNumFAT		2		// ＦＡＴの数
#define	MaxNumFAT		2		// ＦＡＴの数；これ以上にするとWindowsで未フォーマット扱いになる
#define	SecIPL			0x000000	// IPLセクター（位置は固定）
#define	DefSecBPB		0x00004c	// BPBセクター
#define	DefNumClu1Sec2048	2		// 1クラスタのセクター数:2048バイトセクター系
#define	DefNumClu1Sec		4		// 1クラスタのセクター数:その他セクター系
#define	DefCluRootDir		0x000002	// ルートディレクトリの先頭クラスタ
#define	DefNumBPB2FAT1		0x20		// BPBからFAT1までの間のセクター数
#define	DefNumDefectSecHDD	500		// 交替領域の数(HDD)
#define	DefNumDefectSecMO	0		// 交替領域の数(MO)<-デフォルトはこっち

//*****************************************************************************
// ディスク固有値

long	LenSec1;		// １セクターサイズ(本当はunshort)
unlong	NumAllSecD;		// 全セクター数（交替領域含む）

// 算出値
unlong	NumAllSec;		// 有効全セクター数（交替領域含まず）
unlong	NumAllClu;		// 全クラスタ数
long	LenClu1;		// １クラスタサイズ
long	NumSec1FAT;		// １ＦＡＴ領域に必要なセクタ数
short	NumFATSec1;		// １セクターに収まるＦＡＴの数
unlong	SecFAT1;		// ＦＡＴ１開始セクター
unlong	SecRootDir;		// ルートディレクトリ開始セクター

// ディスクキャッシュ
#define	ALLBLOCK	2048		// キャッシュのブロック数
S_CACHE_BUFF		scb;

//*****************************************************************************
// ディスク固定値

#define	MEDIA_ID	0xf8		// メディアＩＤ
#define LenFAT1		4		// FAT32での1FATのバイト(FAT32=32bit)
#define	LenFNAME	8		// ファイル名長さ(OEM名、フォーマット識別子長を含む)
#define	LenFEXT		3		// 拡張子長さ

//			 01234567
#define	NmOEM		"AIG-SOFT"	// OEM名(EOSなし)
#define	NmFAT		"FAT32   "	// FAT名(EOSなし)
#define	NmVOL		"FORMAT32V01"	// ボリューム名(EOSなし):下２桁はメジャーバージョン
//			 01234567012=8+3ファイル名と同じ
#define	NmVER		"1.1"		// プログラムバージョン名

// 単位換算値
#define	K	1024			// キロ
#define	M	(1024*K)		// メガ
#define	G	(1024*M)		// ギガ

//*****************************************************************************
// 汎用関数
//*****************************************************************************

unchar *memcpy2(unchar *des,unchar *src,int len)
{
	if (len==0) { // strcpy相当
		while ((*des++=*src++)!=0);
	} else	while (len-->0) *des++=*src++;
	return(des);
}

#if 0
// 正しいコードは作るが、無駄が多いので削除
inline unsint s68_80(unsint s68)
// 68形式で記録されているshortデータを80形式にする
{
register int _D0 asm("d0");
	_D0=s68;
	asm volatile ("ror.w #8,d0":"=d"(_D0):"0"(_D0):"d0");
	return(_D0);
}

inline unlong l68_80(unlong l68)
// 68形式で記録されているunlongデータを80形式にする
{
register int _D0 asm("d0");
	_D0=l68;
	asm volatile ("ror.w #8,d0":"=d"(_D0):"0"(_D0):"d0");
	asm volatile ("swap d0");
	asm volatile ("ror.w #8,d0":"=d"(_D0):"0"(_D0):"d0");
	return(_D0);
}
#endif

unsint s68_80(unsint s68)
// 68形式で記録されているshortデータを80形式にする
{
	asm("move.l 4(sp),d0");	// 結構必殺技なのでお勧めではない
	asm("ror.w #8,d0");
}

unlong l68_80(unlong l68)
// 68形式で記録されているunlongデータを80形式にする
{
	asm("move.l 4(sp),d0");	// 結構必殺技なのでお勧めではない
	asm("ror.w #8,d0");
	asm("swap d0");
	asm("ror.w #8,d0");
}

//*****************************************************************************
// 本体
//*****************************************************************************

int ClearSec(unlong ssec,unlong esec)
// ssec+1〜esec-1 セクタークリア
{
unchar *b;
	ssec++;
	b=S_CacheGetBuff(&scb,-(esec-ssec));	// クリア付き１セクター分確保
	if (b==NULL) return(1);
	if (FlgLook) printf2("$%08x\r",ssec);
	return(S_CacheWrite(&scb,ssec));
}

//*****************************************************************************

int IPLSec()
// ＩＰＬセクター
{
unchar *b;
int ret;
	#if DEBUG >=2
	printf2("IPL...");
	#endif
	
	b=S_CacheGetBuff(&scb,-1);	// クリア付き１セクター分確保
	if (b==NULL) return(1);
	b[0]=0xFA;
	b[0x1fe]=b[1]=0x55;
	b[0x1ff]=b[2]=0xAA;
	
	// パーティション情報（１パーティション分１６バイト）
	//				      ___=FAT32(LBA)
	b=memcpy2(&b[0x1be],"\x00\x00\x01\x01\x0C\xFF\xFF\xFF",8);
	// [0]=ＩＰＬフラグ : $00=ＩＰＬ不可 , $80=可能 ; FORMAT32はもちろん不可
	// [1]=開始ヘッド  （SCSI-HDDでは無意味）
	// [2]=開始セクタ  （〃）
	// [3]=開始シリンダ（〃）
	// [4]=ＦＡＴ種類 : 0x0b=FAT32(CHS),0x0c=FAT32(LBA)
	//	00=不明 , 02=FAT21 , 02=XENIX , 04=FAT16 , 06=DOS4.0 , $11=東芝日本語DOS
	// [5]=終了ヘッド  （SCSI-HDDでは無意味）
	// [6]=終了セクタ  （〃）
	// [7]=終了シリンダ（〃）
	b=ltoc80((unlong)SecBPB,b);	// このパーティションのBPBセクター番号
	  ltoc80(NumAllSec,b);		// パーティション内全セクター数
	
	// 書き込み
	if (FlgLook) printf2("$%08x\r",SecIPL);
	ret=S_CacheWrite(&scb,SecIPL);
	
	#if DEBUG >=2
	printf2("Complete\n")
	#endif
	return(ret);
}

//*****************************************************************************

int BPBSec(unlong sec,unsint tm,unsint dt)
// ブートセクター
{
unchar *b,*b0;
int ret;
	#if DEBUG >=2
	printf2("BPB:$%08x...",sec);
	#endif
	
	// 構造体にしたいが、そうするとpackingの関係でメンバー境界が空くので直接書きにする
	b0=b=S_CacheGetBuff(&scb,-1);	// クリア付き１セクター分確保
	if (b==NULL) return(1);
	b=memcpy2(b,"\xEB\x45\x90",3);	// BPBシンボル($EB,??,$90であれば良い)
	b=memcpy2(b,NmOEM,LenFNAME);	// OEM名(EOSなし)
	b=stoc80((unshort)LenSec1,b);	// bytes/sec
	*b++=NumClu1Sec;		// sector/cluster
	b=stoc80(NumBPB2FAT1,b);	// FAT1開始セクター（BPBからのオフセット）
	*b++=NumFAT;			// FAT数
	b+=4;				// +2:ルートディレクトリエントリ数;FAT32=0
					// +2:総セクタ数:FAT32=0
	*b++=MEDIA_ID;			// メディアＩＤ;FAT先頭のそれと合わせること
	b+=2;				// セクタ/FAT;FAT32は0
	b=stoc80(0x003f,b);		// セクタ/トラック（無意味）
	b=stoc80(0x00ff,b);		// ヘッド数（無意味）
	b=ltoc80((unlong)SecBPB,b);	// BPB前セクター数
	b=ltoc80(NumAllSec,b);		// 利用可能全セクター数
	b=ltoc80(NumSec1FAT,b);		// 1FATセクター数
	b+=4;				// +2:FAT状態フラグ;アクティブFAT=1
					// +2:システムファイルバージョン=0x0000
	b=ltoc80(CluRootDir,b);		// ルートディレクトリ存在クラスタ
	b=stoc80(0xFFFF,b);		// FSINF使わず
//	b=stoc80(SecFSINFO==0? 0xffff:SecFSINFO-SecBPB,b);
					// FSINFのBPBからの相対セクター
					// 0xffff=なし
	b=stoc80(SecBPBCopy==0? 0xffff:SecBPBCopy-SecBPB,b);
					// BPBセクターコピーのBPBからの存在相対セクター
					// 0xffff=なし
	//
	b=&b0[0x040];
	*b++=0x00;			// ブート不可
	b++;
	*b++=0x29;			// 以下にボリューム番号と名前がある
	b=stoc80(tm,b);			// 時刻
	b=stoc80(dt,b);			// 日付
	b=memcpy2(b,NmVOL,LenFNAME+LenFEXT);	// ボリューム名
	b=memcpy2(b,NmFAT,LenFNAME);		// ＦＡＴ名
	// 書き込み
	if (FlgLook) printf2("$%08x\r",sec);
	ret=S_CacheWrite(&scb,sec);
	
	#if DEBUG >=2
	printf2("Complete\n")
	#endif
	return(ret);
}

//*****************************************************************************

int FSINFOSec(unlong sec)
// FileSystem Infomationセクター
{
unchar *b;
int ret;
	#if DEBUG >=2
	printf2("FSINFO...");
	#endif
	
	b=S_CacheGetBuff(&scb,-1);	// クリア付き１セクター分確保
	if (b==NULL) return(1);

	memcpy2(b,"RRaA",4);		// FSINFOシンボル
	b=memcpy2(&b[0x1e4],"rrAa",4);	// FSINFOシンボル
	b=ltoc80(0L,b);	// 未使用アロケーションユニット数（-1=未計算）
			// このフォーマットではディスクは常にフルなので空き＝０
	ltoc80(2L,b);	// 最終変更アロケーションユニット数（２＝先頭:ルートディレクトリ）
	
	// 書き込み
	if (FlgLook) printf2("$%08x\r",sec);
	ret=S_CacheWrite(&scb,sec);
	
	#if DEBUG >=2
	printf2("Complete\n")
	#endif
	return(ret);
}

//*****************************************************************************

int FATSec(unlong sec)
// ＦＡＴ
{
unlong *b;
unlong fat,fatsec;
int ret;
	#if DEBUG >=2
	printf2("FAT...");
	#endif
	
	fatsec=fat=0;
	do {
		b=(unlong *)S_CacheGetBuff(&scb,-1);	// クリア付き１セクター分確保
		if (b==(unlong *)NULL) return(1);
		
		if (fat==0) {
			ltoc80(0xffffff00|MEDIA_ID,(unchar*)&b[0]);	// 0  = メディアバイト
			ltoc80(ENDofFATs,(unchar*)&b[1]);		// 1  = 予約
		}
		if (CluRootDir>=fat && CluRootDir<fat+NumFATSec1) {
			// このセクター内に入るＦＡＴにルートディレクトリがある
			ltoc80(ENDofFATs,(unchar*)&b[CluRootDir-fat]);
		}
		if (FlgLook) printf2("$%08x\r",sec);
		ret=S_CacheWrite(&scb,sec++);
		if (ret) break;
		fat+=(unlong)NumFATSec1;
		fatsec++;
	} while (fatsec<NumSec1FAT);
	
	#if DEBUG >=2
	printf2("Complete\n")
	#endif
	return(ret);
}

//*****************************************************************************

int RootDir()
// ルートディレクトリ
{
unchar *b;
int ret;
	#if DEBUG >=2
	printf2("RootDir...");
	#endif
	
	b=S_CacheGetBuff(&scb,-NumClu1Sec);	// クリア付き１クラスタ分確保確保
	if (b==NULL) return(1);
	if (FlgLook) printf2("$%08x\r",SecRootDir);
	ret=S_CacheWrite(&scb,SecRootDir);	// 基本的には最初だけ0になっていれば良い。
	
	#if DEBUG >=2
	printf2("Complete\n")
	#endif
	return(ret);
}

//*****************************************************************************

int MakeFormat32()
// ディスクフォーマット作成
{
unsint tm,dt;
unlong sec;
int i;
	tm=GETTIME();	// 現在の時刻をファイル用フォーマットで得る
	dt=GETDATE();	// 現在の日付をファイル用フォーマットで得る
	
	if (!FlgNoSecIPL) {
		// ＩＰＬセクター
		if (IPLSec()) {
			printf2("IPLが作成出来ません\n");
			return(1);
		}
		if (SecBPB>SecIPL+1) {
			if (ClearSec(SecIPL,SecBPB)) {
				printf2("IPLとBPBの間がクリア出来ません\n");
				return(2);
			}
		}
	} // ＩＰＬがない時は先頭からBPBが来る
	
	// BPBセクター
	if (BPBSec(SecBPB,tm,dt)) {
		printf2("BPBが作成出来ません\n");
		return(3);
	}
	if (SecBPBCopy!=0) {
		if (SecBPBCopy>SecBPB+1) {
			if (ClearSec(SecBPB,SecBPBCopy)) { // 空きがある時はクリアする
				printf2("BPBとBPB Copyの間がクリア出来ません\n");
				return(4);
			}
		}
		if (BPBSec(SecBPBCopy,tm,dt)) { // BPBセクターコピー
			printf2("BPB Copyが作成出来ません\n");
			return(5);
		}
		if (SecFAT1>SecBPBCopy+1) {
			if (ClearSec(SecBPBCopy,SecFAT1)) {
				printf2("BPB CopyとFAT1の間がクリア出来ません\n");
				return(6);
			}
		}
	} else {
		if (SecFAT1>SecBPB+1) {
			if (ClearSec(SecBPB,SecFAT1)) {
				printf2("BPBとFAT1の間がクリア出来ません\n");
				return(7);
			}
		}
	}
	
	// FAT
	sec=SecFAT1;
	for (i=0;i<NumFAT;i++) {
		if (FATSec(sec)) {
			printf2("FAT%dが作成出来ません\n",i);
			return(8);
		}
		sec+=NumSec1FAT;	// 次のFATへ
	}
	
	// RootDir
	if (RootDir()) {
		printf2("ROOTが作成出来ません\n",i);
		return(9);
	}
	return(0);
}

//*****************************************************************************
// 必要数値計算
//*****************************************************************************

static unlong roundup(unlong bunbo,unlong bunsi)
// bunbo/bunshiの整数位への切り上げ（unlong版）
{
unlong ret;
	ret=bunbo/bunsi;
	if ((bunbo%bunsi)!=0) ret++; // 割り切れない限り＋１される
	return(ret);
}

int CalcDiscLayout()
// ディスクレイアウト数値計算
{
	// 有効全セクター数
	if (NumAllSecD<NumDefectSec) {
		printf2("交代領域が多すぎます:%d\n",NumDefectSec);
		return(5);
	}
	NumAllSec=NumAllSecD-NumDefectSec;
	
	// １クラスタサイズ
	LenClu1=NumClu1Sec*LenSec1;
	
	// FAT1の先頭セクター位置
	SecFAT1=SecBPB+NumBPB2FAT1;
	
	// 全クラスタ数（とりあえず、ＦＡＴ１迄を除いた全領域をクラスタとみなした場合の数）
	NumAllClu=roundup(NumAllSec-SecFAT1,NumClu1Sec);
	
	// １ＦＡＴ領域に必要なセクタ数
	NumSec1FAT=roundup(NumAllClu*LenFAT1,LenSec1);
	
	// １セクターに収まるＦＡＴの数
	NumFATSec1=(typeof(NumFATSec1))LenSec1/LenFAT1;
	
	// ルートディレクトリセクター位置
	SecRootDir=SecFAT1+NumSec1FAT*NumFAT;
	
	if (FlgLook) {
	int i;
	unlong sec;
	printf2("-----算出値-----\n");
	printf2("有効全セクタ数  : %d($%x)\n", NumAllSec,NumAllSec );
	printf2("全クラスタ数    : %d($%x)\n", NumAllClu,NumAllClu );
	printf2("1ｸﾗｽﾀｻｲｽﾞ       : %d($%x)\n", LenClu1,LenClu1 );
	printf2("1FAT必要セクタ数: %d($%x)\n", NumSec1FAT,NumSec1FAT );
	sec=SecFAT1;
	for (i=0;i<NumFAT;i++) {
		printf2("FAT%d先頭セクタ  : $%x\n", i+1,sec );
		sec+=NumSec1FAT;		// 次のFATへ
	}
	printf2("ﾙｰﾄﾃﾞｨﾚｸﾄﾘｾｸﾀ   : $%x\n", SecRootDir );
	}
	if (FlgLook>=2) {
	printf2("FAT数/1ｾｸﾀ内    : $%x\n", NumFATSec1 );
	}
	
	// エラーチェック（計算値以外も含む）
	if (SecBPB>=NumAllSec) {
		printf2("ＩＰＬセクター番号が大きすぎます\n");
		exit(1);
	}
	if (LenClu1>CLU1MAX) {
		printf2("１クラスタのサイズが%dＫより大きくなります\n",CLU1MAX/K);
		return(2);
	}
	if (SecFAT1>=NumAllSec) {
		printf2("ブートセクタの位置またはブートセクタからＦＡＴ１までの間のセクタ数が異常です");
		return(3);
	}
	if (SecBPB+NumBPB2FAT1>=NumAllSec) {
		printf2("ブートセクターからＦＡＴ１までのあいだが広すぎます\n");
		return(4);
	}
	if (NumFAT*NumSec1FAT>=NumAllSec) {
		printf2("ＦＡＴ領域が大きくなりすぎる（ＦＡＴ数が多すぎる）\n");
		return(5);
	}
	if (SecRootDir>=NumAllSec) {
		printf2("ルートディレクトリの位置が異常です\n");
		return(6);
	}
	
	return(0);
}

//*****************************************************************************
// デバイス状況表示
//*****************************************************************************

static unchar	*DeviceName[] =
{
	/* 00 */ "Direct Access Device",
	/* 01 */ "Sequencial Access Device",
	/* 02 */ "Printer Device",
	/* 03 */ "Processor Device",		// FDS-120はこれ
	/* 04 */ "W-Once,R-Multiple Device",
	/* 05 */ "CD-ROM Device",
	/* 06 */ "Scanner Device",
	/* 07 */ "Optical Memory Device",
	/* 08 */ "Medium Changer Device",
	/* 09 */ "Communication Device",
	/* 0A */ "Defined by ASC IT8:0A",
	/* 0B */ "Defined by ASC IT8:0B",
	/* 0C */ "Array Controller Device",
	/* 0D */ "Enclosure Service Device",
	/* 0E */ "Simplified Direct Access",
	/* 0F */ "Optical Card Device",
	/* 10 */ "Bridge Expander Device",
	/* 11 */ "(???:$11)",
	/* 12 */ "(???:$12)",
	/* 13 */ "(???:$13)",
	/* 14 */ "(???:$14)",
	/* 15 */ "(???:$15)",
	/* 16 */ "(???:$16)",
	/* 17 */ "(???:$17)",
	/* 18 */ "(???:$18)",
	/* 19 */ "(???:$19)",
	/* 1A */ "(???:$1A)",
	/* 1B */ "(???:$1B)",
	/* 1C */ "(???:$1C)",
	/* 1D */ "(???:$1D)",
	/* 1E */ "(???:$1E)",
	/* 1F */ "Unknown or no device type"
};
static unchar	*ANSIVersion[] =
{
	/* 00 */ "not ANSI",
	/* 01 */ "SCSI-1",	// ANSI X3.131-1986 SCSI-1
	/* 02 */ "SCSI-2",	// ANSI X3.131-1994 SCSI-2
	/* 03 */ "SCSI-3",	// ANSI X3.301-1997 SPC Rev.11
	/* 04 */ "SPC-2",	// T10/1236-D SPC-2
	/* 05 */ "(???:$05)",
	/* 06 */ "(???:$06)",
	/* 07 */ "(???:$07)"
};
static unchar	*ResDataFormat[] =
{
	/* 00 */ "SCSI",
	/* 01 */ "CCS",
	/* 02 */ "SCSI-2",
	/* 03 */ "SCSI-3",
	/* 04 */ "(???:$04)",
	/* 05 */ "(???:$05)",
	/* 06 */ "(???:$06)",
	/* 07 */ "(???:$07)",
	/* 08 */ "(???:$08)",
	/* 09 */ "(???:$09)",
	/* 0A */ "(???:$0A)",
	/* 0B */ "(???:$0B)",
	/* 0C */ "(???:$0C)",
	/* 0D */ "(???:$0D)",
	/* 0E */ "(???:$0E)",
	/* 0F */ "(???:$0F)"
};

int InitDevice(int *device)
// デバイス別初期化
// =0:OK,!=0:エラー
{
#define	MAXINQ	128
unchar inq[MAXINQ];
	if (S_CheckReady(id)) {
		printf2("ドライブが準備出来ません:ID%d($%x)\n",id,id);
		return(3);
	}
	// ドライブの情報を得る
	if (S_INQUIRY(MAXINQ,id,(struct INQUIRY *)inq)) {
		printf2("Inquiryが読めません:ID%d($%x)\n",id,id);
		return(1);
	}
	printf2("SCSI-ID         : %d($%x)\n",id,id);
	if (FlgLook) {
	printf2("-----デバイス情報-----\n");
	printf2("Vendor          : \"%s\"\n", &inq[8] );
	printf2("& Product ID    : (|bender |product        |revision)\n" );
	*device=inq[0]&0x1f; // デバイス種類
	printf2("Device          : %s\n", DeviceName[*device] );
	printf2("取り出し        : %s\n", ((inq[1]&0x80)? "可（フォーマット中は禁止します）" : "不可" ) );
	}
	if (FlgLook>=2) {
	printf2("Version         : ISO=%d ECMA=%d ANSI=%d (%s)\n", (inq[2]>>6)&0x03, (inq[2]>>3)&0x07, (inq[2]>>0)&0x07, ANSIVersion[ inq[2]&0x07 ] );
	printf2("Res.DataFormat  : %s\n", ResDataFormat[ inq[3]&0x0f ] );
	printf2("SCSI-2拡張      : 相対adrs=%d,WBus32/16=%d/%d,Sync=%d,Link=%d,Que=%d,SftRe=%d,ATN受信=%d,TrmIO=%d\n"
					,(inq[7]>>7)&1
					,(inq[7]>>6)&1
					,(inq[7]>>5)&1
					,(inq[7]>>4)&1
					,(inq[7]>>3)&1
					,(inq[7]>>1)&1
					,(inq[7]>>0)&1
					,(inq[3]>>7)&1
					,(inq[3]>>6)&1
	);
	}
	if (inq[1]&0x80) {
		S_EjectMode(id,1);	// 取りだし可の時はEject禁止にする
					// 出来なくてもエラーにはしない
	}
	
	// 容量情報取得
	if (S_READCAP2(id,&NumAllSecD,&LenSec1)) {
		printf2("容量情報が読めません:ID%d($%x)\n",id,id);
		return(2);
	}
	if (FlgLook) {
	printf2("全セクター数    : %ld($%x)\n", NumAllSecD ,NumAllSecD);
	printf2("１セクターｻｲｽﾞ  : %d\n"      , LenSec1 );
	}
	
	// MODE SENSE データの要求
	if (S_MODESENSE(0x00,MAXINQ,id,inq)==-1) {
		printf2("モードセンス情報が読めません:ID%d($%x)\n",id,id);
		return(4);
	}
	if (FlgLook) {
	printf2("WriteProtect    : %s\n", inq[2]&BIT7? "On":"Off");
	}
	if (inq[2]&BIT7) {
		// 書き込み禁止
		printf2("書き込み禁止になっています:ID%d($%x)\n",id,id);
		return(5);
	}
	return(0);
}

int TerminateDevice()
// デバイス別解放
{
	S_EjectMode(id,0); // 取りだし可の時はEject許可にする
	return(0);
}

//*****************************************************************************
// メイン
//*****************************************************************************

void main(int argc,unchar *argv[])
{
int fhelp=0;
int ret;
int ftwo,maxid,device;
int fyes;
unlong id0;
time_t ts;
	printf2("FAT32 Formatter Ver" NmVER " (C) 2000,2002 by AIG-Soft\n");

	/* オプションチェック */
	clear_param();
	
    HELP:;
	if (helpcom(argc,argv,fhelp,"FORMAT32","-ID[id] [Options]")) {
		exit(fhelp? fhelp:255);
	}
	
	fyes   =check_param2("Y",argv,argc,0); // 確認無し実行モード
	FlgLook=0;
	if (check_param2("L",argv,argc,0))  FlgLook=1; // 詳細データ表示1
	if (check_param2("LL",argv,argc,0)) FlgLook=2; // 詳細データ表示2
	
	// ＩＰＬセクターなし設定
	FlgNoSecIPL=check_param2("NOIPL",argv,argc,0);
	
	// BPBセクターの位置
	SecBPB=DefSecBPB;
	if (check_param3("SECBPB",argv,argc,&SecBPB,0)) {
		if (SecBPB<=SecIPL) SecBPB=SecIPL+1;
		// 上限の判定は難しいのでしない
	}
	if (FlgNoSecIPL) SecBPB=0x000000; // ＩＰＬセクターがない時は強制的に先頭になる
	
	// BPBセクターコピーのBPBからの相対位置（＝０：なし）
	SecBPBCopy=SecBPB+1;
	if (check_param3("SECBPBCOPY",argv,argc,&SecBPBCopy,0)) {
		if (SecBPBCopy<=SecBPB) SecBPBCopy=SecBPB; // 本当は前に持っていっても良い
		// 上限の判定は難しいのでしない
	}
	
	// BPBからFAT1までの間のセクター数
	NumBPB2FAT1=DefNumBPB2FAT1;
	if (check_param3L("BPB2FAT1",argv,argc,&NumBPB2FAT1,0)) {
		unlong sec;
		sec=(SecBPBCopy!=0? (SecBPBCopy-SecBPB)+1:1);
		if (NumBPB2FAT1<sec) NumBPB2FAT1=sec;
		// 上限の判定は後で
	}
	
	// ＦＡＴの数
	NumFAT=DefNumFAT;
	if (check_param3("FAT",argv,argc,&NumFAT,0)) {
		if (NumFAT<=0) NumFAT=1;
		if (NumFAT>MaxNumFAT) NumFAT=MaxNumFAT;
		// 上限の判定は後で
	}
	
	CluRootDir=DefCluRootDir;
	#if 0
	// ルートディレクトリの先頭クラスタ
	if (check_param3L("ROOT",argv,argc,&CluRootDir,0)) {
		if (CluRootDir<DefCluRootDir) CluRootDir=DefCluRootDir;
		// 上限の判定は後で
	}
	#endif
	
	// ターゲットＩＤ
	ftwo=(S_TW_CHK()==-2? 8:0); // TWOSCSI常駐のIDのオフセットも兼ねる
	maxid=7+ftwo;
	id=4+ftwo; // デフォルトID
	if (check_param3L("ID",argv,argc,&id,0)) {
		id0=id&maxid;
		if (id0<0) id0=0;
		if (id0>maxid) id0=maxid;
		id=(id&0xff00)|id0;
	} else { // ID指定は必須
		fhelp=100;
		goto HELP;
	}
	ret=S_X68ID();
	printf2("X68-ID=%d\n",ret);
	id0=id&maxid;
	if (ret==id0||ret==id0+ftwo) {
		printf2("X68本体は指定出来ません：%d\n",id0);
		exit(2);
	}
	
	// 装置情報取得
	if (InitDevice(&device)) {
		ret=(10);
		goto END;
	}
	
	// １クラスタのセクター数
	NumClu1Sec=(LenSec1==2048? DefNumClu1Sec2048:DefNumClu1Sec);
	if (check_param3("CLU1SEC",argv,argc,&NumClu1Sec,0)) {
		if (NumClu1Sec<=0) NumClu1Sec=1;
		// 上限の判定は後で
	}
	
	// 交替領域のセクタ数
	NumDefectSec=(device==0? DefNumDefectSecHDD:DefNumDefectSecMO);
	if (check_param3L("DEFECT",argv,argc,&NumDefectSec,0)) {
		if (NumDefectSec<0) NumDefectSec=0;
		// 上限の判定は後で
	}
	
	ret=check_param_error(argv,argc);
	if (ret) {
		printf2("オプションエラーです：%s\n",argv[ret]);
		ret=(2);
		goto END;
	}
	
	if (FlgLook>=2) {
	printf2("-----固定値-----\n");
	printf2("ＦＡＴ種類      : %s\n" , NmFAT);
	printf2("１ＦＡＴサイズ  : %dﾊﾞｲﾄ\n", LenFAT1);
	printf2("OEM名           : %s\n" , NmOEM);
	printf2("ボリューム名    : %s\n" , NmVOL);
	printf2("メディアＩＤ    : $%x\n", MEDIA_ID);
	}
	if (FlgLook) {
	printf2("-----オプション変更値-----\n");
	printf2("ＦＡＴ数       : %d\n" , NumFAT);
	printf2("IPLｾｸﾀの有無   : %s\n" , FlgNoSecIPL? "なし":"あり");
	printf2("BPBｾｸﾀ位置     : $%x\n", SecBPB);
	if (SecBPBCopy==SecBPB) {
	printf2("BPBｾｸﾀｺﾋﾟｰ     : なし\n");
	} else {
	printf2("BPBｾｸﾀｺﾋﾟｰ位置 : $%x\n", SecBPBCopy);
	}
	printf2("1ｸﾗｽﾀのｾｸﾀ数   : %d\n"     , NumClu1Sec);
	#if 0
	printf2("ﾙｰﾄﾃﾞｨﾚｸﾄﾘｸﾗｽﾀ : $%x\n"    , CluRootDir);
	#endif
	printf2("BPB-FAT1間ｾｸﾀ数: $%x\n"    , NumBPB2FAT1);
	printf2("交替領域ｾｸﾀ数  : %d($%x)\n", NumDefectSec,NumDefectSec);
	}
	
	// ディスクフォーマット各種数値計算＆データ設定
	if (CalcDiscLayout()) {
		ret=(11);
	    END:;
		exit(ret);
	}
	
	if (!fyes) {
		int c;
		printf2("フォーマットを開始しても良いですか？ (Y/N) ");
		while (kbhit()) getch(); // キーバッファークリア
		c=getch();
		if (c!='Y'&&c!='y') {
			printf2("\n中断します.\n");
			exit(10);
		}
		printf2("Y\n");
	}
	
	if (S_CacheOpen(&scb,id,ALLBLOCK,S_CACHE_WRITE)) {
		printf2("キャッシュが使えません\n");
		exit(4);
	}
	
	if (S_CheckReady(id)) {
		printf2("ドライブが動作可能状態ではありません：%d\n",id);
		exit(2);
	}
	
	printf2("フォーマット中...\n");
	if (FlgLook>=2) {
		ts=clock(); // 開始時刻
	}
	
	ret=MakeFormat32();
	S_CacheClose(&scb);
	TerminateDevice(); // これが結構時間がかかる時がある（キャッシュ内容をディスクに書き出すので）
	printf2("終了しました\n");
	
	if (FlgLook>=2) {
		printf2("time=%d(msec)\n",clock()-ts);	// 経過時間も表示
	}
	exit(0);
}

//*****************************************************************************
/* 実験の結果解ったこと
・ルートディレクトリのクラスタ位置は変更出来そうだが、実際には２固定である。
　変えるとスキャンディスクがエラーを出すし、実際のファイル書き込みも
　正しく行われない。
*/
//*****************************************************************************
