На этом шаге мы рассмотрим функции, которые возвращают void.
Скомпилируйте с опцией -S программу, содержащую простую функцию языка C, которая возвращает void. Вы должны будете ввести с клавиатуры прописную S. Найдите листинг 1 и из приглашения DOS задайте команду bcc -S voidf. Затем загрузите результирующий файл VOIDF.ASM в текстовый редактор.
Не считая нескольких вспомогательных строк в начале сгенерированного ассемблерного файла (в этих строках объявляются макросы, компонуются сегменты программ, а также присутствует отладочная информация), в листинге 2 демонстрируется результирующий файл VOIDF.ASM, содержащий команды ассемблера.
Листинг 1. VOIDF.CPP (скомпилируйте эту программу с опцией -S)
#include <stdio.h> void f() { int x = 123; printf("x == %d\n", x); } main() { f(); return 0; };
Листинг 2. VOIDF.ASM
_TEXT segment byte public 'CODE' ; начало сегмента кода ;main() assume CS : _TEXT ;Директива ассемблера _main proc near ;Начало функции main() push bp ;Сохранить указатель базы mov bp,sp ;Задать bp равным sp ;{ ;f(); call near ptr @f$qv ;Вызвать функцию f() ;return 0 ;} xor ax.ax ;Задать возвращаемое в ax значение равным 0 pop bp ;Восстановить bp ret ;Вернуться в место вызова _main endp ;Конец функции main() ;void f(void) assume CS : _TEXT ;Директива ассемблера @f$qv proc near ;Начало функции f() push bp ;Сохранить регистр bp mov bp, sp ;Задать bp равным sp sub sp,2 ;Зарезервировать стек для х ;{ ;int x= 123; mov word ptr [bp-2], 123 ;Присвоить 123 целому х ;printf("x == %d\n", x); push word ptr [bp-2] ;Запомнить значение х mov ax, offset DGROUP: s@ ;Запомнить адрес строки s, push ax ;хранящийся в сегменте данных call near ptr _printf ;Вызвать функцию printf() pop cx ;Удалить элемент из стека pop cx ;Удалить элемент из стека ;--Механизм удаления аргументов, которые запоминались в стеке перед вызовом, ;известен как соглашение вызова языка C. ;} mov sp,bp ;Очистить указатель стека pop bp ;Восстановить регистр bp ret ;Вернуться в место вызова @f$qv endp ?debug C E9 ;Отладочная информация для ?debug C FA00000000 ; Turbo Debugger _TEXT ends _DATA segment word public 'DATA' ;Глобальный сегмент данных s@ label byte ;Метка строки для printf() db x = =%d ;строка для printf() db 10 ;Символ новой строки db 0 ;Нуль, завершающий строку _DATA ends ;Конец сегмента _TEXT segment byte public 'CODE' ;Сегменты поддерживаются в _TEXT ends ;требуемом порядке public _main ;Экспорт имени _main public @f$qv ;Экспорт функции f() extern _printf : near ;Импорт функции printf() _s@ equ s@ ;В этом коде не используется end ;Конец листинга
В сгенерированном тексте демонстрируются ключевые инструкции, которыми следует пользоваться при написании своих функций на ассемблере.
Даже в этом простом примере за кулисами выполняется огромная работа! Следует понять, что "раскопки" в скомпилированном тексте требуют большого труда.
На следующем шаге мы рассмотрим функции, возвращающие значениz.