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자 소스는 각자의 저작권과 라이선스를 유지함.
댓글 (0)
로그인하면 이 기사에 내 생각을 남길 수 있어요