FABIEN SANGLARD'S WEBSITE

ABOUT  CONTACT  RSS   $$$


January 1, 2020
The polygons of Another World

There is a playful way to study the architecture of computers of the past. Find a piece of software you know well and try to find out how it was ported to these machine you don't.

A good choice would be DOOM. id Software's 1994 mega-hit has been ported to everything. It is designed around a core with no layering violations. It is usually easy to find and read the implementation of its six I/O sub-systems.

An other choice would be Eric Chahi's 1991 critically acclaimed[1]" title "Another World", better known in North America as "Out Of This World" which also happens to be ubiquitous. I would argue it is in fact more interesting to study than DOOM because of its polygon based graphics which are suitable to wild optimizations. In some cases, clever tricks allowed Another World to run on hardware built up to five years prior to the game release.

This series is a journey through the video-games hardware of the early 90s. From the Amiga 500, Atari ST, IBM PC, Super Nintendo, up to the Sega Genesis. For each machine, I attempted to discover how Another World was implemented. I found an environment made rich by its diversity where the now ubiquitous CPU/GPU did not exist yet. In the process, I discovered the untold stories of seemingly impossible problems heroically solved by lone programmers.

In the best case I was able to get in touch with the original developer. In the worse cases, I found myself staring at disassembly. It was a fun trip. Here are my notes.

Another World 101

There is very little code in Another World. The original Amiga version was reportedly 6,000 lines of assembly[2]. The PC DOS executable is only 20 KiB. Surprising for such a vast game which shipped on a single 1.44 MiB floppy. That is because most of the business logic is implemented via bytecode. The Another World executable is in fact a virtual machine host which reads and executes uint8_t opcodes.



Another World VM defines 256 variables, 64 threads, 29 opcodes, and four framebuffers[3]. That's it. If you build a VM host capable of handling these, you can run the game. If you are able to make the VM fast enough to run at 20 frames per seconds, you can actually play the game.

The virtual machine's graphic system uses a coordinate system of 320x200 with 16 palette-based colors. The color limitation may be surprising given that the development platform, the Amiga 500, supported up to 32 colors. This choice was a sweet spot allowing the graphics to be compatible with the other big platform of the era, the Atari ST which supports only 16 colors.

The limitation turned out to be a blessing in disguise. It resulted in an unique style which has aged well.
                               
                               
                               
Even when it would have been possible to use a specific palette for certain scene, Eric Chahi elected not to do so. During the close encounter with "The Beast", only three colors are used for the creature with black for the body, red for the eyes, and beige for the teeth. Imagination did the rest.
                               
The palette system turned out to be a strength to illustrate the accident at the origin of Another World. A non-expensive palette swap is enough to evoke a lightning strike.
                               

                               


               
               
The engine is also capable of translucency effects if the scene features only eight colors.

Here the colors are stored within [0x0,0x7] while the lit shades are within [0x8,0xF].

The Ferrari light beams are translucent. They are drawn with a special color 0x10 which does not exist since only 16 colors are available. The special value is interpreted as "read framebuffer index, add 0x8 and write back". The last part of the trick is to cleverly chose the 8 next colors in the palette.

               
               
Translucency was not used very much in the game but it can be seen one more time during the experiment turning wrong cinematic when the lightning is about to teleport Lester to AnotheR WorlD.

The four framebuffers

Of the four framebuffers[4], two are used for double buffering while two are used to save background (BKGD1 and BKGD2) composition. This is an optimization to avoid redrawing all the static background polygons in favor of a simple copy operation.

In the next video, see how a new scene is drawn first in the BKGD1 buffer. Each new frame, the BKGD1 is fully copied to the double buffer not being scanned by the display. There, moving elements such as Lester are drawn. Notice how once the car is "parked" it is also drawn in the BKGD1 buffer to minimize the number of polygons to render in subsequent frames.

Notice in the video how composition uses a plain painter algorithm which is simple but result in significant overdraw. This is not a problem since it is largely amortized by the BKGD copy.

The second BKGD2 is used to cache the last background. It is useful during cinematics when the background goes back and forth between scenes like in the intro when the view shows alternatively Lester desk and the inside of the particular accelerator. During gameplay it allows a significant speed-up when the player goes back to the previous screen.

The background buffer are not set in stone one rendered. They are often modified with new elements. It happens for the Ferrari 288 in the previous video but also for other subtle details.

In the early screens after emerging from the pool where the game begins, the game draws footprints and skid marks under Lester's feet as he walks on the sand. If the player walks one screen right and then back to the left again, the modified framebuffer is restored to preserve the footprints. It gives the environment a subtle additional sense of permanence.

It is so subtle as to go unnoticed in normal gameplay but I thought this was another example of Eric Chahi's great attention to detail in building this game.

- Martin Atkins
Virtual Machine Opcodes

The next array illustrates the 29 opcodes. We can find here thread (THRD) management, framebuffer (FB) management, and all the register management operations. Most opcodes are "easy" to implement except for "COPY FB", FILL, and "DRAW_POLY*" which are difficult for performances reasons.

  0x00 0x01 0x02 0x03 0x04 0x05 0x06 0x07 0x08 0x09 0x0A 0x0B 0x0C 0x0D 0x0E 0x0F
0x00 CMOV MOV ADD CADD CALL RET PAUSE
THRD
COND
JMP
SET
VECT
JNZ CJMP SET
PAL
RESET
THRD
SLCT
FB
FILL
FB
COPY
FB
0x10 BLIT
FB
KILL
THRD
DRAW
TEXT
SUB AND OR SHL SHR PLAY
SOUND
LOAD
RESC
PLAY
MUSIC
         
0x20                              
0x30                              
0x40 DRAW_POLY_SPRITE
0x50
0x60
0x70
0x80 DRAW_POLY_BACKGROUND
0x90
0xA0
0xB0
0xC0
0xD0
0xE0
0xF0

Both DRAW_POLY_* opcodes span over more than one entry. In the case of DRAW_POLY_BACKGROUND, it takes half the opcode-space from 0x80 to 0xFF. It looks wasteful but it is a space saving trick. Using all opcodes starting with bit "1" allow the 7 other bits to piggy-bag in opcode space as drawing parameters. Since this opcode is used for backgrounds and cinematics, space saving is very significant.

The SPRITE version uses all opcodes starting with bits "01" while the other remaining 6 bits are here to encode x, y and zoom in order to draw "sprites" such as lester, friend, and enemies.

Next

As mentioned in the previous section, 26 out of the 29 opcodes are easy to implement. The real challenge in porting this game was in manipulating pixels within the machine BUS and CPU bandwidth limitations. What will be done in this series is study how each port manipulated the framebuffers and how they solved the DRAW, FILL and COPY problems.

Ready? Let's dive into Another World on Amiga 500.

References

^ [1]Tilt d'or award year 1991
^ [2]Burgertime 8/9/2015: Out of This World
^ [3]Another World Source Code Review
^ [4]Thanks to Dimitri Sokolyuk for figuring out the second backbuffer. You can see a capture of all buffers during gameplay here.


*