ЗАНЯТТЯ № 2
Написання простих програм на мові асемблер
для мікроконтролера AT90S2313
1. Написати підпрограму додавання вмісту двох 8-розрядних комірок SRAM, що знаходяться за адресами Addr_1 = 90Н, Addr_2 = 91Н. Якщо результат не рівний нулю, помістити його в комірку пам’яті SRAM з адресою Addr_1, в протилежному випадку вивести результат в порт B.
; Заголовочний файл
.include <2313def.inc>
; Адреса першого числа
.equ Addr_1 = 0x90
; Адреса другого числа
.equ Addr_2 = 0x91
; Початок сегменту коду
.cseg
; Початкова адреса
.org 0
; Вектор скиду
RESET:
ldi r16, 0xDF
out SPL, r16 ; Ініціалізація стеку
rcall PP_1 ; Виклик підпрограми додавання
Wait:
rjmp Wait ; Вічний цикл
PP_1: ; Початок підпрограми додавання
lds r0, Addr_1 ; Завантажити в регістр r0 перше число
lds r1, Addr_2 ; Завантажити в регістр r1 друге число
add r0, r1 ; Додати два числа, результат занести в r0
breq L1 ; Якщо результат = 0, перейти на мітку L1
sts Addr_1, r0 ; Інакше зберегти результат за адресою Addr_1
rjmp L2 ; Перейти на мітку L2
L1: out PORTB, r0 ; Вивести результат в порт В
L2:
ret ; Повернення з підпрограми
2. Написати підпрограму переводу безнакового цілого числа розташованого в комірці пам’яті SRAM з адресою Addr = 80Н, в двійково-десятковий код. Результат розташувати в регістрах загального призначення.
; Заголовичний файл
.include <2313def.inc>
; Адреса числа, яке потрібно перевести в двійково-десятковий код
.equ Addr = 0x80
; Робочі регістри
.def temp1 = r16
.def temp2 = r17
; Регістри сотень, десятків та одиниць
.def LCD_100 = r20
.def LCD_10 = r21
.def LCD_1 = r22
; Сегмент коду
.cseg
; Початкова адреса 0
.org 0
; Вектор скиду
RESET:
ldi temp1, RAMEND ; RAMEND = 0xDF (див. файл 2313def.inc)
out SPL, temp1 ; Ініціалізація стеку
ldi temp1, 125 ; Нехай потрібно перевести число 125
sts Addr, temp1 ; Зберегти 125 за адресою Addr
rcall Bin_to_Dec ; Виклик підпрограми переводу
Wait:
rjmp Wait ; Вічний цикл
Bin_to_Dec:
lds temp1, Addr ; Занести число для переводу в temp1
ldi LCD_100, -1 ; Присвоїти LCD_100 = -1
B2: inc LCD_100 ; LCD_100 = LCD_100 + 1
subi temp1, 100 ; temp1 = temp1 - 100
brsh B2 ; Якщо temp1 >= 0 перейти на В2
ldi LCD_10, 100 ; Відновити temp1
add temp1, LCD_10 ; temp1 = temp1 + 100
ldi LCD_10, -1 ; Присвоїти LCD_10 = -1
B3: inc LCD_10 ; LCD_10 = LCD_10 + 1
subi temp1, 10 ; temp1 = temp1 - 10
brsh B3 ; Якщо temp1 >= 0 перейти на В3
ldi temp2, 10 ; Відновити temp1
add temp1, temp2 ; temp1 = temp1 + 10
mov LCD_1, temp1 ; Занести в LCD_1 кількість одиниць
ret ; Повернення з підпрограми
3. Написати підпрограму переводу двійково-десяткового числа розташованого в комірці пам’яті SRAM з адресою Addr = С0Н в двійковий код. Результат розташувати в регістрі загального призначення.
; Заголовочний файл
.include <2313def.inc>
; Адреса числа, яке потрібно перевести в двійковий код
.equ Addr = 0xC0
; Робочі регістри
.def temp1 = r16
.def temp2 = r17
; Регістр результату
.def result = r18
; Сегмент коду
.cseg
; Початкова адреса
.org 0
; Вектор скиду
RESET:
ldi temp1, RAMEND
out SPL, temp1 ; Ініціалізація стеку
ldi temp1, 0b01010111
sts Addr, temp1 ; Занести в Addr число 57 (2-10)
rcall BCD_BIN ; Виклик підпрограми переводу 2-10->2
Wait:
rjmp Wait ; Вічний цикл
; Підпрограма переводу з 2-10 коду в двійковий
BCD_BIN:
ldi result, 0 ; Обнулити регістр результату (result = 0)
lds temp1, Addr ; Завантажити в temp1 вміст Addr (temp1 = 0b01010111)
mov result, temp1 ; Переслати 0b01010111 в result
andi result, 0b00001111 ; Виділити одиниці: result = 0b00000111
swap temp1 ; Поміняти місцями тетради: temp1 = 0b01110101
andi temp1, 0b00001111 ; Виділити десятки: result = 0b00000101
ldi temp2, 10 ; temp2 = 10
L1:
subi temp1, 1 ; temp1 = temp - 1
brlo L2 ; Якщо temp1 < 0 перейти на L2
add result, temp2 ; Інакше result = result + 10
rjmp L1 ; Перейти на початок циклу L1
L2: ret ; Повернення з підпрограми
4. Написати підпрограму формування часової затримки тривалістю 3 с. Тактова частота мікроконтролера 8 МГц.
Підпрограма часової затримки будується так:
А. Розраховується кількість тактів необхідна для формування заданого часового інтервалу при тактовій частоті :
.
Б. Складаємо рівняння з врахуванням того, що команда виклику підпрограми виконується за 3 такти, повернення з підпрограми – 4 такти, завантаження регістрів лічби – 1 такт (всього 3 команди), а тіло циклу виконується раз і складається з 5 тактів: – 1 такт, – 1 такт (2 команди), – 2 такти (за винятком одного разу коли здійснюється вихід з циклу – тоді 1 такт). Отже будемо мати
В. Для того щоб рівняння мало цілий розв’язок необхідно в п/п затримки додати одну холосту команду (no operation) тривалістю 1 такт. Тоді наше рівняння запишеться:
Таким чином в циклі, який починається міткою Delay відбувається віднімання від числа , розташованого в регістрах Delay1-Delay3 одиниці, поки воно не стане менше 0, після чого здійснюється вихід з циклу. Ознакою того, що число стало менше 0, є встановлення прапорця позики С в регістрі стану SREG (перевіряється командою ).
; Заголовочний файл
.include <2313def.inc>
; Константа часової затримки: 4 + 3 + 3 + 5*(N + 1) - 1 + 1 = FCLK*T
.equ T_3sec = (24000000 - 15)/5 ; 3 секунди
; Робочий регістр
.def temp = r16
; Регістри затримки
.def Delay1 = r17
.def Delay2 = r18
.def Delay3 = r19
; Сегмент коду
.cseg
; Початкова адреса
.org 0
; Вектор скиду
RESET:
ldi temp, RAMEND
out SPL, temp ; Ініціалізація стеку
ldi Delay1, low(T_3sec) ; Занести в Delay1 1-й байт T_3sec
ldi Delay2, high(T_3sec) ; Занести в Delay2 2-й байт T_3sec
ldi Delay3, byte3(T_3sec) ; Занести в Delay3 3-й байт T_3sec
rcall Delay ; Виклик підпрограми затримки
Wait:
rjmp Wait ; Вічний цикл
Delay:
;Підпрограма часової затримки
subi Delay1, 1 ; Відняти від T_3sec
sbci Delay2, 0
sbci Delay3, 0
brcc Delay ; Якщо результат >= 0 перейти на Delay
nop ; Холоста команда
ret ; Повернення з підпрограми
5. Написати підпрограму додавання двох масивів з адресами Array1 та Array2, розміром 10 байт. Результат додавання записати на місце першого масиву.
; Заголовочний файл
.include <2313def.inc>
; Константа - розмір масиву
.equ Array_Size = 10
; Лічильник
.def Count = r16
; Робочі регістри
.def temp1 = r17
.def temp2 = r18
; Сегмент коду
.cseg
; Початкова адреса
.org 0
; Вектор скиду
RESET:
ldi temp1, RAMEND
out SPL, temp1 ; Ініціалізація стеку
ldi XL, low(Array1) ; Занести в індексний рег. Х адресу
ldi XH, high(Array1) ; першого масиву
ldi YL, low(Array2) ; Занести в індексний рег. Y адресу
ldi YH, high(Array2) ; другого масиву
ldi Count, Array_Size ; Занести в лічильник розмір масиву
rcall Array_Sum ; Виклик підпрограми додавання масивів
Wait:
rjmp Wait ; Вічний цикл
Array_Sum:
ld temp1, X ; Занести в temp1 елемент масиву Array1
ld temp2, Y+ ; Занести в temp2 елемент масиву Array2
add temp1, temp2 ; Додати два елементи
st X+, temp1 ; Зберегти суму в масиві Array1
dec Count ; Зменшити лічильник на 1
brne Array_Sum ; Поки Count не 0, перейти на Array_Sum
.dseg
Array1: .byte 10 ; Резервування пам'яті для масиву Array1
Array2: .byte 10 ; Резервування пам'яті для масиву Array1
6. Написати підпрограму знаходження максимального числа у масиві 8-розрядних беззнакових чисел в SRAM з адресою Array, розміром 10 байт. Максимальне число запам’ятати в комірці SRAM з адресою uMax, його адресу в комірках Max_Addr і Max_Addr + 1.
; Заголовочний файл
.include <2313def.inc>
; Константа - розмір масиву
.equ Array_Size = 10
; Лічильник
.def Count = r16
; Робочі регістри
.def temp1 = r17
.def temp2 = r18
; Сегмент коду
.cseg
; Початкова адреса
.org 0
; Вектор скиду
RESET:
ldi temp1, RAMEND
out SPL, temp1 ; Ініціалізація стеку
ldi XL, low(Array) ; Занести в індексний рег. Х
ldi XH, high(Array) ; адресу масиву Array
; Ініціалізація масиву для тестування підпрограми
ldi temp1, 3
st X+, temp1
ldi temp1, 5
st X+, temp1
ldi temp1, 7
st X+, temp1
ldi temp1, 8
st X+, temp1
ldi temp1, 129
st X+, temp1
ldi temp1, 200
st X+, temp1
ldi temp1, 220
st X+, temp1
ldi temp1, 110
st X+, temp1
ldi temp1, 77
st X+, temp1
ldi temp1, 34
st X+, temp1
ldi XL, low(Array) ; Занести в індексний рег. Х
ldi XH, high(Array) ; адресу масиву Array
ldi Count, Array_Size - 1 ; Занести в Count число 9
rcall Array_uMax ; Виклик підпрограми пошуку максимуму
Wait:
rjmp Wait ; Вічний цикл
Array_uMax:
ld temp1, X+ ; Занести перший елемент масиву в temp1
Loop:
ld temp2, X ; Занести наступний елемент масиву в temp2
cp temp2, temp1 ; Порівняти: temp2 - temp1
brlo L1 ; Якщо temp2 < temp1 перейти на L1
mov temp1, temp2 ; Інакше занести в temp1 значення temp2
sts Max_Addr, XL ; Зберегти адресу temp2
sts Max_Addr+1, XH ; в комірках Max_Addr, Max_Addr+1
L1:
adiw XL, 1 ; Вказати на наступний елемент
dec Count ; Зменшити Count на 1
brne Loop ; Якщо Count не нуль, перейти на Loop
sts uMax, temp1 ; Зберегти масимальне значення в uMax
ret ; Повернення з підпрограми
; Сегмент даних
.dseg
Array: .byte 10 ; Виділення пам'яті для масиву
uMax: .byte 1 ; Комірка для зберігання макс. значення
Max_Addr: .byte 2 ; Адреса максимального значення
7. Написати підпрограму знаходження максимального числа у масиві 8-розрядних знакових чисел в SRAM з адресою Array, розміром 10 байт. Максимальне число запам’ятати в комірці SRAM з адресою sMax, його адресу в комірках Max_Addr і Max_Addr + 1.
; Заголовочний файл
.include <2313def.inc>
; Константа - розмір масиву
.equ Array_Size = 10
; Лічильник
.def Count = r16
; Робочі регістри
.def temp1 = r17
.def temp2 = r18
; Сегмент коду
.cseg
; Початкова адреса
.org 0
; Вектор скиду
RESET:
ldi temp1, RAMEND
out SPL, temp1 ; Ініціалізація стеку
ldi XL, low(Array) ; Занести в індексний рег. Х
ldi XH, high(Array) ; адресу масиву Array
; Ініціалізація масиву для тестування підпрограми
ldi temp1, 3
st X+, temp1
ldi temp1, 5
st X+, temp1
ldi temp1, 7
st X+, temp1
ldi temp1, 8
st X+, temp1
ldi temp1, 129
st X+, temp1
ldi temp1, 200
st X+, temp1
ldi temp1, 220
st X+, temp1
ldi temp1, 110
st X+, temp1
ldi temp1, 77
st X+, temp1
ldi temp1, 34
st X+, temp1
ldi XL, low(Array) ; Занести в індексний рег. Х
ldi XH, high(Array) ; адресу масиву Array
ldi Count, Array_Size - 1 ; Занести в Count число 9
rcall Array_sMax ; Виклик підпрограми пошуку максимуму
Wait:
rjmp Wait ; Вічний цикл
Array_sMax:
ld temp1, X+ ; Занести перший елемент масиву в temp1
Loop:
ld temp2, X ; Занести наступний елемент масиву в temp2
cp temp2, temp1 ; Порівняти: temp2 - temp1
brlt L1 ; Якщо temp2 < temp1 перейти на L1
mov temp1, temp2 ; Інакше занести в temp1 значення temp2
sts Max_Addr, XL ; Зберегти адресу temp2
sts Max_Addr+1, XH ; в комірках Max_Addr, Max_Addr+1
L1:
adiw XL, 1 ; Вказати на наступний елемент
dec Count ; Зменшити Count на 1
brne Loop ; Якщо Count не нуль, перейти на Loop
sts sMax, temp1 ; Зберегти масимальне значення в sMax
ret ; Повернення з підпрограми
; Сегмент даних
.dseg
Array: .byte 10 ; Виділення пам'яті для масиву
sMax: .byte 1 ; Комірка для зберігання макс. значення
Max_Addr: .byte 2 ; Адреса максимального значення
8. Написати п/п кодування циклічним кодом з твірним поліномом 3-розрядної інформаційної послідовності. Продемонструвати роботу п/п для інформаційної послідовності 110.
Побудуємо твірну матрицю шляхом ділення одиниці з 5 нулями на твірний поліном:
Твірна матриця:
Контрольні розряди згідно твірної матриці:
Закодована комбінація має бути:
; Заголовочний файл
.include <2313def.inc>
; Закодована комбінація
.def CRC = r16
; Робочі регістри
.def temp1 = r17
; Сегмент коду
.cseg
; Початкова адреса
.org 0
; Вектор скиду
RESET:
ldi temp1, RAMEND
out SPL, temp1 ; Ініціалізація стеку
ldi CRC, 0b00000110 ; Занести в рег. CRC дані для кодування
rcall CRC_Calc ; Виклик підпрограми кодування циклічним кодом
Wait:
rjmp Wait ; Вічний цикл
CRC_Calc:
ldi ZL, low(CRC_Table*2) ; Занести в інд. рег. Z адресу
ldi ZH, high(CRC_Table*2) ; таблиці CRC_Table в Flash-пам'яті програм
lsl CRC ; Звільнити молодші 3 біти
lsl CRC ; для контрольних розрядів
lsl CRC ; CRC=0 0 0 I3 I2 I1 K3 K2 K1
mov temp1, CRC ; Занести в temp1 вміст CRC
andi temp1, 0b00001000 ; Виділити біт I1
breq L1 ; Якщо I1 = 0 перейти на L1
lpm ; Інакше прочитати залишок з CRC_Table в r0
eor CRC, r0 ; Додати залишок до CRC
L1: adiw Z, 1 ; Вказати на наступний залишок
mov temp1, CRC ; Занести в temp1 вміст CRC
andi temp1, 0b00010000 ; Виділити біт I2
breq L2 ; Якщо I2 = 0 перейти на L2
lpm ; Інакше прочитати залишок з CRC_Table в r0
eor CRC, r0 ; Додати залишок до CRC
L2: adiw Z, 1 ; Вказати на наступний залишок
mov temp1, CRC ; Занести в temp1 вміст CRC
andi temp1, 0b00100000 ; Виділити біт I3
breq L3 ; Якщо I3 = 0 перейти на L3
lpm ; Інакше прочитати залишок з CRC_Table в r0
eor CRC, r0 ; Додати залишок до CRC
L3: ret ; Поверення з підпрограми
; Твірна таблиця для поліному х^3 + x + 1
CRC_Table: .db 0b00000011, 0b00000110, 0b00000111