1

I was taking a look at the disassembly for a loop of mine that seemed to be taking too long and I found these assembly instructions that I don't understand. Why does it load 0 into r25 and then do two adc r25, 25 instructions (which wouldn't do anything since r25 is 0). This is for an ATTiny84 by the way.

EDIT: In general I'm confused about why there are so many instructions required to set DDRA, DDRB, PORTA, and PORTB in the last loop. I feel like an LD to read portOuts[j], OUT to output, and increment of j should be all that's required. I've tried to separate what I think is the relevant part in the disassembly.

void loop() {
  set_sleep_mode(SLEEP_MODE_IDLE);
  button = false;
  DDRA = 0;
  DDRB = 0;
  PORTA = 255;
  PORTB = 255;
  do {
    sleep_mode();
  } while (!button);

//  int digits[4];
//  getTime(digits);
//
//  long lights = calcDisp(digits);
  static const byte highLow[28][2] = {{2, 0}, {2, 1}, {0, 3}, {1, 3}, {1, 0}, {3, 1}, {0, 1},
                                     {0, 2}, {3, 0}, {2, 4}, {3, 4}, {4, 3}, {0, 4}, {4, 0},
                                     {4, 1}, {1, 4}, {1, 5}, {5, 1}, {1, 2}, {5, 3}, {2, 3},
                                     {2, 5}, {5, 2}, {5, 4}, {4, 5}, {3, 5}, {4, 2}, {3, 2}};
  byte portOuts[112];
  byte nLed = 0;
  long lights = 0b00000000000000001111111111111111; // just a constant, for now
  for (byte b = 0; b < 16; ++b) {
    if (bitRead(lights, b)) {
      byte high = highLow[b][0];
      byte low = highLow[b][1];
      byte highA = regs[high][0];
      byte highB = regs[high][1];
      byte lowA = regs[low][0];
      byte lowB = regs[low][1];
      portOuts[4 * nLed] = (highA)|(lowA);
      portOuts[4 * nLed + 1] = (highB)|(lowB);
      portOuts[4 * nLed + 2] = (highA)|(1<<PA6);
      portOuts[4 * nLed + 3] = (highB);
      ++nLed;
    }
  }
  for (byte i = 0; i < 200; ++i) {
    for (byte j = 0; j < nLed * 4;) {
      DDRA = portOuts[j++];
      DDRB = portOuts[j++];
      PORTA = portOuts[j++];
      PORTB = portOuts[j++];
    }
  }

DISASSEMBLY

void loop() {
  set_sleep_mode(SLEEP_MODE_IDLE);
  button = false;
  DDRA = 0;
  DDRB = 0;
  PORTA = 255;
 228:   bb 24           eor     r11, r11
 22a:   ba 94           dec     r11
                                     {2, 5}, {5, 2}, {5, 4}, {4, 5}, {3, 5}, {4, 2}, {3, 2}};
  byte portOuts[112];
  byte nLed = 0;
  long lights = 0b00000000000000001111111111111111; // just a constant, for now
  for (byte b = 0; b < 16; ++b) {
    if (bitRead(lights, b)) {
 22c:   cc 24           eor     r12, r12
 22e:   ca 94           dec     r12
 230:   dc 2c           mov     r13, r12
 232:   e1 2c           mov     r14, r1
 234:   f1 2c           mov     r15, r1
  }
  return lights;
}

void loop() {
  set_sleep_mode(SLEEP_MODE_IDLE);
 236:   85 b7           in      r24, 0x35       ; 53
 238:   87 7e           andi    r24, 0xE7       ; 231
 23a:   85 bf           out     0x35, r24       ; 53
  button = false;
 23c:   10 92 a6 00     sts     0x00A6, r1      ; 0x8000a6 <button>
  DDRA = 0;
 240:   1a ba           out     0x1a, r1        ; 26
  DDRB = 0;
 242:   17 ba           out     0x17, r1        ; 23
  PORTA = 255;
 244:   bb ba           out     0x1b, r11       ; 27
  PORTB = 255;
 246:   b8 ba           out     0x18, r11       ; 24
  do {
    sleep_mode();
 248:   85 b7           in      r24, 0x35       ; 53
 24a:   80 62           ori     r24, 0x20       ; 32
 24c:   85 bf           out     0x35, r24       ; 53
 24e:   88 95           sleep
 250:   85 b7           in      r24, 0x35       ; 53
 252:   8f 7d           andi    r24, 0xDF       ; 223
 254:   85 bf           out     0x35, r24       ; 53
  } while (!button);
 256:   80 91 a6 00     lds     r24, 0x00A6     ; 0x8000a6 <button>
 25a:   88 23           and     r24, r24
 25c:   a9 f3           breq    .-22            ; 0x248 <main+0xec>
 25e:   ac e6           ldi     r26, 0x6C       ; 108
 260:   b0 e0           ldi     r27, 0x00       ; 0
 262:   90 e0           ldi     r25, 0x00       ; 0
 264:   80 e0           ldi     r24, 0x00       ; 0
  static const byte highLow[28][2] = {{2, 0}, {2, 1}, {0, 3}, {1, 3}, {1, 0}, {3, 1}, {0, 1},
                                     {0, 2}, {3, 0}, {2, 4}, {3, 4}, {4, 3}, {0, 4}, {4, 0},
                                     {4, 1}, {1, 4}, {1, 5}, {5, 1}, {1, 2}, {5, 3}, {2, 3},
                                     {2, 5}, {5, 2}, {5, 4}, {4, 5}, {3, 5}, {4, 2}, {3, 2}};
  byte portOuts[112];
  byte nLed = 0;
 266:   60 e0           ldi     r22, 0x00       ; 0
 268:   30 e0           ldi     r19, 0x00       ; 0
 26a:   20 e0           ldi     r18, 0x00       ; 0
 26c:   a9 01           movw    r20, r18
 26e:   44 0f           add     r20, r20
 270:   55 1f           adc     r21, r21
 272:   44 0f           add     r20, r20
 274:   55 1f           adc     r21, r21
  long lights = 0b00000000000000001111111111111111; // just a constant, for now
  for (byte b = 0; b < 16; ++b) {
    if (bitRead(lights, b)) {
 276:   26 01           movw    r4, r12
 278:   37 01           movw    r6, r14
 27a:   08 2e           mov     r0, r24
 27c:   04 c0           rjmp    .+8             ; 0x286 <__stack+0x27>
 27e:   75 94           asr     r7
 280:   67 94           ror     r6
 282:   57 94           ror     r5
 284:   47 94           ror     r4
 286:   0a 94           dec     r0
 288:   d2 f7           brpl    .-12            ; 0x27e <__stack+0x1f>
 28a:   40 fe           sbrs    r4, 0
 28c:   3d c0           rjmp    .+122           ; 0x308 <__stack+0xa9>
      byte high = highLow[b][0];
      byte low = highLow[b][1];
      byte highA = regs[high][0];
 28e:   ec 91           ld      r30, X
 290:   f0 e0           ldi     r31, 0x00       ; 0
 292:   ee 0f           add     r30, r30
 294:   ff 1f           adc     r31, r31
 296:   e0 5a           subi    r30, 0xA0       ; 160
 298:   ff 4f           sbci    r31, 0xFF       ; 255
 29a:   70 81           ld      r23, Z
      byte highB = regs[high][1];
 29c:   a1 80           ldd     r10, Z+1        ; 0x01
      byte lowA = regs[low][0];
 29e:   11 96           adiw    r26, 0x01       ; 1
 2a0:   ec 91           ld      r30, X
 2a2:   11 97           sbiw    r26, 0x01       ; 1
 2a4:   f0 e0           ldi     r31, 0x00       ; 0
      byte lowB = regs[low][1];
 2a6:   ee 0f           add     r30, r30
 2a8:   ff 1f           adc     r31, r31
 2aa:   e0 5a           subi    r30, 0xA0       ; 160
 2ac:   ff 4f           sbci    r31, 0xFF       ; 255
 2ae:   91 80           ldd     r9, Z+1 ; 0x01
      portOuts[4 * nLed] = (highA)|(lowA);
 2b0:   21 e0           ldi     r18, 0x01       ; 1
 2b2:   30 e0           ldi     r19, 0x00       ; 0
 2b4:   2c 0f           add     r18, r28
 2b6:   3d 1f           adc     r19, r29
 2b8:   24 0f           add     r18, r20
 2ba:   35 1f           adc     r19, r21
 2bc:   e0 81           ld      r30, Z
 2be:   7e 2e           mov     r7, r30
 2c0:   77 2a           or      r7, r23
 2c2:   f9 01           movw    r30, r18
 2c4:   70 82           st      Z, r7
      portOuts[4 * nLed + 1] = (highB)|(lowB);
 2c6:   e2 e0           ldi     r30, 0x02       ; 2
 2c8:   f0 e0           ldi     r31, 0x00       ; 0
 2ca:   ec 0f           add     r30, r28
 2cc:   fd 1f           adc     r31, r29
 2ce:   e4 0f           add     r30, r20
 2d0:   f5 1f           adc     r31, r21
 2d2:   2a 2d           mov     r18, r10
 2d4:   29 29           or      r18, r9
 2d6:   20 83           st      Z, r18
      portOuts[4 * nLed + 2] = (highA)|(1<<PA6);
 2d8:   e3 e0           ldi     r30, 0x03       ; 3
 2da:   f0 e0           ldi     r31, 0x00       ; 0
 2dc:   ec 0f           add     r30, r28
 2de:   fd 1f           adc     r31, r29
 2e0:   e4 0f           add     r30, r20
 2e2:   f5 1f           adc     r31, r21
 2e4:   70 64           ori     r23, 0x40       ; 64
 2e6:   70 83           st      Z, r23
      portOuts[4 * nLed + 3] = (highB);
 2e8:   24 e0           ldi     r18, 0x04       ; 4
 2ea:   30 e0           ldi     r19, 0x00       ; 0
 2ec:   2c 0f           add     r18, r28
 2ee:   3d 1f           adc     r19, r29
 2f0:   42 0f           add     r20, r18
 2f2:   53 1f           adc     r21, r19
 2f4:   fa 01           movw    r30, r20
 2f6:   a0 82           st      Z, r10
      ++nLed;
 2f8:   6f 5f           subi    r22, 0xFF       ; 255
 2fa:   26 2f           mov     r18, r22
 2fc:   30 e0           ldi     r19, 0x00       ; 0
 2fe:   a9 01           movw    r20, r18
 300:   44 0f           add     r20, r20
 302:   55 1f           adc     r21, r21
 304:   44 0f           add     r20, r20
 306:   55 1f           adc     r21, r21
 308:   01 96           adiw    r24, 0x01       ; 1
 30a:   12 96           adiw    r26, 0x02       ; 2
                                     {4, 1}, {1, 4}, {1, 5}, {5, 1}, {1, 2}, {5, 3}, {2, 3},
                                     {2, 5}, {5, 2}, {5, 4}, {4, 5}, {3, 5}, {4, 2}, {3, 2}};
  byte portOuts[112];
  byte nLed = 0;
  long lights = 0b00000000000000001111111111111111; // just a constant, for now
  for (byte b = 0; b < 16; ++b) {
 30c:   80 31           cpi     r24, 0x10       ; 16
 30e:   91 05           cpc     r25, r1
 310:   09 f0           breq    .+2             ; 0x314 <__stack+0xb5>
 312:   ac cf           rjmp    .-168           ; 0x26c <__stack+0xd>
 314:   68 ec           ldi     r22, 0xC8       ; 200
      portOuts[4 * nLed + 3] = (highB);
      ++nLed;
    }
  }

####################### START OF RELEVANT SECTION #############################

  for (byte i = 0; i < 200; ++i) {
    for (byte j = 0; j < nLed * 4;) {
 316:   41 15           cp      r20, r1
 318:   51 05           cpc     r21, r1
 31a:   99 f1           breq    .+102           ; 0x382 <__stack+0x123>
 31c:   30 e0           ldi     r19, 0x00       ; 0
 31e:   20 e0           ldi     r18, 0x00       ; 0
 320:   80 e0           ldi     r24, 0x00       ; 0
      DDRA = portOuts[j++];
 322:   e1 e0           ldi     r30, 0x01       ; 1
 324:   f0 e0           ldi     r31, 0x00       ; 0
 326:   ec 0f           add     r30, r28
 328:   fd 1f           adc     r31, r29
 32a:   2e 0f           add     r18, r30
 32c:   3f 1f           adc     r19, r31
 32e:   f9 01           movw    r30, r18
 330:   90 81           ld      r25, Z
 332:   9a bb           out     0x1a, r25       ; 26      STORE IN DDRA
 334:   e1 e0           ldi     r30, 0x01       ; 1
 336:   e8 0f           add     r30, r24
      DDRB = portOuts[j++];
 338:   21 e0           ldi     r18, 0x01       ; 1
 33a:   30 e0           ldi     r19, 0x00       ; 0
 33c:   2c 0f           add     r18, r28
 33e:   3d 1f           adc     r19, r29
 340:   2e 0f           add     r18, r30
 342:   31 1d           adc     r19, r1
 344:   f9 01           movw    r30, r18
 346:   90 81           ld      r25, Z
 348:   97 bb           out     0x17, r25       ; 23      STORE IN DDRB
      PORTA = portOuts[j++];
 34a:   e3 e0           ldi     r30, 0x03       ; 3
 34c:   e8 0f           add     r30, r24
    }
  }
  for (byte i = 0; i < 200; ++i) {
    for (byte j = 0; j < nLed * 4;) {
      DDRA = portOuts[j++];
      DDRB = portOuts[j++];
 34e:   a2 e0           ldi     r26, 0x02       ; 2
 350:   a8 0f           add     r26, r24
      PORTA = portOuts[j++];
 352:   21 e0           ldi     r18, 0x01       ; 1
 354:   30 e0           ldi     r19, 0x00       ; 0
 356:   2c 0f           add     r18, r28
 358:   3d 1f           adc     r19, r29
 35a:   2a 0f           add     r18, r26
 35c:   31 1d           adc     r19, r1
 35e:   d9 01           movw    r26, r18
 360:   9c 91           ld      r25, X
 362:   9b bb           out     0x1b, r25       ; 27      STORE IN PORTA
      PORTB = portOuts[j++];
 364:   8c 5f           subi    r24, 0xFC       ; 252
 366:   21 e0           ldi     r18, 0x01       ; 1
 368:   30 e0           ldi     r19, 0x00       ; 0
 36a:   2c 0f           add     r18, r28
 36c:   3d 1f           adc     r19, r29
 36e:   2e 0f           add     r18, r30
 370:   31 1d           adc     r19, r1
 372:   f9 01           movw    r30, r18
 374:   90 81           ld      r25, Z
 376:   98 bb           out     0x18, r25       ; 24      STORE IN PORTB
      portOuts[4 * nLed + 3] = (highB);
      ++nLed;
    }
  }
  for (byte i = 0; i < 200; ++i) {
    for (byte j = 0; j < nLed * 4;) {
 378:   28 2f           mov     r18, r24
 37a:   30 e0           ldi     r19, 0x00       ; 0
 37c:   24 17           cp      r18, r20
 37e:   35 07           cpc     r19, r21
 380:   84 f2           brlt    .-96            ; 0x322 <__stack+0xc3>   JUMP BACK TO START OF LOOP
 382:   61 50           subi    r22, 0x01       ; 1
      portOuts[4 * nLed + 2] = (highA)|(1<<PA6);
      portOuts[4 * nLed + 3] = (highB);
      ++nLed;
    }
  }
  for (byte i = 0; i < 200; ++i) {
 384:   41 f6           brne    .-112           ; 0x316 <__stack+0xb7>

        setup();

        for (;;) {
                loop();
                if (serialEventRun) serialEventRun();
 386:   01 15           cp      r16, r1
 388:   11 05           cpc     r17, r1
 38a:   09 f4           brne    .+2             ; 0x38e <__stack+0x12f>
 38c:   54 cf           rjmp    .-344           ; 0x236 <main+0xda>
 38e:   38 de           rcall   .-912           ; 0x0 <__vectors>
 390:   52 cf           rjmp    .-348           ; 0x236 <main+0xda>```
  • Would have to see more code to see what is going on - the compiler can move stuff around so that asm code could be based on source code somewhere else. – bigjosh May 24 at 16:02
  • which wouldn't do anything since r25 is 0 ... not quite true ... think about what adc does – jsotola May 24 at 16:04
  • For example, there could be a jump somewhere to 30e after r25 had been updated to a new value. In this case the ldi r25,0 is just initializing the first pass and it would not be worth it to jump over the initial adds even though they dont do anything on that first pass. . – bigjosh May 24 at 16:04
  • Also seems like r24 is coming into this block with a useful value, but that code is not shown. – bigjosh May 24 at 16:09
1

The compiler optimized the computation nLed * 4 into a bit shift, as Led << 2. It seems nLed is a single byte quantity. However, nLed * 4 may not fit in one byte, and the semantics of the language demands that nLed is promoted to an int before the computation. This is done by extending the variable by a byte of zeros:

    ldi r25, 0

Now the register pair r25:r24 contains nLed extended to 16 bits.

Next step is to shift the whole thing left by two positions. This is done by repeating this sequence twice:

    lsl r24
    rol r25

The first instruction shifts r24 to the left. The most significant bit is stored as the carry flag. The second instruction “rotate left through carry”, is similar to a shift except that the rightmost bit position is filled by the carry. This is the way to shift a 16-bit value without loosing a bit.

The instruction lsl and rol are actually aliases of add and adc respectively. The disassembler does not care about aliases, and translates the machine code to the most “canonical” instructions, which is what you see.

| improve this answer | |
  • Hi Edgar, that's really interesting! I just edited my post to include some additional information. 2 questions is it possible to avoid the promotion and is that what's happening with my j++ lines as well? – Anubhav May 24 at 16:46
  • @Anubhav: 1. The promotion is demanded by the language, but if you write (byte)(nLed * 4) the compiler may realize it can optimize it out. 2. The statements DDRA = portOuts[j++]; and following require some address calculations. You may get better results by declaring portOuts as static, which would make its address a compile-time constant. – Edgar Bonet May 24 at 17:01
  • 1
    Thanks, that seemed to make it better. What made it really good was doing byte* p = portOuts; outside of my inner loop and then just incrementing that pointer. Now the compiler just generates an LDD and an out as expected. – Anubhav May 24 at 18:12

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.