---
# System prepended metadata

title: Алгоритмы

---

# Алгоритмы

### Умножение целого на целое (беззначные)
```cp=
; умножение - это сложение числа с самим собой n раз
ldi r22, n;
clr r18
mulR18:
	add r18, r16 ; складываем с с самим собой
	dec r22 ; уменьшаем счетчик цикла
	cp r22, 0; проверяем, что в регистре 0
	brne mulR18 ; если счетчик не кончился - считаем дальше
```

### Деление (целые, беззначные)
```cp=
; умножение - это количество раз, которое нужно из числа вычесть n
clr r18; частное
ldi r17, n ; делитель
cp r16, r17 ; если делимое меньше делителя, то сразу на выход
brmi final

delR16:
inc r18 ; увеличиваем счетчик
sub r16, r17 ; вычитаем из делимого делитель. 
brmi final ; если делимое оказалось меньше, значит счет окончен
jmp delR16

final:
; вот здесь у нас получено итоговое значение r18
```

### Задержка
Высчитывать ровное количество тактов для задержки бесполезно потому что скорее всего какие-то левые куски кода (на которые тоже тратятся такты процессора) всё попортят. Приблизительно можно написать.
```cp=
clr r18 ; cycle param
delay:
; cycle body
inc r18
cpi r18, N	; do while r18(++) < N
brmi delay
```

### Задержка в 255+ тактов
```cp=
ldi r20, 255
ForR20In255To0 :
ldi r21, 255
ForR21In255To0 :
dec r21
brne ForR21In255To0 ; если != 0, то продолжаем уменьшать
dec r20
brne ForR20In255To0
```

### Инициализация стека
```cp=
ldi r16, low(RAMEND)
out spl, r16
ldi r16, hight(RAMEND)
out sph, r16
```

### Заброс/Получение значения в стек
```cp=
out r16 SREG
push r16
...
pop r16
in SREG r16
reti
```

### Получение числа едениц и десятков в двузначном числе
```cp=
; логика такая. У нас число 54. 5 - это количество раз, которое из числа можно отнять 10. 4 - это остаток

clr r20; тут будут десятки
; еденицы останутся в r16

cpi r16, 10; сначала сравниваем с 10, чтобы понять, есть ли вообще смысл считать
brmi final; если число в r16 < 10, то у нас нет десятков, только r16 едениц, на выход

subi r16, 10 ; 

calcDecs:
inc r20 ; если можно из числа вычесть 10, значит прибавить 1 к числу десятков
subi r16, 10; вычитаем из r16 10
brmi final ; такая же логика, как и раньше. Если теперь стало меньше 10, значит в r16 дсятков нет
jmp calcDecs; если больше или равно, продолжаем считать

final:
```

### Чтение с клавиатурки
![](https://i.imgur.com/kZX8pTT.png)

4 пина [0:3] кодируют координату кнопки по вертикали (1 если нажата). 4 пина [4:7] кодируют координату кнопки по горизонтали. Буду делать так, просто сравнивать значение с пина с каждой из координат
```cp=
.equ button7 = 0b10001000
.equ button4 = 0b01001000
...
.equ button8 = 0b10000100
...
.equ buttonEqual = 0b00010001

main:
in r16, PIND
cpi r16 button7
breq clickButton7
cpi r16 button4
breq clickButton4
.... и так далее по каждой кнопке
```
### Добавление цифры
1) для хранения трехзначного числа на индикаторе нам нужно 2 байта (999)
Я думаю как сделать, пока идут цифры сохранять в 4 регистрах цифры. А уже когда случится сигнал на сложение, сформировать из этих цифр трехзначное число. Пожалуй буду использовать для этого стек + регистр счетчик количества цифр. Благодаря счетчику количества цифр мы еще и выведем нужное число на экран

```cp=
.def numberCounter = r20; количество цифр
.def numberToDraw = r21; цифра, которую будем рендерить. если = 10, то ничего не отображать
.equ maxNumberCount = 3

clickButton7: если нажали на кнопку 7
cpi numberCounter, maxNumberCount
breq main; если количество цифр уже максимально, проигнорировать команду
ldi r16, 7; закинуть в стек 7
push r16
jmp renderNumber; идем на рендер
```
### Обработка сброса
нам нужно вытащить все из стека, обнулить текущий numberCounter, отрендерить пустые ячейки
```cp=
clickC:
ldi numberToDraw = 10; конфигурируем рендер на отображение ничего
cpi numberCounter, 0
breq main; если количество цифр = 0, возвращаемся к ожиданию
pop r16; достаем из стека цифру
renderNumber; идем рисуем, что её нет. 
dec numberCounter; уменьшаем количество цифр
jmp clickC; повторяем
```
### Обработка плюса 
нам нужно вытащить все из стека. обнулить текущий numberCounter, отрендерить пустые ячейки и сложить его с первым слагаемым. Для самого первого введенного числа первое слагаемое = 0. После сложения полученное число само становится 1 слагаемым
2) преобразовать 3 цифры в 3значное число. Для этого 1 цифру просто складываем. 2 цифру умножаем на 10 и складываем. 3 цифру умножаем на 100 и складываем.
3) складываем с 1 слагаемым. Сначала складываем младшие байты, потом старшие + флаг С от младших байтов

#### Собираем число из цифр
```cp=
.def inputNumberH r20
.def inputNumberL r29

cpi numberCounter, 0
breq main; игнорируем команду если ничего не введено
pop inputNumberL; достаем последнюю цифру
cpi numberCounter, 1
breq startPlus; если была только 1 цифра перестаем считывать
pop r21 ; достаем десятки
ldi r22, 10 ; нельзя умножить регистр на число, поэтому кидаем число в регистр
mul r21, r22 ; получаем количество десятков
add inputNumberL, r21 ; получаем сумму десятков и едениц
cpi numberCounter, 2
breq startPlus; если была только 2 цифра перестаем считывать
pop r21 ; достаем сотни
; 99 точно помещается в 1 байт, а 9*100 в 1 байт не поместится и mul использовать нельзя. Тогда просто запускаем цикл где 100 раз добавляем в inputNumber r21. Учитываем конечно, что число может стать двубайтным
getHundreds:
ldi r22, 100 ; сто раз складываем к inputNumber число сотен
add inputNumberL, r21; складываем с младшим разрядом число сотен
adc inputHubmerH, 0 ; складываем со старшим разрядом 0 и значение флага C
subi r22, 1 ; уменьшаем значение цикла на 1
breq fin ; если = 0, выходим
jmp getHundreds
```
#### Складываем с 1 слагаемым (которое заранее = 0)
```cp=
add firstNumberL, inputNumberL
adc firstNumberH, inputNumberH ; сложение с учетом флага C
```
#### Обратно в цифры
количество тысяч - количество раз из числа вычесть 1000.
количество сотен - сколько раз из остатка вычесть 100.
количество десятков - сколько раз из остатка вычесть 10.
количество едениц - остаток
тысяча - это 1111101000. 11101000 вычитаем из младшего бита, 11 из старшего
```cp=
mov tempH, firstNumberH
mov tempL, firstNumberL

clr thouthands
calcThouthands:
<тут нужна проверка на то, что число >= 1000>
<и так же где-то тут нужно сделать так, чтобы numberCount стало = 4>
brmi calcHundreds
sub tempL, 0b11101000
subc tempH, 0b11
inc thouthands
jmp calcThouthands

; Аналогично для сотен, десятков, но даже проще, так можно будет с регистром h не работать. Полученное на вывод. 
```


### АЦП

#### Инициализация
```cp=
.org 0x01С jmp ADC_1 ; задаем переход на функцию по вектору прерывания
sei ; включаем прерывания
```
#### Старт
```cp=
.edu ADCSRA_SETUP = 0b11001<3 бита на предделитель>
; 7 - активация
; 6 - включение
; 5 - ADATE (запуск от периферийки). Сами запускаем 
; 4 - флаг прерывания - мы не активируем
; 3 - разрешаем прерывания
; 2 - 0 - коэф. предделителя 
.equ ADMUX_SETUP = 0b11100000
; 7-6 - источник опорного напряжения. Если в задаче никаких всратых пинов не подведено, то берем 11 (внутренний)
; 5 - ADLAR - 8 бит результата будут расположены в ADCH
; 4 - зарезервирован
; 3-0 - адрес пина входа. Смотрим по табличкам

ldi r16, ADMUX_SETUP 
out ADMUX, r16
ldi r16, ADCSRA_SETUP 
out ADCSRA, r16
```


#### Чтение 10 битного значения

```cp=
read:
sbrs ADCRA, 5 ; 5 бит ADLAR регитсра ADCRA означает, что старшие 8 бит в регистре H
jmp readFromL: ; если бит не установлен, то старшие 8 бит в регистре L

readFromH:
out r16 ADCH
out r17 ADCL
jmp calc

readFromL:
out r16 ADCL
out r17 ADCH
```

### SPI
#### Инициализация
Так как мы являемся мастером, то нам самим нужно настроить порты ввода\вывода на выход.
```cp=
SPI_MasterInit:
; Set MOSI, SS and SCK output, all others input
ldi r17,(1<<DD_MOSI)|(1<<DD_SCK)|(1<<DD_SS)
out DDR_SPI,r17
; Enable SPI, Master, set clock rate fck/16
ldi r17, (1<<SPIE)|(1<<SPE)|(1<<MSTR)|(1<<SPR0)
out SPCR,r17
sei ; разрешаем прерывания если нужно
reti
```
#### Передаем данные не используя прерывания
```cp=
SPI_MasterTransmit:
; Start transmission of data (r16)
out SPDR,r16
Wait_Transmit:
; Wait for transmission complete
sbis SPSR,SPIF
rjmp Wait_Transmit
ret
```
#### Получаем данные не используя прерывания
```cp=
SPI_SlaveTransmit:
; Start transmission of data (r16)
Wait_Transmit:
; Wait for transmission complete
sbis SPSR,SPIF
jmp Wait_Transmit
sbis SPSR, WCOL ; если обнаружилась ошибка данных, ждем их снова
ret
ldi r16 (0>>SPIF) ; очищаем флаг завершения передачи
jmp Wait_Transmit:
```


### USART
```cp=

```


### TWI

### таймер