Как мне выполнить модуль на ARM7? - PullRequest
0 голосов
/ 01 марта 2019

У меня много проблем с выполнением модуля на ARM7.

В настоящее время у меня есть этот код:

ADD R0,R0,R1
MOV R0, R0 MOD 2
BX LR

Но он совсем не работает.

Из того, что сделали мои одноклассники, похоже, что мы должны делать это по битам, но я не понимаю, как это будет работать.

1 Ответ

0 голосов
/ 02 марта 2019

Действительно, у вас неправильный синтаксис.Хотя большинство (все?) Сборщиков ARM поддерживают оператор MOD, он работает только тогда, когда оба операнда являются константами времени сборки.Он просто выполняет арифметику во время сборки и свертывание константных выражений.Таким образом, вы можете сделать:

mov  r0, #11 MOD 3     ; R0 = 2 = (11 % 3)

, который по существу будет преобразован в:

mov  r0, #2

, таким образом, переместив значение 2 в регистр R0.

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

Однако это не так.работать, когда вы имеете дело с регистрами, переменными или чем-то, что не является константой времени сборки.


Исходя из кода, который у вас есть в вопросе, похоже, что вы добавляете содержимоерегистр R1 к регистру R0, а затем пытается вычислить R0 по модулю 2.

Предполагая, что целые числа равны unsigned , это так просто:

add  r0, r0, r1     ; R0 = (R0 + R1)
and  r0, r0, #1     ; R0 = (R0 & 1)
bx   lr

Это работает, потому что x % 2 эквивалентно x & 1 для целых чисел без знака.В общем, x % n эквивалентно x & (n - 1) , пока n (делитель) - это степень двойки.Это не только легче написать, но и оптимизировать производительность, потому что побитовые операции выполняются быстрее, чем деление.

Теперь, когда вы знаете шаблон для модуля по степеням двух, вы можете легко выполнить (r0 + r1) % 4:

add  r0, r0, r1     ; R0 = (R0 + R1)
and  r0, r0, #3     ; R0 = (R0 & 1)
bx   lr

Если вы хотите сделать по модулю постоянную, которая является не степенью двойки, то все становится сложнее.Я бы не пытался выписать это вручную при сборке.Вместо этого я бы посмотрел на , чтобы увидеть, что компилятор сгенерирует .Вот как вы будете выполнять (r0 + r1) % 3 в сборке:

add     r0, r0, r1           ; R0 = (R0 + R1)
movw    r3, #43691           ; \ R3 = 0xAAAAAAAB
movt    r3, 43690            ; /
umull   r2, r3, r3, r0       ; R3:R2 = (R3 * R0)  [R3 holds upper and R2 holds lower bits of result]
lsrs    r3, r3, #1           ; R3 = (R3 >> 1)
add     r3, r3, r3, lsl #1   ; R3 = (R3 + R3 * 2)
subs    r0, r0, r3           ; R0 = (R0 - R3)
bx      lr

Компилятор сгенерировал оптимизированный код для вычисления целочисленного модуля.Вместо полного деления он превратил это в умножение на магическое число (обратное умножение).Это стандартный прием от Хакера Delight и общая оптимизация снижения прочности, используемая многими компиляторами .


Пока что мы рассмотрелиОперации по модулю на беззнаковых целочисленных типах.Как насчет того, когда вы хотите сделать модульную арифметику для знаковых целых чисел?Ну, вам нужно принять во внимание бит знака (который является MSB).

Для (r0 + r1) % 2, где r0 и r1 подписаны, и, таким образом, r0 + r1 дает подписанный результат:

adds   r0, r0, r1     ; R0 = (R0 + R1)   <-- note "s" suffix for "signed"
and    r0, r0, #1     ; R0 = (R0 & 1)    <-- same as before for unsigned
it     mi             ; conditionally execute based on sign bit (negative/minus)
rsbmi  r0, r0, #0     ; negate R0 if signed (R0 = abs(R0))
bx     lr

Это очень похоже на код, который мы использовали для модуля без знака, за исключением инструкций IT + RSBMI для условного отрицания, основанных на том, является ли входное значение отрицательным (в другихслова, чтобы принять абсолютное значение).

(Вы указали в вопросе только ARMv7, а не профиль, на который вы нацеливались. Если у вашего чипа есть профиль "A" (приложение), вы можете опустить IT инструкция. Но в противном случае вы ориентируетесь на набор инструкций Thumb-2, который не поддерживает условное выполнение инструкций, не связанных с ветвлением, поэтому вам нужно IT перед инструкцией RSBMI. См. Условное выполнение вThumb-2 .)

К сожалению, вычисление (r0 + r1) % 4 не является простым вопросом изменения постоянного операнда инструкции AND.Вам нужно больше кода, даже по модулю с постоянными степенями два.Опять спросите у компилятора как это сделать.Определенно спросите у компилятора о знаковом модуле не степеней двойки.


Если вы хотите выполнить общую операцию модуля над двумя переменными, все гораздо сложнее, потому что выне могу просто использовать бит-тиддлинг. Компиляторы C собираются передать вызов библиотечной функции :

UnsignedModulo(unsigned int i, unsigned int j, unsigned int m):
    push    {r3, lr}
    add     r0, r0, r1
    mov     r1, r2
    bl      __aeabi_uidivmod
    mov     r0, r1
    pop     {r3, pc}
SignedModulo(int i, int j, int m):
    push    {r3, lr}
    add     r0, r0, r1
    mov     r1, r2
    bl      __aeabi_idivmod
    mov     r0, r1
    pop     {r3, pc}

Здесь GCC отправил библиотечную функцию __aeabi_uidivmod для unsigned и библиотеку __aeabi_idivmodфункция для подписанного модуля / деления.Другие компиляторы будут иметь свои собственные библиотечные функции.

Не пытайтесь писать такого рода код вручную при сборке.Это просто не стоит усилий.При необходимости извлеките функцию из стандартной библиотеки компилятора C и вызовите ее, чтобы выполнить тяжелую работу.(Ваш учитель не ожидает, что вы это сделаете.)

...