Кое-что о резидентах В 6 номере "Монитора" за 93 год автор статьи "Резидентный будильник на Turbo Pascal" задает вопрос "почему не работает обработчик прерывания 2F в резидентном режиме, хотя он прекрасно работает в нерезидентном?". Ответ очевиден - все дело в размерах стека. Процедура , описанная как interrupt в Turbo Pascal при входе сохраняет в стеке содержимое всех регистров процессора, да еще и размещает в нем свои локальные переменные. В нерезидентной программе это не страшно, так как для этого используется ее стек, который по умолчанию равен 16К, а при работе резидентного обработчика объем стека может быть существенно меньше. Поскольку прерывание 2F перехватывают все, кому не лень, и каждый из обработчиков занимает по меньшей мере 6 байт стека (флаги+адрес возврата), при попытке запихать туда все регистры стек может переполниться со всеми вытекающими последствиями. Единственный выход из этой ситуации - описывать процедуру не как interrupt и писать ее на встроенном ассемблере, вставляя в конец кода IRET вручную. (не забудьте поставить перед IRET POP BP, так как в начало каждой процедуры на встроенном ассемблере добавляется PUSH BP MOV BP,SP При этом, если у процедуры нет локальных переменных, вы теряете только 2 байта стека. После этого необходимо переключить стек на свой и далее можно делать все что угодно. Вообще, если вы пишете обработчик прерывания на Turbo Pascal рекомендуется разбить его на две процедуры: Procedure DoHandle; var ..... {Имеет столько локальных переменных, сколько вам надо} begin {Делает то, что вам надо} end; и Procedure HandleInt(...);interrupt; begin {Запись нужных вам регистров в глобальные переменные} GlobalAX:=AX; ... {переключение стека} asm MOV CallerSP,SP MOV CallerSS,SS MOV SS,MySS Mov SP,MYSP end; {Вызов собственно обработчика} DoHandle; {переключение стека обратно} asm MOV SS,CallerSS MOV SP,CallerSP end; {Модификация тех регистров, которые вы хотите изменить} AX:=GlobalAX; end; где использование глобальных переменных GlobalAX и т.д. необходимо потому, что после переключения стека доступ к параметрам процедуры interrupt будет невозможен, назначение переменных CallerSS и CallerSP очевидно (они тоже должны быть глобальными, либо описанными в процедуне HandleInt как типизированные константы, т.к. размещаться должны в сегменте данных, а не в стеке, а глобальные переменные MySS и MySP, хранящие положение собственного стека программы, инициализируются перед тем, как программа сделается резидентной, следующим образом: MySS:=SSeg; MySP:=SPtr; Все ассемблерные вставки в процедуре HandleInt с легкостью заменяются на Inline, так что любители старого доброго Pascal 5.x тоже могут воспользоваться моим советом. И последний советбтем, кто пишет резидентные программы или пользуется ими. А знаете ли вы, что существует способ определить наличие резидентной программы в памяти средствами командного языка DOS? Вот он (требуется DOS 4.x и выше): ╙CheckTSR.BAT∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙░ ┐@echo off ┐ ┐rem Использование: CheckTSR имя-программы (без расширения)┐ ┐if "%1"=="" goto quit ┐ ┐rem Если программа в памяти , файл будет не пустым ┐ ┐mem /C|find "%1" > file$$$1.tmp ┐ ┐rem пустой файл не копируется ┐ ┐copy file$$$1.TMP file$$$2.tmp>nul ┐ ┐del file$$$1.tmp ┐ ┐if not exist file$$$2.tmp goto NoTSR ┐ ┐echo %1 загружена ┐ ┐del file$$$2.tmp ┐ ┐goto quit ┐ ┐:notsr ┐ ┐echo %1 не загружена ┐ ┐:quit ┐ ▒∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙∙╘ B.Б.Вагнер тел 135-46-61 E-Mail: vitus@agropc.msk.su