W65C816

1975年にMCS6502を発表したMOS Technology社は、1976年には主要な顧客のひとつであるCommodore社に吸収され、じきに半導体事業から撤退してしまいます。その後1977年にMCS6502のデザイナは独立してWestern Design Center社、略してWDCを設立し、MCS6502のCMOSプロセス版の開発や、MCS6502を大幅に強化したプロセッサの開発に乗り出します。WDCというとWestern Digital Coop.の略語としても使われることもありますが、まったく別の会社で、ここではWestern Design Centerの略として扱います。

VL65C816
VLSI社の製造で、プラスチック40 pin DIPのVL65C816-04。

W65C816はMCS6502のエミュレーションモードを持ち、ソフトウェア的に高速のMCS6502とみなすこともできます。ハードウェア的にもピン配置もMCS6502に似ていて、わずかな設計変更でMCS6502の代わりに使用することができます。完全互換ではないので、回路変更は必須ですが。変更され追加された信号を利用して、外部キャッシュメモリや仮想記憶機構を追加したり、割り込みベクタの拡張を行うことも可能となっています。
ネーティブモードでは、16 MByteのメモリ空間を持ち、16 bit演算を行うことも可能なプロセッサとなります。アドレッシングモードも初期の8 bitマイクロプロセッサとしては特筆に値するほど豊富なMCS6502から強化されて、13種から24種へ増えています。高速で少し便利なMCS6502としてもプログラミングできますし、新しい16 bitマイクロプロセッサとしてプログラム開発にあたることも可能です。ただ、8 bitデータ型と16 bitデータ型を交互に扱うような場合、ちょうどMCS6502でバイナリ演算とBCD演算をDフラグで切り替えて実行するように、オペコードの方ではなくフラグレジスタの特定のビットで演算データ幅を切り替えるため、煩雑なコードになりがちであるなどの欠点もあります。

W65C816のレジスタセットはこのようになっています。
W65C816 register set
この図の中で、網がかけられているAcc A, XL (X), YL (Y), PC, SL (SP), PSRがMCS6502と共通のレジスタです。ただし詳細は後述しますがPSRについては*の入っている2 bitに変更が加えられています。
アキュムレータはAcc Bが新設され、Acc Aと結合して通常はAcc Cという16 bitアキュムレータとして使用されます。Acc Bは単独でアキュムレータとして使用されることはなく、8 bitモードではAcc AがアキュムレータとなりAcc Bは一種のテンポラリレジスタとして使われるだけです。
MCS6502のIndex XとIndex Yは8 bit幅でしたが、それが16 bitに拡張されています。一応、下位8 bitにはL、上位8 bitにはHを付けて、XH, XL, YH, YLという名前で呼ばれます。必要ならアドレス修飾に下位8 bitのみを使用することもできます。ただしW65C816のアドレスは24 bit幅ですから、16 bitのインデックスレジスタでは8 bit不足します。そのため、Data Bank Register (DBR)も新設され、24 bitでアドレス付けされた空間をポイントできるようになっています。DBRはインデックスレジスタを用いたアドレッシング以外にも、24 bit空間をアドレスするためにさまざまなアドレッシングモードで使用されます。
プログラムカウンタ(PC)は16 bit幅で、これはMCS6502から変更はありませんが、やはり8 bit分不足するアドレスを供給するProgram Bank Register (PBR)が新設され、PCとペアで実行する命令をポイントできるようにしてあります。ただし、PCと一体というわけでなく、たとえPCが0FFFFHからインクリメントされてもPBRは変化しません。あくまでPCで不足する8 bit分を供給するだけのレジスタです。
スタックポインタも16 bitに拡張されました。ただし、スタックポインタにはバンクレジスタの類は存在せず、アドレス上位8 bitは00Hに固定されます。ですから、スタックは000000Hから00FFFFHの範囲に配置されることになります。MCS6502の0100Hから01FFHまでよりは広がりましたが、あいかわらず制限が残りました。まぁ、高級言語を用いてサブルーチンの引数をスタック経由で受け渡す場合、256 Byteのスタック空間では制約がきつすぎますが、64 KByteあればそれほど不便ではないでしょう。マルチタスクモニタで複数のスタック領域を管理するような場合、メモリ管理ユニットを外付けしないとつらいこともあるかもしれませんが。
MCS6502ではゼロページ領域のメモリが特別な役割を持ちました。そのゼロページ領域を拡張するためにダイレクトレジスタD(上下バイト別々に表記する場合はDHとDL)が新設されました。
フラグ類をまとめたProgram Status Register (PSR)は、ほとんどMCS6502と共通ですが、ビット5とビット4がエミュレーションモードとネーティブモードで異なるフラグとして扱われます。エミュレーションモードではビット5が常に1、ビット4がBフラグですが、ネーティブモードではビット5がMemory Select bit (M)で8 bit演算を行うか16 bit演算を行うかの切り替えフラグ、ビット4がIndex Register Select bit (X)でインデクスレジスタ幅の切り替えフラグとなります。Mフラグが1なら演算は8 bit幅でAcc Aだけが使われます。Mフラグが0なら演算は16 bit幅でAcc Cが使われます。Xフラグが1ならインデックスレジスタは8 bit幅でXL, YLのみが使用され、1なら16 bit幅でX, Y全体が使用されます。
さらに動作モードを指定するEフラグが新設されています。Eが1ならエミュレーションモードで、0ならネーティブモードとなります。エミュレーションモードではSHが01Hに固定され、アキュムレータやインデックスレジスタも下位8 bitしか使えなくなるなどの変化があり、他の新設されたレジスタを適切に設定すればMCS6502のコードをそのまま実行できるようになります。

W65C816のアドレッシングモードを考える前に、アドレスの組み立てについて説明します。W65C816は24 bitで表されるアドレス空間を持ちます。なお、I/O専用のアドレス空間は存在せず、メモリマップドI/OとしてI/Oポートを実装します。
W65C816はMCS6502と互換性を持つことからわかるように、256 Byte境界で区切られた単位が特別な意味を持つことがあります。これをページと呼びます。MCS6502では、多様な利用法のあるゼロページと、スタック領域に使用される第1ページが特別な意味を持ちました。また、相対アドレッシングやインデックスアドレッシングでもページ境界をまたぐアドレス計算を行うと実行時間に1クロック加算されるというような性質もありました。W65C816でもページが意味を持ちます。
さらにW65C816では64 KByte境界の単位が特別な意味を持ちます。それをバンクと呼びます。そもそも、どのバンクが操作の対象になるのかを指定するから、DBRとかPBRにバンクレジスタという名前がつけられていたのですね。256個のバンクの中でも0バンクは特別な意味を持ちます。まず、W65C816のスタックは0バンクにしか配置できません。また、0バンクしか対象とならないアドレッシングモードも存在します。ただ、アドレスが隣接したバンクでもソフトウェア的には分離して独立した空間となっていて、バンクをまたいだデータ構造を実現するのは容易ではありませんし、バンク内の分岐は簡単なのですがバンク間での分岐は専用命令が必要で、ある規模以上のプログラムでは常にバンクを意識してプログラミングする必要があります。
W65C816ではバンクなしのアドレスや一般の16 bitデータをメモリ上に配置するとき、MCS6502と同様に下位バイトがメモリアドレスの小さい方に、その次に上位バイトが格納されます。また、24 bitのバンク付きアドレス定数などはアドレスの小さい方から、下位バイト、中位バイト、バンクの順に格納されます。
以下のアドレッシングモードの説明では、16 bit幅のバンク内アドレス定数をaddr16, バンクの値まで含めたアドレス定数をaddr24、インデックス修飾などに使用する8 bitオフセット値をoffset8, 16 bitオフセット値をoffset16のように記します。また、別に与えられるバンク値bankとaddr16を結合する場合にはbank:addr16のように書きます。あるアドレスaddr24で示されるメモリの内容は[addr24]という形で表記し、特にその内容が1, 2, 3 Byteの値であることを明示する場合にはそれぞれ[addr24]8, [addr24]16, [addr24]24のように表現します。()は単に演算の優先順位を示すのに使用します。

アドレッシングモードを順に説明していきます。
イミディエートアドレッシングは定数オペランドを直接記述するモードです。定数値をレジスタにロードしたり、アキュムレータと定数値との演算に使用されます。対象レジスタが8 bitモードか16 bitモードかによってオペランドが1 Byteか2 Byteかが決まります。
アブソリュートアドレッシングはオペランドに16 bitアドレスを持ち、そのアドレスとを連結したDBR:addr16が操作対象の実効アドレスとなります。
さらにバンクアドレスまで直接オペランドに記述するアブソリュートロングアドレッシングもあります。オペランドに3 Byte分のaddr24を持ち、そのaddr24で指されたメモリが操作対象となります。
MCS6502のゼロページアドレッシングに相当するダイレクトアドレッシングは8 bitのオフセット値をオペランドに持ち、ダイレクトレジスタDRとオフセット値を加算してバンクを0に固定した00:(DR+offset8)が操作対象の実効アドレスとなります。DRの内容が0ならMCS6502のゼロページアドレッシングと同じものになります。なお、DLが0以外の場合、つまりDRがページ境界を指さずにDRとoffset8の実質的な加算が必要な場合には1クロックだけ実行時間が増えます。
アキュムレータアドレッシングはオペランドがアキュムレータに固定されているもので、MCS6502と共通です。
インプライドアドレッシングは命令によって操作対象が決まりきっているものを示し、その点はMCS6502と共通ですが、スタックポインタの指す場所への操作についてはW65C816ではスタックアドレッシングとして細かく区分しています。たとえばPHA命令はMCS6502ではインプライドアドレッシングに分類していましたが、W65C816ではスタックアドレッシングとなります。
ダイレクトインデックスXはMCS6502のゼロページXに相当するアドレッシングモードで、ダイレクトアドレッシングと同じようにダイレクトレジスタと8 bitのオフセット値を加算したものに、さらにXの内容を加算して実効アドレスを求めます。バンクは0に固定されます。数式で表現すれば、00:(DR+offset8+X)が対象アドレスとなります。なお、PSWのXフラグが1ならXLだけがアドレス計算に使用され、0なら16 bit幅のX全体が使用されます。後者の場合、アドレス計算にさらに1クロックサイクル必要となります。
ダイレクトインデックスYは上記ダイレクトインデックスXのインデックスレジスタをYに置き換えたものです。
アブソリュートインデックスXは対象アドレス範囲を24 bitアドレス空間に拡大したダイレクトインデックスXアドレッシングで、MCS6502のアブソリュートXアドレッシングに相当します。16 bitアドレスをオペランドに持ち、DBR:addr16とXレジスタを加算したDBR:addr16+Xが対象アドレスとなります。
やはりXの代わりにYを用いたアブソリュートインデックスYアドレッシングも存在します。DBR:addr16+Yが対象アドレスです。
さらにアブソリュートインデックスXの変種として、バンク指定にDBRを使わずに直接24 bitアドレスを指定するアブソリュートロングインデックスXがあります。addr24+Xが対象アドレスです。こちらにはYを用いるモードは存在しません。
インデックスレジスタの代わりにスタックポインタを利用するアドレッシングモードも新設されています。高級言語を意識したモードですね。スタック相対アドレッシングがそれで、8 bitオフセット値を持ち、スタックポインタとオフセット値を加算して対象アドレスを求めます。スタック領域は0バンクにしか配置できませんから、バンクは0固定です。ですから、00:(SP+offset8)が対象アドレスです。
間接アドレッシング系も強化されています。ゼロページX間接の強化版としてダイレクトX間接アドレッシングがあります。8 bitオフセットとDRとXの和で求められるダイレクトインデックスXを先に行い、そこから16 bitアドレスを読み出してDBRと組み合わせて実効アドレスを求めます。すなわち、DBR:[00:(DR+offset8+X)]が対象アドレスとなるわけです。
ゼロページ間接Yの強化版がダイレクト間接Yアドレッシングで、8 bitオフセット値とDRを加算してダイレクトアドレッシングと同じ操作をしてから16 bitアドレスを読み出し、そのアドレスとDBRを組み合わせたものにYレジスタで修飾を行ったものが対象アドレスになるというアドレッシングモードです。DBR:[00:(DR+offset8)]+Yですね。
ダイレクト間接Yアドレッシングには、さらにダイレクト間接ロングYアドレッシングモードというバリエーションがあります。これはダイレクトアドレッシングで読み出すのが16 bitアドレスでなくバンク込みの24 bitアドレスとなって、DBRが使用されないのが特徴です。[00:(DR+offset8)]24+Yが実効アドレスとなります。このロング形式の間接アドレッシングにはXを用いるモードはありません。
さらにスタックポインタと組み合わせたスタック相対間接Yアドレッシングモードもあります。8 bitオフセットを持ち、スタックポインタとオフセットを加算して第0バンクから16 bitアドレスを読み出し、それとDBRを組み合わせて24 bitベースアドレスを構成したうえで、さらにYの内容を加算したものが演算対象アドレスとなります。つまり実効アドレスはDBR:[00:(SP+offset8)]+Yとなります。
ゼロページに格納されたポインタをインデックス修飾を使わずに直接参照するダイレクト間接アドレッシングも存在します。8 bitオフセット値とDRを組み合わせたメモリアドレスから16 bitアドレスを読み出して、そのアドレスをDBRと結合したものが実効アドレスとなります。DBR:[00:(DR+offset8)]です。
また、ダイレクト間接ロングアドレッシングとして、8 bitオフセット値とDRで指されたメモリから24 bitアドレスを読み出して、それを実効アドレスとするモードも存在します。[00:(DR+offset8)]ですね。
分岐命令系に特有のアドレッシングモードもありますね。その代表はプログラムカウンタ相対アドレッシングで、8 bitオフセットをオペランドに持ちます。ただし、このオフセット値はこれまでとは異なり、符号付きと解釈されて、それがプログラムカウンタに加算されます。PBRは影響を受けません。実効アドレスはPBR:(PC+offset8)となります。
さらに、そのロング形式であるプログラムカウンタ相対ロングアドレッシングも特別な命令で使用できます。オフセット値が符号付き16 bit値となったものです。PBR:(PC+offset16)が実効アドレスです。このモードでもPBRは変化しませんから、バンクをまたいだ分岐にはアブソリュートロングか次に示すモードなどの別のアドレッシングモードを利用しなくてはなりません。
分岐命令の中には、アブソリュート間接アドレッシングを使用するものもあります。16 bitアドレスをオペランドに持ち、そのアドレスで指定されるバンク0領域から、JMP命令の場合には16 bitアドレスを、JML命令の場合には24 bitアドレスを取り出して、そこへ分岐します。JMP命令の実行アドレスはPBR:[00:addr16]で、JML命令では[00:addr16]24となります。
アブソリュートインデックス間接アドレッシングも分岐命令特有のアドレッシングモードで、16 bitオフセットをオペランドに取り、それをXに加算してバンク0領域から16 bitアドレスを取り出して新プログラムカウンタ値とします。PBRは変化しません。ですからPBR:[00:(X+offset16)]となります。
最後に、ブロック転送命令特有のXYCアドレッシングモードがあります。このモードでは命令の第2バイトにディスティネーションバンク値、第3バイトにソースバンク値srcbnkを持ち、これとX, YのインデックスレジスタとCアキュムレータを組み合わせてブロック転送を行います。ディスティネーションバンク値はDBRに転送され、DBR:Yがディスティネーションアドレスを指します。また、srcbnk:Xがソースアドレスを指し、Cアキュムレータが転送バイト数を保持します。srcbnk:XからDBR:Yへとデータのコピーを行った後、XとYを変化させ、Cが0より大きければCをデクリメントしてコピーを繰り返します。これでバンクをまたいでデータのコピーを行います。
ずいぶんと煩雑な感じがしますが、以上がW65C816のアドレッシングモードです。これらを表にまとめると、次のようになります。

アドレッシングモード 略号 実効アドレス
イミディエート # データそのもの
アブソリュート a DBR:addr16
アブソリュートロング al addr24
ダイレクト d 00:(DR+offset8)
アキュムレータ A Accumulator
インプライド i 命令固有
スタック s 00:SP
ダイレクトインデックスX d,x 00:(DR+offset8+X)
ダイレクトインデックスY d,y 00:(DR+offset8+Y)
アブソリュートインデックスX a,x DBR:addr16+X
アブソリュートインデックスY a,y DBR:addr16+Y
アブソリュートロングインデックスX al,x addr24+X
スタック相対 d,s 00:(SP+offset8)
ダイレクトX間接 (d,x) DBR:[00:(DR+offset8+X)]
ダイレクト間接Y (d),y DBR:[00:(DR+offset8)]+Y
ダイレクト間接ロングY [d],y [00:(DR+offset8)]24+Y
スタック相対間接Y (d,s),y DBR:[00:(SP+offset8)]+Y
ダイレクト間接 (d) DBR:[00:(DR+offset8)]
ダイレクト間接ロング [d] [00:(DR+offset8)]
プログラムカウンタ相対 r PBR:(PC+offset8)
プログラムカウンタ相対ロング rl PBR:(PC+offset16)
アブソリュート間接 (a) PBR:[00:addr16]または[00:addr16]24
アブソリュートインデックス間接 (a,x) PBR:[00:(X+offset16)]
XYC xyc ブロック転送固有

この表で、略号は命令表を示すところで使用する省略記号です。普通の間接参照を()で、ロング形式の間接参照を[]で表している点は、実効アドレスの記法と異なるので注意して下さい。

さて、命令についてですが、W65C816の命令のオペコードは1 Byteのみで、それに0から最大3 Byteのオペランドのコードが付属して構成されます。したがって、有効なオペコードは最大256種類ということになり、W65C816ではそのすべてに実際の命令が割り当てられています。ただし、1命令だけ、将来のための予約が目的の命令が存在しますが。未定義のオペコードがかなり残っていたMCS6502とはかなり異なります。WDCはMCS6502のCMOS版であるW65C02の開発時に、命令の増強を行いました。W65C816では、そのW65C02に大幅な命令強化を行いましたが、一応はMCS6502とのバイナリ互換性を保ちます。W65C02とは、ビット操作関係の命令であるBBR, BBS, RMB, SMB命令を除き、互換性を保っています。これらの命令コードは別の新設命令が割り当てられました。
もちろん、内部レジスタの16 bit化やアドレス空間の増大に伴い、レジスタや特にフラグの状態に依存してMCS6502のコードを正しく実行できない場合も多々あります。それでも適切な状態設定の上でMCS6502のコードを従来通りに実行できることを、それなりに期待できる体系にはなっています。もっとも、動作モードを決めるフラグを正しく設定して実行する必要があることは、W65C816ネーティブのコードに関しても大事なことです。W65C816ではMフラグやXフラグの状態によって、たとえばイミディエートアドレッシングモードの命令のバイト数が変化してしまいます。ですから、プログラマが想定していたフラグ状態と異なる条件でコードが解釈されると、正しくオペコードをフェッチすることすら保証できず、簡単に暴走してしまいます。フラグによって規定される動作モードの多いプロセッサですので、プログラムの読み書きには注意が必要です。

まずはアキュムレータメモリ間の演算命令で多様なアドレッシングモードの用意されているものから見ていきましょう。
このグループには、加減算や論理演算、ロード、ストア命令など8命令が含まれます。

MNE.  #      a      al     d     d,x    a,x    a,y    al,x   d,s   (d,x)  (d),y  [d],y (d,s),y  (d)    [d]   NVZC 動作
ADC 69 2 2 6D 3 4 6F*4 5 65 2 3 75 2 4 7D 3 4 79 3 4 7F*4 5 63*2 4 61 2 6 71 2 5 77*2 6 73*2 7 72+2 5 67*2 6 **** A + M + C -> A
AND 29 2 2 2D 3 4 2F*4 5 25 2 3 35 2 4 3D 3 4 39 3 4 3F*4 5 23*2 4 21 2 6 31 2 5 37*2 6 33*2 7 32+2 5 27*2 6 * *  A & M -> A
CMP C9 2 2 CD 3 4 CF*4 5 C5 2 3 D5 2 4 DD 3 4 D9 3 4 DF*4 5 C3*2 4 C1 2 6 D1 2 5 D7*2 6 D3*2 7 D2+2 5 C7*2 6 * ** A - M
EOR 49 2 2 4D 3 4 4F*4 5 45 2 3 55 2 4 5D 3 4 59 3 4 5F*4 5 43*2 4 41 2 6 51 2 5 57*2 6 53*2 7 52+2 5 47*2 6 * *  A eor M -> A
LDA A9 2 2 AD 3 4 AF*4 5 A5 2 3 B5 2 4 BD 3 4 B9 3 4 BF*4 5 A3*2 4 A1 2 6 B1 2 5 B7*2 6 B3*2 7 B2+2 5 A7*2 6 * *  M -> A
ORA 09 2 2 0D 3 4 0F*4 5 05 2 3 15 2 4 1D 3 4 19 3 4 1F*4 5 03*2 4 01 2 6 11 2 5 17*2 6 13*2 7 12+2 5 07*2 6 * *  A | M -> A
SBC E9 2 2 ED 3 4 EF*4 5 E5 2 3 F5 2 4 FD 3 4 F9 3 4 FF*4 5 E3*2 4 E1 2 6 F1 2 5 F7*2 6 F3*2 7 F2+2 5 E7*2 6 **** A - M - not C -> A
STA        8D 3 4 8F*4 5 85 2 3 95 2 4 9D 3 5 99 3 5 9F*4 5 83*2 4 81 2 6 91 2 6 97*2 6 93*2 7 92+2 5 87*2 6      A -> M

表のもっとも左側にあるのが命令ニーモニックです。W65C816のニーモニックはすべて3文字で構成されます。その後、このグループに許された15種類のアドレッシングモードに対応して、2桁の16進数と1桁の数字がふたつ組み合わさったものが並びます。このうちの左の2桁の16進数がオペコードです。中央の数値は、その命令の最小バイト数です。最小とあるところに注意すべきで、動作モードによってイミディエートアドレッシングの命令のサイズが変化することがありますが、その最小の値が表示されているわけです。この表の場合、Mフラグが1なら表の通りですが、0ならばイミディエートアドレッシングの命令はすべて3 Byte長となります。右の数値は、その命令の最短実行クロック数で、これもやはり最短です。動作モードやオペランドの値などに応じて、命令実行クロック数は変化します。そのうちのもっとも少ない値が書き込まれています。たとえばこの表に含まれるダイレクト間接Yアドレッシングモード(d),yの命令のクロック数は5となっていますが、MフラグやD, Yレジスタの内容とオフセット値の関係によっては、5クロックから8クロックまで変化する可能性があります。そんなわけで、特にクロック数については目安程度にしてください。なお、オペコードと最小バイト数の間に*が書き込まれているものは、W65C816で新設された命令です。+が書き込まれているものはW65C02の段階で新設された命令です。NVZCとあるのはフラグ変化です。空白で変化なし、*で命令の実行結果に応じて変化することを意味します。なお、これ以外のフラグは、このグループの命令では影響を受けません。動作欄は、簡単に命令動作を記したものです。Aはアキュムレータで、Mフラグが1の8 bitモードならAですがMが0の16 bitモードならCレジスタを意味するものです。Mはオペランドに示された実行アドレスのメモリの内容で、CはCフラグを意味します。
このグループに含まれる命令は、基本的にはMCS6502と同じですが、使用可能なアドレッシングモードが大幅に増強されていることと、Mフラグの状態によっては16 bit演算が普通に行われることが異なります。メモリへのデータの読み書きを行うLDA命令やSTA命令も上の表に含まれることからわかるように、これらの命令もMフラグの状態によって影響を受けます。アキュムレータで16 bit演算を行って、そのうちの下位バイトだけメモリに格納したいとか、逆にメモリから1 Byteのデータを読み出して、それに16 bit演算を施したいというような場合、いちいちMフラグを操作する命令を間に挿入しなくてはなりません。ほかの16 bit演算が普通にできるプロセッサとは、少しばかり異なるスタイルが要求されます。

同じ演算関係でも、アドレッシングモードのバリエーションが少ないものを次に示します。

MNE.  #      a      d      A     d,x    a,x   NVMXDIZC 動作
BIT 89+2 2 2C 3 4 24 2 3        34+2 4 3C+3 4 **    *  A & M
ROR        6E 3 6 66 2 5 6A 1 2 76 2 6 7E 3 7 *     ** C -> MSB, shift right, LSB -> C
ASL        0E 3 6 06 2 5 0A 1 2 16 2 6 1E 3 7 *     ** C <- MSB, shift left, LSB <- 0
LSR        4E 3 6 46 2 5 4A 1 2 56 2 6 5E 3 7 0     ** 0 -> MSB, shift right, LSB -> C
ROL        2E 3 6 26 2 5 2A 1 2 36 2 6 3E 3 7 *     ** C <- MSB, shift left, LSB <- C
INC        EE 3 6 E6 2 5 1A+1 2 F6 2 6 FE 3 7 *     *  M++
DEC        CE 3 6 C6 2 5 3A+1 2 D6 2 6 DE 3 7 *     *  M--
REP C2*2 3                                    ******** not M & P -> P
SEP E2*2 3                                    ******** M | P -> P
TRB        1C+3 6 14+2 5                            *  not A & M -> M
TSB        0C+3 6 04+2 5                            *  A | M -> M

このグループでは、一部W65C02でアドレッシングモードが追加されたものもありますが、DEC命令まではMCS6502と同じことを行います。
REP命令はReset Processor Status Bitの略で、イミディエート値で1になっているビットの位置と同じ位置にあるPSR内のフラグをリセットする命令です。イミディエート値で0となっているビットの位置に対応するフラグは影響を受けません。SEP命令はSet Processor Status Bitの略で、逆にイミディエート値で1になっているビットに対応するフラグをセットする命令です。
TRB命令はTest and Reset Bitの略で、アキュムレータのビットで1になっている場所に対応するメモリ内のビットをリセットするものです。TSB命令は、セットする方の命令です。共に、操作対象ビットが操作前にすべて0であったかどうかに応じてZフラグを変化させます。
REP, SEP命令については、常に2 Byte命令で3クロックで実行されます。もともと1 Byte長のPSRのフラグのリセット・セットを行う命令ですから、当然ですが。
逆にいえば、この表に含まれるREP, SEP命令以外の命令は、Mフラグの値が動作に影響します。BIT命令のイミディエートモードは、16 bitモードなら3 Byte命令となります。さらに、REP, SEP命令以外の命令は、16 bitデータへの操作となります。たとえばASL命令はメモリから2 Byte読み出してシフトして書き戻します。INC命令やTRB命令も2 Byte単位の操作を行います。ループ内は16 bit演算を行いながら、ループカウンタはメモリ上の1 Byte変数で間にあわせるというようなことをしたいなら、いちいちMフラグを操作しなくてはなりません。実はMフラグを変更するもっとも効率的な命令がREP, SEP命令であるため、正直にそのようなコードを書こうとすればコードが長くなりがちです。まぁ、この例のような場合なら、インデックスレジスタの空いている方をループカウンタにするとか、メモリサイズを無視した方が効率的かもしれません。
なお、BIT命令は操作対象前のメモリデータのMSBをNフラグに、MSBより1桁小さいビットをVフラグにコピーします。つまり、Mフラグが1ならビット7とビット6がそれぞれN, Vフラグにコピーされ、Mフラグが0ならビット15とビット14がコピーされることになります。このビットコピー操作はイミディエートモードに限っては行われず、変化するフラグはZフラグのみとなります。

インデックスレジスタ関係の命令で、複数のアドレッシングモードを持つものを次にまとめてみました。

MNE.  #      a      d     d,x    d,y    a,x    a,y   NVZC 動作
LDX A2 2 2 AE 3 4 A6 2 3        B6 2 4        BE 3 4 * *  M -> X
LDY A0 2 2 AC 3 4 A4 2 3 B4 2 4        BC 3 4        * *  M -> Y
CPX E0 2 2 EC 3 4 E4 2 3                             * ** X - M
CPY C0 2 2 CC 3 4 C4 2 3                             * ** Y - M
STX        8E 3 4 86 2 3        96 2 4                    X -> M
STY        8C 3 4 84 2 3 94 2 4                           Y -> M
STZ        9C+3 4 64+2 3 74+2 4        9E+3 4             0 -> M

このグループも、MCS6502のCMOS化段階で追加されたSTZ命令を除き、MCS6502と変わりません。
STZ命令はインデックスレジスタ操作ではありませんが、アドレッシングモードが似ているので、こちらに入れました。この命令だけはMフラグの影響を受け、Mフラグが1なら1 Byteの0がメモリに書き込まれ、Mフラグが0なら2 Byteが0クリアされます。アキュムレータもフラグも変化しません。
それ以外はインデックスレジスタX, Yのロード、比較、ストアだけで、これらの命令はXフラグの影響を受けます。Xフラグが1なら8 bitインデックスモードで、MCS6502と同じ動作をします。Xフラグが0なら16 bitインデックスモードで、イミディエートモードの命令長が1増えて3 Byteとなり、全アドレッシングモードでメモリとの間でやり取りされるデータが2 Byteとなり、その分だけ命令実行クロック数も増加します。

インヘレントモードの命令を以下の表に集めました。まず、インデックスレジスタ関係のものを並べ、次にフラグ操作関係、最後にそれ以外のものをまとめました。

MNE.  i    NVMXDIZC 動作
INX E8 1 2 *     *  X++
INY C8 1 2 *     *  Y++
DEX CA 1 2 *     *  X--
DEY 88 1 2 *     *  Y--
TAX AA 1 2 *     *  A -> X
TAY A8 1 2 *     *  A -> Y
TSX BA 1 2 *     *  S -> X
TXA 8A 1 2 *     *  X -> A
TXS 9A 1 2 *     *  X -> S
TXY 9B*1 2 *     *  X -> Y
TYA 98 1 2 *     *  Y -> A
TYX BB*1 2 *     *  Y -> X
CLC 18 1 2        0 0 -> Cf
CLD D8 1 2     0    0 -> Df
CLI 58 1 2      0   0 -> If
CLV B8 1 2  0       0 -> Vf
SEC 38 1 2        1 1 -> Cf
SED F8 1 2     1    1 -> Df
SEI 78 1 2      1   1 -> If
TCD 5B*1 2 *     *  C -> D
TCS 1B*1 2          C -> S
TDC 7B*1 2 *     *  D -> C
TSC 3B*1 2 *     *  S -> C
XBA EB*1 3 *     *  B <-> A
XCE FB*1 2        E Cf <-> Ef
STP D8+1 3          stop processor
WAI CB+1 3          wait for interrupt
WDM 42*2 2          no operation (reserved)
NOP EA 1 2          no operation

数は多いですが、さらにグループに分類すればそれほどややこしくはありません。
第一のグループはインデックスレジスタのインクリメントとデクリメントで、INX命令からDEY命令までの4命令です。これはMcS6502と共通です。もちろん、Xフラグが0なら16 bit幅のレジスタ全体が操作対象となります。Xフラグが1なら、インデックスレジスタの上位8 bitは常に強制的に0となっていますから、従来通りの8 bit演算です。
TAX命令からTYX命令までの8命令が、インデックスレジスタ関係のレジスタ間転送命令です。TXY命令とTYX命令はW65C816で新設された命令です。常に16 bit全体が転送されますが、ここでもXフラグが1ならインデックスレジスタの上位バイトは0にされる規則が働きます。
CLC命令からSEI命令までの7命令はフラグのセットとリセットを行う命令で、MCS6502から変更はありません。この表に関してだけ、動作のところでCフラグと16 bitアキュムレータのCレジスタとかDフラグとダイレクトレジスタDが混同しやすいので、フラグには小文字のfを使用しています。
TCD命令からTSC命令までの4命令はW65C816で新設されたレジスタ間転送命令で、インデックスレジスタ以外のものです。16 bitアキュムレータであるCと、ダイレクトレジスタおよびスタックポインタとのやり取りが可能となっています。転送命令ではPBRはもちろんDBRも操作できないことに注意して下さい。PBRは分岐命令以外で変化させれば暴走の危険がありますが、今まで出てきたどの命令表にもDBRを変更する命令はありません。DBRは、この次の命令表に含まれる命令でしか変更できません。
XBA命令とXCE命令は交換命令です。XBA命令はAレジスタとBレジスタの内容を入れ替えるもので、16 bitモードで使用すればアキュムレータCの上下バイトの交換命令とみなせます。Mフラグが1の8 bitモードでは、原則としてBレジスタは利用できませんが、唯一この命令を使用してデータの一時待避に用いることができます。XCE命令はCフラグとEフラグの内容を入れ替える命令で、ネーティブモードとエミュレーションモードの切り替えに使用します。CLC, XCEの順で命令を実行すればネーティブモードへの切り替え、SEC, XCEの順に実行すればエミュレーションモードへの切り替えとなります。
STP命令はプロセッサを停止する命令で、省電力のための命令です。内部クロックを停止して、リセットされるまで停止し続けます。WAI命令もプロセッサ動作を停止して低電力モードへ移行する命令ですが、割り込みを受け付けられる状態を保ちます。特にIフラグをセットしIRQ割り込みを禁止している状態でWAI命令実行後IRQ割り込み要求が入力されると、それを無視せずに、しかし通常の割り込み処理とも異なり、単に停止状態を解除してWAI命令の次の命令から実行を再開するようになっています。この機能をうまく使えば、入出力データ転送の応答速度を上げ、消費電力を低減させられることが可能になると思われます。
WDM命令は将来の拡張用の未使用命令で、W65C816では2 ByteのNOP命令として扱われます。WDMにはどのような意味があるのかといえば、設計者のWilliam D. Mensch, Jr.の頭文字のようで。NOP命令は1 Byte長のなにもしない命令です。

次はスタックアドレッシング関係の命令です。

MNE.  s    NVMXDIZC 動作
PHA 48 1 3          A -> (S--)
PHB 8B*1 3          DBR -> (S--)
PHD 0B*1 4          D -> (S), S -= 2
PHK 4B*1 3          PBR -> (S--)
PHP 08 1 3          P -> (S--)
PHX DA+1 3          X -> (S--)
PHY 5A+1 3          Y -> (S--)
PLA 68 1 4 *     *  (++S) -> A
PLB AB*1 4 *     *  (++S) -> DBR
PLD 2B*1 5 *     *  S += 2, (S) -> D
PLP 28 1 4 ******** (++S) -> P
PLX FA+1 4 *     *  (++S) -> X
PLY 7A+1 4 *     *  (++S) -> Y
PEA F4*3 5          (PC++) -> (S--), (PC++) -> (S--)
PEI D4*2 6          (d) -> (S--), (d+1) -> (S--)
PER 62*3 6          PC+rl -> (S), S -= 2
BLK 00 2 7     01   break
COP 02*2 7     01   coprocessor instruction
RTI 40 1 6 ******** return from interrupt
RTL 6B*1 6          return from subroutine long
RTS 60 1 6          return from subroutine

PHA命令からPLY命令までが内部レジスタのスタックへの待避と復帰命令です。アキュムレータのプッシュプルに関してはMフラグ、インデックスレジスタのプッシュプルに関してはXフラグによって、スタックとやり取りされるバイト数が変化します。
DBRを変更する命令はPLB命令しかありません。ですから、DBRを特定の値にセットするには8 bitデータをプッシュして、PLB命令でロードする必要があります。Mフラグが0のときにPHA命令を実行すると2 Byteの値をプッシュしてしまうので、うっかりLDA #n, PHA, PLBなどというコードを書くとスタックが合わなくなります。いちいちMフラグを操作するのも可能ではありますが、いっそのこと後述のPEA命令で上下バイト等しいデータをプッシュしてしまい、2回PLB命令を実行するPEA nn, PLB, PLBというコードの方が短く速くなり、他のレジスタが書き変わることもありません。8 bitモードのレジスタが空いているなら、先に示したコードの方が効率的なのですが。ロングジャンプした先で、コードの含まれるバンクと同じバンクを作業用のメモリとして使用するのなら、つまりPBRとDBRを等しくしたいのなら、PHK, PLBという命令シーケンスを用いることができます。
PEA命令はPush Effective Absolute Addressの略で、オペランドで示されるアドレスそのものをスタックへプッシュする命令です。一応アブソリュートアドレッシングモードのオペランドなのですが、アブソリュートアドレッシングでデータをアクセスするわけではありません。見方を変えれば、イミディエートデータ2 Byteを直接スタックへプッシュする命令と解釈することもできます。MフラグやXフラグによってプッシュされるバイト数は変化せず、必ず2 Byteのデータがプッシュされます。
PEI命令はPush Effective Indirect Addressの略で、1 Byteのオペランドデータでダイレクト間接アドレッシングモードのアドレス計算を行い、そのアドレス2 Byteをスタックへプッシュします。この命令も見方を変えれば、指定したダイレクトアドレスで指されたデータをスタックにプッシュする命令と考えることもできます。やはり同様にM, Xフラグの状態に関わらず必ず2 Byteの値がプッシュされます。
PER命令はPush Effective PC Relative Addressの略で、2 Byteのオペランドでプログラムカウンタ相対ロングアドレッシングモードのアドレス計算を行い、その結果の2 Byteの値をスタックへプッシュします。同様に必ず2 Byteのデータをプッシュします。
BRK命令はソフトウェア割り込みで、2 Byte命令となっています。オペランドのはずの2 Byte目は無視されます。おそらく、ソフトウェア割り込みハンドラで何をさせるか、たとえばファンクションコードなどを書き込んでおき、割り込みハンドラ側で自由に解釈できるようにしたのでしょう。ただし、デバッガでテスト中のプログラムのブレークポイント処理のためにプレー九ポイントの命令を置き換えて使用する場合など、1 Byte命令でないために注意が必要です。
COP命令は将来のためのコプロセッサ用の命令として定義されていますが、W65C816ではBRK命令と同様のソフトウェア割り込み命令として扱われます。この命令も2 Byte命令です。
残り3命令はサブルーチンや割り込みハンドラからの復帰命令ですが、RTL命令だけが新設されています。このRTL命令はロング形式のサブルーチン呼び出し命令からの復帰用に新設されたものです。プログラムカウンタだけでなくPBRまでスタックからポップして復元します。

プログラムカウンタ相対もしくはそのロングモードの分岐命令を以下に集めました。

MNE. r/rl  条件
BCC 90 2 2 C = 0
BCS B0 2 2 C = 1
BEQ F0 2 2 Z = 1
BMI 30 2 2 N = 1
BNE D0 2 2 Z = 0
BPL 10 2 2 N = 0
BRA 80+2 3 always
BRL 82*3 4 always long
BVC 50 2 2 V = 0
BVS 70 2 2 V = 1

この表の中でBRL命令だけがプログラムカウンタ相対ロングアドレッシングで、残りはすべて2 Byte命令のプログラムカウンタ相対アドレッシングモードの分岐命令です。
ほとんどはMCS6502と共通です。BRA命令はCMOS版で追加された無条件分岐命令です。必ず分岐するので、最短命令実行クロック数が3となっています。分岐命令で最短のクロック数が2というのは、条件が成立せずに分岐を行わなかった場合だけということはMCS6502の頃から変わっていません。そのためBRA命令は実効アドレスの計算時の桁上がりの有無で、3クロックか4クロックを消費するため、上の表では3クロックという表記になっています。
BRL命令は新設された命令で、2 Byteオフセットを持つ無条件分岐命令です。常に4クロック消費します。これで相当広い範囲に分岐できるようになりました。ただし、ロング形式だろうと通常のプログラムカウンタ相対だろうと、PBRには影響を与えることはできませんから、同一バンク内への分岐に限られます。バンクの壁の存在はあきらめねばなりませんが、BRL命令ならバンク内の任意の場所へプログラムカウンタ相対で分岐可能です。

以上のほかに分岐命令は4種類あります。

MNE.  a      al    (a)   (a,x)  動作
JML               DC*3 6        addr -> PC; PBR
JMP 4C 3 3 5C*4 4 6C 3 5 7C+3 5 addr -> PC (; PBR)
JSL        22*4 8               push PBR; PC, addr -> PC; PBR
JSR 20 3 6               FC*3 6 push PC, addr -> PC

サブルーチン呼び出しでない単純なジャンプ命令はJML命令とJMP命令があります。アブソリュートインデックス間接アドレッシングのJMP命令はW65C02の段階で追加されています。新設されたのはアブソリュートロング形式のJMP命令とアブソリュート間接形式のJML命令で、共に実効アドレスにPBRまで含めたバンク間ジャンプ命令です。JML命令はJump Longの略でしょうが、実質的にはJMP al命令もロング形式のジャンプに変わりありません。なぜJML命令というニーモニックを持つ命令が新設されたのか考えると、アブソリュート間接ロングアドレッシングモードというアドレッシングモードを分類せずにアブソリュート間接アドレッシングと同じ表記にしてしまったための混乱としか思えません。JML (a)という命令をJMP (a)と書くとMCS6502時代からの命令と見分けがつかなくなってしまうため、別ニーモニックを与えたということでしょう。ただ、それならJMP alを認めずにJML alという表記にした方が、JML命令はPBRまで含めたロング形のジャンプで、JMP命令はPCだけのバンク内ジャンプという分類になって、すっきりするように思います。
同じような混乱はサブルーチン呼び出しのJSL命令とJSR命令にも見られます。Jump Subroutine Longの略であるJSL命令は、対応するアドレッシングモードがJSR命令に存在しないので、JMP命令と同じように扱うならJSLというニーモニックを使用する必要はありません。ただ、プログラマの方からの実用上は、JSL命令で呼び出されるサブルーチンは専用のRTL命令で戻るものに限られますから、JSLとRTLの対応がきちんととれているか、アドレッシングモードまで確認せずに命令ニーモニックを見ただけで確認できる方が便利ではあります。
なお、アセンブラの方では、ニーモニックとしてJMLの代わりにJMP、あるいはJSLの代わりにJSRを使用しても、オペランド解析で区別が付くなら自動的に置き換えてくれるものが多いはずです。

最後に残ったのはブロック転送命令ふたつだけですね。

MNE. xyc   動作
MVN 54*3 7 Block Move Negative
MVP 44*3 7 Block Move Positive

これらはxycアドレッシングモードで説明したような動作を行いますが、MVN命令ではX, Yレジスタをデクリメントしながら、MVP命令ではインクリメントしながらの転送となる点だけが異なります。命令のクロック数は1 Byte転送当たりのクロック数で、実際の実行クロックはこの倍数となります。これらの命令は、Mフラグとは無関係に常にバイト単位でのデータ転送を行います。Xフラグには影響を受けますが、Xフラグが1の8 bitインデックスモードでは、常にXとYの上位バイトが0であるため、ゼロページ内の最大256 Byteまでの転送しか行うことができず、ほとんど意味はありません。このほか、転送命令実行後はDBRがオペランドによって書き換えられてしまうことに注意して下さい。

つづく

Return to IC Collection.