Как длинное целое число хранится в 8-битном регистре Arduino

c

Предположим, следующий код на C:

long int a = 262143;

который в двоичном формате будет иметь вид 111111111111111111 (18 бит). Если регистр atmega328p может содержать 8 бит, как в регистре представлено вышеизложенное?

Скомпилированная сборка:

    .file   "a.c"
__SP_H__ = 0x3e
__SP_L__ = 0x3d
__SREG__ = 0x3f
__tmp_reg__ = 0
__zero_reg__ = 1
    .text
.global main
    .type   main, @function
main:
    push r28
    push r29
    rcall .
    rcall .
    in r28,__SP_L__
    in r29,__SP_H__
/* prologue: function */
/* frame size = 4 */
/* stack size = 6 */
.L__stack_usage = 6
    ldi r24,lo8(-1)
    ldi r25,lo8(-1)
    ldi r26,lo8(3)
    ldi r27,0
    std Y+1,r24
    std Y+2,r25
    std Y+3,r26
    std Y+4,r27
    ldi r24,0
    ldi r25,0
/* epilogue start */
    pop __tmp_reg__
    pop __tmp_reg__
    pop __tmp_reg__
    pop __tmp_reg__
    pop r29
    pop r28
    ret
    .size   main, .-main
    .ident  "GCC: (GNU) 8.2.0"

, 👍1


1 ответ


2

Это не так. Оно разделено на четыре 8-битных значения, каждое из которых представляет четверть бита. Каждое значение хранится в отдельном регистре или ячейке памяти (в зависимости от того, что с ним делается).

Программное обеспечение затем объединяет их вместе, образуя одно 32-битное значение.

,

Тогда как будет выглядеть отдельная инструкция? Разве в этом случае инструкция не должна быть длиной 8 бит?, @Josh l

Нет "единой инструкции". Вам следует скомпилировать код и посмотреть на полученный язык ассемблера - вы увидите, что он состоит из множества инструкций. Вот почему 32-битные процессоры эффективнее 8-битных., @Majenko

Я добавил ассемблерный код выше, так что, полагаю, ldi r24,lo8(-1) ldi r25,lo8(-1) делают это, @Josh l

и ldi r26,lo8(3), поскольку вы работаете с 18 битами, а не с 16. 0xFF равно -1 в дополнительном коде, поэтому у вас есть 0x03 ff ff, что равно 0b1111111111111111111., @Majenko

последний вопрос: что именно делает приведенный выше код? Он заполняет 3 регистра двумя «нижними 8 битами числа -1 = 111111» и одним «нижними 8 битами числа 3 = 000011»?, @Josh l

Да. "ldi" - это "**L**oa**D** **I**mmediate". В итоге получается r24 = 0xFF, r25 = 0xFF и r26 = 0x03., @Majenko

Еще один вопрос не по теме (надеюсь, я смогу задать его правильно). Инструкции хранятся в ОЗУ, верно? В этом случае 8-битная инструкция находится где-то в ОЗУ, и ЦП загружает ее. Как ЦП узнает, где заканчивается инструкция, учитывая адрес ОЗУ? Как он узнает разницу между инструкцией и небольшим целым числом с меньшим количеством бит? В общем, как он не продолжает читать ОЗУ и не знает, где остановиться, @Josh l

Интересный факт: компилятор поддерживает int64_t и uint64_t на arduino uno. Это переменная размером 64 бита (8 байт) на 8-битном микроконтроллере., @Jot

@Joshl Нет - инструкции находятся во флэш-памяти, а не в ОЗУ, а флэш-память имеет ширину 16 бит. Каждая инструкция умещается либо в одно 16-битное слово, либо в два 16-битных слова в зависимости от инструкции. Процессор знает, насколько "длинной" является каждая инструкция, по тому факту, что сама инструкция будет содержать информацию о том, что и откуда нужно извлечь., @Majenko

На самом деле это 4 регистра (r27:r24) и 4 байта стека: (SP+1 to SP+4). Не уверен, зачем используется стек. Возможно, переменная была volatile, или код был скомпилирован в -O0..., @Edgar Bonet

@EdgarBonet Стек используется, потому что r24-r27 находятся в категории "call clobbered". Они просто временно используются для помещения значений в память, поскольку вы не можете напрямую LDI в память. Регистры 18-27, 30 и 31 могут использоваться только для временного хранения, поскольку любая вызванная функция может изменить значение, а не восстановить его. 2-17, 28 и 29 являются "call saved" - любая функция *должна* восстановить их после использования. Решит ли компилятор использовать память или "call saved" регистры для своих переменных, зависит от того, для чего они используются и что еще происходит., @Majenko

https://gcc.gnu.org/wiki/avr-gcc#Call-Used_Registers, @Majenko

Я знаю о различии между сохраненным вызовом и затёртым вызовом. Суть в следующем: если вы не делаете a volatile, то он будет оптимизирован компилятором на любом уровне оптимизации, кроме -O0., @Edgar Bonet

@EdgarBonet нет, это не так. Это *может* быть так, но это или нет, полностью зависит от того, для чего используется a, и от того, какие другие переменные находятся вокруг. Есть ограниченное количество регистров, которые можно использовать. Если a считается менее важным, то он не будет тратить на него выделенную группу регистров. Вы не можете угадать, что сделает оптимизатор, просто основываясь на силе одной переменной., @Majenko

Могу. Я прочитал сгенерированную компилятором сборку., @Edgar Bonet

Вероятно, он был скомпилирован без какой-либо оптимизации. Эта сборка не могла быть создана из кода Arduino, поэтому в любом случае не имеет юридической силы на этом сайте. Так что это во многом зависит от того, что OP набрал в командной строке, и поэтому выходит за рамки этого сайта., @Majenko