Как длинное целое число хранится в 8-битном регистре Arduino
Предположим, следующий код на 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"
@Josh l, 👍1
1 ответ
▲ 2
Это не так. Оно разделено на четыре 8-битных значения, каждое из которых представляет четверть бита. Каждое значение хранится в отдельном регистре или ячейке памяти (в зависимости от того, что с ним делается).
Программное обеспечение затем объединяет их вместе, образуя одно 32-битное значение.
,
@Majenko
Смотрите также:
- устаревшее преобразование из строковой константы в 'char*'
- Как запрограммировать ардуино на чистом C/C++?
- Количество элементов в массиве char
- Регистры ввода-вывода SAM3X8E (Arduino Due)
- как быстро loop() работает в Arduino
- Arduino: как получить тип платы в коде
- Как вызвать функции C из скетча ардуино?
- Как преобразовать полезную нагрузку byte* в строку
Тогда как будет выглядеть отдельная инструкция? Разве в этом случае инструкция не должна быть длиной 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