Выровненная по 8 байтам справочная таблица упрощает индексацию и подходит для чипов AVR, поддерживающих lpm
- Загрузка из памяти программ. (Оптимизировано из ответа @AterLux). Выравнивание таблицы по 8 означает, что все 8 записей имеют одинаковый старший байт своего адреса. И никакого переноса младших 3 битов, поэтому мы можем использовать ori
вместо того, чтобы инвертировать адрес для subi
. (adiw
работает только для 0..63, поэтому может не представлять адрес.)
Я показываю наилучший сценарий, когда вы можете удобно сгенерировать входные данные в r30
(нижняя половина Z), в противном случае вам понадобится mov
. Кроме того, это становится слишком коротким, чтобы вызывать функцию, поэтому я не показываю ret
, а просто фрагмент кода.
Предполагается, что ввод действителен (в диапазоне 0..7); рассмотрите @ReAl, если вам нужно игнорировать старшие биты, или просто andi r30, 0x7
Если вы можете легко перезагрузить Z после этого или вам все равно не нужно было его сохранять, это здорово. Если затирание Z отстой, вы можете подумать о создании таблицы в ОЗУ во время первоначального запуска (с циклом), чтобы вы могли использовать X или Y для указателя с загрузкой данных вместо lpm
. Или, если ваш AVR не поддерживает lpm
.
## gas / clang syntax
### Input: r30 = 0..7 bit position
### Clobbers: r31. (addr of a 256-byte chunk of program memory where you might have other tables)
### Result: r17 = 1 << r30
ldi r31, hi8(shl_lookup_table) // Same high byte for all table elements. Could be hoisted out of a loop
ori r30, lo8(shl_lookup_table) // Z = table | bitpos = &table[bitpos] because alignment
lpm r17, Z
.section .rodata
.p2align 3 // 8-byte alignment so low 3 bits of addresses match the input.
// ideally place it where it will be aligned by 256, and drop the ORI
// but .p2align 8 could waste up to 255 bytes of space! Use carefully
shl_lookup_table:
.byte 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80
Если вы можете найти таблицу на границе выравнивания в 256 байт, вы можете отбросить lo8(table)
= 0, чтобы вы могли отбросить ori
и просто использовать r30
непосредственно как младший байт адреса.
Стоимость для версии с ori
, не считая перезагрузки Z
с чем-то после или, что еще хуже, сохранения/восстановления Z
. (Если Z ценен в тот момент, когда вам это нужно, рассмотрите другую стратегию).
- размер = 3 слова кода + 8 байт (4 слова) данные = 7 слов. (Плюс до 7 байт заполнения для выравнивания, если вы не заботитесь о расположении памяти программы)
- циклы = 1(ldi) + 1(ori) + 3(lpm) = 5 циклов
В цикле, если вам нужны другие данные в том же фрагменте программной памяти объемом 256 байт, ldi r31, hi8
можно поднять/выполнить только один раз.
Если вы можете выровнять таблицу по 256, это сэкономит слово кода и цикл времени. Если вы также поднимете ldi
из цикла, останется только 3-цикл lpm
.
(Не проверено, у меня нет набора инструментов AVR, кроме clang -target avr
. Я думаю, что GAS / clang нужны только обычные ссылки на символы и обрабатывают symbol * 2
внутри. Это успешно ассемблируется с clang -c -target avr -mmcu=atmega128 shl.s
, но дизассемблирование .o приводит к сбою llvm-objdump -d
10.0.0. )
person
Peter Cordes
schedule
27.06.2020