Introdução

O conceito de endianess sempre aparece em computação, principalmente quando estamos manipulando dados. Nos casos dos consoles antigos o Snes é Little Endian e o Mega Drive é Big Endian. Neste artigo explicarei esses conceitos. Assista o vídeo para ver mais detalhes e os códigos rodandos nos debuggers.

Definição de Big Endian e Little Endian

O Endianess é o termo que descreve a ordem em que uma sequência de bytes é guardada na memória. No Big Endian os bytes mais significativos aparecem primeiro, então a ordem na memória fica silimar à forma que escrevemos. No Little Endian os bytes menos significativos aparecem primeiro, portanto na memória a ordem fica invertida.

Endianess

Ordem dos Bytes e dos Bits

Quandos nos referimos a Big Endian ou Little Endian, estamos considerando apenas a ordem dos bytes, e não dos bits. Portanto na quase totalidade das Cpus a ordem dos bits é sempre Big Endian, mesmo a Cpu sendo Little Endian em relação aos bytes. Então tenha em mente que um byte é um byte em qualquer Cpu, porém a partir dos bytes as Cpus se diferenciam entre Big Endian e Little Endian na ordem dos bytes.

Números, Endereços e Strings

No geral quando falamos de Big Endian e Little Endian estamos falando dos dados processados (lidos e enviados) pela Cpu. Então no geral os números e endereços tem impacto do endianess, mas Strings por exemplo já não entram nessas questões. Strings estão em um nível de abstração maior em relação à Cpu, então são considerados como um array de caracteres, e cada caractere é processado individualmente. No contexto dos consoles Snes e Mega Drive, o impacto dessas diferenças de endianess se mostram nos números e endereços que lemos e escrevemos nas memórias. Existem Cpus mais avançadas com instruções dedicadas à manipulação de strings, porém isso é outra história.

Transmissão de Dados

Também existe a questão de endianess quando o assunto é transmissão de dados. Muitos protocolos trabalham com envio de bits em transmissões seriais, então a ordem dos bits pode variar de protocolo para protocolo. No contexto dos consoles antigos existem alguns chips onde a transmissão é serial, bit por bit, então nesses casos você tem que ter esse conhecimento de endianess para saber como enviar e receber os dados de forma correta.

Network Order

Esse é um exemplo clássico de endianess que todo programador uma hora encontra. Quando enviamos coisas pela internet, é necessário se ter um padrão do formato dos dados que são transmitidos. Por exemplo, um endereço IPV4 tem 4 bytes, então a ordem em que os bits são enviados importa, pois o outro lado que recebe o dado tem que saber como decodificar a mensagem. O padrão usado pelos protocolos de rede é o Big Endian, e é o que chamamos de Network Order.

Vantagens do Big Endian

  • Fica mais fácil de ler e procurar os dados na memória ou em um editor hexadecimal, pois a ordem que vemos é a mesma que escrevemos, o que ajuda muito, principalmente para quem está aprendendo.
  • A transmissão bit a bit é mais natural, pois todos os bits são enviados em sequência da esquerda para a direita.

Desvantagens do Big Endian

  • Ao ler um pedaço de um dado maior, como por exemplo um byte ou uma word de uma long word, temos que ajustar o endereço para apontar para o pedaço desejado, o que não acontece com o Little Endian, onde sempre podemos usar o endereço do dado completo para pegar os pedaços.

Endianess

  • Quando fazemos uma soma multi precisão, o número resultante tem que ser expandido para a esquerda, o que nem sempre é possível e demandaria ficar empurrando os dados para a direita, o que impacta bem na performance. Com Little Endian isso não ocorre pois a expansão seria para a direita.

Vantagens do Little Endian

  • Para ler um pedaço de um dado maior, como por exemplo um byte ou uma word de uma long word, podemos usar o endereço do dado completo, pois os bytes menos significativos sempre ficam na esquerda, onde começa o endereço do dado. Quando é Big Endian temos que mudar o endereço para o pedaço em questão, uma vez que os bytes menos significativos ficam na direita.
  • Quando fazemos uma soma multi precisão, por exemplo, o número vai aumentando para a direita, o que é natural no Little Endian. Já no Big Endian teríamos que expandir para a esquerda, o que nem sempre é possível, o que demandaria ficar empurrando todo o bloco de dados para a direita, o que impacta bastante na performance.

Desvantagens do Little Endian

  • Mais difícil de ler os dados na memória, principalmente para quem está começando, porém depois de um tempo começa a ser natural, mas não deixa de ser uma desvantagem em relação ao Big Endian.
  • A transmissão bit a bit é menos natural.

Código do Mega Drive

Abaixo está o código de exemplo do Big Endian no Mega Drive. Veja o vídeo para ver os detalhes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
arch md.cpu
endian msb //Big Endian

output "app/app.bin", create

fill $200000

macro seek(variable offset) {
  origin offset
  base offset
}

include "megadrive-header.asm" // Include Header & Vector Table

seek($200)
	nop
	nop

    //Gravando long word, word e byte
    move.l #$12345678,d0
    move.l #$12345678,$ff0000

    move.w #$1234,d1
    move.w #$1234,$ff0010

    move.b #$12,d2
    move.b #$12,$ff0020

    //Cuidado aqui!!!
    move.l #$2,$ff0030
    move.w $ff0030,d4

    -; bra -

Código do Snes

Abaixo está o código de exemplo do Little Endian no Snes. Veja o vídeo para ver os detalhes:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
arch snes.cpu

output "snesapp.sfc/snesapp.sfc", create

fill $200000

macro seek(variable offset) {
  origin ((offset & $7F0000) >> 1) | (offset & $7FFF)
  base offset
}

include "snes-header.asm" // Include Header & Vector Table

seek($8000)
	clc
	xce
	nop
	rep #$30 //16 bits

    //Gravando word e byte
    lda #$1234
    sta $0000

    sep #$20 //8 bits
    lda #$12
    sta $0010
    rep #$20 //16 bits

    // Lendo uma word
    lda #$0000
    lda $0000

    lda #$0002
    sta $0020
    sep #$20 //8 bits
    lda $0020
    rep #$20 //16 bits

    // Acessando um endereço de 3 bytes
    lda #$beef
    sta $0050
    lda #$0050
    sta $0000
    lda #$007e
    sta $0002
    lda [$00]

-
	bra -