PAGE 60 TITLE CP/M Loader BIOS for Single Density diskettes ; ; LDRBSD.MAC ; Complete Loader BIOS for Single-density disk operation only. ; This driver is set for Programmed I/O operation only. ; NOTE: This version of the loader BIOS contains no console output. ; ; This source is written for MACRO-80 (Microsoft's Z-80 macro assembler) .Z80 ; for ZILOG ; Loader equates LDRE EQU 100H ; MP/M or CP/M loader entry point STACK EQU 80H ; stack area @UDSK EQU 4 ; default drive number location ; I/O Jump Vector. JP LDRE ; from cold start loader JP LDRE ; from page 0 Warm Boot entry point JP DUMMY ; check console status JP DUMMY ; get console character JP DUMMY ; put console character JP DUMMY ; put list device character JP DUMMY ; put punch device character JP DUMMY ; get reader character JP HOME ; Home the head to track zero JP SELDSK ; select disk JP SETTRK ; seek track JP SETSEC ; set sector JP SETDMA ; set DMA address JP READ ; read selected sector JP WRITE ; write selected sector JP DUMMY ; check list device status JP SECTRN ; translate logical to physical sector DUMMY: XOR A ; Select Disk routine. ; Note - only one disk supported SELDSK: PUSH BC ; save disk number in reg C CALL SPECIFY ; initialize NEC 765 CALL DSKSEL ; select in drive CALL RECAL POP BC ; restore disk number ; LD A,C ; get disk # in ACC OR A ; set flags LD HL,0 RET NZ ; if not disk 0 ; LD HL,DPH ; load header address RET ; done, back to loader ; Home routine. ; Position R/W head on track zero ; HOME: LD BC,0 ; prep for SETTRK ; ; Set Track routine. ; New track number in reg C ; SETTRK: LD A,C LD (TRACK),A ; update storage RET ; done ; Set Sector routine. ; New sector number in reg C ; SETSEC: LD A,C LD (SECTOR),A ; set up RW table LD (EOT),A ; set varible RET ; done ; Sector Translation routine. ; Logical CP/M sector in BC, DE->translate table ; If (SPT) greater than 256, then 16-bit translate table is referenced ; If (SPT) less than 256, 8-bit translate table is referenced ; with register H set to 0. ; if DE=0 then no translation will take place (sector adjusted by 1). ; Return with physical sector in reg HL. ; SECTRN: LD H,B LD L,C ; move sector to HL ADD HL,DE ; point to entry LD L,(HL) ; get 8-bit sector LD H,0 ; mask to 8-bit sector RET ; done ; Set DMA address routine. ; New DMA address in regs BC ; SETDMA: LD (DMAADD),BC ; set it RET ; done ; SPECIAL SINGLE-DENSITY VERSION OF DRIVER ; 765 drive parameter equates. RTRYS EQU 5 ; Number of Retrys ND EQU 1 ; 1=non-DMA mode SRT EQU 6 ; drive step rate in ms. HUT EQU 240 ; Head Unload Time in ms. HLDT EQU 50 ; Head Load Time in ms. SRTHUT EQU (16-SRT) SHL 4 + HUT/16 HLDTND EQU HLDT/2 SHL 1 + ND ; FDC-2800 port assignments. DSKB EQU 080H ; base of controller FDCMSR EQU DSKB+0 ; 765 FDC Main status register DDATA EQU DSKB+1 ; 765 FDC Main data register CONTRL EQU DSKB+2 ; Main board control port ZDMA EQU DSKB+4 ; Z-80 DMA device ; NEC 765 intruction set used. SCYCMD EQU 03H ; specify drive parameters SDSCMD EQU 04H ; sense drive status WRCMD EQU 05H ; write sector RDCMD EQU 06H ; read sector RECCMD EQU 07H ; recalibrate SISCMD EQU 08H ; sense interrupt status RIDCMD EQU 0AH ; read sector ID SKCMD EQU 0FH ; seek command ; Control Port (CONTRL) bit assigment for output. BTENA EQU 01H ; Boot enable (0=true) SBENA EQU 02H ; Sector buffer enable (1=true) INTEN EQU 04H ; Interrupt enable (1=unmask) ROMEN EQU 08H ; ROM enable (0=true) MOTOR EQU 10H ; Motor control (1=start) PIOEN EQU 20H ; programmed I/O mode (1=true) ; Control Port bit assignment for input. INTRQ EQU 80H ; interrupt request (input) ; DISK PARAMETER HEADERS. DPH: DW XLTSD0,0,0,0,DIRBUF,DPB,CSV0,ALV0 ; DISK PARAMETER BLOCK - (18-bytes long) DPB: ; Single-density, single-sided, CP/M standard DW 26 ; sectors per track (SPT) DB 3,7 ; block shift and mask (BSH)(BLM) DB 0 ; extent mask (EXM) DW 242 ; disk storage max (DSM) DW 63 ; director max (DRM) DB 0C0H,0 ; dir alloc (AL0)(AL1) DW 16 ; dir check size (CKS) DW 2 ; offset (OFF) XLTSD0: DB 1,7,13,19,25,5,11,17,23 DB 3,9,15,21,2,8,14,20,26 DB 6,12,18,24,4,10,16,22 WRITE: LD A,1 RET ; Read entry point. On entry, HL points to the drive descriptor block. ; On exit, RESULT in drive descriptor block set to 0 for success or ; error code in the case of read errors. ; READ: ; ; Reset the recalibrate flag ; LD A,1 LD (RECFL),A ; set recal flag ; ; Seek to the selected track. NOTE: re-seek entry point for errors. ; RESEEK: LD BC,SKCMD SHL 8 + 3 ; get seek command and length CALL CMDRDY ; send command CALL DPOLL ; wait for completion ; ; Start of reading and writing. NOTE: retry entry point also. ; LD A,RTRYS ; get initial retry count DRETRY: LD (RTCNT),A ; set retry counter ; ; Issue sense drive status command and retreive results. ; LD BC,SDSCMD SHL 8 + 2 ; get sense drive status command CALL CMDRDY ; issue it CALL CMDRES ; read results LD A,(RWSTBL) AND 20H ; test drive ready JP Z,RWERRE ; exit if NOT ready ; ; Now send the 765 RW table to that device to enable ; the current read or write operation. ; LD B,RDCMD ; FDC command in B LD C,9 ; length of command in C CALL CMDRDY ; issue command ; ; If we are operating the 765 in programmed I/O mode, now ; we must enable Programmed I/O on the controller board and ; set up the registers for the INIR or OTIR loops. ; DI ; LD B,80H ; initial INIR count in B reg LD D,1 ; number of INIR's in D reg LD HL,(DMAADD) ; DMA address in HL LD C,DDATA ; FDC data port in C ; LD A,(LATCH) ; get current control latch OR PIOEN ; set the Programmed I/O bit LD (LATCH),A ; update current control latch OUT (CONTRL),A ; send new latch to the board ; ; Z80 Programmed I/O read loop. Register E contains the count ; value for the INIR instruction and register D contains the ; number of INIR instructions that need be performed. ; PGMRD: INIR DEC D JR NZ,PGMRD ; ; Programmed I/O transfer is now complete. Reset the ; programmed I/O enable bit on the controller board. ; LD A,(LATCH) ; get current latch setting AND PIOEN XOR 0FFH ; reset PIOEN bit LD (LATCH),A ; update current latch OUT (CONTRL),A ; send to controller board ; EI ; ; Now the Programmed I/O timeout bit must be checked in ; case of dead-man timeouts in reading or writing. ; IN A,(CONTRL) AND 40H ; test PIOEN time out error JP Z,WTINT ; if not, wait for interrupt ; ; If the Programmed I/O timeout bit was TRUE, we must attempt ; to determine the cause of the error without the help of the ; NEC 765 returned status registers. ; CALL RESYNC ; Re-syncronize with the uPD765 LD B,RIDCMD CALL READID ; read an ID field JP NZ,RWERRE ; if bad read ID operation LD A,(RWSTBL+3) ; get cylinder LD HL,TRACK ; point current sector CP (HL) ; same? JP NZ,RWERR2 ; no, recal and re-seek JP RWERR3 ; yes, retry disk operation ; ; Reading and writing result phase begins here if using DMA ; operation or there was not a deadman time in Programmed I/O mode. ; WTINT: CALL DPOLL ; wait for completion interrupt LD A,(RWSTBL) ; get ST-0 AND 0C0H ; mask error bits RET Z ; if successful operation ; ; If operating in Programmed I/O mode, a 765 end of track ; error is used to break the FDC out of a read or a write operation. ; This code determines if the pending error condition is an EOT error. ; CP 40H ; check ST-0 for improper termination JR NZ,RWERR1 ; it not, then no EOT error occured LD A,(RWSTBL+1) ; get ST-1 AND 80H ; test for EOT LD A,0 ; prep for error exit routine RET NZ ; yes, normal non-DMA termination ; ; Test for a drive not ready condition and branch ; to the error exit routine with the proper result if true. ; RWERR1: LD A,(RWSTBL) ; get ST-0 result AND 08H ; check drive not ready bit JP NZ,RWERRE ; error if not ready ; ; Now check the wrong cylinder bit in ST-2. If not true, then branch ; to more error routines. If true, then recalibrate the drive and perform ; a re-seek to the selected track. RWERR2 provided for Programmed I/O mode. ; A recalibration and re-seek combination is allowed only once by the ; recal flag (RECFL). ; LD A,(RWSTBL+2) ; get ST-2 result AND 10H ; test wrong cylinder JR Z,RWERR3 ; no, skip recalibrate RWERR2: LD HL,RECFL ; point to recal flag DEC (HL) ; recal done? JR NZ,RWERRE ; yes, skip it CALL RECAL ; recalibrate the drive JP RESEEK ; and re-seek selected track ; ; Check the retry counter for zero and perform preset number ; of retrys to read or write a sector. Variable updated at DRETRY ; entry point. ; RWERR3: LD A,(RTCNT) ; get retry counter DEC A ; one less retry JP NZ,DRETRY ; if not zero ; ; Error exit entry point. ; RWERRE: LD A,1 RET ; done ; Drive select subroutine. The new drive number (0-3) should be in ; register B. The new drive is checked against the current drive selected ; on the board and if not the same, the track table is used to store the ; old track number and get the new drives track number, then the new drive ; is selected on the board. ; DSKSEL: LD A,(@UDSK) AND 3 LD (DRIVE),A RRCA RRCA ; shift to bits 6&7 AND 0C0H ; mask off other bits LD E,A ; keep in E LD HL,LATCH ; point to current latch LD A,(HL) ; get it AND 03FH ; clear old drive select OR E ; add in new drive select LD (HL),A ; restore it OUT (CONTRL),A ; send to controller board RET ; done ; Recalibrate the current board selected drive. The 765 recalibrate ; command is issued and the track field in the RW table set to 0. ; RECAL: LD BC,RECCMD SHL 8 + 2 ; get recal command and length CALL CMDRDY ; issue command CALL DPOLL ; wait for completion RET ; done ; Read sector ID field. The B register contains either a single- ; or a double-density read ID command. The command is issued and ; ST-0 error bits mask. ZERO flag is set if no error occured. ; READID: LD C,2 ; length in C CALL CMDRDY ; issue command CALL DPOLL ; wait for completion LD A,(RWSTBL) ; get status AND 0C0H ; mask error bits RET ; with ZERO set for success ; Send command to NEC 765 subroutine. ; initial command in reg B, additional bytes are sent from the ; beginning of the READ/WRITE table as requested by the 765. ; register C contains the number of bytes that should be transfered. ; CMDRDY: IN A,(FDCMSR) ; get main status register BIT 4,A ; mask FDC busy bit-4 JR NZ,CMDRDY ; loop if busy LD HL,RWTBL ; point to RW table CMDOUT: IN A,(FDCMSR) ; get main status register BIT 7,A ; test RQM JR Z,CMDOUT ; loop if not ready BIT 6,A ; test DIO for direction RET NZ ; if 765 full LD A,B ; get byte for output OUT (DDATA),A ; send it LD B,(HL) ; get next byte for output in B INC HL ; bump RW table pointer DEC C ; count=count-1 JR NZ,CMDOUT ; loop if more to send RET ; done ; ; Receive NEC 765 result phase subroutine. ; The results of an operation are read out of the 765 as ; requested to be read by the DIO bit-6. The results are loaded ; into the RW status table. CMDRES: LD HL,RWSTBL ; set result table pointer CMDRS1: IN A,(FDCMSR) ; get main status register BIT 7,A ; test RQM JR Z,CMDRS1 ; loop if not ready BIT 6,A ; test DIO RET Z ; if done receiving IN A,(DDATA) ; get result byte LD (HL),A ; store data in table INC HL ; bump table pointer JR CMDRS1 ; and loop for more ; Disk polling subroutine. This is called when waiting on the 765 ; to perform an operation in which it will interrupt when completed. ; DPOLL: IN A,(CONTRL) ; get control status AND INTRQ ; test interrupt line JR Z,DPOLL ; and loop until received CALL FLINT ; clear 765 INT line JR C,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 A,(FDCMSR) ; get main status register BIT 4,A ; busy? (read or write in process) JR NZ,RWDN1 ; yes, read results out LD BC,SISCMD SHL 8 + 2 ; get command and length CALL CMDRDY ; issue sense interrupt status command RWDN1: CALL CMDRES ; read the results LD A,(RWSTBL) ; get ST-0 AND 0C0H ; mask error bits CP 0C0H ; drive ready change? JR NZ,RWDN2 ; no, exit valid interrupt IN A,(FDCMSR) AND 0FH ; any drive seeking? SCF RET NZ ; yes, wait for completion RWDN2: XOR 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: XOR A DEC A JR NZ,$-1 ; delay for some time ; IN A,(FDCMSR) ; get main status register BIT 7,A ; test DRQ bit JR Z,RESYNC ; wait for this to go true ; BIT 4,A ; test controller busy bit JP Z,SPECIFY ; and quit if busy bit reset BIT 6,A ; now test DIO for direction JR NZ,SYNC1 ; read data port if bit is set XOR A OUT (DDATA),A ; write null data if bit is reset JR RESYNC ; and retest main status SYNC1: IN A,(DDATA) ; read some data JR RESYNC ; and retest main status ; Issue the specify command to the 765 with the pre-defined drive ; parameter equates at the beginning of this source. This sets the ; internal 765 timers. ; SPECIFY: LD HL,(RWTBL) PUSH HL LD HL,RWTBL ; use RWTBL for transfer LD (HL),SRTHUT ; set SRT and HUT INC HL LD (HL),HLDTND ; set HLT and ND LD BC,SCYCMD SHL 8 + 3 ; get command and length CALL CMDRDY ; issue it POP HL LD (RWTBL),HL ; reset the RW table RET ; Data and Variable area DMAADD: DW 0 ; CP/M DMA address ; 765 read/write table RWTBL: DRIVE: DB 0 ; drive number TRACK: DB 0 ; track number HEAD: DB 0 ; head number SECTOR: DB 0 ; sector number N: DB 0 ; bytes/sector code EOT: DB 0 ; end of track GPL: DB 07H ; gap length DTL: DB 80H ; data length RWSTBL: DB 0,0,0,0,0,0,0 ; read/write status table RTCNT: DB 0 ; retry counter (set to number of retrys) RECFL: DB 0 ; recal flag (set to number of recals) LATCH: DB 009H ; programmed I/O default setting ; Un-initialized data area ALVSIZ EQU 31 CSVSIZ EQU 16 ALV0: DS ALVSIZ CSV0: DS CSVSIZ DIRBUF: DS 128 ; directory buffer ; End of single-density disk driver module END