Sum proc far

push ax

push cx

push bx; сохранение регистров

xor ax,ax; ax:=0

mov cx,N

xor bx,bx; индекс 1-го элемента

L: add ax,A[bx]

jno L1

; Обнаружена ошибка

pop bx

pop cx

pop ax

extrn Error:near

jmp Error

L1: add bx,type A

loop L

mov Summa,ax

pop bx

pop cx

pop ax; восстановление регистров

Ret

Code ends

End

Наш второй модуль не является головным, поэтому в его директиве end нет метки первой команды программы. Модуль p2.asm имеет три внешних имени A,N и Error и две входные точки с именами Sum и Summa. Так как второй модуль не производит никаких операций ввода/вывода, то он не подключает к себе файл io.asm. Оба наших модуля используют общий стек объёмом 64 слова, что, наверное, достаточно, так как стековый кадр процедуры Sum невелик.

Разберём работу нашей программы. После ввода массива A головной модуль вызывает внешнюю процедуру Sum. Это статическая связь модулей по управлению, дальний адрес процедуры Sum будет известен головному модулю до начала счёта. Этот адрес будет расположен в формате i32 на месте операнда Sum команды call Sum .

Между основной программой и процедурой установлены следующие (нестандартные) соглашения о связях. Суммируемый массив знаковых чисел расположен в головном модуле и имеет общедоступное имя A. Длина массива является общедоступной константой с именем N, описанной в головном модуле. Вычисленная сумма массива помещается в общедоступную переменную с именем Summa, описанную во втором модуле. Всё это примеры статических связей по данным. Наша программа не содержит динамических связей по данным, в качестве примера такой связи можно привести передачу параметра по ссылке в процедуру другого модуля. Действительно, адрес переменной становится известным процедуре только во время счёта программы, когда он передан ей вызывающей программой (обычно в стеке).

В том случае, если при суммировании массива обнаружена ошибка (переполнение), второй модуль передаёт управление на общедоступную метку с именем Error, описанную в головном модуле. Остальные имена являются локальными в модулях, например, обратите внимание, что в обоих модулях используется одинаковая метка c именем L.

Здесь необходимо отметить важную особенность использования внешних адресов. Рассмотрим, например, команду

L: add ax,A[bx]

во втором модуле. При получении из этого предложения языка Ассемблера машинной команды необходимо знать, по какому сегментному регистру базируется наш внешний адрес A. На это во втором модуле (а только его и видит во время перевода программа Ассемблера, первый модуль недоступен!) указывает местоположение директивы

extrn A:word; Внешний адрес

внутри второго модуля. Эта директива располагается в сегменте с именем Data, а директива

assume cs:Code,ds:Data

определяет, что во время счёта на этот сегмент будет установлен регистр ds. Следовательно, адрес A соответствует области памяти в том сегменте, на который указывает регистр ds.[46] Как видим, директива assume нам снова пригодилась.

Продолжим рассмотрение работы нашей программы. Получив управление, процедура Sum сохраняет в стеке используемые регистры (эта часть соглашения о связях выполняется), и накапливает сумму всех элементов массива A в регистре ax. При ошибке переполнения процедура восстанавливает значения регистров и передаёт управление на метку Error в головном модуле. В нашем примере второй вызов процедуры Sum специально сделан так, чтобы вызвать ошибку переполнения. Заметим, что переход на внешнюю метку Error – это тоже статическая связь по управлению, так как адрес метки известен до начала счёта. В то же время возврат из внешней процедуры по команде ret является динамической связью по управлению, так как конкретный адрес возврата в другой модуль будет помещён в стек только во время счёта программы.

Программа Ассемблера не в состоянии перевести каждый исходный модуль в готовый к счёту фрагмент программы на машинном языке, так как, во-первых, не может определить внешние адреса модуля, а, во-вторых, не знает будущего расположения сегментов модуля в памяти. Говорят, что Ассемблер переводит исходный модуль на специальный промежуточный язык, который называется объектным языком. Следовательно, программа Ассемблер преобразует входной модуль в объектный модуль. Полученный объектный модуль оформляется в виде файла, имя этого файла обычно совпадает с именем исходного файла на языке Ассемблер, но имеет другое расширение. Так, наши исходные файлы p1.asm и p2.asm будут переводиться (или, как чаще говорят, компилироваться или транслироваться) в объектные файлы с именами p1.obj и p2.obj.

Рассмотрим теперь, чего не хватает в объектном модуле, чтобы быть готовым к счёту фрагментом программы на машинном языке. Например, самая первая команда всех наших программ

mov ax,Data

должна переводится в машинную команду пересылки формата mov ax,i16 , однако значение константы i16, которая равна физическому адресу начала сегмента Data в памяти, делённому на 16, неизвестна программе Ассемблера и поле операнда i16 в команде пересылки остаётся незаполненным. Таким образом, в объектном модуле некоторые адреса остаются неизвестными (неопределёнными). До начала счёта программы, однако, все такие адреса обязательно должны получить конкретные значения.

Объектный модуль, получаемый программой Ассемблера, состоит из двух частей: тела модуля и паспорта (или заголовка) модуля. Тело модуля состоит из сегментов, в которых находятся команды и переменные нашего модуля, а паспорт содержит описание структуры объектного модуля. В этом описании содержатся следующие данные об объектном модуле.

· Сведения обо всех сегментах модуля (длина сегмента, его спецификация).

· Сведения обо всех общедоступных (экспортируемых) именах модуля, с каждым таким именем связан его тип (abs,byte,word,near и т.д.) и адрес (входная точка) внутри какого-либо сегмента модуля (для константы типа abs это просто целое число).

· Сведения о местоположении и типе всех внешних адресов модуля.

· Сведения о местоположении всех остальных незаполненных адресов в модуле, для каждого такого адреса содержится информация о способе его заполнения перед началом счёта.

· Другая информация, необходимая для сборки программы из модулей.

На рис. 10.1 показано схематическое изображение объектных модулей p1.obj и p2.obj, полученных программой Ассемблера, для каждого модуля изображены его сегменты, входные точки и внешние адреса. Вся эта информация содержится в паспортах объектных модулей.[47]

p1.obj     p2.obj
       
St segment stack       Data segment public extrn A:word extrn N:abs public Summa:word
Data segment public public A:word public N:abs extrn Summa:word     A N Summa A N Summa
Code segment public Extrn Sum:far public Error:near   Sum Error   Sum Error Code segment public publicSum:far extrnError:near
       
Рис. 10.1. Схематический вид объектных модулей с внешними адресами и входными точками.

Обратимся теперь к проблеме сборки программы из модулей. Как мы уже упоминали, эту работу выполняет специальная системная программа, которая называется редактором внешних связей. Из этого названия хорошо видно одно из назначений этой программы – устанавливать связи между внешними адресами и входными точками модулей. Рассмотрим схему работы редактора внешних связей на нашем примере.








Дата добавления: 2015-10-05; просмотров: 840;


Поиск по сайту:

При помощи поиска вы сможете найти нужную вам информацию.

Поделитесь с друзьями:

Если вам перенёс пользу информационный материал, или помог в учебе – поделитесь этим сайтом с друзьями и знакомыми.
helpiks.org - Хелпикс.Орг - 2014-2024 год. Материал сайта представляется для ознакомительного и учебного использования. | Поддержка
Генерация страницы за: 0.009 сек.