main関数を抜け出したらどこに行くんだろう?、ってミサカはミサカはAVRを心配してみたり。
前回の記事に書きましたがmain関数の最後にはret命令があります。パソコン上のプログラムであればこの後に少し処理を行ってから(stdin, stdout, stderrのクローズなど)、OSに処理を返します。AVRではこのret命令の後にどこに処理が移るのでしょうか。
ret命令のジャンプ先はスタックに積まれています。スタックの先頭が変なアドレスを指していた場合はプログラムは暴走します。main関数が別の場所からcallされていれば、スタックの先頭が正当なアドレスであることが保証されるので問題は起きません。その点に注意しながら見ていきましょう。
今回は
avr-gcc -c -o main1.o -mmcu=atmega328p main1.c
avr-gcc -o main1.elf -mmcu=atmega328p main.o
avr-objdump -h -S -z main1.elf > main1.lss
という順でコマンドを実行して、一度elfファイルを作ってからそれを逆アセンブルしてみました。(main1.cは前回の記事を参照)
逆アセンブル結果 main1.lss
[plain]
main1.elf: file format elf32-avr
Sections:
Idx Name Size VMA LMA File off Algn
0 .text 00000042 00000000 00000000 00000054 2**1
CONTENTS, ALLOC, LOAD, READONLY, CODE
1 .stab 000006b4 00000000 00000000 00000098 2**2
CONTENTS, READONLY, DEBUGGING
2 .stabstr 00000054 00000000 00000000 0000074c 2**0
CONTENTS, READONLY, DEBUGGING
Disassembly of section .text:
00000000 <__vectors>:
0: 0c c0 rjmp .+24 ; 0x1a <__ctors_end>
2: 13 c0 rjmp .+38 ; 0x2a <__bad_interrupt>
4: 12 c0 rjmp .+36 ; 0x2a <__bad_interrupt>
6: 11 c0 rjmp .+34 ; 0x2a <__bad_interrupt>
8: 10 c0 rjmp .+32 ; 0x2a <__bad_interrupt>
a: 0f c0 rjmp .+30 ; 0x2a <__bad_interrupt>
c: 0e c0 rjmp .+28 ; 0x2a <__bad_interrupt>
e: 0d c0 rjmp .+26 ; 0x2a <__bad_interrupt>
10: 0c c0 rjmp .+24 ; 0x2a <__bad_interrupt>
12: 0b c0 rjmp .+22 ; 0x2a <__bad_interrupt>
14: 0a c0 rjmp .+20 ; 0x2a <__bad_interrupt>
16: 09 c0 rjmp .+18 ; 0x2a <__bad_interrupt>
18: 08 c0 rjmp .+16 ; 0x2a <__bad_interrupt>
0000001a <__ctors_end>:
1a: 11 24 eor r1, r1
1c: 1f be out 0x3f, r1 ; 63
1e: cf e5 ldi r28, 0x5F ; 95
20: d2 e0 ldi r29, 0x02 ; 2
22: de bf out 0x3e, r29 ; 62
24: cd bf out 0x3d, r28 ; 61
26: 02 d0 rcall .+4 ; 0x2c <main>
28: 0a c0 rjmp .+20 ; 0x3e <_exit>
0000002a <__bad_interrupt>:
2a: ea cf rjmp .-44 ; 0x0 <__vectors>
0000002c <main>:
2c: df 93 push r29
2e: cf 93 push r28
30: cd b7 in r28, 0x3d ; 61
32: de b7 in r29, 0x3e ; 62
34: 80 e0 ldi r24, 0x00 ; 0
36: 90 e0 ldi r25, 0x00 ; 0
38: cf 91 pop r28
3a: df 91 pop r29
3c: 08 95 ret
0000003e <_exit>:
3e: f8 94 cli
00000040 <__stop_program>:
40: ff cf rjmp .-2 ; 0x40 <__stop_program>
[/plain]
36行目でスタートアップルーチンからmain関数が呼ばれています。なので、51行目でmain関数から抜け出した後は37行目に処理が移ります。その後は54行目で割り込みを禁止して、最終的に57行目の無限ループに入ります。
main関数を抜け出した後はしっかりと無限ループでトラップされることが確認されたので、安心できました。実質的に上記コードはmain関数の最後にfor(;;)があるようなものです。これで、main関数の形式はvoid main(void)形式がベスト(ただし警告が出る)ということが言えました。
と言いたいところですが、まだ見落としている点があります。それはmain関数中にfor(;;)などの無限ループがありmain関数が終了しない時です。マイコンプログラムではむしろこちらの方が多いです。
次回はこの場合のコードを検証してみます。
by Shiozaki