Difference between revisions of "Endianness"

From GbdevWiki
Jump to: navigation, search
(found 11 examples of bitfield ordering or byte ordering)
 
(Expanded the explanation and removed some examples that in my opinion don't clarify the issue.)
 
Line 1: Line 1:
The SM83 CPU core is little-endian, in which less significant bytes are stored in a lower address. This is exposed in three places:
+
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 <code>$1234</code> would be stored as the bytes <code>34 12</code> in little-endian.
# Instructions with 16-bit operands, such as <code>ld de, d16</code> and <code>call a16</code>. This is visible in self-modifying code in SRAM, WRAM, or HRAM.
+
 
# <code>push</code> and <code>pop</code> instructions
+
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.
# <code>ld [a16], sp</code> ($08) instruction
+
 
 +
Consider the following example which stores and loads a 16-bit value as little-endian:
 +
<pre>
 +
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
 +
</pre>
 +
 
 +
However, with only slight modifications, the code could be made to load the 16-bit value as big-endian with no change in performance.
 +
<pre>
 +
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
 +
</pre>
 +
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 <code>ld de, d16</code> and <code>call a16</code>. This is visible in self-modifying code in SRAM, WRAM, or HRAM. For example, the instruction <code>call $1234</code> is stored in memory as <code>CD 34 12</code>.  
 +
# Instructions that interact with the stack, like <code>push/pop</code>, <code>call/ret</code> as well as interrupts. This is visible in code that "manually" interacts with data was previously <code>push</code>ed to the stack or will be <code>pop</code>ed in the future. For example, if <code>HL==$1234</code> and <code>SP==$DFF8</code> initially and you execute <code>push de</code>, then <code>34</code> is stored in the lower memory address (<code>SP-2=$DFF6</code>) and <code>12</code> is stored in the upper memory address (<code>SP-1=$DFF7</code>).
 +
# The <code>ld [a16], sp</code> (<code>$08</code>) instruction, which stores the lower byte of <code>SP</code> at the address specified by <code>a16</code>, and the upper byte of <code>SP</code> at the address specified by <code>a16+1</code>.
 +
 
 +
=== Endianness of the Game Boy peripheral units ===
  
 
The PPU has a mix of endiannesses.
 
The PPU has a mix of endiannesses.
* Pattern data ($8000-$97FF) is big-endian within a byte. The first bit to be sent to the LCD is the most significant bit.
+
* 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).  
* Pattern data is little-endian between bytes. The byte storing bit 0 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.
+
* DMG color palette registers (BGP, OBP0, and OBP1) have the shade values for low color indices in low bits and high color indices in high bits.
+
 
* GBC color palette values written to BCPD and OCPD take bits 7-0 before bits 15-8, where green straddles bits 9-5.
 
* 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 DMA to VRAM are big-endian, with the more significant byte written to the lower address.
+
* 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 noise period bits 7-0 in a lower address than bits 10-8.
+
The APU is little-endian, with pulse and wave period bits 7-0 in a lower address than bits 10-8.
  
Cartridges are mostly little-endian.
+
Additionally, the 16-bit global checksum in the ROM header at $014E-014F is big-endian.
* MBC1 bank number bits 4-0 are in a lower address than bits 6-5.
+
* MBC5 bank number bits 7-0 are in a lower address than bit 8.
+
* The 16-bit checksum in the ROM header at $014E is big-endian.
+

Latest revision as of 02:00, 28 February 2023

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.