LSI C-86 で PC-9801のDISK BIOSを使ってみる練習 セクタの並びを調べる

READ DIAGNOSTICコマンドとREAD IDコマンドを使って、セクタの並びを表示する。
エミュレータで実行する場合、D88などのセクタの並びが保存されるディスクイメージでなければ正常に動作しません。

実行してみた所。1.2MB 2HD MS-DOSフォーマット

640KB 2DD X1のHu-BASICフォーマット。手近にセクタが順番に並んでいないフロッピーディスクが無くて、実行しても面白味がない。

PC-9801DISK BASICのフロッピーがインターリーブしていました。
しかも、シリンダ0のヘッド0のみ FM 方式です。

シリンダ0ヘッド0 以外は普通に MFM でした。

つまらないサンプルプログラムですが、なにかの役に立つかもしれません。




readsseq.c

/*
PC-9801フロッピーディスクBIOSを使ってみるサンプル
フロッピーディスクのセクタシーケンスを調べる。
FDCの動作を正確に再現するPC-9801エミュレータは少ないので、エミュレータで動作確認をする場合は注意を要する。
LSI C-86 Ver.3.30 試食版用
lcc -o readsseq.exe readsseq.c diskbios.c のようにしてコンパイルすればよい

このソースコードは CC0 パブリック・ドメイン提供です。
https://creativecommons.org/publicdomain/zero/1.0/deed.ja

2016年1月29日 2016年5月17日 佐藤恭一 kyoutan.jpn.org
*/
#include <stdio.h>
#include "diskbios.h"

#define SECTMAX   32 /*最大セクタ数 1セクタ256バイトの時26なので余裕を見て32くらい*/
#define BUFFSIZE  (8*1024)
unsigned char BUFF[BUFFSIZE];

int main()
{
    unsigned short count,startpos;
    unsigned char drive,mod,c,h;
    unsigned char sechead,seccount;

    drive=0;    /*ドライブ番号 0-3*/
    c=0;        /*シリンダ番号*/
    h=0;        /*ヘッド番号 0-1*/

    /* 0シリンダに移動 */
    if(! track00(FD2HD | drive))
    {
        /*エラー*/
        puts("RECALIBRATE ERROR");
        return FALSE;
    }

    /* ディスクのタイプを調べる FM/MFM 2DD/2HD 48tpi/96tpi */
    if(! (mod=sectsence(drive, c, h)))
    {
        puts("ID read error");
        return FALSE;
    }

    if(0x80 & mod) printf("2HD ");
    else printf("2DD ");
    drive = drive | (FD2HD & mod);  /* ドライブ番号に 2HD/2DD の指定を追加 */

    if(0x40 & mod) printf("MFM ");
    else printf("FM ");
    mod = FDMFM & mod;

    /*READDIAG で先頭セクタの番号を調べる*/
    if(!secthead(drive, mod, h, BUFF))
    {
        puts(errmsg(BUFF[0]));
        return FALSE;
    }
    sechead=BUFF[0];    /*先頭のセクタ番号*/
    printf(" head of the sector : %d \n", sechead);

    /*READID で1トラック分のセクタのIDを調べる*/
    for(count = 0; count != (SECTMAX - 1); count++)
    {
        if(! readid(drive, mod, h, &BUFF[count * 4]))
        {
            printf("ID read error %d ", count);
            puts(errmsg(BUFF[0]));
            return FALSE;
        }
    }

    /*1トラックに何セクタあるか数える*/
    for(seccount = 1; seccount != (SECTMAX - 1); seccount++)
    {
        if(BUFF[2] == BUFF[(seccount * 4) + 2]) break;
    }
    printf("%d secter/track   ", seccount);

    /*すべてのセクタ長コードが一致するか*/
    for(count = 0; count != seccount; count++)
    {
        if(BUFF[3] != BUFF[(count * 4) + 3])
        {
            /*一致しない*/
            puts("\nUnusual format : Sector length is not unified");
            return FALSE;
        }
    }
    printf("%d byte/sec \n", sectlength(BUFF[3]));

    /*先頭から順にバッファを整頓*/
    /*先頭まで読み飛ばす*/
    for(count=0; count != (SECTMAX - 1); count++)
    {
        if(sechead == BUFF[(count * 4) + 2]) break;
    }
    startpos = count * 4;
    /*バッファ内コピー (整頓)*/
    for(count=0; count != (seccount * 4); count++)
    {
        BUFF[count] = BUFF[startpos + count];
    }

    /*先頭セクタからIDを表示*/
    puts(" C  H  R  N");
    for(count = 0 ; count != (seccount * 4); count += 4)
    {
        printf("%2d %2d %2d %2d \n", BUFF[count + 0]
                                   , BUFF[count + 1]
                                   , BUFF[count + 2]
                                   , BUFF[count + 3]);
    }

    return TRUE;
}


diskbios.h

/*
PC-9801 DISK BIOS を使う関数
FDCの動作を正確に再現するPC-9801エミュレータは少ないので、エミュレータで動作確認をする場合は注意を要する。
LSI C-86 Ver.3.30 試食版用ですが、DOS用コンパイラならどれもだいたい同じ。

このソースコードは CC0 パブリック・ドメイン提供です。
https://creativecommons.org/publicdomain/zero/1.0/deed.ja

2016年2月1日 2016年5月18日 佐藤恭一 kyoutan.jpn.org
*/
#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 DISKSTAT  (*(volatile struct DISK_RESULT far *)0x00000564)

#define FALSE     0
#define TRUE      (!FALSE)
#define DISKBIOS  0x1B
#define FD2HD     0x90  /*デバイスタイプ指定*/
#define FD2DD     0x10  /*デバイスタイプ指定*/
#define FDMFM     0x40  /*コマンド識別*/
#define FDFM      0x00  /*コマンド識別*/
#define MODE2D    0     /*モード切替 48tpi*/
#define MODE2DD   1     /*モード切替 96tpi*/

unsigned char diskinit(unsigned char device);
unsigned char drvmode(unsigned char device      /* デバイス番号 0~3 */
                     ,unsigned char mode);      /* モード MODE2D/MODE2DD */
unsigned char diskmode(unsigned char mode);
unsigned char disksense(unsigned char device
                       ,unsigned char *errcode);
unsigned char track00(unsigned char device);
unsigned char seek(unsigned char device
                  ,unsigned char C);            /* シリンダ番号 */
unsigned char readid(unsigned char device
                    ,unsigned char MF           /* MFM/FM */
                    ,unsigned char H            /* ヘッド番号 */
                    ,unsigned char *buff);      /* IDを書き込むアドレス */
unsigned char readdata(unsigned char device
                      ,unsigned char MF         /* MFM / FM */
                      ,unsigned char C          /* シリンダ番号 */
                      ,unsigned char H          /* ヘッド番号 */
                      ,unsigned char R          /* セクタ番号 */
                      ,unsigned char N          /* セクタ長コード */
                      ,unsigned char *buff);    /* 出力を書き込むアドレス */
unsigned char readdeleted(unsigned char device
                         ,unsigned char MF      /* MFM / FM */
                         ,unsigned char C       /* シリンダ番号 */
                         ,unsigned char H       /* ヘッド番号 */
                         ,unsigned char R       /* セクタ番号 */
                         ,unsigned char N       /* セクタ長コード */
                         ,unsigned char *buff); /* 出力を書き込むアドレス */
unsigned char verify(unsigned char device
                    ,unsigned char MF           /* MFM / FM */
                    ,unsigned char C            /* シリンダ番号 */
                    ,unsigned char H            /* ヘッド番号 */
                    ,unsigned char R            /* セクタ番号 */
                    ,unsigned char N            /* セクタ長コード */
                    ,unsigned char *buff);      /* 出力を書き込むアドレス */
unsigned char writedata(unsigned char device
                       ,unsigned char MF        /* MFM / FM */
                       ,unsigned char C         /* シリンダ番号 */
                       ,unsigned char H         /* ヘッド番号 */
                       ,unsigned char R         /* セクタ番号 */
                       ,unsigned char N         /* セクタ長コード */
                       ,unsigned char *buff);   /* データバッファの先頭アドレス */
unsigned char writedeleted(unsigned char device
                          ,unsigned char MF     /* MFM / FM */
                          ,unsigned char C      /* シリンダ番号 */
                          ,unsigned char H      /* ヘッド番号 */
                          ,unsigned char R      /* セクタ番号 */
                          ,unsigned char N      /* セクタ長コード */
                          ,unsigned char *buff);/* データバッファの先頭アドレス */
unsigned char trackfmt(unsigned char device
                      ,unsigned char MF         /* MFM / FM */
                      ,unsigned char C          /* シリンダ番号 */
                      ,unsigned char H          /* ヘッド番号 */
                      ,unsigned char N          /* セクタ長コード */
                      ,unsigned char SC         /* トラックあたりのセクタ数*/
                      ,unsigned char DATA       /* セクタに書き込むデータパターン */
                      ,unsigned char *buff);    /* データバッファの先頭アドレス */
unsigned char readdiag(unsigned char device
                      ,unsigned char MF         /* MFM / FM */
                      ,unsigned char H          /* ヘッド番号 */
                      ,unsigned char N          /* セクタ長コード */
                      ,unsigned char *buff);    /* 出力を書き込むアドレス */
unsigned char sectsence(unsigned char device
                       ,unsigned char C
                       ,unsigned char H);
unsigned short sectlength(unsigned char N);     /*セクタ長コード*/
unsigned char secthead(unsigned char drive
                      ,unsigned char MF         /* MFM / FM */
                      ,unsigned char H          /* ヘッド番号 */
                      ,unsigned char *buff);    /* 出力を書き込むアドレス */
char *errmsg(unsigned char ah);


diskbios.c

/*
PC-9801 DISK BIOS を使う関数
FDCの動作を正確に再現するPC-9801エミュレータは少ないので、エミュレータで動作確認をする場合は注意を要する。
LSI C-86 Ver.3.30 試食版用

このソースコードは CC0 パブリック・ドメイン提供です。
https://creativecommons.org/publicdomain/zero/1.0/deed.ja

2016年2月1日 2016年5月17日 佐藤恭一 kyoutan.jpn.org
*/
/*#include <stdio.h>*/
#include "diskbios.h"

/* INITIALIZE FDCとBIOSのワークエリアの初期化
戻り値 0:異常終了 0以外:正常終了 */
unsigned char diskinit(unsigned char device)
{
  union REGS reg;

  reg.h.ah=0x03;  /* INITIALIZE */
  reg.h.al=device;
  int86(DISKBIOS, &reg, &reg);

  if(0!=reg.x.cflag) return FALSE; /* 0:異常終了*/

  return TRUE;  /* 0以外:正常終了*/
}

/* DISKMODE 動作モードの設定 デバイスタイプユニットが2DDの時のみ有効
48tpi(2D)/96tpi(2DD)の切り替え 2HDの時は無意味
戻り値 0:異常終了 0以外:正常終了 */
unsigned char drvmode(unsigned char device /* デバイス番号 0~3 */
                     ,unsigned char mode)  /* モード MODE2D/MODE2DD */
{
  unsigned char val;
  unsigned char ret;

  device&=0x03; /*0000 0011 下位2ビットのみ取り出す(0~3)*/
  val=0x0f; /* 初期値 00001111 */
  /*各ドライブの現在のモードを調べる 0:48tpi(2D) 1:96tpi(2DD*/
  if(disksense(FD2DD | 0, &ret)) if((0x04 & ret)==0) val &= ~(1<<0); /* DRIVE0 */
  if(disksense(FD2DD | 1, &ret)) if((0x04 & ret)==0) val &= ~(1<<1); /* DRIVE1 */
  if(disksense(FD2DD | 2, &ret)) if((0x04 & ret)==0) val &= ~(1<<2); /* DRIVE2 */
  if(disksense(FD2DD | 3, &ret)) if((0x04 & ret)==0) val &= ~(1<<3); /* DRIVE3 */

  if(MODE2D==mode) val &= ~(1<<device); /* 2D  ビットクリア */
  else             val |=  (1<<device); /* 2DD ビットセット */

  return diskmode(val);
}

/* DISKMODE 動作モードの設定 デバイスタイプユニットが2DDの時のみ有効
48tpi(2D)/96tpi(2DD)の切り替え 2HDの時は無意味
mode ----1111  0:2D 1:2DD
         |||+- DRIVE0
         ||+-- DRIVE1
         |+--- DRIVE2
         +---- DRIVE3
戻り値 0:異常終了 0以外:正常終了 */
unsigned char diskmode(unsigned char mode)
{
  union REGS reg;

  reg.h.ah=0x0E;  /* 全ドライブ両面モードにする */
  reg.h.al=0x1F;
  int86(DISKBIOS, &reg, &reg);

  mode &= 0x0f;
  mode |= 0x10;
  reg.h.ah=0x8E;  /* MODE設定 */
  reg.h.al=mode;
  int86(DISKBIOS, &reg, &reg);

  if(0!=reg.x.cflag) return FALSE; /* 0:異常終了*/

  return TRUE;  /* 0以外:正常終了*/
}

/* SENSE デバイスの状態を調べる
戻り値 0:異常終了 0以外:正常終了 */
unsigned char disksense(unsigned char device
                       ,unsigned char *errcode)
{
  union REGS reg;

  reg.h.ah=0x04;  /* 00000100 SENSE */
  reg.h.al=device;
  int86(DISKBIOS, &reg, &reg);

  *errcode=reg.h.ah;

  if(0!=reg.x.cflag) return FALSE; /* 0:異常終了*/

  return TRUE;  /* 0以外:正常終了*/
}

/* RECALIBRATE ヘッドをトラック0に移動する
戻り値 0:異常終了 0以外:正常終了 */
unsigned char track00(unsigned char device)
{
  union REGS reg;

  reg.h.ah=0x07;  /* RECALIBRATE */
  reg.h.al=device;
  int86(DISKBIOS, &reg, &reg);

  if(0!=reg.x.cflag) return FALSE; /* 0:異常終了*/
  return TRUE; /* 0以外:正常終了*/
}

/* SEEK 指定シリンダにヘッドを移動する
戻り値 0:異常終了 0以外:正常終了 */
unsigned char seek(unsigned char device
                  ,unsigned char C)     /* シリンダ番号 */
{
  union REGS reg;

  reg.h.ah= 0x10; /*AH 00010000*/
                  /*      +---- SEEK */
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.h.cl= C;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  int86(DISKBIOS, &reg, &reg);

  if(0!=reg.x.cflag)
  {
    return FALSE; /* 0:異常終了*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* READ ID 現在のトラックからIDを読む
アドレスout から C H R N の順に4バイト書き込む
戻り値 0:異常終了 0以外:正常終了 */
unsigned char readid(unsigned char device
                    ,unsigned char MF     /* MFM/FM */
                    ,unsigned char H      /* ヘッド番号 */
                    ,unsigned char *buff) /* IDを書き込むアドレス */
{
  union REGS reg;

  reg.h.ah= MF | 0x0A; /* 01001010 ID READ */
                       /*  ||+---- SEEK */
                       /*  |+----- ~r */
                       /*  +------ MF */
  reg.h.al=device;     /*デバイスタイプユニット番号*/
  reg.h.dh=H;          /*ヘッド番号*/

  int86(DISKBIOS, &reg, &reg);

  if(0!=reg.x.cflag)
  {
      /*エラーだったら アドレスbuffにエラーコードを書く*/
      *buff = reg.h.ah;
      return FALSE; /* 0:異常終了*/
  }

  *buff++ = reg.h.cl;   /* C シリンダ */
  *buff++ = reg.h.dh;   /* H ヘッド */
  *buff++ = reg.h.dl;   /* R セクタ番号 */
  *buff   = reg.h.ch;   /* N セクタ長コード */

  return TRUE; /* 0以外:正常終了*/
}

/* READ DATA 指定した1セクタからデータを読む
戻り値 0:異常終了 0以外:正常終了 */
unsigned char readdata(unsigned char device
                      ,unsigned char MF     /* MFM / FM */
                      ,unsigned char C      /* シリンダ番号 */
                      ,unsigned char H      /* ヘッド番号 */
                      ,unsigned char R      /* セクタ番号 */
                      ,unsigned char N      /* セクタ長コード */
                      ,unsigned char *buff) /* 出力を書き込むアドレス */
{
  union REGS reg;
  struct SREGS seg;

  reg.h.ah= MF | 0x16; /*AH 00010110 READ DATA  セクタからデータを読む。*/
                       /*   |||+---- SEEK */
                       /*   ||+----- ~r   */
                       /*   |+------ MF   */
                       /*   +------- MT   */
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.x.bx= sectlength(N);         /*BX DTL*/
  reg.h.cl= C;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  reg.h.dh= H;                     /*DH ヘッド番号*/
  reg.h.dl= R;                     /*DL セクタ番号*/
  reg.h.ch= N;                     /*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)
  {
    /*エラーだったら アドレスoutにエラーコードを書く*/
    *buff = reg.h.ah;
    return FALSE; /* 0:異常終了*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* READ DELETED DATA 指定した1セクタからデリーテッドデータを読む
戻り値 0:異常終了 0以外:正常終了 */
unsigned char readdeleted(unsigned char device
                         ,unsigned char MF     /* MFM / FM */
                         ,unsigned char C      /* シリンダ番号 */
                         ,unsigned char H      /* ヘッド番号 */
                         ,unsigned char R      /* セクタ番号 */
                         ,unsigned char N      /* セクタ長コード */
                         ,unsigned char *buff) /* 出力を書き込むアドレス */
{
  union REGS reg;
  struct SREGS seg;

  reg.h.ah= MF | 0x16; /*AH 00011100 READ DELETED DATA*/
                       /*   |||+---- SEEK */
                       /*   ||+----- ~r   */
                       /*   |+------ MF   */
                       /*   +------- MT   */
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.x.bx= sectlength(N);         /*BX DTL*/
  reg.h.cl= C;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  reg.h.dh= H;                     /*DH ヘッド番号*/
  reg.h.dl= R;                     /*DL セクタ番号*/
  reg.h.ch= N;                     /*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)
  {
    /*エラーだったら アドレスoutにエラーコードを書く*/
    *buff = reg.h.ah;
    return FALSE; /* 0:異常終了*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* VERIFY 1セクタからデータを読むが、バッファへの転送は行わない。(セクタが存在するか、データCRCエラーが無いかのチェック)
戻り値 0:異常終了 0以外:正常終了 */
unsigned char verify(unsigned char device
                    ,unsigned char MF     /* MFM / FM */
                    ,unsigned char C      /* シリンダ番号 */
                    ,unsigned char H      /* ヘッド番号 */
                    ,unsigned char R      /* セクタ番号 */
                    ,unsigned char N      /* セクタ長コード */
                    ,unsigned char *buff) /* 出力を書き込むアドレス */
{
  union REGS reg;
  struct SREGS seg;

  reg.h.ah= MF | 0x11; /*AH 00010001 READ DATA  セクタからデータを読む。*/
                       /*   |||+---- SEEK */
                       /*   ||+----- ~r   */
                       /*   |+------ MF   */
                       /*   +------- MT   */
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.x.bx= sectlength(N);         /*BX DTL*/
  reg.h.cl= C;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  reg.h.dh= H;                     /*DH ヘッド番号*/
  reg.h.dl= R;                     /*DL セクタ番号*/
  reg.h.ch= N;                     /*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)
  {
    /*エラーだったら アドレスoutにエラーコードを書く*/
    *buff = reg.h.ah;
    return FALSE; /* 0:異常終了*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* WRITE DATA 指定した1セクタかにデータを書く
戻り値 0:異常終了 0以外:正常終了 */
unsigned char writedata(unsigned char device
                       ,unsigned char MF     /* MFM / FM */
                       ,unsigned char C      /* シリンダ番号 */
                       ,unsigned char H      /* ヘッド番号 */
                       ,unsigned char R      /* セクタ番号 */
                       ,unsigned char N      /* セクタ長コード */
                       ,unsigned char *buff)  /* データバッファの先頭アドレス */
{
  union REGS reg;
  struct SREGS seg;

  reg.h.ah= MF | 0x15; /*AH 00010101 WRITE DATA  セクタにデータを書く。*/
                       /*   |||+---- SEEK */
                       /*   ||+----- ~r   */
                       /*   |+------ MF   */
                       /*   +------- MT (マルチトラック指定は使用しないこと)*/
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.x.bx= sectlength(N);         /*BX DTL データ長(DTLで示す書き込みがセクタの途中で終わった場合、残りを0で埋めて正常終了する)*/
  reg.h.cl= C;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  reg.h.dh= H;                     /*DH ヘッド番号*/
  reg.h.dl= R;                     /*DL セクタ番号*/
  reg.h.ch= N;                     /*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)
  {
    return FALSE; /* 0:異常終了*/
    /*エラーが発生するのはIDが見つからない時*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* WRITE DELETED DATA 指定した1セクタかにデリーデッドデータを書く
戻り値 0:異常終了 0以外:正常終了 */
unsigned char writedeleted(unsigned char device
                          ,unsigned char MF     /* MFM / FM */
                          ,unsigned char C      /* シリンダ番号 */
                          ,unsigned char H      /* ヘッド番号 */
                          ,unsigned char R      /* セクタ番号 */
                          ,unsigned char N      /* セクタ長コード */
                          ,unsigned char *buff)  /* データバッファの先頭アドレス */
{
  union REGS reg;
  struct SREGS seg;

  reg.h.ah= MF | 0x19; /*AH 00011001 WRITE DELETED DATA*/
                       /*   |||+---- SEEK */
                       /*   ||+----- ~r   */
                       /*   |+------ MF   */
                       /*   +------- MT (マルチトラック指定は使用しないこと)*/
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.x.bx= sectlength(N);         /*BX DTL データ長(DTLで示す書き込みがセクタの途中で終わった場合、残りを0で埋めて正常終了する)*/
  reg.h.cl= C;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  reg.h.dh= H;                     /*DH ヘッド番号*/
  reg.h.dl= R;                     /*DL セクタ番号*/
  reg.h.ch= N;                     /*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)
  {
    return FALSE; /* 0:異常終了*/
    /*エラーが発生するのはIDが見つからない時*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* FORMAT TRACK 一つのトラックをフォーマットする
戻り値 0:異常終了 0以外:正常終了
与えるデータは
C H R N の4バイト*セクタ数 をバッファに用意しておく
ギャップ長はBIOSが決めてくれる */
unsigned char trackfmt(unsigned char device
                      ,unsigned char MF     /* MFM / FM */
                      ,unsigned char C      /* シリンダ番号 */
                      ,unsigned char H      /* ヘッド番号 */
                      ,unsigned char N      /* セクタ長コード */
                      ,unsigned char SC    /* トラックあたりのセクタ数*/
                      ,unsigned char DATA   /* セクタに書き込むデータパターン */
                      ,unsigned char *buff) /* データバッファの先頭アドレス */
{
  union REGS reg;
  struct SREGS seg;

  reg.h.ah= MF | 0x1D; /*AH 00011101 FORMAT TRACK*/
                       /*    ||+---- SEEK */
                       /*    |+----- ~r   */
                       /*    +------ MF   */
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.x.bx= SC * 4;               /*BX DTL データ長*/
  reg.h.cl= C;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  reg.h.dh= H;                     /*DH ヘッド番号*/
  reg.h.ch= N;                     /*CH セクタ長コード (合わないセクタ長コードを指定するとエラー)*/
  reg.h.dl= DATA;                  /*DL セクタに書き込むデータパターン*/
  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)
  {
    return FALSE; /* 0:異常終了*/
    /*エラーが発生するのはメディアが入っていない時くらい?*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* READ DIAGNOSTIC
インデックス信号直後のセクタからデータを読む。
戻り値 0:異常終了 0以外:正常終了
READ DATAと違い、IDのエラー、データのエラーがあっても処理を続行する。
リザルトステータスはコマンド中に起きたすべてのエラーの論理和になる。
*/
unsigned char readdiag(unsigned char device
                      ,unsigned char MF     /* MFM / FM */
                      ,unsigned char H      /* ヘッド番号 */
                      ,unsigned char N      /* セクタ長コード */
                      ,unsigned char *buff) /* 出力を書き込むアドレス */
{
  union REGS reg;
  struct SREGS seg;

  reg.h.ah= MF | 0x02; /*AH 01000010 READ DIAGNOSTIC インデックス信号直後のセクタからデータを読む。*/
                       /*    ||+---- SEEK            READ DATAと違い、IDのエラー、データのエラーがあっても処理を続行する。*/
                       /*    |+----- ~r              リザルトステータスはコマンド中に起きたすべてのエラーの論理和になる。*/
                       /*    +------ MF */
  reg.h.al= device;                /*AL デバイスタイプユニット番号*/
  reg.x.bx= sectlength(N);         /*BX DTL*/
  reg.h.cl= 0;                     /*CL シリンダ番号(ahのSEEKビットが1の時のみ有効)*/
  reg.h.dh= H;                     /*DH ヘッド番号*/
  reg.h.dl= 1;                     /*DL セクタ番号(無意味)*/
  reg.h.ch= N;                     /*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)
  {
    /*エラーだったら アドレスbuffにエラーコードを書く*/
    *buff = reg.h.ah;
    return FALSE; /* 0:異常終了*/
  }
  return TRUE; /* 0以外:正常終了*/
}

/* 指定したトラックのセクタのタイプを調べる
戻り値 0:異常終了 0以外:セクタのタイプ
2D/2HD MFM/FM */
unsigned char sectsence(unsigned char device
                       ,unsigned char C
                       ,unsigned char H)
{
  unsigned char buff[4];

  device &= 0x0F; /*ユニット番号だけにする*/

  seek(device, C);
  if(readid(FD2HD | device, FDMFM, H, buff)) return FD2HD | FDMFM;
  if(readid(FD2DD | device, FDMFM, H, buff)) return FD2DD | FDMFM;
  if(readid(FD2HD | device, FDFM,  H, buff)) return FD2HD | FDFM;
  if(readid(FD2DD | device, FDFM,  H, buff)) return FD2DD | FDFM;
  /* 2Dと2DDの見分けはつかないか?
  シークしてみれば2Dと2DDの違いでシークエラー出るかな?*/
  return FALSE;
}

/* セクタ長コードからセクタ長を得る
戻り値 0:異常終了 0以外:セクタのバイト数 */
unsigned short sectlength(unsigned char N)  /* セクタ長コード */
{
  switch(N)
  {
    case 0:
      return 128;
    case 1:
      return 256;
    case 2:
      return 512;
    case 3:
      return 1024;
    case 4:
      return 2048;
  }
  return 0;
}

/*READDIAG で先頭セクタの番号を調べる
バッファサイズはセクタ長の分必要
正常終了ならバッファ先頭にセクタ番号
以上終了ならバッファ先頭にエラーコード*/
unsigned char secthead(unsigned char drive
                      ,unsigned char MF     /* MFM / FM */
                      ,unsigned char H      /* ヘッド番号 */
                      ,unsigned char *buff) /* 出力を書き込むアドレス */
{
  unsigned char n;

  /*セクタ長コードを調べる*/
  if(! readid(drive, MF, H, buff)) return FALSE;
  n=buff[3];

  if(! readdiag(drive, MF, H, n, buff))
  {
    /*エラーならエラーコードがバッファの先頭アドレスに入る*/
    /*puts(errmsg(buff[0]));*/
    return FALSE;
  }
  drive &= 0x0F; /*ドライブ番号だけにする*/
  switch(drive)
  {
    case 0:
      buff[0] = DISKSTAT.D0.R - 1;    /*読み込んだ次のセクタ番号を示しているので引き算*/
      return TRUE;
    case 1:
      buff[0] = DISKSTAT.D1.R - 1;
      return TRUE;
    case 2:
      buff[0] = DISKSTAT.D2.R - 1;
      return TRUE;
    case 3:
      buff[0] = DISKSTAT.D3.R - 1;
      return TRUE;
  }
  return FALSE;
}

/* エラーコードからエラーメッセージを得る */
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";
}

このブログの人気の投稿

windowsで「インターネット接続の共有」の設定

月刊 I/O 記事リスト 1976~1989

X68000実機のROMを保存