Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Практичне заняття № 4 МЗКІТ (рус, дороб) (10 ТП).DOCX
Скачиваний:
1
Добавлен:
01.07.2025
Размер:
6.04 Mб
Скачать

Связь с—асемблер

Общие принципы организации такой связи напоминают только что рассмотренное соединение Pascal и ассемблера. Поэтому коротко обсудим отличия на примере конкретных программ. Но прежде отметим, что язык C++ предоставляет дополнительные возможности связи программы с ассемблером, одновременно поддерживая традиционную организацию связи. Поэтому рассмотрим связь с ассемблером в стиле С как стандартную. При необходимости читатель, зная основы подобной связи, без труда разберется с нюансами дополнительных возможностей связи в стиле C++.

Нас по-прежнему интересуют три вопроса: как передать аргументы в процедуру на ассемблере, как к ним обратиться в процедуре на ассемблере и как возвратить результат?

Вначале отметим, что всегда нужно сохранять — и перед выходом из процедуры восстанавливать — содержимое регистров bp, sp, cs, ds и ss. Это делается перед вызовом процедуры. Остальные регистры нужно сохранять по необходимости, но хорошим тоном является сохранение и последующее восстановление всех регистров, которые подвергаются изменению внутри процедуры.

Передача аргументов в процедуру на ассемблере из программы на С осуществляется также через стек (рис. 14.3), но порядок их размещения в стеке является обратным тому, что рассмотрен выше для связи Pascal — ассемблер. В качестве примера используем ту же задачу. После передачи управления ближнего типа процедуре на ассемблере стек имеет вид как на рис. 14.3, а.

Рис. 14.3. Изменение содержимого стека при передаче управления С—ассемблер

Процедуры на ассемблере получают доступ к аргументам, переданным в стеке, посредством регистра Ьр. Принцип доступа — тот же, что и выше (рис. 14.3, б). Прежде всего в начало процедуры ассемблера необходимо вставить код пролога:

push bр

mov bp,sp

После этого доступ к аргументам в стеке осуществляется по смещению относительно содержимого bр, например:

При организации связи С—ассемблер также можно использовать директиву arg. Это избавит нас от необходимости подсчитывать смещения в стеке для доступа к аргументам и позволит обращаться к ним просто по именам.

Чтобы не повторяться, рассмотрим, как изменятся вызываемый и вызывающий модули (см. листинги 14.19 и 14.20) для связи С—ассемблер по сравнению с листингами 14.17 и 14.18.

Листинг 14.19. Вызывающий модуль на С (C++)

Листинг 14.20. Вызываемая процедура на ассемблере

Что касается передачи аргументов С—ассемблер, то здесь, как видите, все до¬вольно прозрачно. Как видите, в листинге 14.20 мы используем директиву MODEL с операндом С и директиву PROC с указанием языка С. Этим мы доверяем компилятору самому сформировать коды пролога и эпилога, а также

организовать обращение к переменным в стеке по их именам. Но при использовании конкретных программных средств организация такой связи выглядит намного проблематичней. Не в последнюю очередь это связано с тем, что компиляторы языка C/C++ разрабатывают множество фирм — в отличие от Pascal, компилятор для которого выпускает практически одна фирма Borland1. Это обстоятельство, на мой взгляд, — основная причина сложности связи С- ассемблер, так как каждая фирма реализует ее по-своему (хотя суть и остается практически неизменной). Поэтому нет смысла рассматривать множество частных случав. Обращайтесь к документации на ваш компилятор C/C++. Опыт показывает, что достаточно хороший эффект дает применение ассемблерных вставок в программу на C/C++. Как правило, компиляторы позволяют связывать модули на C/C++ и ассемблере с использованием средств командной строки. Так как этот процесс достаточно стандартизован, есть смысл его рассмотреть. В качестве примера выберем компилятор Visual C++ 4.0 фирмы Microsoft. Скорее всего, для компиляторов других фирм изменятся только имена файлов транслятора и компоновщика. Типовая последовательность шагов выглядит примерно так:

  • Составить текст программы на C++ (листинг 14.19). В этой программе — объявить процедуру asmproc внешней:

  • Выполнить трансляцию модуля C++ и получить объектный модуль:

cl prg14_19.cpp

  • Составить текст процедуры на ассемблере (листинг 14.20), в которой объявить процедуру asmproc общедоступной с помощью директивы PUBLIC. Заметьте, что идентификатору asmproc предшествует символ подчеркивания — asmproc. Компилятор добавляет знак подчеркивания ко всем глобальным идентификаторам. Поэтому для того, чтобы при компоновке про¬грамма link восприняла asmproc и asmproc как один и тот же идентификатор, ко всем именам, объявляемым глобальными в программе на ассемблере директивой PUBLIC, требуется добавить знак подчеркивания.

  • Выполнить трансляцию программы на ассемблере

masm /zi prg14_20,,,

  • Выполнить объединение объектных модулей с помощью программы-компоновщика link из пакета Visual C++ 4.0:

link prg14_19.obj prg14_20.obj, исполняемому модулю будет присвоено имя prg14_19.ехе.

Как возвратить результат в программу на С из процедуры на ассемблере? Для этого существуют стандартные соглашения (табл. 14.3). Перед возвратом управления в программу на С в программе на ассемблере необходимо поместить результат или сформировать указатель в указанных регистрах. Для иллюстрации работы с функцией С, текст которой написан на ассемблере, рассмотрим листинги 14.21 и 14.22. В них функция, написанная на ассемблере, подсчитывает сумму элементов массива. В функцию передаются адрес массива и его длина. Результат суммы элементов массива возвращается обратно в вызывающую программу на С.

Таблица 14.3. Возврат аргументов из процедуры на ассемблере в программу

на C/C++

Листинг 14.21. Вызывающая программа на С

Листинг 14.22. Вызываемая процедура на ассемблере

Таким образом, мы рассмотрели связь модулей на языках высокого уровня с модулями на ассемблере. Главное — мы разобрались с принципами. Теперь дело за малым: взять литературу — лучше документацию для конкретного языка высокого уровня — и разбираться с тем, как в языке разработчики реализовали связь с ассемблером. Затем следует понять техническое осуществление этой связи, используя особенности и настройки конкретной среды программирования.