	.TITLE keygen - Program to generate encrypted macro source
	.IDENT /14/

.REM |
*+	keygen - Program to generate encrypted macro source

	File:keygen.mac
	Version: 14
	Author:
	Last Edit: 7-MAY-1985 22:56:03 
	Last Update: 7-May-1985 22:56:58 
		     fix psect attributes


*-
|
	.sbttl Documentation

.rem |
	KEYGEN accepts plain text ASCII from an input file, encrypts it
	and produces an output MACRO source file. The output file is suitable
	for assembly. 

	Input file format is one of - 
		LABEL:	Text
		LABEL::	text
		; a comment		
		* a MACRO-11 source line
	where <label> is an optional statement label followed by one or two
	colons, then one or more spaces/tabs, and <text> is the text to be 
	encrypted. Whitespace is ignored at beginning or end of line, and
	between LABEL and text.  
	Comments are allowed, and propogated directly to the output file. 
	Lines beginning with an asterisk have the asterisk removed, and 
	the remainder output unmodified. This allows MACRO statements
	to be passed through to the output file.

	Output file format is -
		LABEL:	.word	length
			.byte	nnn
			.word 	ww1,ww2,...
			.byte  	mmm
	where each input line (other than comments) is encrypted, and converted
	into appropriate .byte and .word directives. The label field is 
	propogated without change, with a tab between the label and the
	first storage directive. The length of the string in bytes is saved
	in the first word (at LABEL). Comments in the input file are simply 
	copied to the output. A command switch will also cause the plain text 
	to be inserted in the output file as a comment. 

	Command Syntax --

	Prompt is KGN> (KeyGeN). May be run installed, as ...KGN. 

	KGN>outfile[/osw]=infile[/isw]

	Switches are specific to either input or output file, and must appear
	with the appropriate file. Both input and output file are required 
	(may be ti:).

	Switches	

	Output switches:

	/CM = include plain text as comment in output  (default = /-CM)
	/AP = append to existing output file (default = /-ap - create new
		output file) 
|

	.LIBRARY /LB:[1,1]GLMAC.MLB/
	.MCALL	push,pop,call,return
	.MCALL GCMLB$,GCML$,CSI$,CSI$1,CSI$2
	.MCALL	QIOW$,DIR$,QIOW$S,EXST$S
	.MCALL FSRSZ$,FDBDF$,fdat$a,FINIT$,NBOFF$,NMBLK$,FDOP$A
	.MCALL	fdrc$a
	.MCALL	open$a, open$w, open$r, get$, put$, close$
	.MCALL FINIT$,CSI$SW,CSI$SV,CSI$ND 

	.sbttl constants
cefn = 1		; efn for command input
recsiz = 132.		; record buffer size for in and out files
	; define some useful ascii characters
tabch = 11	; tab character
spcch = 40	; space character
semich = 73	; semicolon
colnch = 72	; colon
commach = 54	; comma
asterch = 52	; asterisk
	;
	; proflg keeps command line switches 
	;
p.ap	=	1	; 1->append output to existing file
p.cm	=	2	; 1->include plain text as comment in output

	.SBTTL LOCAL DATA
exstat:	.word	0	; cumulative exit status code
proflg:	.word	0	; command line flags
txtbeg:	.word	0	; point to beginning of text field
txtnd:	.word 	0	; points to end of text in fibuf
txtlen:	.word	0	; length of text field
foptr:	.word	0	; points to temp end of fobuf (next avail)
argblk:	.blkw	6	; argument block for edmsg
inbuf:	.blkb	132. 	; command input buffer
fibuf:	.blkb	recsiz	; file input record buffer
fobuf:	.blkb	recsiz	; file output record buffer

	.SBTTL MESSAGES

	.nlist me
	.nlist meb
	.PSECT DATA
ERM1:	.asciz	/KGN -- Error reading command line/<7>
ERM2:	.ASCIZ 	/KGN -- Syntax Error/<7>
ERM3:	.asciz	/KGN -- Input File Open Failure/<7>
ERM4:	.asciz	/KGN -- Creating New Output File/
ERM5:	.ASCIZ 	/KGN -- Input Read Error!/<7>
ERM6:	.asciz	/KGN -- Output File Open Failure/<7>
ERM7:	.ASCIz	/KGN -- Input File Close Error/
ERM8:	.ASCIz	/KGN -- Output File Close Error/
ERM11:	.ASCIZ	/KGN -- Output record too long - truncated/
ERM12:	.asciz	/KGN -- Output Error!/<7>

evnstr:	.ascii	<tabch>/.even/		; set each output line on even
					; address
evnlen = .-evnstr
bytstr:	.ascii	<tabch>/.byte/<tabch>	; first part of byte command
bytlen = .-bytstr
;
; EDMSG control strings
;
str1:	.asciz	/.word %Q/		; for text length

	.even
	.psect

	.SBTTL DPBS	

QIODPB:	qiow$ io.wlb,6,1		; general use message output

	.SBTTL CSI	


; Comand line macros and data blocks

	.PSECT DATA,D,gbl,RW
GCLBLK:	GCMLB$	1,KGN,,5
	CSI$
	.EVEN
CSIBLK:	.BLKB	C.SIZE	
	.EVEN

oswtab:	csi$sw CM,p.cm,proflg,SET,NEG
	csi$sw AP,p.ap,proflg,SET,NEG
	csi$nd

	.sbttl file io

;
; reserve space for three files (in, out, command in)
;      command in is dealt with by CSI

	FSRSZ$ 3

; 
; define input file
;
dfin:	nmblk$  keys,txt
infdb:	fdbdf$
	fdat$a	R.VAR,FD.CR
	fdrc$a	,fibuf,recsiz
	fdop$a	1,csiblk+c.dsds,dfin
;
; define output file
;
dfout:	nmblk$	keys,mac
outfdb:	fdbdf$
	fdat$a	R.VAR,FD.CR
	fdrc$a	,fobuf,recsiz
	fdop$a	2,csiblk+c.dsds,dfout

	.SBTTL ERR - Print (error) message to TI:


.REM |
	ERR is the error output subroutine. It takes an exit status
	parameter. The max of this value and the current EXSTAT value
	is saved in EXSTAT. Always returns Carry SET.
	QIO error results in immediate exit with status of 4.

	CALL:
		JSR R5 xERR
		.WORD exstat
		.WORD <message address>

	INPUT:

	OUTPUT:
		Error message printed to TI:
		returns CARRY SET

	REGISTERS:
		all are preserved
|

ERR:	
	push	<r0>
	mov	(r5)+, r0		; get exit status
	cmp	r0, exstat		
	blos	err1
	mov	r0, exstat		; save new exit status
ERR1:
	mov	(r5)+, r0		; r0 -> message, adjust return PC
	mov	r0, QIODPB+Q.IOPL	; msg pointer to DPB
10$:	tstb	(r0)+			; find length of message
	bne	10$
	sub	qiodpb+q.iopl, r0	; calculate length
	mov	r0, qiodpb+q.iopl+2	; length to DPB
	dir$	#qiodpb			; send message
	bcc	20$
	exst$s	#4			; die on errors 
20$:	pop	<r0>
	sec				; set carry
	return	r5

	.SBTTL PUTOUT -- Put buffer to output file
.REM |
	PUTOUT writes a record to output buffer. Address and length
	are supplied as input params. 	
	
	CALL
		jsr pc, PUTOUT
	INPUT	
		r1 -> buffer to be output
		r2 =  0 --> call slen to find length of asciz buffer
		   <> 0 --> r2 = length of output buffer

	OUTPUT
		buffer written to output file
		shriek and die on output errors
	
	REGS
		if r2 = 0 on entry, it changes to length
		others preserved

|

PUTOUT:	tst	r2		; length specified?
	bne	100$		; yes - br
	mov	r1, r0		; copy string address
	call	slen		; get length
	pop	r2		; length on stack
100$:	cmp	#recsiz, r2	; long record?
	bhis	150$		; no - continue
	call	err,5,<3,erm11> ; bitch about long record
	mov	#recsiz, r2	; truncate
150$:	PUT$	#outfdb,r1,r2	; write out
	bcc	200$		; cc - ok
	call	err,5,<4,erm12>	; shriek 
	exst$s	#4		; 	and die
200$:	return


	.SBTTL SKWHT -- Skip whitespace in buffer
.REM |
	SKWHT scans a buffer looking for non-whitespace.
	Whitespace consists of blank or tab characters.
	Scan terminates when non-whitespace character (including 
	null) is found. 	

	CALL
		jsr pc, skwht
	INPUT
		r1 -> input string (must be terminated!)
	OUTPUT
		r1 -> first non-white character in buffer
	REGS
		r1 modified, others preserved
|
SKWHT:
	cmpb	(r1), #tabch	; is it tab?
	beq	100$		; yes - br
	cmpb	(r1), #spcch	; is it space?
	bne	200$		; no - br
100$:	inc	r1		; skip whitespace
	br	skwht		; and loop
200$:	return			

	.SBTTL PROCES -- Process Input Record
.REM |
	Process does the main work of KEYGEN. It is called once per
	input record. The input record is parsed into relevant fields.
	Comments are output, as is the command text if /CM was specified.
	Command text is scrambled by call to CRYPT, and output. 	

	INPUT
		r0 -> input FDB

	OUTPUT
		one or more lines output to output file

	REGS
		none preserved!

|

PROCES:	
	mov	#fibuf, r1	; point to input buffer
	call	skwht		; skip whitespace
	cmpb	(r1), #semich	; comment?
	bne	100$		; no - br
50$:	mov	txtnd, r2	; calc length
	sub	r1,r2		;
	inc	r2		;
	call 	putout		; write buffer
	jmp	1000$		; skip rest of process

100$:	cmpb	(r1), #asterch	; asterisk?
	bne	120$		; no - br
	inc	r1		; skip asterisk
	br	50$		; output less leading asterisk

120$:	push	r1		; save inbuf ptr
	mov	#evnstr, r1	; point to ".even" line
	mov	#evnlen, r2	; get length
	call	putout		; put out even
	pop	r1		; restore input ptr
	mov	#fobuf, r3	; point to output buffer
	clr	r4		; init colon count
150$:	cmpb	(r1), #colnch	; colon?
	bne	200$		; no - br
	inc	r4		; bump colon count	
200$:	movb	(r1)+, (r3)+	; copy byte of label
	tst	r4		; colon seen?
	beq	150$		; no - loop
	cmpb	(r1), #colnch	; is there another?
	bne	250$		; no - done with label
	movb	(r1)+, (r3)+	; copy second colon
250$:	movb	#tabch, (r3)+	; stuff tab in output buffer
	call	skwht		; skip whitespace	
	mov	r1, txtbeg	; save beginning of text
;
; we now have <label::^I> in output buffer
; r1 -> first text byte
;

	bit	#p.cm, proflg	; write out comments?
	bne	270$		; yes - br
	mov	r3, r1		; copy outbuf pointer
	br	500$		; skip comment output
;
; put text in as comment
;		
270$:	movb	#semich, (r3)+	; shove in semicolon
	movb	#spcch, (r3)+	; and space
300$:	movb	(r1)+, (r3)+	; copy text byte
	bne	300$		; keep copying
	dec	r3		; back up over null byte
	sub	#fobuf, r3	; get length
	mov	r3, r2		; copy length
	mov	#fobuf, r1	; point to output buffer
	call	putout		; write it out
	movb	#tabch, (r1)+	; stuff leading tab in fobuf
;
; put out a .word <text length> 
; r1 -> output buffer
500$:	mov	txtnd, r4	; compute length
	sub	txtbeg, r4	;
	inc	r4		; 
	mov	#argblk, r2	; point to $edmsg args
	mov	r4, (r2)	; save length in arg block
	mov	r1, r0		; point to output buffer
	mov	#str1, r1	; point to control string
	call	$edmsg		; format buffer
	mov	r0, r2		; compute new length
	sub	#fobuf, r2
	mov	#fobuf, r1	; point to buffer
	call	putout		; and write it out
;
; we have the label, comment (if /cm), and length out, now
; encrypt the text
;
	mov	txtbeg, r0	; point to text
	mov	r4, r1		; copy text length
	call	crypt		; encrypt in place
;
; now put out multiple .byte xxx,yyy,zzz as needed
; 20 input bytes per line of output
; 
;@MARK@
	mov	r0, r3		; save crypted text pointer
600$:	mov	#fobuf, r0	; point to output buffer
	mov	#bytstr, r1	; point to ".byte"
	mov	#bytlen, r2	; get length
610$:	movb	(r1)+,(r0)+	; copy string
	sob	r2, 610$

;
;set up to convert 20. or less bytes to ascii,
; and stuff in outbuf
;
	mov	#20., r5	; set counter
	cmp	r5, r4		; full line left?
	blos	620$		; yes - br
	mov	r4, r5		; set remaining count 
	clr	r4		; show done
	br	650$

620$:	sub	r5, r4		; reduce remaining bytes

650$:	movb	(r3)+, r1	; get a byte
	clr	r2		; lz suppress
	call	$cbtmg		; convert a byte
	dec	r5		; one less byte
	beq	700$		; done with line
	movb	#commach, (r0)+	; stuff in separator
	br	650$		; loop thru one output line
700$:	mov	#fobuf, r1	; set up to output
	mov	r0, r2		; compute length
	sub	r1, r2
	call	putout		; write it out
	tst	r4		; all done?
	bne	600$		; no - loop thru entire input text
1000$:	return

	.SBTTL MAIN LOOP


START:	;
	; initialize file system, luns, assorted other stuff
	;
	finit$			; initialize FCS
	; init done, get command line	
REINI:	; reinitialize for successive input commands
	clr	proflg		; flags are not sticky
	clr	exstat		; success so far
200$:
	gcml$	#gclblk		; get command line
				; NOTE: r0 -> gclblk
	bcc	300$		; br if got one
	tstb	g.err(0)	; input errors?
	beq	300$		; br if none
	cmpb	g.err(0), #ge.eof	; end of file?
	beq 	250$		; br if EOF
	call	err,5,<3,erm1>	; 
250$:	jmp	xit		; all done

300$:	tst	g.cmld(0)	; got anything?
	beq 	200$		; no - try again

; we have a command line to process
; parse out command line
400$:	csi$1	#csiblk,<gclblk+g.cmld+2>,<gclblk+g.cmld>	; syntax check
	bcc	500$
	call	err,5,<3,erm2>
	jmp	JIMB

500$:	csi$2	r0,INPUT		; parse input file
	bcc	600$			; no switches allowed
	call	err,5,<3,erm2>
	jmp	jimb

600$:	; open input file
	open$r	#infdb
	bcc	700$			; cc -> opened OK
	call	err,5,<4,erm3>		; oops
	jmp	jimb

700$:	csi$2	#csiblk,OUTPUT,#oswtab	; parse output file 
	bcc	800$			; switches allowed
	call	err,5,<3,erm2>
	br	JIMB

800$:	; open output file
	bit	#p.ap,proflg		; want to append ?
	beq	850$			; no - open new file
	open$a	#outfdb			; open for append
	bcc	1000$			; cc - open ok
	tstb	f.err+1(r0)		; io error?
	bne	820$			; no - just die
	cmpb	#ie.nsf, f.err(r0)	; no such file?
	bne	820$			; no - just die
	call	err,5,<0,ERM5>		; tell creating new file
	br	850$			; and try do do it

820$:	call	err,5,<3,erm6>		; oops

850$:	open$w	#outfdb			; create new file
	bcc	1000$			; cc - open ok
	call	err,5,<3,erm6>		; oops
	br	jimb

1000$:	; files opened ok - loop thru input records

	GET$	#infdb			; read input file
	bcc	1100$
	cmpb	#ie.eof, f.err(r0)	; EOF?
	beq	fini			; yes - br
	call	err,5,<4,erm5>		; shriek read error
;
; strip trailing whitespace from input buffer
;
1100$: 	mov	f.nrbd(r0), r1		; get input length
	beq	1000$			; skip blank lines
	add	#fibuf, r1		; point past last char
1150$:	cmpb	-(r1), #tabch		; whitespace?
	beq	1150$			; yes -br
	cmpb	(r1), #spcch		; whitespace?
	beq	1150$			; yes - br
1300$:	clrb	1(r1)			; force null terminator
	mov	r1, txtnd		; save end of text pointer
;
; call process to munch input line
;
	call	proces		
	br	1000$		; loop thru input file

fini:	close$	#infdb
	bcc	100$
	call	err,5,<4,erm7>
100$:	close$	#outfdb
	bcc	jimb
	call	err,5,<4,erm8>
jimb:	jmp	reini		; do it all again

xit:	exst$s	exstat		;all done

	.END	START
