11.2 Процедура загрузки резидентной части
11. 2 Процедура загрузки резидентной части Эта процедура является основной для нерезидентной части, любая резидентная программа должна содержать эту процедуру. Процедура загрузки резидентной части выполняет следующие действия: 1. Сохраняет в переменных резидентной части значение вектора прерывания, который будет перехвачен; 2. Заменяет значение перехватываемого вектора прерывания на новое; 3. Оставляет резидентную часть программы в памяти и завершает программу. Для сохранения значения перехватываемого вектора прерывания требуется вычислить смещение от начала таблицы векторов прерываний и прочитать по этому смещению старое значение вектора прерывания, а затем записать его в переменные резидентной части. Например (номер вектора прерывания находится в регистре BX): OLD_VEC DD? ; здесь будет храниться старое значение ; вектора прерывания . . . SHL BX, 1 ; вычисление смещения SHL BX, 1 XOR AX, AX ; настройка ES на начало MOV ES, AX ; таблицы векторов прерываний MOV AX, ES: [BX] MOV WORD PTR OLD_VEC, AX MOV AX, ES: [BX + 2] MOV WORD PTR OLD_VEC + 2, AX
Для того, чтобы оставить резидентную часть программы в памяти служит специальная функция прерывания DOS 21H. Прерывание DOS – это вызов диспетчера функций, предоставляемых операционной системой MS-DOS пользовательским программам. При этом конкретная функция определяется номером, который записывается перед вызовом прерывания 21H в регистр AH. Функция может иметь параметры, которые записываются в другие регистры. Конкретные параметры и регистры, в которые они должны быть записаны, зависят от самой функции. Для загрузки резидентной части в память служит функция DOS 31H, при этом для этой функции необходимо в регистре DS указать сегмент резидентной части, а в регистре DX – объем памяти, который занимает резидентная часть в параграфах (блоках по 16 байт). Функция DOS 31H помимо загрузки резидентной части завершает выполнение программы.
Для вычисления размера резидентной части в программах типа COM можно в конце резидентной части поставить какую-либо метку, смещение которой и будет размером резидентной части в байтах. При этом резидентная часть должна располагаться в начале сегмента команд, тогда размер будет вычислен корректно. Если резидентная часть располагается не в начале сегмента команд, то в памяти останется не только сама резидентная часть, но и те коды или данные, которые находились перед ней в начале сегмента команд. Это приводит только к лишнему расходу памяти, в остальном программа будет работать корректно. Если резидентная часть использует переключение стека, то стек должен входить в резидентную часть. То же относится и к переменным резидентной части. После того, как вычислен размер резидентной части в байтах, нужно разделить это значение на 16 (т. е. вычислить, сколько это будет параграфов) и прибавить единицу, чтобы округлить размер в сторону большего параграфа. Это следует выполнить, так как если округлить размер в сторону меньшего параграфа, несколько байт резидентной части могут остаться незагруженными в память, что может привести к некорректной работе программы. Пример вычисления размера резидентной части в параграфах для программы типа COM: CODE SEGMENT Start: OLD_VEC DD? ; начало резидентной части . . . MyProc PROC . . . IRET MyProc ENDP . . . Stac DB 128 DUP (? ) . . . M1: ; конец резидентной части . . . MOV DX, (M1 – Start + 10FH) / 16 ; теперь в DX размер резидентной части в параграфах . . . CODE ENDS
Разберем, как практически вычисляется размер резидентной части в параграфах. Дело в том, что выражение виде (OFFSET M1) / 16 + 1 записать нельзя, так как на этапе трансляции Turbo Assembler еще не знает значения выражения OFFSET M1. Для решения этой проблемы можно вычислить разность между двумя метками – началом и концом резидентной части, как это показано в примере. Однако COM программа имеет еще и префикс программного сегмента, размер которого в 256 байт тоже должен быть учтен. Число 10FH – это размер префикса программного сегмента 100H плюс число 0FH, которое требуется для округления размера резидентной части в параграфах к наибольшему целому параграфу. Исходя из этих рассуждений выражение (M1 – Start + 10FH) / 16 и представляет собой размер резидентной части в параграфах.
Для программ типа EXE вычисление размера резидентной части может оказаться сложнее. Прежде всего, следует разместить сегмент стека резидентной части, сегмент данных и сегмент команд в одном месте в начале программы, т. е. эти три сегмента должны идти последовательно, но все равно в каком порядке. Сегмент стека или сегмент данных могут отсутствовать. Если и резидентная и нерезидентная части программы расположены в одном программном сегменте, то вычисление размера резидентной части в параграфах аналогично случаю для COM программы. Рассмотрим вычисление размера резидентной части, если она состоит из трех сегментов – сегмента команд, сегмента данных и сегмента стека. Первая возможная ситуация – когда сегмент команд является общим для резидентной и нерезидентной части. В этом случае для вычисления размера всей резидентной части, включая ее сегменты данных и стека, необходимо сначала вычислить размер сегментов данных и стека, а затем прибавить к этому значению размер кода резидентной части. В следующем примере показано, как это можно реализовать: Data SEGMENT . . . Data ENDS
Stac SEGMENT PARA DB 128 DUP (? ) Stac ENDS
Code SEGMENT Start: . . . ; код резидентной части M1: . . . ; код нерезидентной части MOV DX, Code SUB DX, Data ADD DX, (M1 – Start + 10FH) / 16 ; теперь в DX размер резидентной части в параграфах . . . Code ENDS
Вторая возможная ситуация – когда сегменты команд отдельны для резидентной и нерезидентной частей и сегмент команд нерезидентной части следует сразу за сегментом команд резидентной части. При этом сегмент команд нерезидентной части должен иметь параметр «PARA», обозначающий выравнивание содержимого сегмента по границе параграфа. Размер резидентной части в параграфах в этом случае – это разница значений сегментных адресов сегмента команд нерезидентной части и начального сегмента резидентой части. Пример:
Data SEGMENT . . . Data ENDS
Stac SEGMENT PARA DB 128 DUP (? ) Stac ENDS
Resident SEGMENT . . . ; код резидентной части Resident ENDS
Code SEGMENT PARA . . . ; код нерезидентной части MOV DX, Code SUB DX, Data ADD DX, 10H ; теперь в DX размер резидентной части в параграфах . . . Code ENDS
Дополнительные 10H параграфов прибавляются для учета размера префикса программного сегмента. Теперь, когда вычислен размер резидентной части, нужно занести его в регистр DX, а в регистр AL занести код возврата из программы (обычно это просто 0). В регистр AH заносится номер функции 31H и вызывается прерывание DOS 21H. Резидентная часть программы остается в памяти, а сама программа завершается и управление передается операционной системе.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|