Как мне разделить на 8, используя ARM Assembly Code? - PullRequest
0 голосов
/ 13 января 2019

Я должен заполнить структуру программы ниже, чтобы вычислить сумму только тех значений в области данных, которые делятся на 8 и сохраните результат в регистре r5. Моя программа должна работать на любое количество значений данных (в пределах памяти и 32-разрядного целочисленного диапазона), и пользователь должен иметь возможность изменять только значения в области данных, и программа продолжит работать правильно.

    THUMB
    AREA RESET, DATA, READONLY
    EXPORT Vectors
    EXPORT Reset_Handler
Vectors
    DCD 0x20001000 ; top of the stack
    DCD Reset_Handler ; reset vector - where the program starts
    AREA Task2Code, CODE, READONLY
    ENTRY
Reset_Handler
num EQU 51
    MOV r1,#0
    MOV r5,#0
    LDR r8,=sum_up
loop
    MOV r2, r1, ROR #3 ; divide by 8
    LDR r2,[r8],#4
    ADD r5,r5,r2
    ADD r1,r1,#1
    CMP r1,#num
    B loop
terminate
    B terminate
sum_up
    DCD -16,100,-456,7,-123,-42,126789,2349,-34,-2344,45,-45,-3345 ; example values
    END

Это код, который я сделал до сих пор, но я не могу его запустить, и я не знаю, как правильно разделить на 8, у кого-нибудь есть какие-нибудь идеи по этому поводу?

Ответы [ 2 ]

0 голосов
/ 14 января 2019

Ответ от @Peter Cordes является всеобъемлющим и точным в отношении использования сдвигов для деления. Однако для выполнения вашей задачи («для вычисления суммы только тех значений в области данных, которые делятся на 8») вам на самом деле не нужно делить на 8. Вам просто нужно проверить, действительно ли число делится на 8.

Вы, вероятно, знаете, что младшие три бита любого числа, которое делится на 8, будут равны нулю в его двоичном представлении, так же как любое число, делимое на 1000, будет иметь свои последние три цифры как ноль в десятичной дроби. И, как повезет, это включает в себя отрицательные числа в двоичном виде - еще один перк арифметики дополнения 2. Итак, ваша задача сводится к проверке, равны ли все три младших бита каждого числа нулю. Если они есть, то число делится на 8; если нет, то это не так.

Инструкция, которую вы ищете для достижения этой цели, TST. Эта инструкция эффективно выполняет побитовую операцию И над своими аргументами и устанавливает флаги состояния в соответствии с результатом, но затем отбрасывает сам результат. Это как ANDS без регистра назначения. Используя TST со значением 0x7 (двоичное число 111) в качестве одного аргумента, результат будет нулевым, если другой операнд делится на 8, и ненулевым в противном случае; поэтому нулевой флаг будет установлен, если другой операнд делится на 8, и будет очищен в противном случае.

Например:

loop
    ; (somehow obtain the next number in r4 here)
    TST r4, #0x7
    BNE loop        ; If the value in r4 is not divisible by 8, continue
    ADD r5, r5, r4  ; Add the value in r4 to the sum in r5
    B loop

Этот пример явно неполон (загрузка значений в r4 не реализована, а конечная ветвь безусловна), но, надеюсь, это даст вам возможность начать.

0 голосов
/ 13 января 2019

Поворот, очевидно, неправильный, который сдвинет младшие биты в положение знакового бита.

Вы хотите сдвиг вправо, например MOV r2, r1, ASR #3, он же asr r2, r1, #3

Или вам нужно целочисленное деление со знаком с тем же поведением округления до нуля, что и для C ? (Арифметическое смещение вправо округляет до -Infinity, но целочисленное деление в стиле C усекается до нуля.)

Если это так, вы можете реализовать его так же, как это делают компиляторы, используя бит знака в качестве исправления. Разделите целое число со знаком на степень 2 . См. Также этот пример x86 Почему компилятор генерирует сдвиг вправо на 31 бит при делении на 2? .

Или просто посмотрите на вывод компилятора ARM в проводнике компилятора Godbolt :

int div8(int a) { return a/8; }

gcc -O3 -mcpu=cortex-m4 (asm-mode asm):

div8(int):
    cmp     r0, #0
    it      lt
    addlt   r0, r0, #7        @@ increase past the next multiple of 8 if it wasn't one
    asrs    r0, r0, #3        @@ arithmetic right-shift by 3
    bx      lr

code-gen clang немного более компактен, но все же все 16-битные инструкции, я думаю, не нуждаются в it для указания одной из них:

div8(int):
    asrs    r1, r0, #31            @ copy the sign bit to all bits of r1
    add.w   r0, r0, r1, lsr #29    @ add 0 or 7 (logical shift right of all-ones or zeros)
    asrs    r0, r0, #3             @ r0 >>= 3
    bx      lr
...