Программирование на Ассемблере

         

Немонотонное изменение параметра цикла


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

Пример 1. Составить программу для нахождения заданного элемента в массиве целых упорядоченных чисел.

nom=-1;

i=0; j=n-1;

while (i<=j){

   k=(i+i)/2;

   if (x[k]==r){

      nom = k;

   break;

 }

 if (x[k]<r)i=k+1; else j=k-1;

}

;nom=-1;

mov     eax, -1

;i=0; j=n-1;

mov     ebx, 0

mov     ecx, [n]

dec       ecx

;while (i<=j){

cmp     ebx, ecx



jg         short m1

;   k=(i+i)/2;

mm9:

mov     edx, ebx

add      edx, ecx

shr       edx, 1

;   if (x[k]==r){

mov     esi, [x+edx*4]

cmp     esi, [r]

jne       mm6

;nom = k;

mov     [nom], edx

;break;

jmp      short mm7

;}

 ;if (x[k]<r)i=k+1; else j=k-1;

mm6:

cmp     esi, [r]

jge       short mm8

mov     ebx, edx

inc       ebx

jmp short mm9

mm8:

mov     ecx, edx

dec       ecx

jmp short mm9

;}

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

for (i=0; str1[i]; i++){

   ch = str1[i];

   n = nom[i];

   str2[n] = ch;

}

str2[i] = 0;

str1      db        ‘abcdef’, 0

nom     dd        5, 1, 0, 2, 4, 3

str2      db        (nom - str1) dup (?)

...

;for (i=0; str1[i]; i++){

mov     eax, 0

;ch = str1[i];

m2:

mov     bl, [str1+eax]

test       bl, bl

je         short m1

;n = nom[i];

mov     ecx, [nom+eax]

;n = nom[i];

mov     [str2+ecx], bl

inc       eax

jmp      m2

m1:



Использование команд для работы с блоками


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

копирование массива в другой массив:

сравнение двух массивов;

заполнение массива заданным значением или последовательным чтением элементов массива;

поиск заданного элемента массива.

Для определения направления поиска устанавливается флаг d регистра флагов (бит №  10).

Адрес исходного массива задается в регистре ESI, а результирующего - в регистре EDI.

Количество элементов массива задается в регистре ECX.

Команды :

MOVS{B|W|D}          - do{*(edi++) = *(esi++) while (ecx--);}

LODS{B|W|D}           {AL|AX|EAX} = *(esi++)

STOS{B|W|D}            *(edi++) = {AL|AX|EAX}

CMPS{B|W|D}           сравнение *(esi++) и *(edi++)

SCAS{B|W|D}            do{ if {AL|AX|EAX} = (!=) ) *(edi++) break; while (ecx--);

В таблице 7.1 представлена сводная таблица команд для работы с блоками

Таблица 7.1. Сводная таблица команд для работы с блоками

Код

Выполняемые действия

Подготовка команды

Используемый префикс

MOVS

dest = src

cld (std);

esi = &src[10]

edi = & dest

ecx = number

REP

LODS

{al, ax, eax} = *esi++

cld (std);

esi = &src

Префикс не используется

STOS

*edi++ = {al, ax, eax}

cld (std);

edi = &dest

ecx = number

REP

CMPS

*src ++

<>*dest++

cld (std);

esi = &src7

edi = & dest

ecx = number

REPE, REPNE

SCAS

{al, ax, eax} <>

*dest++

EAX - что ищем,

edi = & dest (где ищем)

ecx = number

REPE, REPNE

Примеры использования команд

Скопировать область памяти заданной длины в другую область памяти, если адрес начала первой области f1, ее размер len байт, адрес начала второй области f2. Пусть len кратно 4.

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


Е
Movаааа esi, [f1]
Movаааа edi, [f2]
Movаааа ecx, [len]
Shrаааааа ecx, 2
Jecxzааа m1
cld
cmpаааа esi, edi
jaаааааааа short m2
jeаааааааа break
movаааа esi, eax
decаааааа esi
addааааа edi, [len]
decаааааа edi
std
m2:
repаааааа movsd
break:
cld
¦ЁшьхЁ 2. T чрфрээющ ёЄЁюъх ьрыхэ№ъшх ырЄшэёъшх сєътv чрьхэшЄ№ сюы№°шьш.
Str1ааааа dbааааааа СThis is stringТ,0
Е
cld
movаааа esi, offset str1
movаааа edi, offset str1
for1:
lodsb
testаааааа al, al
jeаааааааа short break
cmpаааа al, СaТ
jbаааааааа notl
cmpаааа al, СzТ
jaаааааааа notl
addааааа al, СAТ Ц СaТ
notl:
stosb
jmp for1
break:
cld
¦ЁшьхЁ 3. TюёЄртшЄ№ яЁюуЁрььє фы  ёЁртэхэш  фтєї фышээvї ўшёхы юфшэръютющ фышэv. ¦ЁюуЁрььр фюыцэр ЇюЁьшЁютрЄ№ Ёхчєы№ЄрЄ >0, хёыш яхЁтюх ўшёыю сюы№°х тЄюЁюую, Ёртэю 0, хёыш ўшёыр Ёртэv ш <0, хёыш яхЁтюх ўшёыю ьхэ№°х тЄюЁюую. ¦єёЄ№ ўшёыю ёюёЄюшЄ шч n 32-сшЄэvї лЎшЇЁ¬ ш чряшёvтрхЄё , эрўшэр  ё ьырф°шї ЎшЇЁ.
Xааааааааа ddааааааа Е, Е., Е.
Yааааааааа ddааааааа Е, Е, Е
Nаааааааа ddааааааа (Y Ц X)/4
Resааааа ddааааааа ?
Е
subаааааа eax, eax
movаааа ecx, [N]
leaаааааа esi , [X+ecx*4-4]
leaаааааа edi , [Y+ecx*4-4]
std
repeаааа cmpsd
jecxzааа m1
jbаааааааа letter
movаааа eax, 1
jmpааааа short m1
letter: movаааааа eax, -1
m1:
cld
ааааааааа
¦ЁшьхЁ 4.
T чрфрээюь ьрёёштх ўшёхы тёх эєыхтvх чэрўхэш  чрьхэшЄ№ Ц1
While (){
аа If (хёЄ№ эєыхтюх чэрўхэшх)
аа ааа¦рьхэшЄ№;
аа Else
ааааа Break;
}
movаааа ecx, [n]
cld
subаааааа eax, eax
movаааа edi, offset x
for1:
repneаа scasd
jecxzааа break
movаааа [dword ptr edi-4], -1
jmpааааа for1



Задание двухмерного массива


Пусть необходимо задать матрицу, определенную в С:

int matr[][4]= {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};

matr     dd        1,2,3,4

            dd        5,6,7,8

            dd        9,10,11,12

Задание адреса элемента

Для адресации данных массива можно использовать 2 регистра индексный и базисный [3.4.3]. Для каждого индекса определяется шаг изменения адреса.

Например, для массива:

int x[M][N] шаг изменения адреса по второму индексу Hj равен sizeof (int) = 4, а для первого индекса Hi равен Hj * N. Если длина элемента совпадает со значением масштабного множителя (стандартная длина элемента -1, 2, 4, 8), то в индексный регистр записывается значение индекса, если не совпадает то - значение смещения.

Для задания адреса элемента X[i][j] со стандартной длиной элемента используется запись вида [X+REG1 + REG2 * LEN], где REG1- общий регистр, содержащий смещение для i - ой строки, а REG2-

общий регистр, содержащий значение индекса j. Для элемента массива нестандартной длины адрес задается в виде [X+REG1 + REG2], где REG1- общий регистр, содержащий смещение для i - ой строки, а REG2- общий регистр, содержащий смещение для j - ого элемента строки.

Пример. Оттранслировать операторы:

int matr[][4]= {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};

long double numbers [][4]= {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};

// Формирование i, j

matr[i][j]=0; numbers[i][j]=0;

matr     dd        1,2,3,4

            dd        5,6,7,8

            dd        9,10,11,12

numbers         

dt         1,2,3,4

            dt         5,6,7,8

            dt         9,10,11,12

HIMATR         dd        16

HINumbers     dd        40

...

mov     eax, HIMATR

mul      [i]

mov     ebx, [j]

mov     [matr+eax+ebx*4], 0

mov     eax, HINumbers

mul      [i]

mov     ebx, eax; База

mov     eax, 10

mul      [j]

mov     [dword ptr numbers + eax + ebx], 0

mov     [dword ptr numbers + eax + ebx + 4], 0

mov     [word ptr numbers + eax + ebx + 8], 0



Монотонное изменение индекса


Структура программы с монотонным изменением индексов:

Вычисление Hj , Hi

Цикл для вычисления.

Пример. Составить программу для вычисления сумм столбцов матрицы:

for (j=0; j<N; j++){

            s[j]=0;

            for (i=0; i<M; i++)

                        s[j]+=matr[i][j]

}

Для оптимизации программы запишем:

for (j=0; j<N; j++){

            r=0;

            for (i=0; i<M; i++)

                        r+=matr[i][j]

            s[j]=r;

}

; Вычисление Hi =4*N, Hj=4

mov     eax, [N]

shl        eax, 2; Hi

mov     ebx, 4; Hj

;for (j=0; j<N; j++){

mov     ecx, [N]

shl        ecx, 16

mov     edx, 0; j

forj:

;           r=0;

mov     esi, 0

;           for (i=0; i<M; i++)

mov     cx, [M]

mov     edi, 0; i

;                       r+=matr[i][j]

fori:

add      esi, [matr + edi + edx*4]

; i++

add      edi, eax

dec       cx

jnz        fori

;           s[j]=r;

mov     [s+edx*4], esi

;j++

inc       edx

sub       ecx, 10000h

jnz        forj

}

...

Недостатки метода:

·        для каждого индекса свой регистр;

·        не может быть обобщен на массивы большей размерности.



Метод связанных индексов


Адрес элемента в начале строки используется для формирования адреса очередного элемента для обоих индексов, т.е. фактически требуется 2 экземпляра этого адреса. Т.к. фактически используется одно и тоже значение для работы с каждым индексом метод называется методом связанных индексов. Для получения 2-х экземпляров используется стек. Формируется необходимое значение, один экземпляр которого записывается в стек, второй остается в регистре для работы со вторым индексом. При необходимости работы с первым индексом извлекается значение из стека.

Структура программы для метода связных индексов:

for (i=0; i<.. i++){                             mov     ecx, ...

mov     ebx, Смещение ПОЭ[11]

            ...

for (j=0; j<... ; j++){              fori:

                        ...                                 push     ecx

                                                            push     ebx

                                                            mov     ecx,...

                                                            forj:

                                                            [Имя массива + ebx]

}                                               add      ebx, [Hj]

                                                            loop     forj

}                                                           pop      ebx

                                                            add      ebx, Hi

                                                            pop      ecx

                                                            loop     fori

Пример 1. Составить программу для нахождения сумм строк матрицы с нечетными номерами

for (k=i=0; i<M; i+=2, k++){

   r=0;

   for (j=0; j<N; j++)

      r+=matr[i][j]

   s[k]=r;

}

mov     esi, [N]

shl        esi, 3; Шаг по i

;for (k=0. i=1; i<M; i+=2, k++){

sub       eax, eax; k

mov     ecx, [M]

shr       ecx, 1

mov     ebx, [N]

shl        ebx, 2; i

fori:

;r=0;


subаааааа edx, edx
;аа for (j=0; j<N; j++)
pushаааа ecx
movаааа ecx, [N]
pushаааа ebx
forj:
;ааааа r+=matr[i][j]
addааааа edx, [matr+ebx]
;j++
addааааа ebx, 4
loopаааа forj
;аа s[k]=r;
movаааа [s+eax*4], edx
;i++
incаааааа eax
popааааа ebx
addааааа ebx, esi
popааааа ecx
loop fori
¦ЁшьхЁ 2. TюёЄртшЄ№ яЁюуЁрььє фы  яхЁхьэюцхэш  ьрЄЁшЎ
ideal
p586
model flat
extrn ExitProcess:proc
dataseg
matrаааааа ddааааа 1,1,1,1,1
аааааааааа ddааааа 2,2,2,2,2
аааааааааа ddааааа 3,3,3,3,3
аааааааааа ddааааа 4,4,4,4,4
Mааааааааа ddааааа 4
Nааааааааа ddааааа 5
Sааааааааа ddааааа 5 dup (?)
codeseg
begin:
movааа esi, [N]
shlааа esi, 3; ? г по i
;for (k=0. i=1; i<M; i+=2, k++){
subа eax, eax; k
movа ecx, [M]
shrа ecx, 1
movа ebx, [N]
shlа ebx, 2; i
fori:
;r=0;
subаа edx, edx
;аа for (j=0; j<N; j++)
pushааа ecx
movаааа ecx, [N]
pushааа ebx
forj:
;ааааа r+=matr[i][j]
addааа edx, [matr+ebx]
;j++
addа ebx, 4
loopааааа forj
;аа s[k]=r;
movаааааааа [S+eax*4], edx
;i++
incа eax
popа ebx
addа ebx, esi
popа ecx
loop fori
callааа ExitProcess
endаааа begin
¦ръ тшфэю шч яЁюуЁрььv фы  ърцфюую шч ьрёёштют ЄЁхсєхЄё  Єюы№ъю юфшэ ЁхушёЄЁ.

Использование 2-х мерного массива при немонотонном изменении индекса


Пусть задана разреженная матрица, содержащая очень много нулей.

Для упрощения задания определяются только ненулевые элементы матрицы в виде:

x, y, value,

где x, y - координаты ненулевого значения. Составить программу для заполнения матрицы заданными значениями.

Для решения этой задачи потребуется задать адрес элемента массива matr[x][y], который вычисляется по формуле:

& matr[x][y] = & matr[0][0] + x * Hx + y * Hy

; Задание матриц

PackMatr                    dd        1, 3, 10

                                    dd        2, 7, 20

                                    dd        4, 4, 30

count                           dd        3

UnpackMatr                dd        5 dup (5 dup 0)

hi                                 dd        5*4

; Заполнение матрицы UnpackMatr:

mov     ecx, [count]

mov     ebx, offset PackMatr

for1:

mov     eax, [ebx]        ;x

mul      [hi]                  ; x * hi

mov     edx, [ebx+4]   ;y

mov     esi, [ebx+8]    ;value

mov     [UnpackMatr+eax+edx*4], esi

add      ebx, 12

loop     for1



Использование метода связных индексов для многомерных массивов


Пример. Составить программу для реализации фрагмента программы:

int a[3][5][4];

for (k=1, l=0; k<4; k+=2, l++){

r=0;

            for (i=0; i<3; i++)

                        for(j=0; j<5; j++)

                                    r+=a[i][j][k];

            s[l]=r;

}

...

dataseg

a          dd        1,1,1,1

            dd        2,2,2,2

            dd        3,3,3,3

            dd        4,4,4,4

            dd        5,5,5,5

            dd        1,1,1,1

            dd        2,2,2,2

            dd        3,3,3,3

            dd        4,4,4,4

            dd        5,5,5,5

            dd        1,1,1,1

            dd        2,2,2,2

            dd        3,3,3,3

            dd        4,4,4,4

            dd        5,5,5,5

s           dd        2 dup(?)

hk        dd        4

hj         dd        16

hi         dd        16*5

codeseg

begin:

;for (k=1, l=0; k<4; k+=2, l++){

mov     ecx, 4

shr       ecx, 1

mov     ebx,offset a

add      ebx, 4

mov     esi, offset s

fork:

;r=0;

sub       edi, edi

;for (i=0; i<3; i++)

push     ebx

push     ecx

mov     ecx, 3

;for(j=0; j<5; j++)

fori:

push     ebx

push     ecx

mov     ecx, 3

forj:

;r+=a[i][j][k];

add      edi, [ebx]

;j++

add      ebx, [hj]

loop     forj

;i++

pop      ecx

pop      ebx

add      ebx, [hi]

loop     fori

;s[l]=r;

mov     [esi], edi

;l++

add      esi, 4

pop      ecx

pop      ebx

add      ebx, 8

loop     fork

;}



Использование метода приведенных индексов для многомерных массивов


Пусть определение массива имеет вид:

Тип Имя [p1..q1, p2..q2, ...pn..qn], где pi..qi- минимальное и максимальное значения i-ого индекса

Адрес элемента массива с индексами i1, i2, ...in определяется формулой[12]:

& Имя[i1, i2, ...in] = Адресу начала массива +

 -
, где Hi - шаг изменения адреса для i-ого индекса.

Формулы для вычисления адреса:

Hn = sizeof (Тип);

Hn-1 = Hn * (qn

- pn +1);

...

H1 = H2 * (q2

- p2 + 1).

Формулы вычисления адреса показывают, что наиболее простой вид формула имеет при начальном значении индексов, равном 0.

Пример1. Реализовать фрагмент программы:

int x[3][4][5][6];

...

x[i][j][k][l]=i+j+k+l;

x          dd        (3*4*5*6) dup (?)

i           dd        1

j           dd        2

k          dd        3

l           dd        4

hl         equ      4

hk        equ      6*hl

hj         equ      5*hk

hi         equ      4*hj

...

mov     eax, [i]

imul     eax, hi

mov     ebx, [j]

imul     ebx, hj

add      eax, ebx

mov     ebx, [k]

imul     ebx, [hk]

add      eax, ebx

mov     ebx, [l]

mov     ecx, [i]

add      ecx, [j]

add      ecx, [k]

add      ecx, [l]

mov     [x+eax+ebx*4], ecx

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



Многомерные массивы


Для многомерных массивов используются:

·        метод связных индексов при монотонном изменении индекса

·        метод приведенных индексов, когда вычисляется адрес элемента массива в соответствующем одномерном массиве.



Задание типа структуры


Общий вид задания типа:

Struc Имя  

поле1

поле2

...

ends [Имя]

Полями структуры могут быть директивы определения данных, в том числе и другие структуры, т.е. допускается вложение структур.

Пример. Определить тип для задания структуры «Возраст», содержащей день рождения в виде:

Год(2 байта) Месяц (1 байт)          День (1 байт)

STRUC  AGE

day                  db        ?

month  db        ?

year                 dw       ?

ends



Выделение памяти под структуру


Для выделения памяти используется запись вида:

Имя поля        Тип структуры         ?  или значение полей структуры.

Пример:

age1    AGE    ?

age2    AGE    ?

Как и для стандартных типов данных, можно задавать коэффициенты повторений.

AgeList            AGE    50 dup (?)

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



Инициализация полей структуры


Используется 3 способа инициализации.

Способ 1. Значения полей задаются в задании типа структуры, например:

STRUC  AGE 

day                  db        1

month              db        1

year                 dw       1980

ends

Эти значения переносятся в поля структуры, если для них не используются другие способы задания.

Способ 2. Позиционное задание полей. Значения полей задаются в порядке их следования в определении данного через запятую в угловых скобках, например

age1    AGE    < 1,2, 1979>

age2    AGE    < 25, 10, 1981,>

Если значение какого-то поля переопределять не нужно, оно не записывается, но запятая ставится, например:

age3    AGE    <25, 10,>

В этом случае год рождения 1980, как определено в задании типа.

Если не определяется значение последних полей, запятые можно не ставить, например:

age4    AGE    <25 >

Здесь месяц и год определяются из задания типа.

Если значения полей не надо переопределять, записываются просто угловые скобки, например

age5    AGE    < >

Способ 3. Непозиционное задание полей. Значения полей задаются в произвольном порядке в фигурных скобках. Для каждого значения используется формат:

Имя поля = Значение.

Определения разделяются запятыми. Пример.

age5    AGE    {day = 3, year = 1990, month = 2}



Особенности инициализации строк и массивов


Пусть полем структуры является строка длиной 10 символов. Чтобы можно было задавать начальные значения этому полю, необходимо его представление в виде:

pole1   db        ‘          ’

В кавычках задано требуемое количество пробелов. Если строка задана в виде 10 dup ( ), компилятор ее воспринимает как данное типа 1 байт и допускает инициализацию только одного байта. При задании числовых массивов или используются разные имена для отдельных элементов, или инициализация не допустима.



Обращение к полям структуры


Для обращения к полю по имени структуры используется запись вида:

Имя структуры. Имя поля.

Если адрес структуры задан в регистре, то поле структуры задается в виде [(Имя структуры reg). Имя поля].

Пример. Задан массив с возрастами. Определить возраст самого старшего человека списка

ideal

p386

model   flat

extrn ExitProcess:proc

dataseg

STRUC AGE

day db 1

month db 1

year dw 1980

ends

list  AGE < 3,10>, <1,1,1993>

      AGE  < 1,1, 1989>, <>

count dd 4

old AGE ?

codeseg

begin:

mov eax, offset list

mov bx, [(AGE eax).year]

mov dl, [(AGE eax).month]

mov dh, [(AGE eax).day]

mov ecx, [count]

dec ecx

add eax, 4

for1:

cmp bx, [(AGE eax).year]

jl short next

jg write

cmp dl, [(AGE eax).month]

jl short next

jg short write

cmp dh, [(AGE eax).day]

jle short next

write:

mov bx, [(AGE eax).year]

mov dl, [(AGE eax).month]

mov dh, [(AGE eax).day]

next:

add eax, 4

loop for1

mov [old.year], bx

mov [old.month], dl

mov [old.day], dh

call ExitProcess

end begin

Вариант 2.

Использовать для сравнения целиком всю запись.

ideal

p386

model   flat

extrn ExitProcess:proc

dataseg

STRUC AGE

day db 1

month db 1

year dw 1980

ends

list  AGE < 3,10>, <1,1,1993>, < 1,1, 1989>, <>

count dd 4

old AGE ?

codeseg

begin:

mov eax, offset list

mov ebx, [ eax]

mov ecx, [count]

dec ecx

for1:

add eax, 4

cmp ebx, [ eax]

jle short next

write:

mov ebx, [ eax]

next:

loop for1

mov [dword old], ebx

call ExitProcess

end begin

Пример 2. Составить программу для упорядочивания списка студентов в порядке убывания среднего балла

Способ 1. В запись включить фамилию и оценки - недостаток - размер фамилии может содержать разное число символов - надо выделять память по максимуму.

Способ 2. Записать список фамилий. Адрес каждой фамилии -в запись. Достоинства:

памяти, сколько требуется;

при сортировке перемещается не строка, а ее адрес - уменьшается требуемое время.


Lёяюы№чєхь фы  єяюЁ фюўштрэш  ьхЄюф яюфёўхЄр .
TєЄ№ ьхЄюфр: -ы  ърцфюую ўшёыр юяЁхфхы хь ъюышўхёЄтю ўшёхы, яЁхф°хёЄтє¦•шї фрээюьє ўшёыє. ¦рЄхь ўшёыр ёЄрт Єё  эр ётюш ьхёЄр т фЁєуюь ьрёёштх.
; +яЁхфхыхэшх ъюышўхёЄтр яЁхф°хёЄтє¦•шї ўшёхы
аfor (i=0; i<n; i++) s[i]=0;
for (i=1; i<n; i++)
аа for (j=0; j<i; j++)
ааааа if (x[j]>x[i]) s[j]+=1; else s[i]+=1;
; ¦ряшё№ ўшёхы эр ётюш ьхёЄр
for (i=0; i<n;i++)
аа y[s[i]]=x[i];
ideal
p386
modelаа flat
extrn ExitProcess:proc
dataseg
fio1ааа dbааааа 'aaaaaaaa', 0
fio2ааа dbааааа 'bbbbbb', 0
fio3ааа dbааааа 'cccccccccccccccc', 0
fio4ааа dbааааа 'ddddddd', 0
fio5ааа dbааааа 'eeeeeeeeeeeeeeeeeeee', 0
struc аа data1
pfioаааааа ddаа ?
oc1ааааааа dbаа ?
oc2ааааааа dbаа ?
oc3ааааааа dbаа ?
oc4ааааааа dbаа ?
ends
gr1ааааааа data1а < offset fio1, 2, 3, 4, 5>, < offset fio2, 3,4,5,3>
аааааааааа data1а < offset fio3, 5, 5, 5, 5>, < offset fio4, 5,4,5,5>
аааааааааа data1а < offset fio5, 5, 4, 4, 4>
countааааа ddааа 5
rааааааааа data1 5 dup (?)
sааааааааа ddаа 5 dup(0)
codeseg
begin:
;for (i=1; i<n; i++)
movа ecx, [count]
decа ecx
movаааа eax, 1
movа esi, offset gr1
addааааа esi, 8
fori:
movа bl,0
addа bl, [(data1 esi).oc1]
addа bl, [(data1 esi).oc2]
addа bl, [(data1 esi).oc3]
addа bl, [(data1 esi).oc4]
;аа for (j=0; j<i; j++)
movаааа edx, 0
movаааа edi,offset gr1
forj:
cmpаааа edx, eax
jgeаааа breakj
movаааа bh, 0
addаааа bh, [(data1 edi).oc1]
addаааа bh, [(data1 edi).oc2]
addаааа bh, [(data1 edi).oc1]
addаааа bh, [(data1 edi).oc1]
;ааааа if (x[j]>x[i]) s[j]+=1; else s[i]+=1;
cmpааа bh, bl
jgeааа short mge
incааа [s+edx*4]
jmpааа short nextj
mge:
incааа [s+eax*4]
nextj:
incааа edx
addааааа edi, 8
jmpаа forj
;i++
breakj:
incа eax
addааааа esi, 8
loopаааа fori
;for (i=0; i<n;i++)
movа ecx, [count]
movа eax, 0
for2:
;аа y[s[i]]=x[i];
leaаааааа esi, [ gr1 + eax* 8]
movа ebx, [s+eax*4]
leaаааааа edi, [r+ebx*8]
movа edx, [(data1 esi).pfio]
movа [(data1 edi).pfio], edx
movа edx, [dwordа (data1 esi).oc1]
movа [dword (data1 edi).oc1], edx
;i++
incа eax
loopаа for2
call ExitProcess
end begin

Структуры


Используются для задания полей разной длины, входящих в одну запись.



Особенности инициализации полей объединения


В отличие от структур можно инициализировать только элемент первого поля. Для инициализации можно использовать такие же способы, как для структуры.

Пример. В предыдущей задаче поле оценок использовалось как 4 байта и одно 4-х байтовое слово. Задать это с помощью объединения.

STRUC  STRUC1      

oc1        db   ?

oc2        db   ?

oc3        db   ?

oc4        db   ?

ends

UNION           UNION1

st1     STRUC1     <5,5,5,5>

st2        dd        ?

ends

STRUC  DATA1

pfio       dd   ?

oc        UNION1         ?

ends

Для доступа к полям в этом случае используется запись вида:

data1.oc.st1.oc1



Объединения


Используются, если необходимо одно и тоже поле памяти использовать как данные разного типа. Отдельные поля объединения записываются в памяти, начиная с одного и того же адреса - это отличие от структуры.

Общий вид объединения

UNION Имя  

поле1

поле2

...

ends [Имя]

Требования и способы задания полей как для структур.



Перечисления


Общий вид:

Имя типа ENUM {Имя поля [= значение], Имя поля [= значение],... }

Перечисление используется для задания группы констант, чтобы не повторять директивы EQU или =. Имя поля является именем константы. Значение первого поля, если оно не задано, принимается равным 0, значение любого другого поля, если оно не задано, равно значению предыдущего, увеличенному на 1. Значения разных полей в списке могут повторяться.

Пример.

Запись COLORS ENUM {BLUE, GREEN, RED} эквивалентна определению трех констант:

BLUE  EQU    0

GREEN           EQU    1

RED                EQU    2

Поля объединений можно использовать в любом месте, где допускается использование константы.



Основные команды для работы с битами


Основные команды заданы в табл. 10.1

Таблица 10.1. Основные команды для работы с битами

Но-мер

Назначение

Код

Выполняемые действия

Формируемые флаги

1

Побитовое сложение

OR

оп1|=оп2

c=0, o=0, z, p, s

2

Побитовое умножение

AND

оп1&=оп2

c=0, o=0, z, p, s

3

Проверка

TEST

оп1&оп2

c=0, o=0, z, p, s

4

Побитовое

Отрицание

NOT

~оп1

Флаги не изменяются

5

Сложение по модулю 2

XOR

оп1^=оп2

c=0, o=0, z, p, s

Примеры использования команд.

Пример1. Записать команды для:

·        установки в 1 заданного бита в байте;

·        установки в 0 заданного бита в слове;

·        инвертирования заданного бита в двойном слове;

·        проверки заданного бита в двойном слове;

Пусть номер бита является константой

BitNumber  EQU  3

b        db      37h

w       dw     1234h

d1      dd      12345678h

d2      dd      12345678h

...

;        установка в 1 заданного бита в байте

OR    b, 1 SHL BitNumber

;        установка в 0 заданного бита в слове

AND  w, NOT (1 SHL BitNumber)

;        инвертирование заданного бита в двойном слове

XOR  d1, 1 SHL BitNumber

проверка заданного бита в двойном слове

TEST d2, 1 SHL BitNumber

jz       zero

...

zero:

Пример 2.

Задан массив байтов. Переписать в другой массив те байты первого массива, в которых биты 0 и 4 единичные, биты 1, 3, 7 - нулевые, значения остальных битов не имеет значения.

Для решения этой задачи сформируем маски для выделения требуемых битов и проверки заданных битов на 1. Первая маска может быть сформирована так:

(1 shl 0) or (1 shl 4) or (1 shl 1) or (1 shl 3) or (1 shl 7).

Вторая маска имеет вид:

(1 shl 0) or (1 shl 4).

Ideal

p386

model flat

extrn ExitProcess:proc

dataseg

a          db        11h, 12h, 23h, 34h, 56h, 78h, 90h, 0a1h, 0b2h, 0c3h, 0d4h, 0e5h


count   dd        11
b          db        11 dup (0)
codeseg
begin:
mov     ecx, [count]
mov     eax, 0; индекс исходного массива
mov     edx, 0   ;индекс результирующего массива
fori:
mov     bl, [a+eax]
mov     bh, bl
and      bl, (1 shl 0) or (1 shl 4) or (1 shl 1) or (1 shl 3) or (1 shl 7)
xor       bl, (1 shl 0) or (1 shl 4)
jnz        short next
mov     [b+edx], bl
inc       edx
next:
inc       eax
loop     fori
call      ExitProcess
end      begin
В этом примере только первое число удовлетворяет поставленным требованиям.
Пример 3. Вычислить значение булевского выражения.

Значение выражения равно истине, если значение хотя бы одного слагаемого равно истине. Для выделения требуемых битов и проверки единичных битов используются константы:
; Первое слагаемое
c11      db        11010110b
c12      db        10000110b
; Второе слагаемое
c21      db        01101100b
c22      db        01001000b
; Третье слагаемое
c31      db        00000111b
c32      db        00000101b
Пусть вычисляется значения для байта
x          db        10101010b
Результат записывается в поле RES и равен 0 для ответа «Ложь» и 1 для ответа «Истина».
RES     db        ?
; Проверка первого слагаемого
mov     RES, 1; Пусть ответ равен «Истина»
mov     al, [x]
and      al, [c11]
xor       al, [c12]
jz          short    true
; Проверка второго слагаемого
mov     al, [x]
and      al, [c21]
xor       al, [c22]
jz          short    true
; Проверка третьего слагаемого
mov     al, [x]
and      al, [c31]
xor       al, [c32]
jz          short    true
mov     [RES], 0
true:

Классификация команд


Сдвиг может быть обычным и циклическим. При обычном сдвиге разряды, выходящие за разрядную сетку, уничтожаются. Для циклического сдвига они записываются на место освободившихся разрядов.

Обычный сдвиг может быть арифметическим и логическим. При арифметическом сдвиге вправо свободные позиции слева заполняются знаковым (старшим) разрядом. При логическом сдвиге вправо свободные разряды слева заполняются нулями. Арифметический сдвиг вправо используется обычно для деления нацело знакового числа на 2n, а логический - для беззнакового числа. Операции сдвига влево для обоих типов команд выполняются одинаково.

Циклический сдвиг может быть с переносом и без переноса. Схема выполнения команд для обоих типов сдвига на 1 бит вправо представлен на рис. 10.1a и рис. 10.1b.

а - циклический сдвиг с переносом

b- циклический сдвиг без переноса

Рис. 10.1 Схема выполнения циклического сдвига

Стрелка вверху задает направление сдвига (вправо). Как видно из рисунка, выдвигаемый бит всегда помещается в бит переноса С. При циклическом сдвиге предыдущее содержимое бита переноса записывается в освободившийся разряд, при обычном – теряется, а в освободившийся разряд записывается выдвигаемый бит.

Заметим, что при любом сдвиге последний из выдвинутых разрядов записывается вместо бита переноса.



Кодировка команд сдвига


Первая буква кода определяет тип сдвига. Буква S соответствует обычному, а буква R циклическому сдвигу. Вторая буква уточняет тип сдвига. Для обычного сдвига используется буква A для арифметического и буква H для логического сдвига. Для циклического сдвига вторая буква С

соответствует сдвигу с переносом и буква O сдвигу без переноса. Третья буква определяет направление сдвига. Буква R

соответствует сдвигу вправо, и буква L сдвигу влево. Таким образом коды команд сдвига:



Общий вид команд сдвига


Общий вид команд сдвига:

код      оп1, оп2

оп1 - сдвигаемое данное. Может быть байтом. Словом или двойным словом. Может быть задано в общем регистре или памяти.

оп2 - задает, на сколько разрядов выполняется сдвиг. Может быть константой или регистром CL, в который предварительно необходимо записать константу сдвига.



Примеры использования команд сдвига.


Пример 1. Установить в 1 бит i

двойного слова x, если значение i вычисляется в программе.

Mov     cl, [i]

mov     1, eax

shl        eax, cl

or         [x], eax

Пример 2. Задан массив чисел, каждое число не более 32. Упаковать этот массив таким образом, чтобы под каждое число отводилось 5 бит.

Ideal

p386

model              flat

extrn                ExitProcess:proc

dataseg

x          db        3,5,7,9,2,4,6

xpack   db        (((xpack-x)*8+4)/5) dup (0)

count   dd        xpack   - x

codeseg

begin:

mov     eax, [count]

; for (i=0; i< count; i++){

   unsigned *p = xpack+(i*5)/8

}

mov     esi, 0;

mov     ebx, 0

fori:

mov     dx, 0

mov     ecx, ebx

and      ecx, 7

mov     dl, [x+esi]

shl        dx, cl

mov     ecx, ebx

shr       ecx, 3

or         [xpack+ecx], dx

add      ebx, 5

inc       esi

cmp     esi, [count]

jl          fori

call ExitProcess

end      begin




Группа команд: проверить и изменить


Команды проверяют значение заданного бита (записывают его содержимое в бит C) и меняют значение в соответствии с заданным кодом. Первый операнд задает проверяемое и изменяемое данное, второй - номер бита. В качестве первого операнда может быть регистр и память размером 1, 2 или  4 байта. Второй операнд задается константой или регистром. Команды для проверки и изменеия заданных битов представлены в табл. 10.2

Таблица 10.2. Команды для проверки и изменеия заданных битов

Назначение

Код

Выполняемые действия

Проверка бита

Bt

C=(oп1 & (1<<оп2))>> оп2

Проверка и установка бита

Bts

C=(oп1 & (1<<оп2))>> оп2;

oп1 |= (1<<оп2))

Проверка и сброс бита

Btr

C=(oп1 & (1<<оп2))>> оп2;

oп1 &= (~(1<<оп2))

Проверка и инвертирование бита

Btc

C=(oп1 & (1<<оп2))>> оп2;

oп1 ^= (1<<оп2))

Примеры использования команд

Пример 1. Вычислить сумму всех битов, стоящих на четных местах в двойном слове

p486

ideal

model  flat

extrn ExitProcess:proc

dataseg

x          dd        0a5a5a5a5h

s           dd        ?

codeseg

begin:

xor       edx, edx; s=0

mov     eax, [x]

mov     ecx, 16

mov     ebx, 0

for:

bt         eax, ebx

adc      edx, 0

add      ebx, 2

loop     for

mov     [s], edx

call      ExitProcess

end begin

Программа выдает ответ 8.



Команды длинного сдвига


Общий вид команды:

,

где оп1 - задает, что сдвигается (память или 32 битный регистр);

оп2 - откуда вдвигаются разряды на освободившиеся места (32 - битный регистр);

оп3 - на сколько сдвиг (константа или регистр CL ).

Схема выполнения команды SHLD представлена на рис. 10.2

Рис. 10.2

Заметим, что содержимое оп2 сдвигается виртуально, фактическое содержимое этого регистра не изменяется.

Пример использования команды.

Задано число длиной 1024 бита. Разделить его на 8.

Для деления числа на 8 достаточно сдвинуть его на 3 бита вправо, в этом случае получим частное т деления. Остаток равен младшим трем битам исходного числа.

p486

ideal

model  flat

y=x>>3; ost = x[0]&7

extrn ExitProcess:proc

dataseg

x          dd        32 dup (0ffffffffh)

y          dd        32 dup (?); Частное

ost        dd        ?         ; Остаток

codeseg

begin:

; y=x

mov     ecx, 32

for1:

mov     eax, [x+ecx*4-4]

mov     [y+ecx*4-4], eax

loop     for1

;for (i=0; i<31; i++)

mov     ecx, 31

mov     eax, 0

; shrd (y[i], y[i+1], 3);

for2:

mov     ebx, [y+eax*4+4]

shrd     [y+eax*4], ebx, 3

;i++

inc       eax

loop     for2

;shr (y[i], 3);

shr       [y+eax*4], 3

; ost = x[0] & 7

mov     eax, [x]

and      eax, 7

mov     [ost], eax

call      ExitProcess

end begin




Дополнительные команды


Следующие две команды позволяют осуществить поиск первого установленного в 1 бита операнда. Поиск можно произвести как с начала так и от конца операнда:

bsf

операнд_1,операнд_2 (Bit Scaning Forward) - сканирование битов вперед.

Команда просматривает (сканирует) биты операнд_2

от младшего к старшему (от бита 0 до старшего бита) в поисках первого бита, установленного в 1. Если таковой обнаруживается, в операнд_1 заносится номер этого бита в виде целочисленного значения. Если все биты операнд_2 равны 0, то флаг нуля zf устанавливается в 1, в противном случае флаг zf сбрасывается в 0.

Пример:

mov     al,02h

        bsf     bx,al   ;bx=1

        jz      m1      ;переход, если al=00h

        ...

 

bsr операнд_1,операнд_2

(Bit Scaning Reset) — сканирование битов в обратном порядке. Команда просматривает (сканирует) биты операнд_2

от старшего к младшему (от старшего бита к биту 0) в поисках первого бита, установленного в 1. Если таковой обнаруживается, в операнд_1 заносится номер этого бита в виде целочисленного значения.

При этом важно, что позиция первого единичного бита слева отсчитывается все равно относительно бита 0. Если все биты операнд_2

равны 0, то флаг нуля zf

устанавливается в 1, в противном случае флаг zf

сбрасывается в 0. Заметим, что эти команды для tasm32 не компилируются



Определение типа битовой структуры


Общий вид типа:

record Имя поле1, поле2, ...

Для задания поля используется запись:

Имя: Ширина[= Значение]

Поля задаются, начиная с поля, соответствующего старшим разрядам. Суммарная ширина не может превосходить 32 бит. Если суммарная ширина не равна полному числу байтов, то число располагается в младших разрядах. Старшие разряды обнуляются.



Выделение памяти под битовую структуру


Общий вид директивы выделения памяти:

Тип структуры   Имя данного <Значения полей>

                                                { Значения полей }

Значения полей задаются как для обычной структуры в угловых скобках (позиционный метод) или в фигурных (вместе с именем поля). Если значение поля не задано, оно обнуляется.



Особенности использования битовых структур


Правила:

·        Если в команде используется имя данного, само

 значение данного используется.

·        Если в команде задано имя поля, то результатом является константа сдвига, которая показывает положение поля в записи.

·        Если в команде используется операция mask имя поля или mask имя типа, то формируется маска для выделения поля (записи).

·        Если в команде используется операция width имя поля или width имя типа, то формируется ширина поля (записи) в битах.



Пример использования битовой структуры


Дата создания файла задается в виде:

день :                   5 битов;

месяц :        4 бита;

год:             7 битов.

Год задает разность между текущим годом и 1970[13]

годом.

Значения для текущей даты упаковать в структуру и

проверить правильность упаковки.

ideal

p486

model  flat

extrn    ExitProcess:proc

dataseg

record data1 y:7, m:4, d:5

d1 data1          <>

day      db        1

month  db        11

year     dw       2000

codeseg

begin:

movzx ax, [day]

movzx bx, [month]

mov     cl, m

shl        bx, cl

or         ax, bx

mov     bx, [year]

sub       bx, 1970

mov     cl, y

shl        bx, cl

or         ax, bx

mov     [d1], ax           

mov     ax, [d1]

mov     bx, mask d

and      bx, ax

mov     [day], bl

mov     bx, mask m

and      bx, ax

mov     cl, m

shr       bx, cl

mov     [month], bl

mov     bx, mask y

and      bx, ax

mov     cl, y

shr       bx, cl

add      bx, 1970

mov     [year], bx

call       ExitProcess

end begin

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



Типы программ


Одномодульная и многомодульная.

Одномодульная программа - в одном файле. Многомодульная состоит из нескольких файлов.



Использование функций


Функции позволяют оформлять независимые  участки

программы в виде отдельных модулей, что значительно упрощает  отладку программы и уменьшает ее размер. Ниже будут рассмотрены команды  для  работы  с  функциями,  структура  функций и способы передачи параметров. Вначале даются общие положения для  функций.  Команды для работы с процедурами(функциями)

Основные команды для работы с функциями приведены в табл. 11.1, а директивы в табл. 11.2

Таблица 11.1 Команды для работы с функциями

Назначение

Общий вид команды

Выполняемые действия

Вызов функции

call операнд[14]

push EIP

MOV EIP, операнд

Возврат в вызывающую программу

ret [Число]

Esp+=Число

Pop EIP

Таблица 11.2. Директивы для работы с функциями

Назначение

Общий вид директивы

Заголовок функции

имя proc или proc имя

Директива конца

[Имя] endp или endp [Имя]

Общий вид функции для режима masm:

имя  proc

[; Сохранение используемых регистров]

.................

[; Восстановление используемых регистров]

ret

имя  endp

Общий вид функции для режима ideal:

proc имя

[; Сохранение используемых регистров]

.................

[; Восстановление используемых регистров]

ret

endp имя 

Функция  одномодульной программы должна быть частью сегмента кодов. Может быть задано несколько функций  последовательно.При выполнении программ переход к функции необходимо   выполнять только по команде call, а не в результате естественного  выполнения команд, поэтому функции помещаются или  в  начало  сегмента кода до точки входа, или в конце после команд для выхода из программы.



Передача параметров через регистры


Для задания параметров или их адресов используются регистры.

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

Для данной программы исходными данными являются 2 целых числа, результатом является значение наибольшего общего делителя. Параметрами являются значения 2-х чисел и адрес результата. Пусть исходные данные передаются через регисты EAX, EBX, а адрес результата через регистр ECX

IDEAL

p586

MODEL FLAT

extrn ExitProcess:proc

DATASEG

X          DD      150

Y          DD      120

Z          DD      ?

CODESEG

begin:

mov     eax, [X]

mov     ebx, [Y]

mov     ecx, offset Z

call      gcd

call      ExitProcess

; Процедура

proc     gcd

push     edx

; if (x<y) swap(x, y);

cmp     eax, ebx

jge       short m1

xchg     eax, ebx

; if (y==0) return x

m1:

test       ebx, ebx

je         short break

;           while(1)

for:

xor       edx, edx

div       ebx

test       edx, edx

je         short break

mov     eax, ebx

mov     ebx, edx

jmp      for

break:

mov     [ecx], ebx

pop      edx

ret

endp    gcd

end      begin

Достоинство способа - наиболее быстрый способ. Недостаток - недостаточное число регистров.

Пример 2. Составить процедуру для вычисления  длины  строки  с нулевым завершителем. Пусть адрес начала строки в регистре EBX,  а

адрес длины строки возвращается через регистр EAX.

; Процедура для вычисления длины строки с нулевым

; завершителем. Пусть адрес начала строки в регистре

; EBX, а адрес длины строки - через регистр EAX.

ideal

P486

MODEL  flat

extrn ExitProcess:proc

DATASEG

str1  db  'Это первая строка', 0

str2  db  'А это вторая', 0

n     dd  ?, ?

CODESEG

begin:

lea       ebx, [str1]

lea       eax, [n]

call      strlen

lea       ebx, [str2]

lea       eax, [n+4]

call strlen

call      ExitProcess

proc strlen

push ecx edx

xor  ecx, ecx

for:

mov     dl, [ebx]  ; while (str[i])ax++;

and      dl, dl

je         short lend; конец строки

inc       ecx

inc       ebx

jmp  for

lend:

mov     [eax], ecx

pop      edx ecx

ret

endp strlen

end  begin

Недостатки использования регистров для передачи параметров:

1. Можно использовать только для передачи данных длиной 1, 2, 4 байта или адресов:

2. Количество регистров ограничено.

Для преодоления недостатков можно все параметры записать в структуру и передать адрес начала структуры в единственном регистре или использовать для передачи параметров стек.



Передача параметров через общую область памяти


Т.к. в одномодульной программе  вызывающая программа и функция  расположены в одном файле,  функция может непосредственно использовать переменные программы.

Пример.

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

ideal

P486

MODEL  flat

extrn ExitProcess:proc

DATASEG

x          dd        5

y          dd        3

CODESEG

begin:

call      swap

call      ExitProcess

proc swap

push eax ebx

mov     eax, [x]

mov     ebx, [y]

mov     [y], eax

mov     [x], ebx

pop  ebx eax

ret

endp swap

end  begin

Недостатки использования переменных из сегмента данных:

1. Функция должна использоваться только для переменных с заданными именами, использовать ее для других переменных нельзя.

2. Этот способ не применим для многомодульных программ

Общей областью можно считать стек, в который помещаются параметры. Рассмотрим особенности передачи параметров через стек.

При передаче параметров через стек возникает проблема доступа к ним. Из всех режимов адресации выберем режим, связанный с адресацией в стеке. В этом режиме в  качестве  базисного  регистра  используется регистр EBP, именно этот регистр  будем  применять  для задания элемента стека. С другой стороны, указатель  стека,  т.е. адрес последнего занесенного элемента в стек расположен в  регистре ESP. Чтобы иметь доступ к данным стека используются команды:

                push ebp      ;  сохранение EBP

                mov ebp, esp ;  EBP=ESP

     Эти команды должны присутствовать в любой функции, в кото-

рой параметры передаются через стек.



Передача в списке параметров простых переменных


     Параметры можно разделить на параметры - исходные данные  и  параметры-результаты. Для первых передаются значения, а для вторых - адреса. Чтобы уменьшить число передаваемых параметров,  для  массивов передаются адреса, а не значения элементов, даже  для  массивов исходных данных. Чтобы стек не переполнялся,  после  использования  параметры должны быть извлечены из стека. Эту функцию, называемую  очисткой стека, обычно выполняет функция . Для этого в команде ret задается количество байтов, используемых для параметров, т.е. используется формат команды ret <число>. В этом случае сначала очищается стек, а затем выполняется возврат в вызывающую программу.

     Ниже рассмотрены примеры использования функций с  передачей

параметров через стек. Примеры отражают приемы  передачи  простых переменных (исходных данных и результатов), массивов,  структур, и адресов функций.

      Пример 1. Составить функцию вычисления z=x+y и использовать ее для вычисления c=a+b; f=d+e.

;Функция для вычисления z=x+y и использовать ее для вычисления c=a+b; f=d+e

                        IDEAL

                        P586

MODEL  FLAT

EXTRN ExitProcess:proc

                 DATASEG

          a            DD  5

          b            DD  3

          c            DD  ?

          d   DD  4

          e   DD  -5

          f   DD  ?

          CODESEG

          begin:

             

              push [a]      ; запись в стек a, b

              push [b]

              lea  eax, c      ;Формирование & c

              push  eax    ; и запись его в стек

              call summa  ; Вызов функции

             

              push  [d]     ; запись в стек d, e

              push  [e]

              lea  eax, [f]     ; Формирование & f

              push eax

              call summa  ; Вызов функции

            call ExitProcess

             

          

proc summa

0

EBP

push ebp

4

EIP

mov  ebp, esp

8

&c

Push eax ebx

12

B

mov  eax, [ebp+16]  ; a

16

A

Add  eax, [ebp+12]  ; a+b

mov  ebx, [ebp+8]  ; &c

mov  [ebx], eax    ; c=a+b

Pop ebx eax ebp

Ret 12

Endp summa

End begin

В примере показана передача простых переменных (исходных данных и результатов). Показано состояние стека после обращения к функции для вычисления c = a + b.  Анализ  программы  показывает, что передача параметров с последующей очисткой стека, передача управления процедуре требует дополнительных команд.  Эти  же действия выполняются при программировании на языке высокого уровня. Поэтому, прежде чем писать функцию  на любом языке, подумайте, эффективна ли она! Решение предыдущего примера без  функции явно предпочтительней!



Передача в списке параметров одномерных массивов


Для одномерных массивов – и исходных данных и результатов – передаются адреса. Это исключает необходимость копирования исходных массивов.

    

Пример 1.  Составить функцию для объединения двух строк  и использовать ее для объединения строк, содержащих слова "first  " и "second " в одну строку.

IDEAL

P586

MODEL FLAT

Extrn ExitProcess:proc

DATASEG

str1   db   'first ', 0

str2   db   'second', 0

rez    db    (rez-str1-1) dup (?)

CODESEG

begin:

push     offset str1 offset str2 offset rez  ; передача адресов массивов

call strcat  ;вызов процедуры

call ExitProcess

proc strcat;

0

EBP

push ebp;

4

EIP

mov  ebp,  esp;

8

REZ

Push eax ebx ecx;

12

STR2

; for (I=0; str1[I]; I++) rez[I]=str1[i];

16

STR1

Mov EAX, [EBP+16];

Mov EBX, [EBP+8];

For1:

Mov CL, [EAX]

Test Cl, cl

Je break1

Mov [EBX], cl

Inc eax

Inc ebx

Jmp For1

Break1:

Mov EAX, [ebp+12]

For2:

Mov CL, [EAX]

Mov [EBX], cl

Test Cl, cl

Je break2

Inc eax

Inc ebx

Jmp For2

Break2:

Pop ecx ebx eax ebp

Ret 12

Endp

End begin

     В этом примере признаком конца исходных данных является  нулевой завершитель (символ с кодом 0). В результирующую строку завершитель записывается после копирования второй строки. Для  массивов исходных данных (str1, str2) и массива результата (rez) передаются адреса. Это делается для  экономии  стекового  простран-

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

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

ideal

p386

model   flat

extrn ExitProcess:proc

dataseg

x          dd        1,2,3,4,5,6


ALаааааааааааааааааа - Ёхчєы№ЄрЄ - срщЄ
AXаааааааааааааааааа - Ёхчєы№ЄрЄ - ёыютю
EAXаааааааааааааааа - Ёхчєы№ЄрЄ Ц фтющэюх ёыютю
EDX, EAXаааааа - Ёхчєы№ЄрЄ Ц 2 фтющэvї ёыютр
аааа ¦ЁшьхЁ 3.а TюёЄртшЄ№ ЇєэъЎш¦ фы  ёЁртэхэш  "фышээvї" ўшёхы. LєэъЎш  фюыцэр тючтЁр•рЄ№ 0, хёыш ўшёыр ёютярфр¦Є, 1 Ц хёыш яхЁтюх ўшёыю сюы№°х тЄюЁюую ш Ц1 т яЁюЄштэюь ёыєўрх.
ideal
p686
modelаа flat
extrn ExitProcess:proc
dataseg
xааааааааа ddааааааа 1000 dup (0ffffffffh)
nааааааааа ddааааааа (n-x)/4
yааааааааа ddааааааа 1000 dup (0ffffffffh)
resаааааа ddааааааа ?
t1аааааааа ddааааааа ?
t2аааааааа ddааааааа ?
codeseg
begin:
pushаааа [n] offset x offset yаа
rdtsc
movаааа ecx,eax
callааааа m_cmp
movаааа [res], eax
rdtsc
subаааааа eax, ecx
movаааа [t1], eax
pushаааа [n] offset x offset yаа
rdtsc
movаааа ecx,eax
callааааа m_cmp
movаааа [res], eax
rdtsc
subаааааа eax, ecx
movаааа [t1], eax
pushаааа [n] offset x offset yаа
rdtsc
movаааа ecx,eax
callааааа m_cmp1
movаааа [res], eax
rdtsc
subаааа eax,ecx
movаааа [t2], eax
call ExitProcess
;for (i=n-1; i>=0; i--){ааааааааааааааааааааааа ebpааааа 0
;ааа if (x[i]>y[i]) return -1;а ааа eipаааааа 4
;ааа if (x[i]<y[i]) return 1;аа аааа yааааааааа 8
;}ааааааааааааааааааааааааааааа аааааааааааааа xааааааааа 12
;return 0;ааааааааааааааааааааа аааааааааааааааааааааа nааааааааа 16
proc ааа m_cmp
push ebp;
movа ebp,а esp;
Pushа ebx ecx edx
movаааа ecx, [ebp+16]; n
movаааа eax, [ebp+12]; &x
movаааа ebx, [ebp+8] ; &y
fori:
movаааа edx, [eax+ecx*4-4]
cmpаааа edx, [ebx+ecx*4-4]
jbаааааааа short m1; x[i]< y[i]
jaаааааааа short m2; x[i]> y[i]
loopаааа fori
subаааааа eax, eax
m1:
movаааа eax, -1
jmpааааа short m3
m2:
movаааа eax, 1
m3:
popааааа edx ecx ebx ebp
Retаааааа 12
Endp
proc ааа m_cmp1
push ebp;
movа ebp,а esp;
Pushаа ecx esi edi
movаааа ecx, [ebp+16]; n
movаааа esi, [ebp+12]; &x
leaаааааа esi, [esi + ecx*4-4]
movаааа edi, [ebp+8] ; &y


leaаааааа edi, [edi + ecx*4-4]
std
repeаааа cmpsd
jbаааааааа short m11; x[i]< y[i]
jaаааааааа short m12; x[i]> y[i]
subаааааа eax, eax
m11:
movаааа eax, -1
jmpааааа short m13
m12:
movаааа eax, 1
m13:
cld
popааааа edi esi ecx ebp
Retаааааа 12
Endp
end begin
¦рьхЄшь, ўЄю лЎшЇЁv¬ ўшёыр ёЁртэштр¦Єё , эрўшэр  ёю ёЄрЁ°хщ ЎшЇЁv. ¦рцфр  ЎшЇЁр ёЁртэштрхЄё  ъръ схччэръютюх ўшёыю. TЁртэхэшх яЁюфюыцрхЄё  фю Єхї яюЁ, яюър эрщфхь Ёрчэvх ЎшЇЁv шыш эх ёЁртэшь тёх ЎшЇЁv ўшёыр.
+сЁрЄшЄх тэшьрэшх эр эютvх ъюьрэфv фы  юяЁхфхыхэш  ъюышўхёЄтр ЄръЄют фы  тvяюыэхэш  ЇєэъЎшщ. ¦юьрэфр RDTSC (Read Time-stamp counter) - тvяюыэ хЄё  фы  яЁюЎхёёюЁют, эрўшэр  ё PENTIUM MMX ш тючтЁр•рхЄ т ЁхушёЄЁх EAX
+Єюсv юяЁхфхышЄ№, яюффхЁцштрхЄё  ыш фрээр  ъюьрэфр фы  яЁюЎхёёюЁр, шёяюы№чєхЄё  ъюьрэфр CPUIDЧCPU Identification
¦Єр ъюьрэфр Єръцх яючтюы хЄ юяЁхфхышЄ№ яЁюшчтюфшЄхы  яЁюЎхёёюЁр, ёхьхщёЄтю, ъюЄюЁюьє юэ яЁшэрфыхцшЄ, ьюфхы ш тхЁёшш, р Єръцх фЁєує¦ шэЇюЁьрЎш¦. -ы  юяЁхфхыхэш  Єшяр тvфртрхьющ шэЇюЁьрЎшш шёяюы№чєхЄё  ЁхушёЄЁ EAX ( Єрсы 11.3). ¦юьрэфр CPUID ьюцхЄ тvяюыэ Є№ё  фы  ы¦сюую єЁютэ  яЁштшыхушщ.
TрсышЎр 11.3

Tїюфэvх фрээvх
Tvїюфэvх фрээvх
EAX = 0
EAX Ц ьръёшьры№эюх чэрўхэшх, ъюЄюЁюх ьюцэю чрфртрЄ№ эр тїюфх (юсvўэю 2)
EBX, EDX, ECX Ц яЁюшчтюфшЄхы№ Ц -ы  PENTIUM ёЄЁюър лGenuineIntel¬ ЁрёяЁхфхыхэр Єръ: EBXа ?756e6547h (* "Genu", сєътр G т BL *)
EDXа ?49656e69h (* "ineI", сєътр i т -DL *)
ECXа ?6c65746eh (* "ntel", сєътра n т CL *)
EAX = 1
EAX Ц тхЁёш  яЁюЎхёёюЁр (Єшя, ёхьхщёЄтю, ьюфхы№, step)
EBX - ЁхчхЁт
ECX- ЁхчхЁт
EDX Ц шэЇюЁьрЎш  юс юёюсхээюёЄ ї яЁюЎхёёюЁр
EAX = 2
EAX ЦшэЇюЁьрЎш  ю ъ¤°х
EBX -ЦшэЇюЁьрЎш  ю ъ¤°х
ECX- ЦшэЇюЁьрЎш  ю ъ¤°х
EDX Ц ЦшэЇюЁьрЎш  ю ъ¤°х

¦рёёьюЄЁшь ёюфхЁцшьюх Єюы№ъю эхъюЄюЁvї сшЄют ЁхушёЄЁр EDX:
23 Ц яюффхЁцштрхЄё  MMX Ц Єхїэюыюуш ;
18 Ц яЁюЎхёёюЁ яюффхЁцштрхЄ 96 сшЄэvщ єэшъры№эvщ эюьхЁ яЁюЎхёёюЁр.
4 Ц яюффхЁцштрхЄё  ъюьрэфр RDTSC
¦ЁшьхЁ 4. TюёЄртшЄ№ ЇєэъЎш¦, ъюЄюЁр  тючтЁр•рхЄ юЄтхЄ 1, хёыш ъюьрэфр RDTSC яюффхЁцштрхЄё  ш 0 т яЁюЄштэюь ёыєўрх.
ideal
p686
modelаа flat
extrn ExitProcess:proc
dataseg
resаааааа ddааааааа ?
codeseg
begin:
callааааа IsReadCount
movаааа [res], eax
call ExitProcess
proc ааа IsReadCount
movаааа eax, 1
cpuid
movаааа eax, 1
testаааааа edx, 1 shl 4
jnzааааааа short m1
subаааааа eax, eax
m1:
Retаааааа
Endp
end begin

Передача в списке параметров многомерных массивов


При рассмотрении механизма определения адреса для двухмерного массива было замечено, что для определения адреса текущего элемента массива необходимо знать размер одного элемента

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

Пример 1 . Составить главную программу и функцию для умножения заданного столбца матрицы на заданное значение.

ideal

p686

model   flat

extrn ExitProcess:proc

dataseg

matr     dd        1, 1, 1, 1

            dd        2, 2, 2, 2

            dd        3, 3, 3, 3

value    dd        4

col       dd        1

n          dd        4; количество столбцов

m         dd        3; количество строк           

codeseg

begin:

push     offset matr [n] [m] [value] [col];       ebp      0

call      mc_mul;                                                  eip   4

call ExitProcess;                               col          8

proc     mc_mul;                                 value     12

push     ebp;                                    m  16

mov     ebp, esp;                               n 20

;                                               matr    24

;for(i=0; i<m; i++)

;   matr[i][col]*=value;

push    eax ebx ecx esi

mov     ecx, [ebp+16]

mov     esi, [ebp+24]

mov     ebx, [ebp+8]; col

lea       esi, [esi+ebx*4]; & начала

mov     ebx, [ebp+20]

shl        ebx, 2; Размер строки

mov     edi, [ebp+12]

for1:

mov     eax, [esi]

mul      edi

mov     [esi], eax

add      esi, ebx

loop     for1

pop      esi ecx ebx eax ebp

Ret       5*4

Endp

end begin

Пример 2.

Для заданного массива строк определить длины строк

ideal

p686

model   flat

extrn ExitProcess:proc

dataseg

s1         db        'a', 0

s2         db        'bb', 0

s3         db        'ccc', 0

s4         db        'dddd', 0

array   dd        s1, s2, s3, s4

n          dd        (n-array)/4


lenаааааа ddааааааа 4 dup (?)
codeseg
begin:
push ааа offset array [n] offset lenаааааааа
callааааа TextLen
call ExitProcess;
proc ааа TextLen;аа
pushаааа ebp
movаааа ebp, esp;ааааааааааааааааааааааааааааааааа TT+¦
;for (i=0; i<n; i++ ){ааааааааааааааааааааааааа ebpааааа 0
;аа for (j=0; array[i][j]; j++);аааа ааааааа eipаааааа 4
;аа len[i]=j;аааааааааааааааааааааааааа аааааааааааааа lenаааааа 8
;}ааааааааааааааааааааааааааааааааааааа аааааааааааааааааа nааааааааа 12
;аааааааааааааааааааааааааааааааааааааа ааааааааааааааааааа arrayаа 16
pushad
movаааа ecx, [ebp+12]
movаааа eax, [ebp+16]
movаааа edi, [ebp+8]
fori:
subаааааа edx, edx
leaаааааа ebx, [eax+ecx*4-4]
movаааа esi, [ebx]
forj:
movаааа bl, [esi+edx]
testаааааа bl, bl
jeаааааааа short breakj
incаааааа edx
jmp аааа forj
breakj:
movаааа [edi+ecx*4-4], edx
loopаааа fori
popad
popааааа ebp
Retаааааа 3*4ааааа
Endp
end begin
+сЁрЄшЄх тэшьрэшх эр ЁрсюЄє ё рфЁхёюь ьрёёштр рфЁхёют!
¦ЁшьхЁ 3. LяюЁ фюўшЄ№ ьрёёшт ёЄЁюъ т яюЁ фъх єсvтрэш  фышэ ёЄЁюъ.
ideal
p686
modelаа flat
extrn ExitProcess:proc
dataseg
s1аааааааа dbааааааа 'a', 0
s2аааааааа dbааааааа 'bb', 0
s3аааааааа dbааааааа 'ccc', 0
s4аааааааа dbааааааа 'dddd', 0
arrayаа ddааааааа s1, s2, s3, s4
n аааааааа ddааааааа (n-array)/4
lenаааааа ddааааааа 4 dup (?)
codeseg
begin:
push ааа offset array [n] offset lenаааааааа
callааааа TextLen
push ааа offset array [n] offset len
callааааа SortLenааааааааааа
call ExitProcess;
proc ааа TextLen;аа
pushаааа ebp;ааааааааааааааааааааааааааааааааааа
movаааа ebp, esp;ааааааааааааааааааааааааааа ааа
;for (i=0; i<n; i++ ){ааааааааааааааааааааааааа ebpааааа 0
;аа for (j=0; array[i][j]; j++);ааааааа eipааааааааааа 4
;аа len[i]=j;аааааааааааааааааааааааааа lenааааааааа 8
;}ааааааааааааааааааааааааааааааааааааа nаааа 12
;аааааааааааааааааааааааааааааааааааааа arrayаааааааааа 16
pushad
movаааа ecx, [ebp+12]
movаааа eax, [ebp+16]
movаааа edi, [ebp+8]


fori:
subаааааа edx, edx
leaаааааа ebx, [eax+ecx*4-4]
movаааа esi, [ebx]
forj:
movаааа bl, [esi+edx]
testаааааа bl, bl
jeаааааааа short breakj
incаааааа edx
jmp аааа forj
breakj:
movаааа [edi+ecx*4-4], edx
loopаааа fori
popad
popааааа ebp
Retаааааа 3*4ааааа
Endp
proc SortLen
pushаааа ebp
movаааа ebp, esp
pushad
;for (i=1; i<n; i++){аааааааааааааааааааааааааааааааааааааа ebpааааа 0
;аа r=x[i];аааааааааааааааааааааааааааааааааааа аааааааааааааааааааааааааааааа eipаааааа 4
;аа for (j=i-1; j>=0; j--)аааааааааааааааааааааааааааааааааааааааааааааа arrayаа 8ааааааааа
;аааааааааа if (x[j]<r)x[j+1]=x[j]; else break;аааа ааааааааааа nааааааааа 12аааа
;аа x[j+1]=r;аааааааааааааааааааааааааааааааааа аааааааааааааааааааааааааааа lenаааа 16
;}
movаааа eax, 1; i
movаааа ebx, [ebp+8]; array
movаааа edx, [ebp+16]; len
@@fori:
pushаааа ebp
movаааа esi, [ebx+eax*4]; r1
movаааа edi, [edx+eax*4]; r2
movаааа ecx, eax; j
@@forj:
decаааааа ecx
jsааааааааа short @@breakj
cmpаааа [edx+ecx*4], edi
jaeаааааа short @@breakj
movаааа ebp, [edx+ecx*4]
movаааа [edx+ecx*4+4], ebp
movаааа ebp, [ebx+ecx*4]
movаааа [ebx+ecx*4+4], ebp
jmp @@forj
@@breakj:
movаааа [ebx+ecx*4+4], esi
movаааа [edx+ecx*4+4], edi
popааааа ebp
incаааааа eax
cmpаааа eax, [ebp+12]
jbeаааааа @@foriааа аааааа ааа
popad
popааааа ebp
retааааааа 12
endp
end begin
+сЁрЄшЄх тэшьрэшх эр шёяюы№чютрэшх ьхЄюъ тшфр @@шь . ¦Єю Єръ эрчvтрхьvх ыюъры№эvх ьхЄъш, юэш шёяюы№чє¦Єё  фы  юуЁрэшўхэш  юсырёЄш фхщёЄтш  ьхЄюъ. +сырёЄ№ фхщёЄтш  ьхЄъш Ц ЇєэъЎш  шыш юсырёЄ№ ьхцфє фтєь  юсvўэvьш ьхЄърьш.
¦Ёш ёюЁЄшЁютъх ьхэ ¦Єё  ьхёЄрьш эх ёрьш ёЄЁюъш, р шї рфЁхёр ш фышэv. ¦Єю ьюцхЄ сvЄ№ чэрўшЄхы№эю ¤ЇЇхъЄштэхщ юсьхэр ьхёЄрьш ёЄЁюъ, хёыш юэш фышээvх.
а
Tръ ъръ яЁшэЎшяv ЁрсюЄv ё фтєїьхЁэvьш ш ьэюуюьхЁэvьш ьрёёштрьш юфшэръютv, ёюёЄртыхэшх ЇєэъЎшщ фы  ьэюуюьхЁэvї ьрёёштют тvяюыэ хЄё  яю Єхь цх яЁртшырь, ўЄю фы  фтєїьхЁэюую ьрёёштра

Передача в списке параметров структур


Пример 1. Составить главную программу и функцию для вычисления суммы двух комплексных чисел.

p686

IDEAL

MODEL flat

extrn ExitProcess:proc

DATASEG

struc   complex

real    dd      ?

im      dd      ?

ends

n1   complex    <1, 1>

n2   complex    <2, 2>

n3   complex    ?

CODESEG

begin:

push    [n1.im]

push    [n1.real]

push    [n2.im]

push    [n2.real]

push    offset n3

call    csum

call      ExitProcess

proc     csum

push     ebp

mov      ebp, esp

push      eax ebx

mov       eax, [(complex ebp+20).real]

add       eax, [(complex ebp+12).real]

mov       ebx, [ebp+8]

mov       [(complex ebx).real], eax

mov       eax, [(complex ebp+20).im]

add       eax, [(complex ebp+12).im]

mov       [(complex ebx).im], eax

pop       ebx eax ebp

ret       20

endp   csum

end     begin

 Обратите внимание на запись компонентов комплексного числа в стек. Это число записывается так, чтобы младшая часть числа находилась в младших адресах стека.

Аналогично со структурами используются объединения.



Передача в списке параметров функций


Функции передаются, если необходимо решить класс задач, которые различаются только списком используемых функций, причем эти функции имеют одинаковый список параметров. Пример – вычисление определенного интеграла для разных подинтегральных функций.

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

Пример . Составить главную программу и функции вычисления:

Min –  для вычисления минимального из двух заданных целых чисел;

Max –  для вычисления максимального из двух заданных целых чисел;

MinMax - для вычисления минимального или максимального из двух заданных целых чисел, в зависимости от заданной в списке параметров функции;

ideal

p686

model   flat

extrn ExitProcess:proc

dataseg

x          dd      5

y          dd      3

z1        dd      ?; min

z2        dd      ?; max

array            dd      Min, Max

codeseg

begin:

push  [x] [y]

mov   eax, [array]; min

push  eax

call  MinMax

mov   [z1], eax

push  [x] [y]

mov   eax, [array+4]; min

push  eax

call  MinMax

mov   [z2], eax

call    ExitProcess;                               ebp     0

proc Min;                                        eip      4

push  ebp;                                       y          8

mov   ebp, esp;                              x          12

mov   eax, [ebp+12]

cmp    eax, [ebp+8]

jl        short @@m1

mov   eax, [ebp+8]

@@m1:

pop    ebp

ret     8

endp

;                                                           ebp     0

proc Max;                                       eip      4

push  ebp;                                       y          8

mov   ebp, esp;                              x          12

mov   eax, [ebp+12]

cmp    eax, [ebp+8]

jg       short @@m1

mov   eax, [ebp+8]

@@m1:

pop    ebp

ret     8

endp

;                                                                                   ebp     0

proc MinMax;                                                        eip      4

push  ebp;                                                               &fun 8

mov   ebp, esp;

;                                                                                   y          12

push  [dword ebp+16] [dword ebp+12];   x          16

mov   eax, [ebp+8]

call  eax

pop    ebp

ret     12

endp

end begin



Классификация параметров. Способы передачи параметров процедурам


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



Составление функций с переменным списком параметров


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

p586

IDEAL

MODEL fLat

extrn ExitProcess:proc

DATASEG

x          dd      1

y          dd      2

z          dd      3

u         dd      4

res     dd      ?

CODESEG

begin:

push  [u] [z] [y] [x] 4;      ebp     0

call  max;                           eip      4

add    esp, 5*4;                    4          8 ; Постоянные

mov   [res], eax;               [x]      12

call ExitProcess;                 [y]       16; Переменные

proc     max;                                   [z]       20

push     ebp;                                    [u]      24

mov      ebp, esp;

push              ebx ecx edx

mov   eax, [ebp+12]; x

mov   ecx, [ebp+8]; n

dec    ecx

jecxz            @@m2

lea     ebx, [ebp+16]; & переменной части списка

@@for1:

mov   edx, [ebx]

cmp    eax, edx

jge     short @@m1

mov   eax, edx

@@m1:

add    ebx, 4

loop @@for1

@@m2:

pop    edx ecx ebx ebp

ret

endp

end     begin

Обратите внимание на:

1.     Формирование адреса начала переменной части списка;

2.     Порядок передачи параметров в стек – начинаем с переменных параметров, чтобы постоянные параметры были записаны с постоянным смещением в вершине стека;

3.     очистку стека, которую выполняет вызывающая программа, т.к. только она «знает», сколько передала параметров;



Составление рекурсивных функций на ассемблере


     Функция называется рекурсивной, если она обращается к  самой себе. Рассмотрим составление рекурсивной функции на  примере  вычисления факториала.

          Пример. Составить функцию вычисления Y = N!,

если

                         Y = 1, если N = 0,

                         Y = 1 * 2 ... * N, если N > 1

Если N < 0 функция должна возвращать -1, что говорит о неправильном аргументе.

          Соответствующая функция на языке С имеет вид:

          unsigned fact(int n){

                  if(n < 0) return -1;

                  if(n == 0)return 1

                  return (n * fact(n - 1));

          }

;Использование рекурсий в ассемблер программах

       

 ideal

        p586

        model flat

        extrn ExitProcess:proc

        dataseg

n       dd    4

x       dd    ?

        codeseg

       proc     fact

       push     ebp

       mov      ebp,esp; [ebp+8]-n

       push     ebx ecx edx

       mov      ebx, [ebp+8]

       test     ebx,ebx

       js       m1; <0

       jz       m2; ==0

       mov      ecx, ebx

       dec      ebx

       push     ebx

       call     fact

       mul      ecx

       jmp      short m3

m1:    mov      eax,0

       jmp      short m3

m2:    mov      eax,1

m3:    pop      edx ecx ebx

       pop      ebp

       ret      4

       endp     fact

begin:

       push     [n]

       call     fact

       call     ExitProcess

       end  begin

     "Прокрутите" программу, чтобы увидеть, как  изменяется  состояние стека. Вы увидите, что рекурсивные функции требуют,  чтобы размер стека был не меньше K * N, где  K-число  байт  стека,  используемых при каждом вызове, а N  -  число  вызовов  рекурсивной функции



Одномодульные программы


Одномодульная программа может состоять из главной программы (все рассмотренные до сих пор примеры) или из главной программы и процедур (функций).



Использование общих областей памяти


При составлении многомодульных программ программисты часто используют внешние переменные, что позволяет один раз выделить память под переменную и один раз ее инициализировать. Такие переменные определяются в сегменте данных одного из модулей и задаются в директиве PUBLIC этого модуля, а во всех модулях, где используются эти переменные, они определяются  в директиве EXTRN.

Пример. Составить функцию вычисления суммы двух чисел, передавая исходные данные через внешние переменные.

; главная программа

ideal

p686

model flat

extrn ExitProcess:proc

extrn Summa:proc

dataseg

y          dd        3

x          dd        5

z          dd        ?

public x

public y

codeseg

begin:

call       Summa

mov     [z], eax

call ExitProcess

end begin

; Функция

ideal

p686

model flat

extrn x:dword, y:dword

codeseg

proc Summa

public Summa;                       

mov     eax, [x]

add      eax, [y]

ret       

endp

end



Особенности использования внешних функций


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

    Чтобы объединить несколько  модулей  (эту  функцию  выполняет компоновщик) в одну программу, необходима специальная  информация для вызывающей и вызываемой программ. Рассмотрим эту информацию.

            В Ы З Ы В А Ю Щ А Я   П Р О Г Р А М М А .

    1.  Необходимо знать, что вызываемый модуль является внешним. Для задания этого этого используется директива:

             extrn  имя функции :  proc

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

    2.  Если вызывающая программа резервирует память под  данные, которые должны использоваться в функции,  адреса  этих данных должны быть определены как адреса типа public,  т.е.  доступные(известные)  другим сегментам.

    Общий вид директивы public:

                    public  имя1, имя2, ....

    Директива public, если она необходима, задается в том сегменте, где определено  это  имя.  Директиву  можно  использовать  не только для переменных, но и для меток.

            В Ы З Ы В А Е М А Я    П Р О Г Р А М М А

     1. Имя функции должно  быть  задано  в  директиве public

                 public  имя_ функции

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

              extrn  определение1, определение2,...

     Общий вид определения для передаваемых данных:

                  имя : тип : количество ,

где

     имя - имя данного, память под которое выделена в другом  модуле;


аааааааааааааааа ; ¦хрышчрЎш  рыуюЁшЄьр
аааааааааааааааа ; TюёёЄрэютыхэшх шёяюы№чєхьvї ЁхушёЄЁют
аааааааааааааааааааааааа .........
ааааааааааааааааааааааааааа ret
ааааааааааааааааа шь а endp
аааааааааааааааааааааа end
аааа T ЇєэъЎшш, т юЄышўшх юЄ уыртэющ яЁюуЁрььv, т фшЁхъЄштх end рфЁхё Єюўъш тїюфр эх чрфрхЄё .
аааа +с•шщ тшф тvчvтр¦•хщ яЁюуЁрььv :
аааааааааааааааа .MODELа шь 
ааааааааааааааааааааааа ......
аааааааааааааааа extrnа шь _яЁюЎ : proc
ааааааааааааааааааааааа ......
аааааааааааааааа .CODE
аааааааа begin:
аааааааааааааааааааааааа .......
ааааааааааааааааа endа begin
ааа -шЁхъЄштv public ш extrn т ¤Єшї яЁюуЁрььрї ьюцэю чрьхэшЄ№ фшЁхъЄштющ global шь _яЁюЎ:proc. =ю ьv тёх-Єръш Ёхъюьхэфєхь трь шёяюы№чютрЄ№ фшЁхъЄштv public ш extrn,а Є.ъ.а юэша яючтюы ¦Єа сюыхх уыєсюъю шчєўшЄ№ ьхїрэшчь ёт чш ьюфєыхщ. Lьхээю ¤Єш фшЁхъЄштv эршсюыхх ўрёЄю шёяюы№чє¦Єё  т юяєсышъютрээvї яЁюуЁрььрї. ¦юёых Єюую, ъръ тv єтхЁхээю яюўєтёЄтєхЄх ёхс  т шёяюы№чютрэшш ¤Єшїа фшЁхъЄшт, ьюцэю шї чрьхэшЄ№ фшЁхъЄштющ global.
аааа -ы  ёючфрэш  шёяюыэ хьющ яЁюуЁрььvа шча эхёъюы№ъшїа ьюфєыхщ, тvяюыэ хЄё  Ёрчфхы№эр  ЄЁрэёы Ўш  фы  ърцфюую ьюфєы :
ааааааааааааааааа Tasm32 ./ml /ziа шь _ьюфєы 1
ааааааааааааааааа Tasm32 /ml /ziа шь _ьюфєы 2
ааааааааааааааааа ...
ш ъюьяюэютър тёхї ьюфєыхщ тьхёЄх:
ааааааааааааааааа tlink32 /v шь _ьюфєы 1 шь _ьюфєы 2 ....import32.lib
аааа -ы  ЇюЁьшЁютрэш  юЄырфюўэющ шэЇюЁьрЎшша шёяюы№чє¦Єё а Єха цх ъы¦ўш, ўЄю ш фы  юфэюьюфєы№эvї яЁюуЁрьь.
аааа ¦ЁшьхЁv ёюёЄртыхэш  ш шёяюы№чютрэш  тэх°эшїа ЇєэъЎшщ.
аааа ¦ЁшьхЁ 1. TюёЄртшЄ№ тэх°э¦¦ ЇєэъЎш¦ фы  тvўшёыхэш а z=x+y.
¦рЁрьхЄЁv яхЁхфртрЄ№ ўхЁхч ёЄхъ.
;Tэх°э   ЇєэъЎш  фы  тvўшёыхэш  z=x+y. ¦рЁрьхЄЁv
;ааааааааааааааа яхЁхфр¦Єё  ўхЁхч ёЄхъ.
;+ыртэр  яЁюуЁрььр
; Їрщы main.asm
ideal
p686
model flat
extrn ExitProcess:proc
extrn Summa:proc
dataseg
xааааааааа ddааааааа 5
yааааааааа ddааааааа 3
zаааааааааа ddааааааа ?
codeseg
begin:
pushаааа [x] [y]
callааааа Summa
movаааа [z], eax
call ExitProcess
end begin
; Lрщы PROC.asm
ideal
p586
modelаа flat
codeseg
procа _Summa
public _Summa
arg x:dword, y:dword
pushаааа ebp
movаааа ebp, esp
MOVаа EAX, [x]
addааааа eax, [y]
popааааа ebp
retааааааа
endp
end
аааа -ы  ЄЁрэёы Ўшш ¤Єшї Їрщыют шёяюы№чє¦Єё  ъюьрэфv
аааааааааааааааааааааааа Tasm32 /zi /mlа main
аааааааааааааааааааааааа Tasm32 /zi /mlа proc
аааааааааааааааааааааааа Tlink32 /vа main proc import32.lib,
т Ёхчєы№ЄрЄх тvяюыэхэш  ъюЄюЁvї яюыєўшь Їрщы main.exe.

Реентерабельное программирование


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

Когда это может произойти?

По крайней мере,  это возможно  в  трех  случаях.  Во-первых, мультизадачные  системы  могут  иметь множество выполняемых программ, разделяющих общие библиотеки программ, (dll). Вместо наличия нескольких копий этих библиотек имеется только одна копия. Для правильной работы библиотеки должны  иметь отдельные области данных,  чтобы избежать неумышленное совместное использование и порчу данных. Второй случай, когда одна и та же функция  может быть вызвана программами одновременно,  происходит  в  системах  управления прерываниями.  Допустим,  что  выполняется  некоторая программа и произошло прерывание из-за некоторого внешнего события.  Программа, обслуживающая прерывание, начинает выполнение и ей необходимо вызвать программу,  которая была прервана.  Если она не имеет отдельной области данных, то программа обслуживания прерывания разрушит данные, относящиеся к прерванной программе. По этой причине программам обслуживания прерываний необходимо иметь отдельные области данных.

Третий случай использования отдельных областей данных  происходит  тогда,  когда программе необходимо вызвать саму себя. (рекурсия). 

Для написания повторно входимых (реентерабельных) программ  необходимо, чтобы все промежуточные данные записывались в локальные области памяти. Этому требованию должны удовлетворять все библиотеки, которые могут использоваться в многопоточном режиме.



Использование локальных областей памяти


Для выделения локальной области используется стек.

Структура стека с локальной областью:

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

; Локальная область памяти

EBP

EIP

; Фактические параметры

Для выделения локальной области используется команда:

SUB ESP, Размер локальной области

Для освобождения локальной области используется 2 способа:

ADD ESP, Размер локальной области

MOV   ESP, EBP

Последний способ более защищен от ошибок, связанных с асинхронным использованием стека

Структура функции с локальной областью:

Proc    Имя

Push    EBP

Mov     EBP, ESP

SUB     ESP, Размер локальной области

PUSH  Регистры

POP    Регистры

MOV   ESP, EBP

POP    EBP

RET     [Константа]

Пример. Составить главную программу и функцию для преобразования числа в символьное представление.

Алгоритм.

void itoa (unsigned x){

  int numbers[] = {10000, 1000, 100, 10, 1};

  for (int I=0; I<5; I++){

      Y[I]= x/numbers[i] + ‘0’;

      X%= numbers[i];

}

}

ideal

p586

model   flat

codeseg

proc itoa;                                 ebp                  0

public itoa;                              eip                   4

;;                                              y                      8

;;                                              x                      12

push  ebp

mov   ebp, esp

sub   esp, 5*4

push  eax ebx ecx edx esi

mov   [numbers], 10000

mov   [numbers+4], 1000

mov   [numbers+8], 100

mov   [numbers+12], 10

mov   [numbers+16], 1

mov     ecx, 5

mov     eax, [ebp+12]

mov     ebx, [ebp+8]

lea     esi, [numbers]

for1:

xor     edx, edx

div     [dword ptr esi]

add     al, '0'

mov     [ebx], al

inc     ebx

add     esi, 4

mov     eax, edx

loop    for1

mov     [byte ptr ebx], 0

pop     esi edx ecx ebx eax

mov     esp, ebp

pop     ebp

ret     8

endp    itoa

end

Составьте самостоятельно главную программу для этого примера и проверьте ее на машине!



Особенности использования команд ENTER и LEAVE


Анализ функций, составленных ранее, показывает, что в функции требуется:

·        Сохранить и сформироваить значение регистра EBP;

·        Выделить локальную память.

Для обеспечения этих операций можно использовать команду ENTER (вход), а для автоматического освобождения локальной области и восстановления регистра EBP – команду LEAVE (выход).

Общий вид команды ENTER:

ENTER оп1, оп2

Оп1- определяет размер локальной области в байтах (константное выражение);

Оп2 – уровень вложенности функций (Константное выражение 0 .. 31). Для каждого уровня вложенности копируется фрейм стека при создании нового фрейма. Это делается для обеспечения возможности доступа к параметрам и локальной области внешних функций.

Команда ENTER , если она используется, является первой командой функции, а команда LEAVE – непосредственно перед командой RET.

Если уровень вложенности равен 0, то вставляются команды:

PUSH  EBP

MOV   EBP, ESP

SUB     ESP, оп1.

Для уровня вложенности один или больше, процессор сохраняет указатели на фреймы стеков для предыдущих уровней (содержимое регистра EBP для всех предыдущих уровней).

Команда LEAVE выполняет действия, обратные действиям ENTER, в том числе восстановление регистра EBP.



Директива ARG


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

ARG параметр1, параметр2, … [= переменная]

Параметры задаются в виде:

Имя:Тип[:Количество]

В качестве имени используется любое уникальное имя.

Тип может быть стандартным и нестандартным. В качестве стандартных используются типы: BYTE, WORD, DWORD, PWORD, FWORD, QWORD, TBYTE. В качестве нестандартных можно задавать типы структур и объединений.

Количество задается константным выражением и определяет количество элементов данного типа. Используется, если передается массив, а не его адрес. По умолчанию Количество

= 1. Переменной, заданной в директиве присваивается размер области параметров в стеке. Это значение вычисляется компилятором и используется в команде RET.

Пример. Составить функцию для вычисления значения Y=A*X+B для данных длиной 32 бита. Результат длиной 4 байта

; Главная программа

ideal

p686

model flat

extrn ExitProcess:proc

extrn Fun:proc

dataseg

a          dd        3

x          dd        5

b          dd        7

y          dd        ?

codeseg

begin:

push     [a] [x] [b]

call      Fun

mov     [y], eax

call ExitProcess

end begin

; Функция

ideal

p686

model flat

codeseg

proc     Fun

public  Fun

arg       b:dword, x:dword, a:dword = z

push     ebp

mov     ebp, esp

mov     eax, [a]

mul      [x]

add      eax, [b]

pop      ebp

ret        z

endp

end



Директива LOCAL


Директива используется для определения структуры локальной области и ее размера. Сама локальная область не создается. Для ее создания необходима команда SUB ESP, Размер или команда ENTER.

Общий вид директивы:

LOCAL Параметр1, Параметр2,… = Переменная

Локальные параметры задаются точно также, как формальные параметры, т.е. задается имя, тип и, возможно, количество. Размер локальной области присваивается на этапе компиляции переменной, заданной в конце директивы . Переменная используется в командах выделения локальной области.

Пример. Составить главную программу и функцию на ассемблере

int Fun (int x){

   int a, b[2];

   a=x; b[0]=b[1] = 2 *x;

   return a+ b[0] + b[1];

}

; Главная программа

;Int Fun (int x){

;   Int a, b[2];

;   a=x; b[0]=b[1] = 2 *x;

;   return a+ b[0] + b[1];

;}

ideal

p686

model flat

extrn ExitProcess:proc

extrn Fun:proc

dataseg

x          dd        5

y          dd        ?

codeseg

begin:

push     [x]

call      Fun

mov     [y], eax

call ExitProcess

end begin

; Функция

;Int Fun (int x){

;   Int a, b[2];

;   a=x; b[0]=b[1] = 2 *x;

;   return a+ b[0] + b[1];

;}

ideal

p686

model flat

codeseg

begin:

proc Fun

public Fun

arg x:dword = z1

local a:dword, b:dword:2=z2

push     ebp

mov     ebp, esp

sub       esp, z2

mov     eax, [x]

mov     [a], eax

mov     [b], eax

add      [b], eax

mov     [b+4], eax

add      [b+4], eax

mov     esp, ebp

pop      ebp

ret        z1

endp

end




Вставка в Си команд на ассемблере


     Рекомендации и правила записи вставок.

     1. Команда должна начинаться с ключевого слова asm. При  использовании С++

несколько подряд идущих команд могут быть объединены в блок, например

      asm {

      .......

      }

     Некоторые версии трансляторов с языка СИ требуют  слово  asm перед каждой командой или вместо этого слова используют _asm  или др.

     2. Вставлять можно все допустимые машинные команды и  директивы, не определяющие работу транслятора. Так не допускаются  директивы

        segment и ends ;

        proc    и endp;

        assume  и end

        и др.

3.     В ассемблерной части программы допускается  использование данных Си программы всех классов (локальные,  внешние,  статические и регистровые). Данные могут быть объявлены в ассемблерной части программы, как внешние переменные для использования в ассемблерной части программы. Эти переменные операторы языка «С» не видят, например:

#include <iostream.h>

int y;

asm {

x       dd      ?

}

int main(int argc, char* argv[])

{

        asm{

          mov   [x], 5

          mov   eax,[x]

          mov   [y],eax

        }

        cout << y;

        char cc;

        cin >> cc;

        return 0;

}

Здесь показано как использовать данные, определенные в участках программы на С и на ассемблере.

     4. Для некоторых версий языка С[15], если в ассемблерной части есть команда перехода, то  метка записывается по правилам Си, например:

           asm  jmp  label

           ............

          label:asm xor ax, ax

     5. Комментарии записывают по правилам  Си.  Символ  ';'  используется  в качестве конца команды, а не начала  комментария.  В строке может быть записано несколько команд, например :

       asm {push ax; push bx; push cx}

После последней или единственной команды символ ';'  можно  не ставить. Команды должны быть написаны по правилам MASM, не IDEAL.

     6. В ассемблерной части программы нельзя  изменить  содержимое сегментных регистров, регистров для работы со стеком (esp,  ebp),  а  также  регистров esi,  edi,  если  используются регистровые переменные[16].




     7. Если встречается хотя бы одна ассемблерная вставка, необходим дополнительный шаг трансляции с языка Си на ассемблер.  Для задания  транслятору  режима  перевода  в  ассемблерный  код  используется директива
             #pragma inline
Если директивы нет, возможна повторная трансляция, если  встречена команда на ассемблере.
     Ниже приведен пример программы с ассемблерными вставками
          #pragma inline
          #include <stdio.h>
          int summa (int x, int y){
            int z;
          asm {
              mov  eax, [x]
              add  eax, [y]
              mov  [z], eax
          }
          return z;
          }
          main(){
           printf(" сумма = % d\n", summa(5, 3));
          }
     Достоинство метода: наиболее прост с точки  зрения  программиста.
     Недостаток метода. Нельзя  отдельно  оттранслировать  ассемблерную часть.

Использование отдельных функций на ассемблере


Функция должна удовлетворять соглашениям по вызову для языка С++.

Соглашения по вызову различаются по следующим параметрам:

·        Правила формирования внутреннего имени функции;

·        Правилами передачи параметров;

·        Правилами очистки стека.

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

Для обеспечения возможности использования переменного списка параметров параметры могут записываться в стек, начиная с конца списка, для обеспечения максимальной скорости работы – для передачи параметров используются регистры

Очистку стека может выполнять вызывающая программа или функция.

В табл. 11.4 представлены соглашения по вызову и свойства этих соглашений.

Если файл с программой имеет расширение CPP, то по умолчанию используется режим 1, если файл с расширением С++ - режим 2. Если в программе с расширением СPP необходимо использовать функцию с соглашением С, заголовок этой функции имеет вид:

Extern “C” заголовок;

Если необходимо задать  заголовочный файл, который можно использовать в программе на С и СРР, то применяют директивы

#ifdef __cplusplus

extern “C” {

#endif

заголовок 1;

заголовок 2;

#ifdef __cplusplus

}

#endif

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

Таблица 11.4. Соглашения по вызову, принятые в С++

п/п

Тип соглашения

Как задается

Внутреннее имя функции

Порядок передачи параметров

Очистка стека

Переменный список параметров

1.        

Cpp

-

@имя$q[17]…

¬

Вызывающая программа

Да

2.        

С

Extern “C”…

_имя

¬

Вызывающая программа

Да

3.        

Fastcall

Тип __fastcall …

@имя$qqr…

EAX, EDX, ECX

Вызывающая программа

Нет

4.        

Stdcall

Тип

__stdcall …

@имя$qqs…

¬

Функция

Нет

5.        

Pascal

Тип

__pascal …

@ИМЯ$Q…

®

Функция

Нет


// +ыртэр  яЁюуЁрььр
#pragma hdrstop
#include <condefs.h>
#include <stdio.h>
//---------------------------------------------------------------------------
USEASM("std.asm");
USEASM("std1.asm");
USEASM("std2.asm");
USEASM("std3.asm");
USEASM("std4.asm");
//---------------------------------------------------------------------------
#pragma argsused
int __stdcallа substract ( int x, int y, int *z);
intаа substract1 (int x, int y, int *z);
intаа __fastcall substract2 (int x, int y, int *z);
extern "C" intаа substract3 (int x, int y, int *z);
intаа pascal substract4 (int x, int y, int *z);
intаа __fastcall sum (int *x, int y);
int main(int argc, char **argv)
{
ааааа int z;
ааааа substract (3, 2, &z); printf ("%d\n", z);
ааааа substract1 (3, 2, &z); printf ("%d\n", z);
ааааа substract2 (3, 2, &z); printf ("%d\n", z);
ааааа substract3 (3, 2, &z); printf ("%d\n", z);
ааааа substract4 (3, 2, &z); printf ("%d\n", z);
ааааа getchar ();
ааааааа return 0;
}
; stdcall
ideal
p586
model flat
codeseg
procааа @substract1$qiipi
public @substract1$qiipi
argаа a:dword, b:dword, c:dword=s
pushааа ebp
movаааа ebp, esp
pushааа ebx
movаааа eax, [a]
subаааа eax, [b]
movаааа ebx, [c]
movаааа [ebx], eax
popаааа ebx ebp
retаааа
endp
end
;fastcall
ideal
p586
model flat
codeseg
procааа @substract2$qqriipi
publicа @substract2$qqriipi
pushааа esi
movаааа esi, eax
subаааа esi, edx
movаааа [ecx], esi
popаааа esi
retаааа
endp
end
а
;extern C
ideal
p586
model flat
codeseg
procааа _substract3
publicа _substract3
argаа a:dword, b:dword, c:dword=s
pushааа ebp
movаааа ebp, esp
pushааа esi
movаааа esi, [a]
subаааа esi, [b]
movаааа ecx, [c]
movаааа [ecx], esi
popаааа esi ebp
ret
endp
end
; pascal
ideal
p586
model flat
codeseg
proc @SUBSTRACT4$QIIPI
public @SUBSTRACT4$QIIPI
argаа c:dword, b:dword, a:dword=s
pushааа ebp
movаааа ebp, esp
pushааа esi
movаааа esi, [a]
subаааа esi, [b]
movаааа ecx, [c]
movаааа [ecx], esi
popаааа esi ebp
retаааа s
endp
end

Стыковка с языками высокого уровня


     Существует два способа стыковки:

     1. Вставка в Си программу команд на ассемблере;

     2. Программа на Си вызывает функцию на ассемблере;

         



Особенности создания функций для включения их в DLL


Для включения функции в DLL в качестве экспортируемых функций директива PUBLIC должна быть заменена директивой PUBLICDLL.

Пример. Создать экспортируемую функцию для вычитания целых чисел, которая должна входить в DLL как экспортируемая функция. Пусть функция удовлетворяет соглашению по вызову extern “C”:

Файл с функцией proc.asm

;extern C

ideal

p586

model flat

codeseg

proc    _substract3

publicdll  _substract3

arg     a:dword, b:dword, c:dword=s

push    ebp

mov     ebp, esp

push    esi

mov     esi, [a]

sub     esi, [b]

mov     ecx, [c]

mov     [ecx], esi

pop     esi ebp

ret

endp

end

Главная программа, использующая функцию substract3:

#pragma hdrstop

#pragma argsused

#include <iostream.h>

extern "C"  __declspec (dllimport) int substract3 (int, int, int *);

int main(int argc, char* argv[])

{

        int z;

        substract3 (5, 3, &z);

        cout << z;

        char c;

        cin>>c;

        return 0;

}

//---------------------------------------------------------------------------

К проекту долженбыть поключен файл с расширением LIB, соответствующий сформированной DLL.

Аналогично можно использовать DLL библиотеку в режиме загрузки с помощью функции Load Library.