SN-FDD-UTILS/FDC.PAS

480 lines
13 KiB
Plaintext
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

unit FDC;
interface
const
FDC_MAX_TRACK = 84;
FDC_REG_SRA = $3F0;
FDC_REG_SRB = $3F1;
FDC_REG_DOR = $3F2;
{3F2H ‡ ¯¨áì: <20>¥£¨áâà ã¯à ¢«¥­¨ï
Ö7Â6Â5Â4Â3Â2Â1Â0·
úD C B A³ ³ ³ ú
ÓÄÁÄÁÄÁÄÁÒÁÒÁÄÁĽ ¡¨âë
ÈÍÍËÍͼ ú ú ÈÍÊÍ. 0-1: ‚ë¡®à ¤à ©¢¥à  0-3 (AT: ¡¨â 1 ­¥ ¨á¯.)
ú ú ÈÍÍÍÍÍ. 2: 0=‘¡à®á ª®­â஫«¥à  HƒŒ„; 1=<3D> §à¥è¥­¨¥ ª®­â஫«¥à 
ú ÈÍÍÍÍÍÍÍ. 3: 1=<3D> §à¥è¨¨âì à ¡®âã á DMA ¨ ¯à¥à뢠­¨ï¬¨
ÈÍÍÍÍÍÍÍÍÍÍÍÍ. 4-7: ‚ª«îç¨âì ¬®â®à ¤à ©¢¥à . …᫨ ¡¨â ãáâ ­®¢«¥­ â® ‚Š‹.
(AT: ¡¨âë 6-7 ¨£­®à¨àã¥â)}
FDC_REG_MSR = $3F4;
{3F4H ’®«ìª®-¤«ï ç⥭¨ï: ƒ« ¢­ë© ॣ¨áâà áâ âãá 
Ö7Â6Â5Â4Â3Â2Â1Â0·
ú ³ ³ ³ ³D C B Aú
ÓÒÁÒÁÒÁÒÁÄÁÄÁÄÁĽ ¡¨âë
ú ú ú ú ÈÍÍÍÍÍÊÍ. 0: ‚ë¡®à ¤à ©¢¥à  (AT: ¡¨âë 2-3 ­¥ ¨á¯®«ì§ã¥â)
ú ú ú ÈÍÍÍÍÍÍÍÍÍ. 4: 1=‘¨£­ « ª®­â஫«¥à  (ˆ¤¥â ç⥭¨¥ ¨«¨ § ¯¨áì)
ú ú ÈÍÍÍÍÍÍÍÍÍÍÍ. 5: 1=H…-DMA ०¨¬; 0=DMA ०¨¬ ¢ª«î祭
ú ÈÍÍÍÍÍÍÍÍÍÍÍÍÍ. 6: ‘¨£­ « ã¯à ¢«¥­¨ï: 1 = FDCÍ.CPU; 0 = CPUÍ.FDC
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ. 7: ‡ ¯à®á ­  ‡ ¯¨áì. 1=OK <20>¥à¥¤ ç /<2F>ਥ¬ ª®¬ ¨«¨ ¤ ­.}
FDC_REG_DR = $3F5;
{3F5H —⥭¨¥/‡ ¯¨áì: <20>¥£¨áâà ¤ ­­ëå
K¬¤ ޝ¨á ­¨¥
ßßß ßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßßß
0E6H —¨â âì ¤ ­­ë¥ (’ॡã¥â: 8 ¯ à ¬¥â஢; P¥§ã«ìâ â : 7 ¯ à ¬¥â஢)
0C5H <20>¨á âì ¤ ­­ë¥ (’ॡã¥â 8 ¯ à ¬¥â஢; P¥§ã«ìâ â : 7 ¯ à ¬¥â஢)
04DH ”®à¬ â¨à®¢ âì ¤®à®¦ªã (’ॡã¥â 5 ¯ à ¬¥â஢;P¥§ã«ìâ â : 7 ¯ à ¬¥â஢)
007H <20>¥ª «¨¡à®¢ âì (’ॡã¥â 1 ¯ à ¬¥â஢; P¥§ã«ìâ â : H…)
00FH “áâ ­®¢¨âì ­  ¤®à®¤ªã (’ॡã¥â 2 ¯ à ¬¥â஢;P¥§ã«ìâ â : H…}
FDC_REG_DIR = $3F7;
{3F7H ÝATÞ ’®«ìª®-¤«ï ç⥭¨ï : <20>¥£¨áâà ᨣ­ ¤®¢ ¢å®¤ 
Ö7Â6Â5Â4Â3Â2Â1Â0·
ú ³ ³ ú
ÓÒÁÄÁÄÁÄÁÄÁÄÁÄÁÒ½ ¡¨âì
ú ÈÍÍÍÍËÍÍÍͼ ÈÍ. 0: 1= ‚ë¡à ­  ¤¢®©­ ï ¯«®â­®áâì; 0= ‚ë᮪ ï ¯«®â­®áâì
ú ÈÍÍÍÍÍÍÍÍ. 1-6: ‡ à¥§¥à¢¨à®¢ ­ë.
ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ. 7: 1= <20>ந§®è«  § ¬¥­  ¤¨áª¥âë}
FDC_REG_CCR = $3F8;
{3F8H ÝATÞ ‡ ¯¨áì : <20>¥£¨áâà ª®­ä¨£ãà æ¨¨
Ö7Â6Â5Â4Â3Â2Â1Â0·
ú ³ ú
ÓÄÁÄÁÄÁÄÁÄÁÄÁÄÁĽ ¡¨âë
ÈÍÍÍÍËÍÍÍͼ ÈÍÊÍ. 0-1: ᪮à®áâì ¯¥à¤ ç¨ ¤ ­­ëå
ú 00=500 KBS, 01=300 KBS, 10=250 KBS, 11=§ à¥§¥à.
ÈÍÍÍÍÍÍÍÍÍÍ. 2-7: ‡ à¥§¥à¢¨à®¢ ­®}
FDC_CMD_READ_TRACK = $02;
FDC_CMD_SPECIFY = $03;
FDC_CMD_GET_STATUS3 = $04;
FDC_CMD_WRITE_DATA = $05;
FDC_CMD_READ_DATA = $06;
FDC_CMD_RECALIBRATE = $07;
FDC_CMD_WRITE_DELETED_DATA = $09;
FDC_CMD_READ_DELETED_DATA = $0C;
FDC_CMD_SEEK = $0F;
{ for FDC_CMD_READ_DATA }
FDC_HCMD_SK = $20; { BIT5: SK stands for Skip Deleted Data Addres Mark }
FDC_HCMD_MFM = $40; { BIT6: if high, MFM mode is selected }
FDC_HCMD_MT = $80; { BIT7: If MT is high, a multi-track operation is }
FDC_SPEED_500 = $00;
FDC_SPEED_300 = $01;
FDC_SPEED_250 = $02;
FDC_SPEED_125 = $03;
FDC_SPEED_COUNT = $03;
FDC_SECTOR_256 = 1;
FDC_SECTOR_512 = 2;
FDC_SECTOR_1024 = 3;
FDC_SECTOR_2048 = 4;
FDC_SECTOR_4096 = 5;
FDC_SECTOR_8192 = 6;
{
+----------+-----------------+-----+-----+------+--------+----------------+
| FORMAT | SECTOR SIZE | N | SC | GPL1| GPL2 | REMARKS |
+----------+-----------------+-----+-----+------+--------+----------------+
| |256 | 01 | 1A | 0E | 36H |IBM Diskette 2D |
| |512 | 02 | 0F | 1B | 54H | |
| MFM Mode |1024 | 03 | 08 | 36H | 74H |IBM Diskette 2D |
| |2048 | 04 | 04 | __ | __ | |
| |4096 | 05 | 02 | __ | __ | |
| |8192 | 06 | 01 | __ | __ | |
+----------+-----------------+-----+-----+------+--------+----------------+
Note : 1. GPL1 Suggested values of GPL in Read or Write Commands to avoid
splice point between data and ID field of contiguous sections.
2 GPL2, Suggested values of GPL in format command.
}
FDC_OK = 0;
FDC_ERR_BAD_CONTROLLER = 1;
FDC_ERR_TIMEOUT = 2;
FDC_ERR_SEEK_ERROR = 3;
FDC_ERR_DRIVE_NOT_READY = 4;
FDC_ERR_END_OF_CYLINDER = 5;
FDC_ERR_BAD_SECTOR = 6;
FDC_ERR_DMA_ERROR = 7;
FDC_ERR_SECTOR_NOT_FOUND = 8;
FDC_ERR_WRITE_PROTECT = 9;
FDC_ERR_UNKNOWN = 10;
type
TFDCBbuffer = array[1..8192] of byte;
var
fdcBuf: TFDCBbuffer;
fdcIOError: byte;
fdcStatus: array[1..8] of byte;
procedure fdcMotorOn(drv: byte);
procedure fdcMotorOff;
procedure fdcRecalibrate(drv: byte);
procedure fdcSeek(drv, trck: byte);
procedure fdcSpecify;
procedure fdcReadSector(drv, track_id, head_id, sector_id, sector_size_id, last_sector_id: byte);
procedure fdcInit(drv, sector_size_id: byte);
function fdcErrorStr(value: byte): string;
function fdcSpeedStr(value: byte): string;
implementation
uses
crt;
{ HELPERS }
{----------------------------------------------------------------------------}
function strr(value: longint): string;
var
r: string;
begin
str(value, r);
strr:= r;
end;
{----------------------------------------------------------------------------}
function TestBit(Num, Bit: byte): boolean;
const
Bits: array[0..7] of word = (1,2,4,8,16,32,64,128);
begin
TestBit:= (Num and Bits[Bit]) <> 0;
end;
{----------------------------------------------------------------------------}
procedure IncPtr(var p: pointer; off: Word);
begin
asm
push ds
lds di,p
mov ax,Off
add [di],ax
jnc @1
add word ptr [di+2],1000h
@1: pop ds
end;
end;
{----------------------------------------------------------------------------}
procedure ConvPtr(p: pointer; var page, off: word);
begin
asm
push ds
lds dx,p
mov bx,ds
mov ax,bx
mov cl,4
shl ax,cl
add ax,dx
pushf
lds di,off
mov [di],ax
mov ax,bx
mov cl,12
shr ax,cl
popf
jnc @1
inc ax
@1: lds di,page
mov [di],ax
pop ds
end;
end;
{ PRIVATE }
{----------------------------------------------------------------------------}
procedure fdc_out(value: byte);
var
i: longint;
begin
i:= 128;
while (port[FDC_REG_MSR] and $C0) <> $80 do
begin
dec(i);
if i = 0 then break;
end;
fdcIOError:= FDC_OK;
if i <> 0 then
port[FDC_REG_DR]:= value
else
fdcIOError:= FDC_ERR_TIMEOUT;
end;
{----------------------------------------------------------------------------}
function fdc_in: byte;
var
i: longint;
begin
i:= 128;
while (port[FDC_REG_MSR] and $C0) <> $C0 do
begin
dec(i);
if i = 0 then break;
end;
if i <> 0 then
begin
fdcIOError:= FDC_OK;
fdc_in:= port[FDC_REG_DR]
end
else
begin
fdcIOError:= FDC_ERR_TIMEOUT;
fdc_in:= 0;
end;
end;
{----------------------------------------------------------------------------}
procedure fdc_wait;
var
WasInt: byte;
Timer: word;
begin
fdcIOError:= FDC_OK;
WasInt:= 0;
Timer:= 2000;
while (WasInt = 0) and (Timer > 0) do
begin
dec(Timer);
delay(1);
asm
push DS
push AX
xor AX, AX
mov DS, AX
test byte ptr DS:[43Eh], 80h
jz @noInt
and byte ptr DS:[43Eh], 7Fh
mov WasInt, 1
@noInt:
pop AX
pop DS
end;
end;
if WasInt = 0 then
fdcIOError:= FDC_ERR_TIMEOUT;
end;
{ PUBLIC }
{----------------------------------------------------------------------------}
procedure fdcMotorOn(drv: byte);
begin
{ port[FDC_REG_DOR]:= (drv+1)*16+$C+drv; }
if drv = 0 then
port[FDC_REG_DOR]:= 28
else
port[FDC_REG_DOR]:= 45;
{ wait until motor take rpm }
delay(1000);{}
end;
{----------------------------------------------------------------------------}
procedure fdcMotorOff;
begin
port[FDC_REG_DOR]:= 12; { 12 = $0C = 00001100, bit 3 and 4 }
end;
{----------------------------------------------------------------------------}
procedure fdcRecalibrate(drv: byte);
begin
fdc_out(FDC_CMD_RECALIBRATE);
fdc_out(drv);
fdc_wait;
end;
{----------------------------------------------------------------------------}
procedure fdcSeek(drv, trck: byte);
var
a: byte;
begin
fdc_out(FDC_CMD_SEEK);
fdc_out(drv);
fdc_out(trck);
fdc_wait;
delay(100);
end;
{----------------------------------------------------------------------------}
procedure fdcSpecify;
var
SRT, HUT, HLT, DMA: byte;
begin
{
SRT - Step Rate Interval.
Sets the minimum delay between two drive step pulses.
(1-16 ms, SRT = 0Fh corresponds to 1 ms).
Older PC drives did not perform 3- or 4-ms step rates,
and some cannot perform with 2- or even 1-ms step rates.
HUT - Head Unload Time.
The delay between the completion of a read/write operation and the head lifting.
(16-240 ms, HUT = 0 corresponds to 16 ms). It is usually set to 240 ms.
HLT - Head Load Time.
The delay between the head load command and the start of a read/write operation.
(2-254 ms, HLT=1-2 ms). Usually set to 2 ms.
ND - 0: DMA mode. 1: Non-DMA mode.
}
SRT:= $0F;
HUT:= $0D;
HLT:= $01;
DMA:= $00;
fdc_out(FDC_CMD_SPECIFY);
fdc_out(SRT or (HUT shl 4));
fdc_out(DMA or (HLT shl 1));
end;
{---------------------------------------------------------------------------}
procedure fdcInit(drv, sector_size_id: byte);
begin
{ reset }
port[$3F2]:= 0;
port[$3F2]:= 4;
fdcMotorOn(drv);
fdcRecalibrate(drv);
fdcRecalibrate(drv);
fdcSeek(drv, 20);
fdcRecalibrate(drv);
fdcSpecify;
end;
{---------------------------------------------------------------------------}
procedure fdcReadSector(drv, track_id, head_id, sector_id, sector_size_id, last_sector_id: byte);
var
gpl_id: byte;
DMAPage,DMAOfs:word;
BlockSize:word;
begin
{ init DMA }
ConvPtr(@fdcBuf, DMAPage, DMAOfs);
BlockSize:= (128 SHL sector_size_id);
asm cli end;
port[$0A]:= $06; { mask channel 2 }
port[$0C]:= $46; { reset trigger }
port[$0B]:= $46; { mode: FDD -> Memory }
port[$04]:= DMAOfs and $FF;
port[$04]:= DMAOfs shr 8;
port[$81]:= DMAPage and $FF;
port[$05]:= (BlockSize-1) and $FF;
port[$05]:= (BlockSize-1) shr 8;
port[$0A]:= $02; { unmask channel 2 }
asm sti end;
{ read data command }
fdc_out(FDC_HCMD_MFM or FDC_CMD_READ_DATA);{}
{ fdc_out( ((head_id and $01) shl 2) or (drv and $03) ); { head (1 bit), drive (2 bits) }
fdc_out(drv);
fdc_out(track_id);
fdc_out(head_id and $01);
fdc_out(sector_id);
fdc_out(sector_size_id); { N: sector size id }
fdc_out(last_sector_id); { EOT: last sector_id on track }
{ GPL values for MFM mode ONLY }
case sector_size_id of
FDC_SECTOR_256: gpl_id:= $0E;
FDC_SECTOR_512: gpl_id:= $1B;
FDC_SECTOR_1024: gpl_id:= $3F;
FDC_SECTOR_2048: gpl_id:= $99;
FDC_SECTOR_4096: gpl_id:= $C8;
FDC_SECTOR_8192: gpl_id:= $C8; { TODO: need to calc it }
else gpl_id:= $2A; { default value }
end;
fdc_out(gpl_id); { GPL: stands for the length of Gap3 }
{ should be FF in MFM mode }
fdc_out($FF); { DTL: if N>0 should be FF and will be ignored }
fdc_wait;
fdcStatus[1]:= fdc_in;
fdcStatus[2]:= fdc_in;
fdcStatus[3]:= fdc_in;
fdcStatus[4]:= fdc_in;
fdcStatus[5]:= fdc_in;
fdcStatus[6]:= fdc_in;
fdcStatus[7]:= fdc_in;
fdc_out(FDC_CMD_GET_STATUS3);
fdc_out(drv);
fdcStatus[8]:= fdc_in;
{ fdcIOError }
fdcIOError:= FDC_OK;
if (fdcStatus[1] and $C0) <> 0 then
begin
fdcIOError:= FDC_ERR_UNKNOWN;
if TestBit(fdcStatus[1],3) then fdcIOError:= FDC_ERR_DRIVE_NOT_READY;
if TestBit(fdcStatus[2],7) then fdcIOError:= FDC_ERR_END_OF_CYLINDER;
if TestBit(fdcStatus[2],5) or TestBit(fdcStatus[3],5) or TestBit(fdcStatus[3],0) then fdcIOError:= FDC_ERR_BAD_SECTOR;
if TestBit(fdcStatus[2],2) or TestBit(fdcStatus[2],4) then fdcIOError:= FDC_ERR_SECTOR_NOT_FOUND;
if TestBit(fdcStatus[2],4) then fdcIOError:= FDC_ERR_DMA_ERROR;
end;
if (TestBit(fdcStatus[8],1)) then fdcIOError:= FDC_ERR_DRIVE_NOT_READY;
end;
{----------------------------------------------------------------------------}
function fdcErrorStr(value: byte): string;
begin
fdcErrorStr:= 'Unknown ('+strr(value)+')';
Case value of
0: fdcErrorStr:= 'OK';
1: fdcErrorStr:= 'Bad Controller';
2: fdcErrorStr:= 'Time Out';
3: fdcErrorStr:= 'Seek Error';
4: fdcErrorStr:= 'Drive Not Ready';
5: fdcErrorStr:= 'End Of Cylinder';
6: fdcErrorStr:= 'Bad Sector';
7: fdcErrorStr:= 'DMA Error';
8: fdcErrorStr:= 'Sector Not Found';
9: fdcErrorStr:= 'Write Protect';
10: fdcErrorStr:= 'Unknown Error';
27: fdcErrorStr:= 'User Break';
End;
end;
{----------------------------------------------------------------------------}
function fdcSpeedStr(value: byte): string;
begin
fdcSpeedStr:= 'Unexpected format';
Case value of
0: fdcSpeedStr:= '500 kb/s (0)';
1: fdcSpeedStr:= '300 kb/s (1)';
2: fdcSpeedStr:= '250 kb/s (2)';
3: fdcSpeedStr:= '125 kb/s (3)';
End;
end;
end.