Таки давненько не занимался, особо "темными делами" с табличными переходами...
Поспешишшш не вспомнив хорошентко и ...
МНДЯАА...
И так...
память у АВРок заполняется пословно сначала младший байт, затем старший
разночтения в директивах DB и DW вызваны восприятием написания и автодополнением
0x00 до четного количества байт в случае применения DB.
Теперь к тем вопросам...
имеется изначально в основной программе группа подпрограмм,
выполняющих определенные команды:
Код:
prg_ap:
nop
; ret ; при ICALL вызове
prg_dn:
nop
; ret ; при ICALL вызове
prg_left:
nop
; ret ; при ICALL вызове
prg_right:
nop
; ret ; при ICALL вызове
и далее, насколько потребуется...
Проще непосредственно присылать полный двухбайтовый код адреса нужной
подпрограммы, размещать его в Z и затем выполнять IJMP(или удачнее ICALL).
Для пересылки команды prg_dn на передающей стороне выполним
Код:
ldi r16,low(prg_dn)
rcall trb ; trb - обработчик передачи байта
ldi r16,high(prg_dn)
rcall trb ; trb - обработчик передачи байта
на приемной стороне достаточно принять эти два байта и соответственно
разместить их в Z, а затем просто выполнить
Код:
IJMP
или
Код:
ICALL
Второй вариант предусматривает применение в качестве команды одного байта смещения
относительно начала таблицы команд переходов к подпрограммам исполнителей команд,
размещенной в массиве ПЗУ
Собственно массив команд переходов:
Код:
table:
rjmp prg_ap ; смещение = 0
rjmp prg_dn ; смещение = 1
rjmp prg_left ; смещение = 2
rjmp prg_right ; смещение = 3
команда в данном случае представляет из себя один байт в диапазоне от
0 до 255(0хFF) соответствующий смещению относительно начала таблицы...
На приемной стороне принятый байт предварительно помещается в R16, в
Zh:Zl загружается адрес начала таблицы table, затем выполняем
Код:
add zl,r16
brcc pt0
inc zh
pt0:
ijmp ; или icall по смыслу выполнения программы
тем самым делаем серию переходов от обработчика приема команды на
таблицу переходов и по rjmp из таблицы непосредственно в исполнитель
если вместо ijmp поставить icall, то ret в конце исполнителя команды
вернет нас в конец обработчика приема команды.
Третий вариант похож на второй, но вместо команд переходов в таблице
занесены адреса исполнительных подпрограмм
Код:
table_m:
.dw prg_ap, prg_dn, prg_left, prg_right
команда в данном случае также представляет из себя один байт в диапазоне от
0 до 255(0хFF) соответствующий смещению относительно начала таблицы...
Прием байта смещения проводим в R16, адрес начала таблицы table_m
также предварительно заносим в Zh:Zl...
А вот дальше морока с LPM...
Код:
add zl,r16
brcc pt0
inc zh
pt0:
lsl Zl
rol Zh
lpm r16,z+
lpm r17,z
movw zl,r16
; и лишь затем
ijmp ; или icall
Результат исполнения что во втором варианте, что в третьем - одинаков.
Однако третий вариант удобен в том случае, если указатели размещать
не в ПЗУ, а в ОЗУ - но там и команды иные для реализации потребуются.