189 lines
4.0 KiB
C
189 lines
4.0 KiB
C
#include <ultra64.h>
|
|
#include "constants.h"
|
|
#include "bss.h"
|
|
#include "lib/dma.h"
|
|
#include "data.h"
|
|
#include "types.h"
|
|
|
|
volatile u32 g_DmaNumSlotsBusy;
|
|
u32 var80094ae4;
|
|
OSIoMesg g_DmaIoMsgs[32];
|
|
volatile u8 g_DmaSlotsBusy[32];
|
|
OSMesg g_DmaMesgs[32];
|
|
OSMesgQueue g_DmaMesgQueue;
|
|
|
|
u8 g_LoadType = 0;
|
|
|
|
void dmaInit(void)
|
|
{
|
|
s32 i;
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_DmaSlotsBusy); i++) {
|
|
g_DmaSlotsBusy[i] = 0;
|
|
}
|
|
|
|
g_DmaNumSlotsBusy = 0;
|
|
|
|
osCreateMesgQueue(&g_DmaMesgQueue, g_DmaMesgs, ARRAYCOUNT(g_DmaMesgs));
|
|
}
|
|
|
|
void dmaStart(void *memaddr, u32 romaddr, u32 len, bool priority)
|
|
{
|
|
u32 numiterations;
|
|
u32 remainder;
|
|
s32 i;
|
|
|
|
#if VERSION < VERSION_NTSC_1_0
|
|
if (romaddr >= 1024 * 1024 * 32) {
|
|
crashSetMessage("DMA : Off the end of the rom");
|
|
CRASH();
|
|
}
|
|
#endif
|
|
|
|
if (g_DmaNumSlotsBusy) {
|
|
dmaWait();
|
|
}
|
|
|
|
if (len < 0x4000 * ARRAYCOUNT(g_DmaIoMsgs)) {
|
|
numiterations = len / 0x4000;
|
|
remainder = len % 0x4000;
|
|
} else {
|
|
// DMA size is 0x80000 or more. It won't fit in the queue, so do it
|
|
// all in one call to osPiStartDma using the remainder variable.
|
|
numiterations = 0;
|
|
remainder = len;
|
|
}
|
|
|
|
osInvalDCache(memaddr, len);
|
|
|
|
for (i = 0; i != numiterations; i++) {
|
|
g_DmaSlotsBusy[i] = true;
|
|
g_DmaNumSlotsBusy++;
|
|
|
|
osPiStartDma(&g_DmaIoMsgs[i], priority, 0, romaddr, memaddr, 0x4000, &g_DmaMesgQueue);
|
|
|
|
romaddr += 0x4000;
|
|
memaddr = (void *)((u32)memaddr + 0x4000);
|
|
}
|
|
|
|
if (remainder) {
|
|
g_DmaSlotsBusy[i] = true;
|
|
g_DmaNumSlotsBusy++;
|
|
|
|
osPiStartDma(&g_DmaIoMsgs[i], priority, 0, romaddr, memaddr, remainder, &g_DmaMesgQueue);
|
|
}
|
|
}
|
|
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
u32 xorDeadbeef(u32 value)
|
|
{
|
|
return value ^ 0xdeadbeef;
|
|
}
|
|
|
|
u32 xorDeadbabe(u32 value)
|
|
{
|
|
return value ^ 0xdeadbabe;
|
|
}
|
|
|
|
/**
|
|
* This is executed after a DMA transfer. It xors the first 8 words with
|
|
* 0x0330c820, then reads a value from the boot loader (0x340 in ROM) which
|
|
* should be the same value, and xors the memory again with that value.
|
|
*/
|
|
void dmaCheckPiracy(void *memaddr, u32 len)
|
|
{
|
|
if (g_LoadType != LOADTYPE_NONE && len > 128) {
|
|
#if PIRACYCHECKS
|
|
u32 value = xorDeadbeef((PAL ? 0x0109082b : 0x0330c820) ^ 0xdeadbeef);
|
|
u32 *ptr = (u32 *)memaddr;
|
|
u32 data;
|
|
u32 devaddr;
|
|
s32 i;
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
ptr[i] ^= value;
|
|
}
|
|
|
|
devaddr = xorDeadbabe((PAL ? 0xb0000454 : 0xb0000340) ^ 0xdeadbabe);
|
|
|
|
osPiReadIo(devaddr, &data);
|
|
|
|
for (i = 0; i < 8; i++) {
|
|
ptr[i] ^= data;
|
|
}
|
|
#endif
|
|
|
|
g_LoadType = LOADTYPE_NONE;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
void dmaWait(void)
|
|
{
|
|
u32 stack;
|
|
OSIoMesg *msg;
|
|
s32 i;
|
|
|
|
while (g_DmaNumSlotsBusy) {
|
|
osRecvMesg(&g_DmaMesgQueue, (OSMesg) &msg, OS_MESG_BLOCK);
|
|
|
|
for (i = 0; i < ARRAYCOUNT(g_DmaIoMsgs); i++) {
|
|
if (&g_DmaIoMsgs[i] == msg) {
|
|
break;
|
|
}
|
|
}
|
|
|
|
g_DmaSlotsBusy[i] = false;
|
|
g_DmaNumSlotsBusy--;
|
|
}
|
|
}
|
|
|
|
void dmaExec(void *memaddr, u32 romaddr, u32 len)
|
|
{
|
|
dmaStart(memaddr, romaddr, len, false);
|
|
dmaWait();
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
dmaCheckPiracy(memaddr, len);
|
|
#endif
|
|
}
|
|
|
|
void dmaExecHighPriority(void *memaddr, u32 romaddr, u32 len)
|
|
{
|
|
dmaStart(memaddr, romaddr, len, true);
|
|
dmaWait();
|
|
#if VERSION >= VERSION_NTSC_1_0
|
|
dmaCheckPiracy(memaddr, len);
|
|
#endif
|
|
}
|
|
|
|
/**
|
|
* DMA data from ROM to RAM with automatic alignment.
|
|
*
|
|
* The destination memory address is aligned to 0x10.
|
|
*
|
|
* The ROM address is aligned to 2 bytes (ie. subtract 1 if ROM address is odd).
|
|
* If this is done then the returned memory pointer is bumped forwards by one
|
|
* to compensate. The length of data to be transferred is also increased by one
|
|
* to make it 2-byte aligned.
|
|
*
|
|
* It is assumed that the passed len is 2-byte aligned (ie. an even number).
|
|
*
|
|
* If a length of zero is passed, no DMA is done. This can be used to retrieve
|
|
* the memory address that would have been returned.
|
|
*/
|
|
void *dmaExecWithAutoAlign(void *memaddr, u32 romaddr, u32 len)
|
|
{
|
|
u32 alignedrom = ALIGN2(romaddr);
|
|
u32 alignedmem = ALIGN16((u32)memaddr);
|
|
u32 offset = romaddr - alignedrom; // 0 or 1
|
|
u32 alignedlen = ALIGN16(offset + len);
|
|
|
|
if (len == 0) {
|
|
return (void *)(alignedmem + offset);
|
|
}
|
|
|
|
dmaExec((void *)alignedmem, alignedrom, alignedlen);
|
|
|
|
return (void *)(alignedmem + offset);
|
|
}
|