Codetapper's Amiga Site

Jim Power

Jim Power in Mutant Planet is an Amiga game with some amazing sprite tricks. Even the title screen heavily uses sprites. First, the title screen with sprites enabled on the left, and disabled on the right:

Title screen with sprites enabled.

Title screen with sprites disabled.

If sprites are disabled, the background colour almost disappears completely!

As you can probably tell from the full title screen, the background repeats regularly, every 32 pixels. This gives away the fact that the game is repeating 2 sprites across the screen. The same trick is used in the game, so let's see what it looks like:

Game screen with sprites enabled.

Game screen with sprites disabled.

Analysing the game shows that has been designed around the Amiga's hardware limitations and a game put around it. Sprites are used as follows:

  • Sprites 0 and 1 (attached) are used for the main character and the status bar under the game
  • Sprites 2 and 3 (attached) are the right side of the main character
  • Sprites 4 and 5 are the player bullets (hence a limitation of 2 bullets on screen at once)
  • Sprites 6 and 7 are 4 colour sprites repeated across the screen for the background and the top status bar

As you can probably imagine, the copperlist is a biggie! In the screenshot above, you can see where there are horizontal bands of solid colour. The game carefully fades the sprite patterns into these colours allowing the game to change the rest of the screen palette during these areas.

The main game area part of the copperlist starts by waiting for line $3e, then sets one of the sprite colours, loads the sprite data, and positions sprite 6 at horizontal position $40 (64 in decimal). 64 is the far left of the normal viewable screen. It then positions sprite 7 to $48 (80 in decimal), exactly 16 pixels to the right of sprite 6. During that time, sprite 6 is moved 16 pixels to the right, followed by sprite 7. This effect renders the sprites across the entire screen:

0000872c: 3e21 fffe       ;Wait for vpos >= 0x3e and hpos >= 0x20.
00008730: 01ba 0035       ;COLOR29 := 0x0035
00008734: 0174 0155       ;SPR6DATA := 0x0155
00008738: 0176 0000       ;SPR6DATB := 0x0000
0000873c: 017c ffa8       ;SPR7DATA := 0xffa8
00008740: 017e 0000       ;SPR7DATB := 0x0000
00008744: 0170 0040       ;SPR6POS := 0x0040
00008748: 0178 0048       ;SPR7POS := 0x0048
0000874c: 0170 0050       ;SPR6POS := 0x0050
00008750: 0178 0058       ;SPR7POS := 0x0058
00008754: 0170 0060       ;SPR6POS := 0x0060
00008758: 0178 0068       ;SPR7POS := 0x0068
0000875c: 0170 0070       ;SPR6POS := 0x0070
00008760: 0178 0078       ;SPR7POS := 0x0078
00008764: 0170 0080       ;SPR6POS := 0x0080
00008768: 0178 0088       ;SPR7POS := 0x0088
0000876c: 0170 0090       ;SPR6POS := 0x0090
00008770: 0178 0098       ;SPR7POS := 0x0098
00008774: 0170 00a0       ;SPR6POS := 0x00a0
00008778: 0178 00a8       ;SPR7POS := 0x00a8
0000877c: 0170 00b0       ;SPR6POS := 0x00b0
00008780: 0178 00b8       ;SPR7POS := 0x00b8
00008784: 0170 00c0       ;SPR6POS := 0x00c0
00008788: 0178 00c8       ;SPR7POS := 0x00c8
0000878c: 0170 00d0       ;SPR6POS := 0x00d0
00008790: 0178 00d8       ;SPR7POS := 0x00d8

The next line doesn't use any sprites, and simply sets a colour register, before the next 2 lines do the repeated sprite trick again:

00008794: 3f21 fffe       ;Wait for vpos >= 0x3f and hpos >= 0x20.
00008798: 019e 0035       ;COLOR15 := 0x0035

0000879c: 4021 fffe       ;Wait for vpos >= 0x40 and hpos >= 0x20.
000087a0: 01bc 0267       ;COLOR30 := 0x0267
000087a4: 0174 ffe8       ;SPR6DATA := 0xffe8
000087a8: 0176 0017       ;SPR6DATB := 0x0017
000087ac: 017c 55ff       ;SPR7DATA := 0x55ff
000087b0: 017e aa00       ;SPR7DATB := 0xaa00
000087b4: 0170 0040       ;SPR6POS := 0x0040
000087b8: 0178 0048       ;SPR7POS := 0x0048
000087bc: 0170 0050       ;SPR6POS := 0x0050
000087c0: 0178 0058       ;SPR7POS := 0x0058
000087c4: 0170 0060       ;SPR6POS := 0x0060
000087c8: 0178 0068       ;SPR7POS := 0x0068
000087cc: 0170 0070       ;SPR6POS := 0x0070
000087d0: 0178 0078       ;SPR7POS := 0x0078
000087d4: 0170 0080       ;SPR6POS := 0x0080
000087d8: 0178 0088       ;SPR7POS := 0x0088
000087dc: 0170 0090       ;SPR6POS := 0x0090
000087e0: 0178 0098       ;SPR7POS := 0x0098
000087e4: 0170 00a0       ;SPR6POS := 0x00a0
000087e8: 0178 00a8       ;SPR7POS := 0x00a8
000087ec: 0170 00b0       ;SPR6POS := 0x00b0
000087f0: 0178 00b8       ;SPR7POS := 0x00b8
000087f4: 0170 00c0       ;SPR6POS := 0x00c0
000087f8: 0178 00c8       ;SPR7POS := 0x00c8
000087fc: 0170 00d0       ;SPR6POS := 0x00d0
00008800: 0178 00d8       ;SPR7POS := 0x00d8

00008804: 411d fffe       ;Wait for vpos >= 0x41 and hpos >= 0x1c.
00008808: 01ba 0267       ;COLOR29 := 0x0267
0000880c: 01bc 0035       ;COLOR30 := 0x0035
00008810: 0174 abff       ;SPR6DATA := 0xabff
00008814: 0176 5400       ;SPR6DATB := 0x5400
00008818: 017c feaf       ;SPR7DATA := 0xfeaf
0000881c: 017e 0150       ;SPR7DATB := 0x0150
00008820: 0170 0040       ;SPR6POS := 0x0040
00008824: 0178 0048       ;SPR7POS := 0x0048
00008828: 0170 0050       ;SPR6POS := 0x0050
0000882c: 0178 0058       ;SPR7POS := 0x0058
00008830: 0170 0060       ;SPR6POS := 0x0060
00008834: 0178 0068       ;SPR7POS := 0x0068
00008838: 0170 0070       ;SPR6POS := 0x0070
0000883c: 0178 0078       ;SPR7POS := 0x0078
00008840: 0170 0080       ;SPR6POS := 0x0080
00008844: 0178 0088       ;SPR7POS := 0x0088
00008848: 0170 0090       ;SPR6POS := 0x0090
0000884c: 0178 0098       ;SPR7POS := 0x0098
00008850: 0170 00a0       ;SPR6POS := 0x00a0
00008854: 0178 00a8       ;SPR7POS := 0x00a8
00008858: 0170 00b0       ;SPR6POS := 0x00b0
0000885c: 0178 00b8       ;SPR7POS := 0x00b8
00008860: 0170 00c0       ;SPR6POS := 0x00c0
00008864: 0178 00c8       ;SPR7POS := 0x00c8
00008868: 0170 00d0       ;SPR6POS := 0x00d0
0000886c: 0178 00d8       ;SPR7POS := 0x00d8

00008870: 4221 fffe       ;Wait for vpos >= 0x42 and hpos >= 0x20.
00008874: 019e 0267       ;COLOR15 := 0x0267

00008878: 4321 fffe       ;Wait for vpos >= 0x43 and hpos >= 0x20.
0000887c: 01bc 04aa       ;COLOR30 := 0x04aa
00008880: 0174 f42b       ;SPR6DATA := 0xf42b
00008884: 0176 0bd4       ;SPR6DATB := 0x0bd4
00008888: 017c aa8b       ;SPR7DATA := 0xaa8b
0000888c: 017e 5574       ;SPR7DATB := 0x5574
00008890: 0170 0040       ;SPR6POS := 0x0040
00008894: 0178 0048       ;SPR7POS := 0x0048
00008898: 0170 0050       ;SPR6POS := 0x0050
0000889c: 0178 0058       ;SPR7POS := 0x0058
000088a0: 0170 0060       ;SPR6POS := 0x0060
000088a4: 0178 0068       ;SPR7POS := 0x0068
000088a8: 0170 0070       ;SPR6POS := 0x0070
000088ac: 0178 0078       ;SPR7POS := 0x0078
000088b0: 0170 0080       ;SPR6POS := 0x0080
000088b4: 0178 0088       ;SPR7POS := 0x0088
000088b8: 0170 0090       ;SPR6POS := 0x0090
000088bc: 0178 0098       ;SPR7POS := 0x0098
000088c0: 0170 00a0       ;SPR6POS := 0x00a0
000088c4: 0178 00a8       ;SPR7POS := 0x00a8
000088c8: 0170 00b0       ;SPR6POS := 0x00b0
000088cc: 0178 00b8       ;SPR7POS := 0x00b8
000088d0: 0170 00c0       ;SPR6POS := 0x00c0
000088d4: 0178 00c8       ;SPR7POS := 0x00c8
000088d8: 0170 00d0       ;SPR6POS := 0x00d0
000088dc: 0178 00d8       ;SPR7POS := 0x00d8

000088e0: 4421 fffe       ;Wait for vpos >= 0x44 and hpos >= 0x20.
000088e4: 019e 04aa       ;COLOR15 := 0x04aa

000088e8: 451d fffe       ;Wait for vpos >= 0x45 and hpos >= 0x1c.
000088ec: 01ba 04aa       ;COLOR29 := 0x04aa
000088f0: 01bc 07dc       ;COLOR30 := 0x07dc
000088f4: 0174 ffd5       ;SPR6DATA := 0xffd5
000088f8: 0176 002a       ;SPR6DATB := 0x002a
000088fc: 017c 7fff       ;SPR7DATA := 0x7fff
00008900: 017e 8000       ;SPR7DATB := 0x8000
00008904: 0170 0040       ;SPR6POS := 0x0040
00008908: 0178 0048       ;SPR7POS := 0x0048
0000890c: 0170 0050       ;SPR6POS := 0x0050
00008910: 0178 0058       ;SPR7POS := 0x0058
00008914: 0170 0060       ;SPR6POS := 0x0060
00008918: 0178 0068       ;SPR7POS := 0x0068
0000891c: 0170 0070       ;SPR6POS := 0x0070
00008920: 0178 0078       ;SPR7POS := 0x0078
00008924: 0170 0080       ;SPR6POS := 0x0080
00008928: 0178 0088       ;SPR7POS := 0x0088
0000892c: 0170 0090       ;SPR6POS := 0x0090
00008930: 0178 0098       ;SPR7POS := 0x0098
00008934: 0170 00a0       ;SPR6POS := 0x00a0
00008938: 0178 00a8       ;SPR7POS := 0x00a8
0000893c: 0170 00b0       ;SPR6POS := 0x00b0
00008940: 0178 00b8       ;SPR7POS := 0x00b8
00008944: 0170 00c0       ;SPR6POS := 0x00c0
00008948: 0178 00c8       ;SPR7POS := 0x00c8
0000894c: 0170 00d0       ;SPR6POS := 0x00d0
00008950: 0178 00d8       ;SPR7POS := 0x00d8

00008954: 4625 fffe       ;Wait for vpos >= 0x46 and hpos >= 0x24.
00008958: 0174 d500       ;SPR6DATA := 0xd500
0000895c: 0176 2aff       ;SPR6DATB := 0x2aff
00008960: 017c 0abf       ;SPR7DATA := 0x0abf
00008964: 017e f540       ;SPR7DATB := 0xf540
00008968: 0170 0040       ;SPR6POS := 0x0040
0000896c: 0178 0048       ;SPR7POS := 0x0048
00008970: 0170 0050       ;SPR6POS := 0x0050
00008974: 0178 0058       ;SPR7POS := 0x0058
00008978: 0170 0060       ;SPR6POS := 0x0060
0000897c: 0178 0068       ;SPR7POS := 0x0068
00008980: 0170 0070       ;SPR6POS := 0x0070
00008984: 0178 0078       ;SPR7POS := 0x0078
00008988: 0170 0080       ;SPR6POS := 0x0080
0000898c: 0178 0088       ;SPR7POS := 0x0088
00008990: 0170 0090       ;SPR6POS := 0x0090
00008994: 0178 0098       ;SPR7POS := 0x0098
00008998: 0170 00a0       ;SPR6POS := 0x00a0
0000899c: 0178 00a8       ;SPR7POS := 0x00a8
000089a0: 0170 00b0       ;SPR6POS := 0x00b0
000089a4: 0178 00b8       ;SPR7POS := 0x00b8
000089a8: 0170 00c0       ;SPR6POS := 0x00c0
000089ac: 0178 00c8       ;SPR7POS := 0x00c8
000089b0: 0170 00d0       ;SPR6POS := 0x00d0
000089b4: 0178 00d8       ;SPR7POS := 0x00d8

000089b8: 4721 fffe       ;Wait for vpos >= 0x47 and hpos >= 0x20.
000089bc: 019e 07dc       ;COLOR15 := 0x07dc

000089c0: 481d fffe       ;Wait for vpos >= 0x48 and hpos >= 0x1c.
000089c4: 01ba 07dc       ;COLOR29 := 0x07dc
000089c8: 01bc 0bff       ;COLOR30 := 0x0bff
000089cc: 0174 fafe       ;SPR6DATA := 0xfafe
000089d0: 0176 0501       ;SPR6DATB := 0x0501
000089d4: 017c ab7f       ;SPR7DATA := 0xab7f
000089d8: 017e 5480       ;SPR7DATB := 0x5480
000089dc: 0170 0040       ;SPR6POS := 0x0040
000089e0: 0178 0048       ;SPR7POS := 0x0048
000089e4: 0170 0050       ;SPR6POS := 0x0050
000089e8: 0178 0058       ;SPR7POS := 0x0058
000089ec: 0170 0060       ;SPR6POS := 0x0060
000089f0: 0178 0068       ;SPR7POS := 0x0068
000089f4: 0170 0070       ;SPR6POS := 0x0070
000089f8: 0178 0078       ;SPR7POS := 0x0078
000089fc: 0170 0080       ;SPR6POS := 0x0080
00008a00: 0178 0088       ;SPR7POS := 0x0088
00008a04: 0170 0090       ;SPR6POS := 0x0090
00008a08: 0178 0098       ;SPR7POS := 0x0098
00008a0c: 0170 00a0       ;SPR6POS := 0x00a0
00008a10: 0178 00a8       ;SPR7POS := 0x00a8
00008a14: 0170 00b0       ;SPR6POS := 0x00b0
00008a18: 0178 00b8       ;SPR7POS := 0x00b8
00008a1c: 0170 00c0       ;SPR6POS := 0x00c0
00008a20: 0178 00c8       ;SPR7POS := 0x00c8
00008a24: 0170 00d0       ;SPR6POS := 0x00d0
00008a28: 0178 00d8       ;SPR7POS := 0x00d8

00008a2c: 4925 fffe       ;Wait for vpos >= 0x49 and hpos >= 0x24.
00008a30: 0174 a055       ;SPR6DATA := 0xa055
00008a34: 0176 5faa       ;SPR6DATB := 0x5faa
00008a38: 017c 00af       ;SPR7DATA := 0x00af
00008a3c: 017e ff50       ;SPR7DATB := 0xff50
00008a40: 0170 0040       ;SPR6POS := 0x0040
00008a44: 0178 0048       ;SPR7POS := 0x0048
00008a48: 0170 0050       ;SPR6POS := 0x0050
00008a4c: 0178 0058       ;SPR7POS := 0x0058
00008a50: 0170 0060       ;SPR6POS := 0x0060
00008a54: 0178 0068       ;SPR7POS := 0x0068
00008a58: 0170 0070       ;SPR6POS := 0x0070
00008a5c: 0178 0078       ;SPR7POS := 0x0078
00008a60: 0170 0080       ;SPR6POS := 0x0080
00008a64: 0178 0088       ;SPR7POS := 0x0088
00008a68: 0170 0090       ;SPR6POS := 0x0090
00008a6c: 0178 0098       ;SPR7POS := 0x0098
00008a70: 0170 00a0       ;SPR6POS := 0x00a0
00008a74: 0178 00a8       ;SPR7POS := 0x00a8
00008a78: 0170 00b0       ;SPR6POS := 0x00b0
00008a7c: 0178 00b8       ;SPR7POS := 0x00b8
00008a80: 0170 00c0       ;SPR6POS := 0x00c0
00008a84: 0178 00c8       ;SPR7POS := 0x00c8
00008a88: 0170 00d0       ;SPR6POS := 0x00d0
00008a8c: 0178 00d8       ;SPR7POS := 0x00d8

00008a90: 4a21 fffe       ;Wait for vpos >= 0x4a and hpos >= 0x20.
00008a94: 019e 0bff       ;COLOR15 := 0x0bff

00008a98: 4c1d fffe       ;Wait for vpos >= 0x4c and hpos >= 0x1c.
00008a9c: 01ba 0bff       ;COLOR29 := 0x0bff
00008aa0: 01bc 0fff       ;COLOR30 := 0x0fff
00008aa4: 0174 faaf       ;SPR6DATA := 0xfaaf
00008aa8: 0176 0550       ;SPR6DATB := 0x0550
00008aac: 017c 6abf       ;SPR7DATA := 0x6abf
00008ab0: 017e 9540       ;SPR7DATB := 0x9540
00008ab4: 0170 0040       ;SPR6POS := 0x0040
00008ab8: 0178 0048       ;SPR7POS := 0x0048
00008abc: 0170 0050       ;SPR6POS := 0x0050
00008ac0: 0178 0058       ;SPR7POS := 0x0058
00008ac4: 0170 0060       ;SPR6POS := 0x0060
00008ac8: 0178 0068       ;SPR7POS := 0x0068
00008acc: 0170 0070       ;SPR6POS := 0x0070
00008ad0: 0178 0078       ;SPR7POS := 0x0078
00008ad4: 0170 0080       ;SPR6POS := 0x0080
00008ad8: 0178 0088       ;SPR7POS := 0x0088
00008adc: 0170 0090       ;SPR6POS := 0x0090
00008ae0: 0178 0098       ;SPR7POS := 0x0098
00008ae4: 0170 00a0       ;SPR6POS := 0x00a0
00008ae8: 0178 00a8       ;SPR7POS := 0x00a8
00008aec: 0170 00b0       ;SPR6POS := 0x00b0
00008af0: 0178 00b8       ;SPR7POS := 0x00b8
00008af4: 0170 00c0       ;SPR6POS := 0x00c0
00008af8: 0178 00c8       ;SPR7POS := 0x00c8
00008afc: 0170 00d0       ;SPR6POS := 0x00d0
00008b00: 0178 00d8       ;SPR7POS := 0x00d8

00008b04: 4d1d fffe       ;Wait for vpos >= 0x4d and hpos >= 0x1c.
00008b08: 01ba 0fff       ;COLOR29 := 0x0fff
00008b0c: 01bc 0bff       ;COLOR30 := 0x0bff
00008b10: 0174 afff       ;SPR6DATA := 0xafff
00008b14: 0176 5000       ;SPR6DATB := 0x5000
00008b18: 017c ffaa       ;SPR7DATA := 0xffaa
00008b1c: 017e 0055       ;SPR7DATB := 0x0055
00008b20: 0170 0040       ;SPR6POS := 0x0040
00008b24: 0178 0048       ;SPR7POS := 0x0048
00008b28: 0170 0050       ;SPR6POS := 0x0050
00008b2c: 0178 0058       ;SPR7POS := 0x0058
00008b30: 0170 0060       ;SPR6POS := 0x0060
00008b34: 0178 0068       ;SPR7POS := 0x0068
00008b38: 0170 0070       ;SPR6POS := 0x0070
00008b3c: 0178 0078       ;SPR7POS := 0x0078
00008b40: 0170 0080       ;SPR6POS := 0x0080
00008b44: 0178 0088       ;SPR7POS := 0x0088
00008b48: 0170 0090       ;SPR6POS := 0x0090
00008b4c: 0178 0098       ;SPR7POS := 0x0098
00008b50: 0170 00a0       ;SPR6POS := 0x00a0
00008b54: 0178 00a8       ;SPR7POS := 0x00a8
00008b58: 0170 00b0       ;SPR6POS := 0x00b0
00008b5c: 0178 00b8       ;SPR7POS := 0x00b8
00008b60: 0170 00c0       ;SPR6POS := 0x00c0
00008b64: 0178 00c8       ;SPR7POS := 0x00c8
00008b68: 0170 00d0       ;SPR6POS := 0x00d0
00008b6c: 0178 00d8       ;SPR7POS := 0x00d8

00008b70: 4e21 fffe       ;Wait for vpos >= 0x4e and hpos >= 0x20.
00008b74: 019e 0fff       ;COLOR15 := 0x0fff

00008b78: 5e21 fffe       ;Wait for vpos >= 0x5e and hpos >= 0x20.
00008b7c: 01bc 0ffb       ;COLOR30 := 0x0ffb
00008b80: 0174 fa87       ;SPR6DATA := 0xfa87
00008b84: 0176 0578       ;SPR6DATB := 0x0578
00008b88: 017c fe6f       ;SPR7DATA := 0xfe6f
00008b8c: 017e 0190       ;SPR7DATB := 0x0190

... and continues on ...

Due to heavy use of repeating sprites, the game imposes certain limitations on itself:

  • Because the player sprite maybe located on the same line as the repeated sprites, it is not possible to re-use any of the sprites other than the background sprites 6-7. There is simply no DMA time left for the copper!
  • This restricts the player to having 2 bullets rendered as sprites.
  • The player is drawn with 2 sprites, so can never be more than 32 pixels wide (including gun).
  • All enemy objects and platforms in the game must be drawn with the blitter.

These are by no means a problem, and no doubt the programmer was aware of these limitations and designed the game around them.

It is quite interesting to see exactly where the sprites are by disabling only sprite 7. The black background shines through wherever sprite 7 should be:

Game screen with sprites 0-6 enabled (sprite 7 disabled)

Post your comment

Comments

  • Gravatar for Scoose

    Simply very impressive. I can imagine the headache during coding.

    Scoose 14/05/2017 8:34pm (5 months ago)

  • Gravatar for alex76gr

    Pure digital magic!

    alex76gr 03/04/2015 6:53am (3 years ago)

  • Gravatar for Moose Malloy

    Please keep teaching! I love learning things like this.

    Moose Malloy 05/05/2014 3:16pm (3 years ago)

RSS feed for comments on this page | RSS feed for all comments

Popular Sprite Tricks

Jim Power

Risky Woods

The 16-colour background layer was created by using all 8 hardware sprites and repositioning them across the screen. The same 64 pixel wide graphics are repeated across the entire play area.

Jim Power

Shadow of the Beast

One of the first jaw-droppingly beautiful Amiga games that still looks great today. Sprites were heavily re-used on the screen, along with priority changes to make them appear between the playfields.

Jim Power

Stardust

The amazing tunnel sequence was created with a 6 frame animation sequence made with only 4 colours and mirrored vertically. The asteroid layer sits on top of this, with a status panel and player ship made of sprites sitting on top.