float-formats 0.1.1 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,4 +1,38 @@
1
- require 'config/requirements'
2
- require 'config/hoe' # setup Hoe + all gem configuration
3
-
4
- Dir['tasks/**/*.rake'].each { |rake| load rake }
1
+ # Look in the tasks/setup.rb file for the various options that can be
2
+ # configured in this Rakefile. The .rake files in the tasks directory
3
+ # are where the options are used.
4
+
5
+ begin
6
+ require 'bones'
7
+ Bones.setup
8
+ rescue LoadError
9
+ load 'tasks/setup.rb'
10
+ end
11
+
12
+ ensure_in_path 'lib'
13
+ #require 'float-formats'
14
+ require 'float-formats/version'
15
+
16
+ task :default => 'spec:run'
17
+
18
+ depend_on 'flt', '1.0.0'
19
+ depend_on 'nio', '0.2.4'
20
+
21
+ PROJ.name = 'float-formats'
22
+ PROJ.description = "Floating-Point Formats"
23
+ PROJ.authors = 'Javier Goizueta'
24
+ PROJ.email = 'javier@goizueta.info'
25
+ PROJ.version = Flt::FORMATS_VERSION::STRING
26
+ PROJ.rubyforge.name = 'float-formats'
27
+ PROJ.url = "http://#{PROJ.rubyforge.name}.rubyforge.org"
28
+ PROJ.rdoc.opts = [
29
+ "--main", "README.txt",
30
+ '--title', 'Float-Formats Documentation',
31
+ "--opname", "index.html",
32
+ "--line-numbers",
33
+ "--inline-source"
34
+ ]
35
+ depend_on 'nio', '>=0.2.0'
36
+ depend_on 'flt', '>=1.0.0'
37
+
38
+ # EOF
data/lib/float-formats.rb CHANGED
@@ -1,10 +1,10 @@
1
1
  $:.unshift File.dirname(__FILE__)
2
-
3
- require 'float-formats/version'
4
- require 'float-formats/bytes'
5
- require 'float-formats/classes'
6
- require 'float-formats/formats'
7
-
8
- # FltPnt contains constants for common floating point formats.
9
- module FltPnt
10
- end
2
+
3
+ require 'float-formats/version'
4
+ require 'float-formats/bytes'
5
+ require 'float-formats/classes'
6
+ require 'float-formats/formats'
7
+
8
+ # Flt contains constants for common floating point formats.
9
+ module Flt
10
+ end
@@ -1,304 +1,464 @@
1
- # Float-Formats
2
- # Support for binary data representations
3
-
4
- require 'nio'
5
- require 'nio/sugar'
6
-
7
- require 'enumerator'
8
-
9
- module FltPnt
10
-
11
-
12
- module_function
13
-
14
- # return an hex representation of a byte string
15
- def bytes_to_hex(sgl,sep_bytes=false)
16
- hx = sgl.unpack('H*')[0].upcase
17
- if sep_bytes
18
- sep = ""
19
- (0...hx.size).step(2) do |i|
20
- sep << " " unless i==0
21
- sep << hx[i,2]
22
- end
23
- hx = sep
24
- end
25
- hx
26
- end
27
-
28
- # generate a byte string from an hex representation
29
- def hex_to_bytes(hex)
30
- [hex.tr(' ','')].pack('H*')
31
- end
32
-
33
- # ===== Byte string manipulation ==========================================================
34
-
35
- # Reverse the order of the bits in each byte.
36
- def reverse_byte_bits(b)
37
- b.chr.unpack('b*').pack("B*")[0]
38
- end
39
-
40
- # Reverse the order of the nibbles in each byte.
41
- def reverse_byte_nibbles(v)
42
- if v.kind_of?(String)
43
- # ... reverse each byte
44
- w = ""
45
- v.each_byte do |b|
46
- w << ((b >> 4)|((b&0xF)<<4))
47
- end
48
- v = w
49
- else
50
- # assume one byte
51
- # from_hex(to_hex(v).reverse)
52
- v = (v >> 4)|((v&0xF)<<4)
53
- end
54
- v
55
- end
56
- # reverse the order of bytes in 16-bit words
57
- def reverse_byte_pairs(b)
58
- w = ""
59
- (0...b.size).step(2) do |i|
60
- w << b[i+1]
61
- w << b[i]
62
- end
63
- w
64
- end
65
-
66
- # Supported endianness modes for byte strings are:
67
- # [<tt>:little_endian</tt>] (Intel order): least significant bytes come first.
68
- # [<tt>:big_endian</tt>] (Network order): most significant bytes come first.
69
- # [<tt>:little_big_endian</tt> or <tt>:middle_endian</tt>] (PDP-11 order): each pair of bytes
70
- # (16-bit word) has the bytes in little endian order, but the words
71
- # are stored in big endian order (we assume the number of bytes is even).
72
- # Note that the <tt>:big_little_endian</tt> order which would logically complete the set is
73
- # not currently supported as it has no known uses.
74
- def convert_endianness(byte_str, from_endianness, to_endianness)
75
- if from_endianness!=to_endianness
76
- if ([:little_endian,:big_endian]+[from_endianness, to_endianness]).uniq.size==2
77
- # no middle_endian order
78
- byte_str = byte_str.reverse
79
- else
80
- # from or to is middle_endian
81
- if [:middle_endian, :little_big_endian].include?(to_endianness)
82
- # from little_big_endian
83
- byte_str = convert_endianness(byte_str, from_endianness, :big_endian)
84
- byte_str = reverse_byte_pairs(byte_str)
85
- # now swap the byte pairs
86
- else
87
- # from little_big_endian
88
- byte_str = reverse_byte_pairs(byte_str)
89
- byte_str = convert_endianness(byte_str, :big_endian, to_endianness)
90
- end
91
- end
92
- end
93
- byte_str
94
- end
95
-
96
- # Binary data is handled here in three representations:
97
- # as an Integer
98
- # as a byte sequence (String) (with specific endianness)
99
- # an an hex-string (nibble values) (with specific endianness)
100
-
101
- # Convert a byte string to an integer
102
- def bytes_to_int(bytes, byte_endianness=:little_endian, bits_little_endian=false)
103
- i = 0
104
- bytes = convert_endianness(bytes, byte_endianness, :big_endian)
105
- bytes.each_byte do |b|
106
- # reverse b is bits_little_endian
107
- if bits_little_endian
108
- b = reverse_byte_bits(b)
109
- end
110
- i <<= 8
111
- i |= b
112
- end
113
- i
114
- end
115
-
116
- # Convert an integer to a byte string
117
- def int_to_bytes(i, len=0, byte_endianness=:little_endian, bits_little_endian=false)
118
- return nil if i<0
119
- bytes = ""
120
- while i>0
121
- b = (i&0xFF)
122
- if bits_little_endian
123
- b = reverse_byte_bits(b)
124
- end
125
- #puts "i=#{i} b<<#{b}"
126
- bytes << b
127
- i >>= 8
128
- end
129
- bytes << 0 while bytes.size<len
130
- bytes = convert_endianness(bytes, :little_endian, byte_endianness)
131
- bytes
132
- end
133
-
134
-
135
- # convert a byte string to separate fixed-width bit-fields as integers
136
- def get_bitfields(bytes,lens,byte_endianness=:little_endian, bits_little_endian=false)
137
- fields = []
138
- i = bytes_to_int(bytes,byte_endianness,bits_little_endian)
139
- for len in lens
140
- mask = (1<<len)-1
141
- fields << (i&mask)
142
- i >>= len
143
- end
144
- fields
145
- end
146
-
147
- # pack fixed-width bit-fields as integers into a byte string
148
- def set_bitfields(lens,fields,byte_endianness=:little_endian, bits_little_endian=false)
149
- i = 0
150
- lens = lens.reverse
151
- fields = fields.reverse
152
-
153
- bits = 0
154
-
155
- (0...lens.size).each do |j|
156
- i <<= lens[j]
157
- i |= fields[j]
158
- bits += lens[j]
159
- end
160
- int_to_bytes i,(bits+7)/8,byte_endianness, bits_little_endian
161
- end
162
-
163
- # DPD (Densely Packed Decimal) encoding
164
-
165
- # The bcd2dpd and dpd2bcd methods are adapted from Mike Cowlishaw's Rexx program:
166
- # (mfc 2000.10.03; Rexx version with new equations 2007.02.01)
167
- # available at http://www2.hursley.ibm.com/decimal/DPDecimal.html
168
-
169
- # Negate a bit. Auxiliar method for DPD conversions
170
- def bitnot(b)
171
- (~b)&1
172
- end
173
-
174
- # Compress BCD to Densely Packed Decimal
175
- #
176
- # adapted from Mike Cowlishaw's Rexx program:
177
- # http://www2.hursley.ibm.com/decimal/DPDecimal.html
178
- def bcd2dpd(arg)
179
- # assign each bit to a variable, named as in the description
180
- a,b,c,d,e,f,g,h,i,j,k,m = ("%012B"%arg).split('').collect{|bit| bit.to_i}
181
-
182
- # derive the result bits, using boolean expressions only
183
- #-- [the operators are: '&'=AND, '|'=OR, '\'=NOT.]
184
- p=b | (a & j) | (a & f & i)
185
- q=c | (a & k) | (a & g & i)
186
- r=d
187
- s=(f & (bitnot(a) | bitnot(i))) | (bitnot(a) & e & j) | (e & i)
188
- t=g | (bitnot(a) & e &k) | (a & i)
189
- u=h
190
- v=a | e | i
191
- w=a | (e & i) | (bitnot(e) & j)
192
- x=e | (a & i) | (bitnot(a) & k)
193
- y=m
194
-
195
- # concatenate the bits and return
196
- # result = [p,q,r,s,t,u,v,w,x,y].collect{|bit| bit.to_s}.inject{|aa,bb|aa+bb}.to_i(2)
197
- result = 0
198
- [p,q,r,s,t,u,v,w,x,y].each do |bit|
199
- result <<= 1
200
- result |= bit
201
- end
202
- result
203
- end
204
-
205
- # Expand Densely Packed Decimal to BCD
206
- #
207
- # adapted from Mike Cowlishaw's Rexx program:
208
- # http://www2.hursley.ibm.com/decimal/DPDecimal.html
209
- def dpd2bcd(arg)
210
-
211
- # assign each bit to a variable, named as in the description
212
- p,q,r,s,t,u,v,w,x,y = ("%010B"%arg).split('').collect{|bit| bit.to_i}
213
-
214
- # derive the result bits, using boolean expressions only
215
- a= (v & w) & (bitnot(s) | t | bitnot(x))
216
- b=p & (bitnot(v) | bitnot(w) | (s & bitnot(t) & x))
217
- c=q & (bitnot(v) | bitnot(w) | (s & bitnot(t) & x))
218
- d=r
219
- e=v & ((bitnot(w) & x) | (bitnot(t) & x) | (s & x))
220
- f=(s & (bitnot(v) | bitnot(x))) | (p & bitnot(s) & t & v & w & x)
221
- g=(t & (bitnot(v) | bitnot(x))) | (q & bitnot(s) & t & w)
222
- h=u
223
- i=v & ((bitnot(w) & bitnot(x)) | (w & x & (s | t)))
224
- j=(bitnot(v) & w) | (s & v & bitnot(w) & x) | (p & w & (bitnot(x) | (bitnot(s) & bitnot(t))))
225
- k=(bitnot(v) & x) | (t & bitnot(w) & x) | (q & v & w & (bitnot(x) | (bitnot(s) & bitnot(t))))
226
- m=y
227
- # concatenate the bits and return
228
- # result = [a,b,c,d,e,f,g,h,i,j,k,m].collect{|bit| bit.to_s}.inject{|aa,bb|aa+bb}.to_i(2)
229
- result = 0
230
- [a,b,c,d,e,f,g,h,i,j,k,m].each do |bit|
231
- result <<= 1
232
- result |= bit
233
- end
234
- result
235
- end
236
-
237
- # Pack a bcd digits string into DPD
238
- def hexbcd_to_dpd(bcd, endianness=:big_endian)
239
-
240
- n = bcd.size
241
- dpd = 0
242
- dpd_bits = 0
243
-
244
- i = 0
245
- m = n%3
246
- if m>0
247
- v = bcd2dpd(bcd[0,m].to_i(16))
248
- i += m
249
- n -= m
250
- bits = m==1 ? 4 : 7
251
- dpd_bits += bits
252
- dpd <<= bits
253
- dpd |= v
254
- end
255
-
256
- while n>0
257
- v = bcd2dpd(bcd[i,3].to_i(16))
258
- i += 3
259
- n -= 3
260
- bits = 10
261
- dpd_bits += bits
262
- dpd <<= bits
263
- dpd |= v
264
- end
265
-
266
- [dpd, dpd_bits]
267
- end
268
-
269
- # Unpack DPD digits
270
- def dpd_to_hexbcd(dpd, dpd_bits, endianness=:big_endian)
271
-
272
- bcd = ""
273
-
274
- while dpd_bits>=10
275
- v = dpd2bcd(dpd & 0x3FF)
276
- dpd >>= 10
277
- dpd_bits -= 10
278
- bcd = ("%03X"%v)+bcd
279
- end
280
-
281
- if dpd_bits>0
282
- case dpd_bits
283
- when 4
284
- v = dpd & 0xF
285
- n = 1
286
- when 7
287
- v = dpd & 0x7F
288
- n = 2
289
- else
290
- raise "Invalid DPD data"
291
- end
292
- v = dpd2bcd(v,true)
293
- bcd = ("%0#{n}X"%v)+bcd
294
- end
295
-
296
- bcd = bcd.reverse if endianness==:little_endian
297
-
298
- bcd
299
-
300
- end
301
-
302
-
303
-
1
+ # Float-Formats
2
+ # Support for binary data representations
3
+ require 'nio'
4
+ require 'nio/sugar'
5
+
6
+ require 'enumerator'
7
+ require 'delegate'
8
+
9
+ module Flt
10
+
11
+ class Bits
12
+ # Define a bit string given the number of bits and
13
+ # optinally the initial value (as an integer).
14
+ def initialize(num_bits,v=0)
15
+ @n = num_bits
16
+ @v = v
17
+ end
18
+
19
+ # convert to text representation in a given base
20
+ def to_s(base=2)
21
+ n = Bits.power_of_two(base)
22
+ if n
23
+ fmt = Nio::Fmt.default.base(base,true).mode(:fix,:exact)
24
+ digits = (size+n-1)/n
25
+ fmt.pad0s!(digits)
26
+ @v.nio_write(fmt)
27
+ else
28
+ @v.to_s(base)
29
+ end
30
+ end
31
+
32
+ # produce bit string from a text representation
33
+ def self.from_s(txt,base=2,len=nil)
34
+ txt = txt.tr('., _','')
35
+ v = txt.to_i(base)
36
+ if len.nil?
37
+ n = power_of_two(base)
38
+ if n
39
+ len = txt.size * n
40
+ end
41
+ end
42
+ from_i v, len
43
+ end
44
+
45
+ # convert to integer
46
+ def to_i
47
+ @v
48
+ end
49
+
50
+ # produce bit string from an integer
51
+ def self.from_i(v,len=nil)
52
+ len ||= (Math.log(v)/Math.log(2)).ceil # v.to_s(2).size
53
+ Bits.new(len,v)
54
+ end
55
+
56
+ # Access a bit value. The least significant bit has index 0.
57
+ def [](i)
58
+ (@v >> i) & 1
59
+ end
60
+
61
+ # Set a bit. The least significant bit has index 0. The bit value
62
+ # can be passed as an integer or a boolean value.
63
+ def []=(i,b)
64
+ b = (b && b!=0) ? 1 : 0
65
+ @v &= ~(1 << i)
66
+ @v |= (b << i)
67
+ b
68
+ end
69
+
70
+ # number of bits
71
+ def size
72
+ @n
73
+ end
74
+
75
+ protected
76
+
77
+ def self.is_power_of_two?(v)
78
+ if v==0
79
+ false
80
+ else
81
+ v &= (v-1)
82
+ if v==0
83
+ true
84
+ else
85
+ false
86
+ end
87
+ end
88
+ end
89
+
90
+ def self.power_of_two(v)
91
+ if is_power_of_two?(v)
92
+ n = 0
93
+ while v!=1
94
+ n += 1
95
+ v >>= 1
96
+ end
97
+ n
98
+ else
99
+ nil
100
+ end
101
+ end
102
+
103
+ end
104
+
105
+ class Bytes < DelegateClass(String)
106
+
107
+ def initialize(bytes)
108
+ case bytes
109
+ when Bytes
110
+ @bytes = bytes.to_s
111
+ when Array
112
+ @bytes = bytes.pack("C*")
113
+ else
114
+ @bytes = bytes.to_str
115
+ end
116
+ @bytes.force_encoding(Encoding::BINARY) if RUBY_VERSION>="1.9.0"
117
+ super @bytes
118
+ end
119
+
120
+ def dup
121
+ Bytes.new @bytes.dup
122
+ end
123
+
124
+ if RUBY_VERSION>="1.9.0"
125
+ def size
126
+ bytesize
127
+ end
128
+ alias _get_str []
129
+ alias _set_str []=
130
+ def [](*params)
131
+ if params.size == 1 && !params.first.kind_of?(Range)
132
+ _get_str(params.first).ord # bytes.to_a[params.first]
133
+ else
134
+ Bytes.new(_get_str(*params))
135
+ end
136
+ end
137
+ def []=(i,v)
138
+ _set_str i, v.chr(Encoding::BINARY)
139
+ end
140
+ else
141
+ alias _get_str []
142
+ def [](*params)
143
+ if params.size == 1
144
+ _get_str(params.first)
145
+ else
146
+ Bytes.new(_get_str(*params))
147
+ end
148
+ end
149
+ end
150
+
151
+ def +(b)
152
+ Bytes.new(to_str + b.to_str)
153
+ end
154
+
155
+ # return an hex representation of a byte string
156
+ def to_hex(sep_bytes=false)
157
+ hx = @bytes.unpack('H*')[0].upcase
158
+ if sep_bytes
159
+ sep = ""
160
+ (0...hx.size).step(2) do |i|
161
+ sep << " " unless i==0
162
+ sep << hx[i,2]
163
+ end
164
+ hx = sep
165
+ end
166
+ hx
167
+ end
168
+
169
+ # generate a byte string from an hex representation
170
+ def Bytes.from_hex(hex)
171
+ Bytes.new [hex.tr(' ','')].pack('H*')
172
+ end
173
+
174
+
175
+ # Reverse the order of the bits in each byte.
176
+ def reverse_byte_bits!
177
+ @bytes = @bytes.unpack('b*').pack("B*")[0]
178
+ __setobj__ @bytes
179
+ self
180
+ end
181
+
182
+ def reverse_byte_bits
183
+ dup.reverse_byte_bits!
184
+ end
185
+
186
+ # Reverse the order of the nibbles in each byte.
187
+ def reverse_byte_nibbles!
188
+ w = ""
189
+ @bytes.each_byte do |b|
190
+ w << ((b >> 4)|((b&0xF)<<4))
191
+ end
192
+ @bytes = w
193
+ __setobj__ @bytes
194
+ self
195
+ end
196
+ def reverse_byte_nibbles
197
+ dup.reverse_byte_nibbles!
198
+ end
199
+
200
+ # reverse the order of bytes in 16-bit words
201
+ def reverse_byte_pairs!
202
+ w = ""
203
+ (0...@bytes.size).step(2) do |i|
204
+ w << @bytes[i+1]
205
+ w << @bytes[i]
206
+ end
207
+ @bytes = w
208
+ __setobj__ @bytes
209
+ self
210
+ end
211
+
212
+ def reverse_byte_pairs
213
+ dup.reverse_byte_pairs!
214
+ end
215
+
216
+ # Supported endianness modes for byte strings are:
217
+ # [<tt>:little_endian</tt>] (Intel order): least significant bytes come first.
218
+ # [<tt>:big_endian</tt>] (Network order): most significant bytes come first.
219
+ # [<tt>:little_big_endian</tt> or <tt>:middle_endian</tt>] (PDP-11 order): each pair of bytes
220
+ # (16-bit word) has the bytes in little endian order, but the words
221
+ # are stored in big endian order (we assume the number of bytes is even).
222
+ # Note that the <tt>:big_little_endian</tt> order which would logically complete the set is
223
+ # not currently supported as it has no known uses.
224
+ def convert_endianness!(from_endianness, to_endianness)
225
+ if from_endianness!=to_endianness
226
+ if ([:little_endian,:big_endian]+[from_endianness, to_endianness]).uniq.size==2
227
+ # no middle_endian order
228
+ reverse!
229
+ else
230
+ # from or to is middle_endian
231
+ if [:middle_endian, :little_big_endian].include?(to_endianness)
232
+ # from little_big_endian
233
+ convert_endianness!(from_endianness, :big_endian).reverse_byte_pairs!
234
+ else
235
+ # from little_big_endian
236
+ reverse_byte_pairs!.convert_endianness!(:big_endian, to_endianness)
237
+ end
238
+ end
239
+ end
240
+ self
241
+ end
242
+
243
+ def convert_endianness(from_endianness, to_endianness)
244
+ dup.convert_endianness!(from_endianness, to_endianness)
245
+ end
246
+
247
+ # Binary data is handled here in three representations:
248
+ # as an Integer
249
+ # as a byte sequence (String) (with specific endianness)
250
+ # an an hex-string (nibble values) (with specific endianness)
251
+
252
+ # Convert a byte string to an integer
253
+ def to_i(byte_endianness=:little_endian, bits_little_endian=false)
254
+ i = 0
255
+ bytes = convert_endianness(byte_endianness, :big_endian)
256
+ bytes = bytes.reverse_byte_bits if bits_little_endian
257
+ bytes.each_byte do |b|
258
+ i <<= 8
259
+ i |= b
260
+ end
261
+ i
262
+ end
263
+
264
+ # Convert an integer to a byte string
265
+ def Bytes.from_i(i, len=0, byte_endianness=:little_endian, bits_little_endian=false)
266
+ return nil if i<0
267
+ bytes = Bytes.new("")
268
+ while i>0
269
+ b = (i&0xFF)
270
+ bytes << b
271
+ i >>= 8
272
+ end
273
+ bytes << 0 while bytes.size<len
274
+ bytes.reverse_byte_bits! if bits_little_endian
275
+ bytes.convert_endianness!(:little_endian, byte_endianness)
276
+ bytes
277
+ end
278
+
279
+ # convert a byte string to separate fixed-width bit-fields as integers
280
+ def to_bitfields(lens,byte_endianness=:little_endian, bits_little_endian=false)
281
+ fields = []
282
+ i = to_i(byte_endianness,bits_little_endian)
283
+ for len in lens
284
+ mask = (1<<len)-1
285
+ fields << (i&mask)
286
+ i >>= len
287
+ end
288
+ fields
289
+ end
290
+
291
+ # pack fixed-width bit-fields as integers into a byte string
292
+ def Bytes.from_bitfields(lens,fields,byte_endianness=:little_endian, bits_little_endian=false)
293
+ i = 0
294
+ lens = lens.reverse
295
+ fields = fields.reverse
296
+
297
+ bits = 0
298
+
299
+ (0...lens.size).each do |j|
300
+ i <<= lens[j]
301
+ i |= fields[j]
302
+ bits += lens[j]
303
+ end
304
+ from_i i,(bits+7)/8,byte_endianness, bits_little_endian
305
+ end
306
+
307
+ def to_bits(byte_endianness=:little_endian, bits_little_endian=false,nbits=nil)
308
+ i = to_i(byte_endianness, bits_little_endian)
309
+ nbits ||= 8*size
310
+ Bits.from_i(i,nbits)
311
+ end
312
+
313
+ def Bytes.from_bits(bits,byte_endianness=:little_endian, bits_little_endian=false,nbits=nil)
314
+ nbits ||= (bits.size+7)/8
315
+ from_i bits.to_i, nbits, byte_endianness, bits_little_endian
316
+ end
317
+
318
+ end
319
+
320
+
321
+ module_function
322
+
323
+ # DPD (Densely Packed Decimal) encoding
324
+
325
+ # The bcd2dpd and dpd2bcd methods are adapted from Mike Cowlishaw's Rexx program:
326
+ # (mfc 2000.10.03; Rexx version with new equations 2007.02.01)
327
+ # available at http://www2.hursley.ibm.com/decimal/DPDecimal.html
328
+
329
+ # Negate a bit. Auxiliar method for DPD conversions
330
+ def bitnot(b)
331
+ (~b)&1
332
+ end
333
+
334
+ # Compress BCD to Densely Packed Decimal
335
+ #
336
+ # adapted from Mike Cowlishaw's Rexx program:
337
+ # http://www2.hursley.ibm.com/decimal/DPDecimal.html
338
+ def bcd2dpd(arg)
339
+ # assign each bit to a variable, named as in the description
340
+ a,b,c,d,e,f,g,h,i,j,k,m = ("%012B"%arg).split('').collect{|bit| bit.to_i}
341
+
342
+ # derive the result bits, using boolean expressions only
343
+ #-- [the operators are: '&'=AND, '|'=OR, '\'=NOT.]
344
+ p=b | (a & j) | (a & f & i)
345
+ q=c | (a & k) | (a & g & i)
346
+ r=d
347
+ s=(f & (bitnot(a) | bitnot(i))) | (bitnot(a) & e & j) | (e & i)
348
+ t=g | (bitnot(a) & e &k) | (a & i)
349
+ u=h
350
+ v=a | e | i
351
+ w=a | (e & i) | (bitnot(e) & j)
352
+ x=e | (a & i) | (bitnot(a) & k)
353
+ y=m
354
+
355
+ # concatenate the bits and return
356
+ # result = [p,q,r,s,t,u,v,w,x,y].collect{|bit| bit.to_s}.inject{|aa,bb|aa+bb}.to_i(2)
357
+ result = 0
358
+ [p,q,r,s,t,u,v,w,x,y].each do |bit|
359
+ result <<= 1
360
+ result |= bit
361
+ end
362
+ result
363
+ end
364
+
365
+ # Expand Densely Packed Decimal to BCD
366
+ #
367
+ # adapted from Mike Cowlishaw's Rexx program:
368
+ # http://www2.hursley.ibm.com/decimal/DPDecimal.html
369
+ def dpd2bcd(arg)
370
+
371
+ # assign each bit to a variable, named as in the description
372
+ p,q,r,s,t,u,v,w,x,y = ("%010B"%arg).split('').collect{|bit| bit.to_i}
373
+
374
+ # derive the result bits, using boolean expressions only
375
+ a= (v & w) & (bitnot(s) | t | bitnot(x))
376
+ b=p & (bitnot(v) | bitnot(w) | (s & bitnot(t) & x))
377
+ c=q & (bitnot(v) | bitnot(w) | (s & bitnot(t) & x))
378
+ d=r
379
+ e=v & ((bitnot(w) & x) | (bitnot(t) & x) | (s & x))
380
+ f=(s & (bitnot(v) | bitnot(x))) | (p & bitnot(s) & t & v & w & x)
381
+ g=(t & (bitnot(v) | bitnot(x))) | (q & bitnot(s) & t & w)
382
+ h=u
383
+ i=v & ((bitnot(w) & bitnot(x)) | (w & x & (s | t)))
384
+ j=(bitnot(v) & w) | (s & v & bitnot(w) & x) | (p & w & (bitnot(x) | (bitnot(s) & bitnot(t))))
385
+ k=(bitnot(v) & x) | (t & bitnot(w) & x) | (q & v & w & (bitnot(x) | (bitnot(s) & bitnot(t))))
386
+ m=y
387
+ # concatenate the bits and return
388
+ # result = [a,b,c,d,e,f,g,h,i,j,k,m].collect{|bit| bit.to_s}.inject{|aa,bb|aa+bb}.to_i(2)
389
+ result = 0
390
+ [a,b,c,d,e,f,g,h,i,j,k,m].each do |bit|
391
+ result <<= 1
392
+ result |= bit
393
+ end
394
+ result
395
+ end
396
+
397
+ # Pack a bcd digits string into DPD
398
+ def hexbcd_to_dpd(bcd, endianness=:big_endian)
399
+
400
+ n = bcd.size
401
+ dpd = 0
402
+ dpd_bits = 0
403
+
404
+ i = 0
405
+ m = n%3
406
+ if m>0
407
+ v = bcd2dpd(bcd[0,m].to_i(16))
408
+ i += m
409
+ n -= m
410
+ bits = m==1 ? 4 : 7
411
+ dpd_bits += bits
412
+ dpd <<= bits
413
+ dpd |= v
414
+ end
415
+
416
+ while n>0
417
+ v = bcd2dpd(bcd[i,3].to_i(16))
418
+ i += 3
419
+ n -= 3
420
+ bits = 10
421
+ dpd_bits += bits
422
+ dpd <<= bits
423
+ dpd |= v
424
+ end
425
+
426
+ [dpd, dpd_bits]
427
+ end
428
+
429
+ # Unpack DPD digits
430
+ def dpd_to_hexbcd(dpd, dpd_bits, endianness=:big_endian)
431
+
432
+ bcd = ""
433
+
434
+ while dpd_bits>=10
435
+ v = dpd2bcd(dpd & 0x3FF)
436
+ dpd >>= 10
437
+ dpd_bits -= 10
438
+ bcd = ("%03X"%v)+bcd
439
+ end
440
+
441
+ if dpd_bits>0
442
+ case dpd_bits
443
+ when 4
444
+ v = dpd & 0xF
445
+ n = 1
446
+ when 7
447
+ v = dpd & 0x7F
448
+ n = 2
449
+ else
450
+ raise "Invalid DPD data"
451
+ end
452
+ v = dpd2bcd(v,true)
453
+ bcd = ("%0#{n}X"%v)+bcd
454
+ end
455
+
456
+ bcd = bcd.reverse if endianness==:little_endian
457
+
458
+ bcd
459
+
460
+ end
461
+
462
+
463
+
304
464
  end