// whichEndian.cc  P. Conrad  Fall 2005

// This program will detect whether the system you are
// running on is "big-endian" or "little endian".

// A more full explanation of "big endian" vs. "little endian"
// appears at the end of this note.

// We expect that strauss.udel.edu (SPARC) is big-endian,
// and that a windows PC (Intel) is little-endian 

#include <iostream>
using namespace std;

int main(void)
{
  int x=65;
  int * addressOfX = &x;  // addressOfX is the location of x in memory

  // int * is a type that means "a pointer to an integer".
  // an int * can be used as any of the following:
  //     (a) a pointer to an integer
  //     (b) the address of an integer
  //     (c) the name of an array of integers [more on that later]
  
  char c[4];


  // Note that the name of an array (e.g. c) refers to the address in 
  // memory where that array "lives".  So an array name like c
  // is also a (char *), or "pointer to character".  It can be considered
  //   (a) a pointer to a character (the element [0] in the array)
  //   (b) the address of a character (the element [0] in the array)
  //   (c) the name of an array of characters.

  // The memcpy() function will copy four bytes from where X lives 
  // into the array called c.   

  // (void *) means "generic pointer"; it converts a particular kind of
  // address (e.g. (int *) or (char *) into a "generic address".
  // C++ would normally complain about us "mixing types".   Type casting
  // (putting (void *) in front of an expression) signals the compiler
  // that we "know what we are doing" and are accepting the consequences.

  memcpy((void *) c, (void *) addressOfX, 4);  

  cout << "The value of x is   :" << x << "\n"
       << "The address of x is :" << addressOfX << "\n";

  /* casting to void * is necessary when printing the address
     of a character.  Otherwise, C++ treats that as "printing a string",
     and will print out all characters from that address until hitting
     a \0 */

  for (int i=0; i<4; i++)
    {
      cout << "The value of c[" << i << "] is   :" << c[i] << "\n"
	   << "The address of c[" << i << "] is :" << (void *) &c[i] << "\n";
    }


  if ( c[0] == '\0' &&  c[1] == '\0' &&  c[2] == '\0' &&  c[3] == 'A')
    {
      cout << "Your system is big-endian." << endl;
    }
  else if ( c[0] == 'A' &&  c[1] == '\0' &&  c[2] == '\0' &&  c[3] == '\0')
    {
      cout << "Your system is little-endian." << endl;
    }
  else
    {
      cout << "I can't tell is your system is big or little endian" << endl;
      cout << "Something is probably wrong with the program." << endl;
    }

  return 0;

}


// ***********************************************************
//  A brief explanation of big-endian vs. little endian
//      P. Conrad, Fall 2005, University of Delaware
// ***********************************************************

// A system is "big-endian" if, when you copy the bits
// from a 32-bit integer i into a 4-byte character array c
// the most significant byte i ends up in c[0], and the
// least significant byte ends up in c[3].  

// In little endian, the bytes are arranged from "left to right", just
// the way we are used to seeing them on a chalkboard.  Hence, there
// is a kind of logic to this approach.

// A system is "little-endian" if, when you do the same copy
// as above, the LEAST significant byte of i ends up in c[0],
// and the MOST significant byte of i ends up in c[3].  

// Little endian also has a kind of logic too it---after all, 0 is
// little and 3 is big, and the "little end" of the number ends up in
// position 0, and the "big end" ends up in position 3.  However, when
// you draw this one on the chalkboard, it ends up looking
// strange... the MOST signficant bits are still at the left end of
// each byte, but the LEAST significant bytes are the left end of the
// whole integer.

// However, the big endian approach seems more natural to many folks,
// and I count myself as one of them.  Here's why I feel this way:
//
//   * In the big endian approach the bits and the bytes flow in the same
//     direction as each other (most to least significant, left to right)
// 
//   * The direction that BOTH bits and bytes flow is the SAME way as
//     in the numbers we are used to from "real life" when we write
//     them on a chalkboard, i.e. most to least significant, left to right)
// 
//     e.g. consider my salary of $124.53 a week to teach this class: 
//     the 1 (representing $100) is the most significant digit, 
//     and the 3 (representing 3 cents) is the least signicant. 
//       
// Incidentally, big-endian byte order is also known as "network byte order"
// because it is the standard used on the Internet for the TCP/IP suite of
// protocols.  IP addresses, port numbers, etc. are all stored in 
// "big-endian" format when the packets go on the network.  So machines
// like strauss don't have to do any conversion, while PCs always have to
// flip the byte order on the way to/from the network.
//
// As it turns out, though, WAV files and GIF files grew up in the land
// of the Intel-based IBM PC.  As a result, the byte order in those
// file formats is little-endian.   So, if we want to work with WAV files
// on strauss, we have to do the conversion between big-endian and 
// little-endian, a conversion that does NOT have to be done on the PC
// side for WAV and GIF files.  
//
// The need to convert between endian's when writing WAV files from a C++
// program on strauss is the _only_ reason that we are
// even bothering with this topic, which normally wouldn't even be covered in
// this course!  You'd normally see that in a course like CISC260 or CISC360.






