Missile Command's display has some unusual characteristics. The display width is a friendly 256 pixels wide, but the height is an odd 231 lines (4:3.61 ratio). It uses a typical indexed color scheme, with an 8-entry palette. Most of the screen has 2 bits per pixel and so can only show 4 at a time, but the last 32 lines of the screen support 3 bits per pixel, so all colors can be shown. The colors in the palette can be changed on the fly, providing low-cost color-cycling animation for highlights and explosions.
While many other contemporary games used sprites or vector generators or other hardware tricks to speed up rendering, Missile Command just plots pixels into a frame buffer. It does, however, have one trick up its sleeve.
Most instructions that access RAM will see the 2bpp pixel buffer from $0640
to $3fff, with the 3rd bit stored from $0200 to $05ff. When an instruction
whose opcode has the bit pattern %---00001
takes more than
5 cycles, however, the hardware asserts the MADSEL (Multiplexed Address
Select) signal. This remaps the memory access so that addresses from $1900
to $ffff provide a linear view into the frame buffer, with one byte per pixel
(%XY0-----
for 2-bit areas, %XYZ-----
for 3-bit areas).
To trigger MADSEL accesses, the code will use indexed indirect addressing
mode instructions, i.e. "LDA (zp,X)
" and "STA (zp,X)
".
These have opcodes $81/$a1, and always take 6 cycles on the 6502. Almost
all rendering in the game is done with MADSEL mode, the only exceptions
being clearing parts of the screen and scrolling the text at the bottom
while in attract mode.
MADSEL is slower for bulk writes, like erasing the screen, because it changes one pixel per byte rather than 4 or 8. When setting individual pixels it's faster, because the address calculation is simpler and it isn't necessary to read the existing pixels and mask away the bits that don't change before setting the new value.
In the normal (non-MADSEL) view of video memory, the 2bpp RAM area stores
4 pixels per byte, split across the high and low nibbles. If the bits in
the first byte are %ABCDEFGH
, then the color index for the pixel
at X=0 would be %00000DH0
, X=1 is %00000CG0
, etc.
This means the 2bpp areas use the even-numbered entries in the palette.
The 3rd pixel bit is a bit more convoluted. There are 8 pixels per
byte, but the memory map puts the first 16 lines in the even-numbered
bytes, and the last 16 lines in the odd-numbered bytes. Within a byte
%ABCDEFGH
, X=0 would use bit H, X=1 would use bit G, etc.
The value is combined with the 2bpp data as the low bit, allowing use of
the odd-numbered palette entries.
The color palette is 3-bit RGB. The values have the form
%----RGB-
, where 0=on and 1=off. This yields:
RGB | Hex | Color |
---|---|---|
%000 | $00 | White |
%001 | $02 | Yellow |
%010 | $04 | Magenta |
%011 | $06 | Red |
%100 | $08 | Cyan |
%101 | $0a | Green |
%110 | $0c | Blue |
%111 | $0e | Black |
Missile Command is available in a "cocktail" cabinet, for which players sit on opposite sides during a 2-player game. This requires rotating the screen 180 degrees, which can be done by flipping the graphics vertically and horizontally. The vertical flip must be done in hardware because of the 3bpp color zone, but the horizontal flip is done in software. (Not everything is flipped; for example, the city backdrop will be mirror-imaged for cocktail player 2.)
While playing the game, most elements appear in one of four colors:
one for the background, two for "stable" foreground elements, and one
that is updated on every frame. (The palette entry is simply incremented.
Because colors are xxxxRGBx
, it changes visibly every other
frame, so it's actually cycling at ~30Hz.) The set of palette entries
that cycle is determined by a bit mask (stored at $ec).
The colors in the palette are determined by the wave number. On the first wave, the background is black, the incoming missile trails are red, and player missile trails are blue. On the 3rd wave, the incoming missile trails are green instead.
The color assignments used for wave 1 are:
# | Val | Color | Uses |
---|---|---|---|
0 | $0e | Black | Background, scrolling text foreground |
1 | $02 | Yellow | City / launcher backdrop, scrolling text background |
2 | $06 | Red | ICBM trails, smart bombs, scores |
3 | $08 | Cyan | City color #1 |
4 | $++ | (cycle) | ABM target 'X', missile heads, explosions |
5 | $++ | (cycle) | (unused) |
6 | $0c | Blue | (unused) |
7 | $0c | Blue | ABM trails, Crosshairs, 1UP arrow, ABM ammo icons, city color #2 |
The list of colors used for each wave can be found on the wave guide page.
The code that draws explosions is careful to preserve the 3rd bit. This prevents explosions from eroding the city backdrop. Because the cities are drawn with odd-numbered palette entries, explosions that cover a city briefly obscure it (because palette entries 4 and 5 are cycling at the same rate), then replace the city colors (3/7) with the background color (0/1). This leaves the city outline in the city-backdrop color, creating the sense of wreckage rather than an explosion-shaped hole. The code that draws the missile head and trail does not do this, so you'll see a small divot in the city outline where the missile hit.
The code that erases the crosshairs will restore the previous contents, unless it appears that something drew over the crosshairs (based on the pixel color). This is why you can move the crosshairs over things, like the text at the start of the wave or the flashing target 'X', without erasing it from the screen.
This table shows the location in memory where line N starts. The "Addr" column shows the MADSEL-mode address, while "Bit2" and "Bit3" show the address in "normal" address decoding mode.
The software treats line 0 as being at the bottom of the screen. This makes the calculation of the base address trivial. For the 256-bytes-per-line mode, you simply take the line number, exclusive-OR it with $ff, and use that as the high byte of the pointer (the bottom line starts at $ff00). For the 64-bytes-per-line mode, you set up the pointer the same way, then divide by 4 (the bottom line starts at $3fc0). Accessing the 3rd pixel bit in "normal" memory is more complicated, but that's done infrequently, with the address hard-coded into the instructions.
Line | Addr | Bit2 | Bit3 | Line | Addr | Bit2 | Bit3 | |
---|---|---|---|---|---|---|---|---|
230 | $1900 | $0640 | 114 | $8d00 | $2340 | |||
229 | $1a00 | $0680 | 113 | $8e00 | $2380 | |||
228 | $1b00 | $06c0 | 112 | $8f00 | $23c0 | |||
227 | $1c00 | $0700 | 111 | $9000 | $2400 | |||
226 | $1d00 | $0740 | 110 | $9100 | $2440 | |||
225 | $1e00 | $0780 | 109 | $9200 | $2480 | |||
224 | $1f00 | $07c0 | 108 | $9300 | $24c0 | |||
223 | $2000 | $0800 | 107 | $9400 | $2500 | |||
222 | $2100 | $0840 | 106 | $9500 | $2540 | |||
221 | $2200 | $0880 | 105 | $9600 | $2580 | |||
220 | $2300 | $08c0 | 104 | $9700 | $25c0 | |||
219 | $2400 | $0900 | 103 | $9800 | $2600 | |||
218 | $2500 | $0940 | 102 | $9900 | $2640 | |||
217 | $2600 | $0980 | 101 | $9a00 | $2680 | |||
216 | $2700 | $09c0 | 100 | $9b00 | $26c0 | |||
215 | $2800 | $0a00 | 99 | $9c00 | $2700 | |||
214 | $2900 | $0a40 | 98 | $9d00 | $2740 | |||
213 | $2a00 | $0a80 | 97 | $9e00 | $2780 | |||
212 | $2b00 | $0ac0 | 96 | $9f00 | $27c0 | |||
211 | $2c00 | $0b00 | 95 | $a000 | $2800 | |||
210 | $2d00 | $0b40 | 94 | $a100 | $2840 | |||
209 | $2e00 | $0b80 | 93 | $a200 | $2880 | |||
208 | $2f00 | $0bc0 | 92 | $a300 | $28c0 | |||
207 | $3000 | $0c00 | 91 | $a400 | $2900 | |||
206 | $3100 | $0c40 | 90 | $a500 | $2940 | |||
205 | $3200 | $0c80 | 89 | $a600 | $2980 | |||
204 | $3300 | $0cc0 | 88 | $a700 | $29c0 | |||
203 | $3400 | $0d00 | 87 | $a800 | $2a00 | |||
202 | $3500 | $0d40 | 86 | $a900 | $2a40 | |||
201 | $3600 | $0d80 | 85 | $aa00 | $2a80 | |||
200 | $3700 | $0dc0 | 84 | $ab00 | $2ac0 | |||
199 | $3800 | $0e00 | 83 | $ac00 | $2b00 | |||
198 | $3900 | $0e40 | 82 | $ad00 | $2b40 | |||
197 | $3a00 | $0e80 | 81 | $ae00 | $2b80 | |||
196 | $3b00 | $0ec0 | 80 | $af00 | $2bc0 | |||
195 | $3c00 | $0f00 | 79 | $b000 | $2c00 | |||
194 | $3d00 | $0f40 | 78 | $b100 | $2c40 | |||
193 | $3e00 | $0f80 | 77 | $b200 | $2c80 | |||
192 | $3f00 | $0fc0 | 76 | $b300 | $2cc0 | |||
191 | $4000 | $1000 | 75 | $b400 | $2d00 | |||
190 | $4100 | $1040 | 74 | $b500 | $2d40 | |||
189 | $4200 | $1080 | 73 | $b600 | $2d80 | |||
188 | $4300 | $10c0 | 72 | $b700 | $2dc0 | |||
187 | $4400 | $1100 | 71 | $b800 | $2e00 | |||
186 | $4500 | $1140 | 70 | $b900 | $2e40 | |||
185 | $4600 | $1180 | 69 | $ba00 | $2e80 | |||
184 | $4700 | $11c0 | 68 | $bb00 | $2ec0 | |||
183 | $4800 | $1200 | 67 | $bc00 | $2f00 | |||
182 | $4900 | $1240 | 66 | $bd00 | $2f40 | |||
181 | $4a00 | $1280 | 65 | $be00 | $2f80 | |||
180 | $4b00 | $12c0 | 64 | $bf00 | $2fc0 | |||
179 | $4c00 | $1300 | 63 | $c000 | $3000 | |||
178 | $4d00 | $1340 | 62 | $c100 | $3040 | |||
177 | $4e00 | $1380 | 61 | $c200 | $3080 | |||
176 | $4f00 | $13c0 | 60 | $c300 | $30c0 | |||
175 | $5000 | $1400 | 59 | $c400 | $3100 | |||
174 | $5100 | $1440 | 58 | $c500 | $3140 | |||
173 | $5200 | $1480 | 57 | $c600 | $3180 | |||
172 | $5300 | $14c0 | 56 | $c700 | $31c0 | |||
171 | $5400 | $1500 | 55 | $c800 | $3200 | |||
170 | $5500 | $1540 | 54 | $c900 | $3240 | |||
169 | $5600 | $1580 | 53 | $ca00 | $3280 | |||
168 | $5700 | $15c0 | 52 | $cb00 | $32c0 | |||
167 | $5800 | $1600 | 51 | $cc00 | $3300 | |||
166 | $5900 | $1640 | 50 | $cd00 | $3340 | |||
165 | $5a00 | $1680 | 49 | $ce00 | $3380 | |||
164 | $5b00 | $16c0 | 48 | $cf00 | $33c0 | |||
163 | $5c00 | $1700 | 47 | $d000 | $3400 | |||
162 | $5d00 | $1740 | 46 | $d100 | $3440 | |||
161 | $5e00 | $1780 | 45 | $d200 | $3480 | |||
160 | $5f00 | $17c0 | 44 | $d300 | $34c0 | |||
159 | $6000 | $1800 | 43 | $d400 | $3500 | |||
158 | $6100 | $1840 | 42 | $d500 | $3540 | |||
157 | $6200 | $1880 | 41 | $d600 | $3580 | |||
156 | $6300 | $18c0 | 40 | $d700 | $35c0 | |||
155 | $6400 | $1900 | 39 | $d800 | $3600 | |||
154 | $6500 | $1940 | 38 | $d900 | $3640 | |||
153 | $6600 | $1980 | 37 | $da00 | $3680 | |||
152 | $6700 | $19c0 | 36 | $db00 | $36c0 | |||
151 | $6800 | $1a00 | 35 | $dc00 | $3700 | |||
150 | $6900 | $1a40 | 34 | $dd00 | $3740 | |||
149 | $6a00 | $1a80 | 33 | $de00 | $3780 | |||
148 | $6b00 | $1ac0 | 32 | $df00 | $37c0 | |||
147 | $6c00 | $1b00 | 31 | $e000 | $3800 | $0200 | ||
146 | $6d00 | $1b40 | 30 | $e100 | $3840 | $0240 | ||
145 | $6e00 | $1b80 | 29 | $e200 | $3880 | $0280 | ||
144 | $6f00 | $1bc0 | 28 | $e300 | $38c0 | $02c0 | ||
143 | $7000 | $1c00 | 27 | $e400 | $3900 | $0300 | ||
142 | $7100 | $1c40 | 26 | $e500 | $3940 | $0340 | ||
141 | $7200 | $1c80 | 25 | $e600 | $3980 | $0380 | ||
140 | $7300 | $1cc0 | 24 | $e700 | $39c0 | $03c0 | ||
139 | $7400 | $1d00 | 23 | $e800 | $3a00 | $0400 | ||
138 | $7500 | $1d40 | 22 | $e900 | $3a40 | $0440 | ||
137 | $7600 | $1d80 | 21 | $ea00 | $3a80 | $0480 | ||
136 | $7700 | $1dc0 | 20 | $eb00 | $3ac0 | $04c0 | ||
135 | $7800 | $1e00 | 19 | $ec00 | $3b00 | $0500 | ||
134 | $7900 | $1e40 | 18 | $ed00 | $3b40 | $0540 | ||
133 | $7a00 | $1e80 | 17 | $ee00 | $3b80 | $0580 | ||
132 | $7b00 | $1ec0 | 16 | $ef00 | $3bc0 | $05c0 | ||
131 | $7c00 | $1f00 | 15 | $f000 | $3c00 | $0201 | ||
130 | $7d00 | $1f40 | 14 | $f100 | $3c40 | $0241 | ||
129 | $7e00 | $1f80 | 13 | $f200 | $3c80 | $0281 | ||
128 | $7f00 | $1fc0 | 12 | $f300 | $3cc0 | $02c1 | ||
127 | $8000 | $2000 | 11 | $f400 | $3d00 | $0301 | ||
126 | $8100 | $2040 | 10 | $f500 | $3d40 | $0341 | ||
125 | $8200 | $2080 | 9 | $f600 | $3d80 | $0381 | ||
124 | $8300 | $20c0 | 8 | $f700 | $3dc0 | $03c1 | ||
123 | $8400 | $2100 | 7 | $f800 | $3e00 | $0401 | ||
122 | $8500 | $2140 | 6 | $f900 | $3e40 | $0441 | ||
121 | $8600 | $2180 | 5 | $fa00 | $3e80 | $0481 | ||
120 | $8700 | $21c0 | 4 | $fb00 | $3ec0 | $04c1 | ||
119 | $8800 | $2200 | 3 | $fc00 | $3f00 | $0501 | ||
118 | $8900 | $2240 | 2 | $fd00 | $3f40 | $0541 | ||
117 | $8a00 | $2280 | 1 | $fe00 | $3f80 | $0581 | ||
116 | $8b00 | $22c0 | 0 | $ff00 | $3fc0 | $05c1 | ||
115 | $8c00 | $2300 |
Copyright 2021 by Andy McFadden