intel_hex 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 84c4297419b1ccfc1e823f6a20c501214edde2a36ac39fe99f1160878ef55f98
4
+ data.tar.gz: 8d2b0689beaa411af37370cdfa389b386cc4b44d05b7851ff3165f5ddcd546c4
5
+ SHA512:
6
+ metadata.gz: f983e15e9e06d56517b3101bef8580807c2221a7dbd24252c4de2784996803db5b6015aa2e4634334514024e61bdb63f69f33e7b6a168408ae27eb7223323536
7
+ data.tar.gz: 96208bdfed373003d8ba309af7017ec7065519bf9a638fa3b8b7e56977617ee2a1390686b00730f103fc4b6468159a193d32d10412d2b1707fceebf69b8cd12b
data/LICENSE.md ADDED
@@ -0,0 +1,28 @@
1
+ This software is licensed under the Revised (3-clause) BSD license as follows:
2
+
3
+ Copyright (c) 2019, Jeremy Cole <jeremy@jcole.us>
4
+
5
+ All rights reserved.
6
+
7
+ Redistribution and use in source and binary forms, with or without
8
+ modification, are permitted provided that the following conditions are met:
9
+
10
+ * Redistributions of source code must retain the above copyright
11
+ notice, this list of conditions and the following disclaimer.
12
+ * Redistributions in binary form must reproduce the above copyright
13
+ notice, this list of conditions and the following disclaimer in the
14
+ documentation and/or other materials provided with the distribution.
15
+ * Neither the name of the <organization> nor the
16
+ names of its contributors may be used to endorse or promote products
17
+ derived from this software without specific prior written permission.
18
+
19
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
+ WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22
+ DISCLAIMED. IN NO EVENT SHALL <COPYRIGHT HOLDER> BE LIABLE FOR ANY
23
+ DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
24
+ (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
25
+ LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
26
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
27
+ (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
28
+ SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
data/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # An Intel hex file parser, for Ruby
2
+
3
+ See the [Wikipedia page on Intel HEX](https://en.wikipedia.org/wiki/Intel_HEX).
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require 'intel_hex'
5
+ require 'pp'
6
+
7
+ @options = {
8
+ file: nil,
9
+ }
10
+
11
+ OptionParser.new do |opts|
12
+ opts.on('--help', 'Print this help.') { puts; puts opts; puts; exit }
13
+ opts.on('-f', '--filename=FILE', 'Load the specified file.') { |o| @options[:file] = o }
14
+ end.parse!
15
+
16
+ raise "No file specified" unless @options[:file]
17
+
18
+ intel_hex_file = IntelHex::FileReader.new(@options[:file])
19
+
20
+ intel_hex_file.each_record do |record|
21
+ case record.type
22
+ when :data
23
+ record_data = "data=" + record.data.map { |b| "%02x" % b }.join(" ")
24
+ when :eof
25
+ record_data = ""
26
+ else
27
+ record_data = "value=" + record.send(data.type)
28
+ end
29
+
30
+ puts "Record type=%-4s offset=%-4d length=%-4d checksum=%-4d %s" % [
31
+ record.type,
32
+ record.offset,
33
+ record.length,
34
+ record.checksum,
35
+ record_data,
36
+ ]
37
+ end
38
+
data/lib/intel_hex.rb ADDED
@@ -0,0 +1,6 @@
1
+ module IntelHex
2
+ end
3
+
4
+ require 'intel_hex/version'
5
+ require 'intel_hex/record'
6
+ require 'intel_hex/file_reader'
@@ -0,0 +1,52 @@
1
+ module IntelHex
2
+ class FileReader
3
+ def initialize(filename)
4
+ @filename = filename
5
+ @address_base = 0
6
+ @address_mask = 0xffff
7
+ @esa = 0
8
+ @ssa = 0
9
+ @ela = 0
10
+ @sla = 0
11
+ end
12
+
13
+ def each_record
14
+ return to_enum(:each_record) unless block_given?
15
+
16
+ file = File.open(@filename, 'r')
17
+
18
+ begin
19
+ file.each_line do |line|
20
+ yield Record.parse(line.chomp)
21
+ end
22
+ rescue EOFError
23
+ return nil
24
+ end
25
+
26
+ nil
27
+ end
28
+
29
+ def each_byte_with_address
30
+ return to_enum(:each_byte_with_address) unless block_given?
31
+
32
+ each_record do |record|
33
+ case record.type
34
+ when :data
35
+ record.each_byte_with_address do |byte, offset|
36
+ yield byte, (@address_base + offset) & @address_mask
37
+ end
38
+ when :esa
39
+ @esa = record.esa
40
+ @address_base = @esa << 4 # bits 4..19 of address
41
+ @address_mask = 0xfffff # 20 bit address size
42
+ when :ela
43
+ @ela = record.ela
44
+ @address_base = @ela << 16 # bits 16..31 of address
45
+ @address_mask = 0xffffffff # 32 bit address size
46
+ end
47
+ end
48
+
49
+ nil
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,228 @@
1
+ module IntelHex
2
+ class MisformattedFileError < RuntimeError; end
3
+ class InvalidTypeError < RuntimeError; end
4
+ class InvalidLengthError < RuntimeError; end
5
+ class InvalidOffsetError < RuntimeError; end
6
+ class InvalidDataError < RuntimeError; end
7
+ class InvalidChecksumError < RuntimeError; end
8
+
9
+ class Record
10
+ TYPES = [
11
+ :data,
12
+ :eof,
13
+ :esa,
14
+ :ssa,
15
+ :ela,
16
+ :sla,
17
+ ]
18
+
19
+ TYPE_MAP = TYPES.each_with_index.inject({}) { |h, (k, i)| h[k] = i; h }
20
+
21
+ TYPE_DATA_LENGTH = {
22
+ data: (1..255),
23
+ eof: 0,
24
+ esa: 2,
25
+ ssa: 2,
26
+ ela: 2,
27
+ sla: 4,
28
+ }
29
+
30
+ def self.parse(line)
31
+ raise MisformattedFileError.new("Expected ':' at start of line") unless line[0] == ':'
32
+ raise MisformattedFileError.new("Line length incorrect") unless line.size >= (1 + 2 + 4 + 2)
33
+
34
+ length = line[1..2].to_i(16)
35
+ data_end = (9 + length*2)
36
+
37
+ offset = line[3..6].to_i(16)
38
+ type = TYPES[line[7..8].to_i(16)]
39
+ data = line[9...data_end].split('').each_slice(2).map { |a| a.join.to_i(16) }
40
+ checksum = line[data_end..(data_end + 2)].to_i(16)
41
+
42
+ Record.new(type, length, offset, data, checksum, line: line, validate: true)
43
+ end
44
+
45
+ def self.data(data)
46
+ record = Record.new(:data)
47
+ record.data = data
48
+ record
49
+ end
50
+
51
+ def self.eof
52
+ Record.new(:eof)
53
+ end
54
+
55
+ def self.type_with_value(type, value)
56
+ record = Record.new(type)
57
+ record.send((type.to_s + '=').to_sym, value)
58
+ record
59
+ end
60
+
61
+ def self.esa(value)
62
+ type_with_value(:esa, value)
63
+ end
64
+
65
+ def self.ssa(value)
66
+ type_with_value(:ssa, value)
67
+ end
68
+
69
+ def self.ela(value)
70
+ type_with_value(:ela, value)
71
+ end
72
+
73
+ def self.sla(value)
74
+ type_with_value(:sla, value)
75
+ end
76
+
77
+ attr_reader :length
78
+ attr_reader :offset
79
+ attr_reader :type
80
+ attr_reader :data
81
+ attr_reader :checksum
82
+ attr_reader :line
83
+
84
+ def initialize(type, length = 0, offset = 0, data = [], checksum=nil, line: nil, validate: false)
85
+ @type = type
86
+ @length = length
87
+ @offset = offset
88
+ @data = data
89
+ @calculated_checksum = nil
90
+ @checksum = checksum || calculate_checksum
91
+ @line = line
92
+
93
+ self.validate if validate
94
+ end
95
+
96
+ def to_s
97
+ "#<#{self.class.name} type=#{type} offset=#{offset} length=#{length}>"
98
+ end
99
+
100
+ def to_ascii
101
+ ":%02X%04X%02X%s%02X" % [
102
+ length,
103
+ offset,
104
+ TYPE_MAP[type],
105
+ data.map { |b| "%02X" % b }.join,
106
+ checksum,
107
+ ]
108
+ end
109
+
110
+ def calculate_checksum
111
+ return @calculated_checksum if @calculated_checksum
112
+
113
+ sum = length + ((offset & 0xff00) >> 8) + (offset & 0x00ff) + TYPE_MAP[type]
114
+ sum += data.inject(0) { |s, n| s += n; s }
115
+
116
+ @calculated_checksum = (((sum & 0xff) ^ 0xff) + 1) & 0xff
117
+ end
118
+
119
+ def recalculate_checksum
120
+ @calculated_checksum = nil
121
+ calculate_checksum
122
+ end
123
+
124
+ def valid?
125
+ begin
126
+ validate
127
+ return true
128
+ rescue
129
+ return false
130
+ end
131
+ end
132
+
133
+ def validate
134
+ validate_type
135
+ validate_length
136
+ validate_offset
137
+ validate_data
138
+ validate_checksum
139
+ end
140
+
141
+ def validate_type
142
+ raise InvalidTypeError.new("Type #{type} is invalid") unless TYPE_MAP.include?(type)
143
+ end
144
+
145
+ def validate_offset
146
+ raise InvalidOffsetError.new("Offset #{offset} is negative") unless offset >= 0
147
+ raise InvalidOffsetError.new("Offset #{offset} is too large") unless offset <= 255
148
+ end
149
+
150
+ def validate_length
151
+ valid_length = TYPE_DATA_LENGTH[type]
152
+
153
+ case valid_length
154
+ when Integer
155
+ return if length == valid_length
156
+ when Range
157
+ return if valid_length.include?(length)
158
+ end
159
+
160
+ raise InvalidLengthError.new("Length for type #{type} must be #{valid_length}; #{length} given")
161
+ end
162
+
163
+ def validate_data
164
+ raise InvalidDataError.new("Data length #{data.size} does not match length #{length}") unless data.size == length
165
+ end
166
+
167
+ def validate_checksum
168
+ raise InvalidChecksumError.new("Checksum value #{checksum} does not match expected checksum #{calculate_checksum}") unless calculate_checksum == checksum
169
+ end
170
+
171
+ def each_byte_with_address
172
+ return Enumerator.new(self, :each_byte_with_address) unless block_given?
173
+
174
+ data.each_with_index do |byte, i|
175
+ yield byte, offset + i
176
+ end
177
+
178
+ nil
179
+ end
180
+
181
+ def data=(value)
182
+ raise "Incorrect data type" unless value.is_a?(Array)
183
+ raise InvalidLengthError.new("Data length #{value.size} too large") unless value.size <= 255
184
+ @data = value
185
+ @length = data.size
186
+ @checksum = recalculate_checksum
187
+ nil
188
+ end
189
+
190
+ def data_to_uint16(offset = 0)
191
+ (data[offset + 0] << 8) | data[offset + 1]
192
+ end
193
+
194
+ def uint16_to_data(value, offset = 0)
195
+ self.data[(offset...(offset+2))] = [
196
+ (value & 0xff00) >> 8,
197
+ value & 0x00ff
198
+ ]
199
+ @length = data.size
200
+ @checksum = recalculate_checksum
201
+ end
202
+
203
+ def data_to_uint32(offset = 0)
204
+ (data[offset + 0] << 24) | (data[offset + 1] << 16) | (data[offset + 2] << 8) | data[offset + 3]
205
+ end
206
+
207
+ def uint32_to_data(value, offset = 0)
208
+ self.data[(offset...(offset+4))] = [
209
+ (value & 0xff000000) >> 24,
210
+ (value & 0x00ff0000) >> 16,
211
+ (value & 0x0000ff00) >> 8,
212
+ value & 0x000000ff
213
+ ]
214
+ @length = data.size
215
+ @checksum = recalculate_checksum
216
+ end
217
+
218
+ alias_method :esa, :data_to_uint16
219
+ alias_method :ssa, :data_to_uint16
220
+ alias_method :ela, :data_to_uint16
221
+ alias_method :sla, :data_to_uint32
222
+
223
+ alias_method :esa=, :uint16_to_data
224
+ alias_method :ssa=, :uint16_to_data
225
+ alias_method :ela=, :uint16_to_data
226
+ alias_method :sla=, :uint32_to_data
227
+ end
228
+ end
@@ -0,0 +1,3 @@
1
+ module IntelHex
2
+ VERSION = '0.5.0'
3
+ end
metadata ADDED
@@ -0,0 +1,50 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: intel_hex
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.0
5
+ platform: ruby
6
+ authors:
7
+ - Jeremy Cole
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-05-04 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: An Intel hex file parser for (e.g. AVR) working with firmware files
14
+ email: jeremy@jcole.us
15
+ executables:
16
+ - intel_hex_reader
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE.md
21
+ - README.md
22
+ - bin/intel_hex_reader
23
+ - lib/intel_hex.rb
24
+ - lib/intel_hex/file_reader.rb
25
+ - lib/intel_hex/record.rb
26
+ - lib/intel_hex/version.rb
27
+ homepage: https://github.com/jeremycole/intel_hex
28
+ licenses:
29
+ - BSD-3-Clause
30
+ metadata: {}
31
+ post_install_message:
32
+ rdoc_options: []
33
+ require_paths:
34
+ - lib
35
+ required_ruby_version: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - ">="
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ required_rubygems_version: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - ">="
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ requirements: []
46
+ rubygems_version: 3.0.3
47
+ signing_key:
48
+ specification_version: 4
49
+ summary: Parser for Intel hex files
50
+ test_files: []