Текстовый курсор в графическом режиме
В примере 5.26 для доступа к словам вектора используется регистр fs, поэтому его содержимое предварительно очищается. Затем в регистры ах и bx копируются первое и второе слово сохраненного ранее вектора ich. После запрещения прерываний содержимое регистров ах и bx копируется в слова 70h и 72h, и разрешаются прерывания. Вектор восстановлен, и можно завершать выполнение задачи.
Замечание
Для работы с векторами прерываний предназначены две специальные функции DOS (прерывания int 2lh). Функция Get Vector (код 35h) читает содержимое вектора, a Set vector (код 25h) записывает в вектор новое содержимое. Однако их применение просто не оправдано, в чем вы можете убедиться самостоятельно.
Прерывающая подпрограмма подсчитывает количество тиков таймера и, как только оно будет равно 9 (примерно через каждые 0.5 сек), изменяет текущее состояние курсора. Рисунок курсора находится на экране, только если задача работает с текстом. Поэтому необходим специальный признак, разрешающий или запрещающий изменение состояния курсора. Кроме того, нужен признак, позволяющий узнать, в каком состоянии находится курсор – включен или погашен (виден или не виден на экране).
Для хранения счетчика тиков и признаков в разделе данных задачи надо зарезервировать две однобайтовые переменные, имеющие следующие имена:
- Ntick db 9; – счетчик тиков таймера изменяется от 0 до 9
- CurStat db 0; – текущее состояние курсора изменяется от 0 до 3.
В байте CurStat используются только два младших бита. Нулевой бит разрешает (1) или запрещает (0) изменение состояния (мигание) курсора, он устанавливается в основной программе. Первый бит отражает текущее состояние курсора на экране: 0 – выключен, 1 – включен. Им управляет прерывающая подпрограмма, текст которой приведен в примере 5.27.
Пример 5.27. Подпрограмма, создающая эффект мигающего курсора.
Timeint: PushReg <ax,ds,es>;!! сохранение регистров ах, ds и es mov ax, data;!! ах = значение сегмента данных mov ds, ах;!! ds = код сегмента данных mov es, VBuff;!! es = код сегмента видеобуфера test CurStat, 01 работа с курсором разрешена? jz GoVIC › нет, завершение подпрограммы dec Ntick Ntick = Ntick – 1 jnz GoVIC › пауза продолжается mov Ntick, 09 Ntick = 9 (примерно 0.5 сек) xor CurStat, 02 изменение признака состояния курсора call TglCrsr изменение состояния рисунка курсора GoVIC: PopReg <es,ds,ax>!! восстановление es, ds и ax db OEAh код инструкции jmp, на удаленный адрес VeclC dw 00, 00 старое содержимое вектора ICh
Прежде всего отметим, что подпрограмму примера 5.27 нельзя вызывать командой call Timeint, поскольку ее выполнение завершается не командой ret, а безусловным переходом на тот адрес, который раньше находился в векторе ich. Подпрограмму вызывает процедура BIOS, обрабатывающая задания, адресованные таймеру. Действия, которые надо выполнить для разрешения или запрещения вызовов данной подпрограммы при каждом тике таймера, показаны в примерах 5.25 и 5.26.
При вызове прерывающей подпрограммы в стеке сохраняется содержимое регистров ах, ds и es. В общем случае при входе содержимое регистра ds не определено, и в него надо записать код сегмента данных. Имя сегменту данных присваивается при его описании (см. пример 2.11), обычно это data. Если вы выбрали другое имя, то измените команду mov ax, data. Регистр es используется в подпрограмме TglCrsr, изменяющей текущее состояние рисунка курсора, поэтому он должен содержать код сегмента видеобуфера, который хранится в переменной vbuff.
Основные действия, выполняемые в примере 5.27, достаточно просты. Проверяется состояние младшего разряда байта CurStat, если он очищен, то команда jz GOVIC завершает выполнение подпрограммы, в противном случае работа с рисунком курсора разрешена. Содержимое счетчика тиков уменьшается на 1 и если разность больше нуля, то пауза не закончена и происходит выход из подпрограммы без изменения рисунка курсора. Наконец, если разность равна нулю, то в счетчик тиков записывается число 9, и инвертируются рисунок курсора и признак его состояния.
Перед выходом из подпрограммы восстанавливается сохраненное в стеке содержимое регистров es, ds и ах, а затем выполняется команда jmp, которая передает управление на адрес, сохраненный в двух словах переменной vecic (при выполнении примера 5.25).
В тексте примера 5.27 использован следующий трюк. Имя команды jmp не записано явно, вместо этого в байте, расположенном перед переменной vecic, указан код операции (ОЕАЬ). При ее выполнении микропроцессор интерпретирует содержимое следующих двух слов как адрес, на который производится переход. Это наиболее простой, но не единственно возможный способ вернуться на сохраненное значение вектора прерывания.
Замечание
Если при выполнении вашей задачи содержимое сегментных регистров ds и ез не изменяется после их первоначальной установки, то из текста примера 5.27 можно исключить 5 команд, комментарий к которым начинается с двух восклицательных знаков.