Here is a simple Ruby module for reading files in the Intel HEX format.
I did not put this implementation on RubyGems nor GitHub since there already several such implementations that are probably much better than mine.
Documentation
The documentation is included in the source code bellow.
Download
The Ruby module for reading Intel HEX file can be downloaded from the following link:
ihex_2016_12_18.zip
Source code
The source code of ihex.rb is the following:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 |
######################################################################## ## === Ihex reader tool # # Usage: in *ruby* code # # include Ihex # ... # # Read ihex file "name" # code = Code.new(name) # # # Iterate over each section of the code # code.each_section do |section| # # Iterate over each byte of the section # section.each { |byte| ... } # ... # # Gets the byte at offset 152 is section # byte = section[152] # ... # end # # # Gets the byte at address 0x6000 # byte = code[0x6000] # # Author:: Lovic Gauthier # License:: Distributes under the terms of the MIT Licence. # # Please see: [http://opensource.org/licenses/MIT] ######################################################################## module Ihex VERSION = "0.0.1" ## # Represents a data section. class Section include Enumerable attr_reader :address ## Creates a session from a starting +address+. def initialize(address) @address = address @data = [] # Initially the section is empty end ## Interates over each byte of the section. def each(&blk) @data.each(&blk) end ## Adds a +byte+ of data at the end of the section. def push(byte) @data.push(byte) end ## Gets the size of the section in bytes. def size @data.size end ## Gets a byte by offset (from the start address). def [](offset) @data[offset] end end ## # Reads and stores the the content of a ihex file. class Code ## The start address. attr_reader :_start ## Reads the ihex file +name+ (including the path). def initialize(name) @sections = {} # Initialize the sections @_start = 0 # Address of the boot function file_content = File.read(name) base = 0 # Base address is 0 by default last_addr = 0 # Last address section = nil # Current section file_content.each_line.with_index do |line,i| print line # Checks the line syntax unless line.start_with?(":") then raise "Parse error line #{i+1} col 0: missing ':'" end line.strip! line[1..-1].each_char.with_index do |c,j| unless c =‾ /[0-9A-F]/ then raise "Parse error line #{i+1} col #{j+1}: " + "invalid character: #{c}" end end # Convert the line to byte array bytes = line[1..-1].scan(/.{2}/).map! {|str| str.to_i(16) } # Check the data size size = bytes[0] unless size+5 == bytes.size then raise "Parse error line #{i+1}: invalid size." end # Check the sum checksum = bytes.reduce(0) { |sum,b| sum = (sum+b) & 255 } unless checksum == 0 then raise "Parse error line #{i+1}: invalid checksum." end # Load the line type = bytes[3] # print "Section with type=#{type}¥n" case type when 0 then # Data line # Get the address addr = bytes[1]*256 + bytes[2] + base # print "addr = 0x#{addr.to_s(16)}¥n" if addr > last_addr + 1 || section == nil then # Create a new section section = Section.new(addr) @sections[addr] = section end # Adds the data bytes[4..-2].each { |b| section.push(b) } last_addr = addr + size when 1 then # End of file return when 3 then @_start = (bytes[4] << 24) + (bytes[5] << 16) + (bytes[6] << 8) + (bytes[7]) when 4 then # 32-bit address base = (bytes[4]*256+bytes[5])*65536 when 5 then # 32-bit address (maybe) base = (bytes[4]*256+bytes[5])*65536 else raise "Unsupported line type: #{type}" end end end ## Iterates over the sections. def each_section(&blk) @sections.each_value(&blk) end ## Gets a byte by +address+. def [](address) # Look for the section with right address section = @sections.to_a.lazy.reverse.bsearch do |s| s.address <= address end # Access the section return section[address-section.address] end end end |