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 |



