home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Go64!
/
Go64_1999-06_1999_CSW_Side_A.d64
/
1581cp50.zip
/
SRC
/
FDCLOWLV.C
< prev
next >
Wrap
C/C++ Source or Header
|
1999-05-13
|
34KB
|
1,074 lines
/*
* cOPYRIGHT (c) 1998, 1999 wOLFGANG mOSER
*
* tHIS PROGRAM IS FREE SOFTWARE; YOU CAN REDISTRIBUTE IT AND/OR MODIFY
* IT UNDER THE TERMS OF THE gnu gENERAL pUBLIC lICENSE AS PUBLISHED BY
* THE fREE sOFTWARE fOUNDATION; EITHER VERSION 2, OR (AT YOUR OPTION)
* ANY LATER VERSION.
*
* tHIS PROGRAM IS DISTRIBUTED IN THE HOPE THAT IT WILL BE USEFUL,
* BUT without any warranty; WITHOUT EVEN THE IMPLIED WARRANTY OF
* merchantability OR fitness for a particular purpose. sEE THE
* gnu gENERAL pUBLIC lICENSE FOR MORE DETAILS.
*
* yOU SHOULD HAVE RECEIVED A COPY OF THE gnu gENERAL pUBLIC lICENSE
* ALONG WITH THIS PROGRAM (SEE THE FILE copying); IF NOT, WRITE TO THE
* fREE sOFTWARE fOUNDATION, iNC., 675 mASS aVE, cAMBRIDGE, ma 02139, usa.
*/
/*
* cOMMODORE cbm 1581 FLOPPY DISK COPY UTIL FOR pc'S, fdclowlv.c
*
* wOLFGANG mOSER <WOMO@MINDLESS.COM>
* HTTP://WWW.GM.FH-KOELN.DE/{$7e}WOMO (UP TO dEZEMBER 1999)
*
*
* bASIC INFORMATIONS FROM dAN fANDRICH <DAN@FCH.WIMSEY.BC.CA>.
* hIS README OF THE CBMFS-0.3 DRIVER FOR lINUX EXPLAINED ME, WHAT THE
* DIFFERENCE BETWEEN A dos FORMATTED 800 KB DISK AND A cbm 1581 DISK IS.
* (CHECK: HTTP://VANBC.WIMSEY.COM/{$7e}DANF/SOFTWARE/)
*
*
* bASIC IMPLEMENTATIONS BY cIRIACO gARC{CBM-K}A DE cELIS <CIRI@GUI.UVA.ES>
* hIS UTIL 765dEBUG, vERSION 5.0 IS GREAT FOR LEARNING DMA BASED
* DIRECT FLOPPY DISK CONTROLLER PROGRAMMING.
* (CHECK: FTP://FTP.GUI.UVA.ES/PUB/PC/2M/765D50SR.ZIP)
*
* cHECK OUT FOR HIS FLOPPY DISK UTILS 2m AND 2mgui, THE LAST WORDS
* IN IMPROVING FLOPPY DISK STORAGE CAPACITY.
* HTTP://WWW.GUI.UVA.ES/2M, FTP://FTP.GUI.UVA.ES/PUB/PC/2M
*
*
* fOR ADDITIONAL INFORMATIONS TO fdc PROGRAMMING CHECK:
* HTTP://DEVELOPER.INTEL.COM/DESIGN/PERIPHRL/DATASHTS/290468.HTM
* AND GET THE INTEL 82078 chmos sINGLE-cHIP fLOPPY dISK cONTROLLER
* pdf DOCUMENT:
* HTTP://WWW.INTEL.NL/DESIGN/PERIPHRL/DATASHTS/29047403.PDF
* nATIONAL sEMICONDUCTOR HAS ALSO SOME PAGES ABOUT THEIR pc
* COMPATIBLE CONTROLLERS:
* HTTP://WWW.NATIONAL.COM/PF/dp/dp8473.HTML
* HTTP://WWW.NATIONAL.COM/PF/dp/dp8477b.HTML
*
* aNOTHER GOOD SOURCE FOR FLOPPY DISK CONTROLLER PROGRAMMING INFORMATION
* ARE THE LINUX KERNAL SOURCES, YOU COULD HAVE A LOOK INTO:
* HTTP://WWW.CS.UTEXAS.EDU/USERS/PETERSON/LINUX/INCLUDE/LINUX/FDREG.H
* HTTP://WWW.CS.UTEXAS.EDU/USERS/PETERSON/LINUX/DRIVERS/BLOCK/FLOPPY.C
*/
#INCLUDE "FDCLOWLV.H"
#IFDEF use_tsc
#IFDEF use_p_rdtsc
// SPECIAL: pENTIUM tIME sTAMP cOUNTER READ ROUTINE
// !!! DOESN'T WORK UNDER ms emm !!! (BUT WITH qemm 7.04)
// READS ONLY THE LOWER DWORD
UNSIGNED LONG INT READ_TSC(VOID){$7b}
UNSIGNED LONG INT L,H;
ASM{$7b}
DB 0X0F, 0X31// OPCODE: RDTSC
DB 0X66; MOV WORD PTR L, AX// OPCODE: MOV L, EAX
DB 0X66; MOV WORD PTR H, DX // OPCODE: MOV H, EAX
{$7d}
RETURN L;
{$7d}
UNSIGNED LONG INT DIFF_tsc(UNSIGNED LONG INT LASTtsc, UNSIGNED LONG INT NEWtsc){$7b}
UNSIGNED LONG INT DIFF;
DIFF=NEWtsc-LASTtsc;
RETURN DIFF;
{$7d}
// SPECIAL
#ELSE
UNSIGNED LONG INT READ_TSC(VOID){$7b}
UNION{$7b}
UNSIGNED LONG INT TSC;
STRUCT{$7b}
SIGNED SHORT INT L;
SIGNED SHORT INT H;
{$7d} LH;
{$7d}lLH;
ASMPUSHF// sAVE CURRENT INTERRUPT FLAG
ASMCLI// dISABLE INTERRUPTS
// USING SYSTEM TIMER AND TICKS COUNTER INSTEAD OF RDTSC
ASMMOVAX,0X40
ASMMOVES,AX
ASMMOVDX,ES:[0X6c]
//lLH.LH.H=PEEK(0X40, 0X6c);
ASMMOVAL,0X00
ASMOUT0X43,AL// lATCH TIMER 0
ASMINAL,0X61// wASTE SOME TIME (WAITSTATED)
ASMINAL,0X40// cOUNTER --> BX
ASMMOVBL,AL// lsb IN bl
ASMINAL,0X61// wASTE SOME TIME (WAITSTATED)
ASMINAL,0X40
ASMMOVBH,AL// msb IN bh
ASMNOTBX// nEED ASCENDING COUNTER
ASMPOPF// rESTORE INTERRUPT FLAG
ASM MOVCL,15
ASMMOVDI,BX // iF THE msb OF THE SYSTEM TIMER
ASMSARDI,CL// IS SET, THEN THE SECOND
ASMANDDX,DI// MEASURED TICKS COUNTER VALUE
ASMNOTDI// IS SELECTED, OTHERWISE THE
ASMMOVAX,0X40
ASMMOVES,AX
ASMMOVCX,ES:[0X6c]
//H=PEEK(0X40, 0X6c);
ASMANDCX,DI// FIRST MEASURED TICKS COUNTER
ASMORDX,CX// VALUE IS SELECT AS HIGH WORD
ASM MOVAX,BX// SYSTEM TIMER VALUE IS LOW WORD
lLH.LH.L=_bx;
lLH.LH.H=_dx;
RETURN lLH.TSC;
{$7d}
UNSIGNED LONG INT DIFF_tsc(UNSIGNED LONG INT LASTtsc, UNSIGNED LONG INT NEWtsc){$7b}
UNSIGNED LONG INT DIFF;
DIFF=NEWtsc-LASTtsc;
DIFF+=(DIFF&0X80000000l)>>15;// UNDETECTED OVERFLOW FIX
RETURN DIFF;
{$7d}
#ENDIF
STATIC VOID PRINT_tZ_SINCE(VOID){$7b}
STATIC UNSIGNED LONG INT LASTtSC;
UNSIGNED LONG INT NEWtSC;
NEWtSC=READ_TSC();
PRINTF(" SYSTEM CLOCK CYCLES SINCE LAST: %10LD\N", DIFF_tsc(LASTtSC,NEWtSC));
LASTtSC=NEWtSC;
{$7d}
// SPECIAL
#ENDIF
// cbm1581 FLOPPY DISK PARAMETER SET
// lINUX: /ETC/FDPRM ENTRY FOR CBM1581 FILESYSTEM DRIVER
// # SIZE SEC/T HDS TRK STRE GAP RATE SPEC1 FMT_GAP
// CBM1581 1600 10 2 80 2 0X2a 0X02 0Xdf 0X2e
// ^
// bIT 2 OF THE STRETCH PARAMETER CONTROLS ON lINUX SYSTEMS, THAT
// THE DISK SIDE NUMBERS IN THE SECTOR HEADER id FIELDS ARE SWAPPED.
// tHE CBMFS-0.3 gap SIZES ARE NOT PERFECT. tHE ORIGINAL cbm 1581
// FORMAT gap SIZE IS: 0X23 AND A BETTER WORKING READ/WRITE gap SIZE
// IS 0X0c. i TAKE THESE VALUES AS STANDARDS FOR THE FOLLOWING
// DEFAULT INITIALISATION OF THE INTERNAL fdc PARAMETER SET TABLE.
fdcPARAMETERsET fdPRM={$7b}
80,// cYLINDERS: 80 CYLINDERS PER DISK
2,// hEADS: 2 SIDES (TRACKS) PER CYLINDER
10,// sECTORS: 10 SECTORS PER TRACK (SIDE)
2,// bPs: 512 BYTES PER SECTOR
2,// rATE: 250 KILOBIT PER SECOND
1,// mODULATION: mfm
0Xdf,// sPECIFYcmd
0X0c,// rwgap3: 12
0X23,// fMTgap: 35
0X00// pATTERN: 0
{$7d};
STATIC UINT8 fdcCOMMANDsTRING[9];
// FILLS UP A 9 BYTE COMMAND SEQUENCE WITH THE STANDARD PARAMETERS
//
// INPUT: NONE
// OUTPUT: NONE
//
STATIC VOID PREFILL9CMD(VOID){$7b}
fdcCOMMANDsTRING[5]=fdPRM.bPs;
fdcCOMMANDsTRING[6]=fdPRM.sECTORS;
fdcCOMMANDsTRING[7]=fdPRM.rwgap3;
fdcCOMMANDsTRING[8]=128;
{$7d}
// WAITS UNTIL THE fdc IS READY (COMMAND PHASE)
//
// INPUT: A POINTER TO A BYTE, WHERE THE LAST STATUS IS STORED TO
// THIS POINTER CAN BE null, TOO
// OUTPUT: 1, IF THE fdc IS READY FOR COMMAND TRANSFERS
// 0, IF A TIMEOUT OCCURED
//
INT WAITuNTILrEADY4tRANSFER(UINT8 *stat){$7b}// TIME OUT AFTER 440 MS
INT T,I=fdc_timeout;
UINT8 DUMMY;
IF(stat==null) stat=&DUMMY;
DO{$7b}
T=PEEKB(0X40, 0X6c);
WHILE(T==PEEKB(0X40, 0X6c)){$7b}
IF((*stat=INPORTB(fd_status))&0X80) RETURN 1;
{$7d}
{$7d}WHILE (I-->0);
RETURN 0;
{$7d}
// READ A BYTE FROM THE fdc DATA REGISTER
//
// INPUT: NONE
// OUTPUT: 0...255: THE CORRECT BYTE FROM THE fdc
// -1: A TIMEOUT OCCURED (READ OPERATION)
//
INT INfdc(VOID){$7b}
UINT8 STATUS;
RETURN WAITuNTILrEADY4tRANSFER(&STATUS)?INPORTB (fd_data):-1;
{$7d}
// CLEARS THE fdc COMMAND REGISTERS, WHEN A PREVIOUS OPERATION
// WAS ABORTED FOR SOME REASON AND THE fdc COULDN'T RETURN TO
// THE COMMAND PHASE
//
// INPUT: NONE
// OUTPUT: NONE
//
VOID CLEARfdcRESULTS(VOID){$7b}
WHILE((INPORTB(fd_status)&0Xc0)==0Xc0){$7b}
DELAY(2);
INfdc();// CLEAR fdc DATA REGISTER,
DELAY(2);// GET ALL RETURN VALUES
{$7d}
{$7d}
// WRITE A BYTE TO THE fdc DATA REGISTER
//
// INPUT: THE BYTE TO WRITE
// OUTPUT: 1: OPERATION WAS PERFORMED CORRECTLY
// -1: A TIMEOUT OCCURED (WRITE OPERATION)
//
INT OUTfdc(UINT8 DATA){$7b}
UINT8 STATUS;
DO{$7b}
CLEARfdcRESULTS();// GET OLD RESULT BYTES, IF THERE ARE ANY
IF(!WAITuNTILrEADY4tRANSFER(&STATUS)) RETURN -2;// fdc NOT RESPONDING (OUTfdc)
{$7d}WHILE(STATUS&0X40);
OUTPORTB(fd_data, DATA);
RETURN 1;
{$7d}
// PREPARE THE dma CONTROLLER FOR THE TRANSFER TO/FROM THE fdc
//
// INPUT: MODE: bITFIELDS FOR dma CHANNEL 0-3 MODE REGISTER (PORT 0X0b):
//
//bIT(S)dESCRIPTION
//
//7-6 TRANSFER MODE 5 DIRECTION
// ----------------------------------------------------
// 00 DEMAND MODE 0 ADDRESS INCREMENT SELECT
// 01 SINGLE MODE 1 ADDRESS DECREMENT SELECT
// 10 BLOCK MODE
// 11 CASCADE MODE
//
//3-2OPERATION1-0 CHANNEL NUMBER
// ----------------------------------------------------
// 00 VERIFY OPERATION 00 CHANNEL 0 SELECT
// 01 WRITE TO MEMORY 01 CHANNEL 1 SELECT
// 10 READ FROM MEMORY 10 CHANNEL 2 SELECT
// 11 RESERVED 11 CHANNEL 3 SELECT
//
// BYTES: THE NUMBER OF BYTES, THAT HAVE TO BE TRANSFERRED
// dmaBUF: THE POINTER TO THE dma TRANSFER BUFFER
// OUTPUT: NONE
//
VOID PREPAREdma(UINT8 MODE, UINT16 BYTES,
UINT8 HUGE *dmaBUF){$7b}
UNSIGNED LONGFLAT;
UNSIGNED SHORTPAGE, OFFS;
// NORMALIZE POINTER
FLAT= ((UNSIGNED LONG)fp_seg(dmaBUF)<<4)+fp_off(dmaBUF);
PAGE= FLAT>>16;
OFFS= FLAT&0Xffff;
// DECREMENT TO THE HIGHEST ("ARRAY") INDEX NUMBER
BYTES--;
#IFDEF disable_ints
DISABLE();
#ENDIF
OUTPORTB (0X81, PAGE);// dma CHANNEL 2 ADDRESS BYTE 2
OUTPORTB (0X0b, MODE);// dma CHANNEL 0-3 MODE REGISTER
OUTPORTB (0X0c, 0);// dma CLEAR BYTE POINTER FLIP-FLOP
OUTPORTB (0X04, OFFS&0Xff); // dma CHANNEL 2 ADDRESS BYTE 0
OUTPORTB (0X04, OFFS>>8); // dma CHANNEL 2 ADDRESS BYTE 1
OUTPORTB (0X05, BYTES&0Xff);// dma CHANNEL 2 WORD COUNT BYTE 0
OUTPORTB (0X05, BYTES>>8);// dma CHANNEL 2 WORD COUNT BYTE 1
OUTPORTB (0X0a, 2);// CLEAR MASK BIT OF dma CHANNEL 2
#IFDEF disable_ints
ENABLE();
#ENDIF
{$7d}
// WAIT UNTIL THE fdc SENDS AN irq
//
// INPUT: TIMEOUTTHE NUMBER OF TIMER TICKS AT LEAST TO WAIT FOR AN irq
// OUTPUT: NONE
//
VOID WAITuNTILiNTERRUPT(INT TIMEOUT){$7b}// TIME OUT AFTER 2 SECONDS
INT T;// ,I=op_timeout;
T=PEEKB(0X40, 0X6c);
DO{$7b}
IF(T!=PEEKB(0X40, 0X6c)){$7b}
T=PEEKB(0X40,0X6c);
IF(TIMEOUT--<0) BREAK;// WAIT _AT LEAST_ THE NUMBER OF TICKS
{$7d}
{$7d}WHILE(!(PEEKB(0X40,0X3e)&0X80));
POKEB(0X40,0X3e,PEEKB(0X40,0X3e)&0X7f);
{$7d}
// SWITCH THE MOTOR FROM THE SELECTED DRIVE TO ON
//
// INPUT: THE DRIVE SELECTED
// OUTPUT: NONE
//
VOID SWITCHmOTORoN(UINT8 DRIVE){$7b}
INT I;
// 0040H:0040H - diskette - motor turn-off timeout count
POKEB(0X40,0X40,0Xff);
// 0040H:003fH - diskette - motor status
IF(((I=PEEKB(0X40,0X3f))&(1<<DRIVE))==0){$7b}
OUTPORTB(fd_dor,(1<<(DRIVE+4)){$7c}(4+8){$7c}DRIVE);
POKEB(0X40,0X3f,I{$7c}(1<<DRIVE));
DELAY(1000);
POKEB(0X40,0X40,0Xff);
{$7d}
{$7d}
// SWITCH THE MOTORS FROM ALL DRIVES TO OFF
//
// INPUT: NONE
// OUTPUT: NONE
//
VOID SWITCHmOTORoFF(VOID){$7b}
// 0040H:0040H - diskette - motor turn-off timeout count
POKEB(0X40,0X40,55);
{$7d}
// SEND ALL SPECIFICATIONS FOR THE CURRENT DRIVE TO THE fdc
//
// INPUT: NONE
// OUTPUT: NONE
//
VOID SPECIFYdENSITY(VOID){$7b}
OUTPORTB(fd_dcr, fdPRM.bITRATE);// SELECT BITRATE (DISK DENSITY)
IF(OUTfdc(3)<0 {$7c}{$7c}// SPECIFY COMMAND
OUTfdc(fdPRM.sPECIFYcmd)<0 {$7c}{$7c}// HEAD UNLOAD TIME, STEP RATE TIME
OUTfdc(0X02)<0// HEAD LOAD TIME, SWITCH TO dma MODE
);// THIS IS A TRICK: IF ONE OPERATION FAILS, THE FOLLOWING ARE NOT DONE
{$7d}
// SELECTS A DRIVE FOR THE FOLLOWING OPERATIONS
//
// INPUT: NONE
// OUTPUT: NONE
//
VOID SELECTdRIVE(UINT8 DRIVE){$7b}
// 0040H:0040H - diskette - motor turn-off timeout count
POKEB(0X40,0X40,0Xff);
// SELECT DRIVE, RESET fdc, MOTOR ON
OUTPORTB(fd_dor,(1<<(DRIVE+4)){$7c}(4+8){$7c}DRIVE);
POKEB(0X40,0X3f,PEEKB(0X40,0X3f){$7c}(1<<DRIVE));
SPECIFYdENSITY();
{$7d}
// ISSUE A fdc COMMAND WITH dma TRANSFER (INTERNAL LIB USE ONLY)
//
// INPUT: cmdLEN: THE LENGTH OF THE COMMAND STRING
// dmaMODE: THE dma MODE TO SELECT FOR THIS COMMAND
// dmaBYTES: THE NUMBER OF BYTES TO TRANSFER WITH dma
// dmaBUFFER: THE dma TRANSFER BUFFER
// OUTPUT: 0 - TIMOUT WHILE SENDING A COMMAND TO THE fdc
// 1 - OPERATION WAS SUCCESSFUL
STATIC INT ISSUEcMD(UINT8 cmdLEN,
UINT8 dmaMODE, UINT16 dmaBYTES, UINT8 HUGE *dmaBUF){$7b}
INT I;
SELECTdRIVE(fdcCOMMANDsTRING[1]&3);
SWITCHmOTORoN(fdcCOMMANDsTRING[1]&3);// SET AGAIN TIMEOUT COUNT
#IF (messages >= 6)
FPRINTF(STDERR,"fdc COMMAND: 0X");
FOR(I=0;I<cmdLEN;I++) FPRINTF(STDERR,"%02x.",fdcCOMMANDsTRING[I]);
FPRINTF(STDERR," dma: 0X%02x, 0X%04x, 0X%05Lx\N",dmaMODE,dmaBYTES,
((UNSIGNED LONG)fp_seg(dmaBUF)<<4)+fp_off(dmaBUF));
// PRINTF("\N");
#ENDIF
PREPAREdma(dmaMODE, dmaBYTES, dmaBUF);
FOR(I=0;I<cmdLEN;I++)
IF(OUTfdc(fdcCOMMANDsTRING[I])<0) RETURN 0;
WAITuNTILiNTERRUPT(op_timeout);
RETURN 1;
{$7d}
// PERFORMS THE SENSE INTERRUPT STATUS COMMAND
//
// INPUT: POINTER TO THE RESULT STRING STRUCTURE
// OUTPUT: 1 - OPERATION WAS SUCCESSFUL
// 0 - OPERATION WAS UNSUCCESSFUL
// THE RESULT STRING HOLDS st0 AND THE cYLINDER VALUE
//
INT SENSEiNTERRUPTsTAT(RESULTsTRING *RESsTR){$7b}
INT RES;
IF(RESsTR==null) RETURN 0;
FOR(RES=0;RES<8;RES++) RESsTR->sTATUS[RES]=0;
IF(OUTfdc(8)>=0){$7b}// SENSE INTERRUPT STATUS COMMAND
RESsTR->d.st0=INfdc();
IF(RESsTR->d.st0>0){$7b}
RES=INfdc();
IF(RES>0){$7b}
RESsTR->d.cYLINDER=RES;
RETURN 1;
{$7d}
{$7d}
{$7d}
RETURN 0;
{$7d}
// PERFORMS A DRIVE RESET
//
// INPUT: THE DRIVE, THAT SHOULD BE RESETTED
// OUTPUT: NONE
//
VOID RESETdRIVE(UINT8 DRIVE){$7b}
RESULTsTRING rs;
POKEB(0X40,0X3e,PEEKB(0X40,0X3e)&0X7f);// CLEAR INTERRUPT BIT
OUTPORTB(fd_dor,(1<<(DRIVE+4)){$7c}DRIVE{$7c}8);// RESET
DELAY(2);
OUTPORTB(fd_dor,(1<<(DRIVE+4)){$7c}DRIVE{$7c}(8+4));// FINISH RESET
WAITuNTILiNTERRUPT(op_timeout);
SENSEiNTERRUPTsTAT(&rs);
{$7d}
// GETS A SEVEN BYTE RESULT STRING FROM THE fdc
//
// INPUT: POINTER TO THE RESULT STRING STRUCTURE
// OUTPUT: 1 - OPERATION WAS SUCCESSFUL
// 0 - OPERATION WAS UNSUCCESSFUL
// THE RESULT STRING FILLED UP WITH ALL THE VALUES
//
INT VALID7rESULTsTRING(RESULTsTRING *RESsTR){$7b}
INT I,RES;
IF(RESsTR==null) RETURN 0;
RESsTR->d.st0=INfdc();
IF(RESsTR->d.st0<0) RETURN 0;
FOR(I=2;I<8;I++){$7b}
RES=INfdc();
IF(RES<0){$7b}
RESsTR->d.st0=-1;
RETURN 0;
{$7d}
ELSE RESsTR->sTATUS[I]=RES;
{$7d}
// WONT BE NEEDED, BECAUSE st0 SHOULD REFLECT THE SAME
//IF(RESsTR->d.st1 {$7c}{$7c} RESsTR->d.st2) RETURN 0;
//
RES=RESsTR->d.st0&0Xc0;
RETURN !RES;
{$7d}
// PRINTS ALL VALUES FROM A RESULT STRING TO THE CONSOLE
//
// INPUT: POINTER TO THE RESULT STRING STRUCTURE
// OUTPUT: NONE
//
VOID PRINTrESULTsTRING(RESULTsTRING *RESsTR){$7b}
INT STR, T, BIT;
STATIC CHAR *STERR[2][8]={$7b}{$7b}
"ma (mISSING ADDRESS MARK)",
"nw (nON WRITABLE: WRITE PROTECT ERROR)",
"nd (nO DATA)",
"",
"or (oVERRUN)",
"de (dATA ERROR)",
"",
"en (eND OF CYLINDER)",
{$7d},{$7b}
"md (mISSING ADDRESS MARK IN DATA FIELD",
"bc (bAD CYLINDER)",
"sn (sCAN NOT SATISFIED)",
"se (sCAN EQUAL HIT)",
"wc (wRONG cYLINDER)",
"dd (dATA eRROR IN dATA fIELD)",
"cm (cONTROL mARK)",
""{$7d}{$7d};
IF(RESsTR==null) RETURN;
IF(RESsTR->d.st0<0) PRINTF("[st0 %4D] fdc DOESN'T ANSWER! error!\N",RESsTR->d.st0);
ELSE{$7b}
PRINTF("[st0 0X%02x] [st1 0X%02x] [st2 0X%02x] [cYL %3D] "
"[sIDE %1D] [sEC %2D] [bYTE %3D] : ",
RESsTR->d.st0, RESsTR->d.st1, RESsTR->d.st2,
RESsTR->d.cYLINDER, RESsTR->d.sIDE,
RESsTR->d.sECTOR, RESsTR->d.bYTESps);
RESsTR->d.st1&=0Xb7;
RESsTR->d.st2&=0X7f;
IF(RESsTR->d.st0&0Xc0)PRINTF("error!\N");
ELSEPRINTF("ok\N");
FOR(STR=2; STR<=3; STR++) FOR(BIT=0, T=1; BIT<8; BIT++, T<<=1){$7b}
IF(RESsTR->sTATUS[STR]&T)
PRINTF("st%C(%C): %S\N",STR+'0'-1,BIT+'0',STERR[STR-2][BIT]);
{$7d}
{$7d}
{$7d}
// PERFORMS A RECALIBRATION FOR THE SPECIFIED DRIVE (SEEK TO TRACK 0)
//
// INPUT: DRIVE: THE DRIVE, THAT SHOULD BE RECALIBRATED
// SIDE: THE DISK SIDE (HEAD), THAT SHOULD BE SELECTED
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *RECALIBRATEdRIVE(UINT8 DRIVE, UINT8 SIDE){$7b}
STATIC RESULTsTRING rs;
INT TRIES;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","REACLIBRATEdRIVE");
PRINT_tZ_SINCE();
#ENDIF
SELECTdRIVE(DRIVE);
SWITCHmOTORoN(DRIVE);// SET AGAIN TIMEOUT COUNT
RESETdRIVE(DRIVE);
//RESETdRIVE(DRIVE);
SPECIFYdENSITY();
FOR(TRIES=2;TRIES>0;TRIES--){$7b}
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(OUTfdc(0X07)<0 {$7c}{$7c}// RECALIBRATE COMMAND
OUTfdc(((({$7e}SIDE)&1)<<2){$7c}DRIVE)<0
)RETURN &rs;
WAITuNTILiNTERRUPT(op_timeout);
SENSEiNTERRUPTsTAT(&rs);
IF((rs.d.st0&0Xf0)==0X20 && rs.d.cYLINDER==0U){$7b}
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(OUTfdc(0X04)<0 {$7c}{$7c}// DRIVE STATUS COMMAND (READ st3)
OUTfdc(((({$7e}SIDE)&1)<<2){$7c}DRIVE)<0
)RETURN &rs;
IF(INfdc()&0X10) BREAK;// track0 BIT IS SET, THAT'S OK
{$7d}
{$7d}
RETURN (TRIES>0)?null:&rs;
{$7d}
// PERFORMS A SEEK FOR THE SPECIFIED DRIVE
//
// INPUT: DRIVE: THE DRIVE, THAT SHOULD DO THE SEEK
// CYL: THE CYLINDER TO SEEK TO
// SIDE: THE DISK SIDE (HEAD), THAT SHOULD BE SELECTED
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *SEEKtOcYL(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE){$7b}
STATIC RESULTsTRING rs;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","SEEKtOtRACK");
PRINT_tZ_SINCE();
#ENDIF
SELECTdRIVE(DRIVE);
SWITCHmOTORoN(DRIVE);// SET AGAIN TIMEOUT COUNT
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(OUTfdc(0X0f)<0 {$7c}{$7c}// SEEK COMMAND
OUTfdc(((({$7e}SIDE)&1)<<2){$7c}DRIVE)<0 {$7c}{$7c}
OUTfdc(CYL)<0
)RETURN &rs;
WAITuNTILiNTERRUPT(op_timeout);
SENSEiNTERRUPTsTAT(&rs);
IF(rs.d.st0&0Xc0){$7b}
DELAY(750);
SENSEiNTERRUPTsTAT(&rs);
{$7d}
RETURN((rs.d.st0&0Xf0)==0X20 && rs.d.cYLINDER==CYL)?null:&rs;
{$7d}
// GET THE DISK STATUS OF THE SPECIFIED DRIVE
//
// INPUT: DRIVE: THE DRIVE, THAT SHOULD DO THE SEEK
// OUTPUT: 0 - THERE'S NO DISK IN THE DRIVE
// 1 - THE OLD DISK IS IN (SOME OPERATIONS HAVE BEEN DONE BEFORE)
// 2 - A NEW DISK WAS INSERTED INTO THE DRIVE
//
INT SENSEdISKsTATUS(UINT8 DRIVE){$7b}
//SELECTdRIVE(DRIVE);// THIS MUST NOT BE DONE!!!
// PERHAPS WE SHOULD DO THIS IN THE OUTER LOOP, BUT i
// DON'T KNOW, IF THIS WOULD WORK WITH TWO DRIVES THEN
//
// tHE PROBLEM IS, THAT THE CONTENTS OF fd_status ARE
// ALWAYS 0X8x, WHEN i DO THE SELECTdRIVE(...)
SWITCHmOTORoN(DRIVE);// SET AGAIN TIMEOUT COUNT
IF(INPORTB(fd_dcr)&0X80){$7b}// READ "DISK CHANGE" BIT
DELAY(2);
SWITCH(INPORTB(fd_status)&0Xc0){$7b}
CASE 0Xc0:// THERE ARE fdc RESULTS TO READ
POKEB(0X40,0X3e,PEEKB(0X40,0X3e)&0X7f);// CLEAR INTERRUPT (TO BE SURE)
SEEKtOcYL(DRIVE,1,0);// CLEAR "DISK CHANGE" BIT, THIS
RECALIBRATEdRIVE(DRIVE,0);// CAN ONLY BE DONE BY DOING A SEEK
// IF DISK CHANGE BIT IS CLEARED ==> THERE'S A NEW DISK IS IN
IF(!(INPORTB(fd_dcr)&0X80)) RETURN 2;
// ISSUE READsECTORid
CASE 0X80:// fdc READY, BUT NO RESULTS
RECALIBRATEdRIVE(DRIVE,0);// GO TO A MOSTLY FORMATTED TRACK
// ISSUE A NEW READsECTORid COMMAND
// AND DON'T WAIT FOR ANY RESULTS
READsECTORid(DRIVE,0,null,-1);
//OUTfdc(0X0a {$7c} (fdPRM.mODULATION<<6));// ISSUE A NEW
//OUTfdc(((({$7e}0)&1)<<2) {$7c} DRIVE);// READsECTORid COMMAND
{$7d}
// AFTER EXECUTING READsECTORid, OR WHEN THE fdc
// IS BUSY (0B0XXXXXXX), THE DISK IS ASSUMED AS "OUT"
RETURN 0;
{$7d}
RETURN 1;// DISK CHANGE BIT CLEARED ==> OLD DISK IS IN
{$7d}
// READ THE NEXT POSSIBLE SECTOR HEADER id FROM THE CURRENT CYLINDER
//
// INPUT: DRIVE: THE DRIVE TO READ FROM
// SIDE: THE DISK SIDE (HEAD), THAT SHOULD BE SELECTED
// HEADER: A POINTER TO A RESULT STRUCTURE
// TIMEOUT: THE NUMBER OF TIMER TICKS TO WAIT AT LEAST FOR A
// SECTOR HEADER (TIMEOUT<0 MEANS TO _NEVER_ WAIT)
// OUTPUT: HEADER: THE RESULT STRING POINTED TO FILLED UP WITH THE RESULT
// RETURN: 0 - OPERATION FAILED
// 1 - OPERATION WAS SUCCESSFUL
//
INT READsECTORid(UINT8 DRIVE, UINT8 SIDE, RESULTsTRING *HEADER, INT TIMEOUT){$7b}
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","READsECTORid");
PRINT_tZ_SINCE();
#ENDIF
IF(HEADER==null && TIMEOUT>=0) RETURN 0;
SELECTdRIVE(DRIVE);
SWITCHmOTORoN(DRIVE);// SET AGAIN TIMEOUT COUNT
HEADER->d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(OUTfdc(0X0a {$7c} (fdPRM.mODULATION<<6))<0 {$7c}{$7c}
OUTfdc(((({$7e}SIDE)&1)<<2){$7c}DRIVE)<0
)RETURN 0;
IF(TIMEOUT<0) RETURN 0;// NO VALID RESULT, ONLY COMMAND ISSUEING
WAITuNTILiNTERRUPT(TIMEOUT);
RETURN VALID7rESULTsTRING(HEADER);
{$7d}
// FORMAT A TRACK (ONE SIDE OF A CYLINDER)
//
// INPUT: DRIVE: THE DRIVE TO FORMAT A TRACK AT
// CYL: THE CYLINDER NUMBER TO BE WRITTEN INTO THE HEADERS
// SIDE: THE DISK SIDE NUMBER TO BE WRITTEN INTO THE HEADERS
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *FORMATcbmTRACK(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE){$7b}
STATIC RESULTsTRING rs;
// 4 id BYTES PER SECTOR
UINT8 fMTbUFFER[mAXsECTORS * 4];// mAXsECTORS IS DEFINED IN central.h
INTI,J;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","FORMATc1581TRACK");
PRINT_tZ_SINCE();
#ENDIF
FOR(I=J=0;I<fdPRM.sECTORS;){$7b}// FILL UP THE id BYTES
I++;// START WITH SECTOR 1
fMTbUFFER[J++]=CYL;// CYLINDER DESCRIPTOR
fMTbUFFER[J++]=SIDE;// HEAD DESCRIPTOR
fMTbUFFER[J++]=I;// SECTOR nO.
fMTbUFFER[J++]=fdPRM.bPs;
{$7d}
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0X0d;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=fdPRM.bPs;
fdcCOMMANDsTRING[3]=fdPRM.sECTORS;
fdcCOMMANDsTRING[4]=fdPRM.fMTgap;
fdcCOMMANDsTRING[5]=fdPRM.pATTERN;
IF(!ISSUEcMD(6,0X4a,fdPRM.sECTORS<<2,fMTbUFFER)){$7b}
rs.d.st0=-2;// fdc NOT RESPONDING
RETURN &rs;
{$7d}
ELSE{$7b}
RETURN VALID7rESULTsTRING(&rs)?null:&rs;
{$7d}
{$7d}
// READ A SECTOR
//
// INPUT: DRIVE: THE DRIVE TO READ FROM
// CYL: THE CYLINDER NUMBER TO READ FROM
// SIDE: THE DISK SIDE NUMBER TO READ FROM
// SECTOR: THE SECTOR NUMBER TO READ
// BUFFER: A POINTER TO A BUFFER TO READ THE DATA IN
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *READcbmSECTOR(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE,
UINT8 SECTOR, UINT8 HUGE *BUFFER){$7b}
STATIC RESULTsTRING rs;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","READcbmSECTOR");
PRINT_tZ_SINCE();
#ENDIF
PREFILL9CMD();// FILL UP BYTES 5 TO 8
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0X06;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=CYL;
fdcCOMMANDsTRING[3]=SIDE;
fdcCOMMANDsTRING[4]=SECTOR;
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X46,128U<<fdPRM.bPs,BUFFER) &&
VALID7rESULTsTRING(&rs)
)RETURN null;
ELSE RETURN &rs;
{$7d}
// WRITE A SECTOR
//
// INPUT: DRIVE: THE DRIVE TO WRITE TO
// CYL: THE CYLINDER NUMBER TO WRITE TO
// SIDE: THE DISK SIDE NUMBER TO WRITE TO
// SECTOR: THE SECTOR NUMBER TO WRITE
// BUFFER: A POINTER TO A BUFFER TO WRITE THE DATA FROM
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *WRITEcbmSECTOR(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE,
UINT8 SECTOR, UINT8 HUGE *BUFFER){$7b}
STATIC RESULTsTRING rs;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","WRITEcbmSECTOR");
PRINT_tZ_SINCE();
#ENDIF
PREFILL9CMD();// FILL UP BYTES 5 TO 8
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0X05;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=CYL;
fdcCOMMANDsTRING[3]=SIDE;
fdcCOMMANDsTRING[4]=SECTOR;
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X4a,128U<<fdPRM.bPs,BUFFER) &&
VALID7rESULTsTRING(&rs)
)RETURN null;
ELSE RETURN &rs;
{$7d}
// VERIFY A SECTOR
//
// INPUT: DRIVE: THE DRIVE TO VERIFY AT
// CYL: THE CYLINDER NUMBER TO VERIFY AT
// SIDE: THE DISK SIDE NUMBER TO VERIFY AT
// SECTOR: THE SECTOR NUMBER TO VERIFY
// BUFFER: A POINTER TO A BUFFER TO VERIFY THE DATA WITH
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
RESULTsTRING *VERIFYcbmSECTOR(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE,
UINT8 SECTOR, UINT8 HUGE *BUFFER){$7b}
STATIC RESULTsTRING rs;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","VERIFYcbmSECTOR");
PRINT_tZ_SINCE();
#ENDIF
// lET THE fdc crc CHECK THE WRITTEN DATA
// "rEAL" COMPARISON CANNOT BE DONE
PREFILL9CMD();// FILL UP BYTES 5 TO 8
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0X06;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=CYL;
fdcCOMMANDsTRING[3]=SIDE;
fdcCOMMANDsTRING[4]=SECTOR;
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X42,128U<<fdPRM.bPs,BUFFER) &&
VALID7rESULTsTRING(&rs)
)RETURN null;
ELSE RETURN &rs;
{$7d}
// READ A TRACK WITH THE MULTIPLE SECTOR READING FEATURE OF THE fdc
//
// INPUT: DRIVE: THE DRIVE TO READ FROM
// CYL: THE CYLINDER NUMBER TO READ FROM
// SIDE: THE DISK SIDE NUMBER TO READ FROM
// BUFFER: A POINTER TO A BUFFER TO READ THE DATA IN
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *READcbmTRACK(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE,
UINT8 SECTOR, UINT8 HUGE *BUFFER){$7b}
STATIC RESULTsTRING rs;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","READcbmTRACK");
PRINT_tZ_SINCE();
#ENDIF
PREFILL9CMD();// FILL UP BYTES 5 TO 8
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0X06;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=CYL;
fdcCOMMANDsTRING[3]=SIDE;
fdcCOMMANDsTRING[4]=SECTOR;// START SECTOR TO READ
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X46,(128U << fdPRM.bPs) * (fdPRM.sECTORS - SECTOR + 1),
BUFFER + (128U << fdPRM.bPs) * (SECTOR - 1)) &&
VALID7rESULTsTRING(&rs)
){$7b}// SECOND HALF OF THE TRACK WAS READ WITH SUCCESS
IF(SECTOR<=1) RETURN null;// WHOLE TRACK IS ALREADY IN
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","READcbmTRACK");
PRINT_tZ_SINCE();
#ENDIF
fdcCOMMANDsTRING[4]=1;// BEGIN WITH THE FIRST HALF OF THE TRACK
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X46,(128U << fdPRM.bPs) * (SECTOR - 1),BUFFER) &&
VALID7rESULTsTRING(&rs)
)RETURN null;// FIRST HALF OF TRACK WAS READ WITH SUCCESS
{$7d}
RETURN &rs;// ERROR
{$7d}
// WRITE A TRACK WITH THE MULTIPLE SECTOR READING FEATURE OF THE fdc
//
// INPUT: DRIVE: THE DRIVE TO WRITE TO
// CYL: THE CYLINDER NUMBER TO WRITE TO
// SIDE: THE DISK SIDE NUMBER TO WRITE TO
// BUFFER: A POINTER TO A BUFFER TO WRITE THE DATA FROM
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *WRITEcbmTRACK(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE,
UINT8 SECTOR, UINT8 HUGE *BUFFER){$7b}
STATIC RESULTsTRING rs;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","WRITEcbmTRACK");
PRINT_tZ_SINCE();
#ENDIF
PREFILL9CMD();// FILL UP BYTES 5 TO 8
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0X05;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=CYL;
fdcCOMMANDsTRING[3]=SIDE;
fdcCOMMANDsTRING[4]=SECTOR;// START SECTOR TO READ
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X4a,(128U << fdPRM.bPs) * (fdPRM.sECTORS - SECTOR + 1),
BUFFER + (128U << fdPRM.bPs) * (SECTOR - 1)) &&
VALID7rESULTsTRING(&rs)
){$7b}// SECOND HALF OF THE TRACK WAS READ WITH SUCCESS
IF(SECTOR<=1) RETURN null;// WHOLE TRACK IS ALREADY IN
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","WRITEcbmTRACK");
PRINT_tZ_SINCE();
#ENDIF
fdcCOMMANDsTRING[4]=1;// BEGIN WITH THE FIRST HALF OF THE TRACK
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X4a,(128U << fdPRM.bPs) * (SECTOR - 1),BUFFER) &&
VALID7rESULTsTRING(&rs)
){$7b}
RETURN null;// FIRST HALF OF TRACK WAS READ WITH SUCCESS
{$7d}
{$7d}
RETURN &rs;// ERROR
{$7d}
// VERIFY A TRACK WITH THE MULTIPLE SECTOR READING FEATURE OF THE fdc
//
// INPUT: DRIVE: THE DRIVE TO READ FROM
// CYL: THE CYLINDER NUMBER TO READ FROM
// SIDE: THE DISK SIDE NUMBER TO READ FROM
// BUFFER: A POINTER TO A BUFFER TO READ THE DATA IN
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *VERIFYcbmTRACK(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE,
UINT8 SECTOR, UINT8 HUGE *BUFFER){$7b}
STATIC RESULTsTRING rs;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","VERIFYcbmTRACK");
PRINT_tZ_SINCE();
#ENDIF
// lET THE fdc crc CHECK THE WRITTEN DATA
// "rEAL" COMPARISON CANNOT BE DONE
PREFILL9CMD();// FILL UP BYTES 5 TO 8
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0X06;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=CYL;
fdcCOMMANDsTRING[3]=SIDE;
fdcCOMMANDsTRING[4]=SECTOR;// START SECTOR TO READ
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X42,(128U << fdPRM.bPs) * (fdPRM.sECTORS - SECTOR + 1),
BUFFER + (128U << fdPRM.bPs) * (SECTOR - 1)) &&
VALID7rESULTsTRING(&rs)
){$7b}// SECOND HALF OF THE TRACK WAS READ WITH SUCCESS
IF(SECTOR<=1) RETURN null;// WHOLE TRACK IS ALREADY IN
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","VERIFYcbmTRACK");
PRINT_tZ_SINCE();
#ENDIF
fdcCOMMANDsTRING[4]=1;// BEGIN WITH THE FIRST HALF OF THE TRACK
rs.d.st0=-2;// DEFAULT: fdc NOT RESPONDING
IF(ISSUEcMD(9,0X42,(128U << fdPRM.bPs) * (SECTOR - 1),BUFFER) &&
VALID7rESULTsTRING(&rs)
)RETURN null;// FIRST HALF OF TRACK WAS READ WITH SUCCESS
{$7d}
RETURN &rs;// ERROR
{$7d}
// CHECK IF WE ARE USING AN INTEL EXTENDED CONTROLLER 82078
//
// INPUT: NONE
// OUTPUT: -1 (<0) TIMEOUT OR GENERAL ERROR
// 0 STANDARD nec {$e6}pd 765 COMPATIBLE CONTROLLER
// 0X01XX EXTENDED CONTROLLER (XX - STEPPING RESULT)
// 0X02XX 82078 COMPATIBLE CONTROLLER (XX - STEPPING RESULT)
// 0X03XX FULLY 82078 COMPATIBLE fdc (XX - STEPPING RESULT)
//
SHORT CHECK4INTELfdc(VOID){$7b}
SHORT RES;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","CHECK4INTELfdc");
PRINT_tZ_SINCE();
#ENDIF
IF(OUTfdc(0X10)<0) RETURN -1;// ISSUE VERSION COMMAND
IF((RES=INfdc())<0) RETURN -1;
IF(RES==0X90){$7b}// THIS IS AN EXTENDED CONTROLLER
IF(OUTfdc(0X18)<0) RETURN -1;// ISSUE PART ID COMMAND
IF((RES=INfdc())<0) RETURN -1;
IF(RES>=0X41 && RES<=0X5f){$7b}
// DO SOME MORE INTEL fdc COMPATIBILITY TESTS
IF(RES==0X41) RES{$7c}=0X0300;// ORIGINAL (FIRST STEPPING)
ELSE RES{$7c}=0X0200;
{$7d}
ELSE RES{$7c}=0X0100;
RETURN RES;
{$7d}
ELSE RETURN 0X0000;
{$7d}
// FORMAT'N'WRITE A TRACK (ONE SIDE OF A CYLINDER)
//
// INPUT: DRIVE: THE DRIVE TO FORMAT A TRACK AT
// CYL: THE CYLINDER NUMBER TO BE WRITTEN INTO THE HEADERS
// SIDE: THE DISK SIDE NUMBER TO BE WRITTEN INTO THE HEADERS
// BUFFER: A POINTER TO A BUFFER TO WRITE THE DATA FROM
// OUTPUT: RESULTsTRING: null - OPERATION WAS SUCESSFUL
// OTHER - THE FILLED UP RESULT STRUCTURE
//
RESULTsTRING *FNWRITEcbmTRACK(UINT8 DRIVE, UINT8 CYL, UINT8 SIDE, UINT8 HUGE *BUFFER){$7b}
STATIC RESULTsTRING rs;
// 4 id BYTES PER SECTOR
UINT8 fNwbUFFER[mAXsECTORS * 4 + tRbUFsIZE];// mAXsECTORS AND tRbUFsIZE ARE DEFINED IN central.h
INTI,J,K,L;
#IFDEF use_tsc
FPRINTF(STDERR,"%20S","FNWRITEc1581TRACK");
PRINT_tZ_SINCE();
#ENDIF
FOR(I=J=K=0;I<fdPRM.sECTORS;){$7b}// FILL UP THE id BYTES AND ENCAPSULATED WRITE DATA
I++;// START WITH SECTOR 1
fNwbUFFER[J++]=CYL;// CYLINDER DESCRIPTOR
fNwbUFFER[J++]=SIDE;// HEAD DESCRIPTOR
fNwbUFFER[J++]=I;// SECTOR nO.
fNwbUFFER[J++]=fdPRM.bPs;
FOR(L=(128 << fdPRM.bPs);L>0;L--) fNwbUFFER[J++]=BUFFER[K++];
// DEBUG
// FOR(L=(128 << fdPRM.bPs);L>0;L--) fNwbUFFER[J++]=K++;
{$7d}
// THIS COMES FROM THE LINUX FLOPPY DRIVER, PERHAPS THERE'S A TYPO
// IN THE INTEL 82078 DATA SHEET ABOUT THE FORMAT'N'WRITE COMMAND
// fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0Xaf;
// iT'S NO TYPO...
fdcCOMMANDsTRING[0]=(fdPRM.mODULATION<<6) {$7c} 0Xad;
fdcCOMMANDsTRING[1]=((({$7e}SIDE)&1)<<2) {$7c} DRIVE;
fdcCOMMANDsTRING[2]=fdPRM.bPs;
fdcCOMMANDsTRING[3]=fdPRM.sECTORS;
fdcCOMMANDsTRING[4]=fdPRM.fMTgap;
fdcCOMMANDsTRING[5]=fdPRM.pATTERN;
IF(!ISSUEcMD(6,0X4a,fdPRM.sECTORS*(4+(128 << fdPRM.bPs)),fNwbUFFER)){$7b}
rs.d.st0=-2;// fdc NOT RESPONDING
RETURN &rs;
{$7d}
ELSE{$7b}
RETURN VALID7rESULTsTRING(&rs)?null:&rs;
{$7d}
{$7d}