本記事はリライト中です。
『アベンジャーズ』(2012)
神も含めた超人たちの中、生身の人間ホークアイが、危なっかしくてしょうがないです。でも、きっとそこが良いのでしょう。弓使いというだけで魅力的ですし。
コミックのタイトルは、『アベンジャーズ・アッセンブル』(Avengers Assemble)、「アベンジャーズ!集合せよ!」の意味で、コミック中の名セリフとなっているらしいです。しかし映画のセリフには出てきませんでした。まあいろいろあるのでしょうね。
「AVRマイコンでトンボの羽根の周波数」で8ビットって面白いなあ、とAVRマイコンに興味を持ってしまったので、もう少し理解を深めていこうかと思います。理解を深めるために「Avengers Assemble」つながりで、「AVR Assemble」作っていきます。といっても、一気に作るのは大変なので、命令をひとつずつ理解しながら進めます。
ニモニックを機械語に変換その1
ニモニックからマシン語への変換するのに、以下のデータシートを参考にしました。
上記は、ニモニックと、その機械語の構成です。このニモニックを機械語にするスクリプトを書いてみました。
~$ cat -n avr-asm.pl 1 while(<>){ 2 if(/(?:LDI|ldi)\s+[rR](\d+),([0-9a-fxA-FX]+)/){ 3 $d=$1; 4 $K=$2; 5 $K=hex $K if $K =~ /x/; 6 printf "%01X%01X%01X%01X\n" 7 ,$d-16 8 ,($K&0xf) 9 ,0xE 10 ,($K&0xf0)>>4 11 } 12 } ~$
解説です。正規表現でニモニック解析です。つまり2行目が要です。
(?:LDI|ldi)は、単純にオアです。(?:)をつけて、後方参照記憶をせずに、オアする単語を囲うだけにしています。
[rR](\d+)で、レジスタ番号を取得しています。この命令で指定できるレジスタはr16〜r31なので、$dに格納されて、機械語にするときに、16を引きます。
([0-9a-fxA-FX]+)は、16進または10進のみ許容です。
エラー処理は、まだ追加しません。
さて使ってみます。
~$ echo 'ldi r24,8' | perl avr-asm.pl 88E0 ~$ echo 'ldi r24,9' | perl avr-asm.pl 89E0 ~$ echo 'ldi r24,10' | perl avr-asm.pl 8AE0 ~$ echo 'ldi r24,0xa' | perl avr-asm.pl 8AE0 ~$ echo 'ldi r24,0xF' | perl avr-asm.pl 8FE0 ~$ echo 'ldi r24,0x10' | perl avr-asm.pl 80E1 ~$
マイコンを動かさないと合っているか分かりませんが、とりあえずここまで。
ニモニックを機械語に変換その2
一命令増やしてみましょう。
while(<>){ if(/(?:LDI|ldi)\s+[rR](\d+),([0-9a-fxA-FX]+)/){ $d=$1; $K=$2; $K=hex $K if $K =~ /x/; printf "%01X%01X%01X%01X\n" ,$d-16 ,($K&0xf) ,0xE ,($K&0xf0)>>4 } if(/(?:OUT|out)\s+([0-9a-fxA-FX]+),[rR](\d+)/){ $A=$1; $r=$2; $A=hex $A if $A =~ /x/; printf "%01X%01X%01X%01X\n" ,($r&0xf) ,($A&0xf) ,0xB ,0x8 | ($A>>3&0x6) | ($r>>4 & 0x1) } }
一命令増やしただけですが、行数が多くてげんなりします。命令一つに一行にしたいです。
ということで、書き換えました。要はニモニックを機械語に置換すれば良いのです。
~$ cat avr-asm.pl while(<>){ s/(0[xX][0-9A-Fa-f]+)/hex $1/e; s/LDI\s+[rR](\d+),(\d+)/sprintf("%01X%01X%01X%01X",($1-16),($2&0xf),0xE,($2>>4) )/e; s/OUT\s+(\d+),[rR](\d+)/sprintf("%01X%01X%01X%01X",($2&0x0f),($1&0xf),0xB, 0x8|($1>>3&0x06)|($2>>4&0x01) )/e; print; }
命令を増やしつつ、ダイエットもしていきます。
~$ cat avr-asm.pl while(<>){ s/(0X[0-9A-F]+)/hex $1/ei; s/LDI\s+R(\d+),(\d+)/sprintf("%1X%1X%1X%1X",($1 - 16),($2 & 0xf),0xE,($2>>4) )/ei; s/OUT\s+(\d+),R(\d+)/sprintf("%1X%1X%1X%1X",($2 & 0xf),($1 & 0xf),0xB,(8|($1>>3&6)|($2>>4&1)) )/ei; s/EOR\s+R(\d+),R(\d+)/sprintf("%1X%1X%1X%1X",($1 & 0xf),($2 & 0xf),0x2,(4|($1>>4)|($2>>3&2)) )/ei; print; }
sprintfのところが、冗長に感じてきましたので関数化します。
~$ cat avr-asm.pl while(<>){ s/(0X[0-9A-F]+)/hex $1/ei; s/LDI\s+R(\d+),(\d+)/asm(0xE,($2>>4), ($1 - 16),($2 & 0xf) )/ei; s/OUT\s+(\d+),R(\d+)/asm(0xB,(8|($1>>3&6)|($2>>4&1)), ($2 & 0xf),($1 & 0xf) )/ei; s/EOR\s+R(\d+),R(\d+)/asm(0x2,(4|($1>>4)|($2>>3&2)), ($1 & 0xf),($2 & 0xf) )/ei; print; } sub asm{ my($x3,$x4,$x1,$x2) = @_; sprintf("%1X%1X%1X%1X",$x1,$x2,$x3,$x4); }
()が多い気がします。
演算子の優先順位は、<<>>、|、&で比べると、
高い
<<>>
&
|
低い
ですので、()が取れます。
あと、正規表現を少し変えることで、各行を揃えることができます。
~$ cat avr-asm.pl while(<>){ s/(0X[0-9A-F]+)/hex $1/ei; s/LDI\s+R{1}(\d+),R{0}(\d+)/asm(0xE,( $2>>4 ), $1-16 ,$2&0xf )/ei; s/OUT\s+R{0}(\d+),R{1}(\d+)/asm(0xB,(8|$1>>3&6|$2>>4&1), $2&0xf,$1&0xf )/ei; s/EOR\s+R{1}(\d+),R{1}(\d+)/asm(0x2,(4|$1>>4 |$2>>3&2), $1&0xf,$2&0xf )/ei; print; } sub asm{ my($x3,$x4,$x1,$x2) = @_; sprintf("%1X%1X%1X%1X",$x1,$x2,$x3,$x4); }
使ってみます。
~$ cat sample ldi r24,0x08 out 0x17,r24 eor r24,r25 ~$ perl avr-asm.pl sample 88E0 87BB 8927 ~$
いい感じです。
ニモニックを機械語に変換その3
作ったアセンブラの変換が正しく機能しているか確認するにはどうすればよいでしょうか。
いきなりマイコンに書き込んで動作確認するのは骨が砕けます。やはり、ただしいアセンブラとの結果比較がよいでしょう。
アセンブラの前に、C言語がどのように機械語に落とされているか確認します。このソースを使います。
root@raspberrypi:~# cat led-loop.c #include <avr/io.h> #include <util/delay.h> int main() { DDRB = _BV(3); while(1){ PORTB ^= _BV(3); _delay_ms(500); } }
ビルドします。
root@raspberrypi:~# make -f makefile-ledloop avr-gcc -g -O2 -mmcu=attiny85 -DF_CPU=1000000UL -c -o led-loop.o led-loop.c avr-gcc -g -O2 -mmcu=attiny85 led-loop.o -o led-loop avr-objcopy -j .text -j .data -O ihex led-loop led-loop.hex
ディスアセンブラです。
root@raspberrypi:~# avr-objdump -d led-loop.o led-loop.o: ファイル形式 elf32-avr セクション .text.startup の逆アセンブル: 00000000 <main>: 0: 88 e0 ldi r24, 0x08 ; 8 2: 87 bb out 0x17, r24 ; 23 4: 98 e0 ldi r25, 0x08 ; 8 6: 88 b3 in r24, 0x18 ; 24 8: 89 27 eor r24, r25 a: 88 bb out 0x18, r24 ; 24 c: 2f e9 ldi r18, 0x9F ; 159 e: 36 e8 ldi r19, 0x86 ; 134 10: 81 e0 ldi r24, 0x01 ; 1 12: 21 50 subi r18, 0x01 ; 1 14: 30 40 sbci r19, 0x00 ; 0 16: 80 40 sbci r24, 0x00 ; 0 18: 01 f4 brne .+0 ; 0x1a <main+0x1a> 1a: 00 c0 rjmp .+0 ; 0x1c <main+0x1c> 1c: 00 00 nop 1e: 00 c0 rjmp .+0 ; 0x20 <__zero_reg__+0x1f>
あれ、何か変です。18行目のbrneのジャンプ先がおかしい気がします。これだと動かないはずですが、LEDはピカピカしています。
HEXと比べてみましょう。
root@raspberrypi:~# cat led-loop.hex :100000000EC015C014C013C012C011C010C00FC064 :100010000EC00DC00CC00BC00AC009C008C011241E :100020001FBECFE5D2E0DEBFCDBF02D011C0E8CF0A :1000300088E087BB98E088B3892788BB2FE936E83A :1000400081E0215030408040E1F700C00000F3CF54 :04005000F894FFCF52 :00000001FF root@raspberrypi:~#
HEXの方は合ってそうです。これから作る予定のニモニック置換プログラムは(アセンブラとはあえて呼ばない)は、avr-asでアセンブルしたHEXと比べることにします。
ニモニックを機械語に変換その4
簡単なアセンブラを作って、avr-asでアセンブルしたものを、マイコンに書き込んで確認しました。
割り込みベクタは無視して0番地からプログラム配置しています。
ldi r24,0x08 ldi r25,0x00 out 0x17,r24 out 0x18,r24 LOOP: rjmp LOOP
このプログラムだと、LEDは光りました。
職業病でこの簡単なプログラムでも、リセットがかかっていないか確認したくなります。
マイコン起動時には、LEDはOFFさせて、nopをいれてから、LEDをONして、無限ループ。
ただしこのnopは、Vimで、yy1999pです。(nopが2000個)
ldi r24,0x08 ldi r25,0x00 out 0x17,r24 out 0x18,r25 nop out 0x18,r24 LOOP: rjmp LOOP
2000個nopを入れると、書き込みも時間がかかりますね。
root@raspberrypi:~/led# make write avrdude -p t85 -c linuxgpio -U flash:w:sample.hex avrdude: AVR device initialized and ready to accept instructions Reading | ################################################## | 100% 0.00s avrdude: Device signature = 0x1e930b avrdude: NOTE: "flash" memory has been specified, an erase cycle will be performed To disable this feature, specify the -D option. avrdude: erasing chip avrdude: reading input file "sample.hex" avrdude: input file sample.hex auto detected as Intel Hex avrdude: writing flash (4012 bytes): Writing | ################################################## | 100% 3.10s avrdude: 4012 bytes of flash written avrdude: verifying flash memory against sample.hex: avrdude: load data flash data from input file sample.hex: avrdude: input file sample.hex auto detected as Intel Hex avrdude: input file sample.hex contains 4012 bytes avrdude: reading on-chip flash data: Reading | ################################################## | 100% 1.86s avrdude: verifying ... avrdude: 4012 bytes of flash verified avrdude: safemode: Fuses OK (E:FF, H:DF, L:62) avrdude done. Thank you. root@raspberrypi:~/led#
LEDの点灯の仕方は見た目では変わりませんでした。そこで、ONとOFFを逆にしてみます。
ldi r24,0x08 ldi r25,0x00 out 0x17,r24 out 0x18,r24 nop out 0x18,r25 LOOP: rjmp LOOP
書き込んで確認すると、LEDが点灯しています。つまりどこかでリセットがかかっています。
コメント