I am trying to annotate a disassembly block for exam practice. Here's what I have done so far:
00000190 <mystery>:
190: 2300 movs r3, #0 // move address 190 (offset 0) into r3 ?
192: e004 b.n 19e <mystery+0xe> // if 19e then branch to mystery
194: f010 0f01 tst.w r0, #1 ; 0x1 // update flags to 1 in status register
198: bf18 it ne // if 198 not equal to ??? then ???
19a: 3301 addne r3, #1 // add to r3 if not equal to 19a offset 1?
19c: 1040 asrs r0, r0, #1 // shift r0 right one spot (leave it in r0)
19e: 2800 cmp r0, #0 // compare contents of r0 against 0 ?
1a0: d1f8 bne.n 194 <mystery+0x4> // branch to 194 if not equal to something at line 194?
1a2: 4618 mov r0, r3 // move r3 wholecloth into r0
1a4: 4770 bx lr // branch(return from the mystery function)
1a6: bf00 nop // No operation
So my comments are pretty rudimentary and likely to be massively incorrect but most of all I really don't understand what instructions such as those at 190 or 19a mean. There are only two arguments instead of three, so how do these work?
Taking as an example
19a: 3301 addne r3, #1
My interpretation of this so far is: if not equal to X, then add Y to r3? What are X and Y? Should I be using the result from the previous line? If so, which argument (of the standard three) does it take the place of?
Blah!
I am willing to accept that I have no idea what I am doing and am completely misinterpreting everything.
Please send help!
1) TST instruction is basically the same as ANDS, except it doesn't change the first operand. So, TST r0, #1 sets flags based on the result of (r0 & 1). Specifically, it will set the Z (zero) flag if the result was zero, i.e. bit 0 of r0 was not set.
2) IT stands for "If-Then". It checks the condition indicated, and conditionally executes up to 4 following instructions. In your example you have only one conditional instruction, which the disassembler helpfully provided with the NE suffix from the IT instruction (the suffix is not encoded in the instruction itself for Thumb-2). NE means "not equal", but in this case there was no comparison, so what gives? The trick is that the equality check checks the Z flag, so you can think of this one as "not Zero". So, our ADD will be executed in case the Z flag was not set, i.e. r0 did have bit 0 set.
3) A similar situation happens around CMP/BNE. CMP basically subtracts operands and sets the flags based on the result. In our case, it will set Z if r0 was equal to 0. Next, BNE will test the Z flag and branch if it was not set (i.e. r0 was not equal to 0).
Converting it all to pseudo-C, we get:
r3 = 0
goto test_loop;
loop:
Z = (r0 & 1) == 0;
if (!Z)
r3 += 1;
r0 = r0 >> 1
test_loop:
Z = (r0 - 0) == 0;
if (!Z) goto loop;
r0 = r3;
return;
Or, in "normal" C:
r3 = 0;
while ( r0 != 0 )
{
if ( r0 & 1 )
r3++;
r0 >>= 1;
}
return r3;
Looks like it's counting bits in r0.
Have a look here for the table of condition codes and what flags they check. This describes how and when the flags are set.
Edit: I just reread your question and realized one source of your confusion. In line like this:
b.n 19e <mystery+0xe>
there is one operand, not two. The disassembler tries to be helpful and shows not just the absolute destination address (19e) but also its representation as an offset from the nearest symbol (mystery is at 190, so 19e is mystery+0xe).
Another thing you need to realize is that in ARM (and many other processors), setting flags and using flags is usually done in separate instructions. That's why you first do TST or CMP (or other flag-setting instruction), and then use conditional instructions, IT, or conditional branches.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With