Вместо первой программы

task_inindex dcw 0
task_outindex dcw 0
task_lenindex dcw 0
task_maxlen dcw 0
task_cbuffer space task_cbufferlen
space — такой удобный макро-оператор для заполнения области памяти, а task_cbufferlen — длина заполнения
Сразу прикинем макрос для этой структуры:
macro
cbuffer $name,$len
dcw 0
dcw 0
dcw 0
dcw 0
$name space $len
mend
Ну тут вроде все должно быть понятно. Макросы кейла рулят. Теперь нам нужны три основные (ну две) подпрограммы работы с нашей структурой: инициализация, сохранение элемента, извлечение элемента из буфера. Прикинем и их:
initcbuffer0 ;инициализация кольцевого буфера
push {lr}
movs r2,#0
strh r2,[r0,#0] ;inindex=0
strh r2,[r0,#2] ;outindex=0
strh r2,[r0,#4] ;lenindex=0
strh r1,[r0,#6] ;maxlen=len_cbuffer
pop {pc}
;----------------------------------------------------------
sendtocbuffer0 ;помещаем байт в буфер
push {lr}
ldrh r2,[r0,#4] ;r2<-lenindex
ldrh r3,[r0,#6] ;r3<-x_maxlen
cmp r2,r3 ;lenindex?=x_maxlen
itt eq
moveq r0,#1 ;buffer overflow
popeq {pc} ;exit
adds r2,#1
strh r2,[r0,#4] ;lenindex=lenindex+1
ldrh r2,[r0,#0] ;r2<-inindex
push {r2}
adds r2,#1
cmp r2,r3
it eq
moveq r2,#0
strh r2,[r0,#0] ;inindex=(inindex+1)or(0)
pop {r2}
adds r0,#8
strb r1,[r0,r2] ;data->[offset cbuffer + inindex]
movs r0,#0 ;r0<-0 if ok
pop {pc} ;exit
;----------------------------------------------------------
loadfromcbuffer0 ;извлекаем байт из буфера
push {lr}
ldrh r2,[r0,#4] ;r2<-lenindex
tst r2,#0xFF
itt eq
moveq r0,#1 ;r0<-1 if not ok (buffer empty)
popeq {pc} ;exit
subs r2,#1
strh r2,[r0,#4] ;lenindex+=1
ldrh r2,[r0,#2] ;r2<-outindex
movs r1,r2 ;r1<-outindex
ldrh r3,[r0,#6] ;r3<-maxlen
adds r1,#1
cmp r1,r3
it eq
moveq r1,#0
strh r1,[r0,#2] ;outindex=(outindex+1)or(0)
adds r0,#8
ldrb r1,[r0,r2] ;data<-[offset cbuffer + outindex]
movs r0,#0 ;r0<-0 if ok
pop {pc} ;exit
;----------------------------------------------------------
Обратите внимание как мы входим в подпрограмму и выходим из нее. Прикол в том что у нас нет операторов типа call, ret и их вариаций. У нас есть хитрый оператор перехода BL, который перед переходом сохраняет адрес следующей инструкции (адрес возврата) не в стеке, как мы привыкли, а в спец регистре r14, он же LR, он же link register. Ну а дальше типичный выход через стек. Если не предполагаете вложеных подпрограмм, то lr в стек сохранять не нужно, а из подпрограммы можно выйти bx lr.
В подпрограммах вроде как проверяется несколько условий, а переходов нет. Мы применили т.н. блок условных инструкций — занятная штука. Оглавляет блок инструкция it <условие>. Затем идут условные инструкции до 4х, каждая с суффиксом условия. Но каждая же должна отметится в оглавлении т.е. если it eq то дальше компиллер ждет одну условную инструкцию с суфиксом eq. Пример здесь более красноречив:
ittee eq
moveq r1,#1; выполняется если равенство (Z=1)
moveq r2,r3; выполняется если равенство (Z=1)
movne r1,#0; выполняется если не равенство (Z=0)
movne r3,r2; выполняется если не равенство (Z=0)
Инструкции, которые по условию не должны выполняться, естественно тоже читаются, обрабатываются (вроде как nop). Зачем нам такое чудо. Наверно чтобы уменьшить количество переходов, на которых проявляется латентность флэша. Почему только 4 инструкции в блоке — тоже наверное дальше переход уже эффективнее.
У некоторых инструкций есть суффикс s, заставляющий выставлять флаги. Но, как видите, он поставлен и туда, где флаги нам и не нужны. Смысл этого в том, как я понял, что мы явно указываем компиллеру что инстукция 2х байтная а не 4х. Тут какая-то байда с выравниванием инструкций по слову. И если компиллеру нужно, то он все равно растянет инструкцию до 4х. (тут нужно еще разобраться)
В командах пересылки часто применяют суффиксы размерности операндов, напр. ldrb — читаем байт, strh — пишем полуслово (2байта) и т.д.
Но вернемся к нашей программе. Опять пишем макросы — куда без них.
macro ;инициализация кольцевого буфера
initcbuffer $addr_cbuffer,$len_cbuffer
ldr r0,=$addr_cbuffer -8
movs r1,#$len_cbuffer
bl initcbuffer0
mend
macro ;помещаем байт в буфер
sendtocbuffer $addr_cbuffer,$data
ldr r0,=$addr_cbuffer -8
movs r1,#$data
bl sendtocbuffer0
mend
macro ;извлекаем байт из буфера
loadfromcbuffer $addr_cbuffer
ldr r0,=$addr_cbuffer -8
bl loadfromcbuffer0
mend
Ну и наш страшный диспетчер:
processtask
loadfromcbuffer task_cbuffer
cbnz r0,processtask_notask
adr.w r0,branchtabletask
tbh [r0,r1,lsl #1]
branchtabletask
dci ((task0-branchtabletask)/2)
dci ((task1-branchtabletask)/2)
dci ((task2-branchtabletask)/2)
task0
nop
nop
b processtask
task1
nop
nop
b processtask
task2
nop
nop
b processtask
processtask_notask
b processtask
Читаем номер текущей задачи и если он есть (т.е. если наш флаг r0=0) — прыгаем на задачу. Еще одна хитрая инструкция cbz, cbnz проверяет на 0 но прыгает, зараза, только вперед. Ну и конечно (наш ответ С :) инструкция TBB, TBH аналог case, т.н. табличный переход по индексу. Чтобы не плодить страшных dci опять обратимся к макросу.
macro
$label task $addr_task
dci (($addr_task-branchtabletask)/2)
mend
Ну и теперь вся тестовая прога:
task_cbufferlen equ 8
AREA |header code|,CODE,READONLY
ENTRY
EXPORT __main
__main
macro ;инициализация кольцевого буфера
initcbuffer $addr_cbuffer,$len_cbuffer
ldr r0,=$addr_cbuffer -8
movs r1,#$len_cbuffer
bl initcbuffer0
mend
macro ;помещаем байт в буфер
sendtocbuffer $addr_cbuffer,$data
ldr r0,=$addr_cbuffer -8
movs r1,#$data
bl sendtocbuffer0
mend
macro ;извлекаем байт из буфера
loadfromcbuffer $addr_cbuffer
ldr r0,=$addr_cbuffer -8
bl loadfromcbuffer0
mend
macro
$label task $addr_task
dci (($addr_task-branchtabletask)/2)
mend
macro
cbuffer $name,$len
dcw 0
dcw 0
dcw 0
dcw 0
$name space $len
mend
initcbuffer task_cbuffer,task_cbufferlen
sendtocbuffer task_cbuffer,0x02
sendtocbuffer task_cbuffer,0x01
sendtocbuffer task_cbuffer,0x03
sendtocbuffer task_cbuffer,0x05
sendtocbuffer task_cbuffer,0x04
processtask
loadfromcbuffer task_cbuffer
cbnz r0,processtask_notask
adr.w r0,branchtabletask
tbh [r0,r1,lsl #1]
branchtabletask
task task0
task task1
task task2
task task3
task task4
task task5
task0
nop
nop
b processtask
task1
nop
nop
b processtask
task2
nop
nop
b processtask
task3
nop
nop
b processtask
task4
nop
nop
b processtask
task5
nop
nop
b processtask
processtask_notask
b processtask
initcbuffer0 ;инициализация кольцевого буфера
push {lr}
movs r2,#0
strh r2,[r0,#0] ;inindex=0
strh r2,[r0,#2] ;outindex=0
strh r2,[r0,#4] ;lenindex=0
strh r1,[r0,#6] ;maxlen=len_cbuffer
pop {pc}
;----------------------------------------------------------
sendtocbuffer0 ;помещаем байт в буфер
push {lr}
ldrh r2,[r0,#4] ;r2<-lenindex
ldrh r3,[r0,#6] ;r3<-x_maxlen
cmp r2,r3 ;lenindex?=x_maxlen
itt eq
moveq r0,#1 ;buffer overflow
popeq {pc} ;exit
adds r2,#1
strh r2,[r0,#4] ;lenindex=lenindex+1
ldrh r2,[r0,#0] ;r2<-inindex
push {r2}
adds r2,#1
cmp r2,r3
it eq
moveq r2,#0
strh r2,[r0,#0] ;inindex=(inindex+1)or(0)
pop {r2}
adds r0,#8
strb r1,[r0,r2] ;data->[offset cbuffer + inindex]
movs r0,#0 ;r0<-0 if ok
pop {pc} ;exit
;----------------------------------------------------------
loadfromcbuffer0 ;извлекаем байт из буфера
push {lr}
ldrh r2,[r0,#4] ;r2<-lenindex
tst r2,#0xFF
itt eq
moveq r0,#1 ;r0<-1 if not ok (buffer empty)
popeq {pc} ;exit
subs r2,#1
strh r2,[r0,#4] ;lenindex+=1
ldrh r2,[r0,#2] ;r2<-outindex
movs r1,r2 ;r1<-outindex
ldrh r3,[r0,#6] ;r3<-maxlen
adds r1,#1
cmp r1,r3
it eq
moveq r1,#0
strh r1,[r0,#2] ;outindex=(outindex+1)or(0)
adds r0,#8
ldrb r1,[r0,r2] ;data<-[offset cbuffer + outindex]
movs r0,#0 ;r0<-0 if ok
pop {pc} ;exit
;----------------------------------------------------------
AREA |header data|,DATA,READWRITE
cbuffer task_cbuffer,task_cbufferlen
END
Ну и кто теперь скажет что этот асм не удобный? Все.
PS. Ну и как обычно: это не учебный курс а лишь попытка заинтересовать.
- +4
- 30 марта 2011, 13:54
- psv
Очень даже интерисует, как раз и не хватает таких вот статей. Уже имею опыт использования авров, решил изучить кортексы. Источников информации крайне мало, статьи в интернете ориентированны на начинающих по типу «помигаем светодиодом», ну максимум «понажимаем на кнопку». А Вы как раз раскрывайте нормально введение в контроллеры для «продолжающих обучение» так сказать.
Так что желаю продолжения банкета!)
Так что желаю продолжения банкета!)
- Electroneg
- 30 марта 2011, 14:19
- ↓
А зачем конструкция push LR; pop PC? ЕМНИП, на ARM7 возврат выполнялся командой MOVS PC, LR (или ее разновидностью) — вся фишка LR собственно в том и состоит, чтобы обойтись в leaf-функциях без стека.
Наверно чтобы уменьшить количество переходов, на которых проявляется латентность флэша.У ARM'а инструкции выполняются конвеером и при переходе нужно перезаполнение конвеера. Поэтому, если нужно условно исполнить 1-2 инструкции — выгодне их прогнать как NOP (он выполняется за так, а JMP — за количество тактов, равное количеству ступеней конвеера, в ARM7 их было 3).
Смысл этого в том, как я понял, что мы явно указываем компиллеру что инстукция 2х байтная а не 4х. Тут какая-то байда с выравниванием инструкций по слову.Изначально у ARM применялись 32-битные команды (набор инструкций ARM). Они были трехадресные (dest, src1, src2), условные и имели флажок, показывающий меняет ли команда флаги (т.е. ADD R0, R1, R2 на флаги не влияла, а вот ADDS R0, R1, R2 влияла). Позже, для повышения плотности кода ввели дополнительный набор команд — Thumb. Эти команды были 16-битные, куда больше походили на традиционные наборы команд (двухадресные, условные только переходы, активное использование стека, все влияют на флаги) — но такой код выполнялся медленнее. В Cortex'ах применяется новый набор команд — Thumb 2. Он содержит как 16-битные команды (подмножество Thumb), так и 32-битные (подмножество ARM). Соответственно, не влияющая на флаги арифметическая команда есть только среди 32-битных, и для сокращения кода лучше использовать 16-битную флаговую версию.
Класс!
А как кейл дружит с STM32f100
вот есть «халявная» stm32vldiscovery
хотел тоже ASM-ом заняться
и у нас в «промэлектронике»
stm32f100c4 — 47 руб — так и манит изучить stm32
А как кейл дружит с STM32f100
вот есть «халявная» stm32vldiscovery
хотел тоже ASM-ом заняться
и у нас в «промэлектронике»
stm32f100c4 — 47 руб — так и манит изучить stm32
f100 — это еортекс-м0, а f103 — это кортекс-м3 вроде. и если в этом я не ошибаюсь, то набор интсрукций у них сильно различается.
- prostoRoman
- 17 апреля 2012, 23:02
- ↑
- ↓
Комментарии (14)
RSS свернуть / развернуть