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;
}