1 data SEGMENT ; начало сегмента данных

2 sum DB ? ; резервирование байта

3 data ENDS ; конец сегмента данных

4 code SEGMENT ; начало программного сегмента

5 ASSUME cs: code, ; code – имя программного сегмента

ds: data ; data – имя сегмента данных

6 port1 EQU 110h ; port1 – константа 110h

7 Go: mov ax, data

8 mov ds, ax

9 mov sum,0

10 Comp: cmp sum,100

11 jl Next

12 mov al, sum

13 out port1,al

14 hlt

15 Next: in al, port1

16 add sum, al

17 jmp Comp

18 code ENDS ; конец программного сегмента

19 END Go ; конец ассемблирования

Программа состоит из двух сегментов: сегмента данных (строки 1-3) и программного сегмента (строки 4-18). Для определения начала каждого из этих сегментов служит директива SEGMENT, перед которой указано имя сегмента. В строке 1 определяется начало сегмента данных с именем data, а в строке 4 - начало программного сегмента с именем code. Конец каждого сегмента указывается с помощью директивы ENDS (строки 3 и 18), перед которой записывается имя соответствующего сегмента. Сегмент данных состоит из одной строки (строка 2), содержащей директиву DB - определение байта памяти. По этой директиве программа ассемблера резервирует под переменную с именем sum - 1 байт памяти. Директива ASSUME (строка 5) определяет соответствие сегментов и их имен. В строке 6 записана директива EDU, которая порту ввода-вывода port1 ставит в соответствие его номер. В дальнейшем, когда в тексте программы встретится имя port1, программа-ассемблер заменит его на номер 110h.

НЕ нашли? Не то? Что вы ищете?

Рис. 6.1. Блок-схема программы

Строка 7 - это первая строка программы, которая содержит мнемокод команды, т. е. первая команда результирующей программы будет получена при ассемблировании данной строки. Одно из требований к программам, написанным на языке АSМ-86, состоит в том, что первая строка программы, содержащая мнемокод команды, должна иметь метку. В данной программе имя метки Gо. Строки 7 и 8 служат для занесения начального адреса сегмента данных в регистр сегмента данных DS. Поскольку система команд ЦП не содержит команды загрузки константы в сегментный регистр, для этой цели используются две команды. В строке 9 переменной sum присваивается нулевое начальное значение и далее, в строке 10 производится сравнение значения sum со значением 100. В зависимости от результатов сравнения в строке 11 осуществляется переход по условию «меньше» на метку Next. В строкахполученное значение sum выводится в порт ввода-вывода, а в строкахосуществляется суммирование очередного введенного числа и переход на метку Comp в соответствии со схемой программы (рис. 6.1). Строка 18 завершает программный сегмент, а строка 19 содержит директиву END, которая указывает программе-ассемблеру на окончание ассемблирования и снабжается меткой, соответствующей началу программы.

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

имя1 SEGMENT

оператор

оператор

. . .

имя1 ENDS

имя2 SEGMENT

оператор

оператор

. . .

имя2 ENDS

. . .

имя3 SEGMENT

оператор

оператор

. . .

имя3 ENDS

END метка начала

Вторая особенность состоит в том, что два типа операторов (директивы и мнемокоды команд) имеют идентичные форматы (рис. 6.2). Оператор, написанный на языке ассемблера, может быть разделен на четыре поля.

Рис. 6.2. Поля операторов языка ASM-86: а - директив; б - мнемокодов команд

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

6.2. Директивы языка ассемблера ASM-86

Для определения сегментов программы служат директивы SEGMENT, ENDS, ASSUME и ORG. Назначение первых трех директив было показано в приведенном примере программы. Директива ORG задает ассемблеру адрес ячейки памяти для первой команды транслируемой программы в сегменте CS. Например, фрагмент программы

code SEGMENT

ORG 0F3h

указывает ассемблеру, что результирующая программа должна быть расположена, начиная с адреса 0F3h относительно содержимого регистра СS. Начальное значение регистра CS в этом случае назначает сама программа-ассемблер. При необходимости можно указать конкретный физический адрес начала программы путем использования вспомогательного оператора АТ. Например, фрагмент

code SEGMENT АТ 0А000h

ORG 0F3h

задает начальный адрес первой команды программы, равный 0А00FЗh.

Для определения типов переменных и резервирования памяти служат директивы DB, DW, DD, DQ и DT, которые определяют байт, слово, двойное слово, восемь байт и десять байт, соответственно. Например, совокупность операторов

alpha DB ?

beta DW 0F200h

gamma DD ?

резервирует байт памяти для переменной alpha, 4 байта - для переменной gamma и 2 байта - для переменной beta, причем переменной beta присваивается начальное значение 0F200h. Эти директивы могут задавать также и массивы переменных, например операторы

mas DB 5h, 0Аh, 13h
zero DB 0,0,0,0,0,0
res DW ?,?,?,?

определяют массив mas, включающий три константы 5h, 0АН и 13Н; массив zero, состоящий из шести байтов с нулевыми начальными значениями, и массив res, включающий четыре слова, для которых начальные значения не заданы. В тех случаях, когда требуется задать массив, состоящий из большого числа переменных, используется вспомогательный оператор DUP. Например, оператор

zeros DB 100 DUP (0)

задает массив zeros из 100 байт с нулевыми начальными значениями, а оператор

result DW 200 DUP (?)

резервирует 200 слов памяти под массив result.

При использовании команд АМ возникает необходимость описывать данные размерностью 4, 8 и 10 байт соответственно для форматов КВФ, ДВФ и ВВФ. Для этой цели в языке ASM-86 введены директивы: DD, DQ и. DT, которые позволяют просто присваивать имена, а также резервировать соответствующее число байтов памяти без присвоения и с присвоением начальных значений. Для определения имен переменных или меток используются директивы EQU и PURGE. Например, в рассмотренной выше программе директива EQU ставила в соответствие имени порта port1 значение 110h. С помощью директивы EQU можно в любом месте программы вводить новые имена, например после включения оператора

port2 ЕQU port1

имена port2 и port1 будут восприниматься программой-ассемблером как эквивалентные. После включения оператора Count EQU СХ имя Count будет эквивалентно имени СХ, которое зарезервировано для обозначения регистра общего назначения ЦП. При использовании директивы EQU в поле операнда можно помещать различные выражения, например

sum2 EQU sum1 + 2

cycle EQU return – 5.

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

PURGE port1

использование имени port1 недопустимо.

Для организации процедур применяются директивы PROC и ENDP. Например, процедура myproc увеличивает значение cx на 10:

myproc PROC
add cx,10
ret

myproc ENDP.

Очень часто в ассемблерных программах используются директивы, позволяющие вводить макроопределения. Например, макроопределение с именем mov_string и формальными параметрами dest, src и len.

mov_string MACRO dest, src, len

mov cx, len

lea si, src

lea di, dest

rep movsb

ENDM

Это макроопределение позволит использовать в программе новую «команду» с мнемокодом mov_string и тремя операндами, которые при вызове станут фактическими параметрами. Так вызов макроопределения:

mov_string addrD,addrS,100

осуществит пересылку 100 байтов из адреса addrS по адресу addrD.

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

Как было показано, все мнемокоды команд АП начинаются с буквы F, что облегчает отличать их от мнемокодов команд ЦП. Для команд сопроцессора FNCLEX, FNDISI, ENENI, FNINIT, FNOP, FNSAVE, FNSTCW, FNSTSW не требуется наличия перед ними команды WAIT, поэтому мнемокод этих команд снабжается дополнительной буквой N, что также облегчает работу программы-ассемблера.

В качестве примера рассмотрим фрагмент программы на ассемблере, использующий мнемокоды обоих процессоров:

fcomp

fnstsw flags1

mov ah,flags1

sahf

jz ...

Этот фрагмент показывает, как реализуется условный переход по результатам сравнения чисел с плавающей запятой. Поскольку в системе команд АП отсутствуют команды условных переходов, то используется следующий прием. После выполнения команды сравнения (fcomp) результаты сравнения (старший байт регистра состояния) запоминаются в памяти по адресу flags1. Затем они пересылаются в старший байт аккумулятора ah центрального процессора и запоминаются в регистре флагов F по команде sahf. Разряды С0,СЗ регистра состояния АП соответствуют разрядам регистра F, в которых размещены значения флагов CF и ZF, поэтому можно воспользоваться любой из команд условных переходов.

6.3. Использование регистров Pentium

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

mov ax,[ebx] ; Поместить в AX значение из памяти

; по адресу DS:EBX

mov dx,[ecx] ; Поместить в DX значение из памяти

; по адресу DS:ECX

mov cx, es:[eax] ; Поместить в CX значение из памяти

; по адресу ES:EAX

        Дополнительно к этой возможности введены следующие:

    Использование константы и регистра:

mov eax,[ecx + 1]

mov bl,[edx + h]

    Сумма двух регистров:

mov ebp,[ebx + edi]

mov eax,[ecx + edx]

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

mov bl,[edx + eax + h]

    Масштаб - автоматическое умножение на 2, 4 или 8 одного из регистров, участвующих в образовании адреса:

mov ax,[ebx * 2]

mov cl,[edx + ebp * 4]

mov esi,[edi + eax * 8 + h]

        При использовании 32-разрядных регистров для адресации в режиме реальных адресов, следует учитывать, что размер сегмента фиксирован и равен 64 Кб. Если процессор сформирует адрес, больший 64 Кб, то процессор зависнет. Например:

mov eax,1234h

mov bl,[eax] ; В регистр BL будет произведена загрузка

; значения с адреса DS:EAX.

mov edx, ffffh

mov bl,[eax + edx] ; Эффективный адрес будет равен

; 1234h + ffffh = 11233h (это больше,

; чем 64 Кб. Процессор зависнет.)

        Использование 32-разрядных регистров для адресации памяти в защищённом режиме очень распространено, в основном, из-за того, что размер сегментов может достигать 4 Гб.

Перед тем как создавать программы, использующие MMX - и SSE-расширения, следует убедиться в том, что данный тип ЦП поддерживает эту технологию. Для этого можно выполнить специальную команду cpuid, предварительно поместив в регистр ЕАХ значение 1. После выполнения команды проверка 23-го бита в регистре ЕDХ показывает, поддерживается ли технология ММХ процессором, а 25-го бита – поддерживается ли технология SSE. Ниже приводится текст процедуры на ассемблере, выполняющей эту проверку.

.686

.model flat

.code

check_mmx_sse proc

mov eax, 1

xor bl, bl

cpuid

test edx, 800000h

jz test_sse

inc bl

test_sse: test edx, 2000000h

jz exit

inc bl

inc bl

exit: mov al, bl

ret

check_mmx_sse endp

end

Процедура check_mmx_sse возвращает в регистре AL значение:

Из за большого объема этот материал размещен на нескольких страницах:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27