antirez/anotherworld-zx-spectrum-48k

Actual VM implementation of Another World game, with intro

C++

20

4 commits

updated Sep 28, 2026

See the code

See what people are saying

SourceMessageScoreDate

Another World Ported to ZX Spectrum

1

Sep 29, 2026

README

Another World for the ZX Spectrum 48K

A Z80 port of the actual Another World virtual machine, by antirez, operating GPT6 Astra. It runs the game's adapted bytecode and draws its polygons in real time on a vanilla ZX Spectrum 48K. The silent intro goes from the car's arrival through opening the Coke can, then loops forever. Please note that everything fits into the 48k Spectrum RAM: the VM implementation and the intro data, so there is no streaming whatsover from the tape. You load the program in an actual ZX Spectrum 48k and it runs the full demo, in real time, executing the game code in the VM.

Full demo running in the ZX Spectrum 48K emulator

Recorded at normal emulated speed, with the opening credits shortened to one second.

Load anotherworld.tap with LOAD "" on a 48K Spectrum or compatible emulator. The title waits ten seconds, or until a key is pressed.

Build

You need make and a C++17 compiler, such as Clang or GCC. On macOS, Apple's Command Line Tools provide both; on Debian/Ubuntu, install build-essential.

make

This builds the bundled assembler and creates anotherworld.tap. make tap does the same; make assembler builds only the assembler; make clean removes generated files. No downloads, Python, original game installation, or external data files are needed.

anotherworld.asm contains the commented Z80 engine, packed intro data, and BASIC loader. Its reading guide explains the memory map, VM, drawing routines and data formats. The data is already converted; this repository rebuilds this particular intro rather than converting other game scenes.

How it fits

  • Keep only the script, shapes and palettes needed by these scenes.
  • Execute the shortened game instructions directly from memory.
  • Store small assignments and increments with one-byte values.
  • Give palette and text commands short, one-byte references.
  • Share identical polygons and groups instead of storing them again.
  • Describe rectangles with just a color, width and height.
  • Pack small coordinates two to a byte without rounding them.
  • Store nearby coordinates as small differences, using full bytes when needed.
  • Store related poses as exact changes to a shared polygon.
  • Place related shapes together so their links need fewer bytes.
  • Leave out predictable origins, edge coordinates and small group counts.
  • Remove tiny decorations that cannot read clearly at Spectrum resolution.
  • Preserve meaningful tiny marks, including the lift indicator, eyes and particle.
  • Remove polygons that add no visible change with the chosen palette.
  • Keep the original precision of the remaining coordinates and vertices.
  • Unpack only the polygon being drawn into a small working area.
  • Draw 256×80 polygon pixels and double the rows for a 256×160 picture.
  • Fill polygons between their two edges, one horizontal span at a time.
  • Draw rectangles directly, without walking their individual edges.
  • Use a shorter drawing path for visible shapes at their original size.
  • Use cheap fractional edge steps, with extra precision only when clipping needs it.
  • Look up coordinate scaling, edge slopes and bit counts instead of recalculating them.
  • Reuse scaling tables until the zoom changes.
  • Use both colors in each 8×8 cell to preserve small details.
  • Swap the two colors and invert the pixels when their order needs to change.
  • Share a color slot when different game colors map to the same Spectrum shade.
  • Keep the more widely used color when a third color cannot fit.
  • Fill solid inner cells by setting both colors alike, leaving their pixels untouched.
  • Mask polygon edges, lighting and background copies to preserve surrounding pixels.
  • Choose each scene's palette for both recognizable colors and readable detail.
  • Keep original color indices so later palette changes still work.
  • Rebuild color lookup tables only when the palette changes.
  • Use the hardware screen as the front picture, saving a separate buffer and copy.
  • Copy and refresh only the changed column ranges in each row of cells.
  • Delay page clears until needed and cancel clears made unnecessary by later copies.
  • Remember drawing commands instead of keeping another full graphics page.
  • Compress saved backgrounds as repeated differences from the row above.
  • Use repeated straight-line fills and copies to avoid a loop test for every byte.
  • Patch drawing instructions once to remove repeated choices inside pixel loops.
  • Hide scene changes briefly while installing the next picture.
  • Draw readable terminal letters using the Spectrum ROM font.
  • Share text strings, including common endings, and reuse text command descriptions.
  • Redraw only changed terminal rows and protect unchanged text during graphics updates.
  • Store strings and palettes in invisible pixels inside the black screen margins.
  • Load that hidden data through work buffers which can be reused afterward.
  • Fit small routines and temporary polygon data into otherwise wasted memory gaps.
  • Store variable halves in separate tables for quick indexed access.
  • Keep only the task and variable slots used by the intro.
  • Skip task-management work when no task changes are pending.
  • Dispatch VM instructions directly through a table of routine addresses.
  • Use ROM bytes for the interrupt vector instead of reserving a RAM table.
  • Keep the original scene cues with a small clock driven by the 50 Hz interrupt.

Another World was created by Éric Chahi. Copyright in this port's code is assigned to Éric Chahi, the original author and copyright holder of Another World. The original game's artwork, script and other included game data remain his work.

The Z80 assembler is sjasmplus, by its contributors, based on Sjoerd Mastijn's SjASM. This repository includes the buildable source subset of version 1.24.0, upstream commit bf5e81eb3083bba672bf5cd2ae4f1fb40c95a7d5, without optional Lua support (about 660 KiB). Its BSD license and the zlib notices in individual source files, including the CRC-32C component, are preserved. These third-party sources retain their own copyrights and licenses.

antirez/anotherworld-zx-spectrum-48k

Actual VM implementation of Another World game, with intro

C++

20

4 commits

updated Sep 28, 2026

See the code

See what people are saying

SourceMessageScoreDate

Another World Ported to ZX Spectrum

1

Sep 29, 2026

README

Another World for the ZX Spectrum 48K

A Z80 port of the actual Another World virtual machine, by antirez, operating GPT6 Astra. It runs the game's adapted bytecode and draws its polygons in real time on a vanilla ZX Spectrum 48K. The silent intro goes from the car's arrival through opening the Coke can, then loops forever. Please note that everything fits into the 48k Spectrum RAM: the VM implementation and the intro data, so there is no streaming whatsover from the tape. You load the program in an actual ZX Spectrum 48k and it runs the full demo, in real time, executing the game code in the VM.

Full demo running in the ZX Spectrum 48K emulator

Recorded at normal emulated speed, with the opening credits shortened to one second.

Load anotherworld.tap with LOAD "" on a 48K Spectrum or compatible emulator. The title waits ten seconds, or until a key is pressed.

Build

You need make and a C++17 compiler, such as Clang or GCC. On macOS, Apple's Command Line Tools provide both; on Debian/Ubuntu, install build-essential.

make

This builds the bundled assembler and creates anotherworld.tap. make tap does the same; make assembler builds only the assembler; make clean removes generated files. No downloads, Python, original game installation, or external data files are needed.

anotherworld.asm contains the commented Z80 engine, packed intro data, and BASIC loader. Its reading guide explains the memory map, VM, drawing routines and data formats. The data is already converted; this repository rebuilds this particular intro rather than converting other game scenes.

How it fits

  • Keep only the script, shapes and palettes needed by these scenes.
  • Execute the shortened game instructions directly from memory.
  • Store small assignments and increments with one-byte values.
  • Give palette and text commands short, one-byte references.
  • Share identical polygons and groups instead of storing them again.
  • Describe rectangles with just a color, width and height.
  • Pack small coordinates two to a byte without rounding them.
  • Store nearby coordinates as small differences, using full bytes when needed.
  • Store related poses as exact changes to a shared polygon.
  • Place related shapes together so their links need fewer bytes.
  • Leave out predictable origins, edge coordinates and small group counts.
  • Remove tiny decorations that cannot read clearly at Spectrum resolution.
  • Preserve meaningful tiny marks, including the lift indicator, eyes and particle.
  • Remove polygons that add no visible change with the chosen palette.
  • Keep the original precision of the remaining coordinates and vertices.
  • Unpack only the polygon being drawn into a small working area.
  • Draw 256×80 polygon pixels and double the rows for a 256×160 picture.
  • Fill polygons between their two edges, one horizontal span at a time.
  • Draw rectangles directly, without walking their individual edges.
  • Use a shorter drawing path for visible shapes at their original size.
  • Use cheap fractional edge steps, with extra precision only when clipping needs it.
  • Look up coordinate scaling, edge slopes and bit counts instead of recalculating them.
  • Reuse scaling tables until the zoom changes.
  • Use both colors in each 8×8 cell to preserve small details.
  • Swap the two colors and invert the pixels when their order needs to change.
  • Share a color slot when different game colors map to the same Spectrum shade.
  • Keep the more widely used color when a third color cannot fit.
  • Fill solid inner cells by setting both colors alike, leaving their pixels untouched.
  • Mask polygon edges, lighting and background copies to preserve surrounding pixels.
  • Choose each scene's palette for both recognizable colors and readable detail.
  • Keep original color indices so later palette changes still work.
  • Rebuild color lookup tables only when the palette changes.
  • Use the hardware screen as the front picture, saving a separate buffer and copy.
  • Copy and refresh only the changed column ranges in each row of cells.
  • Delay page clears until needed and cancel clears made unnecessary by later copies.
  • Remember drawing commands instead of keeping another full graphics page.
  • Compress saved backgrounds as repeated differences from the row above.
  • Use repeated straight-line fills and copies to avoid a loop test for every byte.
  • Patch drawing instructions once to remove repeated choices inside pixel loops.
  • Hide scene changes briefly while installing the next picture.
  • Draw readable terminal letters using the Spectrum ROM font.
  • Share text strings, including common endings, and reuse text command descriptions.
  • Redraw only changed terminal rows and protect unchanged text during graphics updates.
  • Store strings and palettes in invisible pixels inside the black screen margins.
  • Load that hidden data through work buffers which can be reused afterward.
  • Fit small routines and temporary polygon data into otherwise wasted memory gaps.
  • Store variable halves in separate tables for quick indexed access.
  • Keep only the task and variable slots used by the intro.
  • Skip task-management work when no task changes are pending.
  • Dispatch VM instructions directly through a table of routine addresses.
  • Use ROM bytes for the interrupt vector instead of reserving a RAM table.
  • Keep the original scene cues with a small clock driven by the 50 Hz interrupt.

Another World was created by Éric Chahi. Copyright in this port's code is assigned to Éric Chahi, the original author and copyright holder of Another World. The original game's artwork, script and other included game data remain his work.

The Z80 assembler is sjasmplus, by its contributors, based on Sjoerd Mastijn's SjASM. This repository includes the buildable source subset of version 1.24.0, upstream commit bf5e81eb3083bba672bf5cd2ae4f1fb40c95a7d5, without optional Lua support (about 660 KiB). Its BSD license and the zlib notices in individual source files, including the CRC-32C component, are preserved. These third-party sources retain their own copyrights and licenses.

Languages

C++

57.1%

Assembly

39.3%

C

2.4%

Makefile

1.1%