Files
Digital-Research-Source-Code/CPM OPERATING SYSTEMS/CPM 3.X/CPM 3.0/SOURCE/fd1797sd.asm
Sepp J Morris 31738079c4 Upload
Digital Research
2020-11-06 18:50:37 +01:00

385 lines
9.7 KiB
NASM

title 'wd1797 w/ Z80 DMA Single density diskette handler'
; CP/M-80 Version 3 -- Modular BIOS
; Disk I/O Module for wd1797 based diskette systems
; Initial version 0.01,
; Single density floppy only. - jrp, 4 Aug 82
dseg
; Disk drive dispatching tables for linked BIOS
public fdsd0,fdsd1
; Variables containing parameters passed by BDOS
extrn @adrv,@rdrv
extrn @dma,@trk,@sect
extrn @dbnk
; System Control Block variables
extrn @ermde ; BDOS error mode
; Utility routines in standard BIOS
extrn ?wboot ; warm boot vector
extrn ?pmsg ; print message @<HL> up to 00, saves <BC> & <DE>
extrn ?pdec ; print binary number in <A> from 0 to 99.
extrn ?pderr ; print BIOS disk error header
extrn ?conin,?cono ; con in and out
extrn ?const ; get console status
; Port Address Equates
maclib ports
; CP/M 3 Disk definition macros
maclib cpm3
; Z80 macro library instruction definitions
maclib z80
; common control characters
cr equ 13
lf equ 10
bell equ 7
; Extended Disk Parameter Headers (XPDHs)
dw fd$write
dw fd$read
dw fd$login
dw fd$init0
db 0,0 ; relative drive zero
fdsd0 dph trans,dpbsd,16,31
dw fd$write
dw fd$read
dw fd$login
dw fd$init1
db 1,0 ; relative drive one
fdsd1 dph trans,dpbsd,16,31
cseg ; DPB must be resident
dpbsd dpb 128,26,77,1024,64,2
dseg ; rest is banked
trans skew 26,6,1
; Disk I/O routines for standardized BIOS interface
; Initialization entry point.
; called for first time initialization.
fd$init0:
lxi h,init$table
fd$init$next:
mov a,m ! ora a ! rz
mov b,a ! inx h ! mov c,m ! inx h
outir
jmp fd$init$next
fd$init1: ; all initialization done by drive 0
ret
init$table db 4,p$zpio$1A
db 11001111b, 11000010b, 00010111b,11111111b
db 4,p$zpio$1B
db 11001111b, 11011101b, 00010111b,11111111b
db 0
fd$login:
; This entry is called when a logical drive is about to
; be logged into for the purpose of density determination.
; It may adjust the parameters contained in the disk
; parameter header pointed at by <DE>
ret ; we have nothing to do in
; simple single density only environment.
; disk READ and WRITE entry points.
; these entries are called with the following arguments:
; relative drive number in @rdrv (8 bits)
; absolute drive number in @adrv (8 bits)
; disk transfer address in @dma (16 bits)
; disk transfer bank in @dbnk (8 bits)
; disk track address in @trk (16 bits)
; disk sector address in @sect (16 bits)
; pointer to XDPH in <DE>
; they transfer the appropriate data, perform retries
; if necessary, then return an error code in <A>
fd$read:
lxi h,read$msg ; point at " Read "
mvi a,88h ! mvi b,01h ; 1797 read + Z80DMA direction
jmp rw$common
fd$write:
lxi h,write$msg ; point at " Write "
mvi a,0A8h ! mvi b,05h ; 1797 write + Z80DMA direction
; jmp wr$common
rw$common: ; seek to correct track (if necessary),
; initialize DMA controller,
; and issue 1797 command.
shld operation$name ; save message for errors
sta disk$command ; save 1797 command
mov a,b ! sta zdma$direction ; save Z80DMA direction code
lhld @dma ! shld zdma$dma ; get and save DMA address
lda @rdrv ! mov l,a ! mvi h,0 ; get controller-relative disk drive
lxi d,select$table ! dad d ; point to select mask for drive
mov a,m ! sta select$mask ; get select mask and save it
out p$select ; select drive
more$retries:
mvi c,10 ; allow 10 retries
retry$operation:
push b ; save retry counter
lda select$mask ! lxi h,old$select ! cmp m
mov m,a
jnz new$track ; if not same drive as last, seek
lda @trk ! lxi h,old$track ! cmp m
mov m,a
jnz new$track ; if not same track, then seek
in p$fdmisc ! ani 2 ! jnz same$track ; head still loaded, we are OK
new$track: ; or drive or unloaded head means we should . . .
call check$seek ; . . read address and seek if wrong track
lxi b,16667 ; 100 ms / (24 t states*250 ns)
spin$loop: ; wait for head/seek settling
dcx b
mov a,b ! ora c
jnz spin$loop
same$track:
lda @trk ! out p$fdtrack ; give 1797 track
lda @sect ! out p$fdsector ; and sector
lxi h,dma$block ; point to dma command block
lxi b,dmab$length*256 + p$zdma ; command block length and port address
outir ; send commands to Z80 DMA
in p$bankselect ; get old value of bank select port
ani 3Fh ! mov b,a ; mask off DMA bank and save
lda @dbnk ! rrc ! rrc ; get DMA bank to 2 hi-order bits
ani 0C0h ! ora b ; merge with other bank stuff
out p$bankselect ; and select the correct DMA bank
lda disk$command ; get 1797 command
call exec$command ; start it then wait for IREQ and read status
sta disk$status ; save status for error messages
pop b ; recover retry counter
ora a ! rz ; check status and return to BDOS if no error
ani 0001$0000b ; see if record not found error
cnz check$seek ; if a record not found, we might need to seek
dcr c ! jnz retry$operation
; suppress error message if BDOS is returning errors to application...
lda @ermde ! cpi 0FFh ! jz hard$error
; Had permanent error, print message like:
; BIOS Err on d: T-nn, S-mm, <operation> <type>, Retry ?
call ?pderr ; print message header
lhld operation$name ! call ?pmsg ; last function
; then, messages for all indicated error bits
lda disk$status ; get status byte from last error
lxi h,error$table ; point at table of message addresses
errm1:
mov e,m ! inx h ! mov d,m ! inx h ; get next message address
add a ! push psw ; shift left and push residual bits with status
xchg ! cc ?pmsg ! xchg ; print message, saving table pointer
pop psw ! jnz errm1 ; if any more bits left, continue
lxi h,error$msg ! call ?pmsg ; print "<BEL>, Retry (Y/N) ? "
call u$conin$echo ; get operator response
cpi 'Y' ! jz more$retries ; Yes, then retry 10 more times
hard$error: ; otherwise,
mvi a,1 ! ret ; return hard error to BDOS
cancel: ; here to abort job
jmp ?wboot ; leap directly to warmstart vector
; subroutine to seek if on wrong track
; called both to set up new track or drive
check$seek:
push b ; save error counter
call read$id ; try to read ID, put track in <B>
jz id$ok ; if OK, we're OK
call step$out ; else step towards Trk 0
call read$id ; and try again
jz id$ok ; if OK, we're OK
call restore ; else, restore the drive
mvi b,0 ; and make like we are at track 0
id$ok:
mov a,b ! out p$fdtrack ; send current track to track port
lda @trk ! cmp b ! pop b ! rz ; if its desired track, we are done
out p$fddata ; else, desired track to data port
mvi a,00011010b ; seek w/ 10 ms. steps
jmp exec$command
step$out:
mvi a,01101010b ; step out once at 10 ms.
jmp exec$command
restore:
mvi a,00001011b ; restore at 15 ms
; jmp exec$command
exec$command: ; issue 1797 command, and wait for IREQ
; return status
out p$fdcmnd ; send 1797 command
wait$IREQ: ; spin til IREQ
in p$fdint ! ani 40h ! jz wait$IREQ
in p$fdstat ; get 1797 status and clear IREQ
ret
read$id:
lxi h,read$id$block ; set up DMA controller
lxi b,length$id$dmab*256 + p$zdma ; for READ ADDRESS operation
outir
mvi a,11000100b ; issue 1797 read address command
call exec$command ; wait for IREQ and read status
ani 10011101b ; mask status
lxi h,id$buffer ! mov b,m ; get actual track number in <B>
ret ; and return with Z flag true for OK
u$conin$echo: ; get console input, echo it, and shift to upper case
call ?const ! ora a ! jz u$c1 ; see if any char already struck
call ?conin ! jmp u$conin$echo ; yes, eat it and try again
u$c1:
call ?conin ! push psw
mov c,a ! call ?cono
pop psw ! cpi 'a' ! rc
sui 'a'-'A' ; make upper case
ret
disk$command ds 1 ; current wd1797 command
select$mask ds 1 ; current drive select code
old$select ds 1 ; last drive selected
old$track ds 1 ; last track seeked to
disk$status ds 1 ; last error status code for messages
select$table db 0001$0000b,0010$0000b ; for now use drives C and D
; error message components
read$msg db ', Read',0
write$msg db ', Write',0
operation$name dw read$msg
; table of pointers to error message strings
; first entry is for bit 7 of 1797 status byte
error$table dw b7$msg
dw b6$msg
dw b5$msg
dw b4$msg
dw b3$msg
dw b2$msg
dw b1$msg
dw b0$msg
b7$msg db ' Not ready,',0
b6$msg db ' Protect,',0
b5$msg db ' Fault,',0
b4$msg db ' Record not found,',0
b3$msg db ' CRC,',0
b2$msg db ' Lost data,',0
b1$msg db ' DREQ,',0
b0$msg db ' Busy,',0
error$msg db ' Retry (Y/N) ? ',0
; command string for Z80DMA device for normal operation
dma$block db 0C3h ; reset DMA channel
db 14h ; channel A is incrementing memory
db 28h ; channel B is fixed port address
db 8Ah ; RDY is high, CE/ only, stop on EOB
db 79h ; program all of ch. A, xfer B->A (temp)
zdma$dma ds 2 ; starting DMA address
dw 128-1 ; 128 byte sectors in SD
db 85h ; xfer byte at a time, ch B is 8 bit address
db p$fddata ; ch B port address (1797 data port)
db 0CFh ; load B as source register
db 05h ; xfer A->B
db 0CFh ; load A as source register
zdma$direction ds 1 ; either A->B or B->A
db 0CFh ; load final source register
db 87h ; enable DMA channel
dmab$length equ $-dma$block
read$id$block db 0C3h ; reset DMA channel
db 14h ; channel A is incrementing memory
db 28h ; channel B is fixed port address
db 8Ah ; RDY is high, CE/ only, stop on EOB
db 7Dh ; program all of ch. A, xfer A->B (temp)
dw id$buffer ; starting DMA address
dw 6-1 ; Read ID always xfers 6 bytes
db 85h ; byte xfer, ch B is 8 bit address
db p$fddata ; ch B port address (1797 data port)
db 0CFh ; load dest (currently source) register
db 01h ; xfer B->A
db 0CFh ; load source register
db 87h ; enable DMA channel
length$id$dmab equ $-read$id$block
cseg ; easier to put ID buffer in common
id$buffer ds 6 ; buffer to hold ID field
; track
; side
; sector
; length
; CRC 1
; CRC 2
end