Префиксы повторения repe или repz

(REPeat while Equal or Zero)

Они заставляют цепочечную команду выполняться до тех пор, пока содержимое ecx/cx не равно нулю или флаг ZF равен 1. Наиболее эффективно эти префиксы можно использовать с командами cmps и scas для поиска отличающихся элементов цепочек.

Действия repe и repz:
1. анализ содержимого cx и флага zf:
если cx<>0 или zf<>0, то выполнить цепочечную команду, следующую за данным префиксом, и перейти к шагу 2;
если cx=0 или zf=0, то передать управление команде, следующей за данной цепочечной командой (выйти из цикла по rep);
2. уменьшить значение cx=cx-1 и вернуться к шагу 1;

Префиксы повторения repne или repnz

(REPeat while Not Equal or Zero)

Префиксы repne/repnz заставляют цепочечную команду циклически выполняться до тех пор, пока содержимое ecx/cx не равно нулю или флаг zf равен нулю.

Данные префиксы также можно использовать с командами cmps и scas, но для поиска совпадающих элементов цепочек.

Действия repne и repnz:
1. анализ содержимого cx и флага zf:
если cx<>0 или zf=0, то выполнить цепочечную команду, следующую за данным префиксом и перейти к шагу 2;
если cx=0 или zf<>0, то передать управление команде, следующей за данной цепочечной командой (выйти из цикла по rep);
2. уменьшить значение cx=cx–1 и вернуться к шагу 1.

Команды пересылки

Команда MOVS копирует байт или слово из одной части памяти в другую. Она имеет формат MOVS строка_приемник, строка_источник
Строка­_источник – строка в сегменте данных, а строка_приемник – строка в дополнительном сегменте.

После пересылки элемента команда MOVS изменяет указатели строки-источника SI и строки-приемника DI. Если флаг DF равен 0, то микропроцессор увеличивает значения регистров SI и DI после пересылки и тем самым адресуется к следующим элементам памяти. Если флаг DF равен 1, то микропроцессор уменьшает значения регистров SI и DI после пересылки и тем самым адресуется к предыдущему элементу памяти.

MOVSBэлементы длиной в байт
MOVSWэлементы длиной в словоне требуют операндов

MOVSDэлементы длиной в двойное слово

Полные физические адреса для операндов цепочечных команд следующие:
адрес_источника — пара ds:esi/si;
адрес_приемника — пара es:edi/di.

Команды сравнения строк

CMPS строка_приемник, строка_источник

Команда сравнения строк CMPS сопоставляет операнд-источник с операндом-приемником и возвращает результат через флаги. Команда не изменяет значения операндов. Алгоритм:

1. выполнить вычитание элементов (источник - приемник), адреса элементов предварительно должны быть загружены:

адрес источника — в пару регистров ds:esi/si;
адрес назначения — в пару регистров es:edi/di;
2. в зависимости от состояния флага df изменить значение регистров esi/si и edi/di: если df=0, то увеличить содержимое этих регистров на длину элемента последовательности; если df=1, то уменьшить содержимое этих регистров на длину элемента последовательности; в зависимости от результата вычитания установить флаги: если очередные элементы цепочек не равны, то cf=1, zf=0; если очередные элементы цепочек или цепочки в целом равны, то cf=0, zf=1;
4. при наличии префикса выполнить определяемые им действия ( repe/repne).

Команда CMPS сравнивает операнды с помощью их вычитания, а именно вычитается операнд-приемник из операнда-источника. Для сравнения нескольких элементов команду CMPS надо использовать с префиксами повторения REPE (REPZ) – пока CX не станет равен нулю или не будет найдена пара несовпадающих элементов; или REPNE (REPNZ) – пока СХ не станет равным нулю, либо не будет найдена пара совпадающих элементов.

CMPSВ
CMPSW не требуют операндов
CMPSD

Команды сканирования

Команды сканирования строк позволяют осуществить поиск заданного значения в строке. При сканировании строки байтов искомое значение должно находиться в регистре AL, а при сканировании строки слов – в регистре АХ, а при сканировании строки двойных слов – в регистре ЕАХ.

SCAS строка_приемник

SCASB
SCASW адрес сканируемой строки ESI/SI

SCASD

Алгоритм работы:
1. выполнить вычитание (элемент цепочки-(eax/ax/al)). Элемент цепочки локализуется парой es:edi/di. Замена сегмента es не допускается;
2. по результату вычитания установить флаги;
3. изменить значение регистра edi/di на величину, равную длине элемента цепочки. Знак этой величины зависит от состояния флага df:
df=0 — величина положительная, то есть просмотр от начала цепочки к ее концу;
df=1 — величина отрицательная, то есть просмотр от конца цепочки к ее началу

Команды загрузки

Данные команды служат для того, чтобы загрузить элемент строки в регистр, имеют формат:

LODS строка_источник
lodsb
lodsw
lodsd

Алгоритм работы:
1. загрузить элемент из ячейки памяти, адресуемой парой ds:esi/si, в регистр al/ax/eax.
2. Размер элемента определяется неявно (для команды lods) или явно в соответствии с применяемой командой ( lodsb, lodsw, lodsd);
3. изменить значение регистра si на величину, равную длине элемента цепочки.
4.Знак величины зависит от состояния флага df:
df=0 — значение положительное, то есть просмотр от начала цепочки к ее концу;
df=1 — значение отрицательное, то есть просмотр от конца цепочки к ее началу.
Команды не влияет на флаги

Команда записи

Данные команды служат для того, чтобы загрузить элемент строки в регистр или изменить его, имеют формат:

STOS строка_приемник
STOSB

STOSW
STOSD

Алгоритм работы:
1. записать элемент из регистра al/ax/eax в ячейку памяти, адресуемую парой es:di/edi.
2. Размер элемента определяется неявно (для команды stos) или конкретной применяемой командой (для команд stosb, stosw, stosd);
3. изменить значение регистра di на величину, равную длине элемента цепочки.
3. Знак изменения зависит от флага df:
df=0 — увеличить, что означает просмотр от начала цепочки к ее концу;
df=1 — уменьшить, что означает просмотр от конца цепочки к ее началу.
выполнение команды не влияет на флаги

получение элементов цепочки из порта ввода-вывода:
ins адрес_приемника,номер_порта
insb
insw
insd

вывод элементов цепочки в порт ввода-вывода:
outs номер_порта,адрес_источника
outsb
outsw
outsd

Регистры сопроцессора

Арифметический сопроцессор содержит 8 численных 80-битовых регистров. Регистры общего назначения (стек): ST(0), ST(1), … , ST(7).

Управляющие регистры: CWR, SWR, TWR, FIP, FDR

Регистр тегов (Tags Word Register). Поля регистра тегов классифицируют содержимое соответствующего численного регистра.

Регистр управления (Control Word Register)

Регистр состояния (Status Word Register) содержит флажки особых случаев.

Команды пересылки данных

Запись в стек

Команды FLD, FILD, FBLD загружают в вершину стека ST(0) вещественное, целое и десятичное числа соответственно

Извлечение из стека

FSTP память <-ST(0), вещественный формат

FISTP память <-ST(0), целочисленный формат

FBSTP память <-ST(0), десятичный формат

После записи содержимое поля ST увеличивается на 1

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

Загрузка констант

fldz 0

fld 1

fld pi

fldl2t log2(10)

fldl2e log2(e)

fldlg2 lg2

fldln2 ln2

Команда обмена fxch меняет содержимое регистра, указанного в этой команде с регистром ST(0)

Арифметические команды

Сопроцессор использует 6 основных типов арифметических команд, имеющих вид FXXX (FADD, FSUB, FSUBR, FMUL, FDIV, FDIVR)

Команды вида FXXX ?… : первый операнд берется из верхушки стека (источник), второй – следующий элемент стека. Результат выполнения команды записывается в стек.

Команды вида FXXX память: источник берется из памяти, приемником является верхушка стека ST(0)

Указатель стека не меняется, команда действительна только для операндов с одинарной и двойной точностью.

FIXXX память – аналогично предыдущему операнду, но операции могут быть 16- или 32-битовые целые числа

FXXX ST, ST(i): регистр ST(i) является источником, а ST(0) – приемником. Указатель стека не изменяется.

FXXX ST(i), ST: регистр ST(0) является источником, а ST(i) – приемником. Указатель стека не изменяется.

Кроме основных арифметических команд существуют еще дополнительные команды: FSQRT (квадратный корень), FSCALE (масштабирование на степень числа 2), FPREM, FRNDINT (округление до целого), FXTRACT, FABS, FCHS

Команды сравнения чисел

FCOM сравнение

FICOM целочисленное сравнение

FCOMP сравнение и извлечение из стека

FICOMP целочисленное сравнение и извлечение из стека

FCOMPP сравнение и двойное извлечение из стека

FTST сравнение операнда с нулем

FXAM анализ операнда

FCOM вычитает содержимое операнда, размещенного в ST(0)

С помощью команды «FSTSW AX» программа может переписать содержимое регистра состояния сопроцессора в регистр AX центрального процессора.

Трансцендентные команды

-тригонометрические:

Fsin sin(st)->st

fsincos sin(st)->st, cos(st)->st

fcos cos(st)

fptan tg(st)->st, fld1

fpatan arctg(st(st(1))->st

-степенные и логарифмеческие

f2xm1 2^st-1->st, -1<x<1

fy2xpl st(1)*log2(st+1)->st(1), pop

fy12x st(1)*log2(st)->st(1), pop

Управляющие команды

FINIT – очищает все регистры

FSAVE – сохраняет состояние регистров в памяти

FSTST

Синхронизация СP и FPU: wait/fwait

Команда CALL

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

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

Все разновидности вызовов имеют одну и ту же мнемонику call, хотя и различающи-еся коды операций. Во многих случаях транслятор может определить вид вызова по контексту, если это невозможно, следует использовать атрибутные операторы:
near ptr - прямой ближний вызов;
far ptr - прямой дальний вызов;
word ptr - косвенный ближний вызов dword ptr - косвенный дальний вызов.

near ptr - прямой ближний вызов;

Команда call прямого ближнего вызова

заносит в стек относительный адрес точки

возврата в текущем программном сегменте и

модифицирует IP так, чтобы в нем

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

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

Прямой дальний вызов far ptr
Этот вызов позволяет обратиться к подпрограмме из другого сегмента.

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

Если процедура имя имеет атрибут NEAR, то команда CALL помещает смеще-ние адреса следующей команды в стек. Если процедура имя имеет атрибут FAR, то команда CALL помещает в стек содержи-мое регистра CS, а затем смещение адреса.
После сохранения адреса возврата команда CALL загружает смещение адреса метки имя в указатель команд IP. Если процедура имеет атрибут FAR, то команда CALL загружает также номер блока метки имя в регистр CS.

Пусть регистр ВХ содержит смещение адреса подпрограммы относительно регистра сегмента CS.
При исполнении этой команды микропроцессор копирует содержимое регистра ВХ в указатель команд IP, затем передает управление команде, адресуемой парой регистров CS : IP.

Процедуру с атрибутом NEAR можно вызвать косвенно, например:
CALL WORD PTR [ ВХ ]
; косвенная регистровая
CALL WORD PTR [ BX ] [ SI ]
; по базе с индексированием
CALL WORD PTR VARIABLE_NAМE
CALL WORD PTR VARIABLE_NAME [ BX ] ; прямая с индексированием
CALL MEM_WORD
CALL WORD PTR ES : [ BX ] [ SI ]
; по базе с индексированием

Процедуру с атрибутом FAR можно вызвать косвенно, используя переменную размером в двойное слово, например:
CALL DWORD PTR [ BX ]
CALL MEM_DWORD
CALL DWORD PTR SS : VARIABLE_NAME [ SI ]
Первые две команды CALL извлекают адреса подпрограмм из сегмента данных, а последняя – из сегмента стека. Подпрограмма может сама вызывать другие подпрограммы. Таким образом возможно использование вложенных процедур.
Т. к. каждая команда CALL помещает в стек 2 или 4 байта адреса, то число уровней вложения ограничено только размером сегмента стека.

23.3. Команда возврата RET
(RETurn from procedure)
RET Возврат из процедуры
RETN Возврат из ближней процедуры
RETF Возврат из дальней процедуры

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

Если командой ret завершается дальняя процедура, объявленная с атрибутом far, или используется модификация команды retf, со стека снимаются два слова: смещение и сегментный адрес точки возврата.
Выполнение команды не влияет на флаги.

Применение: Команду ret необходимо применять для возврата управления вызывающей программе из подпрограммы, управление которой было передано по команде call. Микропроцессор имеет три варианта команды возврата ret - это ret, ее синоним retn, а также команда retf.
Они отличаются типами процедур, в которых используются. Команды ret и retn служат для возврата из процедур ближнего типа. Команда retf — команда возврата для процедур дальнего типа. Какая конкретно команда будет использоваться, определяется компилятором; программисту лучше использовать команду ret и доверить транслятору самому сгенерировать ее ближний или дальний вариант. Количество команд ret в процедуре должно соответствовать количеству точек выхода из подпрограммы

23.4. Способы передачи параметров.
void _cdecl: Func (int A,int B, int C, int D) {}
_cdecl - соглашение о вызовах (определение порядка размещения в стеке и извлечения из стека параметров, передаваемых при вызове функций
-прямой порядок расположения данных в стеке
- в стек заносится сначала последний параметр, затем предпоследний и т.д.

- допускает переменное число параметров, поскольку стек очищает пользователь

void _stdcall Func (int A,int B, int C, int D){}
-прямой порядок данных в стеке
- в стек заносится сначала последний параметр
- стек очищает функция поэтому число параметров фиксировано

void _fastcall Func (int A,int B, int C, int D){}
- аналогично stdcall, но два первых параметра передаются через регистры eax edx

Существует _pascal:
- обратный порядок расположения данных в стеке
- в стек заносится сначала первый параметр
- поэтому число параметров фиксировано

23.5. Правила передачи параметров
Использование регистров в 16-ти битном режиме или Windows, C или C++: -16-ти битное значение возвращается в AX,

-32-х битное значение в DX:AX,
-значение с плавающей запятой в ST(0)

Регистры AX, BX, CX, DX, ES и арифметические флаги могут быть изменены подпрограммой; другие регистры должны быть сохранены и восстановлены.
Подпрограмма может полагаться на то, что при вызове другой процедуры значение ESI, EDI, EBP, DS и SS не изменится

Использование регистров в 32-х битных Windows, C++ и других языках программирования:
-целочисленное значение возвращается в EAX,
-значение с плавающей точкой возвращается вST(0).
Регистры EAX, ECX, EDX (но не EBX) могут быть изменены процедурой;

Bсе другие регистры должны быть сохранены и восстановлены.
Сегментные регистры нельзя изменять даже временно.
CS, DS, ES и SS указывают на плоский сегмент. FS используется операционной системой.
GS не используется, но зарезервирован.

Флаги могут меняться процедурой, но со следующими ограничениями: флаг направления равен 0 по умолчанию. Флаг прерывания не может быть очищен. Стековый регистр плавающей запятой пуст при входе в процедуру и должен быть пустым при выходе, если только ST(0) не используется для возвращения значения. Процедура может полагаться на неизменность EBX, ESI, EDI, EBP и всех сегментных регистров при вызове другой процедуры.

Внешние подпрограммы

#include <stdio.h>
#include <stdlib.h>
extern "C" int _cdecl SubInt(int, int);
extern "C" int _cdecl SumAInt(int[], int,int*);

void Rnd_array (int ar[],int *n)
{// ввод размерности и элементов массива
int i,j;
printf("Enter dimension n \n");
scanf("%d",n);
for (i=0;i<*n;i++)
ar[i]=rand()%100; ; }

void Print_array ( int ar[],int n)
// вывод элементов массива
{ printf("Items array\n");
for ( int i=0;i<n;i++)
printf("%5d",ar[i]);
printf("\n");}

void main()
{int x,y;
printf ("Enter 2 number");
scanf("%d%d",&x,&y);
int r=SubInt(x, y);
// в стеке сначала y потом x

printf ("difference=%d\n",r);

int b[20],n,k;
Rnd_array(b, &n);
Print_array(b, n);
SumAInt(b,n,&k);
// в стеке заносится сначала &k,n,&b
printf ("summa=%d\n",k); }

Подпрограмма во внешнем файле .asm
.486
.model flat ;плоская модель памяти
.data ;описание данных
.code

Public _SubInt
_SubInt proc
; В стеке 4 байта адрес точки возврата
push ebp ;4 байта
mov ebp,esp
mov eax,[ebp+8] ; x
sub eax,[ebp+12]; y
pop ebp
ret
SubInt endp

Public _SumAInt
_SumAInt proc
push ebp
mov ebp,esp
mov esi,[ebp+8] ;&array
mov ecx,[ebp+12];n
xor eax,eax

for1: add eax,[esi]
add esi,4
loop for1
mov edi,[ebp+16];&k
mov [edi],eax
pop ebp
ret
_SumAInt endp
end

Компилятор MASM

В начале 1990-х, альтернативные ассемблеры, типа Borland TASM и условно-бесплатного ассемблера x86 NASM начали брать часть доли на рынке MASM.
Позже в 2000 году, MASM 6.15 был выпущен как часть Visual C++ пакета разработки, который является бесплатным.
В результате все версии Visual C++ позже чем 6.0, включали версию MASM, равную версии Visual C++.

Позже в Visual C++ 2005 появилась 64-битовая версия MASM (название линкера — ml64.exe).
Вместе с большим сообществом программистов MASM, эти события помогли остановить снижение популярности MASM до других ассемблеров.
Сегодня MASM — все ещё ассемблер номер один на платформе Win32, несмотря на конкуренцию с новыми продуктами, как NASM, FASM и HLA.

Компилятор MASM создан специально для написания программ на ассемблере для Win32. В нём есть макросы и специальные директивы для упрощения программирования.
Функции.
Основное преимущество MASM это макрос invoke, он позволяет вызывать API функции по-обычному с проверкой количества и типа параметров. Это почти тот же call, как в TASM, но этот макрос проверяет количество параметров и их типы. Вот так вызывается функция:
Invoke <функция>, <параметр1>, <параметр2>, <параметр3>

Это пример программы Hello World, которая выводит это знаменитое сообщение и завершается.
.386 .model flat, stdcall
option casemap :none
include \masm32\include\masm32.inc
include \masm32\include\kernel32.inc
include \masm32\macros\macros.asm
includelib \masm32\lib\masm32.lib
includelib \masm32\lib\kernel32.lib
.code
start: print "Hello world"
exit
end start

Наши рекомендации