Saint Dragon
Saint Dragon uses a sprite multiplexing technique devised by Ronald Pieket Weeserik and implemented by John Croudy. It re-uses 4 Amiga hardware sprite channels to create all the player bullets.
In the following example, the player has upgraded to 5 way fire and has fired 3 shots on the screen. This creates 15 bullets on the screen, which the Amiga hardware can cleverly display using only 4 sprites!
Sprites have the great advantage of being drawn "on the fly" by the video hardware either in front of or behind the bitplane data. In St Dragon's case, sprites have the highest priority meaning they will appear in front of everything.
Because they are drawn "on the fly" as the video beam is being constructed, the programmer does not have to save the graphics obscured by the sprite and restore it before redisplaying the sprite on the next frame. You can simply set the control words to a new position and the bullets will appear there!
Let's start by examining how this is achieved by looking at how the copperlist sets up the sprites:
Chip Addr Instructions ;Comments ------------------------------------------------------------------------------ $000144fc: 008e 4071 ;DIWSTRT := 0x4071 $00014500: 0090 08c1 ;DIWSTOP := 0x08c1 $00014504: 0180 0222 ;COLOR00 := 0x0222 $00014508: 0104 003f ;BPLCON2 := 0x003f Sprites have highest priority $0001450c: 01a0 0000 ;COLOR16 := 0x0000 $00014510: 01a2 00f8 ;COLOR17 := 0x00f8 Sprite 0/1 colours (light green) $00014514: 01a4 0084 ;COLOR18 := 0x0084 (medium green) $00014518: 01a6 0040 ;COLOR19 := 0x0040 (dark green) $0001451c: 01a8 0000 ;COLOR20 := 0x0000 $00014520: 01aa 00f8 ;COLOR21 := 0x00f8 Sprite 2/3 colours (light green) $00014524: 01ac 0084 ;COLOR22 := 0x0084 (medium green) $00014528: 01ae 0040 ;COLOR23 := 0x0040 (dark green) $0001452c: 0122 5644 ;SPR0PTL := 0x5644 Setup sprite 0 $00014530: 0120 0000 ;SPR0PTH := 0x0000 $00014534: 0126 56ec ;SPR1PTL := 0x56ec Setup sprite 1 $00014538: 0124 0000 ;SPR1PTH := 0x0000 $0001453c: 012a 5794 ;SPR2PTL := 0x5794 Setup sprite 2 $00014540: 0128 0000 ;SPR2PTH := 0x0000 $00014544: 012e 583c ;SPR3PTL := 0x583c Setup sprite 3 $00014548: 012c 0000 ;SPR3PTH := 0x0000 $0001454c: 0132 0000 ;SPR4PTL := 0x0000 Remaining sprites unused $00014550: 0130 0000 ;SPR4PTH := 0x0000 $00014554: 0136 0000 ;SPR5PTL := 0x0000 $00014558: 0134 0000 ;SPR5PTH := 0x0000 $0001455c: 013a 0000 ;SPR6PTL := 0x0000 $00014560: 0138 0000 ;SPR6PTH := 0x0000 $00014564: 013e 0000 ;SPR7PTL := 0x0000 $00014568: 013c 0000 ;SPR7PTH := 0x0000 $0001456c: 0096 8120 ;DMACON := 0x8120
On the Amiga, sprites 0 and 1 have colour 16 as transparent, and use colours 17-19 for their palette. In this case, $f8, $84 and $40 are all shades of green.
Sprites 2 and 3 have colour 20 as transparent, and use colours 21-23 for their palette. You can see above that those colours are set to exactly the same shades of green as the first 2 sprites. This is so all bullets appear the same on the screen.
Sprites 4-7 are all set to null sprites. Due to the game using horizontal scrolling using hardware, sprite 7 is unavailable for use, and with the display fetch specified as $28 at one point in the copperlist, sprite 6 wouldn't appear either.
We now need to have a look at the chip memory addresses that the sprites are pointing to and see how the sprites are re-used. If we start with sprite 0, it has been set to $5644, so looking at memory we see the following:
Address Hexadecimal for sprite 0 Comment for sprite 0 -------- --------------------------------------- -------------------------------------- $0005644 5EBE 6501 0240 03C0 0BA0 0C60 27D0 3830 Display from lines $5e to $65 $0005654 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $0005664 7E70 8500 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $7e to $85 $0005674 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $0005684 8BC0 9201 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $8b to $92 $0005694 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $00056A4 A2C0 A900 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $a2 to $a9 $00056B4 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $00056C4 0000 0000 0240 03C0 0BA0 0C60 27D0 3830 2 zeros indicate the end of the sprite
The first 2 words are the sprite position and control words. For the top bullet, we have $5ebe and $6501. The first byte of each word specifies the vertical starting position for the sprite, and the vertical stopping position respectively.
In this case, when the beam reaches line $5e, the sprite will start displaying. When it reaches line $65, the sprite will stop displaying. Between these 2 positions we require 7 lines of sprite graphics ($65 - $5e = 7).
After the two control words is the sprite data. With 4 colour sprites, we need 2 planes of data, so we need 7 lines x 2 words = 14 words of data, or 7 longwords. These represent the graphics for the bullet. In the memory dump above you can see that there is a definite pattern, as the same bullet graphic is re-used multiple times. The bullet graphic is:
Line 1: 0240 03C0 Line 2: 0BA0 0C60 Line 3: 27D0 3830 Line 4: BFE0 C010 Line 5: 27D0 3830 Line 6: 0BA0 0C60 Line 7: 0240 03C0
The first word of each line represents bit 0 and contributes 1 to the colour value. The second word represents bit 1, and contributes 2 to the colour value.
Where both bit 0 and 1 are set, the colour value will be 3.
The lightest green is sprite colour 1, the mid green is sprite colour 2, and the darkest green is sprite colour 3.
Looking at line 4, the first word is $bfe0 which in binary is 1011 1111 1110 0000. If you look at the pixels, you can see everywhere there is a light green or a dark green, the value 1 is set. This is because these pixels contribute 1 to the colour value.
The second word $c010 is binary 1100 0000 0001 0000. If you look at the pixels you can see everywhere there is a medium green or a dark green, the value 1 is set. This is because these pixels contribute 2 for the colour value.
The darkest green is colour value 3, so it needs a pixel set in both the first word and the second word.
Once the video beam has reached line $65, the sprite is turned off so nothing will display. The Amiga then reads the next 2 words which form the new sprite position and control word. The Amiga will therefore read $7e70 and $8500. This means as soon as the video beam reaches line $7e, it will start displaying sprite data again. When the beam reaches line $85, it will stop.
After the two control words is the sprite data once again. The bullet graphic is identical to the previous instance, so there are 7 longwords of bullet graphics that are identical to the first bullet.
The next re-use of sprite 0 yields position and control words $8bc0 $9201, so the sprite will appear between lines $8b and $92.
The final re-use of sprite 0 yields position and control words $a2c0 $a900, so the sprite will appear between lines $a2 and $a9.
Once the vertical beam reaches line $a9, the sprite is disabled and the new words are read. This time they are the special values $0000 $0000 which turns off the sprite for the rest of the display.
As you can see, by sorting the bullets into order by their Y co-ordinate, the Amiga is able to re-display the bullet 4 times! If we repeat this process looking at the sprite pointers for sprites 1-3, a similar thing takes place.
Sprite 1 is re-used 4 times:
Address Hexadecimal for sprite 1 Comment for sprite 1 -------- --------------------------------------- -------------------------------------- $00056EC 6E97 7500 0240 03C0 0BA0 0C60 27D0 3830 Display from lines $6e to $75 $00056FC BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $000570C 8570 8C01 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $85 to $8c $000571C BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $000572C 9270 9901 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $92 to $99 $000573C BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $000574C A997 B000 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $a9 to $b0 $000575C BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $000576C 0000 0000 0240 03C0 0BA0 0C60 27D0 3830 2 zeros indicate the end of the sprite
Sprite 2 is re-used 4 times:
Address Hexadecimal for sprite 2 Comment for sprite 2 -------- --------------------------------------- -------------------------------------- $0005794 75C0 7C00 0240 03C0 0BA0 0C60 27D0 3830 Display from lines $75 to $7c $00057A4 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $00057B4 8B70 9201 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $8b to $92 $00057C4 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $00057D4 9970 A000 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $99 to $a0 $00057E4 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $00057F4 B8BE BF01 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $b8 to $bf $0005804 BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $0005814 0000 0000 0240 03C0 0BA0 0C60 27D0 3830 2 zeros indicate the end of the sprite
Finally, sprite 3 is re-used 3 times:
Address Hexadecimal for sprite 3 Comment for sprite 3 -------- --------------------------------------- -------------------------------------- $000583C 7D98 8400 0240 03C0 0BA0 0C60 27D0 3830 Display from lines $7d to $84 $000584C BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $000585C 8B98 9201 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $8b to $92 $000586C BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $000587C 9A98 A100 0240 03C0 0BA0 0C60 27D0 3830 Redisplay from lines $9a to $a1 $000588C BFE0 C010 27D0 3830 0BA0 0C60 0240 03C0 $000589C 0000 0000 0240 03C0 0BA0 0C60 27D0 3830 2 zeros indicate the end of the sprite
In total we have had 4 uses of sprite 0, 4 uses of sprite 1, 4 uses of sprite 2, and 3 uses of sprite 3 = 4 + 4 + 4 + 3 = 15 bullets.
There is one thing you need to be aware of. If there is ever a situation where there are more than 4 bullets being displayed in a single horizontal, one of them will not be visible! This is why bullets occassionally are invisible during the game. If 5, 6 or even 7 sprites had been specified, you would be able to have more bullets in a single line.
The entire copperlist follows for the sake of completeness:
Chip Addr Instructions ;Comments ------------------------------------------------------------------------------ $000144fc: 008e 4071 ;DIWSTRT := 0x4071 $00014500: 0090 08c1 ;DIWSTOP := 0x08c1 $00014504: 0180 0222 ;COLOR00 := 0x0222 $00014508: 0104 003f ;BPLCON2 := 0x003f Sprites have highest priority $0001450c: 01a0 0000 ;COLOR16 := 0x0000 $00014510: 01a2 00f8 ;COLOR17 := 0x00f8 Sprite 0/1 colours (light green) $00014514: 01a4 0084 ;COLOR18 := 0x0084 (medium green) $00014518: 01a6 0040 ;COLOR19 := 0x0040 (dark green) $0001451c: 01a8 0000 ;COLOR20 := 0x0000 $00014520: 01aa 00f8 ;COLOR21 := 0x00f8 Sprite 2/3 colours (light green) $00014524: 01ac 0084 ;COLOR22 := 0x0084 (medium green) $00014528: 01ae 0040 ;COLOR23 := 0x0040 (dark green) $0001452c: 0122 5644 ;SPR0PTL := 0x5644 Setup sprite 0 $00014530: 0120 0000 ;SPR0PTH := 0x0000 $00014534: 0126 56ec ;SPR1PTL := 0x56ec Setup sprite 1 $00014538: 0124 0000 ;SPR1PTH := 0x0000 $0001453c: 012a 5794 ;SPR2PTL := 0x5794 Setup sprite 2 $00014540: 0128 0000 ;SPR2PTH := 0x0000 $00014544: 012e 583c ;SPR3PTL := 0x583c Setup sprite 3 $00014548: 012c 0000 ;SPR3PTH := 0x0000 $0001454c: 0132 0000 ;SPR4PTL := 0x0000 Remaining sprites unused $00014550: 0130 0000 ;SPR4PTH := 0x0000 $00014554: 0136 0000 ;SPR5PTL := 0x0000 $00014558: 0134 0000 ;SPR5PTH := 0x0000 $0001455c: 013a 0000 ;SPR6PTL := 0x0000 $00014560: 0138 0000 ;SPR6PTH := 0x0000 $00014564: 013e 0000 ;SPR7PTL := 0x0000 $00014568: 013c 0000 ;SPR7PTH := 0x0000 $0001456c: 0096 8120 ;DMACON := 0x8120 $00014570: 3fd1 fffe ;Wait for vpos >= 0x3f and hpos >= 0xd0. $00014574: 0092 0028 ;DDFSTRT := 0x0028 $00014578: 3fe1 fffe ;Wait for vpos >= 0x3f and hpos >= 0xe0. $0001457c: 00e0 0001 ;BPL1PTH := 0x0001 $00014580: 00e2 a4f0 ;BPL1PTL := 0xa4f0 $00014584: 00e4 0001 ;BPL2PTH := 0x0001 $00014588: 00e6 afc4 ;BPL2PTL := 0xafc4 $0001458c: 00e8 0001 ;BPL3PTH := 0x0001 $00014590: 00ea ba98 ;BPL3PTL := 0xba98 $00014594: 00ec 0001 ;BPL4PTH := 0x0001 $00014598: 00ee c56c ;BPL4PTL := 0xc56c $0001459c: 0100 4200 ;BPLCON0 := 0x4200 $000145a0: 0102 0000 ;BPLCON1 := 0x0000 $000145a4: 0182 0000 ;COLOR01 := 0x0000 $000145a8: 0182 0eca ;COLOR01 := 0x0eca $000145ac: 0184 0b85 ;COLOR02 := 0x0b85 $000145b0: 0186 0964 ;COLOR03 := 0x0964 $000145b4: 0188 0543 ;COLOR04 := 0x0543 $000145b8: 018a 0415 ;COLOR05 := 0x0415 $000145bc: 018c 0538 ;COLOR06 := 0x0538 $000145c0: 018e 0da8 ;COLOR07 := 0x0da8 $000145c4: 0190 07ad ;COLOR08 := 0x07ad $000145c8: 0192 075b ;COLOR09 := 0x075b $000145cc: 0194 0303 ;COLOR10 := 0x0303 $000145d0: 0196 09bf ;COLOR11 := 0x09bf $000145d4: 0198 077d ;COLOR12 := 0x077d $000145d8: 019a 0f32 ;COLOR13 := 0x0f32 $000145dc: 019c 0fc0 ;COLOR14 := 0x0fc0 $000145e0: 019e 05e2 ;COLOR15 := 0x05e2 $000145e4: 0094 00d8 ;DDFSTOP := 0x00d8 $000145e8: 0108 006c ;BPL1MOD := 0x006c $000145ec: 010a 006c ;BPL2MOD := 0x006c $000145f0: 5001 ff00 ;Wait for vpos >= 0x50, ignore horizontal. $000145f4: 0182 0000 ;COLOR01 := 0x0000 $000145f8: 0182 0fff ;COLOR01 := 0x0fff $000145fc: 0184 0dcb ;COLOR02 := 0x0dcb $00014600: 0186 0a98 ;COLOR03 := 0x0a98 $00014604: 0188 0765 ;COLOR04 := 0x0765 $00014608: 018a 0566 ;COLOR05 := 0x0566 $0001460c: 018c 0788 ;COLOR06 := 0x0788 $00014610: 018e 0adf ;COLOR07 := 0x0adf $00014614: 0190 07ad ;COLOR08 := 0x07ad $00014618: 0192 0344 ;COLOR09 := 0x0344 $0001461c: 0194 0122 ;COLOR10 := 0x0122 $00014620: 0196 0cdd ;COLOR11 := 0x0cdd $00014624: 0198 09aa ;COLOR12 := 0x09aa $00014628: 019a 0f32 ;COLOR13 := 0x0f32 $0001462c: 019c 0fc0 ;COLOR14 := 0x0fc0 $00014630: 019e 05e2 ;COLOR15 := 0x05e2 $00014634: 51d1 fffe ;Wait for vpos >= 0x51 and hpos >= 0xd0. $00014638: 0092 0028 ;DDFSTRT := 0x0028 $0001463c: 51e1 fffe ;Wait for vpos >= 0x51 and hpos >= 0xe0. $00014640: 00e0 0003 ;BPL1PTH := 0x0003 $00014644: 00e2 bd8b ;BPL1PTL := 0xbd8b $00014648: 00e4 0003 ;BPL2PTH := 0x0003 $0001464c: 00e6 be4f ;BPL2PTL := 0xbe4f $00014650: 00e8 0003 ;BPL3PTH := 0x0003 $00014654: 00ea bf13 ;BPL3PTL := 0xbf13 $00014658: 00ec 0003 ;BPL4PTH := 0x0003 $0001465c: 00ee bfd7 ;BPL4PTL := 0xbfd7 $00014660: 0180 0000 ;COLOR00 := 0x0000 $00014664: 0100 4200 ;BPLCON0 := 0x4200 $00014668: 0102 0077 ;BPLCON1 := 0x0077 $0001466c: 0094 00d8 ;DDFSTOP := 0x00d8 $00014670: 0108 0096 ;BPL1MOD := 0x0096 $00014674: 010a 0096 ;BPL2MOD := 0x0096 $00014678: 52d1 fffe ;Wait for vpos >= 0x52 and hpos >= 0xd0. $0001467c: 0092 0028 ;DDFSTRT := 0x0028 $00014680: 52e1 fffe ;Wait for vpos >= 0x52 and hpos >= 0xe0. $00014684: 00e0 0001 ;BPL1PTH := 0x0001 $00014688: 00e2 d063 ;BPL1PTL := 0xd063 $0001468c: 00e4 0002 ;BPL2PTH := 0x0002 $00014690: 00e6 4ba7 ;BPL2PTL := 0x4ba7 $00014694: 00e8 0002 ;BPL3PTH := 0x0002 $00014698: 00ea c6eb ;BPL3PTL := 0xc6eb $0001469c: 00ec 0003 ;BPL4PTH := 0x0003 $000146a0: 00ee 422f ;BPL4PTL := 0x422f $000146a4: 0100 4200 ;BPLCON0 := 0x4200 $000146a8: 0102 0011 ;BPLCON1 := 0x0011 $000146ac: 0180 0000 ;COLOR00 := 0x0000 $000146b0: 0182 0fff ;COLOR01 := 0x0fff $000146b4: 0184 0dcb ;COLOR02 := 0x0dcb $000146b8: 0186 0a98 ;COLOR03 := 0x0a98 $000146bc: 0188 0765 ;COLOR04 := 0x0765 $000146c0: 018a 0566 ;COLOR05 := 0x0566 $000146c4: 018c 0788 ;COLOR06 := 0x0788 $000146c8: 018e 0adf ;COLOR07 := 0x0adf $000146cc: 0190 07ad ;COLOR08 := 0x07ad $000146d0: 0192 0344 ;COLOR09 := 0x0344 $000146d4: 0194 0122 ;COLOR10 := 0x0122 $000146d8: 0196 0cdd ;COLOR11 := 0x0cdd $000146dc: 0198 09aa ;COLOR12 := 0x09aa $000146e0: 019a 0f32 ;COLOR13 := 0x0f32 $000146e4: 019c 0fc0 ;COLOR14 := 0x0fc0 $000146e8: 019e 05e2 ;COLOR15 := 0x05e2 $000146ec: 0094 00d8 ;DDFSTOP := 0x00d8 $000146f0: 0108 0096 ;BPL1MOD := 0x0096 $000146f4: 010a 0096 ;BPL2MOD := 0x0096 $000146f8: f3d1 fffe ;Wait for vpos >= 0xf3 and hpos >= 0xd0. $000146fc: 0092 0028 ;DDFSTRT := 0x0028 $00014700: f3e1 fffe ;Wait for vpos >= 0xf3 and hpos >= 0xe0. $00014704: 00e0 0003 ;BPL1PTH := 0x0003 $00014708: 00e2 c09b ;BPL1PTL := 0xc09b $0001470c: 00e4 0003 ;BPL2PTH := 0x0003 $00014710: 00e6 cfeb ;BPL2PTL := 0xcfeb $00014714: 00e8 0003 ;BPL3PTH := 0x0003 $00014718: 00ea df3b ;BPL3PTL := 0xdf3b $0001471c: 00ec 0003 ;BPL4PTH := 0x0003 $00014720: 00ee ee8b ;BPL4PTL := 0xee8b $00014724: 0100 4200 ;BPLCON0 := 0x4200 $00014728: 0102 0077 ;BPLCON1 := 0x0077 $0001472c: 0180 0000 ;COLOR00 := 0x0000 $00014730: 0182 0fff ;COLOR01 := 0x0fff $00014734: 0184 0dcb ;COLOR02 := 0x0dcb $00014738: 0186 0a98 ;COLOR03 := 0x0a98 $0001473c: 0188 0765 ;COLOR04 := 0x0765 $00014740: 018a 0566 ;COLOR05 := 0x0566 $00014744: 018c 0788 ;COLOR06 := 0x0788 $00014748: 018e 0adf ;COLOR07 := 0x0adf $0001474c: 0190 07ad ;COLOR08 := 0x07ad $00014750: 0192 0344 ;COLOR09 := 0x0344 $00014754: 0194 0122 ;COLOR10 := 0x0122 $00014758: 0196 0cdd ;COLOR11 := 0x0cdd $0001475c: 0198 09aa ;COLOR12 := 0x09aa $00014760: 019a 0f32 ;COLOR13 := 0x0f32 $00014764: 019c 0fc0 ;COLOR14 := 0x0fc0 $00014768: 019e 05e2 ;COLOR15 := 0x05e2 $0001476c: 0094 00d8 ;DDFSTOP := 0x00d8 $00014770: 0108 0096 ;BPL1MOD := 0x0096 $00014774: 010a 0096 ;BPL2MOD := 0x0096 $00014778: ffe1 fffe ;Wait for vpos >= 0xff and hpos >= 0xe0. $0001477c: 07e1 fffe ;Wait for vpos >= 0x07 and hpos >= 0xe0. $00014780: 0180 0222 ;COLOR00 := 0x0222 $00014784: ffff fffe ;Wait for vpos >= 0xff and hpos >= 0xfe. ;End of Copperlist
Popular Sprite Tricks
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.
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.
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.
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.
Post your comment
Comments
No one has commented on this page yet.
RSS feed for comments on this page | RSS feed for all comments