<<
>>

Pascal и ассемблер

Организацию связи языков Pascal и ассемблер рассмотрим на следующем приме- ре: разработаем программу на языке Pascal, которая выводит символ заданное ко- личество раз начиная с определенной позиции на экране (листинги 15.12 и 15.13).
Все числовые аргументы определяются в программе на Pascal. Вывод символа осу- ществляет процедура ассемблера. Очевидно, что основная проблема в этой зада- че — организация взаимодействия модулей на Pascal и ассемблере. Листинг 15.12. Взаимодействие Pascal—ассемблер (модуль на Pascal) {рrgl5_12.раs> {Программа, вызывающая процедуру на ассемблере} program rау_раs; {$D+} {включение полной информации для отладчика} uses сrt; . procedure аsmрrос(сh:сhаr;х,у,kоl:integer); external; ■ {процедура аsmрrос обьявлена как внешняя} {$L c:\bp\work\prgl5_12.obj} BEGIN сlrsсr; {очистка экрана} аsmрrос( 'a' ,1,4,5); аsmрrос('s',9,2,7); END. Листинг 15.13. Взаимодействие Pascal—ассемблер (модуль на ассемблере) ;рrgl5_12.asm ;Процедура на ассемблере, которую вызывает ;программа на Pascal. ;Для вывода на экран используются сjт/жбы BIOS: ;02h - позиционирование курсора. ;09h - вывод символа заданное количество раз. МАSМ . MODEL small STACK 256 .code продолжение
Листинг 15.13 (продолжение)

Процесс организации такой связи состоит из нескольких шагов. 1. Написать процедуру на ассемблере дальнего (far) или ближнего типа (near). Назовем ее для примера asmproc. В программе на языке ассемблера (назовем ее рrgl5_lЗ.аsm), в которую входит процедура asmproc, необходимо объявить имя этой процедуры внешним с помощью директивы PUBLIC: PUBLIC asmproc . Для того чтобы процедура на ассемблере при компоновке с программой на Pascal воспринималась компилятором Borland Pascal 7.0 как far или near, недостаточ- но просто объявить ее таковой в директиве PROC (строка 11 листинга 15.13).

Кроме того, вам нужно включить или выключить параметр компилятора, дос- тупный через меню интегрированной среды: Options ► Compiler ► Force far calls. Установка этого параметра заставляет компилятор генерировать дальние вызовы подпрограмм. Альтернатива данного параметра — ключ {$F+} или {$F-} (соответ- ственно, включено или выключено) в программе. Это — локальные ключи, то есть в исходном тексте программы на Pascal их может быть несколько, и они могут, чередуясь друг с другом, поочередно менять форму генерируемых адресов пере- хода: для одних подпрограмм — дальние вызовы, для других — ближние. 2. Произвести компиляцию программы рrgl5_12.аsm с целью устранения синтак- сических ошибок и получения объектного модуля программы рrgl5_12.оbj: tasm /zi рrgi5_i2,,, 3. В программе рrдl5_12.раs на Pascal, которая будет вызывать внешнюю процедуру на ассемблере, следует вставить директиву компилятора {$L\путь\рrд_15_12.оbj}. Эта директива заставит компилятор в процессе компиляции программы рrдl5_12.раs загрузить с диска объектный модуль программы рrдl5_12.оbj. В программе рrдl5_12. раs необходимо объявить процедуру asmproc как внешнюю. В итоге последние два объявления в программе на Pascal будут выглядеть так: {$L mу_аsm}рrосеdurе аsmрrос(сh:сhаr;kоl,х,у:integer); external; 4. Если вы собираетесь исследовать в отладчике работу программы, то необходи- мо потребовать, чтобы компилятор включил отладочную информацию в гене- рируемый им исполняемый модуль. Для этого есть две возможности. Первая заключается в использовании глобального ключа {$D+}. Этот ключ должен быть установлен сразу после заголовка программы на Pascal. Вторая альтернативная возможность заключается в установке параметра компилятора: Options ► Com- piler ► Debug Information. 5. Выполнить компиляцию программы на Pascal. Для компиляции удобно исполь- зовать интегрированную среду. Для изучения особенностей связки Pascal — ассемблер удобно прямо в интегрированной среде перейти к работе в отладчи- ке командой Tools ► Turbo Debugger (или клавишами Shift-t-F4).
Будет загружен отладчик. Его среда вам хорошо знакома; в данном случае в окне Module вы уви- дите текст программы на Pascal. Нажимая клавишу F7, вы в пошаговом режиме будете исполнять программу на Pascal. Когда очередь дойдет до вызова про- цедуры на ассемблере, отладчик откроет окно с текстом программы на ассемб- лере. Но наш совет вам — не ждать этого момента, так как вы пропустите неко- торые интересные вещи. Дело в том, что отладчик скрывает момент перехода из программы на Pascal в процедуру на ассемблере. Поэтому лучше всего испол- нять программу при открытом окне CPU отладчика. И тогда вы станете свидете- лями тех процессов, которые мы будем обсуждать далее. Если бы взаимодействие программ ограничивалось только передачей и возвра- том управления, то на этом обсуждение можно было бы и закончить. Но дело зна- чительно усложняется, когда требуется передать аргументы (в случае процедуры) или передать аргументы и возвратить результат (в случае функции). Рассмотрим процессы, которые при этом происходят. Передача аргументов при связи модулей на разных языках всегда производит- ся через стек. Компилятор Pascal генерирует соответствующие команды при обра- ботке вызова процедуры ассемблера. Это как раз те команды, которые отладчик пытался скрыть от нас. Они записывают в стек аргументы и генерируют команду CALL для вызова процедуры ассемблера. Чтобы убедиться в этом, просмотрите на исполняемый код программы в окне CPU отладчика. После обработки вызова про- цедуры и в момент передачи управления процедуре asmproc содержимое стека бу- дет таким, как показано на рис. 15.1,а. Для доступак этим аргументам можно приме- нять различные методы, наиболее удобный из них—использование регистра В Р. Регистр В Р, как уже отмечалось, специально предназначен для организации произвольного доступа к стеку. Когда мы рассматривали связь ассемблерных мо- дулей, то говорили о необходимости добавления в текст вызываемого модуля фраг- ментов, настраивающих его на передаваемые ему аргументы. При объединении разноязыких модулей также нужно вставлять подобные дополнительные фрагмен- ты кода.
Они, кроме всего прочего, позволят учесть особенности конкретного язы- ка. Фрагмент, вставляемый в самое начало вызываемого модуля, называется про- логом модуля (процедуры). Фрагмент, вставляемый перед командами передачи управления вызывающему модулю, называется эпилогом модуля (процедуры). Его назначение — восстановление состояния вычислительной среды на момент вызо- ва данного модуля.
Рис. 15.1. Изменение содержимого стека при передаче управления в связке Pascal—ассемблер

Рассмотрим действия, выполняемые кодами пролога и эпилога при организа- ции связи Pascal—ассемблер. Действия, выполняемые кодом пролога. 1. Сохранить значение Ьр в стеке. Это делается с целью запоминания контекста вы- зывающего модуля. Стек при этом будет выглядеть, как показано на рис. 15.1, б. 2. Записать содержимое SP в ВР. Тем самым ВР теперь тоже будет указывать на вершину стека (рис. 15.1, в). После написания кода пролога все обращения к аргументам в стеке можно орга- низовывать относительно содержимого регистра ВР. Из рис. 15.1, в видно, что для обращения к верхнему и последующим аргументам в стеке содержимое Ьр необхо- димо откорректировать. Нетрудно посчитать, что величина корректировки будет различаться для процедур дальнего (far) и ближнего (near) типов. Причина понят- на: при вызове процедуры типа near в зависимости от установленного режима адресации (usеlб или usеЗ2) в стек записывается 2(4) байта в качестве адреса возврата (содержимое iр/еiр), а при вызове процедуры типа far в стек записы- вается 4(8) байта (содержимое IР/ЕIР и СS)1. Таким образом, коды пролога для процедур ближнего и дальнего типа соответ- ственно будут выглядеть следующим образом: asmproc procnear ■ ;пролог для процедуры типа near push bp mоv bр,sр ;к прологу можно добавить команду корректировки bр на 4 с тем, чтобы регистр bр ;указывал на верхний из передаваемых аргументов в стеке add bр,4;теперь bр указывает на kоl asmproc proc far Если действует режим адресации usеЗ2, то в стек записываются двойные слова. По этой причине запись 16-разрядного регистра cs также производится четырьмя байтами, при этом два старших бай- та этого значения нулевые. ' ;пролог для процедуры типа far push bp rаоvbр,sр „ ; к прологу можно добавить команду корректировки bр на 6 с тем, чтобы регистр bр ;указывал на верхний из передаваемых аргументов в стеке add bр,6;теперь bр указывает на kоl Далее доступ к переданным в стеке данным осуществляется, как показано в ли- стинге 15.7. Как видите, все довольно просто. Но если мы вдруг решили изменить тип на- шей процедуры ассемблера с far на near или наоборот, то нужно явно изменить и код пролога. Это не совсем удобно. TASM предоставляет выход в виде директи- вы ARG, которая служит для работы с аргументами процедуры. Синтаксис дирек- тивы ARG иллюстрирует рис. 15.2.

Рис. 15.2. Синтаксис директивы ARG

Несколько слов об обозначениях на рисунке: я имя — идентификатор переменной, который будет использоваться в процедуре на ассемблере для доступа к соответствующей переменной в стеке; * тип — тип данных аргумента (по умолчанию WORD дляusеlб и DWORD для use32); Ш значение_1 определяет количество аргументов с данным именем. Место в стеке для них будет определено, исходя из расчета: значение _1 ∙ значение_2 ∙ размер_типа. По умолчанию значение _1 — 1; ш значение _2 определяет, сколько элементов данного типа задает данный аргу- мент. По умолчанию его значение равно 1, но для типа byte значение _2 = 2, так как стековые команды не работают с отдельными байтами. Хотя, если явно за- дать значение _2 = 1, то транслятор действительно будет считать, что в ячейку стека помещен один байт; я идентификатор — имя константы, значение которой присваивает транслятор. Об идентификаторе мы подробно поговорим чуть позже. Таким образом, директива ARG определяет аргументы, передаваемые в про- цедуру. Ее применение позволяет обращаться к аргументам по их именам, а не по смещениям относительно содержимого ВР. К примеру, если в начале рассматри- ваемой нами процедуры на ассемблере asmproc задать директиву ARG в виде arg kol:word,y:word, х:wоrd,сhr:bуtе, то к аргументам процедуры можно будет обращаться по их именам, без подсчета смещений. Ассемблер сам выполнит всю необходимую работу. В этом можно убедиться, запустив программу в отладчике. Обратите вни- мание: порядок следования аргументов в директиве arg является обратным поряд- ку их следования в описании процедуры (строка procedure аsmрrос(сh:сhаr;х,у, kоt:iпtедеr); external; в программе на Pascal). Процедура asmproc с директивой arg представлена в листинге 15.14.

Листинг 15.14. Использование директивы arg После того как решена проблема передачи аргументов в процедуру и выполне- ны все необходимые действия, возникает очередной вопрос: как правильно воз- вратить управление? При возврате управления в программу на Pascal нужно помнить, что соглашения этого языка требуют, чтобы вызываемые процедуры са- мостоятельно очищали за собой стек. Программа на ассемблере также должна удов- летворять этому требованию и заботиться об очистке стека перед своим заверше- нием. Для этого необходимо составить эпилог. Действия, выполняемые кодом эпилога для связи Pascal—ассемблер. 1. Записать содержимое bp в sp командой mov sр,bр. Это действие восстанавлива- ет в sp значение, которое было на момент входа в процедуру. Необходимость в этом действии возникает в том случае, если в процедуре производилась рабо- та со стеком. В листинге 15.13 такой работы не было, поэтому код эпилога реа- лизует только следующие два действия. 2. Восстановить сохраненный в стеке регистр ВР. 3. Удалить из стека переданные процедуре аргументы. Для удаления из стека аргументов можно использовать различные способы. И Можно явно скорректировать значение SP, переместив указатель стека на необ- ходимое количество байтов в положительную сторону. Это — не универсаль- ный способ, к тому же он чреват ошибками, особенно при частых модификаци- ях программы. 11 Можно использовать в директиве arg после записи последнего аргумента опе- ранд, состоящий из символа равенства (=) и идентификатора, указанного за ним в следующей синтаксической конструкции: идентификатор В этом случае TASM при обработке директивы arg подсчитает количество бай- тов, занятых всеми аргументами, и присвоит их значение идентификатору. В нашем случае директиву arg можно определить так: arg сh:byte;X:word;у:word;kоl:wоrd=а_sizе TASM после обработки данной директивы присвоит имени а_sizе значение 8 (байт). Это имя впоследствии нужно будет указать в качестве операнда ко- манды ret: ret a_size Есть еще одна возможность организации данных Pascal—ассемблер — исполь- зовать операнды директивы MODEL. Вы помните, что она позволяет задать модель памяти и учесть соглашения языков высокого уровня о вызове процедур. Для свя- зи Pascal—ассемблер ее можно задавать в виде MODEL large,pascat Задание в таком виде директивы MODEL позволяет: * описать аргументы процедуры непосредственно в директиве рrос: asmproc proc near сh:bуtе,х:wоrd,у:wоrd,kоl:wоrd Ш автоматически сгенерировать код пролога и эпилога в процедуре на ассемблере; ϋ для доступа к аргументам, объявленным в РRОС, использовать их имена (в этом отношении данный вариант является аналогом предыдущего варианта с дирек- тивой ARG). Листинг 15.15 демонстрирует, как отражаются особенности данного варианта на тексте процедуры ассемблера. Обратите внимание на то, что пролога уже нет, так как он формируется транслятором автоматически; вместо эпилога обязатель- но нужно задавать команду RET, только без операндов. Интересно изучить текст листинга 15.16, который получается в результате трансляции листинга 15.15. В нем видны сформированные транслятором коды пролога и эпилога. Кроме того, транс- лятор заменил команду RET без операндов командой ret 0008, которая, в соответ- ствии с требованиями к взаимодействию с программами на Pascal, удалит из стека аргументы, переданные вызываемой процедуре. Листинг 15.15. Использование директивы MODEL {рrgl5_ 15.раs} {Программа на Pascal, вызывающая процедуру на ассемблере, полностью совпадает с листингом 15.12} ;рrgl5_15.asm МАSМ MODEL large, pascal продолжение & Листинг 15.15 (продолжение) BYTE,х:WORD,у:WORD,коl:WORD ; у в dh ; x в dl ;номер службы BIOS ;вызов прерывания BIOS ;номер службы BIOS ;символ - в al ;маска вывода символа ;kol в сх ;вызов прерывания BIOS ;конец процедуры ;конец пропраммы

Листинг 15.16. Результат трансляции листинга 15.15 Таковы стандартные способы вызова ассемблерных процедур из программ на Pascal и передачи им аргументов. Эти способы будут работать всегда, но, совер- шенствуясь, компилятор может предоставлять и более удобные средства. Их мы рассматривать не будем, так как, в конечном счете, они сводятся к рассмотренной нами процедуре. Остались открытыми два вопроса. и Как быть с передачей данных остальных типов Pascal, ведь мы рассмотрели толь- ко данные размером в байт и слово? ш Как возвратить значение в программу на Pascal? Что касается ответа на первый вопрос, то необходимо вспомнить, что в языке Pascal существуют два способа передачи аргументов в процедуру: по ссылке и по значению. Тип аргументов, передаваемых по ссылке, совпадает с типом ассемблера dwоrd и с типом pointer в Pascal. По сути, это указатель из четырех байтов на некоторый объект. Структура указателя обычная: два младших байта — смещение, два стар- ших байта — значение сегментной составляющей адреса. С помощью такого указа- теля в программу на ассемблере передаются адреса следующих объектов: Ш всех аргументов, объявленных при описании в программе на Pascal как var, не- зависимо от их типа; , ⅝ аргументов pointer и lопgiпt; 1' строк string; Ш множеств; . Ш массивов и записей, имеющих размер более четырех байтов. Аргументы по значению передаются следующим образом: ⅜S для типов char и byte — как байт; ш для типа boolean — как байт со значением 0 или 1; 3ί для перечисляемых типов со значением 0...255 — как байт; более 255 — как два байта; Ш для типов integer и word — как два байта (слово); для типа real — как шесть байтов (три слова); массивы и записи, длина которых не превышает четырех байтов, передаются «как есть». ^ Заметим, что аргументы таких типов, как single, double, extended и comp, переда- ются через стек сопроцессора. Что касается ответа на второй вопрос, то мы выясним его на конкретном при- мере. Напомню, что мы рассматриваем вызов из программы на Pascal внешней процедуры на ассемблере. Понятно, что вызов ради вызова вряд ли нужен — вызы- ваемая процедура должна иметь возможность вернуть данные в вызывающую про- грамму. Поэтому такую вызываемую процедуру правильнее рассматривать как функцию. В связке Pascal—ассемблер для того, чтобы возвратить результат, про- цедура на ассемблере должна поместить его значение в строго определенное место (табл. 15.2).

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

В листинге 15.17 приведен текст вызывающего модуля на Pascal, а в листин- ге 15.18 — код вызываемого модуля на ассемблере. Программа на Pascal инициа- лизирует две переменные, valuel и vаluе2, после чего вызывает функцию на ассем- блере AddAsm для их сложения. Результат возвращается в программу на Pascal и присваивается переменной rez. Листинг 15.17. Вызывающая программа на Pascal {рrgl5_17.раs} рrодrаmргgl4lθl; {внешние объявления} function АddАsm:wоrd; external; {$L рrgl5_18.оbj> vаr vаluеl:wоrd;{здесь как внешние} vаluе2:wоrd; . rеz:wогd; begi n vаluеl:=2; vаluе2:=3; {вызов функции} геz:=АddАsm; writеlп("Результат: ",геz); end. Листинг 15.18. Вызываемая процедура на ассемблере ;рrgl5_18.аsm МАSМ MODEL small data segment word public ;сегмент данных .•объявление внешних переменных ■ ехtrп vаluеl:WОRD ехtrп vаluе2:WОRD data ends;конец сегмента данных . code assume ds:dаtа;привязка ds к сегменту ;данных программы на Pascal main : АddАsm рrоспеаr PUBLIC АddАsm ;внешняя mоv сх,ds: vаluеl;vаluеlв сх mоv dх,ds:vаluе2; vаluе2В dх add сх,dх ;сложение mоv ах,сх результат в ах, так как - слово ret ;возврат из функции АddАsm епdр ;конец функции end main;конец программы В последней программе следует обратить внимание еще на одну возможность доступа к разделяемым данным — с помощью сегментов типа PUBLIC (см. главу 5). Совместное использование сегментов данных стало возможным благодаря тому, что компилятор Pascal создает внутреннее представление программы в виде сег- ментов, как и положено программе, выполняющейся в архитектруре IA-32 на процессоре Intel. Сегмент данных в этом представлении тоже имеет название data, и директива SEGMENT для него со всеми вытекающими последствиями выглядит так: data segment word public

<< | >>
Источник: В. И. Юров. Assembler. Учебник для вузов. 2-е изд. 2003

Еще по теме Pascal и ассемблер:

  1. Г.С.Иванова, Т.Н.Ничушкина, Е.К.Пугачев. Объектно- ориентированное программирование, 2001
  2. Таненбаум Э.. Архитектура компьютера. 5-е изд, 2007
  3. В. И. Юров. Assembler. Учебник для вузов. 2-е изд, 2003
  4. Л.О. Доліненко, В.О. Доліненко, С.О. Сарновська. Цивільне право України, 2006
  5. ЦИВІЛЬНЕ ПРАВО УКРАЇНИ
  6. ПЕРЕДМОВА
  7. Частина І ПРОГРАМА КУРСУ «ЦИВІЛЬНЕ ПРАВО УКРАЇНИ»
  8. Розділ І. Загальні положення цивільного права
  9. Тема 1. Поняття цивільного права. Предмет та метод, система цивільного права. Функції та принципи цивільного права
  10. Тема 2. Цивільне законодавство України
  11. Тема 3. Поняття, елементи та види цивільних правовідносин
  12. Тема 4. Здійснення цивільних прав і виконання обов’язків
  13. Тема 5. Захист цивільних прав та інтересів
  14. Тема 6. Об’єкти цивільних прав