TL;DR

  • antirez가 제작한 Z80 포트는 실제 Another World 가상 머신(VM)을 ZX Spectrum 48K에서 실행해 게임 바이트코드를 처리하고 폴리곤을 실시간으로 그려 내는 데모임.
  • 데모는 자동차 도착부터 코카콜라 캔을 여는 장면까지의 무음 인트로를 보여준 뒤 반복하며, VM 구현과 인트로 데이터가 모두 48KB RAM에 들어가 테이프에서 스트리밍하지 않음.
  • 에뮬레이터 정상 속도로 녹화한 영상은 오프닝 크레딧을 1초로 줄였으며, 실제 기기나 호환 에뮬레이터에서 LOAD ""로 실행 가능함.
  • make와 C++17 컴파일러로 빌드하며, 번들 어셈블러와 anotherworld.tap을 생성하는 데 다운로드, Python, 원본 게임 설치 또는 외부 데이터 파일이 필요하지 않음.
  • 장면별 데이터 축소, 폴리곤·배경·색상 처리 최적화, VM 작업 축소와 50Hz 인터럽트 시계 등으로 Spectrum의 메모리와 그래픽 성능에 맞춤.

ZX Spectrum 48K용 Another World

  • antirez가 제작하고 GPT6 Astra가 작동시키는 Z80 포트로, 실제 Another World VM을 구현함.
  • 게임에서 변환한 바이트코드를 실행하고 기본 ZX Spectrum 48K에서 폴리곤을 실시간으로 그림.
  • 무음 인트로는 자동차가 도착하는 장면부터 코카콜라 캔을 여는 장면까지 이어진 뒤 처음부터 반복함.
  • VM 구현과 인트로 데이터가 모두 Spectrum의 48KB RAM에 들어가며, 테이프에서 데이터를 스트리밍하지 않음.
  • 실제 ZX Spectrum 48K에 프로그램을 로드하면 VM에서 게임 코드를 실행해 전체 데모를 실시간으로 재생함.
  • 영상은 정상 에뮬레이션 속도로 녹화했으며, 오프닝 크레딧은 1초로 단축함.
  • 48K Spectrum 또는 호환 에뮬레이터에서 LOAD ""를 입력해 anotherworld.tap을 로드함. 타이틀 화면은 10초 동안 기다리거나 키를 누르면 진행함.

빌드

  • make와 Clang 또는 GCC 같은 C++17 컴파일러가 필요함. macOS에서는 Apple Command Line Tools에 두 도구가 포함되며, Debian/Ubuntu에서는 build-essential을 설치함.
  • make는 번들 어셈블러를 빌드하고 anotherworld.tap을 생성함. make tap도 같은 작업을 수행하며, make assembler는 어셈블러만 빌드하고 make clean은 생성 파일을 제거함.
  • 다운로드, Python, 원본 게임 설치 또는 외부 데이터 파일 없이 빌드 가능함.
  • anotherworld.asm에는 주석이 포함된 Z80 엔진, 압축된 인트로 데이터, BASIC 로더가 들어 있음.
  • 읽기 안내서는 메모리 맵, VM, 그리기 루틴과 데이터 형식을 설명함. 데이터는 이미 변환된 상태이며, 저장소는 다른 게임 장면의 변환이 아니라 이 특정 인트로를 다시 빌드함.

메모리와 실행에 맞춘 구성

  • 이 장면에 필요한 스크립트, 도형, 팔레트만 유지함.
  • 축약된 게임 명령을 메모리에서 직접 실행함.
  • 작은 대입과 증가 연산은 1바이트 값으로 저장함.
  • 팔레트와 텍스트 명령은 짧은 1바이트 참조를 사용함.
  • 동일한 폴리곤과 그룹은 다시 저장하지 않고 공유함.
  • 사각형은 색상, 너비, 높이만으로 표현함.
  • 작은 좌표는 반올림하지 않고 2개씩 1바이트에 저장함.
  • 서로 가까운 좌표는 작은 차이값으로 저장하고, 필요한 경우 전체 바이트를 사용함.
  • 연관된 포즈는 공유 폴리곤에 적용되는 정확한 변경값으로 저장함.
  • 연관된 도형을 함께 배치해 연결 정보에 필요한 바이트 수를 줄임.
  • 예측 가능한 원점, 가장자리 좌표, 작은 그룹 개수는 생략함.
  • Spectrum 해상도에서 명확히 읽히지 않는 작은 장식은 제거함.
  • 엘리베이터 표시, 눈, 입자처럼 의미 있는 작은 표식은 보존함.
  • 선택한 팔레트에서 화면에 변화를 주지 않는 폴리곤은 제거함.
  • 나머지 좌표와 꼭짓점은 원래 정밀도를 유지함.
  • 그리는 폴리곤만 작은 작업 영역에 압축 해제함.

폴리곤 렌더링과 화면 처리

  • 256×80 픽셀 폴리곤을 그린 뒤 행을 두 배로 늘려 256×160 화면을 만듦.
  • 폴리곤의 두 변 사이를 한 번에 하나의 수평 구간씩 채움.
  • 사각형은 각 변을 따라 순회하지 않고 직접 그림.
  • 화면에 보이는 도형은 원래 크기에서 더 짧은 그리기 경로를 사용함.
  • 저렴한 분수 에지 증분을 사용하고, 클리핑이 필요할 때만 정밀도를 높임.
  • 좌표 배율, 에지 기울기, 비트 수는 다시 계산하지 않고 조회함.
  • 확대율이 바뀔 때까지 배율 테이블을 재사용함.
  • 작은 세부 묘사를 보존하도록 8×8 셀마다 두 색을 모두 사용함.
  • 색 순서를 바꿔야 할 때는 두 색을 맞바꾸고 픽셀을 반전함.
  • 서로 다른 게임 색이 같은 Spectrum 색조로 매핑되면 색 슬롯을 공유함.
  • 세 번째 색을 넣을 수 없으면 더 널리 사용되는 색을 유지함.
  • 내부의 단색 셀은 두 색을 같게 설정해 픽셀을 그대로 둠.
  • 폴리곤 가장자리, 조명, 배경 복사에는 마스크를 적용해 주변 픽셀을 보존함.
  • 각 장면의 팔레트는 알아보기 쉬운 색상과 세부 묘사의 가독성을 모두 고려함.
  • 이후 팔레트 변경이 가능하도록 원래 색상 인덱스를 유지함.
  • 팔레트가 바뀔 때만 색상 조회 테이블을 다시 구성함.
  • 별도의 화면 버퍼와 복사본 대신 하드웨어 화면을 전면 그림으로 사용함.
  • 각 셀 행에서 변경된 열 범위만 복사하고 갱신함.
  • 페이지 지우기는 필요할 때까지 미루고, 이후 복사로 불필요해진 지우기는 취소함.
  • 별도의 전체 그래픽 페이지를 보관하는 대신 그리기 명령을 기억함.
  • 저장된 배경은 바로 위 행과의 반복 차이값으로 압축함.
  • 모든 바이트마다 반복문 조건을 검사하지 않도록 직선 채우기와 복사를 반복 사용함.
  • 픽셀 루프 안에서 선택을 반복하지 않도록 그리기 명령을 한 번 패치함.
  • 다음 그림을 설치하는 동안 장면 전환을 잠시 숨김.

색상·텍스트·메모리 최적화

  • Spectrum ROM 글꼴로 읽기 쉬운 터미널 문자를 그림.
  • 공통 어미를 포함한 텍스트 문자열을 공유하고 텍스트 명령 설명도 재사용함.
  • 터미널에서 바뀐 행만 다시 그리고, 그래픽 갱신 중에는 바뀌지 않은 텍스트를 보호함.
  • 검은 화면 여백의 보이지 않는 픽셀에 문자열과 팔레트를 저장함.
  • 재사용 가능한 작업 버퍼를 통해 숨겨진 데이터를 읽어 들임.
  • 작은 루틴과 임시 폴리곤 데이터는 그 외에는 낭비되는 메모리 간격에 저장함.
  • 빠른 인덱스 접근을 위해 변수의 절반을 별도 테이블에 저장함.
  • 인트로에서 사용하는 작업과 변수 슬롯만 유지함.
  • 대기 중인 작업 변경이 없으면 작업 관리 처리를 건너뜀.
  • 루틴 주소 테이블을 통해 VM 명령을 직접 디스패치함.
  • RAM 테이블을 따로 예약하지 않고 인터럽트 벡터에 ROM 바이트를 사용함.
  • 50Hz 인터럽트로 구동되는 작은 시계를 사용해 원래 장면 전환 신호를 유지함.

크레딧과 저작권

  • Another World는 Éric Chahi가 제작함.
  • 이 포트 코드의 저작권은 원작자이자 Another World의 저작권 보유자인 Éric Chahi에게 양도됨. 원작 게임의 아트워크, 스크립트와 기타 포함된 게임 데이터도 Éric Chahi의 작업물임.
  • Z80 어셈블러는 기여자들이 제작한 sjasmplus이며, Sjoerd Mastijn의 SjASM을 기반으로 함.
  • 저장소에는 Lua 선택 기능을 제외한 1.24.0 버전의 빌드 가능한 소스 일부가 포함됨. 업스트림 커밋은 bf5e81eb3083bba672bf5cd2ae4f1fb40c95a7d5이며 크기는 약 660KiB임.
  • BSD 라이선스와 CRC-32C 구성 요소를 포함한 개별 소스 파일의 zlib 고지는 보존됨. 제3자 소스는 각자의 저작권과 라이선스를 유지함.