	.MODEL	TINY
	INCLUDE DOS.INC

MCNT	EQU		1500

;Макрос для сохранения регистров в стеке.
@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 		?
;Кол-во нажатий
CNT	DW		0
;Флаг, определяющий активность
ACT	DB		0
;Смещение экрана
TOFS	DW		?
;Сегмент экрана
TSEG	DW		?
;Буфер для сохранения строки экрана
SBUF	DW		80 DUP(?)
;Сообщения-приколы
MSG	DB		000H,"ЛЕНИН ЖИВЕТ В НАШИХ СЕРДЦАХ!!!", 0FFH
	DB		001H,"ГИТЛЕР - КАПУТ!!!", 0FFH
	DB		002H,"КАЖДОМУ СТУДЕНТУ К 2000-МУ ГОДУ ПО ДИПЛОМУ!!!", 0FFH
	DB		003H,"С НОВЫМ ГОДОМ ВАС, ДОРОГИЕ ТОВАРИЩИ!!!", 0FFH
	DB		004H,"ПРИВЕТ ОТ ФАНТОМАСА!!!", 0FFH
	DB		005H,"ОБОЖАЮ ЛАБЫ!!!", 0FFH
	DB		006H,"2x2=5 - ДУРИТ СОПРОЦЕССОР, ОЙ ДУРИТ...", 0FFH
	DB		007H,"ДАВАЙ ЗНАКОМИТЬСЯ. Я - КОМПЬЮТЕР, А ТЫ КТО?", 0FFH
	DB		008H,"ВСТАВЬ В ДИСКОВОД 5 БАКСОВ, НЕТО ОБИЖУСЬ...", 0FFH
	DB		009H,"ПЕРЕСТАНЬ ЖАТЬ КНОПКИ, ДАЙ ОТДОХНУТЬ!!!", 0FFH
	DB		00AH,"НЕ ХОЧЕШЬ СДАВАТЬ СЕССИЮ, А ПРИДЕТСЯ!!!", 0FFH
	DB		00BH,"ДАВАЙ ПО МАЛЕНЬКОЙ ЧТО-ЛИ?", 0FFH
	DB		00CH,"ДАЗДРАВСТВУЕТ МАТАН!!!", 0FFH
	DB		00DH,"СЛЫШИШИШЬ, HOMO SAPIENS, ТЫ СЛУЧАЙНО НЕ УСТАЛ?", 0FFH
	DB		00EH,"ЧТО-ТО ВИНЧЕСТЕР ЗАУРЧАЛ... У ТЕБЯ ЕСТЬ БУТЕРБРОД?", 0FFH
	DB		00FH,"ФИЗФАК - ЧЕМПИОН!!!", 0FFH
	DB		010H,"КУПИ МНЕ CD-ROM, А?", 0FFH
	DB		011H,"ТАК ХОЧЕТСЯ В ОТПУСК... МАХНЕМ НА МОРЕ?", 0FFH
	DB		012H,"Я КРУТОЙ PENTIUM PRO!!!", 0FFH
	DB		013H,"ЗАПЕВАЙ: В ЛЕСУ РОДИЛАСЬ ЕЛОЧКА, В ЛЕСУ ОНА РОСЛА...", 0FFH
	DB		014H,"ЧТО ЗАВЕЩАЛ ВЕЛИКИЙ ЛЕНИН? ПРАВИЛЬНО,-УЧИТЬСЯ, УЧИТЬСЯ И УЧИТЬСЯ!", 0FFH
	DB		015H,'"КАЖДАЯ КУХАРКА ДОЛЖНА ЗНАТЬ КАК УПРАВЛЯТЬ ГОСУДАРСТВОМ!!!"', 0FFH
PCNT	EQU		016H
;=========================================================================

;======================= Резидентные подпрограммы ========================
;-------------- Сохраняет в буфере верхнюю строку экрана -----------------
SAVE	PROC	NEAR
	MOV		AX, CS:TSEG
	MOV		DS, AX
	MOV		SI, CS:TOFS
	MOV		AX, CS
	MOV		ES, AX
	MOV		DI, OFFSET SBUF
	MOV		CX, 80
	CLD
	REP MOVSW
	RET
SAVE	ENDP
;-------------------------------------------------------------------------

;------------ Восстанавливает из буфера верхнюю строку экрана ------------
REST	PROC	NEAR
	MOV		AX, CS
	MOV		DS, AX
	MOV		SI, OFFSET SBUF
	MOV		AX, CS:TSEG
	MOV		ES, AX
	MOV		DI, CS:TOFS
	MOV		CX, 80
	CLD
	REP MOVSW
	RET
REST	ENDP
;-------------------------------------------------------------------------

;----------- Возвращает случайное число в заданном диапазоне -------------
RANDOM		PROC	NEAR
;IN:	AL=LIMIT
;OUT:	AX=[0,LIMIT)
	MOV		CL, AL
	PUSH		DS
	XOR		AX, AX
	MOV		DS, AX
	MOV		DX, DS:[0046CH]
	POP		DS
	MOV		AX, DX
	MOV		DH, 3FH
	CMP		CL, 64
	JGE		RL
	SHR		DH, 1
	CMP		CL, 32
	JGE		RL
	SHR		DH, 1
	CMP		CL, 16
	JGE		RL
	SHR		DH, 1
	CMP		CL, 0
	JGE		RL
	SHR		DH, 1
RL:
	AND		AH, DH
	XOR		DX, DX
	XOR		CH, CH
	DIV		CX
	MOV		AX, DX
	RET
RANDOM		ENDP
;-------------------------------------------------------------------------

;------------------ Находит строку-прикол по ее номеру -------------------
FINDSTR	PROC	NEAR
	MOV		CX, CS
	MOV		ES, CX
	MOV		DI, OFFSET MSG
	MOV		CX, 65535
	CLD
	REPNE SCASB
	MOV		AX, ES
	MOV		DS, AX
	MOV		SI, DI
	RET
FINDSTR	ENDP
;-------------------------------------------------------------------------

;---------------- Возвращает длину соответствующей строки ----------------
STRLEN	PROC	NEAR
	MOV		CX, 80
	CLD
	MOV		AL, 0FFH
	REPNE SCASB
	MOV		CX, DI
	SUB		CX, SI
	DEC		CX
	RET
STRLEN	ENDP
;-------------------------------------------------------------------------

;----------------------- Выводит сообщение-прикол ------------------------
WRITE	PROC	NEAR
	MOV		AX, CS:TSEG
	MOV		ES, AX
	MOV		DI, CS:TOFS
	MOV		AX, 80-4
	SUB		AX, CX
	AND		AX, NOT 1
	ADD		DI, AX
	CLD
	MOV		AX, (4*16+15)*256
	STOSW
	STOSW
W_CYC:
	LODSB
	STOSW
	LOOP		W_CYC
	MOV		AX, (4*16+15)*256
	STOSW
	STOSW
	RET
WRITE	ENDP
;-------------------------------------------------------------------------

;----------- Обработчик аппаратного клавиатурного прерывания -------------
INT9	PROC	NEAR
	CMP		CS:ACT, 0
	JE		L_NACT			;Не активна
	PUSH		AX
	IN		AL, 060H
	CMP		AL, 081H
	JE		L_ACT			;Активна и ESC отпущена
	IN		AL, 061H
	MOV		AH, AL
	OR		AL, 080H
	OUT		061H, AL		;Деблокировали клавиатуру
	MOV		AL, AH
	OUT		061H, AL		;Восстан. сост. клав. порта
	MOV		AL, 020H
	OUT		020H, AL		;Обработали аппаратное перыв.
	POP		AX
	IRET
L_ACT:
	@PUSH		<BX, CX, DX, SI, DI, DS, ES>
	CALL		REST
	MOV		CS:CNT, 0
	MOV		CS:ACT, 0
	JMP		L_END
L_NACT:
	PUSH		AX
	IN		AL, 060H
	MOV		AH, AL
	AND		AH, 07FH
	TEST		AL, 080H
	JNZ		L0			;Отпущена
	INC		CS:CNT			;Увеличили число нажатий
	CMP		CS:CNT, MCNT
	JC		L0			;Рано прикалываться
	POP		AX
	JMP		L1
L0:
	POP		AX
	JMP		CS:PINT9		;Вызов старого обработчика
L1:
	@PUSH		<AX, BX, CX, DX, SI, DI, DS, ES>
	MOV		AH, 00FH
	INT		010H
	MOV		CS:TSEG, 0B800H		;Сегмент для цветных режимов
	CMP		AL, 002H
	JE		L_GO
	CMP		AL, 003H
	JE		L_GO
	CMP		AL, 007H
	JE		L_GOM
	@POP		<ES, DS, DI, SI, DX, CX, BX, AX>
	JMP		CS:PINT9		;Если не нужный режим
L_GOM:
	MOV		CS:TSEG, 0B000H		;Сегмент для монохромного режима
L_GO:
	XOR		AX, AX
	MOV		DS, AX
	MOV		AX, DS:[0044EH]		;Смещение экрана
	MOV		CS:TOFS, AX
	MOV		CS:ACT, 1
	CALL		SAVE
	MOV		AL, PCNT
	CALL		RANDOM
	CALL		FINDSTR
	CALL		STRLEN
	CALL		WRITE
L_END:
	IN		AL, 061H
	MOV		AH, AL
	OR		AL, 080H
	OUT		061H, AL		;Деблокировали клавиатуру
	MOV		AL, AH
	OUT		061H, AL		;Восстан. сост. клав. порта
	MOV		AL, 020H
	OUT		020H, AL		;Обработали аппаратное перыв.
	@POP		<ES, DS, DI, SI, DX, CX, BX, AX>
	IRET
INT9	ENDP
;-------------------------------------------------------------------------

;Уникальная строка, для определения резидентности программы
UNIQ	DB		"LENIN версия 2.0. Автор: Фрунзе А.А. 1997 (C).",13,10,"$"
;=========================================================================

;========================= Сообщения программы ===========================
M_PROG	DB		"Жми кнопки, и через каждые 1500 нажатий ты увидишь "
	DB		"очередной прикол!!!",13,10
	DB		"Чтобы выгрузить LENIN'а запусти его еще раз.",13,10,"$"
M_RMV	DB		"LENIN выгружен из памяти.",13,10,"$"
M_RERR	DB		"Не возможно выгрузить LENIN'а из памяти!",13,10,"$"
;=========================================================================

;------------------ П/П нахождения резидентной копии ---------------------
FIND	PROC	NEAR
	MOV		AH, 052H
	INT		021H
	SUB		BX, 2
	MOV		AX, ES:[BX]
	MOV		ES, AX
	XOR		DI, DI
	XOR		DX, DX			;НЕТ РЕЗИДЕНТНОЙ КОПИИ
F_CYC:
	MOV		AX, CS
	CMP		AX, ES:[DI+1]
	JE		F_C_END
	@PUSH		<DS, ES, DI>
	MOV		AX, ES:[DI+1]
	MOV		DS, AX
	MOV		AX, CS
	MOV		ES, AX
	MOV		DI, OFFSET UNIQ
	MOV		SI, DI
	CLD
	MOV		CX, OFFSET M_PROG - OFFSET UNIQ
	REPE CMPSB
	JNE		F_NO
	NOT		DX			;ЕСТЬ РЕЗИДЕНТНАЯ КОПИЯ
	MOV		BX, DS			;BX = СЕГМЕНТ КОПИИ
	@POP		<DI, ES, DS>
	JMP		F_END
F_NO:
	@POP		<DI, ES, DS>
F_C_END:
	MOV		AL, ES:[DI]
	CMP		AL, 'Z'
	JE		F_END
	MOV		AX, ES
	MOV		BX, ES:[DI+3]
	ADD		AX, BX
	INC		AX
	MOV		ES, AX
	JMP		F_CYC
F_END:
	RET
FIND	ENDP
;-------------------------------------------------------------------------

;============================= Программа =================================
START:
	MOV		ES, WORD PTR CS:[02CH]
	@FREEBLOCK
	@SHOWSTR	UNIQ
	@GETINT		009H
	MOV		CS:OINT9, BX
	MOV		CS:SINT9, ES		;Получили INT 009H
	PUSH		ES
	CALL		FIND
	POP		ES
	OR		DX, DX
	JE		L_INST			;Уникальная строка не найдена
L_REMOVE:
	MOV		AX, CS:SINT9
	CMP		AX, BX
	JNE		L_RERR			;Не равны сегменты
	MOV		AX, CS:OINT9
	CMP		AX, OFFSET INT9
	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		AX, CS
	MOV		DS, AX
	@FREEBLOCK				;Освободили память
	JC		L_RERR			;Ошибка освобождения
	@SHOWSTR	M_RMV
	JMP		L_EXIT
L_RERR:
	@SHOWSTR	M_RERR
	JMP		L_EXIT
L_INST:
	MOV		AX, CS
	MOV		DS, AX
	@SETINT		009H, INT9		;Установили новый INT 009H
	@SHOWSTR	M_PROG
	MOV		DX, OFFSET M_PROG	;Оставили программу до M_PROG
	INT		027H			;резидентной и вышли в DOS
L_EXIT:
	@EXIT		0			;Выход в DOS
;=========================================================================
SCODE	ENDS
END	BEGIN
