home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Oakland CPM Archive
/
oakcpm.iso
/
cpm
/
kaypro
/
dskdrv13.lbr
/
DSKDRV13.ZQ0
/
DSKDRV13.Z80
Wrap
Text File
|
1985-06-05
|
43KB
|
1,632 lines
title 'DSKDRIVE foreign disk driver - using RSX (85/02/02)'
subttl 'default IBM CPM86 DSDD on Kaypro 4'
;
.z80; for M80 benefit
ver equ 13; Version number to be filled in. <10 debug
;
false equ 0
true equ NOT false
;
; customise the @RSX value for the application. See BDOS call lists
; below for guidance. Try to avoid values likely to create conflicts
@RSX equ 115; 115 is GSX graphics on CPM86, probably safe
driver equ true; TRUE installs drivers visible to DOS
;
; Copyright (c) 1985 by C.B Falconer (203) 281-1438
; 680 Hartford Tpk, Hamden, Ct 06504. All rights reserved.
;
; This program may be copied/used/modified etc, but it may NOT
; be sold without express written permission from C.B. Falconer
;
; This system provides a foreign disk driver on the Kaypro 4
; which can then be configured to any suitable format. The
; default format is that for the IBM PC running CPM86 (DSDD).
; Once installed (by running) the drive configuration can be
; altered by auxiliary software (still to be written). To remove
; the driver run the program again. The default foreign drive
; type can easily be altered, once the paramaters are known.
;
; The system uses the generalized CPM 2.2 RSX system published
; and documented separately, and whose source code is embodied
; within this program. The program uses Z80 opcodes only in the
; actual disk driver (for hardware reasons) and in the transfer
; between user storage and disk buffer (for performance). This
; should ease porting to 8080 only systems.
;
; NOTE: This source file requires SLRs Z80ASM or M80 for assembly,
; and RELOCCP.SYS for run-time load and relocation. M80 has
; not been checked. I suspect some labels may be too long.
;
; Revisions: (in LIFO order)
; 1.3 85/5/15. Revised alternate table for Kaypro 2 SSDD. No code
; changes whatsoever.
; 1.2 85/2/3. Added support for single density. DOS dskreset call
; resets this drive. SECSHF calculated from HSTBLK.
; 1.1 85/2/1. Altered configuration table to reduce portion needed
; in external cookbook files for reconfiguration. A | B | C
; runtime parameters for alternate drive type selection added,
; so that a total of 5 configuration (including native) are
; available without the auxiliary ALTDRIVE program.
; 1.0 85/01/30. Initial release. Alteration software needed
; 0.3 85/01/27. First working disk drive.
; 0.2 85/01/24. Provisions for BDOS driver installation,
; checks etc.
; 0.1 85/01/05. Inactive causes reversion to original BDOS call in
; case this was a substitute for it. debug allows testing.
; Keep track of current DMA address.
; 0.0 Original version, by C.B. Falconer (85/Jan/04).
;
; WARNING - When an RSX is installed all warm boots are converted
; into "returns" to the original calling CCP, which is not removed
; nor reloaded. The original Digital Research CCP will not start
; executing SUBMIT jobs under these circumstances. Installation
; of CCPLUS will repair this omission. ZCPR action has not been
; checked. Other problems (with the original CCP) may appear on
; "select errors", i.e. by logging into a non-existent drive.
;
; This system is the outline of a generalized RSX system, with
; initialization and termination code. It is expected to
; respond to BDOS calls with the value @RSX (normally larger
; than any usual CPM call value), and receive an argument of
; some form in the (de) register. Two special values of (de)
; are reserved (0 and -1). In usual operation the (de) value
; is normally a pointer, which cannot take on either of these
; special values. For character output RSXs (e.g. list output
; replacement) this prevents sending a nul or a rubout with all
; bits set. Remember this is a full word parameter.
;
; 0 implies a residence inquiry, and should return a zero
; value if the system is not resident, (as will CPM 2.2 for any
; invalid calls), and non-zero if resident and activated. This
; allows the initialization code to detect that the system is
; already mounted, and avoid multiple loading. Similarly
; application programs can check that the RSX is available. A
; zero value returned in (a) signifies the RSX is not available.
;
; The second special value is 0ffffh (i.e. -1), which
; signals the system to become inactive, so that the next
; warm boot will remove it entirely.
;
; A third special argument is 0001h. This is optional, and is
; used to return a data address. For DSKDRIVE in particular
; this is used to return a pointer to the memory resident
; drive configuration table (whose structure thus should not
; be altered in order to maintain compatibility with auxiliary
; software). DSKDRIVE actually returns this pointer for all
; DE arguments other than 0 or -1, but this should not be
; counted on.
;
; This file implements the DSKDRIVE RSX, and indicates where the
; code should be installed, and the calling conventions for other
; RSXs. See the label "chkparms" to allow initialization
; parameters, and the label "rsx" to install the actual code.
; Note especially than any code located ahead of the label
; "@keep" is available only during initialization. However
; the initialization section can call routines in the retained
; portion. The "bthoot" connector allows re-initialization on
; each "warm boot".
;
; The system, when combined with "RELOCCP", loads itself just
; below the CCP, and keeps the CCP resident. (See RELOCCP.OVR,
; which is an assembly time option for RELOCCP, for a method
; of minimizing memory wastage for multiple RSX's). If RSX.COM
; is executed while resident, it attempts to remove itself and
; reclaim memory. If other systems have been loaded below RSX
; it will not be able to reclaim memory until those systems
; (probably further RSX's) have been removed.
;
; To create a custom RSX modify the equate for "@RSX" above,
; and create any necessary code in the "rsx" and "chkparms"
; areas. Give the resultant file an approriate name (e.g. MYRSX),
; assemble, and create the MYRSX.COM with RELOCCP (See RELOCCP
; documentation). When executed you now have available a new
; BDOS function, which executes whatever you put in.
;
; Acknowledgement: Some of the mechanisms used in this system
; have been taken from various public domain systems, especially
; those by Gary Novasielski and Bruce Ratoff.
;
; This system was created to ease installation of two operations:
; a generalized foreign disc driver, and a "CHAIN" mechanism
; whereby programs can load and execute arbitrary commands. The
; pre-existing KEYS program can probably be converted to execute
; under this system, as can UNSPOOL40 (enhancement of Br. Ratoffs
; UNSPOL30).
;
; While some code economies could easily be made for any particular
; application, I feel that using one standard environment is much
; more important. Thus the provisions for bios driver modification
; have been installed, with the complete check/restore system. The
; disadvantage remains that custom code must be included in this
; source and assembled, rather than standing by itself.
;
; BDOS Functions:
@SYS equ 0
@KEY equ 1
@CON equ 2
@RDR equ 3
@PUN equ 4
@LST equ 5
@DIO equ 6
@RIO equ 7
@SIO equ 8
@MSG equ 9
@INP equ 10
@RDY equ 11
@VER equ 12
@LOG equ 13
@DSK equ 14
@OPN equ 15
@CLS equ 16
@DIR equ 17
@NXT equ 18
@DEL equ 19
@FRD equ 20
@FWR equ 21
@MAK equ 22
@REN equ 23
@LGV equ 24
@CUR equ 25
@DMA equ 26
@ALO equ 27
@WPT equ 28
@GRO equ 29
@CHG equ 30
@GPM equ 31
@USR equ 32
@RRD equ 33
@RWR equ 34
@SIZ equ 35
@REC equ 36
@RDV equ 37
@WRZ equ 40; 38 and 39 are MPM functions
;
; some CPM 3.0 functions, for reference. Match function if
; possible when a 2.2 RSX is created.
; 41 thru 50 are used
@CHN equ 47; Chain to command line in (dmaadr)
@OVL equ 59; Load overlay
;@RSX equ 60; Call RSX. Different usage than here
; 98 thru 112 are used. 115 is CPM86 GSX graphics.
@PFN equ 152; parse filename
;
; System equates:
CPMBASE equ 0
boot equ CPMBASE
bdos equ boot+5
tfcb equ boot+5CH
tfcb1 equ tfcb
tfcb2 equ tfcb+16
tbuff equ boot+80H
TPA equ boot+100H
CTRL equ ' '-1; CTRL CHAR MASK
CR equ CTRL AND 'M'
LF equ CTRL AND 'J'
TAB equ CTRL AND 'I'
FF equ CTRL AND 'L'
BS equ CTRL AND 'H'
eof equ CTRL AND 'Z'
fcblen equ 36; Length of input FCB
;
; nvects is larger than the CPM 2.2 standard to allow for:
; 1. CPM 3 bios system
; 2. Other systems with extended bios calls and/or use
; e.g. Kaypro has a key table, which some applications
; alter (via the pointer at 1). If the space is not
; reserved system crashes will result.
nvects equ 29; Number of BIOS vectors
n22vec equ 16; Number of CPM 2.2 vectors
;
; Macro Definitions
;
; Force location counter zero modulo val
; with respect to "relative" by filling with "fill" bytes
align macro val,relative,fill
local here
here equ $-relative
if (here+val)/val*val-here-val
if nul fill
ds (here+val)/val*val-here
else;; Z80ASM specific coding.
ds (here+val)/val*val-here,fill
endif
endif
endm
;
; ************************************************************
; * This is my standard page relocatable system. See RELOCCP *
; ************************************************************
;
cseg; system requires org 0 and org 100h modules
; You may replace this with two "orgs" in two
; different assemblies to get the overall system
; for RELOCCP to prepare and relocate at run-time.
;
; This defines the first location of the relocated image.
; The portion from here to "@keep" is not retained after
; initialization. The data here is used for relocation
@base: jp intro; Following is std relocation data
@size: dw segsiz; size of segment to relocate
@memsz: db pages; total memory use in pages
;
; Patch/alter this value to non-zero if the RSX may only be
; loaded in a virgin system. This should be done if the system
; installs bios modifications that the BDOS can see.
chkflg: db driver; Set non-zero for virgin system only
;
; This code gets everything started
intro: ld hl,(bdos+1); First so other calls work
ld (gobdos+1),hl; Save the BDOS entry point
ld de,signon
call tstr
call chkparms; Do any parameter checking needed
jp c,exeunt; ..with help message on carry=fault
ld de,0
ld a,@RSX;
call DOS; enquire whether loaded
or a; If loaded,
jp nz,intro1; then bring it down
call rsxsetup; initialize.
call init; Customized portion
jp bootrq; ..bootrq, which sets connectors
;
; already loaded, bring it down
intro1: ld de,-1
ld a,@RSX
call DOS; tell it to come down on next boot
jp boot; which should kill it
;
; Setup the system. First, check environment to see if
; BIOS vectors are accessible and reasonable.
rsxsetup:
ld a,(boot); Location BOOT should
cp 0c3h; have a JMP instruction
jp nz,vecterr
ld hl,(boot+1); Location one points to
ex de,hl; the table of bios jumps
ld hl,(bdos+1); This should point to BDOS
ld a,(chkflg)
ld (kill+1),a; Save for use at exit time. Unclean
or a; but want it user patchable (1 place)
call nz,chksys; Hook, prevent loading invalid system
ex de,hl
ld c,nvects; preserve z flag in here
ld de,biosv
push de; by default init our vector
jp nz,vecterr; after push, to correct stack
ld de,bsave; which we move into
ex de,hl; the code.
rsxsetup1:
ld a,nvects-n22vec
cp c
jp nc,rsxsetup2; stop checking after 2.2 types
ld a,(de)
xor (hl); another JMP?
and 0f1h; allow Call or Jmp
jp nz,vecterr
rsxsetup2:
ld a,(de)
ld (hl),a; bsave[n,0]
inc de
inc hl
ex (sp),hl
inc hl
ld a,(de)
ld (hl),a; biosv[n,1]
inc hl
ex (sp),hl
ld (hl),a; bsave[n,1]
inc de
inc hl
ld a,(de)
ld (hl),a; bsave[n,2]
ex (sp),hl
ld (hl),a; biosv[n,2]
inc hl; biosv[n+1,0]
ex (sp),hl
inc hl; biosv[n+1,0]
inc de
dec c; n := n+1;
jp nz,rsxsetup1
pop hl; purge 2nd transfer address
; This was satisfactory, now if "driver" is TRUE, patch the
; BIOS drivers to point to the new copy (only the std CPM 2.2
; entries) so that new entries can be made locally. The final
; exit mechanism will restore everything.
ld a,(chkflg)
or a
call nz,bpatch
; Save old vectors and CCP return address. BDOS saved at the very
; beginning of system. User code can patch new vectors as required.
ld hl,(boot+1)
ld (boot0+1),hl; Save the BOOT vector
ld hl,2; Retrieve the CCP
add hl,sp; return address from
ld a,(hl); down the stack a ways.
inc hl
ld h,(hl)
ld l,a
ld (ccpret+1),hl; Save the CCP re-entry point
ret
;
; Patch the original bios drivers, which have been checked
; for validity and a reasonable location, to point to the
; new bios table. Only called when "driver" is TRUE. DO NOT
; PATCH the original warm/cold boot entries.
bpatch: ld hl,(boot+1)
inc hl
inc hl
ld de,biosv
ld c,n22vec-1; count of 2.2 bios entries only
bpat1: inc hl
inc de
inc de
inc de
inc hl
ld (hl),e
inc hl
ld (hl),d
dec c
jp nz,bpat1; more
ret
;
; Check system is in usable state. (de) holds the bios pointer,
; and (hl) holds the bdos pointer. If the values are not
; reasonable, return non-zero flag.
; This routine has no relocated code, so it may be patched.
; a,f,h,l
chksys: ld a,e
cp 3
ret nz; boot pointer should end in 3
add e
sub l
ret nz; bdos pointer should end in 6
ld a,d
sub h
sub 0eh
ret nz; Page difference should be 0eh
ld a,(hl)
sub 0c3h; JMP instruction
ret nz
inc hl
ld a,(hl)
sub 011h
ret nz; bdos destination should go to 11
inc hl
ld a,h
sub (hl); on same page to be valid
ret
;
vecterr:
pop de; remove the extra pointer
ld de,vcterrmsg
; " "
; exit with message de^
exeunt: call tstr
jp boot; try re-booting.
;
vcterrmsg:
db CR,LF,'Invalid system$'
;
; =============================================================
; **** Custom portion of initialization code here ****
; =============================================================
;
; Check run command parameters. Carry if not satisfactory
; This version for DSKDRIVE moves alternate configurations into
; place on "DSKDRIVE A", "DSKDRIVE B" or "DSKDRIVE C" commands.
chkparms:
ld hl,alta
ld a,(tfcb+1)
sub 'A'; fcbs are always upshifted
jp z,chkp1; A, go move it in
dec a
or a; clear any carry
ld hl,altb
jp z,chkp1; B
dec a
ret nz; not C
ld hl,altc
chkp1: ex de,hl; move configuration (hl) into
ld hl,info; "info" area
ld b,infosz
chkp2: ld a,(de)
ld (hl),a
inc de
inc hl
dec b
jp nz,chkp2
ret
;
; Custom initialization.
; When this routine is reached the various bios vector copies
; (and patches if "driver" is true) have been made. This
; routine may alter the connectors in "biosv" to install new
; drivers, etc. The original routines are available thru
; "bsave" table. Remember that any routines connected MUST
; live in the retained portion of code, following "@keep"
; below, and I recommend putting their code in the "rsx" area.
; If "driver" is false any bios modifications made through
; this routine will only be available to applications calling
; the bios directly (through location 1), and not to BDOS.
init: ld de,home
ld a,8
call apatch
ld de,seldsk
ld a,9
call apatch
ld de,setrk
ld a,10
call apatch
ld de,setsec
ld a,11
call apatch
ld de,setdma
ld a,12
call apatch
ld de,read
ld a,13
call apatch
ld de,write
ld a,14
call apatch
ld de,sectran
ld a,16
; " "
; Patch bios connector (a) to connect to (de). Note that
; the 0th connector is the cold boot entry (per DR standards).
; Also note that the first entry in biosv is entry # 1
; DO NOT patch connectors 0 or 1, nor any above "n22vecs"
; a,f
apatch: dec a
dec a
push hl
ld l,a
add a
add l; *3
ld hl,biosv+4; offset past "jmp" opcode
add l
ld l,a
adc h
sub l
ld h,a
ld (hl),e
inc hl
ld (hl),d
pop hl
ret
;
signon: db 'DSKDRIVE [A|B|C] (an RSX) '
db ver / 10 + '0', '.', ver MOD 10 + '0', '$'
;
; Alternate drive configurations, used on "DSKDRIVE A"
; or "DSKDRIVE B" commands. No parameter uses default.
; see "info" table for for parameter meanings. You may
; reconfigure these tables for your most used types, thus
; allowing up to five (including native) drive versions.
;
alta: dw 40; cpmspt
db 3; bsh
db 07h,0; blm,exm
dw 194; dsm
dw 63; drm
db 0F0h,0; al0,al1
dw 16; cks
dw 1; off
db 4; hstblk
db 0, 1, 2, 3, 4, 5, 6, 7; sxltbl
db 8, 9,10,11,12,13,14,15
db 16,17,18,19,20,21,22,23
db 24,25,26,27,28,29,30,31
db 32,33,34,35,36,37,38,39
db 40,41,42,43,44,45,46,47
db 48,49,50,51,52,53,54,55
db 56,57,58,59,60,61,62,63
db 64,65,66,67,68,69,70,71
db 72,73,74,75,76,77,78,79
db 0; spare
db 0; sec1st
db 0; config
db 40; ntrks
db 0; nsecs
db 'KAYPRO 2 (SSDD)$$$$$$$$$$$$'
;
altb: dw 36; cpmspt
db 4; bsh
db 0Fh,0; blm,exm
dw 170; dsm
dw 63; drm
db 080h,0; al0,al1
dw 16; cks
dw 4; off
db 2; hstblk
db 0, 1, 2, 3, 4, 5, 6, 7; sxltbl
db 8, 9,10,11,12,13,14,15
db 16,17,18,19,20,21,22,23
db 24,25,26,27,28,29,30,31
db 32,33,34,35,36,37,38,39
db 40,41,42,43,44,45,46,47
db 48,49,50,51,52,53,54,55
db 56,57,58,59,60,61,62,63
db 64,65,66,67,68,69,70,71
db 72,73,74,75,76,77,78,79
db 0; spare
db 1; sec1st
db 2; config
db 40; ntrks
db 0; nsecs
db 'TS802 (Televideo DSDD)$$$$$'
;
altc: dw 32; cpmspt
db 3; bsh
db 07h,0; blm,exm
dw 251; dsm
dw 127; drm
db 0F0h,0; al0,al1
dw 32; cks
dw 3; off
db 2; hstblk
db 0, 1, 2, 3, 4, 5, 6, 7; sxltbl
db 8, 9,10,11,12,13,14,15
db 16,17,18,19,20,21,22,23
db 24,25,26,27,28,29,30,31
db 32,33,34,35,36,37,38,39
db 40,41,42,43,44,45,46,47
db 48,49,50,51,52,53,54,55
db 56,57,58,59,60,61,62,63
db 64,65,66,67,68,69,70,71
db 72,73,74,75,76,77,78,79
db 0; spare
db 1; sec1st
db 2; config
db 40; ntrks
db 0; nsecs
db 'HP125 (DSDD)$$$$$$$$$$$$$$$'
;
;
; ------------------ End custom initialization area ----------------
;
align 256,@base,0; ensure page aligned
;
; *******************************************************
; * The code from here up is retained in memory after *
; * initialization. It may be used by the initializer *
; * This code MUST start on a page boundary. *
; *******************************************************
@keep:
;
; During operation, this location will point to intercept and will
; be jumped to by BDOS calls from location 5. This organization
; depends on the fact that BDOS calls the bios directly (ignoring
; the pointer at location 1), except when needing a warm boot
; (e.g. after a disk error), when it uses the pointer at 1.
;
; This must be at the lowest location in the protected code segment.
bdosv: jp intercept; the bdos link
;
; This area replaces the bios jump table, allowing intercepts.
; Any intercepts (usually console commands) are set up by the
; initializing code, which is discarded after execution. See
; comments on "nvects" above. Normally these bios vectors will be
; accessed only by executing programs, and not by the BDOS unit.
; To insert drivers in the bios it is necessary to save the original
; bios pointers (for restoration when the RSX is removed) and
; install jumps to this revised table in the original table. Note
; that the original table may contain a CALL for the warmboot vector,
; so that ROM based code can tell the CPM system size when called.
biosv: REPT nvects
jp $-$
endm
;
; This table has two purposes - it allows connection to the original
; bios vectors destinations, and it saves all the values for
; restoration upon RSX exit. Thus the custom initialization section
; can patch the real bios table to install new disk drivers, etc.
; Note that this can only work when this is the FIRST rsx installed.
; Further ones can only intercept user bios calls and BDOS calls.
; Provided that the bios table in effect on entry to this system
; does not hold any data areas, the restoration on RSX removal will
; be harmless.
bsave: REPT nvects
jp $-$
endm
;
; The "old" entries needed. Index 0 here is warmboot, not cold
@home equ bsave+(7*3)
@seldk equ bsave+(8*3)
@setrk equ bsave+(9*3)
@setsec equ bsave+(10*3)
@setdma equ bsave+(11*3)
@read equ bsave+(12*3)
@write equ bsave+(13*3)
@sectran equ bsave+(15*3)
;
; This routine intercepts all BDOS calls.
; Note that the initial code sequence allows applications to
; find the actual BDOS, if necessary. See SD88F6 for one use.
intercept:
ld a,0ffh
cp c
jp c,gobdos; Never taken, but SD trackable
ld a,@SYS; Get function
cp c
jp z,sysreq; a reboot request
ld a,@DMA
cp c
jp z,dodma
ld a,@LOG
cp c
jp z,drvreset; reset this drive also
ld a,@RSX; defined for this system
cp c
jp nz,gobdos; not for us
; DO NOT optimize the above sequence. It is intended to be
; trackable by utilities to list the RSX's active. The series
; stops when the instruction is not "ld a,bytevalue". The
; initial "ld a,0ffh" will not be counted by tracking utilities
; and the checks on @SYS and @DMA are always expected. This
; organization allows a range of BDOS calls to be intercepted.
; " "
ld hl,0
add hl,sp
ld sp,lclstk
push hl; save entry stack pointer
call doit; This is an extension call
ex de,hl; save return value
pop hl
ld sp,hl
ex de,hl; return its result
ld a,l; copy result to (a)
ld b,h; so (ba)=(hl), like BDOS
ret; to the caller.
;
; Reset this drive also when application wants a drive reset
; Added to RSX system for disk drive application.
drvreset:
push bc
call bthook
pop bc
jp gobdos
;
; Keep track of the current DMA address on general principles
dodma: ex de,hl
ld (dmadr),hl; BDOS' view, not BIOS
ex de,hl
; " "
; Connects to the "real" BDOS routine
gobdos: jp $-$; Patched on entry
;
; The RSX (resident system extension). Argument is de as usual,
; and the extension specified is (c). Return value is put in hl.
; The stack is already set to the local stack, with the old
; stack pointer under the return from doit.
; a,f,b,c,d,e,h,l (allowed)
doit: ld hl,(active); set "enquiry" return value in (l)
ld a,l
or a
jp z,gobdos; inactive, use bdos call
ld h,0
ld a,e; check for "loaded enquiry"
or d
ret z; signal active on enquiry
ld a,e
and d
inc a
jp nz,rsx; not "pulldown" request.
; " "
; Inactivate the RSX. Next boot will try to recover the memory.
kill: ld a,$-$; patched with "chkflg" at init
or a
call nz,unpch; Remove any bios alterations
xor a
ld (active),a; mark inactive
ld l,a; return 0, pulldown accepted
ld h,a; and we are now inactive.
ret
;
; Remove any bios patches made on initialization, in case this is a
; driver being inactivated, and further RSX's are loaded beyond it.
; Thus a "kill" request will be logically executed, even though the
; memory is not reclaimed. However bios alterations via the pointer
; at 1 cannot be removed until a warm boot occurs (that pointer may
; be pointing to an RSX installed later). Thus the table at biosv
; must be updated to point to the original bios entries
unpch: ld de,bsave+3
ld hl,(boot0+1)
inc hl
inc hl
inc hl; start at the constat entry
push hl
ld hl,biosv+3
ld c,n22vec-1; dont alter the warm boot entries
unpch1: ld b,l; offset, depends on page alignment
inc de; past opcode
inc hl; Ignore the opcode (dont change)
ld (hl),b; biosv[n,1] --> oldbios[n,0]
inc hl
ex (sp),hl
ld b,h; oldbios page
inc hl
ld a,(de); lsb
ld (hl),a; oldbios[n,1]
inc de; ^msb
inc hl
ld a,(de); msb
ld (hl),a; oldbios[n,2]
inc hl; oldbios[n+1,0]
ex (sp),hl
ld (hl),b; biosv[n,2] --> oldbios[n,0]
inc hl; biosv[n+1,0]
inc de; next opcode
dec c; n := n+1
jp nz,unpch1
pop hl
ret
;
; Note that the following "boot" entries will never be reached if a
; further RSX has been installed, since that RSX will intercept the
; boot and do a direct return to the CCP (unless it is inactive, and
; removes itself, when a whole chain of RSX removals can be started)
;
; The application process has requested a warm-boot by invoking BDOS
; function 0. If the system is inactive remove it, otherwise return
; to the CCP via the stored (on initialization) CCP return value.
sysreq: jp bootrq; This allows separation if needed
;
; The application process has requested a reboot by jumping to
; location 0. If we are no longer active, we will honor the request
; by executing the address found in the BOOT vector at entry.
; Otherwise return to CCP without rebooting.
bootrq: ld sp,lclstk; set up a valid stack
ld a,(active)
or a
jp z,done; Not active, all done. Remove self
call bthook; Application specific boot action
ld c,@LOG
call gobdos; Reset drives, like any
ld de,actmsg; other reboot does.
call tstr
ld de,altid; added message portion for
call tstr; DSKDRIVE use
; " "
; Reset the system pointers to use this system.
ld hl,bdosv
ld (bdos+1),hl
ld hl,bootrq
ld (biosv+1),hl
ld hl,biosv
ld (boot+1),hl
ld hl,tbuff
ld (dmadr),hl; Will be set by CCP
ccpret: jp $-$; Patched on startup
;
; Done with the system
done: ld de,donemsg; Message and jump to old boot addr.
call tstr; as originally read from memory wd 1
boot0: jp $-$; Reboot. Patched on initialization
; This value points to old bios table
;
; ================================================================
; Utility routine area
; ================================================================
;
; Put string (de) to console, '$' terminated
tstr: ld a,@MSG
; " "
; dos call (a) without disturbing registers. Does not use the RSX
DOS: push bc
push de
push hl
ld c,a
call gobdos
pop hl
pop de
pop bc
ret
;
; ==================================================================
; **** To keep as much as possible constant, put real RSX here ****
; ==================================================================
;
; This routine MUST be supplied
; The actual code for the application goes here
; Note that the (de) parameter can never be 0 or 0ffffh on entry.
; If this system is really an i/o driver (such as a foreign disk
; reader/writer) then the RSX itself never need do anything, and
; this code can be left alone (except that a DOS call is available
; to trigger the message below).
; Return any value in hl.
rsx: ld hl,info; return info address only
ret; Used to reconfigure the driver
;
; This routine MUST be supplied. It may be only a "ret"
; for DSKDRIVE recalculates some parameters in case the drive
; configuration has been changed, and ensures drive reset.
; a,f,b,h,l
bthook: xor a
ld (hstact),a
ld (unacnt),a
ld hl,tbuff
ld (dmaadr),hl; BIOS view of it
ld a,(hstblk)
ld b,-1
bthk1: inc b; compute secshf from hstblk
rra; cy was clear
or a
jp nz,bthk1; secshf := log2(hstblk)
ld a,b
ld (secshf),a
ld a,(cpmspt)
ld b,a
ld a,(config)
and 0fh; mask off density selector
cp 1
jp nz,bthk4
ld a,b
or a
rrca
ld b,a
bthk4: ld a,(secshf)
bthk5: or a
jp z,bthk6
push af
ld a,b
rrca
ld b,a
pop af
dec a
jp bthk5
bthk6: ld a,b
ld (offsec),a
ret
;
; ----------- Driver configuration area ------------
;
; Host specific ports
ctrlpt equ 10H; Controller ports (WD 1791)
statpt equ ctrlpt
trkreg equ ctrlpt+1
secreg equ ctrlpt+2
datapt equ ctrlpt+3
;
; 1791/2/3 Controller chip specific
; For most Z80 implementations the data request and interrupt from
; the 1791 is connected to NMI (possibly gated by the CPU halt
; status line). Thus a CPU halt is ended by the appropriate
; controller status, with the RET instruction in memory location
; 066h. A status read after the interrupt will reset the interrupt,
; as will satisfying the data request. 8080s need more hardware.
wrtmsk equ 0FCH; Status bits useful after write
rdmask equ 09CH; " " read
clrcmd equ 0CH; clear controller/reset
rdcmd equ 08CH; read physical sector
wtcmd equ 0ACH; write physical sector
seekcmd equ 014H; seek track
;
; Kaypro Specific
bitpt equ 01CH; KAYPRO control
drvmsk equ 3; bits 0 & 1 select drive
sideb equ 4; side selection bit
prtbsy equ 8; Parallel printer busy (in)
prtstrb equ 16; strobe printer data
densb equ 32; density selection bit, 1=single
mtroff equ 64; 1 bit stops drive motors
bank equ 128; Select memory bank.
;
; *** This area is accessed/altered by support programs ***
info:
; The "info" area has been organized to fit into exactly 128 bytes
; for convenience in auxiliary programs that reconfigure this.
;
; The CPM standard Disk Parameter Block, extended
; Modification to this structure changes the driver
;
dpblk: ; Different values for: KPRO4 KPRO2 HP125 TVTS802
; CPM sectors/physical track
cpmspt: dw 32; 40 40 32 36
; log2(sectors/cpmallocation block)
bsh: db 4; 4 3 3 4
; blm, exm calculated from bsh
db 0FH,01H; 0Fh,1 7,0 7,0 0Fh,0
; total blocks on drive
dsm: dw 157; 196 194 251 170
; directory entries - 1
drm: dw 63; 63 63 127 63
; directory/other blocks pre-allocated
db 80H,00H; 0C0h,0 0F0h,0 0F0h,0 080h,0
; cks=(drm+1)/4 (or 0 for fixed)
cks: dw 16; 16 16 32 16
; reserved tracks
off: dw 1; 1 1 3 4
; CPM extension. 1,2,4,8 sectors/phys record
hstblk: db 4; 4 4 2 2
;
; The sector skew translation table. Room for 10k/track
sxltbl: db 0, 1, 2, 3, 4, 5, 6, 7
db 8, 9,10,11,12,13,14,15
db 16,17,18,19,20,21,22,23
db 24,25,26,27,28,29,30,31
db 32,33,34,35,36,37,38,39
db 40,41,42,43,44,45,46,47
db 48,49,50,51,52,53,54,55
db 56,57,58,59,60,61,62,63
db 64,65,66,67,68,69,70,71
db 72,73,74,75,76,77,78,79
;
; Customization tables for system
db 0; SPARE BYTE
; Physical ID of 1st sector on track
sec1st: db 1; 0 0 1 1
; Strategy in computing physical addresses
config: db 4; 2 0 2 2
; (high bit set for single density)
; = 0: 1 sided, phys adr = hst adr
; 1: sec>offsec side1, adrsec=off-sec
; 2: odd trks on side 1, trkadr=hstrk/2
; 3: side1 is ntrks up
; 4: side1 is ntrks up, count backward
maxcase equ 4
; Number of tracks on physical disk
ntrks: db 40; 40 40 40 40
; Correction to side 1 sector adr.
nsecs: db 0; 10 0 0 0
;
; This portion of "actmsg" is used as ID by altdrive.
altid: db 'IBM PC (CP/M-86 DSDD)$$$$$$'
; At least one terminal "$" must be left in above message
; DO NOT CHANGE LENGTH OF MESSAGE
infosz equ $-info
;
; The following area is not altered when changing disk types,
; but includes the standard CPM configuration tables. This
; portion must be customized on installation to host only.
;
modrv: db 1; Which drive to alter (0 = A)
dskode: db 02H; and the drive selection coding
dendbl: db 0; base command for double density
densgl: db 32; base command for single density
invert: db 0; 1 to invert data bits on transfer
;
; The CPM standard dpb. Pointers to above and CPM storage.
dpb: dw sxltbl; 0 for no translation
dw 0,0,0; *** CPM mungs these, must be in RAM ***
dw dirbuf; pointer
dw dpblk; pointer
dw csv; pointer
dw alv; pointer
;
; END of Unalterable (except for values) configuration area
;
; "donemsg" and "actmsg" messages MUST be supplied
donemsg: db cr,lf,'(DSKDRIVE terminated)$'; on removal
actmsg:
drvmsg: db cr,lf,'Drive '
drvid: db 'B: set to $'
;
; The revised foreign disk drivers.
;
home: ld bc,0
call setrk
ld a,(hstwrt)
or a
jp nz,home1
ld (hstact),a
home1: ret
;
seldsk: ld a,c
ld (sekdsk),a
ld a,(modrv)
cp c
jp nz,@seldk
ld hl,dpb
ret
;
; Postpone actual selection of originals until read/write
; to avoid motor turn-ons, pre-seeks etc. until needed
setrk: ld h,b
ld l,c
ld (sektrk),hl
ret
;
setsec: ld a,c
ld (seksec),a
jp @setsec
setdma: ld h,b
ld l,c
ld (dmaadr),hl
jp @setdma
;
sectran:
ld a,(sekdsk); Needed because RAMDISK fouls
ld hl,modrv; by not knowing it was not
cp (hl); selected.
jp nz,@sectran
ld a,d
or e
jp z,sectran2; no translation, return alone
ex de,hl
add hl,bc
ld l,(hl)
ld h,0
ret
sectran2:
ld h,b
ld l,c
ret
;
oread: ld hl,(sektrk); do original read routine
ld b,h
ld c,l
call @setrk
jp @read
;
owrite: push bc; do original write routine
ld hl,(sektrk)
ld b,h
ld c,l
call @setrk
pop bc
jp @write
;
; Digital Researchs (flawed) deblocking algorithm.
read: ld a,(modrv)
ld b,a
ld a,(sekdsk)
cp b
jp nz,oread
xor a
ld (unacnt),a
ld a,1
ld (readop),a
ld (rsflag),a
ld a,2; mark "write unallocated"
ld (wrtype),a
jp rdwrt
;
write: ld a,(modrv)
ld b,a
ld a,(sekdsk)
cp b
jp nz,owrite
xor a
ld (readop),a; Not a read
ld a,c
ld (wrtype),a
cp 2; Code for "write unallocated"
jp nz,chkuna; else wrt to unalloc, set params
ld hl,(bsh); l := bsh, minimum 3
ld a,1
write1: rlca
dec l
jp nz,write1; Compute block size, in sectors
ld (unacnt),a
; ld a,(sekdsk)
; ld (unadsk),a
ld hl,(sektrk)
ld (unatrk),hl
ld a,(seksec)
ld (unasec),a
; " "
; Check for write to unallocated sector
chkuna: ld a,(unacnt)
or a
jp z,alloc; No unallocated remain
dec a
ld (unacnt),a; Reduce for next time
; ld a,(sekdsk)
; ld hl,unadsk
; cp (hl)
; jp nz,alloc; Not same disk
ld hl,unatrk
call eqtrks
jp nz,alloc; Not same track
ld a,(seksec)
ld hl,unasec
cp (hl)
jp nz,alloc; seksec <> unasec
;
inc (hl); unacnt > 0, disk, track sector ok
ld a,(hl)
ld hl,cpmspt
cp (hl)
jp c,inbuff; Not overflow to next track
xor a
ld (unasec),a
ld hl,(unatrk)
inc hl
ld (unatrk),hl
inbuff: xor a
ld (rsflag),a; match found, mark unnecessary read
jp rdwrt
;
; Not an unallocated record, requires pre-read
alloc: xor a
ld (unacnt),a
inc a
ld (rsflag),a
; " "
rdwrt: xor a; read or write
ld (errflg),a
ld a,(secshf); Compute host sector
ld b,a
inc b
ld a,(seksec)
rla; Saving Hi bit in carry
rdwrt1: rra
or a; clear carry
dec b
jp nz,rdwrt1; sekhst := seksec SHR secshf
ld (sekhst),a
ld hl,hstact
ld a,(hl)
ld (hl),01H; Set hstact for next
or a
jp z,filhst; Was not active
; ld a,(sekdsk)
; ld hl,hstdsk
; cp (hl)
; jp nz,flush
ld hl,hsttrk
call eqtrks
jp nz,flush
ld a,(sekhst)
ld hl,hstsec
cp (hl)
jp z,match
flush: ld a,(hstwrt); Proper disk but not correct sector
or a
call nz,wthst; dirty buffer, write it out
; " "
filhst: ld a,(sekhst)
ld (hstsec),a
ld hl,(sektrk)
ld (hsttrk),hl
; ld a,(sekdsk); May have to fill the host buffer
; ld (hstdsk),a
ld a,(rsflag)
or a
call nz,rdhst
xor a
ld (hstwrt),a; Mark buffer clean
; " "
; Copy data to or from buffer, depending on "readop"
match: ld a,(hstblk)
dec a
ld hl,seksec
and (hl)
ld l,a
ld h,00H
add hl,hl; *128
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
add hl,hl
ld de,buff
add hl,de; point to buffer area
ex de,hl
ld hl,(dmaadr)
ex de,hl
ld bc,128
ld a,(readop)
or a
jp nz,rwmov
ld a,01H
ld (hstwrt),a; Mark buffer dirty for write
ex de,hl; And trade move direction
; " "
; Transfer hl^ to de^ for bc, invert bits on "invert"
rwmov: ld a,(invert)
or a
jp z,rwmov2
rwmov1: ld a,(hl)
cpl
ld (de),a
inc de
inc hl
dec bc
ld a,b
or c
jp nz,rwmov1
jp qflush
;
; No inversion, move as rapidly as possible
rwmov2: ldir; *** Z80 code ***
; " "
; Clear host buffer if this is a directory write
qflush: ld a,(wrtype)
cp 1; Code for directory write
ld a,(errflg)
ret nz
or a
ret nz
xor a
ld (hstwrt),a; will be clean after write
; " "
wthst: call rwtbgn
wthst1: ei; during seeks
call setup
or a
jp nz,rwtxit
ld a,(hstblk)
ld e,wtcmd; write command
ld c,datapt; I/o port for data
ld hl,buff
di
rrca
jp nc,wthst2; Not 128 byte physical rcd
ld b,128
call dcmnde
jp w256
wthst2: ld b,0
rrca
jp nc,wthst3; Not 256 byte physical rcd
call dcmnde
jp w256
wthst3: rrca
jp nc,wthst4; 1024 byte physical rcd
call dcmnde
jp w512; 512 byte physical rcd
wthst4: call dcmnde
; " "
w1024: halt; Else 1024 byte rcd.
outi; *** Z80 code
jp nz,w1024
w768: halt
outi
jp nz,w768
w512: halt
outi
jp nz,w512
w256: halt
outi
jp nz,w256; May be a W128, on entry bc
wthst5: in a,(statpt)
rra
jp c,wthst5; Await non-busy
rla
and wrtmsk
jp z,rwtxit
call clear
dec d
jp nz,wthst1; retry
ld a,0FFH; Signal error
jp rwtxit
;
; Set up NMI return, save controller track setting
rwtbgn: ld a,(0066H)
ld (save66),a
ld a,0C9H; "RET" instruction for NMI
ld (0066H),a
in a,(trkreg)
ld (trksav),a
ld d,10; retries
ret
;
eqtrks: ex de,hl
ld hl,sektrk
ld a,(de)
cp (hl)
ret nz
inc de
inc hl
ld a,(de)
cp (hl)
ret
;
rdhst: call rwtbgn
rdhst1: ei
call setup
or a
jp nz,rwtxit
ld a,(hstblk)
ld e,rdcmd; read command
ld c,datapt; port to read data from
ld hl,buff
di
rrca
jp nc,rdhst2; Not 128 bytes per phys. block
ld b,128
call dcmnde
jp r256
rdhst2: ld b,0; i.e. 256
rrca
jp nc,rdhst3; Not 256 bytes per block
call dcmnde
jp r256
rdhst3: rrca
jp nc,rdhst4; 1024 byte physical record
call dcmnde
jp r512; 512 bytes per block
rdhst4: call dcmnde
; " "
r1024: halt; else 1024 bytes per block
ini; *** Z80 code
jp nz,r1024
r768: halt
ini
jp nz,r768
r512: halt
ini
jp nz,r512
r256: halt
ini
jp nz,r256; May be r128, on entry (b)
rdhst5: in a,(statpt)
rra
jp c,rdhst5; Await non-busy status
rla
and rdmask
jp z,rwtxit
call clear
dec d
jp nz,rdhst1; retry
ld a,0FFH; Signal error
; " "
; Exit, restoring 66, trkreg, setting errflg on (a)
rwtxit: push af
ld a,(save66)
ld (0066H),a
ld a,(trksav)
out (trkreg),a
pop af
ei
ld (errflg),a
ret
;
; docase (a) on table (hl)^
docase: add a
add l
ld l,a
adc h
sub l
ld h,a
ld a,(hl)
inc hl
ld h,(hl)
ld l,a
jp (hl)
;
cases: dw case0, case1, case2
dw case3, case4
;
setup: ld hl,(hsttrk)
ld (adrtrk),hl
ld a,(hstsec)
ld (adrsec),a
xor a
ld (side),a; default use side 0
ld a,(config)
and 0fh; remove density selection
cp maxcase+1
jp nc,error
ld hl,cases; Execute the appropriate code
call docase
jp seek
;
case0: ret
;
case1: ld a,(adrsec); config = 1
ld hl,offsec
sub (hl)
ret c
ld hl,nsecs
add (hl)
ld (adrsec),a
ld a,1
ld (side),a
ret
;
case2: ld hl,(adrtrk)
ld a,l
and 1
ld (side),a
ld a,h
rra
ld h,a
ld a,l
rra
ld l,a
ld (adrtrk),hl
ret nc; on side 0
ld hl,nsecs
ld a,(adrsec); side 1, correct sector address
add (hl)
ld (adrsec),a
ret
;
case3: ld a,(adrtrk)
ld hl,ntrks
cp (hl); tracks per side
ret c
sub (hl); On second side
ld (adrtrk),a
ld a,1
ld (side),a
ret
;
case4: ld a,(adrtrk)
ld hl,ntrks
cp (hl)
ret c; On first side
cpl
add (hl)
add (hl); Now we count down
ld (adrtrk),a
ld a,1
ld (side),a
ret
;
error: ld a,0FFH; config unknown, error
ret
;
; Host specific routines here - setup for Kaypro 4
seek: ld a,(dsktrk); If we have switched drives set
out (trkreg),a; the controller to match history
ld a,(config); Set up for side and density
or a
ld a,(dendbl)
jp p,seek1; Normal double density system
ld a,(densgl); else set single density command
seek1: ld b,a
ld a,(side); Move side selection bit into place.
and 1
rla
rla
or b; Compute drive selection/density code
ld b,a
in a,(bitpt); Keep bank (80), turn mtr on (40=0)
push af; clear DDEN (20), keep lptstrobe (10)
and 98H; ignore prtbusy(8), clear side(4),
or b; drive code (2, 1) bit weights
ld b,a
ld a,(dskode)
or b
or 10H; turn off any lptstrobe bit (Kaypro)
out (bitpt),a; selecting drive/side/density
pop af
and 40H
call nz,pause; Motor was off, wait for it
ld a,(dsktrk)
inc a
call z,clear; Never used yet
ld a,(adrtrk)
ld hl,dsktrk
cp (hl)
jp z,seek4
; " "
out (datapt),a; changing tracks
ld a,seekcmd; seek track
call dcmnd
halt
seek3: in a,(statpt)
rra
jp c,seek3; await non-busy
rla
and 98H; Check status
jp z,seek4
call clear
dec d
jp nz,seek; retry allowd
ld a,0FFH; Else signal error
ret
;
seek4: ld a,(adrsec); Successful seek
ld hl,sec1st
add a,(hl)
out (secreg),a; set physical sector
ld a,(adrtrk)
ld (dsktrk),a
xor a
ret
;
; Clear disc controller.
; a,f
clear: ld a,clrcmd
call dcmnd
halt
clear1: in a,(statpt); Await "not busy"
rra
jp c,clear1
xor a
ld (dsktrk),a
ret
;
; command (e) to disk controller
; a,f
dcmnde: ld a,e
; " "
; command (a) to disk controller
; f
dcmnd: out (ctrlpt),a
push bc
ld b,20
dcmnd1: dec b
jp nz,dcmnd1; pause for controller status
pop bc
ret
;
; pause (b) * 10 Millisec. Set for 2.5 Mhz clock.
; a,f,b
pause: push de
pause1: ld de,0686H; <<<< Change for other clocks <<<
pause2: dec de
ld a,e
or d
jp nz,pause2
dec b
jp nz,pause1
pop de
ret
;
; ------------------- Initialized storage --------------------------
active: db true; Non zero if active. allows 1..255
;
; ==================================================================
; **** Put initialized application storage past this point ****
; ==================================================================
;
dsktrk: db 0FFH; mark not selected/used?
;
; ------------------ End initialized storage -----------------------
;
; ------------------- Uninitialized storage ------------------------
segsiz equ $-@base; Relocation bit table starts here
dmadr: ds 2; This value is known to BDOS. It is not
; necessarily the same as the BIOS value
;
; ==================================================================
; **** Put uninitialized application storage past this point ****
; ==================================================================
;
dmaadr: ds 2; this is the value known to the BIOS
secshf: ds 1; log2(hstblk). For run-time addressing
;
sekdsk: ds 1; seek ids, at CPM level
sektrk: ds 2
seksec: ds 1
;
save66: ds 1; Save/restore 066h, for NMI operation
trksav: ds 1; Save/restore controller track reg.
;
;hstdsk: ds 1; Host ids, at blocked level
hsttrk: ds 2
hstsec: ds 1
;
adrtrk: ds 2; Physical trk adr. for controller
adrsec: ds 1; and sector
offsec: ds 1; Sector offset for 2nd side
;
sekhst: ds 1; block/deblock variables
hstact: ds 1
hstwrt: ds 1
unacnt: ds 1
;unadsk: ds 1
unatrk: ds 2
unasec: ds 1
errflg: ds 1
rsflag: ds 1
readop: ds 1
wrtype: ds 1
;
side: ds 1
dirbuf: ds 128
csv: ds 64
alv: ds 64
buff: ds 1024; Can handle 1024 byte host sector
;
; --------------- End application uninitialized storage --------------
;
ds 32; Local stack of at least 16 words
; plus any remainder on page
; align to next page boundary
align 256,bdosv
lclstk:
;*************************************************
; End of segment to be relocated.
pages equ ($-@base)/256; So loader knows memory needs.
;
end
Eô