Intel HEX: описание формата файла |
![]() |
Добавил(а) microsin | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
[Введение] Этот документ описывает формат шестнадцатеричного объектного файла для 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 форматы) На рисунке показан пример содержимого HEX-файла. Примечание переводчика: для каждой отдельной архитектуры процессора могут понадобиться далеко не все записи. К примеру, для микроконтроллеров AVR реально используются только типы записей Data Record и End of File Record. Линкер GCC также добавляет в файл для AVR запись Start Segment Address Record (ближе к концу файла), но эта запись обычно никак не используется (не учитывается при прошивке микроконтроллера программатором). [Общий формат записи (General Record Format)]
Каждая запись в 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 (запись, содержащая данные) Примечание переводчика: для микроконтроллеров 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-битного формата)]
Запись 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 RECLEN LOAD OFFSET RECTYP ULBA CHKSUM [Extended Segment Address Record (16- или 32-битный формат)]
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 RECLEN LOAD OFFSET RECTYP USBA CHKSUM [Data Record (8-, 16- или 32-битный формат)]
Запись 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 RECLEN LOAD OFFSET RECTYP DATA CHKSUM [Start Linear Address Record (только 32-битный формат)]
Запись 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 RECLEN LOAD OFFSET RECTYP EIP CHKSUM [Start Segment Address Record (16- или 32-битный формат)]
Запись 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 RECLEN LOAD OFFSET RECTYP CS/IP CHKSUM [End of File Record (8-, 16- или 32-битный формат)]
Запись End of File Record указывает конец шестнадцатеричного объектного файла. Это строка, в которой содержатся символы ':00000001FF'. Примечание переводчика: в HEX-файлах микроконтроллера AVR эта запись также используется для обозначения конца файла. В отдельных полях записи имеется следующее содержимое: RECORD MARK RECLEN LOAD OFFSET RECTYP CHKSUM [Алгоритм подсчета контрольной суммы] Байт контрольной суммы 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 |