Page 2 of 2
Why is hex useful?
So far, using hex for a number looks as if it might be some sort of intellectual game or something that examiners can use to set a good question. Programmers are, as a group, fairly lazy and you can bet that they wouldn't make use of something if it didn't make life easy.
There are two ways in which hex makes life easier.
The first is that it can be used to write down very large integers in a compact form.
For example, AD45 is shorter than its decimal equivalent 44357 and as values increase the difference in length becomes even more pronounced.
This `short form' is really only a minor benefit compared with the second reason why we use hex.
Hex values are closely related to binary values and powers of two.
For example, 1KByte of memory is 1024 bytes in decimal. If you address each memory location in decimal the address range is 0 to 1023. In hex 1KByte is 400 bytes and the address range is 0 to 3FF.
This is quite interesting but you begin to see the pay off when you look at the problem of addressing say 64KBytes which in decimal is 65536 but in hex is simply 10000 bytes. The equivalent addressing ranges are 0 to 65535 in decimal and 0 to FFFF in hex.
The following table shows how much simpler these commonly used values and ranges look in hex:
||0 - F
||0 - FF
||0 - 3FF
||0 - FFF
||0 - FFFF
||0 - FFFFF
||0 - FFFFFF
If you look at this table you will notice that 1KByte stands out like a sore thumb as not being a nice hex number. The reason is that 1KByte is a strange unit of memory that has received attention just because it is close to 1000 decimal, i.e. 1024 bytes . It is more natural for the size of memory to double at each step because adding an additional hardware addressing line allows you to work with twice as much memory. This is the reason that the numbers that keep on occurring are always powers of two and hex is particularly good at representing powers of two in a nice neat format.
Now you can begin to see why hex is used so much to report addresses and data. In the case of an address the hex form tells you immediately what area of memory is concerned.
For example, if an error message talks about location EF10 then you should be able to figure out that it is an address in the first 64K, somewhere close to the top.
To really make use of such information however you need to be able to use a debugger to look at that area of memory and step through it a chunk at a time.
Of course being able to do this sometimes means being able to perform hex arithmetic. Not many people are any good at adding and subtracting hex numbers so most debuggers will do this for you and Windows calculator has a hex mode. In the old days programmers, usually assembly language programmers, often had pocket hex calculators - yes a real calculator that worked in hex. Assembly language programmers thought in hex.
And yes I knew things had gone too far when I realized I'd just written a check for $FB.
Hex for data
The reasons for using hex to represent addresses seem understandable but why use it for data?
The answer is that a single byte can store a number in the range 0 to 255 and this is often treated as a code for a displayable character. In this case the contents of a block of memory can be displayed as ASCII text which can be read, but if the memory is being used to store a program or some internally formatted data then displaying it as ASCII text produces nothing but a mess.
The solution is to display each byte as a hex value.
This is very neat because numbers in the range 0 to 255 correspond to 00 to FF in hex and so only two characters have to be allocated to each memory location in the display. (Compare this with the three needed if decimal is used!)
The other advantage of using hex is that particular data patterns are easier to spot in hex. For example you often get runs of FF, 0F or other simple hex values. Machine code instructions and even ASCII codes are also easier to spot in hex and if a item of data turns out to be an address what could be better than to have it already in hex!
Big-endian and little-endian
There is another useful feature of hex when used with addresses that allows the programmer to read a hex address when it is stored in a number of memory locations.
For example, suppose you want to store the data E3F7, as part of a program say, in two memory locations 1000 and 1001. You can do this without any need to resort to arithmetic by storing E3 in one location and F7 in other.
When the memory locations are put together then contents of the `high order byte' is placed in front of the contents of the low order byte to give the full address, E3F7. If the address had been written in decimal then things would be much more complicated.
For example, to store the address 1234 using two locations you have to split it up into a high and low byte:
high byte= INT(1234/256) = 4
low byte = 1234-INT(1234/256)*256 =210
which means that in decimal the high byte stores 4 and the low byte stores 210, which taken together look nothing like 1234.
It is much easier to work with hex because the splitting into high and low order bytes is just a matter of dividing up the hex digits two at a time.
This story would be complete and simple apart from the fact that the early computer chip makers, Intel, IBM, Motorola and Dec, couldn't agree on which order to store the bytes in.
In the case of Motorola and DEC the high order byte is always stored first. This is generally called Big-endian - decreasing numerical significance as the address increases or the big value first.
This means that in a memory listing you can read hex addresses from right to left.
Address 00 01 02 03 04
AF 34 FF 39 ...
can be read as two addresses, AF34 and FF39.
Unfortunately Intel and IBM store their addresses the other way around - that is, low order byte first. This is generally called Little-endian - increasing numerical significance as the address increases or little value first.
This means that in a simple memory dump the pairs of hex digits that form the addresses are reversed. For example,
Address 00 01 02 03 04
AF 34 FF 39 ...
in an Intel machine corresponds to the two addresses 34AF and 39FF.
If you swap between machines, especially any processor derived from the 68000 and the 80x86 family then you should watch out for this difference or risk getting your addresses in a twist. The same problem arises when transmitting bytes or bits even down a serial communication line - do you send the high byte or the low byte first. You always need to check which order the bits are in. The internet generally uses bigendian.
Assemblers and assembly language
C# Bit Bashing - The BitConverter
The Mod function