Another World 3DO Technical Notes

June 19, 2025 ยท View on GitHub

Another World has been ported on many platforms. The way the game was written (interpreted game logic) clearly helped.

This document focuses on the 3DO release made by Interplay in 1994. This version was not a straight port. In addition to reworking the assets, the game code was modified.

Assets

TypeAmiga/DOS3DO
Music4 channels tracker with raw signed 8bits samples (3 files)AIFF SDX2 compressed audio (30 files)
Soundraw signed 8bits mono (103 files)AIFF signed 16 bits (92 files)
Bitmap4 bits depth paletted 320x200 (8 files)True-color RGB555 320x200 (139 files)
BytecodeBig-endian (Motorola 68000)Little-endian (ARMv3)

Bytecode

The game engine is based on a virtual machine with 30 opcodes.

Opcodes

The game was developed on an Amiga. As the Motorola 68000 CPU was big endian, this is the byte order found in the bytecode on the original Amiga/DOS versions. Most of the opcodes operand size is 2 bytes, matching the register size of the target 68000 CPU.

  • ALU
NumNameParametersDescription
0movConstvar: byte, value: wordvar := value
1movvar(dst): byte, var(src): bytedst := src
2addvar(dst): byte, var(src): bytedst += src
3addConstvar: byte; value: wordvar += value
19subvar(dst): byte, var(src): bytevar -= src
20andvar: byte, value: wordvar &= value
21orvar: byte, value: wordvar
22shlvar: byte, count: wordvar <<= count
23shrvar: byte, count: wordvar >>= count
  • Control flow
NumNameParametersDescription
4calloffset: wordfunction call
5retreturn from function call
7jmpoffset: wordpc = offset
9jmpIfVarvar: byte, offset: word--var != 0: pc = offset (dbra)
10condJmpoperator: byte, var(operand1): byte, operand2: byte/word, offset: wordcmp op1, op2: pc = offset
  • Coroutines
NumNameParametersDescription
8installTasknum: byte, offset: wordsetup a coroutine, pc = offset
6yieldTaskpause current coroutine
12changeTasksStatenum1: byte, num2: byte, state:bytechange coroutines num1..num2 state
17removeTaskabort current coroutine
  • Display
NumNameParametersDescription
11setPalettenum: wordset palette (16 colors)
13selectPagenum: byteset current drawing page
14fillPagenum: byte, color: bytefill page with color
15copyPagenum(dst): byte, num(src): bytecopy page content
16updateDisplaynum: bytepresent page to screen
18drawStringnum: word, x(/8): byte, y(/8): byte, color: bytedraw string
  • Assets
NumNameParametersDescription
24playSoundnum: word, frequency: byte, volume: byte, channel: byteplay sound
25updateResourcesnum: wordload asset resource or section
26playMusicnum: word, pitch: word, position: byteplay music

3DO Specific Opcodes

The 3DO port does not use the same bytecode. The game code was modified, recompiled and saved under the target endianness of the target ARM CPU of the 3DO. In the process, some opcodes have been added and some others have been optimized for size by using one byte operand when applicable.

The new shift opcode is a good example : the original bytecode was using a 16bits value to specify the number of bits to shift on the target 16 bits register. 8 bits were twice as enough.

NumNameParametersDescription
11setPalettenum: byte
22shiftLeftvar: byte, count: bytevar <<= count
23shiftRightvar: byte, count: bytevar >>= count
26playMusicnum: byte
27drawStringnum: byte, var(x): byte, var(y): byte, color: byte
28jmpIfVarFalsevar: byte, offset: wordif var == 0: pc = offset
29jmpIfVarTruevar: byte, offset: wordif var != 0: pc = offset
30printTimeoutput running time (via SWI 0x1000E), not referenced in game code

Resources

As any 3DO game, the data-files are read from an OperaFS CD-ROM.

Inside the GameData/ directory

  • The game data-files are numbered from 1 to 340 (File%d)
  • The song files (AIFF-C) are numbered from 1 to 30 (song%d)
  • Three cinematics files : Logo.Cine, Spintitle.Cine, ootw2.cine

The files are stored uncompressed at the exception of the background bitmaps. The decompression code can be found in the DOOM 3DO source code - dlzss.s

Rendering

Transparency

The engine can display semi-transparent shapes such as the car lights in the introduction.

Screenshot Intro Amiga Screenshot Intro 3DO

The original Amiga/DOS game used a palette of 16 colors. The semi-transparency is achieved by allocating the upper half of the palette to the transparent colors. The palette indexes 0 to 7 hold the scene colors, indexes 8 to 15 the blended colors.

Palette Intro Amiga

Turning a pixel semi-transparent is simply done by setting to 1 the bit 3 of the palette color index (|= 8).

This is not directly applicable to the 3DO which renders everything with true-color buffers. The 3DO engine leverages the console capabilities to render an alpha blended shape. Interestingly, the color of the lights is not yellow in that version.

Screenshot Intro Amiga Screenshot Intro 3DO

Background Bitmaps

While the original Amiga/DOS game relied on polygons for most of its graphics, the game engine supports using a raster bitmap to be used as the background. In the original game version, this is only used for 8 different screens.

file067 file068 file069 file070

file072 file073 file144 file145

The 3DO version uses the feature for all the screens of the game. For comparison, the same screens in RGB555.

File246 File224 File225 File226

File221 File220 File306 File302

Drawing Primitives

The original Amiga/DOS used only one primitive for all its drawing : a quad (4 vertices).

The 3DO reworked that format, probably to optimize shapes made of single pixels or straight lines. The upper nibble of the shape color byte specifies the primitive to draw.

0x00 : nested/composite shape
0x20 : rectangle
0x40 : pixel
0xC0 : polygon/quad

Introduction Sequence Synchronization

On Amiga/DOS, the introduction is synchronized to the music.

The music module contains 2 bytes patterns, that are copied to the variable 0xF4.

if (pat.note_1 == -3) {
    _vars[0xF4] = pat.note_2;
}

The condition can be found in the original 68000 SoundFX player routine.

The bytecode contains checks on this variable to wait and continue.

000B: (00) VAR(0xF4) = 0
0F66: (0A) jmpIf(VAR(0xF4) == 43, @0F91)
...
1399: (0A) jmpIf(VAR(0xF4) == 46, @13AA)
13AE: (0A) jmpIf(VAR(0xF4) == 47, @13BF)
13C3: (0A) jmpIf(VAR(0xF4) == 48, @13D4)
13F6: (0A) jmpIf(VAR(0xF4) != 49, @1406)

On the 3DO, the tracker based music has been replaced with digital tracks. The synchronization had to be modified.

That version relies on the variable 0xF7. It holds the total number of VBLs since the game started.

nbtrame = readTick() - OldVBL;
if (_vars[0xFF] != 0) {
    while (_vars[0xFF] > nbtrame) {
       nbtrame = readTick() - OldVBL;
    }
}
_vars[0xF7] += nbtrame;
OldVBL += nbtrame;

The introduction bytecode has the checks against this variable for the timing.

0068: jmpIf(VAR(0xF7) < 3450, @0067)
043E: jmpIf(VAR(0xF7) < 7484, @043D)
046A: jmpIf(VAR(0xF7) < 8602, @0469)
jmpIf(VAR(0xF7) < 9212, @0576)
...
jmpIf(VAR(0xF7) < 400, @1C40)

Game Passwords

Game passwords extracted from the 16008 part bytecode.

CodePartCheckpointNotes
LDKD1600210
HTDC1600320
CLLD1600430
FXLC1600435
KRFK1600437
XDDJ1600433
LBKG1600431
KLFB1600439
TTCT1600441
DDRX1600442
TBHK1600443
BRTD1600449
CKJL1600550
LFCK1600660
BFLX1600444
XJRT1600445
HRTB1600446
HBHK1600447
JCGB1600448
HHFL1600662
TFBB1600664
TXHF1600666
KRTD1600770
BRGR16010-1Stalactites