« 2007年04月 | メイン | 2007年06月 »

2007年05月23日

Wiiリモコンの値をJavaScriptで取得する方法(Wiiインターネットチャンネル)

任天堂のQ&Aサイトで、Wiiリモコンの値を取得する方法が公開されていることを知人から教えてもらいました。

インターネットチャンネル向けのウェブページを作りたいのですが…。 : Q&A - Wii

インターネットチャンネルの拡張機能について知りたい
Wiiインターネットチャンネルでは特別な JavaScript を用いる事でWiiリモコンのひねりや テレビとの距離、複数のコントローラの状態を取得する事ができます。

手元にWii本体の実機がないので(Wiiリモコンはある^^)まだ試していませんが、こんな感じでJavaScriptからWiiリモコンの値が取れるっぽいですね。

if (window.opera && window.opera.wiiremote) {
  var num = 0; // wiiremote number 0,1,2,3
  var pad = window.opera.wiiremote.update(num);
  if (pad.isEnabled && pad.isDataValid) {
    // 
    var x = null;
    var y = null;
    if (pad.dpdValidity > 0) {
       x = pad.dpdX;
       y = pad.dpdY;
    }
    // 
    // if (!pad.isBrowsing) { var hold = pad.hold; }
    var button_left  = (pad.hold & 1);
    var button_right = (pad.hold & 2);
    var button_up    = (pad.hold & 4);
    var button_down  = (pad.hold & 8);
    var button_plus  = (pad.hold & 16);
    var button_2     = (pad.hold & 256);
    var button_1     = (pad.hold & 512);
    var button_B     = (pad.hold & 1024);
    var button_A     = (pad.hold & 2048);
    var button_minus = (pad.hold & 4096);
    // 
    var distance = pad.dpdDistance;
    var roll_angle = Math.atan2(pad.dpdRollY, pad.dpdRollX) * 180.0 / Math.PI;
    // 
    // hogehoge
  }
}

numの値を変えれば、2本目以降のリモコンの値も取れるみたいです。

画面からの距離(dpdDistance)やリモコンの傾き(dpdRollY, dpdRollX)の情報が取れるのは面白いですね。 Wiiリモコンを用いたインタフェースの研究開発とかを個人の家庭用ゲーム機で実現できるので、hackしがいがあるかも。

拡張機能アツすぎます。

実際に動いたー!っていう人がいればぜひ教えてください。

2007年05月22日

SWF Binary Golf on FlashLite

先週の土曜日の勉強会で発表した資料を公開します。

FizzBuzz SWF Binary Golf on FlashLite 1.1
FizzBuzz SWF Binary Golf on FlashLite 1.1
http://namazu.org/~takesako/swf/FizzBuzz-SWF-Golf.ppt

申し訳ないことにActionScriptの話は一つもでてきません。 SWFファイルの構造解析とASバイトコードの話です。 そんなシチュエーションがあるかどうかわかりませんが、 SWFファイルのバイナリを直接いじりたいときに役に立つのではないかと。

勉強会ではどの発表も面白かったのですが、個人的にはBeInteractive!のyossyさんとASバイトコードの話ができて嬉しかったです。

[お約束] SWF で FizzBuzz ってみた

とりあえず

(1) 正解のFizzBuzz文字列をそのまま出力した場合 -> 478 byte (FlashLite1.1)
http://namazu.org/~takesako/swf/fizz478.txt

(2) FizzBuzzを計算しながら出力した場合 -> 251 byte (FlashLite1.1)
http://namazu.org/~takesako/swf/fizz251.txt

(3) FizzBuzzを計算しながら出力した場合 -> 159 byte (FlashLite2.0)
http://namazu.org/~takesako/swf/fizz159.txt

ここまで圧縮できました。

ASバイトコード最適化

ASバイトコードの最適化の過程はこんな感じで

http://namazu.org/~takesako/swf/

--- fizz287.flm	
+++ fizz161.flm	
@@ -1,71 +1,48 @@
-movie 'fizz287.swf' // flash 4, total frames: 1, frame rate: 12 fps, 104x104 px
+movie 'fizz161.swf' compressed // flash 6, total frames: 1, frame rate: 12 fps, 104x104 px
 
   frame 0
-    push 'i', '0'
-    setVariable
    label1:
-    push 'i', 'i'
+    push '100', 'i', 'i', 'i'
     getVariable
     increment
     setVariable
-    push '100', 'i'
     getVariable
     oldLessThan
-    not
-    not
     branchIfTrue label6
-    push 'x', 'x', 'x'
-    getVariable
-    push 'i'
+    push 'x', 'x'
     getVariable
     push 'i'
     getVariable
     push '15'
-    divide
-    int
-    push '15'
-    multiply
-    subtract
+    modulo
     branchIfTrue label2
     push 'FizzBuzz'
     branch label5
    label2:
     push 'i'
     getVariable
-    push 'i'
-    getVariable
     push '5'
-    divide
-    int
-    push '5'
-    multiply
-    subtract
+    modulo
     branchIfTrue label3
     push 'Buzz'
     branch label5
    label3:
     push 'i'
     getVariable
-    push 'i'
-    getVariable
     push '3'
-    divide
-    int
-    push '3'
-    multiply
-    subtract
+    modulo
     branchIfTrue label4
     push 'Fizz'
     branch label5
    label4:
     push 'i'
     getVariable
    label5:
     concat
     push ' '
     concat
     setVariable
     branch label1
    label6:
   end // of frame 0
 end

yossyさんから教えてもらったFlasmのコードでASバイトコードを読みやすい形に変換しています。

最後のFizzBuzz159byteへの圧縮は、ちょっとトリッキーなことをしています。

僕はActionScriptの経験は素人以下ですが、SWFバイナリの話ならできますので、 またどこかでAS関係のイベントがあれば声を掛けてくださると嬉しいです。(><)

この度は貴重な勉強会に参加できてよかったです。ありがとうございました。

2007年05月15日

連載企画 FizzBuzz ではじめる Code Golf (x86 32bit) 入門

Code Golf とは? Matzにっき(2006-10-05) より

ゴルフとは如何に少ないストロークでホールインするかを競う競技である。 コードゴルフとは、如何に少ないキーストローク(バイト数)で、プログラムを実装できるかを競う競技である。

先日FizzBuzz.com (MS-DOS 16bit版) を作ってみたら、32bit版のプログラムにも挑戦したくなりましたので、x86 32bitで命令長を減らすテクニックについて紹介したいと思います。

※まずはコード長の比較のみで実行クロック数は競わないことにします。

■ x86 32bit コード最適化

【問題】EBXレジスタに1を、EAXレジスタに4を代入したい

できるだけ短いバイト数でコードを実現するためには、いろいろなx86命令をフル活用することを考えます。

自分の思いついた解答をNASMの記法で書いてみます。

(1) 10byteの解答 - 素直に32bit数値をMOVする

BB 01 00 00 00          mov EBX, 1
B8 04 00 00 00          mov EAX, 4

これが一番素直な方法ですが、レジスタ長が32bitのため、コードに 00 が6個もうまってしまっているのがもったいないです。

(2) 8byteの解答 - XORでゼロクリアして、INCで1を作る

31 DB                   xor EBX, EBX
43                      inc EBX
89 D8                   mov EAX, EBX
40                      inc EAX
40                      inc EAX
40                      inc EAX

そこで、XOR命令でレジスタをゼロクリアし、INC命令を繰り返して目的の値を作る方法を考えます。-2byteの節約です。しかし、4を作るのにINC命令が3つ並んでいるのはもったいないです。

(3) 8byteの解答 - シフト命令を使う

31 DB                   xor EBX, EBX
43                      inc EBX
89 D8                   mov EAX, EBX
C1 E0 02                shl EAX, 2

シフト命令で一気に1→4にしてしまいます。左2のシフト命令は3byteなので(2)と比べてコード長は変わりませんでしたが、実行クロックを減らすことはできました。

(4) 7byteの解答 - ALレジスタを活用して8bit代入

31 DB                   xor EBX, EBX
43                      inc EBX
31 DB                   xor EAX, EAX
B0 04                   mov AL, 4

32bitのEAXレジスタ、16bitのAXレジスタ、8bitのAH,ALレジスタの包含関係に着目して、下位8bitだけ4を代入する方法を考えます。これで-1byteの節約です。

(5) 6byteの解答 - LEA命令を使う

31 DB                   xor EBX, EBX
43                      inc EBX
8D 43 03                lea EAX, [EBX+3]

→ 6byte達成

EBXレジスタが必ずゼロクリアされているという前提があれば、先頭のXOR命令は省略できるので4byteで目的を達成することができます。

このように、LEA命令は覚えておくと大変便利です。

このようなx86の定石(パターン)は32bit x86 Tips - x86 TIPS AND TRICKSなどのページにまとまっています。

次回はこれらを踏まえたx86 32bit FizzBuzzプログラムの最適化テクニックを紹介する予定です。

2007年05月10日

FizzBuzz x86 for バイナリアン

昨日の続き。今日は息抜きに FizzBuzz.com (MS-DOS 16bit版) を作ってみました。

0000000 b4 02 bb 31 30 30 ed e8 2c 00 e8 29 00 e8 39 00 
0000020 e8 23 00 e8 3e 00 e8 30 00 e8 1a 00 e8 17 00 e8 
0000040 27 00 e8 2f 00 e8 0e 00 e8 1e 00 e8 08 00 e8 05 
0000060 00 e8 13 00 eb d1 80 ff 30 74 04 88 fa cd 21 88 
0000100 da cd 21 e8 28 00 c3 fe c5 b2 46 cd 21 b2 69 cd 
0000120 21 e9 08 00 b2 42 cd 21 b2 75 cd 21 b2 7a cd 21 
0000140 cd 21 84 ed 75 04 e8 05 00 c3 30 ed eb e6 b2 0d 
0000160 cd 21 b2 0a cd 21 fe c3 80 fb 3a 75 09 b3 30 fe 
0000200 c7 80 ff 3a 74 01 c3 b8 00 4c cd 21 
0000214 

これで140byte

DISアセンブルするとこんな感じです。

00000000  B402              mov ah,0x2
00000002  BB3130            mov bx,0x3031
00000005  30ED              xor ch,ch
00000007  E82C00            call 0x36
0000000A  E82900            call 0x36
0000000D  E83900            call 0x49
00000010  E82300            call 0x36
00000013  E83E00            call 0x54
00000016  E83000            call 0x49
00000019  E81A00            call 0x36
0000001C  E81700            call 0x36
0000001F  E82700            call 0x49
00000022  E82F00            call 0x54
00000025  E80E00            call 0x36
00000028  E81E00            call 0x49
0000002B  E80800            call 0x36
0000002E  E80500            call 0x36
00000031  E81300            call 0x47
00000034  EBD1              jmp short 0x7
00000036  80FF30            cmp bh,0x30
00000039  7404              jz 0x3f
0000003B  88FA              mov dl,bh
0000003D  CD21              int 0x21
0000003F  88DA              mov dl,bl
00000041  CD21              int 0x21
00000043  E82800            call 0x6e
00000046  C3                ret
00000047  FEC5              inc ch
00000049  B246              mov dl,0x46
0000004B  CD21              int 0x21
0000004D  B269              mov dl,0x69
0000004F  CD21              int 0x21
00000051  E90800            jmp 0x5c
00000054  B242              mov dl,0x42
00000056  CD21              int 0x21
00000058  B275              mov dl,0x75
0000005A  CD21              int 0x21
0000005C  B27A              mov dl,0x7a
0000005E  CD21              int 0x21
00000060  CD21              int 0x21
00000062  84ED              test ch,ch
00000064  7504              jnz 0x6a
00000066  E80500            call 0x6e
00000069  C3                ret
0000006A  30ED              xor ch,ch
0000006C  EBE6              jmp short 0x54
0000006E  B20D              mov dl,0xd
00000070  CD21              int 0x21
00000072  B20A              mov dl,0xa
00000074  CD21              int 0x21
00000076  FEC3              inc bl
00000078  80FB3A            cmp bl,0x3a
0000007B  7509              jnz 0x86
0000007D  B330              mov bl,0x30
0000007F  FEC7              inc bh
00000081  80FF3A            cmp bh,0x3a
00000084  7401              jz 0x87
00000086  C3                ret
00000087  B8004C            mov ax,0x4c00
0000008A  CD21              int 0x21

もっと短くできるかも。今日はここまで。

2007.05.12 追記

x86のオペコード・マップ(ニーモニック表)を見ると、まだ最適化できる余地が残っていることがわかります。

dias16.lzh に含まれる 80X86_OP.TXT
http://www.vector.co.jp/soft/dos/prog/se008548.html

IA-32 インテル(R) アーキテクチャ・ソフトウェア・デべロッパーズ・マニュアル
中巻:命令セット・リファレンス (日本語 PDF ファイル: 9,139KB)
ftp://download.intel.co.jp/jp/developer/jpdoc/24547103_j.pdf

  • 第3章 命令セット・リファレンス (p.43-811)
  • 付録A オペコード・マップ (p.815-834)
  • 付録B 命令フォーマットおよびエンコーディング (p.837-888)

例えば、8bitのレジスタをINCするよりも16bitのワードレジスタをINCしたほうが実はオプコードが1byte少なかったりします。

inc ch ; FEC5 
inc cl ; FEC1 
inc cx ; 41 

chレジスタの領域までオーバーフローしないときなら、inc cl の代わりに inc cx を使うと1byte節約することができます。

第一段階の最適化

inc *x / dec *x 命令をできるだけ使うようにレジスタを再配置して、call命令の呼び出し回数を減らし、ジャンプ命令を全部相対指定にしたら 140 → 118 byte (-32byte) に短縮できました。

あと、出力する区切り文字も CR LF(2byte) から半角スペース(1byte)に変更しました。

00000000  B402              mov ah,0x2
00000002  BB003A            mov bx,0x3a00
00000005  B93130            mov cx,0x3031
00000008  E82000            call 0x2b
0000000B  E83000            call 0x3e
0000000E  E81D00            call 0x2e
00000011  E83400            call 0x48
00000014  E81100            call 0x28
00000017  E82400            call 0x3e
0000001A  E82B00            call 0x48
0000001D  E80E00            call 0x2e
00000020  E80500            call 0x28
00000023  E81700            call 0x3d
00000026  EBE0              jmp short 0x8
00000028  E81300            call 0x3e
0000002B  E80000            call 0x2e
0000002E  80FD30            cmp ch,0x30
00000031  7404              jz 0x37
00000033  88EA              mov dl,ch
00000035  CD21              int 0x21
00000037  88CA              mov dl,cl
00000039  CD21              int 0x21
0000003B  EB22              jmp short 0x5f
0000003D  43                inc bx
0000003E  B246              mov dl,0x46
00000040  CD21              int 0x21
00000042  B269              mov dl,0x69
00000044  CD21              int 0x21
00000046  EB08              jmp short 0x50
00000048  B242              mov dl,0x42
0000004A  CD21              int 0x21
0000004C  B275              mov dl,0x75
0000004E  CD21              int 0x21
00000050  B27A              mov dl,0x7a
00000052  CD21              int 0x21
00000054  CD21              int 0x21
00000056  84DB              test bl,bl
00000058  7502              jnz 0x5c
0000005A  EB03              jmp short 0x5f
0000005C  4B                dec bx
0000005D  EBE9              jmp short 0x48
0000005F  B220              mov dl,0x20
00000061  CD21              int 0x21
00000063  41                inc cx
00000064  38F9              cmp cl,bh
00000066  7508              jnz 0x70
00000068  B130              mov cl,0x30
0000006A  FEC5              inc ch
0000006C  38FD              cmp ch,bh
0000006E  7401              jz 0x71
00000070  C3                ret
00000071  B8004C            mov ax,0x4c00
00000074  CD21              int 0x21

COM形式の実行ファイルをMS-DOSモードで実行した結果は以下の通りです。

1 2 Fizz 4 Buzz Fizz 7 8 Fizz Buzz 11 Fizz 13 14 FizzBuzz 16 17 Fizz 19 Buzz Fizz 22 23 Fizz Buzz 26 Fizz 28 29 FizzBuzz 31 32 Fizz 34 Buzz Fizz 37 38 Fizz Buzz 41 Fizz 43 44 FizzBuzz 46 47 Fizz 49 Buzz Fizz 52 53 Fizz Buzz 56 Fizz 58 59 FizzBuzz 61 62 Fizz 64 Buzz Fizz 67 68 Fizz Buzz 71 Fizz 73 74 FizzBuzz 76 77 Fizz 79 Buzz Fizz 82 83 Fizz Buzz 86 Fizz 88 89 FizzBuzz 91 92 Fizz 94 Buzz Fizz 97 98 Fizz

コードの解説はあとで書く...かもしれない。

2007.05.12 追記

Yappoさんがコードにコメントをつけてくれました → http://subtech.g.hatena.ne.jp/yappo/20070511/1178907299

2007.05.14 追記

LOOPZ(=LOOPE)命令を活用することにより88byteに短縮できました。

$ od -tx1 -Ax f88.com
000000 b9 05 00 be 03 00 bb 31 30 b4 02 4e 74 10 e2 23
000010 b1 05 b2 42 cd 21 b2 75 cd 21 84 ed eb 0b be 03
000020 00 b2 46 cd 21 b2 69 cd 21 b2 7a cd 21 cd 21 e1
000030 0f eb dd 80 ff 30 74 04 88 fa cd 21 88 da cd 21
000040 b2 20 cd 21 43 80 fb 3a 75 c1 b3 30 fe c7 80 ff
000050 3a 75 b8 b8 00 4c cd 21
000058

ndisasm(w) f88.com でDISアセンブルするとこんな感じです。

00000000  B90500            mov cx,0x5
00000003  BE0300            mov si,0x3
00000006  BB3130            mov bx,0x3031
00000009  B402              mov ah,0x2
0000000B  4E                dec si
0000000C  7410              jz 0x1e
0000000E  E223              loop 0x33
00000010  B105              mov cl,0x5
00000012  B242              mov dl,0x42
00000014  CD21              int 0x21
00000016  B275              mov dl,0x75
00000018  CD21              int 0x21
0000001A  84ED              test ch,ch
0000001C  EB0B              jmp short 0x29
0000001E  BE0300            mov si,0x3
00000021  B246              mov dl,0x46
00000023  CD21              int 0x21
00000025  B269              mov dl,0x69
00000027  CD21              int 0x21
00000029  B27A              mov dl,0x7a
0000002B  CD21              int 0x21
0000002D  CD21              int 0x21
0000002F  E10F              loope 0x40
00000031  EBDD              jmp short 0x10
00000033  80FF30            cmp bh,0x30
00000036  7404              jz 0x3c
00000038  88FA              mov dl,bh
0000003A  CD21              int 0x21
0000003C  88DA              mov dl,bl
0000003E  CD21              int 0x21
00000040  B220              mov dl,0x20
00000042  CD21              int 0x21
00000044  43                inc bx
00000045  80FB3A            cmp bl,0x3a
00000048  75C1              jnz 0xb
0000004A  B330              mov bl,0x30
0000004C  FEC7              inc bh
0000004E  80FF3A            cmp bh,0x3a
00000051  75B8              jnz 0xb
00000053  B8004C            mov ax,0x4c00
00000056  CD21              int 0x21

MS-DOSで一文字ずつ出力する方法(ah=02h, int 21h)ではなく、 文字列表示命令(ah=09h, int 21h)を使えばもっと小さくできるかもしれません。

FizzBuzz - Golf Challenge

FizzBuzzプログラムを書くのが流行っているみたいなので私も参加してみることに。

Perl部門

1. 目指せ最短 (perl -eも含めて56byte)

perl -e'die+map{(Fizz)[$_%3].(Buzz)[$_%5]||$_,$/}1..1e2'

※ perl -lオプションを使わずに最短を目指す。標準エラー出力がNGの場合はprintを使って57byteに

perl -e'print+(Fizz)[$_%3].(Buzz)[$_%5]||$_,$/for 1..1e2'

anarchy golf - FizzBuzz で換算すると48byteでPerl最短 (perl -eを含めない)

print+(Fizz)[$_%3].(Buzz)[$_%5]||$_,$/for 1..1e2

これだと perl FizzBuzz.pl と実行できて Code Golf に参加できます。

お約束の展開

2. ppencodeバージョンで

perl -e "print q q print q and print chr oct oct ord uc q map m and print chr oct oct oct ord uc q else and print chr ord uc qw q for q and print chr ord q tie gt and print chr length q x rename sethostent srand pack pipe setpwent syscall else eq split sleep endservent qw require symlink ne keys ord require x and print chr length q q splice srand getservbyname setnetent ne reset endprotoent foreach scalar rewinddir cos setnetent not else getprotobyname q and print chr oct oct oct ord uc q rmdir and print chr oct hex ord uc q dump and and print chr oct oct oct ord uc qw q binmode q and print chr oct hex ord uc q my m and print chr oct oct oct ord uc q oct no and print chr oct oct ord uc q rmdir and print chr oct hex ord uc qw q wait q and print chr oct oct ord uc qw q fcntl q and print chr oct hex ord q q eq and print chr ord uc qw q bind q and print chr ord q dump and and print chr length q q splice srand getservbyname setnetent ne reset endprotoent foreach scalar rewinddir cos setnetent not else getprotobyname q and print chr length q q splice srand getservbyname setnetent ne reset endprotoent foreach scalar rewinddir cos setnetent not else getprotobyname q and print chr oct oct oct ord uc q rmdir and print chr oct hex ord uc q dump and and print chr oct oct oct ord uc qw q bless q and print chr oct hex ord uc q my m and print chr oct oct oct ord uc q lc eval and print chr oct ord uc q each ne and print chr oct hex ord uc qw q wait q and print chr oct oct hex ord qw q die q and print chr oct oct hex ord qw q die q and print chr oct oct oct ord uc qw q binmode q and print chr oct hex ord uc q my m and print chr oct oct ord uc qw q bind q and print chr oct oct oct ord uc qw q binmode q and print chr oct oct ord uc qw q gt q and print chr ord qw q for q and print chr ord q xor x and print chr ord q or no and print chr ord q q q and print chr oct oct oct ord q eq ne and print chr oct oct ord uc qw q fcntl q and print chr oct oct ord uc qw q for q and print chr oct oct oct ord q eq le and print chr ord q ne sin and print chr hex ord q m alarm" | perl

※ perlの予約語だけでプログラミングした場合

他の Perl Monger の解答

3. 404 Blog Not Found:ブクマゴルフってどうよ? (66byte)

perl -le 'print $_%15?$_%5?$_%3?$_:Fizz:Buzz:FizzBuzz for(1..100)'

※オーソドックスな実装

4. ひおにっき(2007-05-09) [Program] FizzBuzz (56byte)

perl -le'print+(Fizz)[$_%3].(Buzz)[$_%5]||$_ for 1..100'

※インスパイヤの元ネタ。おそらくこれが最短でスマートな実装かも。hioさんの実装よりも短くしようと頑張ったけど、結局抜けませんでした。

5. Golf Challenge: FizzBuzz (この中から面白い実装をピックアップ)

perl -le 'print+($_,Fizz,Buzz,FizzBuzz)[3&19142723>>2*$_%30]for 1..100'

※ あとでググって見つけたページ。自分の解答もかぶっててちょっと悔しかったです。(><)

6. YappoLogs: FizzBuzzのPerlさいたんきろくたっせい(34ばいと) (Yappoさんのwarn実装)

perl -le'warn((Fizz)[$_%3].(Buzz)[$_%5]||$_)for 1..100'

※ FizzBuzz at -e line 1. みたいな表示になるけど、hioさんの実装より1byte短くなる

7. Acme::FizzBuzz を使えば、これ最強

SET PERL5OPT=-MAcme::FizzBuzz # MS-DOSの場合
setenv PERL5OPT="-MAcme::FizzBuzz" # tcsh系の場合
PERL5OPT="-MAcme::FizzBuzz"; export PERL5OPT # bash系の場合

※ 環境変数PERL5OPTを使う方法はmiyagawaさんに教えてもらいました。

perl -e1

やっぱりPerl最強です。Ruby や Python でもこの最短記録を抜けるのかな?

anarchy golf - FizzBuzz #Language Ranking ではPerlを抜いてBashが1位で43byte、すげー。どんなコードなんだろうか。興味津々です。

2007年05月01日

Multi-USB::MissileLauncher for Win32

YAPC::Asia 2007 Tokyo で大人気だった USB::MissileLauncher をマルチに制御するパッチを作りましたので公開します。

Multi-USB::MissileLauncher for Win32 binary package
http://namazu.org/~takesako/pub/usblauncher-20070501.zip

Triple USB Missile Launcher

■ ctlmissileコマンド使用法

Usage: ctlmissile [ up | down | left | right | fire | stop ] [ 3000]

オリジナルからの変更点

http://www.earth.li/~noodles/files/usblauncher-0.0.3.tar.gz からの変更点

* ctlmissile.c.patch

  1. コンパイルオプション -DLINUX -DWIN32 の追加
    (Linux以外のWindows、Mac OS X の環境でも動作するように)
  2. msec秒でコマンドを発行するコマンドライン引数 [ 3000 ] を追加
  3. 複数のUSBミサイルランチャーの接続に対応
    同時制御と逐次制御が可能

■ インストール方法

Windows の場合

事前にLibUsb-Win32からダウンロードできるlibusb-win32-filter-bin-0.1.12.1.exeをインストール

$ gcc -DWIN32 ctlmissile.c -o ctlmissile -lusb

Linux の場合

ディストリビューション付属のlibusbパッケージをインストール

$ gcc -DLINUX ctlmissile.c -o ctlmissile -lusb

Mac OS X の場合

libusbをインストール

$ gcc ctlmissile.c -o ctlmissile -lusb

■ 応用

POE::Component::Server::TCPを使って、USBミサイルランチャーを制御するTCPデーモンを1時間で書く。

missile_server.pl

#!/usr/bin/perl
use POE qw(Component::Server::TCP);
use strict;

sub main {
  my $bind_port = $ARGV[0] || 6969;

  my $kernel = POE::Component::Server::TCP->new(
    Port     => $bind_port,
    Error    => \&error_handler,
    Concurrency => -1,

    ClientInput        => \&handle_client_input,
    ClientConnected    => \&handle_client_connect,
    ClientDisconnected => \&handle_client_disconnect,
    ClientError        => \&handle_client_error,
    ClientFlushed      => \&handle_client_flush,
    ClientShutdownOnError => 0,
  );

  POE::Kernel->run();
}

sub accept_handler {
  my ($socket, $remote_address, $remote_port) = @_[ARG0, ARG1, ARG2];
}

sub error_handler {
  my ($syscall_name, $error_number, $error_string) = @_[ARG0, ARG1, ARG2];
}

sub exe {
  my ($cmd) = @_;
  $cmd =~ s/[^\s\d\w]//g;
  system split(/\s+/, $cmd);
}

sub handle_client_input {
  my $heap = $_[HEAP];
  my $kernel = $_[KERNEL];
  my $input_record = $_[ARG0];
  my $msg = "yes sir, $input_record";
  if ($input_record =~ m/(shutdown|quit)/i) {
    $kernel->yield("shutdown");
    $msg = "connection closed...";
  } elsif ($input_record =~ m/help/i) {
    $msg = "Ugage: [ up | down | left | right | fire | stop ] [ 3000 ]";
  } elsif ($input_record =~ m/^\s*$/ims) {
    $msg = "help";
  } else {
    exe("ctlmissile $input_record");
  }
  $heap->{client}->put($msg);
}

sub handle_client_error {
  my ($syscall_name, $error_number, $error_string) = @_[ARG0, ARG1, ARG2];
}

sub handle_client_connect {
  my $heap = $_[HEAP];
  $heap->{client}->put("connected on USB-Missile-Launcher 200 OK");
}

sub handle_client_disconnect {}

sub handle_client_flush {}

main();

1;

POEのプログラミングは初めてでしたが、perldoc POE::Component::Server::TCP 見ながら簡単に書けました。

■参考URL

YappoLogs: ミサイルは国境を越える 
http://blog.yappo.jp/yappo/archives/000504.html

Google Maps: ミサイルが本当に国境を越えてる???
http://maps.google.com/maps?q=38.22657,-112.299&z=18&t=k

Tilting Google Maps and MissileLauncher - SlideShare (miyagawa-san at osdc.tw)
http://www.slideshare.net/miyagawa/tilting-google-maps-and-missilelauncher/

throw new CybozuLabsException() - libusbを使うPHP Extensionを作りました
http://labs.cybozu.co.jp/blog/tsuruoka/anubis/blog_show/43

TokuLog 改め Perl を極めて結婚するブログ - missile on ruby. 
http://d.hatena.ne.jp/tokuhirom/20070407/1175935832

西尾泰和のブログ @ Cybozu Labs: PythonワンライナーでUSBミサイルランチャーを制御 
http://labs.cybozu.co.jp/blog/nishio/2007/04/pythonusb.html

*BSDでのUSBデバイスドライバーの実装
http://www.ofug.net/~yamajun/presentation/2003/usb.html

ToDo

ctlmissile.cで複数のミサイルランチャーを制御する拡張コマンドを追加したので (ひ)メモ - Assurer::Plugin::Notify::Missileの概要設計の実装が待たれるところです。