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, d16
andcall a16
. This is visible in self-modifying code in SRAM, WRAM, or HRAM. For example, the instructioncall $1234
is stored in memory asCD 34 12
. - Instructions that interact with the stack, like
push/pop
,call/ret
as well as interrupts. This is visible in code that "manually" interacts with data was previouslypush
ed to the stack or will bepop
ed in the future. For example, ifHL==$1234
andSP==$DFF8
initially and you executepush de
, then34
is stored in the lower memory address (SP-2=$DFF6
) and12
is stored in the upper memory address (SP-1=$DFF7
). - The
ld [a16], sp
($08
) instruction, which stores the lower byte ofSP
at the address specified bya16
, and the upper byte ofSP
at 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.