;	title	'PASSWORD.ASM'
;	page	60
;
;
;		PASSWORD.ASM
;		Version 3.0
;	 By Bo McCormick       8/6/81
;
; This is a program that adds password protection
; to programs. Format:
;
; PASSWORD name_of_file
;
; Then answer the prompt with the password to be
; applied to the program:
;
; Password?  enter password here
;
; If everything goes well, the program will be saved to disk.
; If not, a message is printed and control is passed
; to the CCP.
;
; The good part of this is, when you type in the program
; program name next time, instead of running the program
; right away, the program asks you for the password. If you
; reply with something other than the original password, the
; program doesn't run, and it returns to the ccp.
;
; 2/10/82: Set DMA before open because 1.4 CP/M uses buffer at 80H
; Removed "$" from labels so other assemblers can be used. Fixed
; "offset" to use tpa instead of 100H so it would work with
; modified CP/M.  Added encryption of password so dumping .COM
; file will not reveal it.  (Thanks to author of XYZZY.COM)
; Fixed test of supplied password to check all characters.
; Ted Shapin
;
; 12/10/81: changed ot$pw routine to input password one character
; at a time invisibly from BIOS rather than using BDOS's string input
; function.  This allows password to be typed in without anyone
; seeing what it was that was typed.  Jim Mills
;
;EQUATES
rdchar: equ	1
mesout: equ	9		;BDOS functions
incon:	equ	10
open:	equ	15
close:	equ	16
delete: equ	19
read:	equ	20
write:	equ	21
setdma: equ	26
;
cr	equ	0dh		;ascii values
lf	equ	0ah
eos	equ	'$'
;
boot	equ	0;4200H 	;0 for standard CP/M
				;4200H for ALT. CP/M;
bdos	equ	boot+5
fcb	equ	boot+5ch
defbuf	equ	boot+80h
tpa	equ	boot+100h
stack	equ	tpa		;out of way of tpa
;
	org	tpa

;
;
start:	lxi	h,0		;save stack pointer
	dad	sp		;put stack in hl
	shld	oldstack-offset ;save it
	lxi	sp,stack	;get new stack
;
; stack saved so program can return to CCP without
; intervening warm start.
;
	lda	fcb+9		;get first char of extension
	cpi	' '		;if ' ' then change to .COM
	jz	notype
	cpi	'C'		;If there is an extension,
	jnz	notright	;make sure it's .COM
	lda	fcb+10		;check second letter
	cpi	'O'
	jnz	notright
	lda	fcb+11
	cpi	'M'		;last letter
	jz	iscom		;if it is a COM, then cont.
notright:
	call	endmes		;it's not a com file, so tell
;
	db	cr,lf,'Must be a command (.COM) file'
	db	cr,lf,eos
;
endmes:
	pop	d		;get address of message
	mvi	c,mesout	;PRINT STRING command
	call	bdos		;print error message
;
finish: lhld	oldstack-offset ;get old stack
	sphl			;put it in HL
	ret			;return to CP/M
;
notype: mvi	a,'C'		;if there was space, change
	sta	fcb+9		;to COM
	mvi	a,'O'
	sta	fcb+10
	mvi	a,'M'
	sta	fcb+11
;
iscom:	lxi	d,buffer-offset ;point to where program goes
	mvi	c,setdma	;SET DMA command
	push	d		;save it
	call	bdos		;and tell CP/M
	mvi	a,0		;zero record count
	sta	fcb+32
	mvi	c,open		;OPEN file command
	lxi	d,fcb		;load address of FCB in DE
	call	bdos		;Open file
	inr	a		;successful?
	jnz	openok		;if so, then continue
	call	endmes		;if not, then tell
;
	db	cr,lf,'Cannot open file',cr,lf,eos
;
openok: pop	d		;get starting dma back
rloop:	mvi	c,setdma	;and set it in loop
	push	d		;save it
	call	bdos
	lxi	d,fcb		;point to FCB
	mvi	c,read		;READ sector command
	call	bdos		;do it
	pop	d		;get DMA address back
	ana	a		;EOF?
	jnz	doneread	;if so, then ask for password
	lxi	h,80h		;length of sector
	dad	d		;bump DMA
	xchg			;put new address in DE
	jmp	rloop		;and read some more
;
doneread:
	xchg			;dma ==> hl
	shld	endprog-offset	;save last address
gpasag	call	getpas		;print password message
;
pasmes	db	'Password? ',eos
;
getpas	pop	d		;get address of message
	mvi	c,mesout	;PRINT STRING function
	call	bdos		;print it
	lxi	d,defbuf	;point to default buffer
	mvi	a,8		;tell CP/M max chars
	stax	d		;put it there
	mvi	c,incon 	;READ LINE command
	call	bdos		;do it
	lxi	h,defbuf+1	;point to length
	lxi	d,password-offset	;point to storage
	mov	a,m		;get length
	ana	a		;set flags
	jz	gpasag		;if 0 then ask again
	inr	a		;plus 1 for length byte
	mov	b,a		;put length in B
	xra	b		;cancel first xra in loop
	mov	m,a		;and put it back
mploop	mov	a,m		;get char
	xra	b		;encrypt it
	stax	d		;save it
	inx	h		;increment pointer
	inx	d		;  "	      "
	dcr	b		;decrement length
	jnz	mploop		;if not zero, then next char
	xra	a		;zero a
	sta	fcb+12		;zero bytes in FCB
	sta	fcb+14
	sta	fcb+32
	mvi	c,open		;OPEN file command
	lxi	d,fcb		;point to FCB
	call	bdos		;open the file
	lxi	d,nstart	;point to new program start
;
	push	d
wloop1	pop	d		;get DMA
	push	d		;put it back on stack
	mvi	c,setdma	;SET DMA command
	call	bdos		;tell CP/M
	lxi	d,fcb		;point to FCB
	mvi	c,write 	;WRITE SECTOR command
	call	bdos		;do it
	pop	h		;get DMA address from stack
	lxi	d,80h		;length of sector
	dad	d		;HL has new DMA
	push	h		;put it on stack
	mov	a,h		;this is to get 2's complement
	cma			;of address. We are subtracting
	mov	d,a		;the current address from the
	mov	a,l		;high address. If the high byte
	cma			;<1 , we are done
	mov	e,a		;
	inx	d		;Now 2's comp. of address in DE
	lhld	endprog-offset	;get ending address
	dad	d		;Subtract (add 2's comp)
	mov	a,h		;get high byte
	inr	a		;is it FF (-1)?
	ana	a		;set flags
	jnz	wloop1		;if not, write another sector
;
	mvi	c,close 	;That's it. Close the file
	lxi	d,fcb		;point to FCB
	call	bdos		;do it
	jmp	finish		;goto finish
;
;
nstart:
offset	equ	tpa-nstart
;
;	%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;	%% WARNING -					    %%
;	%% From now on, all labels are in		    %%
;	%% the form:					    %%
;	%%	LABEL	EQU  $+OFFSET			    %%
;	%%  This is to allow the program to run at100H	    %%
;	%% when it is saved by the earlier portion.	    %%
;	%%  ALL new labels added MUST be in the form	    %%
;	%% LABEL   EQU	$+OFFSET for this program to work   %%
;	%% properly.					    %%
;	%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
;
;This is portion of the program is placed at the beginning
;of the program to be PASSWORDed. When it is executed, it will
;ask for a password. If the password is incorrect, the program
;warm starts. If the password is correct, the program is moved
;to the TPA and executed.
;
	lxi	h,0		;save stack pointer
	dad	sp		;stack is in HL
	shld	oldstack	;save it
	lxi	sp,stack	;get new stack
	call	otpw		;print password message
;
	db	cr,lf,'Password? '
	db	eos
;
otpw	equ	$+offset
	pop	d		;get address of message
	mvi	c,mesout	;PRINT STRING command
	call	bdos		;print it

	lxi	d,newbuf+1	;point to storage area
	xra	a
	mov	b,a		;init counter
	stax	d		;# of chars typed (for compare)
	inx	d		;and point to string
;
; we don't want to allow 'lookers' to see the password, so call
; bios instead of bdos to prevent echo.  also allows control chars.
;
	lhld	boot+1		;get addr of warm start routine
	push	d
	lxi	d,6		;+ offset to kbd input routine
	dad	d
	pop	d
	shld	bconin+1	;now init'd to bios conin

otpw1	equ	$+offset
	push	b		;save counter..
	push	d		;..& pointer
bconin	equ	$+offset
	call	$-$		;to be filled in by above routine
	pop	d		;restore pointer..
	pop	b		;..& counter
	stax	d		;store char
	inx	d		;bump pointer..
	inr	b		;..& counter
	cpi	cr
	jnz	otpw1		;loop until cr typed
; B has the length of password furnished + 1 for count.
	lxi	h,password	;point to actual password
	lxi	d,newbuf+1	;point to user's input
	xra	a		;this 0 xra with length in B
	stax	d		;should equal count in passwd
;
; first char compared is a count of # of chars in password,
; followed by password itself.
;
clp	equ	$+offset
	ldax	d		;get char
	xra	b		;encrypt it
	cmp	m		;are they the same?
	jnz	boot		;if not, restart
	inx	h		;point to next characters
	inx	d		;  "	"  "	    "
	dcr	b		;decrement length
	jnz	clp		;if not done, then loop
;
; Now we move a segment of code to a part of the default
; buffer. This segment moves the actual program down to the
; TPA
;
	lxi	h,nmv		;point to code
	lxi	d,defbuf+20h	;point to new postion
	mvi	b,nmlen ;length
;
move	equ	$+offset
	mov	a,m		;get byte
	stax	d		;save it
	inx	d		;point to next addresses
	inx	h		;  "   "    "	   "
	dcr	b		;decrement length
	jnz	move		;if not done, loop
	jmp	defbuf+20h	;go to segment
;
nmv	equ	$+offset	;segment that gets moved
	lhld	oldstack	;get stack pointer
	push	h		;save it on stack
	lxi	h,buffer	;get start of actual program
	mov	a,h		;We have to compute the length
	cma			;and because X-Y equals
	mov	d,a		;X + Two's complent(Y), we have
	mov	a,l		;to find the 2's comp. of the
	cma			;first address
	mov	e,a		;
	inx	d		;Y is in DE
	lhld	endprog ;get last address
	dad	d		;subtract (add 2's comp)
	mov	b,h		;put length in BC
	mov	c,l		; "    "     "	"
	lxi	d,tpa		;point to TPA
	lxi	h,buffer	;point to first address
nmlp	equ	defbuf+20h+$+offset-nmv
	mov	a,m		;get byte
	stax	d		;save byte
	inx	h		;increment address
	inx	d		;    "	      "
	dcx	b		;decrement length
	mov	a,b		;check for zero left
	ora	c		;Are we done?
	jnz	nmlp		;if not, loop some more
	pop	h		;get stack from stack
	sphl			;put stack in SP
	jmp	tpa		;run program
;
nmlen	equ	$+offset-nmv	;length of segment
;
;
password	equ	$+offset ;password storage
	db	0,'	    '
;
;12/10/81 changed to allow use of tbuf as input area
;
newbuf	equ	$+offset	;Users input buffer
	db	10H,0,' 	       '
;
oldstack	equ	$+offset ;place for stack
	ds	2
;
endprog equ	$+offset ;place for address
	ds	2
;
buffer	equ	$+offset	;where actual program goes
	end
