
2026/05/08 23:14
以下は「PC Engine CPU」という短縮表現を踏まえて作成した正式な文書形式です: ・PC Engine CPU (※原文中の改行および空白は削除し、独立した項目として再構成しています。)
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
尽管 PC Engine 作为 16 位系统进行宣传,但其在历史上因通过 CD-ROM2 扩展板开创了光盘游戏而著称。其架构的核心是由 Hudson 开发的独特 8 位 HuC6280 CPU,内置 PSG 音频芯片,并以约 7.16 MHz 的高速度运行;凭借单周期内存访问及无典型内存延迟的特性,相比 Sega Genesis 或 Super Nintendo 等竞争对手具备原生性能优势。这种效率由专有架构实现,该架构内置 MMU,通过八个内存页寄存器(MPRs)将寻址范围扩展至 2 MB,并配合五个专用块传输指令以实现快速数据复制。标准机型仅配备 8 KB 工作 RAM,但通过 CD-ROM2 扩展板可有效弥补光盘读取的高延迟问题。开发者利用诸如在卡带中应用银行切换技术(上限为 1 MB)、以及 SET、ST0-ST2(用于直接 VDC 写入)和 BSR 等新指令集等独特功能,巧妙地规避了这些限制,从而有效地连接了卡带与光学媒体的时代,实现了智能内存管理与专用指令集的有机结合。
本文
PC エミュレータ(通称:ターボグラフィックス 16)の製作に取り組み、この新たなシステムを調べていると、そのハードウェア面での面白さに改めて気づかされました。当初は 1987 年に発売され、3 世代機(NES やセガ・マスターシステム)と 4 世代機(メガドライブ/ジェネシスやSNES)の間にある種々の不均衡な位置に置かれているようにも見えるのですが、グラフィック性能が NES とマスターシステムを大幅に上回っている点から、概して後者側に分類されています。日本国内ではそれなりに売れましたが、北米市場ではジェネシスやスーパーファミコンに凌ぐことはできず、ヨーロッパでは公式にはリリースされませんでした。歴史的重要性としては、CD-ROM2 周辺機器を採用し、これが初めて CD ベースのゲームを対応させたゲーム機となった点にあります。
本稿は、PC エンジンの CPU に関する概観に焦点を当てたものです。この CPU は当時の物としては非常に高速である一方で、直ちに対抗していた他機種より指令セットの機能が限定されている点において、特に興味深いと感じています。
ターボ強化された 6502 の実態
北米版で「ターボグラフィックス-"16"」と呼ばれるこのコンソールですが、実は 16 ビットの CPU を搭載しているわけではありません。これは 68000 のように「16 ビットか 32 ビットかの議論がある」といったレベルではなく、PC エンジン CPU に 16 ビットの要素は一切含まれていません。レジスタは 8 ビット、ALU(論理演算ユニット)も 8 ビット、データバスも 8 ビットです。しかしながら、これを補うために単なる「生粋の速度」で勝負しています。
CPU は、ハドソン社によって独自に設計されたパッケージである HuC6280 の一部分を構成しており、その中には CPU コアに加え、PSG サウンドチップやハードウェアタイマーなど他のハードウェアも含まれています。CPU 本体自体は、WDC(Western Design Center)による名古しい 6502 8 ビット CPU を強化した 65C02 に強く基づいています。指令セットは、NES(6502 から BCD モードを除く)や SNES(65C816:WDC の 65C02 の 16 ビット拡張版)を扱った経験のある方々には極めて親しみ深いものでありながら、HuC6280 に固有の追加指令も複数設けられています。
HuC6280 は、65C02 の新指令の大半と新しいアドレスモード(ゼロページ間接指定)、さらに 6502 の怪しい面々の一部に対する修正を受け継いでいます。例えば、
jmp ($xxFF) インストラクションがジャンプ先アドレスを読み取る際に、直感的でない挙動で同じ 256 バイトのページ内をラップしてしまうような現象も是正されています。また、6502 に見られる奇妙な無効指令による挙動も一切なく、PC エンジン CPU は 22 の未使用コードを持ちますが、それらはすべて単純な NOP として動作しており、一部の 6502 の無効コードが実施する冗長なマルチバイトの NOP を並べるような面白い仕組にはなっていません。
CPU は「低速」(約 1.79 MHz、NES と同等)または「高速」(約 7.16 MHz)という 2 つのクロック周波数のいずれかを選択して動作します。CPU は常に低速から起動し、ゲーム側は CSL(Clock Speed Low/時速低め?あるいは Change Speed Low?)および CSH(Clock Speed High)という CPU 指令を用いて両者の間を切り替えることができます。私が出会う限り、7.16 MHz の高速モードで動作させることに何かマイナス要因はありませんでしたので、ゲーム側ではほぼ即座に CSH 指令を実行し、その後は CPU を高速モードのまま残しておくのが一般的です。
7.16 MHz は 80 年代後半~90 年代初頭の 6502 ベース CPU にとっては非常に高速です。これは SNES の CPU の倍速であり、さらに言えば PC エンジンの CPU は SNES の CPU が抱えるようなメモリー遅延をほとんど受けていません。ビデオプロセッサポートへのアクセス時には待機サイクルが発生しますが、それ以外の領域では ROM や RAM のいずれも 7.16 MHz の 1 クロック周期で応答可能です。実際のところ、これは(16 ビット値に対する数学演算や論理処理を行う必要がない限り)PC エンジン CPU が SNES CPU よりも通常は倍以上の速度で動作することを意味します。
純粋な絶対速度において言えば、PC エンジンはジェネシスのメインである 7.67 MHz の 68000 CPU と比較しても有利に立っていますが、これはより複雑な対比となります。68000 は 1 クロックあたり処理する仕事量は 6502 ベースの CPU よりも少ないものの、それを取り返すように多数の汎用性の高い 32 ビットレジスタ(データレジスタとアドレスレジスタの分断による「ほぼ」汎用という qualifier)やはるかに強力な指令セットを備えています。どちらの CPU が優れたパフォーマンスを発揮するかは、コードの内容とその書き方にも依存します。
サイクルカウントの観点からは、多くの HuC6280 の指令が同等の 6502 または 65816 の指令よりも 1~2 サイクル長い点に留意すべきです。例えば、絶対指定による ADC 指令は 6502 では 4 サイクル但对して HuC6280 では 5 サイクル必要となります。また、6502 においてページ境界越え時に潜在的なペナルティサイクルが生じる指令(例えば絶対指定インデックス付き)については、HuC6280 は実際にはページ境界を超えてもそのペナルティサイクルを常に取ることのように見えます。これは高速クロックモードを支えることを容易にするためなのか、あるいは異なる指令間で回路を共用することでコストを抑えるためなのか、いずれにせよ推測に過ぎません。
メモリ管理
HuC6280上で動作するソフトウェアは、6502 のソフトウェアと同じく 16 ビットのメモリアドレスを使用しますが、HuC6280 は内蔵された MMU(メモリ管理ユニット)により物理アドレス空間を 16 ビットから 21 ビット(2 MB のアドレス範囲)まで拡張しています。MMUとしては極めて単純な機構です:16 ビットの論理アドレス空間を 8 つの 8 KB ページに分割し、各ページに対して独自の 8 ビットの MPR(Memory Page Register)を割り当てて、それを特定の 8 KB の物理ページへ直接マッピングします。これは NES やゲームボーイやマスターシステムやゲームギアのカートリッジでよく見られるメモリバンキングマッパーと非常に類似していますが、この場合マッパーハードウェアは CPU に統合されているため、ゲームカートリッジ自体がマッパーハードウェアを含める必要はありません(ただし ROM サイズが異常に大きいため、1 つのゲームだけは例外として独自のマッパーを内蔵した事例もあります)。
| MPR | 論理アドレス範囲 |
|---|---|
| MPR0 | $0000-$1FFF |
| MPR1 | $2000-$3FFF |
| MPR2 | $4000-$5FFF |
| MPR3 | $6000-$7FFF |
| MPR4 | $8000-$9FFF |
| MPR5 | $A000-$BFFF |
| MPR6 | $C000-$DFFF |
| MPR7 | $E000-$FFFF |
アドレス翻訳は以下のように単純です:
# 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 ページ($FF)、ページ 1($2000-$3FFF)を動作 RAM ページ($F8)、ページ 7($E000-$FFFF)をカートリッジ ROM の最初の 8 KB(ページ $00)へマッピングし、中間のページ 2〜6 を必要に応じて自由に再配置するというスタイルが見られます。ゲームは CPU 指令 TAMi(Transfer Accumulator to MPRi)と TMAi(Transfer MPRi to Accumulator)を用いて、8 つの MPR と対話し制御できます。
HuC6280 のゼロページおよびハードウェアスタックは $2000-$21FF に位置しており、6502 上の $0000-$01FF と異なっています。これはおそらく、$0000-$1FFF ページをメモリマップド I/O に専一に使用する予定だったからだと推測されます。はい、「ゼロページ」という名称が $0000 にない場合は少し不気味に聞こえますが、ハドソン社は 6502 と同じ用語を使う方がより有益だろうと考えたようです。対照的に、65816 ではゼロページのリロケーションが可能になり、WDC はこれを「ディレクト ページ」と改名しました。
コンソールの物理メモリーマップは、SNES の複雑な構造に比べて非常にシンプルです:
| ページ範囲 | 説明 |
|---|---|
| $00-$7F | HuCard ゲームカートリッジまたは CD-ROM2 システムカード |
| $80-$F7 | 拡張領域(CD-ROM2 アドオンにより追加 RAM として使用) |
| $F8 | 8 KB ワーキング RAM |
| $F9-$FB | 拡張領域(SuperGrafx により追加 RAM として使用) |
| $FF | メモリーマップド I/O レジスタとポート |
この構成はゲームカードのサイズを 1 MB に制限していますが、『ストリートファイター II』はこの制限を取り回して、巨大な 2.5 MB の ROM にアクセスするためのバンクスイッチングマッパーを実装しています(少なくとも PCE 標準では非常に大きな容量です)。『スーパーストリートファイター II』のジェネシス版も、カートリッジ内でバンクスイッチングマッパーを持つ公式ライセンスされたジェネシスゲームとして唯一のものであり、これはカプコンが自社の旗艦アーケードゲームの移植版を、異常に大きな ROM 量で出荷する際にカスタムハードウェアを利用することに同意していたからだと推測されます。
8 KB のワーキング RAM は決して多くはありません!NECES の 2 KB よりもはるかに多いですが、オリジナルのゲームボーイやセガ・マスターシステム/ゲームギアーと同レベルであり、ジェネシスの 64 KB や SNES の 128 KB に比べれば遥かに少なめです。CD-ROM2 アドオンの各種改修版では、ディスクからの読み取りが極めて高遅延であるため、コードやアセットを実際に使用前に RAM へ事前にロードする必要があります(HuCard ベースのゲームはいつでもカートリッジから直接コードを読み出して実行できる点と対比)。音源チップのバッファ用 RAM を含めずとも、追加 RAM が 64 KB から 2 MB まで増設されていますが、CD-ROM2 ゲームにとっては 64 KB でもあまり多くないと言えます。
新しいトリック
新規指令の内で特に目立つのは、5 つのブロック転送指令:TAI、TDD、TIA、TII、TIN です。これらはいずれも同じ機能「一つのメモリアドレス範囲から別のメモリアドレス範囲へ一括コピー」を実行しますが、ソースアドレスと宛先アドレスに対するアドリブ(アドレスステップ)がそれぞれ異なります。これは大雑把に言えば 65816 の MVN と MVP(Move Memory Negative/Positive)指令に類似していますが、SNES と異なり PC エンジンにはカートリッジ ROM や CPU ワーキング RAM にアクセスできるハードウェア DMA ユニットが存在しないため、これらの指令は PC エンジン上では実際的に有用です。
| 指令 | ソースステップ | 宛先ステップ |
|---|---|---|
| TAI | 交互(Alternate) | インクリメント |
| TDD | デクリメント | デクリメント |
| TIA | インクリメント | 交互(Alternate) |
| TII | インクリメント | インクリメント |
| TIN | インクリメント | なし(固定) |
HuC6280 ブロック転送指令の説明
「交互」はコピーされる各バイトごとに、+1 と -1 の間で交互にアドレスを変更するため、ビデオプロセッサの 16 ビットデータポートへの大量読み書きなどにとって非常に有用です。
これらの指令は、1 バイトあたり 6 サイクルのコピーレートで動作し、さらに各指令実行につき 17 サイクルの上乗せオーバーヘッドが必要です。データ量が十分大きければオーバーヘッドは無視でき、1 バイト 6 サイクルとは爆速とは言えませんが、ソフトウェアによるコピーより遥かに高速です。専用のハードウェア DMA ユニットの方がおそらくさらに高速ですが、PC エンジン上ではこれらが VRAM やワーキング RAM への大量データ転送に対して最も現実的な手段のように見えます。
留意すべき点として、ブロック転送の最中は CPU が割り込みに応答できないという点があります。1 つのブロック転送指令で最大 64 KB をコピーでき、7.16 MHz モードではフレーム当たり約 120,000 サイクルしかないので、十分に大きなブロック転送を実行すると複数の VBlank 割り込みを飛んでしまうことが容易に起こります。画面遷移中など画面が空白になる期間であれば問題ありませんが、通常プレイ中はマルチフレームの転送を実行することは望ましくないと考えられます。
他には、「SET T(T フラグを設定する)」という新規指令も注目すべきです。これは次の直ちに実行される指令のみに対して新しい T フラグを設定しますが、ADC、AND、EOR、ORA の直前にこの指令を執行すると、それらの演算はアキュムレータではなくゼロページ上の値、すなわち ZeroPage[X] を対象に行われます。これにより、アキュムレータを経由せずにゼロページ上の値を操作することが可能になります。
さらに一部の新型指令は PC エンジンに特有の高専属性を持っています:ST0、ST1、ST2 はそれぞれ即座のオペランドを VDC(Video Display Controller)の 3 つのポートの一つへ直接書き出します。これらは各種 VDC レジスタを更新するコードをわずかに高速化するのに有用です。
その他にも、新しい BSR(Branch to Subroutine/サブルーチンへの分岐)指令があります。これは JSR(Jump to Subroutine)とほぼ同じ動作しますが、オペランドが絶対アドレスではなく PC 相対変位であることを特徴としています。BSR を用いたコードはどのメモリーバンクにマッピングされていても影響を受けず、一方 JSR を用いるコードは特定の 8 KB の論理ページにマッピングされていることを前提としている点で、これが BSR の主な利点だと考えられます。
TST(Test Bits/ビットテスト)は 65C02 の TRB と TSB(Test and Reset/Set Bits)に似ていますが、メモリー値を変更せず、指定されたビットのみを検査する点が異なります。また、新しい Swap 指令 SAX、SAY、SXY が用意され、2 つのレジスタ間の値交換を行うことができます。さらに、CLA、CLX、CLY という新しいクリア指令があり、これらは CPU フラグを変更せずにレジスターを素早くゼロ化するために使用されます。
終わり?
この後も PC エンジンハードウェアの他の部分についての続編を投稿したいと考えていますが、その是非はまた後日にしましょう。ビデオハードウェアについては、ジェネシスや SNES のビデオハードウェアとの対比が興味深いと感じているため、少なくともそれに関する投稿を書く予定があります。
参考資料
- HuC6280 CPU: http://shu.emuunlim.com/download/pcedocs/pce_cpu.html
- HuC6280 オペコード行列: https://www.chrismcovell.com/PCEdev/HuC6280_opcodes.html
- TurboGrafx-16 ハードウェアノート: https://www.romhacking.net/documents/302/
- PC Engine / TurboGrafx-16 アーキテクチャ: https://www.copetti.org/writings/consoles/pc-engine/