add, then adc
Adding two 64-bit values is two instructions, and the order between them is fixed by the hardware. add sets the carry flag when the low halves overflow past 2^32, and adc adds that carry in on top of the high halves. So the low halves always go first, because the carry only flows upward:
0 add r0, r2
2 adc r1, r3
4 bx lr
Two pairs in, r0:r1 and r2:r3; one pair out, in r0:r1. Three instructions and no stack frame. Any time you see add rX, rY immediately followed by adc rX+1, rY+1, that is one + on a 64-bit value, not two additions.
Adding a constant is stranger. Thumb has no 64-bit immediate, so the compiler has to materialise the constant as a full register pair - and it builds it in r0:r1, the pair the incoming argument is already sitting in. The argument gets evacuated first, and the addition ends up reading backwards, with the constant as the left operand:
0 mov r3, r1
2 mov r2, r0
4 ldr r1, [pc, #12] (->20)
6 ldr r0, [pc, #8] (->16)
8 add r0, r2
10 adc r1, r3
12 bx lr
14 .hword 0
16 .word 1000
20 .word 0
mov r3, r1 / mov r2, r0 moves the argument out to r2:r3. Then both halves of the constant arrive from the literal pool: 1000 is past the 8-bit immediate limit so it needs a pool word, and the high half - which is zero - gets a .word 0 of its very own rather than a mov r1, #0. Four bytes of ROM spent on a word of zeros, because the pool load was already being emitted for the other half.
The .hword 0 at address 14 is the usual alignment padding, and the bx lr at 12 means none of it executes.
Your target runs the same two instructions on a value the function has to fetch before it can touch it. The .word row tells you where that value lives, and the offsets off it tell you which half is which.
Your task
Write func_0838ff1c to reproduce the target assembly.