インテル HEX 形式

ここでは、インテル HEX 形式ファイルについて説明します。HEX ファイルは、プログラムイメージ(コードとデータを表すの 16 進数列)をテキストで表現したもので、主に ROM (Read Only Memory) 化プログラムを作成する場合に使用されます。HEX は米国インテル社が規定したファイル形式で、世界中で使用されています。

HEX ファイル

lil が生成する HEX ファイルは、このインテル HEX 形式に準拠しています。そのため、市販の多くのローダ、デバッガ、エミュレータ環境などで読み込むことができます。

HEX ファイルは、次のレコードを復帰/改行コード (0Dh, 0Ah) で区切って並べたものです。

各レコードは、次の共通形式を持ちます。各行はコロン (:) で始まり、いくつかの 16 進数が続きます。

:llaaaattdddd...ddddss\r\n

各レコードの意味

lil が生成する HEX ファイルの先頭には、データが 0 以外のアドレスから始まる場合、アドレス レコードが出力されます。アドレス レコードには ESA と ELA の 2 種類があります。これらのレコードの詳細については、後方の「アドレス レコード」を参照してください。
:02 0000 02 aaaa ss ← ESA レコード
:02 0000 04 aaaa ss ← ELA レコード

※実際のファイルに空白は挿入されません。

※aaaa は 10h 単位(ESA)または10000h 単位(ELA)のアドレスです。

※ss はチェックサムです。

次に、いくつかのデータ レコードが続きます。データ レコードが実際のプログラムコードとデータの内容を表します。
:nn aaaa 00 data... ss ← データ レコード

※nn はデータのバイト数です (最大 10h)。

※data... には実際のデータが nn 個続きます。

ソースコードの END 文で開始アドレスを指定した場合は、最後にタイプ 3 の開始アドレス レコードが作成されます。
:04 0000 03 seg-offs ss ← 開始アドレス レコード

※seg-offs は 4 バイトのセグメント:オフセット アドレスです。

最後に、必ずタイプ 1 のファイル終了レコードが出力されます。
:00 0000 01 ss ← 終了レコード

コードの書き方

アセンブラおよび C 言語のソースコードの書き方は、通常の実行ファイル (EXE, COM) を作成する場合と同じです。

ROM 化するプログラムでは、セグメントの絶対アドレスを指定する場合が多くなります。それには、ソースコード中で SEGMENT 文に AT 属性を指定する (アセンブラの場合) か、またはリンク時に lil の /S オプションを使用します。

アセンブル、コンパイル、リンク方法

Light Macro Assembler の場合   アセンブル方法は通常と同じですが、リンク時に lil に対して /HEX オプションを指定する必要があります。その結果、EXE や COM の代わりに HEX ファイルが生成されます。/E オプションで HEX ファイル名を指定することもできます。デフォルトでは、コマンドラインで一番最初に指定されたオブジェクト ファイルのベース名に拡張子「.HEX」を付けた名前になります。

Light C の場合   コンパイル オプション /HEX を指定します。または、コンパイルとリンクを個別に実行して、リンク時に /HEX オプションを指定します。

アドレス レコード

lil が生成する HEX ファイルでは、10000h 以上の配置アドレスを示すために、拡張セグメント アドレス (ESA) レコード拡張リニア アドレス (ELA) レコードの一方または両方が出力される場合があります。

実際のデータの配置アドレスは、ESA レコード、ELA レコード、およびデータ レコードで決まります。この 3 種類のレコードには、いずれも 16 ビットの配置アドレス指定フィールドがあります。各レコードに記入された配置アドレスは、ESA レコードでは 10h バイト単位、ELA レコードでは 10000h バイト単位、データ レコードでは 1 バイト単位で解釈されます。

各データ レコードのデータが実際に配置されるアドレスは、そのデータ レコード自身が指定する 16 ビットアドレスと、直前の ESA および ELA レコードの指定アドレスを加算した値になります。たとえば、直前の ESA レコードが 0002h、ELA レコードが 0003h を指定している場合、データ レコードの配置アドレスには、2h * 10h + 3h * 10000h = 30020h が加算されます。

	-----------------------
	桁      7 6 5 4 3 2 1 0
	-----------------------
	ELA     L L L L          ← 直前の ELA アドレス レコードで指定されたアドレス
	ESA           S S S S    ← 直前の ESA アドレス レコードで指定されたアドレス
	DATA            D D D D  ← データ レコード内で指定されたアドレス
	-----------------------
	ADDR    A A A A A A A A  ← 最終的な実効アドレス
	-----------------------

ESA と ELA の選択

ESA レコードは、10 バイトという細かい単位でアドレスを指定できますが、100000h 以上の大きなアドレスを表現できません。これに対して、ELA レコードは、大きなアドレスでも表現できますが、10000h 単位でしかアドレスを指定できません。したがって、セグメント アドレスの表現に使用されるアドレス レコードは、一意に決まる場合があります。

しかし、ESA レコードと ELA レコードのどちらでも表現できるセグメント アドレスもあります。たとえば、70000h というセグメント アドレスは、ESA レコード値の 7000h で表すことも、ELA レコード値の 0007h で表すこともできます。

lil の場合、デフォルトでは ESA レコードが使用され、ESA レコードだけで表現できない場合にのみ ELA レコードが使用されます。つまり、ESA レコードですべての配置アドレスを表現できるならば、ELA レコードは生成されません。

ただし、lil のオプション /RL を指定すると、ELA レコードが優先的に使用されます。

インテル HEX 形式ファイルの例

次は HEX ファイルの一例です。見やすいようにスペースを入れてありますが、実際の HEX ファイルでは 16 進数が連続して並んでいます。

:02 0000 02 2000 DC
:10 0000 00 B870178ED8BB2000C607AAEA00000120 EE
:02 0000 02 2001 DB
:0E 0000 00 43C607BBC6063412CCEA01008813 C3
:02 0000 02 1388 61
:04 0000 00 909090F4 58
:02 0000 02 1770 75
:04 0020 00 01020304 D2
:04 0000 03 20000000 D9
:00 0000 01 FF

次は、上の HEX ファイルのソースコードです。

assume cs:code
code segment
start: mov ax, seg data
  mov ds, ax
  mov bx, offset d1
  mov byte ptr [bx], 0AAh
  jmp far ptr step2
code ends

  assume cs:code2
code2 segment
step2: inc bx
  mov byte ptr [bx], 0BBh
  mov byte ptr ds:[1234h], 0CCh
  jmp far ptr step3
code2 ends

  assume cs:code3
code3 segment at 5000
  nop
step3: nop
  nop
  hlt
code3 ends

data segment at 6000
  org 20h
d1 db 1,2,3,4
data ends

  end start

アセンブル、リンク方法は次のとおりです。

lasm sample.asm
lil -hex -scode=2000 sample.obj