Arduino は 8-bit の世界
Arduino で使われている Atmel 社の AVR マイクロコントローラーは 8-bit RISC アーキテクチャを採用している。僕は8ビット CPU で C 言語を使うのは初めての経験だったので,これだけでもちょっと面白いことだと思った。
AVR 用 C コンパイラーでは, int
型の大きさは16ビットに設定されている。8ビット CPU なんだから int
も8ビットなんじゃないの?と思うかもしれないけれど, int
はポインター型と互換でなくてはいけないから*1,やはり16ビットになる。でも AVR の汎用レジスターの大きさは8ビットだから,結果として「int
は扱いの重い型」という具合になる。
例えば次のようなコードがあるとする。
int mulTen(int x) { return x * 10; }
このコードをビルドした後に avr-objdump
を使ってディスアセンブリすると,次のようなアセンブリコードになっていることが分かる。
00000000 <_Z6mulTeni>: 0: 9c 01 movw r18, r24 2: ac 01 movw r20, r24 4: 93 e0 ldi r25, 0x03 ; 3 6: 44 0f add r20, r20 8: 55 1f adc r21, r21 a: 9a 95 dec r25 c: 01 f4 brne .+0 ; 0xe <_Z6mulTeni+0xe> e: 22 0f add r18, r18 10: 33 1f adc r19, r19 12: 42 0f add r20, r18 14: 53 1f adc r21, r19 16: ca 01 movw r24, r20 18: 08 95 ret
ずいぶん複雑なコードだ!何をしているのかというと, x+x
を3回繰り返すことによって8倍した後に, x
を2度足すことによって,最終的に10倍の値を得ている。 Arduino に使われている ATmega は乗算命令 (mul
) を2クロックで実行できるのだから,普通に乗算の組み合わせで実現した方が幾分シンプルにできると思うのだけれど……いずれにせよ,少し手間のかかることをしなければ int
の掛け算はできない。
そこで,これを以下のように8ビット型に書き直してみる。
int8_t mulTen(int8_t x) { return x * 10; }
すると,アセンブリコードは以下のようになる。
00000000 <_Z6mulTena>: 0: 9a e0 ldi r25, 0x0A ; 10 2: 89 9f mul r24, r25 4: 80 2d mov r24, r0 6: 11 24 eor r1, r1 8: 08 95 ret
ずいぶんシンプルなコードになった。 AVR の mul
命令では,乗算の結果が r0
と r1
に格納されるので,それを ret
の前で返値として格納し直している。それだけのコード。
Arduino を使う典型的なケースでは,このような細かい話はほとんど問題にならないだろうと思う。でも,例えば Auduino シンセサイザーなどのように非常にタイトな処理を行う場合には,やはりこの辺りのことに気を配らなければならなくなってくる。
*1:この認識は正しくないようです。詳しくはコメントを参照してください