MyTetra Share
Делитесь знаниями!
Быстрое вычисление адреса пикселя через сдвиг и сложение вместо умножения. Зачем нужны готовые машинные инструкции сдвиг+сложение?
Время создания: 24.02.2026 10:44
Автор: xintrea
Текстовые метки: экран, дисплей, размер, разрешение, пиксель, точка, адрес, умножение, сложение, сдвиг, компьютерная, графика, координата
Раздел: Компьютер - Аппаратное обеспечение - Микроконтроллеры ARM
Запись: xintrea/mytetra_syncro/master/base/1771919059phceaj6rf5/text.html на raw.githubusercontent.com

Во многих процессорных архитектурах в процессе их развития, вместо отдельных команд сдвига и сложения появились "объединенные" команды, которые за один такт делают операцию "сложение со сдвигом". Например, в ARM есть одна из форм мнемотики ADD, в которой есть 4 операнда:



ADD R0, R1, R2, LSL #2 ; это R0 = R1+ (R2 << 2)



где:


  • Первый опреранд - куда будет положен результат вычислений;
  • Второй операнд - первое слагаемое;
  • Третий операнд - второе слагаемое, которое будет сдвинуто перед сложением;
  • Четверный операнд - в каком режиме сдвига и на сколько бит будет сдвинут второй операнд.


Зачем такие странные машинные команды "сложение со сдвигом" вообще нужны?


Эта команда может пригодиться, например, для быстрого вычисления адреса пикселя на стандартных размерах экранов через два-три сдвига и пару сложений. Если в процессоре существуют операции "сложение со сдвигом", то можно уместиться в две-три машинные команды, причем не используя умножение. Так как команды "сложения со сдвигом" во всех современных (и даже древних) процессорах обычно исполняются за один такт, то таким образом за 2 такта можно высчитать адрес точки по ее координатам, даже если в процессоре нет блока умножения.



К примеру, регистр R1 содержит координату X, R2 - координату Y. Если один пиксель занимает 1 байт (например, это 256 цветов на пиксель), то чтобы получить адрес пикселя, надо Y умножить на ширину экрана (получится адрес начала линии с номером Y), и прибавить X.


Стандартные ширины экранов устроены таким образом, что умножать на ширину можно просто одним-тремя сдвигами. Что имеется в виду? Вот какие ширины бывают:



320 pix (это 256 + 64)

640 pix (это 512 + 128)

800 pix (это 512 + 256 + 32)

1024 pix (это 1024)

1280 pix (это 1024 + 256)

и т.д.



Если ширина экрана, к примеру, 1280 точек, то чтобы получить адрес начала линии с номером Y не используя умножение, надо сделать два сдвига Y и одно сложение. Вот как из умножения получается формула со сдвигами и сложением:



Адрес начала линии = Y * 1280

= Y * (1024 + 256)

= Y * 1024 + Y * 256

= Y * 2^10 + Y * 2^8

= (Y << 10) + (Y << 8)



А чтобы получить адрес пикселя надо еще прибавить X. Тогда получится формула:



Адрес пикселя = (Y << 10) + (Y << 8) + X



Эти вычисления на ARM укладываются в две команды:



ADD R3, R2, R1, LSL #8 ; R3 = (Y << 8) + X

ADD R0, R3, R1, LSL #10 ; R0 = (Y << 10) + R3



В результате в R0 лежит адрес пикселя.



Если используется 2 байта на пиксель (64 тысячи цветов, High Color, 16 bit), то результат еще умножается на два, путем еще одного сдвига на один бит влево. Пытаться работать с "виртуальным" экраном шириной 2560 точек вместо 1280, в надежде не делать дополнительный сдвиг, не имеет смысла: все равно дополнительный сдвиг придется делать, так как при вычислении адреса в строке, прибавлять надо будет не X, а X*2.


Режим 3 байта на пиксель (16М цветов, стандарт TrueColor, 24 bit) раньше считался самым медленным, когда еще не было команд "сложения со сдвигом", так как для его реализации требуется умножение на 3. А это дополнительно один сдвиг на один бит и одно сложение с самим собой. Но в современных процессорах эти две операции, как было сказано выше, выполняются вместе за один такт, так что работа с ним по производительности не отличается от режима 2 байта на пиксель.


То же самое можно сказать и о режиме 4 байт на пиксель (32 bit) - ему нужно одно дополнительное умножение на 4, что эквивалентно одному сдвигу на 2 бита влево.


Так же в этом разделе:
 
MyTetra Share v.0.67
Яндекс индекс цитирования