intel_hex 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: []