;KSM/ASM - Keystroke Multiply Filter
	TITLE	<KSM/FLT - LS-DOS 6.2>
;
LF	EQU	10
CR	EQU	13
;
*GET	SVCMAC:3		;SVC Macro equivalents
*GET	COPYCOM:3		;Copyright message
;
	ORG	2400H
;
KSM
	@@CKBRKC		;Check for break
	JR	Z,KSMA		;Contine if not
	LD	HL,-1		;  else abort
	RET
;
KSMA	LD	(KSMDCB),DE	;Save ptr to DCB
	PUSH	HL		;Save ptr to cmdline buf
	@@DSPLY	HELLO$		;Display copyright msg
	@@FLAGS			;Get flags
	POP	HL		;Rcvr cmndline pointer
;
;	Check if entry from SET command
;
	BIT	3,(IY+'C'-'A')	;System request?
	JP	Z,VIASET	;Quit if not
	LD	DE,KSMFCB	;Point to FCB
	@@FSPEC			;Fetch the KSM filespec
	JP	NZ,SPCREQ	;Jump on bad spec
	PUSH	DE		;Save FCB pointer
	LD	DE,PRMTBL$	;Load param table pointer
	@@PARAM			;Parse parms
	POP	DE		;Recover FCB pointer
	JP	NZ,IOERR	;Go on parm error
	LD	HL,DFTKSM	;Init to default ext
	@@FEXT			;Fetch if not entered
;
;	Transfer requested ENTER char to test loc
;
EPARM	LD	HL,';'		; set default ";"
	LD	A,(ERSP)	;Test parm response
	BIT	6,A		;Flag is no good!
	JP	NZ,PRMERR
	BIT	5,A		;Test string or value
	LD	A,(HL)		;P/u assumed string
	JR	NZ,$+3		;Go if string entry
	LD	A,L		;P/u hex or dec entry
	LD	(ECHAR+1),A	;Stuff it in there
	PUSH	DE
	LD	DE,KSM$		;Check if filter is
	@@GTMOD			;  already resident
	LD	(KSMMEM+1),HL	;Stuff start
	EX	DE,HL		;Put DCB ptr to HL
	POP	DE
	JR	NZ,OPENKSM	;Go if not
;
;	Make sure that the new DCB is same as the old
;
	PUSH	HL		;Save where to stuff
	LD	C,(HL)		;P/u DCB pointer LSB
	INC	HL
	LD	B,(HL)		;P/u DCB pointer MSB
	LD	HL,6		;Get old DCB name &
	ADD	HL,BC		;  stuff into error
	LD	A,(HL)		;  message in case
	INC	L		;  a different DCB
	LD	H,(HL)		;  is referenced
	LD	L,A
	LD	(DCBNAM$),HL
	OR	H		;If DCB name is null,
	LD	HL,(KSMDCB)
	PUSH	HL		;Save pointer to stuff
	JR	Z,UPDPTR	;  then OK to use
	OR	A
	SBC	HL,BC		;Same DCB pointer?
UPDPTR	POP	BC		;Rcvr pointer to stuff
	POP	HL		;Rcvr address to put pointer
	JP	NZ,DCBERR	;Quit if filter in use
;
;	Same DCB - Okay to stuff
;
	LD	(HL),C		;Store the DCB pointer
	INC	HL
	LD	(HL),B
KSMMEM	LD	HL,$-$		;If res, ptr to start
	LD	BC,ECHAR-DVRBGN+1
	ADD	HL,BC		;Resident, stuff ECHAR
	LD	A,(ECHAR+1)	;  where it is in memory
	LD	(HL),A		;Stuff in upper mem
OPENKSM	LD	HL,KSMBUF	;Pt to buffer area
	LD	B,0		;Init LRL=256
	@@OPEN			;Open the file
	JP	NZ,IOERR	;Jump on open error
	LD	HL,DVREND	;Place file in memory 1st
	LD	B,26		;Init for 26 lines
KSM1	@@GET			;Get a char from file
	JR	NZ,KSM2		;Jump on error
	LD	(HL),A		;Stuff into memory
	INC	HL		;Inc memory pointer
	CP	CR		;Found end-of-line?
	JR	NZ,KSM1		;Loop if not
	DJNZ	KSM1		;Decrement the A-Z loop
	DEC	HL		;Backup over last CR &
	INC	B		;  adjust for one more
	LD	A,1CH		;No error here, just EOF
KSM2	PUSH	AF		;Save error code
	@@CLOSE			;Close the file
	POP	AF
	CP	1CH		;Ck for eof
	JP	NZ,IOERR	;Jump on not eof error
KSM3	LD	(HL),CR		;End with a <ENTER>
	INC	HL		;For all remaining
	DJNZ	KSM3		;"letters" not entered
	LD	IX,(KSMDCB)	;Rcvr user DCB entry
	LD	DE,DVREND	;Calculate the length
	XOR	A		; of the KSM file just
	SBC	HL,DE		; loaded
	LD	B,H		;Xfer length
	LD	C,L
	LD	HL,(KSMMEM+1)	;If not previously res,
	LD	A,L		;  move to HIGH$
	OR	H
	JR	Z,MOVTOHI
	PUSH	BC		;Save length
	PUSH	HL		;Save old start
	ADD	HL,BC		;Start + data
	JR	C,KSM3A		;Bad if wrap past 0
	LD	BC,DVREND-DVRBGN+1
	ADD	HL,BC		;Start + data + filter
	JR	C,KSM3A		;Bad if wrap past 0
	EX	DE,HL		;Save in reg DE
	POP	HL		;Rcvr old start
	INC	HL		;Pt to last byte used
	INC	HL
	LD	A,(HL)		;P/u last byte used
	INC	HL
	LD	H,(HL)		;  into HL
	LD	L,A
	PUSH	HL
	XOR	A		;Clear carry flag
	SBC	HL,DE		;Is req > available?
KSM3A	POP	HL		;Rcvr old start to reuse
	POP	BC		;Rcvr length of req
	JP	C,NOROOM	;Quit if file too big
	JR	KSM0A
MOVTOHI	PUSH	BC		;Save data length
	LD	HL,0		;P/u current high memory
	LD	B,L
	@@HIGH$
	POP	BC		;Recover data length
KSM0A	LD	(DVRBGN+2),HL	;Stuff last byte used
	LD	(RX1),HL	;Stuff ptr to flag byte
	LD	(HL),0		;Init the KSM char ptr
	DEC	HL		;  to zero to show no
	LD	(HL),0		;  char avail at startup
	DEC	HL
	LD	DE,DVREND	;Move data to high
MOVLP	LD	A,(DE)		;Data is in reverse order
	LD	(HL),A
	DEC	HL		;Dec himem ptr
	INC	DE		;  and inc the char ptr
	DEC	BC		;Reduce char count
	LD	A,B		;  and check if done
	OR	C
	JR	NZ,MOVLP	;Loop back if not
	LD	BC,DVREND-DVRBGN	;Get driver len
	XOR	A		;Reduce potential HIGH$
	SBC	HL,BC		;  by driver length
	LD	A,(KSMMEM+1)	;Don't update HIGH$
	OR	A		;  if previously res
	JR	Z,DOHIGH	;Go if not resident
;
;	Module already resident
;
	LD	DE,(KSMMEM+1)	;P/u module entry point
	LD	HL,KSMRPL$	;  & reuse the filter
	JR	KSM8
;
;	Stuff new HIGH$ value (Note: B=0 for driver
;	length so there is no damage on the @@HIGH$ SVC
;
DOHIGH	LD	B,0
	@@HIGH$
	INC	HL		;Pt to driver start
	EX	DE,HL
	PUSH	DE		;Save start of driver
	LD	HL,KSMDCB-DVRBGN
	ADD	HL,DE		;Point to filter DCB ptr
	LD	(RX2),HL
	LD	HL,DVRBGN	;Move parms also
	LDIR
	POP	DE		;Rcvr driver ept
	LD	HL,KSMACT$	;Init "KSM installed
KSM8	LD	(IX),40H!5	;Init DCB type to "input"
	LD	(IX+1),E	;  & filter & stuff the
	LD	(IX+2),D	;  filter address
	SET	6,(IY+'D'-'A')	;Turn on device flag bit
	@@LOGOT			;Display installation msg
	LD	HL,0		;Set no error
	RET			;Back to the user
;
;	Error processing
;
VIASET	LD	HL,VIASET$	;"Install with Set
	DB	0DDH
DCBERR	LD	HL,DCBERR$	;"Filter in use already
	DB	0DDH
NOROOM	LD	HL,NOROOM$	;"Memory frozen
	DB	0DDH
SPCREQ	LD	HL,SPCREQ$	;"Missing filespec
	@@LOGOT			;Display an error
	LD	HL,-1		;Set abort code
	RET
PRMERR	LD	A,44		;Init PARM ERROR
IOERR	LD	L,A		;Error code to HL
	LD	H,0
	OR	0C0H		;Set short, return
	LD	C,A		;Error to C
	@@ERROR			;  for error display
	RET
;
;	Data and message area
;
KSM$	DB	'$KSM',3
DFTKSM	EQU	$		;Note: HELLO$ must follow
HELLO$	DB	'KSM Filter'
*GET	CLIENT:3
;
VIASET$	DB	'Must install via SET',CR
SPCREQ$	DB	'Filespec required',CR
KSMACT$	DB	'KSM is now operational',CR
KSMRPL$	DB	'KSM filter data replaced',CR
DCBERR$	DB	'KSM filter already attached to *xx',CR
DCBNAM$	EQU	$-3
NOROOM$	DB	'Request exceeds available memory',CR
PRMTBL$	DB	'R'!80H,0F5H,'ENTER',0
ERSP	EQU	$-1
;
	DW	EPARM+1
	DB	0
;
KSMFCB	DEFS	32
KSMBUF	DEFS	256
;
;	Key-Stroke Multiplication driver
;
DVRBGN	JR	START		;Branch around header
	DW	$-$		;Last byte used
	DB	4,'$KSM'
KSMDCB	DW	$-$		;Pointer to KSM's DCB
	DW	0
;
START	LD	HL,0		;P/u possible address to
RX1	EQU	$-2
	LD	D,(HL)		;  a KSM that was parsed
	DEC	HL		;  to a ';' logical ENTER
	LD	E,(HL)		;If this vector is zero,
	DEC	HL		;  no KSM continuation is
	EX	DE,HL		;  pending - find a new
	PUSH	AF		;  entry. Save flags.
	LD	A,H		;  If <> 0, grab the KSM
	OR	L		;  line continuation
	JR	NZ,DVR4A
	POP	AF		;Rcvr flags
	PUSH	DE		;Save ptr to 'A'-KSM
DVR1	LD	IX,(KSMDCB)	;Chain to next DCB module
RX2	EQU	$-2
	@@CHNIO
	POP	DE		;Rcvr 'A'-KSM pointer
	RET	NZ		;Back if nothing or error
	BIT	7,A		;Is it a CLEAR function?
	RET	Z		;Ret if <CLEAR> not down
	PUSH	AF		;Save key entry
	CP	'A'+80H		;Ck for range A-Z
	JR	C,DVR2		;Exit if < 'A'
	CP	'Z'+1+80H
	JR	C,DVR3		;Use it if A-Z
DVR2	POP	AF		;Rcvr orig flag
	CP	A		;Set Z-flag
	RET
;
;	Key code entry includes <CLEAR> key
;
DVR3	POP	AF		;Rcvr orig flag
	LD	H,D		;Rcvr ptr to 'A'-KSM
	LD	L,E		;  & xfer to reg HL
	SUB	80H+'A'		;Adjust offset to index
	JR	Z,DVR5		;Bypass if was 'A'
	LD	B,A		;Set loop counter
	LD	A,CR		;Read past the KSM lines
DVR4	CP	(HL)		;  for letters preceding
	DEC	HL		;  key entry to find the
	JR	NZ,DVR4		;  KSM line for entered
	DJNZ	DVR4		;  key code
	DB	3EH		;Ignore next inst
;
;	Routine to pick up the next KSM character
;	& return it to the system KI request
;
DVR4A	POP	AF		;Clean the stack
DVR5	LD	A,(HL)		;P/u the next KSM char
	DEC	HL		;Dec pointer to next one
	EX	DE,HL		;Put either a pointer to
	INC	HL		;  the next KSM char or
	CP	CR		;  if got last, zero the
	JR	Z,DVR6		;  data pointer
	LD	(HL),E		;Stuff pointer to next
	INC	HL		;  character to fetch
	LD	(HL),D
ECHAR	CP	';'		;Ck on logical line end
	JR	NZ,DVR7		;  & convert to <ENTER>
	LD	A,CR		;  if it was semi-colon
DVR7	CP	A		;Tell the system we have
	RET			;  retrieved a char
;
;	Got the terminating X'0D' - Clear the pointer
;
DVR6	XOR	A		;Clear the KSM char ptr
	LD	(HL),A		;  as next request is new
	INC	HL
	LD	(HL),A
	CP	0FFH		;Set NZ & A = 0
	RET
DVREND	EQU	$
;
	END	KSM
