Intel HEX: описание формата файла Печать
Добавил(а) microsin   

intel-logoВ этой статье приведен перевод документа компании Intel "Hexadecimal Object File Format Specification" - описание широко распространенного текстового формата файла для хранения двоичных данных (формат Intel HEX). В тексте часто встречается термин "Шестнадцатеричный объектный файл", который на самом деле соответствует просто какой-то прошивке микроконтроллера (например, файл firmware микроконтроллера AVR, имеющий расширение HEX), т. е. имеется в виду просто сам HEX-файл.

[Введение]

Этот документ описывает формат шестнадцатеричного объектного файла для 8-, 16- и 32-битных микропроцессоров Intel. Шестнадцатеричный формат подходит для входных данных программаторов PROM или аппаратных эмуляторов.

Примечание переводчика: поскольку формат Intel HEX давно стал стандартном де-факто, он применяется для хранения данных и кода почти для всех без исключения встраиваемых (embedded) архитектур микроконтроллеров и процессоров, не только для процессоров Intel x86.

Шестнадцатеричный формат объектного файла - способ представления абсолютного двоичного объектного файла в символах ASCII. Поскольку файл кодируется только символами ASCII вместо двоичных байт, то становится возможным хранить файл на недвоичном носителе, таком как бумажная перфолента (paper-tape), перфокарты (punch cards) и т. д.; HEX-файл можно также легко отобразить на экране CRT терминала, принтере и т. п. 8-битный шестнадцатеричный формат объектного файла позволяет размещение кода и данных в пределах линейного 16-битного адресного пространства 8-битных процессоров Intel. 16-битный шестнадцатеричный формат позволяет использовать 20-битное сегментированное адресное пространство 16-битных процессоров Intel. И 32-битный формат позволяет применять линейное 32-битное адресное пространство 32-битных процессоров Intel.

Примечание переводчика: 8-, 16- и 32-битные форматы отличаются друг от друга только логической структурой, т. е. появлением в HEX-файле различных типов записей (типы записей см. далее). Общий формат HEX-файла и кодирование записей в нем при этом остается неизменными.

Шестнадцатеричное представление двоичного файла закодировано с помощью цифробуквенных символов ASCII. Например, 8-битное двоичное значение 00111111 соответствует шестнадцатеричному 3F. Чтобы закодировать это значение в ASCII, применяется 2 байта символов ASCII. Первый байт для нашего примера кодирования будет равен ASCII-символу '3' (в двоичном виде это 00110011 или 033H) и второй байт будет равен ASCII-символу 'F' (01000110 или 046H). Для каждого значения байта порядок следования шестнадцатеричных цифр всегда такой, что старшая цифра идет первой. ASCII-представление двоичного кода всегда требует в 2 раза больше байт данных, чем двоичное представление данных.

Шестнадцатеричный объектный файл разбит на блоки записей (строки), каждая из которых содержит тип записи, длину, адрес загрузки в память и дополнительную контрольную сумму. Здесь заданы шесть (6) различных типов записей, однако не все типы записей имеют важное значение для каждого конкретного случая. Вот эти типы записей:

· Data Record, запись для данных (8-, 16- или 32-bit форматы)
· End of File Record, запись для сигнала о конце файла (8-, 16- или 32-bit форматы)
· Extended Segment Address Record, запись для адреса расширенного сегмента (16- или 32-bit форматы)
· Start Segment Address Record, запись для начала сегмента (16- или 32-bit форматы)
· Extended Linear Address Record, запись для расширенного линейного адреса (только 32-bit формат)
· Start Linear Address Record, запись для начала линейного адреса (только 32-bit формат)

На рисунке показан пример содержимого HEX-файла.

format intel_hex

Примечание переводчика: для каждой отдельной архитектуры процессора могут понадобиться далеко не все записи. К примеру, для микроконтроллеров AVR реально используются только типы записей Data Record и End of File Record. Линкер GCC также добавляет в файл для AVR запись Start Segment Address Record (ближе к концу файла), но эта запись обычно никак не используется (не учитывается при прошивке микроконтроллера программатором).

[Общий формат записи (General Record Format)]

RECORD MARK
':'
RECLEN LOAD OFFSET RECTYP INFO или DATA CHKSUM
1 байт 1 байт 2 байта 1 байт n байт 1 байт

Каждая запись в HEX-файле (строка) начинается с поля маркера начала записи RECORD MARK, содержащего ASCII-код 03AH, символ двоеточия ':'.

Следующее поле каждой записи RECLEN, которое задает количество байт полезной информации, которая содержится в записи (эти байты идут за полем RECTYP). Помните, что один байт данных представлен двумя символами ASCII. Максимальное значение для поля RECLEN является шестнадцатеричное 'FF', т. е. полезных данных в записи может быть от 0 до 255 байт.

Следующее поле в каждой записи LOAD OFFSET, которое указывает 16-битный начальный адрес загрузки байт данных, так что это поле используется только для записей данных (Data Record). В других типах записей, где это поле не используется, оно должно быть закодировано четырьмя ASCII-символами нуля ('0000' или 030303030H).

Следующее поле в каждой записи RECTYP, которое обозначает тип этой записи. Поле RECTYP используется для интерпретации информации в записи. Вот как кодируются типы записей:

'00' Data Record (запись, содержащая данные)
'01' End of File Record (запись, сигнализирующая о конце файла)
'02' Extended Segment Address Record (запись адреса расширенного сегмента)
'03' Start Segment Address Record (запись адреса начала сегмента)
'04' Extended Linear Address Record (запись расширенного линейного адреса)
'05' Start Linear Address Record (запись адреса начала линейного адреса)

Примечание переводчика: для микроконтроллеров AVR в HEX-файле появляются только записи типа 00, 01 и 03. При этом запись типа 03 содержит абсолютный адрес старта программы в памяти, и не несет в себе никакого практического значения (эта запись обычно находится ближе к концу файла, перед записью 01 End of File Record).

Следующее поле в каждой записи переменной длины, поле INFO/DATA. Оно состоит из нулевого или большего количества байт (байт может быть от 0 до 255, в соответствии со значением поля RECLEN), где каждый байт закодирован как пара шестнадцатеричных цифр. Интерпретация этого поля зависит от значения поля RECTYP.

И наконец, каждая запись заканчивается полем CHKSUM, которое содержит шестнадцатеричное ASCII представление контрольной суммы (дополнение до двух, дополнительный код, two's complement sum) от всех байт записи, начиная включительно от поля RECLEN, и кончая включительно последним байтом поля INFO/DATA. При этом контрольная сумма вычисляется не от самих символов ASCII, а от представления каждой пары HEX-символов ASCII как одного байта. Таким образом, сумма всех пар в записи после конвертирования каждой пары в двоичное представление, от поля RECLEN включительно до поля CHKSUM включительно, равна 0.

[Extended Linear Address Record (только для 32-битного формата)]

RECORD MARK
':'
RECLEN
'02'
LOAD OFFSET
'0000'
RECTYP
'04'
ULBA CHKSUM
1 байт 1 байт 2 байта 1 байт 2 байта 1 байт

Запись 32-bit Extended Linear Address Record используется для указания битов 16..31 от Linear Base Address (линейный базовый адрес, LBA), где биты 0..15 адреса LBA равны 0. Биты 16..31 LBA называются Upper Linear Base Address (верхний линейный базовый адрес, ULBA). Абсолютный адрес памяти в байте содержимого последующей Data Record получается путем добавления LBA к смещению, вычисляемому путем добавления поля LOAD OFFSET, содержащегося в Data Record, к индексу байта в Data Record (0, 1, 2, ... n). Это добавление смещения делается по модулю 4G (32 бита), с игнорированием переноса, так что загрузка смещения (от 0FFFFFFFFH до 000000000H) приводит к циклическому огибанию от конца к началу 4G линейного адреса, задаваемого LBA. Линейный адрес, по которому загружается каждый отдельный байт, при этом вычисляется по формуле:

(LBA + DRLO + DRI) MOD 4G

Где DRLO равно полю LOAD OFFSET в записи Data Record, DRI равен индексу байта в записи Data Record.

Когда запись Extended Linear Address Record задает значение LBA, оно может появиться в любом месте 32-битного шестнадцатеричного объектного файла. Это значение остается эффективным, пока не появится другая запись Extended Linear Address Record. Значение по умолчанию для LBA равно нулю, пока не появится запись Extended Linear Address Record.

Примечание переводчика: в HEX-файлах AVR запись Extended Linear Address Record не используется.

В отдельных полях записи имеется следующее содержимое:

RECORD MARK
Это поле содержит байт 03AH, символ двоеточия, закодированный шестнадцатеричным символом ASCII (':').

RECLEN
Это поле содержит два байта 03032H, кодирующие шестнадцатеричными символами ASCII '02', что означает длину в байтах информации данных ULBA, содержащихся в этой записи.

LOAD OFFSET
Это поле содержит байты 030303030H, кодирующие шестнадцатеричными символами ASCII '0000', поскольку поле не используется в этой записи.

RECTYP
Это поле содержит байты 03034H, кодирующие шестнадцатеричными символами ASCII '04', что задает тип записи Extended Linear Address Record.

ULBA
Это поле содержит 4 шестнадцатеричные цифры ASCII, которые указывают 16-битное значение Upper Linear Base Address. Старший байт в этом адресе находится в паре 10 и 11 символов записи, младший байт в этом адресе находится в паре 12 и 13 символов записи.

CHKSUM
Это поле содержит контрольную сумму полей RECLEN, LOAD OFFSET, RECTYP и ULBA.

[Extended Segment Address Record (16- или 32-битный формат)]

RECORD MARK
':'
RECLEN
'02'
LOAD OFFSET
'0000'
RECTYP
'02'
USBA CHKSUM
1 байт 1 байт 2 байта 1 байт 2 байта 1 байт

16-битная запись Extended Segment Address Record используется, чтобы указать биты 4..19 базового адреса сегмента Segment Base Address (SBA), где биты 0-3 адреса SBA равны 0. Биты 4..19 SBA относятся к адресу Upper Segment Base Address (USBA). Абсолютный адрес памяти, содержащий каждый конкретный байт в последующей записи Data Record получается добавлением SBA к смещению, вычисляемому путем сложения поля LOAD OFFSET в записи Data Record с индексом байта в Data Record (0, 1, 2, ... n). Такое добавление смещение делается по модулю 64K (16-бит), с игнорированием переноса, так что в результате загрузка смещения огибает по циклу (от 0FFFFH до 00000H) от начала в конец сегмента 64K, заданного SBA. Адрес, по которому загружается каждый отдельный байт, при этом вычисляется по формуле:

SBA + ([DRLO + DRI] MOD 64K)

Где DRLO равно полю LOAD OFFSET в Data Record, DRI равен индексу байта в Data Record.

Когда запись Extended Segment Address Record задает значение SBA, оно может появиться в любом месте 16-битного шестнадцатеричного объектного файла. Это значение остается эффективными, пока не появится другая запись Extended Segment Address Record. Значение SBA по умолчанию равно 0, пока не появится запись Extended Segment Address Record.

Примечание переводчика: в HEX-файлах AVR запись Extended Segment Address Record не используется.

В отдельных полях записи имеется следующее содержимое:

RECORD MARK
Это поле содержит байт 03AH, символ двоеточия, закодированный шестнадцатеричным символом ASCII (':').

RECLEN
Это поле содержит два байта 03032H, кодирующие шестнадцатеричными символами ASCII '02', что означает длину в байтах информации данных USBA, содержащихся в этой записи.

LOAD OFFSET
Это поле содержит байты 030303030H, кодирующие шестнадцатеричными символами ASCII '0000', поскольку поле не используется в этой записи.

RECTYP
Это поле содержит байты 03032H, кодирующие шестнадцатеричными символами ASCII '02', что задает тип записи Extended Segment Address Record.

USBA
Это поле содержит 4 шестнадцатеричные цифры ASCII, которые указывают 16-битное значение Upper Segment Base Address. Старший байт в этом адресе находится в паре 10 и 11 символов записи, младший байт в этом адресе находится в паре 12 и 13 символов записи.

CHKSUM
Это поле содержит контрольную сумму полей RECLEN, LOAD OFFSET, RECTYP и USBA.

[Data Record (8-, 16- или 32-битный формат)]

RECORD MARK
':'
RECLEN LOAD OFFSET RECTYP
'00'
DATA CHKSUM
1 байт 1 байт 2 байта 1 байт n байт 1 байт

Запись Data Record предоставляет набор шестнадцатеричных цифр, в которых находится ASCII-код байтов данных, которые содержатся в порции данных образа памяти. Метод, используемый для вычисления абсолютного адреса (линейного в случае 8 и 32 бит и сегментированного в случае 16 бит) каждого байта описан в разделах Extended Linear Address Record и Extended Segment Address Record.

Примечание переводчика: эта запись - основная для HEX-файла прошивок AVR. Обычно в одной такой записи (строке) закодировано 16 байт кода программы firmware (в поле RECLEN содержится значение '10'). В последней строке Data Record файла HEX может содержаться меньше 16 байт (в поле RECLEN появится соответствующее значение).

В отдельных полях записи имеется следующее содержимое:

RECORD MARK
Это поле содержит байт 03AH, символ двоеточия, закодированный шестнадцатеричным символом ASCII (':').

RECLEN
Это поле содержит две шестнадцатеричные цифры ASCII, которые задают количество байт данных, находящихся в записи. Максимальное значение равно 'FF' или 04646H (что соответствует десятичному значению 255).

LOAD OFFSET
Это поле содержит четыре шестнадцатеричные цифры ASCII, представляющие смещение от LBA (см. раздел Extended Linear Address Record) или SBA (см. раздел Extended Segment Address Record), что задает адрес, куда будет помещен первый байт записи.

RECTYP
Это поле содержит байты 03030H, кодирующие шестнадцатеричными символами ASCII '00', что задает тип записи Data Record.

DATA
Это поле содержит пары шестнадцатеричных цифр ASCII, где каждая пара кодирует один байт данных.

CHKSUM
Это поле содержит контрольную сумму полей RECLEN, LOAD OFFSET, RECTYP и DATA.

[Start Linear Address Record (только 32-битный формат)]

RECORD MARK
':'
RECLEN
'04'
LOAD OFFSET
'0000'
RECTYP
'05'
EIP CHKSUM
1 байт 1 байт 2 байта 1 байт 4 байта 1 байт

Запись Start Linear Address Record используется для указания стартового адреса объектного файла, с которого начнется выполнение программы. Значение дает линейный 32-битный адрес для регистра EIP. Имейте в виду, что эта запись только задает адрес кода в пределах 32-битного линейного адресного пространства процессора 80386. Если начинается выполнение кода в реальном режиме (real mode) 80386, то вместо этого должна использоваться Start Segment Address Record, поскольку эта запись указывает содержимое обоих регистров CS и IP, что необходимо для real mode.

Запись Start Linear Address Record может появиться в любом месте 32-битного шестнадцатеричного объектного файла. Если такая запись отсутствует в шестнадцатеричном объектном файле, то загрузчик свободен в назначении адреса старта по умолчанию.

Примечание переводчика: в HEX-файлах AVR запись Start Linear Address Record не используется.

В отдельных полях записи имеется следующее содержимое:

RECORD MARK
Это поле содержит байт 03AH, символ двоеточия, закодированный шестнадцатеричным символом ASCII (':').

RECLEN
Зто поле содержит 03034H, кодирующие шестнадцатеричными символами ASCII '04', что указывает длину, в байтах, содержимого регистра EIP в этой записи.

LOAD OFFSET
Это поле содержит 030303030H, кодирующие шестнадцатеричными символами ASCII '0000', поскольку это поле не используется в записи.

RECTYP
Это поле содержит 03035H, кодирующие шестнадцатеричными символами ASCII '05', что задает тип записи Start Linear Address Record.

EIP
Это поле содержит восемь символов ASCII шестнадцатеричных цифр, указывающих содержимое 32-битного регистра EIP. Старший байт адреса находится в паре символов 10 и 11.

CHKSUM
Это поле содержит контрольную сумму полей RECLEN, LOAD OFFSET, RECTYP и EIP.

[Start Segment Address Record (16- или 32-битный формат)]

RECORD MARK
':'
RECLEN
'04'
LOAD OFFSET
'0000'
RECTYP
'03'
CS/IP CHKSUM
1 байт 1 байт 2 байта 1 байт 4 байта 1 байт

Запись Start Segment Address Record используется для указания стартового адреса объектного файла, с которого начнется выполнение программы. Дается значение 20-битного сегментированного адреса для регистров CS и IP. Имейте в виду, что эта запись задает адрес кода только в пределах 20-битного сегментированного адресного пространства процессоров 8086/80186.

Запись Start Segment Address Record может появиться в любом месте 16-битного шестнадцатеричного объектного файла. Если такая запись отсутствует в шестнадцатеричном объектном файле, то загрузчик свободен в назначении адреса старта по умолчанию.

Примечание переводчика: в HEX-файлах AVR запись Start Segment Address Record используется, однако не несет в себе никакого практического значения. При этом значение CS равно 0, а значение IP равно байтовому адресу запуска программы firmware.

В отдельных полях записи имеется следующее содержимое:

RECORD MARK
Это поле содержит байт 03AH, символ двоеточия, закодированный шестнадцатеричным символом ASCII (':').

RECLEN
Зто поле содержит 03034H, кодирующие шестнадцатеричными символами ASCII '04', что указывает длину, в байтах, содержимого регистров CS/IP в этой записи.

LOAD OFFSET
Это поле содержит 030303030H, кодирующие шестнадцатеричными символами ASCII '0000', поскольку это поле не используется в записи.

RECTYP
Это поле содержит 03033H, кодирующие шестнадцатеричными символами ASCII '03', что задает тип записи Start Segment Address Record.

CS/IP
Это поле содержит восемь символов ASCII шестнадцатеричных цифр, указывающих содержимое 16-битного регистра CS и 16-битного регистра IP. Старший байт содержимого регистра CS находится в паре символов 10 и 11, младший в паре символов 12 и 13. Старший байт содержимого регистра IP находится в паре символов 14 и 15, младший в паре символов 16 и 17.

CHKSUM
Это поле содержит контрольную сумму полей RECLEN, LOAD OFFSET, RECTYP и CS/IP.

[End of File Record (8-, 16- или 32-битный формат)]

RECORD MARK
':'
RECLEN
'00'
LOAD OFFSET
'0000'
RECTYP
'01'
CHKSUM
'F'
1 байт 1 байт 2 байта 1 байт 1 байт

Запись End of File Record указывает конец шестнадцатеричного объектного файла. Это строка, в которой содержатся символы ':00000001FF'.

Примечание переводчика: в HEX-файлах микроконтроллера AVR эта запись также используется для обозначения конца файла.

В отдельных полях записи имеется следующее содержимое:

RECORD MARK
Это поле содержит байт 03AH, символ двоеточия, закодированный шестнадцатеричным символом ASCII (':').

RECLEN
то поле содержит 03030H, кодирующие шестнадцатеричными символами ASCII '00'. Поскольку эта запись не содержит никаких данных INFO/DATA, то длина равна нулю.

LOAD OFFSET
Это поле содержит 030303030H, кодирующие шестнадцатеричными символами ASCII '0000', поскольку это поле не используется в записи.

RECTYP
Это поле содержит 03031H, кодирующие шестнадцатеричными символами ASCII '01', что задает тип записи End of File Record.

CHKSUM
Это поле содержит контрольную сумму полей RECLEN, LOAD OFFSET и RECTYP. Поскольку все поля статические (их содержимое неизменно), то контрольную сумму также можно вычислить статически, и её значение 04646H, кодирующие шестнадцатеричными символами ASCII 'FF'.

[Алгоритм подсчета контрольной суммы]

Байт контрольной суммы CHKSUM для строки HEX-файла вычисляется так, чтобы байтовая сумма всех полезных данных строки и самой контрольной суммы с отбрасыванием переполнений равнялась нулю. При этом складываются не сами символы ASCII, а только данные, что они представляют. Упрощенный алгоритм подсчета контрольной суммы на псевдокоде для записи Data Record:

byte crcacc = 0;
crcacc += RECLEN;
crcacc += HIGHBYTE(LOAD OFFSET);
crcacc += LOWBYTE(LOAD OFFSET);
crcacc += RECTYP;
crcacc += DATA[0];
crcacc += DATA[1];
.. 
crcacc += DATA[RECLEN-1];
CHKSUM = (byte)(0x100-crcacc);

Чтобы было совсем понятно, разберем простой пример. Вот типичная строка HEX-файла прошивки микроконтроллера AVR (для наглядности поля помечены разными цветами):

:103800005CC000008FC0000073C0000071C00000E9

Эта строка требует записать в память FLASH микроконтроллера, начиная с адреса 0x3800 (адрес указан в поле LOAD OFFSET) байты 0x5C, 0xC0, 0x00, 0x00, 0x8F, 0xC0, 0x00, 0x00, 0x73, 0xC0, 0x00, 0x00, 0x71, 0xC0, 0x00, 0x00 (байты указаны в поле DATA), всего 16 байт (так как в поле RECLEN указано значение 0x10). Помеченный красным байт 0x00 RECTYP обозначает тип строки (записи) Data Record. При этом контрольная сумма CHKSUM записи (сумма байт полей RECLEN, LOAD OFFSET, RECTYP, DATA) равна 0xE9. Сумма байт всех полей RECLEN, LOAD OFFSET, RECTYP, DATA, CHKSUM должна быть равна 0.

Итак, контрольная сумма CHKSUM у нас получится, если сложим (отбрасывая перенос) байты данных начиная от поля RECLEN (0x10) до последнего байта поля DATA[RECLEN-1] (0x00), и затем вычтем из нуля полученную сумму (также отбрасывая перенос):

0xE9 = 0 - (0x10+0x38+0x00+0x00+0x5C+0xC0+0x00+0x00+0x8F+0xC0+0x00+0x00+0x73+0xC0+0x00+0x00+0x71+0xC0+0x00+0x00)

Если сложить все байты начиная от RECLEN до CHKSUM включительно (отбрасывая перенос), то получим ноль:

0x10+0x38+0x00+0x00+0x5C+0xC0+0x00+0x00+0x8F+0xC0+0x00+0x00+0x73+0xC0+0x00+0x00+0x71+0xC0+0x00+0x00+0xE9 = 0