УМК на ATmega16

Спойлер: ця стаття не видавить ностальгічної сльози.

Вирішивши не так давно познайомитися ближче (після 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