12. Связь между языками Ассемблер и С 75

Лаб10. Макросы. Числовые массивы

Цель работы: ознакомление с принципами написания макросов и их использования в программах, а также с технологиями обработки числовых массивов.

Теоретическая часть

Макроопределения

Одними из самых мощных языковых средств Ассемблера являются макроопределения. Макроопределением (или макросом) называется набор команд, которому присвоено имя и который вставляется в программу всякий раз, когда Ассемблер встречает это имя в тексте программы. Макрос начинается директивой MACRO и заканчивается ENDM. Например, пусть описано макроопределение PUTSPACE, выводящее символ пробела на экран:

PUTSPACE  	macro
		mov	ah,2
		mov	dl,’ ’
		int	21h
endm

Теперь в программе можно использовать слово PUTSPACE как имя команды, и Ассемблер заменит каждое такое слово на макрорасширение, на три команды, содержащиеся в макроопределении. Макроопределения могут вызываться с параметрами, следовательно, в зависимости от ситуации, включаемый код будет генерироваться с различными параметрами, например, как макрос обмена значений в памяти:

CHANGE_VAR	macro	var1, var2
	         push	var1
	         push	var2
	         pop	var1
	         pop	var2
endm

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

Следующее важное средство, использующееся в макроопределениях, –это директивы условного ассемблирования. Например: напишем макрос, выполняющий умножение регистра AL на число, причем, если множитель – степень двойки, то умножение будет выполняться более быстрой командой сдвига влево.

fast_mul  macro	number
     if number eq 2
        shl ax, 1       ;умножение на 2
     elseif number eq 4
	shl ax, 2       ;умножение на 4
     elseif number eq 8
	shl ax, 3       ;умножение на 8
	...
     elseif number eq 128
	shl ax, 7       ;умножение на 128
     else
	mov dx, number 	;умножение на число, не являющееся
	mul	dx	;степенью двойки.
	endif
endm

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

fast_mul_1	macro	number
	i=1
	while i le number and i ne 0 ;пока i меньше или равно number 
                                     ;и не равно 0
	if number eq       ;если i = number
	   rept number
	   shl ax,1	   ;умножаем ax на 2number логическим сдвигом
;на единицу number раз
	   endm
	   i=0		   ;если умножение уже сделано, то i = 0
	else	           ;если i ? number,
	   i=i*2           ;получаем следующую степень числа 2
	endif
endm
	if i ne 0				
	   mov	dx, number ;умножение на число, не являющееся
	   mul	dx	   ;степенью двойки.
	endif
endm

Числовые массивы

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

Описание и инициализация массива в программе:

1) Можно перечислить элементы массива в поле операндов. При перечислении элементы разделяются запятыми:
;массив из 5 элементов. Размер каждого элемента 2 байта
mas1	dw	1, 2, 3, 4, 5	
2) Можно использовать оператор повторения DUP:
;массивы из 5 элементов. Размер каждого элемента 2 байта
mas2 	dw	5 dup (?)
mas3 	dw	5 dup (0)
3) Инициализировать элементы массива можно и в цикле:
        mov cx, 5	 ;значение счетчика циклов в cx
        mov si, 0	 ;индекс начального элемента в si
        mov ax, 1	 ;значение первого элемента массива
go:			 ;метка начала цикла
        mov mas2[si], ax ;запись в массив
 
        inc si	;продвижение к следующему элементу массива
        inc si	;или add si,2 ,т.к. данные размером в слово
        inc ax	;инкремент ax
        loop go	;повторить цикл

Доступ к элементам массива

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

Например, пусть дан массив

mas     dw   5, 3, 7, 1, 2

Считываем третий элемент – число 7 в регистр dx. Этот элемент отстоит от начала массива на 4 байта. Поэтому, чтобы обратиться к этому элементу, к базовому адресу необходимо добавить смещение 4.

mov     si, 4
mov     dx, mas[si]

В общем случае для получения адреса элемента в массиве необходимо начальный (базовый) адрес массива сложить с произведением индекса этого элемента на размер элемента массива:

база + (индекс * размер_элемента)

Возможность масштабирования появилась в процессорах Intel, начиная с модели i486. С учетом этих возможностей обращение к элементу массива может быть представлено следующим образом:

mov     si, 2
mov     dx, mas[si*2]

Масштабирование эффективно лишь тогда, когда размерность элементов массива равна 2, 4 или 8. Если же размерность элементов другая, то обращение к элементам массива нужно организовывать обычным способом, описанным ранее.

Типовые операции с массивами

Рассмотрим пример, в котором просматривается массив, состоящий из элементов типа word. Выполняется сравнение этих элементов с нулем и выводится соответствующее сообщение.

Примеры

.model	small
puts	macro s	       ;вывод строки на экран
mov	ah, 9
	lea	dx, s
	int	21h
	endm
putch	macro c	       ;вывод символа на экран
        mov	ah, 2
	mov	dl, c
	int	21h
	endm
 
prolog	macro 	       ;связка ds с сегментом данных
        mov	ax, @data
	mov	ds, ax			 
	endm
epilog	macro	       ;стандартный выход
        mov	ah, 4ch	
	int	21h			
	endm
.stack	64
 
.data	       ;начало сегмента данных
;тексты сообщений
mes1	db	“Не равен 0!”, 0ah, 0dh,$”
mes2	db	“Равен 0!”, 0ah, 0dh,$”
mes3	db	0ah, 0dh, “Элемент $”
mas	dw	5, 3, 0, 7, 1, 0, 0, 2	;исходный массив
 
.code
.486			;директива, позволяющая использовать
			;32–разрядные регистры и масштабирование
			;при обращении к элементам массива
main:
	prolog
	xor	ax, ax	;обнуление ax
prepare:
        mov	cx, 8	;в cx – количество элементов массива,
                        ;которые служат счетчиком циклов
        mov	esi, 0	;индекс в esi
compare: 
        puts	mes3	;вывод сообщения на экран
        mov	dx, si		
        or	dl, 30h	;преобразование цифры в символ
        putch	dl	;вывод значения индекса
 
        mov	dx, mas[esi*2] ;помещаем элемент массива в dx
        cmp	dx, 0	       ;сравнение dx с 0
        je	equal	       ;переход, если равно
not_equal:
	puts	mes1		;вывод сообщения на экран
	jmp	next			
equal:
	puts	mes2		;вывод сообщения на экран
next:
	inc	esi		;на следующий элемент
	dec	cx		;условие для выхода из цикла
	jcxz	exit		;cx = 0? если да – на выход
	jmp	compare		;нет – повторить цикл
exit:
	epilog
	end	main

Задания

  1. Подсчитать количество положительных и количество отрицательных элементов в заданном числовом векторе размера m.
  2. Дана числовая матрица размера mх Создать вектор размера m, содержащий минимальные элементы каждой строки матрицы.
  3. Дана числовая матрица размера mх Создать вектор размера m, содержащий максимальные элементы каждого столбца матрицы.
  4. Дана числовая матрица размера nхn. Определить минимальные элементы в каждой строке матрицы, записать их в первый столбец, а элементы первого столбца – на место соответствующих минимальных.
  5. Дана числовая матрица размера nхn. Определить максимальные элементы в каждом столбце матрицы, записать их в первую строку, а элементы первой строки – на место соответствующих максимальных.
  6. Дан числовой вектор размера m. Подсчитать, сколько раз встречается в нем максимальный элемент.
  7. Дана матрица размера nхn. Определить такие k, где k–я строка матрицы совпадает с k–м столбцом.
  8. Дана целочисленная матрица размера nхn. Определить столбец, содержащий только такие элементы, которые по модулю не больше наперед заданного числа a.
  9. Дан числовой вектор размера m. Найти минимальный из положительных и максимальный из отрицательных элементов.
  10. Дана матрица a размера mхn. Построить вектор b, элементы которого вычисляются по формуле: bi=ai,0+ai,n-1, где i=0,1,…,m-1.
  11. Дана матрица a размера mхn. Построить вектор b, элементы которого вычисляются по формуле: bj=a0,j+am-1,j, где j=0,1,…,n-1.
  12. Дана числовая матрица размера nхn. Построить вектор, элементы которого являются суммами элементов каждой строки матрицы.
  13. Дана числовая матрица размера nхn. Построить вектор, элементы которого являются суммами элементов каждого столбца матрицы.
  14. Дана числовая матрица a размера nхn. Получить матрицу b размера nхn, элементы которой определяются по правилам:

bi,j= {lbrace}{0, if Ai,j >0, 1 otherwise}

  1. Даны числовой вектор из n элементов и числовая матрица размера mхn. Умножить матрицу на вектор.
  2. Перемножить две данные числовые матрицы размера nхn.

Лаб9. Обработка одномерных символьных массивов

Цель работы: ознакомление с основными командами обработки символьных массивов.

Теоретическая часть

Все команды работы со строками (символьными массивами) могут оперировать со строками до 64 Kбайт. Команде может предшествовать префикс, который задает повторное выполнение команды заданное число раз, что облегчает обработку строк.

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

Строка–приемник располагается в дополнительном сегменте данных, обычно адресуемом регистром ES, регистр DI (индекс приемника) используется для адресации смещения в строке–приемнике. Регистры SI и DI должны быть инициализированы адресами массивов источника и приемника. Обрабатываемые строки могут содержать элементы стандартных типов (байт, слово или двойное слово).

Для строк можно явно переопределять сегмент с помощью префикса, например, DS:ARR1, DS:ARR2, а это означает, что обе строки находятся в одном сегменте. Если же загрузить регистры DS и ES одним значением, то обе строки смогут находиться в одном сегменте.

При выполнении любой из команд обработки строк значения регистров SI и DI изменяются, указывая на следующий элемент строки, требующий обработки. Шаг изменения зависит от значения флажка DF. Значение DF = 0 приводит к инкрементации регистров SI и DI, при DF = 1 – к декрементации. Если размерность элементов массива равна одному байту, значения регистров SI и DI изменяются после каждой операции на 1, при размерности WORD – на 2.

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

Префиксы повторений REP, REPE, REPZ, REPNE и REPNZ

Префикс REP(repeat) используется с командами MOVS и STOS и интерпретируется как «повторять до конца строки» (пока CX отлично от 0).

Префиксы REPE и REPZ (repeat while equal, repeat while zero) идентичны и используются с командами CMPS и SCAS. В случае использования данных префиксов команда будет повторяться до тех пор, пока флажок ZF =1 и значение регистра CX отлично от 0. Префиксы REPNE и REPNZ (repeat while not equal, repeat while not zero) повторяют команду, пока ZF = 0 и CX отлично от 0.

Команда CLD

Команда CLD (CLear Direction flag) устанавливает флажок направления (DF) в 0 для автоматического увеличения значений регистров SI и DI (просмотр массива слева направо). Команда CLD других флажков не меняет.

Синтаксис: CLD

Команда STD

Команда STD (SeT Direction flag) устанавливает флажок направления (DF) в 1 для автоматического уменьшения значений регистров SI и DI (просмотр массива справа налево). Команда STD других флажков не меняет.

Синтаксис: STD

Команда MOVS

Команда MOVS (MOVe String) копирует байт, слово или двойное слово из строки–источника, адресованной регистром SI в строку–приемник, адресованную регистром DI. После копирования элемента значения регистров–указателей SI и DI автоматически сдвигаются на следующий элемент в зависимости от значения флажка направления DF. С префиксом повторения команда MOVS может быть использована для копирования блоков памяти.

Синтаксис: MOVs [<строка_приемник>, <строка_источник>]

MOVSB ;пересылка байта

MOVSW ;пересылка слова

MOVSD ;пересылка двойного слова

В команде MOVS операнды указывают только на тип элементов, а не на сами обрабатываемые элементы. Типы операндов можно задавать явно, используя команды MOVSB, MOVSW и MOVSD, сами операнды указывать необязательно. Команды работают, соответственно, с массивами, типы которых определяются последним символом команды: BBYTE, WWORD, DDWORD.

Команда CMPS

Команда CMPS (CoMPare String) сравнивает один элемент из строки–источника, адресованный регистром SI, с элементом строки–приемника, адресованным регистром DI. После сравнения элементов признак результата сохраняется во флажковом регистре и значения указателей SI и DI автоматически сдвигаются на следующий элемент в зависимости от значения флажка направления DF.

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

Синтаксис: CMPS [<строка_приемник>, <строка_источник>]

cmpsb ;сравнение байта

cmpsw ;сравнение слова

cmpsd ;сравнение двойного слова

Команда SCAS

Команда SCAS (SCAn String) сравнивает элемент строки–приемника, адресованный регистром DI, с содержимым регистра al/ax/eax, изменяет содержимое регистра DI, формирует признак результата и сохраняет его во флажковом регистре (флажки AF, CF, OF, PF, SF, ZF). Команда SCAS может быть использована с условным префиксом повторения для сравнения символов строки с элементом, хранящимся в регистре.

Сравнение будет производиться до тех пор, пока не будет найдено совпадение (REPNE) или несовпадение (REPE). В случае, когда условие ни разу не выполняется, цикл прекратится после заданного в CX количества раз.

Синтаксис: SCAS [< строка_приемник >]

SCASB ;сканирование байта

SCASW ;сканирование слова

SCASD ;сканирование двойного слова

Команда LODS

Команда LODS (LOaD String) копирует один элемент (байт, слово или двойное слово) из строки–источника в регистр al/ax/eax и изменяет содержимое регистра SI для возможности копирования следующих элементов строки. Команда LODS может быть использована с префиксом повторения, но обычно префикс не используется, так как запомнится только результат последнего копирования.

Синтаксис: LODS [<строка_источник>]

LODSB ;загрузка байта

lodsw ; загрузка слова

lodsd ; загрузка двойного слова

Команда STOS

Команда STOS (STOre String) копирует байт, слово или двойное слово из регистра al/ax/eax в строку–приемник, адресованную регистром DI. После выполнения команды регистр DI обновляется, указывая на следующий символ строки. Если перед командой использовать префикс безусловного повторения, можно всю строку инициализировать одним и тем же значением, например – заполнить строку символами пробела или числами 0.

Синтаксис: STOS [< строка_приемник>]

STOSB ;запись байта

STOSW ;запись слова

STOSD ;запись двойного слова

Пример

С клавиатуры вводятся 2 строки. Вывести сообщение “Yes”, если строки совпадают, и “No” в противном случае.

.data
str1	db	100 dup (?)	;буфер ввода1
str2 	db	100 dup ('$')	;буфер ввода2
len1 	dw 	?
len2 	dw 	?
.code
	mov ax, @data	;инициализация регистров ds и es
	mov ds, ax	;регистры ds и es инициализированы одним и тем же 
	mov es, ax	;зачением, т.к. строки str1 и str1 находятся 
			;в одном и том же сегменте
	...
	;ввод 2–х строк
	;длина введенных строк сохраняется в len1 и len2
	...
	lea di, str2	;в di – адрес строки–приемника
	lea si, str1	;в si – строки–источника
	cld		;задаем направление просмотра 
;строки слева направо
	mov cx, len1	;cx – количество повторений для repe
	cmp cx, len2	;сравниваем длины строк
	jne	NO	;если длины строк не совпадают, идем на NO
repe	cmpsb		;сравниваем строки
	jne NO		;если строки не совпадают, переход на NO
                        ;вывод сообщения “Yes”
...
	jmp	finish
NO:	...		;вывод сообщения “No”
finish: ...

Задания

  • Составьте программу (вариант A) на языке Ассемблер, которая читает строку символов с клавиатуры и обрабатывает ее, используя команды работы со строками.
  • Составьте программу (вариант B), используя команды сравнения, перехода и команды организации циклов.
  1. Заменить в строке все заглавные буквы ‘A’ на ‘а’.
  2. Определить, является ли введенная строка целым положительным числом.
  3. Найти первый символ ‘*’ в строке и заменить его на символ ‘#’.
  4. Найти последний символ ‘?’ в строке и заменить его на символ ‘!’.
  5. Подсчитать количество слов в предложении (разделитель между словами – символ пробела).
  6. Удалить в строке повторяющиеся пробелы.
  7. Найти в строке все слова длиннее заданного количества символов (разделитель между словами – символ пробела).
  8. Найти в строке положение последней запятой и поменять ее на первый непробельный символ строки.
  9. Для всех слов в строке, заканчивающихся на “ing”, заменить окончания на “ed” (разделитель между словами – символ пробела).
  10. Заменить в строке все символы ‘–’, предшествующие первому символу ‘+’, на символ ‘*’.
  11. Определить, сколько раз каждая буква встречается в заданном предложении.
  12. Заменить в строке каждую точку ‘.’ на многоточие “…”.
  13. Найти в строке все слова, в которых буква ‘a’ встречается более двух раз (разделитель между словами – символ пробела).
  14. Найти в строке все слова, состоящие не более чем из 6 символов (разделитель между словами – символ пробела).
  15. Определить, расположены ли слова предложения в алфавитном порядке (разделитель между словами – символ пробела).
  16. Определить, начинается ли каждое следующее слово в предложении с последней буквы предыдущего слова (разделитель между словами – символ пробела).

Лаб8. Циклы. Процедуры

Цель работы: изучение операторов цикла, приобретение навыков использования команд циклов при написании ассемблерных программ; организация и вызов процедур, передача параметров в процедуры и возврат результатов из процедур.

Теоретическая часть

Команды циклов

Циклы организуются для многократного повторения одной или нескольких команд программы или процедуры. Цикл можно организовать, используя команды условного и безусловного переходов, рассмотренных в лабораторной работе 6, а можно с помощью специальных команд. Формат команд представлен в таблице.

ТАБЛИЦА ФОРМАТОВ КОМАНД ЦИКЛА

Название команды Мнемоника и формат команды Проверяемое условие цикла
Повторять цикл LOOP label (ECX/ CX) = 0
Повторять цикл, пока ноль или равно LOOPZ/ LOOPE label (ECX/ CX) = 0 или ZF = 0
Повторять цикл, пока не ноль или не равно LOOPNZ/ LOOPNE label (ECX/ CX) = 0 или ZF = 1
Переход по CX JCXZ label (ECX/ CX) = 0

 

Команда LOOP и ее расширения позволяют организовывать циклы, подобные циклам в языках высокого уровня. Регистр ECX/CX служит счетчиком максимального числа повторений цикла, его загружают перед началом цикла. Все команды, за исключением команды JCXZ, которая не изменяет ECX/CX, вычитают единицу из ECX/CX перед повторением цикла. В случае, когда условие цикла удовлетворяется, управление передается на метку label, в противном случае управление переходит на следующую за LOOP команду. Флажки этими командами не модифицируются. Метка label должна находиться в диапазоне от -128 до 127 байтов от команды, следующей за командой цикла.

Примеры.

;складываем числа от 1 до 12
mov	bx, 0
mov	ax, 0
mov	cx, 12	        ;количество повторений цикла
a1:	inc	bx
	add	ax, bx	;складываем числа
	loop	a1	;повторять, пока cx не станет равен 0
 
;тот же самый пример, но с использованием jcxz
;складываем числа от 1 до 12
mov	bx, 0
mov	ax, 0
mov	cx, 12	        ;количество повторений цикла
a1:jcxz	a2		;выход из цикла, если cx равен 0
	inc	bx
	add	ax, bx	;складываем числа
	dec	cx
	jmp	a1	;переход на начало цикла
a2: ...			
 
;ищем первую букву ’а’ в строке, адресуемой регистром bx
;длина строки 120 байтов
	mov	cx, 120		;количество повторений цикла
m1:	inc	bx
	cmp	byte ptr[bx],’а’;ищем первую букву ’а’
	loopne	m1		;повторяем цикл 120 раз или пока не найдем ’а’
                                ;проверяем, закончилась ли строка или найдена ’а’
	jz	naidena
	... 		        ;строка закончилась, буква ’а’ не найдена
naidena:...	                ;адрес ’а’ в bx

Обращение к процедурам

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

Описание процедуры состоит из заголовка, тела и конца процедуры:

имя_процедуры PROC [расстояние]     ;заголовок процедуры

;команды, директивы Ассемблера – тело процедуры

ret

имя_процедуры ENDP                                  ;конец процедуры

Имя процедуры – это идентификатор (символический адрес), по которому происходит обращение к процедуре. Необязательный параметр расстояние характеризует возможность обращения к процедуре из другого сегмента кода. Этот параметр может принимать значения: NEAR – для процедур ближнего вызова (описание процедуры находится в том же самом сегменте кода, что и основная программа) и FAR – для процедур дальнего вызова (описание процедуры находится в другом сегменте кода). По умолчанию этот параметр равен NEAR.

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

Процедуру можно разместить:
1) в начале программы (до первой исполняемой команды);

c_s segment
	assume cs:c_s
pr1	proc near
	...
	ret
p1	endp
begin:            ;начало программы
	...
	end begin

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

c_s	segment
	assume cs:c_s
begin:
	...
	mov ah, 4ch
	int 21h	;корректное завершение работы 
		;и передача управления ОС
	;ret	;можно использовать команду ret
p1	proc near
	...
	ret
p1	endp
c_s	ends
end	begin

3) внутри тела программы или другой процедуры (должен быть предусмотрен обход процедуры с помощью оператора jmp);

c_s	segment
	assume cs:c_s
begin:
	...
	jmp m1
p1	proc near
	...
	ret
p1	endp
m1:	...
	mov ah, 4ch
	int 21h    ;корректное завершение работы 
		   ;и передача управления ОС
c_s	ends
end	begin

4) в другом модуле.

Команда CALL осуществляет вызов процедуры, сохраняя предварительно в стеке адрес возврата – адрес команды, следующей после команды CALL. При выполнении команды CALL осуществляется передача управления по адресу с символическим именем имя_процедуры.

Формат команды CALL: CALL [модификатор] имя_процедуры

Модификатор может принимать значения NEAR или FAR, для обращения к процедурам ближнего или дальнего вызовов, соответственно.

Команда RET считывает из стека адрес возврата и загружает его в регистр IP/EIP или в пару регистров CS и IP/EIP, возвращая тем самым управление на команду, следующую в программе за командой CALL. Для процедур ближнего вызова адрес возврата полностью определяется содержимым регистра IP/EIP, а для процедур дальнего вызова – содержимым двух регистров: CS и IP/EIP. При этом команда CALL запоминает в стеке сначала значение регистра CS, а затем значение регистра IP/EIP.

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

Примеры

;1. Фрагмент программы для вычисления в цикле выражения
;y = 5+6+7+8+9+10+16
mov	cx, 6		;количество циклов (10-5+1)
mov	ax, 0		;накопитель результата
mov	bx, 5		;первоначальное значение i=5
m:	add ax, bx	;ax=5+6+… 
inc	bx		;инкрементируем i
loop	m		;если цикл не завершен, возвращаемся на m
add	ax, 16		;прибавляем к сумме 16
 
;2. Программа и процедуры вывода сообщений
dseg	segment	para publicdata’
m1	db	’message1’,10,13,$’
m2	db	’message2’,10,13,$’
contor1	dw	0
contor2	dw	1
dseg	ends
 
cseg	segment	para publiccode’
	assume	cs:cseg, ds:dseg
 
main_pr	proc		;основная программа
	...
	lea	dx, m1	;параметр для процедуры print в dx
	call	print
	lea	dx, m2	;параметр для процедуры print в dх
	call	print
	...
	mov	ax,0
	push	ax	;параметр для incnum в стеке
	call	incnum
	pop	ax	;получение результата
	...
	mov	cx,offset contor1	
	push	cx	;параметр для addnum в стеке
	call	addnum
	mov	cx,offset contor2	
	push	cx	;параметр для addnum в стеке
	call	addnum
	ret
main_pr	endp
 
print	proc	near	;процедура вывода сообщений
	mov	ah, 9
	int	21h		
	ret
print	endp
incnum	proc	near	;процедура увеличения значения на 3
	pop	cx	;извлечение параметра из стека в сх
	add	cx,3
	push	cx
	ret
incnum	endp
 
;процедура увеличения значения в памяти на 1
;параметр передается через стек
;результат записывается в память в вызывающей программе
addnum	proc	near	
	pop	bx	;извлечение адреса памяти из стека в bх
	mov	ax,[bx]
	add	ax,1
	mov	[bx],ax	 ;запись результата в память
	ret
addnum	endp
 
cseg	ends
	end	main_pr
	end

Задания

Написать программу на языке Ассемблер, которая использует процедуры для вычисления следующего выражения:

  1. y = sum{i=0}{16}{i*6}
  2. y = sum{i=1}{7}{(i-2)}
  3. y = sum{i=5}{17}{i%3}
  4. y = sum{i=-8}{10}{i*2}
  5. y = sum{i=0}{20}{i/2}
  6. y = sum{i=-1}{15}{(i+2)}
  7. y = sum{i=-5}{5}{i*i}
  8. y = prod{i=1}{5}{i+2}
  9. y = prod{i=1}{4}{i%2}
  10. y = prod{i=1}{5}{i*2}
  11. y = prod{i=1}{7}{i+4}
  12. y = prod{i=1}{6}{i+7}
  13. y = sum{i=5}{20}{(i+i/2)}
  14. y = sum{i=-3}{20}{(i%2+i)}
  15. y = sum{i=-3}{17}{(2*(i-2))}
  16. y = sum{i=2}{10}{((i+1)/3)}

7. Команды целочисленной арифметики

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

Теоретическая часть

Команды целочисленной двоичной арифметики

Команда ADD

Команда суммирует два операнда, результат сохраняет в операнде назначение.

Синтаксис: ADD <назначение>, <источник>

Примеры:

add  ax, [bx]   ;элемент, хранящийся по адресу, указанному 
	        ;в регистре BX, сложить с содержимым регистра AX, 
                ;результат сохранить в регистре AX.
add  ax, 10	;к содержимому регистра AX прибавить 10,
                ;результат сохранить в регистре AX.
add  bh, LM     ;к содержимому регистра BH прибавить значение 
                ;переменной LM, результат сохранить в регистре BH.

Команда INC

Команда увеличивает значение операнда на 1.

Синтаксис: INC <назначение>

Примеры:

inc  CX	   ;увеличить значение регистра CX на 1 
inc  [BX]  ;значение, хранящееся по адресу, 
           ;указанному в регистре BX, увеличить на 1
inc  LM	   ;значение переменной LM увеличить на 1.

Команда SUB

Команда из операнда назначение вычитает операнд источник. Результат сохраняется в операнде назначение.

Синтаксис: SUB <назначение>, <источник>

Примеры:

sub  ax, [bx]   ;значение, хранящееся по адресу, указанному
                ;в регистре BX, вычитается из регистра AX и
                ;результат сохраняется в регистре AX 
sub  ax, 10     ;из содержимого регистра AX вычитается число 10,
                ;результат сохраняется в регистре AX.
sub  bh, LM	;содержимое переменной LM вычитается из 
                ;регистра DH, результат сохраняется в регистре BH.

Команда DEC

Команда уменьшает значение операнда на 1.

Синтаксис: DEC <назначение>

Примеры:

dec  cx	        ;значение регистра CX уменьшается на единицу 
dec  [bx]	;значение, хранящееся по адресу, указанному в 
                ;регистре BX, уменьшается на единицу 
dec LM		;значение переменной LM уменьшается на 1

Команда NEG

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

Синтаксис: NEG <назначение>

Примеры:

neg  CX		;изменить знак значения в регистре CX 
neg  [BX]	;изменить знак значения, хранящегося по адресу,
                ;указанному в регистре BX 
neg  LM		;изменить знак переменной LM

Команда изменения знака neg не изменит значения операнда, если число находится на границе допустимого диапазона значений. Например, последовательность команд

mov AL, -128
neg AL

оставит без изменения значение регистра AL, так как 128 и -128 имеют одинаковое внутреннее представление (80H).

Команда MUL

Команда предназначена для беззнакового умножения. Операнд–источник умножается на содержимое регистра–накопителя. Если операндом–источником является величина типа BYTE, то источник умножается на содержимое регистра AL и результат сохраняется в регистре AX. Если операндом–источником является величина типа WORD, то источник умножается на содержимое регистра AX и результат помещается в пару регистров DX:AX

Синтаксис: MUL <источник>

Примеры:

mul   CL	   ;CL умножается на AL и результат сохраняется 
                   ;в регистре AX
mul word ptr [BX]  ;значение, хранящееся по адресу, указанному
                   ;в регистре BX, умножается на AX, результат
                   ;помещается в пару регистров DX:AX 
mul LM		   ;действие команды зависит от размера памяти,
                   ;адресуемой LM

Команда IMUL

Команды MUL и IMUL похожи. Разница в том, что команда IMUL при умножении учитывает знаки операндов.

Синтаксис: IMUL <источник>

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

Команда DIV

Команда осуществляет беззнаковое деление. Регистр–накопитель делится на операнд, и если источником является величина типа BYTE, то содержимое регистра AX делится на операнд–источник, а частное помещается в регистр AL, а остаток от целочисленного деления – в регистр AH. В случае, когда источником является операнд типа WORD, в качестве делимого предполагается пара регистров DX:AX. Результат деления помещается в регистр AX, а остаток – в регистр DX.

Синтаксис: DIV <источник>

Примеры:

div  CL	      ;содержимое регистра AX делится на CL, 
              ;частное – в AL, остаток – в AH
div  LM	      ;действие команды зависит от размера памяти, 
	      ;адресуемой LM

Команда IDIV

Команда осуществляет деление со знаком. Действия команд DIV и IDIV схожи. Разница в том, что команда IDIV при делении учитывает знаки операндов.

Синтаксис: IDIV <источник>

Примеры:

idiv  DL	;содержимое регистра AX делится на DL, со знаком 
div  MEM_WORD	;разделить DX:AX на слово памяти, со знаком 
	        ;частное – в AX, остаток – в DX

Использование команд деления может привести к неожиданным результатам. В случае, если в результате деления получится величина, превосходящая максимально допустимое значение (8 или 16 бит), или если делитель окажется равным 0, будет сгенерировано программное прерывание Divide Overflow (переполнение при делении).

Например, в последовательности

mov  ax, 1000
mov  bl, 2
div  bl

результат должен быть 500. Эта величина должна быть сохранена в однобайтовом регистре AL. В этом случае выполнение программы будет прервано и на консоль будет выведено сообщение об ошибке, т.к. число со знаком, помещаемое в один байт, должно находиться в диапазоне [-128, +127].

Команда CBW

Команда преобразует байт в слово, воспроизводя старший 7-й бит регистра AL во всех битах регистра AH. Таким образом, команда CBW позволяет сложить байт и слово, вычесть слово из байта и т.д.

Синтаксис: CBW

Например:

cbw
add  AX, BX    ;сложить байт в AL со словом в BX
cbw
imul  BX      ;умножить байт в AL на слово в BX

Команда CWD

Команда преобразует слово в двойное слово, воспроизводя старший 15-й бит регистра AX во всех битах регистра DX. Таким образом, команда CWD позволяет разделить слово на слово. Например:

cwd
idiv BX		;разделить слово в AX на слово в BX

Синтаксис: CWD

Вычисление значения выражения

Рассмотрим задачу вычисления арифметического выражения. Так как Ассемблер не располагает встроенными средствами ввода и вывода чисел, для работы с числовыми переменными необходимы некоторые преобразования. Так, перед отображением числа его надо преобразовать в последовательность символов. Вводить Ассемблер также позволяет только символы, поэтому для работы с числами введенную строку надо преобразовать в число.

Рассмотрим пример преобразования числа в строку. Пусть numar – число, которое необходимо вывести на экран. Преобразуем его в строку символов s и выведем её на экран.

s   db  6 dup(’ ’),$.code
	...				
	mov ax, 	;число может быть отрицательным 
                        ;или положительным
	push ax	        ;сохраняем число из ax в стеке
	mov si, 10      ;делитель для выделения младшего разряда
	mov di, 5	;максимально число типа dw может занимать 6
                        ;позиций. Устанавливаем указатель на последнюю 
                        ;позицию в строку результата
	mov dx, 0	;dx=0, так как (dx:ax)/si
	cmp ax, 0	;проверяем знак числа 
	jge m		;если число положительное, переходим на метку m
	neg ax		;если число отрицательное, меняем знак
m: 	div si		;значение из (dx:ax) делим на 10, выделяя 
                        ;последний разряд числа, который помещается в 
                        ;регистр dx. Так как это число не будет больше, 
                        ;чем 9, оно помещается в dl
	add dl, 30h	;преобразуем полученную цифру в символ
	mov s[di], dl 	;вставляем полученный символ в строку результата
	xor dx, dx	;очищаем dx 		
	dec di		;сдвигаем позицию указателя в строке влево
	cmp ax, 0	;проверяем, преобразованы ли все символы числа
 	jne m		;если преобразованы не все символы, возвращаемся 
                        ;на начало цикла	
	pop ax		;если всё число преобразовано, извлекаем 
                        ;первоначальное значение из стека для проверки 
                        ;знака
	cmp ax, 0	;проверяем знак числа
	jge m1		;если число >0, переходим на вывод строки
	mov s[di],-;если число отрицательное, перед числом
			;вставляем ”-” 
m1:	mov ah, 9	;вывод строки, содержащей преобразованное число
	lea dx, s
	int 21h

Пример
Вычислим значение выражения ((120+15)/5-12)*16, значения переменных задаются в тексте программы.

data
 	a	db 120
 	b	db 15
	c	db 5
	d	db 12
	e	db 16
.code
	mov 	al, a 		; al=120
	add 	al, b		; al=120+15
	mov 	ah, 0
	div 	c 		; al=(120+15)/5
	sub 	al, d		; al=(120+15)/5-12
	mov 	ah,0
	mul 	e		; ax=((120+15)/5-12)*16
;Для преобразования введенной строки в число 
;со знаком можно использовать следующий алгоритм:
.data
n		dw 0		;результирующее число
mult10	dw 1			;отвечает за разряд числа i. Будет 
                                ;содержать значение 10i.
ten 		dw 10		;множитель 10
sir 		db 8 dup (?)  	;исходная строка
.code
	...
	mov ah, 3fh	;вводим число в виде набора символов 
	mov bx, 0	
	lea dx, sir
	int 21h		;регистр ax будет содержать количество введенных 
                        ;символов +2
        mov di, ax	;сохраняем количество введенных символов в di
	sub di, 3	;находим позицию последнего символа
	cmp di, 0	;для случая числа из одной цифры 
	je  b		
a:	mov al, sir[di]	;копируем символ, на который показывает di, в al
	sub al, 30h	;преобразуем символ в число (цифру)
	mov ah, 0	;ah обнуляется
	mul mult10	;умножаем полученное число на mult10
	dec di		;сдвигаем позицию индекса на один разряд влево
	add n, ax	;n=c0*100 +c1*101 +…
	mov ax, ten	;помещаем в ax 10
	mul mult10	;mult10=mult10*10 получаем 10i
	mov mult10,ax	;перемещаем новое значение mult10 из ax в mult10
        cmp di, 0	;проверяем, преобразованы ли все символы, кроме 
                        ;первого, который может содержать знак числа 
	jne a		;если не первый символ, повторяем цикл
	cmp sir[di], '-';сравниваем первый символ с символом '-'
	je  m1		;если '-'‚переход на m1,иначе число положительное
b:	mov al, sir[di]	;преобразуем старший разряд числа
	sub al, 30h
	mov ah, 0
	mul mult10
	add n, ax	;копируем полученное число из ax в n
	jmp m2
m1: 	neg n		;получаем отрицательное число
m2:...

Задания

а). Вычислить значение выражения (значения переменных задаются в тексте программы):

  1. (170-16)/2+12*5
  2. 45+((23-2)*2)/7
  3. 80+(45-5)*3+45/9
  4. 34*2-(15+9)/3
  5. (21+7)/14-4*5
  6. (81-60)/7+3*7
  7. (17+16)/3+12*5
  8. (7+(23-2)*2)/7
  9. 8+(14-5)*3+45/5
  10. 24*3-(45+18)/3
  11. (41-20)/3-4+3*5
  12. (46+10)/2-10*5
  13. (87-16+7)/3-16*5
  14. (83-20)/3+4-3*5
  15. 30+(45-5+2)/3+2*3
  16. 90-(41-5)*3/6

 

b). Вычислить значение выражения (значения переменных вводятся с клавиатуры):

 

1 (a+b+c)/a*d a – define byte,b, c, d – define word
2 (a+b/c)*d a, b, c – define byte, d – define word
3 (10*a-2*b)*c-d a, b – define byte,c, d – define word
4 a/b+c*d/(c+b) b, c, d – define byte, a – define word
5 10*a/(a+b)+10*b/(c+d) a, b, c – define byte, d – define word
6 60*a+b*d+c*(a–b) a, b – define byte, c, d – define word
7 (a+b)/c+d*a-c a – define byte, b, c, d – define word
8 c-(a+b)/(a–c)+c*d d – define byte, b, c, a – define word
9 a*b+c*d)/d a, d – define byte, b, c – define word
10 (a*c+b*d)*c a, b, d – define byte, c – define word
11 (a*b*c+c*d)/d b – define byte, a, c, d – define word
12 (a*b–c*d)*(a+b) a, b – define byte, c, d – define word
13 a*c*(a+b)-d a, b, c – define byte, d – define word
14 d*b+c*a/(a+b) c – define byte, b, a, d – define word
15 25*a+13*b-d*c c, b – define byte, a, d – define word
16 a*c-(a+b)/c*d c, b, d – define byte, a – define word

 

6. Команды сравнений и переходов

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

Теоретическая часть

Команда CMP

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

Синтаксис:

CMP <назначение>, <источник>

Изменяемые флажки: af, cf, pf, sf, zf, of.

Анализируя флажки, можно определить результат сравнения. Известно, что команда cmp ax, bx выполняет операцию (ax)–(bx), и если после этого вычитания zf = 1, следовательно, (ax)=(bx), а если cf = 1, то первый операнд меньше второго для беззнаковых чисел:(ax)<(bx).

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

Команда безусловного перехода JMP

Синтаксис:

JMP <назначение>

где назначение – это адрес (метка), по которому должен быть осуществлен переход. Определить назначение можно также с помощью метки или выражения. Метка может быть дальней или ближней (near или far) и содержать имя процедуры или имя метки, описанной с помощью директивы label.

Примеры задания меток:

proc_add procedure

m1:

aici:

et3 label far

gata label near

Примеры команды jmp:

JMP proc_add

JMP m1

JMP et3

JMP gata

С помощью команды jmp можно задать три типа переходов:

  • короткий переход – short, адрес назначения находится на расстоянии [-128, +127] байтов от места нахождения команды jmp;
  • ближний переход – near, адрес назначения находится в том же сегменте, что и команда jmp;
  • дальний переход – far, адрес назначения может находиться в другом сегменте кода.

Команды условного перехода

Синтаксис:

jcc <назначение>

где cc – условие перехода.

Директивы этой группы осуществляют переход только при выполнении определенных условий. Команды условного перехода имеют следующие характеристики:

  • Все инструкции условного перехода являются командами короткого перехода (тип short), что означает, что целевой адрес должен быть в пределах диапазона [-128, 127] байт от команды перехода;
  • Для одной и той же команды может существовать несколько мнемонических обозначений;
  • Команды условных переходов флажков не меняют.

Команды условных переходов используются после команд, изменяющих флажки состояний. Чаще всего их используют после команды сравнения cmp. В таблице приведены мнемоники команды перехода. При чтении таблицы необходимо помнить два правила: 1) для анализа знаковых величин используются сравнения ,,greater” и ,,less”; 2) для анализа беззнаковых величин используются сравнения ,,above” и ,,bellow”.

Мнемоника команды Условия перехода Пояснения
je, jz zf=1 Equal, Zero
jl, jnge sf?of Less, Not Greater or Equal
jle, jng sf?of или zf=1 Less or Equal, Not Greater
jb, jnae, jc cf=1 Below, Not Above or Equal, Carry
jbe, jna cf=1 или zf=1 Below or Equal, Not Above
jp, jpe pf=1 Parity, Parity Even
jo of=1 Overflow
js sf=1 Sign
jne, jnz zf=0 Not Eaual, Not Zero
jnl, jge sf=of Not Less, Greater or Equal
jnle, jg sf=of и zf=0 Not Less or Equal, Greater
jnb, jae, jnc cf=0 Not Below, Above or Equal, Not Carry
jnbe, ja cf=0 или zf=0 Not Below or Equal, Above
jnp, jpo pf=0 Not Parity, Parity Odd
jno of=0 Not Overflow
jns sf=0 Not Sign

Пример

Написать программу, которая проверяет, является ли введенный символ буквой ‘t’, и выводит ее эквивалент в верхнем регистре или сообщение “false” в противном случае.

Например:
Введите символ: t
T
или
Введите символ: s
false
Рассмотрим фрагмент программы.

	mov ah,1	;читаем символ
	int 21h
	mov var, al	;сохраняем введенный символ в переменной var
	cmp var, ’t’	;сравниваем введенный символ с символом t
	jne m1		;если символы не совпали, то переходим на m1
	sub var, 20h	;если символы совпали, то преобразуем ‘t’ в ‘T’
	mov ah, 2	;выводим новый символ на экран
	mov dl, var
	int 21h
	jmp sfarsit
m1:	mov ah, 9	;выводим сообщение False, содержащее 
lea dx, False		;текст “false”
	int 21h
sfarsit: ...

Задания

  1. Написать программу, соответствующую вашему варианту.
  2. Зациклить выполнение задания до тех пор, пока не будет введен символ пробел.

Варианты задач

1. Написать программу, которая проверяет, является ли введенный символ заглавной латинской буквой, и выводит ее эквивалент в нижнем регистре или сообщение „error”.

Например:
Введите символ: A
a
или
Введите символ: b
Error

2. Написать программу, которая проверяет, является ли введенный символ арифметической операцией (+, -, *, /), и выводит сообщение „true”, иначе выводит сообщение „false”.

Например:
Введите символ: *
true
или
Введите символ: b
false

3. Написать программу, которая проверяет, является ли введенный символ цифрой, и выводит символ ‚!’, иначе выводит сообщение „error”.

Например:
Введите символ: 5
!
или
Введите символ: (
error

4. Написать программу, которая проверяет, является ли введенный символ знаком препинания (– , . ! ? : ;), и выводит сообщение „true”, иначе выводит сообщение „false”.

Например:
Введите символ: !
true
или
Введите символ: 2
false

5. Написать программу, которая проверяет, является ли введенный символ малой латинской буквой, и выводит ее эквивалент в верхнем регистре или сообщение „error”.

Например:
Введите символ: r
R
или
Введите символ: 6
error

6. Написать программу, которая проверяет, является ли введенный символ скобкой (, ), [, ], {, }, и выводит сообщение „true”, иначе выводит сообщение „false”.

Например:
Введите символ: )
true
или
Введите символ: k
false

7. Написать программу, которая проверяет, является ли введенный символ гласной буквой (a, e, i, o, u, A, E, I, O, U), и выводит сообщение „ok”, иначе выводит сообщение „error”.

Например:
Введите символ: e
ok
или
Введите символ: m
error

8. Написать программу, которая проверяет, является ли введенный символ согласной буквой ‚m’, ‘n’, ‘p’ или ‘r’, и выводит сообщение „ok”, иначе выводит сообщение „error”.

Например:
Введите символ: m
ok
или
Введите символ: 4
error

9. Написать программу, которая проверяет, является ли введенный символ специальным символом (~, @, #, $, %, ^, &, |, \), и выводит сообщение „ true”, иначе выводит сообщение „ false”.

Например:

Введите символ: &
true
или
Введите символ: A
false

10. Написать программу, которая проверяет, является ли введенный символ символом с кодом меньше чем 48 или больше чем 122, и выводит сообщение „true”, иначе выводит сообщение „false”.

Например:
Введите символ: “
true
или
Введите символ: z
false

11. Написать программу, которая проверяет, является ли введенный символ одной из цифр ‘1’, ‘3’, ‘5’, ‘7’ или ‘9’, и выводит сообщение „ok”, иначе выводит сообщение „error”.

Например:
Введите символ: 3
ok
или
Введите символ: z
error

12. Написать программу, которая проверяет, является ли введенный символ одной из цифр ‘2’, ‘4’, ‘6’ или ‘8’, и выводит сообщение „ok”, иначе выводит сообщение „error”.

Например:
Введите символ: 4
ok
или
Введите символ: z
error

13. Написать программу, которая проверяет, является ли введенный символ символом ‘$’ или ‘@’, и выводит сообщение „true”, иначе выводит сообщение „false”.

Например:
Введите символ: $
true
или
Введите символ: z
false

14. Написать программу, которая проверяет, является ли введенный символ одним из символов ‘j’, ‘k’, ‘f’, ‘g’, и преобразовывает их в верхний регистр, иначе выводит сообщение „false”.

Например:
Введите символ: k
K
или
Введите символ: T
false

15. Написать программу, которая проверяет, является ли введенный символ одним из символов ‘A’, ‘C’, ‘D’, ‘E’, и выводит сообщение ‘OK’, иначе выводит сообщение „error”.

Например:
Введите символ: D
OK
или
Введите символ: T
error

16. Написать программу, которая проверяет, является ли введенный символ одним из символов ‘<’, ‘=’, ‘>’, ‘?’, и выводит сообщение “true”, иначе выводит сообщение „false”.

Например:
Введите символ: <
true
или
Введите символ: T
false

Translate Переводчик

Подписка на новости

SmartResponder.ru
Ваш e-mail: *
Ваше имя: *

Хостинг для Wordpress сайтов