Upgrade to PRO for Only $50/Year—Limited-Time Offer! 🔥
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
ファミコンに思いを馳せる〜エミュレータ自作を添えて〜
Search
noharu36
December 01, 2025
Programming
0
3
ファミコンに思いを馳せる〜エミュレータ自作を添えて〜
noharu36
December 01, 2025
Tweet
Share
More Decks by noharu36
See All by noharu36
検証!会津は本当に地盤が硬いのか?
noharu36
0
2
自作オブジェクトストレージをRustで
noharu36
0
1
shell自作した話
noharu36
0
4
Rustを布教したい
noharu36
0
7
neofetchよ、永遠に
noharu36
0
2
ISSの軌道計算をRustで
noharu36
0
5
Other Decks in Programming
See All in Programming
30分でDoctrineの仕組みと使い方を完全にマスターする / phpconkagawa 2025 Doctrine
ttskch
3
680
大体よく分かるscala.collection.immutable.HashMap ~ Compressed Hash-Array Mapped Prefix-tree (CHAMP) ~
matsu_chara
1
200
これだけで丸わかり!LangChain v1.0 アップデートまとめ
os1ma
6
1.2k
【CA.ai #3】Google ADKを活用したAI Agent開発と運用知見
harappa80
0
140
ソフトウェア設計の課題・原則・実践技法
masuda220
PRO
24
20k
Querying Design System デザインシステムの意思決定を支える構造検索
ikumatadokoro
1
1.2k
Integrating WordPress and Symfony
alexandresalome
0
110
Rediscover the Console - SymfonyCon Amsterdam 2025
chalasr
2
120
Building AI with AI
inesmontani
PRO
1
460
スタートアップを支える技術戦略と組織づくり
pospome
8
14k
All(?) About Point Sets
hole
0
240
全員アーキテクトで挑む、 巨大で高密度なドメインの紐解き方
agatan
8
18k
Featured
See All Featured
Designing Experiences People Love
moore
142
24k
The MySQL Ecosystem @ GitHub 2015
samlambert
251
13k
Art, The Web, and Tiny UX
lynnandtonic
303
21k
Build your cross-platform service in a week with App Engine
jlugia
234
18k
ピンチをチャンスに:未来をつくるプロダクトロードマップ #pmconf2020
aki_iinuma
128
54k
Product Roadmaps are Hard
iamctodd
PRO
55
12k
Large-scale JavaScript Application Architecture
addyosmani
514
110k
Testing 201, or: Great Expectations
jmmastey
46
7.8k
The Cost Of JavaScript in 2023
addyosmani
55
9.3k
Chrome DevTools: State of the Union 2024 - Debugging React & Beyond
addyosmani
9
1k
Responsive Adventures: Dirty Tricks From The Dark Corners of Front-End
smashingmag
253
22k
Java REST API Framework Comparison - PWX 2021
mraible
34
9k
Transcript
ファミコンに思いを馳せる 〜エミュレータ自作を添えて〜 学部2年 harukun
自己紹介 • 名前: 能島明希 • ハンドルネーム: harukun • 出身: 広島->岡山->大阪->東京->会津 • 技術: フロント(React+TS), バックエンド(Rust,
Go勉強中), その他(Rust, Haskell勉強中), Rust(Rust) • 好きなもの: ゲーム、タバコ、炒飯、 Rust、Neovim • 落とした単位数: おそらく同期内1位 • Twitter: https://twitter.com/pieceofharuki • Blog: https://zenn.dev/haru_blog
ファミコンとは
基本情報 • 1983年7月15日発売 正式名称ファミリーコンピュータ • 累計出荷台数6000万台 • 当時の家庭用ゲーム機 脅威の80%シェア • 世帯普及率25% 小中学生がいる家庭のほとんど •
スーパーマリオブラザーズ 世界売上 4000万本 2020年にあつ森に抜かれるまで 30年以上国内売上1位 今でも歴代世界売上本数6位
技術 • CPU Ricoh 2A03 MOS 6502(Apple Ⅱに搭載)をカスタマイズしたもの 当時よく使われていたz80に比べクロック数は劣るが、チップ面積が1/4でコストを削減できた 当時日本ではほぼ無名でリバースエンジニアリングされづらかった(ナムコは執念で解析した)
• ROM USBメモリとは違う メモリにアクセスするようにCPUから直接アクセスできて早い(カセットの中身をメモリに展開する必要がない) キャラクターROMとプログラムROMに分かれている • APU(オーディオプロセッシングユニット) CPUに内蔵されている 主にパルス波・三角波・ノイズで構成されている • PPU(ピクチャープロセッシングユニット) Ricoh 2C02(最初期) Apple Ⅱ
ファミコンエミュレータ自作してみた
Why • Rustでものづくりしたかった • 低レイヤなことにも少し興味があった • ちょうどGWだから何か作ろうと思った • 9月ごろに一回挑戦して失敗したから再挑戦することにした •
Rust書きたかった
技術スタック
実装の流れ 1. NES(ファミコンのこと)についておおまかに理解する 2. アセンブラの復習 3. CPUの実装 4. BUSの実装 5.
テスト、そして圧倒的デバッグ 6. PPUの実装 7. コントローラーの実装 8. (スクロールの実装) 9. APUの実装 //まだ!
ファミコンについて大まかに理解する • アーキテクチャ OSがないのでアプリケーション層が直接ハードウェアと通信する • CPUとPPUは直接カセットに接続できる • カセットを挿入するとCPUがプログラムROMに、PPUがキャラクターROMに接 続される •
CPUのメモリは2KiB(2048B)しかなかった • CPUの内部にAPUが実装されている • CPUにはWRAMという2KiBのメモリが接続されている • PPUにはVRAMという背景情報・スプライト情報などが格納される 2KiBのメモリ が接続されている • CPUは直接VRAMにアクセスできないが、PPUのレジスタを介してアクセスで きる
https://qiita.com/bokuweb/items/1575337bef44ae82f4d3#%E7%B0%A1%E6%98%93%E3%83%8F%E3%83%BC%E3%83%89%E3%82% A6%E3%82%A7%E3%82%A2%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E5%9B%B3
CPUの実装 • アドレスによってどこにアクセスできるか決まる • メモリアクセスは比較的遅いので非常に速くアクセスできるレジ スタと呼ばれる内部メモリ スロットがいくつかある ◦ レジスタのみにアクセス ◦
RAMの最初の255バイトへのアクセス • 上記に注意しながら約150の命令を実装する(白目)
こんな感じで命令を羅列して こんな感じで命令を実装して こんな感じで分岐を書く
これでようやくHello Worldができた
テスト テスト用のROMを読み込んで実行する diffコマンドを用いて実行ログとテスト用の ログを比較する (左が実行結果 右がテスト用ログ 左右が等しくなれば正常) すると実装されてない命令がいくつか (110個)あることがわかる これらは非公式命令と言われていて、 6502には公式に実装されていなかったが多くのゲームで使われている命 令たちなので、追加で全部実装する
()
こんな感じで命令を羅列して こんな感じで命令を実装して こんな感じで分岐を書く(ヤケクソ)
PPUの実装 • レジスタ CPUとの接続に使う( CPUアドレス空間の0x2000~0x2007) • 内部メモリとしてスプライトの状態を保持する OAMを持つ(256B) • 描画サイクル
◦ PPUは1秒間に262ラインレンダリングする ◦ NESの解像度は320 x 240 ◦ よって262-240=21ラインは画面に映らない ◦ 241ライン目に入ると VBlankというNMI割り込み命令が CPUに送 られる ◦ この間PPUのメモリは変更されないので CPUから自由にアクセス できるため次のフレームのために画面の状態を更新する
だるかったこと PPUは1クロックサイクルで1ドット描写する PPUはCPUの3倍高速なのでCPU1クロックサイクル=PPU3クロックサイクル PPUとCPUのクロックサイクルを合わせてあげないと画面が崩れてしまう 1ラインごとのPPUのクロックサイクルは341 1フレーム毎に341x262=89342PPUクロックサイクルになる CPUは29780CPUクロックサイクルごとにNMI割り込みを受ける
• まずCPUの命令毎にクロックサイクルを定める • PPUのtick関数に3倍したものを引数として渡す • cyclesが341を超えた時にscanlineをインクリメントし、 scanlineが241になった時にCPUにNMI割り込み命令 を送る • CPUはNMI割り込み命令を受け取ると現在の命令の実
行を終了し次フレームの画面の描写の準備に入る
スクロール 長くなるのでカット! ファミコンには画面の状態を保持する VRAMと呼ば リがあって、サイズは2 KiB。1画面を表示するのに モリは1KiBなので2画面分を割り当てられる。まず zero hit flagの設定をする。sprite
zeroを画面の特 置(X, Y)に配置し、sprite zero hit flagが0から1に と、CPUはPPUが[0..Y]スキャンラインのレンダリン しYスキャンライン上でXピクセルのレンダリングが ことを示す。垂直または水平ミラーリングを実装し viewportを動かしていった分を shift_x, shift_yとお 256-shift_x, 240-shift_yを2つ目の画面のサイズ 定しフレーム毎に更新することで、静的な背景を動
できたもの
まとめ • 当時、ファミコンの開発に携わってた人たちはほんとにすごい • エミュレータの実装を通して、少しだけでも当時のエンジニアたちの気持ちを理解することができた • メモリアクセスやアセンブラについて詳しくなれた • エミュレータ自作とは結局小さなコンピュータを自作すること。次は OS自作に挑戦したい。
• 最後まで完成させることはできなかったけど、今までやったことがない低レイヤなことができてよかった
おまけ セキュキャンに申し込みました。 今回の開発で低レイヤにもっと興味が沸いたので OS自作や探査機自作ゼミに参加したい。 5/20が応募課題の締切なので1日8時間ほど取り組めばなんとか終わるだろうという計算 ...