Пример использования команд усл. перехода, сравнения и циклов
Дана матрица целых байтовых величин, размером 4*5, необходимо подсчитать количество нулей и заменить их числом 0FFh. Под стек отведем 256 байтов, программу оформим как две последовательные процедуры: внешняя (FAR)– это связь с ОС, внутренняя (NEAR) – решение поставленной задачи.
1. ; prim.asm
2. title prim.asm
3. page , 132
4. Sseg segment para stack ‘stack’
5. db 256 dup (?)
6. Sseg ends
7. Dseg segment para public ‘data’
8. Dan db 0,2,5,0,91 ; адрес первого элемента массива
9. db 4,0,0,15,47 ; имя - Dan
10. db 24,15,0,9,55
11. db 1,7,12,0,4
12. Dseg ends
15. Cseg segment para public ‘code’
16. Assume cs: cseg, ds:dseg, ss:sseg
17. start proc far
18. push DS ; для связи
19. push AX ; с ОС
20. mov BX, Dseg ; загрузка адреса сегмента данных
21. mov DS, BX ; в регистр DS
22. call main
23. ret
24. start endp
25. main proc near
26. mov BX, offset Dan
27. mov CX, 4 ; количество повторений внешнего цикла
28. nz1: push CX
29. mov DL, 0 ; счетчик нулей в строке матрицы
30. mov SI, 0
31. mov CX, 5 ; количество повторений внутреннего цикла
32. nz2: push CX
33. cmp byte ptr [BX+SI], 0
34. jne mz
35. mov byte ptr [BX+SI], 0FFh
36. inc DL
37. mz: inc SI
38. pop CX
39. kz2: loop nz2
40. add DL, ‘0’ ; вывод на экран
41. mov AH, 6 ; количества нулей
42. int 21h
43. add BX, 5 ; переход к следующей строке матрицы
44. pop CX
45. kz1: loop nz1
46. ret
47. main endp
48. Sseg ends
49. end start
Задача решена с помощью двух вложенных циклов, во внутреннем осуществляется просмотр элементов текущей строки (32-39), увеличение счетчика нулей и пересылка константы 0FFh в байт, содержащий ноль. Во внешнем цикле осуществляется переход к следующей строке очисткой регистра SI (строка 30 ) и увеличением регистра BX на количество элементов в строке (40).
Физически последняя команда программы (49) в качестве параметра указывает метку команды, с которой необходимо начинать выполнение программы.
Директива title задает заголовок каждой странице листинга, заголовок может содержать до 60 символов.
Директива page устанавливает количество строк на странице листинга – 1-й параметр (здесь он отсутствует, значит берется значение по умолчанию 57) и количество символов в каждой строке ( здесь 132, возможно от 60 до 132, по умолчанию – 80).
Pageбезпараметров осуществляет перевод печати на новую страницу и увеличение на 1 номера страницы листинга.
Эти директивы могут отсутствовать.
30) Работа с массивами в Ассемблере, примеры.
Массивы в языке Ассемблер описываются директивами определения данных, возможно с использование конструкции повторения DUP.
Например, x DW 30 dup ( ? )
Так можно описать массив x, состоящий из 30 элементов длиной в слово, но в этом описании не указано как нумеруются элементы массива, т.е. это может быть x[0..29] и x[1..30] и x[k..29+k].
Если в задаче жестко не оговорена нумерация элементов, то в Ассемблере удобнее считать элементыот нуля, тогда адрес любого элемента будет записываться наиболее просто:
адрес (x[i]) = x + (type x) * i
В общем виде, когда первый элемент имеет номер k , для одномерного массива будет:
адрес (x[i]) = x + (type x) * (i – k)
Для двумерного массива - A[0..n-1, 0..m-1]адрес (i,j) – го элемента можно вычислить так:
адрес (A[i,j]) = A + m * (type A) * i + (type A) *j
С учетом этих формул для записи адреса элемента массива можно использовать различные способы адресации.
Для описанного выше массива слов, адрес его i-го элемента равен:
x + 2*i = x + type (x) * i,
Т.е. адрес состоит из двух частей: постоянной x и переменной 2 * i, зависящей от номера элемента массива. Логично использовать адресацию прямую с индексированием: x – смещение адреса относительно начала, а 2*i – в регистре модификаторе SI или DI x[ i ]
Для двумерного массива, например:
A DD n DUP (m Dup (?) ) ; A[0..n - 1, 0..m - 1] получим
адрес (A[ i,j ]) = A + m * 4 * i + 4 *j,
Т.е. имеем в адресе постоянную часть А и две переменных m * 4 * iи 4 *j ,которые можно хранить в регистрах. Два модификатора есть в адресации по базе с индексированием, например: A[BX][DI].
Фрагмент программы, в которой в регистр AL записывается количество строк матрицы X DB 10 dup ( 20 dup (?) ), в которых начальный элемент повторяется хотя бы один раз.
----------------------------------
mov AL, 0 ; количество искомых строк
mov CX, 10 ; количество повторений внешнего цикла
mov BX, 0 ; начало строки 20*i
m1: push CX
mov AH, X[BX] ; 1-й элемент строки в AH
mov CX, 19 ; количество повторений внутреннего цикла
mov DI, 0 ; номер элемента в строке ( j )
m2: inc DI ; j = j + 1
cmp AH, X[BX][DI] ; A[i,0] = A[i,j]
loopne m2 ; первый не повторился? Переход на m2
jne L ; не было в строке равных первому? Переход на L
inc AL ; первый повторился, увеличиваем счетчик строк
L: pop CX ; восстанавливаем CX для внешнего цикла
add BX, 20 ; в BX начало следующей строки
loop m1
------------------------------
12) Команды побитовой обработки данных: логические операции, операции сдвига.
К командам побитовой обработки данных относятся логические команды, команды сдвига, установки, сброса и инверсии битов.
Логические команды: and, or, xor, not. Для всех логических команд, кроме not, операнды одновременно не могут находиться в памяти, OF = CF = 0, AF – не определен, SF, ZF, PF определяются результатом команды.
and OP1, OP2; (OP1) логически умножается на (OP2), результат OP1
Пример: (AL) = 1011 0011, (DL) = 0000 1111,
and AL, DL; (AL) = 0000 0011
Второй операнд называют маской. Основным назначением команды and является установка в ноль с помощью маски некоторых разрядов первого операнда. Нулевые разряды маски обнуляют соответствующие разряды первого операнда, а единичные оставляют соответствующие разряды первого операнда без изменения. Маску можно задавать непосредственно в команде и можно извлекать из регистра или памяти.
Например:
1) and CX, 0FFh ; маской является константа
2) and AX, CX ; маска содержится в регистре
3) and AX, TOT ; маска в ОП по адресу (DS) + TOT
4) and CX, TOT[BX+SI] ; …в ОП по адресу (DS) + (BX) + (SI) + TOT
5) and TOT[BX+SI], CX ; в ноль устанавливаются некоторые разряды ОП
6) and CL, 0Fh ; в ноль устанавливаются старшие 4 разряда регистра CL
Команда – or OP1, OP2; результатом является логическое сложение 1-ого со 2-ым и результат по адресу первого.0 – если оба нули.
Эта команда используется для установки в 1 заданных битов 1-го операнда с помощью маски OP2. … Например:
(AL) = 1011 0011, (DL) = 0000 1111
or AL, DL; (AL) = 1011 1111 ……
В команде могут использоваться различные операнды:
or CX, 00FFh ; or TAM, AL ; or TAM[BX][DX], CX
Если во всех битах результата будет 0, то ZF = 1.
Команда xor OP1, OP2; 1 xor 1 = 0, 0 xor 0 = 0, в ост. сл. = 1
Например: (AL) = 1011 0011, маска = 000 01111
xor AL, 0Fh; (AL) = 1011 1100
Команда not OP; результат – инверсия значения операнда
Если (AL) = 0000 0000, not AL; (AL) = 1111 1111
Значения флагов не изменяются.
Примеры.
1) xor AX, AX ; обнуляет регистр AX быстрее, чем mov и sub
2) xor AX, BX; меняет местами значения AX и BX
xor BX, AX ; быстрее, чем команда
xor AX, BX; xchg AX, BX
3) Определить количество задолжников в группе из 20 студентов. Информация о студентах содержится в массиве байтов X DB 20 DUP (?), причем в младших 4 битах каждого байта содержатся оценки, т.е. 1 – сдал экзамен, 0 – «хвост». В DL сохраним количество задолжников.
-----------------------------
mov DL, 0
mov SI, 0 ; i = 0
mov CX, 20 ; количество повторений цикла
nz: mov AL, X[SI]
and AL, 0Fh ; обнуляем старшую часть байта
xor AL, 0Fh ;
jz m ; ZF = 1, хвостов нет, передаем на повторение цикла
inc DL ; увеличиваем количество задолжников
m: inc SI ;переходим к следующему студенту
loop nz
add DL, “0”
mov AH, 6
int 21h
--------------------------
Команды сдвига.
Формат команд арифметического и логического сдвига можно представить так:
sXY OP1, OP2 ; <комментарий>
Здесь X - h или a, Y – l или r; OP1 – r (регистр) или m (память), OP2 – d (непоср) или CL. ХУ определяет тип и направление.
И для всех команд сдвига в CL используются только 5 младших разрядов, принимающих значения от 0 до 31. При сдвиге на один разряд:
Здесь знаковый бит распространяется на сдвигаемые разряды. Например, (AL) = 1101 0101
sar AL, 1 ; (AL) = 1110 1010 и CF = 1
Сдвиги больше, чем на 1,эквивалентны соответствующим сдвигам на 1, выполненным последовательно.
Сдвиги повышенной точности для i186 и выше:
shrd OP1, OP2, OP3 ;
shld OP1, OP2, OP3 ;
Содержимое первого операнда (OP1) сдвигается на (OP3) разрядов также, как и в командах shr и shl но бит, вышедший за разрядную сетку, не обнуляется, а заполняется содержимым второго операнда, которым может быть только регистр.
Циклические сдвиги:
После выполнения команды циклического сдвига CF всегда равен последнему биту, вышедшему за пределы приемника.
Циклические сдвиги с переносом содержимого Флажка CF:
Для всех команд сдвига флаги ZF, SF, PF устанавливаются в соответствии с результатом. AF – не определен. OF – не определен при сдвигах на несколько разрядов, при сдвиге на 1 разряд в зависимости от команды:
- для циклических команд, повышенной точности и sal , shl флаг OF = 1, если после сдвига старший бит изменился;
- после sar OF = 0;
- после shr OF = значению старшего бита исходного числа.
BT <приемник>, <источник>
4) Структуры в Ассемблере, их описание и использование.
Структура состоит из полей-данных различного типа и длины, занимая последовательные байты памяти. Чтобы использовать переменные типа структура, необходимо вначале описать тип структуры, а затем описать переменные такого типа. Описание типа структуры:
<имя типа> struc
<описание поля>
------------------------
<описание поля>
<имя типа> ends
<имя типа> - это идентификатор типа структуры, struc и ends - директивы, причем <имя типа > в директиве ends также обязательно. Для описания полей используются директивы определения данных и памяти: DB, DW, DD.Имя, указанное в этих директивах, является именем поля, но имена полей не локализованы внутри структуры, т.е. они должны быть уникальными в рамках всей программы, кроме того, поля не могут быть структурами – не допускаются вложенные структуры.
Например
TData struc ; TData – идентификатор типа
y DW 2000 ; y, m, d – имена полей. Значения, указанные
m DB ? ; в поле операндов директив DW и DB ,
d DB 28 ; называются значениями полей, принятыми
TData ends ; по умолчанию.
? – означает, что значения по умолчанию нет.
На основании описания типа в программу ничего не записывается и память не выделяется. Описание типа может располагаться в любом месте программы, но только до описания переменных данного типа. На основании описания переменных Ассемблером выделяется память в соответствии с описанием типа в последовательных ячейках, так что в нашем случае размещение полей можно представить так:
2б 1б 1б размеры полей в байтах
TData y m d
+0 +2 +3 смещение относительно начала
структуры.
Описание переменных типа структуры осуществляется с помощью директивы вида:
имя переменной имя типа <нач. значения>
Здесь уголки не метасимволы, а реальные символы языка, внутри которых через запятую указываются начальные значения полей.
Нач-ым значением может быть: 1) ? 2) выражение 3) строка 4) пусто.
Например:
y m d
dt1 TData <?, 6, 4> ? 6 4
dt2 TData <1999, , > 1999 ? 28
dt3 TData < , , > 2000 ? 28
Идентификатор типа TData используется как директива для описания переменных также, как используются стандартные директивы DB, DW и т.д. Если начальные значения не будут умещаться в отведенное ему при описании типа поле, то будет фиксироваться ошибка. Правила использования начальных значений и значений по умолчанию:
- приоритетными являются начальные значения полей,
- при описании переменных, т.е. если при описании переменной для поля указан ? или какое-либо значение, то значения этих полей по умолчанию игнорируются.
Правила использования начальных значений и значений по умолчанию:
1) если в поле переменной указан знак ?, то это поле не имеет начального значения, даже если это поле имеет значение по умолчанию (поле y переменной dt1);
2) Если в поле переменной указано выражение или строка, то значение этого выражения или сама строка становится начальным значением этого поля (поля m и d переменной dt1 и поле y переменной dt2);
3) Если начальное значение поля переменной «пусто» - ничего не указано при описании переменной, то в качестве начального устанавливается значение по умолчанию – значение, указанное при описании типа, если же в этом поле при описании типа стоит знак ?, то данное поле не имеет никакого начального значения (поля m переменных dt2 и dt3).
Значения по умолчанию устанавливаются для тех полей, которые являются одинаковыми для нескольких переменных одного типа, например, год поступления на факультет одинаков для группы студентов. Любая переменная может изменять свое значение в процессе выполнения программы и поэтому структура может не иметь как значений по умолчанию, так и начальных значений.
Отсутствие начального значения отмечается запятой.
Если отсутствуют начальные значения нескольких последних полей, то запятые можно не ставить. Если отсутствуют значения первого поля или полей, расположенных в середине списка полей, то запятые опускать нельзя. Например:
dt4 TData <1980, ,> можно dt4 TData <1980>
dt5 TData <, , 5> нельзя заменить на dt5 TData < 5 >.
Если отсутствуют все начальные значения, опускаются все запятые, но угловые скобки сохраняются:
dt6 TData < >
При описании переменных, каждая переменная описывается отдельной переменной, но можно описать массив структур, для этого в директиве описания переменной указывается несколько операндов и (или) конструкция повторения DUP. Например:
dst TData <, 4, 1>, 25 DUP (< >)
Описан массив из 26 элементов типа TData, и первый элемент (первая структура) будет иметь начальные значения
2000, 4, 1, а все остальные 25 в качестве начальных будут иметь значения, принятые по умолчанию: 2000, ?, 28.
Имя первой структуры dst, второй – dst+4, третьей – dst+8 и т.д.
Работать с полями структуры можно также, как с полями переменной комбинированного типа в языках высокого уровня:
<имя переменной > . < имя поля>
Например, dt1.y, dt2.m, dt3.d
Ассемблер приписывает имени типа и имени переменной размер (тип), равный количеству байтов, занимаемых структурой
type TData = type dt1 = 4
И это можно использовать при программировании, например, так:
; выполнить побайтовую пересылку dt1 в dt2
mov CX, type TData ; количество повторений в CX
mov SI, 0 ; i = 0
m: mov AL, byte ptr dt1[SI] ; побайтовая пересылка
mov byte ptr dt2[SI], AL ; dt1 в dt2
inc SI ; i = i+1
loop m ; использование byte ptr обязательно т.к. операторы разного типа
-----------------------------------
Точка, указанная при обращении к полю, это оператор Ассемблера, который вычисляет адрес по формуле:
<адресное выражение> + <смещение поля в структуре>
Тип полученного адреса совпадает с типом поля, т.е.
type (dt1.m) = type m = byte
Адресное выражение может быть любой сложности, например:
1) mov AX, (dts+8).y
2) mov SI, 8
inc (dts[SI]).m ; Aисп = (dts + [SI]). m = (dts + 8).m
3) lea BX, dt1
mov [BX].d, 10 ; Aисп = [BX] + d = dt1.d
Замечания:
1) type (dts[SI]).m = type (dts[SI].m) = 1, но
type dts[SI].m = type dts = 4
2) Если при описании типа структуры в директиве, описывающей некоторое поле, содержится несколько операндов или конструкция повторения, то при описании переменной этого типа данное поле не может иметь начального значения и не может быть определено знаком ?, это поле должно быть пустым.
Одно исключение: если поле описано как строка, то оно может иметь начальным значением строку той же длины или меньшей,
в последнем случае строка дополняется справа пробелами.
Например: student struc
f DB 10 DUP (?) ; фамилия
i DB “ ******* “ ; имя
gr DW ? ; группа
oz DB 5, 5, 5 ; оценки
student ends
Описание переменных:
st1 student <“Petrov”, > ; нельзя, т.к. поле f не строка
st2 student < , “Petr”, 112, > ; можно, f – не имеет начального ; значения
st3 student < , “Aleksandra” >
; нельзя, в i 10 символов, а допустимо не больше 7.
Примеры программ с использованием данных типа структура.
; prim1.asm – прямое обращение к полям структуры
.model tiny
.code
org 100h ; обход 256 байтного префикса пр-го сегмента – PSP…
Start: mov AH, 9
mov DX, offset message
int 21h
;
lea DX, st1.s
int 21h
lea DX, st1.f
int 21h
lea DX, st1.i
int 21h
ret
;
message DB “ hello”, 0dh, 0ah, ”$”
tst struc ; описание типа структуры
s DB “student”,”$”
f DB “Ivanov “,”$”
i DB “Ivan “,”$”
tst ends
st1 tst < > ; описание переменной типа tst
end start
org 100h - все сегментные регистры вначале выполнения программы содержат адрес блока PSP, который резервируется непосредственно перед EXE и COM файлами. Смещением для 1-ой команды программы является адрес 100h. Переход на первую выполняемую команду и происходит с помощью директивы ORG 100h.
Prim2.asm – обращение к полям структуры в цикле
Model tiny
Code
org 100h ; обход 256 байтного префикса пр-го сегмента – PSP…
Start: mov AH, 9