pureikyubu/Docs/RE/ar.txt
ogamespec c91662b127 DspCore WIP
- RTI in analyzer
- IROM mapped in DMEM
- ARAM -> IRAM DMA size multiplied by 4
- OSInitAudioSystem stub reversing
2020-03-18 11:45:24 +03:00

479 lines
12 KiB
Text

// ARAM library reversing by org <ogamespec@gmail.com>
// ----------------------------------------------------------------------------
//
// internal variables
//
static ARCallback __AR_Callback; // interrupt handler callback
static u32 __AR_Size;
static u32 __AR_InternalSize; // standard ARAM size in bytes
static u32 __AR_ExpansionSize; // expansion ARAM size in bytes
static BOOL __AR_init_flag; // are we initialized ?
// some variables for ARAlloc / ARFree
static u32 __AR_StackPointer;
static u32 __AR_FreeBlocks;
static u32 * __AR_BlockLength;
//
// known ARAM controller registers
//
#define AR_SIZE (*(volatile u16 *)(0xCC005012))
#define AR_MODE (*(volatile u16 *)(0xCC005016))
#define AR_REFRESH (*(volatile u16 *)(0xCC00501A))
#define AR_DMA_MMADDR_H (*(volatile u16 *)(0xCC005020))
#define AR_DMA_MMADDR_L (*(volatile u16 *)(0xCC005022))
#define AR_DMA_ARADDR_H (*(volatile u16 *)(0xCC005024))
#define AR_DMA_ARADDR_L (*(volatile u16 *)(0xCC005026))
#define AR_DMA_CNT_H (*(volatile u16 *)(0xCC005028))
#define AR_DMA_CNT_L (*(volatile u16 *)(0xCC00502A))
//
// DSP interface main control register
//
// AIDCR bits
#define AIDCR_ARDMA (1 << 9) // ARAM dma in progress, if set
#define AIDCR_DSPINTMSK (1 << 8) // * interrupt mask (RW)
#define AIDCR_DSPINT (1 << 7) // * interrupt active (RWC)
#define AIDCR_ARINTMSK (1 << 6)
#define AIDCR_ARINT (1 << 5)
#define AIDCR_AIINTMSK (1 << 4)
#define AIDCR_AIINT (1 << 3)
#define AIDCR_HALT (1 << 2) // halt DSP
#define AIDCR_PIINT (1 << 1) // assert DSP PI interrupt
#define AIDCR_RES (1 << 0) // reset DSP
#define AIDCR (*(volatile u16 *)(0xCC00500A))
// ----------------------------------------------------------------------------
ARCallback ARRegisterDMACallback(ARCallback callback)
{
BOOL old = OSDisableInterrupts();
ARCallback old_callback = __AR_Callback;
__AR_Callback = callback;
OSRestoreInterrupts(old);
return old_callback;
}
u32 ARGetDMAStatus(void)
{
BOOL old = OSDisableInterrupts;
u32 val = *(u16 *)(0xCC00500A) & 0x0200;
OSRestoreInterrupts(old);
return val;
}
#define ARAM_DIR_MRAM_TO_ARAM 0
#define ARAM_DIR_ARAM_TO_MRAM 1
void ARStartDMA(u32 type, u32 mainmem_addr, u32 aram_addr, u32 length)
{
BOOL old = OSDisableInterrupts();
//
// do some checks
// not important for emulator
//
if(*(u16 *)(0xCC00500A) & 0x0200)
{
OSHalt("ARAM DMA already in progress\n");
}
if(mainmem_addr & 0x1f)
{
OSHalt("AR: Main memory address is not a multiple of 32 bytes!\n");
}
if(length & 0x1f)
{
OSHalt("AR: DMA transfer length is not a multiple of 32 bytes!\n");
}
//
// do transfer
//
AR_DMA_MMADDR_H = (AR_DMA_MMADDR_H & 0x03FF) | (mainmem_addr >> 16);
AR_DMA_MMADDR_L = (AR_DMA_MMADDR_L & 0x001F) | (mainmem_addr & 0xFFFF);
AR_DMA_ARADDR_H = (AR_DMA_ARADDR_H & 0x03FF) | (aram_addr >> 16);
AR_DMA_ARADDR_L = (AR_DMA_ARADDR_L & 0x001F) | (aram_addr & 0xFFFF);
AR_DMA_CNT_H = (AR_DMA_CNT_H & 0x7FFF) | (type << 15);
AR_DMA_CNT_H = (AR_DMA_CNT_H & 0x03FF) | (length >> 16);
AR_DMA_CNT_L = (AR_DMA_CNT_L & 0x001F) | (length & 0xFFFF);
OSRestoreInterrupts(old);
}
//
// allocation wrappers
//
u32 ARAlloc(u32 length)
{
u32 tmp;
BOOL old = OSDisableInterrupts();
if(length & 0x1f)
{
OSHalt("ARAlloc(): length is not multiple of 32bytes!\n");
}
if(__AR_StackPointer > __AR_Size)
{
OSHalt("ARAlloc(): Out of ARAM!\n");
}
if(__AR_FreeBlocks == 0)
{
OSHalt("ARAlloc(): No more free blocks!\n");
}
tmp = __AR_StackPointer;
__AR_StackPointer += length;
*__AR_BlockLength++ = length;
__AR_FreeBlocks--;
OSRestoreInterrupts(old);
return tmp;
}
u32 ARFree(u32 *length)
{
BOOL old = OSDisableInterrupts();
__AR_BlockLength--;
if(__AR_BlockLength)
{
*length = *__AR_BlockLength;
}
__AR_StackPointer -= *__AR_BlockLength;
__AR_FreeBlocks++;
OSRestoreInterrupts(old);
return __AR_StackPointer;
}
BOOL ARCheckInit(void)
{
return __AR_init_flag;
}
u32 ARInit(u32 *stack_index_addr, u32 num_entries)
{
BOOL old;
// return default aram base, if already inited
if(__AR_init_flag) return 0x4000; // Skip first 16 KBytes
// disable all interrupts
old = OSDisableInterrupts();
// clear DMA callback
__AR_Callback = NULL;
// set ARAM interrupt handler
__OSSetInterruptHandler(
__OS_INTERRUPT_DSP_ARAM,
__ARHandler
);
// enable ARAM interrupt
__OSUnmaskInterrupts(OS_INTERRUPTMASK_DSP_ARAM);
// setup ARAM stack environment
__AR_StackPointer = 0x4000;
__AR_FreeBlocks = num_entries;
__AR_BlockLength = stack_index_addr;
// initialize ARAM aggregate refresh frequency
#ifndef NDEBUG
if((float)(AR_REFRESH & 0xFF) != 176.0)
{
OSHalt("ARInit(): ILLEGAL SDRAM REFRESH VALUE\n");
}
#endif
AR_REFRESH |= (AR_REFRESH << 8);
// check size of ARAM (look for expansion)
// DONT USE EMPTY HLE !
__ARChecksize();
// all is ok and ARAM is ready
__AR_init_flag = TRUE;
OSRestoreInterrupts(old);
return __AR_StackPointer;
}
void ARReset(void)
{
__AR_init_flag = FALSE;
}
void ARSetSize(void)
{
OSReport("ARSetSize(): I don't do anything anymore!\n");
}
u32 ARGetBaseAddress(void)
{
return 0x4000; // Skip first 16 KBytes
}
u32 ARGetSize(void)
{
return __AR_Size;
}
u32 ARGetInternalSize(void)
{
return __AR_InternalSize;
}
// possible flag values
#define AR_CLEAR_INTERNAL_ALL 0x0 // whole 16 mb of aram, starting from 0
#define AR_CLEAR_INTERNAL_USER 0x1 // only starting from 0x4000
#define AR_CLEAR_EXPANSION_ALL 0x2 // clear expansion aram
void ARClear(u32 flag)
{
switch(flag)
{
case AR_CLEAR_INTERNAL_ALL:
if(__AR_InternalSize)
{
__ARClearArea(0, __AR_InternalSize);
}
break;
case AR_CLEAR_INTERNAL_USER:
if(__AR_InternalSize)
{
__ARClearArea(0x4000, __AR_InternalSize - 0x4000);
}
break;
case AR_CLEAR_EXPANSION_ALL:
if(__AR_InternalSize && __AR_ExpansionSize)
{
__ARClearArea(__AR_InternalSize, __AR_ExpansionSize);
}
break;
default:
OSHalt("ARClear(): Unknown flag.\n");
}
}
static void __ARHandler(__OSInterrupt interrupt, OSContext *context)
{
OSContext exceptionContext;
// clear ARINT
u16 tmp = AIDCR & ~(AIDCR_AIINT | AIDCR_DSPINT);
tmp |= AIDCR_ARINT;
AIDCR = tmp;
OSClearContext(&exceptionContext);
OSSetCurrentContext(&exceptionContext);
// user callback
if(__AR_Callback) __AR_Callback();
OSClearContext(&exceptionContext);
OSSetCurrentContext(context);
}
// clear AR trransfer complete interrupt bit
void __ARClearInterrupt(void)
{
// dont touch other interrupt bits
u16 tmp = AIDCR & ~(AIDCR_AIINT | AIDCR_DSPINT);
// clear ARINT
tmp |= AIDCR_ARINT;
// update register
AIDCR = tmp;
}
// zero, if no AR interrupt
// non-zero, otherwise
u32 __ARGetInterruptStatus(void)
{
return (AIDCR & AIDCR_ARINT);
}
void __ARWaitForDMA(void)
{
while(AIDCR & AIDCR_ARDMA) ;
}
void inline __ARWriteDMA(u32 mmem_addr, u32 aram_addr, u32 length)
{
AR_DMA_MMADDR_H = (AR_DMA_MMADDR_H & 0x03FF) | (mainmem_addr >> 16);
AR_DMA_MMADDR_L = (AR_DMA_MMADDR_L & 0x001F) | (mainmem_addr & 0xFFFF);
AR_DMA_ARADDR_H = (AR_DMA_ARADDR_H & 0x03FF) | (aram_addr >> 16);
AR_DMA_ARADDR_L = (AR_DMA_ARADDR_L & 0x001F) | (aram_addr & 0xFFFF);
AR_DMA_CNT_H = (AR_DMA_CNT_H & 0x7FFF) | (ARAM_DIR_MRAM_TO_ARAM << 15);
AR_DMA_CNT_H = (AR_DMA_CNT_H & 0x03FF) | (length >> 16);
AR_DMA_CNT_L = (AR_DMA_CNT_L & 0x001F) | (length & 0xFFFF);
__ARWaitForDMA();
__ARClearInterrupt();
}
void inline __ARReadDMA(u32 mmem_addr, u32 aram_addr, u32 length)
{
AR_DMA_MMADDR_H = (AR_DMA_MMADDR_H & 0x03FF) | (mainmem_addr >> 16);
AR_DMA_MMADDR_L = (AR_DMA_MMADDR_L & 0x001F) | (mainmem_addr & 0xFFFF);
AR_DMA_ARADDR_H = (AR_DMA_ARADDR_H & 0x03FF) | (aram_addr >> 16);
AR_DMA_ARADDR_L = (AR_DMA_ARADDR_L & 0x001F) | (aram_addr & 0xFFFF);
AR_DMA_CNT_H = (AR_DMA_CNT_H & 0x7FFF) | (ARAM_DIR_ARAM_TO_MRAM << 15);
AR_DMA_CNT_H = (AR_DMA_CNT_H & 0x03FF) | (length >> 16);
AR_DMA_CNT_L = (AR_DMA_CNT_L & 0x001F) | (length & 0xFFFF);
__ARWaitForDMA();
__ARClearInterrupt();
}
#define ARAM_2MB 2 * 1024 * 1024
#define ARAM_16MB 16 * 1024 * 1024
#define ARAM_20MB 16 * 1024 * 1024
#define ARAM_32MB 16 * 1024 * 1024
#define ARAM_48MB 16 * 1024 * 1024
void __ARChecksize(void)
{
u32 test_data_pad[8];
u32 dummy_data_pad[8];
u32 buffer_pad[8];
u32 *test_data, *dummy_data, *buffer;
u16 ARAM_mode, ARAM_size;
// wait until ARAM is "ready"
// this loop is locking emulator, if no ARAM_MODE hack
while( ( (r0 = ARAM_MODE) & 0x0001 ) == 0) ;
__AR_InternalSize = ARAM_size = ARAM_16MB;
r0 = ARAM_SIZE & 0xFFC0; // clear last 6 bits
r0 |= 0x0003;
r0 |= 0x0020;
ARAM_SIZE = r0;
// make sure test buffers are 32-byte aligned
test_data = OSRoundDown32B(test_data_pad);
dummy_data = OSRoundDown32B(dummy_data_pad);
buffer = OSRoundDown32B(buffer_pad);
// fill buffers by test data (leet N language again ?)
for(n=0; n<8; n++)
{
test_data[n] = 0xDEADBEEF;
dummy_data[n] = 0xBAD0BAD0;
}
// make sure our test data is in RAM
DCFlushRange(test_data, 32);
DCFlushRange(dummy_data, 32);
// no expansion by default
__AR_ExpansionSize = 0;
// write test data in various ARAM regions
__ARWriteDMA(test_data, ARAM_16MB, 32);
__ARWriteDMA(test_data, ARAM_16MB + ARAM_2MB, 32);
__ARWriteDMA(test_data, ARAM_32MB, 32);
__ARWriteDMA(test_data, ARAM_48MB, 32);
__ARWriteDMA(test_data, ARAM_20MB, 32);
// now read and check with test data
memset(buffer, 0, 32);
DCFlushRange(buffer, 32);
__ARWriteDMA(dummy_data, ARAM_16MB, 32);
DCInvalidateRange(buffer, 32);
__ARReadDMA(buffer, ARAM_16MB, 32);
PPCSync();
if(buffer[0] == dummy_data[0])
{
// * ARAM EXPANSION DETECTED *
// now check expansion amount, by reading test data
// expansion checking is buggy..
// production boards has 16 MB of ARAM, so dont care
OSReport("__ARChecksize(): ARAM Expansion present.\n");
r0 = AR_SIZE & 0xFFC0;
r0 |= 0x0003;
AR_SIZE = r0;
}
// save ARAM configuration
OSPhysicalToUncached(0x000000D0) = ARAM_size; // in OS global
__AR_Size = ARAM_size; // for ARAM driver
}
// clear specified region of ARAM
void __ARClearArea(u32 start_addr, u32 length)
{
u8 zero_buffer[2100], *ptr;
u32 remainder;
u32 curr_addr, curr_len, end_addr;
//
// some checks
//
if(start_addr & 0x1f)
{
OSHalt("__ARClearArea(): Destination address not 32-byte aligned.\n");
}
if(length & 0x1f)
{
OSHalt("__ARClearArea(): Length not multiple of 32 bytes.\n");
}
ptr = OSRoundDown32B(zero_buffer);
// wait, until ARAM is "ready"
while((AR_MODE & 1) == 0) ;
memset(ptr, 0, 2048);
DCFlushRange(ptr, 2048);
curr_addr = start_addr;
end_addr = start_addr + length;
while(curr_addr < end_addr)
{
// buggy ?
remainder = end_addr - curr_addr;
if(remainder >= 2048) remainder = 2048;
curr_len = (remainder + 31) & 0x3f; <-- maybe & 0x1f ?
__ARWriteDMA(ptr, curr_addr, curr_len);
curr_addr += curr_len;
}
}