Спойлер: ця стаття не видавить ностальгічної сльози.
Вирішивши не так давно познайомитися ближче (після Arduino) з мікроконтролерами взагалі
і з мікроконтролерами ATmega зокрема, я швидко зрозумів, що просто читати про МК,
проганяти код в емуляторі, «моргати світлодіодом» - це якось не те. Тому я вирішив
«запиляти» який-небудь - нехай навіть марний - проект, і розбиратися з усім
вже по ходу справи. На думку прийшли лабораторні роботи з мікропроцесорних систем
і навчальний стенд, на якому ці роботи виконувалися - так званий навчальний мікропроцесорний
комплект (УМК), який у нас звався просто «Умкою».
Про УМК
Отже, якщо не вдаватися в подробиці (якщо вдаватися - то за посиланнями нижче), то
«Умка» це мікро-ЕВМ на базі мікропроцесора К580ИК80А, випускався у 80-х роках
минулого століття (мені ж пощастило працювати з ним у не зовсім далекому 2009 році),
представляє з себе вагомий такий ящик. Більш детальну інформацію про історію «Умок»
та іншим можна знайти за другим посиланням нижче. Ось так він виглядає.
(Фото нахабно взято за першим посиланням нижче, більше фото - за тим же
посилання)
Що тут є: індикація шин даних та адреси, індикація регістру статусу процесора,
дисплей для показу вмісту комірок пам'яті і даних, що вводяться, кнопки вводу
даних. Два стовпчики кнопок лівіше - це так звані директиви вшитої в «Умку»
програми «Монітор» - вони-то і стали метою мого проекту (директиви, не кнопки).
Коротко про директиви. Тут нічого особливого немає.
«П» - перегляд вмісту комірки пам'яті: вводимо адресу комірки, дивимося, що там
лежить, при бажанні - записуємо інше значення.
«РГ» - перегляд вмісту регістрів: подібно до перегляду вмісту комірок. У моїй
«версії» ця директива відсутня: в ній немає особливої необхідності, тому що в
ATmega ОЗУ, регістри загального призначення та регістри периферійних пристроїв знаходяться в
одному адресному просторі, і передбачається, що до них можна звернутися через
директиву «П».
«КС» - підрахунок контрольної суми масиву даних: вводимо адресу початку масиву,
кінцеву адресу масиву, сума вмісту комірок масиву з'являється на дисплей.
«ЗК» - заповнення масиву пам'яті константою: вводимо початкову адресу, вводимо кінцеву
адреса масиву, вводимо змінну - комірки масиву заповнюються цією змінною.
«ПМ» - переміщення масиву пам'яті: вводимо початкову адресу масиву
пам'яті, вводимо його кінцеву адресу, вводимо початкову адресу розміщення масиву - масив
пересувається в область пам'яті, що починається з введеної адреси розміщення.
Директиву «СТ» - виконання програми користувача - я пропустив, про це нижче.
Кнопка "ВП" - виконання директиви, кнопка "_" "розділяє змінні при введенні", РБ "- кнопка
скидання. Детальніше про введення і виконання директив - тут: практикум.
sfrolov.livejournal.com/136290.html - фото УМК, спогади, враження.
www.computer-museum.ru/histussr/umk_sorucom_2011.htm - трохи історії.
Що планувалося
Метою мого проекту було створення пристрою, здатного виконувати ці директиви і
виводити інформацію на дисплей, така собі lite-версія «Умки» (ну дуже-дуже lite).
Що використовувалося в проекті:
1. Мікроконтролер ATmega16. Програму написано на асемблері в AVRStudio.
2. Матрична клавіатура для введення значень. Код для роботи з клавіатурою я знайшов
тут (спасибі автору), потім просто переписав його на асемблері. Оскільки ОЗУ ATmega16
обмежується адресою $045F, вводяться три останні цифри. До речі, дані/адреси
вводяться і виводяться в шістнадцятковому форматі.
3. Дисплей LCD 16х2 на контролері HD44780. Розібратися в роботі дисплея допомогли ці
дві статті - ось і ось (спасибі авторам), правильно ж ініціалізувати дисплей допоміг даташит.
4. Прості кнопки для директив. Також була додана ще одна кнопка - «Enter» (на
фото вона зі стрілочкою): потрібна для підтвердження введення кожної цифри адреси або даних
- такий захист від дребезу контактів матричної клавіатури (без якої цілком можна
було обійтися, але розумна думка, як кажуть, приходить опісля).
Що в підсумку
Вдалося досягти всього, що було заплановано: можна переглядати і змінювати
вміст ОЗУ і регістрів МК, записувати константу в масив комірок, підраховувати
контрольну суму масиву, переміщати масив комірок (ось розвага-то * sarcastic *);
запис/пересування масиву куди-небудь за адресу $0440 (приблизно) вносить закономірний
хаос в область стека. Можна виконати аж шість пунктів першої лабораторної роботи з
практикуму за посиланням вище.
Судячи зі знайденої інформації, програма «Монітор», зашита в «Умку», займає 1Кбайт.
Одним із завдань було укласти програму в 1 Кбайт, що успішно вдалося (навіть з
запасом), але через LCD-дисплей і бажання зробити все більш інформативним код
збільшився практично вдвічі.
Справжня «Умка» (professional edition) дозволяла написати свою програму на
асемблері мікропроцесора К580ИК80А, записуючи в пам'ять коди відповідних команд,
і потім виконувати її або покроково, або циклічно. Я не знаю, як це було
реалізовано в «Умці», і рішення не здається мені очевидним, тому такий опції в
проекті немає (для початку, втім, мені вистачило і директив), із задоволенням прийму
пропозиції щодо реалізації цього. За це я ласкаво прозвав пристрій «Недоумкою».
Це код
.include «m16def.inc»
.device ATmega16
.def count = r18; r18 - лічильник
.def mode = r19; r19 - ознаки режимів
.def buf = r25; для введення значень
.def rLCD = r22; для роботи з LCD
.def rKey = r20; значення з клавіатури і по дрібниці
; команди LCD
.equ off = 0b00001000 ;off
.equ clrSc = 0b00000001 ;clear
.equ config = 0b00111000; 8bit, 2 lines
.equ incr = 0b00000110; addr + (збільшення адреси, статичний екран)
.equ on = 0b00001100 ;on
.equ right = 0b00010100; зсув праворуч
.equ down = 0b11000000; перейти на другий рядок
.equ up = 0b10000000; на перший рядок
.equ cursor = 0b00001111; миготливий курсор
.equ noCursor = 0b00001100
.equ left = 0b00010000; ліворуч
; дані LCD (порт D)
.equ _dp = DDRD
.equ _dpo = PORTD
.equ _dpi = PIND
; керування LCD (порт C)
.equ _cp = DDRC
.equ _cpo = PORTC
.equ rs = 0
.equ rw = 1
.equ e = 7
; клавіатура (порт A)
.equ _mp = DDRA
.equ _mpi = PINA
.equ _mpo = PORTA
; директиви (порт B)
.equ BUT = PINB
.equ BUTddr = DDRB
.equ BUTp = PORTB
.equ P = 0; кнопка "П" (режим перегляду/зміни вмісту пам "яті)
.equ quit = 1; вихід
.equ exe = 2; кнопка ВП (виконання)
.equ enter = 3; кнопка вводу
.equ space = 4; кнопка «_»
.equ CF = 5; кнопка ЗК (заповнення константою)
.equ CS = 6; кнопка КС (контрольна сума)
.equ AM = 7; кнопка ПМ (пересування масиву)
; Прапор Т - режим введення даних
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
.macro matrix
rcall _exit
ldi rKey, 0
ldi r16, 0xff; завантаження одиниць у r16
cbi _mpo, @0; очистити біт, відповідний стовпчику
out _mpo, r16; одиниці в portd
in r16, _mpi; читання регістру даних порту D
cpi r16, @1
breq ein
cpi r16, @2
breq zwei
cpi r16, @3
breq drei
cpi r16, @4
breq vier
sbi _mpo, @0; виставити біт, відповідний стовпчику (повернення у вих. стан)
rjmp @9; якщо жодна з кнопок у стовпчику не була натиснута,
; переходимо на мітку - макрос з параметрами наступного стовпчика або на початок опитування
ein:
ldi rKey, @5; завантаження у rKey відповідного натиснутої кнопки значення
ldi rLCD, @5; завантаження того ж значення для виводу на екран
rcall _BF
rcall _charInput
sbi _mpo, @0; виставити біт, відповідний стовпчику (повернення у вих. стан)
rjmp _check; перейти до мітки view
zwei:
ldi rKey, @6
ldi rLCD, @6
rcall _BF
rcall _charInput
sbi _mpo, @0
rjmp _check
drei:
ldi rKey, @7
ldi rLCD, @7
rcall _BF
rcall _charInput
sbi _mpo, @0
rjmp _check
vier:
ldi rKey, @8
ldi rLCD, @8
rcall _BF
rcall _charInput
sbi _mpo, @0
rjmp _check
.endmacro
.cseg
.org 0
; стек
ldi r16, Low(RAMEND)
out SPL, r16
ldi r16, High(RAMEND)
out SPH, r16
sbi _cp, 0 ;rs
sbi _cp, 1 ;rw
sbi _cp, 7 ;e
; ініціалізація портів
ldi r16, 0b00001111
out _mp, r16; DDRA - вхід
ldi r16, 0b11110000
out _mpo, r16; з підтяжкою
ldi r16, 0x00
out BUTddr, r16; DDRB - вхід
ldi r16, 0xff
out BUTp, r16; з підтяжкою
; ініціалізація дисплея
rcall altInit
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
go:
rcall _resetLCD
ldi xl, 0x00
ldi xh, 0x00
ldi yl, 0x00
ldi yh, 0x00
ldi zl, 0x00
ldi zh, 0x00
clt
clc
clh
clr mode
clr buf
ldi count, 1
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
modeCheck:
sbis BUT, P; Режим П
rjmp _P
sbis BUT, CF; режим ЗК
rjmp _setConst
sbis BUT, CS; режим КС
rjmp _setSum
sbis BUT, AM; режим ПМ
rjmp _setArray
rjmp modeCheck
_setConst:
sbr mode, 1; встановлення ознак режиму ЗК
; вивід на екран інформації про режим
rcall _clear
rcall _labelWrite; write
rcall _labelSpace; _
rcall _labelConst; const
rcall _point;.
rcall _down
rcall _labelAddr; address
rcall _labelOne; 1
rcall _colon;:
rcall _labelZero; 0
rcall _cursor
rjmp scan
_setSum:
sbr mode, 5; встановлення ознак режиму КС
rcall _clear
rcall _labelSum; checksum
rcall _down
rcall _labelAddr; address
rcall _labelOne; 1
rcall _colon;:
rcall _labelZero; 0
rcall _cursor
rjmp scan
_setArray:
sbr mode, 9; встановлення ознаки режиму ПМ
rcall _clear
rcall _mArray
rcall _down
rcall _labelAddr
rcall _labelOne
rcall _colon
rcall _labelZero
rcall _cursor
rjmp scan
_check:; після введення цифри - сюди
sbrc mode, 0; перевіряємо режим
rjmp _const; якщо ЗК, КС або ПМ, то сюди
rjmp _view
_P:; Режим П
rcall _clear
rcall _browse
rcall _labelSpace
rcall _labelZero
rcall _cursor
scan:; опитування матричної клавіатури
rcall _exit
rcall _cursor
matrix 0, 0b11101110, 0b11011110, 0b10111110, 0b01111110, 3, 7, 11, 15, next
next:
matrix 1, 0b11101101, 0b11011101, 0b10111101, 0b01111101, 2, 6, 10, 14, next1
next1:
matrix 2, 0b11101011, 0b11011011, 0b10111011, 0b01111011, 1, 5, 9, 13, next2
next2:
matrix 3, 0b11100111, 0b11010111, 0b10110111, 0b01110111, 0, 4, 8, 12, scan
rjmp scan
; ==========================================================================
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
_view:
brts _input; переходимо на input, якщо Т-прапор встановлено (тобто режим введення даних)
rcall _exit
sbic BUT, enter; якщо не натиснуто кнопку вводу,
rjmp _view; то замикаємо цикл і очікуємо натискання кнопок ВИП., скидання або введення
cpi count, 1; порівняння значення лічильника: якщо 1,
breq one; то вводиться перша цифра
cpi count, 2; якщо 2,
breq two; то вводиться друга
cpi count, 3; якщо 3,
breq three; то вводиться остання цифра
rjmp _view
; ввід першої цифри адреси
one:
mov xh, rKey
inc count
rjmp scan; перехід до опитування доль. клав.
; введення другої цифри адреси
two:
mov xl, rKey
swap xl
inc count
rjmp scan
; введення третьої цифри адреси
three:
rcall _noCursor; курсор не потрібен
add xl, rKey
inc count
rjmp finish; після введення третьої ццфри, перейти до finish
;==================================================================================
;================================== режим ввода ================================
_input:
rcall _exit
sbic BUT, enter; якщо натиснути кнопку вводу, то перестрибуємо на порівняння лічильника
rjmp _input
cpi count, 1; якщо лічильник дорівнює 1, то вводиться перша цифра
breq first
cpi count, 2; якщо 2, то друга
breq second
rjmp _input
first:; ввід першої цифри
mov buf, rKey
swap buf
inc count
rjmp scan
second:; введення другої цифри
rcall _noCursor
add buf, rKey
inc count
rjmp _waitFor
;=================================================================================
= = = = = = = = = = = = = = = = = = = = = = = = = = = = =
finish:; переходимо сюди після введення третьої цифри
brts link; якщо Т-прапор виставлено, то це режим введення даних, - переходимо на scan
rcall _exit
sbis BUT, exe; кнопка ВИП.
rjmp view; перейти на view, якщо натиснуто ВИП.
sbic BUT, space; кнопка «_»
rjmp finish
ldi count, 1; скидаємо лічильник
set; виставити Т-прапор у регістрі статусу
; вивести в першому рядку WRITE DATA
rcall _clear
rcall _labelWrite
rcall _labelSpace
rcall _labelData
; перейти на другу,
rcall _down
; встановити курсор
rcall _cursor
rjmp finish
link:
rjmp scan
; ============================================================================
; = = = = = = = = = = = = = перегляд вмісту комірки (після натискання кнопки ВИП. - > сюди)
view:
; вивід напису «BROWSE XXXX»
rcall _clear
rcall _browse; BROWSE
rcall _labelSpace; _
mov rLCD, xh; перші XX
rcall _charFromMemory
mov rLCD, xl; другі XX
rcall _charFromMemory
; вивід вмісту комірки пам "яті
rcall _noCursor
rcall _down
ld rLCD, X+
rcall _charFromMemory
view1:
rcall _exit
ldi count, 1
sbic BUT, space; Якщо ви натиснете кнопку «_», ви зможете перейти до комірки з адресою 1 більше
rjmp view1
rcall _delay1
rjmp view
; =============================================================================
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
input:
st X, buf
clt; очищаємо Т-прапор після завершення режиму введення даних
rjmp go
;==================================================================================
; = = = = = = = = = очікування на ВИП. або «» _ «» для продовження вводу = = = = = = = = = = = = = = = = = =
_waitFor:; після введення другої цифри (дані) - сюди
sbrc mode, 0
rjmp _WC
rcall _exit
sbis BUT, exe; Якщо ви натиснете кнопку ВИП., програма переходить на input
rjmp input
sbic BUT, space; якщо натиснути «_»,
rjmp _waitFor; то перестрибуємо це,
st X+, buf; пишемо в пам'ять введені дані,
ldi count, 1; скидаємо лічильник
rcall _clear
rcall _labelWrite
rcall _labelSpace
rcall _labelData
rcall _down
rjmp scan
_WC:
rcall _exit
sbic BUT, exe
rjmp _WC
rjmp _execConst
; ==============================================================================
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
_const:
brts to_input
rcall _exit
sbic BUT, enter; якщо не натиснуто кнопку вводу,
rjmp _const; то замикаємо цикл і очікуємо натискання кнопок ВИП., скидання або введення
cpi count, 1; порівняння значення лічильника: якщо 1,
breq _one; то вводиться перша цифра
cpi count, 2; якщо 2,
breq _two; то вводиться друга
cpi count, 3; якщо 3,
breq _three; то вводиться остання цифра
rjmp _const; до циклу
to_input:
rjmp _input
; введення першої цифри першої адреси
_one:
sbrc mode, 4; якщо виставлено 2-й біт (ознака введення третьої адреси),
rjmp _ad31; < - рухаємо сюди
sbrc mode, 1; якщо не виставлений 1-й біт в r19 (ознака введення другої адреси),
rjmp inpConst1; то пропускаємо це
mov xh, rKey
inc count
rjmp scan
; ввід першої цифри другої адреси
inpConst1:
mov yh, rKey
inc count
rjmp scan
; введення другої цифри першої адреси
_two:
sbrc mode, 4; якщо виставлено 2-й біт (ознака введення третьої адреси),
rjmp _ad32; < - рухаємо сюди
sbrc mode, 1; якщо виставлено 1-й біт у r19 (ознака введення другої адреси),
rjmp inpConts2; то переходимо на inpConst1
mov xl, rKey
swap xl
inc count
rjmp scan
; введення другої цифри другої адреси
inpConts2:
mov yl, rKey
swap yl
inc count
rjmp scan
; введення третьої цифри першої адреси
_three:
sbrc mode, 4; якщо виставлено 2-й біт (ознака введення третьої адреси),
rjmp _ad33; < - рухаємо сюди
sbrc mode, 1
rjmp inpConst3
rcall _noCursor
add xl, rKey; rKey - > до молодшої зошита. байта рег. пари X
inc count
rjmp constEnd; після введення третьої ццфри, перейти на constEnd
; введення третьої цифри другої адреси
inpConst3:
rcall _noCursor
add yl, rKey
inc count
rjmp constEnd
constEnd:; переходимо сюди після введення третьої цифри
brts _link; якщо Т-прапор виставлено, то це режим введення даних, - переходимо на scan
sbrc mode, 1; якщо виставлено ознаку введення другої адреси,
rjmp _ad2; то сюда.
sbrc mode, 2; якщо ознака режиму «КС» виставлена,
rjmp _ifCheckSum; то сюда.
sbrc mode, 3; якщо ознака режиму «ПМ» виставлена,
rjmp _ifArray; то сюда.
rcall _exit
sbic BUT, space; якщо натиснути кнопку «_»,
rjmp constEnd; то перестрибуємо це,
ldi count, 1; скидаємо лічильник
sbr mode, 3; в r19 тепер 0b00000011 (ознака введення другої адреси в ЗК)
; облагородження вмісту екрана
;address2
rcall _down
rcall _labelAddr
rcall _labelTwo
rcall _colon
rcall _labelZero
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _left
rcall _left
rcall _left
rjmp scan
_ad2:; переходимо сюди після введення другої адреси
sbrc mode, 2; якщо ознака «КС» виставлена, то перейти
rjmp _toCheckSum
sbrc mode, 3; якщо виставлено ознаку «ПМ»,
rjmp _toArray; < - сюди
rcall _exit
sbic BUT, space; якщо натиснути кнопку «_»,
rjmp _ad2; то пропускаємо це,
ldi count, 1; скидаємо лічильник
set; і виставляємо ознаку введення даних
; облагородження вмісту екрана
; const
rcall _down
rcall _labelConst
rcall _colon
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _left
rcall _left
rcall _left
rcall _left
rcall _left
rcall _left
rcall _left
rjmp constEnd; - > до циклу
_link:
rjmp scan
_ifCheckSum:; гілка від «ЗК» до «КС», якщо 2-й біт у mode виставлено
sbic BUT, space
rjmp _ifCheckSum
ldi count, 1
; облагородження вмісту екрану:
; address2
rcall _down
rcall _labelAddr
rcall _labelTwo
rcall _colon
rcall _labelZero
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _left
rcall _left
rcall _left
sbr mode, 7; ознака введення другої адреси «КС»
rjmp scan
_toCheckSum:; на виконання «КС»
rcall _exit
sbic BUT, exe; Якщо натиснуто «ВИП», перейти до виконання КС
rjmp _toCheckSum
ldi rKey, 0x00; попередньо обнулити регістри, які використовуються
ldi buf, 0x00
rjmp _SUM
_ifArray:; гілка від «КС» до «ПМ»
sbic BUT, space
rjmp _ifArray
ldi count, 1
; облагородження вмісту екрана
;address2
rcall _down
rcall _labelAddr
rcall _labelTwo
rcall _colon
rcall _labelZero
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _left
rcall _left
rcall _left
sbr mode, 11
rjmp scan
_toArray:; перехід після введення другої адреси
ldi count, 1
sbr mode, 27; 0b00011011 - > r19 - ознака введення третьої адреси
rcall _exit
sbic BUT, space
rjmp _toArray
; облагородження вмісту екрана
; address3
rcall _down
rcall _labelAddr
rcall _labelThree
rcall _colon
rcall _labelZero
rcall _labelSpace
rcall _labelSpace
rcall _labelSpace
rcall _left
rcall _left
rcall _left
rjmp scan
; перша цифра третьої адреси (ПМ)
_ad31:
mov zh, rKey
inc count
rjmp scan
; друга цифра першої адреси
_ad32:
mov zl, rKey
swap zl
inc count
rjmp scan
; третя цифра третьої адреси
_ad33:
rcall _noCursor
add zl, rKey
inc count
rjmp _arrayEnd
; очікування натискання кнопки ГД після введення третьої адреси
_arrayEnd:
rcall _exit
sbic BUT, exe
rjmp _arrayEnd
ldi buf, 0x00; попереднє обнулення buf
rjmp _execArray
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
_execConst:
; перше порівняння
_ccp1:; yh > xh?
cp yh, xh
brlo _errConst; if yh < xh
breq _eq1; if yh = xh
rjmp _ccp2
_eq1:; if yh = xh
cp yl, xl; yl > xl?
brlo _errConst; if yl < xl
; якщо перше число менше другого,
; то переходимо до другого порівнянні
_ccp2:; if yh > xh
cp yh, xh; yh > xh?
breq _ccp22; if yh = xh
st X+, buf
rjmp _ccp2
_ccp22:; if yh = xh
cp yl, xl; yl > xl?
brlo _end; if xl > yl
st X+, buf
rjmp _ccp22
_errConst:; «ERROR», якщо перше число більше другого
rcall _clear
rcall _labelWrite
rcall _labelSpace
rcall _labelConst
rcall _point
rcall _down
rcall _noCursor
rcall _labelErr
rjmp _wait
_end:; вихід
rjmp go
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
_SUM:
; перше порівняння
_scp1:; yh > xh?
cp yh, xh
brlo _errSum; if yh < xh
breq _eq2; if yh = xh
rjmp _scp2
_eq2:; if yh = xh
cp yl, xl; yl > xl?
brlo _errSum; if yl < xl
; якщо перше число менше другого,
; то переходимо до другого порівнянні
_scp2:; if yh > xh
cp yh, xh; yh > xh?
breq _scp22; if yh = xh
rcall _exCS
brcs _overflow
rjmp _scp2
_scp22:; if yh = xh
cp yl, xl; yl > xl?
brlo _showSum; if xl > yl
rcall _exCS
brcs _overflow
rjmp _scp22
_showSum:; вивід контрольної суми на екран
rcall _clear
rcall _labelSum
rcall _down
rcall _noCursor
mov rLCD, rKey
rcall _charFromMemory
_wait:
rcall _exit
rjmp _wait
_errSum:; «ERROR», якщо перше число більше другого
rcall _clear
rcall _labelSum
rcall _down
rcall _noCursor
rcall _labelErr
rjmp _wait
_overflow:; при перевищенні КС 255 - сюди
; скидання прапорів перенесення і напівпереноса (про всяк випадок)
clh
clc
ldi rKey, 0xff; якщо КС більше, ніж 0xFF, то на екран виводиться FF
rjmp _showSum
_exCS:; підпрограма виконання КС
ld buf, X+
add rKey, buf
;rcall _cpixl
ret
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
_execArray:
; перше порівняння
_acp1:; yh > xh?
cp yh, xh
brlo _errArr; if yh < xh
breq _eq3; if yh = xh
rjmp _acp2
_eq3:; if yh = xh
cp yl, xl; yl > xl?
brlo _errArr; if yl < xl
; якщо перше число менше другого,
; то переходимо до другого порівнянні
_acp2:; if yh > xh
cp yh, xh; yh > xh?
breq _acp22; if yh = xh
rcall _exMA
rjmp _acp2
_acp22:; if yh = xh
cp yl, xl; yl > xl?
brlo _end; if xl > yl
rcall _exMA
rjmp _acp22
_errArr:; «ERROR», якщо перше число більше другого
rcall _clear
rcall _mArray
rcall _down
rcall _noCursor
rcall _labelErr
rjmp _wait
_exMA:; підпрограма виконання ПМ
ld rKey, X+
st Z+, rKey
ret
; ===================================================================
_exit:
sbis BUT, quit; якщо натиснуто кнопку виходу,
rjmp go; то виходимо з режиму на go
ret
; ====================================================================
; робота з екраном
; ініціалізація LCD
altInit:
rcall _delay1
ldi rLCD, config; налаштування дисплея: 8 біт, 2 рядки, 5х8
rcall _cWrite
rcall _delay1
ldi rLCD, on; увімкнути дисплей
rcall _cWrite
rcall _delay1
ldi rLCD, clrSc; очищення екрана
rcall _cWrite
rcall _delay1
ldi rLCD, incr; збільшення адреси, екран статичний
rcall _cWrite
ret
; перевірка біта зайнятості
_BF:
rcall _portIn; порт даних на вхід
rcall _modeB; режим читання команди
_loop:
sbi _cpo, e
rcall _delay
cbi _cpo, e
in r24, _dpi; чтені шини даних
andi r24, 0x80; перевірка 7-го біта
brne _loop
ret
; затримка
_delay:
ldi r23, 20
_del:
dec r23
brne _del
ret
//затримка більше
_delay1:
ldi r24, 0xff ;255
_d:
ldi r23, 0xff ;255
_cmp_d:
dec r23
brne _cmp_d
dec r24
brne _cmp_d
ret
; запис команди
_cWrite:
rcall _modeC
rcall _portOut
out _dpo, rLCD
rcall _delay
cbi _cpo, e
ret
; запис даних
_dWrite:
rcall _modeD
rcall _portOut
out _dpo, rLCD
rcall _delay
cbi _cpo, e
ret
; порт даних на вихід
_portOut:
ldi r23, 0xff
out _dp, r23
ret
; порт даних на вхід з підтяжкою
_portIn:
ldi r23, 0x00
out _dp, r23
ldi r23, 0xff
out _dpo, r23
ret
; режим запису команд
_modeC:
cbi _cpo, rw
cbi _cpo, rs
sbi _cpo, e
ret
; режим запису даних
_modeD:
sbi _cpo, rs
cbi _cpo, rw
sbi _cpo, e
ret
; режим очікування BF
_modeB:
sbi _cpo, rw; читання
cbi _cpo, rs; команди
ret
; створення коду символу при введенні адрес і даних
_charInput:
cpi rLCD, 0x0a; порівняти з 10
brge _grt; якщо більше, то перейти на _ grtA
rcall _lstA
rjmp _return
_grt:
rcall _grtA
_return:
ret
_lstA:
ldi r21, 0x30; якщо менше,
add rLCD, r21; то додати 48
rcall _dWrite; записати в LCD
ret
_grtA:; якщо число від А... F
ldi r21, 0x37; 55 - додати до rLCD, щоб отримати код символу
add rLCD, r21;
rcall _dWrite; записати в LCD
ret
; формування символу при читанні з пам'яті
; працює через rLCD
_charFromMemory:
push rLCD; відправили в стек
; формування старшого байту
andi rLCD, 0xf0; очистити молодший ніббл
swap rLCD; поміняти ніблі місцями
rcall _BF
rcall _charInput
; формування старшого байту
pop rLCD; дістати зі стека
andi rLCD, 0x0f; очистити старший ніббл
rcall _BF
rcall _charInput
ret
= = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = = =
; ""-"
_none:
;rcall _up
ldi rLCD, 0x2D
rcall _BF
rcall _dWrite
ret
; на другий рядок
_down:
ldi rLCD, down
rcall _BF
rcall _cWrite
ret
; напис «BROWSE»
_browse:
ldi rLCD, 0x42; B
rcall _BF
rcall _dWrite
ldi rLCD, 0x52; R
rcall _BF
rcall _dWrite
ldi rLCD, 0x4f; O
rcall _BF
rcall _dWrite
ldi rLCD, 0x57; W
rcall _BF
rcall _dWrite
ldi rLCD, 0x53; S
rcall _BF
rcall _dWrite
ldi rLCD, 0x45; E
rcall _BF
rcall _dWrite
ret
; напис «MOVE ARRAY»
_mArray:
ldi rLCD, 0x4d; M
rcall _BF
rcall _dWrite
ldi rLCD, 0x4f; O
rcall _BF
rcall _dWrite
ldi rLCD, 0x56; V
rcall _BF
rcall _dWrite
ldi rLCD, 0x45; E
rcall _BF
rcall _dWrite
rcall _labelSpace
ldi rLCD, 0x41; A
rcall _BF
rcall _dWrite
ldi rLCD, 0x52; R
rcall _BF
rcall _dWrite
ldi rLCD, 0x52; R
rcall _BF
rcall _dWrite
ldi rLCD, 0x41; A
rcall _BF
rcall _dWrite
ldi rLCD, 0x59; Y
rcall _BF
rcall _dWrite
ret
; напис «DATA»
_labelData:
ldi rLCD, 0x44; D
rcall _BF
rcall _dWrite
ldi rLCD, 0x41; A
rcall _BF
rcall _dWrite
ldi rLCD, 0x54; T
rcall _BF
rcall _dWrite
ldi rLCD, 0x41; A
rcall _BF
rcall _dWrite
ret
; напис «WRITE»
_labelWrite:
ldi rLCD, 0x57; W
rcall _BF
rcall _dWrite
ldi rLCD, 0x52; R
rcall _BF
rcall _dWrite
ldi rLCD, 0x49; I
rcall _BF
rcall _dWrite
ldi rLCD, 0x54; T
rcall _BF
rcall _dWrite
ldi rLCD, 0x45; E
rcall _BF
rcall _dWrite
ret
; напис «CONST»
_labelConst:
ldi rLCD, 0x43; C
rcall _BF
rcall _dWrite
ldi rLCD, 0x4f; O
rcall _BF
rcall _dWrite
ldi rLCD, 0x4e; N
rcall _BF
rcall _dWrite
ldi rLCD, 0x53; S
rcall _BF
rcall _dWrite
ldi rLCD, 0x54; T
rcall _BF
rcall _dWrite
ret
; напис ADDRESS
_labelAddr:
ldi rLCD, 0x41; A
rcall _BF
rcall _dWrite
ldi rLCD, 0x44; D
rcall _BF
rcall _dWrite
ldi rLCD, 0x44; D
rcall _BF
rcall _dWrite
ldi rLCD, 0x52; R
rcall _BF
rcall _dWrite
ldi rLCD, 0x45; E
rcall _BF
rcall _dWrite
ldi rLCD, 0x53; S
rcall _BF
rcall _dWrite
ldi rLCD, 0x53; S
rcall _BF
rcall _dWrite
ret
; напис «CHECKSUM»
_labelSum:
ldi rLCD, 0x43; C
rcall _BF
rcall _dWrite
ldi rLCD, 0x48; H
rcall _BF
rcall _dWrite
ldi rLCD, 0x45; E
rcall _BF
rcall _dWrite
ldi rLCD, 0x43; C
rcall _BF
rcall _dWrite
ldi rLCD, 0x4b; K
rcall _BF
rcall _dWrite
ldi rLCD, 0x53; S
rcall _BF
rcall _dWrite
ldi rLCD, 0x55; U
rcall _BF
rcall _dWrite
ldi rLCD, 0x4d; M
rcall _BF
rcall _dWrite
ret
