Return to Site-Index

Mnemonic6502 for NES

NES(Famicom)のMPUである6502(のカスタムチップ)の解説とニーモニック表です。

NESで使用されているMPUについて

NESに使用されているMPUはリコー製の6502プロセッサのカスタムチップ(RP2A03)です。 動作クロックは1.7897725MHzでした。(このクロックの数字は後で音源を操作するのに必要になります) MPUとはいわゆるCPUのことで、6502では慣習的にCPUではなくMPUと表記しています。 このカスタムチップではいくつかの機能の削除・追加がなされています。 具体的にはBCDモードが削除されI/Oがメモリにマップされる形に変更されています。 また矩形波2ch、三角波1ch、ノイズ1chとΔPCM1chからなるサウンド機能を内蔵しています。 その他NESではハードウェアの構成に特殊な部分が多数あります。これについては改めて紹介します。

MPU6502の解説

16進数の表記と16bit数値について

6502などモトローラ系列のMPUのニーモニックでは16進数を表記する場合に数値の頭に"$"を付けます。16進数DEは$DEとなります。 6502はモトローラの系列ですが、メモリ内の16bit数値をリトルエンディアンで扱う珍しいMPUです。 (他のモトローラ系MPUでは基本的にビッグエンディアンで扱います)

このページではアセンブリ・ニーモニックをNESASMでの表記で記述しています。 NESASMでは2進数を"%"を付けて記述できるように拡張されています。

次の2つのコードは同一の内容です。
	lda	#$CD
	lda	#%11001101

レジスタ

表1は6502のレジスタをまとめたものです。PSRレジスタはIntel系のCPUでいうところのフラグレジスタに相当するもので、 演算結果により変化するレジスタです。その他、MPUの動作モードの設定も行います。 特徴的なのは6502はPC以外のレジスタすべてが8bitであることに加え、 演算に使用するアキュムレータレジスタがAレジスタ1つしかありません。 これに関してはページの説明と併せて後ほど説明します。

表2にはPSRレジスタの各ビットの名称と動作についてまとめてあります。 ネガティブフラグは演算結果の正負を現しますが要するにAレジスタの最上位ビットになります。 オーバーフローフラグは演算結果がオーバーフローした際にセットされます。 ブレイクフラグはBRK割り込み発生時セットされます。 6502ではIRQとBRKの割り込みが共通のアドレスとなっているため、 このフラグを見て判別します。 デシマルモードフラグはMPUをBCDモードに切り替える際セットしますが、 NESでは機能が削除されているため、セットされているとMPUが正常に動作しません。 電源投入時のフラグの状態が不定のためプログラムの最初にこのビットのクリアを行うのが定石となっています。 インタラプトフラグはIRQ/BRK割り込みを禁止する際セットします。 ゼロフラグは演算結果が0の時にセットされます。 キャリーフラグは桁上がり(キャリー)発生時にビットがセットされるのはごく普通の動作ですが、ボロー(桁下がり)発生時には逆にクリアされるという特殊な仕様になっていることに気をつけてください。 また、加算命令・減算命令ではこのキャリーフラグも併せて加算・減算される仕様になっています。

表1 6502のレジスタ
PC(プログラムカウンタ)16bit
A(アキュムレータレジスタ)8bit
X(インデックスレジスタX)8bit
Y(インデックスレジスタY)8bit
S(スタックポインタレジスタ)8bit
PSR(プロセッサステータスレジスタ)8bit
表2 PSRについて
PSRレジスタの各ビットの配置NV0BDIZC
N:ネガティブフラグ正負符号。Aレジスタの最上位ビット。
V:オーバーフローフラグ演算結果が127を超える、または-128を下回るとセットされる。
B:ブレイクフラグ BRK割り込み発生時にセットされる。割り込みがBRKかIRQかを識別するために使用する。
D:デシマルモードフラグ セットするとBCDモードで動作する。NESでは機能が削除されているのでセットしてはならない。
I:インタラプトフラグセットするとIRQ/BRK割り込みが禁止される。
Z:ゼロフラグ演算結果が0の時にセットされる。
C:キャリーフラグキャリー発生時およびボローが発生しなかった時にセットされる。

メモリ・ページ

6502で扱うことができるメモリ空間は$0000から$FFFFまでの64KByteです。しかしながら、前述のようにレジスタが8bitのため これらのメモリをレジスタにより直接指定することができません。スタックについても同様の理由により、 固定された256Byteのみがスタックとして利用可能となっています。6502ではこのように8bitのレジスタで処理する ためにメモリを256Byteごとに区切ってページという概念を用いて表現します。

ページとは8bitのレジスタでメモリを操作するためにメモリを256Byte単位で区切ったものです。スタックは$0100から$01FFまでの1ページ256ByteをSレジスタを使ってアドレスの下位8Byteを管理します。スタックの動作はスタックを積む時Sレジスタをデクリメントしスタックから取り出す時Sレジスタをインクリメントします。そのためプログラム開始時にSレジスタを$FFに初期化するのが一般的です。

$0000から$00FFまでの256Byteは特に0ページと呼ばれ特別な意味を持ちます。 0ページへのアクセスは通常より命令の長さを短くすることができ通常より少ないクロックでアクセスできるため、 他のメモリより高速にアクセスすることができるようになっています。 また連続した2Byteをメモリ空間のポインタとして使う間接アドレッシング機能があり これにより8bitのレジスタで全メモリ空間をアクセスでき、 また0ページへの高速アクセスを使うことで8bitプロセッサでありながら高い性能を発揮することができます。

アドレッシングモード

6502には豊富なアドレッシングモードが用意されています。6502でのプログラミングにはこのアドレッシングモードに関する理解が不可欠です。ここではそのアドレッシングモードについて紹介します。

Immediate addressing mode

	lda	#$80

Immediate addressing modeはオペランドに指定した値をそのまま指定します。 ldaはメモリの内容をAレジスタにロードする命令です。この場合ですとAレジスタに$80の値がロードされます。 Immediate addressing modeを使用する場合は値に"#"を付けて記述します。

このアドレッシングモードで記述された命令はオペコードに展開すると2Byteになります。

Zero-Page addressing mode

	lda	<$D0

オペランドの頭に"<"を付加するとZero-Page addressing modeになります。 オペランドには8bitの0ページアドレスを指定し、指定した0ページのアドレスの値を読むように指定します。 この場合Aレジスタに$00D0の内容がロードされます。 Zero-Page addressing modeでは次のAbsolute addressing modeよりもオペコードに展開した際の長さが短く 少ないクロック数で処理が行える利点があります。

展開されるオペコードは2Byteになります。

Absolute addressing mode

	lda	$8020

Absolute addressing modeでは16bitでアドレスを指定します。この例では$8020の内容がAレジスタにロードされます。

展開されるオペコードは3Byteになります。2,3Byte目がアドレスになりますが、リトルエンディアンになっています。

Implied addressing mode

	inx

Implied addressing modeは命令自体に処理対象が含まれているものです。 このアドレッシングモードの場合オペランドを指定しません。 inxはインデックスレジスタXをインクリメントします。

展開されるオペコードは1Byteになります。

Accumulator addressing mode

	lsr	a

Accumulator addressing modeはAレジスタのみを対象とする命令のアドレッシングモードです。 lsrはAレジスタのみを対象として論理右シフトを行う命令です。

展開されるオペコードは1Byteになります。

Index Absolute X addressing mode / Index Absolute Y addressing mode

	lda	$C080, x
	sta	$6000, y

Index Absolute X addressing mode / Index Absolute Y addressing modeは第1オペランドに記述したアドレスに 第2オペランドにxまたはyを記述することで指定したインデックスレジスタの値を第1オペランドのアドレスに 加算してそのアドレスを指定することができます。Xレジスタの値を$F0, Yレジスタの値を$20とすると $C080+$F0=$C170となり、$C170の内容がAレジスタにロードされます。 staは指定したアドレスにAレジスタの内容をセットする命令で、 この場合$6000+$20=$6020となり、$6020にAレジスタの内容をセットします。

展開されるオペコードは3Byteになります。

Index Zero-Page X addressing mode / Index Zero-Page Y addressing mode

	sta	<$D0, x
	ldx	<$D0, y

Index Zero-Page X addressing mode / Index Zero-Page Y addressing modeはAbsolute addressing modeに対する Zero-Page addressing modeと同様にIndex Absolute addressing modeに対する0ページを対象とするアドレッシングモードです。 ただし、注意点があります。このアドレッシングモードではldxstxの2命令に限り インデックスレジスタYが使用できそれ以外の場合はインデックスレジスタXしか使用できません。 ldxstxはそれぞれXレジスタを対象とするロード、セット命令であるため 代用としてインデックスレジスタYが使用できるようになっています。

展開されるオペコードは2Byteになります。

Index Indirect addressing mode

	lda	[$40, x]

Index Indirect addressing modeはインデックスレジスタXを使用する間接アドレッシングです。 間接アドレッシングとは2Byteの連続した0ページアドレスにセットされた値を実際の対象アドレスとして 処理するアドレッシングです。 この例では0ページアドレス$40にインデックスレジスタXを加算した0ページアドレスを間接アドレッシングに使用します。 Xレジスタの内容が$02の場合、$42, $43を間接アドレッシングに使用します。 $42が対象アドレスの下位8bit、$43が対象アドレスの上位8bitを表します。 $42, $43の内容がそれぞれ$F0, $DEだった場合、この例ではアドレス$DEF0の値をAレジスタにロードします。

展開されるオペコードは2Byteになります。

Indirect Index addressing mode

	lda	[$40], y

Indirect Index addressing modeはインデックスレジスタYを使用する間接アドレッシングです。 先ほどのIndex Indirect addressing modeとはインデックスレジスタを加算するタイミングが異なります。 こちらでは$40,$41を間接アドレッシングに使用します。 $40,$41の内容が$00,$C0なら$C000にインデックスレジスタYの値を加算しこれが対象アドレスになります。 インデックスレジスタYが$80だったとすると、この例の場合$C080の内容がAレジスタにロードされます。

展開されるオペコードは2Byteになります。

Absolute Indirect addressing mode

	jmp	[$0320]

Absolute Indirect addressing modeは唯一jmp命令にのみ使用できるアドレッシングモードです。 この例では$0320,$0321にセットされたアドレスにPCを移動(ジャンプ)しています。 あらかじめ間接アドレッシングに使用するアドレス($0320,$0321)の値を書き換えておくことで ジャンプ先を変えることができます。

このアドレッシングモードにはバグがあり、$03FFのような8bit境界を跨ぐような指定をすると 正しく動作しないようです。

展開されるオペコードは2Byteになります。

Relative addressing mode

	bne	Label1

Label1:

Relative addressing modeは条件分岐系の命令で使用されるアドレッシングモードです。 条件分岐命令は条件が成立した場合にオペランドに指定されたアドレスにジャンプしますが、 Relative addressing modeではジャンプ先のアドレスを絶対アドレス(2Byte)ではなく現在のアドレスからの 相対値(1Byte)で指定します。このため離れたアドレスへのジャンプはできませんが、 jmp命令よりやや速くジャンプできます。

展開されるオペコードは2Byteになります。

この例のように実際にはラベル名を指定しアドレス値はアセンブラが計算するので相対値を 計算しておく必要はありません。これまで紹介した他のアドレッシングモードでも同様です。 逆にラベルを使用せず実際の数値で記述することは、プログラムの可読性を低下させるだけでなく プログラムの修正の際に設定した値すべてを書き換える必要が出て作業効率の面でも 悪影響があります。

6502には以上の13種類の豊富なアドレッシングモードが提供されておりこれらを駆使することで、 少ないレジスタでも効率的な処理を可能にしています。

割り込み

6502には4種類の割り込みがあり、それぞれNMI, RESET, BRK, IRQと呼ばれています。 これらの割り込みが発生すると$FFFAから始まる3Word(1Word=2Byte)のベクタアドレスに ジャンプします。アドレスは16bitなので割り込み4種類に対してアドレスが3箇所しか設定できません。 これはBRKとIRQが共通のアドレスを使用し、ジャンプ後に必要であればPSRレジスタのBフラグを見ることで 分離する仕様だからです。

割り込みでのジャンプは割り込みが発生した時点で処理を行っていたアドレスとPSRをスタックに積みます。 割り込み処理が終了したらrti命令によりスタックからアドレスとPSRをもどして元の処理に戻ります。 他のA,X,Yレジスタはプログラマが必要であると判断したらスタックに積むようプログラムするという設計で 自動ではスタックに積まれないので注意が必要です。

NMI割り込み

NMI割り込みのベクタアドレスは$FFFAでRESET割り込みの次に優先順位の高い割り込みです。 NMIとはマスク禁止割り込みの意味でPSRレジスタを割り込み禁止に設定しても、 別に割り込みを禁止する処置がされていない限り割り込みが発生します。

NESではこの割り込みをVBlank割り込みに設定しています。VBlank割り込みは日本のテレビで使用されている NTSC信号では1/60秒ごとに定期的に発生します。NESでは画面の更新が基本的にVBlank中にしか行えないため、 このNMI割り込みが処理の中心になります。

RESET割り込み

RESET割り込みはベクタアドレス$FFFCで最も優先順位の高い割り込みです。 電源投入時やリセットボタンを押した場合などに発生する割り込みで、 プログラムの開始位置を指定する割り込みです。

BRK/IRQ

BRK/IRQのベクタアドレスは$FFFEで共通です。PSRのIフラグをセットすると割り込みが禁止されます。 BRKはブレーク割り込みのことでソフトウェア割り込みとも呼ばれます。 プログラム内でbrk命令を実行すると発生する割り込みです。 IRQはハードウェア割り込みでハードウェアにより発生する条件が異なります。 NESではマッパーによっては走査線カウンタによってこのIRQを発生させることができ、 これを使用して画面内分割表示などが行える場合があります。

BRK割り込みでは復帰時のアドレスが+1される仕様のようです。扱いがややこしいのであまり使用している場面を見かけません。

ニーモニック表

6502のニーモニックを表にまとめたものです。操作内容のA,X,Y,S,Pはレジスタを意味し、 Mはアドレッシングで指定されたメモリを意味します。Cはキャリーフラグです。 !Cと書いた場合はキャリーフラグを反転(NOT)して演算を行うという意味です。

ニーモニック変化するフラグ操作内容備考
ldaNZA <- M
staNZA -> M
ldxNZX <- M
stxNZX -> M
ldyNZY <- M
styNZY -> M
taxNZA -> XImplied addressing
txaNZX -> AImplied addressing
tayNZA -> YImplied addressing
tyaNZY -> AImplied addressing
txsNZX -> SImplied addressing
tsxNZS -> XImplied addressing
phaNZA -> [S], S--AレジスタをスタックにPHSH。Implied addressing
plaNZA <- [S], S++AレジスタにスタックからPULL。Implied addressing
phpNZP -> [S], S--PSRをスタックにPHSH。Implied addressing
plpNZP <- [S], S++PSRにスタックからPULL。Implied addressing
secCC <- 1Cフラグセット。Implied addressing
clcCC <- 0Cフラグクリア。Implied addressing
seiII <- 1Iフラグセット。Implied addressing
cliII <- 0Iフラグクリア。Implied addressing
sedDD <- 1Dフラグセット。Implied addressing
cldDD <- 0Dフラグクリア。Implied addressing
clvVV <- 0Vフラグクリア。Implied addressing
incNZM <- M+1メモリ内容をインクリメント。
decNZM <- M-1メモリ内容をデクリメント。
inxNZX <- X+1Xレジスタをインクリメント。Implied addressing
dexNZX <- X-1Xレジスタをデクリメント。Implied addressing
inyNZY <- Y+1Yレジスタをインクリメント。Implied addressing
deyNZY <- Y-1Yレジスタをデクリメント。Implied addressing
adcNVZCA <- A+M+CAレジスタにメモリ内容とCレジスタを加算。通常adcの前にclcを行いCレジスタをクリアする。
sbcNVZCA <- A-M-!CAレジスタからメモリ内容とCレジスタのNOTを減算。通常sbcの前にsecを行いCレジスタをセットする。
aslNZCC<-A:bit7, A:bitN<-A:bitN-1, A:bit0<-0Aレジスタ左シフト。Accumulator addressing
lsrNZCC<-A:bit0, A:bitN<-A:bitN+1, A:bit7<-0Aレジスタ論理右シフト。Accumulator addressing
rolNZCC<-A:bit7, A:bitN<-A:bitN-1, A:bit0<-CAレジスタ左ローテーション。Accumulator addressing
rorNZCC<-A:bit0, A:bitN<-A:bitN+1, A:bit7<-CAレジスタ右ローテーション。Accumulator addressing
andNZA <- A and M論理積演算。
oraNZA <- A or M論理和演算。
eorNZA <- A eor M排他論理和演算。
cmpNZCA-MAレジスタとメモリの比較。
cpxNZCX-MXレジスタとメモリの比較。
cpyNZCY-MYレジスタとメモリの比較。
bitNVZA and M, N=M:bit7, V=M:bit6論理積の演算結果によりZフラグ変更
beqZ=1で指定アドレスへ相対ジャンプします。
bneZ=0で指定アドレスへ相対ジャンプします。
bcsC=1で指定アドレスへ相対ジャンプします。
bccC=0で指定アドレスへ相対ジャンプします。
bvsV=1で指定アドレスへ相対ジャンプします。
bvcV=0で指定アドレスへ相対ジャンプします。
bmiN=1で指定アドレスへ相対ジャンプします。
bplN=0で指定アドレスへ相対ジャンプします。
jmp指定アドレスへジャンプします。
jsr指定アドレスへサブルーチンジャンプします。
rtsサブルーチンから復帰します。
rti割り込みから復帰します。
nopクロックを消費するだけで何もしません。
brkBRK割り込みを発生させます。