origen_memory_image 0.7.0 → 0.8.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.
@@ -1,69 +1,69 @@
1
- module OrigenMemoryImage
2
- class Binary < Base
3
- def self.match?(file, snippet = false)
4
- if snippet
5
- file.all? { |l| l.strip =~ /^[01]*$/ }
6
- else
7
- # detect whether the data is mostly not alpha numeric
8
- filedata = (File.read(file, 256) || '')
9
- (filedata.gsub(/\s+/, '').gsub(/\w/, '').length.to_f / filedata.length.to_f) > 0.3
10
- end
11
- end
12
-
13
- # Always returns 0 since binary files do not contain addresses
14
- def start_address
15
- 0
16
- end
17
-
18
- def create_test_file
19
- data = [
20
- 0x1EE0021C, 0x22401BE0, 0x021C2243,
21
- 0x18E0021C, 0x5A780A43, 0x03E0034B,
22
- 0xF7215A78, 0x0A400020, 0x22E08442,
23
- 0x22D31FE0, 0x84421FD9, 0x1CE08442,
24
- 0x002B20D1, 0x03E0012A, 0x01D1002B,
25
- 0x1BD00223, 0x2340022A, 0x02D1002B,
26
- 0x15D103E0, 0x032A01D1, 0x78000018,
27
- 0x7C000018, 0x82000018, 0x88000018
28
- ]
29
- data = data.map { |d| d.to_s(2).rjust(32, '0') }.join
30
- File.open('examples/bin1.bin', 'wb') do |output|
31
- output.write [data].pack('B*')
32
- end
33
- end
34
-
35
- private
36
-
37
- # Returns an array containing all address/data from the given s-record
38
- # No address manipulation is performed, that is left to the caller to apply
39
- # any scrambling as required by the target system
40
- def extract_addr_data(options = {})
41
- options = {
42
- data_width_in_bytes: 4
43
- }.merge(options)
44
-
45
- result = []
46
- width = options[:data_width_in_bytes]
47
- address = 0
48
-
49
- if file
50
- raw = File.binread(file)
51
- bytes = raw.unpack('C*')
52
- else
53
- raw = lines.map(&:strip).join
54
- bytes = raw.scan(/.{1,8}/).map { |s| s.to_i(2) }
55
- end
56
-
57
- bytes.each_slice(width) do |d|
58
- v = 0
59
- width.times do |i|
60
- v |= d[i] << ((width - 1 - i) * 8) if d[i]
61
- end
62
- result << [address, v]
63
- address += width
64
- end
65
-
66
- result
67
- end
68
- end
69
- end
1
+ module OrigenMemoryImage
2
+ class Binary < Base
3
+ def self.match?(file, snippet = false)
4
+ if snippet
5
+ file.all? { |l| l.strip =~ /^[01]*$/ }
6
+ else
7
+ # detect whether the data is mostly not alpha numeric
8
+ filedata = (File.read(file, 256) || '')
9
+ (filedata.gsub(/\s+/, '').gsub(/\w/, '').length.to_f / filedata.length.to_f) > 0.3
10
+ end
11
+ end
12
+
13
+ # Always returns 0 since binary files do not contain addresses
14
+ def start_address
15
+ 0
16
+ end
17
+
18
+ def create_test_file
19
+ data = [
20
+ 0x1EE0021C, 0x22401BE0, 0x021C2243,
21
+ 0x18E0021C, 0x5A780A43, 0x03E0034B,
22
+ 0xF7215A78, 0x0A400020, 0x22E08442,
23
+ 0x22D31FE0, 0x84421FD9, 0x1CE08442,
24
+ 0x002B20D1, 0x03E0012A, 0x01D1002B,
25
+ 0x1BD00223, 0x2340022A, 0x02D1002B,
26
+ 0x15D103E0, 0x032A01D1, 0x78000018,
27
+ 0x7C000018, 0x82000018, 0x88000018
28
+ ]
29
+ data = data.map { |d| d.to_s(2).rjust(32, '0') }.join
30
+ File.open('examples/bin1.bin', 'wb') do |output|
31
+ output.write [data].pack('B*')
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ # Returns an array containing all address/data from the given s-record
38
+ # No address manipulation is performed, that is left to the caller to apply
39
+ # any scrambling as required by the target system
40
+ def extract_addr_data(options = {})
41
+ options = {
42
+ data_width_in_bytes: 4
43
+ }.merge(options)
44
+
45
+ result = []
46
+ width = options[:data_width_in_bytes]
47
+ address = 0
48
+
49
+ if file
50
+ raw = File.binread(file)
51
+ bytes = raw.unpack('C*')
52
+ else
53
+ raw = lines.map(&:strip).join
54
+ bytes = raw.scan(/.{1,8}/).map { |s| s.to_i(2) }
55
+ end
56
+
57
+ bytes.each_slice(width) do |d|
58
+ v = 0
59
+ width.times do |i|
60
+ v |= d[i] << ((width - 1 - i) * 8) if d[i]
61
+ end
62
+ result << [address, v]
63
+ address += width
64
+ end
65
+
66
+ result
67
+ end
68
+ end
69
+ end
@@ -1,56 +1,56 @@
1
- module OrigenMemoryImage
2
- class Hex < Base
3
- def self.match?(snippet)
4
- snippet.any? do |line|
5
- # Match a line like:
6
- # @180000F0
7
- line =~ /^@[0-9a-fA-F]+\s?$/
8
- end
9
- end
10
-
11
- # The first in the file will be taken as the start address
12
- def start_address
13
- @start_address ||= begin
14
- lines.each do |line|
15
- if line =~ /^@([0-9a-fA-F]+)\s?$/
16
- return Regexp.last_match[1].to_i(16)
17
- end
18
- end
19
- end
20
- end
21
-
22
- private
23
-
24
- # Returns an array containing all address/data from the given s-record
25
- # No address manipulation is performed, that is left to the caller to apply
26
- # any scrambling as required by the target system
27
- def extract_addr_data(options = {})
28
- options = {
29
- data_width_in_bytes: 4
30
- }.merge(options)
31
-
32
- result = []
33
- lines.each do |line|
34
- # Only if the line is an s-record with data...
35
- if line =~ /^@([0-9a-fA-F]+)\s?$/
36
- @address = Regexp.last_match[1].to_i(16)
37
- elsif line =~ /^[0-9A-F]/
38
- unless @address
39
- fail "Hex data found before an @address line in #{file_name}"
40
- end
41
- data = line.strip.gsub(/\s/, '')
42
- data_matcher = '\w\w' * options[:data_width_in_bytes]
43
- data.scan(/#{data_matcher}/).each do |data_packet|
44
- result << [@address, data_packet.to_i(16)]
45
- @address += options[:data_width_in_bytes]
46
- end
47
- # If a partial word is left over
48
- if (remainder = data.length % (2 * options[:data_width_in_bytes])) > 0
49
- result << [@address, data[data.length - remainder..data.length].to_i(16)]
50
- end
51
- end
52
- end
53
- result
54
- end
55
- end
56
- end
1
+ module OrigenMemoryImage
2
+ class Hex < Base
3
+ def self.match?(snippet)
4
+ snippet.any? do |line|
5
+ # Match a line like:
6
+ # @180000F0
7
+ line =~ /^@[0-9a-fA-F]+\s?$/
8
+ end
9
+ end
10
+
11
+ # The first in the file will be taken as the start address
12
+ def start_address
13
+ @start_address ||= begin
14
+ lines.each do |line|
15
+ if line =~ /^@([0-9a-fA-F]+)\s?$/
16
+ return Regexp.last_match[1].to_i(16)
17
+ end
18
+ end
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ # Returns an array containing all address/data from the given s-record
25
+ # No address manipulation is performed, that is left to the caller to apply
26
+ # any scrambling as required by the target system
27
+ def extract_addr_data(options = {})
28
+ options = {
29
+ data_width_in_bytes: 4
30
+ }.merge(options)
31
+
32
+ result = []
33
+ lines.each do |line|
34
+ # Only if the line is an s-record with data...
35
+ if line =~ /^@([0-9a-fA-F]+)\s?$/
36
+ @address = Regexp.last_match[1].to_i(16)
37
+ elsif line =~ /^[0-9A-F]/
38
+ unless @address
39
+ fail "Hex data found before an @address line in #{file_name}"
40
+ end
41
+ data = line.strip.gsub(/\s/, '')
42
+ data_matcher = '\w\w' * options[:data_width_in_bytes]
43
+ data.scan(/#{data_matcher}/).each do |data_packet|
44
+ result << [@address, data_packet.to_i(16)]
45
+ @address += options[:data_width_in_bytes]
46
+ end
47
+ # If a partial word is left over
48
+ if (remainder = data.length % (2 * options[:data_width_in_bytes])) > 0
49
+ result << [@address, data[data.length - remainder..data.length].to_i(16)]
50
+ end
51
+ end
52
+ end
53
+ result
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,98 @@
1
+ module OrigenMemoryImage
2
+ class IntelHex < Base
3
+ def self.match?(snippet)
4
+ snippet.all? do |line|
5
+ line.empty? || line =~ /^:[0-9A-Fa-f]{6}0[0-5]/
6
+ end
7
+ end
8
+
9
+ def start_address
10
+ @start_address ||= begin
11
+ addrs = []
12
+ lines.each do |line|
13
+ line = line.strip
14
+ if start_linear_address?(line)
15
+ addrs << decode(line)[:data].to_i(16)
16
+ end
17
+ end
18
+ addrs.last || 0
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def decode(line)
25
+ d = {}
26
+ if line =~ /^:([0-9A-Fa-f]{2})([0-9A-Fa-f]{4})(\d\d)([0-9A-Fa-f]+)([0-9A-Fa-f]{2})$/
27
+ d[:byte_count] = Regexp.last_match(1).to_i(16)
28
+ d[:address] = Regexp.last_match(2).to_i(16)
29
+ d[:record_type] = Regexp.last_match(3).to_i(16)
30
+ d[:data] = Regexp.last_match(4)
31
+ d[:checksum] = Regexp.last_match(5).to_i(16)
32
+ else
33
+ fail "Invalid line encountered in Intel Hex formatted file: #{line}"
34
+ end
35
+ d
36
+ end
37
+
38
+ def data?(line)
39
+ !!(line =~ /^:[0-9A-Fa-f]{6}00/)
40
+ end
41
+
42
+ def extended_segment_address?(line)
43
+ !!(line =~ /^:[0-9A-Fa-f]{6}02/)
44
+ end
45
+
46
+ def extended_linear_address?(line)
47
+ !!(line =~ /^:[0-9A-Fa-f]{6}04/)
48
+ end
49
+
50
+ def start_linear_address?(line)
51
+ !!(line =~ /^:[0-9A-Fa-f]{6}05/)
52
+ end
53
+
54
+ def upper_addr
55
+ @upper_addr || 0
56
+ end
57
+
58
+ def segment_address
59
+ @segment_address || 0
60
+ end
61
+
62
+ # Returns an array containing all address/data from the given s-record
63
+ # No address manipulation is performed, that is left to the caller to apply
64
+ # any scrambling as required by the target system
65
+ def extract_addr_data(options = {})
66
+ options = {
67
+ data_width_in_bytes: 4
68
+ }.merge(options)
69
+
70
+ result = []
71
+ lines.each do |line|
72
+ line = line.strip
73
+ if extended_segment_address?(line)
74
+ @segment_address = decode(line)[:data].to_i(16) * 16
75
+
76
+ elsif extended_linear_address?(line)
77
+ @upper_addr = (decode(line)[:data].to_i(16)) << 16
78
+
79
+ elsif data?(line)
80
+ d = decode(line)
81
+ addr = d[:address] + segment_address + upper_addr
82
+
83
+ data = d[:data]
84
+ data_matcher = '\w\w' * options[:data_width_in_bytes]
85
+ data.scan(/#{data_matcher}/).each do |data_packet|
86
+ result << [addr, data_packet.to_i(16)]
87
+ addr += options[:data_width_in_bytes]
88
+ end
89
+ # If a partial word is left over
90
+ if (remainder = data.length % (2 * options[:data_width_in_bytes])) > 0
91
+ result << [addr, data[data.length - remainder..data.length].to_i(16)]
92
+ end
93
+ end
94
+ end
95
+ result
96
+ end
97
+ end
98
+ end
@@ -1,212 +1,212 @@
1
- module OrigenMemoryImage
2
- # An S-record file consists of a sequence of specially formatted ASCII character strings. An S-record will
3
- # be less than or equal to 78 bytes in length.
4
- # The order of S-records within a file is of no significance and no particular order may be assumed.
5
- #
6
- # The general format of an S-record follows:
7
- #
8
- # +-------------------//------------------//-----------------------+
9
- # | type | count | address | data | checksum |
10
- # +-------------------//------------------//-----------------------+
11
- #
12
- # type
13
- # : A char[2] field. These characters describe the type of record (S0, S1, S2, S3, S5, S7, S8, or S9).
14
- #
15
- # count
16
- # : A char[2] field. These characters when paired and interpreted as a hexadecimal value, display
17
- # the count of remaining character pairs in the record.
18
- #
19
- # address
20
- # : A char[4,6, or 8] field. These characters grouped and interpreted as a hexadecimal value,
21
- # display the address at which the data field is to be loaded into memory. The length of the field depends
22
- # on the number of bytes necessary to hold the address. A 2-byte address uses 4 characters, a 3-byte
23
- # address uses 6 characters, and a 4-byte address uses 8 characters.
24
- #
25
- # data
26
- # : A char [0-64] field. These characters when paired and interpreted as hexadecimal values represent
27
- # the memory loadable data or descriptive information.
28
- #
29
- # checksum
30
- # : A char[2] field. These characters when paired and interpreted as a hexadecimal value display
31
- # the least significant byte of the ones complement of the sum of the byte values represented by the pairs
32
- # of characters making up the count, the address, and the data fields.
33
- #
34
- # Each record is terminated with a line feed. If any additional or different record terminator(s) or delay
35
- # characters are needed during transmission to the target system it is the responsibility of the
36
- # transmitting program to provide them.
37
- #
38
- # #### S0 Record
39
- #
40
- # The type of record is 'S0' (0x5330). The address field is unused and will be filled with zeros
41
- # (0x0000). The header information within the data field is divided into the following subfields.
42
- #
43
- # * mname is char[20] and is the module name.
44
- # * ver is char[2] and is the version number.
45
- # * rev is char[2] and is the revision number.
46
- # * description is char[0-36] and is a text comment.
47
- #
48
- # Each of the subfields is composed of ASCII bytes whose associated characters, when paired, represent one
49
- # byte hexadecimal values in the case of the version and revision numbers, or represent the hexadecimal
50
- # values of the ASCII characters comprising the module name and description.
51
- #
52
- # #### S1 Record
53
- #
54
- # The type of record field is 'S1' (0x5331). The address field is intrepreted as a 2-byte
55
- # address. The data field is composed of memory loadable data.
56
- #
57
- # #### S2 Record
58
- #
59
- # The type of record field is 'S2' (0x5332). The address field is intrepreted as a 3-byte
60
- # address. The data field is composed of memory loadable data.
61
- #
62
- # #### S3 Record
63
- #
64
- # The type of record field is 'S3' (0x5333). The address field is intrepreted as a 4-byte
65
- # address. The data field is composed of memory loadable data.
66
- #
67
- # #### S5 Record
68
- #
69
- # The type of record field is 'S5' (0x5335). The address field is intrepreted as a 2-byte value
70
- # and contains the count of S1, S2, and S3 records previously transmitted. There is no data field.
71
- #
72
- # #### S7 Record
73
- #
74
- # The type of record field is 'S7' (0x5337). The address field contains the starting execution
75
- # address and is intrepreted as 4-byte address. There is no data field.
76
- #
77
- # #### S8 Record
78
- #
79
- # The type of record field is 'S8' (0x5338). The address field contains the starting execution
80
- # address and is intrepreted as 3-byte address. There is no data field.
81
- #
82
- # #### S9 Record
83
- #
84
- # The type of record field is 'S9' (0x5339). The address field contains the starting execution
85
- # address and is intrepreted as 2-byte address. There is no data field.
86
- #
87
- # ### Example
88
- #
89
- # Shown below is a typical S-record format file.
90
- #
91
- # S00600004844521B
92
- # S1130000285F245F2212226A000424290008237C2A
93
- # S11300100002000800082629001853812341001813
94
- # S113002041E900084E42234300182342000824A952
95
- # S107003000144ED492
96
- # S5030004F8
97
- # S9030000FC
98
- #
99
- # The file consists of one S0 record, four S1 records, one S5 record and an S9 record.
100
- #
101
- # The S0 record is comprised as follows:
102
- #
103
- # * S0 S-record type S0, indicating it is a header record.
104
- # * 06 Hexadecimal 06 (decimal 6), indicating that six character pairs (or ASCII bytes) follow.
105
- # * 00 00 Four character 2-byte address field, zeroes in this example.
106
- # * 48 44 52 ASCII H, D, and R - "HDR".
107
- # * 1B The checksum.
108
- #
109
- # The first S1 record is comprised as follows:
110
- #
111
- # * S1 S-record type S1, indicating it is a data record to be loaded at a 2-byte address.
112
- # * 13 Hexadecimal 13 (decimal 19), indicating that nineteen character pairs, representing a 2 byte address,
113
- # * 16 bytes of binary data, and a 1 byte checksum, follow.
114
- # * 00 00 Four character 2-byte address field; hexidecimal address 0x0000, where the data which follows is to
115
- # be loaded.
116
- # * 28 5F 24 5F 22 12 22 6A 00 04 24 29 00 08 23 7C Sixteen character pairs representing the actual binary
117
- # data.
118
- # * 2A The checksum.
119
- # * The second and third S1 records each contain 0x13 (19) character pairs and are ended with checksums of 13
120
- # and 52, respectively. The fourth S1 record contains 07 character pairs and has a checksum of 92.
121
- #
122
- # The S5 record is comprised as follows:
123
- #
124
- # * S5 S-record type S5, indicating it is a count record indicating the number of S1 records
125
- # * 03 Hexadecimal 03 (decimal 3), indicating that three character pairs follow.
126
- # * 00 04 Hexadecimal 0004 (decimal 4), indicating that there are four data records previous to this record.
127
- # * F8 The checksum.
128
- #
129
- # The S9 record is comprised as follows:
130
- #
131
- # * S9 S-record type S9, indicating it is a termination record.
132
- # * 03 Hexadecimal 03 (decimal 3), indicating that three character pairs follow.
133
- # * 00 00 The address field, hexadecimal 0 (decimal 0) indicating the starting execution address.
134
- # * FC The checksum.
135
- #
136
- # ### Additional Notes
137
- #
138
- # There isn't any evidence that Motorola ever has made use of the header information within the data field
139
- # of the S0 record, as described above. This must have been used by some third party vendors.
140
- # This is the only place that a 78-byte limit on total record length or 64-byte limit on data length is
141
- # documented. These values shouldn't be trusted for the general case.
142
- #
143
- # The count field can have values in the range of 0x3 (2 bytes of address + 1 byte checksum = 3, a not
144
- # very useful record) to 0xff; this is the count of remaining character pairs, including checksum.
145
- # If you write code to convert S-Records, you should always assume that a record can be as long as 514
146
- # (decimal) characters in length (255 * 2 = 510, plus 4 characters for the type and count fields), plus
147
- # any terminating character(s).
148
- #
149
- # That is, in establishing an input buffer in C, you would declare it to be
150
- # an array of 515 chars, thus leaving room for the terminating null character.
151
- class SRecord < Base
152
- def self.match?(snippet)
153
- snippet.all? do |line|
154
- line.empty? || line =~ /^S[01235789]/
155
- end
156
- end
157
-
158
- def start_address
159
- @start_address ||= begin
160
- lines.each do |line|
161
- if line =~ /^S([789])(.*)/
162
- type = Regexp.last_match[1]
163
- case type
164
- when '7'
165
- return line.slice(4, 8).to_i(16)
166
- when '8'
167
- return line.slice(4, 6).to_i(16)
168
- when '9'
169
- return line.slice(4, 4).to_i(16)
170
- end
171
- end
172
- end
173
- end
174
- end
175
-
176
- private
177
-
178
- # Returns an array containing all address/data from the given s-record
179
- # No address manipulation is performed, that is left to the caller to apply
180
- # any scrambling as required by the target system
181
- def extract_addr_data(options = {})
182
- options = {
183
- data_width_in_bytes: 4
184
- }.merge(options)
185
-
186
- result = []
187
- lines.each do |line|
188
- # Only if the line is an s-record with data...
189
- if line =~ /^S([1-3])/
190
- type = Regexp.last_match[1].to_i(16) # S-record type, 1-3
191
- # Set the matcher to capture x number of bytes dependent on the s-rec type
192
- addr_matcher = '\w\w' * (1 + type)
193
- line.strip =~ /^S\d\w\w(#{addr_matcher})(\w*)\w\w$/ # $1 = address, $2 = data
194
- addr = Regexp.last_match[1].to_i(16)
195
- @start_address ||= addr
196
- @start_address = addr if addr < @start_address
197
- data = Regexp.last_match[2]
198
- data_matcher = '\w\w' * options[:data_width_in_bytes]
199
- data.scan(/#{data_matcher}/).each do |data_packet|
200
- result << [addr, data_packet.to_i(16)]
201
- addr += options[:data_width_in_bytes]
202
- end
203
- # If a partial word is left over
204
- if (remainder = data.length % (2 * options[:data_width_in_bytes])) > 0
205
- result << [addr, data[data.length - remainder..data.length].to_i(16)]
206
- end
207
- end
208
- end
209
- result
210
- end
211
- end
212
- end
1
+ module OrigenMemoryImage
2
+ # An S-record file consists of a sequence of specially formatted ASCII character strings. An S-record will
3
+ # be less than or equal to 78 bytes in length.
4
+ # The order of S-records within a file is of no significance and no particular order may be assumed.
5
+ #
6
+ # The general format of an S-record follows:
7
+ #
8
+ # +-------------------//------------------//-----------------------+
9
+ # | type | count | address | data | checksum |
10
+ # +-------------------//------------------//-----------------------+
11
+ #
12
+ # type
13
+ # : A char[2] field. These characters describe the type of record (S0, S1, S2, S3, S5, S7, S8, or S9).
14
+ #
15
+ # count
16
+ # : A char[2] field. These characters when paired and interpreted as a hexadecimal value, display
17
+ # the count of remaining character pairs in the record.
18
+ #
19
+ # address
20
+ # : A char[4,6, or 8] field. These characters grouped and interpreted as a hexadecimal value,
21
+ # display the address at which the data field is to be loaded into memory. The length of the field depends
22
+ # on the number of bytes necessary to hold the address. A 2-byte address uses 4 characters, a 3-byte
23
+ # address uses 6 characters, and a 4-byte address uses 8 characters.
24
+ #
25
+ # data
26
+ # : A char [0-64] field. These characters when paired and interpreted as hexadecimal values represent
27
+ # the memory loadable data or descriptive information.
28
+ #
29
+ # checksum
30
+ # : A char[2] field. These characters when paired and interpreted as a hexadecimal value display
31
+ # the least significant byte of the ones complement of the sum of the byte values represented by the pairs
32
+ # of characters making up the count, the address, and the data fields.
33
+ #
34
+ # Each record is terminated with a line feed. If any additional or different record terminator(s) or delay
35
+ # characters are needed during transmission to the target system it is the responsibility of the
36
+ # transmitting program to provide them.
37
+ #
38
+ # #### S0 Record
39
+ #
40
+ # The type of record is 'S0' (0x5330). The address field is unused and will be filled with zeros
41
+ # (0x0000). The header information within the data field is divided into the following subfields.
42
+ #
43
+ # * mname is char[20] and is the module name.
44
+ # * ver is char[2] and is the version number.
45
+ # * rev is char[2] and is the revision number.
46
+ # * description is char[0-36] and is a text comment.
47
+ #
48
+ # Each of the subfields is composed of ASCII bytes whose associated characters, when paired, represent one
49
+ # byte hexadecimal values in the case of the version and revision numbers, or represent the hexadecimal
50
+ # values of the ASCII characters comprising the module name and description.
51
+ #
52
+ # #### S1 Record
53
+ #
54
+ # The type of record field is 'S1' (0x5331). The address field is intrepreted as a 2-byte
55
+ # address. The data field is composed of memory loadable data.
56
+ #
57
+ # #### S2 Record
58
+ #
59
+ # The type of record field is 'S2' (0x5332). The address field is intrepreted as a 3-byte
60
+ # address. The data field is composed of memory loadable data.
61
+ #
62
+ # #### S3 Record
63
+ #
64
+ # The type of record field is 'S3' (0x5333). The address field is intrepreted as a 4-byte
65
+ # address. The data field is composed of memory loadable data.
66
+ #
67
+ # #### S5 Record
68
+ #
69
+ # The type of record field is 'S5' (0x5335). The address field is intrepreted as a 2-byte value
70
+ # and contains the count of S1, S2, and S3 records previously transmitted. There is no data field.
71
+ #
72
+ # #### S7 Record
73
+ #
74
+ # The type of record field is 'S7' (0x5337). The address field contains the starting execution
75
+ # address and is intrepreted as 4-byte address. There is no data field.
76
+ #
77
+ # #### S8 Record
78
+ #
79
+ # The type of record field is 'S8' (0x5338). The address field contains the starting execution
80
+ # address and is intrepreted as 3-byte address. There is no data field.
81
+ #
82
+ # #### S9 Record
83
+ #
84
+ # The type of record field is 'S9' (0x5339). The address field contains the starting execution
85
+ # address and is intrepreted as 2-byte address. There is no data field.
86
+ #
87
+ # ### Example
88
+ #
89
+ # Shown below is a typical S-record format file.
90
+ #
91
+ # S00600004844521B
92
+ # S1130000285F245F2212226A000424290008237C2A
93
+ # S11300100002000800082629001853812341001813
94
+ # S113002041E900084E42234300182342000824A952
95
+ # S107003000144ED492
96
+ # S5030004F8
97
+ # S9030000FC
98
+ #
99
+ # The file consists of one S0 record, four S1 records, one S5 record and an S9 record.
100
+ #
101
+ # The S0 record is comprised as follows:
102
+ #
103
+ # * S0 S-record type S0, indicating it is a header record.
104
+ # * 06 Hexadecimal 06 (decimal 6), indicating that six character pairs (or ASCII bytes) follow.
105
+ # * 00 00 Four character 2-byte address field, zeroes in this example.
106
+ # * 48 44 52 ASCII H, D, and R - "HDR".
107
+ # * 1B The checksum.
108
+ #
109
+ # The first S1 record is comprised as follows:
110
+ #
111
+ # * S1 S-record type S1, indicating it is a data record to be loaded at a 2-byte address.
112
+ # * 13 Hexadecimal 13 (decimal 19), indicating that nineteen character pairs, representing a 2 byte address,
113
+ # * 16 bytes of binary data, and a 1 byte checksum, follow.
114
+ # * 00 00 Four character 2-byte address field; hexidecimal address 0x0000, where the data which follows is to
115
+ # be loaded.
116
+ # * 28 5F 24 5F 22 12 22 6A 00 04 24 29 00 08 23 7C Sixteen character pairs representing the actual binary
117
+ # data.
118
+ # * 2A The checksum.
119
+ # * The second and third S1 records each contain 0x13 (19) character pairs and are ended with checksums of 13
120
+ # and 52, respectively. The fourth S1 record contains 07 character pairs and has a checksum of 92.
121
+ #
122
+ # The S5 record is comprised as follows:
123
+ #
124
+ # * S5 S-record type S5, indicating it is a count record indicating the number of S1 records
125
+ # * 03 Hexadecimal 03 (decimal 3), indicating that three character pairs follow.
126
+ # * 00 04 Hexadecimal 0004 (decimal 4), indicating that there are four data records previous to this record.
127
+ # * F8 The checksum.
128
+ #
129
+ # The S9 record is comprised as follows:
130
+ #
131
+ # * S9 S-record type S9, indicating it is a termination record.
132
+ # * 03 Hexadecimal 03 (decimal 3), indicating that three character pairs follow.
133
+ # * 00 00 The address field, hexadecimal 0 (decimal 0) indicating the starting execution address.
134
+ # * FC The checksum.
135
+ #
136
+ # ### Additional Notes
137
+ #
138
+ # There isn't any evidence that Motorola ever has made use of the header information within the data field
139
+ # of the S0 record, as described above. This must have been used by some third party vendors.
140
+ # This is the only place that a 78-byte limit on total record length or 64-byte limit on data length is
141
+ # documented. These values shouldn't be trusted for the general case.
142
+ #
143
+ # The count field can have values in the range of 0x3 (2 bytes of address + 1 byte checksum = 3, a not
144
+ # very useful record) to 0xff; this is the count of remaining character pairs, including checksum.
145
+ # If you write code to convert S-Records, you should always assume that a record can be as long as 514
146
+ # (decimal) characters in length (255 * 2 = 510, plus 4 characters for the type and count fields), plus
147
+ # any terminating character(s).
148
+ #
149
+ # That is, in establishing an input buffer in C, you would declare it to be
150
+ # an array of 515 chars, thus leaving room for the terminating null character.
151
+ class SRecord < Base
152
+ def self.match?(snippet)
153
+ snippet.all? do |line|
154
+ line.empty? || line =~ /^S[01235789]/
155
+ end
156
+ end
157
+
158
+ def start_address
159
+ @start_address ||= begin
160
+ lines.each do |line|
161
+ if line =~ /^S([789])(.*)/
162
+ type = Regexp.last_match[1]
163
+ case type
164
+ when '7'
165
+ return line.slice(4, 8).to_i(16)
166
+ when '8'
167
+ return line.slice(4, 6).to_i(16)
168
+ when '9'
169
+ return line.slice(4, 4).to_i(16)
170
+ end
171
+ end
172
+ end
173
+ end
174
+ end
175
+
176
+ private
177
+
178
+ # Returns an array containing all address/data from the given s-record
179
+ # No address manipulation is performed, that is left to the caller to apply
180
+ # any scrambling as required by the target system
181
+ def extract_addr_data(options = {})
182
+ options = {
183
+ data_width_in_bytes: 4
184
+ }.merge(options)
185
+
186
+ result = []
187
+ lines.each do |line|
188
+ # Only if the line is an s-record with data...
189
+ if line =~ /^S([1-3])/
190
+ type = Regexp.last_match[1].to_i(16) # S-record type, 1-3
191
+ # Set the matcher to capture x number of bytes dependent on the s-rec type
192
+ addr_matcher = '\w\w' * (1 + type)
193
+ line.strip =~ /^S\d\w\w(#{addr_matcher})(\w*)\w\w$/ # $1 = address, $2 = data
194
+ addr = Regexp.last_match[1].to_i(16)
195
+ @start_address ||= addr
196
+ @start_address = addr if addr < @start_address
197
+ data = Regexp.last_match[2]
198
+ data_matcher = '\w\w' * options[:data_width_in_bytes]
199
+ data.scan(/#{data_matcher}/).each do |data_packet|
200
+ result << [addr, data_packet.to_i(16)]
201
+ addr += options[:data_width_in_bytes]
202
+ end
203
+ # If a partial word is left over
204
+ if (remainder = data.length % (2 * options[:data_width_in_bytes])) > 0
205
+ result << [addr, data[data.length - remainder..data.length].to_i(16)]
206
+ end
207
+ end
208
+ end
209
+ result
210
+ end
211
+ end
212
+ end