All About BATs
All About BATs

For advanced ASM coders/devs.

This won't be useful for Wii Gecko Codes, but I've made this thread due to the Broadway Manual lacking detail on the BAT Registers and how to configure them. There are other PPC-based manuals that explain it, but said explanations are not all that "user-friendly".

Chapter 1: Intro

The BAT Registers are responsible for taking a Virtual Address and translating (converting) it to its Physical Address. As an Advanced ASM Coder/Dev, you should already know that mem1 and mem2 operate as Virtual Memory. Broadway needs to be 'told' how to convert any Virtual Memory Address to its Physical Address equivalent.

BAT stands for Block Address Translation. There are 16 total BAT registers. There are 8 BATs available for areas of memory containing instructions (known as IBATs), and 8 BATs available for memory containing data (known as DBATs). Each BAT is 64 bits (double-word) in length and is split into upper 32 bit and lower 32 bit portions.

Any area or region of memory that a BAT is responsible for is known as a Block. The BATs are mostly configured sometime shortly after the game is booted and a final BAT (DBAT3 for the L2 Cache) is configured after the bootstrap screen. From personal limited testing, it appears all Wii games setup the BATs in the same universal way.

Here's a picture of the BATs while having the emulation randomly paused in MKWii at the Main Menu

[Image: bat.png]

NOTE: Older Dolphin Dev Versions (such as 11xxx series and earlier) will not show proper values for the BATs! Use a newer version!

Here's a list of SPR numbers for all the BATs. The list is formatted to directly be placed into an ASM Code compiler (i.e. PyiiASMH)

.set IBAT0U, 528
.set IBAT0L, 529
.set IBAT1U, 530
.set IBAT1L, 531
.set IBAT2U, 532
.set IBAT2L, 533
.set IBAT3U, 534
.set IBAT3L, 535
.set IBAT4U, 560
.set IBAT4L, 561
.set IBAT5U, 562
.set IBAT5L, 563
.set IBAT6U, 564
.set IBAT6L, 565
.set IBAT7U, 566
.set IBAT7L, 567
.set DBAT0U, 536
.set DBAT0L, 537
.set DBAT1U, 538
.set DBAT1L, 539
.set DBAT2U, 540
.set DBAT2L, 541
.set DBAT3U, 542
.set DBAT3L, 543
.set DBAT4U, 568
.set DBAT4L, 569
.set DBAT5U, 570
.set DBAT5L, 571
.set DBAT6U, 572
.set DBAT6L, 573
.set DBAT7U, 574
.set DBAT7L, 585

Chapter 2: BAT Strcuture Upper 32 bits

Upper Portion:
  • Bits 0 - 14: BEPI
  • Bits 15 - 18: Unused
  • Bits 19 - 29: BL
  • Bit 30: Vs
  • Bit 31: Vp

BEPI: Block Effective Page Index. This is simply the beginning Virtual Address that you choose to use for the translation. If desired, you can set this to exactly match the Physical Address (for Wii games that is only done for what is called the L2 Cache). The ending Virtual Address is determined by the memory block size in the BL field.

BL: Block Length. This sets the amount (region/block) of Memory that the BAT will cover using BEPI as the starting address. Use the bit map below to determine the size.

BL Bit Map:
  • 128KB = 000 0000 0000
  • 256KB = 000 0000 0001
  • 512KB = 000 0000 0011
  • 1MB = 000 0000 0111
  • 2MB = 000 0000 1111
  • 4MB = 000 0001 1111
  • 8MB = 000 0011 1111
  • 16MB = 000 0111 1111
  • 32MB = 000 1111 1111
  • 64MB = 001 1111 1111
  • 128MB = 011 1111 1111
  • 256MB = 111 1111 1111

Setting an invalid bit combo will corrupt the BAT and cause undefined behavior. If the exact size you want is not listed, then you need round up or split the sizes and use multiple BATs.

Vs: Supervisor Valid Bit. If you are in Supervisor Mode (Machine State Register PR Bit Low), this bit will determine whether or not you are allowed to access that memory.

Vp: User Valid Bit. If you are in User Mode (Machine State Register PR bit High), this bit will determine whether or not you are allowed to access that memory.

Cheat Sheet for Upper Portion of BAT if using Vs & Vp as both set high.

XXXX = Upper 16 bits of the starting Virtual Address (i.e. 0x8000 represents 0x80000000)
zzzz = BL + Vs&Vp combo (whenever Vs and Vp are set high)

zzzz value map (assumes Vs and Vp are set high)
  • 0003 = 128KB
  • 0007 = 256KB
  • 000F = 512KB
  • 001F = 1MB
  • 003F = 2MB
  • 007F = 4MB
  • 00FF = 8MB
  • 01FF = 16MB
  • 03FF = 32MB
  • 07FF = 64MB
  • 0FFF = 128MB
  • 1FFF = 256MB

Here's an example BAT Upper 32-bit Value using 8MB and 0x81000000 as the virtual start address; Vs and Vp both set high

Btw here's a handy memory size cheat sheet to help calculate the zzzz values. Take the end address minus the start address, then add 1. All calculations are in Hex obviously..

Example: (0x80FFFFFF - 0x80000000) + 1 = 0x01000000  (size of 16MB; use zzzz value of 01FF)
  • 0x10000000 = 256MB
  • 0x08000000 = 128MB
  • 0x04000000 = 64MB
  • 0x02000000 = 32MB
  • 0x01000000 = 16MB
  • 0x00800000 = 8MB
  • 0x00400000 = 4MB
  • 0x00200000 = 2MB
  • 0x00100000 = 1MB (1,024KB actual)
  • 0x00080000 = 512KB
  • 0x00040000 = 256KB
  • 0x00020000 = 128KB
  • 0x00010000 = 64KB
  • 0x00008000 = 32KB
  • 0x00004000 = 16KB
  • 0x00002000 = 8KB
  • 0x00001000 = 4KB
  • 0x00000800 = 2KB
  • 0x00000400 = 1KB (1,024 bytes actual; 0x400 in hex is 1,024 in decimal)

Chapter 3: BAT Structure Lower 32 bits

Bits 0 - 14: BRPN
Bits 15 - 24: Unused
Bit 25: W
Bit 26: I
Bit 27: M
Bit 28: G
Bit 29: Unused
Bits 30 & 31: PP

BRPN: Block Register Page Number. This is the physical address that is used in conjuction to your Virtual Address, this must be a legit physical address.

W: Write Through. If this bit is high, any changes to cached memory are written (forced) thru to physical memory, think of this like a dcbst instruction. If the bit is low, this is known as 'write-back', and physical memory isn't updated until its respective cache block is flushed. To boost performance, you want this bit LOW.

I: Cache-Inhibited. If this bit is high, no cache-ing shall occur for the specified block of memory. Setting this high can be useful for I/O devices.

M: Memory coherence: When this bit is high, other devices or processors will be notified whenever the specified region of memory is accessed. Considering Broadway runs on 'its own' and doesn't need to notify other processors, M bit is set low on every BAT by every Wii menu, channel, game, etc.

G: Guarded:  This bit should be high if there are missing gaps in the specified memory block. This bit should also be set high for any memory that is not "well behaved". "Well behaved" simply means if the block of memory includes access to something such as an I/O device. I'm not sure on this but you may also need the G bit high if you had to roundup the BL bits. That would technically cause gaps in the memory block. Also G bit should be high if you want to reduce out-of-order operations (for something such as setting up a block of uncached memory, I bit should also be high for this case). Also any BATs setup for the L2 Cache is recommended to have the G bit high (Broadway Manual Page 320; Section 9.1.5)

PP: Page Protection. Responsible for allowing access to the block of memory. Think of it like a firewall. See bit map below.

PP bit map:
  • 00 = No access (can't read or write)
  • x1 = Read only (x = don't care value)
  • 10 = Read & Write

There is no available bit combo for Write only!

If you are wanting a block of memory to allow full access in any 'situation', set both the Vs and Vp bits high in the upper portion of the BAT, and then set the PP bit combo to 10 for the lower portion.

Here's an example of a BAT with its lower portion using a physical start address 0x01000000 with WIMG all low, and PP set to 10. We will use the upper portion example from Chapter 2 that used Virtual Start Address 0x81000000. The BAT will have Vs+Vp high with PP as 10 which means both user and supervisor have read+write access to the block of memory

BAT value: 0x810000FF 01000002

Virtual Start Address: 0x81000000
Block Length: 8MB
Vs and Vp high
Physical Start Address: 0x01000000
WIMG all low
PP = 10 (0x2); Read & Write

IMPORTANT NOTE: The IBATs (Instruction BATs) can NEVER have the W and/or G bit set high. Doing so will corrupt the IBAT. Undefined behavior will occur.

Chapter 4: Getting In & Out of Real Mode

You must be in Real Mode when doing any invalidations/modifications to the BATs. Real Mode simply means you are executing in Physical Memory that is NOT part of an Exception. However, you do NOT need to be in Real Mode if you are simply copying BAT Register Data to the GPRs (maybe for something such as a Debug Report type code/source). Also, interrupts need to be disabled the entire time you are invalidating and/or modifying the BATs.

Example Routine to get into Physical Mode~

mfmsr r3 #Backup original MSR to some safe place if necessary
rlwinm r4, r3, 0, 17, 15
mtmsr r4

bl get_pc #Get Program counter
mflr r3

margin = real_mode - get_pc

addi r3, r3, margin #This points to instruction right after rfi
clrlwi r3, r3, 1 #Change address to physical
mtspr srr0, r3 #Place physical address into srr0
rlwinm r4, r4, 0, 28, 25 #Flip off IR and DR bits
mtspr srr1, r3 #Place updated MSR into srr1
rfi #Go into real mode


Example Routine to get back into Virtual Mode~

bl get_pc #Get Program Counter
mflr r4

margin = virtual_mode - get_pc

mfmsr r3 #Get MSR
ori r3, r3, 0x8030 #Flip IR and DR bits high, turn back on Interrupts
mtspr srr1, r3 #Place updated MSR into srr1
addi r3, r4, margin #Points to instruction right after rfi
oris r3, r3, 0x8000 #Change address to Virtual, mem80 used for this example
mtspr srr0, r3 #Place virtual address into srr0
rfi #End real mode


Alternatively instead of placing the address in srr0 and MSR into srr1, you could do something like this (do NOT use this for Exceptions btw)...

lis r0 #Will be your physical or virtual address
ori r0, r0, 0xXXXX
mtlr r0 #or mtctr if you want to use the CTR
mfmsr r0
rlwinm / ori using r0 #Depends on if you are setting IR+DR high or low
mtmsr r0
isync** #This is NEEDED!
blr #or bctr if you chose to use the CTR

**isync is needed to update the instruction context. If isync isn't used (remember Broadway is an out-of-order execution-type CPU), Broadway may fetch and execute instructions meant for virtual mode as real mode, and vice versa. The earlier two sources that use rfi do not need an isync because rfi, by design, is instruction synchronizing.

Chapter 5: Invalidating BATs

To modify a BAT, it MUST be invalidated first. Also when Broadway is powered on, the BATs are in an unknown state. All BATs should be marked invalid before configuring them (necessary for something such as writing a boot sequence code).

To invalidate a BAT, both Vs and Vp bits of the Upper Portion must be set low. Super simple. The quickest way to invalidate a BAT is this...

li rX, 0
mtspr ZZZU, rX

rX = Safe GPR to use for your code/source
ZZZ = the SPR number of the BAT
U = reminding you the write needs to be done the the UPPER portion

Any modification/invalidation to an IBAT requires an isync instruction AFTERWARDS (Reference: PowerPC Microprocessor Family: The Programming Environments, table 2-22 (page 2-42))

Any modification/invalidation to a DBAT requires isync instructions BEFORE and AFTER (Reference: PowerPC Microprocessor Family: The Programming Environments, table 2-23 (page 2-43))

If you are invalidating a group of IBATS or DBATS, you do not need an isync after every single individual BAT invalidation.

Example: Invalidate DBATS 2 thru 4~

li r0, 0
mtspr r0, DBATU2
mtspr r0, DBATU3
mtspr r0, DBATU4

Since IBAT invalidation doesn't require an isync beforehand, if you are writing a boot sequence code, you can invalidate all BATs simply like this...

li r0, 0
mtspr IBAT0U, r0
mtspr IBAT1U, r0
mtspr IBAT2U, r0
mtpsr IBAT3U, r0
mtspr IBAT4U, r0
mtspr IBAT5U, r0
mtspr IBAT6U, r0
mtspr IBAT7U, r0
mtspr DBAT0U, r0
mtspr DBAT1U, r0
mtspr DBAT2U, r0
mtspr DBAT3U, r0
mtpsr DBAT4U, r0
mtspr DBAT5U, r0
mtpsr DBAT6U, r0
mtspr DBAT7U, r0

Chapter 6: Modifying BATs Correctly

Assuming you have invalidated a BAT correctly, you can now modify it. The Lower 32-bits of the BAT MUST ALWAYS BE WRITTEN FIRST! Another rule is that you can only modify one BAT at a time.

Example: Configure the 5th IBAT to have virtual address 0x80000000 represent physical address 0x00000000. 0x80000000 thru 0x80FFFFFF is 16MB in total size which is a perfect size match for setting the BL bits. Vs, Vp both High. PP = 10 (0x2). WIMG is all low.

#Pretend the IBAT was properly invalidated some time ago. Fyi no isync required beforehand since this is an IBAT
#Set r0 as lower portion
li r0, 2

#Set r3 as upper portion
lis r3, 0x8000
ori r3, r3, 0x01FF

#Write lower portion
mtspr IBAT5L, r0

#Write upper portion
mtspr IBAT5U, r0

#isync required now

One more example, set one BAT (DBAT2) to cover Virtual Uncached Mem2 (aka mem9). Mem9's full size is 64MB which is a perfect size match once again for the BL bits. Assume DBAT2 was properly invalidated beforehand.

#W = low
#I = high! (cache must be blocked)
#M = low
#G = high! (prevent out-of-order operations)
#PP = 10 (read & write)
lis r0, 0x1000
ori r0, r0, 0x002A

#Upper Portion config
#Virtual Start Addr = 0xD0000000
#Block size = 64MB
#Vs and Vp both set high
lis r3, 0xD000
ori r3, r3, 0x07FF

#Modify the BAT
mtspr DBAT2L, r0
mtspr DBAT2L, r3

Chapter 7: More Rules to Follow

You cannot have multiple BATs (for the BAT same type; instruction or data), that do a translation to the same physical address.

Example: You have IBAT0 translate 0x80000000 to physical 0x00000000 and IBAT6 translate 0x70000000 to physical 0x00000000. That's a no-go.

Also, you cannot have memory blocks overlap from multiple BATs (for the same BAT type: instruction or data).

Example: You have DBAT2 for 0x90000000 using block-length of 64MB (so this covers all of mem9 which is 0x90000000 thru 0x93FFFFFF. You then also have DBAT4 for 0x93000000 using block length of 16MB (so this covers just 0x93000000 thru 0x93FFFFFF). As a result memory addresses 0x93000000 thru 0x93FFFFFF are both part of DBAT2 and DBAT4.

Whenever BATs are incorrectly invalidated and/or modified, the processor may enter into what is known as a Checkstop Condition. In simple terms, the processor halts without any Exception Routine being taken.

Chapter 8: Hardware Register Notes

By default (Broadway from a powered-on state), IBATs & DBATs 4 thru 7 are disabled. To enable these extra BATs, you need to set Bit 6 (SBE) of HID4 high. From personal limited testing, it appears all Wii applications that make modifications to HID4 will always set/keep this bit high

All BAT M bits (for instruction fetching) can be overridden by setting bit 23 (IFEM) low on HID0. From personal limited testing, it appears all Wii applications set/keep this bit low.

Chapter 9: Example Code of Messing Around with the BATs

Here is a code I've clobbered together really quick that will run a couple of instructions in 0x50XXXXXX memory. This will most likely crash on most versions of Dolphin, because Dolphin is Dolphin. The code runs fine on real Hardware.

If Dolphin does work for you then you will see (by stepping thru the code) that you will arrive at 0x50XXXXXX memory to execute two instructions. They set r4 to 0, then increment it by 1. In Dolphin's code view it will say something like.... "No RAM Contents Here". You won't be able to see the instructions in the Code View, but as you are stepping, you will see r4 get the proper updates in the Register tab/window.

Code is hooked at old historical Shared Item Code Address (MKWii game ofc, credits to Guru for original Shared Item Code). Hit (or let a CPU) hit an item box for the code to execute.

Btw to know that the code works on real Hardware, a Fib is set to be received from the Item Box when you (or the CPU) hits said item box.

C27BA164 0000001C
7D8000A6 5583045E
7C600124 48000005
7D6802A6 386B0020
5463007E 7C7A03A6
7C6000A6 54630732
7C7B03A6 4C000064
38000000 7C1083A6
4C00012C 38000002
3C605000 606301FF
7C1183A6 7C7083A6
4C00012C 7C6000A6
60630030 7C7B03A6
5563007E 64635000
38630064 7C7A03A6
4C000064 38800000
38840001 386B0088
5463013E 7C7A03A6
7C6000A6 54630732
7C7B03A6 4C000064
38000000 7C1083A6
4C00012C 38000002
3C608000 606301FF
7C1183A6 7C7083A6
4C00012C 7D9B03A6
5563013E 64638000
386300C4 7C7A03A6
4C000064 38600003
90770020 00000000


#PAL = 807BA164

#Register Notes
#r0, r3, r4, & LR are safe

#Disable interrupts
mfmsr r12
rlwinm r3, r12, 0, 17, 15
mtmsr r3

#Go into real Mode
bl get_pc #Get Program counter
mflr r11 #Will need this value later to get back to Virtual Mode

margin = real_mode - get_pc

addi r3, r11, margin #This points to instruction right after rfi
clrlwi r3, r3, 1 #Change address to physical
mtspr srr0, r3 #Place physical address into srr0
mfmsr r3 #Get MSR
rlwinm r3, r3, 0, 28, 25 #Flip off IR and DR bits
mtspr srr1, r3 #Place updated MSR into srr1
rfi #Go into real mode



#Invalidate IBAT0 (Vs and Vp of Upper portion must be low)
li r0, 0
mtibatu 0, r0

#Set r0 as lower portion
li r0, 2 #PP bits as 10, WIMG all low

#Set r3 as upper portion
lis r3, 0x5000 #Start block at EA 0x50000000
ori r3, r3, 0x01FF #Size of 16MB

#Modify the IBAT!, lower portion (r0) must be written first
mtibatl 0, r0
mtibatu 0, r3

#Go to Vritual Mode
virt_margin = virtual_mode - get_pc

#Turn back on IR and DR, do not turn on EE just yet!
mfmsr r3
ori r3, r3, 0x0030
mtspr srr1, r3

#Clear bit 0 of r11
clrlwi r3, r11, 1

#OR it with 0x5000
oris r3, r3, 0x5000
addi r3, r3, virt_margin
mtspr srr0, r3


#We are now executing in 0x50xxxxxx memory!!!

#Execute random instructions to prove to Dolphin User this is working
li r4, 0
addi r4, r4, 1

#Go back into real mode again
second_phys_margin = second_real_mode - get_pc

addi r3, r11, second_phys_margin
clrlwi r3, r3, 4 #Change address to physical
mtspr srr0, r3 #Place physical address into srr0
mfmsr r3 #Get MSR
rlwinm r3, r3, 0, 28, 25 #Flip off IR and DR bits
mtspr srr1, r3 #Place updated MSR into srr1
rfi #Go into real mode



#Invalidate IBAT0 again
li r0, 0
mtibatu 0, r0

#Set r0 as lower portion
li r0, 2

#Set r3 as upper portion
lis r3, 0x8000
ori r3, r3, 0x01FF

#Modify the IBAT!, lower portion (r0) must be written first
mtibatl 0, r0
mtibatu 0, r3

#Go to Vritual Mode
second_virt_margin = second_virtual_mode - get_pc

#Restore very first original MSR (r12)
mtspr srr1, r12

#Clear bits 0 thru 3 of r11
clrlwi r3, r11, 4

#OR it with 0x8000
oris r3, r3, 0x8000
addi r3, r3, second_virt_margin
mtspr srr0, r3


#Set fib
li r3, 3

#Default/Original Instruction
stw r3, 0x0020 (r23)

Chapter 10: Some Oddities I've Found

Every Wii Menu Version starts off executing in Physical Memory at what is called the BS1 Boot Code (located at 0x3400). The Wii Menu will execute a small amount of code to setup some Hardware Registers, some BATs, and then jump to Virtual Memory at 0x81330000.

What's odd is that in this code, during BAT invalidation, there is no isync instruction placed before multiple DBAT instances.

A second oddity I have found is that DBAT3 for the L2 Cache (setup by Wii games after the bootscrap screen), sets an improper size for the BAT. Wii games uses a block size of 16MB when the L2 Cache has a maximum size of 256KB. Not only that, the Broadway Manual (Page 320; Section 9.1.5) recommends any BAT configured for the L2 Cache should have its G bit high. You will see that DBAT3 has its G bit low (lol?) Maybe somebody can explain why that is?

Forum Jump:

Users browsing this thread: 1 Guest(s)