; ; FDCFMT.ASM (8080 VERSION) ; The Systems Group ; Floppy disk formatter program for 8" and 5" media. ; For the SG Floppy disk controller Model FDC-2800/2801 ; Written by: John E. Lauber ; ; Edit history: ; 80Dec18 jl - File created. ; 81Jan16 jl - 8" debugged for single- and double-density formats. ; 81Jun06 jl - Added support of driver DMA flag option byte. ; 81Jun06 jl - Added NEC 765 init conditional. ; ; This FORMAT porgram was written for specific use on the SG floppy disk ; controller Model FDC-2800 and FDC-2801. Its supports the formatting of ; the standard sector sizes adopted by SG for both single- and double- ; density diskettes, and in the case of double-density, double-sided ; diskettes are automatically determined and the second side then formated. ; The sector formatting is defined as follows: ; ; FOR 8" DISKETTES ; ; DENSITY | SECTOR SIZE | # OF SECTORS | SECTOR NUMBERING ; ---------|-------------|--------------|------------------ ; SINGLE | 128 bytes | 26 | 1 - 26 ; ---------|-------------|--------------|------------------ ; DOUBLE | 1024 bytes | 8 per side | 1 - 8 per side ; ---------|-------------|--------------|------------------ ; ; FOR 5" DISKETTES ; ; DENSITY | SECTOR SIZE | # OF SECTORS | SECTOR NUMBERING ; ---------|-------------|--------------|------------------ ; SINGLE | 128 bytes | 16 | 1 - 16 ; ---------|-------------|--------------|------------------ ; DOUBLE | 1024 bytes | 5 per side | 1 - 5 per side ; ---------|-------------|--------------|------------------ ; ; Attemps to format a double-sided diskette in single-density with this ; utility will be flaged and reported as an error. ; ; This source was developed using Digital Research's "MAC" macro assembler, ; but designed to be compatible with ASM also. TITLE 'FDC-2800/1 formatter vers 1.0' PAGE 58 TRUE EQU -1 ; define true FALSE EQU 0 ; define false VERS EQU 10 ; version number times 10 RTRYS EQU 5 ; number of read retrys ; conditional equates NECINI EQU FALSE ; if NEC 765 init required MINI EQU FALSE ; if 5" operation TPI96 EQU FALSE ; if 96 TPI mini drives ; system equates WBOOT EQU 0 ; CP/M warm boot entry BIOS EQU 1 ; BIOS page address USRDSK EQU 4 ; current user/disk number BDOS EQU 5 ; BDOS entry point TRUE EQU -1 ; define true FALSE EQU 0 ; define false BEL EQU 07H ; define bell code BS EQU 08H ; define back space CR EQU 0DH ; define return LF EQU 0AH ; define linefeed TPA EQU 100H ; start of transient program area ; Drive parameters. Used if NECINI conditional set TRUE. IF MINI AND TPI96 NOTRKS EQU 80 ; number of tracks on 96 TPI drive ENDIF IF MINI AND NOT TPI96 NOTRKS EQU 40 ; number of tracks on 48 TPI drive ENDIF IF MINI SRT EQU 4 HUT EQU 480 HLDT EQU 40 SRTHUT EQU (16-SRT/2) SHL 4 + HUT/32 HLDTND EQU HLDT/4 SHL 1 ENDIF IF NOT MINI NOTRKS EQU 77 ; number of tracks SRT EQU 6 ; step rate in ms. HUT EQU 240 ; head unload time in ms. HLDT EQU 38 ; head load time in ms. SRTHUT EQU (16-SRT) SHL 4 + HUT/16 HLDTND EQU HLDT/2 SHL 1 ENDIF ; FDC-2800/1 disk controller ports. IF MINI DSKB EQU 090H ; base of 5" controller ENDIF IF NOT MINI DSKB EQU 080H ; base of 8" controller ENDIF FDCMSR EQU DSKB+0 ; 765 FDC main status register DDATA EQU DSKB+1 ; 765 main data register CONTRL EQU DSKB+2 ; Main board control ZDMA EQU DSKB+4 ; Z-80 DMA device ; uPD765 floppy disk controller intruction set SCYCMD EQU 03H ; specify drive parameters SDSCMD EQU 04H ; sense drive status WRCMD EQU 05H ; single density write data RDCMD EQU 06H ; single density Read data RECCMD EQU 07H ; recalibrate SISCMD EQU 08H ; sense interrupt RIDCMD EQU 0AH ; read sector ID FMTCMD EQU 0DH ; format track command SKCMD EQU 0FH ; seek command ; Control port bits SBENA EQU 02H ; sector buffer enable bit MOTOR EQU 10H ; motor-on bit PIOEN EQU 20H ; programmed I/O enable bit PGMER EQU 40H ; programmed I/O error bit INTRQ EQU 80H ; INTRQ from uPD765 (input) ; FDC board addressing SECBUF EQU 0FC00H ; address of on-board buffer ; CP/M functions CIN EQU 1 ; console input COUT EQU 2 ; console output RBUF EQU 10 ; read input line CST EQU 11 ; console status CVERS EQU 12 ; return version number DRESET EQU 13 ; disk system reset SDSK EQU 14 ; select disk ; MP/M functions OPENQUE EQU 135 ; open queue READQUE EQU 137 ; read queue WRITQUE EQU 139 ; write queue ; beginning of program ORG TPA START: LXI SP,STACK ; initialize stack ; MVI C,DRESET CALL BDOS ; reset the disk system INR A JZ WBOOT ; if reset denied ; LDA USRDSK MOV E,A MVI C,SDSK CALL BDOS ; select in default disk ; MVI C,CVERS CALL BDOS MOV A,H STA MPMFL ORA A JZ ST1 ; if CP/M system ; LXI D,UQCB MVI C,OPENQUE CALL BDOS ; open disk queue ; ST1: CALL GETMXD ; get disk queue CALL SETDMA ; use SETDMA return value for PUSH PSW CALL PUTMXD ; put disk queue POP PSW ANI 1 ; mask DMA mode bit STA DMAFL ; DMA or PIO modes JZ ST2 ; if PIO mode LDA LATCH ORI SBENA ; add in sector buffer enable bit STA LATCH ; ST2: CALL INIT ; initialize disk driver ; CALL TYPE ; print sign-on message DB '\The Systems Group' IF MINI DB '\5"' ENDIF IF MINI AND TPI96 DB ' (96 TPI)' ENDIF IF NOT MINI DB '\8"' ENDIF DB ' Floppy Disk Formatter vers ' DB VERS/10+'0','.',VERS MOD 10+'0','$' ; MAIN FORMAT LOOP FMTLOOP: LXI SP,STACK CNZ PUTMXD ; write disk queue if MP/M CALL GETDENS ; get format density type CALL GETDRV ; get drive name for formatting MVI A,1 STA LACE ; initialize interleave factor LDA DENSFL ; get density mode INR A ; test it CZ GETLACE ; if double-density only CALL GETVER ; set verify flag CALL DBLCHK ; double-check the format operation CNZ GETMXD ; read disk queue if MP/M CALL FORMAT ; format the diskette JMP FMTLOOP ; and loop forever ; Get Density from user: ; User is prompted for density of format or quit program. ; If user quits: control branches to REBOOT, else the format and ; read tables of the density operation are set-up. GETDENS: CALL TYPE DB '\' DB '\"S" = Single-density, single-sided' DB '\"D" = Double-density, single- or double-sided' DB '\"Q" = Quit and return to CP/M' DB '\\Select Format option (S,D,or Q)? $' CALL LINEIN CALL UPCASE CPI 'Q' JZ REBOOT ; if quitting MVI B,0 ; SD flag value CPI 'S' JZ SETDENS MVI B,0FFH ; DD flag value CPI 'D' JZ SETDENS CALL INVMSG ; invalid input JMP GETDENS ; re-enter SETDENS: MOV A,B STA DENSFL ; set density flag LXI H,SDVALS ORA A JZ SETD1 LXI H,DDVALS SETD1: MOV A,M INX H STA N ; set RW N STA FMN ; set format N MOV A,M INX H STA SC ; set SC MOV A,M INX H STA GPL1 ; set GPL1 MOV A,M INX H STA GPL2 ; set GPL2 MOV A,M INX H STA DTL ; set DTL byte LDA DMAFL ; get DMA flag ORA A JNZ SETD2 INX H INX H ; point to transfer code SETD2: MOV A,M INX H MOV H,M MOV L,A SHLD TRMCNT ; store as terminal count RET ; from get density ; Get the selected drive for formatting BADDRV: CALL INVMSG GETDRV: CALL TYPE DB '\Select drive (A-D) for format? $' CALL LINEIN ; wait for response CALL UPCASE ; fold to upper case SUI 'A' JC BADDRV ; if out of range CPI 4 JNC BADDRV ; if out of range STA FMDRV ; store in format table STA DRIVE ; store in RWTBL RRC RRC ; bits 0-1 to 6-7 ANI 0C0H ; mask bits 6-7 MOV B,A LDA LATCH ANI 03FH ORA B ; include new drive STA LATCH RET ; Get the interleave factor for sector numbering BADLACE: CALL INVMSG GETLACE: CALL TYPE DB '\Enter sector interleave factor (2-$' LDA SC MOV B,A PUSH B DCR A CALL DECBYTE CALL TYPE DB ')? $' CALL LINEIN POP B RZ PUSH B CALL DECIN POP B JC BADLACE MOV A,H ORA A JNZ BADLACE MOV A,L ORA A JZ BADLACE CMP B JNC BADLACE STA LACE RET ; The the verify flag information: GETVER: CALL TYPE DB '\Verify sectors after format (Y/N)? $' CALL LINEIN CALL UPCASE MVI B,0 CPI 'N' JZ SETVER MVI B,0FFH CPI 'Y' JZ SETVER CALL INVMSG JMP GETVER SETVER: MOV A,B STA VERFL RET ; Double-check format operation routine DBLCHK: CALL TYPE DB '\FORMAT WILL DESTROY ALL DATA ON DRIVE $' LDA FMDRV ADI 'A' CALL CHROUT CALL TYPE DB '\DO YOU WISH TO CONTINUE (Y/N)? $' CALL LINEIN ; wait for response CALL UPCASE CPI 'Y' JNZ FMTLOOP CALL CRLF RET ; track by track format loop FORMAT: LDA LATCH OUT CONTRL ; send latch to board MVI A,2 STA RWFL ; set flag for format ; CALL RECAL ; recalibrate the drive ; CALL SDS ; get drive status ANI 08H ; test two-side status JZ FORMT1 LDA DENSFL ; get density flag ORA A JNZ FORMT1 ; if double-density selected CALL TYPE DB BEL,'SINGLE-DENSITY, DOUBLE-SIDED FORMAT NOT ALLOWED$' JMP FMTLOOP FORMT1: ; Build the initial CHRN table for formatting LXI H,CHRN ; point HL at table XRA A CALL UPDCHRN ; set up cylinder LXI H,CHRN+1 XRA A CALL UPDCHRN ; set up head LXI H,CHRN+3 LDA FMN CALL UPDCHRN ; set up N value CALL ADDLACE ; set up sector interleaving ; FORMT3: CALL FMTTRK ; format side one CALL SDS ; get drive status ANI 08H ; test two side JZ FORMT4 ; no, skip this stuff ; LXI H,CHRN+1 MVI A,1 CALL UPDCHRN ; set CHRN head for 1 LDA FMDRV ; get drive from format table ORI 4 ; set for head two STA FMDRV CALL FMTTRK ; format side two LXI H,CHRN+1 XRA A ; restore head one CALL UPDCHRN ; update table LDA FMDRV ; get drive from format table ANI 3 ; set for head one STA FMDRV ; FORMT4: CALL BREAK LDA VERFL ORA A JZ FORMT5 CALL READTRACK ; re-read the track ORA A ; set flags JZ FORMT5 ; if no errors CALL TYPE DB '\BAD MEDIA, REPLACE$' RET ; FORMT5: CALL BREAK ; check for program interrupt LDA TRACK ; get current track number INR A ; bump track number CPI NOTRKS ; last track? JNC FORMT9 ; yes, were done LXI H,CHRN ; bump track number in chrn table CALL UPDCHRN CALL SEEK ; seek there JMP FORMT3 ; and format another track FORMT9: CALL RECAL ; home the head CALL TYPE DB 'FORMAT FUNCTION COMPLETE$' RET ; clean house and return to CP/M REBOOT: CALL TYPE DB '\Replace system disk in and press RETURN $' CALL LINEIN CALL CRLF JMP WBOOT ; SUBROUTINES ; Make dummy set dma call to BIOS for driver options flag SETDMA: LHLD BIOS LDA MPMFL ORA A JZ STDMA1 INX H MOV A,M INX H MOV H,M MOV L,A STDMA1: LXI D,21H ; offset for DMA entry point DAD D LXI B,80H ; dummy parameter for call PCHL ; call and return ; Print invalid input message INVMSG: CALL TYPE DB BEL,'\INVALID INPUT, RE-ENTER$' RET ; print a character on the console. CHROUT: PUSH PSW PUSH B PUSH D PUSH H MOV E,A MVI C,COUT CALL BDOS POP H POP D POP B POP PSW RET ; fold ascii ACC to upper case. UPCASE: CPI 'a' RC CPI 'z'+1 RNC SUI 20H RET ; check console for input BREAK: PUSH B PUSH D PUSH H MVI C,CST CALL BDOS POP H POP D POP B ORA A RZ MVI C,CIN CALL BDOS ; read the character CALL TYPE DB BEL,'FORMAT ABORTED$' JMP FMTLOOP ; message type utility TYPE: XTHL MOV A,M INX H CPI '$' XTHL RZ CPI '\' CNZ CHROUT CZ CRLF JMP TYPE PMSG: LDAX D INX D CPI '$' RZ CPI '\' CNZ CHROUT CZ CRLF JMP PMSG CRLF: CALL TYPE DB CR,LF,'$' RET ; Get an input line using cp/m function LINEIN: LXI D,LNBUF ; allocated buffer PUSH D ; save it MVI C,RBUF CALL BDOS ; call function POP H ; restore beg line INX H ; move to # of entrys MOV C,M ; prep for dad MVI B,0 ; prep b INX H ; point to first char PUSH H ; save it DAD B ; point to last char+1 MVI M,0 ; mark it POP H FNDCHR: MOV A,M ORA A RZ CPI 20H RNZ ; with HL -> first char INX H JMP FNDCHR ; Convert decimal string to binary value, returned in HL DECIN: LXI D,0 ; zero de XCHG ; addr pointer to de, zero to hl DLOOP: LDAX D ; get an ascii digit ORA A RZ ; on line terminator SUI '0' ; convert to bcd and test RC ; terminate conversion if < ZERO CPI 10 ; check legitimate digit (0-9) CMC RC ; ret with carry set if error INX D ; incr addr pointer DAD H ; shift left 1 PUSH H ; save result DAD H DAD H ; shift left 2 POP B ; # * 2 TO B DAD B ; hl now contains 10*# MOV C,A ; add product to digit MVI B,0 DAD B JMP DLOOP ; back for another digit ; Print the ACC in decimal on the console DECBYTE: PUSH PSW PUSH B MVI B,0FFH DECB2: SUI 10 INR B JNC DECB2 ADI 10 MOV C,A MOV A,B ORA A CNZ DECBYTE MVI A,'0' ADD C CALL CHROUT POP B POP PSW RET ; Update CHRN table ; HL points to part of table, reg A contains new value UPDCHRN: PUSH PSW LDA SC MOV C,A POP PSW LXI D,4 UPDT0: MOV M,A DAD D DCR C RZ JMP UPDT0 ; add specified interleave factor ADDLACE: LDA LACE MOV C,A ; lace factor in C LDA SC MOV E,A ; sectors in E INR A MOV B,A ; sectors+1 in B LXI H,CHRN+2 ; point at memory table MVI D,0 ; set up lace values ADDL1: INR D MOV A,D ADDL2: DCR E ; records done? RM MOV M,A ; record # INX H INX H INX H INX H ADD C CMP B JC ADDL2 DCR B SUB B INR B CMP D JNZ ADDL2 JMP ADDL1 ; Get the MXDisk queue from MP/M ; GETMXD: LDA MPMFL ; get MP/M flag ORA A RZ ; if not MP/M LDA MXDFL ; get queue flag ORA A RNZ ; if queue already owned LXI D,UQCB MVI C,READQUE CALL BDOS ; get the queue MVI A,-1 STA MXDFL ; set internal flag RET ; done ; ; Put the MXDisk queue to MP/M ; PUTMXD: LDA MPMFL ; get MP/M flag ORA A RZ ; if not MP/M LDA MXDFL ; get queue flag ORA A RZ ; if not already owned LXI D,UQCB MVI C,WRITQUE CALL BDOS ; put the queue back XRA A STA MXDFL ; reset internal flag RET ; done ; *********************** ; ** ** ; ** FLOPPY DISK I/O ** ; ** ** ; *********************** ; format one track per pass through this routine FMTTRK: LDA DMAFL ; get DMA flag ORA A JZ FMTT1 ; if not DMA mode ; MVI A,7DH STA WR0 ; set write reg 0 LHLD SC MVI H,0 DAD H DAD H DCX H SHLD TC ; set terminal count MOV B,H MOV C,L INX B LXI H,CHRN LXI D,SECBUF DMAF1: MOV A,M STAX D INX H INX D DCX B MOV A,C ORA B JNZ DMAF1 ; LXI H,RWDMA MVI B,RWDMAL DMAF2: MOV A,M INX H OUT ZDMA DCR B JNZ DMAF2 JMP FMTT2 ; FMTT1: CALL SDS ; get drive status MOV B,A ANI 20H ; test drive ready JNZ PIOF1 ; branch if drive ready LXI D,DNRMSG CALL PMSG ; print drive not ready message JMP FMTLOOP ; loop back ; PIOF1: LDA RWFL ORA A JZ FMTT2 MOV A,B ANI 40H ; test write protect line MVI A,3 JZ FMTT2 LXI D,FWRMSG CALL PMSG LXI D,WPMSG ; print write protect message CALL PMSG JMP FMTLOOP ; loop back ; FMTT2: LDA DENSFL ANI 40H ; mask MF bit ORI FMTCMD ; add in command MOV B,A MVI C,6 CALL CMDRDY ; LDA DMAFL ; get DMA mode flag ORA A JNZ FMTT3 ; if in DMA mode ; DI ; LXI H,CHRN MVI C,DDATA LDA SC ADD A ADD A MOV B,A LDA LATCH ORI PIOEN ; set programmed I/O STA LATCH OUT CONTRL ; send to board XRA A OUT DDATA DB 0EDH,0B3H ; Z80 = OTIR LDA LATCH ANI (NOT PIOEN) AND 0FFH ; reset Programmed I/O STA LATCH OUT CONTRL ; send to board ; EI ; FMTT3: CALL DPOLL ; wait for INTRQ LDA RWSTBL ANI 0C0H ; check for errors RZ ; good operation if zero LXI D,FWRMSG CALL PMSG CALL ERR1 ; use common error routines JP FMTLOOP ; and restart loop ; Read a full tracks data based of the read/write table READTRACK: LDA DENSFL ORA A ; test density LXI H,XLTSD JZ XLTSET ; if single LXI H,XLTDD XLTSET: LDA DRIVE ANI 3 ; set side 0 STA DRIVE XRA A STA HEAD CALL RDTLOOP ; read side 0 ORA A ; set flags RNZ ; if error PUSH H CALL SDS ; get drive status POP H ANI 08H ; test two sided RZ ; done if not LDA DRIVE ORI 4 ; set side 1 STA DRIVE MVI A,1 STA HEAD CALL RDTLOOP ; read side 1 RET ; done with readtrack RDTLOOP: LDA SC ; get number of sectors MOV B,A ; save in B MVI C,0 RDTL1: PUSH B PUSH H LDA LACE ; get interleave factor CPI 1 ; check for interleave JNZ RDTL2 ; if no interleave MOV A,C INR A ; read logical sequential JMP RDTL3 RDTL2: MVI B,0 DAD B ; find table entry MOV A,M ; and get it RDTL3: STA SECTOR ; set sector CALL READ ; read it POP H POP B ORA A RNZ ; if error INR C ; bump sector MOV A,C CMP B ; past end of track JC RDTL1 ; loop if not XRA A ; clear errors RET ; done ; Read the sector based on the read/write table ; and transfer beginning at SBUF for PIO mode READ: MVI C,79H ; DMA port B to port A MVI B,RDCMD XRA A ; write flag JMP RW ; Write the sector based on the read/write table ; transfer begins at DMA address WRITE: MVI C,7DH ; DMA port A to port B MVI B,WRCMD MVI A,1 ; write flag ; ; perform read or write command RW: STA RWFL ; store RW flag LDA DENSFL ; get density flag ANI 40H ; mask for MF bit ORA B ; combine with FDC command STA FDCOP ; store FDC operation LDA SECTOR STA EOT ; make this the EOT value ; MOV A,C ; get DMA mode STA WR0 ; set write reg 0 ; MVI A,1 STA RECFL ; initialize recal flag ; MVI A,RTRYS ; initialize retry counter RETRY: STA RTCNT LDA DMAFL ; get DMA flag ORA A JZ RW1 ; if not DMA mode ; LHLD TRMCNT SHLD TC LXI H,RWDMA MVI B,RWDMAL DMARWE: MOV A,M INX H OUT ZDMA DCR B JNZ DMARWE ; RW1: LDA FDCOP MOV B,A MVI C,9 CALL CMDRDY ; send command ; LDA DMAFL ; get DMA flag ORA A JNZ WTINT ; if DMA mode ; DI ; LHLD TRMCNT XCHG MOV B,E ; initial INIR count in B reg LXI H,SBUF MVI C,DDATA ; for Z80 port address LDA LATCH ORI PIOEN ; set programmed I/O enable STA LATCH OUT CONTRL ; send to board LDA RWFL ORA A JZ PGMRD ; OUT DDATA ; PGMWR: DB 0EDH,0B3H ; Z80 = OTIR DCR D DB 020H,0FBH ; Z80 = JR NZ,PGMWR JMP PGMDN ; done with I/O ; PGMRD: DB 0EDH,0B2H ; Z80 = INIR DCR D DB 020H,0FBH ; Z80 = JR NZ,PGMRD ; PGMDN: LDA LATCH ANI (NOT PIOEN) AND 0FFH ; clear programmed I/O enable STA LATCH OUT CONTRL ; send to board ; EI ; IN CONTRL ANI PGMER ; test programmed I/O error bit JZ WTINT ; if NO error ; CALL RESYNC ; flush the 765 CALL READID ; read an ID field JNZ RWERR1 ; branch if bad read LDA RWSTBL+3 ; get the cylinder number LXI H,TRACK CMP M ; compare with selected track JZ RWERR0 ; if the same track found LDA RECFL ; get recal flag DCR A JNZ RWERR1 ; if recal already performed STA RECFL LDA TRACK PUSH PSW CALL RECAL POP PSW CALL SEEK MVI A,RTRYS ; restore retry counter JMP RETRY ; ; WTINT: CALL DPOLL ; wait for interrupt LDA RWSTBL ANI 0C0H ; are either bits 6 or 7 set? RZ ; no, return with zero set ; MOV B,A LDA DMAFL ; get DMA flag ORA A MOV A,B JNZ RWERR0 ; CPI 40H ; check for EOT termination JNZ RWERR0 LDA RWSTBL+1 ; get ST-1 CPI 80H ; EOT termination? MVI A,0 RZ ; yes, good read ; follow through with error handling routines RWERR0: LDA RTCNT DCR A JNZ RETRY ; RWERR1: LXI D,RDMSG LDA RWFL ORA A JZ ERR0 LXI D,WRMSG ERR0: CALL PMSG ERR1: LXI H,RWSTBL MOV A,M ANI 08H LXI D,DNRMSG CNZ PMSG INX H MOV A,M ; get back ST-1 ANI 80H LXI D,EOCMSG CNZ PMSG MOV A,M ; get back ST-1 ANI 10H LXI D,ORMSG CNZ PMSG MOV A,M ; get back ST-1 ANI 20H JZ ERR3 ; if not CRC error INX H ; if CRC error MOV A,M ; check for data or ID field DCX H ANI 20H LXI D,IDCMSG JZ ERR2 LXI D,DCMSG ERR2: CALL PMSG ERR3: MOV A,M ; get back ST-1 ANI 04H LXI D,SNFMSG CNZ PMSG MOV A,M ; get ST-1 ANI 02H LXI D,WPMSG CNZ PMSG MOV A,M ; get ST-1 ANI 01H JZ ERR5 INX H ; if missing address mark MOV A,M ; get ST-2 ANI 01H ; check for data or ID field LXI D,IDMSG JZ ERR4 LXI D,DATMSG ERR4: CALL PMSG LXI D,ADMSG CALL PMSG ERR5: LXI D,ERTMSG CALL PMSG LDA TRACK CALL DECBYTE CALL SDS ANI 08H JZ ERR7 LXI D,HDMSG CALL PMSG LDA RWFL CPI 2 LDA FMDRV JZ ERR6 LDA DRIVE ERR6: RAR RAR ANI 1 CALL DECBYTE ERR7: LDA RWFL CPI 2 JZ ERR9 LXI D,SECMSG CALL PMSG LDA SECTOR CALL DECBYTE ERR9: XRA A INR A RET ; Read an ID field from the current track READID: LDA DENSFL ; get density flag ANI 40H ; mask for MFM bit ORI RIDCMD ; add in 765 command MOV B,A MVI C,2 ; 2 byte to send CALL CMDRDY ; issue command LDA RWSTBL ; get ST-0 ANI 0C0H ; mask error bits RET ; Recalibrate selected drive RECAL: LXI B,RECCMD SHL 8 + 2 ; get command CALL CMDRDY ; send it CALL DPOLL ; wait for completion IF MINI AND TPI96 LXI B,RECCMD SHL 8 + 2 CALL CMDRDY ; for 96 TPI mini drives CALL DPOLL ENDIF XRA A STA TRACK ; initialize table RET ; Perform seek on the current drive to track number in reg A SEEK: STA TRACK LXI B,SKCMD SHL 8 + 3 ; get seek command CALL CMDRDY CALL DPOLL RET ; sense current drive status SDS: LXI B,SDSCMD SHL 8 + 2 CALL CMDSND ; issue sense drive status CALL CMDRES LDA RWSTBL ; get ST-3 RET ; Send command to FDC: ; initial command in reg B, addition bytes are sent ; from the READ/WRITE table as requested by the FDC ; if reg C=0 else reg C number of bytes are transfered. CMDRDY: IF MINI ; if 5" system PUSH B ; save command and length LXI B,SDSCMD SHL 8 + 2 CALL CMDSND ; sense drive status CALL CMDRES LXI H,RWSTBL ; point to ST-0 ; LDA LATCH ; turn the motor on ORI MOTOR ; set motor on bit OUT CONTRL ANI MOTOR XOR 0FFH ; reset motor on bit OUT CONTRL ; POP B ; restore callers command and length LDA RWSTBL ; get ST-0 ANI 20H ; test for ready line from drive status JNZ CMDSND ; and issue command if ready ; ; Now time out for one second to allow the drives to start-up LXI H,0 WT1SEC: XTHL XTHL DCX H MOV A,H ORA L JNZ WT1SEC ENDIF ; for MINI CMDSND: IN FDCMSR ; get status ANI 10H ; mask ready bit JNZ CMDRDY ; loop if busy ; MOV A,B ANI 0FH ; mask out command type CPI FMTCMD ; is it a format LXI H,FMTBL JZ CMDOUT ; yes, use format table LXI H,RWTBL ; or use RW table CMDOUT: IN FDCMSR RAL ; test RQM JNC CMDOUT ; loop if not ready RAL ; test DIO RC ; if done MOV A,B ; get output value OUT DDATA ; send it MOV B,M ; get next value INX H DCR C ; cut byte count JNZ CMDOUT ; and loop if not done RET ; read command results from FDC CMDRES: LXI H,RWSTBL ; set result table pointer CMDRES1: IN FDCMSR ; get FDC status RAL ; test RQM JNC CMDRES1 ; loop if not ready RAL ; test DIO RNC ; if done receiving IN DDATA MOV M,A ; store data in table INX H JMP CMDRES1 ; read more info ; ; Disk polling subroutine. This is called when waiting on the 765 ; to perform an operation in which it will interrupt when completed. ; NOTE that for MP/M system driver, the flagwait routine is called ; from the XIOS. ; DPOLL: IN CONTRL ; get control status ANI INTRQ ; test interrupt line JZ DPOLL ; and loop until received CALL FLINT ; clear 765 INT line JC DPOLL ; if invalid interrupt RET ; from DPOLL ; ; ; Now the 765 result phase must be performed for any interrupting ; type of command. If the 765 busy bit is set, the results from a ; read or a write type command must be read. If the 765 busy bit is ; not set, then a sense interrupt status command is sent and the ; results of a seek, recal, or drive ready change interrupt are read out. ; FLINT: IN FDCMSR ; get main status register ANI 10H ; busy? (read or write in process) JNZ RWDN1 ; yes, read results out LXI B,SISCMD SHL 8 + 2 ; get command and length CALL CMDSND ; issue sense interrupt status command RWDN1: CALL CMDRES ; read the results LDA RWSTBL ; get ST-0 ANI 0C0H ; mask error bits CPI 0C0H ; drive ready change? JNZ RWDN2 ; no, exit valid interrupt IN FDCMSR ANI 0FH ; any drive seeking? STC RNZ ; yes, wait for completion RWDN2: ; ; If using the DMA operation mode, the Z80 DMA device is disabled ; and any pending DMA interrupt is reset. ; LDA DMAFL ; get DMA flag ORA A RZ ; if not DMA mode ; MVI A,083H OUT ZDMA ; disable DMA device MVI A,0A3H OUT ZDMA ; reset INT ; XRA A ; clear CY for exit valid RET ; from Polled DPOLL ; ; ; Clear the uPD765 status port. ; This routine is called after an error in a programmed I/O transfer ; with the uPD765. The Main status register is used to clear the device ; of any command or result transfer and re-syncronize with the device. ; RESYNC: IN FDCMSR ; get main status register RLC ; test DRQ bit JNC RESYNC ; wait for this to go true ; IN FDCMSR ANI 10H ; test controller busy bit RZ ; and quit if busy bit reset IN FDCMSR ANI 40H ; now test DIO for direction JNZ SYNC1 ; read data port if bit is set XRA A OUT DDATA ; write null data if bit is reset JMP RESYNC ; and retest main status SYNC1: IN DDATA ; read some data JMP RESYNC ; and retest main status ; ; ; Driver initialization INIT: ; IF NECINI ; if NEC 765 initialization ; initialize Z80 DMA device LXI H,IZDMA ; point to table MVI B,IZDMAL ; get length INI1: MOV A,M INX H OUT ZDMA DCR B JNZ INI1 ; ; specify drive parameters to the 765 LXI H,RWTBL MVI M,SRTHUT INX H MVI M,HLDTND LDA DMAFL ; get DMA flag ORA A JNZ INI2 ; if DMA mode MOV A,M ORI 1 ; set ND bit MOV M,A INI2: LXI B,SCYCMD SHL 8 + 3 ; get command CALL CMDSND ; and send it LXI H,0 SHLD RWTBL ; clear RWTBL ENDIF RET ; from driver initialization ; MESSAGES, TABELS, VARIABLES AND BUFFERS DNRMSG: DB BEL,'\DRIVE NOT READY $' FWRMSG: DB BEL,'\FORMAT WRITE $' WRMSG: DB BEL,'\WRITE $' RDMSG: DB BEL,'\READ $' EOCMSG: DB 'END OF CYLINDER $' ORMSG: DB 'DATA OVER-RUN $' DCMSG: DB 'DATA CRC $' IDCMSG: DB 'ID FIELD CRC $' SNFMSG: DB 'SECTOR NOT FOUND $' WPMSG: DB 'WRITE PROTECT $' DATMSG: DB 'DATA $' IDMSG: DB 'ID FIELD $' ADMSG: DB 'ADDRESS MARK $' ERTMSG: DB 'ERROR, TRACK: $' HDMSG: DB ' HEAD: $' SECMSG: DB ' SECTOR: $' ; Format type tables defines parameters relative to sector format. ; Single-density table IF NOT MINI ; Single-density table SDVALS: DB 0,26,07H,1BH,80H ; N,SC,GPL1,GPL2,DTL DW 07FH ; DMA terminal count DB 128,1 ; PIO transfer code ; Double-density table DDVALS: DB 3,8,35H,74H,0FFH ; N,SC,GPL1,GPL2,DTL DW 03FFH ; DMA terminal count DB 0,4 ; PIO transfer code ; Sector translation tables XLTSD: DB 1,3,5,7,9,11,13,15,17,19,21,23,25 DB 2,4,6,8,10,12,14,16,18,20,22,24,26 XLTDD: DB 1,3,5,7,2,4,6,8 ENDIF IF MINI ; if 5" system ; Single-density table SDVALS: DB 0,16,07H,1BH,80H ; N,SC,GPL1,GPL2,DTL DW 07FH ; DMA terminal count DB 128,1 ; PIO transfer code ; ; Double-density table DDVALS: DB 3,5,35H,74H,0FFH ; N,SC,GPL1,GPL2,DTL DW 03FFH ; DMA terminal count DB 0,4 ; PIO transfer code ; Sector translation table XLTSD: DB 1,3,5,7,9,11,13,15,2,4,6,8,10,12,14,16 XLTDD: DB 1,3,5,2,4 ENDIF LATCH: DB 009H ; disable board memory TRMCNT: DW 0 ; transfer code storage FDCOP: DB 0 ; FDC operation DENSFL: DB 0 ; density flag LACE: DB 0 ; interleave factor RWFL: DB 0 ; read/write/format flag VERFL: DB 0 ; verify flag MPMFL: DB 0 ; MP/M system flag DMAFL: DB 0 ; DMA or PIO mode flag MXDFL: DB 0 ; MXDisk queue flag RECFL: DB 0 ; recalibrate flag RTCNT: DB 0 ; retry counter LNBUF: DB 5 ; console line input buffer DS 8 ; MP/M disk device queue storage ; UQCB: DW 0 ; pointer filled by open DW 0 ; buffer (not used) DB 'MXDisk ' ; queue name (8 chars) ; Read / Write table: ; This table defines sector format to the uPD765 ; and is sent to that device every read or write RWTBL: DRIVE: DB 0 ; currently selected disk TRACK: DB 0 ; currently selected track HEAD: DB 0 ; head address (0 or 1) SECTOR: DB 0 ; current record N: DB 0 ; always double-density EOT: DB 0 ; end of track GPL1: DB 0 ; gap always double-density DTL: DB 0 ; data length always double-density ; Format tabel: ; This table is sent to the uPD765 for formatting a track FMTBL: FMDRV: DB 0 ; current disk number FMN: DB 0 ; bytes/sector code SC: DB 0 ; sectors/track GPL2: DB 0 ; gap length FBYT: DB 0E5H ; e5"s as fill byte RWSTBL: DB 0,0,0,0,0,0,0 ; result phase status table ; Z80 DMA table: Sent to device for initialization or re-initialization IZDMA: DB 083H ; disable DMA DB 0C3H,0C3H,0C3H DB 0C3H,0C3H,0C3H ; reset device. DB 0A3H ; reset INT DB 014H ; port A=memory, port A increments. DB 028H ; port B=I/O, port B fixed. DB 08AH ; stop end of block, ready active high DB 0D5H ; burst mode, ICB and port B LSB follows DB DSKB+6 ; port B LSB DB 002H ; INT at end of block DB 001H ; Port B equals temp source DB 0CFH ; load Port B fixed address IZDMAL EQU $-IZDMA ; length of transfer ; Z80 DMA table: Sent to device for reading and writing. RWDMA: DB 083H ; disable DMA WR0: DB 079H ; Port A address and block length follows ADDR: DW SECBUF ; patched port A address TC: DW 0 ; patched terminal count (length) DB 0CFH ; load registers DB 0ABH ; enable INT DB 087H ; enable DMA RWDMAL EQU $-RWDMA ; length of transfer ; This table is dma'd to the fdc during track formating ; it contains info about cylinder#, head#, sector# and N code CHRN: DS 26*4 SBUF: DS 1024 ; sector buffer for PIO DS 64 ; stack area STACK EQU $ ; stack top END