LSI C-86 で PC-9801のDISK BIOSを使ってみる練習 READ DIAGNOSTIC
READ DIAGNOSTICコマンドを発行して、読んだデータとINDEX信号直後のセクタIDを表示する
/*
PC-9801フロッピーディスクBIOSを使ってみるサンプル
READ DIAGNOSTICコマンドでINDEX信号直後のセクタ番号を調べる。
FDCの動作を正確に再現するPC-9801エミュレータは少ないので、エミュレータで動作確認をする場合は注意を要する。
実機では期待通りの動きになった。
LSI C-86 Ver.3.30 試食版用
このソースコードは CC0 パブリック・ドメイン提供です。
https://creativecommons.org/publicdomain/zero/1.0/deed.ja
2016年1月28日 佐藤恭一 kyoutan.jpn.org
*/
/*
メモ
LSIC86試食版の dos.h で定義されている8086のレジスタを格納する構造体
Microsoft C とはセグメントレジスタの所が少し違う
TURBO C では擬似変数を使う
union REGS {
struct XREGS {
unsigned short ax, bx, cx, dx, si, di, bp, ds, es, flags, cflag;
} x;
struct HREGS {
unsigned char al, ah, bl, bh, cl, ch, dl, dh;
} h;
};
struct SREGS {
unsigned short es, cs, ss, ds;
};
DISK BIOSコマンド識別コード
****----
||||++++- コマンド
|||+----- SEEK 1:シークを行う 0:現在のトラック
||+------ ~r 1:リトライしない 0:8回リトライする
|+------- MF 1:MFM 0:FM
+-------- MT 1:マルチトラック(同一シリンダの両面トラックのみ) 0:シングルトラック
デバイスタイプユニット番号
****00**
|||| ++- ユニット番号 (0-3)
|+++----- 000:固定ディスク 001:1MB/640K両用 111:640K
+-------- アクセスモード 0:640K 1:1M
1M/640K両用ドライブのデバイスNO
1M FD 0x9*
640K FD 0x1*
セクタ長
0:128
1:256
2:512
3:1024
リザルトステータス
ST0
00000000
||||||++- US Unit Select (ユニット0~3)
|||||+--- HD Head Address ヘッド番号
||||+---- NR Not Ready
|||+----- EC Equipment Chech ドライブからFault信号を受け取った / RECALIBRATEコマンドでトラックが見つからない
||+------ SE Seek End SEEK/RECALIBRATEコマンド実行終了
++------- IC interrupt code 割り込み要因
00:Normal Terminate コマンド正常終了
01:Abnormal Terminate コマンド異常終了
10:Invalid Command 与えられたコマンドが無効
11:Attention Interrupt デバイスに状態遍移があった
ST1
0-00-000
| || ||+- MA Missing Address Mark
| || |+-- NW Not Writable
| || +--- ND No Data 指定セクタが見つからない
| |+----- OR Over Run データ転送が間に合わなかった
| +------ DE Data Error IDやデータにCRCエラーを検出した
+-------- EN End Of Cylinder コマンドのEOTパラメータで与えた最終セクタを超えてリード/ライトしようとした
ST2
-000000
|||||+- MD Missing Address Mark データアドレスマークが見つからない
||||+-- BC Bad Cylinder ST1のNDビットが1の時、IDのCが0xFFである(READ DIAGNOSを除く)
|||+--- SN Scan Not Satisfield SCANコマンドで最終セクタまで条件を満足しない
||+---- SH Scna Equal Hit SCANコマンドでEqual条件を満たした
|+----- DD Data Error in Data Field データCRCエラーを検出した
+------ CM Control Mark 1:READ DATEでDDAMを検出 0:READ DELETED DATAでDAMを検出
*/
#include <stdio.h>
#include <dos.h>
/*システム共通域の DISK_RESULT*/
struct FDC_RESULT
{
unsigned char ST0, ST1, ST2, C, H, R, N,NCN;
};
struct DISK_RESULT
{
struct FDC_RESULT D0,D1,D2,D3;
};
#define result (*(volatile struct DISK_RESULT far *)0x00000564)
#define DISKBIOS 0x1B
#define BUFFSIZE (16*1024)
unsigned char BUFF[BUFFSIZE];
char *errmsg(unsigned char ah);
void main()
{
union REGS reg;
struct SREGS seg;
unsigned short pos;
reg.h.ah=0x07; /* RECALIBRATE */
reg.h.al=0x90; /* DRIVE #0 */
int86(DISKBIOS, ®, ®);
if(0!=reg.x.cflag)
{
puts("RECALIBRATE ERROR");
puts(errmsg(reg.h.ah));
return;
}
reg.h.ah=0x42; /*AH 01000010 READ DIAGNOSTIC インデックス信号直後のセクタからデータを読む。*/
/* ||+---- SEEK READ DATAと違い、IDのエラー、データのエラーがあっても処理を続行する。*/
/* |+----- ~r リザルトステータスはコマンド中に起きたすべてのエラーの論理和になる。*/
/* +------ MF */
reg.h.al=0x90; /*AL デバイスタイプユニット番号*/
reg.x.bx=1024; /*BX DTL*/
reg.h.cl=0; /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
reg.h.dh=0; /*DH ヘッド番号*/
reg.h.dl=1; /*DL セクタ番号*/
reg.h.ch=3; /*CH セクタ長コード (合わないセクタ長コードを指定するとエラー)*/
segread(&seg); /* セグメントレジスタの値を取得 */
seg.es=seg.ds; /*ES バッファの先頭アドレス[セグメント]*/
reg.x.bp=(unsigned short)BUFF; /*BP バッファの先頭アドレス[オフセット]*/
int86x(DISKBIOS, ®, ®, &seg); /*セグメントレジスタも設定するのでint86x*/
if(0==reg.x.cflag)
{
/*正常終了*/
for(pos=0; pos!=1024; pos++)
{
printf("%02X ",BUFF[pos]);
if(0x0F==(pos&0x0F)) puts(""); /*16個ごと改行*/
}
printf("ST0 ST1 ST2 C H R N\n %02X %02X %02X %d %d %d %d"
,result.D0.ST0
,result.D0.ST1
,result.D0.ST2
,result.D0.C
,result.D0.H
,result.D0.R - 1 /*読み込んだ次のセクタ番号を示しているので引き算*/
,result.D0.N );
}
else
{
/*異常終了*/
puts(errmsg(reg.h.ah));
}
}
char *errmsg(unsigned char ah)
{
switch(ah & 0xF8)
{
case 0x08:
return "Corrected data";
case 0x38:
return "Illegal disk Addres";
case 0x88:
return "Direct access an alternate track";
case 0xB8:
return "Data error";
case 0xC8:
return "Seek error";
case 0xD8:
return "Not read the alternate track";
}
switch(ah & 0xF0)
{
case 0x20:
return "DMA Boundary";
case 0x30:
return "End of cylinder";
case 0x40:
return "Equipment check over run";
case 0x60:
return "Not ready";
case 0x70:
return "Not writable";
case 0x80:
return "Error 0x80";
case 0x90:
return "Time out";
case 0xA0:
return "ID CRC error";
case 0xB0:
return "DATA CRC error";
case 0xC0:
return "No data (ID not found)";
case 0xD0:
return "Bad cylinder";
case 0xE0:
return "Missing address mark (ID)";
case 0xF0:
return "Missing address mark (DATA)";
}
return "Unknown error";
}