home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
ftp.barnyard.co.uk
/
2015.02.ftp.barnyard.co.uk.tar
/
ftp.barnyard.co.uk
/
cpm
/
walnut-creek-CDROM
/
KAYPRO
/
KPNUROM.LBR
/
KDSKDRV.ZQ0
/
KDSKDRV.Z80
Wrap
Text File
|
2000-06-30
|
22KB
|
841 lines
title Kaypro 4-83 Resident Software Package
subttl Disk Equate and Parameters
;
; ################################################################
; ## ##
; ## Disk support routines ##
; ## ##
; ################################################################
; ## Last Update:06/08/82 [001] ##
; ################################################################
; # Revised to KPRO4, 3 Mar. 1984, C.B. Falconer ##
; ################################################################
; ## Revised for integral data segment with Z80ASM 85/6/15 cbf ##
; ## General purpose disk routines, reduced Ram use. Avoid use ##
; ## of AF' register (reserved for other uses). Added use of ##
; ## register E for drive sensing, per CPM standards. Added ##
; ## provision for disabling write checking. Re-organized. ##
; ## The "home" routine now returns a pointer to a data area. ##
; ## Note the unmodified Kaypro returns a pointer in page zero, ##
; ## so software can tell them apart. ##
; ################################################################
; ## 85/6/18 cbf. Added disk error messages, corrected summary ##
; ## reset on any cold boot via diskinit. Reduced error retrys ##
; ################################################################
;
extrn bitport, bankbit; bank selection
extrn .thnsd; delay
extrn .print, .vidout; for error system
;
; linkage to deblocking system
extrn erflag, unacnt
extrn hstact, hstbuf, hstdsk
extrn hstsec, hsttrk, hstwrt
;
entry home, seldsk, settrk, setsec
entry sectran
entry diskinit, datainit, diskon, diskoff
entry readhst, writehst
;
; Public data areas (for deblocking area)
entry sekdsk, seksec, sektrk
entry denflag
;
; Entries to installed routines in data segment
entry move, rd, wrt
;
; ---------------------------------------------------------
;
drvmask equ 0FCH; drive select mask
denmask equ 0DFH; density bit mask
ddbit equ 00H; double density bit
sdbit equ 20H; single density bit
sidebit equ 4;
mtroff equ 40h
;
; Disk controller ports
control equ 10H; I/O port of disk controller
status equ control+0; status register
cmnd equ control+0; command register
track equ control+1; track register
sector equ control+2; sector register
data equ control+3; data register
;
; Controller commands (selected)
ficmd equ 11010000B; force interrupt (Abort current command)
rdcmd equ 10001000B; read command
wrtcmd equ 10101100B; write command
seekcmd equ 00010000B; seek command
rstcmd equ 00000000B; home (restore) command
adrcmd equ 11000100B; read track address
rdmask equ 10011100B; read status mask
wrtmask equ 11111100B; write status mask
;
; retry control
tries1 equ 3; re-home on bad sector # of tries+1
tries2 equ 6; re-read/write # of retries+1
;
; Miscellaneous
retcod equ 0C9H; return op code
nmivec equ 0066H; non-maskable interupt vector
esc equ 01bh; for error message positioning
;
subttl Physical disk routines
;
cseg
;
; enter here on system boot to initialize
; a,f,b,c,d,e,h,l
diskinit:
ld hl,ioimage; move rd/wrt/dphs routines into RAM
ld de,move
ld bc,imaglen
ldir
;
xor a; 0 to accumulator
ld (hstact),a; host buffer inactive
ld (unacnt),a; clear unalloc count
ld a,ddbit; set double density flag
ld (denflag),a
ld a,255; set track numbers to 255
ld (dsk),a; clear disk number
ld (tracka),a
ld (trackb),a
ret
;
; called on cold start to initialize error counts etc
; f,b,c,d,e,h,l
datainit:
ld hl,.ckwrt
ld de,chkwrt
ld bc,.ckend-.ckwrt
ldir
ret
;
; perform logical to physical sector translation.
; logical sector number in BC, table address in DE
; return physical sector number in HL
; a,f,d,e,h,l
sectran:
ld a,d; table address 0?
or e
ld h,b; if so no xlate
ld l,c
ret z
ex de,hl; table address in hl
add hl,bc; index by logical sector number
ld l,(hl)
ld h,0
ret
;
; set sector given by register c
; a,f
setsec: ld a,c
ld (seksec),a; sector to seek
ld a,(denflag)
or a
ret z; single density
; " "
; select sector #, BC=Sector # (double density)
; a,f
secset: in a,(bitport); single or double sided?
and sidebit
ld a,c; move sector number to A
jp z,secst1; pure sector no in single sided
add a,10
secst1: out (sector),a; to controller register
ret
;
; Set track
settrk: ld h,b
ld l,c
ld (sektrk),hl; set track given by registers BC
ld a,(denflag)
or a
ret z; single density
; " "
; seek track #, BC=Track # (double density)
trkset: call ready; make sure drive is on and ready
ld a,(sidflg); {}
or a
jp z,tkset3; single sided, select side 0
ld a,c; else double sided drive
rra; trk no divided /2
ld c,a; for actual selection
in a,(bitport)
jp c,tkset1; and select side 0 for even track
and not sidebit; else select side 1
jp tkset2
tkset1: or sidebit
tkset2: out (bitport),a; select side
tkset3: ld a,c; get physical track number
ld (ctrack),a
out (data),a; track # to seek to
ld a,seekcmd; seek command
out (cmnd),a; issue command
; " "
; check status of controller, wait for command to finish executing
; a,f
busy: ld a,(nmivec)
push af
ld a,retcod
ld (nmivec),a
halt; wait for command done
pop af
ld (nmivec),a
busy1: in a,(status); now wait for not busy
bit 0,a; preserve code to return
jp nz,busy1
ret
;
; home disk
; a,f,h,l
home: ld a,(denflag)
or a
jp nz,dohome; double density
ld a,(hstwrt); patch by DRI
or a
jp nz,dohome
ld (hstact),a
; " "
; home disk head
dohome: call ready; make sure drive is on and ready
in a,(bitport)
and not sidebit
out (bitport),a; select side 0
xor a
ld (ctrack),a; reset current track number
ld a,rstcmd; restore command
out (cmnd),a; issue command
call busy; test and wait for not busy
ld hl,chkwrt
ret
;
; select disk
seldsk: ld a,c; selected disk number
ld (sekdsk),a; seek disk number
; " "
; Physical disk drive select, C=drive number 0=A:, 1=B:
; return HL=dph for selected drive, or HL=0 for non-existent drive
; Set up density for ddrive selected
dsksel: ld hl,0; hl = 0 for non-existent drive
ld a,c
cp 2
ret nc; drive number >B:
or a; z flag set => A: drive else B: drive
ld a,e; Save the CPM flag momentarily
ld de,tracka; find propert track(x) and..
ld hl,dpha; select proper dph for drive
jp z,sel0
ld de,trackb
ld hl,dphb
sel0: push hl; save dph of disk to be selected
and 1
ld a,(dsk)
jp nz,sel0a; Previously selected to CPM
ld a,255
ld (de),a; Mark for sensing
xor a
ld (unacnt),a; home will reset hstact on sense
ld a,(dsk)
cp c
jp z,selnot; reselecting the current disk
sel0a: cp c; selecting disk already selected?
jp z,selx; yes, no further action needed
ld a,c; save new disk number
ld (dsk),a
or a; set zero flag if A: drive
ld hl,ctrack
ld de,trackb; now find proper track(x)
jp z,sel1
ld de,tracka
sel1: ld a,(de); have we been on disk we're "leaving"
cp 255; if not do not update tracks
jp z,selnot
push bc
ld bc,3
ldir; save old table
pop bc
selnot: ld de,ctrack; now to update main table {}
ld a,c; set z flag if A: drive
or a
ld hl,tracka
jp z,sel2
ld hl,trackb
sel2: ld a,(hl)
cp 255; first time for this drive?
jp z,density; if so, go set it up
ld bc,3
ldir; init table for density
ld bc,15; dparm size
ld de,dpbd; dparm addr
ld hl,.dpbd; single sided table
ld a,(sidflg)
or a
jp z,sel2a
ld hl,.dsdd; double sided table
sel2a: ldir; move in dparm for this drive
ld a,(ctrack)
out (track),a
selx: pop hl; adr of dph for disk we are selecting
ret
;
density:
ld a,ddbit
ld (denflag),a
call ready; physical disk select
call home; seek track 0, side 0
pop hl
call dcheck; see if we can read address
jp z,dend; if so, density is double
ld a,sdbit
ld (denflag),a
call ready; try single density
call dcheck
ret nz; can't read, so don't change
; " "
; set up for single density operations
push hl; (* single density *) hl^ to dph
push de
ld de,tbl1; single density sector xlate table
ld (hl),e; store table^ into dph
inc hl
ld (hl),d
ld de,9; move foward in dph to dpb pointer
add hl,de
ld de,dpbs; single density dpb
ld (hl),e
inc hl
ld (hl),d; {}
jp den1
;
; set up for double density operations
dend: push hl; (* double density *) hl^ to dph
push de
ld de,0; no xlate table (done by FORMAT prog)
ld (hl),e; store table^ into dph
inc hl
ld (hl),d
ld de,9; move forward in dph to dpb pointer
add hl,de
ld de,dpbd; double density dpb
ld (hl),e
inc hl
ld (hl),d
in a,(bitport); now try side 1 (double sided?)
or sidebit
out (bitport),a; select side 1
call dcheck; try to read it
ld bc,15
ld de,dpbd
ld hl,.dpbd; source, single sided dparm
ld a,0; single sided flag (dont chg flags)
jp nz,selsid; single sided if not zero.
ld a,(dsksns+2); is sector # > 9
cp 10; (it must be for valid dbl sided op)
ld a,0; (if not, single sided drives)
jp c,selsid; select single sided
ld hl,.dsdd; source double sided dparm
ld a,255; proper sidflg
selsid: ld (sidflg),a; set up double density dparm {}
ldir
in a,(bitport); return to size zero
and not sidebit
out (bitport),a
; " " exit
den1: ld hl,ctrack
ld de,tracka
ld a,(dsk)
or a
jp z,dskupd; update dsk trk, side, etc. table
ld de,trackb
dskupd: push bc
ld bc,3
ldir
pop bc
pop de; restore pointer to dph
pop hl; pointer to track(x) bios register
ret
;
; check disk legible at current format. z flag if so
; a,f
dcheck: push hl
push bc
ld hl,dsksns
ld bc,6*256+data; 6 bytes from adr. header via "data"
ld a,(nmivec)
push af
ld a,retcod
ld (nmivec),a
ld a,adrcmd; get address command
out (cmnd),a; issue command to controller
dchk1: halt; await interrupt from controller
ini
jp nz,dchk1
pop af
ld (nmivec),a
call busy; wait for command done
and 10h; record not found flag
pop bc
pop hl
ret; return status
;
; ready disk drive, perform physical disk select, set density bit
; a,f
ready: push hl; save hl
push de; and de
push bc
ld a,ficmd; abort any controller action
out (cmnd),a
call diskon; turn drive motor on
ld a,(dsk); A=drive #
ld e,a; save drive # in E
in a,(bitport); A=bit port
and drvmask and denmask; strip drive/density bits
or e; or in requested drive
inc a; bump, 01=A: 10=B:
ld hl,denflag; hl^ to density bit for this drive
or (hl)
out (bitport),a; to bit port
pop bc
pop de
pop hl
ret
;
; turn disk motor on, delay for drive speed
; a,f,b (because .thnsd protected)
diskon: in a,(bitport); get current drive motor status
bit 6,a; is motor on? (save other bits)
ret z; motor on, do nothing
and not mtroff; motor on bit
out (bitport),a; turn motor on
ld b,50; delay 1/2 second & exit
jp .thnsd
;
; turn disk motor off
; a,f
diskoff:
in a,(bitport)
or mtroff; motor off bit
out (bitport),a
ret
;
subttl Writehst and Readhst logical to Physical routines
;* WRITEHST performs the physical write to *;
;* the host disk, READHST reads the physical *;
;* disk. *;
;
; hstdsk = host disk #, hsttrk = host track #,
; hstsec = host sect #. write "hstsiz" bytes
; from hstbuf and return error flag in erflag.
; return erflag non-zero if error
writehst:
ld l,3; readback retries
wthst0: ld a,(rtry1)
ld d,a; reseek tries
wthst1: ld a,(rtry2)
ld e,a; retry error counts
wthst2: push hl
push de; save error counts
call hstcom; set track and sector
ld de,hstbuf
ld b,4; record sector
call wrt; write sector into hstbuf
pop de; restore error flags
pop hl
jp z,wtchk; good op
dec e; retry count
jp nz,wthst2; try again
dec d; home and reseek count
jp z,wtchk3; can't recover
push hl
call dohome; re seek
pop hl
jp wthst1; reset retry count
;
; Read back the sector to check CRC for a good write
wtchk: ld a,(chkwrt)
or a
ret z; write checking disabled
ld b,0
in a,(nmivec)
push af
ld a,retcod
ld (nmivec),a
ld a,rdcmd; read it back to check CRC
out (cmnd),a
wtchk1: halt
in a,(data)
djnz wtchk1
wtchk2: halt
in a,(data)
djnz wtchk2
pop af
ld (nmivec),a
call busy
and rdmask
call nz,saverr; record any soft errors
wtchk3: ld (erflag),a
call errmsg; saves regs/flags, msg if a <> 0
ret z; either good or entry from wthst
dec l
jp nz,wthst0; try again
ret; with hardware error flags
;
; hstdsk = host disk #, hsttrk = host track #,
; hstsec = host sect #. read "hstsiz" bytes
; into hstbuf and return error flag in erflag.
readhst:
ld a,(rtry1)
ld d,a
rdhst1: ld a,(rtry2)
ld e,a; retry error counts
rdhst2: push de; save error counts
call hstcom; set track and sector
ld de,hstbuf
ld b,4; record sector
call rd; read sector into hostbuf
ld (erflag),a; error return flag
pop de; restore error flags
ret z; good op
dec e; retry count
jp nz,rdhst2; try again
dec d; home and reseek count
call z,errmsg; saves regs/flags, message if a<>0
ret z; can't recover
call dohome; re seek
jp rdhst1; reset retry count
;
hstcom: ld a,(hstdsk); select disk
ld c,a
ld e,1; NOT a CPM call, dont force sensing
call dsksel
ld hl,(hsttrk); set track to hsttrk
ld b,h; one byte more than "ld bc,", and
ld c,l; easily tracked with ddt etc.
call trkset; physical seek
ld a,(hstsec); set physical sector
ld c,a; c=sector
jp secset; and exit
;
; show error details if a <> 0
errmsg: push af
or a
jp z,errmsgx; all well
; " "
; Show disk, track, sector, error kind.
; Must save and restore cursor position.
; This can foul the screen if it occurs in the midst of
; an escape sequence from the application program.
push bc
push de
push hl
ld c,esc
call .vidout; separate, to get orig. cursor
push hl; save cursor
ld de,posn
call .print; position to error msg location
; " "
; all positioned, now we can write the error details
ld a,(dsk)
add 'A'
ld c,a
call .vidout
ld de,part2
call .print
ld a,(hsttrk)
call decout
ld de,part3
call .print
ld a,(hstsec)
call decout
ld a,(erflag)
ld hl,errids-5
ld de,5
erm1: add hl,de; at least one bit must be set
rlca
jp nc,erm1; find appropriate message
ex de,hl
call .print
; " "
; restore the original cursor position
ld c,esc
call .vidout
ld c,'='
call .vidout
pop hl
push hl
ld c,h; the original "y"
call .vidout
pop bc; and the original "x"
call .vidout
pop hl
pop de
pop bc
errmsgx:
pop af
ret
;
posn: db '=', 23+' ', 64+' ',0; leaving 16 char msg space
part2: db ': T',0
part3: db ', S',0
errids: db ' NRY',0; bit 7 - not ready
db ' WPR',0; 6 - write protect
db ' WFT',0; 5 - write fault
db ' RNF',0; 4 - record not found
db ' CRC',0; 3 - CRC error
db ' LDA',0; 2 - data overrun
db ' DRQ',0; 1 - data not supplied
db ' BSY',0; bit 0 - not ready
;
; output (a) as 2 digit decimal no.
; a,f,c,h,l
decout: ld c,'0'-1
dco1: inc c
sub 10
jp nc,dco1
push af
ld a,'0'
cp c
call nz,.vidout; suppress leading zero
pop af
add '0'+10
ld c,a
jp .vidout
;
; save and record soft error counts. Save regs.
saverr: push hl
push af; the error
ld a,(dsk)
ld hl,aerr
or a
jp z,saver1; a drive
inc hl
inc hl; b drive, advance pointer
inc hl
saver1: pop af
push af
ld (hl),a
inc hl
inc (hl); count the error
jp nz,saver2; no carry
inc hl
inc (hl)
saver2: pop af
pop hl
ret
;
subttl Physical disk I/O, RAM image
;
ioimage:
; This area is moved to memory for execution.
; All jumps must be relative for position independance
;
; block memory move, turn rom on/off
.move: in a,(bitport); turn rom off
and not bankbit
out (bitport),a
ldir; move logical sector from hstbuf
in a,(bitport); turn rom back on
or bankbit
out (bitport),a
ret; back to rom
;
; read a sector into de^
; return A=0 for no errors,
; A=1 for non-recoverable error
; if b=1 128, b=2 256, b=3 384, b=4 512 bytes/sector
.rd: ld hl,rdmask*256+rdcmd; d=read status mask
jr action; e=read command
;
; write a sector from de^
; return as per read from de^.
; b is sector length in 128 byte records
.wrt: ld hl,wrtmask*256+wrtcmd; d=status mask,
; " " e=write command
; " "
action: ex de,hl
call ready; make sure drive is on and ready
di; no interrupts during disk I/O
in a,(bitport); turn rom off
and not bankbit
out (bitport),a
ld a,(nmivec)
push af
ld a,retcod
ld (nmivec),a; set up nmi vector
ld a,b; sector multiple
ld bc,128*256+data; b=sector length, c=data port
bit 0,a; if 0 then 256 or 512 bytes/sector
jr nz,actn; b set for 128 or 384 bytes/sector
ld b,0; b set for 256 or 512 bytes/sector
actn: cp 1; compute entry point 1st or 2nd loop
push psw; save as Z flag
ld a,e; i/o command
cp wrtcmd; a write?
jr z,wstart; start write command
; " "
out (cmnd),a; fall through to read loop
pop psw
jr z,rl2
rl1: halt; wait for controller
ini
jr nz,rl1
rl2: halt
ini
jr nz,rl2
jr done; read loop done, exit
;
wstart: out (cmnd),a; write loop
pop psw
jr z,wl2
wl1: halt
outi
jr nz,wl1
wl2: halt
outi
jr nz,wl2
; " "
done: pop af; byte at nmi vector address
ld (nmivec),a; restore it
in a,(bitport); turn rom back on
or bankbit
out (bitport),a
ei; turn interrupts on
call busy; get status when contoller not busy
and d; status mask
jp nz,saverr; If soft error record it & exit
ret; with any non-zero error code
;
; This section defines the disk parameters
; (dph's are images moved to RAM)
.dpha: dw 0,0,0,0; dph for unit A:
dw dirbuf,dpbd; directory buffer, Disk Param. Block
dw csva, alva; check sum pointer, alloc. map ptr
db ddbit; density flag for this drive
;
.dphb: dw 0,0,0,0; dph for unit B:
dw dirbuf,dpbd; directory buffer, Disk Param. Block
dw csvb, alvb; check sum pointer, alloc. map ptr.
db ddbit; density flag for this drive
;
; ( single density );
.dpbs: dw 18; (spt) sectors per track
db 3; (bsh) block shift factor
db 7; (blm) block mask
db 0; (exm) extent mask
dw 82; (dsm) max logical block #
dw 31; (drm) max directory #
db 80H; (al0) directory allocation map
db 00H; (al1)
dw 8; (cks) size of dir. check vector
dw 3; (off) reserved tracks
;
; ( single sided/double density ) moved in as needed
.dpbd: dw 40; (spt) sectors per track
db 3; (bsh) block shift factor
db 7; (blm) block mask
db 0; (exm) extent mask
dw 194; (dsm) max logical block #
dw 63; (drm) max directory #
db 0F0H; (al0) dir. alloc. map/BIOS space
db 00H; (al1)
dw 16; (cks) size of directory check vector
dw 1; (off) reserved tracks
;
; sector interleave table ( single density ). Kept in RAM
.tbl1: db 1,6,11,16
db 3,8,13,18
db 5,10,15,2
db 7,12,17,4
db 9,14
;
.imgend:
imaglen equ .imgend-ioimage; length of this image
;
; Following only initialized on cold boot (power on)
.ckwrt: db 0ffh; initializer, check for good writes
.rtry1: db tries1; (0th table entry special)
.rtry2: db tries2; initial error retry counts
.adata: db 0,0,0; last error and soft error counts
.bdata: db 0,0,0; and the same for the "b" drive
.ckend:
;
; ( double sided/double density). Moved in as needed
.dsdd: dw 40; (spt) sectors per track
db 4; (bsh) block shift factor
db 0fh; (blm) block mask
db 1; (exm) extent mask
dw 196; (dsm) max logical block
dw 63; (drm) max directory #
db 0c0h; (al0) dir. alloc. map & bios space
db 0; (al1)
dw 16; (cks) size of directory check vector
dw 1; (off) reserved tracks
;
; ----------------------------------------------------------
; NOTE: the data segment is placed after the code segment
; because storage lengths depend on the code to be moved in
; ----------------------------------------------------------
dseg
;
;
; Disk operations - requested values
sekdsk: ds 1; disk requested
sektrk: ds 2; track number
seksec: ds 1; sector number
;
dsk: ds 1; current disk drive
ctrack: ds 1; track number (real) for current drive
denflag: ds 1; density flag for current drive
sidflg: ds 1
tracka: ds 3; Drive A track (255 means density unknown)
trackb: ds 3; Drive B track (255 means density unknown)
csva: ds 16; Drive A directory check
alva: ds 26; Drive A allocation map
csvb: ds 16; Drive B directory check
alvb: ds 26; Drive B allocation map
;
; Routines inserted on power-on start
; ** THIS AREA MUST MATCH THE CODE ABOVE, moved in **
;
move: ds .rd-.move; move mem blocks, callable from rom
rd: ds .wrt-.rd; read physical sector size b, to de^
wrt: ds .dpha-.wrt; write physical sect size b from de^
; END of installed routines
;
; Disk drive stuff, initialized on power-on start
;
; Disk paramater blocks etc
dpha: ds .dphb-.dpha; dph for A
dphb: ds .dpbs-.dphb; dph for B
dpbs: ds .dpbd-.dpbs; single density disk param blk
dpbd: ds .tbl1-.dpbd; double density disk param blk
tbl1: ds .imgend-.tbl1; single density skew table
;
; Any disk error parameters follow CHKWRT. HOME returns a pointer
; to chkwrt (in hl), to allow external systems to modify.
; An unmodified Kaypro returns a pointer to page 0, so we can
; tell them apart.
; MAKE SURE these agree with declaration from .chkwrt on in code
chkwrt: ds 1; init true, check for good writes
rtry1: ds 1
rtry2: ds 1
;
; Following counts etc measured from last cold start time
aerr: ds 1; last soft error on "a"
aerrct: ds 2; count of soft errors on "a"
berr: ds 1; last soft error on "b"
berrct: ds 2; count of soft errors on "b"
; Can add further disk drives here. Use only for this structure
;
; ** ------- END of moved in area --------- **
;
; Storage
dsksns: ds 6; buffer for disk trk/adr data
;
dirbuf: ds 128; CP/m directory buffer
;
end