# 楽しいプログラミング 《第4回》  年末に待望の雪が降って、まわりの話題はスキー一色。私のように滑った事のない者にとっては、肩身の狭い事この上ありません。一度行ってみようかなぁ。でも、指が霜焼けになってキーボードが叩けなくなったらヤだなぁ。 ## ■ アセンブラ使ってます?  高級言語からプログラムを始められた方の目に、アセンブラってどういうふうに映るんでしょう。やはり多少なりともコンプレックスは感じるのでしょうね。私の場合、逆にCを始める時にかなりの抵抗がありました。慣れてしまえばあんなに楽なものはないのですが。書く、書かないは別にしても、アセンブラにコンプレックスがあるようでは、いいプログラムは作れません。ここで延々、アセンブラの効用を説いてもよろしいのですが、本書のタイトルはあくまで「Cジャーナル」。というわけで、今回は私が愛用しているアセンブラの便利マクロを、いくつかご紹介するにとどめます。  Cのライブラリを書く場合は、TASMが最適です。OPTASMはMASM5.1の高級言語サポートに対応していませんので、かっこよく引数やローカル変数を書けません。いずれにしろ、MASMを使う理由はありません。そもそも、ラディカルなプログラマが、Mと名のつく開発ツールの速度に耐えられるはずはないのです。 ## ■ clrとtstがない  それまでずっと68系のMPUに馴染んできて、初めて86の命令コード表を眺めた時、「`clr`」と「`tst`」が無いのには我が目を疑いました。対レジスタでは、「`clr`」を「`xor`」で、「`tst`」を「`or`」で代用できる事を知り、なるほどと思いましたが、そんなふうに本来の動作とは異なった記述はすべきではありません。きちんと「`clr`」、「`tst`」と書きましょう(リスト1)。 [リスト1] clr macro reg xor reg,reg endm tst macro reg or reg,reg endm tstb macro label cmp byte ptr label,0 endm tstw macro label cmp word ptr label,0 endm ## ■ 非力なジャンプ命令  86のジャンプ命令は、前方への無条件ジャンプを明示的に「short」としないと2バイト命令にならない、ロングの条件ジャンプがない、等の不満があります。TASM2.0やOPTASMでは、このあたりを自動的に対応するよう強化されていますから、それに頼ってもよいのですが、私はリスト2のようなマクロを利用してきました。「`jmpl z,label`」、「`jmpln z,label`」のように使います。また、「`jmpw`」は286,386対応のウェイト用です。 [リスト2] jmps macro label jmp short label endm jmpl macro cc,label local next jn&cc next jmp label next: endm jmpln macro cc,label local next j&cc next jmp label next: endm jmpw macro local next jmp short next next: endm ## ■ 複数レジスタのpush、pop  これができないのも目が点でした。アスキーの黒本第2弾、「MS-DOSプログラマーズハンドブック」様々です(リスト3)。「`pushm `」のようにブラケットでくくってレジスタを指定します。OPTASMは気が効いてて、最初から「`pushm,popm`」命令が使えますから、ブラケットでくくる必要がありません。TASM2.0でも「`push ax bx`」と書けるようになりましたが、レジスタをスペースで区切る(カンマは使えない)のは、きもち悪いです。 [リスト3] pushm macro regs irp reg, push reg endm endm popm macro regs irp reg, pop reg endm endm ## ■ 複数回のレジスタシフト  186以降で「`shr reg,imm`」の命令がサポートされました。「今どき8086なんて無視無視」、と言い切れる人はそのまま書いてしまえばよいのですが、少々はばかられる人はリスト4のようなマクロを使いましょう。「`@Cpu`」はTASMの定義済みシンボルで、「`.186`」疑似命令で指定したプロセッサの型を表します。 [リスト4] $shift macro op,reg,imm if @Cpu and 2 op reg,imm else rept imm op reg,1 endm endif endm shlm macro reg,imm $shift shl,reg,imm endm shrm macro reg,imm $shift shr,reg,imm endm ## ■ システムコール  MS-DOSやBIOSのシステムコールも、当然マクロを使います。ALレジスタも使うものは、まとめてAXレジスタへロードできるようにしましょう。そういう場合に便利な「'movhl'」マクロも用意しておきます(リスト5)。ファンクション番号もシンボル定義すると、いっそう読みやすくなります。 F_OPEN equ 3Dh F_CLOSE equ 3Eh O_READ equ 0 msdos F_OPEN,O_READ ... msdos F_CLOSE [リスト5] movhl macro rx,rh,rl mov rx,(rh shl 8)+(rl and 0FFh) endm msdos macro cmd,prm ifb mov ah,cmd else movhl ax,cmd,prm endif int 21h endm ## ■ 構造化マクロ  アセンブラの「構造化マクロ」というと、高級言語風にインデントまでしてしまい、マクロを理解していないととても読めないようなものまでありますが、私が使っているものはもっと素朴な、「IF~THEN~ELSE~」とループだけのものです(リスト6)。それでもローカルラベルが大幅に減り、可読性と記述性は格段に向上します。そもそも、アセンブラを使う限り、ソースから生成コードがただちにイメージできないまでに「構造化」する必要などありません。それならCとインラインアセンブラで書けば良いのですから。
 使い方の例をリスト7に示します。ネストの深さ、ネスト内で並列に書ける条件ブロックの数に制限がありますが、かなり以前に書いたマクロで、ドキュメントも残っていないため、正確なところはわかりません。それらの制限を越えるとアセンブルエラーとなりますから、その場合は止むを得ずローカルラベルを使いましょう。普通の素直なプログラムであれば、まず大丈夫です。 [リスト6] $base = 0 $field = 1 $add = 1 $nest = 0 _if macro cc $begin $jcc n,cc,%$base,%$field endm _ifn macro cc $begin $jcc <>,cc,%$base,%$field endm _else macro $end $jmp %$base,%($field+1) $label %$base,%$field $field = $field+1 endm _endif macro $end $label %$base,%$field $end2 endm _repeat macro $begin $label %$base,%$field endm _until macro cc $end $jcc n,cc,%$base,%$field $label %$base,%($field+1) $end2 endm _while macro cc $end $jcc <>,cc,%$base,%$field $label %$base,%($field+1) $end2 endm _loop macro $end $loop %$base,%$field $label %$base,%($field+1) $end2 endm _break macro cc $jcc <>,cc,%$base,%($field+1) endm _cont macro cc $jcc <>,cc,%$base,%$field endm $begin macro $field = $field shl 1 $nest = $nest + 1 endm $end macro if ($nest le 1) $add = 1 $base = $base mod 128 else $add = 8 shl ($nest * 2) $base = $base mod ($add * 4) endif endm $end2 macro $base = $base + $add $field = $field shr 1 $nest = $nest - 1 endm $jmp macro l1,l2 jmp short $L&l1&_&l2 endm $jcc macro n,cc,l1,l2 ifnb j&n&cc $L&l1&_&l2 else $jmp l1,l2 endif endm $loop macro l1,l2 loop $L&l1&_&l2 endm $label macro l1,l2 $L&l1&_&l2: endm [リスト7] ; _if ... _else ... _endif ; _ifn ... _else ... _endif cmp al,1 _if b ... _else cmp al,3 _ifn e ... _else ... _endif _endif ; _repeat ... _until ; _repeat ... _while ; _repeat ... _loop mov dx,25 _repeat ... mov cx,80 _repeat ... _loop ... dec dx _until z ; _repeat ... _break ... _until ; _repeat ... _cont ... _until _repeat ... cmp al,'A' _break e cmp al,'a' _break e tst al _cont z ... _until  アセンブラも、このように意図している動作をコードから自然に読み取れるような記述をすれば、決して難しいものではありません。アセンブラの難しさは、可読性というよりはむしろ、レジスタの内容、フラグ、スタックレベル等を常に注意深く把握しておかなくてはならない点にあります。  次回はがらりと趣向を変えて、Turbo Pascal6.0のTurbo Visionでいってみましょう。(こう言っておけば、読まざるを得ないですから) (雑誌「**PC POWER** 1992年10月号」掲載)