.z80 ;-------------------------------------------------------------------------- ; ; Floppy disk loader bios ; ;-------------------------------------------------------------------------- true equ 0ffffh false equ 0 mini equ false ;true for minifloppy BIOS special equ false ;true for 8"-5" special board bootdrv equ 0 ;floppy to boot from bootcon equ 0 ;console for loader messages ; board hardware equates cmd equ 0ch ;fdc command register trk equ cmd+1 ;track register sec equ cmd+2 ;sector register data equ cmd+3 ;data register wait equ 14h ;INTRQ and DRQ synch port (see manual) memry equ 16h ;memory control port ; sector deblocking equates hstcnt equ 8 ;number of sectors in buffer hstshft equ 3 ;shift factor for # of sectors in buffer if mini ddpspt equ 4 ;double density physical sectors per track tracks equ 39 ;minifloppies else ddpspt equ 8 tracks equ 76 endif dpblen equ 15 ;length of a DPB ; disk hardware parameter offsets density equ 0 ;0=single, 1=single side double D, 2= 2S2D seekrt equ 1 ;seek rate 0=3ms, 1=6ms, 2=10ms, 3=15ms pspt equ 2 ;physical sectors per track (one side) drvtrk equ 3 ;track a floppy is at parmlen equ 4 ;length of the parameter block ; miscellaneous equates iobyte equ 3 ;used to select various consoles and printers cdisk equ 4 ;default disk user number retries equ 10 ;retry count for disk operations page 60 seekrate equ 1 ;6ms for 8", 12ms for 5" ; ; BIOS jump table ;start: jp coldboot ;wboota: jp wboot ret dw 0 ret dw 0 jp const jp conin jp conout ; jp list ; jp punch ; jp reader ret dw 0 ret dw 0 ret dw 0 jp fhome jp fseldsk jp fsettrk jp fsetsec jp fsetdma jp fread ; jp fwrite ret nop nop ; jp listst ret dw 0 jp sectran page ; console vectors for later use const: ld A,bootcon jp pserin conin: ld A,bootcon jp serin conout: ld A,bootcon jp serout ; ; Sectran input: Logical sector in BC, translate table in DE ; no translation if DE=0 ; ; Returns physical sector in HL ; Note: only used in single density sectran: ld A,D or E ld L,C ld H,B ret z ;no sector translation ex DE,HL add HL,BC ld L,(HL) ld H,0 ret page ; ; Console/list drivers ; All have entry A=driver number, other parameters per CP/M ; A=0 serial port 0; A=1 serial port 1; A=2 parallel (list only) cpu: ;standard console/list routines for CPU serial ports jp pserin ; jp serin ;these three go direct to serial jp serout ; pserin: ;poll serial in-return A=0ff if char ready, else 0 add A,A inc A ;A=command port ld C,A in A,(C) and 1 ret z ;no character waiting ld A,0ffh ret serin: ld B,A call pserin ld A,B jr z,serin ;loop until character received add A,A ld C,A in A,(C) and 7fh ;mask high order bit ret serout: ld B,C ;character to output add A,A inc A ld C,A serst: in A,(C) and 4 jr z,serst dec C out (C),B ret page page ; ; Floppy disk drive driver module ; dskparm: ;disk hardware parameter block ;one block for each drive rept 4 if mini db 1 ;single sided double density else db 0 ;density - 0=single D; 1=1 side, Double D; 2= 2S 2D endif db seekrate ;seek rate - 0=3ms, 1=6ms, 2=10ms, 3=15ms if mini db ddpspt else db 26 ;physical sectors per track - 26 in SD, ddpspt in DD endif db 0 ;track drive currently set at endm ; ; CP/M disk tables fDPHbase: dw trans,0,0,0 dw dirbuf,fdpbase dw 0,0 ; ; DPB's for the three formats of disk: ; ; 8" Single sided, single density ; 5" Single sided, double density ; 5" Double sided, double density ; fdpbase: sssddpb: ;single density (1K block) dw 26 ;sectors per track db 3 ;block shift (log2[block size/128]) db 7 ;block mask ([block size/128]-1) db 0 ;extent mask dw 242 ;highest block on disk (numbered from 0) dw 63 ;directory entries-1 db 0c0h ;alloc0 -first two bits for two blocks for Dir db 0 ;alloc1 dw 16 ;checked directories dw 2 ;track offset (system tracks) ssdddpb: ;single sided double density dw 8*ddpspt ;128 byte sectors per track db 4 db 0fh db 0 dw (tracks*ddpspt/2)-1 ;each of 76 tracks has ddpspt/2 2K blocks if mini dw 63 else dw 127 endif if mini db 80h else db 0c0h endif db 0 dw 32 dw 1 dsdddpb: ;double sided double density dw 16*ddpspt ;one track consists of both sides db 4 db 0fh db 0 dw (tracks*ddpspt)-1 ;each of 76 tracks has ddpspt 2K blocks if mini dw 127 else dw 255 endif if mini db 0c0h else db 0f0h endif db 0 dw 32 dw 1 trans: ;single density translate table db 1,7,13,19,25,5,11,17,23,3,9,15,21 ;sectors 1-13 db 2,8,14,20,26,6,12,18,24,4,10,16,22 ;secotrs 14-16 fhome: ld C,0 fsettrk: ld A,C ld (iotrk),A ret fsetsec: ld A,C ld (iosec),A ret fsetdma: ld (iodma),BC ret page ; ; Fseldsk selects the physical floppy in A (0-3) fseldsk: ld D,bootdrv ld E,C ;save logical disk call getden ;will set density byte if successful or A jr nz,fbadsel ;couldn't get density ;all physical operations OK here ld (hwptr),IX ;store for later use ld A,(IX+density) inc A ;make 1-3 ld B,A ld HL,fdpbase-dpblen ld DE,dpblen fgetdpb: add HL,DE djnz fgetdpb ;HL=DPB address ld C,L ld B,H ld HL,fdphbase ld A,(IX+density) or A jr z,setran ;single density,set translate vector xor A ld (HL),A inc HL ld (HL),A jr putdpb setran: ld DE,trans ;single density translate table ld (HL),E inc HL ld (HL),D putdpb: ld DE,9 ;offset of DPB in DPH add HL,DE ld (HL),C inc HL ld (HL),B dec HL dec HL sbc HL,DE ;restore DPH (carry reset by or A) ret fbadsel: ld HL,0 ret ;return error page ; ; Seldrv selects the drive from (curfpy), head from (head) ; (bit 0 set for head1), and density from (IX+density) ; it assembles the correct byte and outputs to wait ; and updates the track register with the most recent information seldrv: ld A,(IX+density) or A jr z,setdens ld A,00001000b ;set double density bit ld B,A ld A,(head) rlca rlca ;move head bit to bit 2 or B setdens: ld B,A ld A,(curfpy) or B out (wait),A ld A,(IX+drvtrk) ;get track from parameter table ;this may be first physical i/o out (trk),A ret page ; ; Seek attempts to step the R/W head to the track in (iotrk) ; seek: ld A,retries ld (retryc),A seek2: ld A,(iotrk) ld C,A ;track stays in C in A,(trk) sub C ret z ;already there ld A,C out (data),A ld A,(IX+seekrt) ;seek rate mask or 1ch ;seek with verify di out (cmd),A in A,(wait) ei rla jr c,seekerr ;no INTRQ from FDC in A,(cmd) and 10011001b ;seek error, CRC error, or incomplete jr nz,seeker ;seek successful ld (IX+drvtrk),C ret seekerr: call diskerr jr seek2 page page ; ; Read reads the sector from the selected disk ; In double density, the sector may already be in the host buffer ; fread: ld IX,(hwptr) ;restore parameter pointer ld A,(IX+density) ;density byte or A jr z,rdsngl ;singl density call inbuf jr c,rddbl ;sector not in buffer ; sector is in buffer movrd: call mkbufad ;HL=start of sector in buffer ldir xor A ;no errors ret ;transfer done rddbl: call sidesec call readprep or A ret nz ld (bufvalid),A ;buffer contains valid data ld A,(iotrk) ld (blktrk),A ld A,(iosec) and hstcnt-1 jr movrd rdsngl: ld A,(iosec) ld (psec),A ;physical sector same as CP/M sector ;in single density readprep: ld A,0A2h ;second byte of INI instruction ld (iotran+1),A ;patch rdwrite routine ld A,08ch ;sector read command ld (oper),A page strtsel: call seldrv ;physically select drive and head call seek ;step to correct track ; ; diskio actually reads or writes the necessary sector ; it assumes that the head has already settled on the correct track ; (and that the head has been selected on the correct side!) ; and that the bytes in rdwrite for R/W and sector size have been filled diskio: ld A,retries ld (retryc),A iotry: ld A,0d0h ;force interrupt no conditions di out (cmd),A ld A,(oper) ld B,A ld C,data ;prepare for indirect I/O intruction ex (SP),HL ;waste some time ex (SP),HL ex (SP),HL ex (SP),HL in A,(cmd) and 20h ;bit 5=head loaded rrca rrca rrca ;move bit 5 to bit 2 cpl and B ; the purpose of these manipulations has been ; to set bit 2 of the FDC command if the head ; isn't settled. Bit 2 will give a 15 ms delay ld E,A ld A,(IX+density) or A ld D,1 ;one sector i/o transfer for single denisty jr z,dmasingl ;use the CP/M DMA buffer in single density ld HL,hstbuf ;use host buffer for double density operations ld D,hstcnt ;number of 128 byte units to transfer jr strtio dmasingl: ld HL,(iodma) strtio: ld A,(psec) out (sec),A ;set physical sector ld A,E out (cmd),A ;start read/write operation call rdwrite ;do the actual i/o in A,(cmd) ei ld E,A ;save status or B ;B returned from rdwrite is lost bytes count ret z ;if status OK and no lost bytes call diskerror call seek or A jr z,iotry ;if nonzero then hopeless seek error ret ; ; rdwrite does the actual transfer to/from the FDC ; HL set to DMA address on entry, D=number of 128 byte units to transfer ; transfer direction has been set by poking INI or OUTI instruction rdwrite: ld B,128 ;bytes in one CP/M sector loop: in A,(wait) or A ret p ;no more DRQ iotran: ini ;start with read ;the second byte of this instruction is patched to ;be either INI or OUTI depending on need jr nz,loop dec D jr nz,rdwrite jr loop ;sector done, wait for INTRQ from fdc ; disk error will eventually have all kinds of nice messages ; diskerror: ld A,(retryc) dec A jr nz,restore ;more retries to attempt ld A,1 ;return error code to CP/M pop DE ret ;return to BDOS ; ; ; Restore is called from a disk operation with A=retries left ; restore: ld (retryc),A in A,(cmd) and 00010000b ;bit 4=record not found ret z ;try again if record was found but ;read/written with error resto1: ;restore to track 0 and seek again ld A,(IX+seekrt) ;get seek rate mask or 00001100b ;head load, restore, verify track 0 out (cmd),A tk0wait: in A,(cmd) and 00000100b ;at track 0 jr z,tk0wait xor A out (trk),A ;back at track 0 ld (IX+drvtrk),A ;update track table ret page ; ; sidesec is the read/write preparation for double density ; sidesec computes the correct physical sector and side ; sidesec: ld A,(iosec) and not(hstcnt-1) ;computer first CP/M sector in block ld (blksec),A sideflsh: ;called to set up for a flush rept hstshft rrca endm ;A=physical sector number, but it may ;be on the second side ld B,(IX+pspt) cp B ld C,0 jr c,side0 sub B inc C side0: inc A ld (psec),A ;physical sector on one side ld A,C ld (head),A ;set head control byte ret page ; ; inbuf returns carry flag set if sector no in buffer ; also returns (iosec) in D ; if sector is in buffer, returns offset (0 - hstcnt-1) in A ; inbuf: ld A,(iosec) ld D,A ld A,(bufvalid) ;0 if contains valid data, else 255 rra ret c ld A,(iotrk) ld B,A ld A,(blktrk) cp B jr z,rttrk ;on right track, at least scf ret ;not a match rttrk: ld A,(blksec) ld B,A ld A,D sub B ret C ;sector lower # than buffer cp hstcnt ;carry set if in buffer ccf ret ; mkbufad receives A=relative sector in host buffer ; returns HL=start of CP/M sector ; uses B,HL,DE mkbufad: ld B,A ;B=relative sector in buffer inc B ld HL,hstbuf-128 ld DE,128 shft2: add HL,DE djnz shft2 ld BC,128 ;make ready for sector LDIR ld DE,(iodma) ret ; ; returns IX=start of DHPB (disk hardware parameter block) for ; the drive in A (0-3) ; uses B,DE also, returns D=0 getparm: ld B,A inc B ld IX,dskparm-parmlen ;hardware parameter block ld DE,parmlen shft1: add IX,DE djnz shft1 ret page ; ; Getden attempts to find the density of the disk in drive (D) ; by trying to read the current track address in both densities ; If the attempt is successful, Getden will update the ; dens, pspt, and drvtrk fields of the paramter table ; If E (logical disk) is zero, then getden assumes the density hasn't ; changed (if it has, then we can't do a warm boot-table is wrong getden: ld A,0d0h ;reset FDC out (cmd),A ld A,D call getparm if mini if not special set 3,A ;set double density for minifloppy endif endif out (wait),A ;select new drive, head 0, single D call resto1 ;back to track zero xor A ld (iotrk),A if not mini ld (IX+density),A ;track zero always single density endif inc A if mini ld (IX+density),A ;double density for minifloppies endif ld (iosec),A ;read sector 1 ld HL,hstbuf ;buffer has already been flushed ld (iodma),HL call rdsngl or A ret nz ;can't read sector ld A,(hstbuf+7fh) ;code byte for disk type OR A jr z,codeok ;some SD disks have old loaders here sub 0e5h ;code for a normal single density disk cp 3 ret nc ;not our code byte codeok: ld (IX+density),A or A jr z,snglspt ld A,ddpspt ;physical sectors on one side of DD jr putpspt snglspt: ld A,26d ;single density physical sectors putpspt: ld (IX+pspt),A xor A ret ;no errors page ; ; Floppy disk driver storage ; iotrk: ds 1 ;current track for current disk iosec: ds 1 ;current logical sector for DD, physical for SD blksec: ds 1 ;first logical sector in current host blk2sec: ds 1 ;8th CP/M sector in an unallocated 2K block blktrk: ds 1 ;track the buffer is from psec: ds 1 ;current physical sector bufvalid: db 0ffh ;buffer contains valid data for (blksec) ;0 = valid data wrtpend: db 0 ;write pending from buffer iodma: dw 80h ;current dma address retryc: db 0 ;number of retries left curfpy: db 0 ;current selected physical floppy drive head: db 0 ;head control = 0 or 1 oper: db 0 ;operation (read/write) to be performed next trkstor: db 0 ;storage for iotrk during a flush hwptr: dw dskparm ;storage for pointer to current hw parameters page ; ; disk buffers ; These are not part of the floppy driver module as such and ; should be shared by all disk modules as much as possible dirbuf: ;coldboot code lives in the directory buffer ; hstbuf equ dirbuf+128d ;host sector deblocking buffer ;no allocation vectors needed end