Резидентные программы
Перехват прерывания 5 осуществляется значительно проще, через перехват "истинного" аппаратного прерывания от клавиш клавиатуры, из-за чего мы и воспользовались им в нашем примере.
code segment assume CS:text org 100h main proc jmp init; Переход на секцию инициализации new_05: push AX; Сохраним регистры AX и BX, push BX; используемые далее mov BX,CS; BX= сегментный адрес программы mov AH,0Eh; Функция вывода на экран символа mov AL,BH; Выведем старшую половину ; сегментного адреса int 10h; Вызов BIOS pop BX; Восстановим pop AX; регистры iret; Завершение обработчика main endp init proc; Секция инициализации mov AX,2505h; Функция установки вектора mov DX,offset new_05; Смещение обработчика int 21h; Вызов DOS mov DX,(init-main+10Fh)/16; Размер в параграфах mov AX3100h; Функция " завершить и int 21h; оставить в памяти" init endp code ends end main
Структура программы соответствует описанной ранее. В секции инициализации выполняется установка обработчика прерывания 05h, при этом исходное содержимое вектора 5 не сохраняется. Это, разумеется, очень плохо, так как лишает нас возможности этот вектор восстановить. С другой стороны, восстанавливать перехваченные векторы надлежит при завершении программы, а применительно к резидентной программе – при ее выгрузке из памяти. Однако в нашей простой программе не предусмотрено средств выгрузки (процедура выгрузки довольно сложна), и программе придется находиться в памяти до перезагрузки машины.
Установив вектор, программа завершается с оставлением в памяти ее резидентной части с помощью функции 31h.
Резидентная часть программы является классическим обработчиком программного прерывания. В первых же предложениях сохраняются регистры АХ и ВХ, используемые далее в программе, а затем содержимое сегментного регистра CS переносится в регистр ВХ. С таким же успехом можно было скопировать содержимое любого из регистров DS, ES или SS, так как в программе типа .СОМ все регистры настроены на один и тот же сегментный адрес (см. рис. 3.1). Копирование из сегментного регистра в регистр общего назначения понадобился потому, что в дальнейшем нам придется работать с отдельными половинками сегментного адреса, а у сегментных регистров половинок нет.
Далее старшая половина сегментного адреса заносится в регистр AL, и вызовом уже знакомой нам функции BIOS 0 Eh этот код выводится на экран. Затем таким же образом выводится младшая половина сегментного адреса. Наконец, после восстановления регистров ВХ и АХ (в обратном порядке по отношению к их сохранению) командой iret управление возвращается в прерванную программу, которой в данном случае является COMMAND.COM.
Вывод программы (ей для наглядности было дано имя tsr.com) для конкретного прогона показан на рис. 3.7.
Рис. 3.7. Вывод программы 3.4.
Полученный результат далек от наглядности. Действительно, разделив сегментный адрес на две половины длиной в байт каждая, мы просто записали в видеобуфер эти числа. Каждое число размером в байт можно трактовать, как код ASCII какого-то символа. При выводе числа на экран эти символы и отображаются. Изображение пикового туза соответствует коду 06, а знак равенства имеет код 3Dh (см. таблицу кодов ACSII на рис. 3.1). Таким образом, сегментный адрес находящейся в памяти резидентной программы оказался равен 063Dh, что соответствует приблизительно 25 Кбайт. Так и должно быть, так как конфигурация компьютера, использованного для подготовки примеров, предусматривала хранение большей части DOS в расширенной памяти, в области НМА. В основной памяти в этом случае располагается кусочек DOS вместе с драйверами обслуживания расширенной памяти и частью программы COMMAND.COM общим объемом около 25 Кбайт.
Для того, чтобы получить на экране сегментный адрес в привычной нам форме, его двоичное машинное представление необходимо преобразовать в коды ASCII, отображающие шестнадцатеричное (или, если угодно, десятичное) представление этого числа. В нашем примере, чтобы получить на экране изображение числа 063Dh, надо было сформировать такую цепочку кодов ASCII (в шестнадцатеричном представлении):
30 36 33 44 68
Рассмотренный метод вывода на экран чисел в виде изображений символов, конечно, далек от совершенства, однако подкупает свой исключительной простотой и вполне может быть использован в процессе отладки резидентных программ и обработчиков прерываний, включение в которые довольно громоздких программ перекодировки может оказаться нежелательным или даже невозможным.
Читатель может, подготовив рассмотренный пример, загрузить несколько экземпляров программы и посмотреть, как изменяются в этом случае их начальные адреса.