	.MODEL	TINY
	INCLUDE DOS.INC

SW	EQU		036H			;Скан-код "рус/лат"-RightShift
BORDER	EQU		001H			;Цвет рамки экрана

;Макрос для сохранения регистров в стеке.
@PUSH	MACRO	REGLIST
	IRP	REG, <REGLIST>
		PUSH	REG
	ENDM
ENDM

;Макрос для восстановления регистров из стека.
@POP	MACRO	REGLIST
	IRP	REG, <REGLIST>
		POP	REG
	ENDM
ENDM

SCODE	SEGMENT PARA 'CODE'
	ORG	100H
BEGIN:
	JMP	START
;================================= Данные ================================
;Указатель на старый оработчик INT 009H
PINT9	LABEL DWORD
OINT9	DW 		?
SINT9	DW 		?
;Указатель на старый обработчик INT 010H
PINT10	LABEL DWORD
OINT10	DW		?
SINT10	DW		?
;Указатель на старый обработчик INT 02FH
PINT2F	LABEL DWORD
OINT2F	DW		?
SINT2F	DW		?
;MUX резидентной программы
MUX_ID	DB		?
;Свободный MUX
MUX_0	DB		0
;Флаг режима рус/лат: 0 - лат, 1 - рус
ACT	DB		0
;Флаг нажатия переключателя
SWPR	DB		0
;Таблица символов для клавиш в режиме рус
SCAN1	DB		"йцукенгшщзхъ"		;10...1B\
SCAN2	DB		"фывапролджэ"		;1E...28 }клавиша
SCAN3	DB		"ячсмитьбю"		;2C...34/
GRAPH	DB		"┌┬┐╓╥╖│─║═[]"		;       \
	DB		"├┼┤╟╫╢░▒▓;'"		;  -//-  }Alt+клавиша
	DB		"└┴┘╙╨╜√,."		;       /
	DB		"╔╦╗╒╤╕█▄▌▐{}"		;       \
	DB		'╠╬╣╞╪╡▀■¤:"'		;  -//-  }Alt+Shift+клавиша
	DB		"╚╩╝╘╧╛№<>"		;       /
;F8X8 - Шрифт 8x8 с кириллицей для 50-строчных текстовых
;режимов и графических режимов с высотой экрана - 480 точек
INCLUDE	8X8RUST.INC
;F8X16 - Шрифт 8x16 с кириллицей для 25-строчных текстовых
;режимов и графических режимов с высотой экрана - 200 точек
INCLUDE	8X16RUST.INC
;F8X14 - Шрифт 8x14 с кириллицей для 43-строчных текстовых
;режимов и графических режимов с высотой экрана - 350 точек
;INCLUDE	8X14RUS.INC
;=========================================================================

;======================= Резидентные подпрограммы ========================

;----------- Обработчик аппаратного клавиатурного прерывания -------------
INT9	PROC	NEAR
	PUSH		AX
	IN		AL, 060H
	CMP		AL, SW
	JE		LSD			;Нажали переключатель
	CMP		AL, SW OR 080H
	JE		LSU			;Отпустили переключатель
	TEST		CS:SWPR, 001H
	JZ		L0			;Нажат был только переключатель
	OR		CS:SWPR, 080H		;Переключатель нажат с чем-то
L0:
	CMP		CS:ACT, 0
	JE		L1			;Режим лат
	@PUSH		<ES, AX>
	XOR		AX, AX
	MOV		ES, AX
	TEST		BYTE PTR ES:[00417H], 004H
	@POP		<AX, ES>
	JZ		LCYR			;Ctrl отпущен
L1:
	POP		AX
	JMP		CS:PINT9		;Режим лат
LSD:
	MOV		CS:SWPR, 001H
	POP		AX
	JMP		CS:PINT9
LSU:
	TEST		CS:SWPR, 080H
	JNZ		LSU1			;Shift был нажат с чем-то
	NOT		CS:ACT			;Поменяли режим рус/лат
	PUSH		BX
	MOV		AX, 01001H
	MOV		BH, CS:ACT
	AND		BH, BORDER
	INT		010H			;Нарисовали рамку
	POP		BX
LSU1:
	MOV		CS:SWPR, 0
	POP		AX
	JMP		CS:PINT9
LCYR:
	@PUSH		<BX, ES>
	XOR		AH, AH
	CMP		AL, 010H
	JC		LS2
	CMP		AL, 01BH+1
	JNC		LS2
	MOV		BX, OFFSET SCAN1
	ADD		BX, AX
	SUB		BX, 010H
	JMP		LSFND
LS2:
	CMP		AL, 01EH
	JC		LS3
	CMP		AL, 028H+1
	JNC		LS3
	MOV		BX, OFFSET SCAN2
	ADD		BX, AX
	SUB		BX, 01EH
	JMP		LSFND
LS3:
	CMP		AL, 02CH
	JC		LS4
	CMP		AL, 034H+1
	JNC		LS4
	MOV		BX, OFFSET SCAN3
	ADD		BX, AX
	SUB		BX, 02CH
	JMP		LSFND
LS4:
	@POP		<ES, BX, AX>
	JMP		CS:PINT9		;Не "русская" клавиша
LSFND:
	XOR		AX, AX
	MOV		ES, AX
	MOV		AH, ES:[0041AH]		;Голова буфера клавиатуры
	MOV		AL, ES:[0041CH]		;Хвост буфера клавиатуры
	PUSHF
	CALL		CS:PINT9
	DEC		AH
	DEC		AH
	CMP		AH, AL
	JE		LEND			;Переполнение буфера клавиат.
	CMP		AH, 01CH
	JNE		LNBO
	CMP		AL, 03CH
	JE		LEND			;Переполнение буфера клавиат.
LNBO:
	TEST		BYTE PTR ES:[00417H], 008H
	JZ		LTEXT			;Alt не нажат
	ADD		BX, 00020H
	TEST		BYTE PTR ES:[00417H], 003H
	JZ		LALT			;Shift не нажат
	ADD		BX, 00020H
LALT:
	MOV		AH, CS:[BX]		;Псевдографический символ
	JMP		LDNC
LTEXT:
	MOV		AH, CS:[BX]		;Кириллический символ
	MOV		BL, ES:[00417H]
	AND		BL, 043H
	CMP		BL, 040H
	JE		LUPC			;Нажата Caps
	JNC		LDNC			;Нажаты Caps и Shift
	TEST		BL, 003H
	JZ		LDNC			;Не нажаты: ни Caps, ни Shift
LUPC:
	CMP		AH, "р"
	JC		LUPC2
	SUB		AH, 030H
LUPC2:
	SUB		AH, 020H		;Символ в верхнем регистре
LDNC:
	MOV		BL, AL
	MOV		BH, 004H
	MOV		ES:[BX], AH		;Записали символ
	INC		BX
	MOV		Byte Ptr ES:[BX], 0	;Записали нулевой скан-код
LEND:
	@POP		<ES, BX, AX>
	IRET
INT9	ENDP
;-------------------------------------------------------------------------

;--------- Новый обработчик сервисного прерывания BIOS - 010H ------------
;-------------- Устанавливает шрифт для текстового режима ----------------
STFONT	PROC	NEAR
	MOV		DX, 128
	MOV		CX, 48
	XOR		BL, BL
	MOV		AX, CS
	MOV		ES, AX
	MOV		AX, 01100H
	PUSHF
	CALL		CS:PINT10
	ADD		BP, 384
	CMP		BP, OFFSET F8X8 + 384
	JE		STF_NEXT
	ADD		BP, 384
STF_NEXT:
	MOV		DX, 224
	MOV		CX, 16
	XOR		BL, BL
	MOV		AX, CS
	MOV		ES, AX
	MOV		AX, 01100H
	PUSHF
	CALL		CS:PINT10
	MOV		AX, 01001H
	MOV		BH, CS:ACT
	AND		BH, BORDER
	PUSHF
	CALL		CS:PINT10		;Нарисовали рамку
	RET
STFONT	ENDP
;-------------------------------------------------------------------------
;------------------- Собственно сам новый обработчик ---------------------
INT10	PROC	NEAR
	OR		AH, AH
	JNZ		IT_FONT			;Не ф-ция выбора режима
	PUSH		AX
	AND		AL, 07FH		;Сброс бита сохранности памяти
	CMP		AL, 4
	JC		TMODE
	CMP		AL, 7
	JE		TMODE
	POP		AX
	JMP		CS:PINT10		;Другие режимы (не нужные нам)
IT_FONT:
	CMP		AH, 011H
	JE		IT_FSEL			;Ф-ция выбора шрифта
	JMP		CS:PINT10		;Другие ф-ции
TMODE:
	PUSHF					;
	CALL		CS:PINT10		;Установить текстовый режим
	@PUSH		<BX, CX, DX, ES, BP>
TF_16:
	MOV		BP, OFFSET F8X16	;Шрифт
	MOV		BH, 16			;8x16
TF_SET:
	CALL		STFONT			;Установить текстовый шрифт
	@POP		<BP, ES, DX, CX, BX, AX>
	IRET
IT_FSEL:
	PUSH		AX
	CMP		AL, 012H
	JE		TFONT			;Установка текстовых
	CMP		AL, 014H
	JE		TFONT			;шрифтов
;	CMP		AL, 022H
;	JE		EMD_SF			;Графич. шрифт 8x14
	POP		AX			;Другие подфункции -
	JMP		CS:PINT10		;старые
TFONT:
	PUSHF					;Уст. старый вариант шрифта -
	CALL		CS:PINT10		;без этого творятся чудеса
	@PUSH		<BX, CX, DX, ES, BP>
	MOV		BP, OFFSET F8X8
	MOV		BH, 8
	CMP		AL, 012H
	JE		TF_SET			;Шрифт 8x8
	JMP		TF_16			;Шрифт 8x16
INT10	ENDP
;-------------------------------------------------------------------------

;----------------------- Новый обработчик INT 02FH -----------------------
INT2F	PROC	NEAR
	CMP		AH, CS:MUX_ID
	JE		OUR_MUX			;Наш MUX
	JMP		CS:PINT2F
OUR_MUX:
	OR		AL, AL
	JZ		CHECK			;Ф-ция проверки установки
	JMP		CS:PINT2F
CHECK:
	NOT		AL			;Признак установки - AL=0FFH
	MOV		DX, "CY"
	MOV		CX, "R0"		;CX и DX - уникальный код
	MOV		BX, CS			;BX - сегмент драйвера
	IRET
INT2F	ENDP
;-------------------------------------------------------------------------
;=========================================================================

;========================= Сообщения программы ===========================
M_PROG	DB		"Cyrillic driver for keyboard & VGA-display "
	DB		"installed.",13,10
	DB		"Author: Frunze A.A. (C) '97",13,10
	DB		"Rus/Lat key - <Right Shift>",13,10,"$"
M_RMV	DB		"CYRT removed.",13,10,"$"
M_RERR	DB		"Can't remove CYRT from memory!",13,10,"$"
;=========================================================================

;------------------- П/П проверки установки драйвера ---------------------
FIND	PROC	NEAR
	MOV		MUX_ID, 0C0H
F_NEXT:
	MOV		AH, MUX_ID
	XOR		AL, AL
	INT		02FH
	CMP		AL, 0FFH
	JE		F_CHECK			;Что-то установлено
	MOV		AH, MUX_ID
	MOV		MUX_0, AH		;Свободный MUX
F_OTHER:
	INC		MUX_ID
	JNZ		F_NEXT
	CLC					;Наш драйвер не установлен
	RET
F_CHECK:
	CMP		DX, "CY"
	JNE		F_OTHER
	CMP		CX, "R0"
	JNE		F_OTHER
	STC					;Наш драйвер уже установлен
	RET
FIND	ENDP
;-------------------------------------------------------------------------

;--------- Подпрограмма инициализации шрифта для текущего режима ---------
ACTIVAT	PROC	NEAR
	XOR		AX, AX
	MOV		ES, AX
	MOV		AL, ES:[00484H]		;Кол-во строк на эране-1
	CMP		AL, 24
	JNE		AL1			;Не 24
	MOV		AX, 01114H
	XOR		BL, BL
	INT		010H			;Установить шрифт 8x16
	JMP		AL2
AL1:
	CMP		AL, 49
	JNE		AL2			;Не 49
	MOV		AX, 01112H
	XOR		BL, BL
	INT		010H			;Установить шрифт 8x8
AL2:
	RET
ACTIVAT	ENDP
;-------------------------------------------------------------------------

;============================= Программа =================================
START:
	MOV		ES, WORD PTR CS:[02CH]  ;Освободили блок переменных
	@FREEBLOCK				;окружения драйвера
	@GETINT		009H
	MOV		CS:OINT9, BX
	MOV		CS:SINT9, ES		;Получили INT 009H
	@GETINT		010H
	MOV		CS:OINT10, BX
	MOV		CS:SINT10, ES		;Получили INT 010H
	@GETINT		02FH
	MOV		CS:OINT2F, BX
	MOV		CS:SINT2F, ES		;Получили INT 02FH
	CALL		FIND
	JNC		L_INST			;Наш драйвер еще не установлен
L_REMOVE:
	MOV		AX, CS:SINT9
	CMP		AX, BX
	JNE		L_RERR			;Не равны сегменты
	MOV		AX, CS:SINT10
	CMP		AX, BX
	JNE		L_RERR			;Не равны сегменты
	MOV		AX, CS:SINT2F
	CMP		AX, BX
	JNE		L_RERR			;Не равны сегменты
	MOV		AX, CS:OINT9
	CMP		AX, OFFSET INT9
	JNE		L_RERR			;Не равны смещения
	MOV		AX, CS:OINT10
	CMP		AX, OFFSET INT10
	JNE		L_RERR			;Не равны смещения
	MOV		AX, CS:OINT2F
	CMP		AX, OFFSET INT2F
	JNE		L_RERR			;Не равны смещения
	MOV		DI, OFFSET PINT9
	MOV		DX, ES:[DI]
	MOV		AX, ES:[DI+2]
	MOV		DS, AX
	MOV		AX, 02509H
	INT		021H			;Восстановили INT 009H
	MOV		DX, ES:[DI+4]
	MOV		AX, ES:[DI+6]
	MOV		DS, AX
	MOV		AX, 02510H
	INT		021H			;Восстановили INT 010H
	MOV		DX, ES:[DI+8]
	MOV		AX, ES:[DI+10]
	MOV		DS, AX
	MOV		AX, 0252FH
	INT		021H			;Восстановили INT 02FH
	MOV		AX, CS
	MOV		DS, AX
	@FREEBLOCK				;Освободили память
	JC		L_RERR			;Ошибка освобождения
	@SHOWSTR	M_RMV			;Сообщение об успешн. выгрузке
	CALL		ACTIVAT
	JMP		L_EXIT
L_RERR:
	@SHOWSTR	M_RERR			;Сообщение об ошибке выгрузки
	JMP		L_EXIT
L_INST:
	MOV		AH, MUX_0
	MOV		MUX_ID, AH		;Заняли свободный MUX
	MOV		AX, CS
	MOV		DS, AX
	@SETINT		009H, INT9		;Установили новый INT 009H
	@SETINT		010H, INT10		;Установили новый INT 010H
	@SETINT		02FH, INT2F		;Установили новый INT 02FH
	@SHOWSTR	M_PROG			;Сообщение о резидентности
	CALL		ACTIVAT
	MOV		DX, OFFSET M_PROG	;Оставили программу до M_PROG
	INT		027H			;резидентной и вышли в DOS
L_EXIT:
	@EXIT		0			;Выход в DOS
;=========================================================================
SCODE	ENDS
END	BEGIN
