Skip to content

Instantly share code, notes, and snippets.

@parasyte
Created September 13, 2013 05:25
Show Gist options
  • Save parasyte/6547020 to your computer and use it in GitHub Desktop.
Save parasyte/6547020 to your computer and use it in GitHub Desktop.
N64 TLB
FORMATS
EntryLo0 & EntryLo1:
00 pppppppppppppppppppppppp ccc d v g
(32-bit: 2.24.3.1.1.1)
p = Page frame number; the upper bits of the physical address.
c = Specifies the TLB page coherency attribute. (See below)
d = Dirty. If this bit is set, the page is marked as dirty and, therefore, writable
This bit is actually a write-protect bit that software can use to prevent alteration
of data.
v = Valid. If this bit is set, it indicated that the TLB entry is valid; otherwise, a
TLBL or TLBS miss occurs.
g = Global. If this bit is set in both Lo0 and Lo1, then the process ignores the ASID
during TLB lookup.
PageMask:
0000000 mmmmmmmmmmmm 0000000000000
(32-bit: 7.12.13)
m = Page comparison mask
EntryHi:
vvvvvvvvvvvvvvvvvvv 00000 aaaaaaaa
(32-bit: 19.5.8)
v = Virtual page number divided by 2 (maps to two pages)
a = Address space ID field. (ASID) An 8-bit field that lets multiple processes share
the TLB; each process has a distinct mapping of otherwise identical virtual page
numbers.
NOTES ON TLB PAGE COHERENCY (c bits)
The TLB page coherency attribute (C) bits specify whether references to the page should
be cached; if cached, the algorithm selects between several coherency attributes. The
table below shows the coherency attributes selected by the C bits.
TLB Page Coherency (C) Bit Values
0: Reserved
1: Reserved
2: Uncached
3: Cacheable noncoherent (noncoherent)
4: Cacheable coherent exclusive (exclusive)
5: Cacheable coherent exclusive on write (sharable)
6: Cacheable coherent update on write (update)
7: Reserved
TRANSLATING VIRTUAL ADDRESSES TO PHYSICAL ADDRESSES
In order to translate virtual to physical addresses, you must be able to read each TLB
entry, looking for a virtual address hit.
Start by dumping the TLB entries one-by-one. First, load the CP0 Index register with
the TLB entry index you wish to dump. You should start with 0x00, and work your way up
to 0x2F. That gives 48 total TLB entries.
After loading CP0 Index with the entry number, call a TLBR instruction. This will dump
the TLB entry to the EntryLo0, EntryLo1, PageMask, and EntryHi registers in the formats
described above.
After the TLB entry has been dumped, begin decoding the data in each register.
To decode a TLB entry, start with the m bits from the PageMask register. This value tells
the size of a virtual page for the TLB entry. The page size can be determined by checking
the values against the following table.
0x0000: 4KB (0x1000 bytes)
0x0003: 16KB (0x4000 bytes)
0x000F: 64KB (0x10000 bytes)
0x003F: 256KB (0x40000 bytes)
0x00FF: 1MB (0x100000 bytes)
0x03FF: 4MB (0x400000 bytes)
0x0FFF: 16MB (0x1000000 bytes)
If the value is anything other than listed, the page size is undefined\erroneous.
Here is a bit of C which will allow you to grab the page size and a page mask:
u32 mask = (PageMask >> 1) | 0x0FFF;
u32 pagesize = mask+1;
Next, extract the virtual page number from EntryHi, based on PageMask.
u16 tmp = PageMask | 0x1FFF;
u32 vpn = EntryHi & ~tmp;
Then use the virtual page number as a mask to the virtual address. If the result is the
same as the vpn, it's counted as a hit. If not, restart the process with the next TLB
index.
if ((vaddr & vpn) != vpn) continue;
If it's a hit, find out if the virtual address is on an odd or even page, based on the
TLB page size.
u32 odd = vaddr & pagesize;
Now perform a bitwise AND on EntryLo0 or EntryLo1, depending on the 'odd' variable. If
the result is 0, the page frame is invalid. If successful, extract the page frame number.
If not, restart the process with the next TLB index.
u32 pfn;
if (!odd) {
if (!(EntryLo0 & 0x02)) continue;
pfn = (EntryLo0 >> 6) & 0x00FFFFFF;
}
else {
if (!(EntryLo1 & 0x02)) continue;
pfn = (EntryLo1 >> 6) & 0x00FFFFFF;
}
Finally, if it gets this far, mask the virtual address with the 'mask' variable created
earlier. Then attach the physical page number, and OR with 0x80000000 to complete the
process. Lastly, exit the loop. Checking the remaining entries for a hit is not required.
u32 paddr = (0x80000000 | (pfn * pagesize) | (vaddr & mask));
break;
And there you have it! Your paddr variable will be a pointer to the translated address.
Now you should be able to use this info to add a new N64 code type to your AR ROM. Maybe
the code type could be something like this...
4sxxxxxx nnnn
aaaaaaaa vvvv
Where s is the size, 8- or 16-bit, x's can be expanded on later, n's are the number of
virtual addresses following. a's and v's are the virtual address, and value to write,
respectively. The number of a\v lines following the 4-code will be determined by nnnn+1,
giving a total of 65536 virtual addresses, max. If that's too much, use 00nn, for 256 total.
An example TLB code for GoldenEye 007 would look like this:
Infinite Health, All Levels
41000000 0000
7F089CC0 2400
This code would work great, because the virtual address is always 7F089CC0, but the physical
address changes after you exit a level.
If it's possible to simply write to the virtual address, bypassing all the manual translation,
then by all means, do it that way. Either way, TLB codes are needed.
EOF
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment