TITLE ADVANCED DIGITAL LOADER BIOS FOR SUPER QUAD Oct 15 1982 08:00 ;***************************************************************** ;** ** ;** ADVANCED DIGITAL SUPER QUAD Z80 SBC ** ;** ** ;** LOADER BIOS ** ;** ** ;** This bios contains drivers for: ** ;** ** ;** Floppy disk or ** ;** Hard disk ** ;** Serial input & output ** ;** ** ;** Written by: ** ;** ** ;** Scott Carter ** ;** Greg Lindberg ** ;** ** ;***************************************************************** SUBTTL Customization Equates page 60 .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 nmbfpy equ 2 ;number of floppy disk drives ;------------------------------------------------------------------------ ; This is the seek rate constant!! seekrt, below, is an offset! seekrate equ 0 ;6ms for 8", 12ms for 5" ;seek rate 0=3ms, 1=6ms, 2=10ms, 3=15ms ;these times double for minifloppies ;*** Hard Disk selection choices *** ST503 EQU 1 ; Seagate Technology ST503 ST506 EQU 2 TM601S EQU 3 ; Tandon Magnetics TM601S TM602S EQU 4 TM603S EQU 5 TM603SE EQU 6 TM501 EQU 7 ; Tandon Magnetics TM501 TM502 EQU 8 TM503 EQU 9 SA602 EQU 10 ; Shugart Associates SA602 SA604 EQU 11 SA606 EQU 12 SA1002 EQU 13 ; Shugart Associates SA1002 SA1004 EQU 14 Q2010 EQU 15 ; Quantum Q2010 Q2020 EQU 16 Q2030 EQU 17 Q2040 EQU 18 M4010 EQU 19 ; MiniScribe 4010 M4020 EQU 20 ;*** HDC1001 Physical drives *** hd0 equ SA1002 ; Set to type of drive or false if not used SUBTTL SYMBOLIC EQUATES page ; 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 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 ; ; floppy disk hardware parameter offsets ; density equ 0 ;0=single, 1=single side double D, 2= 2S2D seekrt equ 1 ;THIS IS OFFSET INTO TABLE, VALUE IN TABLE CAN BE ;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 inbfsz equ 32 ;size of input buffer for interrupt input must be power of 2 outbfsz equ 64 ;size of output buffer for interrupt output must be power of 2 SUBTTL Hard disk equates page ;*** HDC1001 Disk equates *** HOFF EQU 1 ; Number of reserved tracks for loader TST MACRO DN ;physical hard disk defined IF HD&DN x defl 1 else x defl 0 endif endm hddsks defl 0 ;number of physical hard disk drives hdldrvs defl 0 ;number of logical hard disk drives hdtst macro tst %hddsks ;test for physical drives iff x exitm endif .lall hddsks defl hddsks+1 .xall endm ;end hdtst hdtst ;calculate number of physical hard disks if hddsks ;set flag for hard disks hard equ true else hard equ false endif .sfcond if hard ;put this stuff in only if needed .lfcond ;*** Port equates for HDC1001 *** HDCBASE EQU 0E0H ; Base of HDC1001 HDCDATA EQU HDCBASE ; Data port WPC EQU HDCBASE+1 ; Write precomp port HDCERR EQU WPC ; Error port SECNT EQU HDCBASE+2 ; Sector count SECNO EQU HDCBASE+3 ; Sector number CYLLO EQU HDCBASE+4 ; Cylinder low CYLHI EQU HDCBASE+5 ; Cylinder high SDH EQU HDCBASE+6 ; Size/Drive/Head COMND EQU HDCBASE+7 ; Command register STATUS EQU COMND ; Status register ;*** Command equates for HDC1001 *** CREST EQU 10H ; Restore command CSEEK EQU 70H ; Seek command CREAD EQU 20H ; Read sector command CWRITE EQU 30H ; Write command CFORM EQU 50H ; Format track inter equ 8 ;hard disk sector interleave factor secs equ 16 ;Physical sectors per track per head hstsiz equ 512 ;size of a hard disk physical sector hdstcnt equ hstsiz/128 ;cp/m sectors per physical sector blksiz equ 4096 ;cp/m block size cpmspt equ hdstcnt*secs ;cp/m sectors per track per head cpmscbk equ blksiz/128 ;cp/m sectors per cp/m block ; dpbg macro dn,secs,bls,blm,ext,dks,dir,al0,al1,cks,off,phys .lall dpb&dn: dw secs ;sec per track db bls ;block shift db blm ;block mask db ext ;extnt mask dw dks ;disk size-1 dw dir ;directory max db al0 ;alloc0 db al1 ;alloc1 dw cks ;check size dw off ;offset db phys ;physical disk drive .xall endm hdscg macro dn,mxcl,mxhd,stprt .lall hdesc&dn: db hdcbase ;base i/o port address db dn ;physical unit no. db inter ;hardware interleave db secs ;sectors per track dw mxcl ;last cylinder db mxhd ;last head db hstsiz/128 ;sector size/128 db stprt ;step rate .xall endm dsktyp macro dn,typ .lall .sfcond if typ eq ST503 hpb&dn macro no dw -1,hdesc&dn als&no defl 004Ch ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,128,5,31,1,607,511,240,0,0,1,%dn hdscg %dn,152,1,3 endm hddr&dn defl 1 endif if typ eq ST506 hpb&dn macro no dw -1,hdesc&dn als&no defl 0098h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,1215,511,240,0,0,1,%dn hdscg %dn,152,3,3 endm hddr&dn defl 1 endif if typ eq TM601S hpb&dn macro no dw -1,hdesc&dn als&no defl 004Ch ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,128,5,31,1,607,511,240,0,0,1,%dn hdsc %dn,152,1,3 endm hddr&dn defl 1 endif if typ eq TM602S hpb&dn macro no dw -1,hdesc&dn als&no defl 0098h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,1215,511,240,0,0,1,%dn hdsc %dn,152,3,3 endm hddr&dn defl 1 endif if typ eq TM603S hpb&dn macro no dw -1,hdesc&dn als&no defl 00E4h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,1823,1023,255,0,0,1,%dn hdscg %dn,152,5,3 endm hddr&dn defl 1 endif if typ eq TM603SE hdpb603E0&dn macro no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,2047,1023,255,0,0,1,%dn hdscg %dn,229,5,3 endm hdpb603E1&dn macro no als&no defl 00FFh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,699,511,240,0,0,172,%dn endm hpb&dn macro no local x x defl no hdpb603E0&dn %x x defl x+1 hdpb603E1&dn %x endm hddr&dn defl 2 endif if typ eq TM501 hpb&dn macro no dw -1,hdesc&dn als&no defl 0099h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,128,5,31,1,1219,511,240,0,0,1,%dn hdscg %dn,305,1,3 endm hddr&dn defl 1 endif if typ eq TM502 hdpb5020&dn macro no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,2047,1023,255,0,0,1,%dn hdscg %dn,305,3,3 endm hdpb5021&dn macro no als&no defl 0031H ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,391,511,240,0,0,257,%dn endm hpb&dn macro no local x x defl no hdpb5020&dn %x x defl x+1 hdpb5021&dn %x endm hddr&dn defl 2 endif if typ eq TM503 hdpb5030&dn macro no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,2047,1023,255,0,0,1,%dn hdscg %dn,305,5,3 endm hdpb5031&dn macro no als&no defl 00CAH ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,1611,1023,255,0,0,172,%dn endm hpb&dn macro no local x x defl no hdpb5030&dn %x x defl x+1 hdpb5031&dn %x endm hddr&dn defl 2 endif if typ eq SA602 hpb&dn macro no dw -1,hdesc&dn als&no defl 0050h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,128,5,31,1,635,511,240,0,0,1,%dn hdscg %dn,159,1,3 endm hddr&dn defl 1 endif if typ eq SA604 hpb&dn macro no dw -1,hdesc&dn als&no defl 009Fh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,1271,511,240,0,0,1,%dn hdsc %dn,159,3,3 endm hddr&dn defl 1 endif if typ eq SA606 hpb&dn macro no dw -1,hdesc&dn als&no defl 00EFh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,1907,1023,255,0,0,1,%dn hdscg %dn,159,5,3 endm hddr&dn defl 1 endif if typ eq SA1002 hpb&dn macro no dw -1,hdesc&dn als&no defl 0080h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,128,5,31,1,1019,511,240,0,0,1,%dn hdscg %dn,255,1,0 endm hddr&dn defl 1 endif if typ eq SA1004 hpb&dn macro no dw -1,hdesc&dn als&no defl 00FFh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,2039,1023,255,0,0,1,%dn hdscg %dn,255,3,0 endm hddr&dn defl 1 endif if typ eq Q2010 hpb&dî macrï no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,128,5,31,1,2043,1023,255,0,0,1,%dn hdscg %dn,511,1,0 endm hddr&dn defl 1 endif if typ eq Q2020 hdpb20&dn macro no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,2047,1023,255,0,0,1,%dn hdscg %dn,511,3,0 endm hdpb21&dn macro no als&no defl 00FFh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,2039,1023,255,0,0,257,%dn endm hpb&dn macro no local x x defl no hdpb20&dn %x x defl x+1 hdpb21&dn %x endm hddr&dn defl 2 endif if typ eq Q2030 hdpb30&dn macro no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,2047,1023,255,0,0,1,%dn hdscg %dn,511,5,0 endm hdpb31&dn macro no als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,2047,1023,255,0,0,172,%dn endm hdpb32&dn macro no als&no defl 00FEh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,384,5,31,1,2031,1023,255,0,0,343,%dn endm hpb&dn macro no local x x defl no hdpb30&dn %x x defl x+1 hdpb31&dn %x x defl x+1 hdpb32&dn %x endm hddr&dn defl 3 endif if typ eq Q2040 hdpb40&dn macro no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,512,5,31,1,2047,1023,255,0,0,1,%dn hdscg %dn,511,7,0 endm hdpb41&dn macro no als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,512,5,31,1,2047,1023,255,0,0,129,%dn endm hdpb42&dn macro no als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,512,5,31,1,2047,1023,255,0,0,257,%dn endm hdpb43&dn macro no als&no defl 00FEh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,512,5,31,1,2031,1023,255,0,0,385,%dn endm hdpb4021&dn macro no als&no defl 00DFh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,1783,1023,255,0,0,257,%dn endm hpb&dn macro no local x x defl no hdpb40&dn %x x defl x+1 hdpb41&dn %x x defl x+1 hdpb42&dn %x x defl x+1 hdpb43&dn %x endm hddr&dn defl 4 endif if typ eq M4010 hpb&dn macro no dw -1,hdesc&dn als&no defl 00F0h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,128,5,31,1,1915,1023,255,0,0,1,%dn hdscg %dn,479,1,0 endm hddr&dn defl 1 endif if typ eq M4020 hdpb4020&dn macro no dw -1,hdesc&dn als&no defl 0100h ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,2047,1023,255,0,0,1,%dn hdscg %dn,480,3,0 endm hdpb4021&dn macro no als&no defl 00DFh ;size of allocation vector css&no defl 0 ;number of checksum elements dpbg %no,256,5,31,1,1783,1023,255,0,0,257,%dn endm hpb&dn macro no local x x defl no hdpb4020&dn %x x defl x+1 hdpb4021&dn %x endm hddr&dn defl 2 endif hdldrvs defl hdldrvs+hddr&dn .lfcond .xall endm x defl 0 rept hddsks dsktyp %x,hd%x ;define hard disk parameters x defl x+1 endm ; ; dskhdr macro dn ; define a single disk header list .lall hdph&dn: dw 0000h,0000h ;translate table dw 0000h,0000h ;scratch area dw dirbuf,dpb&dn ;dir buff,parm block dw 0000h,0000h ;check, alloc vectors .xall endm ; hddisks macro nd ; define nd disks ndisks defl nd ;for later reference dpbase equ $ ;base of disk parameter blocks ; generate the nd elements dsknxt defl nmbfpy rept nd dskhdr %dsknxt dsknxt defl dsknxt+1 endm endm ; lgdrvs defl 0 ; hdpbs macro dn,lgno hpb&dn lgno lgdrvs defl hddr&dn endm ; hddpbs macro times ;define hard disk parameter blocks local x,y x defl 0 y defl nmbfpy rept times hdpbs %x,%y ;define dpb's for this hd drive endm x defl x+1 ;bump counters y defl y+lgdrvs endm ; ddb macro data,comm ; define a db statement db data comm endm ; ddw macro data,comm ; define a dw statement dw data comm endm ; defds macro lab,space lab: ds space endm ; lds macro lb,dn,val size defl val&dn defds lb&dn,%size endm ; ; logdsk defl nmbfpy endif ;hard disk definitions .lfcond fdskhdr macro dn .lall fdph&dn: dw trans,0,0,0 dw dirbuf,fdpbase dw 0000h,0000h .xall endm fdisks macro x defl 0 rept nmbfpy fdskhdr %x x defl x+1 endm endm dwdsk macro drvr,lgdsk dw drvr+lgdsk endm DSKTBL MACRO ;DEFINES DISK ASSIGNMENT TABLE x defl 0 .lall drvtbl equ $+1 .xall if fpyfrst dw fpy rept nmbfpy-1 dwdsk fpy,%x x defl x+1 endm ;rept y defl 0 rept hdldrvs dwdsk hdc,%x iff y ;if first time thru x defl 0 ;reset x for hard disk logical drives starting at 0 y defl y+1 else x defl x+1 endif endm ;rept else ;fpyfrst dw hdc rept hdldrvs-1 dwdsk hdc,%x x defl x+1 endm ;rept y defl 0 rept nmbfpy dwdsk fpy,%x iff y ;if first time thru x defl 0 ;reset x for hard disk logical drives starting at 0 y defl y+1 else x defl x+1 endif endm ;rept endif ddb %x,<;last logical drive> rept 16-nmbfpy-hdldrvs dw 0 endm ;rept endm ;dsktbl SUBTTL BIOS ENTRY AND PUBLIC TABLES page ; ; BIOS jump table start:: ret ;no cold boot dw 0 ret ;no warm boot dw 0 jp pserin jp serin jp serout ret ;no list dw 0 ret ;no punch dw 0 ret ;no reader dw 0 jp home jp seldsk jp settrk jp setsec jp setdma jp read ret ;no write dw 0 ret ;no list status dw 0 jp sectran SUBTTL Console drivers page ; ; Console drivers ; All have entry A=driver number, other parameters per CP/M ; A=0 serial port 0; A=1 serial port 1 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 ; 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 xor A call serout 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 xor A jp serout ; ; setdma:: ld (iodma),BC ;shared among all drivers 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 SUBTTL Floppy disk driver module page ; ; Floppy disk drive driver module ; .sfcond iff hard ;if not hard disk .lfcond dskparm:: ;disk hardware parameter block ;one block for each drive rept nmbfpy 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:: fdisks ; defind disk parameter headers for floppys page ; ; 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 db 80h else dw 127 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 db 0c0h else dw 255 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 home:: ld C,0 settrk:: ld A,C ld (iotrk),A ret setsec:: 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 seldsk:: ld D,0 ;physcial floppy 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 , 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 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 ; ; Read reads the sector from the selected disk ; In double density, the sector may already be in the host buffer ; read:: 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 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 instruction 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 page ; ; 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 DE,crlf call pmsg ld A,255 ret page ; ; 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 page ; ; 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 and 7fh call getparm ld A,(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::call getparm ld A,(IX+density) jr codeok ; drive can't change density else ;iff hard SUBTTL Hard disk drivers page .lfcond ; ; Hard disk drive driver module ; ; ; CP/M Hard disk tables ; hDPHbase:: hddisks hdldrvs ;set up disk parameter headers ; ; DPB's for HARD DISKS ; ; hdpbase equ $+4 hddpbs hddsks ;set up disk parameter blocks page home:: ld BC,0 ;force track 0 settrk:: ld (hdiotrk),BC ;save track number ret setsec:: ld (hdiosec),BC ;save sector number ret ; ; Hseldsk selects the hard disk seldsk:: ld HL,hdphbase ;add in base address ret ; and go ; ; Seldrv selects the drive from (curhdsk), head from (hhead) ; it assembles the correct byte and outputs it ; and updates the track register with the most recent information hseldrv:: ld A,(hhead) ;get head or 20h ;set sector size to 512 bytes out (sdh),a ;send to controller ret page ; ; Seek sets head to the track in (hdiotrk) ; and sector to (hpsec), sector count to one ; hseek:: ld a,(hdiotrk) ;send low order byte of track out (cyllo),a ld a,(hdiotrk+1) ;send high byte out (cylhi),a ld A,(hpsec) ;send physical sector out (secno),A ld A,1 ;set sector count out (secnt),A ret page ; ; Read reads the sector from the selected disk ; it handles any necessary buffering ; read:: call hinbuf ;is it in the buffer jr c,hrd ;sector not in buffer ; sector is in buffer hmvrd:: call hmkbfad ;HL=start of sector in buffer ldir ret ;transfer done hrd:: call hsidselc call hrdprep or A ret nz jr hmvrd ; ; read preperation ; hrdprep:: call hseldrv ;physically select drive and head call hseek ;step to correct track ; ; this actually reads the necessary sector ; it assumes that the head has already settled on the correct track ; (and that the proper head has been selected) ld HL,hstbuf ;point to buffer ld BC,hdcdata ;count and port di ;protect transfer ld A,cread ;send read command out (comnd),A hrdw:: in A,(status) ;done yet and A jp m,hrdw ;if not wait inir ;256 bytes twice inir ei in A,(status) and 1 ;any errors ret z ;return if not ; page ; ; hard disk error message processor ; ; This routine gives the user a detailed error report ; herrors:: push AF ;save error indication ld DE,herrst CALL pmsg ; First the error code IN A,(HDCERR) CALL phex ld DE,errhd CALL pmsg ; then the head IN A,(SDH) push AF ;save drive no AND 7 CALL hex1 ; Print single digit ld DE,errcyl CALL pmsg ; the cylinder IN A,(CYLHI) ; Report CYLHI first CALL phex IN A,(CYLLO) ; then CYLLO CALL phex ld DE,errsec CALL pmsg ; and finally the sector IN A,(SECNO) CALL phex ld DE,errdr ;send drive mess call pmsg pop AF rlca ;get drive rlca rlca and 3 call hex1 ld A,crest ;restore drive out (comnd),A herrlp:: in A,(status) rlca jr c,herrlp ;wait until done poð AF RET page ; ; hsidselc is the read/write preparation for hard disk ; hsidselc computes the correct physical sector and side ; hsidselc:: ld HL,(hdiosec) ld E,L ;save L ld A,L and not(31) ;compute first cp/m sector in buffer ld L,A ld (hblksec),HL ;save it ld L,E ;restore sector to HL hflsdsc:: ;called to set up for a flush or a ;clear carry ld C,-1 ;set up head count ld DE,secs*4 ;set up number cpm sectors per head sdsclp: inc C ;increment head count sbc HL,DE ;subtract out one heads worth of sectors jp p,sdsclp ;if not negitive do more add HL,DE ;restore sector number to HL ld A,L srl A ;find physical sector srl A ld (hpsec),A ld A,C ld (hhead),A ;set head control byte ret page ; ; hinbuf returns carry flag set if sector not in buffer ; if sector is in buffer, returns offset (0 - hdstcnt-1) in A ; hinbuf:: ld A,(bufvalid) ;0 if contains valid data, else 255 rra ret c hinbuf2:: ld A,(hiodrvtrk) ;check for right drive ld B,A ld A,(hblkdrvtrk) sub B jr z,rthdd ;skip if right drive scf ret ;wrong drive return with carry set rthdd:: ld HL,(hiodrvtrk+1) ;check for right track ld BC,(hblkdrvtrk+1) sbc HL,BC ;same drive and track scf ret nz ;not a match ld DE,(hblksec) ld HL,(hdiosec) ld A,D ;high bytes = cp H scf ;set failure flag ret nz ;exit if not equal ccf ;clear carry sbc HL,DE ret C ;sector lower # than buffer ld A,L cp hdstcnt ;carry set if in buffer ccf ret ; ; stores drive, track, sector of contents of buffer for use by flush ; also sets buffer valid flag ; returns HL=start of sector in buffer, DE=DMA address, BC=128, A=0 ; hmkbfad:: ld A,(hiodrvtrk) ld (hblkdrvtrk),a ld HL,(hiodrvtrk+1) ld (hblkdrvtrk+1),HL ld HL,(hdiosec) ld B,L ld A,L and not(hdstcnt-1) ld L,A ld (hblksec),HL ld A,B and hdstcnt-1 ld B,A ;B=relative sector in buffer inc B ld HL,hstbuf-128 ld DE,128 hshft2:: add HL,DE djnz hshft2 ld BC,128 ;make ready for sector LDIR ld DE,(iodma) xor A ld (bufvalid),A ret endif SUBTTL Floppy disk storage page ; ; Floppy disk driver storage ; bufvalid:: db 0ffh ;buffer contains valid data for (blksec) ;0 = valid data iodma:: ds 2 ;dma storage .sfcond iff hard ;if not hard .lfcond 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 wrtpend:: db 0 ;write pending from buffer retryc:: db 0 ;number of retries left newfpy:: db 0 ;new floppy to be selected 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 SUBTTL Hard disk storage page ; ; Hard disk driver storage ; else .lfcond hiodrvtrk:: curhdsk:: db 0 ;current selected physical hard disk drive hdiotrk:: ds 2 ;current track for current disk hblkdrvtrk:: ds 3 ;drive and track for deblocking buffer hdiosec:: ds 2 ;current logical sector hblksec:: dw 0 ;first logical sector in host buffer hunalsec:: dw 0 ;first logical sector in current host unallocated block unalcv:: db 0 ;unallocated block vector hpsec:: ds 1 ;current physical sector hwrtpnd:: db 0 ;write pending from buffer hhead:: db 0 ;head control endif SUBTTL Error messages page .sfcond iff hard .lfcond ; ; Floppy 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 $' crlf:: db 0dh,0ah,'$' page ; ; Hard disk error messages ; else .lfcond herrst:: DB 0dH,0ah,'HD1001 Error $' errhd:: DB ' on Head $' errcyl:: DB ', Cylinder $' errsec:: DB ', Sector $' errdr:: db ', Drive $' endif SUBTTL Disk buffers and Cold Boot code 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:: hstbuf equ dirbuf+128 ;sector deblocking buffer ; ; lastadd equ $ SUBTTL SYMBOLS end