PC-6601用3.5インチ1DディスクのLinuxでの読み取りに関する覚え書き
LinuxでPC-6601用の1Dディスクを読み取る手順についての覚え書きです。そのものズバリの情報が意外と見つからないので、自分用のメモという意味で、調べたことや試行錯誤した結果を書き連ねてみました。66本体を経由するか、Ditt! や1DDITT などのDOSツールに頼るしか方法がないのだと思っていたのですが、Linuxも結構便利じゃないですか。SR用の1DDや、その他機種の2D/2DDを読むことも可能です。
こんな人向けです。
PCで普段Linuxが動きっぱなしなので、吸い出しの度にDitt!等のためリブートするのが面倒臭い
Ditt!を使うにはDOSが必要だが、ブート用のFDやHDD上のDOS領域は、もう鬱陶しいのでなくしてしまいたい
FDCをいじって遊ぶための叩き台が欲しい
(追記:最近のWindowsの場合、fdrawcmd.sys というデバイスドライバを入れて、NDitt やSAMdisk 、HxC Floppy Emulator Software などのツールを使えば読み取れるようです。)
(追記:マイコンを用いた3.5インチ倍密度フロッピーのUSBリード/ライト実験 というのもやってみました。)
目次
ドライブ:Y-E DATA社製 YD-702D-6238D (玄人志向 FDD-YE/S)
Linux-2.6.16.13
fdutils 5.5-20060227-1.1 (Debian GNU/Linuxのパッケージ)
一般的な内蔵型FDD、一般的なLinuxディストリビューションなら大丈夫か?USB接続のFDDではどうも無理っぽい。
フォーマットの種類は星の数ほどあるらしいが、とりあえすP6関連フォーマットや似ているものについてまとめ。FDに関する基礎知識も含めてメモ。
1D
片面倍密度。PC-6601で用いられているもの。6601の場合、35トラック16セクタ256バイトというフォーマットが標準とされている。
2D
両面倍密度。
1DD
片面倍密度倍トラック。PC-6601SRで用いられているもの。66SRの場合、80トラック16セクタ256バイトが標準。
2DD
両面倍密度倍トラック。
2HD
両面高密度倍トラック。現在普通に使われるもの。両面、1面あたり80トラック、トラックあたり18セクタ、セクタあたり512バイトで合計1440KB。PC-98で用いられた、両面77トラック8セクタ1024バイトで1232KBというのもあるが、これはFDDの回転数が異なっていて何かと話が違うらしい。
P6シリーズでは片面メディアである1D/1DDを使用するのでトラックとシリンダは結果的に同じ実体を指すのだが、両面メディアだと同じシリンダに2つのトラックが存在することになる。この覚え書きは基本的にP6用なので、他の機種に適用する場合は注意されたい。
DDという言葉は普通「倍密度倍トラック」の意味だが、たまに「Double Density=倍密度」の意味で用いられるケースもあるので注意が必要。
トラック番号(片面メディアではシリンダ番号と同じ)は0スタート、セクタ番号は1スタートが普通。
両面メディアのトラック番号は、表面→裏面→表面→…の順に割り当てられる。トラック番号=シリンダ番号×2+n (n=0 or 1)
1DD/2DDの設定で1D/2Dを読むと、本来のシリンダ番号を2倍した場所にデータがあるように見える。すなわち、偶数シリンダ(シリンダ番号=0,2,4,…)のトラックにのみデータが存在し、奇数シリンダのトラックは未フォーマットのように見える。
にもかかわらず(PC-6601で採用されている)1Dのトラック数が1DDのきっちり半分でないのは、1Dでは内側の領域を使い切っていないから。その気になればトラック数=40として使えるようで、実際我が家の6601では(機械語で)40番目のトラックも読むことができた。5インチとの互換性のための制限なのかもしれない。
最大シリンダ数は倍トラック換算で80が普通だが、より内側まで頑張って使う「オーバートラック」という技法もある。D88形式ではシリンダ0〜シリンダ81の82シリンダ分、計164トラックのデータを扱うようになっている。
3.5インチでは1Dという形式はあまり一般的でないようだが、PC-6601でこれを採用したのは5インチとの互換性を考慮した結果だろうか。
続いて、具体的なLinuxでの読み取り方法について。2HDなどメジャーなフォーマットはマイナー番号やデバイスファイルが用意されていて簡単に読めるようになっているのだが、残念ながら1D用のものは用意されていない。そこで、fdutilsパッケージのsetfdprmコマンドを使い、FDのパラメータを直接指定する。
$ setfdprm /dev/fd0 dd stretch=1 cyl=35 head=1 sect=16 ssize=256
$ cp /dev/fd0 image.dsk
などとやると1Dディスク全体のベタイメージが作成される。cpコマンドのかわりに、ddコマンドで特定のセクタだけ読むこともできる。
$ dd bs=256 skip=18 count=1 if=/dev/fd0 of=image.dsk
例えば、これでトラック1セクタ3のイメージが作成される。18セクタ分スキップするので、最初のトラック(トラック0)の全16セクタ+次のトラック(トラック1)の2セクタがスキップされ、2番目のトラックの3番目のセクタ、即ちトラック1セクタ3が読まれるわけだ。ただし、実作業では全体のイメージをHDDにコピーしてしまってからの方が楽だろう。
入出力を逆にすれば書き込みも可能だ。当時6601用として売られていたディスクでは未確認だが、現在売られている2HDディスクの識別孔にテープを貼り、6601形式で物理フォーマットして書き込むと、実機でも読み出す事ができた。
$ superformat /dev/fd0 dd stretch=1 cyl=35 head=1 sect=16 ssize=256
$ cp image.dsk /dev/fd0
setfdprm/superformatの2番目の引数のddはDouble Density=倍密度の意味なので、1D/2D/1DD/2DDすべてで必要。2HDを読む時はhdとする。
1D/2Dを扱うには、stretch=1という指定が必要。指定しないと、倍トラックのディスク扱いになる。結果、1Dだと奇数トラックが未フォーマットのディスクであるように見えるため、最初のトラック(トラック番号=0)しか読めなくなる。
残りのパラメータはシリンダ数35、片面、セクタ数16、セクタサイズ256バイトの意味。
stretchについては、man 8 setfdprmに項目だけはあるものの、説明はなかった。superformatの--stretchオプションの説明や、/usr/src/linux/drivers/block/floppy.cと/usr/include/linux/fd.hのコメントなどから類推。2^stretchごとにトラックを読むという指定らしい?
1DDなら「stretch=0 cyl=80」に変えればOK。
省略時のデフォルト引数は「hd stretch=0 cyl=80 head=2 sect=18 ssize=512」か?getfdprmコマンドで現在の設定を確認できる。
setfdprmは何度かやらないと設定されない場合があった。getfdprmが正しく返って来るまで繰り返した方がいいかも。
6601形式のファイルシステムには対応していないので、マウントは無理。
普通の2モードor3モード用のドライブでは、1D/2Dとして読み込みはできても書き込みはできないという話も見かけるのだが、手元の環境では何故か書けてしまった。ただし信頼性の問題はあるかもしれない。また、PC-98用のMS-DOSでは書き込めないようになっているらしい。
特殊なフォーマットのディスクは、以上の方法では読むことができない。例えば「マリオブラザースSPECIAL」では、トラック0は16セクタ×256バイト、トラック1以降は4セクタ×1024バイトと、トラックごとにセクタ構成が異なるので、setfdprmでの指定ではうまくいかない。幸い、LinuxのfdutilsパッケージにはFDC(Floppy Disk Controller)を直接叩く「fdrawcmd」というコマンドがあるので、これを使えばセクタ構成を確認して1セクタずつ読み出すことが可能だ。このコマンドはタイミング取りからデータの受け渡しまで自動的にやってくれるので、かなり気軽にFDCを触ることができる。
(※追記: fdrawcmdは、Linux5.18以降はデフォルトでは無効にされているそうです。 )
$ fdrawcmd [option] FDCcommand args
という形で入力すればよい。引数の数はFDC命令によって異なる。FDCからの返り値(リザルトステータスなど)は標準エラー出力に出力される。I/Oエラーが出てどうにもならなくなった場合は、
$ floppycontrol --resetnow 2
とするといいらしい。(--reset 2でないと駄目な場合もあるみたい?)以下、fdrawcmdのオプションや、FDC命令の引数などについて。普通に読むだけならば、以下の四種類のFDC命令で大体事足りるようだ。
RECALIBRATE
ヘッドをトラック0まで戻し、位置を較正する。最初に一回やっておく。オーバートラックを使った後は二度やるのが作法らしい。初期のFDCはステップパルスを77回分しか出さないのだとか。
$ fdrawcmd recalibrate 0
引数は「ヘッド番号(0〜1)×4+ドライブ番号(0〜3)」。片面ディスク(1D/1DD)でドライブが1台なら常に0。返り値の1個目はステータス、2個目はシリンダ位置なので正常終了なら0になる。
(※実際には返り値はRECALIBRATE命令自体に対するものではなく、終了後にfdrawcmdが自動的に与えるSENSE INTERRUPT STATUSという命令に対するもの。)
SEEK
目的のシリンダ位置までヘッドを移動する。
$ fdrawcmd seek 0 cylinder(0〜79)
引数の0はRECALIBRATE命令と同じ。2DD/2HD用ドライブの場合、cylinderは倍トラック換算なので、1Dのディスクに対しては読みたいトラック番号の倍の数字を指定することになる。返り値はrecalibrateと同じ。正常終了なら2個目は指定したのと同じトラック番号が返ることになる。
(※これもRECALIBRATEと同様、SENSE INTERRUPT STATUS命令に対する返り値。)
READ ID
ヘッドの直近にあるセクタの情報を取得する。
$ fdrawcmd rate=2 [short] [repeat=nrepeat] readid 0
倍密度のディスク(1D/2D/1DD/2DDすべて)を読み書きする場合は、(fdrawcmdコマンドのオプションである)rate=2を指定する。rate=1とすると5インチディスクが読めるらしいがドライブが無いので未確認。指定しないとrate=0となり2HD用になる。shortオプションとrepeatオプションは別にREAD ID命令専用ではないが、特にわかりやすくなるのでここに記した。shortは返り値を一行にまとめて表示するオプション。repeatは同じ命令を繰り返すオプション。(READ ID命令の)引数の0はRECALIBRATE/SEEKの場合と同じ。
返り値の1個目〜3個目はリザルトステータス。4個目〜7個目が見つかったセクタに記載してある情報で、それぞれシリンダ番号、ヘッド番号(1D/1DDなら常に0)、セクタ番号、セクタサイズ(128×2^Nバイト)に対応する。repeat=20ぐらいにしてみると、第6カラム(セクタ番号)がFDの回転に伴って順に増えていくのがわかる。
READ DATA
ヘッドが位置するトラックにおいて、指定されたセクタを探してデータを読む。読んだデータは標準出力に出力される。
$ fdrawcmd length=length rate=2 read 0 C H R N EOT GPL DTL > image.dsk
何バイト読むかをlengthオプションで指定しておく。1D標準フォーマットのディスクから1セクタずつ読むならlength=256。READ DATA命令の引数は以下の通り。
0 - RECALIBRATE/SEEK/READ IDの場合と同じ。
C - シリンダ番号。現在ヘッドがあるシリンダの番号を書く。ただしここではディスクに実際に書かれている番号を使うので、1Dならば0〜34となり、SEEK命令で指定したものとは倍違うので注意。(2DD/2HD用ドライブの場合)
H - ヘッド番号。0〜1。1D/1DDなら常に0。
R - セクタ番号。1D標準フォーマットでは1〜16。
N - セクタの大きさ。128×2^Nバイト。1D標準フォーマットでは1。
EOT - 最後に読み取るセクタ番号。
GPL - 連続セクタ読み取りを行う場合に参照される。GAP3からVFO SYNCを除いた値、と資料にはあるが、適切な値はよくわからない。参考ページの2HDでの例では27となっている。手元の環境で1Dを読んだ場合、0や60にすると失敗したが27だと成功したので、その付近の値なのだろう。PC-6601のROM内ルーチンでは20になっていた。1セクタずつ読むなら関係ない。
DTL - N=0の場合にはここで指定したバイト数だけ読み込めるらしい。使わない場合は255にしておく。
返り値はREAD ID命令と同じ7個である。
フォーマットのわからないFDを読み取ってD88形式で出力するシェルスクリプト(2013.4.30バージョン )を書いてみた。あくまで自分用に書いたもので、エラーチェックなどあまりしていないしバグがある可能性も高いので、扱いには注意されたい。
SEEKした後、まずREAD ID命令を100回発行し重複を除いてそのトラック内にあるセクタの一覧を作成し、それを見ながら1セクタずつ読み出すようにした。
100回もやる必要もなさそうだが、読み飛ばしが起こりうるのかどうか判断が付かなかったので多めにしてみた。
トラック内でのセクタの並び順は再現していない(常にセクタ番号が小さい順に並ぶ)。
アルゴリズムが悪いのか、シェルスクリプトなせいか、fdrawcmdの限界か、Dittなどに比べるとだいぶ遅い。速度を求めるなら、ioctlを直接呼ぶようなコードを書くべきかもしれない。
特殊なフォーマットのディスクの場合、出力は必ずしもDittなどと同じにはならない。また、エミュレータでの対応はまちまちなので、読み込ませて動くとは限らない。また、手の込んだプロテクトが使われているとたぶん再現できないと思うのだが、そういうディスクを所持していないので何とも言えない。
正常に読めないセクタに関しては、各セクタのステータスデータ部分にその旨書き込むようにした。ただし、ここはPC-88/98のDISK BIOSのステータスを書くべき部分のようなので、FDCのリザルトステータスに対応しそうな値への翻訳でしかない事に注意。
$ ./disk.sh image.d88
などとして動かす。動作条件を変える時は以下のようにスクリプト内の変数代入を書き換える。
type
1D/2D/1DD/2DD/2HDのどれか。
ncylinder
(倍トラック換算での)シリンダ数。D88形式に則りDittに倣って82シリンダ分のトラックを読むようにしてあるが、オーバートラックのデータが無いのなら減らしても構わない。6601の1Dは標準で35トラックなので、倍トラックに換算してncylinder=69とすると吸い出しが早く終わるかもしれない。
retry
エラーが検出された時に再試行する回数。retry回試しても駄目な場合は、諦めて最後の試行で読み取れたデータを使う。
drive
ドライブ番号。複数台のFDDを接続していて2台目以降を使いたい場合は変更する。
gpl
セクタ間のギャップ。このスクリプトでは1セクタずつ読んでいるので実は関係ない。
protected
ライトプロテクトの有無。0だとかかっていない状態、それ以外だとかかっている状態のイメージになる。
output
出力ファイル名をいちいち入力するのは面倒臭い、固定してしまえという場合は変更する。
seclist,img
一時ファイル名。/tmpにimg-$head-$cylinderといったファイルが大量に作られる。名前や場所を変更したい場合は変更する。
error
スクリプト内で利用する場合を除き、標準エラー出力は/dev/nullに捨てているので、見たい場合は変更する。
rm
rm=0にすると一時ファイルを消さない。
rate
rate=1にすると5インチディスクが読めるらしいが未確認。
nhead
片面ディスクなら0、両面ディスクなら1になる。なんか直感と1ずれているような気もする。
d88type
D88形式においてディスクの種類の判別に使われる値。2D(0)/2DD(0x10)/2HD(0x20)の三種類のはずだが、1DDITTで1DDを吸い出すとオリジナルの値(0x40)が書き込まれるとのこと。1DDITTで作成するイメージのフォーマット (.1dd) はD88形式とは若干異なる (表裏を交互に並べるのでなく表だけまとめて並べる) ので、それを明示するためなのだろう。このスクリプトでは出力はD88形式に従うので、1Dは2Dと、1DDは2DDと共通の値を使う事にした。もしエミュレータの対応などで不都合が出るようならば、1DDITTに合わせる必要があるかもしれない。
step
1Dの場合、倍トラック換算における奇数トラックは飛ばすようにしたが、実際には、存在しないはずのトラックでも謎のデータが読める場合がある。例えば手元のマリオブラザーズSPECIALには、倍トラック換算でのトラック1にC/H/R/N=0/0/0Eh,0Fh/1という2個の変なセクタが存在するように見える。なので正確なディスクのコピーという意味では飛ばさない方がいいのかもしれないが、たぶん隣のトラックのデータが何かの弾みで見えているだけな気がする。step=1にするとそういうのも全部読むが、エミュレータでの扱いによっては動かなくなる可能性もあるので注意。
手元の6601に入っていたドライブは、松下通信工業株式会社のJU-312というもの。
JU-312については、Disk/Trend ReportのFlexible Disk Drivesについての1984年版、spec-35 (131ページ) に少しだけ記載があるのを見つけた。
サイズがやや小さい後継機種、JU-313/314というのもあるようだ。
JU-313についてはコネクタ配置の資料 (PDF ) が見つかった。たぶんJU-312も同じなのではないか。一般的な配置と思っていいのかな。
Disk/Trend Reportによれば、最後期まで製造されていた1D/2Dドライブとしては、Brother FB300やY-E Data YD-625B等があるらしい。
YD-625については、 http://ja1wby.art.coocan.jp/hamg/7-fm7-fdd/2-yd-625.html が詳しい。
手元の6601SRに入っていたドライブは、チノンのF-353。
トップ > 雑多なもの > LinuxでのPC-6601用3.5インチ1Dディスク読み取りに関する覚え書き