Endianness
Endianness is referring to the order that bytes comprising a word are stored in memory. Little-endian means that less significant bytes are stored in a lower memory address. Big-endian means that more significant bytes are stored in a lower memory address. Some people call little-endian "reverse endian" or similar, as values appear to be "backwards" when viewed in a hexadecimal memory viewer. For example, the 16-bit value $1234 would be stored as the bytes 34 12 in little-endian.
Most parts of the Game Boy hardware are natively little-endian, as described below. However, when your code does not depend directly on the endianness of a certain hardware feature, you as the programmer are free to choose the endianness with which you store data.
Consider the following example which stores and loads a 16-bit value as little-endian:
SECTION "Code", ROMX
do_16_bit_operations:
; Load a 16-bit value into hl.
ld hl, variable
ld a,[hl+] ; Load lower byte of 16-bit value from the lower address.
ld h,[hl] ; Load upper byte of 16-bit value from the higher address.
ld l,a
; Do some other stuff.
; ...
; Save the value back to memory.
ld a,l
ld [variable],a ; Store lower byte of the 16-bit value to the lower adress.
ld a,h
ld [variable+1],a ; Store upper byte of the 16-bit value to the higher adress.
ret
SECTION "Variables", WRAM0
variable: dw
However, with only slight modifications, the code could be made to load the 16-bit value as big-endian with no change in performance.
SECTION "Code", ROMX
do_16_bit_operations:
; Load a 16-bit value into hl.
ld hl, variable
ld a,[hl+] ; Load upper byte of 16-bit value from the lower address.
ld l,[hl] ; Load lower byte of 16-bit value from the higher address.
ld h,a
; Do some other stuff.
; ...
; Save the value back to memory.
ld a,h
ld [variable],a ; Store upper byte of the 16-bit value to the lower adress.
ld a,l
ld [variable+1],a ; Store lower byte of the 16-bit value to the higher adress.
ret
SECTION "Variables", WRAM0
variable: dw
Most code targeting the Game Boy would use the former format. However, for code that doesn't depend on hardware features like the ones listed below, the endianness can be considered merely a convention, and not an inherent property of the hardware. Thus, the programmer is free to choose either convention if it improves the function of the code, for example by introducing opportunities to optimize the order in which byte are read sequentially.
Endianness of the SM83 CPU core
The SM83 CPU core is little-endian. This is exposed in three places:
- Instructions with 16-bit operands, such as
ld de, d16andcall a16. This is visible in self-modifying code in SRAM, WRAM, or HRAM. For example, the instructioncall $1234is stored in memory asCD 34 12. - Instructions that interact with the stack, like
push/pop,call/retas well as interrupts. This is visible in code that "manually" interacts with data was previouslypushed to the stack or will bepoped in the future. For example, ifHL==$1234andSP==$DFF8initially and you executepush de, then34is stored in the lower memory address (SP-2=$DFF6) and12is stored in the upper memory address (SP-1=$DFF7). - The
ld [a16], sp($08) instruction, which stores the lower byte ofSPat the address specified bya16, and the upper byte ofSPat the address specified bya16+1.
Endianness of the Game Boy peripheral units
The PPU has a mix of endiannesses.
- Pattern data is little-endian between bytes. The byte storing bit 0 (the lower bitplane) of the color indices of the 8 pixels in each 8×1-pixel sliver is at a lower address than the byte storing bit 1 (the upper bitplane).
- GBC color palette values written to BCPD and OCPD take bits 7-0 before bits 15-8, where green straddles bits 9-5.
- Addresses in the GBC specific VRAM DMA are big-endian, with the most significant byte written to the lower address.
The APU is little-endian, with pulse and wave period bits 7-0 in a lower address than bits 10-8.
Additionally, the 16-bit global checksum in the ROM header at $014E-014F is big-endian.