; FILENAME: AK.ASM
P386
IDEAL
JUMPS
LOCALS @_


include	'useful.inc'

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
segment code
        org     100h
        assume cs:code, ds:code, es:nothing, ss:code
start:
        jmp     main



        REM     -- Backspace and CarriageReturn key respectively
        BS			= 08h
	NL			= 0Dh
	TAB			= 09h

	REM   -- we use the alternate keyboard cursor movements(WS-like)
	REM      hotkey because, the Turbo C++ editor refuse
	REM      to distinguish LEFT from CTRL+LEFT, and also
	REM	 with RIGHT from CTRL+LEFT when we
	REM	 press the hotkey CTRL+ENTER.

	REM	   -- The Ctrl+S in Pascal and C/C++ is move left
	REM		Ctrl+D Ctrl+E and Ctrl+X are right, up, down
	REM	        respectively
	KB_CTRL_S		= 13h
	KB_CTRL_X		= 18h
	KB_CTRL_E		= 05h
	KB_CTRL_D		= 04h

	KB_UP			= 4800h
	KB_DOWN			= 5000h
	KB_LEFT			= 4B00h
	KB_RIGHT		= 4D00h


	KB_CTRL_RIGHT_BRACKET	= 1B1Dh
	KB_CTRL_LEFT_BRACKET	= 1A1Bh

	INDENT_HOTKEY		= KB_CTRL_RIGHT_BRACKET
	UNINDENT_HOTKEY		= KB_CTRL_LEFT_BRACKET


	UP			= KB_CTRL_E
	DOWN			= KB_CTRL_X
	LEFT			= KB_CTRL_S
	RIGHT			= KB_CTRL_D

	REM 			-- ASCII code of Enter
	KB_CTRL_ENTER_ASCII	= 1C0Ah
        REM                     -- Scan code of Enter
	SHORTCUT_HOTKEY         = KB_CTRL_ENTER_ASCII

	KB_SOFTWARE_INTR 	= 16h


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        label   dosBusyFlagAddr dword
                dosBusyFlagOfs  dw ?
                dosBusyFlagSeg  dw ?



	struc	keyTemplates
		label	shortcut	word
		firstKey		db ' '
		secondKey		db ' '
		textToInsertOffset	dw ?
	ends


	stuffing     	db FALSE
	ptrToText	dw ?


	label	keyBuff	word
		firstKeyBuff	db 0
		secondKeyBuff	db 0

	C_DoubleKeys	keyTemplates< 'e', 'i', C_elseIf >
			keyTemplates< 's', 't', C_struct >
			keyTemplates< 's', 'w', C_switch >
			keyTemplates< 'i', 'e', C_ifElse >
			keyTemplates< 'c', 'a', C_case >
			keyTemplates< 'c', 'o', C_continue >
			keyTemplates< 'c', 'l', C_class >
			keyTemplates< '#', 'i', C_include >
			keyTemplates< '#', 'd', C_define >
			keyTemplates< '#', '#', C_includeU >
			keyTemplates< 'i', 'n', C_intFunc >
			keyTemplates< 'v', 'o', C_voidFunc >
			keyTemplates< 'd', 'o', C_doubleFunc >
			keyTemplates< 'c', 'p', C_cprintf >
			keyTemplates< 'c', 's', C_cscanf >
			db	NULL, NULL

	C_SingleKeys	keyTemplates< , 'i', C_if >
			keyTemplates< , 'w', C_while >
			keyTemplates< , 'u', C_unsigned >
			keyTemplates< , 'e', C_else >
			keyTemplates< , 'd', C_do >
			keyTemplates< , 'f', C_for >
			keyTemplates< , 'b', C_break >
			keyTemplates< , 'r', C_return >
			keyTemplates< , 'g', C_gotoxy >
			keyTemplates< , 'p', C_printf >
			keyTemplates< , 's', C_scanf >
			keyTemplates< , '!', C_program >
			db	NULL, NULL


	Pas_DoubleKeys	keyTemplates< 'w', 'h', Pas_while >
        		keyTemplates< 'w', 'i', Pas_with >
			keyTemplates< 'r', 'u', Pas_repeatUntil >
			keyTemplates< 'r', 'e', Pas_recordEnd >
			keyTemplates< 'f', 'o', Pas_for >
			keyTemplates< 'f', 'd', Pas_for_down >
			keyTemplates< 'f', 'u', Pas_function >
			keyTemplates< 'c', 'a', Pas_case >
			keyTemplates< 'c', 'o', Pas_continue >
			keyTemplates< 'b', 'e', Pas_beginEnd >
			keyTemplates< 'b', 'r', Pas_break >
			keyTemplates< 'i', 'e', Pas_ifElse >
			keyTemplates< 'e', 'i', Pas_elseIf >
			keyTemplates< 'i', 'n', Pas_integer >
			keyTemplates< 'o', 'b', Pas_object >
			db	NULL, NULL

	Pas_SingleKeys	keyTemplates< , 'p', Pas_procedure >
			keyTemplates< , 'e', Pas_else >
			keyTemplates< , 'i', Pas_if >
			keyTemplates< , 'a', Pas_array >
			keyTemplates< , 'w', Pas_writeln >
			keyTemplates< , 'r', Pas_readln >
			keyTemplates< , 'g', Pas_gotoxy >
			keyTemplates< , '!', Pas_program >
			db	NULL, NULL

	REM		-- Pascal double keys
	Pas_while	db BS, BS, 'do', NL
			db 'begin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP
			db 'while  '
			db LEFT
			db NULL

	Pas_with	db BS, BS, 'do', NL
			db 'begin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP
			db 'with  '
			db LEFT
			db NULL

	Pas_repeatUntil	db BS, BS
			db 'repeat', NL, NL
			db 'until ;', LEFT
			db NULL

	Pas_recordEnd	db 'cord', NL, NL
			db 'end;'
			db NULL

	Pas_for		db BS, BS, ':=  to  do', NL
			db 'begin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP
			db 'for  '
			db LEFT
			db NULL

	Pas_for_down	db BS, BS, ':=  downto  do', NL
			db 'begin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP
			db 'for  '
			db LEFT
			db NULL

	Pas_function	db BS, BS, ':;', NL
			db 'begin', NL, NL
			db 'end;', NL
			db UP, UP, UP, UP
			db 'function '
			db NULL

	Pas_case	db BS, BS, 'of', NL
			db ':', NL
			db ':', NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP
			db 'case  '
			db LEFT
			db NULL

	Pas_continue	db 'ntinue;', NL
			db NULL

	Pas_beginEnd	db 'gin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, TAB
			db NULL

	Pas_break	db 'eak;', NL
			db NULL

	Pas_ifElse      db BS, BS, 'then', NL
			db 'begin', NL, NL
			db 'end', NL
			db 'else', NL
			db 'begin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP, UP
			db UP, UP, UP
			db 'if  '
			db LEFT
			db NULL

	Pas_elseIf	db BS, BS, 'then', NL
			db 'begin', NL, NL
			db 'end', LEFT, LEFT, LEFT
			db UP, UP, UP
			db 'else if  '
			db LEFT
			db NULL

	Pas_integer	db 'teger;', NL
			db NULL

	Pas_object	db BS, BS, 'object', NL
			db 'private', NL, NL
			db 'public', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP, UP, UP
			db NULL

	REM		-- Pascal Single Keys
	Pas_procedure	db BS, ';', NL
			db 'begin', NL, NL
			db 'end;', NL
			db UP, UP, UP, UP
			db 'procedure '
			db NULL

	Pas_else	db 'lse', NL
			db 'begin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP
			db NULL

	Pas_if		db BS, 'then', NL
			db 'begin', NL, NL
			db 'end;', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP
			db 'if  '
			db LEFT
			db NULL

	Pas_array       db 'rray [ .. ] of'
			db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
			db NULL

	Pas_writeln 	db 'riteln;', LEFT
			db NULL

	Pas_readln	db 'eadln;', LEFT
			db NULL

	Pas_gotoxy	db 'otoxy( ,  );'
			db LEFT, LEFT, LEFT, LEFT, LEFT
			db NULL

	Pas_program     db BS
			db 'uses crt, dos, printer, graph;', NL
			db 'var', NL
			db NL
			db NL
			db 'begin', NL
			db NL
			db 'end.', LEFT, LEFT, LEFT, LEFT
			db UP, UP, UP, UP, TAB
			db NULL


	REM		-- C double keys
	C_elseIf	db BS, BS, ')',NL
			db '{', NL, NL
			db '}', LEFT
			db UP, UP, UP
			db 'else if(  '
			db LEFT
			db NULL

	C_struct	db 'ruct', NL
			db '{', NL
			db NL
			db '};', LEFT, LEFT
			db UP, UP, UP
			db RIGHT, RIGHT, RIGHT, RIGHT
			db RIGHT, RIGHT
			db ' '
			db NULL


	C_switch	db BS, BS, ')', NL
			db '{', NL
			db 'case :',NL, NL
			db '    break;', NL, NL, BS
			db 'case :',NL, NL
			db '    break;', NL, NL, BS
			db 'default:', NL, NL
			db '    break;', NL, BS
			db '}', LEFT
			db UP, UP, UP, UP, UP, UP, UP
			db UP, UP, UP, UP, UP, UP
			db 'switch(  '
			db LEFT
			db NULL

	C_ifElse	db BS, BS, ')', NL
			db '{', NL, NL
			db '}', NL
			db 'else', NL
			db '{', NL, NL
			db '}', LEFT
			db UP, UP, UP, UP
			db UP, UP, UP
			db 'if(  '
			db LEFT
			db NULL

	C_case          db 'se:', NL, NL
			db '    break;'
			db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
			db UP, UP, ' '
			db NULL

	C_continue	db 'ntinue;', NL
			db NULL

	C_class		db 'ass', NL
			db '{', NL
			db 'private:', NL, NL
			db 'protected:', NL, NL
			db 'public:', NL, NL
			db '};', LEFT, LEFT
			db UP, UP, UP, UP, UP, UP, UP, UP
			db RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, ' '
			db NULL

	C_include	db 'nclude <.h>', LEFT, LEFT, LEFT
			db NULL

	C_define	db 'efine '
			db NULL

	C_includeU	db BS, 'include ".h"', LEFT, LEFT, LEFT
			db NULL

	C_intFunc	db 't ()', NL
			db '{', NL
			db NL
			db '}', NL
			db UP, UP, UP, UP, RIGHT, RIGHT, RIGHT, RIGHT
			db NULL


	C_voidFunc	db 'id ()', NL
			db '{', NL
			db NL
			db '}', NL
			db UP, UP, UP, UP, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT
			db NULL

	C_doubleFunc	db 'uble ()', NL
			db '{', NL
			db NL
			db '}', NL
			db UP, UP, UP, UP
			db RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT, RIGHT
			db NULL


	C_cprintf	db 'rintf( "",  );'
			db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
			db NULL

	C_cscanf	db 'canf( "",  );'
			db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
			db NULL




	REM		-- C single keys
	C_if		db BS, ')', NL
			db '{', NL, NL
			db '}', LEFT
			db UP, UP, UP
			db 'if(  '
			db LEFT
			db NULL

	C_while		db BS, ')', NL
			db '{', NL, NL
			db '}', LEFT
			db UP, UP, UP
			db 'while(  '
			db LEFT
			db NULL

	C_unsigned	db 'nsigned '
			db NULL

	C_else		db 'lse', NL
			db '{', NL, NL
			db '}', LEFT
			db UP
			db NULL

	C_do		db 'o', NL
			db '{', NL
			db '}', NL
			db 'while(  );'
			db LEFT, LEFT, LEFT
			db NULL

	C_for		db BS, '; ;  )', NL
			db '{', NL, NL
			db '}', LEFT
			db UP, UP, UP
			db 'for( '
			db NULL

	C_break		db 'reak;', NL
			db NULL

	C_return	db 'eturn '
			db NULL


	C_printf	db 'rintf( "",  );'
			db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
			db NULL

	C_scanf		db 'canf( "",  );'
			db LEFT, LEFT, LEFT, LEFT, LEFT, LEFT
			db NULL

	C_gotoxy	db 'otoxy( ,  );'
			db LEFT, LEFT, LEFT, LEFT, LEFT
			db NULL


	C_program	db BS
			db '#include <stdio.h>', NL
			db '#include <io.h>', NL
			db '#include <conio.h>', NL
			db '#include <math.h>', NL
			db NL
			db 'void main()', NL
			db '{', NL
			db NL
			db '}', LEFT, UP, TAB
			db NULL


	CPas_indent	db 0Bh, 'I'
			db NULL

	CPas_unindent	db 0Bh, 'U'
			db NULL



	doublekeysPtr	dw ?
	singlekeysPtr	dw ?


	REM	-- prev address of keyboard interrupt 16h
	label   preAKAddr      	dword
		preAKOffset     dw ?
		preAKSegment    dw ?

	initialMCBSegment	dw ?
        mcbAsciiNameLength      dw ?

        struc   langNameList
                len     dw ?
                desc    db 8 dup(NULL)
        ends


	pascalLang	langNameList< 5, 'TURBO' >
			langNameList< 2, 'TP' >
			langNameList< 3, 'TPX' >
			langNameList< 2, 'BP' >
			langNameList< 3, 'BPX' >
			langNameList< 6, 'PASCAL' >
                        db NULL

	ccplusLang      langNameList< 2, 'TC' >
			langNameList< 3, 'TCX' >
			langNameList< 2, 'BC' >
			langNameList< 3, 'BCX' >
			langNameList< 2, 'QC' >
			langNameList< 3, 'QCX' >
			langNameList< 1, 'C' >
                        db NULL



        langToDetect    dw ?



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isLanguageInMem?
                push    ax
                push    bx
                push    cx
                push    ds
                push    si
                push    es
                push    di


        @_firstMCB:
		push	[cs:initialMCBSegment]
		pop	es

                push    cs
                pop     ds

;............................................................................
        @_traverseMCB:

                mov     bx, [cs:langToDetect]

	@_loopLang:

                mov     di, 0
                mov     cx, 8
        @_findMCBAsciiNameLength:
                inc     di
                cmp     [(mcb es:di).asciiName], NULL
                loopne  @_findMCBAsciiNameLength

                mov     [cs:mcbAsciiNameLength], di

                mov     cx, [(langNameList cs:bx).len]

                REM     -- if not same string length scan the
                REM        next language name
                cmp     cx, [cs:mcbAsciiNameLength]
                        ohNo    @_scanNextLanguageName

                lea     di, [es:mcb.asciiName]
                lea     si, [(langNameList cs:bx).desc]


                cld
                repe    cmpsb
                        ohYes   @_found


        @_scanNextLanguageName:

                add     bx, size langNameList
                cmp     [(langNameList cs:bx).desc], NULL
                        ohYes   @_stopScanningLang

		jmp     @_loopLang

        @_stopScanningLang:

	@_scanTheNextMCB:

                cmp     [es:mcb.linkIndicator], 'Z'
                je      @_notFound

                mov     ax, es
                inc     ax

                add     ax, [es:mcb.paragraphLen]
                mov     es, ax

                jmp     @_traverseMCB

;............................................................................
        @_found:
                ste
                jmp     @_finish

;............................................................................
        @_notFound:
                cle
                jmp     @_finish

        @_finish:

                pop     di
                pop     es
                pop     si
                pop     ds
                pop     cx
                pop     bx
                pop     ax

		ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isPascalInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset pascalLang
                call    isLanguageInMem?
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCCPlusplusInMem?
        REM     Input(s):
        REM        langToDetect -- pass here to offset of name list of
        REM                        the language
        REM     Output(s):
        REM        Equality/Zero Flag -- if present in memory
;............................................................................
                mov     [cs:langToDetect], offset ccplusLang
                call    isLanguageInMem?
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
	proc	getInitialMCBSegment
	REM	Purpose:
	REM		Get initial MCB Segment, which is very useful
	REM		  in traversing memory links, to see if a particular
	REM		  program is in memory
	REM             In our program this is use for inquiring the
	REM		  language loaded in memory (Pascal or C/C++?)
	REM	Input(s):
	REM		None
	REM	Output(s):
	REM		initialMCBSegment: data type = word
;............................................................................
		push	ax
		push	es
		push	bx

		mov     ah, DOS_GET_INITIAL_MCB
		int	21h

		mov	ax, [word es:bx-2]
		mov	[cs:initialMCBSegment], ax

		pop	bx
		pop	es
		pop	ax
		ret
	endp




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
	proc	insertAtOurBuffer
	REM	Purpose:
	REM		Simulate keyboard buffer reading
	REM	Input(s):
	REM		None
	REM	Output(s):
	REM		keybuff: data type = word
	REM		has two components:
	REM		    firstkeyBuff
	REM		    secondkeyBuff
;............................................................................
		pusha
		mov	ah, BIOS_GET_CURSOR_POS
		REM	-- 0 is page number
		mov	bh, 0
		int	10h

		mov	cx, dx

		REM	-- DH is row DL is column
		dec	dl
		mov	ah, BIOS_SET_CURSOR_POS
		int	10h
		mov	ah, BIOS_READ_CHAR_AT_POS
		int	10h
		mov	[cs:secondKeyBuff], al

                REM	-- DH is row DL is column
                dec	dl
		mov	ah, BIOS_SET_CURSOR_POS
		int	10h
                mov	ah, BIOS_READ_CHAR_AT_POS
		int	10h
		mov	[cs:firstKeyBuff], al


		mov	ah, BIOS_SET_CURSOR_POS
		REM	-- 0 is page number
		mov	bh, 0
		mov	dx, cx
		int	10h

		popa
		ret
	endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
	proc    keyIntercept
		jmp	@_beginning

		tsrMarker	db 'AK-MLTH'

	@_beginning:

                pushf
                push    es
                push    di
                les     di, [cs:dosBusyFlagAddr]
                cmp     [byte ptr es:di], 0
			ohYes    @_continueBeginning
	@_donotContinueBeginning:
		pop	di
		pop	es
		popf
		jmp	@_doOld
	@_continueBeginning:
                pop     di
                pop     es
		popf


		cmp	ah, BIOS_OLD_READKEY
			ohYes	@_tryReadkey

		cmp	ah, BIOS_NEW_READKEY
			ohYes	@_tryReadkey

		cmp	ah, BIOS_OLD_KEYSTAT
			ohYes	@_tryKeystat

		cmp	ah, BIOS_NEW_KEYSTAT
			ohYes	@_tryKeystat


;............................................................................
	@_tryReadkey:
		cmp	[cs:stuffing], TRUE
			ohYes	@_fakeReadkey

		jmp	@_realReadkey

;............................................................................
	@_realReadkey:
		pushf
		call	[cs:preAKAddr]
		cmp	ax, SHORTCUT_HOTKEY
			ohYes	@_readKeyShortcutContinue

		cmp	ax, INDENT_HOTKEY
			ohYes	@_readkeyIndent

		cmp	ax, UNINDENT_HOTKEY
			ohYes	@_readkeyUnindent

		jmp	@_readkeyShortcutNotTriggerBackToCaller

	@_readkeyIndent:
		mov	[cs:stuffing], TRUE
		mov	[cs:ptrToText], offset CPas_indent
		jmp	@_fakeReadkey

	@_readkeyUnindent:
        	mov	[cs:stuffing], TRUE
		mov	[cs:ptrToText], offset CPas_unindent
		jmp	@_fakeReadkey


	@_readkeyShortcutContinue:

		call	insertAtOurBuffer

		call	isPascalInMem?
			ohYes	@_readkeyPascal
		call	isCCPlusplusInMem?
			ohYes	@_readkeyCCPlusplus

		jmp	@_doOld


;............................................................................
	@_readkeyPascal:
		mov	[cs:doublekeysPtr], offset Pas_doubleKeys
		mov	[cs:singlekeysPtr], offset Pas_singleKeys
		jmp	@_readkeyLanguage

;............................................................................
	@_readkeyCCPlusplus:
                mov	[cs:doublekeysPtr], offset C_doubleKeys
                mov	[cs:singlekeysPtr], offset C_singleKeys
		jmp	@_readkeyLanguage

;............................................................................
	@_readkeyLanguage:


		push	dx
		push	bx
		mov	dx, [cs:keyBuff]
		mov	bx, [cs:doublekeysPtr]
	@_readkeyDoublekeyCheckKeysLoop:
		cmp	[(keyTemplates cs:bx).shortcut], NULL
			ohYes	@_readkeyDoublekeyShortcutNotAvailable
		cmp	[(keyTemplates cs:bx).shortcut], dx
			ohYes	@_readkeyDoublekeyShortcutAvailable
		add	bx, size keyTemplates
		jmp	@_readkeyDoublekeyCheckKeysLoop
	@_readkeyDoublekeyShortcutAvailable:
		REM	-- clear first our own keyboard buffer
		mov	[cs:keyBuff], 0000h
		mov	bx, [(keyTemplates cs:bx).textToInsertOffset]
		mov	[cs:ptrToText], bx
		mov	[cs:stuffing], TRUE
		jmp	@_readkeyDoublekeyCheckKeysStop
	@_readkeyDoublekeyShortcutNotAvailable:
		mov	[cs:ptrToText], 0
		mov	[cs:stuffing], FALSE
		jmp	@_readkeyDoublekeyCheckKeysStop
	@_readkeyDoublekeyCheckKeysStop:
		pop	bx
		pop	dx

		cmp	[cs:stuffing], TRUE
		je	@_fakeReadkey

		push	dx
		push	bx
		mov	dl, [cs:secondKeybuff]
		mov	bx, [cs:singlekeysPtr]
	@_readkeySinglekeyCheckKeysLoop:
		cmp	[(keyTemplates cs:bx).secondKey], NULL
			ohYes	@_readkeySinglekeyShortcutNotAvailable
		cmp	[(keyTemplates cs:bx).secondKey], dl
			ohYes	@_readkeySinglekeyShortcutAvailable
		add	bx, size keyTemplates
		jmp	@_readkeySinglekeyCheckKeysLoop
	@_readkeySinglekeyShortcutAvailable:
		REM	-- clear first our own keyboard buffer
		mov	[cs:keyBuff], 0000h
		mov	bx, [(keyTemplates cs:bx).textToInsertOffset]
		mov	[cs:ptrToText], bx
		mov	[cs:stuffing], TRUE
		jmp	@_readkeySinglekeyCheckKeysStop
	@_readkeySinglekeyShortcutNotAvailable:
		mov	[cs:ptrToText], 0
		mov	[cs:stuffing], FALSE
		jmp	@_readkeySinglekeyCheckKeysStop
	@_readkeySinglekeyCheckKeysStop:
		pop	bx
		pop	dx

		cmp	[cs:stuffing], TRUE
		je	@_fakeReadkey

		iret

;............................................................................
	@_fakeReadkey:
		push	bx
		mov	bx, [cs:ptrToText]
		mov	al, [byte cs:bx]

		mov	ah, 0

		inc     [cs:ptrToText]
		mov	bx, [cs:ptrToText]
		cmp	[byte cs:bx], NULL
		setne	[cs:stuffing]
		pop	bx

		iret

;............................................................................
	@_readkeyShortcutNotTriggerBackToCaller:
		iret

;............................................................................
	@_tryKeystat:
		cmp	[cs:stuffing], TRUE
			ohYes	@_fakeKeystat

		jmp	@_realKeystat

;............................................................................
	@_realKeystat:
		pushf
		call	[cs:preAKAddr]
		REM	-- Zero keys Available so just back to caller
			jz	@_keystatBackToCaller

		cmp	ax, SHORTCUT_HOTKEY
			ohYes	@_keyStatShortcutContinue

		cmp	ax, INDENT_HOTKEY
			ohYes	@_keystatIndent

		cmp	ax, UNINDENT_HOTKEY
			ohYes	@_keystatUnindent

		jmp	@_keystatBackToCaller


	@_keystatIndent:
        	mov	ah, BIOS_OLD_READKEY
		pushf
		call	[cs:preAKAddr]
		mov	[cs:stuffing], TRUE
		mov	[cs:ptrToText], offset CPas_indent
		jmp	@_fakeKeystat

	@_keystatUnindent:
        	mov	ah, BIOS_OLD_READKEY
		pushf
		call	[cs:preAKAddr]
        	mov	[cs:stuffing], TRUE
		mov	[cs:ptrToText], offset CPas_unindent
		jmp	@_fakeKeystat

	@_keystatShortcutContinue:

		REM	-- we do this just to remove the hotkey
		REM	   so it won't be evaluated again,
		REM	   if you try to remove this
		REM 	   three lines of code you'll see
		REM	   the insertion of keys will go into
		REM	   infinitum
		mov	ah, BIOS_OLD_READKEY
		pushf
		call	[cs:preAKAddr]

		call	insertAtOurBuffer

		call	isPascalInMem?
			ohYes	@_keystatPascal
		call	isCCPlusplusInMem?
			ohYes	@_keystatCCPlusplus

		stz
		retf	2

;............................................................................
	@_keystatPascal:
		mov	[cs:doublekeysPtr], offset Pas_doubleKeys
		mov	[cs:singlekeysPtr], offset Pas_singleKeys
		jmp	@_keystatLanguage

;............................................................................
	@_keystatCCPlusplus:
                mov	[cs:doublekeysPtr], offset C_doubleKeys
                mov	[cs:singlekeysPtr], offset C_singleKeys
		jmp	@_keystatLanguage

;............................................................................
	@_keystatLanguage:


                push	dx
		push	bx
		mov	dx, [cs:keyBuff]
		mov	bx, [cs:doublekeysPtr]
	@_keystatDoubleCheckKeysLoop:
		cmp	[(keyTemplates cs:bx).shortcut], NULL
			ohYes	@_keystatDoubleShortcutNotAvailable
		cmp	[(keyTemplates cs:bx).shortcut], dx
			ohYes	@_keystatDoubleShortcutAvailable
		add	bx, size keyTemplates
		jmp	@_keystatDoubleCheckKeysLoop
	@_keystatDoubleShortcutAvailable:
        	REM	-- clear first our own keyboard buffer
		mov	[cs:keyBuff], 0000h
		mov	bx, [(keyTemplates cs:bx).textToInsertOffset]
		mov	[cs:ptrToText], bx
		mov	[cs:stuffing], TRUE
		jmp	@_keystatDoubleCheckKeysStop
	@_keystatDoubleShortcutNotAvailable:
		mov	[cs:ptrToText], 0
		mov	[cs:stuffing], FALSE
		jmp	@_keystatDoubleCheckKeysStop
	@_keystatDoubleCheckKeysStop:
		pop	bx
		pop	dx

		cmp	[cs:stuffing], TRUE
		je	@_fakekeystat

		push	dx
		push	bx
		mov	dl, [cs:secondKeybuff]
		mov	bx, [cs:singlekeysPtr]
	@_keystatSingleCheckKeysLoop:
		cmp	[(keyTemplates cs:bx).secondKey], NULL
			ohYes	@_keystatSingleShortcutNotAvailable
		cmp	[(keyTemplates cs:bx).secondKey], dl
			ohYes	@_keystatSingleShortcutAvailable
		add	bx, size keyTemplates
		jmp	@_keystatSingleCheckKeysLoop
	@_keystatSingleShortcutAvailable:
        	REM	-- clear first our own keyboard buffer
		mov	[cs:keyBuff], 0000h
		mov	bx, [(keyTemplates cs:bx).textToInsertOffset]
		mov	[cs:ptrToText], bx
		mov	[cs:stuffing], TRUE
		jmp	@_keystatSingleCheckKeysStop
	@_keystatSingleShortcutNotAvailable:
		mov	[cs:ptrToText], 0
		mov	[cs:stuffing], FALSE
		jmp	@_keystatSingleCheckKeysStop
	@_keystatSingleCheckKeysStop:
		pop	bx
		pop	dx

		cmp	[cs:stuffing], TRUE
		je	@_fakekeyStat

                mov	[cs:keyBuff], 0
		stz
		retf	2

;............................................................................
	@_fakeKeystat:
		push	bx
		mov	bx, [cs:ptrToText]
		mov	al, [byte cs:bx]

		mov	ah, 0
		cmp	al, NULL
			ohNo	@_continueKeystatOrdinaryKey
		mov	[cs:stuffing], FALSE
		stz
		retf	2


	@_continueKeystatOrdinaryKey:
		jmp     @_keystatInsert

	@_keystatInsert:
		pop	bx
		REM	-- clear zero meaning simulate key availability
		clz

		retf	2


;............................................................................
	@_keystatShortcutNotTriggerBackToCaller:
		clz
		retf	2

;............................................................................
	@_keystatBackToCaller:
		retf	2

;............................................................................
	@_doOld:
		jmp	[cs:preAKAddr]
        endp


        tsrSize = ( ( $ - start ) + 256 + 16 ) / 16







;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
	proc    freeEnvironment

        REM     Output Register(s):
	REM             ES - segment address of the Environment

;............................................................................

                push    ax

                mov     ah, DOS_RELEASE_MEM
		mov     es, [ds:ENVIRONMENT_SEGMENT]
                int     21h

                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isTSRInMem?

        REM     Output Register(s):
        REM         ES - segment address of the TSR

        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne

;............................................................................

		push	si
		push	di
                push	ax
		push	cx

                mov     ah, DOS_INTR_GET_VECT
		mov     al, KB_SOFTWARE_INTR
                int     21h

                REM     -- 8 is an arbitrary value, it can be
                REM        any number greater than one, but for
		REM        the program to surely detect if TS
		REM        signature really match we choose
		REM	   more or less 8
                mov     cx, 8
                mov     si, offset tsrMarker
                mov     di, bx
                REM     -- we add 3 to DI because the tsrmarker offset
                REM        from the beginning of jmp instruction is 3
                add     di, 3
                cld
                repe    cmpsb
                        ohYes   @_alreadyInMem

                jmp     @_notInMem
;............................................................................
        @_notInMem:
                REM     -- the ff. will force clear the equality
                cle
                jmp     @_finally

;............................................................................
        @_alreadyInMem:
                ste
                jmp     @_finally


;............................................................................
	@_finally:
		pop	cx
		pop	ax
		pop	di
		pop	si
                ret
        endp

;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    removeTSRInMem

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when SUCCESS   -- z / e
        REM             Clear when FAILED  -- nz / ne
        REM         Carry Flag:
        REM             Set when FAILED    -- c
        REM             Clear when SUCCESS -- nc


;............................................................................
                push    ax
;............................................................................
        @_tryRelease:
                mov     ah, DOS_RELEASE_MEM
                int     21h
                jc      @_clearEqual

                REM     -- success removing
                ste
                jmp     @_finally

;............................................................................
        @_clearEqual:
                REM     -- no success in removing
                cle
                jmp     @_finally

;............................................................................
        @_finally:
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    putCodeInMem

        REM     -- returns nothing.
        REM        this function does not return to the calling function
        REM        instead it directly returns to DOS
        REM        so it is okay not to preserve used registers

;............................................................................
		REM     -- we have no use for environment strings
		call    freeEnvironment

                mov     ah, DOS_GET_BUSY_FLAG_ADDR
                int     21h
                mov     [dosBusyFlagOfs], bx
		mov     [dosBusyFlagSeg], es

                mov     ah, DOS_INTR_GET_VECT
                mov     al, KB_SOFTWARE_INTR
                int     21h

                mov     [preAKOffset], bx
		mov     [preAKSegment], es


                mov     ah, DOS_INTR_SET_VECT
		mov     al, KB_SOFTWARE_INTR
                mov     dx, offset keyIntercept
		int     21h

                mov     ah, DOS_KEEP_TSR
                mov     dx, tsrSize
                int     21h

        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineEmpty?

        REM     -- returns equal(e/z) when true otherwise not equal(ne/nz)

;............................................................................
		REM     -- do we have a command line? test the length
		pusha
		cmp     [byte es:COMMAND_LINE_LEN_OFFSET], 00h
			jz	@_noCommandLine

		mov	di, 81h
		mov	ch, 0
		mov	cl, [byte es:80h]
		mov	al, ' '
		cld
		repe	scasb
			je	@_noCommandLine


		clz
		jmp	@_finally

	@_noCommandLine:
		ste
		jmp	@_finally

	@_finally:
		popa
                ret
        endp



;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    isCommandLineSlashU?

        REM     Output Register(s):
        REM         Zero/Equality Flag:
        REM             Set when TRUE    -- z / e
        REM             Clear when FALSE -- nz / ne


;............................................................................
                push    ax
                push    cx
                push    dx
                push    di
                push    si

                REM    -- scan for slash character
                mov     al, '/'
                mov     ch, 0
                mov     cl, [byte ds:80h]
                mov     di, 81h
                cld
                repne   scasb
                        ohNo    @_finally

                REM     -- convert the next character to uppercase
                mov     ax, DOS_CHAR_UP_CASE
                mov     dl, [byte di]
                int     21h


                mov     [byte di], dl
                cmp     [byte di], 'U'
                        ohNo    @_finally

                jmp     @_finally

;............................................................................
        @_finally:
                pop     si
                pop     di
                pop     dx
                pop     cx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    restoreOldRoutine

        REM     Input Register(s):
        REM             ES - segment address of the TSR

        REM     Output Register(s):
        REM             None

;............................................................................
                push    ax
                push    dx
                push    ds

                mov     ax, [es:preAKSegment]
                mov     ds, ax

                mov     ah, DOS_INTR_SET_VECT
                mov     al, KB_SOFTWARE_INTR
                mov     dx, [es:preAKOffset]
		int     21h

                pop     ds
                pop     dx
                pop     ax
                ret
        endp


;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-




	proc	motd
            	newLine

                jmp     @_motdj

                @_michael db 'Vzos~l;R5;Yn~u'

                @_terminator db '$'

        

        @_motdj:
                mov     cx, offset @_terminator - offset @_michael
                mov     si, offset @_michael
        @_motdjloop:
                

                xor     [byte ptr si], 27
                inc     si
                loop    @_motdjloop




                mov     ah, 09h
                mov     dx, offset @_michael
                int     21h


		newLine
		newLine
		puts	'"From the programmer''s point of view,'
		newLine
		puts	'     a user is just a peripheral that types'
		newline
		puts	'     in when the program issues a read request."'
		newLine
		newLine
		puts	'   -- From Java Book(the source Sun)'
		newLine
		ret
	endp


                




;--)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)---)-
        proc    main
		call	getInitialMCBSegment

        @_tryCommandLine:
                call    isCommandLineEmpty?
                        ohYes    @_tryInstall

                call    isCommandLineSlashU?
                        ohYes    @_tryUninstall

                puts    'Unknown Option'
                jmp     @_finally

;............................................................................
        @_tryUninstall:
                call    isTSRInMem?
                        ohNo    @_tsrNotYetInMem

                call    removeTSRInMem
                        ohNo    @_cantRemoveTSRInMem

                call    restoreOldRoutine

		puts    'Augmented Keys Unloaded'
                jmp     @_finally



;............................................................................
        @_cantRemoveTSRInMem:
		puts    'Cant''t Remove Augmented Keys in Memory'
                jmp     @_finally

;............................................................................
        @_tryInstall:
                call    isTSRInMem?
			ohYes   @_tsrAlreadyInMem

		puts	'Augmented Keys Loaded'

		call 	motd
                call    putCodeInMem
                jmp     @_finally

;............................................................................
        @_tsrNotYetInMem:
		puts    'Augmented Keys not yet in memory'
                jmp     @_finally

;............................................................................
        @_tsrAlreadyInMem:
		puts    'Augmented Keys already in memory'
                jmp     @_finally

;............................................................................
	@_finally:
		call	motd
                mov     ah, DOS_EXIT
                int     21h

;............................................................................
        endp
ends

end     start



; MICHAEL I. BUEN
