2016年4月14日木曜日

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, &reg, &reg);
    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, &reg, &reg, &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";
}