Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология
Образование Политология Производство Психология Стандартизация Технологии


Арифметические операции Ассемблер. Команды Ассемблера



Введение в язык Ассемблер

 

Для того, чтобы программировать на Assembler, необходимо:

Программа MASM_v9.0 или MASM_v10.0
Отладчик, например: OLLYDBG.EXE
В установленном месте, где находится программа MASM, создаете файл: aaa.bat

Я выбрал такое название ( aaa.bat ) для того, чтобы она была в самом верху. И вы могли всегда его видеть. В этом aaa.bat вносите такую информацию:

ml /c /coff " work.asm"
link /SUBSYSTEM: CONSOLE " work.obj"

work.asm - это имя программы, которую нужно компилировать. После ввода этой информации и сохранения можно приступать к программированию.

Ассемблер имеет:

- директиву определения типа микропроцессора,
- метку начала программы,
- тело программы,
- метку окончания программы

В языке Ассемблер есть переменые разных типов: знаковые и беззнаковые форматытипов Shortlnt (signed char), Byte (unsigned char), Integer (int), Word (unsigned int) и т. д.

Напишем программу вычисления выражения: a – e/b – de, где:

a = 5;
b = 27;
c = 86;
е = 1986;
d = 1112;

и сохраним ее там же, где и aaa.bat: work.asm. Если мы хотил откомпилировать другую программу, то нужно в aaa.bat изменить имя файлов, т. е. вместо work заменить на... имя. И сохранить его. Если программа не содержит синтаксические ошибки, то должен получиться файл с расширением exe.


Программа:

.686; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
; но соглашения ОС Windows

.data; директива определения данных
_a dw 5; запись в 16-разрядный амбарчик памяти с именем _а числа 5
_b dw 27; запись _b = 16h
_c dw 86; запись _c = 56h
_e dw 1986; запись _e = 7c2h
_d dw 1112; запись _d = 458
res dw 0; резервирование памяти для сохранения переменной res

.code; директива начала сегмента команд
start:
mov edx, 0; очистка регистров
mov ebx, 0
mov ecx, 0
mov ах, _e; в регистр ах заносим число _e = 7c2h
mul _d; умножаем _e на _d
SHL edx, 16; делаем здвиг на 16
mov dx, ax
push edx; бросаем значение в стек
mov edx, 0
mov ах, _e
mov cx, _b
div cx; делим ах на cx
pop ecx; достаем из стека значене
sub ecx, eax; отнимает
mov ах, _a
sub eax, ecx
mov res, eax
ret; возвращение управления ОС
end start; окончание программы с именем _start

Результат работы программы:

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

mov куда, откуда - это команда перевылки

mul _d - это команда умнжения регистра ax на _d. Результат попадает в ax

shl edx, 16 - команда здвига на 16 разрядов

div cx - команда деления ах на cx. Результат попадает в ax

pop ecx - команда достает из стека значене

sub ecx, eax - команда отнимает значение ecx - eax. Результат попадает в ecx

Арифметические операции Ассемблер. Команды Ассемблера

 

Программирование арифметических выражений в языке Ассемблер происходит через некоторые команды такие, как: mul, div, sub, add. Эти команды называются командами арифметических операций.


Mul – команда умножения. Она умножает регистр ax на то, что стоит после нее. Результат заносится в ax.
Div – команда деления. Она делит регистр ax на то, что стоит после нее. Результат заносится в ax.
Add – команда сложения. Слаживает два числа. Результат заносится в первый регистр.
Sub – команда вычитания. Вычитает два числа. Результат заносится в первый регистр.

Пример: Написать программу на ассемблере вычисления выражения: а – e/b – de;
где а =5;
b =27;
c = 86;
е =1986;
d =1112;
Результат вычисления выражения сохранить в памяти. Навести значение и порядок размещения данные в памяти.

Текст программы

.686; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
; но соглашения ОС Windows

.data; директива определения данные
_a dw 5; запись в 16-разрядный амбарчик памяти с именем _а числа 5
_b dw 27; запись _b = 16h
_c dw 86; запись _c = 56h
_e dw 1986; запись _e = 7C2h
_d dw 1112; запись _d = 458
res dw 0; резервирование памяти для сохранения переменной res

.code; директива начала сегмента команд
start:
mov edx, 0; очистка регистров
mov ebx, 0; очистка регистров
mov ecx, 0; очистка регистров
mov ах, _e; в регистр ах заносим число _e = 7C2h
mul _d; множим _e и _d
SHL edx, 16; делаем здвиг на 16
mov dx, ax
push edx; бросаем значение в стек
mov edx, 0
mov ах, _e
mov cx, _b
div cx; делим ах с cx
pop ecx; достаем из стеку значения
sub ecx, eax; отнимаем
mov ах, _a
sub eax, ecx
mov res, eax
ret; возвращение управление ОС
end start; окончание программы с именем _start

Результат работы программы


Двумерные массивы на Ассемблере. Ассемблер двумерными массивами примеры

 

В данном уроке паказывается ввод элементов в двумерный массив. Приводится пример нахождение максимального элемента массива в ассемблере. И происходит вывод в MessageBox.

Пример: Ввести двумерный массив размером 7 х 4. Найти наибольший элемент двумерного массива. Удалить строку с максимальным элементом.


Текст программы:

.686; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
option casemap: none; отличие малых и больших букв
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\fpu.inc
include \masm32\include\user32.inc
include \masm32\include\msvcrt.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib
includelib \masm32\lib\fpu.lib

.data; директива определения данные
_c dd 28
sum dd 0
max dd 0; переменная для максимального числа

frmt db " %d", 0
buf db 30 dup(? )
stdout DWORD?
stdin DWORD?
cRead dd?
temp dd?
mas1 dd 28 dup(0)
nomer dd 0
st1 db " Vvesty masiv: "
st2 db " Вывод результата удаления", 0
st3 db 10 dup(0)
ifmt db " Удалена %d строка. Внимание!!! Проверьте правильность ввода, чтобы результат был правильным", 0

.code; директива начала кода программы
_start:
lea esi, mas1; загрузка адреса начала массива
mov ecx, _c
m1:
mov ebx, ecx
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov stdout, eax
invoke GetStdHandle, STD_INPUT_HANDLE
mov stdin, eax
invoke WriteConsoleA, stdout, ADDR st1, 14, NULL, NULL; VIVOD ST1
invoke ReadConsole, stdin, ADDR buf, 20, ADDR cRead, NULL; чтения числа как символ
invoke crt_atoi, ADDR buf; преобразовать символ в число
mov [esi], eax
add esi, 4
mov ecx, ebx
loop m1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov ecx, _c
lea esi, mas1; загрузка адреса начала массива
mov eax [esi]; загрузка числа
m3:
.IF(eax> max); условие
mov max, eax
add esi, 4; расчет адреса нового числа
mov eax[esi]
loop m3

.ELSE; иначе
add esi, 4; расчет адреса нового числа
mov eax[esi]
loop m3; перейти, если ecx не равен 0

.ENDIF; окончание директивы высокого уровня

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov ecx, _c
lea esi, mas1; загрузка адреса начала массива
mov eax [esi]; загрузка числа
m4:

.IF(eax == max); условие
inc nomer
add esi, 4; расчет адреса нового числа
mov eax[esi]
jmp m5
loop m4

.ELSE; иначе
inc nomer
add esi, 4; расчет адреса нового числа
mov eax[esi]
loop m4; перейти, если ecx не равен 0

.ENDIF; окончание директивы высокого уровня

m5:
.IF(nomer < = 7); условие
mov ecx, 7
x1: lea esi, mas1; загрузка адреса начала массива
mov eax [esi]; загрузка числа
mov eax, 0
add esi, 4
loop x1
mov nomer, 1
jmp end_prog
.ENDIF

.IF(nomer < =14); условие
mov ecx, 7
lea esi, mas1; загрузка адреса начала массива
xx: add esi, 4
loop xx
mov ecx, 7
x2: mov eax [esi]; загрузка числа
mov eax, 0
add esi, 4
loop x2
mov nomer, 2
jmp end_prog
.ENDIF

.IF(nomer < =21); условие
mov ecx, 7
lea esi, mas1; загрузка адреса начала массива
xx2: add esi, 4
loop xx2
mov ecx, 7
xx3: add esi, 4
loop xx3
mov ecx, 7
x3: mov eax [esi]; загрузка числа
mov eax, 0
add esi, 4
loop x3
mov nomer, 3
jmp end_prog
.ENDIF

.IF(nomer < =28); условие
mov ecx, 7
lea esi, mas1; загрузка адреса начала массива
xxx2: add esi, 4
loop xxx2
mov ecx, 7
xxx3: add esi, 4
loop xxx3
mov ecx, 7
xxx4: add esi, 4
loop xxx4
mov ecx, 7
x4: mov eax [esi]; загрузка числа
mov eax, 0
add esi, 4
loop x4
mov nomer, 4
jmp end_prog
.ENDIF

end_prog:

mov ebx, nomer

invoke wsprintf \
ADDR st3 \
ADDR ifmt \
ebx
invoke MessageBox \
NULL \
addr st3 \
addr st2 \
MB_OK
invoke ExitProcess, 0
ret
end _start; конец программы

Результат работы программы:



Ассемблер структуры. Примеры задач

 

Пример: Задана матрица 3 X 4. Определить максимальный элемент каждой строки. Результат выполнения программы вывести в окно консоли.

Текст программы:

.386; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
option casemap: none; отличие малых и больших букв
include \masm32\include\windows.inc
include \masm32\include\user32.inc
include \masm32\include\kernel32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
ExitProcess proto: dword; прототип API-функции

DATE1 STRUCT; тип данных СТРУКТУРА с именем DATE1
elem1 db? ; имя первого поля структуры
elem2 db? ; имя второго поля структуры
elem3 db? ; имя третьего поля структуры
elem4 db? ; имя четвертого поля структуры
DATE1 ENDS
.data; директива определения данные
max db 0
nomer db 1
str1 DATE1 < 20, 9, 2, 15> ; структура с именем str1
str2 DATE1 < 6, 31, 1, 3> ; структура с именем str2
str3 DATE1 < 4, 6, 155, 2> ; структура с именем str2
st1 db " Вывод максимальных чисел ", 0
st2 db 180 dup(? ), 0
stemp db 0
stemp2 db 3 dup(? ), 0
st4 db " Нажмите Ок", 0
st3 db " -й ряд имеет максимальное число = "
kol3 = $ - st3
kol4 = $ - st4
ifmt db " %d ", 0
.code; директива начала сегмента-данных
start: ; метка начала программы с именем start
xor edx, edx; заполнение нулями
mov ebx, 3; загрузка количества строк
lea esi, str1; загрузка адреса первой строки структуры
lea edi, st2
m1: mov ecx, 4; количество элементов в строке
mov max, 0
m2: mov al[esi]; загрузка элемента из строки структуры
.IF (al > max)
mov max, al
.ENDIF
jmp m4; безусловный переход, если наоборот
m3: add edx, eax; добавление негативных элементов строки структуры
m4: inc esi; подготовка адреса нового элемента
loop m2; есх: = ecx – 1 и переход на m3, если не нуль

mov eax, 0
mov al, nomer
invoke wsprintf \; API-ФУНКЦИЯ превращения числа
ADDR stemp \; адрес буфф., куда будет записан помет. символов
ADDR ifmt \; адрес строки превращения формата
eax; регистр, содержание которого превращается
mov al, stemp
mov [edi], al
inc edi

lea esi, st3
mov ecx, kol3

_m1:
mov al[esi]
mov [edi], al
inc esi
inc edi
loop _m1
mov eax, 0
mov al, max

invoke wsprintf \; API-ФУНКЦИЯ превращения числа
ADDR stemp2 \; адрес буфф., куда будет записан помет. символов
ADDR ifmt \; адрес строки превращения формата
eax; регистр, содержание которого превращается
lea esi, stemp2
mov ecx, 3
z1:
mov al[esi]
mov [edi], al
inc edi
inc esi
loop z1

mov al, 10
mov [edi], al
inc edi
inc nomer

dec ebx; ebx: = ebx – 1
.IF ebx == 2
lea esi, str2; загрузка адреса новой строки
jmp m1; переход на новый цикл
.ELSEIF ebx == 1
lea esi, str3; загрузка адреса новой строки
jmp m1; переход на новый цикл
.ENDIF

mov al, 10
mov [edi], al
inc edi
lea esi, st4
mov ecx, kol4
inc esi
_z1:
mov al[esi]
mov [edi], al
inc esi
inc edi
loop _z1

invoke MessageBox \; API-ФУНКЦИЯ выведения окна консоли
NULL \; hwnd – идентификатор окна
addr st2 \; адрес строки, который содержит текст сообщения
addr st1 \; адрес строки, который содержит заглавие сообщения
MB_OK; вид диалогового окна

invoke ExitProcess, 0; возвращение управления ОС Windows
end start; директива окончания программы с именем start


Результат работы программы:


Текст процедуры программы

.686; директива определения типа микропроцессора
.model flat; задание линейной модели памяти
option casemap: none
public _abcd
extern _a: dword, _b: dword, _c: dword, _d: dword
.code; директива начала программы
_abcd proc; ab - c/d
mov eax, _a; пересылка из памяти с именем _а в eax
mov ebx, _b; пересылка из памяти с именем _b в ebx
mul ebx; edx, eax: = eax? ebx
mov esi, eax
mov edi, edx
mov eax, _c
mov ebx, _d
xor edx, edx; подготовление к делению
div ebx
mov ecx, 0
sub ecx, edx; вычитание мелкой части из целого числа
sbb esi, eax; вычитание целой младшей части
sbb edi, 0; вычитание позычки, если она есть
ret
_abcd endp
end; директива окончания программы


Результат работы программы


Массивы в Ассемблере. Программа на Ассемблере. Пример массив элементов

 

Пример задачи с использованием массива на ассемблере: Заданы массивы Aи Виз N= 8 элементов. Сформировать новый массивСпо правилу: если у элементов Aiи Bi биты 0, 1 и 2 совпадают, то Ci= Аi + Вi.

Текст прогаммы

.686; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
; но соглашения ОС Windows
option casemap: none; отличие малых и больших букв
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\fpu.inc
include \masm32\include\user32.inc
includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\fpu.lib
ExitProcess proto: DWORD

.data; директива определения данные
st1 db " Вывод массива! С", 0
st2 db 10 dup(? ), 0
ifmt db " %d", 0
masivA db 7, 34, 34, 45, 2, 6, 2-6
masivB db 54, 2, 7, 43, 13-7, 65, 9
masivC db 8 dup(0)
work1 db 0
work2 db 0
prom dd 0

.code; директива начала кода программы
_start:
mov eax, 0
mov ebx, 0
mov ecx, 8
mov edx, 0

 

lea esi, masivA
lea edi, masivB
lea edx, masivC

M1:
mov prom, ebx
mov al, byte ptr[esi+ebx]
mov bl, byte ptr[edi+ebx]
mov work1, al
mov work2, bl
and eax, 07h
and ebx, 07h
sub eax, ebx
jz M3

M2:
mov ebx, prom
inc ebx
loop M1
jmp M4

M3:
mov al, work1
mov bl, work2
add al, bl
mov [edx+ebx], al
jmp M2

M4:
mov edx, 0
mov ecx, 08h
Prom:
lea esi, masivC;
mov al[esi+edx]
cmp al, 0
jnz _M2
_M1: inc edx
loop Prom
jmp _M3
_M2:
mov ebx, eax
jmp _M1
_M3:
invoke wsprintf \
ADDR st2 \
ADDR ifmt \
ebx
invoke MessageBox \
NULL \
addr st2 \
addr st1 \
MB_OK
invoke ExitProcess, 0
end _start; окончание программы


Результат работы программы

Макросы в Ассемблере. Примеры задач

 

Пример: Написать программу с использованием макросов для вычисления одного из выражений без предыдущего математического упрощения операций:
2x – 3 + 8(2x –3);

Текст программы:

.686; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
; но соглашения ОС Windows
option casemap: none; отличие малых и больших букв
includelib \masm32\lib\kernel32.lib
ExitProcess proto: dword; прототип API-функції
mSubB macro x, b; макрос с именем mSubB
mov al, x
shl al, 1; занос переменной а
sub al, b; вычитание а – b
mov res1, al;; сохранение результата в памяти
endm; окончание макроса
.data; директива определения данные
x db 6; сохранение в амбарчике памяти, размером в байт операнда 6
b db 3
res1 db 0; резервирование памяти для результата res1
res2 dw 0; резервирование памяти для результата res2
.code; директива начала программы
_start: ; метка начала программы с именем _start
xor eax, eax
xor ebx, ebx
mSubB [x][b]; вызов макроса
mov al, 8
mov bl, res1
mul bl
mov bl, al
mSubB [x][b]; вызов макроса
mov al, res1; занос с расширением разрядности
add bx, ax
mov res2, bx; сохранение остаточного результата
invoke ExitProcess, 0; возвращение управления ОС Windows
end _start; директива окончания программы с именем _start

Результат работы программы:


Файл 1dll.asm


.386
.model flat, stdcall
option casemap: none; отличие строчных и прописных букв
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\fpu.inc
include \masm32\include\user32.inc
include \masm32\include\msvcrt.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\msvcrt.lib
includelib \masm32\lib\fpu.lib

includelib 1.lib
ExitProcess proto: DWORD
Mas_sum proto: DWORD, : DWORD, : DWORD; прототип процедуры

.data; директива определения данные
_c dd 40
sum dd 0
op1 dd 6; запись в 32-разрядную память op1
op2 dd 22; минимальных предел
frmt db " %d", 0
buf db 30 dup(? )
stdout DWORD?
stdin DWORD?
cRead dd?
temp dd?
mas1 dd 40 dup(0)

st1 db " Vvesty masiv: "
st2 db " Вывод количества элементов в пределах (6, 22) массива! А, 0
st3 db 10 dup(0)
ifmt db " количество = %d", 0
.code; директива начала кода программы

_start:
lea esi, mas1; загрузка адреса начала массива
mov ecx, _c
m1:
mov ebx, ecx
invoke GetStdHandle, STD_OUTPUT_HANDLE
mov stdout, eax
invoke GetStdHandle, STD_INPUT_HANDLE
mov stdin, eax
invoke WriteConsoleA, stdout, ADDR st1, 14, NULL, NULL; VIVOD ST1
invoke ReadConsole, stdin, ADDR buf, 20
ADDR cRead, NULL; чтения числа как символ
invoke crt_atoi, ADDR buf; преобразовать символ в число
mov [esi], eax
add esi, 4
mov ecx, ebx
loop m1
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

mov ecx, _c
lea esi, mas1; загрузка адреса начала массива
mov eax [esi]; загрузка числа
m3:
invoke Mas_sum, op1, op2, eax
add sum, ebx
add esi, 4; расчет адреса нового числа
mov eax[esi]
loop m3

mov ebx, sum

invoke wsprintf \
ADDR st3 \
ADDR ifmt \
ebx
invoke MessageBox \
NULL \
addr st3 \
addr st2 \
MB_OK
invoke ExitProcess, 0
ret
end _start; конец программы

Результат работы программы:

Ассемблер сопроцессор. Примеры задач

 

Пример 1: Вычислить 6 значений функции: Yn = 4x/(x + 5) (х изменяется от 3 с шагом 1, 25). Результат округлить к целому, разместить в памяти и вывести на экран.


1.1 Текст программы


.386; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
option casemap: none; отличие малых и больших букв
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\fpu.inc
include \masm32\include\user32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\fpu.lib

.data; директива определения данные

_x dd 3; сохранение в 32-разрядном амбарчике памяти переменной х
_y dd 0; резервирование 32-х разрядов памяти для переменной в
tmp1 dd? ; резервирование 32-х разрядов памяти для переменной tmp1
tmp2 dd? ; резервирование 32-х разрядов памяти для переменной tmp2
hod dd 1.25
krok dd 5
_umnoj dd 4; умножение на 4
probel dd 09h; для вывода на экран
res dd 0
ifmt db " Yn = %d", 0
st1 db " Yn = 4x/(x + 5) ", 0
st2 dd 10 dup(? ), 0
st3 dd 10 dup(? ), 0

.code; директива начала кода программы
_start: ; директива начала кода программы
lea edi, st2
lea esi, st3
xor eax, eax; обнуление регистров
xor ebx, ebx
xor ecx, ecx
xor edx, edx
finit; инициирующее сопроцессора
mov ecx, 6
fild _x
m1:
fld st(0)
fiadd krok
fld st(1)
fimul _umnoj
fmul st(0), st(1)
fistp dword ptr [edi]
fistp dword ptr [esi]
fadd hod

add edi, 4

loop m1

lea edi, st2
mov eax[edi+20]

invoke wsprintf \
ADDR st2 \
ADDR ifmt \
eax
invoke MessageBox \
NULL \
addr st2 \
addr st1 \
MB_OK
invoke ExitProcess, 0
end _start; зак? нчення программы


1.2 Результат работы программы


 

 

Пример 2 : Определить номер (х) элемента функции: xn = + 5, при котором сумма элементов превысит 12 000. Результат разместить в памяти и вывести соответствующие сообщения.


2.1 Текст программы


.386; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
option casemap: none; отличие малых и больших букв
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\fpu.inc
include \masm32\include\user32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\fpu.lib

.data; директива определения данные

_x dd 0; сохранение переменной х
_y dd 0; резервирование 32-х разрядов памяти для переменной в
tmp1 dd? ; резервирование 32-х разрядов памяти для переменной tmp1
tmp2 dd? ; резервирование 32-х разрядов памяти для переменной tmp2
hod dd 1
krok dd 3
plus dd 5
mem dw?
_MASK equ 0C00h
limit dd 12000
ifmt db " № = %d", 0
st1 db " Вывод номера елемента", 0
st2 dd 10 dup(? ), 0
st3 dd 10 dup(? ), 0

.code; директива начала кода программы
_start: ; директива начала кода программы
lea edi, st2
lea esi, st3
xor eax, eax; обнуление регистров
xor ebx, ebx
xor ecx, ecx
xor edx, edx
mov edx, limit
finit; инициализация сопроцессора
fstcw mem
OR mem, _MASK
fldcw mem
mov ecx, 6
m1:
inc _x
fild _x
fild krok
fyl2x
fld st(0)
frndint
fsub st(1), st(0)
frndint
f2xm1
fiadd hod
fldz
fadd st(0), st(2)
f2xm1
fiadd hod
fmul st(0), st(1)
fiadd plus
fistp dword ptr [edi]
fistp dword ptr [esi]
fistp dword ptr [esi]
mov eax[edi]
add ebx, eax
add edi, 4
add esi, 4
cmp edx, ebx
jns m1

mov eax, _x

invoke wsprintf \
ADDR st2 \
ADDR ifmt \
eax
invoke MessageBox \
NULL \
addr st2 \
addr st1 \
MB_OK
invoke ExitProcess, 0
end _start; зак? нчення программы


2.2 Результат работы программы


 

Пример 3: Вычислить 4 значения функции: Y = 3 * log2(x2+1), x изменяется от 0, 2 с шагом 0, 3.


3.1 Текст программы


.386; директива определения типа микропроцессора
.model flat, stdcall; задание линейной модели памяти
option casemap: none; отличие малых и больших букв
include \masm32\include\windows.inc
include \masm32\include\kernel32.inc
include \masm32\include\fpu.inc
include \masm32\include\user32.inc

includelib \masm32\lib\user32.lib
includelib \masm32\lib\kernel32.lib
includelib \masm32\lib\fpu.lib

.data; директива определения данные

_x dd 0.2; сохранение в 32-разрядном амбарчике памяти переменной х
_y dd 0; резервирование 32-х разрядов памяти для переменной в
tmp1 dd? ; резервирование 32-х разрядов памяти для переменной tmp1
tmp2 dd? ; резервирование 32-х разрядов памяти для переменной tmp2
hod dd 0.3
umnoj dd 3
const dd 4
mem dw?
_MASK equ 0C00h
ifmt db " Y = %d", 0
st1 db " Вывод функции", 0
st2 dd 10 dup(? ), 0
st3 dd 10 dup(? ), 0

.code; директива начала кода программы
_start: ; директива начала кода программы
lea edi, st2
lea esi, st3
xor eax, eax; обнуление регистров
xor ebx, ebx
xor ecx, ecx
xor edx, edx
finit; инициализания сопроцессора
fstcw mem
OR mem, _MASK
fldcw mem
mov ecx, 4

fld _x
fld _x
m1:
fild umnoj
fld st(1)
fmulp st(2), st(0)
fyl2x
fld hod
fadd st(2), st(0)

fistp dword ptr [edi]
dec const
jz m2
fistp dword ptr [esi]
fld st(0)
mov eax[esi]
add edi, 4
add esi, 4
loop m1
m2:

 

invoke FpuFLtoA, 0, 10, ADDR st2, SRC1_FPU or SRC2_DIMM
invoke MessageBox, NULL, addr st2, addr st1, MB_OK
invoke ExitProcess, NULL; возвращение управления ОС Windows
; но освобождение ресурсов

end _start; директива окончания программы с именем start

Результат работы программы


Общая информация

Здесь представлена информация по ассемблеру всей серии AVR, т.к. все микроконтроллеры этой серии программно совместимы.
Ассемблер – это инструмент, с помощью которого создаётся программа для микроконтроллера. Ассемблер транслирует ассемблируемый исходный код программы в объектный код, который может использоваться в симуляторах или эмуляторах AVR. Также ассемблер генерирует код, который может быть непосредственно введен в программную память микроконтроллера.
При работе с ассемблером нет никакой необходимости в непосредственном соединении с микроконтроллером.

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

Строка программы может быть в одной из четырёх форм:

[ Метка: ] директива [операнды] [Комментарий]
[ Метка: ] команда [операнды] [Комментарий]
Комментарий
Пустая строка

Комментарий имеет следующую форму:

; [Текст]

Таким образом любой текст после символа “; ” игнорируется ассемблером и имеет значение только для пользователя.

Операнды можно задавать в различных форматах:

- десятичный (по умолчанию): 10, 255
- шестнадцатеричный (два способа): 0x0а, $0а
- двоичный: 0b00001010, 0b11111111
- восьмеричный (впереди ноль): 010, 077

 

 

Система команд

Система команд микроконтроллеров ATMEL семейства AVR очень большая и в то же время эффективная. Одной из отличительных особенностей микроконтроллеров AVR является то, что почти все команды выполняются за 1 тактовый цикл. Исключение составляют команды перехода. Это существенно увеличивает производительность микроконтроллера даже при относительно невысокой тактовой частоте.

Все команды можно классифицировать на 5 типов:
1. арифметические команды;
2. логические команды;
3. команды перехода;

· команды передачи данных;

· побитовые команды и команды тестирования битов.

 

 

Директивы ассемблера

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

Таблица 1.2.
Директивы ассемблера

Директива Описание
BYTE Зарезервировать байт под переменную
CSEG Сегмент кодов
DB Задать постоянным(и) байт(ы) в памяти
DEF Задать символическое имя регистру
DEVICE Задать для какого типа микроконтроллера компилировать
DSEG Сегмент данных
DW Задать постоянное(ые) слово(а) в памяти
EQU Установите символ равный выражению
ESEG Сегмент EEPROM
EXIT Выход из файла
INCLUDE Включить исходный код из другого файла
LIST Включить генерацию.lst - файла
NOLIST Выключить генерацию.lst - файла
ORG Начальный адрес программы
SET Установите символ равный выражению

Синтаксис всех директив следующий:
.[директива]
То есть перед директивой должна стоять точка. Иначе ассемблер воспринимает это как метку.
Дадим несколько пояснений наиболее важным директивам ассемблера

CSEG - Code segment

Директива CSEG указывает на начало сегмента кодов. Ассемблируемый файл может иметь несколько кодовых сегментов, которые будут объединены в один при ассемблировании.
Синтаксис:

.CSEG
Пример:
.DSEG; Начало сегмента данных
vartab: .BYTE 4; Резервируется 4 байта в СОЗУ
.CSEG; Начало сегмента кодов
const: .DW 2; Записать 0x0002 в программной памяти
mov r1, r0; Что-то делать

DSEG - Data Segment

Директива DSEG указывает на начало сегмента данных. Ассемблируемый файл может содержать несколько сегментов данных, которые потом будут собраны в один при ассемблировании. Обычно сегмент данных состоит лишь из директив BYTE и меток.

Синтаксис:

.DSEG
Пример:
.DSEG; Начало сегмента данных
var1: .BYTE 1; Резервировать 1 байт под переменную var1
table: .BYTE tab_size; Резервировать tab_size байтов.
.CSEG
ldi r30, low(var1)
ldi r31, high(var1)
ld r1, Z

ESEG - EEPROM Segment

Директива ESEG указывает на начало сегмента EEPROM памяти. Ассемблируемый файл может содержать несколько EEPROM сегментов, которые будут собраны в один сегмент при ассемблировании. Обычно сегмент EEPROM состоит из DB и DW директив (и меток). Сегмент EEPROM памяти имеет свой собственный счетчик. Директива ORG может использоваться для размещения переменных в нужной области EEPROM.
Синтаксис:

.ESEG

Пример:

.DSEG; Начало сегмента данных
var1: .BYTE 1; Резервировать 1 байт под переменную var1
table: .BYTE tab_size; Зарезервировать tab_size байт.
.ESEG
eevar1: .DW 0xffff; Записать 1 слово в EEPROM

Написание программы

Текст программы можно набирать в любом текстовом редакторе, такие как встроенный редактор Norton Commander, FAR, а также Microsoft Word, WordPad и других. Также можно использовать специально предназначенные для этого программы Wavrasm, AVR Studio
Для создания программы не обязательно использовать ассемблер, программное обеспечение AVR поддерживает также и язык C или С++. Но в лабораторном комплексе этот вариант не рассматривается.
Программа, написанная на ассемблере, должна иметь определенную структуру.
Предлагается следующий шаблон (для AT90S8535)

; *******************************************
; название программы,
; краткое описание, необходимые пояснения
: *******************************************
; ******подключаемые дополнительные файлы
.include “8535def.inc”; файл описания AT90S8535
.include «имя_файла1.расширение»; включение дополнительных
.include «имя_файла2.расширение»; файлов
; ******глобальные константы
.equ имя1 = xxxx;
.equ имя2 = nnnn
; ******глобальные регистровые переменные
.def имя1= регистр
.def имя2= регистр
; *******сегмент данных
.dseg
.org xxxx; адрес первого зарезервированного байта
label1: .BYTE 1; резервировать 1 байт под переменную label1
label2: .BYTE m; резервировать m байт под переменную label2
; ****** сегментEEPROM(ЭСППЗУ)
.eseg
.org xxxx; адрес первого зарезервированного байта
.db выражение1, выражение2, …; записать список байтов в EEPROM.
.dw выражение1, выражение2, …; записать список слов в EEPROM.
; ******сегмент кодов
.cseg
.org $0000; адрес начала программы в программной памяти
; ****** вектора прерываний (если они используются)
rjmp reset; прерывание по сбросу
.org $0002
rjmp INT0; обработчик прерывания IRQ0
.org $0004
rjmp INT1; обработчик прерывания IRQ1
.org adrINTx; адрес следующего обработчика прерываний
rjmp INTx; обработчик прерывания x
…….; далее по порядку располагать обработчики остальных; прерываний

; *******начало основной программы
main: < команда> xxxx
… …

; ******* подпрограммы
; *******подпрограмма 1
subr1: < команда> xxxx
…… ………. ……
ret
; *******подпрограмма 2
subr2: < команда> xxxx
…… ………. ……
ret
…………….
; ******* программы обработчиков прерываний
INT0: < команда> xxxx
…… ………. ……
reti
INT1: < команда> xxxx
…… ………. ……
reti
INTx: < команда> xxxx
…… ………. ……
reti
………………………
; конец программы никак не обозначается.

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

Как уже указывалось, программа простейшая: вычесть из числа 5 число 3. Если включен тумблер SA1 (рис. 1.2), то на индикацию выдать результат вычитания. Если тумблер SA1отключен – на индикацию вывести цифру ноль.
Алгоритм программы (рис. 1.5) соответствует программе №1, использующей директиву equ ассемблера.
Программа №2 отличается от программы №1 резервированием по одному байту оперативной памяти под семисегментные коды цифр от 0 до 9.

Программа №3 самая короткая. Она использует директиву.dw для определения слов в программной памяти. В программе используется команда LPM ассемблера. По этой команде загружается байт, адресуемый регистром Z в регистр R0. Команда обеспечивает доступ к любому байту памяти программы, организованной как 16 битное слово. Младший бит регистра Z определяет, осуществляется ли доступ к младшему байту слова (0) или к старшему (1).

Рис. 1.5 Алгоритм программы №1

; Программа №1.Использование директивы equ

.include " 8535def.inc" ; включить файл – описание для AT90S8535
.dseg; сегмент данных
.equ cod0=$64; присвоение имен ячейкам SRAM
.equ cod1=$65
.equ cod2=$66
.equ cod3=$67
.equ cod4=$68
.equ cod5=$69
.equ cod6=$6a
.equ cod7=$6b
.equ cod8=$6c
.equ cod9=$6d

.cseg
.org 0
rjmp reset
.org $30; начало программы

reset:
ldi r16, $00; определение стека с вершиной по адресу $00ff
out sph, r16
ldi r16, $ff
out spl, r16

ldi zl, $64; задание адреса начала зарезервированных ячеек
ldi zh, $00

ldi r16, $ff; настроить порт С на выход
out ddrc, r16
ldi r16, 00; настроить порт А на вход
out ddra, r16
ldi r16, $c; настроить порт В: биты 2 и 3 на выход, остальные на вход
out ddrb, r16
ldi r16, $f0; настроить порт D: биты 0...4 на вход, остальные на выход
out ddrd, r16

sbi portB, 3; выдать 1 на разряд 3 порта В

 

ldi r17, $3f; задание семисегментных кодов
sts cod0, r17
ldi r17, $06
sts cod1, r17
ldi r17, $5b
sts cod2, r17
ldi r17, $4f
sts cod3, r17
ldi r17, $66
sts cod4, r17
ldi r17, $6d
sts cod5, r17
ldi r17, $7d
sts cod6, r17
ldi r17, $07
sts cod7, r17
ldi r17, $7f
sts cod8, r17
ldi r17, $6f
sts cod9, r17

ldi r17, 5; задание уменьшаемого
ldi r18, 3; задание вычитаемого

m1: sbis pina, 4; если включен тумблер SA1, то пропустить
rjmp m2; следующую команду

mov r20, r17; в r20 поместить уменьшаемое
sub r20, r18; вычесть вычитаемое
rjmp vv
m2:
ldi r20, 0
vv:
push zl; сохранить zl в стеке
add zl, r20; сложить zl с результатом
ld r0, z; семисегментный код результата переслать в r20
pop zl; извлечь zl из стека
out portc, r0; выдать результат на индикацию
rjmp m1

; Программа №2. Использование оперативной памяти под переменные

.include " 8535def.inc" ; подключение файла описания AT90S8535

.dseg; сегмент данных
.org $64; адрес первого зарезервированного байта
cod0:.byte 1; резервирование по одному байту под переменные
cod1:.byte 1
cod2:.byte 1
cod3:.byte 1
cod4:.byte 1
cod5:.byte 1
cod6:.byte 1
cod7:.byte 1
cod8:.byte 1
cod9:.byte 1

.cseg; сегмент кодов
.org $0; адрес начала программы в программной памяти
rjmp reset; прерывание по сбросу при подаче питания
; или при нажатии на кнопку " Сброс"
reset:
ldi r16, $00; определение стека с вершиной по адресу $00ff
out sph, r16
ldi r16, $ff
out spl, r16

ldi zl, $64; задание адреса начала зарезервированных ячеек
ldi zh, $00

ldi r16, $ff; настроить порт С на выход
out ddrc, r16
ldi r16, 00; настроить порт А на вход
out ddra, r16
ldi r16, $c; настроить порт В: биты 2 и 3 на выход, остальные на вход
out ddrb, r16
ldi r16, $f0; настроить порт D: биты 0...4 на вход, остальные на выход
out ddrd, r16

sbi portb, 3; выдать 1 на разряд 3 порта В

 

ldi r17, $3f; задание семисегментных кодов
sts cod0, r17
ldi r17, $06
sts cod1, r17
ldi r17, $5b
sts cod2, r17
ldi r17, $4f
sts cod3, r17
ldi r17, $66
sts cod4, r17
ldi r17, $6d
sts cod5, r17
ldi r17, $7d
sts cod6, r17
ldi r17, $07
sts cod7, r17
ldi r17, $7f
sts cod8, r17
ldi r17, $6f
sts cod9, r17

ldi r17, 5; задание уменьшаемого
ldi r18, 3; задание вычитаемого

m1: sbis pina, 4; если включен тумблер SA1, то пропустить
rjmp m2; следующую команду

mov r20, r17; в r20 поместить уменьшаемое
sub r20, r18; вычесть вычитаемое
rjmp vv
m2:
ldi r20, 0
vv: push zl; сохранить zl в стеке
add zl, r20; сложить zl с результатом
ld r0, z; семисегментный код результата переслать в r20
pop zl; извлечь zl из стека
out portc, r0; выдать результат на индикацию
rjmp m1

; Программа №3. Использование директивы.dw

.include " 8535def.inc" ; подключение файла описания AT90S8535

.cseg
.org 0
rjmp reset
.dw $063f, $4f5b, $6d66, $077d, $6f7f, $7c77, $5e39, $7179; семисегментные коды
reset:
ldi r16, $00; определение стека с вершиной по адресу $00ff
out sph, r16
ldi r16, $ff
out spl, r16
ldi r16, $ff
out ddrc, r16; настроить порт С на выход
ldi r16, 00
out ddra, r16; настроить порт А на вход
ldi r16, $c
out ddrb, r16; настроить порт В: биты 2 и 3 на выход, остальные на вход
ldi r16, $f0
out ddrd, r16; настроить порт D: биты 0...4 на вход, остальные на выход

sbi portb, 3; выдать 1 на разряд 3 порта В

ldi zl, 02; установить адрес семисегментного кода нуля в регистр Z
ldi zh, 00

ldi r17, 5; задание уменьшаемого
ldi r18, 3; задание вычитаемого

m1: sbis pina, 4; если включен SA1, то пропустить следующую команду
rjmp m2
mov r20, r17; в r20 поместить уменьшаемое
sub r20, r18; вычесть вычитаемое
rjmp vv
m2:
ldi r20, 0; присвоить результату значение нуль
vv: push zl; сохранить zl в стеке
add zl, r20; сложить zl с результатом
lpm; загружаем бит, адресуемый регистром Z, в регистр R0
pop zl; извлечь zl из стека
out portc, r0; выдать результат на индикацию
rjmp m1

 

Ввод программы

Подготовленная программа вводится в ПЭВМ с рабочего места после включения комплекса в работу.
Включение комплекса, собранного по схеме рис.1, осуществляется включением составных частей ПЭВМ и нажатием кнопки включения на блоке питания комплекса БП. Номер рабочего места устанавливается переключателем внутри блока управления и должен совпадать с номером на блоке связи с ЭВМ.

Переключатель режима работы платы ЖКИ рабочего места необходимо поставить в положение “Программирование”. При нажатии на кнопку “Сброс” блока управления на ЖКИ появляется надпись “Место” с соответствующим номером. Этому номеру будет соответствовать окно на экране дисплея ПЭВМ.

Программное обеспечение располагается в каталоге STAND90, в котором есть два подкаталога SERVER и PK& MK. Программное обеспечение работает в среде WINDOWS. Программа SERVER запускается файлом server.bat, программа PK& MK – файлом Pk& mk.exe.
Сразу после запуска программы-сервера на экран выводится меню из трех пунктов: 1 – удалить старые файлы (например, оставшиеся после предыдущей работы), 2 – загрузить старые файлы (для продолжения работы с ними), 3 – любая другая клавиша (например, пробел или Esc) – не удалять и не загружать. После нажатия соответствующей клавиши на экране появляется восемь окон.
Пример содержимого экрана ПЭВМ во время работы программы представлен на рис. 1.6.

Каждое окно имеет надпись “Место” с номером рабочего места и область размером 4х16 знакомест, в которой отображается информация, выводимая на ЖКИ соответствующего рабочего места. Внизу экрана имеется строка, в которой описаны “горячие” клавиши – клавиши и сочетания клавиш, нажатия на которые вызывают определенные действия.


Поделиться:



Популярное:

Последнее изменение этой страницы: 2016-03-22; Просмотров: 8295; Нарушение авторского права страницы


lektsia.com 2007 - 2024 год. Все материалы представленные на сайте исключительно с целью ознакомления читателями и не преследуют коммерческих целей или нарушение авторских прав! (0.173 с.)
Главная | Случайная страница | Обратная связь