Codetapper's Atari ST Site

Turrican 2

Rainbow Arts programmer Thomas Engel converted the Amiga version of Turrican 2 to the Atari ST, and did an amazing job considering the lack of hardware scrolling on the ST. To achieve the smooth scrolling, a number of compromises were made to keep the game running at a fair clip. The most obvious changes:

  • Game area reduced from 304x192 pixels on the Amiga down to 288x144 pixels for the ST.
  • Frame rate halved from 50 frames per second on the Amiga to 25 on the ST.
  • Score panel size increased on the ST to fill some of the blank space, and life counter and score overlays moved into it.
Turrican 2 Amiga bees (game area 304x192 pixels)

Amiga Turrican 2 (game area 304x192)

Turrican 2 Atari ST bees (game area 288x144 pixels)

Atari ST Turrican 2 (game area 288x144)

Level maps

On the Amiga, each tile in the map is 32x32 pixels, and is drawn in 16 colours. These 32x32 blocks are then combined in a large map that is a series of bytes stored in vertical strips that becomes the map. The first level for the game is 163 blocks wide x 51 blocks tall.

On the Atari ST, 2 maps are used. Each tile is now 16x16 pixels in size, and the tile graphics are only stored once in memory — there are no pre-shifted combinations to speed up the drawing process. World 1-1 contains 290 of these 16x16 tiles:

Turrican 2 Atari ST 16x16 tiles for world 1-1

Atari ST 16x16 tiles for world 1-1

A second map is used to place the tiles in a 2x2 table to form tiles that are now 32x32 pixels in size, and there are 197 of these tiles on world 1-1 (which is the same as the Amiga):

Turrican 2 Atari ST 32x32 tiles for world 1-1

Atari ST 32x32 tiles for world 1-1

The game uses the 163x51 map to lookup each tile in the relevant 2x2 block to work out which tile needs to be placed onto the map. This forms the complete level:

Atari ST world 1-1 map

Atari ST world 1-1 map

The tiles on the ST are similar to the Amiga versions, but not identical. This is because the Amiga artist had the freedom to draw anything in each tile, but the ST was limited to a certain number of 16x16 tiles. Therefore, many tiles were re-used to reduce the number of them. You can see the differences in this animation:

Turrican 2 Amiga vs Atari ST tiles

Amiga vs Atari ST tiles

Graphics

Each of the animated graphics in the ST version are stored 4 times in memory, along with a 1 bitplane mask before each graphical object that indicates which pixels should overwrite the background. Each object moves sideways in increments of 4 pixels. Here is a small selection of the graphics from the ST version as they exist in memory:

Turrican 2 Atari ST sprites in 4 pre-shifted combinations

Atari ST sprites in 4 pre-shifted combinations

Bosses

Turrican 2 is famous for its bosses that can appear at any point during a level. Because of all the graphical combinations in memory, there isn't space on a 512k ST to store all the bosses, so these are loaded from disk prior to appearing. The boss graphics overwrite some unnecessary graphics, such as the waterfall animations on world 1-1. Once the boss is defeated, the game re-loads the graphics that were previously overwritten.

Like the other objects, there are 4 combinations of each and they are broken down into smaller pieces to save memory:

Turrican 2 Atari ST mid-level boss on world 1-1

Atari ST mid-level boss on world 1-1.

Background buffers

The game stores 4 copies of the backgrounds in a similar way to how the Amiga handles an "infinite scroll" cylindrical wrap. Each buffer is the same size as the gameplay window (288 pixels wide by 144 pixels high), and contains a different 4-pixel indented version of the gameplay area.

The most important thing to note is that the top left corner of the buffers do not correspond with the top left corner of the visible screen. They simply contain all the graphical tiles for the screen, and a later routine copies the tiles into the correct order to build the display.

Turrican 2 Atari ST buffer layout

Turrican 2 Atari ST buffer layout

The scrolling routine is as follows:

  • When the game scrolls the screen sideways (by 4 pixels), the relevant indented buffer is updated with a vertical column of 16x16 tiles copied into it.
  • When the game scrolls the screen vertically (by 8 pixels), the game copies a complete horizontal strip (288x8) into all 4 buffers. This obviously takes a lot longer than the horizontal scrolling because it's having to move more data around.

The easiest way to see what's happening is to view an animated sequence showing the 4 buffers changing as the player moves around the map. In this sequence, the player jumps upwards and to the right before landing, then moving to the right.

Turrican 2 Atari ST buffer animation

Turrican 2 Atari ST buffer animation

Double-buffered display

The game also uses two screen buffers 320x180 in size, preparing the next frame while the previous is being displayed. The graphics are copied from one of the 4 indented buffers, then any animated objects (such as the player, enemies and bullets) are drawn over the top.

Tile merging routine

The main routine that fills the 4 buffers has to continually merge 2 tiles together, working on 8 pixel high blocks at a time. This is because the minimum the game scrolls vertically at a time is 8 pixels. The 4 combinations are:

  • Copy the entire 8 pixels high from one tile.
  • Copy 4 pixels from the first tile, and 12 pixels from the second.
  • Copy 8 pixels from each tile.
  • Copy 12 pixels from the first tile, and 4 pixels from the second.

One of the four display updates will be copying an entire tile, so a more efficient routine could have been used in that case. However, the same routine is used in all cases. When the routine is called, register d6 contains the number of pixels to indent the tile graphics, either 0, 4, 8 or 12. The same chunk of code is inlined 8 times, but it's pretty much identical so I've cut out 5 of the loops. The code looks like this:

_a854   movem.w (a1)+,d0-d3     ;Read right tile line 1 graphics into d0-d3
        swap    d0              ;Bitplane 1 into high word
        swap    d1              ;Bitplane 2 into high word
        swap    d2              ;Bitplane 3 into high word
        swap    d3              ;Bitplane 4 into high word
        move.w  (a0)+,d0        ;Read left tile bitplane 1 graphics to low word of d0
        move.w  (a0)+,d1        ;Read left tile bitplane 2 graphics to low word of d1
        move.w  (a0)+,d2        ;Read left tile bitplane 3 graphics to low word of d2
        move.w  (a0)+,d3        ;Read left tile bitplane 4 graphics to low word of d3
        rol.l   d6,d0           ;Rotate bitplane 1 data to merge the tiles
        rol.l   d6,d1           ;Rotate bitplane 2 data to merge the tiles
        rol.l   d6,d2           ;Rotate bitplane 3 data to merge the tiles
        rol.l   d6,d3           ;Rotate bitplane 4 data to merge the tiles
        movem.w d0-d3,(a2)      ;Write the merged line 1 graphics into buffer

        movem.w (a1)+,d0-d3     ;Read right tile line 2 graphics into d0-d3
        swap    d0              ;Bitplane 1 into high word
        swap    d1              ;Bitplane 2 into high word
        swap    d2              ;Bitplane 3 into high word
        swap    d3              ;Bitplane 4 into high word
        move.w  (a0)+,d0        ;Read left tile bitplane 1 graphics to low word of d0
        move.w  (a0)+,d1        ;Read left tile bitplane 2 graphics to low word of d1
        move.w  (a0)+,d2        ;Read left tile bitplane 3 graphics to low word of d2
        move.w  (a0)+,d3        ;Read left tile bitplane 4 graphics to low word of d3
        rol.l   d6,d0           ;Rotate bitplane 1 data to merge the tiles
        rol.l   d6,d1           ;Rotate bitplane 2 data to merge the tiles
        rol.l   d6,d2           ;Rotate bitplane 3 data to merge the tiles
        rol.l   d6,d3           ;Rotate bitplane 4 data to merge the tiles
        movem.w d0-d3,$90(a2)   ;Write the merged line 2 graphics into buffer

        ... repeat another 5 times ...
		
        movem.w (a1)+,d0-d3     ;Read right tile line 8 graphics into d0-d3
        swap    d0              ;Bitplane 1 into high word
        swap    d1              ;Bitplane 2 into high word
        swap    d2              ;Bitplane 3 into high word
        swap    d3              ;Bitplane 4 into high word
        move.w  (a0)+,d0        ;Read left tile bitplane 1 graphics to low word of d0
        move.w  (a0)+,d1        ;Read left tile bitplane 2 graphics to low word of d1
        move.w  (a0)+,d2        ;Read left tile bitplane 3 graphics to low word of d2
        move.w  (a0)+,d3        ;Read left tile bitplane 4 graphics to low word of d3
        rol.l   d6,d0           ;Rotate bitplane 1 data to merge the tiles
        rol.l   d6,d1           ;Rotate bitplane 2 data to merge the tiles
        rol.l   d6,d2           ;Rotate bitplane 3 data to merge the tiles
        rol.l   d6,d3           ;Rotate bitplane 4 data to merge the tiles
        movem.w d0-d3,$3f0(a2)  ;Write the merged line 8 graphics into buffer

At this point, the game does some magic and works out whether it can continue copying another 8 pixels high into the buffer, or has to switch to copying new tiles. And if the game scrolled vertically, it has to do extra work again. This extra code is beyond the scope of this analysis, but if you wish to disassemble the game yourself, have a look around $a854.

Now the buffer is complete, the final step is to copy the buffer as fast as possible into the screen display, which erases the previous frame and all objects on it. Then the game can plot animated tiles, the player, enemies and bullets. The routine is at $ba00.

Post your comment

Comments

No one has commented on this page yet.

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

Any suggestions?

If you have any idea what should go in this box, please let me know! :)