kbits

PCエンジンのCPU

原題: PC Engine CPU
著者: jsgroth
公開日: 2026-05-07
ソースURL: https://jsgroth.dev/blog/posts/pc-engine-cpu/
アーカイブ日: 2026-06-03


要約

PCエンジン(北米名 TurboGrafx-16)のエミュレータを開発している著者 jsgroth が、その心臓部であるCPU「HuC6280」を技術的に掘り下げた解説記事。1987年に登場したPCエンジンは、第3世代機(NES、セガ・マスターシステム)と第4世代機(メガドライブ、スーパーファミコン)のちょうど狭間に位置するハードで、グラフィックの優秀さから後者と一緒に語られることが多い。日本ではそれなりに売れたが、北米ではメガドライブとSNESに勝てず、欧州では公式発売すらされなかった。CD-ROM²(TurboGrafx-CD)によって世界初のCD-ROMゲーム機となった点が歴史的に特筆される。

本記事のテーマは、このCPUが「当時としては非常に高速でありながら、競合機に比べて命令セットの能力はずっと限定的」という独特なポジションにあることだ。

TurboGrafx-“16” なのに16ビットではない

最大のポイントは、TurboGrafx-16という名前にもかかわらず、このCPUは16ビットCPUではないということ。68000のように「16ビットか32ビットか」と議論が分かれる類の話ではなく、PCエンジンのCPUには16ビット的な要素が一切ない。8ビットレジスタ、8ビットALU、8ビットデータバスである。その代わり、生のスピードで勝負している。

CPUはハドソンが独自設計したパッケージ「HuC6280」の一部で、CPUコアのほかにPSG音源チップやハードウェアタイマーも内包する。CPU自体はWestern Design Center(WDC)が往年の6502を強化した「65C02」をベースにしている。命令セットはNES(6502からBCDモードを除いたもの)やSNES(65C816)の経験者には馴染み深く、さらにHuC6280独自の命令が多数追加されている。

HuC6280は65C02の新命令や新アドレッシングモード(ゼロページ間接)を継承し、6502の「ジャンキー」な挙動(jmp ($xxFF) が同一256バイトページ内で折り返してしまうバグなど)も修正されている。6502の有名な「不正命令(illegal opcode)」の奇妙な挙動もなく、22個の未使用オペコードはすべて単なるNOPとして動作する。

クロックスピード:低速と高速の切り替え

CPUは2つのクロックスピードで動作できる:

モード 周波数 備考
低速(Low) 約1.79 MHz NESと同じ。電源投入時はこのモード
高速(High) 約7.16 MHz CSH 命令で切り替え

ゲームは CSL(Clock Speed Low)と CSH(Clock Speed High)命令で切り替える。高速モードに欠点は見当たらないため、ゲームはほぼ起動直後に CSH を実行して高速のまま動かすのが通例。

7.16 MHzは80年代末〜90年代初頭の6502系CPUとしては非常に高速だ。これはSNESのCPUのちょうど2倍の速さであり、しかもPCエンジンのCPUはSNESのようなメモリレイテンシにほとんど悩まされない。ビデオプロセッサのポートにアクセスするときだけウェイトサイクルが入るが、それ以外のROM・RAMはすべて7.16 MHzの1クロックで応答する。結果として、16ビット値の演算を必要としない限り、PCエンジンのCPUはSNESの2倍以上速いことが多い。

メガドライブの7.67 MHz 68000との比較も興味深い。68000はクロックあたりの仕事量こそ6502系より少ないが、ほぼ汎用の32ビットレジスタを多数持ち、命令セットも遥かに強力なため、どちらが速いかはコード次第となる。

なお、サイクル計算上は、多くのHuC6280命令が等価な6502/65816命令より1〜2サイクル多くかかる。例えば絶対アドレッシングの ADC は6502で4サイクル、HuC6280で5サイクル。またページクロスのペナルティサイクルも、HuC6280はページクロスがなくても常に課されるようだ。高速クロック対応のため、あるいはコスト削減のため回路を共有した結果ではないかと著者は推測している。

メモリ管理:シンプルな内蔵MMU

HuC6280上のソフトは6502と同じ16ビットアドレスで動作するが、CPUには内蔵MMUがあり、物理アドレス空間を16ビットから21ビット(2MB)に拡張する。仕組みは極めてシンプルで、16ビット論理アドレス空間を8つの8KBページに分割し、各ページが専用の8ビットMPR(Memory Page Register)で物理ページに直接マッピングされる。NES・ゲームボーイ・マスターシステムなどのカートリッジに見られるバンク切り替えマッパーに似ているが、それがCPUに内蔵されているため、ゲームカートリッジ側でマッパーハードを持つ必要がない。

アドレス変換のロジックは単純そのもの:

# logical_addr は16ビットアドレス、21ビット物理アドレスを返す
def translate_address(logical_addr):
    logical_page = logical_addr >> 13
    physical_page = MPR[logical_page]
    return (physical_page << 13) | (logical_addr & 0x1FFF)

慣習として、PCエンジンのゲームはページ0($0000-$1FFF)をI/Oページに、ページ1($2000-$3FFF)をワーキングRAMに、ページ7($E000-$FFFF)をカートリッジROMの先頭8KBに割り当て、ページ2〜6を自由に再マッピングする。MPRの操作には TAMi(アキュムレータ→MPRi)と TMAi(MPRi→アキュムレータ)命令を使う。

興味深い点として、HuC6280のゼロページとハードウェアスタックは6502の $0000-$01FF ではなく $2000-$21FF に置かれている。これは $0000-$1FFF ページをI/O専用にするためだと思われる。ゼロページが実際には $0000 にないのは名前として少々不格好だが、ハドソンは6502と同じ用語を使うメリットを取ったのだろう(対照的に65816はゼロページの再配置に対応し、WDCはこれを「ダイレクトページ」と改名した)。

物理メモリマップはSNESの込み入ったマップよりずっと素直だ:

ページ 説明
$00-$7F HuCardカートリッジ または CD-ROM²システムカード
$80-$F7 拡張(CD-ROM²の追加RAM)
$F8 8KBワーキングRAM
$F9-$FB 拡張(スーパーグラフィックスの追加RAM)
$FF メモリマップドI/Oレジスタ・ポート

これによりゲームカードは1MBに制限される。『ストリートファイターII』は2.5MBという(PCエンジン基準では)巨大なROMを扱うためバンク切り替えマッパーで対処している。

ワーキングRAMはわずか8KB。NES(2KB)よりは多いが、初代ゲームボーイやマスターシステムと同等で、メガドライブ(64KB)やSNES(128KB)よりはるかに少ない。CD-ROM²の各リビジョンはこれを64KB〜2MB分補う。ただしCD読み込みのレイテンシが極端に高いため、CD-ROM²ゲームは使用のずっと前にコードとアセットをRAMにロードしておく必要がある。HuCardゲームがいつでもカートリッジから直接コードを実行しデータを読めるのとは対照的だ。

いくつかの新しい技

追加命令の中で際立つのが、5つのブロック転送命令 TAI, TDD, TIA, TII, TIN だ。すべてメモリ間のバルクコピーを行うが、ソースとデスティネーションのアドレスステップが異なる:

命令 ソースステップ デスティネーションステップ
TAI 交互(Alternate) 増加
TDD 減少 減少
TIA 増加 交互
TII 増加 増加
TIN 増加 固定

「交互(Alternate)」は1バイトごとに+1と-1を交互に適用するもので、ビデオプロセッサの16ビットデータポートへの出し入れに非常に有用。SNESの MVN/MVP に似ているが、PCエンジンにはカートリッジROMやCPUワーキングRAMにアクセスできるDMAハードがないため、これらの命令が実際に役立つ。

転送速度は1バイトあたり6サイクル+命令ごとに17サイクルのオーバーヘッド。ソフトウェアでコピーするよりずっと速い。注意点として、CPUはブロック転送の最中に割り込みに応答できない。1命令で最大64KBコピーでき、CPUは7.16 MHzモードで1フレームあたり約12万サイクルしか得られないため、大きな転送は複数のVBlank割り込みを簡単に飛び越えてしまう。画面が暗転している遷移中なら問題ないが、ゲームプレイ中の複数フレームにわたる転送は避けたい。

もう一つ注目すべきは SET(Set T)命令。新しいTフラグを立てるが、効果は直後の1命令のみ。ADC/AND/EOR/ORA の直前に置くと、それらがアキュムレータではなくゼロページの値(ZeroPage[X])に作用するようになり、アキュムレータを経由せずにゼロページの値を操作できる。

PCエンジン固有の命令として ST0/ST1/ST2(即値を3つのVDCポートに直接書き込む)、相対変位でサブルーチンを呼ぶ BSRJSR と違いマッピング先バンクに依存しない)、メモリを書き換えずビットをテストする TST、レジスタ間スワップの SAX/SAY/SXY、フラグを変えずレジスタをゼロクリアする CLA/CLX/CLY などがある。

続編は?

著者はPCエンジンの他のハードウェア(特にメガドライブやSNESと対照的なビデオハードウェア)についても続編を書きたいと述べて締めくくっている。


論評

レトロゲーム機のハードウェア解説は数多いが、本記事はエミュレータを実装中の開発者が「実際に作りながら気づいたこと」を語っている点に独自の価値がある。スペックシートの引き写しではなく、ペナルティサイクルが常に課される挙動への推測、ブロック転送命令がなぜSNESのDMAと違って「実際に役立つ」のか、CD-ROM²のレイテンシがゲーム設計に課す制約といった、実装者の視点でしか出てこない観察が随所に挟まれる。

「TurboGrafx-16 なのに16ビット要素が一切ない」という導入は、命名と実態の乖離という普遍的な面白さを突いている。8ビットのまま生のクロック速度(SNESの2倍)で殴り合うという設計思想は、トランジスタ予算の制約下でどこに賭けるかという当時のエンジニアリング判断の記録でもある。

ハードウェアの仕様は時間が経っても変わらないため、この種の技術解説は5年後・10年後も同じ価値を保つ。6502系アーキテクチャ、エミュレーション、レトロハードに関心がある読者にとって、HuC6280という「速いが命令セットは質素」なCPUの設計トレードオフを理解する良質な一次解説として参照に値する。


タグ: #エミュレーション #レトロゲーム #CPUアーキテクチャ #PCエンジン #6502