4. СВЕДЕНИЯ, НЕОБХОДИМЫЕ ДЛЯ ВЫПОЛНЕНИЯ ЛАБОРАТОРНОЙ РАБОТЫ
4.1. Программы, состоящие из нескольких модулей
Использование для исходного кода программы одного файла подходит для небольших программ, но даже программы среднего размера приходится разбивать на несколько файлов или модулей, которые ассемблируются отдельно и компонуются вместе. Основное преимущество программ, состоящих из нескольких модулей, состоит в том, что после того, как вы отредактируете исходный код, вам потребуется переассемблировать только те модули, которые вы изменили, не затрагивая остальных модулей программы.
Для создания программы, состоящей из нескольких модулей, ассемблер предусматривает три директивы: public, extrn и global.
Пример программы, состоящей из двух модулей (см. пример 2.1). Основная программа l_1a#.asm имеет следующий вид:
;-------
;EXE-программа. Модуль 1-й "Вызов процедуры умножения" (l_1a#.asm) extrn submul:far
;-----------------------------------------------
stacksg segment para Stack 'Stack' dw 64 dup (?)
stacksg ends ;-----------------------------------------------
datasg segment para 'Data'
qty |
dw 0002h |
;количество |
price dw 0004h |
;стоимость |
|
msg |
db '0',0dh,0ah,'$' |
|
datasg ends |
|
|
;----------------------------------------------- |
|
|
codesg segment para 'Code' |
||
|
assume cs:codesg,ds:datasg,ss:stacksg |
|
begin proc far |
|
|
|
push ds |
|
|
xor ax,ax |
|
|
push ax |
|
|
mov ax,datasg |
|
|
mov ds,ax |
|
|
mov ax,price |
;ax:= стоимость (price) |
|
mov bx,qty |
;bx:= количество (qty) |
|
|
16 |
call submul |
;вызвать подпрограмму submul (внешнюю) |
;результат возвращается в регистрах (dx:ax)
;в частном случае (для заданных данных), т.е. когда получается однораз-
;рядное десятичное число, предусмотрен вывод результата на экран
or al,30h |
; преобразование в код ASCII |
mov msg,al |
|
mov ah,09h |
; вывод на экран полученного значения |
lea dx,msg |
|
int 21h |
|
ret |
; возврат в DOS |
begin endp codesg ends
end begin
;------- Модуль 2-й "Процедура умножения" (l_1b#.asm) codesg segment para 'Code'
submul proc far assume cs:codesg public submul
; параметры процедуре передаются в регистрах ax и bx
mul bx ; результат (произведение) возвращается в (dx:ax) ret
submul endp codesg ends
end ;-------
Эти два модуля ассемблируются отдельно с помощью команд: tasm l_1a#.asm
и
tasm l_1b#.asm
а затем компонуются в программу l_1a#.exe с помощью команды: tlink l_1a#.obj+ l_1b#.obj
При запуске программы l_1a#.exe на экран выводится результат совместной работы двух модулей, т.е. произведение price и qty: 8.
Теперь, когда вы увидели программу, состоящую из нескольких модулей, в действии, рассмотрим три директивы, обеспечивающие такое программирование.
4.2. Директива public
Она указывает ассемблеру, что соответствующую метку или метки нужно сделать доступными для других модулей. Здесь можно использовать практически любые метки, включая имена процедур и
17
переменных памяти, а также приравненные метки. Директива public обеспечивает доступность этих меток другим модулям. Например:
datasg segment para ‘Data’
|
public |
MemVar, Array1, array_length |
array_length equ |
100 |
|
MemVar |
dw |
10 |
Array1 |
db |
array_length dup (?) |
datasg.ends |
|
|
codesg segment para ‘Code’ |
||
|
public |
NearProc, FarProc |
NearProc |
proc near |
|
... |
|
|
NearProc |
endp |
; ближняя процедура |
... |
|
|
FarProc |
label proc ; дальняя процедура |
|
codesg ends end
Здесь имена приравненной метки, переменной размером в слово, массива и процедуры с ближним типом обращения доступны другому модулю, который будет компоноваться с данным модулем.
Однако имеется один тип меток, которые нельзя сделать общедоступными. Это приравненные метки, которые равны значениямконстантам с размерами, отличными от 1 или 2 байтов. Например, следующие метки общедоступными сделать нельзя:
long_value equ 1000h text_symbol equ <TextString>
Ассемблер обычно игнорирует регистр буквы, поэтому все общедоступные метки преобразуются в прописные символы (верхний регистр). Если требуется, чтобы в общедоступных метках различались буквы верхнего и нижнего регистров, то при ассемблировании всех модулей, содержащих ссылки на такие метки, нужно использовать в командной строке Турбоассемблера параметр /ml или /mx.
Например, без параметра /mx или /ml в других модулях следующие две метки будут эквивалентными:
public Symbol1, symbol1
При использовании для обеспечения различимости строчных и прописных букв в общедоступных и внешних идентификаторах параметра командной строки /mx нужно внимательно указывать буквы верхнего или нижнего регистра в директивах public или extrn. Ассемблер делает доступным для других модулей тот идентификатор, который указывает в
18
директиве public или extrn, а не тот, на который делается ссылка или который переопределяется внутри модуля. Например, директива:
public Abc abC dw
приводит к тому, что общедоступным будет имя Abc, а не abC.
Для каждого идентификатора в директиве public можно также задать язык: C, FORTRAN, PASCAL, BASIC, PROLOG и notlanguage (нет языка). Это приводит к тому, что к имени идентификатора до того, как одно в объектном файле станет общедоступным, автоматически применяются правила конкретного языка. Например, если вы описали:
public C myprog
то идентификатор myprog в исходном файле станет общедоступным, как _myprog, поскольку по соглашениям языка Си перед именами идентификаторов следует символ подчеркивания. Использование идентификатора языка в директиве public временно отменяет текущее задание языка (используемое по умолчанию или заданное в директиве
.model).
4.3.Директива extrn
Впоследнем разделе, чтобы сделать метки MemVar, Array1, array_length, NearProc и FarProc доступными для других модулей, мы использовали директиву public. Но далее возникает вопрос, каким образом другие модули могут ссылаться на эти метки?
Для того, чтобы сделать метки из другого модуля доступными в данном модуле, используется директива extrn. После того, как директива extrn будет использована, чтобы сделать доступным в данном модуле метку из другого модуля, эту метку можно использовать так же, как если бы она была определена в текущем модуле. Приведем пример другого модуля, в котором директива extern используется для ссылок на общедоступные метки, описанные в последнем разделе:
datasg segment para ‘Data’
extrn MemVar:word,Array1:byte,array_length:abs datasg.ends
codesg segment para ‘Code’
extrn NearProc:near,FarProc:far
...
mov ax,[MemVar] mov bx,offset Array1 mov cx,array_length
...
call NearProc
19
...
call FarProc codesg ends
Заметим, что все пять меток используются, как обычно. Единственным отличием от программы на ассемблере, состоящей из одного модуля, является директива extrn.
За каждой меткой, объявленной в директиве extrn, следует двоеточие и тип. Тип необходимо указывать, иначе ассемблер не будет знать, какую именно метку вы объявляете с помощью директивы extrn. Допустимы следующие типы:
abs– абсолютное значение;
byte – переменная (данные) размером в байт;
dword – переменная (данные) размером в двойное слово (4 байта); dataptr – указатель на данные ближнего или дальнего типа (зависит от
модели памяти);
far – метка кода с дальним типом обращения (переход – загрузкой регистров cs:ip);
fword – 6-байтовая переменная (данные);
near – метка кода с ближним типом обращения (переход – загрузкой регистра ip);
proc – метка процедуры (near или far, в соответствии с директивой
.model);
qword – переменная (данные) размером в четверное слово (8 байтов); Имя структуры – имя определенного пользователем типа struc;
tbyte – 10-байтовая переменная (данные); unknown – неизвестный тип;
word – переменная (данные) размером в слово (2 байта).
Тип внешних данных abs используется для объявления метки, определенной в ее исходном модуле с помощью директивы equ или =. Другими словами, это метка, которая просто представляет собой имя константы и не связана с адресами кода или данных.
Очень важно, чтобы для внешних меток вы задавали корректный тип данных, так как ассемблер будет генерировать код на основе заданных типов данных и у него нет другого способа определить, что ваша спецификация некорректна. Например, если вы случайно ввели:
codesg segment para ‘Code’
extrn |
FarProc:near |
. . . |
|
call |
FarProc |
. . . |
|
|
20 |