Endianness

From GbdevWiki
Jump to: navigation, search

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:

  1. Instructions with 16-bit operands, such as ld de, d16 and call a16. This is visible in self-modifying code in SRAM, WRAM, or HRAM. For example, the instruction call $1234 is stored in memory as CD 34 12.
  2. 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 previously pushed to the stack or will be poped in the future. For example, if HL==$1234 and SP==$DFF8 initially and you execute push de, then 34 is stored in the lower memory address (SP-2=$DFF6) and 12 is stored in the upper memory address (SP-1=$DFF7).
  3. The ld [a16], sp ($08) instruction, which stores the lower byte of SP at the address specified by a16, and the upper byte of SP at the address specified by a16+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.