	.title 'CP/M logical unit assignment program.'
	.sbttl 'ASSIGN'
version	=	5
revision=	2
	.pabs
	.phex	
	.loc	100h
	sspd	saveSP
	lxi	SP,stack
	jmp	MAIN
saveSP:	.word	0	; room for CCP's stack ptr
retCCP:
	lda	4
	mov	C,A	; re-select correct disk
	call	SELDSK
	lspd	saveSP
	ret
;----------
; If the user types in simply "ASSIGN" a list of all 
; four logical units and their respective physical  
; device unit numbers is displayed. A boot follows.
;
; If a user types "ASSIGN X YZ" where X is the CP/M
; logical unit, Y is the desired physical device that
; X is to be ASSIGNed to, and Z is the devices unit 
; number, then a message is printed indicating that
; the assignment was accepted. 
;
; If a user types "ASSIGN X NAME" where X is the CP/M
; logical unit and NAME is the desired hard disk or
; network unit name, a message is printed telling the
; user to input his password.  (An execption to this
; is if there is no password, i.e. all blanks.) The 
; name and password are then sent to the BIOS for 
; translation into an absolute physical device and
; unit number.  The user is then notified if the
; assignment is accepted or denied.
;
; If an error is found on the command line, the first
; bad command line character is pointed out (by an 
; arrow), and a summary of instructions is then 
; displayed followed by a warm boot.
;------------------
; (1) Data addressing and variable definition
BASE	=	0	; wboot address
BDOS	=	5	; console in/out subroutine
wbadr	=	1	; vector for bios calls
backsp	=	8	; ascii backspace
delete	=	7Fh	; ascii delete
cr	=	0Dh	; ascii carriage return
lf	=	0Ah	; ascii line feed
userno	=	47h	; user number (FF is SA)
assnNET =	17h	; assign to NETWORK command
assnHARD=	17h	; assign to HARDDISK command
HARDP	=	01h	; HARDDISK port number
PIOAD	=	08h	; status port
;
wbaddr:	.word	0	; bios vector address
buffer: .word	80h	; beginning of command line
chrcnt:	.byte	0	; num of command chrs entered
namchr:	.byte	0	; num of com chrs entred at NME
numchr:	.byte	0	; num of command chrs read
MAPbyte:.byte	0	; physical command byte
logname:.byte	0	; logical unit name
logbyte:.byte	0	; current logical unit
physname:.byte	0	; physical unit name
unitnum:.byte	0	; unit number
size:	.byte	0	; unit size for allocation table
NAMEaddr:.word	00	; address of name assignment
conbuf:	.byte	10,0
	.ascii	'                '  ; console buffer
NETcom:	.byte	assnNET	; Network assign command
BIOSbuf:.ascii	'        '  	    ; BIOS Name holder
PASSbuf:.ascii	'       '    	    ; Password holder
NETres:	.blkb	4	; Network assign result
HARDcom:.byte	assnHARD ; Hard disk assign command
	.byte	0,0,0,0,0,0,0
HARDstat:.blkb	8	; hard disk status
locnet:	.byte	0	; 0 if local, 1 if NETwork
sysflg:	.byte	0	; 0ffh if mpm, 0 if cpm1.4, else cpm2.
NAMEstr:.ascii	'        $'
retries:.byte	0	; password retries
maxtries=	3	; Max num of retries allowed
;
CONIN:	mvi	c,1	; console input function
	jmp	bdos
seldsk:	lda	sysflg
	ora	a
	jp	notmpm	; skip if some cpm version
	lxi	h,selret
	push	h	; force return to us
notmpm:	lhld	wbaddr
	lxi	d,18h
	dad	d
	pchl		; goto bios
selret:	shld	mpmtab	; save dpb address
	ret
mpmtab:	.word	0
cpmMAP:	lda	sysflg
	ora	A
	jm	mpmmap	; skip if mpm system
	lhld	wbaddr
	lxi	d,60h
	dad	d
	pchl		; goto bios
mpmmap:	lhld	mpmtab
	lxi	d,10	; offset to disk parm tab
	dad	d
	mov	e,m
	inx	h
	mov	d,m
	dcx	d	; point to unit+devtype
	xchg		; inot hl
	mov	a,m	; get unit byte
	ret
NACKpol:jmp	0
ACKpoll:jmp	0
SENDNET:jmp	0
RECNET:	jmp	0
NETadr:	.word	0	; address of NETcom in BIOS
	.page
;------------------
; Set up jumps to BIOS
MAIN:			; MAIN BODY
	lhld	wbadr	; bios vector
	shld	wbaddr
	mvi	c,12	; get version function
	call	bdos
	mov	a,h	; see if mpm
	cpi	1
	mov	a,l	; use cpm indicator
	jnz	gotsys	; skip if not mpm
	mvi	a,1
	sta	DD2+4	; extent mask = 1 for mpm
	mvi	a,0ffh
	lhld	wbaddr	; adjust bios vector
	inx	h
	mov	e,m
	inx	h
	mov	d,m
	sded	wbaddr
gotsys:	sta	sysflg	; save system indicator
	lbcd	wbaddr	; get wboot address
	lxi	H,69h
	dad	B
	inx	H	; get address from BIOS
	mov	E,M
	inx	H
	mov	D,M
	inx	D	; skip NETmsg
	sded	NETadr	; store address in NETadr
	lxi	H,72h	; setup NACKpoll
	dad	B
	shld	NACKpoll+1
	lxi	H,75h	; setup ACKpoll
	dad	B
	shld	ACKpoll+1
	lxi	H,6Ch	; setup SENDNET
	dad	B
	shld	SENDNET+1
	lxi	H,6Fh	; setup RECNET
	dad	B
	shld	RECNET+1
;------------------
; Set stack pointer
; Get the number of command line characters in numchr
; Jump to 'info' if no command line
	lhld	buffer
	mov	A,M
	sta	numchr	; save number of com chrs
	cpi 	0
	jz	info	; print help if no comm line
	cpi	1
	jz	info	; print help if only carr ret
;
; Assume command line is present
	jmp 	getlog	; continue past INFO
;------------------
; NO COMMAND LINE - PRINT OUT CURRENT ASSIGNMENT INFO
;------------------
INFO:	
	lxi	D,LOGmsg
	mvi	C,9
	call	BDOS	; print LOG-on message
	lxi	D,infomsg
	mvi	C,9
	call	BDOS	; print out info message
prtMAP:
	call	getque	; freeze disk if mpm
	lda	logbyte	; get logical
	mov	C,A	; get logical in C
	call	seldsk	;
	call	putque	; ok to free disk now
	call	cpmMAP	; physical is in A
	mov	D,A	; get physical in D
	call	infout	; output the data
	lda	logbyte	; retrieve logical
	cpi	3
	jz	RETccp  ; wboot if all are printed
	inr	A	; increm to next logical
	sta	logbyte	; store it
	jmp	prtMAP	; loop until finished printing
;------------------
;		Subroutine: infout
;This is called by 'info' and will print out the
;logical to physical map information.
;Regs in:logical unit in A (0,1,2, or 3)
;	 physical unit in D
;Regs out: none
;
infout:
	push	D	; save physical byte
	lda	logbyte	; get logica unit in A
	adi	'A'	; 0,1,2,3 = A,B,C,D
	mov	E,A	; put logical in E
	mvi	C,2
	call	BDOS	; print logical unit

; Find and save the physical device
	pop	D	; get back physical byte
	lda	sysflg
	ora	a
	jp	xmpm	; skip if not mpm
	mov	a,d
	cpi	0ffh	; unassigned ?
	jnz	xmpm	; skip if not
	lxi	d,unamsg
	mvi	c,9
	call	bdos
	ret		; done with entry
xmpm:	mov	A,D
	push	D	; save for finding unit#
	rlc
	rlc
	ani	03h	; physical name
	sta	physname
; Print out the appropriate message for the phys name.
	cpi	0	; 0 is single density
	lxi	D,singmsg
	jz	prntphys
	cpi	1	; 1 is double density
	lxi	D,doubmsg
	jz	prntphys
	cpi	2	; 2 is hard disk
	lxi	D,hdskmsg 
	jz	prtNAME 
	cpi	3	; 3 is network
	lxi	D,netmsg
	jz	prtNAME
;
; Assignment being listed is either a single or double
; density floppy. These have sizes of 241K or 482K.
prntphys:
	mvi	C,9
	call	BDOS	; output physical name
	pop	D	; restore the physical word
	mov	A,D	; load the unit number
	ani	3Fh	; mask unneeded bits for unit #
	call	cvtbcd	; make unit number decimal
	call	prtbyt	; print out unit number
	mvi	A,' '
	call	CONOUT	; and move over one space.
	lxi	D,SIZmsg
	mvi	C,9
	call	BDOS	  ; print size header
	lda	physname  ; get the phys name
	lxi	D,msg241K ; Load sing dens size.
	cpi	00   	  ; sing dens?
	jz	prtSIZ	  ; Yes. Print the size.
	lxi	D,msg482K ; Load doub dens size.
	cpi	01	  ; doub dens?
	jz	prtSIZ
	lxi 	D,Qmsg	  ; size = ???
	jmp	prtSIZ	  ; and print the ??? for size.

; Assignment being listed is either a network or hard
; disk. These have sizes of 256K,512K,1M,2M,4M, or 8M.
prtNAME:
	mvi	C,9
	call	BDOS	; Print out assignment type
	pop	D	; Rebalance the stack
	call	cpmMAP	; Print out assignment name
;
; Name in different place in CP/M 1.4 and 2.2
	lda	sysflg
	ora	A
	jrz	..aCPM1	; skip if cpm1.4
	lxi	D,8
	dsbc	D
	jmpr	..aboth
..aCPM1:lda	logbyte	; HL = BIOS MAP address
	mov	B,A
	mvi	A,36
	sub	B	; A = 36 - logical unit number
	mvi	B,00
	mov	C,A	; BC = 36 - logical unit number	    
	dad	B	    ; HL = BIOS MAP Name addr
..aboth:lxi	D,NAMEstr   ; DE = Destination
	lxi	B,8	    ; BC = num of bytes 
	LDIR		    ; Copy bytes from BIOS
;
	mvi	C,9
	lxi	D,NAMEstr
	call	BDOS	; print the assignment name
	mvi	C,9
	lxi	D,SIZmsg
	call	BDOS	; print size header
	call	cpmMAP
	inx	H
	inx	H
	inx	H
;
; Size computed differently in CP/M 1.4 and 2.2
	lda	sysflg
	ora	A
	jrz	..bCPM1	; skip if cpm1.4
	mov	B,M	; use BSH and DSM to find size
	inx	H
	inx	H
	inx	H
	inx	H
	mov	A,M
	ora	A
..1:	jrz	..2
	inr	B
	srar	A
	jmpr	..1
..2:	mov	A,B
	jmpr	..bboth
..bCPM1:mov	A,M	; A = log of sectors per block
..bboth:sui	2	; A = size of unit (1-6)
	push	PSW
	call	cpmMAP	; check for fixed heads
	ani	3Fh	; check unit no. = 63
	cpi	63
	jrnz	..notf	; jump if not fixed heads
	pop	PSW
	lxi	D,msg136K
	jmpr	prtSIZ
..notf:	pop	PSW
	cpi	1
	lxi	D,msg256K
	jrz	prtSIZ
	cpi	2
	lxi	D,msg512K
	jrz	prtSIZ
	cpi	3
	lxi	D,msg1M
	jrz	prtSIZ
	cpi	4
	lxi	D,msg2M
	jrz	prtSIZ
	cpi	5
	lxi	D,msg4M
	jrz	prtSIZ
	cpi	6
	lxi	D,msg8M
	jrz	prtSIZ

; If all tries for assigning a size have failed,
; a question mark is printed out in its stead.

	lxi	D,Qmsg
prtSIZ:	mvi	C,9
	call	BDOS

fin:	
	lxi	D,crlf
	mvi	C,9
	call	BDOS	; print car ret & lf

	ret

;------------------
; COMMAND LINE EXISTS - READ THE COMMAND LINE
;------------------
; TRY TO MAKE AN ABSOLUTE ASSIGNMENT, i.e. ASSIGN b d0
;------------------
; Call 'getchr' and fetch a non-blank character.
; Test for A,B,C,D (proper CP/M drive selects).
; Jump to 'error' if unacceptable entry found
GETLOG:
	cAll	getque	; freeze disk if mpm
..1:	call	getchr
	cpi	' '	; is it a blank?
	jrz	..1	; loop until chr found
;
	cpi	'A'
	mvi	C,0	; A is logical unit 0
	jz	stolog
	cpi	'B'
	mvi	C,1	; B is logical unit 1
	jz	stolog
	cpi	'C'
	mvi	C,2	; C is logical unit 2
	jz	stolog
	cpi	'D'
	mvi	C,3	; D is logical unit 3
	jz	stolog
	jmp	error	; all tests failed
;
; mpm disk que stuff
;
getque:	lda	sysflg
	ora	a
	jp	..1	;skip if not mpm
	mvi	a,0ffh
	sta	queflg
	mvi	c,87h	; open que function
	lxi	d,mpmq
	call	bdos
	mvi	c,89h	; read que function
	lxi	d,mpmq
	call	bdos
..1:	ret
putque:	lda	queflg
	ora	a
	rz
	xra	a	; reset flg
	sta	queflg
	mvi	c,8bh	; write que function
	lxi	d,mpmq
	call	bdos
	ret
queflg:	.byte	0
mpmq:	.byte	0,0
	.word	qbuff
	.ascii	'MXDisk  '
qbuff:	.byte	0,0

stolog:
	mov	A,C	; get logname in reg A
	sta	logname	; store it
;
; A logical unit name has been read from the com line.
; Eat the blank(s) expected before the next character.
; Test for S or D. (proper density selections)
; Jump to READNAME if entry found is unacceptable.
GETPHYS:

..1:	call	getchr
	cpi	' '
	jrz	..1	; fetch a non-blank character

	lhld	buffer
	shld	NAMEaddr ; prospective name address
	push	PSW
	lda	chrcnt
	sta	namchr	; prospective num of name chrs
	pop	PSW	

	cpi	'S'
	mvi	C,00000000b
	jz	stophys
	cpi	'D'
	mvi	C,01000000b
	jz	stophys
	jmp	READNAME	; all tests failed,
				; try to read a name.
stophys:
	mov	A,C	; get physname in acc
 	sta	physname; store it
;	
;------------------
; Fetch the next character.
; Test for 0 through 63 (proper unit numbers).
; Jump to READNAME if unacceptabe entry found.
GETUNIT:

	lda	numchr	 ; get no. of chrs 
	lxi	H,chrcnt ; no. of com chrs read
	sub	M	 ; A=no. of chrs left to read

	call	makDBYT	 ; turn chrs into byte
	cpi	64	; past unit number 63?
	jnc	READNAME	; Yes. MAYBE ITS A NAME
stounit:
	sta	unitnum	; save for later use

;------------------
; The absolute Logical name, Physcial unit, and unit
; number are have been compiled from the command line.
; MAPbyte is created by <or>ing the physcial unit and 
; unit number. 
MAKEBYTE:

	lda	unitnum
	mov	C,A	; unitnum in C
	lda	physname; physname in A
	ora	C	; A <or> C
	sta	MAPbyte	; store physical byte
	lda	logname
	mov	C,A	; get logical in A for seldisk
	call	seldsk	; select logical unit
	call	cpmMAP	; BIOS 'MAP' address in HL
;----------
;Now the appropriate BIOS location will be found for
;MAPbyte. The appropriate 8 bytes for CP/M compata-
;bility will have their address loaded into DE.
;
; HL = BIOS Map address (Provided by cpmMAP in BIOS)
FINDMAP:
	lda	sysflg
	ora	a
	jp	godo	; skip if not mpm
	call	putque	; free disk
	lxi	h,1
	lda	logname	; disk number
	ora	A
	jrz	..1	; skip if done
..2:	dad	h	; next drive vector
	dcr	a
	jrnz	..2
..1:	xchg		; vector to de
	mvi	c,25h	; reset disk function
	call	bdos
	ora	a
	jrz	godox	; skip if it reset ok
	lxi	d,bsymes
	mvi	c,9	; print msg function
	call	bdos
	jmp	retCCP	; and exit
bsymes:	.ascii	[cr][lf]' rejected - disk busy.$'
godox:	call	getque	; freeze disk again
godo:	lda	sysflg
	ora	a
	jrz	cpm14	; skip if cpm 1.4
	mvi	a,0	; scan for duplicate drv
..ckit:	push	psw	; save curr drv #
	mov	c,a
	call	seldsk	; select a drive
	mov	a,h
	ora	l	; error ?
	jrz	..nodk	; skip if select error
	call	cpmmap	; get map byte address
	lda	MAPbyte	; get our map byte
	cmp	m	; same ?
	jrnz	..nodk	; skip if not
	lxi	d,nono	; duplicate assign error
	mvi	c,9
	call	bdos	; print error
	jmp	retccp	; exit
..nodk:	pop	psw	; get last drv
	inr	a	; next drive
	cpi	16	; past end ?
	jrnz	..ckit	; loop if not all checked
	jmpr	cpm14
nono:	.ascii	[cr][lf]' REJECTED -'
	.ascii	' unit already assigned!$'
cpm14:	lda	logname
	mov	C,A	; get logical in A for seldisk
	call	seldsk	; select logical unit
	call	cpmMAP	; BIOS 'MAP' address in HL
	lda	MAPbyte
	ani	0C0h
	lxi	H,Param
	cpi	00000000b; single density
	jrz	..chkver
	lxi	H,Param+2
	cpi	01000000b; double density
	jrz	..chkver
;
; Network or hard disk - compute Param table entry
	lda	MAPbyte	; don't allow assignment
	ani	00111111b; to unit 0
	jz	error
	cpi	63	; check for unit 63 (fixed hds)
	jrnz	..notfix
	mvi	A,7	; fixed heads = size 7
	sta	size
..notfix:
	lda	size
	rlc
	mvi	D,0
	mov	E,A
	dad	D
;
; Overlay 8 bytes for CP/M 1.4, and 15 bytes for 2.2
..chkver:
	lxi	B,8	; length of 1.4 overlay string
	lda	sysflg
	ora	A
	jrz	..overlay	; skip if cpm1.4
	lxi	B,15	; length of 2.2 overlay string
	lxi	D,Param2-Param; move to other table
	dad	D
;
; Overlay the map byte and the disk parameters
..overlay:
	mov	E,M	; get pointer to disk params
	inx	H
	mov	D,M
	push	D
	ora	A
	jrz	..ok	; jump if CP/M 1.4
;
» Makå surå thaô thå CP/M 2.2 allocatioî vectoò ió
; large enough for this disk
	xchg		; HL = addr of parm table
	lxi	D,5
	dad	D	; HL = addr of (block cnt-1)
	mov	E,M
	inx	H
	mov	D,M	; DE = block count -1
	inx	D	; DE = block count

	mov	A,E
	srar	D
	rar
	srar	D
	rar
	srar	D
	rar
	mov	E,A	; DE = block count / 8

	push	D	; save block count
	call	cpmMAP	; get addr of map byte
	lxi	D,10
	ora	A
	dsbc	D	; HL = addr of lenalv
	mov	E,M
	inx	H
	mov	D,M	; DE = lenalv
	pop	H	; HL = block count/8
	ora	A
	dsbc	D
	jrc	..ok
	jrz	..ok
;
; Not enough room in BIOS
	lxi	D,noroom
	mvi	C,9
	call	BDOS
	jmp	retCCP
;
; Finally, we can get down to business and overlay
..ok:
	call	cpmMAP	; get pointer to overlay area
	pop	D
	xchg		; HL = source, DE = destination
	lda	MAPbyte
	stax	D
	inx	D
	ldir
;
; Compute address of name in BIOS
	call	cpmMAP
	lda	sysflg
	ora	A
	jrz	..CPM1	; skip if cpm1.4
	lxi	D,8
	dsbc	D
	jmpr	..both
..CPM1:
	lda	logname
	mov	B,A
	mvi	A,36
	sub	B
	mvi	B,0
	mov	C,A
	dad	B
;
; Overlay name in BIOS
..both:
	xchg
	lxi	H,BIOSbuf
	lxi	B,8
	ldir
	jmp	exit
;----------
; Pointer to disk parameters
Param:
	.word	SD1
	.word	DD1
	.word	H1DSKqM
	.word	H1DSKhM
	.word	H1DSK1M
	.word	H1DSK2M
	.word	H1DSK4M
	.word	H1DSK4M
	.word	H1DSKf
Param2:
	.word	SD2
	.word	DD2
	.word	H2DSKqM
	.word	H2DSKhM
	.word	H2DSK1M
	.word	H2DSK2M
	.word	H2DSK4M
	.word	H2DSK8M
	.word	H2DSKf
;------------------
;  CP/M 1.4 disk parameters
;
; (1) physical device and unit number
; (2) sectors per track
; (3) maximum directory number
; (4) logarithm of sectors per block ( 3 means 2**3 )
; (5) sectors per block minus one
; (6) number of blocks minus one
; (7) bits indicate blocks for op sys
; (8) number of op sys tracks
; (9) 4Eh for CP/M sector mapping, 0C0h otherwise
;               (2) (3) (4) (5) (6) (7)  (8) (9)
SD1:	.byte	 26, 63,  3,  7,242,0C0h,  2,4Eh
DD1:	.byte    52,127,  4, 15,242,0C0h,  2,0Ch
H1DSKqM:.byte	128, 63,  3,  7,255,0C0h,000,0Ch
H1DSKhM:.byte	128, 95,  4, 15,255,0C0h,000,0Ch
H1DSK1M:.byte	128,127,  5, 31,255,080h,000,0Ch
H1DSK2M:.byte	128,191,  6, 63,255,080h,000,0Ch
H1DSK4M:.byte	128,254,  7,127,255,080h,000,0Ch
H1DSKf:	.byte	128, 31,  3,  7,135,080h,000,0Ch
;----------
;  CP/M 2.2 disk parameters
;
SD2:	.word	26	; sectors per track
	.byte	3,7,0	; blk shift, blk and ext mask
	.word	242	; number of blocks - 1
	.word	63	; maximum directory entry
	.byte	0C0h,0	; bits indicate directory blks
	.word	16	; check vectors size
	.word	2	; number of op sys tracks

DD2:	.word	52	; sectors per track
	.byte	4,15,0	; blk shift, blk and ext mask
	.word	242	; number of blocks - 1
	.word	127	; maximum directory entry
	.byte	0C0h,0	; bits indicate directory blks
	.word	32	; check vectors size
	.word	2	; number of op sys tracks

H2DSKqM:.word	128	; sectors per track
	.byte	3,7,0	; blk shift, blk and ext mask
	.word	255	; number of blocks - 1
	.word	63	; maximum directory entry
	.byte	0C0h,0	; bits indicate directory blks
	.word	0	; check vectors size
	.word	0	; number of op sys tracks

H2DSKhM:.word	128	; sectors per track
	.byte	4,15,1	; blk shift, blk and ext mask
	.word	255	; number of blocks - 1
	.word	127	; maximum directory entry
	.byte	0C0h,0	; bits indicate directory blks
	.word	0	; check vectors size
	.word	0	; number of op sys tracks

H2DSK1M:.word	128	; sectors per track
	.byte	4,15,0	; blk shift, blk and ext mask
	.word	511	; number of blocks - 1
	.word	255	; maximum directory entry
	.byte	0F0h,0	; bits indicate directory blks
	.word	0	; check vectors size
	.word	0	; number of op sys tracks

H2DSK2M:.word	128	; sectors per track
	.byte	4,15,0	; blk shift, blk and ext mask
	.word	1023	; number of blocks - 1
	.word	511	; maximum directory entry
	.byte	0FFh,0	; bits indicate directory blks
	.word	0	; check vectors size
	.word	0	; number of op sys tracks

H2DSK4M:.word	128	; sectors per track
	.byte	4,15,0	; blk shift, blk and ext mask
	.word	2047	; number of blocks - 1
	.word	1023	; maximum directory entry
	.word	0FFFFh	; bits indicate directory blks
	.word	0	; check vectors size
	.word	0	; number of op sys tracks

H2DSK8M:.word	128	; sectors per track
	.byte	5,31,1	; blk shift, blk and ext mask
	.word	2047	; number of blocks - 1
	.word	1023	; maximum directory entry
	.byte	0FFh,0	; bits indicate directory blks
	.word	0	; check vectors size
	.word	0	; number of op sys tracks

H2DSKf: .word	128	; sectors per track
	.byte	3,7,0 	; blk shift, blk and ext mask
	.word	135	; number of blocks - 1
	.word	31	; maximum directory entry
	.byte	080h,0	; bits indicate directory blks
	.word	0	; check vectors size
	.word	0	; number of op sys tracks

;--------------
; ABSOLUTE ASSIGNMENT COULDN'T BE MADE. COMLINE = NAME.
;--------------
; READ IN A COM LINE NAME, GET PASSWORD & CALL BIOS
;--------------
; We assume the rest of the command line holds a name
; since the absolute assignment attempt failed. Back-
; space to the first chr found AFTER the valid logical
; unit name (A,B,C, or D) and  LDIR rest of com line
; into the first eight chrs of a buffer to send to
; the BIOS.
READNAME:
	lda	chrcnt	; character count may change
	mov	C,A
	lhld	NAMEaddr ; see if name has N: or H:
	inx	H
	mov	A,M
	dcx	H
	cpi	':'
	jrnz	..def	; if not default
	mov	A,M	; get disk type (H or N)
	inr	C	; skip past :
	inr	C
	inx	H
	inx	H
	cpi	'H'	; if harddisk, set locnet=0
	jrz	..loc
	cpi	'N'	; if network, set locnet=1
	jrz	..net	; else default
..def:	lda	userno	; default is
	inr	A	; if logged in, locnet=1
	jrz	..loc	; else locnet=0
..net:	mvi	A,1	; handle network case
	jmpr	..1
..loc:	sub	A	; handle local case
..1:	sta	locnet	; store locnet
	lda	numchr	; determine number of chars
	inr	A	; in name
	sub	C
	mov	C,A
	mvi	B,0	; put answer in BC
	lxi	D,BIOSbuf ; DE = address of destination
	LDIR

; The name is in the buffer. Assume the password is
; all blanks (ascii 20's).  Call the BIOS and try to
; convert the name and blank password into a physical
; name and unit number.

	lxi	H,BIOSbuf ; name and pass address
	call	asnNAME

; B = Mapbyte if accepted or FF if denied.
; C = size or FF if denied.

	mov	A,B
	cpi	0FFh	; assignment denied?
	jnz	savMAP	; No. Its AOK. Make the assnmt.
;----------
; The blank password assignment was denied.
; Ask for the password. Transfer the password from 
; the console buffer to the password buffer.
getPAS:	
	lxi	D,PASSmsg
	mvi	C,9	; BDOS print function
	call	BDOS	; print 'Password?' msg
	lxi	H,PASSbuf ; get password address
	mvi	B,0	; initialize chr counter
chrget:	push	H
	push	B
	call	CONIN
	pop	B
	pop	H
	cpi	backsp	; backspace entered?
	jrnz	..del	; No. Check for delete.
	mov	A,B
	cpi	0	; All characters deleted?
	jrz	chrget	; Yes. Fetch a character.
	dcr	B	; No. Decr chr counter,
	dcx	H	; backstep to last addr,
	jmpr	chrget	; and fetch a character.

..del:	cpi	delete	; delete entered?
	jrnz	..crrtn	; No. Check for carriage ret
	mov	A,B
	cpi	0	; All characters deleted?
	jrz	chrget	; Yes. Fetch a character.
	dcr	B	; No. Decr char counter
	dcx	H	; and backstep to last addr
	jmpr	chrget	; then fetch a character.
	
..crrtn:cpi	cr	; car ret entered?
	jrz	BIOScall; Yes. Attempt the assignment.
			; No. check for non-prtng chrs.
	cpi	3	; 3 is Control-C.
	jz	retCCP	; Abort if Control-C.
	cpi	20h	; Is chr a non-printing chr?
	jc 	chrget	; Ignore non-printing chrs.
; Valid password chr found. Load it into PASSbuf.
	mov	M,A	; Put valid chr into password.
	inx	H	; Get to next PASS buffer addr
	inr	B	; Increment chr counter.
	mov	A,B
	cpi	7	; 7 chrs entered yet?
	jc	chrget	; No. fetch a character.
	push	H
	push	B
	mvi	A,07	; Yes. Ring the bell.
	call	CONOUT
	pop	B
	pop	H
	dcr	B	; Decrement the chr counter.
	dcx	H	; Move back to sixth space
	jmp	chrget	; and get the next chr.

; Name and password obtained.  Call the BIOS and
; convert the name and password into a physical
; name and unit number.
BIOScall:
	lxi	D,crlf
	mvi	C,9
	call	BDOS	  ; space down a line
	lxi	H,BIOSbuf ; name and pass address
	call	asnNAME

; B = Mapbyte if accepted or FF if denied.
; C = size or FF if denied.

	mov	A,B
	cpi	0FFh	; assignment denied?
	jrnz	savMAP	; No. Its AOK.

	lxi	D,DENIEDmsg
	mvi	C,9	; BDOS print function
	call	BDOS	; Print the denied msg
	lda	retries	; How many password tries?
	inr	A	; A = num of pass retries.
	cpi	maxtries; Last allowable time?
	jz	retCCP	; Yes. Exit to the CCP
	sta	retries ; No. Save the retries
	jmp	GETPAS	; and ask again.

savMAP:	mov	A,B
	sta	MAPbyte	; save the Mapbyte.
	mov	A,C
	sta	size	; save the units' size
 
	lda	logname
	mov	C,A	; get logical in A for seldisk
	call	seldsk	; select logical unit
	call	cpmMAP	; MAP address in HL
	jmp	FINDMAP ; Map address found. Now select
			; appropriate size string for
			; the unit.
;-----------
; END OF NAME,PASSWORD, AND BIOS-CALL ROUTINES
;-----------

;-----------
; Utility Routines and Subroutines
;-----------
ERROR:	
	lda	chrcnt	; get num of chrs read
	adi	7	; increment past 'ASSIGN '
	push	PSW	; save space-over count in reg A
;
arrow:	mvi	E,' '
	mvi	C,2
	call	bdos	; print a space
        pop	PSW
	sbi	1
	cpi	0
	push	PSW
	jnz	arrow	; repeat until under error
	pop	PSW
	mvi	E,'^'
	mvi	C,2
	call	bdos	; print arrow under error

	lxi	D,errmsg
	mvi	C,9	; string console output function
	call	bdos	; print 'errmsg' error message
	jmp	retCCP	; jump to Console Command Proc


;------------------
;		Subroutine: getchr
; Regs in: none     Regs out: A (next command chr)
; Increment buffer to next command character
; Test for reading past last character,if true  exit
; read next non-blank character, incrementing chrcnt
getchr:		
	lxi	H,buffer
	inr	M	; increment to next byte
	lxi	H,chrcnt
	inr	M	; increment chr counter
;
	lda	numchr
	inr	A
	cmp	M	; compare with chrcnt
	jz	error	; abort if last chr read
;
	lhld	buffer	; read the command char
	mov	A,M
	ret		; got the chr now go back
;----------
;		Subroutine: makDBYT
; Regs  in:	A=length of console buffer
; Regs out:	A=decimal byte made from console buffer
;Destroyed:	HL,A,B,C
;Make a decimal byte from one or two buffer chrs
makDBYT:
	cpi	1	   ; just one chr?
	mvi	B,0
	lhld	buffer     
	inx	H	   ; get next com chr addr
	jrz	..1	   ; bypass mult if one chr
;multiply first chr by 10 and save
	mov	A,M	; get ascii chr in A
	cpi	'9'+1	; make sure its a number
	jnc	READNAME
	cpi	'0'
	jc	READNAME
	sui	'0'	; make ascii chr numeric
	mov	B,A	; save original number
	rlc
	rlc
	rlc
	mov	C,A
	mov	A,B
	rlc
	add	C
	mov	B,A	; save 10*number in B
	inx	H	; increment to 2nd chr addr
..1:	mov	A,M	; get ascii chr in A
	cpi	'9'+1	; make sure its a number
	jnc	READNAME
	cpi	'0'
	jc	READNAME
	sui	'0'	; make ascii chr numeric
	add	B	; add the two chrs
	ret

;----------
;		Subroutine: prtbyt
; Regs  in:	A=byte to be printed
; Regs out:	none
; Destroyed:	A,B,C
;Print a byte on the console
prtbyt:
	push	PSW	; save the chr
	rlc
	rlc
	rlc
	rlc
	call	prtnbl
	pop	PSW
	call	prtnbl
	ret
prtnbl:	ani	0Fh
	adi	'0'
	cpi	'9'+1
	jc	CONOUT
	adi	'A'-('9'+1)
	jmp	CONOUT


;----------
;		Subroutine: CONOUT
; Print a character on the console
;  Regs in:	A = character to be printed
;  Regs out:	none
;  Destroyed:	C
CONOUT:
	mov	e,a
	mvi	c,2	; console output function
	jmp	bdos

;----------
;		Subroutine: cvtbcd
; Regs  in:	A=byte to be converted
; Regs out:	A=byte, in BCD format
; Destroyed:	B
;Convert binary to BCD
cvtbcd:
	ora	A
	rz
	mov	B,A
	xra	A
..1:	inr	A
	daa
	djnz	..1
	ret

EXIT:	lxi	D,EXITmsg  ; line feed addr in HL
	mvi	C,9	   ; console output function
	call	bdos	 
	jmp	0
;
;---------------------------
; PERFORM ASSIGN for HARDDISK or NETWORK - 6 cases
;
;	0 - single user & local harddisk
;		communicate directly with harddisk
;
;	1 - single user & network harddisk
;		not allowed
;
;	2 - master & local harddisk
;		not allowed
;
;	3 - master & network harddisk
;		communicate to harddisk through BIOS
;
;	4 - slave & local harddisk
;		communicate directly with harddisk
;
;	5 - slave & network harddisk
;		communicate on NETWORK through BIOS
;
asnNAME:
	lda	sysflg
	ora	a
	jp	mpmno	; skip if not mpm
	lxi	d,56h
	lixd	wbaddr
	dadx	d
	pcix		;use bios assign for mpm
mpmno:	lda	userno	; determine which case using
	inr	A	; locnet and userno
	cpi	2
	jrc	..1
	mvi	A,2
..1:	slar	A
	mov	B,A
	lda	locnet
	add	B
	cpi	0	; jump to the appropriate case
	jrz	asnTP0
	cpi	1
	jz	asnTP1
	cpi	2
	jz	asnTP2
	cpi	3
	jz	asnTP3
	cpi	4
	jz	asnTP4
	cpi	5
	jz	asnTP5

	lxi	D,intERR ; internal ERROR
	mvi	C,9
	call	BDOS
	call	BASE
;
asnTP0:
asnTP4:
;----------
; Hard disk unit assignment
;  Regs in:   HL = address of unit name and password
;  Regs out:  BC = unit and size
;	      DE = control byte
HARDas:
	push	H	; save address of name
	mvi	A,assnHARD
	call	CMDhard	; send command to hard disk
	pop	H
	mvi	B,14
	call	SENDHARD; send name and password
	call	RESHARD	; get disk assignment
	lbcd	HARDstat; B = unit, C = size
	lded	HARDstat+2
	mvi	A,80h	; set HARDDISK bits
	ora	B
	mov	B,A
	ret
;----------
; Transmit a block to the hard disk
;  Regs in:   HL = block address
;             B  = byte count
;  Regs out:  none
;  Destroyed: A, BC, HL
SENDHARD:
	mvi	C,HARDP
..1:	in	PIOAD
	bit	3,A
	jrnz	..1
	outi
	jrnz	..1
	ret
;----------
; Receive a block from the hard disk
;  Regs in:   HL = block address
;	      B  = byte count
;  Regs out:  none
;  Destroyed: A, BC, HL
RECHARD:
	mvi	C,HARDP
..1:	in	PIOAD
	bit	4,A
	jrz	..1
	ini
	jrnz	..1
	sub	A	; CP/M wants A = 0
	ret
;----------
; Send a command to the hard disk
;  Regs in:   A = command byte
;  Regs out:  none
;  Destroyed: A, BC, HL
cmdHARD:
	sta	HARDcom
	lxi	B,0	; keep activity count
..1:	in	HARDP	; clear status
	mvi	A,51h	; "request to send"
	out	HARDP
..2:	dcx	B
	mov	A,B
	ora	C
	jz	HDdead	; jump if hard disk is dead
	in	PIOAD	; wait for HDC send
	bit	4,A
	jrz	..2
	in	HARDP	; check if "clear to send"
	cpi	52h
	jrnz	..1	; if not, retry
	lxi	H,HARDcom ; send the command
	mvi	B,8
	jmpr	SENDHARD
;----------
; Receive status info from the hard disk
;  Regs in:   none
;  Regs out:  A = error status
;  Destroyed: A, BC, HL
RESHARD:
	lxi	H,HARDstat
	mvi	B,8
	call	RECHARD
	lda	HARDstat+7
	ora	A
	rz

HDdead:	lxi	D,deadHD ; print error message
	mvi	C,9
	call	BDOS
	call	retCCP
;
asnTP1:
	lxi	D,noNET	; print error message
	mvi	C,9
	call	BDOS
	call	retCCP
;
asnTP2:
	lxi	D,badMST ; print error message
	mvi	C,9
	call	BDOS
	call	retCCP
;
asnTP3:
	lded	NETadr	; move NAME and PASSWORD
	inx	D	; into BIOS area
	lxi	B,14
	di
	ldir
	lhld	NETadr	; load in ASSIGN command
	mvi	M,assnNET
	ei
..1:	mov	A,M	; wait for BIOS to finish
	ora	A
	jrnz	..1
	inx	H	; return status in BC
	mov	C,M
	inx	H
	mov	A,M	; set network bits
	ori	0C0h
	mov	B,A
	inx	H	; DE gets some too
	mov	E,M
	inx	H
	mov	D,M
	ret
;
asnTP5:
	call	NACKpoll ; get ahold of NETWORK
	bit	7,A	 ; check is NETWORK dead
	jrz	NETdead
	mvi	A,0	; send request to master
	lxi	B,15
	lxi	H,NETcom
	call	SENDNET	; through BIOS
	lda	userno	; get response
	lxi	B,4	; from MASTER
	lxi	H,NETres ; through BIOS
	call	RECNET
	bit	7,A	; make sure NETWORK is alive
	jrz	NETdead
	call	ACKpoll ; restart BIOS polling
	lbcd	NETres  ; load results into regs
	lded	NETres+2
	mvi	A,0C0h	; add NETWORK bits
	ora	B
	mov	B,A
	ret

NETdead:lxi	D,deadNEt ; print NETWORK dead
	mvi	C,9
	call	BDOS
	call	retCCP
;------------------
; Data for console output
LOGmsg:	.ascii	[cr][lf]'ASSIGN version '
	.byte	version +'0','.'
	.byte	revision/10+'0',revision@10+'0'
	.ascii	[cr][lf]'$'

errmsg:	.ascii	[cr][lf]'*** SYNTAX ERROR ***'[cr][lf]
	.ascii	[cr][lf]'The general form of the '
	.ascii  'ASSIGN command is:'
	.ascii	[cr][lf]' '
	.ascii	[cr][lf]'ASSIGN [ <disk name> '
	.ascii	'<device name> ]'
	.ascii	[cr][lf]' '
	.ascii	[cr][lf]'where <disk name>    '
	.ascii	'= A,B,C, or D'
	.ascii	[cr][lf]' '
	.ascii	[cr][lf]'      <device name>  = '
	.ascii	'Sn  (single density)'
	.ascii	[cr][lf]'                       '
	.ascii	'Dn  (double density)'
	.ascii	[cr][lf]'                       '
	.ascii	'partition-name'
	.ascii	[cr][lf]' '
	.ascii	[cr][lf]'where n is a unit number '
	.ascii	'from 0 to 7'
	.ascii	[cr][lf]' '
	.ascii	[cr][lf]'For example, the command '
	.ascii	'"ASSIGN B S1" will as-'
	.ascii	[cr][lf]'sign disk B to the single '
	.ascii	'density drive on unit 1.'
	.ascii	[cr][lf]'To see the list of current '
	.ascii	'disk assignments,'
	.ascii	[cr][lf]'enter "ASSIGN" without '
	.ascii	'any arguments.'
	.ascii	[cr][lf]'$'
 
PASSmsg:.ascii	[cr][lf]'Enter Password: $'
 
infomsg:.ascii	[cr][lf]'          '
	.ascii	' Current Disk Assignments '
	.ascii	[cr][lf]'          '
	.ascii	' ------------------------ '
	.ascii	[cr][lf][lf]'$'
space4:	.ascii	'    $'
crlf:	.ascii	[cr][lf]'$'

EXITmsg:.ascii	'Assignment accepted. $'

DENIEDmsg:
	.ascii	'Assignment denied. '
	.ascii	[cr][lf]' $'
doubmsg:.ascii	' - double density, unit $'
singmsg:.ascii	' - single density, unit $'
netmsg:	.ascii	' - network       , $'
unamsg:	.ascii	' - ** unassigned **'[cr][lf]'$'
hdskmsg:.ascii	' - hard disk     , $'
SIZmsg:	.ascii	'    size: $'
msg241K:.ascii	'243K bytes$'	; sing dens disk size
msg482K:.ascii	'486K bytes$'	; doub dens disk size
msg256K:.ascii	'256K bytes$'	; disk partition sizes
msg512K:.ascii	'512K bytes$'
msg1M:	.ascii	'  1M bytes$'
msg2M:	.ascii	'  2M bytes$'
msg4M:	.ascii	'  4M bytes$'
msg8M:	.ascii	'  8M bytes$'
msg136K:.ascii	'136K bytes$'
Qmsg:	.ascii	'   ??? $'
noNET:	.ascii	'Not logged in.$'
badMST: .ascii	'Cannot assign local hard disk.$'
deadNET:.ascii	'Network not operational.$'
deadHD: .ascii	'Hard disk not operational.$'
intERR: .ascii	'Internal error.$'
noroom:	.ascii	'BIOS allocation vector too small.$'
badmsg	==	intERR
	.blkb	60	   ; room for stack
stack:			   ; stack location
	.end

