Win32 в машинных кодах


Консольное приложение - часть 3


Настало время обсудить один важный вопрос. Функции не только принимают параметры, часто они еще возвращают значения. Результат работы функции по ее возвращении (т.е. перед выполнением следующей после вызова функции инструкции) оказывается в регистре EAX. Это общее соглашение: когда мы начнем создавать свои функции, мы тоже будем должны записывать в регистр EAX значение, которое должно быть возвращено как результат функции. В случае функции GetStdHandle таким результатом как раз и является нужный нам описатель. Его можно либо сохранить где-то в памяти (переписав туда значение из регистра), либо использовать прямо в регистре, если вызов нужной функции непосредственно следует после получения описателя.

Здесь нужно отметить еще один момент. Мы уже знаем, что из 8 общих регистров один (ESP) используется в качестве указателя стека, и его трогать нельзя. На самом деле, при работе со стеком используется еще и второй регистр - EBP, поэтому число доступных для манипуляций регистров сокращается до 6. Теперь задумайтесь над вопросом: а что случается с данными, которые находились в регистрах, после вызова функции? Особенно, если это "чужие" функции, являющиеся для нас "черными ящиками" (как в случае с функциями API). Значения в регистрах могут быть перезаписаны (ведь надо с чем-то работать!), а могут остаться без изменения. Чтобы внести ясность в этот вопрос, для работы с функциями Win32 API было принято следующее соглашение: при вызовах любых функций значения регистров EBX, ESI и EDI остаются без изменений - какие были перед вызовом функции, такие будут и после; значения же регистров EAX, ECX и EDX могут быть изменены произвольным образом. В регистре EAX, как мы уже знаем, будет находиться результат работы функции (если функция возвращает результат). Если функция не имеет возвращаемого результата, значение в EAX не определено.

Практический же вывод такой. Если нам нужно, чтобы значение в "изменяемых" регистрах (EAX, ECX или EDX) сохранилось после вызова функции, перед ее вызовом необходимо поместить значение соответствующего регистра в стек, а после вызова функции - извлечь его оттуда (в тот же регистр). И наоборот: если мы создаем свою функцию, которую может вызвать система (например, забегая вперед, это относится к главной функции окна), и если в работе этой функции нам приходится использовать регистры, которые не должны изменяться (EBX, ESI или EDI), мы должны в самом начале функции сохранить значение этого используемого регистра в стеке, а перед возвратом из функции - восстановить его. В случае "изменяемых" регистров этого делать не нужно.




Начало  Назад  Вперед



Книжный магазин