Учет особенностей компилятора
Оформление процедуры cnvindec
В комплект поставки Макроассемблера входят исходные тексты программных модулей, иллюстрирующие различные случаи применения директивы PROC. Тем не менее автор счел целесообразным показать, что изменится в тексте процедуры cnvindec (см. пример В.5), если в ее описание включить полную форму директивы PROC. Измененный текст приведен в примере В.7.
Пример В.7. Измененный текст процедуры cnvindec.
.LI STALL разрешаем печатать все subr SEGMENT word public ' su эг '; начало сегмента s,ubr .386 задаем тип микропроцессора dten dd 10 константа для умножения на 10 cnvindec PROC FAR PASCAL USES e 3х fs si, address:dword Ifs si, address fs:si = адрес начала строки текста mov address, 0 result = 0 очистка результата cnvloop: xor eax, eax очистка еах lods byte ptr fs: [si] al = очередной символ строки cmp al, '0' код символа меньше кода цифры 0? jb endcnv › да, конец формирования числа cmp al, '9' код символа больше кода цифры 9? ja endcnv › да, конец формирования числа sub al, 30h вычитаем код цифры 0 xchg eax, address переставляем еах и result mul cs:dten edx: eax = result * 10 add address, eax result = result + eax jmp short cnvloop › на начало цикла преобразования endcnv: pop si восстанавливаем содержимое si pop fs восстанавливаем содержимое fs pop edx восстанавливаем содержимое edx pop bp восстанавливаем содержимое bp retf возврат из подпрограммы cnvindec ENDP конец блока процедуры subr ENDS конец сегмента subr END конец текста модуля
В тексте примера В.7 отсутствуют директивы PUBLIC и ASSUME. Первая из них не нужна потому, что при полном описании процедура, по умолчанию, является общедоступной. Директива ASSUME необходима только при использовании компилятора MASM 5.1, а данный пример предназначен для компиляции на более поздних версиях, которые не требуют указания этой директивы. Зато в тексте модуля появилась новая директива .Listaii, она нужна для того, чтобы Макроассемблер включил в листинг команды пролога.
В основном тексте процедуры отсутствуют ссылки на регистр bp, вместо них используется имя параметра address. Если вы посмотрите листинг, то увидите, что ему соответствует тип Dword и значение [bp+6].
В пролог, кроме двух стандартных команд, включены команды, выполняющие сохранение в стеке регистров edx, fs и si. Эпилог в данном случае исключен, поскольку использована команда retf, а не ret. Поэтому команды, восстанавливающие содержимое регистров si, fs, edx и bp, включены в текст процедуры, первая из них имеет метку endcnv.
Замечание
Процедура примера В. 7 предназначена для вызова из программных модулей, составленных на языке ассемблера. Это объясняется тем, что сформированное число возвращается в стеке, именно по этой причине в тексте процедуры использована команда retf, исключающая вставку эпилога. Если вызывающий модуль составлен на алгоритмическом языке, то взять результат из стека, без специальных ухищрений, невозможно. Проще внести изменения в основной текст процедуры, позволяющие вызывать ее из модуля, составленного на любом языке.
Первый вариант таких изменений заключается в том, что перед выходом из процедуры сформированное число помещается в регистр еах. Для этого пять последних команд текста процедуры заменяются следующими:
endcnv: raov еах, address; копируем результат в еах ret; стандартная форма команды возврата
В данном случае выполнение процедуры завершает команда ret, поэтому Макроассемблер вставит перед ней эпилог, а к команде ret добавит операнд, равный 4, для выталкивания параметров из стека.
Обоснованием этого варианта является то, что в алгоритмических языках функция возвращает результат в регистре еах (или ах). Например, в модуле, составленном на Фортране, возможна такая форма вызова данной функции:
argument = cnvindec (string)
Описанный вариант применим, если результатом является только одно число. В общем случае у процедуры появляются дополнительные параметры, содержащие адреса для записи результатов вычислений. В конце раздела В. 4 было сказано, что если выходные параметры заданы в виде адресов, то при возврате из процедуры допустимо их удаление из стека.
При описании примера В.5 говорилось, что в регистре al передается код символа, являющегося ограничителем числа. Если он используется в вызывающем модуле, то к процедуре примера В.7 надо добавить еще один параметр, содержащий адрес для записи кода символа.
Таким образом, при составлении основного текста процедуры важно учитывать, что именно передается в качестве параметра – значение переменной или ее адрес. От этого будут зависеть действия, выполняемые как в самой процедуре, так и в вызывающем модуле.