.z80 true equ 0ffffh false equ 0 mini equ false ;true for minifloppy BIOS special equ false ;true for 8"-5" special board m48tpi equ false ;true for 48tpi mini drives m96tpi equ false ;true for 96tpi mini drives ; 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) pioad equ 4 ;PIO channel A data pioac equ 6 ; " " " control piobd equ 5 ; " " B data piobc equ 7 ; " " " control 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 if m48tpi tracks equ 39 ;minifloppies else tracks equ 76 ;96tpi drives endif else ddpspt equ 8 ;eight inch 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 ;these times double for minifloppies 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 ;------------------------------------------------------------------------ ; This is the seek rate constant!! seekrt, above, is an offset! seekrate equ 1 ;6ms for 8", 12ms for 5" ;seek rate 0=3ms, 1=6ms, 2=10ms, 3=15ms ;these times double for minifloppies ; ; BIOS jump table start:: jp coldboot wboota:: jp wboot jp const jp conin jp conout jp list jp punch jp reader jp home jp seldsk jp settrk jp setsec jp setdma jp read jp write jp listst jp sectran ; ; warm boot table ; filled in by CPMLDR ; ; contains entries of form dw track, db first sector, db last sector ; it is a map of where the image of the CCP and BDOS reside on disk ccpstart:: dw start-1600h ;CP/M boot address bdosjmp:: dw start-0dfah ;BDOS jump bootbl:: ds 16 ;space for four entries page ; ; Seldsk selects the logical disk in C (0-15) ; Seldsk will flush the previous disk if it is in a different driver ; module than the new disk. Seldsk also returns an error if the ; selected disk has not had a driver installed seldsk:: ld A,(curdsk) cp C jr nz,chgdsk ;selecting a disk different from the current ld B,0 ;no other modules have been selected frstime:: ld D,3 jr dskjmp ;if same disk then do normal select for DPH chgdsk:: ld DE,drvtbl ;drvtbl is always on 256 byte boundary add A,A ld E,A ld A,(DE) ;driver module for current disk ld B,A ld A,C add A,A ld E,A ;address of new disk driver page ld A,(DE) ;driver module for new disk or A jr Z,badsel ;new disk not installed ld H,A sub B jr z,samemod ;different disk but same module, don't flush ld B,H push BC push DE ld D,12h ;flush vector call dskjmp ;flush current drive pop DE pop BC or A jr nz,badsel ;error in flushing disk inc A ;make non-zero to indicate a new disk module ld H,B ;module address page samemod:: ld B,A ;B=0 if last disk in same module ld L,3 ;seldsk offset push BC inc DE ;(DE) = driver number ld A,(DE) push DE call jphl ;do select pop DE pop BC ld A,H or L ret z ;bad select from driver ld A,(DE) ;driver number and 7fh ;reset first time flag ld (DE),A ld A,C ;succesfully selected disk ld (curdsk),A ret badsel:: ld HL,0 ret ; ; 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 setdma:: ld (iodma),BC ;shared among all drivers ret page home:: ld D,0 jr dskjmp settrk:: ld D,6 jr dskjmp setsec:: ld D,9 jr dskjmp read:: ld D,0ch jr dskjmp write:: ld D,0fh dskjmp:: ;go to correct disk routine ld A,(curdsk) ;current logical drive getvec:: ld HL,drvtbl dskcon:: add A,A ld E,A ld A,D ;low order byte of vector supplied ld D,0 add HL,DE ld D,(HL) ;high order byte of jump vector inc HL ld E,A ;vector lsb ld A,(HL) ;driver number ex DE,HL jphl:: jp (HL) ;go to driver curdsk:: db 0 ;currently selcted logical drive page ; Console/list vectors const:: ld D,0 jr conjmp conin:: ld D,3 jr conjmp conout:: ld D,6 jr conjmp list:: ld D,9 jr lstjmp listst:: ld D,0ch lstjmp:: ld A,(iobyte) and 11000000b ;get list field rlca rlca ;make 0-3 ld HL,lsttbl jr dskcon conjmp:: ;get jump for console drivers ld A,(iobyte) and 00000011b ;get console field ld HL,contbl jr dskcon reader:: ld A,(rdrtbl) ;reader driver jr gordr punch:: ld A,(puntbl) gordr:: or A ret z ld H,A ld L,0 jp (HL) page ;----------------------------------------------------------------------- ; ; Hardware configuration table ; org 100h-2 iodma:: db 80h ;this is the low order byte ;of the 80h default drvtbl equ $+1 ;logical drive assignment dw fpy ;floppy page dw fpy ;default floppies 0-3 dw fpy+81h dw fpy+82h db 83h ds 23d ;space for drives E::-P:: contbl equ $+1 dw cpu dw cpu ;default=serial 0 db 1 ;alternate=serial 1 ds 3 ;space for two more console drivers lsttbl equ $+1 dw cpu dw cpu+2 ;default=parallel db 1 ;alternate=serial 1 ds 4 rdrtbl:: db 0 ;no reader installed puntbl:: db 0 ;no punch installed page ; ; Wboot reads in the CCP and BDOS from the CPM.SYS file ; CPMLDR initializes the bootbl to be a list of items of ; ; track (dw), first sector(db), last sector(db) ; wboot:: ld SP,100 ;use default buffer ld C,0 ;drive A:: call seldsk ;select drive A:: ld A,H or L jp z,booterr ;bad select ld E,(HL) inc HL ld D,(HL) ;DE=sector translate from DPH ld (bootxlt),DE ld HL,(ccpstart) ;start of CCP-starting DMA nxttrk:: push HL ;unitialized data-sector count push HL ;current DMA ;read sectors of next track ld HL,(bootptr) ;point to bootbl entry ld C,(HL) inc HL ld B,(HL) ;BC=track inc HL push HL ld A,B or C jr z,bootdone ;last track done call settrk pop HL ;boottbl pointer ld C,(HL) ;first sector dec C ;will be incremented before setsec inc HL ld B,(HL) ;last sector inc HL ld (bootptr),HL ;save pointer pop HL ;DMA pop DE ;old sectors push BC ;new sectors push HL ;DMA page nxtsec:: pop HL ;DMA push HL ld B,H ld C,L ;move to BC call setdma pop HL ;last DMA pop BC ;sector data ld A,B ;last sector to read inc C ;read next sector cp C jr c,nxttrk ;done with last sector ld DE,128 add HL,DE ;update DMA push BC push HL ld B,0 ;C=sector to read (<256) ld DE,(bootxlt) call sectran ld C,L ld B,H call setsec call read or A jr nz,booterr ;physical read error jr nxtsec bootdone:: ;CP/M read from disk, set low memory ld HL,(bdosjmp) ld (6),HL ;normal BDOS jump ld HL,wboota ld (1),HL ;warm boot jump ld A,0c3h ;'JP' intruction ld (0),A ld (5),A ld HL,bootbl ld (bootptr),HL ;reset pointer for next warm boot ld HL,drvtbl+3 ld b,15d ;reset all drives but A:: to not logged unlog:: set 7,(HL) inc HL inc HL djnz unlog ld HL,(ccpstart) ld A,(cdisk) ld C,A jp (HL) ;go to CP/M bootptr:: dw bootbl ;initial track value bootxlt:: dw 0 ;boot disk sector translate table page booterr:: ld DE,badbootmsg call pmsg halt ;must reset system badbootmsg:: db 'Warm boot error-reset system' crlf:: db 0dh,0ah,'$' page cpuio equ (($-start-1)/256)+1 ; ; 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) org cpuio*256 cpu:: ;standard console/list routines for CPU serial ports jp pserin ; jp serin ;these three go direct to serial jp serout ; jp lstout jp pollst db 'SBCIO ' ;module name for display 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 ; these are the drivers modified for use with the printer lstout:: cp 2 ;parallel? jr z,plist ld B,A ;must be serial ld E,C lstout1:: call plserout or A ld A,B jr z,lstout1 dec C out (C),E ret pollst:: cp 2 jr z,polplist plserout:: add A,A ;serial out poll for printer-check busy line inc A ld C,A ld A,10h ;reset DART latch to get current value of BUSY out (C),A in A,(C) and 0ch cp 0ch ld A,0ffh ret z cpl ret page polplist:: ;parallel printer driver poll ld A,(frstpar) ;first time parallel called? or A jr z,noinit ;no, interface already initialized out (pioac),A ;set channel A to output ld A,0cfh out (piobc),A ;set channel B to bit mode ld A,00011111b ;bit 7-5 output, 4-0 input out (piobc),A xor A out (piobd),A ld (frstpar),A ;reset first time flag noinit:: in A,(piobd) and 1 ;test busy bit ret z ;printer busy ld A,0ffh ret ;printer not busy plist:: ;parallel printer driver call polplist or A jr z,plist ld A,C out (pioad),A ;character on PIO A channel ld A,80h out (piobd),A ;strobe printer xor A out (piobd),A ;reset strobe ret frstpar:: db 0fh ;first initialization code for parallel ;printer-flag that PIO has not been initialized page ; miscellaneous character i/o routines pmsg:: ;equivalent to BDOS function 9 (print) ld A,(DE) cp '$' ret z ld C,A inc DE push DE call conout pop DE jr pmsg phex:: ;print A in hex push AF rra rra rra rra call hex1 pop AF hex1:: and 0fh add A,90h daa adc A,40h daa ld C,A jp conout fpyio equ (($-start-1)/256)+1 ;make line up on page boundary ; ; Floppy disk drive driver module ; org fpyio*256 fpy:: ; floppy driver jump table jp fhome jp fseldsk jp fsettrk jp fsetsec jp fread jp fwrite jp fflush db 'FLOPPY ' ;module name for display purposes dskparm:: ;disk hardware parameter block ;one block for each drive rept 4 if (mini and not special) 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 and not special) db ddpspt else db 26 ;physical sectors per track - 26 in SD, ddpspt in DD endif db 0 ;track drive currently set at endm page ; ; CP/M disk tables fDPHbase:: dw trans,0,0,0 dw dirbuf,fdpbase dw chk00,all00 dw trans,0,0,0 dw dirbuf,fdpbase dw chk01,all01 dw trans,0,0,0 dw dirbuf,fdpbase dw chk02,all02 dw trans,0,0,0 dw dirbuf,fdpbase dw chk03,all03 ; ; 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) page 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 page 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 page ; ; Fseldsk selects the physical floppy in A (0-3) ; B=0 if last disk selected was a different floppy ; C=logical disk the floppy corresponds to fseldsk:: ld D,A ;physcial floppy ld A,B ;zero if last selected was floppy cp 128 ;first select after cold boot? jr nz,notfirst ld C,B ;make density check on drive A:: notfirst:: 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 A,(curfpy) call seldph 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 A,(tempfpy) ld (curfpy),A ;restore changes made by getden ld HL,0 ret ;return error seldph:: add A,A add A,A add A,A add A,A ld L,A ld H,0 ld DE,fdphbase add HL,DE ret 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,seekerr ;seek successful ld (IX+drvtrk),C ret seekerr:: ld E,1ch ;seek command call diskerror jr seek2 page ; write writes the sector to the selected disk fwrite:: ld IX,(hwptr) ;restore IX to parameter block ld A,(IX+density) or A jr z,wrtsngl ld A,2 cp C jr z,unalloc dec A cp C jr z,directry ;directory sector jr wrtnorm unalloc:: ;unallocated sector write (no preread) call fflush or A ret nz ;flush error ld A,(iosec) and not(hstcnt-1) ;get first sector in block ld (blksec),A add A,hstcnt ld (blk2sec),A ;if this is an unallocated block ;then two physical sectors (2K) were ;unallocated ;here we have a sector to write that ;is currently in the host buffer movwrt:: call mkbufad ;get correct address in buffer ex DE,HL ldir ld A,0ffh ld (wrtpend),A ;write pending cpl ;return with no errors ret directry:: ;write to a directory sector ;the disk may be removed after this ;so we will make this write immediate call wrtnorm ;do a normal write or A ret nz ;error in previous flush or read jr fflush ;and flush this sector wrtnorm:: ;a block that has been written before call inbuf ;returns C flag set if not in buffer jr nc,movwrt ;and A to relative sector if in buffer ;not in buffer but might be 2nd 1K of ;an unallocated 2K data block ld A,(blk2sec) cp D ;iosec jr nz,wrtdbl ;unless first sector of 2nd 1K in ;unallocated data block call inbuf2 ;checks iotrk=blktrk jr c,wrtdbl call fflush ;previously unallocated data or A ret nz jr movwrt ;and write this sector wrtsngl:: ld A,(iosec) ld (psec),A ld A,0A3h ;second byte of OUTI instruction ld (iotran+1),A ld A,0ACh ;sector write ld (oper),A jp strtsel ;and do i/o wrtdbl:: call fflush ;flush buffer or A ret nz ;return bad sector if hard flush error call sidesec ;set up for new i/o call readprep ;do read to buffer or A ret nz ;physical error jr movwrt ;and move new sector from DMA page fflush:: ;flush deblocking buffer to disk ld A,(wrtpend) ;do we have a write pending? or A ld A,0ffh ld (bufvalid),A ;buffer data not valid cpl ret z push IX ;save pointer (drive may have changed) ld IX,(blkptr) ;for most recently selected drive ;which is the one to flush ld A,0A3h ;second byte of OUTI instruction ld (iotran+1),A ld A,0ACh ;FDC write sector command ld (oper),A ld HL,(iodrvtrk) push HL ld HL,(blkdrvtrk) ld (iodrvtrk),HL ;set drive to select and track ld A,(blksec) call sideflsh ;set up head bit and sector for flush call strtsel ;do physical flush pop HL ld (iodrvtrk),HL ;restore selected drive and track pop IX ld (wrtpend),A ;clear flag if no errors ret 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 ret ;transfer done rddbl:: call fflush ;flush previous buffer or A ret nz ;physical error in flush call sidesec call readprep or A ret nz 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 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 di ;no interrupts from here to end of transfer 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 ;now ok to interrupt-status is saved 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,E cp 1ch ;was it a seek error? jr z,pseekerr cp 0ach ;writing? jr z,pwriterr ld DE,rderr jr perrtyp pwriterr:: ld DE,wrterr jr perrtyp pseekerr:: ld DE,skerr perrtyp:: call pmsg ld DE,trkerr call pmsg ld A,(iotrk) call phex ld DE,secerr call pmsg ld A,(psec) call phex ld DE,siderr call pmsg ld A,(head) call phex pdrv:: ld DE,drverr call pmsg ld A,(curfpy) call phex ld DE,crlf call pmsg ld A,255 ret ; ; 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 not 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 inbuf2:: ld HL,(iodrvtrk) ;check for 2nd sector ;of unallocated block ld BC,(blkdrvtrk) sbc HL,BC ;same drive and track jr z,rttrk 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 ; stores drive, track, sector of contents of buffer for use by flush ; also saves hardware pointer and sets buffer valid flag ; returns HL=start of sector in buffer, DE=DMA address, BC=128, A=0 mkbufad:: ld (blkptr),IX ld HL,(iodrvtrk) ld (blkdrvtrk),HL ld A,(iosec) ld B,A and not(hstcnt-1) ld (blksec),A ld A,B and hstcnt-1 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) xor A ld (bufvalid),A 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,E or A jr z,logdin ;logical drive A:: bit 7,D jr z,logdin ;drive already logged in call fflush ;getden uses buffer or A ret nz ld A,0d0h ;reset FDC out (cmd),A ld A,(curfpy) ld (tempfpy),A ;save most recent floppy ld A,D and 7fh ld (curfpy),A 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,(iodma) ;save old DMA address push HL ld HL,hstbuf ;buffer has already been flushed ld (iodma),HL call rdsngl pop HL ld (iodma),HL ;restore dma 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 jr c,codeok ld DE,badcode ;not our code byte call pmsg call pdrv ld A,255 ret 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 logdin:: ld A,D ;drive already logged in ld (curfpy),A call getparm ld A,(IX+density) jr codeok ; drive can't change density page ; ; Floppy disk driver storage ; iodrvtrk:: curfpy:: db 0 ;current selected physical floppy drive iotrk:: ds 1 ;current track for current disk blkdrvtrk:: ds 2 ;drive and track for deblocking buffer 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 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 retryc:: db 0 ;number of retries left newfpy:: db 0 ;new floppy to be selected tempfpy:: db 0 ;storage for current floppy while checking ;density of new one head:: db 0 ;head control = 0 or 1 oper:: db 0 ;operation (read/write) to be performed next hwptr:: dw dskparm ;storage for pointer to current hw parameters blkptr:: dw dskparm ;pointer to paramters for block drive ; error messages badcode:: db 'Can''t recognize density of disk in$' rderr:: db 'Read$' wrterr:: db 'Write$' skerr:: db 'Seek$' trkerr:: db ' error on track $' secerr:: db ' sector $' siderr:: db ' side $' drverr:: db ' drive $' 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 org fpy+500h coldboot:: dirbuf:: ;coldboot code lives in the directory buffer xor A ld (iobyte),A ;back to default routing ld (cdisk),A ;user 0, drive A:: ld DE,signon call pmsg ld A,(wboota+2) ;warm boot vector add A,6 srl a srl A ;A=memory size in K call twodec ;get ASCII digits for memory size push BC ld C,B call conout pop BC call conout ld DE,sign2 call pmsg ld A,(contbl+1) ;default console add A,31h ;make 1-2 ld C,A call conout ld DE,prnmsg call pmsg ld A,(lsttbl+1) cp 2 jr c,serprn ld DE,parprn call pmsg jr lastmsg serprn:: push AF ld DE,serpmsg call pmsg pop AF add A,31h ld C,A call conout lastmsg:: ld DE,sign3 call pmsg page ld E,0 if not special ld BC,8000h ;get density of boot floppy ld A,(drvtbl) ld H,A ld L,3 ;select vector ld A,(drvtbl+1) or 80h ;set flag for first logon call jphl ;select boot drive in BIOS ld A,H or L jp z,booterr ;can`t select system drive endif jp bootdone ;set up low memory and go to CCP page signon:: db 0dh,0ah,'Super Quad CP/M v X2.0',0dh,0ah if special db 'Special 8"-5" BIOS',0dh,0ah endif if mini db 'for ' if m48tpi db '48' else db '96' endif ' tpi minifloppies',0dh,0ah endif db 0dh,0ah,'$' sign2:: db 'K CP/M 2.2 installed',0dh,0ah,0ah db 'Default console is serial port $' prnmsg:: db 0dh,0ah,'Default printer is $' parprn:: db 'parallel printer driver$' serpmsg:: db 'serial port $' sign3:: db 0dh,0ah,0ah,'$' page twodec:: ;convert A < 100 to two ASCII digits in BC ld BC,0ff0ah nxtten:: inc B sub C jr nc,nxtten add A,C add A,30h ld C,A ld A,30h add A,B ld B,A ret hstbuf equ dirbuf+128 ;sector deblocking buffer ds 1024+128-($-dirbuf) chk00:: ds 32 ;the check an allocation vectors are assigned all00:: ds 86 ;to physical floppies 0-3 ;if fewer than four drives are connected, or if ;some drives are single sided, these areas can ;be reduced chk01:: ds 32 all01:: ds 86 chk02:: ds 32 all02:: ds 86 chk03:: ds 32 all03:: ds 86 ; ; end