480 lines
13 KiB
Plaintext
480 lines
13 KiB
Plaintext
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.
|