Intel HEX format
Intel Hex is one of the oldest file formats available and is adopted by many newcomers on the market.
Therefore this file format is almost always supported by various development systems and tools.
All data lines are called records and each record contains the following fields:
Every record begins with a colon (ASCII value $3A). Records contain only ASCII characters! No spaces or tabs are allowed in a record. In fact, apart from the 1st colon, no other characters than 0..9 and A..F are allowed in a record. Interpretation of a record should be case insensitive, so it does not matter if you use a..f or A..F.
The byte count cc counts the actual data bytes in the current record.
Usually records have 32 data bytes, but any number between 0 and 255 is possible.
This is the address where the first data byte of the record should be stored.
After storing that data byte the address is incremented by 1 to point to the address for the next data byte of the record.
And so on, until all data bytes are stored.
target address = segment*16+aaaa
In case of Upper Linear Base Address mode the upper 16 bits of the 32 bit address are pre-set by a special record type.
In this case the address space is expanded to 32 bits, which gives us a total range of 4Gb.
target address = ulba*65536+aaaa
There are 5 record types defined:
'00' = Data Record
Type '00' is the main record type. The real data are sent using this record type. The 1st data byte of the record is stored in the address specified by the address field of the record (plus the pre-set Segment or Linear Base Address). After that the address of the address field is incremented and the next data byte is stored on the next address. The address in the address field is 16 bits, so a rollover from $FFFF to $0000 can occur. This will not produce a carry into the Segment or Linear Base Address, so addressing space is wrapped back!
Type '01' is the End Of File record. The receiver of the file will stop waiting for new records after receiving this record. The byte count and the address field of this record must always be $00. Because the contents of this record type is fixed, the checksum field is always $FF.
Type '02' records are used to pre-set the Extended Segment Address. With this segment address it is possible to send files up to 1Mb in length. The Segment address is multiplied by 16 and then added to all subsequent address fields of type '00' records to obtain the effective address. By default the Extended Segment address will be $0000, until it is specified by a type '02' record. The address field of a type '02' record must be $00. The byte count field will be $02 (the segment address consists of 2 bytes). The data field of the type '02' record contains the actual Extended Segment address. Bits 3..0 of this Extended Segment address always should always be 0!
Type '03' records don't contribute to file transfers. They are used to specify the start address for Intel processors, like the 8086. So if you would upload a file to an Intel based development board, the starting address of the code can be specified with this record type. This starting address will be loaded into the CS and IP registers of the processor. For normal file transfers the type '03' records can be ignored. The byte count of type '03' record is $04, because 4 data bytes will be sent. The address field remains $0000. The data field of type '03' records contain 4 bytes, the first 2 bytes represent the value to be loaded into CS, the last 2 bytes are the value to be loaded into IP. Bytes are sent MSB first.
Type '04' records are used to pre-set the Linear Base Address. This 16 bit Linear Base Address, specified in the data area, is used to obtain a full 32 bit address range when combined with the address field of type '00' records. With this LBA it is possible to send files of up to 4Gb in length. The Linear Base Address is used as the upper 16 bits in the 32 bit linear address space. The lower 16 bits will come from the address field of type '00' records. By default the Linear Base Address will be $0000, until specified by a type '04' record. The address field of a type '04' record must be $0000. The byte count field will be $02 (the LBA consists of 2 bytes). The data field of the type '04' record contains the actual 2 byte Linear Base Address. MSB is sent first.
Type '05' records don't contribute to file transfers. They are used to specify the start address for Intel processors, like the 80386. If you would upload a file to an Intel based development board, the starting address of the code can be specified with a type 05 record. This starting address will be loaded in the EIP register of the processor. For normal file transfers the type '05' records can be ignored. The byte count of type '05' records is $04, because 4 data bytes will be sent. The address field remains $0000. The data field of type '05' records contain the 4 byte linear 32 bit starting address to be loaded into the EIP register of the processor.
Data or Offset field
This field contains 0 or more data bytes. The actual number of data bytes is indicated by the byte count in the beginning of the record. The data bytes are interpreted as real "payload" data in type '00' records. In other record types the data represent pre-set address values.
This field is a one byte (2 hex digits) 2's complement checksum of the entire record. To create the checksum make a 1 byte sum from all fields of the record:
byte count + both address bytes + record type + all data bytes.
Then take the 2's complement of this sum to create the final checksum. Checking the checksum at the receiver's end is simply done by adding all bytes together including the checksum itself, discarding all carries, and the result must be $00.
In the example above you can see a piece of code with normal 16 bit addressing.
The first 4 lines have 16 bytes of data each, which can be seen by the byte count, the first byte of each line.
The 5th line has only 4 bytes because the program is at its end there.
Here you see an example with an address gap.
The first part of the program starts at address $0000.
After the second record the address has suddenly changed to $1000.
All date in the addresses in between remain unchanged, or are undefined.
It is even possible to fill in these "blanks" later, without destroying the code presented in this example.
As you can see not all lines have the same number of data bytes, which is no problem.
In this final example I show you a piece of code with Extended Segment records in it.
The first record is one of them.
Here the Extended Segment address is set to $2BC0, which means that $2BC00 is added to all subsequent address fields to obtain the target address of the data.
E.g. the first data byte of the 2nd record is stored at location $2BC00+$1234=$2CE34.