Program FlopTest; { debug software for new floppy controller } { works with drive 0 only.} { NOTE: this will work only with the new floppy controller.} { this is the one after march 25.} {Changed on 29 April for inverted device select enable} {bit in local register.} { June 17, 1983 kb } uses {$U /ccutil/cclib} CCDefn; const {good for both 8 and 5 1/4 inch} BufStart = 0; SDBufEnd = 127; DDBufEnd = 512; MtrON = TRUE; MtrOFF = FALSE; RdDMA = TRUE; WrDMA = FALSE; Slot0 = $30001; OFFSET = $20; {indices off BaseAddr} CtlDATAreg = 0; CtlSTATUSreg = 2; CtlCNTR = 4; CtlBUFFER = 6; CtlLOCALreg = 8; { FDC status register } Drv0BUSY = 1; {drive 0 busy} Drv1BUSY = 2; {drive 1 busy} Drv2BUSY = 4; {drive 2 busy} Drv3BUSY = 8; {drive 3 busy} FDCBUSYbit = 16; {read or write cmd in progress} NDMAbit = 32; {Non-DMA mode operation} DATADDRbit = 64; {data direction bit,input or output} FDCRDYbit = 128; {ready to send/rcv data at data reg} { FDC status register 0 } INTcode1 = 128; INTcode0 = 64; SEEKEND = 32; EquipChk = 16; FDDNRdy = 8; HdAddr = 4; Unit0 = 2; Unit1 = 1; { FDC status register 0 } CTLmark = 64; { FDC status register 3 } TWOSIDEbit = 8; {if set drive is 2 sided} { Local card register - command bits} {IMPORTANT NOTE:} {motor off bit only used for 5 1/4 inch drive} {all 8 inch drives must be configured such that the device} {select turns on the motor. The motoron line will be inverted } {for the TANDON definition of the line but will be correct for} {the MPI 92. the motoron line, for now, is connected to the} {NHDLD/NMOTON line, pin 18, of the MPI interface.} DevSel0 = 1; {Drive select bit 0} DevSel1 = 2; {Drive select bit 1} DvSenbBit = 4; {Enable device select circuitry} MOTOROFF = 8; {controller register motor on bit} EnblINTbit= 32; {enable interrupt to 68K } DMARWbit = 64; {dma direction, 1=read, 0=write} RESETbit = 128; {reset FDC} { Local card register - status bits} DISKCHG = 1; NIRQbit = 2; {IRQ when = 0} NDrvRDY = 64; NMINIbit = 128; {0= 5 1/4 inch flop, 1=8 inch} { read and write command bytes } SDNUMbytes = 128; { number of bytes per sector - FM } DDNUMbytes = 512; { number of bytes per sector - MFM } SDDTL = 128; DDDTL = 256; { when NUMbytes <> 0 then $FF} EOT8 = 26; { 8 INCH final sector number on track } EOT5 = 9; { 5 1/4 INCH final sector number on track } SIDE0 = 0; { side number } { old floppy controller driver writes Gap 3 length of 27 } SDGPL = 7; { gap 3 length for read/write } { suggested by ap note for gap of 27 } DDGPL = $1B; { gap 3 length for read/write } { suggested by ap note for gap of 54 } {gap 3 lengths are good for both 5 and 8 inch} SDGAP3LEN = $1B; {27 byte gap 3 len for format cmd - FM } DDGAP3LEN = $36; {54 byte gap 3 len for format cmd - MFM } {command bits} MFMbit = 64; { read/write with MFM - double density} {type differences} ENDTRK8 = 76; ENDTRK5 = 79; {96 TPI - MPI model 92} {floppy type indicators} EIGHTINCH = 0; FIVEINCH = 1; {DENSITY indicators} SGLDNSTY = 0; DBLDNSTY = 64; {SAME AS MFMbit} INTTOint = 100000; {????} TOint = 10000; {????} type buffer = array[BufStart..DDBufEnd] of byte; bf512 = array[0..511] of byte; cmdbuf = array[1..40] of byte; opstatus = (LCLERR, WRCERR, RDCERR, FNCERR, FAILED, GOOD); str10 = string[10]; var track,sector,slot,drive: integer; secinwr,secinrd: integer; density,head,floptype: integer; spcfycmd: cmdbuf; RdBuf,WrBuf: buffer; BaseAddr: pBytes; hex: string[16]; TPI48: boolean; function Ucase(c: char) : char; begin if (c>='a') and (c<='z') then c := chr( ord(c) - $20 ); Ucase := c; end; function cnvtob(i: integer) : byte; begin i := i mod 256; if i>127 then cnvtob := i - 256 else cnvtob := i; end; function cnvtoi(b: byte) : integer; begin if b<0 then cnvtoi := 256 + b else cnvtoi := b; end; procedure gotoxy(x,y: integer); begin write('\1B=',chr(cnvtob(x)),chr(cnvtob(y))); end; procedure clrline; begin write('\1BK'); end; function cnv2ch(b : byte) : char; var i:integer; begin i := cnvtoi(b); if ((i<$20) or (i>$7F)) then cnv2ch := '.' else cnv2ch := chr(i); end; function IsBreakPress : boolean; var b: integer; begin unitstatus(35,b,1); {test and clear break flag} IsBreakPress := (b = 1); end; function BuildCmd(c: byte) : byte; begin BuildCmd := cnvtob( (cnvtoi(c) or density) ); end; procedure showbyte(b: byte); var i,hi: integer; begin i := cnvtoi(b); hi := i div 16; write( hex[hi + 1] ); write( hex[ (i - (hi*16)) + 1 ] ); end; procedure showcmd(cmd: cmdbuf; cnt: integer); var i,b: integer; begin for i := 1 to cnt do begin b := cnvtoi(cmd[i]); write('Command array byte ',i:1,' = '); showbyte(cmd[i]); writeln; end; end; procedure DumpBuf(B: buffer); var i,j,k,n: integer; strg : string[16]; s: string[1]; begin n := 0; s := ' '; strg := ''; if density = SGLDNSTY then k := SDBufEnd else k := DDBufEnd; BaseAddr^[ CtlCNTR ] := cnvtob(k DIV 2); for i := BufStart to k do begin showbyte(B[i]); write(' '); s[1] := cnv2ch(B[i]); strg := concat(strg,s); n := n + 1; if (n mod 16) = 0 then begin write(' '); writeln(strg); strg := ''; end; end; end; procedure cnvitos(i: integer; var s: str10); var k: integer; numbers: str10; s1: string[1]; begin if i = 0 then s := '0' else begin s := ''; s1 := 'x'; numbers := '0123456789'; while (i>0) do begin k := i mod 10; i := i div 10; s1[1] := numbers[k+1]; s := concat(s1,s); end; end; end; procedure BuildWRbuf(var wrb: buffer; sector,trk: integer); var tstr,scstr: str10; line: String80; i,k: integer; begin cnvitos(trk,tstr); cnvitos(sector,scstr); line := concat('Track: ',tstr,' Sector: ',scstr); k := 1; for i:=0 to DDBufEnd do begin wrb[i] := cnvtob(ord(line[k])); k := (k mod (length(line)) ) + 1; end; end; function RdController(var Cmd: cmdbuf; n: integer) : boolean; {read n bytes into cmd buffer from controller data register} {returns true if did read, false if timeout} var Ready, timeout: boolean; tmot: longint; i,sb: integer; begin i := 1; repeat tmot := 0; repeat sb := cnvtoi(BaseAddr^[ CtlSTATUSreg ]); Ready := ( ((DATADDRbit + FDCRDYbit) and sb) = (DATADDRbit + FDCRDYbit) ); tmot := tmot + 1; timeout := (tmot > TOint); until ((Ready) or (timeout)); if NOT timeout then Cmd[i] := BaseAddr^[ CtlDATAreg ]; i := i + 1; until ( (timeout) or (i>n) ); RdController := NOT timeout; end; function WrController(Cmd: cmdbuf; n: integer) : boolean; {write n bytes from cmd buffer to controller data register} {returns true if did write, false if timeout} var Ready, timeout: boolean; tmot: longint; i,sb: integer; begin i := 1; repeat tmot := 0; repeat sb := cnvtoi(BaseAddr^[ CtlSTATUSreg ]); Ready := ( ((DATADDRbit + FDCRDYbit) and sb) = (FDCRDYbit) ); tmot := tmot + 1; timeout := (tmot > TOint); until ((Ready) or (timeout)); if NOT timeout then BaseAddr^[ CtlDATAreg ] := Cmd[i]; i := i + 1; until ( (timeout) or (i>n) ); WrController := NOT timeout; end; function SenseDriveStatus(hd,drv: integer; Var ST3: integer): boolean; {true if worked, false if failed} var cmd: cmdbuf; begin SenseDriveStatus := FALSE; cmd[1] := $04; {sense drive stat cmd} {shift head left 2 bits and add drv number} cmd[2] := cnvtob( (hd * 4) or drv ); if WrController(cmd,2) then if RdController(cmd,1) then begin ST3 := cnvtoi(cmd[1]); SenseDriveStatus := TRUE; end; end; function SetLocalReg(Motor,DMAdir: boolean) : boolean; {returns ture if done, false if timeout} var b,sb,ST3: integer; Ready, timeout: boolean; tmot: longint; begin {5 1/4 drives do not have a ready line. Controller ties down line} {to always ready. MPI model 92 has a 500 ms. motor on delay} {Can either turn off and on motor and wait 500 ms. or leave motor on } {and never wait. } {assume drive 0,Enable device select,motor on,disable int,write dma and normal} b := DvSenbBit; {shift drive number to the left and put in reg} b := (b or (drive * 2)); {should turn off motor and deselect drive to guarantee } {motor is turned off. ?? will 5 1/4 not turn motor off if } {deselected same time turn off motor.} if (floptype = EIGHTINCH) then {clear motr off bit and set dev sel enbl bit} if Motor = MtrOFF then b := ( MOTOROFF or ((NOT DvSenbBit) and b) ); {we are always leaving the motor on for 5 1/4} if DMAdir = RdDMA then b := (DMARWbit or b); BaseAddr^[ CtlLOCALreg ] := cnvtob(b); if Motor = MtrOFF then SetLocalReg := TRUE else begin if floptype = EIGHTINCH then begin {turn on side select - 0} {must do before do r/w after do seek} Ready := SenseDriveStatus(head,0,ST3); {wait for drive to be ready} tmot := 0; repeat sb := cnvtoi(BaseAddr^[ CtlLOCALreg ]); Ready := (NDrvRDY and sb) = 0; tmot := tmot + 1; timeout := (tmot > TOint); until( (Ready) or (timeout) ); SetLocalReg := Ready; end else begin {wait 500 ms. or always leave motor on} SetLocalReg := TRUE; end; end; end; procedure MoveIN(WrBuf: buffer); {move data from write buffer to controller} { assumes 1st byte of buffer must go into controller} { buffer first. Data is DMAed into (out of) buffer} { starting at high counter and ending at low counter} { low value = 0 } { down count from end to beginning } var i,k: integer; begin { setup counter address } if density = SGLDNSTY then k := SDBufEnd else k := DDBufEnd; BaseAddr^[ CtlCNTR ] := cnvtob(k DIV 2); for i := BufStart to k do BaseAddr^[ CtlBUFFER ] := WrBuf[i]; end; procedure MoveOUT(var RdBuf: buffer); {move data from controller to read buffer} { assumes 1st byte of buffer must go into controller} { buffer first. Data is DMAed into(out of) buffer} { starting at high counter and ending at low counter} { low value = 0 } { down count from end to beginning } var i,k: integer; begin { setup counter address } if density = SGLDNSTY then k := SDBufEnd else k := DDBufEnd; BaseAddr^[ CtlCNTR ] := cnvtob(k DIV 2); for i := BufStart to k do RdBuf[i] := BaseAddr^[ CtlBUFFER ]; end; function WaitFDC : boolean; {returns true if got interrupt} {and false if timeout} var Ready,timeout: boolean; tmot: longint; sb: integer; begin tmot := 0; repeat sb := cnvtoi(BaseAddr^[ CtlLOCALreg ]); Ready := ( (NIRQbit and sb) = 0 ); tmot := tmot + 1; timeout := (tmot > INTTOint); until ((Ready) or (timeout)); WaitFDC := NOT timeout; end; function SenseInt(Var ST0, PCN: integer) : boolean; {gets status register 0 to find out what caused } {interrupt. PCN is the track head is over. } {returns true if completed operation. } var cmd: cmdbuf; begin SenseInt := FALSE; ST0 := 0; PCN := 0; cmd[1] := 8; {sense int status command} if WrController(cmd,1) then begin if RdController(cmd,2) then begin SenseInt := TRUE; ST0 := cnvtoi(cmd[1]); PCN := cnvtoi(cmd[2]); end else writeln(chr(7),'Timed out on controller read'); end else writeln(chr(7),'Timed out on controller write'); end; function Track0(var ST0,PCN: integer): opstatus; {the FDC will stop the restore if it sends 77 step signals} {without getting NTRK0 saying on track 0. The 5 1/4 has 80 tracks} {and therefore the FDC will not correctly restore the driver} {when the head is beyond track 77.} var cmd: cmdbuf; cnt: integer; OK: boolean; begin PCN := 0; {setup cmd array} cmd[1] := cnvtob($07); {restore command} cmd[2] := cnvtob($00); {drive 0 select} cnt := 2; if NOT SetLocalReg(MtrON,RdDMA) then begin Track0 := LCLERR; OK := SetLocalReg(MtrOFF,RdDMA); exit(Track0); end; if WrController(cmd,cnt) then begin if NOT WaitFDC then begin Track0 := FNCERR; OK := SetLocalReg(MtrOFF,RdDMA); exit(Track0); end; if SenseInt(ST0, PCN) then {if fails reports to user} begin OK := ((INTcode1 + INTcode0 + SEEKEND + EquipChk) and ST0) = (SEEKEND); if NOT OK then Track0 := FAILED else Track0 := GOOD; end; end else Track0 := WRCERR; OK := SetLocalReg(MtrOFF,RdDMA); end; function SeekTrk(track: integer; var ST0,PCN: integer): opstatus; var cmd: cmdbuf; cnt,t: integer; OK: boolean; begin {setup cmd array} cmd[1] := cnvtob($0F); {seek command} cmd[2] := cnvtob( (head * 4) or $00 ); {drive 0 select} t := track; if TPI48 then t := t * 2; cmd[3] := cnvtob(t); cnt := 3; if NOT SetLocalReg(MtrON,RdDMA) then begin SeekTrk := LCLERR; OK := SetLocalReg(MtrOFF,RdDMA); exit(SeekTrk); end; if WrController(cmd,cnt) then begin if NOT WaitFDC then begin SeekTrk := FNCERR; OK := SetLocalReg(MtrOFF,RdDMA); exit(SeekTrk); end; if SenseInt(ST0, PCN) then {if fails reports to user} begin OK := ((INTcode1 + INTcode0 + SEEKEND + EquipChk) and ST0) = (SEEKEND); if NOT OK then SeekTrk := FAILED else SeekTrk := GOOD; end; end else SeekTrk := WRCERR; OK := SetLocalReg(MtrOFF,RdDMA); end; procedure SetupCmdRW(var cmd: cmdbuf; s,t: integer); var n,d,g,e: integer; begin {select states for density differences} if density = SGLDNSTY then begin { N = 00 when 128 bytes/sector and FM} {Since FM N = 0 must put bytes/sector in DTL} n := SDNumbytes; d := SDDTL; g := SDGPL; end else begin n := DDNumbytes; d := DDDTL; g := DDGPL; end; {select states for different floppy types} if floptype = EIGHTINCH then e := EOT8 else e := EOT5; cmd[2] := cnvtob( (head * 4) or $00 ); {drive 0 select} cmd[3] := cnvtob(t); {track} cmd[4] := cnvtob(head); {side} cmd[5] := cnvtob(s); {sector} cmd[6] := cnvtob(2);{(n - 1) div 128);} {# bytes per sector code} cmd[7] := cnvtob(e); {last sector in track} cmd[8] := cnvtob(g); {gap 3 wait byte time} cmd[9] := cnvtob(d); {DTL read/write len differ from sector length} end; function Read1(var RB: buffer; var cmd: cmdbuf; s,t: integer) : opstatus; {s = sector number, t = track number} var cnt,ST0,ST1,a: integer; OK: boolean; begin if NOT SetLocalReg(MtrON,RdDMA) then begin Read1 := LCLERR; OK := SetLocalReg(MtrOFF,RdDMA); exit(Read1); end; {setup cmd array and controller <= bufferptr} SetupCmdRW(cmd,s,t); cmd[1] := BuildCmd($06); {MT=0,SK=0,READ DATA add density bit} cnt := 9; {set up buffer address} if density = SGLDNSTY then a := SDBufEnd else a := DDBufEnd; BaseAddr^[ CtlCNTR ] := cnvtob(a DIV 2); if WrController(cmd,cnt) then begin if WaitFDC then begin if RdController(cmd,7) then begin {see if cmd done} ST0 := cnvtoi(cmd[1]); OK := ((INTcode1 + INTcode0) and ST0) = 0; {if is done then move data from controller} { to read buffer} if OK then begin MoveOUT(RB); Read1 := GOOD; end else Read1 := FAILED; end else Read1 := RDCERR; end else Read1 := FNCERR; end else Read1 := WRCERR; OK := SetLocalReg(MtrOFF,RdDMA); end; function Write1(WB: buffer; var cmd: cmdbuf; s,t: integer) : opstatus; {s = sector number, t = track number} var cnt,ST0,ST1,a: integer; OK: boolean; begin if NOT SetLocalReg(MtrON,WrDMA) then begin Write1 := LCLERR; OK := SetLocalReg(MtrOFF,RdDMA); exit(Write1); end; MoveIN(WB); {setup cmd array and controller <= bufferptr} SetupCmdRW(cmd,s,t); cmd[1] := BuildCmd($05); {MT=0,SK=0,WRITE DATA add density bit} cnt := 9; {set up buffer address} if density = SGLDNSTY then a := SDBufEnd else a := DDBufEnd; BaseAddr^[ CtlCNTR ] := cnvtob(a DIV 2); if WrController(cmd,cnt) then begin if WaitFDC then begin if RdController(cmd,7) then begin {see if cmd done} ST0 := cnvtoi(cmd[1]); OK := ((INTcode1 + INTcode0) and ST0) = 0; {if is done then move data from controller} { to read buffer} if OK then Write1 := GOOD else Write1 := FAILED; end else Write1 := RDCERR; end else Write1 := FNCERR; end else Write1 := WRCERR; OK := SetLocalReg(MtrOFF,RdDMA); end; procedure Restore; var st: opstatus; ST0,PCN: integer; begin writeln; writeln('Restoring to track 0'); writeln; st := Track0(ST0,PCN); case st of LCLERR : writeln('Timed out waiting for Drive to come READY.'); WRCERR : writeln('Timed out on controller write'); RDCERR : writeln('Timeout Read Controller. Unexpected.'); FNCERR : writeln('Timed out waiting for Restore to finish.'); FAILED : begin write('Restore failed. Status register 0 : '); showbyte(cnvtob(ST0)); writeln; write('Reported PCN: '); showbyte(cnvtob(PCN)); writeln; track := PCN; if TPI48 then track := track div 2; end; GOOD : begin writeln('Restore completed.'); track := PCN; if TPI48 then track := track div 2; end; end; end; procedure Seek; var st: opstatus; ST0,PCN: integer; begin writeln; PCN := track; writeln('Current track: ',track); write('Enter track number: '); readln(track); st := SeekTrk(track,ST0,PCN); case st of LCLERR : writeln('Timed out waiting for Drive to come READY.'); WRCERR : writeln('Timed out on controller write'); RDCERR : writeln('Timeout Read Controller. Unexpected.'); FNCERR : writeln('Timed out waiting for Restore to finish.'); FAILED : begin write('Seek failed. Status register 0 : '); showbyte(cnvtob(ST0)); writeln; write('Reported PCN: '); showbyte(cnvtob(PCN)); writeln; track := PCN; if TPI48 then track := track div 2; end; GOOD : begin writeln('Seek completed.'); track := PCN; if TPI48 then track := track div 2; end; end; end; procedure ShowRWstatus(cmd: cmdbuf); begin write( 'Status reg 0 : '); showbyte(cmd[1]); write(' Status reg 1 : '); showbyte(cmd[2]); write(' Status reg 2 : '); showbyte(cmd[3]); writeln; write('Track : '); showbyte(cmd[4]); write(' Side : '); showbyte(cmd[5]); write(' Sector : '); showbyte(cmd[6]); writeln; write('Bytes/Sector : '); showbyte(cmd[7]); writeln; end; procedure ReadSec; var cmd: cmdbuf; ST1: integer; st: opstatus; ch: char; OK: boolean; begin writeln; writeln('READ SECTOR > Current sector number: ',sector); write('Do you want to change: '); read(ch); writeln; if (ch='y') or (ch='Y') then begin write('Enter sector number: '); readln(sector); end; st := Read1(RdBuf,cmd,sector,track); case st of LCLERR : writeln('Timed out waiting for Drive to come READY.'); WRCERR : writeln('Timed out on controller write'); RDCERR : writeln('Timed out on controller read'); FNCERR : writeln('Timed out during wait for READ.'); FAILED : begin writeln('Read failed.'); ShowRWstatus(cmd); end; GOOD : begin writeln('Sector read succesful.'); secinrd := sector; {save what sector read} ST1 := cnvtoi(cmd[2]); if ( (CTLmark and ST1) = CTLmark) then writeln('Read data marked deleted.'); end; end; end; procedure WriteSec; var cmd: cmdbuf; ST1: integer; st: opstatus; ch: char; OK: boolean; begin writeln; writeln('WRITE SECTOR > Current sector number: ',sector); write('Do you want to change: '); read(ch); writeln; if (ch='y') or (ch='Y') then begin write('Enter sector number: '); readln(sector); end; st := Write1(WrBuf,cmd,sector,track); case st of LCLERR : writeln('Timed out waiting for Drive to come READY.'); WRCERR : writeln('Timed out on controller write'); RDCERR : writeln('Timed out on controller read'); FNCERR : writeln('Timed out during wait for WRITE.'); FAILED : begin writeln('Write failed.'); ShowRWstatus(cmd); end; GOOD : begin writeln('Sector write succesful.'); secinwr := sector; {save what sector written} end; end; end; procedure CmpBuffs; var i,k,Ecnt: integer; ch: char; begin writeln; writeln('Comparing Read and Write buffers'); writeln('Read sector: ',secinrd,' Write sector: ',secinwr); if (secinrd <> secinwr) then begin write('They are different, do you still want to do compare? '); read(ch); if NOT((ch='y') or (ch='Y')) then exit(CmpBuffs); writeln; end; if density = SGLDNSTY then k := SDBufEnd else k := DDBufEnd; BaseAddr^[ CtlCNTR ] := cnvtob(k DIV 2); Ecnt := 0; for i := BufStart to k do if (RdBuf[i] <> WrBuf[i]) then Ecnt := Ecnt + 1; writeln('Number of MisCompares: ',Ecnt); writeln; writeln('The Read buffer:'); DumpBuf(RdBuf); if IsBreakPress then exit(CmpBuffs); writeln; writeln('The Write buffer:'); DumpBuf(WrBuf); end; procedure FillWrBuf(var WrBuf: buffer); var n,i,k,d: integer; b: byte; ch: char; Done: boolean; begin Done := FALSE; writeln; writeln('Fill write Buffer.'); writeln; write('Select option: [ S)ame, I)ncremented ] '); read(ch); ch := Ucase(ch); writeln; case ch of 'I': begin write('Enter starting value: [0..255] '); readln(n); n := abs(n) mod 256; write('Enter increment: '); readln(i); for k:=Bufstart to DDBufEnd do begin WrBuf[k] := cnvtob(n); n:= (n + i) mod 256; end; Done := TRUE; end; 'S': begin write('Enter byte value: [0..255] '); readln(n); b := cnvtob(abs(n)); for k:=Bufstart to DDBufEnd do WrBuf[k] := b; Done := TRUE; end; end; writeln; if Done then begin writeln('The Write buffer:'); d := density; density := DBLDNSTY; DumpBuf(WrBuf); density := d; end; end; procedure SetSlot; var sn: integer; begin writeln; writeln('Current slot: ',slot); write('Enter slot [1,2,3,4]: '); readln(sn); if (sn<1) or (sn>4) then writeln(chr(7),'Invalid slot number') else begin slot := sn; BaseAddr := pointer(Slot0 + (slot*OFFSET) ); end; end; procedure WaitUser; var ch: char; begin writeln; write('Press return when ready'); readln(ch); end; procedure InitFDC; var i,ST0,PCN: integer; cmd: cmdbuf; begin sector := 1; track := 0; secinrd := sector; secinwr := sector; for i := BufStart to DDBufEnd do begin RdBuf[i] := -2; WrBuf[i] := -2; end; BaseAddr^[ CtlLOCALreg ] := cnvtob(RESETbit + MOTOROFF); {reset chip} {set back to normal} if floptype = FIVEINCH then {turn motor back on drive 0} BaseAddr^[ CtlLOCALreg ] := 0 else BaseAddr^[ CtlLOCALreg ] := cnvtob(MOTOROFF); writeln('FDC RESET'); end; procedure Specify; {NOTE: For 8" drive} {it seems Restore needs 2 to 4 ms. more step rate then seek} {6 or 8 ms. for seek and 10 ms. for restore} {driver should have a table for step rates, seek and restore,} { head load time } { head unload time } {means must do specify before do restore and change to seek } {value after do restore. } {FOR BOOT: pick a safe step rate??? will slow it down. maybe} { always use 10 ms. } var cmd: cmdbuf; Ready, timeout: boolean; tmot: longint; i,sb,step: integer; begin {do specify cmd} writeln; cmd[1] := cnvtob($03); {specify cmommand} if floptype = EIGHTINCH then begin writeln('8 inch drive :'); write('Enter Step Rate in ms.: [1 to 16] '); readln(step); step := 15 - (step - 1); {no specification for head unload time for TANDON drive} {therefore use minimum ?????} cmd[2] := cnvtob( (step * 16) or $01); {step rate and 16 ms. HUT} {the TANDON has the head LOADED whenever the door is closed} cmd[3] := cnvtob($02); {16 ms. HLT, DMA mode} end else begin writeln('5 1/4 inch drive :'); {MPI model 92 : needs combined head load time and head settle time} {of 60 ms. It has 5 ms. step rate. Motor up delay is 500 ms.} {clock rate is 4 Mhz on for both densities therefore all data sheet} {times increase by a factor of 2.} {no specification for head unload time for MPI drive} {therefore use minimum ?????} write('Enter Step Rate in ms.: [2 to 32, even only] '); readln(step); step := 15 - ((step DIV 2) - 1); cmd[2] := cnvtob( (step * 16) or $01); {step rate and 16 ms. HUT} cmd[3] := cnvtob($1E); { 60 ms. HLT, DMA mode} end; write('Step byte: '); showbyte(cmd[2]); writeln; tmot := 0; {wait for FDC to not be busy} repeat sb := cnvtoi(BaseAddr^[ CtlSTATUSreg ]); Ready := ((Drv0BUSY + Drv1BUSY + Drv2BUSY + Drv3BUSY + FDCBUSYbit) and sb) = 0; tmot := tmot + 1; timeout := (tmot > TOint); until ((Ready) or (timeout)); if Ready then begin if NOT WrController(cmd,3) then writeln('Timed out trying to do Specify.') else for i := 1 to 3 do spcfycmd[i] := cmd[i]; end else writeln('Timed out waiting for FDC to come ready.'); end; function QuietSpecify: boolean; {returns true if specify worked} var Ready, timeout: boolean; tmot: longint; sb: integer; begin tmot := 0; {wait for FDC to not be busy} repeat sb := cnvtoi(BaseAddr^[ CtlSTATUSreg ]); Ready := ((Drv0BUSY + Drv1BUSY + Drv2BUSY + Drv3BUSY + FDCBUSYbit) and sb) = 0; tmot := tmot + 1; timeout := (tmot > TOint); until ((Ready) or (timeout)); QuietSpecify := Ready; if Ready then QuietSpecify := WrController(spcfycmd,3); end; procedure Examine; var ch: char; b: byte; ex,disp: boolean; begin writeln; ex := FALSE; repeat write('Examine : [ L)ocal, S)tatus, D)ata, E)xit ] '); read(ch); ch := Ucase(ch); writeln; disp := TRUE; case ch of 'D': begin b := BaseAddr^[ CtlDATAreg ]; write(' FDC data register : '); end; 'S': begin b := BaseAddr^[ CtlSTATUSreg ]; write(' FDC status register : '); end; 'L': begin b := BaseAddr^[ CtlLOCALreg ]; write(' Local status register : '); end; 'E': begin ex := TRUE; disp := FALSE; end; Otherwise : disp := FALSE; end; if disp then showbyte(b); writeln; until ex; end; procedure DriveSelect; {5 1/4 does not have two sided line. so can't check just try.} {do a sense drive status command} var hd,ST3: integer; OK,twosd: boolean; begin writeln; writeln('Drive status. Current Head: ',head:1); write('Enter Head select : '); readln(hd); if NOT SetLocalReg(MtrON,RdDMA) then writeln('Timed out on Local Register') else if NOT SenseDriveStatus(hd,0,ST3) then writeln('Failed to do drive status command.') else begin write('ST3 : '); showbyte(cnvtob(ST3)); writeln; twosd := ((ST3 and TWOSIDEbit) = TWOSIDEbit); if ((hd = 1) and (twosd)) or (hd = 0) or (floptype = FIVEINCH) then head := hd else writeln('Drive does not have side ',hd:1); end; OK := SetLocalReg(MtrOFF,RdDMA); end; procedure ShowBf(B: bf512; cnt: integer); var i,j,n: integer; strg : string[16]; s: string[1]; begin n := 0; s := ' '; strg := ''; for i := 0 to cnt-1 do begin showbyte(B[i]); write(' '); s[1] := cnv2ch(B[i]); strg := concat(strg,s); n := n + 1; if (n mod 16) = 0 then begin write(' '); writeln(strg); strg := ''; end; end; end; procedure FillCardBuf; var n,i,k,addr,cnt: integer; b: byte; ch: char; Done: boolean; buf: bf512; begin writeln; writeln('Fill Controller Buffer.'); writeln; write('Enter address : '); readln(addr); write('Enter number of bytes to write: '); readln(cnt); write('Bytes are: [ S)ame, I)ncremented ] '); read(ch); ch := Ucase(ch); writeln; case ch of 'I': begin write('Enter starting value: [0..255] '); readln(n); n := abs(n) mod 256; write('Enter increment: '); readln(i); BaseAddr^[ CtlCNTR ] := cnvtob(addr div 2); for k := 0 to cnt-1 do begin BaseAddr^[ CtlBUFFER ] := cnvtob(n); n := (n + i) mod 256; end; end; 'S': begin write('Enter byte value: [0..255] '); readln(n); BaseAddr^[ CtlCNTR ] := cnvtob(addr div 2); b := cnvtob(abs(n)); for k := 0 to cnt-1 do BaseAddr^[ CtlBUFFER ] := b; end; end; writeln; end; procedure LookBuffer; var ex: boolean; addr,cnt,i: integer; buf: bf512; ch: char; begin writeln; writeln('Check out the Buffer.'); ex := FALSE; repeat write(' Select option : [ D)ump, F)ill, E)xit ] '); read(ch); ch := Ucase(ch); writeln; case ch of 'D': begin write('Enter address : '); readln(addr); write('Enter number of bytes to read: '); readln(cnt); BaseAddr^[ CtlCNTR ] := cnvtob(addr div 2); for i := 0 to cnt-1 do buf[i] := BaseAddr^[ CtlBUFFER ]; writeln; writeln('The buffer from ',addr:1,' for ',cnt:1,' bytes.'); ShowBf(buf,cnt); writeln; end; 'F': FillCardBuf; 'E': ex := TRUE; end; until ex; end; procedure FormatSide; var l,filler,gap,ST0,PCN,trk: integer; lastsector,lasttrack: integer; OK,error: boolean; ch: char; st: opstatus; cmd: cmdbuf; procedure FrmtTbl(seclen,track: integer); var i,k: integer; begin BaseAddr^[ CtlCNTR ] := cnvtob( ((lastsector * 4) - 1) DIV 2); for i := 1 to lastsector do begin {does interleave of 2} k := i+(i-1); if k > lastsector then k := (k mod lastsector) + 1; BaseAddr^[ CtlBUFFER ] := cnvtob(track); BaseAddr^[ CtlBUFFER ] := cnvtob(head); { BaseAddr^[ CtlBUFFER ] := cnvtob(i);} BaseAddr^[ CtlBUFFER ] := cnvtob(k); BaseAddr^[ CtlBUFFER ] := cnvtob(seclen); end; end; procedure SetFrmtCmd; begin cmd[1] := BuildCmd($0D); {format trk cmd} cmd[2] := cnvtob( (head * 4) or 0); cmd[3] := cnvtob(l); cmd[4] := cnvtob(lastsector); cmd[5] := cnvtob(gap); cmd[6] := cnvtob(filler); end; procedure FrmtInit; begin if density = SGLDNSTY then begin {single density} l := SDNUMbytes; filler := $E5; gap := SDGAP3LEN; end else begin {double density} l := DDNUMbytes; filler := $1A; gap := DDGAP3LEN; end; l := (l - 1) DIV 128; if (floptype = EIGHTINCH) then begin lastsector := EOT8; lasttrack := ENDTRK8; end else begin lastsector := 9; {EOT5;} lasttrack := ENDTRK5; end; trk := 0; end; begin writeln; if (floptype = EIGHTINCH) then write('8 inch drive') else write('5 1/4 inch drive'); write(' Format a side : '); if density = SGLDNSTY then write('FM') else write('MFM'); writeln(' Head : ',head:1); write('OK to continue? [Y/N] '); read(ch); ch := Ucase(ch); writeln; writeln; if ch<>'Y' then exit(FormatSide); FrmtInit; gotoxy(0,20); clrline; writeln('Formatting track : '); error := IsBreakPress; repeat error := TRUE; if SeekTrk(trk,ST0,PCN) = GOOD then begin {set up sector table} FrmtTbl(l,trk); SetFrmtCmd; {LookBuffer;} gotoxy(20,20); clrline; writeln(trk:2); BaseAddr^[ CtlCNTR ] := cnvtob( ((lastsector * 4) - 1) DIV 2); if NOT SetLocalReg(MtrON,WrDMA) then begin writeln('Timed out waiting for Drive to come READY.'); OK := SetLocalReg(MtrOFF,RdDMA); exit(FormatSide); end; {writeln('Format command :'); showcmd(cmd,6);} if WrController(cmd,6) then begin if WaitFDC then begin if RdController(cmd,7) then begin {see if cmd done} ST0 := cnvtoi(cmd[1]); OK := ((INTcode1 + INTcode0) and ST0) = 0; { to read buffer} if OK then error := FALSE else begin writeln('Format command failed.'); writeln; ShowRWstatus(cmd); end; end else writeln('Timed out on controller write'); end else writeln('Timed out on controller read'); end else writeln('Timed out during wait for FORMAT.'); end else writeln('Seek of track ',trk:1,' failed.'); trk := trk + 1; until ( (trk>lasttrack) or (error) or (IsBreakPress) ); OK := SetLocalReg(MtrOFF,RdDMA); st := Track0(ST0,PCN); if st <> GOOD then writeln('Restore failed.'); if NOT error then writeln('Format successful.') else writeln('Format failed.'); end; {$I-} procedure ExerciseDrive; var i,trk,sec,lastsector,lasttrack: integer; skretry,total,cebytes: longint; skerr,rderr,wrerr,cmperr: longint; Stop: boolean; wrb,rdb: buffer; fn: string[30]; fout: text; procedure dispbyte(b: byte); var i,hi: integer; begin i := cnvtoi(b); hi := i div 16; write( fout, hex[hi + 1] ); write( fout, hex[ (i - (hi*16)) + 1 ] ); end; procedure DoRestr; var ST0,PCN,i: integer; done: boolean; Estr: string[40]; st: opstatus; begin i := 1; repeat done := TRUE; st := Track0(ST0,PCN); case st of LCLERR : Estr := 'Timeout Local register.'; WRCERR : Estr := 'Timeout Write controller.'; RDCERR : Estr := 'Timeout Read controller.'; FNCERR : Estr := 'Timeout wait for Restore.'; GOOD : done := TRUE; FAILED : begin done := FALSE; i := i + 1; Estr := 'command failed.'; end; end; until (done) or (i>3); if st <> GOOD then begin writeln(fout,'Failed to do Restore: ',Estr); exit(ExerciseDrive); end; end; procedure resetdrive; begin {reset controller} BaseAddr^[ CtlLOCALreg ] := cnvtob(RESETbit); {reset chip} BaseAddr^[ CtlLOCALreg ] := 0; {set back to normal} if NOT QuietSpecify then {uses last cmd string for manual specify} begin writeln(fout,'Specify command failed.'); exit(ExerciseDrive); end; DoRestr; {will exit exercise if fails} end; function DoSkTrk(var t: integer) : boolean; {if get seek error retry 4 times} {if 4th retry fails go to next track} {keep track of seek errors and retries} var ST0,PCN,i: integer; done: boolean; st: opstatus; begin i := 1; repeat st := SeekTrk(t,ST0,PCN); case st of LCLERR, WRCERR, RDCERR, FNCERR : begin done := FALSE; i := i + 1; resetdrive; end; FAILED : begin done := FALSE; i := i + 1; end; GOOD : done := TRUE; end; until (done) or (i>4); if st <> GOOD then begin skerr := skerr + 1; skretry := skretry + (i-2); end; DoSkTrk := (st = GOOD); end; procedure resetRW(t : integer); begin writeln(fout,'Reset on chip and drive.'); resetdrive; if NOT DoSkTrk(t) then begin writeln(fout,'Seek on error recovery failed.'); exit(ExerciseDrive); end; end; procedure RWerrst(tk: integer; st: opstatus; cmd: cmdbuf; var err: longint); begin case st of LCLERR : writeln(fout,'Timeout on local register.'); WRCERR : writeln(fout,'Timeout on controller write.'); RDCERR : writeln(fout,'Timeout on controller read.'); FNCERR : writeln(fout,'Timeout on wait for command.'); FAILED : writeln(fout,'Command failed.'); GOOD : writeln(fout,'Successfull.'); end; write(fout,'ST0 : '); dispbyte(cmd[1]); writeln(fout); write(fout,'ST1 : '); dispbyte(cmd[2]); writeln(fout); write(fout,'ST2 : '); dispbyte(cmd[3]); writeln(fout); if (st in [ WRCERR, RDCERR, FNCERR ]) then resetRW(tk); err := err + 1; end; function RWsector(s,t: integer) : boolean; {returns true if both read and write worked} var st: opstatus; i: integer; cmd: cmdbuf; begin BuildWRbuf(wrb,s,t); for i := BufStart to DDBufEnd do rdb[i] := 0; RWsector := FALSE; {assume fails} st := Write1(wrb,cmd,s,t); if st = GOOD then begin st := Read1(rdb,cmd,s,t); if st = GOOD then RWsector := TRUE else begin write(fout,'Read sector ',s:1,' failed. status : '); RWerrst(t,st,cmd,rderr); end; end else begin write(fout,'Write sector ',s:1,' failed. status : '); RWerrst(t,st,cmd,wrerr); end; end; procedure CmpRWbuf; var i,k: integer; ce: boolean; begin ce := FALSE; {compare write and read buffer} if density = SGLDNSTY then k := SDBufEnd else k := DDBufEnd; for i := BufStart to k do if wrb[i] <> rdb[i] then begin ce := TRUE; cebytes := cebytes + 1; end; {keep track of the number of compare errors} if ce then cmperr := cmperr + 1; end; procedure DumpSummary; begin {display error summary} writeln(fout,'*** Summary ***'); writeln(fout,'Total tracks read: ',total:1); writeln(fout,'Seek errors: ',skerr:1,' Seek retries: ',skretry:1); writeln(fout,'Read errors: ',rderr:1,' Write errors: ',wrerr:1); writeln(fout,'Sectors w/ compare errors: ',cmperr:1); writeln(fout,'Bytes w/ compare errors: ',cebytes:1); end; procedure XrciseInit; begin trk := 0; total := 0; skerr := 0; rderr := 0; wrerr := 0; cebytes := 0; cmperr := 0; total := 0; skretry := 0; if (floptype = EIGHTINCH) then begin lastsector := EOT8; lasttrack := ENDTRK8; end else begin lastsector := EOT5; lasttrack := ENDTRK5; end; writeln; writeln('Exercise Drive....Side ',head:1,' '); if (density = SGLDNSTY) then writeln('FM') else writeln('MFM'); write('Enter file name: '); readln(fn); if length(fn) = 0 then reset(fout,'/console') else begin rewrite(fout,fn); if ioresult <> 0 then begin close(fout,purge); writeln('Open failed.'); exit(ExerciseDrive); end; end; { FillWrBuf(wrb); } end; begin XrciseInit; if (length(fn) <> 0) then begin writeln(fout,'Exercise Drive....'); if (floptype = EIGHTINCH) then write(fout,'8" drive') else write(fout,'5 1/4" drive'); write(' Side ',head:1,' '); if (density = SGLDNSTY) then writeln(fout,'FM') else writeln(fout,'MFM'); end; Stop := IsBreakPress; DoRestr; {restore drive} repeat writeln(fout,'pass : ',(total+1):1,' track: ',trk:1); if DoSkTrk(trk) then {seek to trk} begin sec := 1; repeat if RWsector(sec,trk) then CmpRWbuf; sec := sec + 1; Stop := IsBreakPress; until (Stop) or (sec>lastsector); end else writeln(fout,'Seek to track ',trk:1,' failed.'); trk := (trk + 1) mod (lasttrack + 1); total := total + 1; until Stop; DumpSummary; close(fout,lock); if ioresult <> 0 then begin writeln('Close failed.'); close(fout,purge); end; end; {$I+} procedure SelectDensity; var ch: char; begin writeln; write('Current density selection : '); if density = SGLDNSTY then writeln('FM') else writeln('MFM'); writeln; write('Select : [ M)FM, F)M ] '); read(ch); ch := Ucase(ch); case ch of 'F': density := SGLDNSTY; 'M': density := DBLDNSTY; {must be same as MFMbit} end; end; procedure ChkDiskChng; var OK: boolean; lr: integer; begin writeln; writeln('Check Disk Changed bit flag.'); if SetLocalReg(MtrON,RdDMA) then begin lr := cnvtoi(BaseAddr^[ CtlLOCALreg ]); if ((lr and DISKCHG) = DISKCHG) then writeln('Disk has CHANGED.') else writeln('Disk is the SAME.'); end else writeln('Timedout on wait for READY.'); OK := SetLocalReg(MtrOFF,RdDMA); end; procedure TrkInch; var ch: char; begin write('Current TPI selection : '); if TPI48 then write('48') else write('96'); writeln(' TPI'); write('Select : [ 4)8, 9)6 ] '); read(ch); ch := Ucase(ch); case ch of '4': TPI48 := TRUE; '9': TPI48 := FALSE; end; end; function menu: boolean; {returns true when should quit} var ch: char; stop: boolean; begin stop := FALSE; writeln('\1BJ'); write('Controller test program : '); if floptype = EIGHTINCH then writeln('8 inch drive.\0D') else writeln('5 1/4 inch drive.\0D'); writeln('Select option: [S)eek, F)ill, R)ead, W)rite, I)nit FDC, P)ick density,'); writeln(' E)xamine, B)uffer, C)ompare, A)lt slot, X)ercise, T)rack/Inch'); write( ' Z)ero, D)efine, H)ead, M)forMatter, N)ew Disk, Q)uit] '); read(ch); ch := Ucase(ch); writeln; case ch of 'A': SetSlot; 'B': LookBuffer; 'C': CmpBuffs; 'D': Specify; 'E': Examine; 'F': FillWrBuf(wrbuf); 'H': DriveSelect; 'I': InitFDC; 'M': FormatSide; 'N': ChkDiskChng; 'P': SelectDensity; 'Q': stop := TRUE; 'R': ReadSec; 'S': Seek; 'T': TrkInch; 'W': WriteSec; 'X': ExerciseDrive; 'Z': Restore; end; menu := stop; if not stop then WaitUser; end; procedure initialize; var sb: integer; begin hex := '0123456789ABCDEF'; slot := 3; {assume slot 3} drive := 0; {uses drive 0} density := SGLDNSTY; {single density} head := 0; {side 0} TPI48 := FALSE; BaseAddr := pointer(Slot0 + (slot*OFFSET) ); sb := cnvtoi(BaseAddr^[ CtlLOCALreg ]); if ((sb and NMINIbit) = NMINIbit) then floptype := EIGHTINCH else floptype := FIVEINCH; {to completly initialize - reset FDC, specify cmd, and Restore} InitFDC; {only reset FDC} {if 5" floppy then turn on motor for drive 0} {leave in normal mode, IRQ disabled, and dma write} {for motor to stay on must have device select and motor on} if floptype = FIVEINCH then BaseAddr^[ CtlLOCALreg ] := cnvtob(DvSenbBit); WaitUser; end; Begin initialize; repeat until menu; {if 5" drive turn off motor} if floptype = FIVEINCH then BaseAddr^[ CtlLOCALreg ] := cnvtob(MOTOROFF); End.