Жанр: Учеба

Техника программирования на турбо-С

сторінка №18

казателя функции
прерывания ZeroDivVector. Внешний указатель - это как раз то, что
вы только что объявили public в стартовом коде. В функции main,
до объявления себя резидентной, clock.c использует функцию
setvect для восстановления вектора прерывания по делению на ноль.
Затем clock.c завершается с объявлением себя резидентной.

Если у вас нет исходных текстов стартового кода, можно найти
следующий выход: позволить TSR-программе указывать на ошибку
деления на ноль, как только она случилась. При возникновении
такой ошибки в другой программе стартовый код вашей программы

будет выдавать сообщение об ошибке и завершать программу, в
точности как и соответствующий обработчик ДОС. При завершении
программы ДОС устанавливает вектор прерывания на свой обработчик
деления на ноль. Ваша программа больше не будет работать.
Конечно, вы должны удалить ссылки на ZeroDivVector в clock.c и
resident.c.

Выполнение обработчика прерываний от таймера.
-----------------------------------------------------------------

С каждым "тиканьем часов", происходящим 18.2 раза в секунду,
вызывается функция newtimer, объявленная как interrupt в Турбо
Си. Это объявление означает, что при вызове функции регистры
сохраняются в стеке и регистр сегмента данных указывает на
сегмент данных программы, с которой функция связана с помощью
link. Такое объявление также гарантирует восстановление регистров
из стека и выполнение машинной команды IRET при завершении работы
функции. Команда IRET используется обычно для выхода из
обработчика прерывания. Она восстанавливает регистры программного
счетчика, флагов и сегмента кодов, сохраненные при возникновении
прерывания.

Связывание старого вектора прерывания по таймеру.
-----------------------------------------------------------------

При выполнении newtimer прежде всего вызывается обработчик
прерывания, на который указывает oldtimer. Это действие позволяет
другим программам, уже присоединенным к вектору, произвести
необходимые действия. Функция newtimer проверяет флаг, который
устанавливает сама же эта функция, и означающий, что она еще
работает.

Сохранение и переключение контекста стека.
-----------------------------------------------------------------

Функция newtimer сохраняет сегмент стека и регистры
указателей - эти величины принадлежат прерванному процессу.
Значения стековых регистров, сохраненные при выполнении clock.c,
восстанавливаются в регистрах процессора, поэтому clock. c может
использовать свой собственный стек и не портить стек прерванной
программы.

Вычисление времени.
-----------------------------------------------------------------

Функция newtimer подсчитывает сигналы таймера. При
прохождении 18 сигналов (19 каждый пятый раз, так как сигналы
приходят 18.2 раза в секунду), новая величина времени вычисляется
для отображения на экран.

Затем дата и время отображаются в верхнем левом углу экрана,
восстанавливаются значения регистров стека прерванной программы,
и newtimer возвращает ей управление.

Заметим, что newtimer не переводит дату в полночь и не
изменяет значения на экране после ввода новых даты и времени
командой ДОС. Эта программа лишь иллюстрирует работу простейшей
TSR-программы. Если вы не работаете после полуночи, вы можете
использовать ее для отображения даты и времени на экране. Она
обновляет значения каждую секунду, поэтому вывод на экран другими
программами ничего не испортит. В качестве эксперимента вы можете
добавить будильник в clock.c. Включите время, когда надо
"звонить", как параметр, передаваемый в командной строке при
первом запуске clock.exe. Затем, при каждом изменении значения
часов сравнивайте его с этим временем. При равенстве времен,
выдайте звуковой сигнал, избегая, естественно, использования
функций ДОС. Позднее, когда вы узнаете, как использовать
коммуникационный вектор прерывания, вы сможете модифицировать
clock.c для установки и изменения времени звонка путем запуска
clock.exe из командной строки с параметром в то время, как TSR
уже резидентна. Вы можете добавить комментарии к звонку путем
использования оконных функций и оконного редактора. Путем именно
таких последовательных улучшений были созданы программы мировой
известности.


Программа clock.c использует прерывание от таймера. Если вы
загрузите ее после Sidekick, часы перестанут идти при вызове
Sidekick. Так как newtimer просто считает секунды, а не читает
время ДОС, такая смесь программы с Sidekick'ом сделает ее
результаты неверными. Sidekick отбирает вектор прерывания от
таймера у любой TSR-программы, загружаемой после него, чем и
вызыает такой результат. Остерегайтесь Sidekick'а при загрузке
ваших резидентных программ.

Чтобы запустить "часы", введите следующую команду:

C"clock

( Листинг 12.1 ).

/*--------- clock.c -----------*/

#include "dos.h"

void interrupt (*oldtimer)();
void interrupt newtimer();
extern void interrupt (*ZeroDivVector)();
#define sizeprogram 375
unsigned intsp,intss;
unsigned myss,stack;
static union REGS rg;
struct date dat;
struct time tim;
unsigned vseg;
int running = 0;
char bf[20];
unsigned v;
char tmsk []= " %2d-%02d-%02d %02d:%02d:%02d ";
int ticker = 0;

static struct SREGS seg;

main()
{
segread(&seg);

myss = _SS;

oldtimer = getvect(0x1c);
setvect(0x1c,newtimer);
stack = (sizeprogram - (seg.ds - seg.cs))*16-300;
vseg = vmode() == 7 ? 0xb000 : 0xb800;

gettime(&tim);
getdate(&dat);

setvect(0,ZeroDivVector);

rg.x.ax = 0x3100;
rg.x.dx = sizeprogram;
intdos(&rg,&rg);
}

void interrupt newtimer()
{
(*oldtimer)();
if (running ==0)
{
running = 1;
disable();
intsp = _SP;
intss = _SS;
_SP = stack;
_SS = myss;
enable();
if (ticker ==0)
{
ticker = (((tim.ti_sec % 5) ==0)? 19 :18 );
tim.ti_sec++;
if (tim.ti_sec == 60)
{
tim.ti_sec =0;
tim.ti_min++;
if (tim.ti_min == 60)
{
tim.ti_min=0;
tim.ti_hour++;
if (tim.ti_hour == 24)
tim.ti_hour = 0;
}
}
sprintf(bf,tmsk,dat.da_day,dat.da_man,dat.da_year % 100,
tim.ti_hour,tim.ti_min,tim.ti_sec);
}

for (v=0;v"19;v++)
vpoke (vseg,(60+v)*2,0x7000+bf[]);

disable();
_SS = intsp;
_SS = intss;
enable();
running = 0;

}
}

Файл проекта для построения clock.exe с именем clock.prj
имеет следующий вид:

Листинг 12.2: clock.prj

clock
ibmpc.obj

ПРОГРАММЫ TSR-ДРАЙВЕРА.
-----------------------------------------------------------------

Чтобы расширить возможности TSR-программ по использованию
функций ДОС при ее вызове, в этой главе приводится два исходных
текста на Си. После их адаптации и связи с любой стандартной
программой на Си, та становится резидентной программой. Первый
текст содержит функцию main, и туда помещаются параметры,
зависящие от вашей программы. Второй файл - это основной
TSR-драйвер, управляющий присоединением векторов прерываний,
самих прерываний, арбитраж столкновений ДОС и BIOS, определение,
резидентна ли уже программа, приостановка и возобновление работы
TSR-программы, и удаление TSR-программы из памяти.

Третий модуль в этом наборе - ваша прогррамма на Си, которая
должна придерживаться следующих правил, чтобы правильно
выполняться в этом окружении:

- программа должна быть построена в крохотной или малой
моделях памяти;
- программа не должна использовать функций ДОС от 0 до 12;
- при изменении текущего дискового каталога программа должна
восстанавливать его при возврате в прерванную программу;
- программа не должна использовать операции с плавающей
запятой;
- программа не должна завершаться или выходить в ДОС.

Вас может заинтересовать, почему надо избегать использования
операций с плавающей запятой. Дело в том, что подпрограммы
с плавающей запятой Турбо Си используют ряд векторов прерываний,
которые присоединяются во время выполнения стартового кода. Эти
вектора не восстанавливаются до завершения программы и передачи
управления при этом в стартовый код. В стартовом коде не
поддерживается область сохранения для этих векторов (среди
которых имеется и вектор немаскируемого прерывания 2); таким
образом, при завершении работы TSR-программы и удалении ее из
памяти эти векторы не будут восстанавливаться.

В этолй главе используются программа exec.c из главы 10 и
все примеры оконных программ практически готовых стать
резидентными.

Действия трех программных модулей.
-----------------------------------------------------------------

Три программных модуля для TSR-программ - это popup.c,
resident.c и ваша утилита на Турбо Си. Popup.c (листинг 12.3) и
resident.c (листинг 12.4) содержат сам TSR-драйвер. Popup.c - это
модуль, который надо изменить соответственно требованиям вашей
утилиты, а resident.c - модуль, остающийся неизменным для всех
TSR-программ. В дальнейшем обсуждении мы будем переключаться
между двумя этими модулями, так как они оба созданы для поддержки
создания вами TSR-программ.

Popup.c содержит кроме любого установочного кода,
необходимого вам, еще и несколько переменных, которые надо
инициализировать значениями, описывающими вашу программу.


Размер TSR-программы.
-----------------------------------------------------------------

Беззнаковая переменная sizeprogram специфицирует размер
программы в параграфах по 16 байт. Вы уже прочитали в главе 11,
как определять это значение. До тех пор, пока ваша программа не
начнет действовать, увеличивайте это значение. Программа в
крохотной модели не может быть больше, чем 64К (4096 параграфов),
а в малой модели - больше 128К (8192 параграфов).

Присвоение "горячего ключа".
-----------------------------------------------------------------

Значение клавиши "горячего ключа" TSR-программы определяется
значениями беззнаковых переменных scancode и keymask. При нажатии
клавиши обработчик прерывания 09 читает входной порт клавиатуры,
в котором находится скан-код клавиши (ее порядковый номер).
Каждая клавиша имеет свой скан-код, которые изображены на рисунке
12.1. Следовало бы присваивать такие клавиши, которые не
совпадали бы с клавишами других программ, в том числе и
нерезидентных. Стоит избегать функциональных клавиш, комбинаций с
Alt- и Ctrl-, так как многие программы их используют. Наилучшим
выбором является редкая и оригинальная комбинация клавиш, такая,
как Alt-точка.

------------------------------------------------------------------¬
¦---T--¬---T-T--T--T--T--T--T--T--T--T--T--T--T--T--¬---T--T--T--¬¦
¦¦59¦60¦¦41¦2¦ 3¦ 4¦ 5¦ 6¦ 7¦ 8¦ 9¦10¦11¦12¦13¦43¦14¦¦1 ¦69¦70¦84¦¦
¦+--+--++--+T+-T+-T+-T+-T+-T+-T+-T+-T+-T+-T+-T+-T+--++--+--+--+--+¦
¦¦61¦62¦¦15 ¦16¦17¦18¦19¦20¦21¦22¦23¦24¦25¦26¦27¦ ¦¦71¦72¦73¦55¦¦
¦+--+--++---+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-- 28¦+--+--+--+--+¦
¦¦63¦64¦¦ 29 ¦30¦31¦32¦33¦34¦35¦36¦37¦38¦39¦40¦ ¦¦75¦76¦77¦74¦¦
¦+--+--++----+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+-----++--+--+--+--+¦
¦¦65¦66¦¦ 42 ¦44¦45¦46¦47¦48¦49¦50¦51¦52¦53¦ 54 ¦¦79¦80¦81¦ ¦¦
¦+--+--++----T+-T+--+--+--+--+--+--+--+--+--+-T-----++--+--+--+78¦¦
¦¦67¦68¦¦ 56 ¦ ¦ 57 ¦ ¦ 58 ¦¦ 82 ¦83¦ ¦¦
¦L--+---L----- L---------------------------- L------L-----+--+---¦
¦ ¦
¦ Клавиатура IBM AT ¦
¦ ¦
¦ ¦
¦---T--¬---T-T--T--T--T--T--T--T--T--T--T--T--T-----¬------T-----¬¦
¦¦59¦60¦¦ 1¦2¦ 3¦ 4¦ 5¦ 6¦ 7¦ 8¦ 9¦10¦11¦12¦13¦ 14 ¦¦ 69 ¦ 70 ¦¦
¦+--+--++--+T+-T+-T+-T+-T+-T+-T+-T+-T+-T+-T+-T+-T---++--T--+--T--+¦
¦¦61¦62¦¦15 ¦16¦17¦18¦19¦20¦21¦22¦23¦24¦25¦26¦27¦ ¦¦71¦72¦73¦74¦¦
¦+--+--++---+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+¬28¦+--+--+--+--+¦
¦¦63¦64¦¦ 29 ¦30¦31¦32¦33¦34¦35¦36¦37¦38¦39¦40¦41¦ ¦¦75¦76¦77¦ ¦¦
¦+--+--++----+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+T-+--+--++--+--+--+ ¦¦
¦¦65¦66¦¦ 42 ¦44¦45¦46¦47¦48¦49¦50¦51¦52¦53¦ 54 ¦55¦¦79¦80¦81¦78¦¦
¦+--+--++----T+-T+--+--+--+--+--+--+--+--+TT+---T+--++T-+--+--+ ¦¦
¦¦67¦68¦¦ 56 ¦ ¦ 57 ¦¦ 58 ¦¦ 82 ¦ 83 ¦ ¦¦
¦L--+---L----- L--------------------------L-----L----+-------+---¦
¦ ¦
¦ Клавиатура IBM PC ¦
L------------------------------------------------------------------

Рис.12.1. Скан-коды клавиатуры.

Обнаружение комбинации клавиш облегчается при использовании
маски статуса по адресу 0:417, содержащей биты для клавиш Ctrl,
Alt, Shift, Ins, Caps Lock и Scroll Lock. Эта маска показывает,
какая из этих клавиш в текущий момент нажата. На рис.12.2
показано положение битов в маске.

-------T-------T-------T--------T-------T-------T-------T-------¬
¦ INS ¦ CAPS ¦ NUM ¦ SCROLL ¦ ALT ¦ CTRL ¦ LEFT ¦ RIGHT ¦
¦ ¦ LOCK ¦ LOCK ¦ LOCK ¦ ¦ ¦ SHIFT ¦ SHIFT ¦
L------+-------+-------+--------+-------+-------+-------+--------

Рис.12.2.

Если соответствующий бит в маске установлен в 1, то клавиша
нажата. По скан-коду нажатой клавиши и значению бита в маске
обработчик прерываний от клавиатуры может определить, нажата ли
клавиша "горячего ключа". Чтобы специфицировать эти значения, вам
надо присвоить значения переменным scancode и keymask в
popup.c. Как показано в листинге, scancode равен 52, и keymask
равен 8, что соответствует Alt-точка.


Сигнатура TSR-программы.
-----------------------------------------------------------------

Программа popup.c также обеспечивает сигнатуру, которая
используется для определения, не резидентна ли уже TSR-программа.
Символьный массив с именем signature - это строка,
заканчивающаяся \0.

При первом старте popup.c вызывает функцию resident. Она
находится в resident.c, ее задачей является определение,
резидентна ли уже программа, и если нет, определить и назначить
коммуникационный вектор. Это делается сканированием
пользовательских векторов прерываний от 0х60 до 0х67. Если какойлибо
вектор содержит значение, то сегментная половина этого
значения комбинируется со смещением сигнатуры в программе. Это
смещение является указателем на сигнатуру, то есть смещением
относительно регистра сегмента данных. Сегментная часть вектора
суммируется с разницей между значениями регистров сегмента кода и
сегмента данных.

Помните, что значение сегментного регистра, взятое из
вектора, должно быть значением сегмента кода первоначально
стартовавшей TSR-программы. Сигнатура имеется как в области
данных TSR-программы, так и в области данных копии TSR-программы,
которая пытается стать резидентной. Нам нужен сегментный адрес
данных TSR-программы, который не сохраняется в коммуникационном
векторе. Это значение должно быть вычислено.

Программа, просматривающая векторы, является второй копией
TSR-программы (в предположении, что TSR-программа уже загружена),
поэтому разность значений регистров кода и данных у нее должна
быть такой же, как и у уже резидентной программы. Пользуясь этим
алгоритмом, вы сравниваете значение по соответствующим смещением,
и если они одинаковы, то программа уже загружена, и функция
resident вернет найденный вектор в функцию main в popup.с. Если
сигнатуры не равны, сканирование продолжается, пока не будут
проверены все векторы. Если определяется, что TSR-программа еще
не резидентна, то первый подходящий вектор становится
коммуникационным. В функцию main возвращается 0, что означает,
что программа стала резидентной.

Коммуникационные прерывания.
----------------------------------------------------------------

Если функция main обнаруживает, что копия программы уже
резидентна, она проверяет параметры командной строки.
Используемая техника позволяет передавать при запуске программы
параметры для ее резидентной копии. Напомним, что в памяти
имеется в этот момент две копии TSR-программы - резидентная и
только что загруженная в нерезидентную область. Нерезидентная
версия может связываться с резидентной копией через
коммуникационный вектор. В типичной TSR-программе используются
три параметра, но вы можете добавить еще, если
необходимо. Стандартные три позволяют пользователю удалять
программу из памяти, приостанавливать и возобновлять ее
выполнение. Если один из этих параметров присутствует в командной
строке (это определяется при помощи args,argv), то генерируется
программное прерывание, с установлением регистра ax в
соответствующее опции значение. Прерывание генерируется функцией
main нерезидентной копии TSR-программы.

Посмотрим далее на листинг popup.c. Функция прерывания с
именем ifunc является обработчиком коммуникационного прерывания.
Когда функция main нерезидентной копии TSR-программы инициирует
прерывание, вызывается обработчик из резидентной копии. Он
проверяет значение регистра ax и производит соответствующие
действия. При этом вызывается одна из трех функций: terminate,
restart или wait в resident.c. Вы можете вставить сюда и другую
логику, чтобы управлять состоянием резидентной программы.

Функции wait и restart в resident.c просто устанавливают и
очищают флаг, означающий для обработчика клавиатуры необходимость
реакции на нажатие клавиши "горячего ключа".

Функция terminate в resident.c должна определять, может ли
быть завершена TSR-программа, и затем, если да, производить все
действия, аналогичные действиям ДОС при завершении нерезидентной
программы. Этот процесс будет обсуждаться после того, как вы
поймете, как программа становится резидентной.

Подготовка к резидентности.
-----------------------------------------------------------------

Если функция resident обнаруживает, что программа еще не в
памяти, то popup.c подготавливает себя к переходу в резидентные.
ваша программа захочет открыть файлы или сделать еще что-нибудь
"по хозяйству" при ее вызове. Popup.c, взятая для примера,
вызывает load_help для установки функций помощи из главы 7,
устанавливает путь ДОС для использования программой notepad из
главы 9, и выдает сообщение. Потом она вызывает resinit, функцию
из resident.c, которая делает все остальное, чтобы программа
стала резидентной.

Первоначально, resinit сохраняет значение регистра сегмента
стека для переключения контекста стеков при вызове TSR-программы.
Затем читается флаг занятости ДОС с помощью функции ДОС 0х34.
Функция getdta получает адрес дискового буфера для TSR-программы.
Этот адрес также используется при дальнейшем переключении
контекстов. Адрес идентификатора процесса (PID) восстанавливается
и сохраняется. Эта техника объяснялась в главе 11. Векторы
прерывания для таймера, клавиатуры, диска и DOSOK читаются и
запоминаются для связывания прерываний в цепочки, и собственные
обработчики присоединяются к этим прерываниям. Указатель стека
TSR-программы вычисляется так, чтобы быть на 300 байт меньше, чем
размер программы, и вектор обработки деления на ноль
восстанавливается на то значение, которое он имел до того , как
стартовый код присоединил его. Затем TSR-функция ДОС используется
для завершения программы, оставляя ее резидентной.

Обработчик обращения к диску.
-----------------------------------------------------------------

В то время, как компьютер выполняет программу, и программа
вызывает ДОС, ДОС использует дисковое прерывание BIOS (0х13) для
чтения и записи секторов данных. Напомним, что в главе 11 вас уже
предупреждали о том, что дисковые операции прерывать нельзя.
Обработчику диска в resident.c дано имя newdisk, и он защищает
дисковые операции от прерывания вашей TSR-программой. При
возникновении прерывания 0х13 устанавливается флаг, а после этого
управление передается на обработку дисковых операций. После
окончания обработки флаг очищается. Если этот флаг установлен,
когда программа вызывается по нажатию "горячего ключа", то
производится задержка выполнения до очистки этого флага.

Обратите внимание на трюк в newdisk. Функция, описанная как
interrupt, сохраняет все регистры в стеке, устанавливает регистр
ds на сегмент данных программы, содержащей эту функцию. Затем
начинается выполнение кода функции. При завершении функции
регистры восстанавливаются, и выполняется машинная команда
IRET. Эта команда восстанавливает регистры программного счетчика,
сегмента кода и флажков. Таким образом, выполняется полное
восстановление регистров прерванной программы.

Все работает до тех пор, пока вы не имеете прерывания,
которое возвращает условие в флаге переноса. Команда IRET
восстанавливает все флаги в состояние, которое было при
возникновении прерывания. Некоторые программные прерывания ДОС и
BIOS используют этот флаг для индикации результата. В их числе и
прерывание 0х13 BIOS; однако программа обработчика обращений к
диску сохраняет значение флажка переноса. Когда заканчивает свои
действия присоединенный обработчик olddisk, необходимо вернуть
вызывавшему процессу два значения: регистр ax и флажок переноса.
Значение ax берется из псевдопеременной _AX и помещается в целую
переменную ax, являющуюся одним из параметров функции прерывания.
Турбо Си использует это средство, чтобы изменять значения
регистров, которые будут восстановлены из стека при возврате из
функции. Для регистра флагов псевдопеременной нет, и, чтобы
избежать программирования на ассемблере, делается следующий
хэккерский трюк: за функцией newdisk может быть сразу же вызвана
функция newcrit, и она запишет флаговый регистр, сохраненный в
стеке при ее вызове, во внешнюю переменную cflag. При возврате из
newcrit cflag записывается в стек, где было сохранено прошлое
значение регистра флагов.При возврате из функции результирующие
флаги из olddisk будут восстановлены в регистр флагов.


Эти действия базируются на понимании того, как Турбо Си
использует регистры и производится вызов и возврат из функций
interrupt. Таким образом, эти программы не переносимы на другой
компилятор, и могут быть несовместимы даже с будущими версиями
Турбо Си, если Borland изменит свои соглашения. Программисты на
ассемблере, возможно, скажут, что подобные дествия можно было бы
легко проделать на их любимом языке. Эта критика верна, но этот
программный трюк является примером, как достигнуть пределов
возможностей Турбо Си. Автор благодарен Турбо Си за тот сервис,
который предоставляется для разрешения всех проблем, возникающих
при создании TSR-программ.

Обработчик критических ситуаций.
-----------------------------------------------------------------

Функция interrupt с именем newcrit является присоединенным
обработчиком критических ситуаций. Она не присоединяется к
прерыванию, когда программа объявляет себя резидентной, а делает
это лишь временно, при "всплытии" TSR-программы. Его задача -
обезопасить TSR-программу от возникновения критических ошибок в
то время, как она переключила контекст на себя. Обработчик
возвращает ноль в регистре ax, что означает для ДОС игнорировать
ошибку.

Обработчик клавиатуры.
-----------------------------------------------------------------

Функция interrupt с именем newkb является обработчиком
клавиатуры для TSR-программы. Она читает порт данных клавиатуры и
проверяет на соответствие определенному скан-коду, означающему
нажатие клавиши "горячего ключа". При равенстве и если
TSR-программа не приостановлена, "горячий ключ" активизируется.
Функция сбрасывает клавиатуру, чтобы не было будущих прерываний,
и затем проверяет, а не вызвана ли уже TSR-программа. Если нет,
то устанавливается флаг, означающий, что нажат "горячий ключ", и
выполнение функции заканчивается.

Если скан-код и маска статуса не равны "горячему ключу", то
управление передается старой программе обработки прерываний от
клавиатуры.

Обработчик таймера.
-----------------------------------------------------------------

Каждый импульс таймера вызывает выполнение функции newtimer
в resident.c. Прежде всего она вызывает старый обработчик
таймера. Затем она проверяет, не нажат ли "горячий ключ". Если
да, то проверяется флажок занятости ДОС. Если ДОС не занята, то
newtimer проверяет, не производится ли в этот момент дисковая
операция. Если нет, то сбрасывается прерывание от таймера,
очищается флажок "горячего ключа", и вызывается функция dores,
начинающая выполнение TSR-программы.

Обработчик DOSOK.
-----------------------------------------------------------------

Прерывание DOSOK обслуживается обработчиком с именем new28 в
resident.c. Он присоединяется к старому обработчику этого
прерывания, и проверяет флажок "горячего ключа". Если он
установлен, то проверяется, занята ли ДОС, и затем очищается
флажок "горячего ключа" и вызывается dores.

Выполнение TSR-программы.
-----------------------------------------------------------------

Функция dores вызывается лишь после того, как программа
убедилась в безопасности своего выполнения. Dores прежде всего
устанавливает флажок, означающий, что она выпол

Список сторінок

Поділитися у соцмережах

☏ Замовлення реклами: +3805044-68-872

© Асоціація електронних бібліотек України

☝ Усі матеріали сайту (зокрема статті, зображення, рекламні оголошення тощо) призначені лише для попереднього ознайомлення. Усі права на публікації, представлені на сайті, належать їхнім законним власникам. Просимо Вас не зберігати копії інформації.