ihex:   Intel HEX files
1 Quick start
2 Records
ihex-record
parse-ihex
parse-ihex-line
3 Writing
write-ihex-record
bytes->ihex
4 Images
ihex->bytes
ihex-start-address
5 License
9.3

ihex: Intel HEX files🔗ℹ

turinglambdaai

 (require ihex) package: ihex

The "ihex" package reads and writes Intel HEX files — the record-oriented firmware image format used by toolchains for 8051, MCS-96, ARM and countless other targets. Everything is implemented in pure Racket: no FFI, no system dependency.

The writer is validated byte-for-byte against the Python bincopy library (its outputs ship as golden fixtures), and the parser accepts everything bincopy produces. The image writer goes one step further than bincopy: data records never cross a 64K address window (a record’s address field is only 16 bits, so a straddling record would be unrepresentable) — an extended-linear record is inserted at every window change.

1 Quick start🔗ℹ

Examples:
> (define image (bytes 222 173 190 239))
> (define text (bytes->ihex image
                            #:base-address 4096
                            #:start-address 4096))
> (display text)

:04100000DEADBEEFB4

:0400000500001000E7

:00000001FF

> (ihex->bytes text)

#"\336\255\276\357"

> (ihex-start-address text)

4096

2 Records🔗ℹ

A record line is :<length><address><type><data><checksum>, all hexadecimal, with a two’s-complement checksum over length, address, type and data — ihex-checksum. Record types: 00 data, 01 end-of-file, 02 extended segment address (segment × 16 + offset), 03 start segment address, 04 extended linear address (upper 16 bits of a 32-bit address) and 05 start linear address (a 32-bit entry point).

struct

(struct ihex-record (kind address data))

  kind : 
(or/c 'data
      'eof
      'extended-segment
      'start-segment
      'extended-linear
      'start-linear)
  address : exact-nonnegative-integer?
  data : bytes?
One record line. Non-data records are validated structurally on parse: their address must be 0 and their data length fixed (2 bytes for the extended-address records, 4 for the start records, 0 for end-of-file).

procedure

(parse-ihex text) → (listof ihex-record?)

  text : string?
Parses a whole file. Blank lines are skipped and \r\n endings are accepted. Grammar violations — bad data lengths, checksum mismatches, odd hex, unknown record types, misplaced addresses — raise exn:fail with the line number.

procedure

(parse-ihex-line line line-no) → ihex-record?

  line : string?
  line-no : exact-nonnegative-integer?
Parses one record line; line-no only appears in error messages.

3 Writing🔗ℹ

procedure

(write-ihex-record rec) → string?

  rec : ihex-record?
Renders one record line (no newline). Non-data records must carry address 0.

procedure

(bytes->ihex bs    
  [#:base-address base    
  #:data-bytes-per-line per-line    
  #:start-address start    
  #:trailing-newline? trailing-newline?]) → string?
  bs : bytes?
  base : exact-nonnegative-integer? = 0
  per-line : (integer-in 1 255) = 16
  start : (or/c exact-nonnegative-integer? #f) = #f
  trailing-newline? : boolean? = #t
Renders a complete image: data records split on the configured line length and at 64K window boundaries (each window change emits an extended-linear record), the optional start-linear record and the end-of-file record.

4 Images🔗ℹ

procedure

(ihex->bytes text [#:fill fill]) → bytes?

  text : string?
  fill : (integer-in 0 255) = 255
Assembles the data records into one byte string, applying extended-linear and extended-segment bases. Gaps are filled with fill; overlapping records are an error; end-of-file and start records are ignored.

procedure

(ihex-start-address text)

 → (or/c exact-nonnegative-integer? #f)
  text : string?
The entry address of the last start record: the 32-bit big-endian word of a start-linear record, or the two 16-bit words of a start-segment record concatenated big-endian (the same interpretation bincopy uses). #f when the file declares no start address.

5 License🔗ℹ

MIT.