Codetapper's Amiga Site

Risky Woods

Risky Woods has one of the biggest copperlists you will ever see in an Amiga game! To begin, let's see what the game looks like:

If you disable sprites, you will be amazed to see that the entire background layer has been created using sprites!

If bitplanes are disabled, you are left with the sprite playfield. The first thing that you notice is that the background pattern repeats every 64 pixels. This is a dead giveaway that it is created using 4 sixteen colour sprites and redrawing them horizontally across each line.

Because the sprites are repeated, there is no need to change the data and the copper spends all it's time repositioning the sprites.

The limitation with this technique is that by the time we reach the right hand side of the screen, all the sprites will be drawn there for the next line, so the game has to reset all 8 sprites back to the far left of the screen. This takes up almost all the available DMA time, so there is no time left to change the colour palette or do any other copper tricks. This gives the game quite a muted appearance compared with many games that have a multi-coloured background (Shadow of the Beast, Turrican etc).

For the parallax effect, whenever the player scrolls several pixels sideways, the game has to shift the sprite data in the background horizontally by 1 pixel. Rather than updating the graphics every pixel (which may well slow the game down), the game pre-buffers the sprite data. At address $1c1e4, the following graphics are found:

Risky Woods pre-buffered sprites

As you can see, there are 8 sets of the 4 attached background sprites, all offset by 2 pixels each time. At first glance, this would suggest that the game alters the sprite pointers to simulate scrolling the background graphics by 2 pixels each time. But if you play the game, you can see that the background graphics smoothly scroll along 1 pixel at a time! How is that possible?

When I first noticed this, I assumed the game was rotating the graphics as required, but the sprite graphics never change. If you scroll by 1 pixel and check the copperlist, the sprite pointers remain pointing to the same graphics.

And this is where another stunningly simple trick comes into play. If you examine the copperlist, you can see the game sets up the sprite pointers once at the top of the screen:

000798b4: 0120 0002    ;SPR0PTH := 0x0002 Setup sprites
000798b8: 0122 5da4    ;SPR0PTL := 0x5da4
000798bc: 0124 0002    ;SPR1PTH := 0x0002
000798c0: 0126 606c    ;SPR1PTL := 0x606c
000798c4: 0128 0002    ;SPR2PTH := 0x0002
000798c8: 012a 6334    ;SPR2PTL := 0x6334
000798cc: 012c 0002    ;SPR3PTH := 0x0002
000798d0: 012e 65fc    ;SPR3PTL := 0x65fc
000798d4: 0130 0002    ;SPR4PTH := 0x0002
000798d8: 0132 68c4    ;SPR4PTL := 0x68c4
000798dc: 0134 0002    ;SPR5PTH := 0x0002
000798e0: 0136 6b8c    ;SPR5PTL := 0x6b8c
000798e4: 0138 0002    ;SPR6PTH := 0x0002
000798e8: 013a 6e54    ;SPR6PTL := 0x6e54
000798ec: 013c 0002    ;SPR7PTH := 0x0002
000798f0: 013e 711c    ;SPR7PTL := 0x711c

If you examine the first sprite data at address $25da4, you will see the following:

00025da4: 3800 e800
0002606c: 3800 e880

The first 2 words in the sprite data correspond to SPRxPOS and SPRxCTL respectively. When the game scrolls by a single pixel, the game updates the SPRxCTL values from $e800 to $e801 (sprite 0), and $e880 to $e881 (sprite 1), and that shifts the sprites across by a single pixel.

SPRxCTL

To see how this works, have a look at the bits for the SPRxCTL registers:

  • Bits 15-8 specify vertical stop position for a sprite image, bits V7 - V0.
  • Bit 7 is the attach bit, used to bolt 2 sprites together.
  • Bits 6-3 are reserved for future use (make zero).
  • Bit 2 is bit V8 of vertical start.
  • Bit 1 is bit V8 of vertical stop.
  • Bit 0 is bit H0 of horizontal start.

The Amiga can display more than 256 pixels horizontally, which requires 9 bits to store the horizontal location. The upper 8 bits are stored in the SPRxPOS registers, but the lowest bit is stored in bit 0 of the SPRxCTL register. The game can therefore display the background graphics at any even location by storing 8 versions (indented by 2 pixels extra each time) and then by toggling that one bit, it can shift the graphics across to an odd value, accessing the remaining positions.

So you don't notice the sprites shifting by a single pixel, the game area is made smaller than the expected width of 288 pixels, and instead only displays 287 pixels across. That way you never notice one vertical strip of blank pixels appearing.

The reason the game can't alter the SPRxPOS registers is that the enormous copperlist has precalculated all the positions for the sprites positions, and altering thousands of values every frame would be too slow.

By storing 8 copies of the sprite graphics, the game only has to update 8 words in memory to shift the graphics by a pixel, and when the game scrolls the background by 2 pixels it only has to alter the sprite pointer addresses in the copperlist, 8 sprites x 2 words = 32 bytes of data!

Copperlist

And now for the amazing copperlist! The poor copper in your Amiga is having to work overtime and process 27k of screen changes every frame!

00079800: 0100 4200    ;BPLCON0 := 0x4200 Setup screen
00079804: 00e0 0000    ;BPL1PTH := 0x0000
00079808: 00e2 0402    ;BPL1PTL := 0x0402
0007980c: 00e4 0000    ;BPL2PTH := 0x0000
00079810: 00e6 2382    ;BPL2PTL := 0x2382
00079814: 00e8 0000    ;BPL3PTH := 0x0000
00079818: 00ea 4302    ;BPL3PTL := 0x4302
0007981c: 00ec 0000    ;BPL4PTH := 0x0000
00079820: 00ee 6282    ;BPL4PTL := 0x6282
00079824: 0108 0004    ;BPL1MOD := 0x0004
00079828: 010a 0004    ;BPL2MOD := 0x0004
0007982c: 0092 0038    ;DDFSTRT := 0x0038
00079830: 0094 00c8    ;DDFSTOP := 0x00c8

00079834: 0180 0000    ;COLOR00 := 0x0000 Set colour palette
00079838: 0182 0b62    ;COLOR01 := 0x0b62
0007983c: 0184 0b96    ;COLOR02 := 0x0b96
00079840: 0186 0963    ;COLOR03 := 0x0963
00079844: 0188 0741    ;COLOR04 := 0x0741
00079848: 018a 0520    ;COLOR05 := 0x0520
0007984c: 018c 0720    ;COLOR06 := 0x0720
00079850: 018e 0a20    ;COLOR07 := 0x0a20
00079854: 0190 0ca3    ;COLOR08 := 0x0ca3
00079858: 0192 0bab    ;COLOR09 := 0x0bab
0007985c: 0194 0788    ;COLOR10 := 0x0788
00079860: 0196 0565    ;COLOR11 := 0x0565
00079864: 0198 0310    ;COLOR12 := 0x0310
00079868: 019a 0433    ;COLOR13 := 0x0433
0007986c: 019c 0ddd    ;COLOR14 := 0x0ddd
00079870: 019e 0000    ;COLOR15 := 0x0000
00079874: 01a0 0000    ;COLOR16 := 0x0000
00079878: 01a2 0112    ;COLOR17 := 0x0112
0007987c: 01a4 0aab    ;COLOR18 := 0x0aab
00079880: 01a6 0889    ;COLOR19 := 0x0889
00079884: 01a8 0667    ;COLOR20 := 0x0667
00079888: 01aa 0445    ;COLOR21 := 0x0445
0007988c: 01ac 0223    ;COLOR22 := 0x0223
00079890: 01ae 02cd    ;COLOR23 := 0x02cd
00079894: 01b0 0000    ;COLOR24 := 0x0000
00079898: 01b2 0003    ;COLOR25 := 0x0003
0007989c: 01b4 0004    ;COLOR26 := 0x0004
000798a0: 01b6 0005    ;COLOR27 := 0x0005
000798a4: 01b8 0007    ;COLOR28 := 0x0007
000798a8: 01ba 0009    ;COLOR29 := 0x0009
000798ac: 01bc 000b    ;COLOR30 := 0x000b
000798b0: 01be 0334    ;COLOR31 := 0x0334

Now the game sets up the 8 sprites:

000798b4: 0120 0002    ;SPR0PTH := 0x0002 Setup sprites
000798b8: 0122 5da4    ;SPR0PTL := 0x5da4
000798bc: 0124 0002    ;SPR1PTH := 0x0002
000798c0: 0126 606c    ;SPR1PTL := 0x606c
000798c4: 0128 0002    ;SPR2PTH := 0x0002
000798c8: 012a 6334    ;SPR2PTL := 0x6334
000798cc: 012c 0002    ;SPR3PTH := 0x0002
000798d0: 012e 65fc    ;SPR3PTL := 0x65fc
000798d4: 0130 0002    ;SPR4PTH := 0x0002
000798d8: 0132 68c4    ;SPR4PTL := 0x68c4
000798dc: 0134 0002    ;SPR5PTH := 0x0002
000798e0: 0136 6b8c    ;SPR5PTL := 0x6b8c
000798e4: 0138 0002    ;SPR6PTH := 0x0002
000798e8: 013a 6e54    ;SPR6PTL := 0x6e54
000798ec: 013c 0002    ;SPR7PTH := 0x0002
000798f0: 013e 711c    ;SPR7PTL := 0x711c

And here the madness begins! For every scanline in the top part of the screen, the game positions all 8 sprites and while screen DMA occurs, it quickly moves the sprites 64 pixels to the right so they are re-displayed. Sprites are all attached to each other meaning they are 16 colour sprites. This is required as the game leaves no DMA time left to change the colour palette as it's having to reposition sprites so often!

What is effectively happening is that in the horizontal sweep of the video beam, the copper is racing ahead shifting the sprites into their next repeated position. There is only just enough DMA time to beat the video beam before it has to repeat it again for the next line. The copper gets an $18 (24) pixel headstart for shifting the sprites, as it waits for horizontal position $30, setting the first 2 sprites to appear at position $48 and then the next 2 at position $50 etc.

The closest analogy I can think of for this is in the Wallace and Gromit episode where Gromit is laying down the train track just ahead of the train that he is riding on!

000798f4: 3831 fffe    ;Wait for vpos >= 0x38 and hpos >= 0x30.
000798f8: 0140 3848    ;SPR0POS := 0x3848
000798fc: 0148 3848    ;SPR1POS := 0x3848
00079900: 0150 3850    ;SPR2POS := 0x3850
00079904: 0158 3850    ;SPR3POS := 0x3850
00079908: 0160 3858    ;SPR4POS := 0x3858
0007990c: 0168 3858    ;SPR5POS := 0x3858
00079910: 0170 3860    ;SPR6POS := 0x3860
00079914: 0178 3860    ;SPR7POS := 0x3860
00079918: 0140 3868    ;SPR0POS := 0x3868
0007991c: 0148 3868    ;SPR1POS := 0x3868
00079920: 0150 3870    ;SPR2POS := 0x3870
00079924: 0158 3870    ;SPR3POS := 0x3870
00079928: 0160 3878    ;SPR4POS := 0x3878
0007992c: 0168 3878    ;SPR5POS := 0x3878
00079930: 0170 3880    ;SPR6POS := 0x3880
00079934: 0178 3880    ;SPR7POS := 0x3880
00079938: 0140 3888    ;SPR0POS := 0x3888
0007993c: 0148 3888    ;SPR1POS := 0x3888
00079940: 0150 3890    ;SPR2POS := 0x3890
00079944: 0158 3890    ;SPR3POS := 0x3890
00079948: 0160 3898    ;SPR4POS := 0x3898
0007994c: 0168 3898    ;SPR5POS := 0x3898
00079950: 0170 38a0    ;SPR6POS := 0x38a0
00079954: 0178 38a0    ;SPR7POS := 0x38a0
00079958: 0140 38a8    ;SPR0POS := 0x38a8
0007995c: 0148 38a8    ;SPR1POS := 0x38a8
00079960: 0150 38b0    ;SPR2POS := 0x38b0
00079964: 0158 38b0    ;SPR3POS := 0x38b0
00079968: 0160 38b8    ;SPR4POS := 0x38b8
0007996c: 0168 38b8    ;SPR5POS := 0x38b8
00079970: 0170 38c0    ;SPR6POS := 0x38c0
00079974: 0178 38c0    ;SPR7POS := 0x38c0
00079978: 0140 38c8    ;SPR0POS := 0x38c8
0007997c: 0148 38c8    ;SPR1POS := 0x38c8
00079980: 0150 38d0    ;SPR2POS := 0x38d0
00079984: 0158 38d0    ;SPR3POS := 0x38d0

And the next line is much the same, waiting for the next vertical scanline:

00079988: 3931 fffe    ;Wait for vpos >= 0x39 and hpos >= 0x30.
0007998c: 0140 3948    ;SPR0POS := 0x3948
00079990: 0148 3948    ;SPR1POS := 0x3948
00079994: 0150 3950    ;SPR2POS := 0x3950
00079998: 0158 3950    ;SPR3POS := 0x3950
0007999c: 0160 3958    ;SPR4POS := 0x3958
000799a0: 0168 3958    ;SPR5POS := 0x3958
000799a4: 0170 3960    ;SPR6POS := 0x3960
000799a8: 0178 3960    ;SPR7POS := 0x3960
000799ac: 0140 3968    ;SPR0POS := 0x3968
000799b0: 0148 3968    ;SPR1POS := 0x3968
000799b4: 0150 3970    ;SPR2POS := 0x3970
000799b8: 0158 3970    ;SPR3POS := 0x3970
000799bc: 0160 3978    ;SPR4POS := 0x3978
000799c0: 0168 3978    ;SPR5POS := 0x3978
000799c4: 0170 3980    ;SPR6POS := 0x3980
000799c8: 0178 3980    ;SPR7POS := 0x3980
000799cc: 0140 3988    ;SPR0POS := 0x3988
000799d0: 0148 3988    ;SPR1POS := 0x3988
000799d4: 0150 3990    ;SPR2POS := 0x3990
000799d8: 0158 3990    ;SPR3POS := 0x3990
000799dc: 0160 3998    ;SPR4POS := 0x3998
000799e0: 0168 3998    ;SPR5POS := 0x3998
000799e4: 0170 39a0    ;SPR6POS := 0x39a0
000799e8: 0178 39a0    ;SPR7POS := 0x39a0
000799ec: 0140 39a8    ;SPR0POS := 0x39a8
000799f0: 0148 39a8    ;SPR1POS := 0x39a8
000799f4: 0150 39b0    ;SPR2POS := 0x39b0
000799f8: 0158 39b0    ;SPR3POS := 0x39b0
000799fc: 0160 39b8    ;SPR4POS := 0x39b8
00079a00: 0168 39b8    ;SPR5POS := 0x39b8
00079a04: 0170 39c0    ;SPR6POS := 0x39c0
00079a08: 0178 39c0    ;SPR7POS := 0x39c0
00079a0c: 0140 39c8    ;SPR0POS := 0x39c8
00079a10: 0148 39c8    ;SPR1POS := 0x39c8
00079a14: 0150 39d0    ;SPR2POS := 0x39d0
00079a18: 0158 39d0    ;SPR3POS := 0x39d0

This repeats on every scanline until the final line of the play area:

0007fe20: e731 fffe    ;Wait for vpos >= 0xe7 and hpos >= 0x30.
0007fe24: 0140 e748    ;SPR0POS := 0xe748
0007fe28: 0148 e748    ;SPR1POS := 0xe748
0007fe2c: 0150 e750    ;SPR2POS := 0xe750
0007fe30: 0158 e750    ;SPR3POS := 0xe750
0007fe34: 0160 e758    ;SPR4POS := 0xe758
0007fe38: 0168 e758    ;SPR5POS := 0xe758
0007fe3c: 0170 e760    ;SPR6POS := 0xe760
0007fe40: 0178 e760    ;SPR7POS := 0xe760
0007fe44: 0140 e768    ;SPR0POS := 0xe768
0007fe48: 0148 e768    ;SPR1POS := 0xe768
0007fe4c: 0150 e770    ;SPR2POS := 0xe770
0007fe50: 0158 e770    ;SPR3POS := 0xe770
0007fe54: 0160 e778    ;SPR4POS := 0xe778
0007fe58: 0168 e778    ;SPR5POS := 0xe778
0007fe5c: 0170 e780    ;SPR6POS := 0xe780
0007fe60: 0178 e780    ;SPR7POS := 0xe780
0007fe64: 0140 e788    ;SPR0POS := 0xe788
0007fe68: 0148 e788    ;SPR1POS := 0xe788
0007fe6c: 0150 e790    ;SPR2POS := 0xe790
0007fe70: 0158 e790    ;SPR3POS := 0xe790
0007fe74: 0160 e798    ;SPR4POS := 0xe798
0007fe78: 0168 e798    ;SPR5POS := 0xe798
0007fe7c: 0170 e7a0    ;SPR6POS := 0xe7a0
0007fe80: 0178 e7a0    ;SPR7POS := 0xe7a0
0007fe84: 0140 e7a8    ;SPR0POS := 0xe7a8
0007fe88: 0148 e7a8    ;SPR1POS := 0xe7a8
0007fe8c: 0150 e7b0    ;SPR2POS := 0xe7b0
0007fe90: 0158 e7b0    ;SPR3POS := 0xe7b0
0007fe94: 0160 e7b8    ;SPR4POS := 0xe7b8
0007fe98: 0168 e7b8    ;SPR5POS := 0xe7b8
0007fe9c: 0170 e7c0    ;SPR6POS := 0xe7c0
0007fea0: 0178 e7c0    ;SPR7POS := 0xe7c0
0007fea4: 0140 e7c8    ;SPR0POS := 0xe7c8
0007fea8: 0148 e7c8    ;SPR1POS := 0xe7c8
0007feac: 0150 e7d0    ;SPR2POS := 0xe7d0
0007feb0: 0158 e7d0    ;SPR3POS := 0xe7d0
0007feb4: 0080 0002    ;COP1LCH := 0x0002 Setup score panel copperlist
0007feb8: 0082 aac2    ;COP1LCL := 0xaac2
0007febc: 0088 0000    ;COPJMP1 := 0x0000

Now the game has set the copperlist to $2aac2 and invoked it. This creates the score panel at the bottom of the screen:

0002aac2: 0080 0007    ;COP1LCH := 0x0007
0002aac6: 0082 9800    ;COP1LCL := 0x9800
0002aaca: e801 fffe    ;Wait for vpos >= 0xe8 and hpos >= 0x00.
0002aace: 00e0 0001    ;BPL1PTH := 0x0001
0002aad2: 00e2 0000    ;BPL1PTL := 0x0000
0002aad6: 00e4 0001    ;BPL2PTH := 0x0001
0002aada: 00e6 06c0    ;BPL2PTL := 0x06c0
0002aade: 00e8 0001    ;BPL3PTH := 0x0001
0002aae2: 00ea 0d80    ;BPL3PTL := 0x0d80
0002aae6: 00ec 0001    ;BPL4PTH := 0x0001
0002aaea: 00ee 1440    ;BPL4PTL := 0x1440
0002aaee: 00f0 0001    ;BPL5PTH := 0x0001
0002aaf2: 00f2 1b00    ;BPL5PTL := 0x1b00
0002aaf6: 0092 0040    ;DDFSTRT := 0x0040
0002aafa: 0094 00c8    ;DDFSTOP := 0x00c8
0002aafe: 0108 0000    ;BPL1MOD := 0x0000
0002ab02: 010a 0000    ;BPL2MOD := 0x0000
0002ab06: 0100 5200    ;BPLCON0 := 0x5200
0002ab0a: 0102 0011    ;BPLCON1 := 0x0011
0002ab0e: 0180 0000    ;COLOR00 := 0x0000
0002ab12: 0182 0b62    ;COLOR01 := 0x0b62
0002ab16: 0184 0b96    ;COLOR02 := 0x0b96
0002ab1a: 0186 0963    ;COLOR03 := 0x0963
0002ab1e: 0188 0741    ;COLOR04 := 0x0741
0002ab22: 018a 0520    ;COLOR05 := 0x0520
0002ab26: 018c 0720    ;COLOR06 := 0x0720
0002ab2a: 018e 0a20    ;COLOR07 := 0x0a20
0002ab2e: 0190 0ca3    ;COLOR08 := 0x0ca3
0002ab32: 0192 0aaa    ;COLOR09 := 0x0aaa
0002ab36: 0194 0777    ;COLOR10 := 0x0777
0002ab3a: 0196 0555    ;COLOR11 := 0x0555
0002ab3e: 0198 0310    ;COLOR12 := 0x0310
0002ab42: 019a 0333    ;COLOR13 := 0x0333
0002ab46: 019c 0ddd    ;COLOR14 := 0x0ddd
0002ab4a: 019e 0000    ;COLOR15 := 0x0000
0002ab4e: 01a0 0f0f    ;COLOR16 := 0x0f0f
0002ab52: 01a2 0a62    ;COLOR17 := 0x0a62
0002ab56: 01a4 0941    ;COLOR18 := 0x0941
0002ab5a: 01a6 0831    ;COLOR19 := 0x0831
0002ab5e: 01a8 0710    ;COLOR20 := 0x0710
0002ab62: 01aa 0b82    ;COLOR21 := 0x0b82
0002ab66: 01ac 000b    ;COLOR22 := 0x000b
0002ab6a: 01ae 0008    ;COLOR23 := 0x0008
0002ab6e: 01b0 0999    ;COLOR24 := 0x0999
0002ab72: 01b2 0666    ;COLOR25 := 0x0666
0002ab76: 01b4 0888    ;COLOR26 := 0x0888
0002ab7a: 01b6 0444    ;COLOR27 := 0x0444
0002ab7e: 01b8 0222    ;COLOR28 := 0x0222
0002ab82: 01ba 0111    ;COLOR29 := 0x0111
0002ab86: 01bc 0004    ;COLOR30 := 0x0004
0002ab8a: 01be 0006    ;COLOR31 := 0x0006
0002ab8e: e901 fffe    ;Wait for vpos >= 0xe9 and hpos >= 0x00.
0002ab92: 01a0 0444    ;COLOR16 := 0x0444
0002ab96: ea01 fffe    ;Wait for vpos >= 0xea and hpos >= 0x00.
0002ab9a: 01a0 0666    ;COLOR16 := 0x0666
0002ab9e: eb01 fffe    ;Wait for vpos >= 0xeb and hpos >= 0x00.
0002aba2: 01a0 0777    ;COLOR16 := 0x0777
0002aba6: ec01 fffe    ;Wait for vpos >= 0xec and hpos >= 0x00.
0002abaa: 01a0 0999    ;COLOR16 := 0x0999
0002abae: ed01 fffe    ;Wait for vpos >= 0xed and hpos >= 0x00.
0002abb2: 01a0 0aaa    ;COLOR16 := 0x0aaa
0002abb6: ffdf fffe    ;Wait for vpos >= 0xff and hpos >= 0xde.
0002abba: 0101 fffe    ;Wait for vpos >= 0x01 and hpos >= 0x00.
0002abbe: 01a0 0444    ;COLOR16 := 0x0444
0002abc2: 01a9 fffe    ;Wait for vpos >= 0x01 and hpos >= 0xa8.
0002abc6: 01a0 0444    ;COLOR16 := 0x0444
0002abca: 0201 fffe    ;Wait for vpos >= 0x02 and hpos >= 0x00.
0002abce: 01a0 0666    ;COLOR16 := 0x0666
0002abd2: 02a9 fffe    ;Wait for vpos >= 0x02 and hpos >= 0xa8.
0002abd6: 01a0 0666    ;COLOR16 := 0x0666
0002abda: 0301 fffe    ;Wait for vpos >= 0x03 and hpos >= 0x00.
0002abde: 01a0 0777    ;COLOR16 := 0x0777
0002abe2: 03a9 fffe    ;Wait for vpos >= 0x03 and hpos >= 0xa8.
0002abe6: 01a0 0777    ;COLOR16 := 0x0777
0002abea: 0401 fffe    ;Wait for vpos >= 0x04 and hpos >= 0x00.
0002abee: 01a0 0999    ;COLOR16 := 0x0999
0002abf2: 04a9 fffe    ;Wait for vpos >= 0x04 and hpos >= 0xa8.
0002abf6: 01a0 0999    ;COLOR16 := 0x0999
0002abfa: 0501 fffe    ;Wait for vpos >= 0x05 and hpos >= 0x00.
0002abfe: 01a0 0aaa    ;COLOR16 := 0x0aaa
0002ac02: 05a9 fffe    ;Wait for vpos >= 0x05 and hpos >= 0xa8.
0002ac06: 01a0 0aaa    ;COLOR16 := 0x0aaa
0002ac0a: 0601 fffe    ;Wait for vpos >= 0x06 and hpos >= 0x00.
0002ac0e: 01a0 0ddd    ;COLOR16 := 0x0ddd
0002ac12: 06a9 fffe    ;Wait for vpos >= 0x06 and hpos >= 0xa8.
0002ac16: 01a0 0ddd    ;COLOR16 := 0x0ddd
0002ac1a: 0701 fffe    ;Wait for vpos >= 0x07 and hpos >= 0x00.
0002ac1e: 01a0 0ddd    ;COLOR16 := 0x0ddd
0002ac22: 07a9 fffe    ;Wait for vpos >= 0x07 and hpos >= 0xa8.
0002ac26: 01a0 0ddd    ;COLOR16 := 0x0ddd
0002ac2a: 1101 fffe    ;Wait for vpos >= 0x11 and hpos >= 0x00.
0002ac2e: 01a0 0444    ;COLOR16 := 0x0444
0002ac32: 1201 fffe    ;Wait for vpos >= 0x12 and hpos >= 0x00.
0002ac36: 01a0 0666    ;COLOR16 := 0x0666
0002ac3a: 1301 fffe    ;Wait for vpos >= 0x13 and hpos >= 0x00.
0002ac3e: 01a0 0777    ;COLOR16 := 0x0777
0002ac42: 1401 fffe    ;Wait for vpos >= 0x14 and hpos >= 0x00.
0002ac46: 01a0 0999    ;COLOR16 := 0x0999
0002ac4a: 1501 fffe    ;Wait for vpos >= 0x15 and hpos >= 0x00.
0002ac4e: 01a0 0aaa    ;COLOR16 := 0x0aaa
0002ac52: 1901 fffe    ;Wait for vpos >= 0x19 and hpos >= 0x00.
0002ac56: 009c 8010    ;INTREQ := 0x8010
0002ac5a: 0180 0000    ;COLOR00 := 0x0000
0002ac5e: 0182 0000    ;COLOR01 := 0x0000
0002ac62: 0184 0000    ;COLOR02 := 0x0000
0002ac66: 0186 0000    ;COLOR03 := 0x0000
0002ac6a: 0188 0000    ;COLOR04 := 0x0000
0002ac6e: 018a 0000    ;COLOR05 := 0x0000
0002ac72: 018c 0000    ;COLOR06 := 0x0000
0002ac76: 018e 0000    ;COLOR07 := 0x0000
0002ac7a: 0190 0000    ;COLOR08 := 0x0000
0002ac7e: 0192 0000    ;COLOR09 := 0x0000
0002ac82: 0194 0000    ;COLOR10 := 0x0000
0002ac86: 0196 0000    ;COLOR11 := 0x0000
0002ac8a: 0198 0000    ;COLOR12 := 0x0000
0002ac8e: 019a 0000    ;COLOR13 := 0x0000
0002ac92: 019c 0000    ;COLOR14 := 0x0000
0002ac96: 019e 0000    ;COLOR15 := 0x0000
0002ac9a: 01a0 0000    ;COLOR16 := 0x0000
0002ac9e: 01a2 0000    ;COLOR17 := 0x0000
0002aca2: 01a4 0000    ;COLOR18 := 0x0000
0002aca6: 01a6 0000    ;COLOR19 := 0x0000
0002acaa: 01a8 0000    ;COLOR20 := 0x0000
0002acae: 01aa 0000    ;COLOR21 := 0x0000
0002acb2: 01ac 0000    ;COLOR22 := 0x0000
0002acb6: 01ae 0000    ;COLOR23 := 0x0000
0002acba: 01b0 0000    ;COLOR24 := 0x0000
0002acbe: 01b2 0000    ;COLOR25 := 0x0000
0002acc2: 01b4 0000    ;COLOR26 := 0x0000
0002acc6: 01b6 0000    ;COLOR27 := 0x0000
0002acca: 01b8 0000    ;COLOR28 := 0x0000
0002acce: 01ba 0000    ;COLOR29 := 0x0000
0002acd2: 01bc 0000    ;COLOR30 := 0x0000
0002acd6: 01be 0000    ;COLOR31 := 0x0000
0002acda: ffff fffe    ;Wait for vpos >= 0xff and hpos >= 0xfe.
                       ;End of Copperlist

Quite an interesting technique!

Post your comment

Comments

  • Gravatar for J-Bot

    I'm pretty sure the racing-the-beam aspect of the Copper is by design since the Amiga's lineage goes back to the Atari 2600 and is essentially an extension of the Atari 800. Everything in the OCS Amigas was timed precisely. More info in the Byte Magazine articles on the Amiga and interviews with the design team: http://www.blitter.com/~nebulous/otherworld/Amiga/AmigaByte-Aug1985.pdf http://www.blitter.com/~nebulous/otherworld/Amiga/AmigaByte-Nov1985(JMiner).pdf https://www.youtube.com/watch?v=6YqDOKXQk1w https://archive.org/details/amiga-hardware-reference-manual-3rd-edition

    J-Bot 30/05/2023 7:08am (18 months ago)

  • Gravatar for Ragnarok

    Thank you for this article! I always wondered how they achieved this.

    Ragnarok 11/01/2016 1:56pm (9 years ago)

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

Popular Sprite Tricks

Risky Woods

Jim Power

2 sprites are displayed then repositioned horizontally right across the screen to create the colourful static background. The remaining sprites are used for the main character, the status bar and player bullets.

Risky Woods

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.

Risky Woods

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.