Сборка AVR: загрузка 16-битного числа в два 8-битных регистра - PullRequest
0 голосов
/ 16 ноября 2018

Я совершенно новичок в сборке AVR, и у меня есть шестнадцатеричное число 0x20C, равное 16 битам.

Я хочу загрузить это постоянное шестнадцатеричное число в двух 8-битных регистрах в сборке AVR

Можно ли сделать что-то вроде:

LDI R17:R18 0x20C

РЕДАКТИРОВАТЬ: если невозможно таким образом загрузить 16-битное число в два 8-битных регистра, может кто-нибудь дать мне альтернативный вариант?

Ответы [ 2 ]

0 голосов
/ 17 ноября 2018

Как упоминалось в ответе Питера Кордеса, в наборе команд AVR нет инструкции для загрузки 16-битного значения в регистровую пару. Но ...

, если невозможно загрузить 16-битное число в двух 8-битных регистрах таким образом, может кто-нибудь может дать мне альтернативный вариант?

Для вашего удобства ассемблер обычно имеет возможность создавать макросы (обратите внимание на эту возможность, если вы планируете много программировать на ассемблере).

Синтаксис макроса зависит от набора инструментов. Для gnu as я использую следующий макрос, который загружает 16-битное непосредственное значение в заданный и следующий регистр

.macro ldi_w reg:req, val:req
    ldi \reg, lo8(\val)
    ldi \reg + 1, hi8(\val)
.endm

So

ldi_w   r16, 0xBEEF

загружает 0xEF в r16 и 0xBE в r17.

Важное примечание: gnu as позволяет использовать номера регистров (ldi 16, 0xC3), а также имена регистров (ldi r16, 0xC3). Приведенный выше макрос требует регистр числа , так что все мои источники ассемблера включают avr_reg_numbers.h, который содержит определения, такие как

#ifdef r0
#undef r0
#endif
#define r0 0

С помощью этого макроса стиля пример ответа от Питера Кордеса можно переписать как

mov_w r30, r24 ; r30 ← r24, r31 ← r25
ld_w  r24, Z
mov_w r30, r22
ld_w  r18, Z
add_w r24, r18

p.s.0 My avr_as_macro.h содержит много макросов, таких как lds_w, sts_w, push_w,…, ror_w и так далее. Большинство из них генерируются другими командами макроассемблера. Например, asr_w, lsr_w и ror_w, сгенерированные

//----------------------------    right word shifts
// asr_w, lsr_w, ror_w
.irp cmd, asr, lsr, ror
    .macro \cmd\()_w reg:req
        \cmd \reg+1  $  ror \reg
    .endm
.endr

p.s.1 Некоторые микросхемы AVR имеют инструкцию по сборке movw, которая перемещает пару регистров за один цикл процессора, некоторые из них (старые или очень простые) не имеют такой команды. Поэтому я использую макрос mov_w, который вызывает movw или генерирует пару команд в зависимости от текущего уровня арки AVR.

//----------------------------------    word move
// movw exist in all new AVRs except __AVR_TINY__ (tiny4..10/20/40)
// Not exist in tiny26
// odd register numbers and overlapping are not supported
#ifdef __AVR_HAVE_MOVW__
.macro  mov_w   dst:req, src:req
    movw    \dst, \src
.endm
#else
.macro  mov_w   dst:req, src:req
    mov \dst, \src  $  mov \dst+1, \src+1
.endm
#endif
0 голосов
/ 16 ноября 2018

Есть несколько инструкций для 16-битного сложения или приращения с парами регистров, но они не загружаются из памяти и, конечно, не выполняются немедленно. Вам необходимо загрузить каждый байт отдельно , используя одну ld / ldi / ldd / lds / любую инструкцию для каждого байта / каждого регистра назначения.

(Естьявляется инструкцией для копирования пары регистров в другую пару, поддерживаемую во многих современных процессорах AVR (см. @ ReAl's answer ), но не для загрузки или немедленной загрузки. Вы можете сделать макрос для 2 инструкций какReAl показывает, но это не улучшит производительность или размер кода, только удобочитаемость.


AVR - это набор команд RISC, где большинство (почти все?) Целых инструкций являются 16-битными.не место для 16-битного немедленного, только 8. Вы всегда можете разбить немедленное на две 8-битные половины, например ldi r17, 0x0c для нижней половины и ldi r18, 0x2 для верхней половины.

Я проверил набор инструкций AVR и не увидел многобайтовых загрузок или немедленных загрузок (https://www.microchip.com/webdoc/avrassembler/avrassembler.wb_instruction_list.html),, затем скомпилировал этот C с AVR gcc в проводнике компилятора Godbolt чтобы увидеть, может быть, я что-то упустил.

int return_immediate(void) { return 0x20c; }

    ldi r24,lo8(524)    # ldi r24, 0x0c
    ldi r25,hi8(524)    # ldi r25, 0x02
    ret


int glob;
int return_global(void) {
    return glob;
}

    lds r24,glob
    lds r25,glob+1
    ret


int add(int *a, int *b) {
    return *a + *b;
}

    mov r30,r24
    mov r31,r25   Z = a
    ld r24,Z
    ldd r25,Z+1   retval = *a
    mov r30,r22
    mov r31,r23   Z = b
    ld r18,Z
    ldd r19,Z+1   tmp = *b
    add r24,r18
    adc r25,r19   retval += tmp
    ret

Поэтому, если в AVR gcc отсутствует пропущенная оптимизация или по какой-то причине он избегает загрузки слов, вы не сможете этого сделать.

Обновление: gcc нацелен на базовый AVR, поэтому он не может использоватьmovw для копирования пары регистров.

Компиляция с -mmcu=avr6 делает add(int*, int*) более эффективным:

add:
    movw r30,r24   # Z = a
    ld r24,Z
    ldd r25,Z+1
    movw r30,r22   # Z = b
    ld r18,Z
    ldd r19,Z+1
    add r24,r18
    adc r25,r19
    ret

Но, как мы видим, инструкции по выполнению чего-либо еще нетеще пару регистров, так что все это должно быть сделано отдельно.Тем не менее, копирование пары регистров с помощью одной машинной инструкции довольно удобно, потому что это нередко требуется.

...