ファミコンと6502CPUに触れる

こんばんは、自分の担当を23日と間違えて前日まで記事を書いておらず
焦りまくったyaitoです。
42tokyo Advent Calendar 2020の21日目を担当しています。
ちなみに記事初投稿です...

前日はケチゴンさんによる自作言語(コンパイラ作成)の進め方という、少しマニアックな記事でしたね。彼とは仲が良く、最近二人とも低レイヤに興味を持って活動しています!

明日は、私と同じ高校で低レイヤー好き同士のyaitoが担当します。ぜひご覧ください!!

上記のようにケチゴン氏から重めのバトン受け取ってます😂

実は、今日もタイトルから見て分かる方もいるように、多少マニアックな内容となっています。みなさんお腹いっぱいかもしれませんが、詳細になりすぎないように書いているので最後までお付き合いください!

ファミコン発売当時の位置付け

画像1

みんな大好きファミリーコンピュター。
この記事では、ファミコンの動作の仕組みについて簡単に書いていきますが、最初に基本情報と同時期に発売されたコンピュータとの性能を比較していきます。

ファミコンはいつ発売された?

ファミコンは1983年に発売されました。
現在までの累計販売台数は6191万台となっています。
最も売れたゲーム機ランキングでは13位に位置しています。当時のゲーム普及率を考えると、相当な販売数だったと思います。

ファミコンの性能は?

ファミコンは大きく分けると以下のパーツで構成されています。

画像3

CPU :
8ビット、1.79MHz
WRAM(ワーキングRAM)というメモリにゲーム中の状態を保存している

グラフィックス:
52色中25色表示、256×240ピクセル、スプライト、BG(バックグラウンド)、スクロール機能
主にPPUが担当していて、VRAMに値を代入することで画素を表現する

メモリ:
WRAM(ワーキングRAM)2KB、VRAM(ビデオRAM)2KB
WRAMはCPUからのみ、VRAMはPPUからのみアクセスできる

プログラムROM32KBとキャラクタROM8KBはカセットに搭載されている。
(これもファミコン本体の低価格を実現する為)

サウンド:
短形波2系統、三角波1系統、ノイズ1系統、DPCM1系統の計5系統

入力装置:
十字ボタン1個、4個のボタンを配置したコントローラが2機
入力の有無はメモリマップを参照することで確認できる
(メモリマップドIO)

価格:
14800円

同時期発売のコンピュータと比較すると?

同時期発売のコンピュータにはPC-8801mk ⅡやFM-7、X1Cなどがあります。
これらのコンピュータは64KBのメインRAMと48KBのグラフィックスRAMを搭載しています。また、BASIC言語を使ったプログラムを組むこともでき、ファミコンの何倍もの性能がありました。
しかし、当時のメモリは高価だった為、比較的大きなメモリを搭載しているコンピュータは価格面ではどれも10万円以上を要しました。
比べて、ファミコンはゲームで遊ぶことだけに特化したハードウェアである為、メモリ容量は2KBのワーキングRAMと2KBのビデオRAMの計4KBに抑えられています。その分、14800円という価格で販売することが可能になり多くの家庭に普及したと考えられます。


What`s 6502?

画像6

さて、タイトルにもある「6502」という数列は何を表しているのでしょうか?答えは、ファミコンの内部で使用されているCPUの名称です。
正確には6502CPUのリコーカスタム(RP2A03)が使われています。
先ほどファミコンには大きく分けて以下のパーツが存在するとお話ししました。ここからは、CPUの基本動作について説明していきます。

CPUは主に4つのステップを繰り返し、処理を行っています。
具体的な処理としてはメモリに値を代入したり、メモリの中身を参照したり、演算を行ったりと非常に基本的なことしかしていません。
グラフィックについてはPPUが担当していますが、今回の記事の内容とは逸れるので省略します。

ステップ1では
PC(プログラムカウンタ)はCPU内のレジスタの内の1つで、現在の命令が格納されているメモリ番号を示しています。

#疑似コード

PC = 0x8000;
opecode = Memory[PC];

画像6

ステップ2では
フェッチした命令コード(0xA5など)を元に、実行する命令とその操作対象を決定します。例えばLDAはAレジスタに操作対象をロードする命令ですが、
6502アーキテクチャにはなんと13種類の操作対象があります.....

#疑似コード

switch(opecode)
{
    0xA5: LDA(Zero);
    0xA6: LDX(Zero);
    ...
    ..
}

画像6

ステップ3では
必要な場合演算を行います。演算といってもアドレス同士の加算であったり、命令実行時に数値を演算したりいろいろあります。
(つまりステップ3とステップ4が区別できない場合もあります)

下の擬似コードでは実行命令の中で演算を行っています。

#疑似コード

void CPU::ADC(uint8_t operand)
{
    A_reg += operand;  //本来はキャリーフラグを考慮する
    ......
}

画像7

ステップ4では
ステップ2で決定した命令を実行します。
高級言語で実装する場合は各命令を関数化しておくと、該当する関数を呼び出すだけで良いので、コードの可読性が向上します。

画像8

長くなりましたが、まとめると以下のようになります。

1. PCから命令をフェッチ
2. 命令と操作対象を決定(デコード)
3. 必要であれば演算
4. 命令の実行

以上を繰り返す。

#疑似コード

PC = 0x8000;
opecode = Memory[PC]; //opecode = 0xA5

switch(opecode)
{
    0xA5: LDA(Zero); //実行される命令 
    0xA6: LDX(Zero);
    ...
    ..
}

void CPU::LDA(uint8_t operand)
{
    A = operand; //Aレジスタに操作対象(operand)をロードする
    ......
}


6502アーキテクチャについてもっと知る

画像4

ここについては(というかファミコン全般)正直、インターネット上を探せば非常に詳しく解説されているサイトはいくつもあります。6502アーキテクチャにはたくさんの特徴があり、この記事では解説しきれないのでご興味のある方は下記のサイトを参考にしてください。

NES on FPGA
https://pgate1.at-ninja.jp/NES_on_FPGA/index.html
NES研究所
http://hp.vector.co.jp/authors/VA042397/nes/index.html
NesDev
https://nesdev.com/


(おまけ)ファミコンを自作したい!

現在、FPGAボード上でファミコンを再現するというプロジェクトを勝手に一人でやってます。C++で書いたコードをintel HSLコンパイラでハードウェア記述言語(HDL)に変換してから、FPGAボードに転送して実行しています。
有料ライセンスを持っていない&Linuxでの環境構築は思った以上に大変で時間がかかったので、環境構築の仕方も含めて大きな進捗が出たタイミングで一つの記事にしたいと思っています。

画像9


最後に

クリスマスまでもう一週間を切り、一年が過ぎるのは本当に早く感じます。
ところで、明日はすみなべさんが記事を書いてくれます。
タイトルは今のところ未定ですが、どんな記事になるのでしょうか?
読むのが益々楽しみですね!

参考書籍:
松浦健一郎/司ゆき「ファミコンの驚くべき発想力」技術評論社、2019年
渡波郁「CPUの創りかた」マイナビ出版、2019年

引用:
Ricoh RP2A03 (画像)
https://electrelic.com/electrelic/node/1365


この記事が気に入ったらサポートをしてみませんか?