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 でした。
つまらないサンプルプログラムですが、なにかの役に立つかもしれません。
実行してみた所。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, ®, ®);
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, ®, ®);
mode &= 0x0f;
mode |= 0x10;
reg.h.ah=0x8E; /* MODE設定 */
reg.h.al=mode;
int86(DISKBIOS, ®, ®);
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, ®, ®);
*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, ®, ®);
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, ®, ®);
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, ®, ®);
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, ®, ®, &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, ®, ®, &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, ®, ®, &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, ®, ®, &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, ®, ®, &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, ®, ®, &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, ®, ®, &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";
}