N64 编程/内存映射
外观
< N64 编程
CPU Main Memory (RDRAM) (2) CDROM (8) | | | ------------------------------------------ | | Registers (1) Cartridge (ROM) (4)
当然,我们希望将数据保存在寄存器中(1 个时钟周期)。
让 CPU 从 CDROM 执行会很慢(8 个周期)。即使是卡带内存也更快。
最有可能的是,游戏会将重要的代码和数据复制到 RDRAM 以减少加载时间。并提高执行速度。
这是通过直接内存访问 (DMA) 完成的,快速传输。
请注意,由于我们从 RAM 运行,因此您也可能会发现自修改代码。
因为代码是从内存中运行的,所以它是从 RAM 编译的,而不是 ROM 地址。
想想 8 位 NES/SMS/GB 页面偏移量。
[0361:d824] 8002A14C: AND k1[0000FF03],k1[0000FF03],at[FFFF00FF] [0369:d825] 8002A150: OR k1[00000003],k1[00000003],t1[0000FF00] [3c09:a430] 8002A154: LUI t1[0000FF00],FFFFA430h
位于 ROM $554C(不可思议的迷宫 2)。
我们的内存映射从 $0000:0000-FFFF:FFFF。
COP0 可以访问 $2000:0000 及以上。这意味着它可以改变指向 24 位偏移量(16 MB)的物理地址。或者 8MB 下降到 4KB 页面。
一般而言,
- $0000:0000 = ROM。
- $1000:0000 = ROM。
- $8000:0000 = RDRAM。代码。
- $A000:0000 = RDRAM。数据。
- $A400:0000 = PI,SI。DMA 寄存器。
- $B000:0000 = ROM(DMA,LD)。
您会看到它被称为“转换后备缓冲区”(TLB)。
DMA 是所有现代系统中的一种功能,用于在不涉及 CPU 的情况下快速在不同组件之间移动数据。需要注意的是,PI DMA 内存传输以 64 位块进行。我们可以从
- RDRAM 到/从卡带(ROM,SRAM,FlashRAM,..)
- RDRAM 到/从 RCP
DMA 寄存器
- $A460:0000 = RAM 地址(address & 0x00FFFFFF)
- $A460:0004 = ROM 地址(address & 0x1FFFFFFF)
- $A460:0008 = 传输大小(从 RAM 到卡带)
- $A460:000C = 传输大小(从卡带到 RAM)
- $A460:0010 = DMA 状态
最后两个地址接受 DMA 复制长度(减 1 - BPL 循环)并启动传输。您写入哪个长度寄存器取决于您想要的传输方向。一旦传输启动,可以通过读取状态寄存器来检查传输状态。以下标志可用于检查状态
- DMA_BUSY = 0x00000001
- DMA_ERROR = 0x00000008
以下是使用 N64 的 DMA 功能的一些示例 C 代码
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ **
** 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;
}