N64 : Recompiled는 N64 Binaries를 C 코드로 정적으로 재 컴파일하는 도구입니다. 이것은 포트 또는 도구뿐만 아니라 통역사 나 동적 재 컴파일 할 수있는 것보다 동작을 훨씬 빠르게 시뮬레이션하는 데 사용할 수 있습니다. 더 널리, 독립형 환경에서 N64 바이너리의 일부를 실행하려는 컨텍스트에서 사용할 수 있습니다.
이것은 게임 콘솔 바이너리에서 정적 재 컴파일을 사용하는 첫 번째 프로젝트가 아닙니다. 잘 알려진 예는 NES 바이너리를 대상으로하는 Jamulator입니다. 또한이 프로젝트는 N64 관련 프로젝트에 정적 재 컴파일을 적용한 첫 번째 프로젝트가 아닙니다. IDO 정적 재 컴파일은 N64 게임의 대결을 촉진하기 위해 최신 시스템의 SGI IRIX IDO 컴파일러를 다시 컴파일합니다. 이 프로젝트는 어떤면에서 IDO 정적 리콤 프로젝트와 유사하게 작동하며, 그 프로젝트는 이것을 만들기위한 주요 영감이었습니다.
리 컴파일러는 입력 바이너리를 메타 데이터에 따라 명명 된 C 함수로 개별적으로 호환되는 함수로 분할하는 목표와 함께 이진과 함께 기호 및 메타 데이터 목록을 수락하여 작동합니다.
지침은 하나씩 처리되며 해당 C 코드는 각각 처리 될 때 방출됩니다. 이 번역은 복잡성을 낮게 유지하기 위해 매우 문자 적입니다. 예를 들어, 명령어 addiu $r4, $r4, 0x20
은 레지스터 $r4
의 낮은 바이트에서 32 비트 값에 0x20
추가하고 $r4
에 64 비트 결과를 확장 한 부호를 저장하고 ctx->r4 = ADD32(ctx->r4, 0X20);
jal
(Jump-and-Link) 명령어는 함수 호출에 직접 고환되며 꼬리 콜 최적화로 식별 될 수있는 j
또는 b
명령 (무조건 점프 및 가지)도 기능 호출에도 적용됩니다. 분기 지연 슬롯은 필요에 따라 중복 지침으로 처리됩니다. jr
명령을 스위치 케이스 문으로 전환하려는 시도와 같은 특정 지침에 대한 다른 특정 동작이 있습니다. 리 컴파일러는 주로 오래된 MIPS 컴파일러 (예 : MIPS GCC 2.7.2 및 IDO)와 현대적인 Clang 타겟팅 MIP로 제작 된 바이너리에서 테스트되었습니다. 현대의 MIPS GCC는 특정 최적화로 인해 레 컴파일러를 트립 할 수 있지만 특정 컴파일 플래그를 설정하여 이러한 경우를 피할 수 있습니다.
리 컴파일러가 생성 한 모든 출력 기능은 현재 자체 파일로 방출됩니다. 향후 옵션에 그룹 기능을 함께 출력 파일로 제공 할 수 있으며, 이는 빌드 프로세스에서 파일 I/O를 줄임으로써 레콤필러 출력의 빌드 시간을 개선하는 데 도움이 될 것입니다.
레콤 필러 출력은 임의의 C 컴파일러 (MSVC, GCC 및 Clang로 테스트)로 컴파일 될 수 있습니다. 출력은 필요한 기능을 제공하고이를 실행하는 데 필요한 기능을 제공 할 수있는 런타임과 함께 사용될 것으로 예상됩니다. N64ModernRuntime에서 런타임이 제공되며 Zelda 64 : Recompiled Project에서 실제로 볼 수 있습니다.
정적으로 연결되고 재배치 가능한 오버레이는이 도구로 처리 할 수 있습니다. 두 경우 모두, 도구는 제공된 런타임이 모든 종류의 조회 테이블을 사용하여 구현할 수있는 점프 및 링크 등록 (즉, 기능 포인터 또는 가상 함수)에 대한 기능 조회를 방출합니다. 예를 들어, 지침 jalr $25
LOOKUP_FUNC(ctx->r25)(rdram, ctx);
그런 다음 런타임은 런타임 중에 조회가 트리거 될 때마다 실행될 기능을 결정하기 위해 어떤 프로그램 섹션이로드되는지와 그 주소로 목록을 유지할 수 있습니다.
이전 가능한 오버레이의 경우이 도구는 런타임이 명령어의 즉각적인 값 필드를 재배치 할 수있는 추가 매크로를 방출하여 재배치 데이터 ( lui
, addiu
, Load and Store 명령어)를 보유한 지원 지침을 수정합니다. 예를 들어, 0x80BFA730
의 주소가있는 기호로 재배치 된 주소 0x80BFA100
에서 시작하는 섹션에서 명령 lui $24, 0x80C0
ctx->r24 = S32(RELOC_HI16(1754, 0X630) << 16);
, 여기서 1754는이 섹션의 색인입니다. 그런 다음 런타임은 섹션의 현재로드 된 주소를 기반으로 즉시 수정을 처리하기 위해 rejoc_hi16 및 rech_lo16 매크로를 구현할 수 있습니다.
TLB 매핑에 대한 재배치 지원이 향후에 나오고 있으며, 이는 런타임이로드시 재배치 할 수 있도록 MIPS32 재배그 목록을 제공 할 수있는 기능을 추가 할 것입니다. 이를 재배치 가능한 오버레이에 사용 된 기능과 결합하면 모든 RAM 액세스에서 성능 페널티를 제공하지 않고도 대부분의 TLB 맵핑 코드를 실행할 수 있습니다.
레콤필러는 레콤필러 동작을 구성하기 위해 TOML 파일을 제공하여 구성됩니다. TOML은 입력 및 출력 파일 경로를 지정할뿐만 아니라 특정 기능을 선택적으로 스 3, 특정 기능의 재 컴파일 및 대상 바이너리의 단일 지침을 패치하는 곳입니다. 또한 TOML ( [[patches.func]]
및 [[patches.hook]]
섹션의 섹션에 추가하여 리 컴파일러 출력에 후크를 방출 할 수있는 계획된 기능도 있지만 현재입니다. 구현되지 않았습니다. 리 컴파일러가 제공하는 모든 옵션에 대한 문서화는 현재 사용할 수 없지만 Toml은 Zelda 64 : Recompiled Project에서 찾을 수 있습니다.
현재 필요한 메타 데이터를 제공하는 유일한 방법은 ELF 파일을이 도구에 전달하는 것입니다. 이러한 ELF를 얻는 가장 쉬운 방법은 대상 바이너리의 분해 또는 탈퇴를 설정하는 것이지만, 미래에 그렇게해야 할 필요성을 우회하기 위해 사용자 정의 형식을 통해 메타 데이터를 제공하는 것을 지원할 것입니다.
이 도구는 구성 TOML의 옵션을 통해 "단일 파일 출력"모드로 다시 컴파일하도록 구성 할 수 있습니다. 이것은 제공된 ELF의 모든 기능을 단일 출력 파일로 방출합니다. 이 모드의 목적은 대상 바이너리에서 패치 된 함수 버전을 컴파일 할 수 있어야합니다.
이 모드는 원래의 재 컴파일러 출력의 기능을 수정 된 버전으로 바꾸어 거의 모든 링커 (LD, LLD, MSVC의 Link.exe 등)가 제공하는 기능과 결합 할 수 있습니다. 해당 링커는 이전 입력 파일에서 아직 발견되지 않은 경우 정적 라이브러리에서 기호 만 찾으므로 원래의 레콤필러 출력을 제공하기 전에 링커에 다시 컴파일 된 패치를 제공하면 패치가 동일한 이름의 함수보다 우선 순위를 갖습니다. 원래 레콤필러 출력에서.
이로 인해 대상 바이너리의 패치를 반복하는 동안 엄청난 시간이 절약됩니다. 대상 바이너리의 레콤필러를 우회하고 원래의 레콤필러 출력을 컴파일 할 수 있습니다. 해당 목적 으로이 단일 파일 출력 모드를 사용하는 예는 Zelda 64 : Recompiled Project에서 찾을 수 있으며 여기에 해당 패치의 엘프를 구축하는 데 사용되는 해당 MakeFile과 함께 여기에 있습니다.
이 도구와 함께 RSP 마이크로 코드를 다시 컴파일 할 수도 있습니다. 현재 RSP 오버레이를 다시 컴파일하는 데 도움이되지 않지만 원하는 경우 향후 추가 될 수 있습니다. 이 기능을 사용하는 방법에 대한 문서는 곧 출시 될 예정입니다.
이 프로젝트는 CMAKE 3.20 이상 및 C ++ 20을 지원하는 C ++ 컴파일러로 구축 할 수 있습니다. 이 repo는 git 서브 모듈을 사용하므로 복제 적으로 복제하십시오 ( git clone --recurse-submodules
) 또는 클로닝 후 재귀 적으로 하위 모듈을 초기화하십시오 ( git submodule update --init --recursive
). 거기에서 빌딩은 다른 CMAKE 프로젝트와 동일합니다. 예를 들어 대상 빌드 폴더에서 cmake
실행 하고이 리베소의 루트를 가리킨 다음 cmake --build .
그 대상 폴더에서.