;タイムディレイ・サブルーチン。START第6話:ちょっとひと休み(SLR社製OPTASMについて)MS-DOS用のアッセンブラと言えばマイクロソフト社製のマクロアッセンブラである“MASM”が有名かつ主流のようです。このMASM、アメリカではすでにバージョン5.0が発売されており、その機能にも磨きがかかってきました。この独占を許すまじとばかりに、Z80ASMで有名な米SLR社がMS-DOSアッセンブラ界に殴り込みをかけてまいりました。その名もオプティマイズアッセンブラ“OPTASM”。V5.0完全上位コンパチブル。(実際には『完全』ではないようで、私もMASM用のとあるマクロライブラリを使用した際にエラーを出しまくられまして少々がっかりしました。)本当に終わったのか?と思ってしまうような超高速性。V3.0と比べると、なんと15倍程度。)それでいながら条件ジャンプの自動最適化処理を行なうため、NEARジャンプとSHORTジャンプを使い分ける必要がありません。他にもローカルラベル機能やクロスリファレンサ内臓など、うれしい機能が満載されています。私は一人で一気にプログラムを組む関係上分割アッセンブルは避けたいわけで、先日も依頼ソフトを完成させたところソースサイズが80Kバイトを越えてしまいました。V3.0にかけますと、RAMディスクを使用しているにもかかわらずアッセンブルに2分以上もかかってしまいました。これにしびれを切らした私は大枚3万9800円(3万5000円にまけてもらいましたが)をはたいてOPTASMを購入したのです。結果は驚き、10秒足らずでアッセンブルが終了しました。その日からアッセンブルが楽しくなったのは言うまでもありません。現在もOPTASMを愛用していますが、ついこの前、ふとMASMの遅さをもう一度実感してみようと思い立ち、開発中の30K程度のソースをMASMにかけてみたのです。OPTASMだと3秒足らずで帰ってきますから、MASM(V3.0)だと40秒以上かかる計算になります。ところがそのとき私の見たものは、出るわ出るわのエラーの山でした。errors”と表示されるのに............そう、オプティマイズの影響とV3.0のバグのために発生するものでした。これでおわかりでしょう。私はもうMASMを使えない身体になってしまったのです。オブジェクトサイズにも違いが現れます。OPTASMの作成したOBJファイルはMASMのものよりも小さくなるのです。これはJMP命令が多く使用されていればいるほどこの差は大きくなります。OPTASMは不用なNOP命令を全く生成しないためです。MASMはNEARジャンプ(+127/-128)が2バイト命令、SHORTジャンプ(+32767/-32768)が3バイト命令であることからパス1実行時にとにかく3バイト確保し、パス2でNEARジャンプだと判断されると余分となった3バイト目にNOPを挿入するのです。現在MASMのV5.0は日本国内では販売されていません。これの上位コンパチのOPTASM(V0.96)は国内代理店を通して現在販売されております。これは何と言ってもおすすめ品です。私個人にしてみれば腹が立つのですが、現在は定価33,800円と6000円安で販売されております。(私のように発売前から注文をしていると6000円損したことになってしまいました。ちなみに私の購入したフロッピーのシリアルNo.は「6」でした。おまけにタイトルはボールペンの直筆ですし。「V30の話」をいたしました。ここではその続きを兼ねまして8086との差やアッセンブラプログラミングの高速テクニックなどを紹介します。●回数指定SHIFT命令8086にはCLレジスタにより回数を指定するSHIFT命令があることは周知の通りです。1ステップの命令で最大15回までの任意シフトができるわけですから、定数乗除算やビット編集にはもってこいの命令のように思われます。DX,CL確かにソースレベルではまことに便利なのですが、オブジェクトレベルになると話は別です。実行速度がかなり遅くなるのです。8086CPUの場合、レジスタの1回シフトに要するクロックは最低の2クロックですが、回数指定シフトの場合は1回のシフトでも12クロックもかかってしまうのです。【表.1】を見てください。ClockCLによる回数指定シフトの場合は、基本8クロックにシフト回数×4クロックを加えた分のクロックを消費します。なり、6回シフトする場合は約2.7倍も高速になります。ちなみに2回シフトの場合は4倍、15回シフトの場合は約2.3倍と、共に回数指定をしない方がずっと高速です。8086の場合は回数指定SHIFT命令の使用は、高速性が必要なく、ビット操作の多用が必要なとき以外は控えるべきでしょう。以上の話はあくまで8086のときのことで、これがV30になるとかなり話は変わってきます。表.2を見ればわかるように、回数指定シフトのクロック数が減っています。Clockこれから計算しますと、7回以上シフトするときは回数指定SHIFT命令を使用した方が良いことになります。7回シフトした場合は共に14クロックですが、当然サイズの小さい回数指定シフトの方がよいでしょう。これはV30が定数指定によるシフトが可能なためです。8086のようにCLレジスタによる指定しかできないと、CLレジスタへ回数をロードする手間がかかってしまうので11回以上シフトしないと元がとれないことになってしまいます。7回以上一度にシフトすることはめずらしいとは思いますが、いちおう憶えておくことをおすすめします。●DEC,INC命令の不条理DEC,INC命令はZ80や6809などの8bitCPUにも存在するオーソドックスな命令です。8086にもあり、任意の汎用レジスタやメモリの内容を±1することができます。ところが8086の場合、この命令の実行サイクルが不条理なのです。Z80などでは当然8bitレジスタに対するINC,DEC命令の方が16bitのペアレジスタの場合よりも速いのですが、8086ではこれが逆になります。16bitレジスタのINC,DEC命令が2クロック、8bitレジスタのINC,DEC命令が3クロックと16bitレジスタの方が高速になっているのです。ゆえに、より高速にDLレジスタ(8bit)の値を±1したいときは、DHレジスタの破壊を考慮しながらDXレジスタをINC,DECすべきです。これは8086のときだけで、V30の場合は8bit/16bitどちらでも2クロックです。V30インテル系CPUの特徴のひとつであるブロック転送専用命令は、8086ではストリング命令として用意されています。MOVS命令がそれで、バイト転送/ワード転送どちらを選択することも自由です。この命令の簡便性、高速性は使用経験ある方は実感済みのことと思います。ここで、8086のMOVS命令の高速性を証明するために、通常のMOV命令を使用した等価ルーチンとの所要クロックの差を見ていくことにしましょう。括弧内の数字はV30の場合。【表.3】まず断わっておくことにしますが、【表.3】のクロック数は2回目以降、最終回の直前までの場合を指しています。ちなみにMOVSを使った場合の第1回目は26(11)Clockとなり、MOVSを使わない場合の最終回は36(29)Clockとなります。【表.3】をご覧になればおわかりのように、8086のストリング命令を使用した場合としなかった場合では実に約2.5倍もの実行速度の差が生じます。このことは脳裏にしっかりととどめておいてください。3ワード程度のデータ転送だからといって1ワードずつレジスタを介して転送するよりも、多少の初期設定をしてでもストリング命令を使用した方が高速な場合だって多々あるのです。V30ならばなおさらです。ここでV30のケースがでましたが、【表.3】の括弧内を見れば一目瞭然なように、ストリング命令を使用しない場合でも25%程度、使用した場合はちょうど2倍も8086より高速なのです。このストリング命令の高速化はV30の特徴のひとつでして、PC-9802がVシリーズになってからグラフィックを多用したゲームなどの速度がグ~ンとアップした理由のひとつはここにあるといってよいでしょう(VRAMアクセスのウエイトが下がったのも大きいですが)。V30プログラマの人はここんとこをうまく利用しましょう。(※以上はメモリアクセスに際してウエイトがかからないという前提で計算したものです。)●ビットフィールド操作命令V30特有の命令にビットフィールド操作命令があります。これは他の16bitCPUにも類を見ないものです。通常、メモリにデータを格納する際やメモリからレジスタにデータをロードする際などは、バイト単位、あるいはワード単位で行なわれます。3bitや14bitといった中途半端なビット長でのロード/ストアはできないのが普通です。いった場合は、AND,ORといった論理演算やSHL,RORなどのシフト命令を使用して実現しなければなりません。V30のビットフィールド操作命令を使うと、1~16bitの任意ビット長のデータをメモリ空間上の任意のビット位置に格納したり、またレジスタにロードしたりすることができます。言うなれば「ビット単位のブロック転送命令」です。これはグラフィックVRAMを操作する際にはこの上なく高速かつ便利であります。PC-9801のN-88BASICのPUT@文,GET@文を考えてみてください。任意の大きさのグラフィックデータをドット単位の任意の画面位置に表示、あるいは画面から取り込みを行なうわけですから、結構複雑なビット操作が必要になることはご想像いただけることと思います。右の【図.1】は青VRAMに対してビットフィールド操作命令のINS命令を実行したときの様子です。この例ではAXレジスタの下位5bitを0A8024H番地の下位2bitから0A8025H番地の上位3bitにかけて転送します。;5ビット転送となります。WRITE.このようになってしまいます。ここでは2バイトにまたがる例でしたが、これが3バイトにまたがる場合だとこんなもんでは済みません。INS命令を使用すれば第2オペランド(アンダーライン部分)を例えば14にするだけで済みますが、8086コードだとさらに複雑になってしまいます。速度は言うに及ばず、平均でも3~5倍高速化されます。余談ですが、PC-8801VAのCPUであるμPD9002はV30同様、NECオリジナルの8086コード上位コンパチの機能を持っていますが、V30ほどの機能は有していません。言い方を変えれば、V30からいくつかの機能を削減し、8080エミュレート機能をZ80エミュレート機能に変えたCPUであるということができます。どのような機能が削減されたかというと、このビットフィールド命令やBCDストリング演算命令なのです。●STOS命令の高速性STOS命令も知らないと損をする命令のひとつでしょう。13(6)Clockこれくらい速くなります(【表.4】)。例によってクロック計算は初回と最終回を除くループ内部の合計、括弧内はV30での値です。この場合、メモリを32Kバイト0でクリアするルーチンになります。ご覧のようにSTOS命令をREPプリフィクスと併用して使用すると8086で2.6倍、V30で4倍も高速化します。STOS命令はメモリを一定のデータで埋める用途以外にはあまり使い道はないように定説化されていますが、数列や演算テーブルの作成など応用はまだまだあると思われます(リピートプリフィクスは使用できませんけど)。V30の場合はそれによってかなり高速化が図れます。(※これもメモリアクセスに際してウエイトがかからないという前提で計算したものです。●ビット操作命令Z80のプログラミングをされたことのある方はビット操作命令のありがたみがおわかりになると思います。任意1ビットのセット,クリア,テストが1ラインで実現できるのですから。8080でこれをやろうとすると大変。シフトやAND,ORの連発になります。実は8086も8080と同様で、任意のビットを操作するには論理演算の連発にならざるを得ません。ところがV30にはZ80以上に完備したビット操作命令が用意されているのです。セット,クリア,テストの他に反転(NOT)が追加され、アドレッシングも豊富になりました。8086の未定義コード“0FH”で命令を拡張しています。ビット操作命令はBASICのPSET文を実現するのに便利そうですが、他にもテキストのアトリビュートをいじったり、不揮発性メモリスイッチの設定を変えたり、また効率の良いデータ形式に、と応用は限りないと思います。実行速度も申し分なく高速です(レジスタの場合3~5クロック)。●ブロックPUSH/POPV30特有命令の中でも光っているもののひとつです。1ラインで8ライン分のPUSH/POPを行なうのですから嬉しさいっぱい夢いっぱいです。8本の汎用レジスタをもれなく一気にスタックに放り込んだり戻したりしてくれます。問題の速度ですが、【表.5】を見てみましょう。PUSHもPOPも8回行なえば64クロックを消費しますから、PUSH_Rで1.8倍、POP_Rでも1.5倍は高速になります。これだけ速度に余裕があると、なにも全レジスタをPUSH/POPする場合のみに使用する必要もありません。あるサブルーチンで5本の汎用レジスタをPUSHしてPOPする必要があるとき、合計10回のPUSH/POP=80クロックのクロック消費があるわけです。PUSH_R+POP_R=78クロックと、16回のPUSH/POPがあるにもかかわらずより高速です。5本以上のレジスタの退避復元が必要なときはブロックPUSH/POP命令を使用した方が、高速かつ省メモリということです。8Clock(AXに限らず他のレジスタも同様)【表.5】(※これもメモリアクセスに際してウエイトがかからないという前提で計算したものです。)最終話:特に脈絡ありません先日、NECからV30の改良型と呼ぶべきV33が発表になりました。16MHzで2.8MIPS、10MHzのV30に比べ4倍の処理能力といいますからなかなかのものです。物理アドレス空間も16Mバイトになり、内部回路も完全ワイヤードロジックになりました。これによってインテルとの訴訟問題も解決するとのことです。今秋発表のNECのPC-9801シリーズの新型にもおそらくV33が載ってくることでしょう。(「心から......」ではないけど)※この原稿は1990年頃に書かれたものです。