origen_memory_image 0.7.0 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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