Neko Project 21/W 専用SCSIの仕様

Neko Project 21/W ver0.86 rev95β3以降は、PIO転送でとてつもなく遅いIDEの代替としてNeko Project 21/W 専用SCSIを実装しています。 既存のSCSIドライバを使用出来るようにするという方法は正統な気もしますがかなり大変な上にベストパフォーマンスにはならないので、いっそのこと独自にドライバを書いて最速を目指すことにしました。

Neko Project 21/W 専用SCSIということなので当然ながら実機では使えません。 エミュレータ専用の高速ストレージとお考えください。

使いたいだけであれば、なんかくれページから「Neko Project 21/W 専用SCSIドライバ for Windows NT」としてドライバをダウンロード出来ます。

これ以降はNeko Project 21/W 専用SCSIの仕様について説明します。

np21wソースのcbus\scsiio.cやnp2tool\npstor\npstor.cも参照してください。

BIOSレベルでの処理

Neko Project 21/W 専用SCSIはBIOSレベルでは従来通り普通の33C93なSCSIデバイスのように見えます。

したがってBIOSレベルでは高速化の恩恵は受けられません。 I/Oポートベースの処理に切り替わった後から効果が得られます。 一方、BIOSレベルの挙動が従来通りなことで、ブートデバイスとして問題なく使用出来ます。

I/Oポート

Neko Project 21/W 専用SCSIは以下の専用のI/Oポートを使用します。 33C93関係のI/Oポートはアクセス出来ますが全て無意味です。

普通のDMA転送ではなく処理乗っ取りで実装されているため、IRQやDMAは使用しません。

I/Oポート役割
7EAh猫本体へ読み書きさせるメモリへのアドレス(仮想メモリアドレス・Non Paged Pool)を書き込む。読み取りは常時98(10進数)
7EBh猫本体に処理を実行させるためのポート。読み取りは常時21(10進数)

これらのポートから読み取られる値は98と21(いずれも10進数)ですので、簡易的な存在確認に用いることができます。 厳密に判定したい場合はNeko Project IIシステムポートのNP2コマンドもご利用ください。

Neko Project 21/W 専用SCSIの処理の実行方法

Neko Project 21/W 専用SCSIは完全にエミュレータを前提とした方法で動作します。

以下の手順で入出力データを用意し、特定のI/Oポートに値を書き込むと、np2本体が仮想マシンの処理を奪い取って代わりにデータを読み書きし、再度仮想マシンに制御を戻します。 したがって、仮想マシン内からはI/Oポートに書き込んだ瞬間にデータが処理されたように見えます。

Neko Project 21/W 専用SCSIでの処理実行は以下の手順で行います。

  1. NP2STOR_INVOKEINFO構造体を用意し、Neko Project 21/W 専用SCSIの呼び出しパラメータを書き込みます。
  2. I/Oポート7EAhにNP2STOR_INVOKEINFO構造体のアドレスを1byteずつ書き込みます。
  3. I/Oポート7EBhに0x98→0x01の順で書き込みます。書き込みが終わった瞬間に処理が実行され結果が返ります。
  4. 渡したNP2STOR_INVOKEINFO構造体の中身に基づいて成否判定などを行います。

データ入出力に使用する構造体の中身

NP2STOR_INVOKEINFO構造体の中身

NP2STOR_INVOKEINFO構造体は次のような内容です。


typedef struct
{
	ULONG version; // Neko Project 21/W 専用SCSIバージョン番号 現在は1のみ
	ULONG cmd; // Neko Project 21/W 専用SCSIコマンド番号NP2STOR_INVOKECMD_xxx
	PSCSI_REQUEST_BLOCK srbAddr; // SCSI_REQUEST_BLOCK構造体へのポインタ(仮想メモリアドレス)
} NP2STOR_INVOKEINFO;

cmdフィールドは実行するコマンドの種類を表しますが、現在のところ次の2種類のみが存在します。


#define NP2STOR_INVOKECMD_DEFAULT	0x00 // 20MB書き込む毎にSRB_STATUS_BUSYを返す
#define NP2STOR_INVOKECMD_NOBUSY	0x01 // SRB_STATUS_BUSYを返さない

NP2STOR_INVOKECMD_DEFAULTはWin2000においてEDB*.logが大量に生成されてディスクを埋める問題を回避するために、定期的にSRB_STATUS_BUSYを返します。 SRB_STATUS_BUSYが来た場合はリトライする必要があります。

NP2STOR_INVOKECMD_NOBUSYは上記の動作をせずに素直に書き込みができます。 ただし、プレリリース版Neko Project 21/W ver0.86 rev95β3では動作しません(気にしなくていいと思いますが)。

PSCSI_REQUEST_BLOCKはWindows NTのSCSIミニポートドライバで使用されるSCSI_REQUEST_BLOCK構造体へのポインタです。 こちらも仮想メモリアドレス・Non Paged Poolである必要があります。

SCSI_REQUEST_BLOCK構造体の中身

SCSI_REQUEST_BLOCK構造体はWindows 2000 DDKのそれと同じものです。 MSDNで見られますのでここには書きません。

たくさんフィールドがありますが、Neko Project 21/W 専用SCSIで使用されているのはごく一部です。 以下のフィールドのみをセットすれば残りは0で問題ありません。

フィールド中身の意味
Functionミニポート機能番号。SRB_FUNCTION_EXECUTE_SCSI(0x00)でOK。
SrbStatusここに処理結果のステータスが返される。自分で設定する必要はない。SRB_STATUS_SUCCESS(0x01)で成功。SRB_STATUS_BUSYが返ってきたときはリトライする。
PathId対象デバイスの指定。0しかないので0に設定する(デバイスがない場合はSrbStatusにSRB_STATUS_NO_DEVICEが返る)
TargetId対象デバイスの指定。0~3がnp2メニューのSCSI#に対応する(デバイスがない場合はSrbStatusにSRB_STATUS_NO_DEVICEが返る)
Lun対象デバイスの指定。0しかないので0に設定する(デバイスがない場合はSrbStatusにSRB_STATUS_NO_DEVICEが返る)
DataTransferLengthDataBufferのサイズをバイト単位で書き込む。猫本体の処理が完了した後はDataBufferへ書き込まれたのデータサイズが格納される。
DataBuffer猫本体とデータをやりとりするためのバッファへのポインタ。仮想メモリアドレス・Non Paged Poolである必要があります。
CdbSCSIコマンドのCommand Descriptor Block。

Neko Project 21/W ver0.86 rev95β3時点で対応しているSCSIコマンドは以下のもののみです。

コマンド備考
SCSIOP_TEST_UNIT_READY常時成功
SCSIOP_INQUIRY一部のフィールドのみ結果を返す(np21/wソース参照)
SCSIOP_READ_CAPACITY
SCSIOP_READ
SCSIOP_WRITE
SCSIOP_READ6
SCSIOP_WRITE6
SCSIOP_SEEK常時成功
SCSIOP_VERIFYLBA範囲外チェックのみ実施
SCSIOP_MODE_SENSE全ページを返すモードのみ実装 ページ指定不可

具体的なSCSI_REQUEST_BLOCK構造体の中身の例 ~SCSIOP_INQUIRYの場合~

例としてSCSIOP_INQUIRYを送る場合の中身を以下に示します。

フィールド中身の意味
FunctionSRB_FUNCTION_EXECUTE_SCSI(0x00)
SrbStatus処理結果がSRB_STATUS_SUCCESS(0x01)なことを確認
PathId0
TargetId0(np2メニューのSCSI #0)
Lun0
DataTransferLengthINQUIRYDATA構造体のサイズ。バッファが足りない場合は途中までしか格納されない。
DataBufferINQUIRYDATA構造体へのポインタ
Cdb[0]SCSIOP_INQUIRY(0x12)

具体的なSCSI_REQUEST_BLOCK構造体の中身の例 ~SCSIOP_READ_CAPACITYの場合~

例としてSCSIOP_READ_CAPACITYを送る場合の中身を以下に示します。

フィールド中身の意味
FunctionSRB_FUNCTION_EXECUTE_SCSI(0x00)
SrbStatus処理結果がSRB_STATUS_SUCCESS(0x01)なことを確認
PathId0
TargetId0(np2メニューのSCSI #0)
Lun0
DataTransferLengthREAD_CAPACITY_DATA構造体のサイズ
DataBufferREAD_CAPACITY_DATA構造体へのポインタ
Cdb[0]SCSIOP_READ_CAPACITY(0x25)

具体的なSCSI_REQUEST_BLOCK構造体の中身の例 ~SCSIOP_WRITEの場合~

例としてSCSIOP_WRITEを送る場合の中身を以下に示します。

フィールド中身の意味
FunctionSRB_FUNCTION_EXECUTE_SCSI(0x00)
SrbStatus処理結果がSRB_STATUS_SUCCESS(0x01)なことを確認 SRB_STATUS_BUSY(0x05)の場合は同じコマンドをリトライする
PathId0
TargetId0(np2メニューのSCSI #0)
Lun0
DataTransferLength書き込むデータのサイズ
DataBuffer書き込むデータへのポインタ
Cdb[0]SCSIOP_WRITE(0x2A)
Cdb[2], Cdb[3], Cdb[4], Cdb[5]書き込み先セクタLBA(ビッグエンディアン)
Cdb[7], Cdb[8]書き込むセクタ数(ビッグエンディアン)

資料集に戻る

トップに戻る