Arduino Bloat! (A comparison of the Blink program)
So, I've been working with Arduino lately, learning the environment, and trying to learn more about the bare-metal underneath.
I've installed the AVR plugin into Eclipse (another programming IDE) and I wrote the obligatory "Hello World" program for micro-controllers...the LED "Blink" program.
After compiling and looking at the HEX, I was amazed at the size difference between writing code for the AVR itself, and writing code in the Arduino IDE.
Don't get me wrong, I really like the Arduino ecosystem...and I think Arduino is a GREAT learning environment. However, I wanted to illustrate how much overhead the Arduino IDE puts into its code.
Here are the programs...
Arduino: (Everybody should know this one!)
And the straight "coded for AVR":
As you can see, it's the same program. Turn port PB5 (called "13" in Arduino land) on, wait 100 milliseconds, turn it off, wait 900 milliseconds. Lather, Rinse, Repeat.
Eclipse Compiled the program and it weighed in at 176 bytes (0.5% full) with 0 bytes of data memory used.
Arduino compiled the same program and it came in at: 1,070 bytes! (3% full) with 9 bytes of data memory used!
Here is the HEX file comparison: (if you're a visual person like me)
AVR:
Code: :100000000C9434000C943E000C943E000C943E0082
:100010000C943E000C943E000C943E000C943E0068
:100020000C943E000C943E000C943E000C943E0058
:100030000C943E000C943E000C943E000C943E0048
:100040000C943E000C943E000C943E000C943E0038
:100050000C943E000C943E000C943E000C943E0028
:100060000C943E000C943E0011241FBECFEFD8E04C
:10007000DEBFCDBF0E9440000C9456000C940000DF
:10008000259A2D9A2FE78AE196E0215080409040F2
:10009000E1F700C000002D982FE78EEE96E3215087
:1000A00080409040E1F700C00000EBCFF894FFCF14
:00000001FF
Arduino:
Code: :100000000C945C000C946E000C946E000C946E00CA
:100010000C946E000C946E000C946E000C946E00A8
:100020000C946E000C946E000C946E000C946E0098
:100030000C946E000C946E000C946E000C946E0088
:100040000C9488000C946E000C946E000C946E005E
:100050000C946E000C946E000C946E000C946E0068
:100060000C946E000C946E00000000080002010069
:100070000003040700000000000000000102040863
:100080001020408001020408102001020408102002
:10009000040404040404040402020202020203032E
:1000A0000303030300000000250028002B000000CC
:1000B0000000240027002A0011241FBECFEFD8E043
:1000C000DEBFCDBF21E0A0E0B1E001C01D92A930AC
:1000D000B207E1F70E94EB010C9415020C940000AA
:1000E00061E08DE00C947B0161E08DE00E94B40141
:1000F00064E670E080E090E00E94EF0060E08DE058
:100100000E94B40164E873E080E090E00C94EF009A
:100110001F920F920FB60F9211242F933F938F933C
:100120009F93AF93BF938091010190910201A091A1
:100130000301B09104013091000126E6230F2D3711
:1001400028F029EE230F0196A11DB11D2093000177
:100150008093010190930201A0930301B0930401E5
:100160008091050190910601A0910701B0910801CD
:100170000196A11DB11D8093050190930601A093E6
:100180000701B0930801BF91AF919F918F913F916B
:100190002F910F900FBE0F901F9018959FB7F89456
:1001A000409105015091060160910701709108018D
:1001B00086B5A89B06C08F3F21F04F5F5F4F6F4F02
:1001C0007F4F9FBF2227342F452F562F280F311DD9
:1001D000411D511DA3E0B0E00E94FB010895CF92A4
:1001E000DF92EF92FF92CF93DF936B017C010E942D
:1001F000CE00EB01C114D104E104F10489F00E94A6
:10020000FA010E94CE006C1B7D0B683E734090F398
:1002100081E0C81AD108E108F108C851DC4FEACFE3
:10022000DF91CF91FF90EF90DF90CF900895789479
:1002300084B5826084BD84B5816084BD85B58260EB
:1002400085BD85B5816085BDEEE6F0E08081816089
:100250008083E1E8F0E01082808182608083808189
:1002600081608083E0E8F0E0808181608083E1EB61
:10027000F0E0808184608083E0EBF0E08081816049
:100280008083EAE7F0E080818460808380818260FF
:1002900080838081816080838081806880831092E8
:1002A000C1000895833081F028F4813099F08230C4
:1002B000A1F008958730A9F08830B9F08430D1F4E6
:1002C000809180008F7D03C0809180008F77809324
:1002D0008000089584B58F7702C084B58F7D84BD7A
:1002E00008958091B0008F7703C08091B0008F7D1A
:1002F0008093B0000895CF93DF9390E0FC01E45821
:10030000FF4F2491FC01E057FF4F8491882349F16E
:1003100090E0880F991FFC01E255FF4FA591B49121
:100320008C559F4FFC01C591D4919FB7611108C0B6
:10033000F8948C91209582238C93888182230AC023
:10034000623051F4F8948C91322F309583238C9342
:100350008881822B888304C0F8948C91822B8C93A3
:100360009FBFDF91CF9108950F931F93CF93DF939A
:100370001F92CDB7DEB7282F30E0F901E859FF4FC3
:100380008491F901E458FF4F1491F901E057FF4FB0
:1003900004910023C9F0882321F069830E9452014F
:1003A0006981E02FF0E0EE0FFF1FEC55FF4FA591A4
:1003B000B4919FB7F8948C91611103C0109581237B
:1003C00001C0812B8C939FBF0F90DF91CF911F9124
:1003D0000F91089508950E9417010E94EA010E945A
:1003E0007000C0E0D0E00E9474002097E1F30E940A
:1003F0000000F9CF08950E940602A59F900DB49FBA
:10040000900DA49F800D911D11240895A29FB0010D
:10041000B39FC001A39F700D811D1124911DB29F38
:0E042000700D811D1124911D0895F894FFCFD9
:00000001FF
I then went on to dump the code back into Assembly, as I'm trying to learn how to read Assembly (I don't have a desire to program in it, just read it!) Here are the dumps...
Avr:
Code: BlinkyLight.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
8: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
10: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
14: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
18: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
1c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
20: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
24: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
28: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
2c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
30: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
34: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
38: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
3c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
40: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
44: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
48: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
4c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
50: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
54: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
58: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
5c: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
60: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
64: 0c 94 3e 00 jmp 0x7c ; 0x7c <__bad_interrupt>
00000068 <__ctors_end>:
68: 11 24 eor r1, r1
6a: 1f be out 0x3f, r1 ; 63
6c: cf ef ldi r28, 0xFF ; 255
6e: d8 e0 ldi r29, 0x08 ; 8
70: de bf out 0x3e, r29 ; 62
72: cd bf out 0x3d, r28 ; 61
74: 0e 94 40 00 call 0x80 ; 0x80 <main>
78: 0c 94 56 00 jmp 0xac ; 0xac <_exit>
0000007c <__bad_interrupt>:
7c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
00000080 <main>:
80: 25 9a sbi 0x04, 5 ; 4
82: 2d 9a sbi 0x05, 5 ; 5
84: 2f e7 ldi r18, 0x7F ; 127
86: 8a e1 ldi r24, 0x1A ; 26
88: 96 e0 ldi r25, 0x06 ; 6
8a: 21 50 subi r18, 0x01 ; 1
8c: 80 40 sbci r24, 0x00 ; 0
8e: 90 40 sbci r25, 0x00 ; 0
90: e1 f7 brne .-8 ; 0x8a <main+0xa>
92: 00 c0 rjmp .+0 ; 0x94 <main+0x14>
94: 00 00 nop
96: 2d 98 cbi 0x05, 5 ; 5
98: 2f e7 ldi r18, 0x7F ; 127
9a: 8e ee ldi r24, 0xEE ; 238
9c: 96 e3 ldi r25, 0x36 ; 54
9e: 21 50 subi r18, 0x01 ; 1
a0: 80 40 sbci r24, 0x00 ; 0
a2: 90 40 sbci r25, 0x00 ; 0
a4: e1 f7 brne .-8 ; 0x9e <main+0x1e>
a6: 00 c0 rjmp .+0 ; 0xa8 <main+0x28>
a8: 00 00 nop
aa: eb cf rjmp .-42 ; 0x82 <main+0x2>
000000ac <_exit>:
ac: f8 94 cli
000000ae <__stop_program>:
ae: ff cf rjmp .-2 ; 0xae <__stop_program>
And Arduino:
Code: Blink.ino.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 5c 00 jmp 0xb8 ; 0xb8 <__ctors_end>
4: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
8: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
10: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
14: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
18: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
1c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
20: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
24: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
28: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
2c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
30: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
34: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
38: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
3c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
40: 0c 94 88 00 jmp 0x110 ; 0x110 <__vector_16>
44: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
48: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
4c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
50: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
54: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
58: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
5c: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
60: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
64: 0c 94 6e 00 jmp 0xdc ; 0xdc <__bad_interrupt>
00000068 <__trampolines_end>:
68: 00 00 nop
6a: 00 08 sbc r0, r0
6c: 00 02 muls r16, r16
6e: 01 00 .word 0x0001 ; ????
70: 00 03 mulsu r16, r16
72: 04 07 cpc r16, r20
...
0000007c <digital_pin_to_bit_mask_PGM>:
7c: 01 02 04 08 10 20 40 80 01 02 04 08 10 20 01 02 ..... @...... ..
8c: 04 08 10 20 ...
00000090 <digital_pin_to_port_PGM>:
90: 04 04 04 04 04 04 04 04 02 02 02 02 02 02 03 03 ................
a0: 03 03 03 03 ....
000000a4 <port_to_output_PGM>:
a4: 00 00 00 00 25 00 28 00 2b 00 ....%.(.+.
000000ae <port_to_mode_PGM>:
ae: 00 00 00 00 24 00 27 00 2a 00 ....$.'.*.
000000b8 <__ctors_end>:
b8: 11 24 eor r1, r1
ba: 1f be out 0x3f, r1 ; 63
bc: cf ef ldi r28, 0xFF ; 255
be: d8 e0 ldi r29, 0x08 ; 8
c0: de bf out 0x3e, r29 ; 62
c2: cd bf out 0x3d, r28 ; 61
000000c4 <__do_clear_bss>:
c4: 21 e0 ldi r18, 0x01 ; 1
c6: a0 e0 ldi r26, 0x00 ; 0
c8: b1 e0 ldi r27, 0x01 ; 1
ca: 01 c0 rjmp .+2 ; 0xce <.do_clear_bss_start>
000000cc <.do_clear_bss_loop>:
cc: 1d 92 st X+, r1
000000ce <.do_clear_bss_start>:
ce: a9 30 cpi r26, 0x09 ; 9
d0: b2 07 cpc r27, r18
d2: e1 f7 brne .-8 ; 0xcc <.do_clear_bss_loop>
d4: 0e 94 eb 01 call 0x3d6 ; 0x3d6 <main>
d8: 0c 94 15 02 jmp 0x42a ; 0x42a <_exit>
000000dc <__bad_interrupt>:
dc: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
000000e0 <setup>:
// the setup function runs once when you press reset or power the board
void setup() {
// initialize digital pin 13 as an output.
pinMode(13, OUTPUT);
e0: 61 e0 ldi r22, 0x01 ; 1
e2: 8d e0 ldi r24, 0x0D ; 13
e4: 0c 94 7b 01 jmp 0x2f6 ; 0x2f6 <pinMode>
000000e8 <loop>:
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(13, HIGH); // turn the LED on (HIGH is the voltage level)
e8: 61 e0 ldi r22, 0x01 ; 1
ea: 8d e0 ldi r24, 0x0D ; 13
ec: 0e 94 b4 01 call 0x368 ; 0x368 <digitalWrite>
delay(100); // wait for a second
f0: 64 e6 ldi r22, 0x64 ; 100
f2: 70 e0 ldi r23, 0x00 ; 0
f4: 80 e0 ldi r24, 0x00 ; 0
f6: 90 e0 ldi r25, 0x00 ; 0
f8: 0e 94 ef 00 call 0x1de ; 0x1de <delay>
digitalWrite(13, LOW); // turn the LED off by making the voltage LOW
fc: 60 e0 ldi r22, 0x00 ; 0
fe: 8d e0 ldi r24, 0x0D ; 13
100: 0e 94 b4 01 call 0x368 ; 0x368 <digitalWrite>
delay(900); // wait for a second
104: 64 e8 ldi r22, 0x84 ; 132
106: 73 e0 ldi r23, 0x03 ; 3
108: 80 e0 ldi r24, 0x00 ; 0
10a: 90 e0 ldi r25, 0x00 ; 0
10c: 0c 94 ef 00 jmp 0x1de ; 0x1de <delay>
00000110 <__vector_16>:
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
110: 1f 92 push r1
112: 0f 92 push r0
114: 0f b6 in r0, 0x3f ; 63
116: 0f 92 push r0
118: 11 24 eor r1, r1
11a: 2f 93 push r18
11c: 3f 93 push r19
11e: 8f 93 push r24
120: 9f 93 push r25
122: af 93 push r26
124: bf 93 push r27
// copy these to local variables so they can be stored in registers
// (volatile variables must be read from memory on every access)
unsigned long m = timer0_millis;
126: 80 91 01 01 lds r24, 0x0101
12a: 90 91 02 01 lds r25, 0x0102
12e: a0 91 03 01 lds r26, 0x0103
132: b0 91 04 01 lds r27, 0x0104
unsigned char f = timer0_fract;
136: 30 91 00 01 lds r19, 0x0100
m += MILLIS_INC;
f += FRACT_INC;
13a: 26 e6 ldi r18, 0x66 ; 102
13c: 23 0f add r18, r19
if (f >= FRACT_MAX) {
13e: 2d 37 cpi r18, 0x7D ; 125
140: 28 f0 brcs .+10 ; 0x14c <__vector_16+0x3c>
f -= FRACT_MAX;
142: 29 ee ldi r18, 0xE9 ; 233
144: 23 0f add r18, r19
m += 1;
146: 01 96 adiw r24, 0x01 ; 1
148: a1 1d adc r26, r1
14a: b1 1d adc r27, r1
}
timer0_fract = f;
14c: 20 93 00 01 sts 0x0100, r18
timer0_millis = m;
150: 80 93 01 01 sts 0x0101, r24
154: 90 93 02 01 sts 0x0102, r25
158: a0 93 03 01 sts 0x0103, r26
15c: b0 93 04 01 sts 0x0104, r27
timer0_overflow_count++;
160: 80 91 05 01 lds r24, 0x0105
164: 90 91 06 01 lds r25, 0x0106
168: a0 91 07 01 lds r26, 0x0107
16c: b0 91 08 01 lds r27, 0x0108
170: 01 96 adiw r24, 0x01 ; 1
172: a1 1d adc r26, r1
174: b1 1d adc r27, r1
176: 80 93 05 01 sts 0x0105, r24
17a: 90 93 06 01 sts 0x0106, r25
17e: a0 93 07 01 sts 0x0107, r26
182: b0 93 08 01 sts 0x0108, r27
}
186: bf 91 pop r27
188: af 91 pop r26
18a: 9f 91 pop r25
18c: 8f 91 pop r24
18e: 3f 91 pop r19
190: 2f 91 pop r18
192: 0f 90 pop r0
194: 0f be out 0x3f, r0 ; 63
196: 0f 90 pop r0
198: 1f 90 pop r1
19a: 18 95 reti
0000019c <micros>:
return m;
}
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
19c: 9f b7 in r25, 0x3f ; 63
cli();
19e: f8 94 cli
m = timer0_overflow_count;
1a0: 40 91 05 01 lds r20, 0x0105
1a4: 50 91 06 01 lds r21, 0x0106
1a8: 60 91 07 01 lds r22, 0x0107
1ac: 70 91 08 01 lds r23, 0x0108
#if defined(TCNT0)
t = TCNT0;
1b0: 86 b5 in r24, 0x26 ; 38
#else
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t < 255))
1b2: a8 9b sbis 0x15, 0 ; 21
1b4: 06 c0 rjmp .+12 ; 0x1c2 <micros+0x26>
1b6: 8f 3f cpi r24, 0xFF ; 255
1b8: 21 f0 breq .+8 ; 0x1c2 <micros+0x26>
m++;
1ba: 4f 5f subi r20, 0xFF ; 255
1bc: 5f 4f sbci r21, 0xFF ; 255
1be: 6f 4f sbci r22, 0xFF ; 255
1c0: 7f 4f sbci r23, 0xFF ; 255
#else
if ((TIFR & _BV(TOV0)) && (t < 255))
m++;
#endif
SREG = oldSREG;
1c2: 9f bf out 0x3f, r25 ; 63
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
1c4: 22 27 eor r18, r18
1c6: 34 2f mov r19, r20
1c8: 45 2f mov r20, r21
1ca: 56 2f mov r21, r22
1cc: 28 0f add r18, r24
1ce: 31 1d adc r19, r1
1d0: 41 1d adc r20, r1
1d2: 51 1d adc r21, r1
1d4: a3 e0 ldi r26, 0x03 ; 3
1d6: b0 e0 ldi r27, 0x00 ; 0
1d8: 0e 94 fb 01 call 0x3f6 ; 0x3f6 <__muluhisi3>
}
1dc: 08 95 ret
000001de <delay>:
void delay(unsigned long ms)
{
1de: cf 92 push r12
1e0: df 92 push r13
1e2: ef 92 push r14
1e4: ff 92 push r15
1e6: cf 93 push r28
1e8: df 93 push r29
1ea: 6b 01 movw r12, r22
1ec: 7c 01 movw r14, r24
uint16_t start = (uint16_t)micros();
1ee: 0e 94 ce 00 call 0x19c ; 0x19c <micros>
1f2: eb 01 movw r28, r22
while (ms > 0) {
1f4: c1 14 cp r12, r1
1f6: d1 04 cpc r13, r1
1f8: e1 04 cpc r14, r1
1fa: f1 04 cpc r15, r1
1fc: 89 f0 breq .+34 ; 0x220 <delay+0x42>
yield();
1fe: 0e 94 fa 01 call 0x3f4 ; 0x3f4 <yield>
if (((uint16_t)micros() - start) >= 1000) {
202: 0e 94 ce 00 call 0x19c ; 0x19c <micros>
206: 6c 1b sub r22, r28
208: 7d 0b sbc r23, r29
20a: 68 3e cpi r22, 0xE8 ; 232
20c: 73 40 sbci r23, 0x03 ; 3
20e: 90 f3 brcs .-28 ; 0x1f4 <delay+0x16>
ms--;
210: 81 e0 ldi r24, 0x01 ; 1
212: c8 1a sub r12, r24
214: d1 08 sbc r13, r1
216: e1 08 sbc r14, r1
218: f1 08 sbc r15, r1
start += 1000;
21a: c8 51 subi r28, 0x18 ; 24
21c: dc 4f sbci r29, 0xFC ; 252
21e: ea cf rjmp .-44 ; 0x1f4 <delay+0x16>
}
}
}
220: df 91 pop r29
222: cf 91 pop r28
224: ff 90 pop r15
226: ef 90 pop r14
228: df 90 pop r13
22a: cf 90 pop r12
22c: 08 95 ret
0000022e <init>:
void init()
{
// this needs to be called before setup() or some functions won't
// work there
sei();
22e: 78 94 sei
// on the ATmega168, timer 0 is also used for fast hardware pwm
// (using phase-correct PWM would mean that timer 0 overflowed half as often
// resulting in different millis() behavior on the ATmega8 and ATmega168)
#if defined(TCCR0A) && defined(WGM01)
sbi(TCCR0A, WGM01);
230: 84 b5 in r24, 0x24 ; 36
232: 82 60 ori r24, 0x02 ; 2
234: 84 bd out 0x24, r24 ; 36
sbi(TCCR0A, WGM00);
236: 84 b5 in r24, 0x24 ; 36
238: 81 60 ori r24, 0x01 ; 1
23a: 84 bd out 0x24, r24 ; 36
// this combination is for the standard atmega8
sbi(TCCR0, CS01);
sbi(TCCR0, CS00);
#elif defined(TCCR0B) && defined(CS01) && defined(CS00)
// this combination is for the standard 168/328/1280/2560
sbi(TCCR0B, CS01);
23c: 85 b5 in r24, 0x25 ; 37
23e: 82 60 ori r24, 0x02 ; 2
240: 85 bd out 0x25, r24 ; 37
sbi(TCCR0B, CS00);
242: 85 b5 in r24, 0x25 ; 37
244: 81 60 ori r24, 0x01 ; 1
246: 85 bd out 0x25, r24 ; 37
// enable timer 0 overflow interrupt
#if defined(TIMSK) && defined(TOIE0)
sbi(TIMSK, TOIE0);
#elif defined(TIMSK0) && defined(TOIE0)
sbi(TIMSK0, TOIE0);
248: ee e6 ldi r30, 0x6E ; 110
24a: f0 e0 ldi r31, 0x00 ; 0
24c: 80 81 ld r24, Z
24e: 81 60 ori r24, 0x01 ; 1
250: 80 83 st Z, r24
// this is better for motors as it ensures an even waveform
// note, however, that fast pwm mode can achieve a frequency of up
// 8 MHz (with a 16 MHz clock) at 50% duty cycle
#if defined(TCCR1B) && defined(CS11) && defined(CS10)
TCCR1B = 0;
252: e1 e8 ldi r30, 0x81 ; 129
254: f0 e0 ldi r31, 0x00 ; 0
256: 10 82 st Z, r1
// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
258: 80 81 ld r24, Z
25a: 82 60 ori r24, 0x02 ; 2
25c: 80 83 st Z, r24
#if F_CPU >= 8000000L
sbi(TCCR1B, CS10);
25e: 80 81 ld r24, Z
260: 81 60 ori r24, 0x01 ; 1
262: 80 83 st Z, r24
sbi(TCCR1, CS10);
#endif
#endif
// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
sbi(TCCR1A, WGM10);
264: e0 e8 ldi r30, 0x80 ; 128
266: f0 e0 ldi r31, 0x00 ; 0
268: 80 81 ld r24, Z
26a: 81 60 ori r24, 0x01 ; 1
26c: 80 83 st Z, r24
// set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
sbi(TCCR2B, CS22);
26e: e1 eb ldi r30, 0xB1 ; 177
270: f0 e0 ldi r31, 0x00 ; 0
272: 80 81 ld r24, Z
274: 84 60 ori r24, 0x04 ; 4
276: 80 83 st Z, r24
// configure timer 2 for phase correct pwm (8-bit)
#if defined(TCCR2) && defined(WGM20)
sbi(TCCR2, WGM20);
#elif defined(TCCR2A) && defined(WGM20)
sbi(TCCR2A, WGM20);
278: e0 eb ldi r30, 0xB0 ; 176
27a: f0 e0 ldi r31, 0x00 ; 0
27c: 80 81 ld r24, Z
27e: 81 60 ori r24, 0x01 ; 1
280: 80 83 st Z, r24
#endif
#if defined(ADCSRA)
// set a2d prescaler so we are inside the desired 50-200 KHz range.
#if F_CPU >= 16000000 // 16 MHz / 128 = 125 KHz
sbi(ADCSRA, ADPS2);
282: ea e7 ldi r30, 0x7A ; 122
284: f0 e0 ldi r31, 0x00 ; 0
286: 80 81 ld r24, Z
288: 84 60 ori r24, 0x04 ; 4
28a: 80 83 st Z, r24
sbi(ADCSRA, ADPS1);
28c: 80 81 ld r24, Z
28e: 82 60 ori r24, 0x02 ; 2
290: 80 83 st Z, r24
sbi(ADCSRA, ADPS0);
292: 80 81 ld r24, Z
294: 81 60 ori r24, 0x01 ; 1
296: 80 83 st Z, r24
cbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
#endif
// enable a2d conversions
sbi(ADCSRA, ADEN);
298: 80 81 ld r24, Z
29a: 80 68 ori r24, 0x80 ; 128
29c: 80 83 st Z, r24
// here so they can be used as normal digital i/o; they will be
// reconnected in Serial.begin()
#if defined(UCSRB)
UCSRB = 0;
#elif defined(UCSR0B)
UCSR0B = 0;
29e: 10 92 c1 00 sts 0x00C1, r1
2a2: 08 95 ret
000002a4 <turnOffPWM>:
//
//static inline void turnOffPWM(uint8_t timer) __attribute__ ((always_inline));
//static inline void turnOffPWM(uint8_t timer)
static void turnOffPWM(uint8_t timer)
{
switch (timer)
2a4: 83 30 cpi r24, 0x03 ; 3
2a6: 81 f0 breq .+32 ; 0x2c8 <turnOffPWM+0x24>
2a8: 28 f4 brcc .+10 ; 0x2b4 <turnOffPWM+0x10>
2aa: 81 30 cpi r24, 0x01 ; 1
2ac: 99 f0 breq .+38 ; 0x2d4 <turnOffPWM+0x30>
2ae: 82 30 cpi r24, 0x02 ; 2
2b0: a1 f0 breq .+40 ; 0x2da <turnOffPWM+0x36>
2b2: 08 95 ret
2b4: 87 30 cpi r24, 0x07 ; 7
2b6: a9 f0 breq .+42 ; 0x2e2 <turnOffPWM+0x3e>
2b8: 88 30 cpi r24, 0x08 ; 8
2ba: b9 f0 breq .+46 ; 0x2ea <turnOffPWM+0x46>
2bc: 84 30 cpi r24, 0x04 ; 4
2be: d1 f4 brne .+52 ; 0x2f4 <turnOffPWM+0x50>
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A: cbi(TCCR1A, COM1A1); break;
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B: cbi(TCCR1A, COM1B1); break;
2c0: 80 91 80 00 lds r24, 0x0080
2c4: 8f 7d andi r24, 0xDF ; 223
2c6: 03 c0 rjmp .+6 ; 0x2ce <turnOffPWM+0x2a>
static void turnOffPWM(uint8_t timer)
{
switch (timer)
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A: cbi(TCCR1A, COM1A1); break;
2c8: 80 91 80 00 lds r24, 0x0080
2cc: 8f 77 andi r24, 0x7F ; 127
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B: cbi(TCCR1A, COM1B1); break;
2ce: 80 93 80 00 sts 0x0080, r24
2d2: 08 95 ret
#if defined(TCCR2) && defined(COM21)
case TIMER2: cbi(TCCR2, COM21); break;
#endif
#if defined(TCCR0A) && defined(COM0A1)
case TIMER0A: cbi(TCCR0A, COM0A1); break;
2d4: 84 b5 in r24, 0x24 ; 36
2d6: 8f 77 andi r24, 0x7F ; 127
2d8: 02 c0 rjmp .+4 ; 0x2de <turnOffPWM+0x3a>
#endif
#if defined(TCCR0A) && defined(COM0B1)
case TIMER0B: cbi(TCCR0A, COM0B1); break;
2da: 84 b5 in r24, 0x24 ; 36
2dc: 8f 7d andi r24, 0xDF ; 223
2de: 84 bd out 0x24, r24 ; 36
2e0: 08 95 ret
#endif
#if defined(TCCR2A) && defined(COM2A1)
case TIMER2A: cbi(TCCR2A, COM2A1); break;
2e2: 80 91 b0 00 lds r24, 0x00B0
2e6: 8f 77 andi r24, 0x7F ; 127
2e8: 03 c0 rjmp .+6 ; 0x2f0 <turnOffPWM+0x4c>
#endif
#if defined(TCCR2A) && defined(COM2B1)
case TIMER2B: cbi(TCCR2A, COM2B1); break;
2ea: 80 91 b0 00 lds r24, 0x00B0
2ee: 8f 7d andi r24, 0xDF ; 223
2f0: 80 93 b0 00 sts 0x00B0, r24
2f4: 08 95 ret
000002f6 <pinMode>:
#define ARDUINO_MAIN
#include "wiring_private.h"
#include "pins_arduino.h"
void pinMode(uint8_t pin, uint8_t mode)
{
2f6: cf 93 push r28
2f8: df 93 push r29
uint8_t bit = digitalPinToBitMask(pin);
2fa: 90 e0 ldi r25, 0x00 ; 0
2fc: fc 01 movw r30, r24
2fe: e4 58 subi r30, 0x84 ; 132
300: ff 4f sbci r31, 0xFF ; 255
302: 24 91 lpm r18, Z
uint8_t port = digitalPinToPort(pin);
304: fc 01 movw r30, r24
306: e0 57 subi r30, 0x70 ; 112
308: ff 4f sbci r31, 0xFF ; 255
30a: 84 91 lpm r24, Z
volatile uint8_t *reg, *out;
if (port == NOT_A_PIN) return;
30c: 88 23 and r24, r24
30e: 49 f1 breq .+82 ; 0x362 <pinMode+0x6c>
// JWS: can I let the optimizer do this?
reg = portModeRegister(port);
310: 90 e0 ldi r25, 0x00 ; 0
312: 88 0f add r24, r24
314: 99 1f adc r25, r25
316: fc 01 movw r30, r24
318: e2 55 subi r30, 0x52 ; 82
31a: ff 4f sbci r31, 0xFF ; 255
31c: a5 91 lpm r26, Z+
31e: b4 91 lpm r27, Z
out = portOutputRegister(port);
320: 8c 55 subi r24, 0x5C ; 92
322: 9f 4f sbci r25, 0xFF ; 255
324: fc 01 movw r30, r24
326: c5 91 lpm r28, Z+
328: d4 91 lpm r29, Z
if (mode == INPUT) {
uint8_t oldSREG = SREG;
32a: 9f b7 in r25, 0x3f ; 63
// JWS: can I let the optimizer do this?
reg = portModeRegister(port);
out = portOutputRegister(port);
if (mode == INPUT) {
32c: 61 11 cpse r22, r1
32e: 08 c0 rjmp .+16 ; 0x340 <pinMode+0x4a>
uint8_t oldSREG = SREG;
cli();
330: f8 94 cli
*reg &= ~bit;
332: 8c 91 ld r24, X
334: 20 95 com r18
336: 82 23 and r24, r18
338: 8c 93 st X, r24
*out &= ~bit;
33a: 88 81 ld r24, Y
33c: 82 23 and r24, r18
33e: 0a c0 rjmp .+20 ; 0x354 <pinMode+0x5e>
SREG = oldSREG;
} else if (mode == INPUT_PULLUP) {
340: 62 30 cpi r22, 0x02 ; 2
342: 51 f4 brne .+20 ; 0x358 <pinMode+0x62>
uint8_t oldSREG = SREG;
cli();
344: f8 94 cli
*reg &= ~bit;
346: 8c 91 ld r24, X
348: 32 2f mov r19, r18
34a: 30 95 com r19
34c: 83 23 and r24, r19
34e: 8c 93 st X, r24
*out |= bit;
350: 88 81 ld r24, Y
352: 82 2b or r24, r18
354: 88 83 st Y, r24
356: 04 c0 rjmp .+8 ; 0x360 <pinMode+0x6a>
SREG = oldSREG;
} else {
uint8_t oldSREG = SREG;
cli();
358: f8 94 cli
*reg |= bit;
35a: 8c 91 ld r24, X
35c: 82 2b or r24, r18
35e: 8c 93 st X, r24
SREG = oldSREG;
360: 9f bf out 0x3f, r25 ; 63
}
}
362: df 91 pop r29
364: cf 91 pop r28
366: 08 95 ret
00000368 <digitalWrite>:
#endif
}
}
void digitalWrite(uint8_t pin, uint8_t val)
{
368: 0f 93 push r16
36a: 1f 93 push r17
36c: cf 93 push r28
36e: df 93 push r29
370: 1f 92 push r1
372: cd b7 in r28, 0x3d ; 61
374: de b7 in r29, 0x3e ; 62
uint8_t timer = digitalPinToTimer(pin);
376: 28 2f mov r18, r24
378: 30 e0 ldi r19, 0x00 ; 0
37a: f9 01 movw r30, r18
37c: e8 59 subi r30, 0x98 ; 152
37e: ff 4f sbci r31, 0xFF ; 255
380: 84 91 lpm r24, Z
uint8_t bit = digitalPinToBitMask(pin);
382: f9 01 movw r30, r18
384: e4 58 subi r30, 0x84 ; 132
386: ff 4f sbci r31, 0xFF ; 255
388: 14 91 lpm r17, Z
uint8_t port = digitalPinToPort(pin);
38a: f9 01 movw r30, r18
38c: e0 57 subi r30, 0x70 ; 112
38e: ff 4f sbci r31, 0xFF ; 255
390: 04 91 lpm r16, Z
volatile uint8_t *out;
if (port == NOT_A_PIN) return;
392: 00 23 and r16, r16
394: c9 f0 breq .+50 ; 0x3c8 <digitalWrite+0x60>
// If the pin that support PWM output, we need to turn it off
// before doing a digital write.
if (timer != NOT_ON_TIMER) turnOffPWM(timer);
396: 88 23 and r24, r24
398: 21 f0 breq .+8 ; 0x3a2 <digitalWrite+0x3a>
39a: 69 83 std Y+1, r22 ; 0x01
39c: 0e 94 52 01 call 0x2a4 ; 0x2a4 <turnOffPWM>
3a0: 69 81 ldd r22, Y+1 ; 0x01
out = portOutputRegister(port);
3a2: e0 2f mov r30, r16
3a4: f0 e0 ldi r31, 0x00 ; 0
3a6: ee 0f add r30, r30
3a8: ff 1f adc r31, r31
3aa: ec 55 subi r30, 0x5C ; 92
3ac: ff 4f sbci r31, 0xFF ; 255
3ae: a5 91 lpm r26, Z+
3b0: b4 91 lpm r27, Z
uint8_t oldSREG = SREG;
3b2: 9f b7 in r25, 0x3f ; 63
cli();
3b4: f8 94 cli
if (val == LOW) {
*out &= ~bit;
3b6: 8c 91 ld r24, X
out = portOutputRegister(port);
uint8_t oldSREG = SREG;
cli();
if (val == LOW) {
3b8: 61 11 cpse r22, r1
3ba: 03 c0 rjmp .+6 ; 0x3c2 <digitalWrite+0x5a>
*out &= ~bit;
3bc: 10 95 com r17
3be: 81 23 and r24, r17
3c0: 01 c0 rjmp .+2 ; 0x3c4 <digitalWrite+0x5c>
} else {
*out |= bit;
3c2: 81 2b or r24, r17
3c4: 8c 93 st X, r24
}
SREG = oldSREG;
3c6: 9f bf out 0x3f, r25 ; 63
}
3c8: 0f 90 pop r0
3ca: df 91 pop r29
3cc: cf 91 pop r28
3ce: 1f 91 pop r17
3d0: 0f 91 pop r16
3d2: 08 95 ret
000003d4 <initVariant>:
int atexit(void (* /*func*/ )()) { return 0; }
// Weak empty variant initialization function.
// May be redefined by variant files.
void initVariant() __attribute__((weak));
void initVariant() { }
3d4: 08 95 ret
000003d6 <main>:
void setupUSB() __attribute__((weak));
void setupUSB() { }
int main(void)
{
init();
3d6: 0e 94 17 01 call 0x22e ; 0x22e <init>
initVariant();
3da: 0e 94 ea 01 call 0x3d4 ; 0x3d4 <initVariant>
#if defined(USBCON)
USBDevice.attach();
#endif
setup();
3de: 0e 94 70 00 call 0xe0 ; 0xe0 <setup>
for (;;) {
loop();
if (serialEventRun) serialEventRun();
3e2: c0 e0 ldi r28, 0x00 ; 0
3e4: d0 e0 ldi r29, 0x00 ; 0
#endif
setup();
for (;;) {
loop();
3e6: 0e 94 74 00 call 0xe8 ; 0xe8 <loop>
if (serialEventRun) serialEventRun();
3ea: 20 97 sbiw r28, 0x00 ; 0
3ec: e1 f3 breq .-8 ; 0x3e6 <main+0x10>
3ee: 0e 94 00 00 call 0 ; 0x0 <__vectors>
3f2: f9 cf rjmp .-14 ; 0x3e6 <main+0x10>
000003f4 <yield>:
* libraries or sketches that supports cooperative threads.
*
* Its defined as a weak symbol and it can be redefined to implement a
* real cooperative scheduler.
*/
static void __empty() {
3f4: 08 95 ret
000003f6 <__muluhisi3>:
3f6: 0e 94 06 02 call 0x40c ; 0x40c <__umulhisi3>
3fa: a5 9f mul r26, r21
3fc: 90 0d add r25, r0
3fe: b4 9f mul r27, r20
400: 90 0d add r25, r0
402: a4 9f mul r26, r20
404: 80 0d add r24, r0
406: 91 1d adc r25, r1
408: 11 24 eor r1, r1
40a: 08 95 ret
0000040c <__umulhisi3>:
40c: a2 9f mul r26, r18
40e: b0 01 movw r22, r0
410: b3 9f mul r27, r19
412: c0 01 movw r24, r0
414: a3 9f mul r26, r19
416: 70 0d add r23, r0
418: 81 1d adc r24, r1
41a: 11 24 eor r1, r1
41c: 91 1d adc r25, r1
41e: b2 9f mul r27, r18
420: 70 0d add r23, r0
422: 81 1d adc r24, r1
424: 11 24 eor r1, r1
426: 91 1d adc r25, r1
428: 08 95 ret
0000042a <_exit>:
42a: f8 94 cli
0000042c <__stop_program>:
42c: ff cf rjmp .-2 ; 0x42c <__stop_program>
I've taken some time and figured out the AVR assembly. I may post a quick write-up walking us through the code...I don't think I'll take the time to figure out all the Arduino code!
The Assembly is very elegant, and fun to figure out.
I just wanted to show a little bit of what happens "under the hood".
Brendan
|