You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
206 lines
7.3 KiB
206 lines
7.3 KiB
Big and Little Endian Byte Orderings
|
|
Big and Little Endian Byte Orderings
|
|
|
|
The MIPS processor and compilers support both the Big Endian and Little
|
|
Endian byte-ordering conventions. The names Big Endian and Little
|
|
Endian are used because of the apt analogy to the bloody feud in the
|
|
classic children's book Gulliver's Travels (quod vide). The feud was
|
|
between the two mythical islands, Lilliput and Blefescu, over the
|
|
correct end (big or little) at which to crack an egg.
|
|
In our case, the
|
|
issue has to do with the "end" (most significant or least significant)
|
|
of a multiple-byte data type.
|
|
|
|
With Big Endian ordering, the address of a multiple-byte data type is
|
|
of its most significant byte (its "big end"), whereas with Little
|
|
Endian ordering, the address is of its least significant byte (its
|
|
"little end"). This is shown in Figure A.14. For structures declared
|
|
in a high-level language, the order of bytes in memory will differ
|
|
depending on the byte ordering and the particular data type, as shown
|
|
for a C structure in Figure A.15.
|
|
|
|
��������������������������������������������������������������������������������
|
|
Code Big Endian Little Endian
|
|
Memory Contents Memory Contents
|
|
��������������������������������������������������������������������������������
|
|
|
|
struct {
|
|
long a;
|
|
short b[2];
|
|
char c[4];
|
|
} S1 = { /* Big Endian Little Endian */
|
|
0x12345678, /* 12 34 56 78 78 56 34 12 */
|
|
0x1234,0x5678, /* 12 34 56 78 34 12 78 56 */
|
|
"ABC" /* 41 42 43 00 41 42 43 00 */
|
|
};
|
|
��������������������������������������������������������������������������������
|
|
|
|
Figure A.15 Big and Little Endian Byte-Ordering Example
|
|
|
|
Note that Endianness only affects the operation of the load and store
|
|
instructions, which means when data is moved between memory or
|
|
peripheral devices and registers.
|
|
|
|
Except in cases where the host and target have the same byte-ordering
|
|
convention, you must explicitly define the required Endianness of the target
|
|
code using the appropriate command-line option (EB or EL). This will
|
|
override the host's default byte ordering. For example,
|
|
|
|
|
|
pmcc -EB -o prog prog.c
|
|
pmcc -EL -o prog prog.c
|
|
|
|
|
|
To write code that can be compiled/assembled to execute correctly in
|
|
either a Big or a Little Endian target environment, the user must
|
|
follow one basic rule: either eliminate Endian-specific code or enclose
|
|
it with #ifdef/else statements, using the appropriate preprocessor
|
|
flag (MIPSEB or MIPSEL). Endian-specific code is produced whenever the
|
|
data type implicit in the instruction (e.g., "byte" in load byte)
|
|
differs from the data type of the accessed data, such as using four
|
|
load-byte instructions to read a single word or using a store-word
|
|
instruction to store 4 bytes. I/O addresses are also Endian-specific,
|
|
because a peripheral device is hardwired to a specific part of the data
|
|
bus (typically D0-D7).
|
|
|
|
The example programs main.c and asm.s print Endian-sensitive values
|
|
returned from the functions end1 and end2 and the contents of two
|
|
Endian-sensitive I/O locations, SIOCNTL and SIODATA. For purposes of
|
|
illustration, the functions return correct results when passed the
|
|
value 0 and incorrect results when passed the value 1. The programs are
|
|
shown in their entirety in the final section of this appendix and are
|
|
discussed below.
|
|
|
|
The C program defines two base addresses for the peripheral device
|
|
(SIOBASE) and uses the preprocessor variable MIPSEB (Big Endian) to select
|
|
between the two byte-ordering conventions.
|
|
|
|
1 #ifdef MIPSEB
|
|
2 #define SIOBASE 0xbe000003 /* Big Endian base address */
|
|
3 #else
|
|
4 #define SIOBASE 0xbe000000 /* Little Endian base address */
|
|
5 #endif
|
|
6 #define SIOCNTL *((volatile unsigned char *)SIOBASE+4)
|
|
7 #define SIODATA *((volatile unsigned char *)SIOBASE+12)
|
|
|
|
The function main prints the results of the functions end1 and end2.
|
|
Incorrect results are produced by end1 because when the function is
|
|
passed a 1, it reads the word as a series of 4 bytes, rather than as a
|
|
single word. Starting at the word address, each byte is read into the
|
|
variable v, such that the lowest addressed byte ends up in the most
|
|
significant byte of the variable. This is the correct byte ordering for
|
|
Big Endian but incorrect for Little Endian:
|
|
|
|
19 for (i=0;i
|
|
|
|
Incorrect results are produced by end2 because the lwl and lwr instruc-
|
|
tions are used to access unaligned data; the non-Endian-sensitive solution
|
|
uses the ulw instruction:
|
|
|
|
8 end2: la t0,dat2
|
|
9 beq a0,zero,1f
|
|
10 nop
|
|
11 lwl v0,0(t0)
|
|
12 lwr v0,3(t0)
|
|
13 b 2f
|
|
14 nop
|
|
15 1: ulw v0,(t0)
|
|
16 2: jr ra
|
|
|
|
For Big Endian the program prints:
|
|
|
|
12345678 12345678
|
|
12345678 12345678
|
|
00 3b
|
|
|
|
and for Little Endian it prints:
|
|
|
|
12345678 78563412
|
|
12345678 78345612
|
|
00 3b
|
|
|
|
|
|
Keep in mind that a program's binary cannot be converted from one
|
|
byte-ordering convention to another by simply swapping all the bytes. This
|
|
would produce correct results only if all of the program's data were of the
|
|
same type (size) and the same size as the instructions. To change the byte
|
|
ordering, programs must be recompiled using the appropriate compiler
|
|
option.
|
|
|
|
A.7 PROGRAM LISTINGS
|
|
|
|
main.c:
|
|
1 #ifdef MIPSEB
|
|
2 #define SIOBASE 0xbe000003 /* Big Endian base address */
|
|
3 #else
|
|
4 #define SIOBASE 0xbe000000 /* Little Endian base address */
|
|
5 #endif
|
|
|
|
6 #define SIOCNTL *((volatile unsigned char *)SIOBASE+4)
|
|
7 #define SIODATA *((volatile unsigned char *)SIOBASE+12)
|
|
|
|
8 int dat1 = 0x12345678;
|
|
9 end1(n)
|
|
10 int n;
|
|
11 {
|
|
12 int i,v;
|
|
13 unsigned char *p;
|
|
|
|
14 /* set up a pointer into the data */
|
|
15 p = (unsigned char *)
|
|
|
|
16 if (n==0) v = dat1;
|
|
17 else {
|
|
18 v = 0;
|
|
19 for (i=0;i<4;i++) {
|
|
20 v <<= 8;
|
|
21 v |= p[i];
|
|
22 }
|
|
23 }
|
|
24 return(v);
|
|
25 }
|
|
|
|
26 main()
|
|
27 {
|
|
|
|
28 printf("%08x %08x\n");
|
|
29 printf("%08x %08x\n");
|
|
30 printf("%02x %02x\n");
|
|
31 }
|
|
|
|
asm.s:
|
|
1 #include "mips.h"
|
|
|
|
2 .data
|
|
3 dat2: .word 0x12345678
|
|
4 .text
|
|
5 .globl end2
|
|
6 .ent end2
|
|
7 .set noreorder
|
|
8 end2: la t0,dat2
|
|
9 beq a0,zero,1f
|
|
10 nop
|
|
11 lwl v0,0(t0)
|
|
12 lwr v0,3(t0)
|
|
13 b 2f
|
|
14 nop
|
|
|
|
15 1: ulw v0,(t0)
|
|
16 2: jr ra
|
|
17 nop
|
|
18 .end end2
|
|
|
|
[EGG] Danny Cohen, "On Holy Wars and a Plea for Peace," IEEE
|
|
Computer, Oct. 1981, pp. 48-54.
|
|
|
|
��������������������������������������������������������������������������������
|
|
This explanation was extracted from Appendix A of
|
|
|
|
The MIPS Programmer's Handbook, by Erin Farquhar and Philip Bunce.
|
|
|
|
��������������������������������������������������������������������������������
|
|
Navigation:
|
|
Document Home |
|
|
Document Contents |
|
|
Document Index
|
|
|
|
|