1

I have to complete the program framework below to calculate the sum of only those values in the data area divisible by 8 and store the result in register r5. My program must be able to work for any number of data values (within memory and 32-bit integer range limits) and it must be possible for a user to change only the values in the data area and the program will continue to work correctly.

    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

This is the code I have made so far but I cannot get it to run, nor do I know how to divide by 8 properly, does anyone have any ideas on this?

cooperised
  • 2,404
  • 1
  • 15
  • 18
Hysteria103
  • 77
  • 1
  • 6

2 Answers2

4

The answer from @Peter Cordes is comprehensive and accurate as regards using shifts to divide. However, to complete your task ("to calculate the sum of only those values in the data area divisible by 8"), you do not actually need to divide by 8. You just need to test whether or not a number is divisible by 8.

You're probably aware that the least significant three bits of any number that's divisible by 8 will be zero in its binary representation, just as any number divisible by 1000 will have its last three digits as zero in decimal. And as luck would have it, this includes negative numbers in binary - just one more perk of 2's complement arithmetic. So your task comes down to testing whether or not the least significant three bits of each number are all zero. If they are, then the number is divisible by 8; if not, then it isn't.

The instruction you're looking for to achieve this is TST. This instruction effectively performs a bitwise AND operation on its arguments, and sets the status flags according to the result, but then discards the result itself. It's like an ANDS without a destination register. By using TST with the value 0x7 (binary 111) as one argument, the result will be zero if the other operand is divisible by 8, and nonzero otherwise; so the zero flag will be set if the other operand is divisible by 8, and it will be cleared otherwise.

For example:

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

This example is clearly incomplete (the loading of values into r4 is not implemented, and the final branch is unconditional), but hopefully it will give you a place to start.

cooperised
  • 2,404
  • 1
  • 15
  • 18
2

A rotate is obviously not right, that will move low bits up to the sign bit position.

You want a right shift, like MOV r2, r1, ASR #3, aka asr r2, r1, #3

Or do you need signed integer division with the same round-towards-zero behaviour as C? (Arithmetic right shift rounds toward -Infinity, but C-style integer division truncates towards zero.)

If so, you can implement it the same way compilers do, using the sign bit as a correction. Divide a signed integer by a power of 2. See also this x86 example Why does the compiler generate a right-shift by 31 bits when dividing by 2?.

Or just look at ARM compiler output on the Godbolt compiler explorer:

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

gcc -O3 -mcpu=cortex-m4 (thumb-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

clang's code-gen is slightly more compact, still all 16-bit instructions I think without needing an it to predicate one of them:

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
Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
  • @old_timer: yeah probably not, I was just a little curious exactly what compilers would use on ARM for signed division. – Peter Cordes Jan 13 '19 at 16:37