x86アセンブラDSLを作ってみるテスト
ずーっと前にStringでバイナリを作ってDLで呼び出すコードを書いたことあったけども、x86アセンブラの文法はわりとそのままRubyで解釈できるんじゃね?って思ってDSLを作ってみるテスト。Rubyも新しくなったからfiddleを使う。
require 'fiddle' module X86ASM class Register32;end class EAX < Register32;end class Assembler attr_reader :buf def initialize @buf = "" end def eax EAX.new end def mov(dst, src) if Register32 === dst if Integer === src @buf += "\xb8".b + [src].pack("V").b end end end def ret @buf += "\xc3".b end end def self.asm(&b) tmp = Assembler.new tmp.instance_eval &b Fiddle::Function.new([tmp.buf].pack("P").unpack("l!")[0], [], Fiddle::TYPE_INT) end end hoge = X86ASM.asm do mov eax, 510 ret end p hoge.call #=> 510
movやeaxはメソッドとして定義して、eaxはEAXクラスのインスタンスを返す。今はb8固定で生成してるけども、レジスタオブジェクトの種類により値を変えれば好きなレジスタに格納することができるだろうし、例えばmov eax, [ecx]とかなら第2引数が配列になるからそれで判定できるし、+とか付く場合はレジスタオブジェクトに+メソッドを定義するとかRefinementsでArrayの定義を上書きしてやるとかで結構それっぽくできるのではなかろうか。
難点はラベルで、LABEL:という構文はエラーになるから書けなくて、せいぜいlabel :LABELと書くぐらいが関の山か。