unit FDC; interface const FDC_MAX_TRACK = 84; FDC_REG_SRA = $3F0; FDC_REG_SRB = $3F1; FDC_REG_DOR = $3F2; {3F2H Запись: Регистр управления ╓7┬6┬5┬4┬3┬2┬1┬0╖ ·D C B A│ │ │ · ╙─┴─┴─┴─┴╥┴╥┴─┴─╜ биты ╚══╦══╝ · · ╚═╩═. 0-1: Выбор драйвера 0-3 (AT: бит 1 не исп.) · · ╚═════. 2: 0=Сброс контроллера HГМД; 1=Разрешение контроллера · ╚═══════. 3: 1=Разрешиить работу с 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 Передача/Прием ком или дан.} FDC_REG_DR = $3F5; {3F5H Чтение/Запись: Регистр данных Kмд Описание ▀▀▀ ▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀ 0E6H Читать данные (Требует: 8 параметров; Pезультат : 7 параметров) 0C5H Писать данные (Требует 8 параметров; Pезультат : 7 параметров) 04DH Форматировать дорожку (Требует 5 параметров;Pезультат : 7 параметров) 007H Рекалибровать (Требует 1 параметров; Pезультат : HЕТ) 00FH Установить на дородку (Требует 2 параметров;Pезультат : HЕТ} FDC_REG_DIR = $3F7; {3F7H ▌AT▐ Только-для чтения : Регистр сигнадов входа ╓7┬6┬5┬4┬3┬2┬1┬0╖ · │ │ · ╙╥┴─┴─┴─┴─┴─┴─┴╥╜ бить · ╚════╦════╝ ╚═. 0: 1= Выбрана двойная плотность; 0= Высокая плотность · ╚════════. 1-6: Зарезервированы. ╚═══════════════. 7: 1= Произошла замена дискеты} FDC_REG_CCR = $3F8; {3F8H ▌AT▐ Запись : Регистр конфигурации ╓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.