N64 编程/编译
外观
< N64 编程
N64 绝不是资源受限的,因此用 C 语言为其编写软件是完全合理的。不过,您必须牢记一件事:为 N64 编码需要深入了解 C 语言和 MIPS R4K 汇编。但是,汇编只需要在初始化 N64(或处理异常)的小例程中使用。您还需要熟悉 GNU 工具链(主要是 binutils 和 gcc)。
选择一个您想要编译二进制文件的目录,将其设置为 $PREFIX,并确保它存在。例如,
export PREFIX=/opt/n64
mkdir -p $PREFIX
接下来,将 $GCC 设置为您将使用的编译器。例如,
export GCC=gcc
下载 binutils 源代码(我们将使用 2.35.1 版本),并解压缩它。
创建一个树外构建目录,并进入其中
mkdir -p build-binutils
cd build-binutils
然后运行以下命令
../binutils-2.35.1/configure \
--target=mips64-elf --prefix=$PREFIX \
--program-prefix=mips64- --with-cpu=vr4300 \
--with-sysroot --disable-nls --disable-werror
make CC=$GCC
make install
希望一切顺利,现在您将拥有一个针对 MIPS 的 binutils 软件包。回到您的工作目录,现在该构建 GCC 了。
GCC 需要使用您上面编译的一些二进制文件,因此请执行以下操作
export PATH=$PREFIX/bin:$PATH
这将使 GCC 能够找到它们。
与 binutils 一样,下载 gcc 源代码(我们将使用 10.2.0 版本)并解压缩它。
创建一个树外构建目录,并进入其中
mkdir -p build-gcc
cd build-gcc
然后运行以下命令
../gcc-10.2.0/configure \
--target=mips64-elf --prefix=$PREFIX \
--program-prefix=mips64- --with-arch=vr4300 \
-with-languages=c,c++ --disable-threads \
--disable-nls --without-headers
make all-gcc CC=$GCC
make all-target-libgcc CC=$GCC
make install-gcc
make install-target-libgcc
mips64 工具链现在应该安装在您选择的 $PREFIX/bin 中。
虽然为 N64 编写 C 代码与任何其他平台没有区别(除了您没有可用的库),但您有时可能需要写入内存映射寄存器。以下示例(使用 DMA)演示了这一点
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **
** N64 DMA **
** ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
typedef struct
{
/* Pointers to data */
void *ramp;
void *romp;
/* Filesizes (8-byte aligned) */
u32 size_ramrom; /* RAM -> ROM */
u32 size_romram; /* RAM <- ROM */
/* Status register */
u32 status;
} DMA_REG;
/* DMA status flags */
enum
{
DMA_BUSY = 0x00000001,
DMA_ERROR = 0x00000008
};
/* DMA registers ptr */
static volatile DMA_REG * dmaregs = (DMA_REG*)0xA4600000;
/* Copy data from ROM to RAM */
int dma_write_ram ( void *ram_ptr, void *rom_ptr, u32 length )
{
/* Check that DMA is not busy already */
while( dmaregs->status & DMA_BUSY );
/* Write addresses */
dmaregs->ramp = (u32)ram_ptr & 0x00FFFFFF; /* ram pointer */
dmaregs->romp = (u32)rom_ptr & 0x1FFFFFFF; /* rom pointer */
/* Write size */
dmaregs->size_romram = length - 1;
/* Wait for transfer to finish */
while( dmaregs->status & DMA_BUSY );
/* Return size written */
return length & 0xFFFFFFF8;
}
您可能还必须经常使用内联汇编。以下函数在内存区域设置断点
enum
{
BREAKPOINT_READ = 1,
BREAKPOINT_WRITE = 2
};
/* Set breakpoint */
void bp_set ( u32 addr, u8 flags )
{
addr &= 0x3FFFF8; /* assuming lower 4MB, also doubleword */
flags &= 0x03; /* only lower two bits */
addr |= flags;
asm("mtc0 %0, $18\n" /* WatchLo */
"mtc0 $zero, $19\n" /* WatchHi */
::"r"(addr));
}