
;            RESID12.ASM - программа к Главе № 12

; (С) Авторские права на файлы-приложения принадлежат автору книги
; "Ассемблер? Это просто! Учимся программировать под MS-DOS"
; Автор: Калашников Олег Александрович (e-mail: Assembler@Kalashnikoff.ru)
;	 http://www.Kalashnikoff.ru

; --- Ассемблирование (получение *.com файла) ---
;При использовании MASM 6.11 - 6.13:
;ML.EXE resid12.asm /AT

;При использовании TASM:
;TASM.EXE resid12.asm
;TLINK.EXE resid12.obj /t/x


CSEG segment
assume cs:CSEG, ds:CSEG, ss:CSEG, es:CSEG
org 100h

Begin:
       jmp Init ;На метку инициализации 


; === Процедура обработки 10h прерывания ===
Int_10h_proc proc
      pushf ;Сохраним флаги в стеке, т.к. они поменяются...

      cmp ax,8899h	;Проверим на повторную загрузку в память (см. ниже)
      jne Next_test	;Если не проверка, то смотрим дальше... 

      xchg ah,al	;Меняем местами AH и AL (признак того, что мы в памяти
			;что-то вроде ответного сигнала)
      popf  ;Выровним стек
      iret  ;Выйдем из прерывания (вернемся в нашу программу)
            ;AX теперь равен 9988h !!!

Next_test:
      cmp ah,88h	;Это вызов нашей функции?
      jne Go_10h	;Если нет - уйдем в оригинальный обработчик 10h 

      push es		;Сохраним ES...

      mov ax,0B800h	;...и занесем в него сегмент 0-ой страницы дисплея
      mov es,ax

      xor di,di		;Подготвимся к чистке экрана (равносильно mov di,0)
      mov cx,2000	;Просто "забьем" его пробелами.
      mov ax,0720h	;Будем заносить: код цвета (07) / символ пробела (20h)
      rep stosw		;"Забиваем"... Очень быстро...

      mov di,1014	;Будем выводить вот отсюда (примерно центр экрана)
      mov ah,14		;С такими атрибутами (светло-желтый)

Next_sym:
      lodsb		;Заносим в AL очередной символ (один байт, т.к. lodsB),
			;Расположенный по адресу DS:SI, увеличив затем SI
			;на единицу

      cmp al,0		;Конец строки?
      jz Stop_out	;Да - на выход... 

      stosw	;Заносим по адресу ES:DI значение из AX и увеличиваем DI на 2
		;Здесь заносим слово, т.к. stosW, поэтому на два и
		;увеличивается значение DI

      jmp short Next_sym ;Следующий символ... 
			 ;SHORT (короткий) - делает в некоторых случаях
			 ;команду jmp на один байт короче.
			 ;Работает ТОЛЬКО в том случае, если между меткой
			 ;и jmp расстояние не более 127 байт!

Stop_out:
      pop es	;Если достигли конца строки, то восстановим регистры,
      popf	;выровнив стек...
      iret	;...и выйдем из прерывания

Go_10h:
      popf	;Выровним стек
      jmp dword ptr cs:[Int_10h_vect]	;"Прыгаем" на оригинальный обработчик

Int_10h_vect dd ? ;Здесь хранится адрес оргинального обработчика
Int_10h_proc endp


; ===============================================================
; Инициализация (подготовка и настройка резидента)
Init:
       mov ax,8899h ;Проверим, в памяти ли мы уже или еще нет
       int 10h      ;8899h - наш позывной
       cmp ax,9988h ;Отозвался обработчик (AH/AL должены поменяться местами)?
       jne Set_resident

       mov ah,9	    ;Если в памяти, то выведем соответствующее сообщение
       mov dx,offset In_memory ;Зачем нас двое в памяти?
       int 21h

       ret	    ;...И вернемся в DOS


Set_resident:		;Если нас в памяти нет, то установим резидент
       mov ax,3510h
       int 21h		;Получим и сохраним адрес (вектор) 10h прерывания
       mov word ptr Int_10h_vect,bx	;Вначале младшее слово (смещение)
       mov word ptr Int_10h_vect+2,es	;Затем старшее (сегмент)

       mov ax,2510h
       mov dx,offset Int_10h_proc
       int 21h		;"Повесим" нашу процедуру на 10h прерывание

       mov dx,offset Init
       int 27h		;Оставим программу резидентной в памяти.

In_memory db 'Мы уже в памяти!!!!$'

CSEG ends
end Begin
