С руководством по набору инструкций AVR можно ознакомиться здесь:
http://ww1.microchip.com/downloads/en/devicedoc/atmel-0856-avr-instruction-set-manual.pdf
Давайте рассмотрим ваш код для _delay_ms(1000);
.
Ser, ldi иИнструкции ldi в начале занимают 3 цикла и устанавливают 24-битный счетчик на 0x1869FF.
Тогда у нас есть цикл.Каждая итерация этого цикла уменьшает 24-битный счетчик на 1, и цикл завершается, когда счетчик достигает нуля.Таким образом, будет 0x1869FF итераций цикла.
Большинство итераций цикла занимает 5 циклов, потому что brne берет два цикла, когда он разветвляется.На последней итерации цикла brne не разветвляется, поэтому последняя итерация занимает всего 4 цикла.
Две инструкции после цикла занимают всего 3 цикла.
Складывая все, мы видимобщее число циклов:
3 + 0x1869FF * 5 - 1 + 3 = 8000000
Поскольку частота вашего процессора составляет 8 МГц, результирующая задержка составляет 1 секунду.
Хитрость для разработчиков компилятора состоит в том, чтобы выбрать, сколько итераций цикла нужно сделать, сколько rjmps сделать после цикла и сколько nops нужно сделать после этого.Поскольку добавление итераций в цикл является бесплатным с точки зрения программного пространства, вы хотите иметь как можно больше итераций цикла.Поскольку rjmp занимает меньше места, чем два nops, вы хотите иметь как можно больше rjmp после цикла.И тогда вам может понадобиться один nop после этого, чтобы сделать правильное число циклов.
Для более коротких задержек компилятор, вероятно, использует 8-битный или 16-битный счетчик.Для действительно коротких задержек он, вероятно, просто использует rjmp и nop.Для очень длительных задержек, он, вероятно, использует счетчики с более чем 24 битами.
В некоторых случаях может быть возможно сэкономить место в программе, добавив несколько nops внутри цикла, так как это может привести к уменьшению счетчика циклавозможно или может позволить вам удалить несколько инструкций в конце.Так что действительно умная реализация будет учитывать это.