assembler. Учебник для вузов_Юров В.И_2003 -637с (862834), страница 77
Текст из файла (страница 77)
Все это делает директива USES.Имена перечисляемых регистров разделяются пробелами, а имена переменных —запятыми. Для переменных может указываться тип. В качестве значения типа может быть либо имя простого типа (например, DWORD), либо VARARG. Служебное словоVARARG позволяет задать переменное число параметров.
При его использованиионо должно быть последним в списке параметров процедуры. Служебное словоVARARG указывается только, если параметр язык равен С, SYSCALL или STDCALL Поумолчанию параметр тип равен WORD для 16-разрядного сегмента и DWORD — для32-разрядного сегмента.344Глава 15. Модульное программированиеСвязь ассемблера с языкамивысокого уровняНа протяжении всего учебника мы неоднократно подчеркивали сильные и слабыестороны языка ассемблера как языка программирования.
Писать на нем достаточно объемные программы утомительно. И всегда ли это нужно? Конечно, не всегда.Если программа не предназначена для решения каких-то системных задач, требующих максимально эффективного использования ресурсов компьютера, если к нейне предъявляются сверхжесткие требования по размеру и времени работы, есливы не «фанат» ассемблера — то, на мой взгляд, следует подумать о выборе одногоиз языков высокого уровня.
Существует и третий, компромиссный путь — комбинирование программ на языке высокого уровня с кодом на ассемблере. Такой способ обычно используют в том случае, если в вашей программе есть фрагменты,которые либо вообще невозможно реализовать без ассемблера, либо ассемблерможет значительно повысить эффективность работы программы.Большинство компиляторов учитывают возможность комбинирования их «родного» кода с ассемблером. Как именно? Это зависит от конкретного компилятораязыка высокого уровня.
Учитывая, что большинство программистов работают или,по крайней мере, владеют основами программирования на языках C/C++ и Pascal,дальнейшее обсуждение будет касаться именно этих языков. В настоящее времясуществует несколько их основных реализаций, поддерживаемых разными фирмами-производителями. В этих реализациях имеются, в основном, одинаковыемеханизмы связи с языком ассемблера. Единственное, что остается сделать приреализации конкретной задачи на конкретном компиляторе, — уточнить в документации на язык нужные параметры связи и, возможно, особенности организации связи с кодом на ассемблере. Невозможно дать универсальные рекомендациипо этому вопросу и остается лишь сосредоточиться на отображении наиболее принципиальных моментов связи программ на языках Pascal и C/C++ с ассемблером,актуальных для большинства реализаций этих языков.Вначале мы отметим общие моменты, актуальные как для C/C++, так и дляPascal.
Затем на примерах конкретных программ мы обсудим моменты, специфичные для каждого из этих языков.Существуют следующие формы комбинирования программ на языках высокого уровня с ассемблером.Р Использование операторов типа inline и ассемблерных вставок в виде встраиваемого ассемблерного кода. Эта форма в значительной степени зависит от синтаксиса языка высокого уровня и конкретного компилятора. Она предполагает, что ассемблерные коды в виде команд ассемблера или прямо в машинныхкомандах вставляются в текст программы на языке высокого уровня.
Компилятор языка распознает их как команды ассемблера (машинные коды) и без изменений включает в формируемый им объектный код. Эта форма удобна, еслинадо вставить небольшой фрагмент.« Использование внешних процедур и функций. Это более универсальная форма комбинирования. У нее есть ряд преимуществ:Связь ассемблера с языками высокого уровня345П написание и отладку программ можно производить независимо;П написанные подпрограммы можно использовать в других проектах;П облегчаются модификация и сопровождение подпрограмм в течение жизненного цикла проекта (к примеру, если ваша процедура на ассемблере производит работу с некоторым внешним устройством, то при смене устройства вам нет необходимости перекраивать весь проект — достаточно заменитьтолько работающую с ним процедуру,.оставив неизменным интерфейс программы на языке высокого уровня с ассемблером).Встраиваемый ассемблерный кодБольшинство компиляторов языков высокого уровня поддерживают возможностьвставки ассемблерного кода в обрабатываемые ими программы.
В данном разделеосновное внимание уделяется ассемблерным вставкам в программах на C++, таккак именно этот язык наиболее часто используется для реализации задач системного программирования.Оператор inline языка Pascal представляет собой следующую синтаксическуюконструкцию:inline(машинные_коды)В скобках указывается строка машинных кодов. Для получения такой строкицелесообразно написать нужный фрагмент на ассемблере, скомпилировать исполняемый модуль, а затем запустить отладчик.
В окне CPU отладчика вы увидите машинные коды ваших инструкций; их нужно переписать и вставить в скобки оператора inline.Встраивание ассемблерного кода в программы C++ производится директивой_asm. Причем этой директивой возможна вставка как одной команды ассемблера,так и группы команд. Синтаксис:asmкоманда_ассемблера [;комментарий]asm {команда_ассемблера [;комментарий]команда_ассемблера}[//комментарий или /* комментарий */]Планируя использование встроенного ассемблера, важно хорошо представлятьсебе его возможности и ограничения.Можно:ш не передавать параметры, как в случае с внешней ассемблерной процедурой;ш иметь непосредственный доступ к командам и регистрам процессора;И ссылаться на метки и переменные вне текущего блока, находящиеся в пределахвидимости ассемблерной вставки;•ж вызывать функции вне пределов ассемблерной вставки, причем эти функциидолжны быть ранее объявлены в программе (на уровне прототипа).И использовать описание констант как в стиле ассемблера, так и C++;» использовать операторы PTR, LENGTH, SIZE, TYPE и директивы EVEN и ALIGN.346Глава 15.
Модульное программированиеНельзя:» использовать директивы определения данных простых (DB и DD) и сложныхтипов (STRUC, RECORD), то есть каким-либо образом определять данные любоготипа;описывать функции в пределах ассемблерной вставки;использовать в командах большинство операторов ассемблера типа OFFSET, SEG,SHR, SHL (вместо OFFSET можно использовать LEA);1использовать любые директивы макроопределений;1обращаться к полям структур и объединений.Внешний ассемблерный кодТак как вариант с использованием операторов inline и ассемблерных вставок обладает довольно большими ограничениями, он не может быть признан универсальным, и для реализации сложных задач остается организация связи программ наязыках высокого уровня с ассемблерным кодом через внешние процедуры и функции.
Возможны два вида такой связи — программа на языке высокого уровня вызывает процедуру на ассемблере и наоборот. В данной главе ограничимся рассмотрением связи только в одну сторону, когда программа на языке высокогоуровня вызывает процедуру на ассемблере. Это наиболее часто используемыйвид связи.Вспомним (см. главу 10) синтаксис директивы PROC компилятора TASM:имя_процедуры PROC [ [ м о д и ф и к а т о р _ я з ы к а ] я з ы к ] [расстояние]Один из операндов — язык. Он служит для того, чтобы компилятор мог правильно организовать интерфейс (связь) между процедурой на ассемблере и программой на языке высокого уровня. Необходимость такого указания возникаетвследствие того, что способы передачи аргументов при вызове процедур различныдля разных языков высокого уровня.TASM поддерживает несколько значений операнда язык.
Ранее в табл. 15.1 длянекоторых из этих значений были приведены характерные особенности передачиаргументов и соглашения о том, какая процедура очищает стек — вызывающая иливызываемая. Под направлением передачи аргументов понимается порядок, в котором аргументы включаются в стек, по сравнению с порядком их следования в вызове процедуры. Для языка Pascal характерен прямой порядок включения аргументов в стек: первым в стек записывается первый передаваемый аргумент изоператора вызова процедуры, вторым — второй аргумент и т. д. На вершине стекапосле записи всех передаваемых аргументов оказывается последний аргумент.
Дляязыков C/C++, наоборот, характерен обратный порядок передачи аргументов. В соответствии с ним в стек сначала включается последний аргумент из операторавызова процедуры (или функции), затем предпоследний и т. д. В конечном итогена вершине стека оказывается первый аргумент. Это делает возможной передачупеременного количества параметров при вызове функций в языках C/C++. Наверху стека оказывается первый параметр функции, значение которого — предоставление информации о количестве аргументов в данном вызове функции. Чтоже касается очистки стека, то понятно, что должны быть определенные договорен-Связь ассемблера с языками высокого уровня347ности об этом.
В языке Pascal эту операцию всегда совершает вызываемая процедура, в языках C/C++ — вызывающая. При разработке программы с использованием только одного языка высокого уровня об этом задумываться не имеет смысла, но если мы собираемся связывать несколько «разноязыких» модулей, то этисоглашения нужно иметь в виду.Вспомните действия, которые мы проделывали для того, чтобы настроиться нааргументы в стеке.
Теперь, после указания языка, с программой на котором должна осуществляться связь, все действия по настройке стека будут производитьсякомпилятором. При этом в текст процедуры он включит дополнительные команды входа в процедуру (пролог) и выхода из нее (эпилог), причем код эпилога может повторяться несколько раз — перед каждой командой RET.
Для значенияNOLANGUAGE и по умолчанию коды пролога и эпилога не создаются.Для пояснения последних замечаний рассмотрим на конкретных примерах организацию связей между модулями на ассемблере и модулями наиболее популярных языков высокого уровня — С и Pascal. He стоит воспринимать нижеследующие примеры как образец оптимальности — они лишь в учебных целях призваныпроиллюстрировать принципы организации межъязыковых связей.Pascal и ассемблерОрганизацию связи языков Pascal и ассемблер рассмотрим на следующем примере: разработаем программу на языке Pascal, которая выводит символ заданное количество раз начиная с определенной позиции на экране (листинги 15.12 и 15.13).Все числовые аргументы определяются в программе на Pascal.
Вывод символа осуществляет процедура ассемблера. Очевидно, что основная проблема в этой задаче — организация взаимодействия модулей на Pascal и ассемблере.Листинг 15.12. Взаимодействие Pascal—ассемблер (модуль на Pascal)<l>{prgl5_12.pas}<2>{Программа, вызывающая процедуру на ассемблере}<3>program ray_pas;<4>{$D+} {включение полной информации для отладчика}<5>uses crt;<6>procedure asmproc(ch:char;х,у,kol:integer); external;<7>{процедура asmproc обьявлена как внешняя}<8>{$L c:\bp\work\prgl5_12.obj}<9>BEGIN<10>clrscr; {очистка экрана}<11>asmproc( 'а' ,1,4,5);<12>asmproc('s',9,2,7);<13> END.Листинг 15.13.