Tips and Tricks (15)
Diassembling code!
What for? Well, once you fight for speed and code efficiency, you may have to get into the ultimate human readable code: assembler.
In the latest Arduino revisions, getting to here is surprisingly not so trivial. The given explanations will work for PC running XP.
First of all, make the hidden files visible. Open “My computer”, under “Tools” select “File options…”, check “Display”, activate the “Display hidden files and directories” radio button. The hidden files and directories will not appear in a shaded manner in the browser.
Then you have to tell Arduino IDE that you decided to change the place where the bits and pieces required to build the .hex files have be recorded. By default, they are recorded @
C:\Documents and Settings\[user_name]\Local Settings\Temp
Close all running Arduino IDEs! Find and open the preference.txt file which is located @
C:\Documents and Settings\[user_name]\Application Data\Arduino
Warning: do not modify the preference.txt file from C:\Program Files\arduino-1.0\lib. This file contains the default settings.
Append the following line:
build.path=[destination_directory]
As an organized man, this is the way I managed it:
build.path=C:\Documents and Settings\[User]\Mes documents\Arduino\Tools\Diassemble\Build
Save and close the preference.txt file, open an existing sketch and compile it (no need to upload). the .o, .a, .eep, .elf, .hex files are now available (and visible)in the specified destination directory.
Now, let’s write a very simple script which will launch avr-objdump.exe and diassemble the .elf file
Open a new text file and name it at your convenience (e.g. diassemble.bat). Enter the following text:
C:\PROGRA~1\arduino-1.0\hardware\tools\avr\bin\avr-objdump.exe -S C:\DOCUME~1\[user_name]\MESDOC~1\Arduino\Tools\Diassemble\Build*.elf > C:\DOCUME~1\[user_name]\MESDOC~1\Arduino\Tools\Diassemble\Build\diassembled_code.s pause
Note: avr-objdump.exe expects DOS arguments so that short names must be used. Use the dir /x option in a DOS session in order to get these names.
Points of attention:
- All your builds will now be recorded in the specified directory
- The files will leave as long as your Arduino session is open and shall be removed when closing it
- The use of *.elf in the script allows a moderate degree of automation. In this way, you do not have to update the script for all projects.
Here comes the basic blink.ino sketch…
C:\DOCUME~1\[User_name]\MESDOC~1\Arduino\Tools\Diassemble\Build\Blink.cpp.elf: file format elf32-avr
Disassembly of section .text:
00000000 :
timer0_millis = m;
timer0_overflow_count++;
}
unsigned long millis()
{
0: 0c 94 61 00 jmp 0xc2 ; 0xc2
SREG = oldSREG;
}
int digitalRead(uint8_t pin)
{
4: 0c 94 7e 00 jmp 0xfc ; 0xfc
8: 0c 94 7e 00 jmp 0xfc ; 0xfc
c: 0c 94 7e 00 jmp 0xfc ; 0xfc
10: 0c 94 7e 00 jmp 0xfc ; 0xfc
14: 0c 94 7e 00 jmp 0xfc ; 0xfc
18: 0c 94 7e 00 jmp 0xfc ; 0xfc
1c: 0c 94 7e 00 jmp 0xfc ; 0xfc
20: 0c 94 7e 00 jmp 0xfc ; 0xfc
24: 0c 94 7e 00 jmp 0xfc ; 0xfc
28: 0c 94 7e 00 jmp 0xfc ; 0xfc
2c: 0c 94 7e 00 jmp 0xfc ; 0xfc
30: 0c 94 7e 00 jmp 0xfc ; 0xfc
34: 0c 94 7e 00 jmp 0xfc ; 0xfc
38: 0c 94 7e 00 jmp 0xfc ; 0xfc
3c: 0c 94 7e 00 jmp 0xfc ; 0xfc
40: 0c 94 9a 00 jmp 0x134 ; 0x134
44: 0c 94 7e 00 jmp 0xfc ; 0xfc
48: 0c 94 7e 00 jmp 0xfc ; 0xfc
4c: 0c 94 7e 00 jmp 0xfc ; 0xfc
50: 0c 94 7e 00 jmp 0xfc ; 0xfc
54: 0c 94 7e 00 jmp 0xfc ; 0xfc
58: 0c 94 7e 00 jmp 0xfc ; 0xfc
5c: 0c 94 7e 00 jmp 0xfc ; 0xfc
60: 0c 94 7e 00 jmp 0xfc ; 0xfc
64: 0c 94 7e 00 jmp 0xfc ; 0xfc
00000068 :
68: 00 00 00 00 24 00 27 00 2a 00 ....$.'.*.
00000072 :
72: 00 00 00 00 25 00 28 00 2b 00 ....%.(.+.
0000007c :
7c: 00 00 00 00 23 00 26 00 29 00 ....#.&.).
00000086 :
86: 04 04 04 04 04 04 04 04 02 02 02 02 02 02 03 03 ................
96: 03 03 03 03 ....
0000009a :
9a: 01 02 04 08 10 20 40 80 01 02 04 08 10 20 01 02 ..... @...... ..
aa: 04 08 10 20 ...
000000ae :
ae: 00 00 00 07 00 02 01 00 00 03 04 06 00 00 00 00 ................
be: 00 00 00 00 ....
000000c2 :
c2: 11 24 eor r1, r1
c4: 1f be out 0x3f, r1 ; 63
c6: cf ef ldi r28, 0xFF ; 255
c8: d8 e0 ldi r29, 0x08 ; 8
ca: de bf out 0x3e, r29 ; 62
cc: cd bf out 0x3d, r28 ; 61
000000ce :
ce: 11 e0 ldi r17, 0x01 ; 1
d0: a0 e0 ldi r26, 0x00 ; 0
d2: b1 e0 ldi r27, 0x01 ; 1
d4: e2 e0 ldi r30, 0x02 ; 2
d6: f4 e0 ldi r31, 0x04 ; 4
d8: 02 c0 rjmp .+4 ; 0xde
000000da :
da: 05 90 lpm r0, Z+
dc: 0d 92 st X+, r0
000000de :
de: a0 30 cpi r26, 0x00 ; 0
e0: b1 07 cpc r27, r17
e2: d9 f7 brne .-10 ; 0xda
000000e4 :
e4: 11 e0 ldi r17, 0x01 ; 1
e6: a0 e0 ldi r26, 0x00 ; 0
e8: b1 e0 ldi r27, 0x01 ; 1
ea: 01 c0 rjmp .+2 ; 0xee
000000ec :
ec: 1d 92 st X+, r1
000000ee :
ee: a9 30 cpi r26, 0x09 ; 9
f0: b1 07 cpc r27, r17
f2: e1 f7 brne .-8 ; 0xec
f4: 0e 94 f0 01 call 0x3e0 ; 0x3e0
f8: 0c 94 ff 01 jmp 0x3fe ; 0x3fe
000000fc :
fc: 0c 94 00 00 jmp 0 ; 0x0
00000100 :
// Pin 13 has an LED connected on most Arduino boards:
pinMode(13, OUTPUT);
}
void loop() {
digitalWrite(13, HIGH); // set the LED on
100: 8d e0 ldi r24, 0x0D ; 13
102: 61 e0 ldi r22, 0x01 ; 1
104: 0e 94 9c 01 call 0x338 ; 0x338
delay(1000); // wait for a second
108: 68 ee ldi r22, 0xE8 ; 232
10a: 73 e0 ldi r23, 0x03 ; 3
10c: 80 e0 ldi r24, 0x00 ; 0
10e: 90 e0 ldi r25, 0x00 ; 0
110: 0e 94 e2 00 call 0x1c4 ; 0x1c4
digitalWrite(13, LOW); // set the LED off
114: 8d e0 ldi r24, 0x0D ; 13
116: 60 e0 ldi r22, 0x00 ; 0
118: 0e 94 9c 01 call 0x338 ; 0x338
delay(1000); // wait for a second
11c: 68 ee ldi r22, 0xE8 ; 232
11e: 73 e0 ldi r23, 0x03 ; 3
120: 80 e0 ldi r24, 0x00 ; 0
122: 90 e0 ldi r25, 0x00 ; 0
124: 0e 94 e2 00 call 0x1c4 ; 0x1c4
}
128: 08 95 ret
0000012a :
void setup();
void loop();
void setup() {
// initialize the digital pin as an output.
// Pin 13 has an LED connected on most Arduino boards:
pinMode(13, OUTPUT);
12a: 8d e0 ldi r24, 0x0D ; 13
12c: 61 e0 ldi r22, 0x01 ; 1
12e: 0e 94 76 01 call 0x2ec ; 0x2ec
}
132: 08 95 ret
00000134 :
#if defined(__AVR_ATtiny24__) || defined(__AVR_ATtiny44__) || defined(__AVR_ATtiny84__)
SIGNAL(TIM0_OVF_vect)
#else
SIGNAL(TIMER0_OVF_vect)
#endif
{
134: 1f 92 push r1
136: 0f 92 push r0
138: 0f b6 in r0, 0x3f ; 63
13a: 0f 92 push r0
13c: 11 24 eor r1, r1
13e: 2f 93 push r18
140: 3f 93 push r19
142: 8f 93 push r24
144: 9f 93 push r25
146: af 93 push r26
148: 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;
14a: 80 91 04 01 lds r24, 0x0104
14e: 90 91 05 01 lds r25, 0x0105
152: a0 91 06 01 lds r26, 0x0106
156: b0 91 07 01 lds r27, 0x0107
unsigned char f = timer0_fract;
15a: 30 91 08 01 lds r19, 0x0108
m += MILLIS_INC;
15e: 01 96 adiw r24, 0x01 ; 1
160: a1 1d adc r26, r1
162: b1 1d adc r27, r1
f += FRACT_INC;
164: 23 2f mov r18, r19
166: 2d 5f subi r18, 0xFD ; 253
if (f >= FRACT_MAX) {
168: 2d 37 cpi r18, 0x7D ; 125
16a: 20 f0 brcs .+8 ; 0x174
f -= FRACT_MAX;
16c: 2d 57 subi r18, 0x7D ; 125
m += 1;
16e: 01 96 adiw r24, 0x01 ; 1
170: a1 1d adc r26, r1
172: b1 1d adc r27, r1
}
timer0_fract = f;
174: 20 93 08 01 sts 0x0108, r18
timer0_millis = m;
178: 80 93 04 01 sts 0x0104, r24
17c: 90 93 05 01 sts 0x0105, r25
180: a0 93 06 01 sts 0x0106, r26
184: b0 93 07 01 sts 0x0107, r27
timer0_overflow_count++;
188: 80 91 00 01 lds r24, 0x0100
18c: 90 91 01 01 lds r25, 0x0101
190: a0 91 02 01 lds r26, 0x0102
194: b0 91 03 01 lds r27, 0x0103
198: 01 96 adiw r24, 0x01 ; 1
19a: a1 1d adc r26, r1
19c: b1 1d adc r27, r1
19e: 80 93 00 01 sts 0x0100, r24
1a2: 90 93 01 01 sts 0x0101, r25
1a6: a0 93 02 01 sts 0x0102, r26
1aa: b0 93 03 01 sts 0x0103, r27
}
1ae: bf 91 pop r27
1b0: af 91 pop r26
1b2: 9f 91 pop r25
1b4: 8f 91 pop r24
1b6: 3f 91 pop r19
1b8: 2f 91 pop r18
1ba: 0f 90 pop r0
1bc: 0f be out 0x3f, r0 ; 63
1be: 0f 90 pop r0
1c0: 1f 90 pop r1
1c2: 18 95 reti
000001c4 :
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
void delay(unsigned long ms)
{
1c4: 9b 01 movw r18, r22
1c6: ac 01 movw r20, r24
return m;
}
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
1c8: 7f b7 in r23, 0x3f ; 63
cli();
1ca: f8 94 cli
m = timer0_overflow_count;
1cc: 80 91 00 01 lds r24, 0x0100
1d0: 90 91 01 01 lds r25, 0x0101
1d4: a0 91 02 01 lds r26, 0x0102
1d8: b0 91 03 01 lds r27, 0x0103
#if defined(TCNT0)
t = TCNT0;
1dc: 66 b5 in r22, 0x26 ; 38
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t < 255))
1de: a8 9b sbis 0x15, 0 ; 21
1e0: 05 c0 rjmp .+10 ; 0x1ec
1e2: 6f 3f cpi r22, 0xFF ; 255
1e4: 19 f0 breq .+6 ; 0x1ec
m++;
1e6: 01 96 adiw r24, 0x01 ; 1
1e8: a1 1d adc r26, r1
1ea: b1 1d adc r27, r1
#else
if ((TIFR & _BV(TOV0)) && (t < 255))
m++;
#endif
SREG = oldSREG;
1ec: 7f bf out 0x3f, r23 ; 63
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
}
void delay(unsigned long ms)
{
uint16_t start = (uint16_t)micros();
1ee: ba 2f mov r27, r26
1f0: a9 2f mov r26, r25
1f2: 98 2f mov r25, r24
1f4: 88 27 eor r24, r24
1f6: 86 0f add r24, r22
1f8: 91 1d adc r25, r1
1fa: a1 1d adc r26, r1
1fc: b1 1d adc r27, r1
1fe: 62 e0 ldi r22, 0x02 ; 2
200: 88 0f add r24, r24
202: 99 1f adc r25, r25
204: aa 1f adc r26, r26
206: bb 1f adc r27, r27
208: 6a 95 dec r22
20a: d1 f7 brne .-12 ; 0x200
20c: bc 01 movw r22, r24
20e: 2d c0 rjmp .+90 ; 0x26a
return m;
}
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
210: ff b7 in r31, 0x3f ; 63
cli();
212: f8 94 cli
m = timer0_overflow_count;
214: 80 91 00 01 lds r24, 0x0100
218: 90 91 01 01 lds r25, 0x0101
21c: a0 91 02 01 lds r26, 0x0102
220: b0 91 03 01 lds r27, 0x0103
#if defined(TCNT0)
t = TCNT0;
224: e6 b5 in r30, 0x26 ; 38
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t < 255))
226: a8 9b sbis 0x15, 0 ; 21
228: 05 c0 rjmp .+10 ; 0x234
22a: ef 3f cpi r30, 0xFF ; 255
22c: 19 f0 breq .+6 ; 0x234
m++;
22e: 01 96 adiw r24, 0x01 ; 1
230: a1 1d adc r26, r1
232: b1 1d adc r27, r1
#else
if ((TIFR & _BV(TOV0)) && (t < 255))
m++;
#endif
SREG = oldSREG;
234: ff bf out 0x3f, r31 ; 63
void delay(unsigned long ms)
{
uint16_t start = (uint16_t)micros();
while (ms > 0) {
if (((uint16_t)micros() - start) >= 1000) {
236: ba 2f mov r27, r26
238: a9 2f mov r26, r25
23a: 98 2f mov r25, r24
23c: 88 27 eor r24, r24
23e: 8e 0f add r24, r30
240: 91 1d adc r25, r1
242: a1 1d adc r26, r1
244: b1 1d adc r27, r1
246: e2 e0 ldi r30, 0x02 ; 2
248: 88 0f add r24, r24
24a: 99 1f adc r25, r25
24c: aa 1f adc r26, r26
24e: bb 1f adc r27, r27
250: ea 95 dec r30
252: d1 f7 brne .-12 ; 0x248
254: 86 1b sub r24, r22
256: 97 0b sbc r25, r23
258: 88 5e subi r24, 0xE8 ; 232
25a: 93 40 sbci r25, 0x03 ; 3
25c: c8 f2 brcs .-78 ; 0x210
ms--;
25e: 21 50 subi r18, 0x01 ; 1
260: 30 40 sbci r19, 0x00 ; 0
262: 40 40 sbci r20, 0x00 ; 0
264: 50 40 sbci r21, 0x00 ; 0
start += 1000;
266: 68 51 subi r22, 0x18 ; 24
268: 7c 4f sbci r23, 0xFC ; 252
void delay(unsigned long ms)
{
uint16_t start = (uint16_t)micros();
while (ms > 0) {
26a: 21 15 cp r18, r1
26c: 31 05 cpc r19, r1
26e: 41 05 cpc r20, r1
270: 51 05 cpc r21, r1
272: 71 f6 brne .-100 ; 0x210
if (((uint16_t)micros() - start) >= 1000) {
ms--;
start += 1000;
}
}
}
274: 08 95 ret
00000276 :
void init()
{
// this needs to be called before setup() or some functions won't
// work there
sei();
276: 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);
278: 84 b5 in r24, 0x24 ; 36
27a: 82 60 ori r24, 0x02 ; 2
27c: 84 bd out 0x24, r24 ; 36
sbi(TCCR0A, WGM00);
27e: 84 b5 in r24, 0x24 ; 36
280: 81 60 ori r24, 0x01 ; 1
282: 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);
284: 85 b5 in r24, 0x25 ; 37
286: 82 60 ori r24, 0x02 ; 2
288: 85 bd out 0x25, r24 ; 37
sbi(TCCR0B, CS00);
28a: 85 b5 in r24, 0x25 ; 37
28c: 81 60 ori r24, 0x01 ; 1
28e: 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);
290: ee e6 ldi r30, 0x6E ; 110
292: f0 e0 ldi r31, 0x00 ; 0
294: 80 81 ld r24, Z
296: 81 60 ori r24, 0x01 ; 1
298: 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;
29a: e1 e8 ldi r30, 0x81 ; 129
29c: f0 e0 ldi r31, 0x00 ; 0
29e: 10 82 st Z, r1
// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
2a0: 80 81 ld r24, Z
2a2: 82 60 ori r24, 0x02 ; 2
2a4: 80 83 st Z, r24
#if F_CPU >= 8000000L
sbi(TCCR1B, CS10);
2a6: 80 81 ld r24, Z
2a8: 81 60 ori r24, 0x01 ; 1
2aa: 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);
2ac: e0 e8 ldi r30, 0x80 ; 128
2ae: f0 e0 ldi r31, 0x00 ; 0
2b0: 80 81 ld r24, Z
2b2: 81 60 ori r24, 0x01 ; 1
2b4: 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);
2b6: e1 eb ldi r30, 0xB1 ; 177
2b8: f0 e0 ldi r31, 0x00 ; 0
2ba: 80 81 ld r24, Z
2bc: 84 60 ori r24, 0x04 ; 4
2be: 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);
2c0: e0 eb ldi r30, 0xB0 ; 176
2c2: f0 e0 ldi r31, 0x00 ; 0
2c4: 80 81 ld r24, Z
2c6: 81 60 ori r24, 0x01 ; 1
2c8: 80 83 st Z, r24
#if defined(ADCSRA)
// set a2d prescale factor to 128
// 16 MHz / 128 = 125 KHz, inside the desired 50-200 KHz range.
// XXX: this will not work properly for other clock speeds, and
// this code should use F_CPU to determine the prescale factor.
sbi(ADCSRA, ADPS2);
2ca: ea e7 ldi r30, 0x7A ; 122
2cc: f0 e0 ldi r31, 0x00 ; 0
2ce: 80 81 ld r24, Z
2d0: 84 60 ori r24, 0x04 ; 4
2d2: 80 83 st Z, r24
sbi(ADCSRA, ADPS1);
2d4: 80 81 ld r24, Z
2d6: 82 60 ori r24, 0x02 ; 2
2d8: 80 83 st Z, r24
sbi(ADCSRA, ADPS0);
2da: 80 81 ld r24, Z
2dc: 81 60 ori r24, 0x01 ; 1
2de: 80 83 st Z, r24
// enable a2d conversions
sbi(ADCSRA, ADEN);
2e0: 80 81 ld r24, Z
2e2: 80 68 ori r24, 0x80 ; 128
2e4: 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;
2e6: 10 92 c1 00 sts 0x00C1, r1
#endif
}
2ea: 08 95 ret
000002ec :
#include "wiring_private.h"
#include "pins_arduino.h"
void pinMode(uint8_t pin, uint8_t mode)
{
uint8_t bit = digitalPinToBitMask(pin);
2ec: 48 2f mov r20, r24
2ee: 50 e0 ldi r21, 0x00 ; 0
2f0: ca 01 movw r24, r20
2f2: 86 56 subi r24, 0x66 ; 102
2f4: 9f 4f sbci r25, 0xFF ; 255
2f6: fc 01 movw r30, r24
2f8: 24 91 lpm r18, Z+
uint8_t port = digitalPinToPort(pin);
2fa: 4a 57 subi r20, 0x7A ; 122
2fc: 5f 4f sbci r21, 0xFF ; 255
2fe: fa 01 movw r30, r20
300: 84 91 lpm r24, Z+
volatile uint8_t *reg;
if (port == NOT_A_PIN) return;
302: 88 23 and r24, r24
304: c1 f0 breq .+48 ; 0x336
// JWS: can I let the optimizer do this?
reg = portModeRegister(port);
306: e8 2f mov r30, r24
308: f0 e0 ldi r31, 0x00 ; 0
30a: ee 0f add r30, r30
30c: ff 1f adc r31, r31
30e: e8 59 subi r30, 0x98 ; 152
310: ff 4f sbci r31, 0xFF ; 255
312: a5 91 lpm r26, Z+
314: b4 91 lpm r27, Z+
if (mode == INPUT) {
316: 66 23 and r22, r22
318: 41 f4 brne .+16 ; 0x32a
uint8_t oldSREG = SREG;
31a: 9f b7 in r25, 0x3f ; 63
cli();
31c: f8 94 cli
*reg &= ~bit;
31e: 8c 91 ld r24, X
320: 20 95 com r18
322: 82 23 and r24, r18
324: 8c 93 st X, r24
SREG = oldSREG;
326: 9f bf out 0x3f, r25 ; 63
328: 08 95 ret
} else {
uint8_t oldSREG = SREG;
32a: 9f b7 in r25, 0x3f ; 63
cli();
32c: f8 94 cli
*reg |= bit;
32e: 8c 91 ld r24, X
330: 82 2b or r24, r18
332: 8c 93 st X, r24
SREG = oldSREG;
334: 9f bf out 0x3f, r25 ; 63
336: 08 95 ret
00000338 :
}
}
void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
338: 48 2f mov r20, r24
33a: 50 e0 ldi r21, 0x00 ; 0
33c: ca 01 movw r24, r20
33e: 82 55 subi r24, 0x52 ; 82
340: 9f 4f sbci r25, 0xFF ; 255
342: fc 01 movw r30, r24
344: 24 91 lpm r18, Z+
uint8_t bit = digitalPinToBitMask(pin);
346: ca 01 movw r24, r20
348: 86 56 subi r24, 0x66 ; 102
34a: 9f 4f sbci r25, 0xFF ; 255
34c: fc 01 movw r30, r24
34e: 94 91 lpm r25, Z+
uint8_t port = digitalPinToPort(pin);
350: 4a 57 subi r20, 0x7A ; 122
352: 5f 4f sbci r21, 0xFF ; 255
354: fa 01 movw r30, r20
356: 34 91 lpm r19, Z+
volatile uint8_t *out;
if (port == NOT_A_PIN) return;
358: 33 23 and r19, r19
35a: 09 f4 brne .+2 ; 0x35e
35c: 40 c0 rjmp .+128 ; 0x3de
// 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);
35e: 22 23 and r18, r18
360: 51 f1 breq .+84 ; 0x3b6
//
//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)
362: 23 30 cpi r18, 0x03 ; 3
364: 71 f0 breq .+28 ; 0x382
366: 24 30 cpi r18, 0x04 ; 4
368: 28 f4 brcc .+10 ; 0x374
36a: 21 30 cpi r18, 0x01 ; 1
36c: a1 f0 breq .+40 ; 0x396
36e: 22 30 cpi r18, 0x02 ; 2
370: 11 f5 brne .+68 ; 0x3b6
372: 14 c0 rjmp .+40 ; 0x39c
374: 26 30 cpi r18, 0x06 ; 6
376: b1 f0 breq .+44 ; 0x3a4
378: 27 30 cpi r18, 0x07 ; 7
37a: c1 f0 breq .+48 ; 0x3ac
37c: 24 30 cpi r18, 0x04 ; 4
37e: d9 f4 brne .+54 ; 0x3b6
380: 04 c0 rjmp .+8 ; 0x38a
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A: cbi(TCCR1A, COM1A1); break;
382: 80 91 80 00 lds r24, 0x0080
386: 8f 77 andi r24, 0x7F ; 127
388: 03 c0 rjmp .+6 ; 0x390
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B: cbi(TCCR1A, COM1B1); break;
38a: 80 91 80 00 lds r24, 0x0080
38e: 8f 7d andi r24, 0xDF ; 223
390: 80 93 80 00 sts 0x0080, r24
394: 10 c0 rjmp .+32 ; 0x3b6
#if defined(TCCR2) && defined(COM21)
case TIMER2: cbi(TCCR2, COM21); break;
#endif
#if defined(TCCR0A) && defined(COM0A1)
case TIMER0A: cbi(TCCR0A, COM0A1); break;
396: 84 b5 in r24, 0x24 ; 36
398: 8f 77 andi r24, 0x7F ; 127
39a: 02 c0 rjmp .+4 ; 0x3a0
#endif
#if defined(TIMER0B) && defined(COM0B1)
case TIMER0B: cbi(TCCR0A, COM0B1); break;
39c: 84 b5 in r24, 0x24 ; 36
39e: 8f 7d andi r24, 0xDF ; 223
3a0: 84 bd out 0x24, r24 ; 36
3a2: 09 c0 rjmp .+18 ; 0x3b6
#endif
#if defined(TCCR2A) && defined(COM2A1)
case TIMER2A: cbi(TCCR2A, COM2A1); break;
3a4: 80 91 b0 00 lds r24, 0x00B0
3a8: 8f 77 andi r24, 0x7F ; 127
3aa: 03 c0 rjmp .+6 ; 0x3b2
#endif
#if defined(TCCR2A) && defined(COM2B1)
case TIMER2B: cbi(TCCR2A, COM2B1); break;
3ac: 80 91 b0 00 lds r24, 0x00B0
3b0: 8f 7d andi r24, 0xDF ; 223
3b2: 80 93 b0 00 sts 0x00B0, r24
// 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);
out = portOutputRegister(port);
3b6: e3 2f mov r30, r19
3b8: f0 e0 ldi r31, 0x00 ; 0
3ba: ee 0f add r30, r30
3bc: ff 1f adc r31, r31
3be: ee 58 subi r30, 0x8E ; 142
3c0: ff 4f sbci r31, 0xFF ; 255
3c2: a5 91 lpm r26, Z+
3c4: b4 91 lpm r27, Z+
uint8_t oldSREG = SREG;
3c6: 2f b7 in r18, 0x3f ; 63
cli();
3c8: f8 94 cli
if (val == LOW) {
3ca: 66 23 and r22, r22
3cc: 21 f4 brne .+8 ; 0x3d6
*out &= ~bit;
3ce: 8c 91 ld r24, X
3d0: 90 95 com r25
3d2: 89 23 and r24, r25
3d4: 02 c0 rjmp .+4 ; 0x3da
} else {
*out |= bit;
3d6: 8c 91 ld r24, X
3d8: 89 2b or r24, r25
3da: 8c 93 st X, r24
}
SREG = oldSREG;
3dc: 2f bf out 0x3f, r18 ; 63
3de: 08 95 ret
000003e0 :
#include
int main(void)
3e0: cf 93 push r28
3e2: df 93 push r29
{
init();
3e4: 0e 94 3b 01 call 0x276 ; 0x276
#if defined(USBCON)
USB.attach();
#endif
setup();
3e8: 0e 94 95 00 call 0x12a ; 0x12a
for (;;) {
loop();
if (serialEventRun) serialEventRun();
3ec: c0 e0 ldi r28, 0x00 ; 0
3ee: d0 e0 ldi r29, 0x00 ; 0
#endif
setup();
for (;;) {
loop();
3f0: 0e 94 80 00 call 0x100 ; 0x100
if (serialEventRun) serialEventRun();
3f4: 20 97 sbiw r28, 0x00 ; 0
3f6: e1 f3 breq .-8 ; 0x3f0
3f8: 0e 94 00 00 call 0 ; 0x0
3fc: f9 cf rjmp .-14 ; 0x3f0
000003fe :
3fe: f8 94 cli
00000400 :
400: ff cf rjmp .-2 ; 0x400
Related links
- Inside Arduino – The Build Process
HTH