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も参照してください。
Neko Project 21/W 専用SCSIはBIOSレベルでは従来通り普通の33C93なSCSIデバイスのように見えます。
したがってBIOSレベルでは高速化の恩恵は受けられません。 I/Oポートベースの処理に切り替わった後から効果が得られます。 一方、BIOSレベルの挙動が従来通りなことで、ブートデバイスとして問題なく使用出来ます。
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は完全にエミュレータを前提とした方法で動作します。
以下の手順で入出力データを用意し、特定のI/Oポートに値を書き込むと、np2本体が仮想マシンの処理を奪い取って代わりにデータを読み書きし、再度仮想マシンに制御を戻します。 したがって、仮想マシン内からはI/Oポートに書き込んだ瞬間にデータが処理されたように見えます。
Neko Project 21/W 専用SCSIでの処理実行は以下の手順で行います。
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構造体は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が返る) |
DataTransferLength | DataBufferのサイズをバイト単位で書き込む。猫本体の処理が完了した後はDataBufferへ書き込まれたのデータサイズが格納される。 |
DataBuffer | 猫本体とデータをやりとりするためのバッファへのポインタ。仮想メモリアドレス・Non Paged Poolである必要があります。 |
Cdb | SCSIコマンドの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_VERIFY | LBA範囲外チェックのみ実施 |
SCSIOP_MODE_SENSE | 全ページを返すモードのみ実装 ページ指定不可 |
例としてSCSIOP_INQUIRYを送る場合の中身を以下に示します。
フィールド | 中身の意味 |
---|---|
Function | SRB_FUNCTION_EXECUTE_SCSI(0x00) |
SrbStatus | 処理結果がSRB_STATUS_SUCCESS(0x01)なことを確認 |
PathId | 0 |
TargetId | 0(np2メニューのSCSI #0) |
Lun | 0 |
DataTransferLength | INQUIRYDATA構造体のサイズ。バッファが足りない場合は途中までしか格納されない。 |
DataBuffer | INQUIRYDATA構造体へのポインタ |
Cdb[0] | SCSIOP_INQUIRY(0x12) |
例としてSCSIOP_READ_CAPACITYを送る場合の中身を以下に示します。
フィールド | 中身の意味 |
---|---|
Function | SRB_FUNCTION_EXECUTE_SCSI(0x00) |
SrbStatus | 処理結果がSRB_STATUS_SUCCESS(0x01)なことを確認 |
PathId | 0 |
TargetId | 0(np2メニューのSCSI #0) |
Lun | 0 |
DataTransferLength | READ_CAPACITY_DATA構造体のサイズ |
DataBuffer | READ_CAPACITY_DATA構造体へのポインタ |
Cdb[0] | SCSIOP_READ_CAPACITY(0x25) |
例としてSCSIOP_WRITEを送る場合の中身を以下に示します。
フィールド | 中身の意味 |
---|---|
Function | SRB_FUNCTION_EXECUTE_SCSI(0x00) |
SrbStatus | 処理結果がSRB_STATUS_SUCCESS(0x01)なことを確認 SRB_STATUS_BUSY(0x05)の場合は同じコマンドをリトライする |
PathId | 0 |
TargetId | 0(np2メニューのSCSI #0) |
Lun | 0 |
DataTransferLength | 書き込むデータのサイズ |
DataBuffer | 書き込むデータへのポインタ |
Cdb[0] | SCSIOP_WRITE(0x2A) |
Cdb[2], Cdb[3], Cdb[4], Cdb[5] | 書き込み先セクタLBA(ビッグエンディアン) |
Cdb[7], Cdb[8] | 書き込むセクタ数(ビッグエンディアン) |