mirror of
https://github.com/SEPPDROID/Digital-Research-Source-Code.git
synced 2025-10-23 16:34:07 +00:00
385 lines
9.7 KiB
NASM
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
|