N64:重新编译是一种静态重新编译N64二进制文件的工具,可以为任何平台编译。这可用于端口或工具,以及比解释者或动态重新编译更快地模拟行为。更广泛地说,它可以在您想在独立环境中运行N64二进制的某个部分的任何情况下使用它。
这不是第一个在游戏机二进制游戏上使用静态重新编译的项目。一个众所周知的例子是Jamulator,它针对NES二进制。此外,这甚至不是第一个将静态重新编码应用于N64相关项目的项目:IDO静态重新编译会使现代系统上的Sgi Irix Ido编译器重新编译,以促进N64游戏的匹配解说。该项目在某些方面与IDO静态重新组件项目相似,而该项目是我制作此项目的主要灵感。
重新编译器通过接受符号和元数据的列表以及二进制的列表的起作用,其目的是将输入二进制分解为单独重新编译为C函数的函数,该函数根据元数据命名。
指令是一对一处理的,并且在处理每个方案时会发出相应的C代码。这种翻译是非常字面的,以使复杂性保持较低。例如,指令addiu $r4, $r4, 0x20
,将0x20
添加到寄存器$r4
的低字节中的32位值,并存储符号扩展64位结果$r4
,被重新编译到ctx->r4 = ADD32(ctx->r4, 0X20);
将jal
(跳跃链接)指令直接重新编译到函数调用中, j
或b
指令(无条件跳跃和分支)也可以识别为尾巴呼叫优化,也可以重新编译到功能调用中。分支延迟插槽通过必要的重复说明来处理。对于某些说明,还有其他特定的行为,例如,如果可以说明它与跳台一起使用,则试图将jr
指令转换为开关案例语句。重新编译器主要是对使用旧MIPS编译器(例如MIPS GCC 2.7.2和IDO)以及现代叮当靶MIPS构建的二进制文件进行了测试。现代MIPS GCC可能会由于某些优化而可能会落入重新编译器,但是可以通过设置特定的汇编标志来避免这些情况。
当前,由重新编译器创建的每个输出函数都散布在其自己的文件中。将来可能会提供一个选项,以将组合函数共同到输出文件中,这应该有助于通过在构建过程中减少文件I/O来改善重新编译器输出的构建时间。
可以使用任何C编译器(用MSVC,GCC和CLANG测试)编译重新编译器输出。预计输出将与运行时一起使用,该运行时间可以提供必要的功能和宏实现来运行它。 N64ModernRuntime提供了一个运行时,可以在Zelda 64:重编译项目中看到。
该工具都可以处理静态链接和重新定位的叠加层。在这两种情况下,该工具发射了跳连接登录的功能查找(即功能指针或虚拟功能),提供的运行时可以使用任何类型的查找表实现。例如,指令jalr $25
将被重新编译为LOOKUP_FUNC(ctx->r25)(rdram, ctx);
然后,运行时可以维护一个加载程序部分的列表,以及在运行时触发查找时要运行的函数的地址。
对于可重新定位的覆盖层,该工具将通过发出额外的宏来修改具有重新定位数据( lui
, addiu
,LOAD和Store指令)的支持指令,以使运行时可以重新安置指令的直接值字段。例如,从地址开始的一节中的指令lui $24, 0x80C0
在0x80BFA100
开始,其搬迁的符号将被固定为0x80BFA730
的符号将被重新编译为ctx->r24 = S32(RELOC_HI16(1754, 0X630) << 16);
,其中1754年是本节的索引。然后,运行时可以实现reloc_hi16和relioc_lo16宏,以根据本节的当前加载地址处理即时进行修改。
对TLB映射的重新定位的支持将来将在将来增加,这将增加提供MIPS32重新卸载列表的能力,以便运行时可以在负载下重新安置它们。将其与可重新定位覆盖的功能相结合,应允许运行大多数TLB映射的代码,而不会在每个RAM访问中遭受性能罚款。
通过提供一个TOML文件以配置Recompiler行为,这是提供给Recompiler的唯一参数,从而配置了重新编译器。 TOML是您指定输入和输出文件路径的位置,并且可选地删除特定功能,跳过特定功能的重新编码以及目标二进制中的单个指令。还有计划的功能可以通过将其添加到TOML( [[patches.func]]
和[[patches.hook]]
段的链接toml的章节中,可以在重新编译器输出中发出挂钩,但这当前是未完成。有关重新编译器提供的所有选项的文档目前尚不可用,但是可以在此处的Zelda 64:重新编译的项目中找到一个示例。
当前,提供所需元数据的唯一方法是将精灵文件传递给该工具。获得这样的精灵的最简单方法是建立目标二进制文件的拆卸或解码,但是将支持通过自定义格式提供元数据,以绕过将来这样做的需求。
该工具还可以配置为通过配置TOML中的选项以“单文件输出”模式重新编译。这将使所提供的ELF中的所有功能都放在单个输出文件中。此模式的目的是能够从目标二进制文件中编译函数的修补版本。
该模式可以与几乎所有链接器(LD,LLD,MSVC的链接。Exe等)提供的功能结合使用,以用原始的Recompiler Output用修改版本替换功能。这些链接器仅在以前的输入文件中没有找到静态库中的符号,因此在提供原始重新编译器输出之前,将重编译的补丁程序提供给链接器,将导致补丁比具有相同名称的功能优先从原始的重新编译器输出。
在目标二进制的贴片上迭代时,这节省了大量时间,因为您可以绕过目标二进制中的重新编译器并编译原始的重新编译器输出。可以在此处的Zelda 64:重新编译的项目中找到一个单个文件输出模式的示例,其中相应的makefile用于在此处为这些补丁构建精灵。
RSP Microcode也可以使用此工具重新编译。当前没有支持重新编译RSP叠加层的支持,但是如果需要,它可能会在将来添加。关于如何使用此功能的文档将很快推出。
该项目可以使用CMAKE 3.20或更高版本以及支持C ++ 20的C ++编译器构建。此存储库使用git子模块,因此请确保递归克隆( git clone --recurse-submodules
)或在克隆后递归初始化子模块( git submodule update --init --recursive
)。从那里开始,构建与任何其他CMAKE项目相同,例如在目标构建文件夹中运行cmake
,并将其指向此存储库的根,然后运行cmake --build .
从该目标文件夹。