MSXのフロッピーディスクアクセス低レベルI/O（TC8566AF編）

# はじめに

MSXのフロッピーディスクアクセスは、基本的にディスク用のBIOSを介してアクセスするようにとのことで、MSX用のユーザプログラムは基本的にそのようにプログラムを作る必要があると思います。

BIOSの仕様については[MSX Datapack wiki](http://ngs.no.coocan.jp/doc/wiki.cgi/datapack)に記載されてませんが、MSX Resource Center の方には記載がありました。

https://www.msx.org/wiki/Disk-ROM_BIOS

ただし、（飽くまでも規格ではなく実態ベースで）低レベルI/Oがどうなっているのか興味があったので、blueMSXのソースコードを解析して調べた低レベルI/Oの内容を記します。

# FDC Low Level I/O Map

|Maker|I/O method|R/W or I/O range|Internal H/W|
|:-:|:-:|:-:|:-:|
|Microsol|System Port|0xD0〜0xD4|WD2793|
|National|Memory Mapped|0x3F80 〜 0x3F87|WD2793|
|Philips|Memory Mapped|0x3FF8 〜 0x3FFF|WD2793|
|Svi328|User Port|0x30〜0x34, 0x38|WD2793|
|Svi707|Memory Mapped|0x3FB8 〜 0x3FBF|WD2793|
|Svi738|Memory Mapped|0x3FB8 〜 0x3FBC|WD2793|
|Toshiba|Memory Mapped|0x3FF0 〜 0x3FFx|TC8566AF|

※アドレスは `MOD 0x4000` です

MSX実機からBIOSを吸い出した時の 16KB の DISK.ROM がディスク BIOS のようです。

MSXの規格としてディスクI/Oの仕様が定められていないらしく、各社で色々とアクセスI/Oの仕様が異なるようですが、繋ぎ先のFDCは概ね WD2793 or TC8566AF のようです。（[こちら](https://www.msx.org/forum/msx-talk/general-discussion/disk-copy-protections)によるとNEC μPD765 もあるようですがblueMSXの実装を眺めた限り、μPD765 の MSX 向けマッパーは見当たりませんでした）

どちらかといえば WD2793 の方がメジャー（?）のように見えますが、検証機種の BIOS が TC8566AF だったので **以下 TC8566AF の低レベルI/O仕様の調査結果** になります。

# TC8566AF

## Datasheet

https://www.alldatasheet.jp/datasheet-pdf/pdf/111838/TOSHIBA/TC8566AF.html

## FDC I/O

ページ1にDISK.ROMのスロットを割り当てた状態で絶対アドレス 0x7FF8〜0x7FFB へロードストア（LD）をすることで、TC8566AFのレジスタを読み書きすることができるようです。

|Address|R/W|Feature|
|:-:|:-:|:-|
|0x3FF8|W|`レジスタ2` を更新 (write only)|
|0x3FF9|W|`レジスタ3` を更新 (write only)|
|0x3FFA|R/W|`レジスタ4` を参照・更新|
|0x3FFB|R/W|`レジスタ5` を参照・更新|

### 0x3FF8 - レジスタ2 - ドライブ選択 (Write Only)

|Bit-7|Bit-6|Bit-5|Bit-4|Bit-3|Bit-2|Bit-1|Bit-0|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|?|?|LED2|LED1|?|DriveId|DriveId|DriveId|

### 0x3FF9 - レジスタ3 - 不明 (Write Only)

実質、何も起こらない?

### 0x3FFA - レジスタ4 - ステータスレジスタ （実質ReadOnly）

※FDC では更新可能になっているがTC8566AFでは更新は無視（実質ReadOnly）

|Bit-7|Bit-6|Bit-5|Bit-4|Bit-3|Bit-2|Bit-1|Bit-0|
|:-:|:-:|:-:|:-:|:-:|:-:|:-:|:-:|
|RQM|DIO|NDM|CB|D3B|D2B|D1B|D0B|

- RQM; Request for Master
  - 1: FDCがデータ受け付け中、またはデータ送信準備完了
  - 0: FDCがデータ受け取り不可、またはデータ送信準備未完了
- DIO; Data Input/Output
  - 1: FDC→ホスト方向転送
  - 0: ホスト→FDC方向転送
- NDM; NonDMA Mode
  - 1: FDCがNonDMAモードでデータ転送中でサービスを要求している
  - 0: FDCのサービス要求なし
- CB; FDC Busy
  - 1: FDCがコマンドを実行中
  - 0: FDCはコマンドを受け付け可能
- D3B〜D0B; FDD 3~0 Busy
  - 1: FDD 0〜3 がシーク中
  - 0: FDD 0〜3 がシーク中ではない

> 各ステータスの意味は以下から参酌しました:bow:
> https://www.webtech.co.jp/company/doc/undocumented_mem/io_fdd.txt
>
> 上記は NEC μPD765 (PC-98などのFDC) ですが、ステータスレジスタの内容は TC8566AF と同じのようです。

### 0x3FFB - レジスタ5 - データI/O

このレジスタI/Oで、コマンドの種類とパラメタデータを書き込み後、転送データの読み書きを行います。

#### (1) Idle Phase

実行するコマンドの種類を書き込む

```c++
if ((value & 0x1f) == 0x06) command = CMD_READ_DATA;
if ((value & 0x3f) == 0x05) command = CMD_WRITE_DATA;
if ((value & 0x3f) == 0x09) command = CMD_WRITE_DELETED_DATA;
if ((value & 0x1f) == 0x0c) command = CMD_READ_DELETED_DATA;
if ((value & 0xbf) == 0x02) command = CMD_READ_DIAGNOSTIC;
if ((value & 0xbf) == 0x0a) command = CMD_READ_ID;
if ((value & 0xbf) == 0x0d) command = CMD_FORMAT;
if ((value & 0x1f) == 0x11) command = CMD_SCAN_EQUAL;
if ((value & 0x1f) == 0x19) command = CMD_SCAN_LOW_OR_EQUAL;
if ((value & 0x1f) == 0x1d) command = CMD_SCAN_HIGH_OR_EQUAL;
if ((value & 0xff) == 0x0f) command = CMD_SEEK;
if ((value & 0xff) == 0x07) command = CMD_RECALIBRATE;
if ((value & 0xff) == 0x08) command = CMD_SENSE_INTERRUPT_STATUS;
if ((value & 0xff) == 0x03) command = CMD_SPECIFY;
if ((value & 0xff) == 0x04) command = CMD_SENSE_DEVICE_STATUS;
```

#### (2) Command Phase

コマンドパラメタを書き込む（コマンドの種類によって何回か繰り返しwrite）

#### (3) Data Transfer Phase

データ読み込み or 書き込みを行う

# まとめ

DISK.ROM のスロットレイアウトはページ1（0x4000〜0x7FFF）のようなので、ENASLT等で DISK.ROM が入っているスロットをページ1へ割り当て後、以下のような形でLDすればフロッピーディスクにアクセスできそうです。（※FDCがTC8566AFの機種に限る）

- `LD (0x7FF8), xx` でドライブ選択
- `LD (0x7FFB), xx` でコマンド選択後 
- `LD xx, (0x7FFB)` でデータ転送（FDC→ホスト）
- `LD (0x7FFB), xx` でデータ転送（ホスト→FDC）
- 必要に応じて `LD xx, (0x7FFA)` でステータスチェック

もちろん、MSX向けのソフトを書く場合は基本的に[BIOS](https://www.msx.org/wiki/Disk-ROM_BIOS)経由でコールすべきだと思います。

# 編集後記

低レベルI/Oの調査動機としては、自作のMSX2+エミュレータをFDD対応してみるためで、上記調査内容から無事TC8566AFエミュレータを作って対応することができました。

無事、ディスク専用のゲームが動かせる感じになりました。

|ディスク専用ゲーム動作の様子1<br>イース2 / (C)日本ファルコム|ディスク専用ゲーム動作の様子2<br>英雄伝説 / (C)日本ファルコム|
|:-:|:-:|
|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2161417/33953427-7bc5-7597-1243-fc91843e8a41.png)|![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2161417/1cec021c-778f-ba3d-8cbf-c0792b7bd686.png)|

MSX2+で対応するに当たって、ディスクBIOS（DISK.ROM）のスロット 3-2 のページ1（0x4000〜0x7FFF）に配置しているのですが（下図の `DOS` の部分）、
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2161417/123aaebf-38c1-c1c1-a630-2d2ce3fb160d.png)
__3-2のページ 0, 2, 3 を空状態（0x00で埋めた状態）にしつつ、それぞれのページアドレスの MOD 0x4000 した時の 0x3FFx を FDC レジスタへマッピングをする必要がある__ ようです。

この点に最初気づかず「あれ〜、起動しないな〜？」と1日ぐらい迷ってしまいましたw

MSXの規格としてディスクの存在が曖昧なので、MSX2以降はエミュレータを作ろうとすると中々大変かもしれません。ディスクに限らず（例えばV9938や拡張スロットレジスタの細かい挙動など）ドキュメント化されていない仕様や、素人目に見てどう考えてもバグのような仕様（V9938なら縦スクロールでスプライトが連動したりetc）もあり、中々楽しいです。エミュレータを書いてみると「当時のプログラマーは大変だったんだろうなぁ」と思いを馳せることができるので、中々楽しいです。