Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
MAIN_ASS.DOC
Скачиваний:
0
Добавлен:
01.07.2025
Размер:
153.09 Кб
Скачать

Оформление программ в виде отдельных модулей

В этом случае программа собирается в виде отдельных модулей. Признаком программного модуля является директива END [имя_модуля]. Она располагается в конце модуля. При этом только головной модуль имеет в директиве END имя.

На Рис. 1.6. приведена программа, состоящая из двух модулей CALLMUL1 и SUBMUL. Модуль CALLMUL1 является головным и вызывает модуль SUBMUL. Все вызываемые модули в вызывающем должны быть описаны с помощью директивы EXTRN. Головной модуль должен обеспечить возврат в MS-DOS. Все модули транслируются независимо и объединяются в единый проект на этапе компоновки. Вызываемые модули могут предварительно помещаться в библиотеку.

TITLE CALLMUL1 (EXE)

EXTRN SUBMUL: FAR

;======================================================

STACKSG SEGMENT PARA STACK ‘Stack’

dw 64 dup(?)

STACKSG ENDS

;======================================================

DATASG SEGMENT PARA ‘Data’

QTY dw 0140h

PRICE dw 2500h

REZ dd ?

DATASG ENDS

;======================================================

CODESG SEGMENT para ‘Code’

BEGIN PROC FAR

ASSUME cs:codesg,ds:datasg,ss:stacksg

push ds

sub ax,ax

push ax

mov ax,datasg

mov ds,ax

mov ax,price ;загрузить стоимость

mov bx,qty ;и количество

call submul ;вызвать подпрограмму

mov WORD ptr REZ,ax ; результат умножения

mov WORD ptr REZ+2,dx

ret

BEGIN ENDP

CODESG ENDS

END BEGIN

TITLE SUBMUL Подпрограмма для умножения

;=====================================================

CODESG SEGMENT PARA ‘Code’

SUBMUL PROC FAR

ASSUME cs:codesg

PUBLIC submul

mul bx ;ax-стоимость

;bx-количество

;произведение в dx:ax

ret

SUBMUL ENDP

CODESG ENDS

END

Рис. 1.6.

Передача параметров в подпрограмму

При вызове подпрограммы необходимо решить вопрос о передачи параметров вызываемой функции. Передача параметров может осуществляться через регистры процессора. Это самый простой способ. Однако, он имеет ряд существенных недостатков:

  • при таком способе передачи необходимо повсеместно отслеживать содержимое регистров до и после вызова подпрограммы;

  • число переданных параметров ограничивается числом регистров общего назначения, которых, как правило, не хватает.

В связи с этим часто параметры в вызываемую процедуру передаются через стек. В дальнейшем, при передаче параметров будем использовать соглашение, принятое для языка СИ.

На Рис. 1.7 показан вид стека перед выполнением первой команды процедуры Test, если ее вызов из С++ имел следующий вид:

int i,j;

.

.

.

i=25;

j=4;

Test(i,j,1) ;

SP

Адрес возврата

SP+2

25(i)

SP+4

4(j)

SP+6

1

Рис. 1.7. Вид стека перед началом выполнения процедуры Test.

На следующем рисунке показан вид стека после выполнения следующих строк вызываемой процедуры:

...

push bp

mov bp,sp

...

SP

BP при вызове

BP

SP+2

Адрес возврата

BP+2

SP+4

25(i)

BP+4

SP+6

4(j)

BP+6

SP+8

1

BP+8

Рис. 1.8. Вид стека после выполнения PUSH и MOV.

Место для автоматически размещаемых переменных резервируется путем вычитания нужного количества байтов из SP. Например, для автоматического размещения массива из 100 байт в процедуре Test должны использоваться следующие операторы:

...

push bp

mov bp,sp

sub sp,100

...

SP

BP-100

...

SP+100

BP при вызове

BP

SP+102

Адрес возврата

BP+2

SP+104

25(i)

BP+4

SP+106

4(j)

BP+6

SP+108

1

BP+8

Рис. 1.9. Состояние стека после размещения 100-байтного массива.

Для адресации автоматически размещаемых переменных используется отрицательное смещение относительно регистра BP. Например,

mov BYTE PTR [bp-100], 0

установит значение первого байта 100-байтного массива в 0.

На рис. 1.10 приведена программа написанная на языке СИ, а на рис. 1.11 десамблируемый код этой программы. При этом следует иметь ввиду, что вызовы функций, написанных на языке СИ, производятся не непосредственно из операционной системы, а из специальной программы - загрузчика(Startup Code). При завершении программы управление передается функции завершения(Exit Code). Эти две функции находятся в модуле COx.OBJ, который включается в исполняемый модуль при компоновке.

int test(int, int);

void main(void)

{

int i,j,k;

i=2;

j=4;

k=test(i,j);

}

int test(int i, int j)

{

int k;

k=i*j;

return k;

}

Рис. 1.10

;_main

push bp

mov bp,sp

sub sp,0006

mov word ptr [bp-02], 0002

mov word ptr [bp-04], 0004

push word ptr [bp-04]

push word ptr [bp-02]

call _test

pop cx

pop cx

mov [bp-06], ax

mov sp, bp

pop bp

ret

;_test

push bp

mov bp, sp

sub sp, 0002

mov ax, [bp+04]

imul word ptr [bp+06]

mov [bp-02], ax

mov sp, bp

pop bp

ret

Рис. 1.11.

Для автоматического размещения переменных используется директива LOCAL, которая проводит распределение в стеке поименованных локальных переменных. Дитектива LOCAL имеет следующий формат:

LOCAL аргумент [,аргумент]... [=идентификатор]

Каждый аргумент имеет следующий синтаксис:

имя [ счетчик 1] [:сложный_тип [:счетчик 2 ]]

Здесь сложный_тип представляет тип данных аргумента. Если тип не задан в явной форме используется тип WORD для 16-битовой модели или DWORD для 32-битовой.

Параметр счетчик2 указывает, сколько элементов такого типа опредедяет данный аргумент. Например, описание аргумента

LOCAL tmp:DWORD:4

означает, что аргумент tmp состоит из четырех двойных слов.

По умолчанию счетчик2 имеет значение 1 для всех типов элементов , кроме типаBYTE .Поскольку операции со стеком не работают с отдельными байтами , то при выборе типа BYTE счетчик2 получает значение 2. Для того чтобы аргумент помещался в стек в виде одного байта ,необходимо задать значение счетчика в явном виде:

LOCAL realbyte: BYTE:1

Счетчик1 определяет количество элементов с данным именем, Общий размер места ,выделяемого в стеке ,является произведением счетчика1 на счетчик2 и на размер одного элемента, определяемого типом элемента. Значение счетчика1 ,принимаемого по умолчанию -1.

Если в конце списка аргументов указан символ равенства и идентификатор ,то Turbo Assembler вычисляет общий размер блока аргументов в байтах и присваивает результат этому идентификатору. Это значение можно использовать в качестве аргумента команды RET , особенно если не используется соглашение о поддержке языков высокого уровня .В этом случае перед возвратом в вызывающую процедуру выполняется очистка стека от всех аргументов (‘это соответствует соглашениям языка PASCAL о вызовах процедур).

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]