mirror of
https://github.com/pret/pokestadium.git
synced 2026-03-22 09:44:18 -05:00
224 lines
7.1 KiB
C
224 lines
7.1 KiB
C
#include <ultra64.h>
|
|
#include "memmap.h"
|
|
|
|
/*
|
|
* Set the base virtual address and size of the specified segment memmap.
|
|
*/
|
|
void Memmap_SetSegmentMap(u32 id, uintptr_t vaddr, size_t size) {
|
|
gSegments[id].vaddr = vaddr;
|
|
gSegments[id].size = size;
|
|
}
|
|
|
|
/*
|
|
* Return the base address of the specified segment number.
|
|
*/
|
|
uintptr_t Memmap_GetSegmentBaseVaddr(u32 id) {
|
|
return gSegments[id].vaddr;
|
|
}
|
|
|
|
/*
|
|
* Return the current virtual address of a given offset into a segment. This is
|
|
* calculated based on the input bitmask constructed of the following values:
|
|
*
|
|
* Input: XYZZZZZZ
|
|
* X: Safety nibble. Only process if 0.
|
|
* Y: Segment number.
|
|
* Z: Segment offset.
|
|
*/
|
|
uintptr_t Memmap_GetSegmentVaddr(u32 mask) {
|
|
if ((mask >> 0x1C) == 0) {
|
|
u32 id = (mask & 0x0F000000) >> 0x18;
|
|
u32 value = (mask & 0x00FFFFFF) >> 0x00;
|
|
if (gSegments[id].vaddr != NULL) {
|
|
mask = gSegments[id].vaddr + value;
|
|
}
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
/*
|
|
* Returns the input bitmask equivalent for Memmap_GetSegmentVaddr's input. This function
|
|
* appears to be unused.
|
|
*/
|
|
u32 Memmap_GetSegmentVaddrMask(u32 i, uintptr_t addr) {
|
|
struct MemoryMap* entry = &gSegments[i];
|
|
size_t diff = addr - entry->vaddr;
|
|
|
|
if (diff >= entry->size) {
|
|
return 0;
|
|
}
|
|
return (i << 0x18) | diff;
|
|
}
|
|
|
|
/*
|
|
* Clear the memmap for the segment ID specified.
|
|
*/
|
|
void Memmap_ClearSegmentMemmap(u32 id) {
|
|
gSegments[id].vaddr = NULL;
|
|
gSegments[id].size = 0;
|
|
}
|
|
|
|
/*
|
|
* Initialize the 16 segments with the segment memmap virtual addresses for the
|
|
* display list head.
|
|
*/
|
|
void Memmap_SetSegments(Gfx** gfxDl) {
|
|
u32 i;
|
|
Gfx* gfx = *gfxDl;
|
|
|
|
for (i = 0; i < 16; i++) {
|
|
gSPSegment(gfx++, i, osVirtualToPhysical((void*)gSegments[i].vaddr));
|
|
}
|
|
*gfxDl = gfx;
|
|
}
|
|
|
|
/*
|
|
* Perform fragment relocation on a given Fragment.
|
|
*/
|
|
void Memmap_RelocateFragment(u32 id, struct Fragment* fragment) {
|
|
u32 isLoNeg;
|
|
u32* luiRefs[32];
|
|
u32 luiVals[32];
|
|
u32* luiInstRef;
|
|
u32* relocDataP;
|
|
u32 relocSize;
|
|
struct RelocTable* relocInfo;
|
|
UNUSED u32 relocOffset;
|
|
u32 reloc;
|
|
u32 temp_v0_5;
|
|
u32 i;
|
|
u32* regValP;
|
|
UNUSED s32 pad;
|
|
|
|
relocOffset = fragment->relocOffset;
|
|
relocSize = fragment->sizeInRam - fragment->relocOffset;
|
|
relocInfo = (struct RelocTable*)((uintptr_t)fragment->relocOffset + (uintptr_t)fragment);
|
|
|
|
osInvalICache(fragment, fragment->sizeInRam);
|
|
osInvalDCache(fragment, fragment->sizeInRam);
|
|
Memmap_SetFragmentMap(id, (uintptr_t)fragment, fragment->sizeInRam);
|
|
|
|
for (i = 0; i < relocInfo->nRelocations; i++) {
|
|
reloc = relocInfo->relocations[i];
|
|
relocDataP = (u32*)((reloc & 0xFFFFFF) + (uintptr_t)fragment);
|
|
|
|
switch ((reloc & 0x7F000000) >> 24) {
|
|
case R_MIPS_32:
|
|
// Handles 32-bit address relocation, used for things such as jump tables and pointers in data.
|
|
// Just relocate the full address.
|
|
*relocDataP = Memmap_GetFragmentVaddr(*relocDataP);
|
|
break;
|
|
case R_MIPS_26:
|
|
// Handles 26-bit address relocation, used for jumps and jals.
|
|
// Extract the address from the target field of the J-type MIPS instruction.
|
|
// Relocate the address and update the instruction.
|
|
*relocDataP =
|
|
(((u32)(Memmap_GetFragmentVaddr(((*relocDataP * 4) & 0x0FFFFFFC) + 0x80000000) & 0x0FFFFFFF) >> 2) |
|
|
(*relocDataP & 0xFC000000));
|
|
break;
|
|
case R_MIPS_HI16:
|
|
// Handles relocation for a hi/lo pair, part 1.
|
|
// Store the reference to the LUI instruction (hi) using the `rt` register of the instruction.
|
|
// This will be updated later in the `R_MIPS_LO16` section.
|
|
luiRefs[(*relocDataP >> 0x10) & 0x1F] = relocDataP;
|
|
luiVals[(*relocDataP >> 0x10) & 0x1F] = *relocDataP;
|
|
break;
|
|
case R_MIPS_LO16:
|
|
// Handles relocation for a hi/lo pair, part 2.
|
|
// Grab the stored LUI (hi) from the `R_MIPS_HI16` section using the `rs` register of the instruction.
|
|
// The full address is calculated, relocated, and then used to update both the LUI and lo instructions.
|
|
// If the lo part is negative, add 1 to the LUI value.
|
|
// Note: The lo instruction is assumed to have a signed immediate.
|
|
luiInstRef = luiRefs[(*relocDataP >> 0x15) & 0x1F];
|
|
regValP = &luiVals[(*relocDataP >> 0x15) & 0x1F];
|
|
|
|
temp_v0_5 = Memmap_GetFragmentVaddr((*regValP << 0x10) + (s16)*relocDataP);
|
|
isLoNeg = (temp_v0_5 & 0x8000) ? 1 : 0;
|
|
*luiInstRef = (u32)((*luiInstRef & 0xFFFF0000) | ((temp_v0_5 >> 16) + isLoNeg));
|
|
*relocDataP = (u32)((*relocDataP & 0xFFFF0000) | (temp_v0_5 & 0xFFFF));
|
|
break;
|
|
}
|
|
}
|
|
if (relocSize != 0) {
|
|
bzero((void*)((uintptr_t)fragment->relocOffset + (uintptr_t)fragment), relocSize);
|
|
}
|
|
osWritebackDCache(fragment, fragment->sizeInRam);
|
|
}
|
|
|
|
/*
|
|
* Set the base virtual address and size of the specified fragment memmap.
|
|
*/
|
|
void Memmap_SetFragmentMap(u32 id, uintptr_t vaddr, size_t size) {
|
|
gFragments[id].vaddr = vaddr;
|
|
gFragments[id].size = size;
|
|
}
|
|
|
|
/*
|
|
* Return the base vaddress of the fragment ID specified.
|
|
*/
|
|
uintptr_t Memmap_GetFragmentBaseVaddr(u32 id) {
|
|
return gFragments[id].vaddr;
|
|
}
|
|
|
|
/*
|
|
* Return the current virtual address of a given offset into a fragment. This is
|
|
* calculated based on the input bitmask constructed of the following values:
|
|
*
|
|
* Input: XYYZZZZZ
|
|
* X: Safety nibble. Only process if 0. This is handled by the address range check
|
|
* at the start.
|
|
* Y: Fragment number.
|
|
* Z: Fragment offset.
|
|
*/
|
|
uintptr_t Memmap_GetFragmentVaddr(u32 mask) {
|
|
if ((mask >= 0x81000000U) && (mask < 0x90000000U)) {
|
|
u32 id = ((mask & 0x0FF00000) >> 0x14) - 0x10;
|
|
u32 value = ((mask & 0x000FFFFF));
|
|
if (gFragments[id].vaddr != NULL) {
|
|
mask = gFragments[id].vaddr + value;
|
|
}
|
|
}
|
|
return mask;
|
|
}
|
|
|
|
/*
|
|
* Returns the input bitmask equivalent for Memmap_GetFragmentVaddr's input. This function
|
|
* appears to be unused.
|
|
*/
|
|
u32 Memmap_GetFragmentVaddrMask(u32 i, uintptr_t addr) {
|
|
struct MemoryMap* entry = &gFragments[i];
|
|
size_t diff = addr - entry->vaddr;
|
|
|
|
if (diff >= entry->size) {
|
|
return 0;
|
|
}
|
|
return ((i + 0x10) << 0x14) | 0x80000000 | diff;
|
|
}
|
|
|
|
/*
|
|
* Clear the memmap for the fragment ID specified.
|
|
*/
|
|
void Memmap_ClearFragmentMemmap(u32 id) {
|
|
gFragments[id].vaddr = NULL;
|
|
gFragments[id].size = 0;
|
|
}
|
|
|
|
/*
|
|
* Given a specified address, check to see if it is located inside of a fragment
|
|
* currently loaded. Return a calculated address.
|
|
*/
|
|
uintptr_t Memmap_GetLoadedFragmentVaddr(uintptr_t addr) {
|
|
struct MemoryMap* fraglist = gFragments;
|
|
int i, UNUSED j;
|
|
|
|
for (i = 0, fraglist = gFragments; i < 0xF0; i++, fraglist++) {
|
|
if (addr >= fraglist->vaddr) {
|
|
size_t diff = addr - fraglist->vaddr;
|
|
if (diff < fraglist->size) {
|
|
return ((i + 0x10) << 0x14) | 0x80000000 | diff;
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|