アタリショック。
この言葉が有名になったのは
任天堂社長の山内博の「サードパーティによる低品質ゲームソフト(俗に言う「クソゲー」)の乱発がアタリの市場崩壊を招いた」と言う言葉によるものだろう。
しかしこれらの舞台となった、アタリ社のゲーム機Atari VCSに関しては、ほんどの日本人は馴染みがないものだ。ファミコン世代と称されるように、我々日本人の家庭用ゲームの体験は任天堂のファミリーコンピュータによってはじまったのであり、それ以前の紀元前の部分はピンとこないと思う。
そこで、理解を深めるためにVCSとはいったい何者であったかについて、すこし吟じてみようかと思う。またそのことによってVCS自身に潜在していた崩壊への要因について探ってみよう。
#Atari VCS
Atari VCS
米国アタリ社が開発した家庭用ゲーム機。
それ以前のプログラム固定方式のゲーム機と異なるロムカートリッジによってゲームソフトを供給するゲーム機として1977年に『Video Computer System』の名で発売され『Atari VCS』の通称で親しまれた。
#Atari VCSの問題点
1977年に登場したAtari VCSは扱える内部リソースに乏しく、表現力に制限があった。
特にRAMが少なく128byteしかない。
これはグラフィックに使えるメモリーもプログラミングに使えるメモリーも少ないということを示している。例えばフレームバッファどころか、キャラクターバッファも無く文字テキストデータを扱うことができないため情報量が多いゲームを扱うのは難しかった。
プログラミングに使えるメモリーとしても、これはあまりに少なく複雑なゲームを作る制限となったと思われる。
しかし、メモリー量の少なさがVCSのコストダウンにつながり、長く市場で生き残る結果ともなった。
1977年には世界で初めてのパーソナルコンピュータといえるApple IIが登場している。
Atari VCSとApple IIは同系の650XCPUを採用している。
Apple IIの設計者Stephen Wozniakは、一時期にSteven Jobsに要請されてアタリの業務用ゲームBreak Outのコストダウン(部品削減による)をおこなったことが有名で。本人もその設計に影響をうけたといっている。だがVCSの設計者Jay MinerとStephen Wozniakに接点があったという話は無く、CPU以外の点では、この2つのハードウエアは対照的である。48kbyteものRAMに加え、8つの拡張スロットを持つApple IIの扱えるリソースは大きく、このことが成功へと導き製品寿命も延ばした。
VCS、Apple II共に650XCPUの貢献は大きい。
VCSは画面表示の制御をタイミングにあわせてCPUが直接おこなう必要があり、6507の高速さと簡便さが大きく役立った。画面表示のために命令実行時間をサイクル単位で数えて調整してやらなければならなかったのだ。
Apple IIは6502がCPUサイクルの後半にしかメモリーにアクセスしないのを利用して、残り半分を画面表示に割り振ることができた(VRAMのマッピングを工夫して、このときに同時にDRAMのリフレッシュも済ましてしまっていた)。このことによりフレームバッファの表示がCPUの動作に干渉することがなくCPUは与えられたクロックのままの速度で動作することができた。またフロッピーディスクドライブの制御に関して、大幅にコストダウンをおこないソフトウエア制御化したが、やはり6502の高速さと前述のCPUの動作が邪魔されないことに助けられ実行時間を正確に数えることにより制御をおこなった。
6502はコストの安さと、シンプルさ扱いやすさによって以降、民生の世界では大きな成功を収め続けた。こうした6502のシンプルさに触発された設計されたのが、Acorn社のARMプロセッサだった。初期のARMは特にプロセッサクロックの設計について6502の影響が直接的であった。ARMが特にモバイルの分野では支配的需要を持っていることを考えると、6502の影響はテクノロジーの先端においても依然健在だと考えることもできるだろう。
VCSの設計者達はVCSが短命に終わると考えていて、後継ハードウエアの準備をおこなっていた。ハードウエアは1979年11月には早くもAtari 400、Atari 800というPCとして登場した。これはVCSとはうってかわって大量の内部リソースを持ち。Apple II同様48kbyteもの最大RAMを持つことができた。
1982年11月にはvcsの後継であるAtari 400、Atari 800と同じ性能を持ったAtari 5200(PC部門と家庭用機部門の対立もあって、あえて互換性を無くしたといわれている)が登場したが、コストの高さとクリスマスシーズンに需要を満たすだけの生産をおこなえなかったことなどからvcsほどの成功はおさめられなかった。
1985年NESが上陸するとアタリはvcsとの互換性を持ちRAMが少ない設計に戻したAtari 7800を1986年に出すが失敗。1987年にはAtari 400、Atari 800と互換性を持つAtari XEGSを出すなどアタリ社の方針は迷走している。
アタリショックの起きた1983年から1985年にかけての北米家庭用ゲーム市場の裏側では、
家庭用PC市場でC64、800XL、TI-99/4による争いが繰り広げられていて、ゲーム市場の崩壊にはその影響も大きいといわれている。アタリ社は、ゲーム機とPCの双方での戦いを余儀なくされていて、その双方で敗北してしまったのだ。
アタリショックには勝者はいなかったが、家庭用PC市場ではコモドール社が生き残ることができた。
#Atari VCSのSpec
VCSのSpecを示す。
CPU | 6507 |
RAM | 128byte |
ROM | 4KbyteMAX |
CPU clock | 1.19Mhz |
Graphics clock | 3.58Mhz |
Slot | ROMonly |
ROMはカートリッジで供給され最大4kbyte、RAMはわずか128byte。
さすがにこれではあまりに非力であったために、後期ではカートリッジ内部にバンク切り替えでアクセスできるROMと256byteまでのRAMを拡張してあるものがある。
カートリッジメモリーの空間はアドレスF000以降に限定されて、拡張されたハードウエアもここに重複して置くしかなかったようである。
#####・カートリッジメモリ(4kbytes area)
Addr | Assy Name | Bits Used | Function |
---|---|---|---|
F000-FFFF | ROM | xxxx xxxx | Cartridge ROM (4 Kbytes max) |
F000-F07F | RAMW | xxxx xxxx | Cartridge RAM Write (optional 128 bytes) |
F000-F0FF | RAMW | xxxx xxxx | Cartridge RAM Write (optional 256 bytes) |
F080-F0FF | RAMR | xxxx xxxx | Cartridge RAM Read (optional 128 bytes) |
F100-F1FF | RAMR | xxxx xxxx | Cartridge RAM Read (optional 256 bytes) |
003F | BANK | ---- --xx | Cart Bank Switching (for some 8K ROMs, 4x2K) |
FFF4-FFFB | BANK | ---- ---- | Cart Bank Switching (for ROMs greater 4K) |
FFFC-FFFD | ENTRY | xxxx xxxx | Cart Entrypoint (16bit pointer) |
FFFE-FFFF | BREAK | xxxx xxxx | Cart Breakpoint (16bit pointer) |
このあたりは、ファミコンやスーパーファミコンでも後期になるとカートリッジ側に追加ハードウエアを載せて拡張したものがあったのと同様の状況だろう。
#VCSを構成するLSI
VCSは3つのLSIで構成されている。
####・VCSのメモリーマップ
Addr | Function |
---|---|
0000-002C | TIA Write |
0000-000D | TIA Read (sometimes mirrored at 0030-003D) |
0080-00FF | PIA RAM (128 bytes) |
0280-0297 | PIA Ports and Timer |
F000-FFFF | Cartridge Memory (4 Kbytes area) |
CPUは、MOSテクノロジーの6507。これは6502と同じシリーズのLSIで28ピンのパッケージ(6502は40ピン)にコストダウンされているかわりにアドレス空間が8kbyteで割り込み入力もなかった。VCSはこのうち4kbyteをROMカートリッジに割り当てて、残りをRAMと周辺チップのために使っている。動作周波数は1.19Mhz(NTSC色副搬送波周波数3.58Mhzの3分の1)。
外部との入出力には6532というPIAを使っている。このLSIは8bitポート2本、8bitカウンタ1本と128byteのRAMを搭載している。8bitポートはVCSではジョイスティックの読み出しに使われる。
そしてTIA(Television Interface Adaptor、ステラチップ)。VCSの中核をなすLSIで画像出力と音声出力を受け持っている。
####・PIA 6532 - RAM, IO,タイマー(Read/Write)
Addr | Assy Name | Bits Used | Function |
---|---|---|---|
80..FF | RAM | xxxx xxxx | 128 bytes RAM (in PIA chip) for variables and stack |
0280 | SWCHA | xxxx xxxx | Port A; input or output (read or write) |
0281 | SWACNT | xxxx xxxx | Port A DDR, 0= input, 1=output |
0282 | SWCHB | xxxx xxxx | Port B; console switches (read only) |
0283 | SWBCNT | xxxx xxxx | Port B DDR (hardwired as input) |
0284 | INTIM | xxxx xxxx | Timer output (read only) |
0285 | INSTAT | xx-- ---- | Timer Status (read only, undocumented) |
0294 | TIM1T | xxxx xxxx | set 1 clock interval (838 nsec/interval) |
0295 | TIM8T | xxxx xxxx | set 8 clock interval (6.7 usec/interval) |
0296 | TIM64T | xxxx xxxx | set 64 clock interval (53.6 usec/interval) |
0297 | T1024T | xxxx xxxx | set 1024 clock interval (858.2 usec/interval) |
#TIA
TIAは44byteのコントロールレジスタと13byteのステータスレジスタを持つ。
表示能力としては、背景画面と5個のスプライトを表示することができる。背景は横40pixel、スプライトは160pixelの解像度を持つ。
このLSIはCRTコントローラやVDP(ビデオディスプレイプロセッサ)、GPUなどは大きく異なる設計思想を持つ。
TIAはフレームバッファを持っていない。
例えば多くのパソコンは最低でも1画面ぶんのビットマップ画像を格納するメモリーを持っているし、ファミコンやスーファミのような家庭用ゲーム機でもキャラクタジェネレータやスプライトの働きによってCRTに出力する1枚ぶんの絵の情報は管理することができる。しかしTIAは、画面1枚(1フレーム)の絵を持つことができない。持てるのはその中の横1ラインぶんの要素だけである(つまりラインバッファ)。
どうやって1画面を表示するかというと、ブラウン管において画面を電子ビームがスキャンしていく(走査していくことによって描画されるラインなので走査線という)のにあわせてCPUが、この1ラインぶんのデータを書き換えて次々表示させてやる(加えて、垂直同期信号も自動では発生させることができないのでCPUが発生させてやる)、TIAはフレームバッファを持ったLSIのように画面出力のためにメモリー読み出しを自分でおこない画像を出力するようなことができない。TIA(テレビジョン・インターフェース・アダプタ)の名前のとおりCPUとTVのインターフェースの役目をしてくれるだけなのだ。
VCSは、このようにデオディスプレイの表示の流れがわからなければプログラミングをおこなえない。
走査線は左上から始まって、横に向かって1ラインを描画する。右端に至ると再度描画を始めるために一旦左端に戻らなければならない、この期間を水平ブランク期間という。
画面下端にまできた走査線は画面の上端へと戻る。この期間を垂直ブランク期間という。
また画面下端には実際には表示されていない、オーバースキャン期間が存在している。
####・画面の構造
このような走査線の移動を計算することにより、VCSはいまどこに映像が表示されているかを知ることができ、その瞬間に映像を表示したり書き換えたりすることによって、最終的な画面を構築していくのである。
CPUのほとんどの処理能力は画像書き換え(画面作成)に使われることになる。CPUは3.58Mhzの映像出力クロックを1/3に分周した1.19Mhzで動作している。これはCPUが1ラインあたり76クロックの処理時間を持てるということを示す。しかしこれだけでは水平走査とタイミングをあわせることが難しいので、TIAはコントロールレジスタ02(WSYNC)に書き込みをすると水平帰線期間までCPUを止めて(wait for leading edge of horizontal blank)くれるようになっている。コントロールレジスタ02にCPUが書き込みをした次の命令では画面走査線は常に画面の左端にいるということになる。
####・TIAのレジスタ群
TIAのコントロールレジスタ(Write only)
Addr | Assy Name | Bits Used | Function |
---|---|---|---|
00 | VSYNC | 0000 00x0 | vertical sync set-clear |
01 | VBLANK | xx00 00x0 | vertical blank set-clear |
02 | WSYNC | ---- ---- | wait for leading edge of horizontal blank |
03 | RSYNC | ---- ---- | reset horizontal sync counter |
04 | NUSIZ0 | 00xx 0xxx | number-size player-missile 0 |
05 | NUSIZ1 | 00xx 0xxx | number-size player-missile 1 |
06 | COLUP0 | xxxx xxx0 | color-lum player 0 and missile 0 |
07 | COLUP1 | xxxx xxx0 | color-lum player 1 and missile 1 |
08 | COLUPF | xxxx xxx0 | color-lum playfield and ball |
09 | COLUBK | xxxx xxx0 | color-lum background |
0A | CTRLPF | 00xx 0xxx | control playfield ball size & collisions |
0B | REFP0 | 0000 x000 | reflect player 0 |
0C | REFP1 | 0000 x000 | reflect player 1 |
0D | PF0 | xxxx 0000 | playfield register byte 0 |
0E | PF1 | xxxx xxxx | playfield register byte 1 |
0F | PF2 | xxxx xxxx | playfield register byte 2 |
10 | RESP0 | ---- ---- | reset player 0 |
11 | RESP1 | ---- ---- | reset player 1 |
12 | RESM0 | ---- ---- | reset missile 0 |
13 | RESM1 | ---- ---- | reset missile 1 |
14 | RESBL | ---- ---- | reset ball |
15 | AUDC0 | 0000 xxxx | audio control 0 |
16 | AUDC1 | 0000 xxxx | audio control 1 |
17 | AUDF0 | 000x xxxx | audio frequency 0 |
18 | AUDF1 | 000x xxxx | audio frequency 1 |
19 | AUDV0 | 0000 xxxx | audio volume 0 |
1A | AUDV1 | 0000 xxxx | audio volume 1 |
1B | GRP0 | xxxx xxxx | graphics player 0 |
1C | GRP1 | xxxx xxxx | graphics player 1 |
1D | ENAM0 | 0000 00x0 | graphics (enable) missile 0 |
1E | ENAM1 | 0000 00x0 | graphics (enable) missile 1 |
1F | ENABL | 0000 00x0 | graphics (enable) ball |
20 | HMP0 | xxxx 0000 | horizontal motion player 0 |
21 | HMP1 | xxxx 0000 | horizontal motion player 1 |
22 | HMM0 | xxxx 0000 | horizontal motion missile 0 |
23 | HMM1 | xxxx 0000 | horizontal motion missile 1 |
24 | HMBL | xxxx 0000 | horizontal motion ball |
25 | VDELP0 | 0000 000x | vertical delay player 0 |
26 | VDELP1 | 0000 000x | vertical delay player 1 |
27 | VDELBL | 0000 000x | vertical delay ball |
28 | RESMP0 | 0000 00x0 | reset missile 0 to player 0 |
29 | RESMP1 | 0000 00x0 | reset missile 1 to player 1 |
2A | HMOVE | ---- ---- | apply horizontal motion |
2B | HMCLR | ---- ---- | clear horizontal motion registers |
2C | CXCLR | ---- ---- | clear collision latches |
TIAのステータスレジスタ(Read only) |
Addr | Assy Name | Bits Used | Function |
---|---|---|---|
30 | CXM0P | xx00 0000 | read collision M0-P1, M0-P0 (Bit 7,6) |
31 | CXM1P | xx00 0000 | read collision M1-P0, M1-P1 |
32 | CXP0FB | xx00 0000 | read collision P0-PF, P0-BL |
33 | CXP1FB | xx00 0000 | read collision P1-PF, P1-BL |
34 | CXM0FB | xx00 0000 | read collision M0-PF, M0-BL |
35 | CXM1FB | xx00 0000 | read collision M1-PF, M1-BL |
36 | CXBLPF | x000 0000 | read collision BL-PF, unused |
37 | CXPPMM | xx00 0000 | read collision P0-P1, M0-M1 |
38 | INPT0 | x000 0000 | read pot port |
39 | INPT1 | x000 0000 | read pot port |
3A | INPT2 | x000 0000 | read pot port |
3B | INPT3 | x000 0000 | read pot port |
3C | INPT4 | x000 0000 | read input |
3D | INPT5 | x000 0000 | read input |
#TIAの画面出力
VCSでは背景のことをPlayField(PF)と呼ぶ。
横方向に40pixelの解像度を持つが、実際に設定できるのはそのうち20pixelぶんだけである(コントロールレジスタ0D、0E、0FのPF0、PF1、PF2に設定してやる)。このデータを、そのまま、または左右反転して横方向に2回表示するのである(コントロールレジスタ0A、CTRLPFのbit0で設定)。
5個のスプライトは機能によってplayer0(P0)、player1(P1)、missile0(M0)、missile1(M1)、ball(BL)と呼ばれている。名前のとおりplayer0、1はゲーム中でプレイヤーが操作する自機を表示する。2つあるのは2Pで対戦するためだ。missile0、1は自機から発射される玉をあらわす。ballはテニス、サッカーなどの球技の球そのものである。1個しか無いのは、たとえ対戦であったとしてもそれはゲーム中に1個しか存在しないからだ。
M0、M1、BLは1、2、4、8pixelの横線(コントロールレジスタ04、05、0AのNUSIZ0、NUSIZ1、CTRLPF、bit4、5で制御)で画面に出力できる(そのまま何もしないと縦方向に伸びていき縦長の帯になる)だけだが、P0、P1は8pixelのパターンを(コントロールレジスタ1B、1CのGRP0、GRP1)を左右反転つき(コントロールレジスタ1B、1CのREFP0、REFP1、bit3で制御)で表示できるうえに、表示個数や大きさの設定(コントロールレジスタ04、05のNUSIZ0、NUSIZ1のbit0、1、2で制御)も可能である。
####・3bitのデータによるスプライト表示の制御
問題なのは表示位置である。
TIAは後のハードウエアに搭載されているスプライトのように、画面中での表示位置を縦、横の座標で指定するような機能がない。ディスプレイの画面表示がその場所まできたタイミングでもって表示を指示することによって、その場所に絵を出すのである(TIAのコントロールレジスタのどこにも、そういった座標を指示するフィールドが無いのがわかるだろう)。
縦方向については走査線を数えていって表示位置になったら表示を開始してやればいい。そして縦方向のサイズに達したら表示を終了する。
しかし横方向に関しても似たような仕組みをとっているために、タイミング的にはシビアになってしまった。
TIAにおいてスプライトの水平表示位置はCPUがスプライトリセットレジスタ(コントロールレジスタ10、11、12、13、14のRESP0、RESP1、RESM0、RESM1、RESBL)に書き込みをした瞬間にディスプレイ走査線が画面中のどの水平位置を描画するかによって決定するのだ。
VCSのCPU、650Xの最低実行サイクルは2サイクル。
sta WSYNC
nop ; 2 cycles
nop ; +2
sta RESP0
のように位置を調整することができる。
Graphics clockは3.58Mhzで160pixelを表示する。6507はその1/3の1.19Mhzで駆動されているから、最大横方向に53pixelの解像度で調整が可能である。
2クロックで実行されるnopの数だけで調整するなら、横26pixel程度の調整しかできないことになってしまう。
また、これではレジスタ内の値を元に任意の位置に、スプライトを移動させるようなことは不可能である。
xレジスタの値を元に表示位置を変えるようなループを作るとすれば。
SimpleLoop dex ; 2
bne SimpleLoop ; 3 (2)
sta RESP0
のように、最低5クロックのループになるから、横方向には26pixel程度の解像度しか持たないことになる。
TIAはこうした表示位置をさらに微調整可能な機能を持っている。
表示位置を、7〜-8pixel左方向に調整する機能で、横移動レジスタ(コントロールレジスタ20、21、22、23、24のHMP0、HMP1、HMM0、HMM0、HMBL)の上位4bitに2の補数として書き込んで実行レジスタ(コントロールレジスタ2AのHMOVE)に書きこみをおこなえば移動がおこなわれる。
これを利用すれば最終的には、横方向160pixel精度での表示は可能である。
以下にコード例を示す。
; xレジスタにスプライト番号
; aレジスタに表示位置
PositionASpriteSubroutine
sta HMCLR ; 横移動レジスタクリア
sec
sta WSYNC ; begin line 1
DivideLoop
sbc #15
bcs DivideLoop ;+4/5 15pixel単位での大雑把な移動のためのウエイトループ
eor #7 ;+2
asl
asl
asl
asl ;+8 残ったpixelを微調整レジスタに設定できる値に加工
sta HMP0,X ;+5 ここで大雑把な移動をおこなう、余分な消費クロックはHBlank時間として消費
sta RESP0,X ;+4 微調整pixelを設定
sta WSYNC ;+3 begin line 2
sta HMOVE ;+3 微調整の実行
こうして獲得した横位置だが、player、missileでは共有可能になっている。位置共有設定レジスタ(コントロールレジスタ28、29のRESMP0、RESMP1L)、bit1をセットすればP0とM0、P1とM1の横位置は同じ値にロックされる。
例えばインベーダーゲームのような画面では、発射された玉が自機と同じ横位置を保ち続けることも多いということから追加された機能だろう。
#TIAの衝突判定
このようにメモリーが少なく、処理の多くが画面表示のために使用されてしまうマシンにおいて、衝突判定機能というのは相対的に大きな意味を持つのではないだろうか?
TIAのステータスレジスタの多くは衝突判定のために使われている。
MSXなどに使われているTMS9918やファミコンなどにも衝突判定機能はあったが、機能に制限が多く、結局ソフトウエアで判定してしまうことがほとんどだった1。
TIAではPF、P0、P1、M0、M1、BLの全画面表示オブジェクト間の衝突を判定できるので、機能としては十分である。
#TIAの色表示
TIAは表示物を4つに分類して単色の色をつけることが可能である。
つまり表示物は、背景色の上に1色で描かれる。
####表示物と色設定
色設定レジスタ | 表示物 |
---|---|
COLUP0 | player0(P0) and missile0(M0) |
COLUP1 | player1(P1) and missile1(M1) |
COLUPF | playfield(PF) and ball(BL) |
COLUBK | background(BG) |
色は上位4bitの色パレットと下位3bitの明るさで決まる。 | |
VCSは単色しか使えないが、走査線ごとに色を操作することによって水平ラインごと色設定を変えることも可能である。これを利用してキャラクターを複数の色で描画することも不可能ではない。これは1ライン上の表示物が少ないならば容易だが、複数のオブジェクトがある場合は処理が煩雑化することは避けられない。 | |
また下位の明るさのみを変化させるようにして、縦方向にグラデーションがかかっているような表現もおこなうことができ、VCS以降のアタリ系のハードウエアに特徴的な表現となっている。52色から選択可能なファミコンなどと比較して、明るさを別に持っているVCSはこの点に関しては有利だといえるだろう。 |
#TIAのサウンド
TIAの機能の中でもっとも時代を感じさせるのがサウンド機能だろう。
4音同時発生だが(トーン2音、ノイズ2音)。トーンは32khzの基本波を1〜32分周できるだけだし、ノイズもいくつかのパターンと分周から選択できるだけである。これでは曲のようなものは演奏できないだろう。まさしくポン、とかピッといったブロック崩しやインベーダーなどでおなじみな単調な音しか作り出せない。
-
ファミコンでは「走査線の垂直位置を調べる」という、衝突とは別の用途のために使われたりした。衝突判定機能のは走査線のスキャンによって、画面上のその位置に映像が発生した瞬間に結果が出る。スプライトを画面の途中のBGと衝突する場所に置いておいて、衝突判定フラグが立った瞬間に走査線がそこを通ったと判断するのである。この方法によって任天堂の「F1レース」は画面下部のうねるようにスクロールするエリアと、画面上部の背景を判別して分割していた。画面下部ではVCSのスプライト表示位置決定と同様に、プログラム実行クロック数を数えることによって、走査線の水平スキャンと同期して画面の水平ラインごとにスクロール量を変化させてうねらせていた。この処理のためにはCPUの処理能力を占有ししまうために、ゲーム本来の処理をおこなうために、スクロール処理の開始ポジションまで走査線がきたことを正確に知る必要があった(任天堂の社長であった故岩田聡氏は、このゲームのプログラマーの一人だった)。ちなみにMSX、MSX2でも同様に走査線の位置を特定するために衝突判定を使った例がある。 ↩