マイコンのアセンブリ言語を教えるには...?
いまさらアセンブラでプログラミングの演習とかやりたいわけじゃなくて、新しいハードウェアの開発環境を整えようとすると、高級言語との橋渡しとして機械語(アセンブリ言語)の知識が必要になって来るので、その辺の関係性だけ浅くでも知っておいてほしい、と思ったときに何を教材にすればよいでしょうか?
少し前なら、CASLを教えとけば、基本情報処理技術者試験ででてくるからという理由は付いたのですが、いまはCASLの試験は無くなって、言語選択さえなくなってます。
例えばx86系だとPCで何か別の言語で書いた方が気が利いてるのでわざわざアセンブリ言語で書いても面白くない。
マイコンでシェアのあるARM系かなぁと思ったけど、ちょっと大変そう。
低学年でArduinoの使い方を教えてるのでAVRマイコンのアセンブリ言語を教えて、ハードとの関連性を見てもらうというのはどうかと思いました。
AVR studio(Microchip studio)だと、C言語でプログラムを書いてビルド後にできるファイルの中に.lssファイルがあってこれをテキストで開くとアセンブリ言語になってます。
Arduinoで作業領域にいったんアセンブリ言語のプログラムが作られてないか探そうと思ったら、そもそも作業領域がどこか探すのに手間取りました。
見つけたのがこちらのサイト
https://dlna-paradise.blogspot.com/2019/06/arduinoidec.html
ここにあるように、設定の中でコンパイル時により詳細な情報を表示するようにするとコンパイル時の作業フォルダ名が表示されます。
例えば、
"C:\\Users\\ユーザ名\\AppData\\Local\\Temp\\arduino\\sketches\\932B4C1D9695F7DB71234520971B/sketch_feb13a.ino.elf"
という感じでかなり見つけにくいところにありました。
このフォルダ内を探しても、アセンブリ言語のファイルはありません。
コンパイル時にオプションでアセンブリ言語を出力するように設定しないといけないですが、その記述がどこにあるかも探すのが大変です。
最初に紹介されてる方法として、avr-objdump -S を使ってアセンブリを出力すると書いてあったので、avr-objdumpを検索したけど、これもどこにあるか分かりません。
検索して見つかったのは、PC内に保存してあった古いバージョンのArdinoのシステム一式の中のavr-objdump.exe のみ。
しょうがないのでこれで試してみました。
新規スケッチを作成。
setupもloopも空のままコンパイル。
できたelfファイルを作業しやすいフォルダにコピー。
コマンドラインを開いて、avr-objdump -S ファイル名.elf > アセンブリファイル名.S
を実行。
すると、.Sファイルが作成されてメモ帳で開くとアセンブリ言語になってます。
以下の通りですが、長いです。周辺回路の初期設定をいろいろやらないといけないようです。
プログラムの実体は、下の方の数行だと思います。
----------------------------------------------
更にdigital blinkのプログラムをアセンブラに直してみました。
LEDを点滅させるだけでも結構プログラムの行数があります。
少し前なら、CASLを教えとけば、基本情報処理技術者試験ででてくるからという理由は付いたのですが、いまはCASLの試験は無くなって、言語選択さえなくなってます。
例えばx86系だとPCで何か別の言語で書いた方が気が利いてるのでわざわざアセンブリ言語で書いても面白くない。
マイコンでシェアのあるARM系かなぁと思ったけど、ちょっと大変そう。
低学年でArduinoの使い方を教えてるのでAVRマイコンのアセンブリ言語を教えて、ハードとの関連性を見てもらうというのはどうかと思いました。
AVR studio(Microchip studio)だと、C言語でプログラムを書いてビルド後にできるファイルの中に.lssファイルがあってこれをテキストで開くとアセンブリ言語になってます。
Arduinoで作業領域にいったんアセンブリ言語のプログラムが作られてないか探そうと思ったら、そもそも作業領域がどこか探すのに手間取りました。
見つけたのがこちらのサイト
https://dlna-paradise.blogspot.com/2019/06/arduinoidec.html
ここにあるように、設定の中でコンパイル時により詳細な情報を表示するようにするとコンパイル時の作業フォルダ名が表示されます。
例えば、
"C:\\Users\\ユーザ名\\AppData\\Local\\Temp\\arduino\\sketches\\932B4C1D9695F7DB71234520971B/sketch_feb13a.ino.elf"
という感じでかなり見つけにくいところにありました。
このフォルダ内を探しても、アセンブリ言語のファイルはありません。
コンパイル時にオプションでアセンブリ言語を出力するように設定しないといけないですが、その記述がどこにあるかも探すのが大変です。
最初に紹介されてる方法として、avr-objdump -S を使ってアセンブリを出力すると書いてあったので、avr-objdumpを検索したけど、これもどこにあるか分かりません。
検索して見つかったのは、PC内に保存してあった古いバージョンのArdinoのシステム一式の中のavr-objdump.exe のみ。
しょうがないのでこれで試してみました。
新規スケッチを作成。
setupもloopも空のままコンパイル。
できたelfファイルを作業しやすいフォルダにコピー。
コマンドラインを開いて、avr-objdump -S ファイル名.elf > アセンブリファイル名.S
を実行。
すると、.Sファイルが作成されてメモ帳で開くとアセンブリ言語になってます。
以下の通りですが、長いです。周辺回路の初期設定をいろいろやらないといけないようです。
プログラムの実体は、下の方の数行だと思います。
blank.elf: file format elf32-avr
Disassembly of section .text:
00000000 <__vectors>:
0: 0c 94 34 00 jmp 0x68 ; 0x68 <__ctors_end>
4: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
8: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
10: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
14: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
18: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
1c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
20: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
24: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
28: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
2c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
30: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
34: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
38: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
3c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
40: 0c 94 48 00 jmp 0x90 ; 0x90 <__vector_16>
44: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
48: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
4c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
50: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
54: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
58: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
5c: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
60: 0c 94 46 00 jmp 0x8c ; 0x8c <__bad_interrupt>
64: 0c 94 46 00 jmp 0x8c ; 0x8c <__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
00000074 <__do_clear_bss>:
74: 21 e0 ldi r18, 0x01 ; 1
76: a0 e0 ldi r26, 0x00 ; 0
78: b1 e0 ldi r27, 0x01 ; 1
7a: 01 c0 rjmp .+2 ; 0x7e <.do_clear_bss_start>
0000007c <.do_clear_bss_loop>:
7c: 1d 92 st X+, r1
0000007e <.do_clear_bss_start>:
7e: a9 30 cpi r26, 0x09 ; 9
80: b2 07 cpc r27, r18
82: e1 f7 brne .-8 ; 0x7c <.do_clear_bss_loop>
84: 0e 94 92 00 call 0x124 ; 0x124
88: 0c 94 dc 00 jmp 0x1b8 ; 0x1b8 <_exit>
0000008c <__bad_interrupt>:
8c: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
00000090 <__vector_16>:
#if defined(TIM0_OVF_vect)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
90: 1f 92 push r1
92: 0f 92 push r0
94: 0f b6 in r0, 0x3f ; 63
96: 0f 92 push r0
98: 11 24 eor r1, r1
9a: 2f 93 push r18
9c: 3f 93 push r19
9e: 8f 93 push r24
a0: 9f 93 push r25
a2: af 93 push r26
a4: 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;
a6: 80 91 05 01 lds r24, 0x0105
aa: 90 91 06 01 lds r25, 0x0106
ae: a0 91 07 01 lds r26, 0x0107
b2: b0 91 08 01 lds r27, 0x0108
unsigned char f = timer0_fract;
b6: 30 91 04 01 lds r19, 0x0104
m += MILLIS_INC;
f += FRACT_INC;
ba: 23 e0 ldi r18, 0x03 ; 3
bc: 23 0f add r18, r19
if (f >= FRACT_MAX) {
be: 2d 37 cpi r18, 0x7D ; 125
c0: 58 f5 brcc .+86 ; 0x118 <__vector_16+0x88>
// 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;
unsigned char f = timer0_fract;
m += MILLIS_INC;
c2: 01 96 adiw r24, 0x01 ; 1
c4: a1 1d adc r26, r1
c6: b1 1d adc r27, r1
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}
timer0_fract = f;
c8: 20 93 04 01 sts 0x0104, r18
timer0_millis = m;
cc: 80 93 05 01 sts 0x0105, r24
d0: 90 93 06 01 sts 0x0106, r25
d4: a0 93 07 01 sts 0x0107, r26
d8: b0 93 08 01 sts 0x0108, r27
timer0_overflow_count++;
dc: 80 91 00 01 lds r24, 0x0100
e0: 90 91 01 01 lds r25, 0x0101
e4: a0 91 02 01 lds r26, 0x0102
e8: b0 91 03 01 lds r27, 0x0103
ec: 01 96 adiw r24, 0x01 ; 1
ee: a1 1d adc r26, r1
f0: b1 1d adc r27, r1
f2: 80 93 00 01 sts 0x0100, r24
f6: 90 93 01 01 sts 0x0101, r25
fa: a0 93 02 01 sts 0x0102, r26
fe: b0 93 03 01 sts 0x0103, r27
}
102: bf 91 pop r27
104: af 91 pop r26
106: 9f 91 pop r25
108: 8f 91 pop r24
10a: 3f 91 pop r19
10c: 2f 91 pop r18
10e: 0f 90 pop r0
110: 0f be out 0x3f, r0 ; 63
112: 0f 90 pop r0
114: 1f 90 pop r1
116: 18 95 reti
unsigned char f = timer0_fract;
m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
118: 26 e8 ldi r18, 0x86 ; 134
11a: 23 0f add r18, r19
m += 1;
11c: 02 96 adiw r24, 0x02 ; 2
11e: a1 1d adc r26, r1
120: b1 1d adc r27, r1
122: d2 cf rjmp .-92 ; 0xc8 <__vector_16+0x38>
00000124:
void init()
{
// this needs to be called before setup() or some functions won't
// work there
sei();
124: 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);
126: 84 b5 in r24, 0x24 ; 36
128: 82 60 ori r24, 0x02 ; 2
12a: 84 bd out 0x24, r24 ; 36
sbi(TCCR0A, WGM00);
12c: 84 b5 in r24, 0x24 ; 36
12e: 81 60 ori r24, 0x01 ; 1
130: 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);
132: 85 b5 in r24, 0x25 ; 37
134: 82 60 ori r24, 0x02 ; 2
136: 85 bd out 0x25, r24 ; 37
sbi(TCCR0B, CS00);
138: 85 b5 in r24, 0x25 ; 37
13a: 81 60 ori r24, 0x01 ; 1
13c: 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);
13e: 80 91 6e 00 lds r24, 0x006E
142: 81 60 ori r24, 0x01 ; 1
144: 80 93 6e 00 sts 0x006E, 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;
148: 10 92 81 00 sts 0x0081, r1
// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
14c: 80 91 81 00 lds r24, 0x0081
150: 82 60 ori r24, 0x02 ; 2
152: 80 93 81 00 sts 0x0081, r24
#if F_CPU >= 8000000L
sbi(TCCR1B, CS10);
156: 80 91 81 00 lds r24, 0x0081
15a: 81 60 ori r24, 0x01 ; 1
15c: 80 93 81 00 sts 0x0081, r24
sbi(TCCR1, CS10);
#endif
#endif
// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
sbi(TCCR1A, WGM10);
160: 80 91 80 00 lds r24, 0x0080
164: 81 60 ori r24, 0x01 ; 1
166: 80 93 80 00 sts 0x0080, r24
// set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
sbi(TCCR2B, CS22);
16a: 80 91 b1 00 lds r24, 0x00B1
16e: 84 60 ori r24, 0x04 ; 4
170: 80 93 b1 00 sts 0x00B1, 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);
174: 80 91 b0 00 lds r24, 0x00B0
178: 81 60 ori r24, 0x01 ; 1
17a: 80 93 b0 00 sts 0x00B0, 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);
17e: 80 91 7a 00 lds r24, 0x007A
182: 84 60 ori r24, 0x04 ; 4
184: 80 93 7a 00 sts 0x007A, r24
sbi(ADCSRA, ADPS1);
188: 80 91 7a 00 lds r24, 0x007A
18c: 82 60 ori r24, 0x02 ; 2
18e: 80 93 7a 00 sts 0x007A, r24
sbi(ADCSRA, ADPS0);
192: 80 91 7a 00 lds r24, 0x007A
196: 81 60 ori r24, 0x01 ; 1
198: 80 93 7a 00 sts 0x007A, r24
cbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
#endif
// enable a2d conversions
sbi(ADCSRA, ADEN);
19c: 80 91 7a 00 lds r24, 0x007A
1a0: 80 68 ori r24, 0x80 ; 128
1a2: 80 93 7a 00 sts 0x007A, 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;
1a6: 10 92 c1 00 sts 0x00C1, r1
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
1aa: c0 e0 ldi r28, 0x00 ; 0
1ac: d0 e0 ldi r29, 0x00 ; 0
1ae: 20 97 sbiw r28, 0x00 ; 0
1b0: f1 f3 breq .-4 ; 0x1ae
1b2: 0e 94 00 00 call 0 ; 0x0 <__vectors>
1b6: fb cf rjmp .-10 ; 0x1ae
000001b8 <_exit>:
1b8: f8 94 cli
000001ba <__stop_program>:
1ba: ff cf rjmp .-2 ; 0x1ba <__stop_program>
----------------------------------------------
更にdigital blinkのプログラムをアセンブラに直してみました。
LEDを点滅させるだけでも結構プログラムの行数があります。
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 13 01 jmp 0x226 ; 0x226 <__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 00 nop
6c: 24 00 .word 0x0024 ; ????
6e: 27 00 .word 0x0027 ; ????
70: 2a 00 .word 0x002a ; ????
00000072:
72: 00 00 00 00 25 00 28 00 2b 00 ....%.(.+.
0000007c:
7c: 04 04 04 04 04 04 04 04 02 02 02 02 02 02 03 03 ................
8c: 03 03 03 03 ....
00000090:
90: 01 02 04 08 10 20 40 80 01 02 04 08 10 20 01 02 ..... @...... ..
a0: 04 08 10 20 ...
000000a4:
a4: 00 00 00 08 00 02 01 00 00 03 04 07 00 00 00 00 ................
b4: 00 00 00 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 5d 01 call 0x2ba ; 0x2ba
d8: 0c 94 cc 01 jmp 0x398 ; 0x398 <_exit>
000000dc <__bad_interrupt>:
dc: 0c 94 00 00 jmp 0 ; 0x0 <__vectors>
000000e0:
}
}
void digitalWrite(uint8_t pin, uint8_t val)
{
uint8_t timer = digitalPinToTimer(pin);
e0: e1 eb ldi r30, 0xB1 ; 177
e2: f0 e0 ldi r31, 0x00 ; 0
e4: 24 91 lpm r18, Z+
uint8_t bit = digitalPinToBitMask(pin);
e6: ed e9 ldi r30, 0x9D ; 157
e8: f0 e0 ldi r31, 0x00 ; 0
ea: 94 91 lpm r25, Z+
uint8_t port = digitalPinToPort(pin);
ec: e9 e8 ldi r30, 0x89 ; 137
ee: f0 e0 ldi r31, 0x00 ; 0
f0: e4 91 lpm r30, Z+
volatile uint8_t *out;
if (port == NOT_A_PIN) return;
f2: ee 23 and r30, r30
f4: c9 f0 breq .+50 ; 0x128
// 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);
f6: 22 23 and r18, r18
f8: 39 f0 breq .+14 ; 0x108
//
//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)
fa: 23 30 cpi r18, 0x03 ; 3
fc: 01 f1 breq .+64 ; 0x13e
fe: a8 f4 brcc .+42 ; 0x12a
100: 21 30 cpi r18, 0x01 ; 1
102: 19 f1 breq .+70 ; 0x14a
104: 22 30 cpi r18, 0x02 ; 2
106: 29 f1 breq .+74 ; 0x152
// 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);
108: f0 e0 ldi r31, 0x00 ; 0
10a: ee 0f add r30, r30
10c: ff 1f adc r31, r31
10e: ee 58 subi r30, 0x8E ; 142
110: ff 4f sbci r31, 0xFF ; 255
112: a5 91 lpm r26, Z+
114: b4 91 lpm r27, Z+
uint8_t oldSREG = SREG;
116: 2f b7 in r18, 0x3f ; 63
cli();
118: f8 94 cli
if (val == LOW) {
*out &= ~bit;
11a: ec 91 ld r30, X
out = portOutputRegister(port);
uint8_t oldSREG = SREG;
cli();
if (val == LOW) {
11c: 81 11 cpse r24, r1
11e: 26 c0 rjmp .+76 ; 0x16c
*out &= ~bit;
120: 90 95 com r25
122: 9e 23 and r25, r30
} else {
*out |= bit;
124: 9c 93 st X, r25
}
SREG = oldSREG;
126: 2f bf out 0x3f, r18 ; 63
}
128: 08 95 ret
//
//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)
12a: 27 30 cpi r18, 0x07 ; 7
12c: a9 f0 breq .+42 ; 0x158
12e: 28 30 cpi r18, 0x08 ; 8
130: c9 f0 breq .+50 ; 0x164
132: 24 30 cpi r18, 0x04 ; 4
134: 49 f7 brne .-46 ; 0x108
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A: cbi(TCCR1A, COM1A1); break;
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B: cbi(TCCR1A, COM1B1); break;
136: 20 91 80 00 lds r18, 0x0080
13a: 2f 7d andi r18, 0xDF ; 223
13c: 03 c0 rjmp .+6 ; 0x144
static void turnOffPWM(uint8_t timer)
{
switch (timer)
{
#if defined(TCCR1A) && defined(COM1A1)
case TIMER1A: cbi(TCCR1A, COM1A1); break;
13e: 20 91 80 00 lds r18, 0x0080
142: 2f 77 andi r18, 0x7F ; 127
#endif
#if defined(TCCR1A) && defined(COM1B1)
case TIMER1B: cbi(TCCR1A, COM1B1); break;
144: 20 93 80 00 sts 0x0080, r18
148: df cf rjmp .-66 ; 0x108
#if defined(TCCR2) && defined(COM21)
case TIMER2: cbi(TCCR2, COM21); break;
#endif
#if defined(TCCR0A) && defined(COM0A1)
case TIMER0A: cbi(TCCR0A, COM0A1); break;
14a: 24 b5 in r18, 0x24 ; 36
14c: 2f 77 andi r18, 0x7F ; 127
#endif
#if defined(TCCR0A) && defined(COM0B1)
case TIMER0B: cbi(TCCR0A, COM0B1); break;
14e: 24 bd out 0x24, r18 ; 36
150: db cf rjmp .-74 ; 0x108
152: 24 b5 in r18, 0x24 ; 36
154: 2f 7d andi r18, 0xDF ; 223
156: fb cf rjmp .-10 ; 0x14e
#endif
#if defined(TCCR2A) && defined(COM2A1)
case TIMER2A: cbi(TCCR2A, COM2A1); break;
158: 20 91 b0 00 lds r18, 0x00B0
15c: 2f 77 andi r18, 0x7F ; 127
#endif
#if defined(TCCR2A) && defined(COM2B1)
case TIMER2B: cbi(TCCR2A, COM2B1); break;
15e: 20 93 b0 00 sts 0x00B0, r18
162: d2 cf rjmp .-92 ; 0x108
164: 20 91 b0 00 lds r18, 0x00B0
168: 2f 7d andi r18, 0xDF ; 223
16a: f9 cf rjmp .-14 ; 0x15e
cli();
if (val == LOW) {
*out &= ~bit;
} else {
*out |= bit;
16c: 9e 2b or r25, r30
16e: da cf rjmp .-76 ; 0x124
00000170:
return m;
}
unsigned long micros() {
unsigned long m;
uint8_t oldSREG = SREG, t;
170: 3f b7 in r19, 0x3f ; 63
cli();
172: f8 94 cli
m = timer0_overflow_count;
174: 80 91 05 01 lds r24, 0x0105
178: 90 91 06 01 lds r25, 0x0106
17c: a0 91 07 01 lds r26, 0x0107
180: b0 91 08 01 lds r27, 0x0108
#if defined(TCNT0)
t = TCNT0;
184: 26 b5 in r18, 0x26 ; 38
#else
#error TIMER 0 not defined
#endif
#ifdef TIFR0
if ((TIFR0 & _BV(TOV0)) && (t < 255))
186: a8 9b sbis 0x15, 0 ; 21
188: 05 c0 rjmp .+10 ; 0x194
18a: 2f 3f cpi r18, 0xFF ; 255
18c: 19 f0 breq .+6 ; 0x194
m++;
18e: 01 96 adiw r24, 0x01 ; 1
190: a1 1d adc r26, r1
192: b1 1d adc r27, r1
#else
if ((TIFR & _BV(TOV0)) && (t < 255))
m++;
#endif
SREG = oldSREG;
194: 3f bf out 0x3f, r19 ; 63
return ((m << 8) + t) * (64 / clockCyclesPerMicrosecond());
196: ba 2f mov r27, r26
198: a9 2f mov r26, r25
19a: 98 2f mov r25, r24
19c: 88 27 eor r24, r24
19e: bc 01 movw r22, r24
1a0: cd 01 movw r24, r26
1a2: 62 0f add r22, r18
1a4: 71 1d adc r23, r1
1a6: 81 1d adc r24, r1
1a8: 91 1d adc r25, r1
1aa: 42 e0 ldi r20, 0x02 ; 2
1ac: 66 0f add r22, r22
1ae: 77 1f adc r23, r23
1b0: 88 1f adc r24, r24
1b2: 99 1f adc r25, r25
1b4: 4a 95 dec r20
1b6: d1 f7 brne .-12 ; 0x1ac
}
1b8: 08 95 ret
000001ba:
void delay(unsigned long ms)
1ba: 8f 92 push r8
1bc: 9f 92 push r9
1be: af 92 push r10
1c0: bf 92 push r11
1c2: cf 92 push r12
1c4: df 92 push r13
1c6: ef 92 push r14
1c8: ff 92 push r15
{
uint32_t start = micros();
1ca: 0e 94 b8 00 call 0x170 ; 0x170
1ce: 4b 01 movw r8, r22
1d0: 5c 01 movw r10, r24
1d2: 80 ed ldi r24, 0xD0 ; 208
1d4: c8 2e mov r12, r24
1d6: 87 e0 ldi r24, 0x07 ; 7
1d8: d8 2e mov r13, r24
1da: e1 2c mov r14, r1
1dc: f1 2c mov r15, r1
while (ms > 0) {
yield();
while ( ms > 0 && (micros() - start) >= 1000) {
1de: 0e 94 b8 00 call 0x170 ; 0x170
1e2: 68 19 sub r22, r8
1e4: 79 09 sbc r23, r9
1e6: 8a 09 sbc r24, r10
1e8: 9b 09 sbc r25, r11
1ea: 68 3e cpi r22, 0xE8 ; 232
1ec: 73 40 sbci r23, 0x03 ; 3
1ee: 81 05 cpc r24, r1
1f0: 91 05 cpc r25, r1
1f2: a8 f3 brcs .-22 ; 0x1de
ms--;
1f4: 21 e0 ldi r18, 0x01 ; 1
1f6: c2 1a sub r12, r18
1f8: d1 08 sbc r13, r1
1fa: e1 08 sbc r14, r1
1fc: f1 08 sbc r15, r1
start += 1000;
1fe: 88 ee ldi r24, 0xE8 ; 232
200: 88 0e add r8, r24
202: 83 e0 ldi r24, 0x03 ; 3
204: 98 1e adc r9, r24
206: a1 1c adc r10, r1
208: b1 1c adc r11, r1
{
uint32_t start = micros();
while (ms > 0) {
yield();
while ( ms > 0 && (micros() - start) >= 1000) {
20a: c1 14 cp r12, r1
20c: d1 04 cpc r13, r1
20e: e1 04 cpc r14, r1
210: f1 04 cpc r15, r1
212: 29 f7 brne .-54 ; 0x1de
ms--;
start += 1000;
}
}
}
214: ff 90 pop r15
216: ef 90 pop r14
218: df 90 pop r13
21a: cf 90 pop r12
21c: bf 90 pop r11
21e: af 90 pop r10
220: 9f 90 pop r9
222: 8f 90 pop r8
224: 08 95 ret
00000226 <__vector_16>:
#if defined(TIM0_OVF_vect)
ISR(TIM0_OVF_vect)
#else
ISR(TIMER0_OVF_vect)
#endif
{
226: 1f 92 push r1
228: 0f 92 push r0
22a: 0f b6 in r0, 0x3f ; 63
22c: 0f 92 push r0
22e: 11 24 eor r1, r1
230: 2f 93 push r18
232: 3f 93 push r19
234: 8f 93 push r24
236: 9f 93 push r25
238: af 93 push r26
23a: 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;
23c: 80 91 01 01 lds r24, 0x0101
240: 90 91 02 01 lds r25, 0x0102
244: a0 91 03 01 lds r26, 0x0103
248: b0 91 04 01 lds r27, 0x0104
unsigned char f = timer0_fract;
24c: 30 91 00 01 lds r19, 0x0100
m += MILLIS_INC;
f += FRACT_INC;
250: 23 e0 ldi r18, 0x03 ; 3
252: 23 0f add r18, r19
if (f >= FRACT_MAX) {
254: 2d 37 cpi r18, 0x7D ; 125
256: 58 f5 brcc .+86 ; 0x2ae <__vector_16+0x88>
// 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;
unsigned char f = timer0_fract;
m += MILLIS_INC;
258: 01 96 adiw r24, 0x01 ; 1
25a: a1 1d adc r26, r1
25c: b1 1d adc r27, r1
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
m += 1;
}
timer0_fract = f;
25e: 20 93 00 01 sts 0x0100, r18
timer0_millis = m;
262: 80 93 01 01 sts 0x0101, r24
266: 90 93 02 01 sts 0x0102, r25
26a: a0 93 03 01 sts 0x0103, r26
26e: b0 93 04 01 sts 0x0104, r27
timer0_overflow_count++;
272: 80 91 05 01 lds r24, 0x0105
276: 90 91 06 01 lds r25, 0x0106
27a: a0 91 07 01 lds r26, 0x0107
27e: b0 91 08 01 lds r27, 0x0108
282: 01 96 adiw r24, 0x01 ; 1
284: a1 1d adc r26, r1
286: b1 1d adc r27, r1
288: 80 93 05 01 sts 0x0105, r24
28c: 90 93 06 01 sts 0x0106, r25
290: a0 93 07 01 sts 0x0107, r26
294: b0 93 08 01 sts 0x0108, r27
}
298: bf 91 pop r27
29a: af 91 pop r26
29c: 9f 91 pop r25
29e: 8f 91 pop r24
2a0: 3f 91 pop r19
2a2: 2f 91 pop r18
2a4: 0f 90 pop r0
2a6: 0f be out 0x3f, r0 ; 63
2a8: 0f 90 pop r0
2aa: 1f 90 pop r1
2ac: 18 95 reti
unsigned char f = timer0_fract;
m += MILLIS_INC;
f += FRACT_INC;
if (f >= FRACT_MAX) {
f -= FRACT_MAX;
2ae: 26 e8 ldi r18, 0x86 ; 134
2b0: 23 0f add r18, r19
m += 1;
2b2: 02 96 adiw r24, 0x02 ; 2
2b4: a1 1d adc r26, r1
2b6: b1 1d adc r27, r1
2b8: d2 cf rjmp .-92 ; 0x25e <__vector_16+0x38>
000002ba:
void init()
{
// this needs to be called before setup() or some functions won't
// work there
sei();
2ba: 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);
2bc: 84 b5 in r24, 0x24 ; 36
2be: 82 60 ori r24, 0x02 ; 2
2c0: 84 bd out 0x24, r24 ; 36
sbi(TCCR0A, WGM00);
2c2: 84 b5 in r24, 0x24 ; 36
2c4: 81 60 ori r24, 0x01 ; 1
2c6: 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);
2c8: 85 b5 in r24, 0x25 ; 37
2ca: 82 60 ori r24, 0x02 ; 2
2cc: 85 bd out 0x25, r24 ; 37
sbi(TCCR0B, CS00);
2ce: 85 b5 in r24, 0x25 ; 37
2d0: 81 60 ori r24, 0x01 ; 1
2d2: 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);
2d4: 80 91 6e 00 lds r24, 0x006E
2d8: 81 60 ori r24, 0x01 ; 1
2da: 80 93 6e 00 sts 0x006E, 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;
2de: 10 92 81 00 sts 0x0081, r1
// set timer 1 prescale factor to 64
sbi(TCCR1B, CS11);
2e2: 80 91 81 00 lds r24, 0x0081
2e6: 82 60 ori r24, 0x02 ; 2
2e8: 80 93 81 00 sts 0x0081, r24
#if F_CPU >= 8000000L
sbi(TCCR1B, CS10);
2ec: 80 91 81 00 lds r24, 0x0081
2f0: 81 60 ori r24, 0x01 ; 1
2f2: 80 93 81 00 sts 0x0081, r24
sbi(TCCR1, CS10);
#endif
#endif
// put timer 1 in 8-bit phase correct pwm mode
#if defined(TCCR1A) && defined(WGM10)
sbi(TCCR1A, WGM10);
2f6: 80 91 80 00 lds r24, 0x0080
2fa: 81 60 ori r24, 0x01 ; 1
2fc: 80 93 80 00 sts 0x0080, r24
// set timer 2 prescale factor to 64
#if defined(TCCR2) && defined(CS22)
sbi(TCCR2, CS22);
#elif defined(TCCR2B) && defined(CS22)
sbi(TCCR2B, CS22);
300: 80 91 b1 00 lds r24, 0x00B1
304: 84 60 ori r24, 0x04 ; 4
306: 80 93 b1 00 sts 0x00B1, 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);
30a: 80 91 b0 00 lds r24, 0x00B0
30e: 81 60 ori r24, 0x01 ; 1
310: 80 93 b0 00 sts 0x00B0, 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);
314: 80 91 7a 00 lds r24, 0x007A
318: 84 60 ori r24, 0x04 ; 4
31a: 80 93 7a 00 sts 0x007A, r24
sbi(ADCSRA, ADPS1);
31e: 80 91 7a 00 lds r24, 0x007A
322: 82 60 ori r24, 0x02 ; 2
324: 80 93 7a 00 sts 0x007A, r24
sbi(ADCSRA, ADPS0);
328: 80 91 7a 00 lds r24, 0x007A
32c: 81 60 ori r24, 0x01 ; 1
32e: 80 93 7a 00 sts 0x007A, r24
cbi(ADCSRA, ADPS2);
cbi(ADCSRA, ADPS1);
sbi(ADCSRA, ADPS0);
#endif
// enable a2d conversions
sbi(ADCSRA, ADEN);
332: 80 91 7a 00 lds r24, 0x007A
336: 80 68 ori r24, 0x80 ; 128
338: 80 93 7a 00 sts 0x007A, 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;
33c: 10 92 c1 00 sts 0x00C1, r1
#include "wiring_private.h"
#include "pins_arduino.h"
void pinMode(uint8_t pin, uint8_t mode)
{
uint8_t bit = digitalPinToBitMask(pin);
340: ed e9 ldi r30, 0x9D ; 157
342: f0 e0 ldi r31, 0x00 ; 0
344: 24 91 lpm r18, Z+
uint8_t port = digitalPinToPort(pin);
346: e9 e8 ldi r30, 0x89 ; 137
348: f0 e0 ldi r31, 0x00 ; 0
34a: 84 91 lpm r24, Z+
volatile uint8_t *reg, *out;
if (port == NOT_A_PIN) return;
34c: 88 23 and r24, r24
34e: 99 f0 breq .+38 ; 0x376
// JWS: can I let the optimizer do this?
reg = portModeRegister(port);
350: 90 e0 ldi r25, 0x00 ; 0
352: 88 0f add r24, r24
354: 99 1f adc r25, r25
356: fc 01 movw r30, r24
358: e8 59 subi r30, 0x98 ; 152
35a: ff 4f sbci r31, 0xFF ; 255
35c: a5 91 lpm r26, Z+
35e: b4 91 lpm r27, Z+
out = portOutputRegister(port);
360: fc 01 movw r30, r24
362: ee 58 subi r30, 0x8E ; 142
364: ff 4f sbci r31, 0xFF ; 255
366: 85 91 lpm r24, Z+
368: 94 91 lpm r25, Z+
cli();
*reg &= ~bit;
*out |= bit;
SREG = oldSREG;
} else {
uint8_t oldSREG = SREG;
36a: 8f b7 in r24, 0x3f ; 63
cli();
36c: f8 94 cli
*reg |= bit;
36e: ec 91 ld r30, X
370: e2 2b or r30, r18
372: ec 93 st X, r30
SREG = oldSREG;
374: 8f bf out 0x3f, r24 ; 63
setup();
for (;;) {
loop();
if (serialEventRun) serialEventRun();
376: c0 e0 ldi r28, 0x00 ; 0
378: d0 e0 ldi r29, 0x00 ; 0
pinMode(LED_BUILTIN, OUTPUT);
}
// the loop function runs over and over again forever
void loop() {
digitalWrite(LED_BUILTIN, HIGH); // turn the LED on (HIGH is the voltage level)
37a: 81 e0 ldi r24, 0x01 ; 1
37c: 0e 94 70 00 call 0xe0 ; 0xe0
delay(2000); // wait for a second
380: 0e 94 dd 00 call 0x1ba ; 0x1ba
digitalWrite(LED_BUILTIN, LOW); // turn the LED off by making the voltage LOW
384: 80 e0 ldi r24, 0x00 ; 0
386: 0e 94 70 00 call 0xe0 ; 0xe0
delay(2000); // wait for a second
38a: 0e 94 dd 00 call 0x1ba ; 0x1ba
38e: 20 97 sbiw r28, 0x00 ; 0
390: a1 f3 breq .-24 ; 0x37a
392: 0e 94 00 00 call 0 ; 0x0 <__vectors>
396: f1 cf rjmp .-30 ; 0x37a
00000398 <_exit>:
398: f8 94 cli
0000039a <__stop_program>:
39a: ff cf rjmp .-2 ; 0x39a <__stop_program>