float-formats 0.1.1 → 0.2.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.
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