;*************************************************************** ;* CBIOS for 8MHz Z80 Project * ;* * ;* Includes sector block/deblock as in the example in * ;* shown in the CP/M 2.2 documentation. * ;*************************************************************** msize equ 62 ;memory size in kb bias equ (msize-20)*1024 cpmb equ 3400h+bias cpmst equ cpmb+3 ccplen equ 800h ccplbdos equ 1600h bdos equ cpmb+ccplen bdosv equ bdos+6 cbios equ cpmb+ccplbdos cr equ 0dh lf equ 0ah vers equ 22 iobyte equ 3 mxdisks equ 2 romst equ 0c00h ;beg. of cp/m image in rom org cbios jp boot wbootv: 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 prstat jp sectrn signon: db 1bh,'[2J',lf db 'Roger',27h,'s ' db '62' db 'k CP/M vers ' db vers/10+'0','.',vers mod 10+'0' db cr,lf,0 crlf: ld c,cr call conout ld c,lf call conout ret pmsg: ld a,(hl) or a ret z ld c,a call conout inc hl jr pmsg ; prnta: push af rrca rrca rrca rrca call cnv4pr pop af cnv4pr: and 0fh add a,90h daa adc a,'@' daa ld c,a call conout ret ; adat equ 0 ; data, port A bdat equ adat+1 ; data, port B acon equ adat+2 ; control, port A bcon equ adat+3 ; control, port B ramrom equ 0feh ; RAM/ROM enable ; ; get input serial port status ; const: in a,(acon) ; status and 1 ; (A)=0, not ready ret z ; (A)!=0, ready ld a,0ffh ret ; ; load a character ; conin: call const ; check input status jp z,conin in a,(adat) and 7fh ret ; ; send a character ; conout: in a,(acon) ; check tx empty bit 2,a ; bit in D2 jp z,conout ; zero means not empty, wait ; ld a,c ; get character to print out (adat),a ; send it out ret list: punch: reader: prstat: ret sectrn: ld h,b ld l,c ret boot: di ld hl,signon call pmsg xor a ld (iobyte),a ld (sekdsk),a jp gocpm wboot: di ; ///////////////////////// ; // // ; // Reset FDC here // ; // // ; ///////////////////////// out (ramrom),a ;switch to ROM ld hl,romst ld de,0dc00h ld bc,ccplbdos ldir out (ramrom),a ;switch back to RAM gocpm: xor a ;0 to accumulator ld (hstact),a ;host buffer inactive ld (unacnt),a ;clear unalloc count ld a,0c3h ld (0),a ld (5),a ld hl,wbootv ld (1),hl ld hl,bdosv ld (6),hl ld bc,80h call setdma ei ld a,(sekdsk) ld (4),a ld c,a jp cpmst home: ld c,0 settrk: ld a,c ld (sektrk),a ret setsec: ld a,c ld (seksec),a ret setdma: ld (dmaadr),bc ret seldsk: ld hl,0 ld a,c cp mxdisks ret nc ld (sekdsk),a ld l,a ld h,0 add hl,hl add hl,hl add hl,hl add hl,hl ld de,dpbase add hl,de ret ;*********************************************************** ;* * ;* Sector Deblocking Algorithms for CP/M 2.0 * ;* * ;*********************************************************** ; ;*********************************************************** ;* * ;* CP/M to host disk constants * ;* * ;*********************************************************** blksize equ 2048 ;CP/M allocation size hstsiz equ 1024 ;host disk sector size hstspt equ 16 ;host disk sectors/trk hstblk equ hstsiz/128 ;CP/M sectors/host buff cpmspt equ hstblk*hstspt ;CP/M sectors/track secmsk equ hstblk-1 ;sector mask ; smask hstblk ;compute sector mask secshf equ 3 ;log2(hstblk) ; ;*********************************************************** ;* * ;* BDOS constants on entry to write * ;* * ;*********************************************************** wrall equ 0 ;write to allocated wrdir equ 1 ;write to directory wrual equ 2 ;write to unallocated ; ;*********************************************************** ;* * ;* The READ entry point takes the place of the previous * ;* BIOS definition for READ. * ;* * ;*********************************************************** read: ; read the selected CP/M sector xor a ;these 2 lines are ld (unacnt),a ; a DRI patch ld a,1 ld (readop),a ;read operation ld (rsflag),a ;must read data ld a,wrual ld (wrtype),a ;treat as unalloc jp rwoper ;to perform the read ; ;*********************************************************** ;* * ;* The WRITE entry point takes the place of the previous * ;* BIOS definition for WRITE. * ;* * ;*********************************************************** write: ; write the selected CP/M sector xor a ;0 to accumulator ld (readop),a ;not a read operation ld a,c ;write type in c ld (wrtype),a cp wrual ;write unallocated? jp nz,chkuna ;check for unalloc ; ; write to unallocated, set parameters ld a,blksize/128 ;next unalloc recs ld (unacnt),a ld a,(sekdsk) ;disk to seek ld (unadsk),a ;unadsk = sekdsk ld hl,(sektrk) ld (unatrk),hl ;unatrk = sectrk ld a,(seksec) ld (unasec),a ;unasec = seksec ; chkuna: ; check to write to unallocated sector ld a,(unacnt) ;any unalloc remain? or a jp z,alloc ;skip if not ; ; more unallocated records remain dec a ;unacnt = unacnt-1 ld (unacnt),a ld a,(sekdsk) ;same disk? ld hl,unadsk cp (hl) ;sekdsk = unadsk ? jp nz,alloc ;skip if not ; ; disks are the same ld hl,unatrk call sektrkcmp ;sektrk = unatrk ? jp nz,alloc ;skip if not ; ; tracks are the same ld a,(seksec) ;same sector ? ld hl,unasec cp (hl) ;seksec = unasec ? jp nz,alloc ;skip if not ; ; match, move to next sector for future ref inc (hl) ;unasec = unasec+1 ld a,(hl) ;end of track ? cp cpmspt ;count CP/M sectors jp c,noovf ;skip if no overflow ; ; overflow to next track ld (hl),0 ;unasec = 0 ld hl,(unatrk) inc hl ld (unatrk),hl ;unatrk = unatrk+1 ; noovf: ; match found, mark as unnecessary read xor a ;0 to accumulator ld (rsflag),a ;rsflag = 0 jp rwoper ;to perform the write ; alloc: ; not an unallocated record, requires pre-read xor a ;0 to accumulator ld (unacnt),a ;unacnt = 0 inc a ;1 to accum ld (rsflag),a ;rsflag = 1 ; ;*********************************************************** ;* * ;* Common code for READ and WRITE follows * ;* * ;*********************************************************** rwoper: ; enter here to perform the read/write xor a ;zero to accum ld (fdcst),a ;no errors (yet) ld a,(seksec) ;compute host sector or a ;carry = 0 rra ;shift right or a ;carry = 0 rra ;shift right or a ;carry = 0 rra ;shift right ld (sekhst),a ;host sector to seek ; ; active host sector? ld hl,hstact ;host active flag ld a,(hl) ld (hl),1 ;always becomes 1 or a ;was it already? jp z,filhst ;fill host if not ; ; host buffer active, same as seek buffer? ld a,(sekdsk) ld hl,hstdsk ;same disk? cp (hl) ;sekdsk = hstdsk ? jp nz,nomatch ; ; same disk, same track? ld hl,hsttrk call sektrkcmp ;sektrk = hsttrk ? jp nz,nomatch ; ; same disk, same track, same buffer ? ld a,(sekhst) ld hl,hstsec ;sekhst = hstsec ? cp (hl) jp z,match ;skip if match ; nomatch: ; proper disk, but not correct sector ld a,(hstwrt) ;host written? or a call nz,writehst ;clear host buff ; filhst: ; may have to fill the host buffer ld a,(sekdsk) ld (hstdsk),a ld hl,(sektrk) ld (hsttrk),hl ld a,(sekhst) ld (hstsec),a ld a,(rsflag) ;need to read? or a call nz,readhst ;yes, if 1 xor a ;0 to accum ld (hstwrt),a ;no pending write ; match: ; copy data to or from buffer ld a,(seksec) ;mask buffer number and secmsk ;least significant bits ld l,a ;ready to shift ld h,0 ;double count add hl,hl add hl,hl add hl,hl add hl,hl add hl,hl add hl,hl add hl,hl ; hl has relative host buffer address ld de,hstbuf add hl,de ;hl = host address ex de,hl ;now in DE ld hl,(dmaadr) ;get/put CP/M data ld c,128 ;length of move ld a,(readop) ;which way? or a jp nz,rwmove ;skip if read ; ; write operation, mark and switch direction ld a,1 ld (hstwrt),a ;hstwrt = 1 ex de,hl ; rwmove: ; C initially 128, DE is source, HL is dest ld a,(de) ;source character inc de ld (hl),a ;to dest inc hl dec c ;loop 128 times jp nz,rwmove ; ; data has been moved to/from host buffer ld a,(wrtype) ;write type cp wrdir ;to directory? ld a,(fdcst) ;in case of errors ret nz ;no further processing ; ; clear host buffer for directory write or a ;errors? ret nz ;skip if so xor a ;0 to accum. ld (hstwrt),a call writehst ld a,(fdcst) ret ; ;*********************************************************** ;* * ;* Utility subroutine for 16-bit compare * ;* * ;*********************************************************** sektrkcmp: ; HL = .unatrk or .hsttrk, compare with sektrk ex de,hl ld hl,sektrk ld a,(de) ;low byte compare cp (hl) ;same? ret nz ;return if not ; low bytes equal, test high 1s inc de inc hl ld a,(de) cp (hl) ;sets flags ret ; ; diskette interface ; ; disk commands ; seek equ 11h ;seek wtrak equ 0f4h ;write track restore equ 1 ;restore sectrd0 equ 82h ;read sector side zero sectrd1 equ 8ah ;read sector side one sectwr0 equ 0a2h ;write sector side zero sectwr1 equ 0aah ;write sector side one ; cmd equ 4 ;command register track equ cmd + 1 ;track register sect equ cmd + 2 ;sector register dal equ cmd + 3 ;data port selr equ 0ffh ;status/select register wait equ selr ; DRQ/IRQ bits ; ; I/O from/to 0ffh is defined as: ; ; input -> I D output -> D D S S S ; R R T E I E E ; Q Q P N D L L ; - - - - - - - ; 1 0 4 3 2 1 0 ; ;*********************************************************** ;* * ;* WRITEHST performs the physical write to the host * ;* disk, READHST reads the physical disk. * ;* * ;*********************************************************** ; ; hstdsk = host disk #, hsttrk = host track #, ; hstsec = host sect #. read "hstsiz" bytes ; into hstbuf and return error flag in fdcst. readhst: call preio rd1: call rdsec call donesect ld a,(fdcst) or a ret z ld a,(rtry) inc a ld (rtry),a cp 11 jp c,rd1 ld hl,ascrd ld de,fatal+14 ld bc,5 ldir call erranl ld a,1 ret ascrd: db 'read ' ; hstdsk = host disk #, hsttrk = host track #, ; hstsec = host sect #. write "hstsiz" bytes ; from hstbuf and return error flag in fdcst. ; return fdcst non-zero if error writehst: call preio wr1: call wrsec call donesect ld a,(fdcst) or a ret z ld a,(rtry) inc a ld (rtry),a cp 11 jp c,wr1 ld hl,ascwr ld de,fatal+14 ld bc,5 ldir call erranl ld a,1 ret ascwr: db 'write' preio: ld a,(hstsec) cp 8 jp c,side0 ld c,1ch ld b,'1' jp slect side0: ld c,18h ld b,'0' slect: ld a,(sekdsk) or c out (selr),a ld a,b ld (nside),a xor a ld (rtry),a ld a,(hsttrk) call seekr ;position the head ret ; ; Write a sector ; wrsec: ld a,(hstsec) inc a out (sect),a cp 9 jp c,sid0 sub 8 out (sect),a ld a,sectwr1 jr continu sid0: ld a,sectwr0 continu: di ld hl,hstbuf ld c,dal out (cmd),a nxtby: in a,(wait) bit 0,a jp nz,wod ; bit 1,a ret nz jp nxtby ; wod: outi jp nxtby ; ; Read a sector ; rdsec: ld a,(hstsec) ;set sector inc a out (sect),a cp 9 jr c,drsid0 sub 8 out (sect),a ld a,sectrd1 ;read side one jr startrd drsid0: ld a,sectrd0 ;read side zero startrd: di ;no outside interference ld hl,hstbuf ld c,dal out (cmd),a ;do sector read nextbyte: in a,(wait) ;wait for operation complete and 1 jr nz,rid in a,(wait) and 2 ret nz jr nextbyte rid: nop ini ;read data jr nextbyte ;get next byte ; donesect: ei ;can now be bothered again in a,(cmd) ;get status and 0ddh ;error? ld (fdcst),a ;save status or a ret nz ld a,19h ;deselect hardware out (selr),a ret ;i/o completed ; ; 10 retries failed, report fatal error ; erranl: ld hl,fatal call pmsg ld a,(fdcst) call prnta call crlf ; ld hl,probid call pmsg ld a,(hstdsk) call prnta ld hl,probid1 call pmsg ld a,(hsttrk) call prnta ld hl,probid2 call pmsg ld a,(hstsec) call prnta ld hl,probid3 call pmsg call crlf ; call deterr ret fatal: db 'unrecoverable xxxxx error ' db ' FDC status = ',0 probid: db 'drive: ',0 probid1: db ' track: ',0 probid2: db ' sector: ',0 probid3: db ' side: ' nside: db '0',0 ; ; deterr: ld a,(fdcst) ld b,a and 80h jr z,dte1 ld hl,drvnr call pmsg ;drive not ready dte1: ld a,b and 40h jr z,dte2 ld hl,wrtprt call pmsg ;write protect dte2: ld a,b and 20h jr z,dte3 ld hl,wrtflt call pmsg ;write fault dte3: ld a,b and 10h jr z,dte4 ld hl,rnf call pmsg ;record not found dte4: ld a,b and 8 jr z,dte5 ld hl,crcerr call pmsg ;CRC error dte5: ld a,b and 4 jr z,dte6 ld hl,lstdat ;lost data call pmsg dte6: ld a,b and 2 ret z ld hl,drqm ;data request call pmsg ret drvnr: db 'Drive not ready.',0 wrtprt: db 'Write protected.',0 wrtflt: db 'Write fault.',0 rnf: db 'Record not found.',0 crcerr: db 'CRC error.',0 lstdat: db 'Lost data.',0 drqm: db 'Data request.',0 ; seekr: ld b,a xor a ld (rtry),a skr1: ld a,b out (dal),a ld a,seek out (cmd),a skr2: in a,(wait) bit 1,a jr z,skr2 in a,(cmd) and 10h jp z,skr3 ld a,(rtry) inc a ld (rtry),a cp 20h jr nc,skr1 scf ret skr3: xor a ld (rtry),a ret ; rtry: db 0 fdcst: db 0 dpbase equ $ dpe0: dw 0h,0h dw 0h,0h dw dirbuf,dpb0 dw csv0,alv0 dpe1: dw 0h,0h dw 0h,0h dw dirbuf,dpb1 dw csv1,alv1 offset equ 1 dpb0: dpb1: dw 128 ; SPT db 4 ; BSH db 15 ; BLM db 0 ; EXM dw 631 ; disk size dw 191 ; dir max db 0e0h ; alloc 0 db 0 ; alloc 1 dw 48 ; check size dw offset ; offset ; ;*********************************************************** ;* * ;* Uninitialized RAM data areas * ;* * ;*********************************************************** ; sekdsk: ds 1 ;seek disk number sektrk: ds 2 ;seek track number seksec: ds 1 ;seek sector number ; hstdsk: ds 1 ;host disk number hsttrk: ds 2 ;host track number hstsec: ds 1 ;host sector number ; sekhst: ds 1 ;seek shr secshf hstact: ds 1 ;host active flag hstwrt: ds 1 ;host written flag ; unacnt: ds 1 ;unalloc rec cnt unadsk: ds 1 ;last unalloc disk unatrk: ds 2 ;last unalloc track unasec: ds 1 ;last unalloc sector ; rsflag: ds 1 ;read sector flag readop: ds 1 ;1 if read operation wrtype: ds 1 ;write operation type dmaadr: ds 2 ;last dma address hstbuf: ds hstsiz ;host buffer ; dirbuf: ds 128 alv0: ds 80 csv0: ds 48 alv1: ds 80 csv1: ds 48 end