Oct 2, 2020
CPUは2A03(RP2A03[G])であり, 6502をベースとして機能をいくつか削りつつオーディオ生成機能を載せたものだ. $4000-$4013, $4015, $4018-$401Fがオーディオのレジスタ.
Address | 内容 |
---|---|
$0000-$00FF | Zero Page |
$0100-$01FF | Stack |
$0200-$07FF | RAM |
$0800-$1FFF | Mirrors $0000-$07FF |
$2000-$2007 | I/O Registers |
$2008-$3FFF | Mirrors $2000-$2007 |
$4000-$401F | I/O Registers |
$4020-$5FFF | Expansion ROM |
$6000-$7FFF | SRAM |
$8000-$BFFF | PRG-ROM Lower Bank |
$C000-$FFFF | PRG-ROM Upper Bank |
http://obelisk.me.uk/6502/reference.html
NES 基礎知識 - CPU がとてもわかりやすい.
いくつかの命令を説明する. JSR実行前のPCはJSRオペランドの次のアドレスを指しているが, pushされるのはPC-1であることに注意する. つまり帰還点の1つ前のアドレスを指すことになる.
シフトは4つ, ASL(Arithmetic Shift Left), LSR(Logical Shift Right), ROL(Rotational Shift Left), ROR(Rotational Shift Right)であるが, ASLは2倍演算, 7bitが1ならばCが1となる. LSRは2で割る演算で余りがCに格納される. ROL, RORはCフラグも含めたシフト演算.
RTI(Return from Interrupt)ではRTS(Return from Subroutine)と違い帰還点のアドレスをpullする. RTSではpullしたアドレスをさらに+1しなくてはならない.
SBCではCが0のときborrowしていると考える, つまりCが1のときは通常の減算であり, 0のときはborrowしている分もひく. これは, 複数バイトの演算で役に立ち, low byteのSBCでborrowが発生した時にCフラグが1となるので, hi byteでborrowの分を引くことができ2byteの引き算ができる.
ADC signed resultが不正の時にVフラグが立つ. 例えば0x80+0x80で0x00になった時など. 同じ符号で足しているのにもかかわらず結果が違う符号になるという時に立てれば良い.
SBC も, 符号が違うもの同士の引き算で結果が引かれる側の符号と異なる時に立てればいいはず
PはステータスレジスタでNV.BDIZC というビットフラグを表している.
VはOverflow.
SP(Stack Pointer)は$0100-$01FFのスタック領域のポインターであり, 1byteで$0100からのオフセットを表す. 後ろから前の方向に積み重なっていく. 通常スタックといえばポインターが指す場所の解釈は2通りあり, full stackとempty stackだ. full stack はポインターはスタックの一番先頭の保存場所を指し, push時にポインターを進めてから書き込む, という動きをする. empty stackはスタックの保存場所の直前を指し, push時に書き込んでからポインターが動くという動作となる. イメージ的にはfullは既に値が埋まっており, emptyは空いていて値を入れられる場所を指すということだ. 6502や65816ではemptyだ. reset時は0xFFにするのが普通だが, https://github.com/fogleman/nes/blob/master/nes/cpu.go のようにエミュレーターによっては0xFDにしたりしているがこの理由を調査したい, 単純ミスかもしれないが. 256byte埋まった瞬間ポインターは0xFFとなり, つまり$01FFを指すようになる. スタック領域でも, lo, hiの順に並んでいる.
CPUにはバグがあり, 0x??FFのアドレスを参照して2byte取得すると, low部分は0x??FFだが, hi部分は0x?(?+1)00ではなく0x??00を参照してしまう, つまり0x100分戻った場所を参照してしまう.