|
|
|
|
Переключение контекста и переключение стека
В связи с тем, что большинство тем, обсужденных в этой главе, относятся к операциям между отдельными программами с отдельными стеками, процесс
переключения заслуживает некоторого внимания. Переключение стека, или переход от одного стека к другому является частью большой темы, называемой
переключением контекста (context switching).
Если отобразить сегменты, в которых выполняется программа, как ее контекст, то можно увидеть, что при многих обстоятельствах бывает необходимо
изменять полный контекст программы. Примерами этого могут служить вызовы резидентных подпрограмм, вызывающих библиотеки RTL и использующие
некоторые типы оверлеев или сопрограмм. (Сопрограмма является структурной единицей программы, которая используется для описания логически параллельных
действий и вызывается подобно подпрограмме. В отличие от подпрограммы, каждый вызов сопрограммы возобновляет ее выполнение с точки последнего возврата.
Сопрограмма представляет собой вид специального оверлея, который не имеет связей порождающая-порожденная подпрограммы). В этом случае, когда одна
подпрограмма получает управление, она желает установить для выполнения свои собственные сегмент данных, внешний сегмент и сегмент стека. Во время
получения управления из другой программы наверняка известно только то, что ее программный сегмент и указатель инструкции установлены в надлежащие
значения. Обратимся к листингу 3-9. После вызова функции загрузки и выполнения программы контекст вызывающей программы был сброшен и этот листинг
показывает как устанавливать контекст программы. Пример, приведенный в листинге 3-9, несколько неудачен тем, что не сохраняет контекст предыдущей
программы, а просто перезаписывает его.
При получении управления, если необходимо сохранить целый набор регистров, наиболее легким способом выполнения этого является способ, заключающийся
в том, чтобы сначала установить новый стек программы и затем записать в него эти регистры. Поскольку значения стекового сегмента и указателя стека не
могут быть сохранены в стеке вызывающей программы (в связи с отсутствием способа получения их обратно) и поскольку они не могут быть сохранены в новом
стеке (который еще не установлен), параметры стека должны сохраняться в памяти. Если в виде исключения поместить в один и тот же сегмент программные
коды и данные, то для сохранения старых стекового сегмента и указателя стека и установке новых стекового сегмента и указателя стека может быть
использована последовательность программных кодов, показанная в листинге 3-11.
Листинг 3-11. Переключение стека для программы типа .EXE
----------------------------------------------------------------
enter: mov cs:old_stk_seg,ss ; Сохранение значений старого
mov cs:old_stk_ptr,sp ; стека
mov ss,cs:new_stk_seg ; загрузка значений нового
mov sp,cs:new_stk_ptr ; стека
push ds ; регистры стекового сегмента
push es
push ax ; начало записи в стек общих регистров
...
push bp
push si
push di
...
body:<тело программы> ; здесь начинается тело программы
...
pop di ; начало восстановления общих регистров
pop si
pop bp
...
pop ax
pop es ; восстановление регистров сегмента
mov ss,cs:old_stk_seg ; восстановление старых зна-
mov sp,cs:old_stk_ptr ; чений стека
jmp exit ; обход памяти данных
old_stk_seg dw ? ; стековый сегмент вызывающей программы
old_str_ptr dw ? ; указатель стека вызывающей программы
; стековый сегмент подпрограммы
new_stk_seg dw segment stack
; указатель стека подпрограммы
new_stk_ptr dw top_of_stack
exit: ; позиция выхода
ret ; возврат в вызывающую программу
----------------------------------------------------------------
|
Программные коды в листинге 3-11 зависят от имеющихся значений для стекового сегмента и указателя стека, уже размещенных в памяти. Для резидентных
подпрограмм и подпрограмм RTL это должно быть выполнено с помощью процесса инициализации. Надлежащие значения в память для программ типа .EXE
операционная система MS-DOS помещает в процессе настройки.
В связи с тем, что подпрограммы типа .COM не могут содержать значения сегмента, эти подпрограммы требуют другого способа переключения стеков.
Запоминание значений для вершины стека в памяти не вызывает проблем, за исключением адреса начала сегмента. Т.к. подпрограммы типа .COM для своих целей
совместно используют один и тот же сегмент, то значение стекового сегмента может быть получено из регистра программного сегмента. К несчастью, семейство
микропроцессоров 8086 не поддерживает пересылку из регистра сегмента в регистр сегмента, поэтому значение может быть передано косвенным путем. В связи
с отсутствием регистра, в котором можно было бы сохранить значение, значение передается через память, используя кодовый сегмент. Для реализации
этого способа начинайте подпрограмму со следующей инструкции:
mov cs:new_stk_seg,cs ; получение нового стекового сегмента
|
Если необходимо, то для переключения стеков в программе можно разработать два макроса, содержащих требуемые программные коды. Первый макрос включает
программный код из входа в тело программы, а второй макрос программный код из выхода из тела программы. Оба макроса должны соответствовать именам
переменных стека в области данных, а второй макрос, кроме того, должен принимать метку вершины стека top_of_stack как параметр для включения в
предложение dw для указателя нового стека new_stk_ptr. В эти макросы не должна входить инструкция RET. Это позволит использовать эти макросы для
выхода как с помощью инструкций JMP и IRET, так и с помощью инструкции RET.
Для файлов типа .EXE второй макрос должен также принимать как параметр имя стекового сегмента. Пример описанных выше макросов для файлов типа
.COM содержится в листинге 3-12 (INIT28), приводимом позднее в этой главе.
|
|