0から作るソフトウェア開発

日々勉強中。。。

0から作るOS開発

環境準備

環境設定

ブートローダ

カーネルローダ

GRUB

カーネル

ドライバーその他

0から作るOS開発 フロッピーディスクドライバその2

前回までの内容

これまでで、 についてわかりました。それではカーネルを0から開発していきましょう!

今回は前回みてきましたデータレジスター(FDC0のポートアドレスは0x3F5)に書き込む

制御コマンドについて詳細を見ていきたいと思います。

フロッピーディスクの制御コマンド概要

フロッピーディスクの制御コマンドはポートアドレス0x3F5(FDC0)または0x375(FDC1)のデータレジスターに書き込みます

フロッピーディスクコントローラに制御コマンドを書き込むとコントローラがフロッピーディスクドライブを制御します

制御コマンドを書き込む前には必ずメインステータスレジスターMSRのRQMビット(ビット7)が1になっていることを

確認してから書き込みます。(詳細はフロッピーディスクドライバその1 を参照してください)

8272Aで使用する制御コマンドは全部で15個あります。また、その後改良されたICで例えば82077AAでは全部で

22個あります。ここでは、8272Aの基本的な制御コマンドを含めた82077AAまでの制御コマンドについて見ていきます

(8272Aと82077AAについてはフロッピーディスクドライバその1を 参照してください)



それぞれの制御コマンドは1バイトのコマンドから9バイトのコマンドまであります。制御コマンドの第1バイト目を

データレジスターに書き込むと、フロッピーディスクコントローラは書き込まれた1バイト目を調べ、その制御コマンドが

後何バイト書き込まれるのかがわかりますので、対応する制御コマンドのバイト数読み込んでくれます

ですので、制御コマンドの第1バイト目はどのコマンドかを示すデータとなります。その後に書き込むデータは

パラメーターといいます。

制御コマンドでドライブを制御するときには、通常1つのヘッド(ディスクの片面のみ)のトラックを制御することに

なりますが、フロッピーディスクドライブの2つのヘッドを動かしてディスクの両面のトラックを制御するときは

制御コマンドの複数トラック(MT:Multiple Track)ビットを1にします。(詳細は追って説明していきます)。

このように制御コマンドには1つのコマンドに対して機能を変えることができるビットがありますので、あわせて見ていきます

フロッピーディスクコントローラの制御コマンド一覧

では、まずはどのような制御コマンドがあるのか見ていきます

下記表は8272Aの基本的な制御コマンドと82077AAの制御コマンドを一覧にしています

(82077AAのコマンドには種類欄に”拡張コマンド”と記載しています。また、制御コマンドをクリックすると

該当コマンドの詳細までジャンプします)

フロッピーディスクコントローラの制御コマンド一覧
ID 制御コマンド 名称 種類 説明
0x02 READ TRACK 1トラック読み込み データ転送 指定した1トラックを読み込みます
0x03 SPECIFY 制御時間指定 制御 ドライブを制御する時間を指定するコマンドです
0x04 SENSE DRIVE STATUS ドライブステータス取得 制御 ドライブのステータス情報を取得します
0x05 WRITE DATA データ書き込み データ転送 セクター単位でデータを書き込みます
0x06 READ DATA データ読み込み データ転送 セクター単位でデータを読み込みます
0x07 RECALIBRATE ヘッド位置再修正 制御 ヘッドをトラック0の位置に修正します(戻します)
0x08 SENSE INTERRUPT STATUS 割り込みステータス情報取得 制御 割り込みのステータス情報を取得します
0x09 WRITE DELETED DATA 削除済みデータ書き込み データ転送 削除済みマークが付いているセクターにデータを書き込みます
0x0A READ ID ID読み込み 制御 ヘッドの位置を取得します
0x0C READ DELETED DATA 削除済みデータ読み込み データ転送 削除済みマークが付いているセクターにデータを読み込みます
0x0D FORMAT TRACK トラックのフォーマット データ転送 トラック全体をフォーマットします
0x0E DUMPREG レジスターダンプ 制御
(拡張)
レジスターの値を取得します。デバッグ用です
0x0F SEEK シーク 制御 ヘッドを現在のトラックから別のトラックに移動します
0x10 VERSION バージョン取得 制御
(拡張)
フロッピーディスクコントローラが旧バージョンか新バージョンかの情報を取得します
0x11 SCAN EQUAL 指定データのスキャン 制御 指定したデータと同じデータがあるかどうか調べます
0x12 PERPENDICULAR MODE 垂直モード 制御
(拡張)
フロッピーディスクドライブの垂直モードを制御します
0x13 CONFIGURE 設定 制御
(拡張)
フロッピーディスクコントローラの特定機能を設定します
0x14 LOCK ロック 制御
(拡張)
バッファ(FIFO)をロックします
0x16 VERIFY データ検査 制御
(拡張)
データを検査します
0x19 SCAN LOW OR EQUAL 指定データ以下のデータスキャン データ転送 指定したデータと同じかそれより下の値のデータがあるかどうか調べます
0x1D SCAN HIGH OR EQUAL 指定データ以上のデータスキャン データ転送 指定したデータと同じかそれより上の値のデータがあるかどうか調べます
0x8F RELATIVE SEEK 相対シーク 制御
(拡張)
現在のヘッド位置から相対位置を指定してシークします


フロッピーディスクコントローラの制御コマンドを定義する

上記で見てきました制御コマンドの第1バイト目をとりあえず定義していきます


/*********************************************************************************
 File:floppy.h
 Description:definition for floppy disk driver

*********************************************************************************/
#ifndef __FLOPPY__H
#define __FLOPPY__H

/*
===================================================================================

    Description:Control Commands for Floppy Disk Controller

===================================================================================
*/
typedef enum
{
    FDC_CMD_READ_TRACK             = 0x02,
    FDC_CMD_SPECIFY                = 0x03,
    FDC_CMD_SENSE_DRIVE_STATUS     = 0x04,
    FDC_CMD_WRITE_DATA             = 0x05,
    FDC_CMD_READ_DATA              = 0x06,
    FDC_CMD_RECALIBRATE            = 0x07,
    FDC_CMD_SENSE_INTERRUPT_STATUS = 0x08,
    FDC_CMD_WRITE_DELETED_DATA     = 0x09,
    FDC_CMD_READ_ID                = 0x0A,
    FDC_CMD_READ_DELETED_DATA      = 0x0C,
    FDC_CMD_FORMAT_TRACK           = 0x0D,
    FDC_CMD_DUMPREG                = 0x0E,
    FDC_CMD_SEEK                   = 0x0F,
    FDC_CMD_VERSION                = 0x10,
    FDC_CMD_SCAN_EQUAL             = 0x11,
    FDC_CMD_PERPENDICULAR_MODE     = 0x12,
    FDC_CMD_CONFIGURE              = 0x13,
    FDC_CMD_LOCK                   = 0x14,
    FDC_CMD_VERIFY                 = 0x16,
    FDC_CMD_SCAN_LOW_OR_EQUAL      = 0x19,
    FDC_CMD_SCAN_HIGH_OR_EQUAL     = 0x1D,
    FDC_CMD_RELATIVE_SEEK          = 0x8F
} E_FDC_CMDS;

#endif    /* __FLOPPY__H */



これらのコマンドをフロッピーディスクドライバその1で作成した writeFd0Command関数で書き込んでいきます

フロッピーディスクコントローラの制御コマンドの拡張ビット

制御コマンドには共通する拡張ビットがあります。例えばREAD DATAコマンドのビット7を1にすれば

両面のトラックを同時に制御することができるようになります。このようなビットをこのサイトでは拡張ビットと

します。拡張ビットには次のようなビットがあります。制御コマンド詳細を見ていくなかでしょっちゅう出てきます

ので適宜ご参照ください

フロッピーディスクコントローラの制御コマンドの拡張ビット
シンボル ラベル 名称 説明
MT Multi-track selector マルチトラックセレクター 両面のトラックを1つのトラックとして操作するかどうかを指定します

0:シングルトラックとして操作します
1:マルチトラックとして操作します。マルチトラック時にはヘッド0とヘッド1の上にあるディスク両面の トラックを1つのトラックとして操作します(合計32セクターとして操作します)。セクターはヘッド0上の トラックから数え始めて、ヘッド1のトラック上の最後のセクターがマルチトラックとしての最終セクターと なります。
MFM MFM(Modified Frequency Modulation) mode selector MFMモードセレクター 倍密度モードとして操作するかどうかを指定します

0:単密度のディスクモードとして操作します(FMモード)
1:倍密度ディスクモードとして操作します(MFMモード)。通常の3.5インチフロッピーディスク(1.44MB)は 倍密度となります。
SK Skip flag スキップフラグ 削除済みデータアドレスマークが付いているセクターを飛ばすかどうかを指定します

0:READ/WRITE DATAコマンド時に削除済みデータアドレスマークが付いているセクターを飛ばしません
1:READ/WRITE DATAコマンド時に削除済みデータアドレスマークが付いているセクターを 自動的に飛ばします。逆にREAD/WRITE DELETED DATAコマンドは削除済みデータアドレス マークが付いているセクターのみアクセスします。
H/HDS Head ヘッド 操作するヘッドを指定します

0:ヘッド0を操作します
1:ヘッド1を操作します
DS0、DS1 Disk Drive ディスクドライブ 操作するディスクドライブを指定します

DS0、DS1とディスクドライブ
DS1 DS0 ディスクドライブ
0 0 ドライブ0
0 1 ドライブ1
1 0 ドライブ2
1 1 ドライブ3


拡張ビットを定義する

各ビットを定義していきます。それぞれの値についてはコマンド詳細で後ほど見ていきます


/*
===================================================================================

    Description:Extended Control Command Bit

===================================================================================
*/
typedef enum
{
    FDC_CMD_EXT_SK = 0x20,
    FDC_CMD_EXT_MF = 0x40,
    FDC_CMD_EXT_MT = 0x80
} E_FDC_CMDS_EXT;

/*
===================================================================================

    Description:1st Parameters of Control Command

===================================================================================
*/
typedef enum
{
    FDC_CMD_1ST_PRM_DRIVE0 = 0x00,
    FDC_CMD_1ST_PRM_DRIVE1 = 0x01,
    FDC_CMD_1ST_PRM_DRIVE2 = 0x02,
    FDC_CMD_1ST_PRM_DRIVE3 = 0x03,
    FDC_CMD_1ST_PRM_HEAD0  = 0x00,
    FDC_CMD_1ST_PRM_HEAD1  = 0x04
} E_FDC_CMDS_1ST_PRM;



ギャップ3サイズ(GPL:Gap3 Length)

拡張ビットの他にパラメーターとしてギャプ3のサイズを指定します。Gap3 Lengthはビットではなく1バイトあります。

ギャップ3とは隣あうセクターを区切るための物理的なディスク上の区間のことです。

この区間の長さをギャップ3のサイズといいます

制御コマンドのGap3サイズ
Gap3サイズ 説明
27 3.5インチディスクのギャップ3サイズです。この値を使用します
32 5.25インチディスクのギャップ3サイズです
42 標準のギャップ3サイズです


ギャップ3サイズを定義する

ギャップ3サイズを定義してきます


/*
===================================================================================

    Description:Gap3 Size of Control Command

===================================================================================
*/
typedef enum
{
    FDC_CMD_GAP3_SIZE_3_5  = 27,
    FDC_CMD_GAP3_SIZE_5_25 = 32,
    FDC_CMD_GAP3_SIZE_STD  = 42
} E_FDC_CMDS_GP3_SIZE;



セクターサイズ

拡張ビットの他にパラメーターとしてセクターサイズを指定する必要があります。

フロッピーディスクのセクターサイズは512バイトでした。しかし、制御コマンドで指定するのは

バイト数ではありません。次の式にあてはまるNを指定します。


セクターサイズ = 2 ^ N * 128



この式のセクターサイズに512を代入してNについて求めるとN = 2となります

制御コマンドのセクターサイズには2を指定します

(ここでNは0-7の範囲の値をとります。N=7のときのセクターサイズは16KBとなります。

事実上セクターサイズが16KBとなるフロッピーディスクはありませんが。。。)

セクターサイズを定義する

セクターサイズを定義していきます


/*
===================================================================================

    Description:Sector Size of Control Command

===================================================================================
*/
typedef enum
{
    FDC_CMD_SECTOR_SIZE_128   = 0,
    FDC_CMD_SECTOR_SIZE_256   = 1,
    FDC_CMD_SECTOR_SIZE_512   = 2,
    FDC_CMD_SECTOR_SIZE_1024  = 3,
    FDC_CMD_SECTOR_SIZE_2048  = 4,
    FDC_CMD_SECTOR_SIZE_4096  = 5,
    FDC_CMD_SECTOR_SIZE_8192  = 6,
    FDC_CMD_SECTOR_SIZE_16384 = 7
} E_FDC_CMDS_SECTOR_SIZE;



フロッピーディスクコントローラの制御コマンドの書き込み

制御コマンドの書き込みはフロッピーディスクドライバその1 で見てきましたwriteFd0Command関数を使用します

ここで制御コマンドの書き込み方法について簡単な例を見ていきたいと思います

制御コマンドはコマンド1バイトとそれに付随する何バイトかの(コマンドに依って決まっています)

パラメーターを書き込みます



例えばSEEKコマンドの場合はSEEKコマンド0x0Fとパラメーターその1(ヘッドの指定、ドライブの指定)と

パラメーターその2(移動先のシリンダ番号の指定)の合計3バイト書き込みます

この場合次のようなプログラム例となります(コマンドの詳細については後ほど見ていきます)

				
writeFd0Command( FDC_CMD_SEEK );    /* SEEKコマンド書き込み                        */
writeFd0Command( 0x00 );            /* パラメーターその1(ヘッド0、ドライブ0指定)*/
writeFd0Command( 0x12 );            /* パラメーターその2(移動先シリンダ番号=18) */



制御コマンドのステータス読み込み

制御コマンドを書き込むとコントローラはコマンドに対応する処理を実施します。その結果として、

コントローラはほとんどのコマンドに対してステータスをポートアドレス0x3F5のデータレジスターに

格納します。コントローラはステータスをデータレジスターに格納するとIRQ6を発生させます

(IRQについては割り込みその3に表を載せています)



格納されるステータスは制御コマンドに関係なく常に7バイトとなります。ですので、

IRQ6の割り込みハンドラ(もしくはIRQ6のハンドラで割り込みが入ったことを通知して別のプログラムで読み込む)

で7バイト読み込むようにします。



ポートアドレス0x3F5のデータレジスターからステータスを読み込む関数readFd0Statusは

フロッピーディスクドライバその1で作成しています。 このreadFd0Status関数で7バイト読み込むようにします


#define DEF_FDC_NUM_STATUS    7

unsigned char fdc_status[ DEF_FDC_NUM_STATUS ];
STATUS        status;
	
for( i = 0 ; i < DEF_FDC_NUM_STATUS ; i++ )
{
    status = readFd0Status( &fdc_status[ i ] );
}



格納されるステータスについて現在のステータスレジスターの情報が格納されます。

ステータスレジスターは次の4種類あります。


それぞれのレジスターについて詳細を見ていきます。

ステータスレジスターST0

ステータスレジスターST0は1バイトのレジスターです。

ステータスレジスターST0

ステータスレジスターST0の各シンボルは次のようになります

ステータスレジスターST0のビットアサイン
ビット シンボル 名称 説明
0, 1 DS0, DS1 選択ドライブ 現在選択中のドライブが格納されます

DS0、DS1とディスクドライブ
DS1 DS0 ディスクドライブ
0 0 ドライブ0
0 1 ドライブ1
1 0 ドライブ2
1 1 ドライブ3
2 H 選択ヘッド 現在のヘッドが格納されます
0:ヘッド0
1:ヘッド1
3 - 予約 予約ビットです
4 EC ユニットチェック ドライブの制御で次のフォルトが発生した場合1が格納されます。0だと制御成功です。
1:RECALIBRATEコマンドの失敗(80ステップ以上のパルスを出力しました)
2:REALTIVE SEEKコマンドでトラック0を超えるシークをしました
5 SE シーク終了 シークが終了した場合に1が格納されます。1がセットされる条件は次のようになります
1:SEEK、RECALIBRATEコマンドが完了したとき
2:指定したセクターに対してREAD、WRITEが完了したとき
6, 7 IC0, IC1 割り込みコード 割り込みが発生した条件コードが格納されます

割り込みコード
IC1 IC0 説明
0 0 制御コマンドがエラー無しで正常に完了したことを示します。
0 1 制御コマンドが失敗したことを示します。
1 0 書き込んだ制御コマンドが無効なコマンドであったことを示します。
1 1 ポーリングによる異常終了したことを示します


ステータスレジスターST1

ステータスレジスターST1は1バイトのレジスターです。

ステータスレジスターST1

ステータスレジスターST1の各シンボルは次のようになります

ステータスレジスターST1のビットアサイン
ビット シンボル 名称 説明
0 MA アドレスマーク消失 次の条件でアドレスマークが消失した場合に1が格納されます
1:IDXピンが出力するインデックスパルスの後に指定トラックのIDアドレスマークを検出できない場合
2:指定トラックのデータアドレスマークもしくは削除済みデータアドレスマークを検出できない場合
1 NW 書き込み不可 次のコマンドを実行中にWPピンが1になり、このビットに1が格納されます
WRITE DATAコマンド
WRITE DELETED DATAコマンド
FORMAT TRACKコマンド
2 ND データ無し 次の条件時に1が格納されます
1:READ DATAコマンド、READ DELETED DATAコマンド実行時に指定されたセクターが検出できなかった
2:READ IDコマンド実行時にIDが読み込めなかった(エラーが発生した)
3:READ TRACKコマンドで連続したセクターを検出できなかった
3 - 予約 0固定です
4 OR オーバーラン/アンダーラン オーバーランまたはアンダーランが発生して、時間内にCPUまたはDMAからデータを受信できなかった ときに1が格納されます
5 DE データエラー セクターのIDフィールドまたはデータフィールドでCRCエラーを検出した場合に1が格納されます
6 - 予約 0固定です
7 EN シリンダー終端検出 READ系コマンド、WRITE系コマンド書き込み後に TCピンの入力(DMAコントローラからのデータ転送中止要求)が無い場合に1が格納されます。 (現在のトラックの最終セクターを越えてアクセスしようとした)


ステータスレジスターST2

ステータスレジスターST2は1バイトのレジスターです。

ステータスレジスターST2

ステータスレジスターST2の各シンボルは次のようになります

ステータスレジスターST2のビットアサイン
ビット シンボル 名称 説明
0 MD データアドレスマーク消失 データアドレスマークまたは削除済みデータアドレスマークを検出できなかった場合に1が格納されます
1 BC 不良シリンダー セクターのIDフィールドからトラックアドレスまでがフロッピーディスクコントローラが保持している トラックアドレスと異なり、トラックアドレスの値が0xFFであった場合に1が格納されます
2 SN スキャン条件不適合 SCAN系コマンド処理中に指定された条件に適合したセクターを検出できなかった場合に1が格納されます
3 SH スキャンヒット SCAN系コマンド処理中に指定された条件に適合したセクターを検出できた場合に1が格納されます
4 WC エラーシリンダー セクターのIDフィールドからトラックアドレスまでがフロッピーディスクコントローラが保持している トラックアドレスと異なった場合に1が格納されます
5 DD データフィールドのデータエラー データフィールドでCRCエラーを検出した場合に1が格納されます
6 CM コントロールマーク 次の条件時に1が格納されます
1:READ DATAコマンド時に削除済みデータアドレスマークを検出した場合
2:READ DELETED DATAコマンド時にデータアドレスマークを検出した場合
7 - 予約 0固定です


ステータスレジスターST3

ステータスレジスターST3は1バイトのレジスターです。

ステータスレジスターST3

ステータスレジスターST3の各シンボルは次のようになります

ステータスレジスターST3のビットアサイン
ビット シンボル 名称 説明
0, 1 DS0, DS1 選択ドライブ DS0ピン、DS1ピンの状態が格納されます
1 HD ヘッドアドレス HDSELピンの状態が格納されます
3 - 予約 1固定です
4 T0 トラック0 TRK0ピンの状態が格納されます
5 - 予約 1固定です
6 WP 書き込み保護 WPピンの状態が格納されます
7 - 予約 0固定です


制御コマンドの書き込みとステータスの読み込み方法がわかりましたので、

いよいよ制御コマンドの詳細について見ていきたいと思います

フロッピーディスクコントローラの制御コマンド詳細

制御コマンドについてその詳細を見ていきます。途中重要なコマンドについては

プログラム制御例もあわせて見ていきます

READ TRACK(0x02)

READ TRACKコマンドは指定されたトラック上のセクターを全て読み出します。

トラック上のセクターを全て読みだす以外はREAD DATAと同じです

ドライブからIDXピン(トラックの開始検出するピン)にパルスが入るとそのトラックの

セクターを読み込んで行きます(指定したセクター番号は無視します)。

DMAを使用していない場合はデータレジスターに1バイト格納されるごとに

IRQ6が発生しますので、割り込みが入った後にデータレジスターから1バイト読み込む

ようにプログラムします。

DMAを使用する場合は DMAドライバ を参照してください

READ TRACKコマンド

READ TRACK(0x02)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 MFM 0 0 0 0 1 0
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 データ長(セクターサイズが0以外の場合は0xFFを指定)


READ TRACKコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

READ TRACK(0x02)ステータス
Byte/Bit 7 6 5 4 3 2 1 0
0 ST0
1 ST1
2 ST2
3 シリンダー番号
4 ヘッド番号
5 セクター番号
6 セクターサイズ


SPECIFY(0x03)

SPECIFYコマンドはコントローラがドライブのメカ動作を制御するときの時間を設定するコマンドです

SPECIFYコマンドで設定する時間は制御設定レジスター(またはデータレートセレクトレジスター)の

DRATE SEL1、DRATE SEL2で設定するデータ転送速度によって変わります

(制御設定レジスターについてはフロッピーディスクドライバその1を参照してください)

設定できる時間は次の項目があります

これに加えSPECIFYコマンドのNDMビットでDMAを使用するかどうか設定することができます

SPECIFYコマンドを見ていきます

SPECIFYコマンド

SPECIFY(0x03)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 0 0 0 1 1
1 ステップレート ヘッドアンロードタイム
2 ヘッドロードタイム NDM


ステップレートSRT(Step Rate)

ステップレートビットに設定する値と制御設定レジスター(データレートセレクトレジスター)の

DRATE SEL1、DRATE SEL2で設定するデータ転送速度でステップレートは次のように決まります

(表のステップレートの単位はmsです)

ステップレートSRT(Step Rate)[ms]
データ転送レート(DRATE SELで設定したデータレート)
1Mbps 500Kbps 300Kbps 250Kbps
0x0 8.0 16 26.7 32
0x1 7.5 15 25.0 30
0x2 7.0 14 23.3 28
0x3 6.5 13 21.7 26
0x4 6.0 12 20.0 24
0x5 5.5 11 18.3 22
0x6 5.0 10 16.7 20
0x7 4.5 9 15.0 18
0x8 4.0 8 13.3 16
0x9 3.5 7 11.7 14
0xA 3.0 6 10.0 12
0xB 2.5 5 8.3 10
0xC 2.0 4 6.7 8
0xD 1.5 3 5.0 6
0xE 1.0 2 3.3 4
0xF 0.5 1 1.2 2


ヘッドアンロードタイムHUT(Head Unload Time)

ヘッドアンロードタイムに設定する値と制御設定レジスター(データレートセレクトレジスター)の

DRATE SEL1、DRATE SEL2で設定するデータ転送速度でヘッドアンロードタイムは次のように決まります

(表のヘッドアンロードタイムの単位はmsです)

ヘッドアンロードタイムHUT(Head Unload Time)[ms]
データ転送レート(DRATE SELで設定したデータレート)
1Mbps 500Kbps 300Kbps 250Kbps
0x0 128 256 426 512
0x1 8 16 26.7 32
0x2 16 32 53.3 64
0x3 24 48 80 96
0x4 32 64 106.7 128
0x5 40 80 133.3 160
0x6 48 96 160.0 192
0x7 56 112 186.7 224
0x8 64 128 213.3 256
0x9 72 144 240 288
0xA 80 160 266.7 320
0xB 88 176 293.3 352
0xC 96 192 320 384
0xD 104 208 346.7 412
0xE 112 224 373.3 448
0xF 120 240 400 480


ヘッドロードタイムHUT(Head Load Time)

ヘッドロードタイムに設定する値と制御設定レジスター(データレートセレクトレジスター)の

DRATE SEL1、DRATE SEL2で設定するデータ転送速度でヘッドロードタイムは次のように決まります

(表のヘッドロードタイムの単位はmsです)

ヘッドロードタイムHLT(Head Load Time)[ms]
データ転送レート(DRATE SELで設定したデータレート)
1Mbps 500Kbps 300Kbps 250Kbps
0x00 128 256 426 512
0x1 1 2 3.3 4
0x2 2 4 6.6 8
0x7E 126 252 420 504
0x7F 127 254 423 508


NDMビット(Non-DMAビット)

NDMビットでDMAを使用するかどうかを設定することができます

NDMビット(Non-DMAビット)
説明
0 DMAを使用します
1 DMAを使用しません


SPECIFYコマンドステータス

SPECIFYコマンドにはステータスがありません。

SPECIFYコマンドの実装

SPECIFYコマンドの実装例を見てみます



#define DEF_FDC_CMD_SPECIFY_MASK_SRT	( 0x0F << 4 )
#define DEF_FDC_CMD_SPECIFY_MASK_HUT	( 0x0F << 0 )
#define DEF_FDC_CMD_SPECIFY_MASK_HLT	( 0x7F << 1 )
#define DEF_FDC_CMD_SPECIFY_NDM         0x01

/*
==================================================================================
    Funtion     :writeFd0CmdSpecify
    Input       :unsigned char step_rate
                 < step rate : input range is between 0x00 and 0x0F
                                1M : 0.5 - 8.0 msec
                                500K : 1 - 16 msec
                                300K : 1.7 - 26.7 msec
                                250K : 2 - 32 msec >
                 unsigned char head_unload_time
                 < head unload time : input range is between 0x00 and 0x0F
                                1M : 8 - 128 msec  
                                500K : 16 - 256 msec
                                300K : 26.7 - 426 msec
                                250K : 32 - 512 msec >
                 unsigned char head_load_time
                 < head load time : input range is between 0x00 and 0x7F
                                1M : 1 - 128 msec  
                                500K : 2 - 256 msec
                                300K : 3.3 - 426 msec
                                250K : 4 - 512 msec >
                 BOOL non_dma_mode
                 < Non DMA mode : true = non DMA mode, false = DMA mode >
    Output      :void
    Return      :STAUS

    Description :write specify 0x03 Command
==================================================================================
*/
PRIVATE STATUS writeFd0CmdSpecify( unsigned char step_rate,
                                   unsigned char head_unload_time,
                                   unsigned char head_load_time,
                                   BOOL non_dma_mode)
{
    STATUS        status;
    unsgined char parameter;

    /*--------------------------------------------------------------------------*/
    /* write SPECIFY command                                                    */
    /*--------------------------------------------------------------------------*/
    status = writeFd0Command( FDC_CMD_SPECIFY );

    /*--------------------------------------------------------------------------*/
    /* write srt and hut parameters                                             */
    /*--------------------------------------------------------------------------*/
    parameter =  ( step_rate        << 4 ) & DEF_FDC_CMD_SPECIFY_MASK_SRT;

    parameter |= ( head_unload_time << 0 ) & DEF_FDC_CMD_SPECIFY_MASK_HUT;

    status    |= writeFd0Command( parameter );

    /*--------------------------------------------------------------------------*/
    /* write hlt and non-dma mode                                               */
    /*--------------------------------------------------------------------------*/
    parameter =  ( head_load_time   << 1 ) & DEF_FDC_CMD_SPECIFY_MASK_HLT;
    
    if( non_dma_mode )
    {
        parameter |= DEF_FDC_CMD_SPECIFY_NDM;
    }
    else
    {
        parameter &= ~DEF_FDC_CMD_SPECIFY_NDM;
    }

    status    |= writeFd0Command( parameter );

    return( status );
}



SENSE DRIVE STATUS(0x04)

SENSE DRIVE STATUSコマンドはドライブステータス情報を取得するためのコマンドです

ドライブステータス情報としてST3を返すだけとなりますので

コマンド処理時間は無くコマンド書き込み後直ぐにデータレジスターからST3を読み出すように

プログラムします

SENSE DRIVE STATUSコマンド

SENSE DRIVE STATUS(0x04)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 0 0 1 0 0
1 0 0 0 0 0 HDS DS1 DS0


SENSE DRIVE STATUSコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

SENSE DRIVE STATUS(0x04)ステータス
Byte/Bit 7 6 5 4 3 2 1 0
0 ST3


WRITE DATA(0x05)

WRITE DATAコマンドは指定したトラック上のセクターにデータを書き込みます。

コントローラは指定されたセクターを検出すると、データレジスターから1バイトずつ読み込み

ディスクに書き込んで行きます。1セクター分のデータを書き込むとCRCをCRCフィールドに書き込みます

(CRCの書き込みはコントローラが自動的に行います)。次のセクターに移動すると

セクター番号に+1されます。

DMAを使用していない場合はデータレジスターの1バイトを書き込む毎に

IRQ6が発生しますので、割り込みが入った後にデータレジスターに次の1バイトを書き込む

ようにプログラムします。

DMAを使用する場合は DMAドライバ を参照してください

WRITE DATAコマンド

WRITE DATA(0x05)
Byte/Bit 7 6 5 4 3 2 1 0
0 MT MFM 0 0 0 1 0 1
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 データ長(セクターサイズが0以外の場合は0xFFを指定)


WRITE DATAコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

WRITE DATAコマンドのステータスは READ TRACKのステータス と同じステータスとなります

READ DATA(0x06)

READ DATAコマンドは指定されたトラック上のセクターを読み出します。

1セクターを読み終えると次のセクター(MTビットがセットされている場合は論理上のセクター)に

移動します(セクター番号が1増えます)。読み込むデータ量はセクターサイズとMTビットが

セットされているかどうかで決まります。

通常は1セクターは512バイトなのでMT=0の時は512バイト×18セクターでMT=1の時は

512バイト×36セクターとなります。ステータスのセクター番号やシリンダー番号、ヘッド番号は

MTビット有り無しで変わります(ヘッドが最終的にどこにあるのか変わるので)

DMAを使用していない場合はデータレジスターに1バイト格納されるごとに

IRQ6が発生しますので、割り込みが入った後にデータレジスターから1バイト読み込む

ようにプログラムします。

DMAを使用する場合は DMAドライバ を参照してください

READ DATAコマンド

READ DATA(0x06)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 MFM 0 0 0 0 1 0
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 データ長(セクターサイズが0以外の場合は0xFFを指定)


READ DATAコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

READ DATAコマンドのステータスは READ TRACKのステータス と同じステータスとなります

READ DATAコマンドの実装

READ DATAコマンドの実装例を見ていきます。


#define DEF_FDC_CMDS_PRM_DTL       0xFF

#define DEF_FDD_SECTORS_PER_TRACK    18

/*
==================================================================================
    Funtion     :writeFdc0CmdReadData
    Input       :E_FDC_CMDS_1ST_PRM drive
                 E_FDC_CMDS_1ST_PRM head
                 unsigned char      track
                 unsigned char      sector
    Output      :void
    Return      :STAUS
    Description :issue Read Data Command
==================================================================================
*/
PRIVATE STATUS writeFdc0CmdReadData( E_FDC_CMDS_1ST_PRM    drive,
                                     E_FDC_CMDS_1ST_PRM    head,
                                     unsigned char         track,
                                     unsigned char         sector )
{
    int             i;
    STATUS          status;
    unsigned char   parameter;
    unsigned char   fdd_statuses[ DEF_FDC_NUM_STATUS ];
    unsigned char   st0;     /* status of sense interrupt command */
    unsigned char   pcn;     /* status of sense interrupt command */

    /*--------------------------------------------------------------------------*/
    /* write read data command                                                  */
    /*--------------------------------------------------------------------------*/
    status = writeFd0Command( FDC_CMD_READ_DATA );

    /*--------------------------------------------------------------------------*/
    /* write head and drive parameters                                          */
    /*--------------------------------------------------------------------------*/
    parameter = ( unsigned char )( head | drive );

    status |= writeFd0Command( parameter );

    /*--------------------------------------------------------------------------*/
    /* write specified track                                                    */
    /*--------------------------------------------------------------------------*/
    status |= writeFd0Command( track );

    /*--------------------------------------------------------------------------*/
    /* write specified drive                                                    */
    /*--------------------------------------------------------------------------*/
    status |= writeFd0Command( ( unsigned char )head >> 2 );

    /*--------------------------------------------------------------------------*/
    /* write specified sector                                                   */
    /*--------------------------------------------------------------------------*/
    if( DEF_FDD_SECTORS_PER_TRACK < secotr )
    {
        sector = DEF_FDD_SECTORS_PER_TRACK;
    }
    status |= writeFd0Command( sector );

    /*--------------------------------------------------------------------------*/
    /* write sector size                                                        */
    /*--------------------------------------------------------------------------*/
    status |= writeFd0Command( ( unsgined char )FDC_CMD_SECTOR_SIZE_512 );

    /*--------------------------------------------------------------------------*/
    /* write length of track                                                    */
    /*--------------------------------------------------------------------------*/
    status |= writeFd0Command( DEF_FDD_SECTORS_PER_TRACK );

    /*--------------------------------------------------------------------------*/
    /* write gap3 length                                                        */
    /*--------------------------------------------------------------------------*/
    status |= writeFd0Command( ( unsgined char )FDC_CMD_GAP3_SIZE_3_5 );

    /*--------------------------------------------------------------------------*/
    /* write data length( special sector size )                                 */
    /*--------------------------------------------------------------------------*/
    status |= writeFd0Command( DEF_FDD_CMDS_PRM_DTL );

    if( DEF_FDD_OK == status )
    {
        /*----------------------------------------------------------------------*/
        /* wait until irq of fdd is fired                                       */
        /*----------------------------------------------------------------------*/
        fddWaitIrq( );

        /*----------------------------------------------------------------------*/
        /* read statuses                                                        */
        /*----------------------------------------------------------------------*/
        for( i = 0 ; i < DEF_FDC_NUM_STATUS ; i++ )
        {
            status |= readFd0Status( &fdc_statuses[ i ] );
        }

        if( DEF_FDD_OK != status )
        {
            return( status );
        }

        /*----------------------------------------------------------------------*/
        /* issue sense interrupt status command and read its statuses           */
        /*----------------------------------------------------------------------*/
        readFdc0SenseInterruptStatus( &st0, &pcn );
    }

    return( status );
}



この実装例ではコマンド発行後にIRQ6が入るまでウェイトするような処理となっています

この関数呼び出し後フロッピーディスクからデータが読み込まれます。1バイト読み出す毎に

IRQ6の割り込みが入り、割り込みが入るごとにデータレジスターからディスクから読み込まれた

データを読みだしていきます。また割り込みが入った後に SENSE INTERRUPT STATUSコマンドを

発行して割り込みのステータス情報を必要に応じて取得します



しかし通常1バイト毎に割り込みが入って処理を行うと非常に処理効率が悪くなります

ですので、普通はこのような場合にDMAを使用します。

次の例はDMAを使用する場合の簡単な例となります。

DMAに関してはDMAドライバを参照してください


    /*--------------------------------------------------------------------------*/
    /* set up DMA controller for fdd                                            */
    /*--------------------------------------------------------------------------*/
    initDMAforReadFDD( trasfered_address, data_length - 1 );

    /*--------------------------------------------------------------------------*/
    /* start the motor of specified dirve                                       */
    /*--------------------------------------------------------------------------*/
    startFdc0Motor( drive );

    /*--------------------------------------------------------------------------*/
    /* issue read data command to read a sector                                 */
    /*--------------------------------------------------------------------------*/
    writeFdc0CmdReadData( drive, head, track, sector );

    /*--------------------------------------------------------------------------*/
    /* wait until irq of dma is fired                                           */
    /*--------------------------------------------------------------------------*/
    dmaWaitIrq( E_DMA_CHANNEL2 );

    /*--------------------------------------------------------------------------*/
    /* stop the motor of specified dirve                                        */
    /*--------------------------------------------------------------------------*/
    stopFdc0Motor( drive );



RECALIBRATE(0x07)

RECALIBRATEコマンドを発行することでヘッドをトラック0の位置まで戻します

このコマンド発行後、コントローラは内部のシリンダー情報をクリアしてTRK0ピンをチェックします

TRK0ピンがLowの状態だとDIRピンをLowにしステップパルスを出力してトラック0に戻します

TRK0ピンがHighになるとステータスレジスターST0のSEビットを 1にし、

コマンド処理を終了します。TRK0がステップパルスを79パルス出力してもLowのままだと

ステータスレジスターST0のSEビットとECビットを1にし、

コマンド処理を終了します。(80トラック以上あるディスクの場合はもう一度RECALIBRATEコマンドを

発行する必要があります)。RECALIBRATEコマンドの後はIRQ6割り込みが入ります。

その後必ず SENSE INTERRUPT STATUS コマンドを発行し、トラック0になっていることを確認します

また、システム起動後には必ずRECALIBRATEコマンドを発行する必要があります


RECALIBRATEコマンド

RECALIBRATE(0x07)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 0 0 1 1 1
1 0 0 0 0 0 0 DS1 DS0


RECALIBRATEコマンドステータス

RECALIBRATEコマンドにはステータスがありません

(このコマンド発行後は SENSE INTERRUPT STATUSコマンド を発行する必要があります)

RECALIBRATEコマンドの実装

RECALIBRATEコマンドを発行してトラック0に戻っていない場合はリトライを行います

またコマンド発行前にはヘッドを動かすために、ドライブのモーターをONにします

コマンドが終了した後はドライブのモーターをOFFにしておきます


#define DEF_FDD_CALIBRATE_RETRY    10

#define DEF_FDC_ST0_IC             0xC0

/*
==================================================================================
    Funtion     :writeFd0CmdRecalibrate
    Input       :E_FDC_CMDS_1ST_PRM drive
    Output      :void
    Return      :STAUS

    Description :recalibrate
==================================================================================
*/
PRIVATE STATUS writeFd0CmdRecalibrate( E_FDC_CMDS_1ST_PRM drive )
{
    int           i;
    STATUS        status
    unsigned char st0;
    unsigned char pcn;

    /*--------------------------------------------------------------------------*/
    /* start the morter before of a calibration                                 */
    /*--------------------------------------------------------------------------*/
    status = startFdc0Motor( drive );

    if( DEF_FDD_OK == status )
    {
        /*----------------------------------------------------------------------*/
        /* retry a calibration                                                  */
        /*----------------------------------------------------------------------*/
        for( i = 0 ; i < DEF_FDD_CALIBRATE_RETRY ; i++ )
        {
            /*------------------------------------------------------------------*/
            /* issue recalibrate command                                        */
            /*------------------------------------------------------------------*/
            status  = writeFd0Command( FDC_CMD_RECALIBRATE );

            status |= writeFd0Command( ( unsigned char )drive );

            /*------------------------------------------------------------------*/
            /* wait irq of fdd                                                  */
            /*------------------------------------------------------------------*/
            if( DEF_FDD_OK == status )
            {
                fddWaitIrq( );
            }
            else
            {
                continue;
            }

            /*------------------------------------------------------------------*/
            /* read sense interrupt status                                      */
            /*------------------------------------------------------------------*/
            status = readFdc0SenseInterruptStatus( &st0, &pcn );

            /*------------------------------------------------------------------*/
            /* if an error occurs, retry a recalibration                        */
            /*------------------------------------------------------------------*/
            if( ( status != DEF_FDD_OK ) || ( ( st0 & DEF_FDC_ST0_IC )  != 0x00 ) )
            {
                continue;
            }

            /*------------------------------------------------------------------*/
            /* whether head back to track 0 or not                              */
            /*------------------------------------------------------------------*/
            if( 0 == pcn )
            {
                /*--------------------------------------------------------------*/
                /* stop the motor                                               */
                /*--------------------------------------------------------------*/
                status = stopFdc0Motor( drive );

                return( status );
            }
        }
    }

    /*--------------------------------------------------------------------------*/
    /* if the drive causes an error, stop the motor                             */
    /*--------------------------------------------------------------------------*/
    status = stopFdc0Motor( drive );

    return( DEF_FDD_ERROR );
}



SENSE INTERRUPT STATUS(0x08)

SENSE INTERRUPT STATUSコマンドは割り込みのステータス情報を読み込みます

割り込みは次の条件で発生します

SENSE INTERRUPT STATUSコマンドを発行することで割り込み信号と ステータスレジスターST0

ICコードとSEビットがクリアされます。割り込みが発生していない状態でこのコマンドを発行すると

0x80(無効なコマンド)がステータスとしてデータレジスターに格納されます

割り込みの原因によるステータスレジスターST0のSEビットとICコード
SEビット ICコード(ビット) 割り込み原因
0 11b ポーリング
1 00b SEEK、RECALIBRATEコマンドの正常終了
0 01b SEEK、RECALIBRATEコマンドの異常終了


SEEKコマンドRECALIBRATEコマンドRELATIVE SEEKコマンドはステータスを返しませんが、

コマンド処理終了後SENSE INTERRUPT STATUSを発行する必要があり、トラックの状態を確認する必要があります

SENSE INTERRUPT STATUSが発行されない場合はコントローラはビジー状態のままとなってしまいます

SENSE INTERRUPT STATUSコマンド

SENSE INTERRUPT STATUS(0x08)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 0 1 0 0 0


SENSE INTERRUPT STATUSコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

SENSE INTERRUPT STATUS(0x08)ステータス
Byte/Bit 7 6 5 4 3 2 1 0
0 ST0
1 シリンダー番号(PCN:Present Cylinder Number)


SENSE INTERRUPT STATUSコマンドの実装

SENSE INTERRUPT STATUSコマンド発行後直ぐにステータスを読み込みます


 /*
==================================================================================
    Funtion     :readFdc0SenseInterruptStatus
    Input       :void
    Output      :unsigned char* st0
                 < status register st0 >
                 unsigned char* pcn
                 < present cylinder number >
    Return      :STATUS
    Description :read sense interrupt command status and output them
==================================================================================
*/
PRIVATE STATUS
readFdc0SenseInterruptStatus( unsigned char* st0, unsigned char* pcn )
{
    STATUS status;

    if( ( st0 == Null ) || ( pcn == Null ) )
    {
        return( DEF_FDD_ERROR );
    }

    /*--------------------------------------------------------------------------*/
    /* issue sense intterup status command                                      */
    /*--------------------------------------------------------------------------*/
    status = writeFd0Command( FDC_CMD_SENSE_INTERRUPT_STATUS );

    /*--------------------------------------------------------------------------*/
    /* read the results                                                         */
    /*--------------------------------------------------------------------------*/

    status |= readFd0Status( st0 );

    status |= readFd0Status( pcn );

    return( status );
}



WRITE DELETED DATA(0x09)

WRITE DELETED DATAコマンドはほぼWRITE DATAコマンドと同じです

異なる点は削除済みデータアドレスマークをデータフィールドの先頭に書き込むことです。

(WRITE DATAコマンドは通常のデータアドレスマークを書き込みます)。

ディスク上に不良セクターがある場合にこのコマンドを使用し、削除済みデータとして扱います

DMAを使用していない場合はデータレジスターの1バイトを書き込む毎に

IRQ6が発生しますので、割り込みが入った後にデータレジスターに次の1バイトを書き込む

ようにプログラムします。

DMAを使用する場合は DMAドライバ を参照してください

WRITE DELETED DATAコマンド

WRITE DELETED DATA(0x09)
Byte/Bit 7 6 5 4 3 2 1 0
0 MT MFM 0 0 1 0 0 1
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 データ長(セクターサイズが0以外の場合は0xFFを指定)


WRITE DELETED DATAコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

WRITE DELETED DATAコマンドのステータスは READ TRACKのステータス と同じステータスとなります

READ ID(0x0A)

READ IDコマンドを使用することで現在の磁気ヘッドの位置が取得できます

フロッピーディスクコントローラは読み込み可能な最初のIDアドレスマークを読み込みます

IDアドレスマークが検出できなかった場合は ステータスレジスターST0

ICコードビットに値01bを、 ステータスレジスターST1 のMAビットに値1を格納します。

READ IDコマンド

READ ID(0x0A)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 MFM 0 0 1 0 1 0
1 0 0 0 0 0 HDS DS1 DS0


READ IDコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

READ IDコマンドのステータスは READ TRACKのステータス と同じステータスとなります

READ DELETED DATA(0x0C)

READ DELETED DATAコマンドはほぼREAD DATAコマンドと同じです

異なる点はデータフィールドの先頭に削除済みデータアドレスマークがあるセクターのデータを

読み込むことです。 コマンドで使用する拡張ビットのSKビットとREAD DELETED DATAコマンドの

ステータスの関係は次のうようになります

SKビットとREAD DELETED DATAコマンドのステータスとの関係

SKビットとREAD DELETED DATAコマンドのステータスとの関係
SKビットの値 検出したデータアドレスマークのタイプ READ DELETED DATAコマンドのステータス
セクターが読み込めたか? ST2のCMビットの値 説明
0 通常データ Yes 1 次のセクターはサーチされていないのでステータスに格納されるセクター番号はインクリメントされていません
0 削除済みデータ Yes 0 コマンドは正常完了しました
1 通常データ No 1 コマンドは正常完了しましたがセクターは読み込みませんでした
(スキップしました)
1 削除済みデータデータ Yes 0 コマンドは正常完了しました


READ DELETED DATAコマンド

READ DELETED DATA(0x09)
Byte/Bit 7 6 5 4 3 2 1 0
0 MT MFM SK 0 1 0 0 1
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 データ長(セクターサイズが0以外の場合は0xFFを指定)


READ DELETED DATAコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

READ DELETED DATAコマンドのステータスは READ TRACKのステータス と同じステータスとなります

FORMAT TRACK(0x0D)

FORMAT TRACKコマンドを使用することで指定したトラック全体をフォーマットすることができます

コマンド発行後IDXピンのパルスを検出するとディスクのギャップ、アドレスマーク、IDフィールド、

データフィールドとデータを書き込みます。各フィールドはコマンドで指定する セクターサイズ

セクター数、ギャップ3サイズ、データパターンに従って

書き込まれます。(各セクターはデータパターンの値で512バイト埋められます)

IDフィールドはコントローラがシリンダー、ヘッド、セクター番号、セクターサイズから計算して

書き込んでくれます。このコマンドは1セクター分のフォーマットが終わるごとに発行します

1セクター分のフォーマットが終わったあとはIRQ6が発生しますので

次にフォーマットするセクターを指定してコマンドを発行します

(1トラック分のフォーマットコマンドですので、セクター番号のみ順次変えていきます)

参考 フォーマット時の標準値

参考程度にフォーマットするときの各値について下記表に記載します

(コマンドで指定するのはセクターサイズとセクター数です)

フォーマット時の標準値
ドライブ ディスク記録密度 セクターサイズ
(バイト)
セクターサイズ
(コマンドで指定)
セクター数
(コマンドで指定)
ギャップ1サイズ ギャップ2サイズ
5.25インチ 倍密度 256 0x01 0x12 0x0A 0x0C
256 0x01 0x10 0x20 0x32
512
(PC-ATの標準)
0x02 0x09 0x2A 0x50
1024 0x03 0x04 0x80 0xF0
2048 0x04 0x02 0xC8 0xFF
4096 0x05 0x01 0xC8 0xFF
4096 0x05 0x01 0xC8 0xFF
3.5インチ 倍密度 256 0x01 0x0F 0x0E 0x36
512
(PS/2標準)
0x02 0x09 0x1B 0x54
1024 0x03 0x05 0x35 0x74


FORMAT TRACKコマンド

フォーマット開始時に下記コマンドを発行します

FORMAT TRACK(0x0D)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 MFM 0 0 1 1 0 1
1 0 0 0 0 0 HDS DS1 DS0
2 セクターサイズ
3 トラック長(1トラック中のセクター数)
4 ギャップ3サイズ
5 フォーマットデータ(この1バイトデータでデータを埋めます)


続けて次のコマンドを発行して、フォーマットするセクターを指定します

このコマンドは1セクター分フォーマットが終わる毎に発行する必要があります

(コマンド発行後にIRQ6が発生しますのでその後に書き込みます)

FORMAT TRACKのセクター指定コマンド
Byte/Bit 7 6 5 4 3 2 1 0
0 シリンダー番号
1 ヘッド番号
2 セクター番号
3 セクターサイズ


FORMAT TRACKコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

FORMAT TRACKコマンドのステータスの4バイト目以降は未定義となっていますが

ステータス自体は7バイトありますので、7バイト分読み込みます

(読み込んだ4バイト目以降は破棄します)

FORMAT TRACK(0x0D)ステータス
Byte/Bit 7 6 5 4 3 2 1 0
0 ST0
1 ST1
2 ST2
3 未定義
4 未定義
5 未定義
6 未定義


DUMPREG(0x0E)

DUMPREGコマンドはデバッグ情報を取得するためのコマンドです

DUMPREGコマンド

DUMPREG(0x0E)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 0 1 1 1 0


DUMPREGコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

DUMPREGコマンドのステータスは特殊で全部で10バイトあります

DUMPREG(0x0E)ステータス
Byte/Bit 7 6 5 4 3 2 1 0
0 ドライブ0の現在のシリンダー番号(PCN:Present Cylinder Number)
1 ドライブ1の現在のシリンダー番号(PCN:Present Cylinder Number)
2 ドライブ2の現在のシリンダー番号(PCN:Present Cylinder Number)
3 ドライブ0の現在のシリンダー番号(PCN:Present Cylinder Number)
4 ステップレート ヘッドアンロードタイム
5 ヘッドロードタイム NDM
6 セクター数/トラック長(1トラック中のセクター数)
7 LOCK 0 D3 D2 D1 D0 GAP WGATE
8 0 EIS EFIFO POLL FIFOTHR
9 PRETRK


ここで各ビットについて

DUMPREGコマンドステータスの各ビットについて
ビット名称 説明
LOCK CONFIGUREコマンドで使用するEFIFO、FIFOTHR、PRETRKをソフトウェアリセットで デフォルト値にするかどうかを決定するビットです
D3-D0 D0:ドライブ0が選択されているかどうかを示します
D1:ドライブ1が選択されているかどうかを示します
D2:ドライブ2が選択されているかどうかを示します
D3:ドライブ3が選択されているかどうかを示します
GAP 垂直(Perpendicular)モード時にギャップ2サイズを変更するかどうかを示します
WGATE 垂直(Perpendicular)ドライブで事前消去メカ動作をするために、WEピンのタイミングを変更するかどうかを示します
EIS 事前シークが有効になっているかどうかを示します。 詳細はCONFIGUREコマンドの EISを参照してください
EFIFO FIFO(ICの内部バッファ)が有効になっているかどうかを示します。 詳細はCONFIGUREコマンドの EFIFOを参照してください
POLL ICの内部ポーリング処理が無効になっているかどうか示します。 詳細はCONFIGUREコマンドの POLLを参照してください
FIFOTHR READ系/WRITE系コマンドの実行時のFIFO(ICの内部バッファ)閾値が格納されます。 詳細はCONFIGUREコマンドの FIFOTHRを参照してください
PRETRK 前補正開始トラック番号が格納されます。 詳細はCONFIGUREコマンドの PRETRKを参照してください


SEEK(0x0F)

SEEKコマンドを使用することで磁気ヘッドをトラック間移動させることができます

SEEKコマンド発行後ステップパルスを出力し、モーターを動かすことでヘッドを移動します

移動する方向に従ってDIRピンの出力がかわります。ステップパルスの間隔は

SPECIFYコマンドステップレートによって設定できます

指定したトラックに到達するとST0の SEビットに1が格納されます

SEEK、RECALIBRATEコマンド書き込み中は ドライブがビジー状態となりますが

コマンド実行中はアイドル状態となり次のコマンドが発行できます



事前シークが無効となっている場合にはREAD/WRITEコマンドは
  1. SEEKコマンドを発行して目的のトラックまで移動します

  2. SENSE INTERRUPT STATUSコマンド を発行してSEEKコマンドを完全に完了させます

  3. READ IDコマンドを発行して目的のトラックに 移動できているか確認します

  4. READ/WRITEコマンドを発行します

という手順で行います。



SEEKコマンドはステータスがありませんので、必ずSENSE INTERRUPT STATUSコマンド を発行して

SEEKコマンドを完全に完了させる必要があります。

SEEKコマンド

SEEK(0x0F)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 0 1 1 1 1
1 0 0 0 0 0 HDS DS1 DS0
2 移動先のシリンダー番号


SEEKコマンドステータス

SEEKコマンドにはステータスがありません。

ステータスが無いコマンドでもIRQ6は発生します

必ずSENSE INTERRUPT STATUSコマンドを発行しておきます

SEEKコマンドの実装

SEEKコマンドの簡単な実装例を見ていきます

要領はRECALIBRATEコマンドと同じです

事前シークをしない設定にしている場合はREAD/WRITEコマンド発行前にSEEKコマンドで

目的のトラックまでヘッドを移動させておきます


#define DEF_FDD_SEEK_RETRY    10

/*
==================================================================================
    Funtion     :writeFd0CmdSeek
    Input       :E_FDC_CMDS_1ST_PRM drive
                 < specify drive to seek >
                 E_FDC_CMDS_1ST_PRM head
                 < specify head to seek >
                 unsgined char cylinder
                 < specify cylinder to seek >
    Output      :void
    Return      :void

    Description :seek 
==================================================================================
*/
PRIVATE STATUS writeFd0CmdSeek( E_FDC_CMDS_1ST_PRM drive,
                                E_FDC_CMDS_1ST_PRM head,
                                unsigned char      cylinder)
{
    int           i;
    STATUS        status
    unsigned char st0;
    unsigned char pcn;

    /*--------------------------------------------------------------------------*/
    /* retry seek                                                               */
    /*--------------------------------------------------------------------------*/
    for( i = 0 ; i < DEF_FDD_SEEK_RETRY ; i++ )
    {
        /*----------------------------------------------------------------------*/
        /* issue seek command                                                   */
        /*----------------------------------------------------------------------*/
        status  = writeFd0Command( FDC_CMD_SEEK );

        status |= writeFd0Command( ( unsigned char )( drive | head ) );
        
        status |= writeFd0Command( cylinder );

        /*----------------------------------------------------------------------*/
        /* wait irq of fdd                                                      */
        /*----------------------------------------------------------------------*/
        if( DEF_FDD_OK == status )
        {
            fddWaitIrq( );
        }
        else
        {
            continue;
        }

        /*----------------------------------------------------------------------*/
        /* read sense interrupt status                                          */
        /*----------------------------------------------------------------------*/
        status = fddReadSenseInterruptStatus( &st0, &pcn );

        /*----------------------------------------------------------------------*/
        /* if an error occurs, retry a seek                                     */
        /*----------------------------------------------------------------------*/
        if( ( status != DEF_FDD_OK ) || ( ( st0 & DEF_FDC_ST0_IC )  != 0x00 ) )
        {
            continue;
        }

        /*----------------------------------------------------------------------*/
        /* whether specified cylinder is sought for or not                      */
        /*----------------------------------------------------------------------*/
        if( cylinder == pcn )
        {
            /*------------------------------------------------------------------*/
            /* seek operation is finished!                                      */
            /*------------------------------------------------------------------*/
            return( status );
        }
    }

    /*--------------------------------------------------------------------------*/
    /* if the drive causes an error                                             */
    /*--------------------------------------------------------------------------*/
    return( DEF_FDD_ERROR );
}



VERSION(0x10)

VERSIONコマンドを使用することでフロッピーディスクコントローラが

改良型IC(例えば82077AA)か旧型(8272A/765A)を判断することができます

ステータスとして0x90が格納されていれば改良型ICとなります

このコマンドは割り込みが発生しませんので、コマンド発行後直ぐに

ステータスを読み出します

VERSIONコマンド

VERSION(0x10)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 1 0 0 0 0


VERSIONコマンドステータス

VERSION(0x10)ステータス
Byte/Bit 7 6 5 4 3 2 1 0
1 0 0 0 0 0 0 0 0


SCAN EQUAL(0x11)

SCAN系のコマンドを発行することで、ディスク上のデータを読みだして指定したデータと

比較することができます。(DMAモードと非DMAモードの両方で使用可能です)

SCAN系コマンドでは次のうように比較します

データは0x00が最小値で0xFFが最大値となります。

コマンド実行中に、CPUまたはDMAがターミナルカウントTCピンをHighにすることで

フロッピーディスクコントローラは比較処理中のバイトで処理を完了させます

SCAN系コマンドはそのトラックの最終セクターまで到達するか、TCピンに信号が入るまで継続します

この場合ステータスレジスターST2のSHビットに1が格納されます

指定したセクターから最終セクターまでスキャンを行い、その結果が不適合の場合に

ステータスレジスターST2のSNビットに1を格納します

SCANコマンドとST2のSNビット、SHビットの関係は次のようになります

SCANコマンドとST2のSNビット、SHビットの関係
コマンド ステータスレジスターST2 説明
ビット2 SNビット ビット3 SHビット
SCAN EQUAL 0
1
1
0
ディスク上のデータ = 指定したデータ
ディスク上のデータ ≠ 指定したデータ
SCAN LOW OR EQUAL 0
0
1
1
0
0
ディスク上のデータ = 指定したデータ
ディスク上のデータ > 指定したデータ
ディスク上のデータ ≯ 指定したデータ
SCAN HIGH OR EQUAL 0
0
1
1
0
0
ディスク上のデータ = 指定したデータ
ディスク上のデータ < 指定したデータ
ディスク上のデータ ≮ 指定したデータ


SK=0で削除済みデータを検出した場合はそのセクターをそのトラックの最終セクターと

みなし、ステータスレジスターST2のCMビットに1を格納します

SK=1で削除済みデータを検出した場合はそのセクターをスキップして次の

セクターを読み込みに行きます。(この場合でも ステータスレジスターST2 のCMビットに1を格納します)

SCAN系コマンドのSTPパラメーターやMTビットを設定している場合は

特に注意が必要です。SCAN系コマンドはそのトラックの最終セクターまで読み込みますので

例えば最終セクターが18でSTPに2を設定している場合にセクター13からスキャンを開始して

セクター13、15、17を順次スキャンしていきます。この場合最終セクターは18にもかかわらず

次のスキャン対象はセクター19となります。この場合スキャンコマンドは異常終了となります。

SCAN EQUALコマンド

SCAN EQUAL(0x11)
Byte/Bit 7 6 5 4 3 2 1 0
0 MT MFM SK 1 0 0 0 1
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 STP


STP

STPに1を設定しておくとスキャンコマンド実行中に、ディスク上のセクターのデータを1バイトずつスキャンして

いきます。2を設定しておくと、1を設定してしていた動作に加え次のセクターについても1バイトずつスキャンして

していきます。

SCAN EQUALコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

SCAN EQUALコマンドのステータスは READ TRACKのステータス と同じステータスとなります

PERPENDICULAR MODE(0x12)

垂直記録ができるドライブにアクセスする場合には、READ、WRITE、FORMATコマンドを

発行する前にPERPENDICULAR MODEコマンドを発行しておきます。このコマンドを発行することで

該当ドライブのギャップ2フィールドとVCO有効タイミングを変更することができます

指定するパラメーターとその効果については下記表を参考にしてください

PERPENDICULAR MODEコマンドとパラメーターの効果
GAP WGATE モード インデックスパルス後のVCOをLowにする時間 Gap2サイズ WRITEコマンド時のGap2率 READコマンド時のGap2とVCOをLowにする時間
0 0 従来モード 33バイト 22バイト 0バイト 24バイト
0 1 垂直モード
(データレート500Kbps)
33バイト 22バイト 19バイト 24バイト
1 0 予約
(従来モードとの互換のため)
33バイト 22バイト 0バイト 24バイト
1 1 垂直モード
(データレート1Mbps)
18バイト 41バイト 38バイト 43バイト


PERPENDICULAR MODEコマンドで指定するモードのデータレートは

データレートセレクトレジスターDSRで設定したデータレートに合わせる必要があります



ギャップ2とVCOタイミングは垂直記録型ドライブの磁気ヘッドによって変わります

(READ/WRITEする磁気ヘッド200マイクロメートルの前にプレイレイズヘッドがあります)。

このヘッドの間の200マイクロメートルは、1Mbpsで約38バイトにあたります

WRピンがアクティブになると記録磁気ヘッドとプレイレイズヘッドの両方が

有効になります。この構造により、記磁気ヘッドを先にONにすることで、

プレイレイズヘッドが後で動作するので、ディスク上に38バイトの不定値が

書き込まれてしまいます。このように、ヘッドの動作時間を考慮して、

ギャップ2フィールドは41バイトにしておきます。



コントローラは、データ読み込み時はディスク上のSyncフィールドと同期をとる必要があります

従来モードでは、ギャップ2フィールドの開始から約24バイトでPLL VCOが有効になります(VCOEN)

しかし、1Mbpsの垂直モード( WGATE=1GAP=1)でコントローラが動作している場合、

43バイト後にVCOENピンをアクティブにします。(ギャップ2フィールドサイズを41バイトに設定している

ので2バイトの余裕を持っています。従来モードでも2バイトの余裕を見ています)。



書き込み時には、コントローラは従来モードで動作中、ディスクのSyncフィールドの開始と同期して

WRピンを有効にします。その後、新しくSyncフィールドを書き込み、続けてデータアドレスマーク、

データフィールド、CRCを書き込みます。垂直ドライブのプレイレイズヘッドを使用する場合は

記録磁気ヘッドは次のSyncフィールド書き込みのために、ギャップ2フィールド区間上で

アクティブにしておく必要があります。1Mbpsの垂直モードでは ( WGATE=1GAP=1)、

ギャップ2に38バイト書き込みされます。記録ビット密度はデータレートに比例しますので、

500Kbps( WGATE=1GAP=0)ではギャップ2に19バイト書き込まれます



プログラムでは特にギャップ2サイズ、VCOタイミング、WGATEタイミングを変更する必要はありません

PERPENDICULAR MODEコマンド

PERPENDICULAR MODE(0x12)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 0 1 0 1 0
1 OW 0 D3 D2 D1 D0 GAP WGATE


OWビット

OWビットに1をセットするとPERPENDICULAR MODEコマンドでD0、D1、D2、D3を

変更することができます

PERPENDICULAR MODEコマンドステータス

PERPENDICULAR MODEコマンドにはステータスがありません。

CONFIGURE(0x13)

CONFIGUREコマンドでコントローラの特別機能を選択することができます

(コントローラに設定されているデフォルト機能がシステム要件に合致していればCONFIGUREコマンドを

発行する必要はありません)

CONFIGUREコマンド

CONFIGURE(0x13)
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 1 0 0 1 1
1 0 0 0 0 0 0 0 0
2 0 EIS EFIFO POLL FIFOTHR
3 PRETRK


CONFIGUREコマンドのパラメーターの各ビットについて

CONFIGUREコマンドのパラメーター
ビット 名称 説明
EIS 事前シーク有効 0:事前シークを行いません<デフォルト値>
1:READ/WRITEコマンドの前に自動的にSEEK操作します(事前シーク)
EFIFO FIFO無効 0:FIFOを有効にします
1:FIFOを無効にします。ステータスレジスターの状態を判断して、データレジスターのデータを1バイトずつ読み出します<デフォルト値>
POLL ドライブのポーリング無効 0:ドライブをポーリングします<デフォルト値>。リセット後IRQ6を1回発生させます。ヘッドロード中とヘッドアンロード中はポーリングしません
1:ドライブのポーリングを無効にします
FIFOTHR FIFO閾値 READ/WRITEコマンド実行中のFIFO閾値を設定します。閾値は1バイトから16バイトまで設定可能です。デフォルトは1が設定されています。 0x00を設定すると16バイトを選択したことになります。
PRETRK トラック番号の開始位置前補正 トラック0からトラック255まで設定できます。デフォルトはトラック0です。0x00を設定するとトラック0で、0xFFを設定するとトラック255を選択したことになります。


CONFIGUREコマンドステータス

CONFIGUREコマンドにステータスはありません。

LOCK(0x14)

LOCKコマンドを使用することで、古いソフトウェアがDMA転送を使用してFIFOを長時間使用することを

防ぐことができます。(このコマンドはシステムソフトウェアのみ使用します。)。LOCKコマンドを使用すると

リセット後の EFIFOFIFOTHRPRETRKの値を決めることができます。

LOCKビットを1に設定すると、これらのパラメーターはデジタルアウトプットレジスターDORと

データレートセレクトレジスターDSRの設定値にかかわらず、リセットした後でも設定値を保持します

LOCKビットを0に設置すると、これらのパラメーターはデフォルト値に戻ります

ハードウェアリセットではLOCKビットは0に設定されたことになり、デフォルト値に戻ります

ステータスはコマンド発行後直ぐに読みだすことができます(割り込みは発生しません)

LOCKコマンド

LOCK(0x14)
Byte/Bit 7 6 5 4 3 2 1 0
0 LOCK 0 0 1 0 1 0 0


LOCKコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

LOCK(0x14)ステータス
Byte/Bit 7 6 5 4 3 2 1 0
0 0 0 0 LOCK 0 0 0 0


VERIFY(0x16)

VERIFYコマンドはディスク上のデータを検査するときに使用します

VERIFYコマンドはCPUにデータ転送しないこと、CRCのチェックを行うこと以外は

READ DATAコマンドと同じです。

データ転送はしないので、コマンド完了を検知するためにTCピンを使用することはできません

ECビットを1にすることで擬似的なTCピン動作をさせることができます

(セクター数に0を指定すると255セクターを検査することになります)

ECビットを0にしたときはトラックの最終セクターの検査を終えると

コマンド処理完了となります。(ECビットを0にしたときにはデータ長/セクター数を0xFFにします)

VERIFYコマンド

VERIFY(0x16)
Byte/Bit 7 6 5 4 3 2 1 0
0 MT MFM SK 1 0 1 1 0
1 EC 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 データ長(セクターサイズが0以外の場合は0xFFを指定)/セクター数


ECビット

カウント有効ビットです。このビットが1のときはコマンドのパラメーターのデータ長がセクター数として解釈されます

VERIFYコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

VERIFYコマンドのステータスは READ TRACKのステータス と同じステータスとなります

SCAN LOW OR EQUAL(0x19)

指定したデータよりも大きいか等しいディスク上のデータをスキャンします

(指定したデータがディスク上のデータより等しいか小さい条件でスキャンします)

スキャンの詳細はSCAN EQUALコマンドを参照してください

SCAN LOW OR EQUALコマンド

SCAN LOW OR EQUAL(0x19)
Byte/Bit 7 6 5 4 3 2 1 0
0 MT MFM SK 1 0 0 0 1
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 STP


SCAN LOW OR EQUALコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

SCAN LOW OR EQUALコマンドのステータスは READ TRACKのステータス と同じステータスとなります

SCAN HIGH OR EQUAL(0x1D)

指定したデータよりも小さいか等しいディスク上のデータをスキャンします

(指定したデータがディスク上のデータより等しいか大きい条件でスキャンします)

スキャンの詳細はSCAN EQUALコマンドを参照してください

SCAN HIGH OR EQUALコマンド

SCAN HIGH OR EQUAL(0x1D)
Byte/Bit 7 6 5 4 3 2 1 0
0 MT MFM SK 1 0 0 0 1
1 0 0 0 0 0 HDS DS1 DS0
2 シリンダー番号
3 ヘッド番号
4 セクター番号
5 セクターサイズ
6 トラック長(1トラック中のセクター数)
7 ギャップ3サイズ
8 STP


SCAN HIGH OR EQUALコマンドステータス

コマンドの処理完了後下記ステータスが格納されます

SCAN HIGH OR EQUALコマンドのステータスは READ TRACKのステータス と同じステータスとなります

RELATIVE SEEK(0x8F)

MSBが1であり、DIRビット以外はSEEKコマンドと同じです

RELATIVE SEEKコマンドはSEEKコマンドと異なり現在のトラックから相対シークします

シークするトラック数はコマンドのパラメーターで指定します。RELATIVE SEEKは全ドライブ中同時に1つだけ

シークすることができます。(SEEKは各ドライブでシーク可能です)。トラック0を越えて外側にシークしようとすると

ステータスレジスターST0のECビットを1にします

トラック0またはトラックの最大値を超えるRELATIVE SEEKを行うとトラックの最大値からmodした値からシークを開始します

(通常フロッピーディスクの最大トラックは255です)。

RELATIVE SEEKコマンド

RELATIVE SEEK(0x8F)
Byte/Bit 7 6 5 4 3 2 1 0
0 1 DIR 0 0 1 1 1 1
1 0 0 0 0 0 HDS DS1 DS0
2 相対シリンダー番号


DIRビット

RELATIVE SEEKコマンドステータス

RELATIVE SEEKコマンドにステータスはありません。

SENSE INTERRUPT STATUSコマンド を発行する必要があります。

フロッピードライブの制御まとめ

ここまでで、ポートアドレスを介してのレジスター制御とコマンドによるドライブ制御をひと通り見てきました

ここでフロッピーディスクの制御のまとめとして、初期化処理、リード/ライト処理、

フォーマット処理について例を見ていきたいと思います。

フロッピードライブの初期化

フロッピードライブの初期化には、コントローラのリセットを行ってからコントローラを有効にします。

コントローラのリセット

コントローラをリセットするにはデータアウトプットレジスターDORのRESETビットに0を書き込みます

(データアウトプットレジスターDORについてはフロッピーディスクドライバその1 を参照してください)。


    writeFdc0Dor( DEF_FDC_DOR_RESET );



コントローラの有効化

コントローラを有効にするにはデータアウトプットレジスターDORのRESETビットに1を書き込みます


    writeFdc0Dor( DEF_FDC_DOR_ENABLE );



コントローラの初期化

コントローラの初期化時には次の処理を行い、データレートなどドライブに最適な設定を行います

コントローラはリセット後デフォルトでポーリングが有効になっているので、

各ドライブに対してSENSE INTERRUPT STATUSコマンドを発行し、 ICが内部で持っている割り込みフラグをクリアしておきます。

初期化フロー

初期化時のフローは下記のようになります

フロッピーディスクコントローラ初期化フロー

300msの時間計測には割り込みその3で みてきましたようにPITを

利用すれば正確に測れます。

初期化実装

初期化フローの簡便な実装例を見ていきます


#define	DEF_FDD_STEP_RATE_500K_03           0xD     /*  3msec  */
#define	DEF_FDD_HEAD_UNLOAD_TIME_500K_032   0x2     /*  32mese */
#define	DEF_FDD_HEAD_LOAD_TIME_500K_032     0x10    /*  32msec */

#defnie DEF_FDC_CCR_DRATE_500KBPS           0x00    /* 500Kbps */
#define DEF_FDC_CCR_DRATE_300KBPS           0x01    /* 300Kbps */
#define DEF_FDC_CCR_DRATE_250KBPS           0x02    /* 250Kbps */
#define DEF_FDC_CCR_DRATE_1MBPS             0x03    /* 1Mbps   */

/*
==================================================================================
    Funtion     :initFdc0
    Input       :unsigned char drive
                 < 0x00 : drive0
                      0x01 : drive1
                      0x02 : drive2
                      0x03 : drive3 >
    Output      :void
    Return      :STATUS
    Description :initialize the fdc0
==================================================================================
*/
PRIVATE STATUS initFdc0( unsigned char drive )
{
    STATUS        status;
    unsigned char st0;
    unsigned char pcn;

    /*--------------------------------------------------------------------------*/
    /* reset the controller                                                     */
    /*--------------------------------------------------------------------------*/
    writeFdc0Dor( DEF_FDC_DOR_RESET );

    writeFdc0Dor( DEF_FDC_DOR_ENABLE );

    /*--------------------------------------------------------------------------*/
    /* wait irq after the controller resets                                     */
    /*--------------------------------------------------------------------------*/
    fddWaitIrq( );

    /*--------------------------------------------------------------------------*/
    /* issue sense interrupt status commands four times                         */
    /*--------------------------------------------------------------------------*/
    /* 1st drive */
    status  = readFdc0SenseInterruptStatus( &st0, &pcn );
    /* 2nd drive */
    status |= readFdc0SenseInterruptStatus( &st0, &pcn );
    /* 3rd drive */
    status |= readFdc0SenseInterruptStatus( &st0, &pcn );
    /* 4th drive */
    status |= readFdc0SenseInterruptStatus( &st0, &pcn );

    if( DEF_FDD_OK != status )
    {
        return( DEF_FDD_ERROR );
    }

    /*--------------------------------------------------------------------------*/
    /* steprate = 3ms, head unload time = 32ms, head load time = 32ms           */
    /*--------------------------------------------------------------------------*/
    status = writeFd0CmdSpecify( DEF_FDD_STEP_RATE_500K_03,
                                 DEF_FDD_HEAD_UNLOAD_TIME_500K_032,
                                 DEF_FDD_HEAD_LOAD_TIME_500K_032,
                                 False );

    if( DEF_FDD_OK != status )
    {
        return( DEF_FDD_ERROR );
    }

    /*--------------------------------------------------------------------------*/
    /* set data transfer rate to 500kbps( for 3.5 inch )                        */
    /*--------------------------------------------------------------------------*/
    writeFdc0Ccr( DEF_FDC_CCR_DRATE_500KBPS );

    /*--------------------------------------------------------------------------*/
    /* calibrate a head position                                                */
    /*--------------------------------------------------------------------------*/
    status = writeFd0CmdRecalibrate( ( E_FDC_CMDS_1ST_PRM )drive );

    if( DEF_FDD_OK != status )
    {
        return( DEF_FDD_ERROR );
    }

    return( status );
}



この制御例では、まずフロッピーディスクコントローラのリセットを行っています。

その次にIRQが発生しますので、IRQが発生するまでウェイトを行っています

IRQが発生した後は合計4回の SENSE INTERRUPT STATUSコマンドを発行します



次に行っているのがSPECIFYコマンドでディスクの制御時間を設定しています

ここでは、3.5インチフロッピーディスクドライブを想定していますので、一般的なドライブの制御時間を設定します

通常 ステップレート は3msまたは6msで、 ヘッドアンロードタイムは約30ms、 ヘッドロードタイムも約30msとなります。

また、この実装例ではDMAを使用するように設定します。



その次には制御設定レジスターCCRにデータレート500Kbpsを設定しています。

(制御設定レジスターCCRについてはフロッピーディスクドライバその1を 参照してください)。



そして、最後にRECALIBRATEコマンドでトラック0の位置にヘッドを戻しておきます



この初期化処理が終わった後は制御コマンドを使用してフロッピーディスクドライブを好きなように制御します

ディスクの読み込み/書き込み

読み込み/書き込みを行う前にはモーターをONにして、SEEKを行い( 事前シークを有効にしている場合は、

その必要はありません。)、DMAコントローラ を初期化し(DMAを使用しない場合は必要ありません)、READ/WRITEコマンドを発行します



モーターをONにした後は、モーターの回転が安定するまでのメカ動作待ちが必要となります

その時間は3.5インチフロッピーディスクドライブで300ms待ちます(5.25インチは500ms)。

300msは結構長いので、短縮するテクニックとして、モーターをONにした後すぐにREADコマンドを

発行します。そしてステータスを読み取ると、メカ動作が完了していないとエラーとなったままとなりますので

ステータスがOKとなるまでREADコマンドを発行するという方法がとれます。この場合は300ms待つ必要は

ありません。ただ、WRITEコマンドではこのテクニックを使用しないで、300ms待ちます

(WRITEする前にIDフィールドを読み込みますが、モーターのメカ動作が安定していない状態でも

IDフィールドを読み込めてしまうので、意図しないデータを書き込むかエラーとなります)



モーターがONできた後、挿入されたディスクに対応したデータレートを設定します。

(現在の3.5インチフロッピーディスクは規格化されていますので、挿入されるメディアは既知となります)

読み込み/書き込みするドライバは、制御設定レジスターCCRに設定値を書き込むために

事前に挿入されたディスクのデータレートを知っておく必要があります。データレートを知るためには

制御設定レジスターCCRで設定値を変えながら、READ IDコマンドを発行し

そのステータス情報がエラーになっていないかどうかで決定する方法があります。エラーが無ければ

そのデータレートで読み込み/書き込みを行うようにプログラムします



事前シークを無効にしている場合は、READ/WRITEコマンドの前に

SEEKコマンドを発行する必要がありますが、シークした後にデータの読み込み/書き込み

するために、ヘッドをディスク表面に近づける必要があります。このメカ動作の時間をセトリングタイムと言います

SEEKコマンドが完了した後に、セトリングタイムは最低15msの間待つ必要があります。

事前シークを有効にしている場合は ヘッドロードタイム がセトリングタイムとなります。このときのヘッドロードタイムは16ms(500Kbsのときに値は0x8)となります。

このサイトで紹介している初期化実装例では32msとなりますので、十分な時間を確保しています

ヘッドが既に、READ/WRITEしたいトラック上に有る場合はセトリングタイムは考慮しなくてもOKです



では、読み込み/書き込みのフローを見て行きましょう。

読み込み/書き込みフロー

フロッピーディスクリード/ライトフロー

LBAとCHS

フロッピーディスクのLBA(論理セクター:Logical Block Addressing)は0-2879でアクセスします

しかし、今回見てきましたように制御コマンドはLBAではなくCHS(シリンダー、ヘッド、セクター)の

物理アドレスでパラメーターをしていします。LBAからCHSへの変換は ブートローダその10 で見てきました

(LABで制御できるICもなかにはあります)

ブートローダではアセンブラでしたが、今回はC言語で見て行きましょう


#define DEF_FDD_SECTORS_PER_TRACK    18
#define DEF_FDD_NUM_HEADS             2

/*
==================================================================================
    Funtion     :convertLBA2Sector
    Input       :unsigned int lba
                 < logical block addressing format >
    Output      :void
    Return      :unsigned char
                 < sector number of converted physical address chs >
    Description :convert lba to sector number
===================================================================================
*/
PRIVATE INLINE unsigned char
convertLBA2Sector( unsigned int lba )
{
    return( ( lba % DEF_FDD_SECTORS_PER_TRACK ) + 1 );
}

/*
===================================================================================
    Funtion     :ConvertLBA2Head
    Input       :unsigned int lba
                 < logical block addressing format >
    Output      :void
    Return      :unsigned char
                 < head number of converted physical address chs >
    Description :convert lba to head number
===================================================================================
*/
PRIVATE INLINE unsigned char
convertLBA2Head( unsigned int lba )
{
    return( ( lba / ( DEF_FDD_SECTORS_PER_TRACK ) ) % DEF_FDD_NUM_HEADS );
}

/*
===================================================================================
    Funtion     :convertLBA2Track
    Input       :unsigned int lba
                 < logical block addressing format >
    Output      :void
    Return      :unsigned char
                 < cylinder number of converted physical address chs >
    Description :convert lba to cylinder number
===================================================================================
*/
PRIVATE INLINE unsigned char
convertLBA2Track( unsigned int lba )
{
    return( lba / ( DEF_FDD_SECTORS_PER_TRACK * DEF_FDD_NUM_HEADS ) );
}



ここでDEF_FDD_SECTORS_PER_TRACKは1トラック中のセクター数です。3.5インチフロッピーディスクでは

1トラックに18セクターあります。この関数例では、LBAから制御コマンドで使用する物理セクター番号、

物理ヘッド番号、物理シリンダー番号を計算する関数です。

LBAを使用したセクター読み込み

LBAを使用してセクターの読み込み関数は次のようにCHSを指定します


/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :fddReadSector
    Input       :unsigned char drive 
                 < selected drive >
                 unsigned int lba
                 < read address >
                 unsigned char *trans_address
                 < DMA transfers data read from fdd to this address >
                 unsigned int trans_length
                 < DMA transfer count >
    Output      :void
    Return      :STATUS
    Description :read sector
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
PUBLIC STATUS
fddReadSector( unsigned char drive,
               unsigned int  lba,
               unsigned char *trasfered_address,
               unsigned int  data_length )
{
    unsigned char head;
    unsigned char track;
    unsigned char sector;
    STATUS        status;
    
    if( DEF_FDC_DOR_DRIVE3 < drive )
    {
        return( DEF_FDD_ERROR );
    }

    /*--------------------------------------------------------------------------*/
    /* set up DMA controller for fdd                                            */
    /*--------------------------------------------------------------------------*/
    status = initDMAforReadFDD( trasfered_address, data_length - 1 );

    /*--------------------------------------------------------------------------*/
    /* start the motor of specified dirve                                       */
    /*--------------------------------------------------------------------------*/
    startFdc0Motor( drive );

    /*--------------------------------------------------------------------------*/
    /* convert lba to chs                                                       */
    /*--------------------------------------------------------------------------*/
    head   = convertLBA2Head( lba   );
    track  = convertLBA2Track( lba  );
    sector = convertLBA2Sector( lba ); 

    /*--------------------------------------------------------------------------*/
    /* seek specified track                                                     */
    /*--------------------------------------------------------------------------*/
    status = fddSeek( drive, head, track );
    
    if( status != DEF_FDD_OK )
    {
        return( status );
    }   

    /*--------------------------------------------------------------------------*/
    /* issue read data command to read a sector                                 */
    /*--------------------------------------------------------------------------*/
    status = writeFdc0CmdReadData( drive, head, track, sector );

    /*--------------------------------------------------------------------------*/
    /* wait until irq of dma is fired                                           */
    /*--------------------------------------------------------------------------*/
    dmaWaitIrq( E_DMA_CHANNEL2 );

    /*--------------------------------------------------------------------------*/
    /* stop the motor of specified dirve                                        */
    /*--------------------------------------------------------------------------*/
    stopFdc0Motor( drive );
    
    return( status );
}



この実装例ではフローにありますようなエラー制御は省いております。。。

ディスクのフォーマット

ディスクのフォーマットはFORMAT TRACKコマンドで行います。

モーターがONするまでの300msウェイトして、DMAコントローラを初期化するまではREAD/WRITEの

フローと同じです。フォーマット処理でIDデータフィールド(シリンダー番号、トラック番号を特定するID)が

DMAコントローラによって書き込まれます。ですので、DMAコントローラにフォーマットするシリンダー番号、

ヘッド番号、セクター番号、セクターサイズを指定する必要があります。

フォーマットするデータは任意で指定できます。両方のヘッドのシリンダーをフォーマットした後は

SEEKコマンドを発行して、トラックを移動する必要があります

(フォーマットは事前シークをすることができません)

ディスクのREAD/WRITEで見てきましたように、フォーマット時にもセトリングタイムが必要となります



それでは、フォーマットのフローを見て行きましょう

フロッピーディスクのフォーマットフロー

フロッピードライブの割り込みハンドラ

フロッピードライブの割り込みハンドラを見ていきます

下記実装例はごくシンプルな実装例となります

			
/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
	Funtion     :fddInterrupt
	Input       :void
	Output      :void
	Return      :void
	Description :handlin floppy drive interrupt
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
VOID fddInterrupt( VOID )
{
    /* --------------------------------------------------------------------- */
    /*  entering interrupt                                                   */
    /* --------------------------------------------------------------------- */
    enter_interrupt( );

    /* --------------------------------------------------------------------- */
    /*  inform irq fired                                                     */
    /* --------------------------------------------------------------------- */
    fddIrqFlagSet( True );

    /* --------------------------------------------------------------------- */
    /*  inform end of interrupt to pics                                      */
    /* --------------------------------------------------------------------- */
    interrupt_done( );

    /* --------------------------------------------------------------------- */
    /*  exit interrupt                                                       */
    /* --------------------------------------------------------------------- */
    exit_interrupt( );
}



その他にフロッピードライブの割り込みは 割り込みその2割り込みその3

見てきましたように、PICの設定と、IDTの設定を行う必要があります

PICの設定

PICの設定は割り込みその3 で作りましたinitPIC関数にIRQ6のマスクを解除するように変更します


/*
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
    Funtion     :initPIC
    Input       :void
    Output      :void
    Return      :void

    Description :initialize pics
_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
*/
void initPIC( void )
{
    /* PIC 初期化関数続き	*/
    /* --------------------------------------------------------------------- */
    /* 	send IMR ot PIC                                                      */
    /* --------------------------------------------------------------------- */
    outPortByte( PORT_MASTER_PIC_IMR,
                   ( ~PIC_IMR_MASK_IRQ0 )
                 & ( ~PIC_IMR_MASK_IRQ1 )
                 & ( ~PIC_IMR_MASK_IRQ2 )
                 & ( ~PIC_IMR_MASK_IRQ6 ) );
    outPortByte( PORT_SLAVE_PIC_IMR, PIC_IMR_MASK_IRQ_ALL );
}



フロッピードライブのIDTを設定する

フロッピードライブのIDTを設定し、割り込みベクタに登録します。

IDTの登録には割り込みその1 で作りましたsetupInterruptGate関数を使用します

また、割り込みその3の IRQの割り込みベクタ一覧を見てきましたようにIRQ6は

割り込みベクタ番号38でしたので



IDTの設定するソースで次の関数呼び出し処理を追加しておきます


#define DEF_IDT_INT_NUM_IRQ6 38

setupInterruptGate( DEF_IDT_INT_NUM_IRQ6, fddInterrupt );





ここまでで、フロッピーディスクの読み書きができるようになりました。

とりあえずはOS開発で必要な最低限(データの出力、入力、ファイルの読み書き)のことができるようになりました。

次回はプラスアルファでDMAについて見ていきたいと思います

inserted by FC2 system