Thursday, November 29, 2007

Endian conversions

Since all Turbo C functions have been run only on Intel 'x86 processors, it is not uncommon in Turbo C code to find that the programmer has simply assumed that the CPU is little-endian -- i.e., that the LSB of multi-byte integers precedes the MSB. In porting such code to GNU gcc, one has to recognize that it may not necessarily be run on a little-endian processor. For example, I do a lot of work on an iMac, which is based on a big-endian PowerPC processor. This is not important in most cases, but does affect various operations such as the matching of bytes within union objects and, especially, reading/writing files.

To make it easier to deal with this problem, I've provided a number of functions not originally present in Turbo C that can be used to convert endian types where required. Unfortunately, it's still up to you to figure out that there's a problem and to add these function calls to the program.

There are two separate groups of functions, those which convert the endian type of an integer value already stored in memory, and those which convert data on-the-fly whilst reading/writing a file. All of the functions require the TurboC.h header file.

Function prototype Description
void FixLittle16 (uint16_t *); Performs an in-place conversion of a 16-bit integer value stored in memory from little-endian format to the natural CPU endian format, or vice-versa . Using this function twice in succession gets back whatever you started with. There's no harm using this function if the CPU happens to be little-endian, since the value is passed through unchanged in that case.
void FixLittle32 (uint32_t *); Same as FixLittle16, but 32-bit instead.
void FixBig16 (uint16_t *); Same as FixLittle16, but big-endian instead.
void FixBig32 (uint32_t *); Same as FixLittle16, but 32-bit big-endian instead.
int ReadLittle16 (FILE *fp, uint16_t *Value); Reads a 16-bit little-endian integer from a file, and delivers it in the natural endian-format of the CPU. Returns 0 on success, non-zero on failure.
int ReadBig16 (FILE *fp, uint16_t *Value); Same as ReadLittle16, but big-endian instead.
int ReadLittle32 (FILE *fp, uint32_t *Value); Same as ReadLittle16, but 32-bit instead.
int ReadBig32 (FILE *fp, uint32_t *Value); Same as ReadLittle16, but 32-bit big-endian instead.
int WriteLittle16 (FILE *fp, uint16_t Value); Takes a 16-bit integer value in the natural endian format of the CPU, and writes it to a file as a 16-bit little-endian integer. Returns 0 on success and non-zero on failure.
int WriteBig16 (FILE *fp, uint16_t Value); Same as WriteLittle16, but big-endian instead.
int WriteLittle32 (FILE *fp, uint32_t Value); Same as WriteLittle16, but 32-bit instead.
int WriteBig32 (FILE *fp, uint32_t Value); Same as WriteLittle16, but 32-bit big-endian instead.

No comments: