1/*  Copyright (C) 2012 IBM
2
3 Author: Maynard Johnson <maynardj@us.ibm.com>
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful, but
11 WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307, USA.
19
20 The GNU General Public License is contained in the file COPYING.
21 */
22
23#include <stdio.h>
24#include <stdlib.h>
25#include <stdint.h>
26
27#if defined(HAS_DFP)
28
29typedef union stuff {
30   _Decimal64  dec_val;
31   _Decimal128  dec_val128;
32   unsigned long long u64_val;
33   struct {
34#if defined(VGP_ppc64le_linux)
35      unsigned long long vall;
36      unsigned long long valu;
37#else
38      unsigned long long valu;
39      unsigned long long vall;
40#endif
41   } u128;
42} dfp_val_t;
43
44
45typedef unsigned char Bool;
46#define True 1
47#define False 0
48
49
50#define ALLCR "cr0","cr1","cr2","cr3","cr4","cr5","cr6","cr7"
51
52#define SET_CR(_arg) \
53      __asm__ __volatile__ ("mtcr  %0" : : "b"(_arg) : ALLCR );
54
55#define SET_XER(_arg) \
56      __asm__ __volatile__ ("mtxer %0" : : "b"(_arg) : "xer" );
57
58#define GET_CR(_lval) \
59      __asm__ __volatile__ ("mfcr %0"  : "=b"(_lval) )
60
61#define GET_XER(_lval) \
62      __asm__ __volatile__ ("mfxer %0" : "=b"(_lval) )
63
64#define GET_CR_XER(_lval_cr,_lval_xer) \
65   do { GET_CR(_lval_cr); GET_XER(_lval_xer); } while (0)
66
67#define SET_CR_ZERO \
68      SET_CR(0)
69
70#define SET_XER_ZERO \
71      SET_XER(0)
72
73#define SET_CR_XER_ZERO \
74   do { SET_CR_ZERO; SET_XER_ZERO; } while (0)
75
76#define SET_FPSCR_ZERO \
77   do { double _d = 0.0; \
78        __asm__ __volatile__ ("mtfsf 0xFF, %0" : : "f"(_d) ); \
79   } while (0)
80
81#define GET_FPSCR(_arg) \
82    __asm__ __volatile__ ("mffs %0"  : "=f"(_arg) )
83
84#define SET_FPSCR_DRN \
85    __asm__ __volatile__ ("mtfsf  1, %0, 0, 1" :  : "f"(f14) )
86
87#ifndef __powerpc64__
88typedef uint32_t HWord_t;
89#else
90typedef uint64_t HWord_t;
91#endif /* __powerpc64__ */
92
93enum BF_vals { BF_val1 = 0, BF_val2 = 1, BF_val3 =6};
94
95// The assembly-level instructions being tested
96static void _test_dtstsf(unsigned int BF, unsigned int ref_sig, dfp_val_t *valB)
97{
98   _Decimal64 f16 = valB->dec_val;
99   register HWord_t r14 __asm__ ("r14");
100   double f14;
101   r14 = (HWord_t)&ref_sig;
102
103   __asm __volatile__ ("lfiwax %0, 0, %1" : "=f" (f14): "r" (r14));
104   switch (BF) {
105      case BF_val1:
106         __asm__ __volatile__ ("dtstsf %0, %1, %2" : : "i" (BF_val1), "f" (f14), "f" (f16));
107         break;
108      case BF_val2:
109         __asm__ __volatile__ ("dtstsf %0, %1, %2" : : "i" (BF_val2), "f" (f14), "f" (f16));
110         break;
111      case BF_val3:
112         __asm__ __volatile__ ("dtstsf %0, %1, %2" : : "i" (BF_val3), "f" (f14), "f" (f16));
113         break;
114      default:
115         fprintf(stderr, "Invalid value %d for BF\n", BF);
116         break;
117   }
118}
119
120static void _test_dtstsfq(unsigned int BF, unsigned int ref_sig, dfp_val_t *valB)
121{
122   _Decimal128 f16 = valB->dec_val128;
123   register HWord_t r14 __asm__ ("r14");
124   double f14;
125   r14 = (HWord_t)&ref_sig;
126
127   __asm __volatile__ ("lfiwax %0, 0, %1" : "=f" (f14): "r" (r14));
128   switch (BF) {
129      case BF_val1:
130         __asm__ __volatile__ ("dtstsfq %0, %1, %2" : : "i" (BF_val1), "f" (f14), "f" (f16));
131         break;
132      case BF_val2:
133         __asm__ __volatile__ ("dtstsfq %0, %1, %2" : : "i" (BF_val2), "f" (f14), "f" (f16));
134         break;
135      case BF_val3:
136         __asm__ __volatile__ ("dtstsfq %0, %1, %2" : : "i" (BF_val3), "f" (f14), "f" (f16));
137         break;
138      default:
139         fprintf(stderr, "Invalid value %d for BF\n", BF);
140         break;
141   }
142}
143
144static dfp_val_t _test_ddedpd(unsigned int SP, dfp_val_t *valB)
145{
146   _Decimal64 ret = 0;
147   dfp_val_t result;
148   _Decimal64 f16 = valB->dec_val;
149   switch (SP) {
150      case 0:
151         __asm__ __volatile__ ("ddedpd. 0, %0, %1" : "=f" (ret) : "f" (f16));
152         break;
153      case 1:
154         __asm__ __volatile__ ("ddedpd. 1, %0, %1" : "=f" (ret) : "f" (f16));
155         break;
156      case 2:
157         __asm__ __volatile__ ("ddedpd. 2, %0, %1" : "=f" (ret) : "f" (f16));
158         break;
159      case 3:
160         __asm__ __volatile__ ("ddedpd. 3, %0, %1" : "=f" (ret) : "f" (f16));
161         break;
162      default:
163         fprintf(stderr, "Invalid value %d for SP\n", SP);
164         break;
165   }
166   result.dec_val = ret;
167   return result;
168}
169
170
171static dfp_val_t _test_ddedpdq(unsigned int SP, dfp_val_t *valB)
172{
173   _Decimal128 ret = 0;
174   dfp_val_t result;
175   _Decimal128 f16 = valB->dec_val128;
176   switch (SP) {
177      case 0:
178         __asm__ __volatile__ ("ddedpdq 0, %0, %1" : "=f" (ret) : "f" (f16));
179         break;
180      case 1:
181         __asm__ __volatile__ ("ddedpdq 1, %0, %1" : "=f" (ret) : "f" (f16));
182         break;
183      case 2:
184         __asm__ __volatile__ ("ddedpdq 2, %0, %1" : "=f" (ret) : "f" (f16));
185         break;
186      case 3:
187         __asm__ __volatile__ ("ddedpdq 3, %0, %1" : "=f" (ret) : "f" (f16));
188         break;
189      default:
190         fprintf(stderr, "Invalid value %d for SP\n", SP);
191         break;
192   }
193   result.dec_val128 = ret;
194   return result;
195}
196
197static dfp_val_t _test_denbcd(unsigned int S, dfp_val_t *valB)
198{
199   _Decimal64 ret = 0;
200   dfp_val_t result;
201   _Decimal64 f16 = valB->dec_val;
202   switch (S) {
203      case 0:
204         __asm__ __volatile__ ("denbcd. 0, %0, %1" : "=f" (ret) : "f" (f16));
205         break;
206      case 1:
207         __asm__ __volatile__ ("denbcd. 1, %0, %1" : "=f" (ret) : "f" (f16));
208         break;
209      default:
210         fprintf(stderr, "Invalid value %d for S\n", S);
211         break;
212   }
213   result.dec_val = ret;
214   return result;
215}
216
217
218static dfp_val_t _test_denbcdq(unsigned int S, dfp_val_t *valB)
219{
220   _Decimal128 ret = 0;
221   dfp_val_t result;
222   _Decimal128 f16 = valB->dec_val128;
223   switch (S) {
224      case 0:
225         __asm__ __volatile__ ("denbcdq 0, %0, %1" : "=f" (ret) : "f" (f16));
226         break;
227      case 1:
228         __asm__ __volatile__ ("denbcdq 1, %0, %1" : "=f" (ret) : "f" (f16));
229         break;
230      default:
231         fprintf(stderr, "Invalid value %d for S\n", S);
232         break;
233   }
234   result.dec_val128 = ret;
235   return result;
236}
237
238
239typedef void (*test_funcp_t)(unsigned int imm, unsigned int imm2,  dfp_val_t *valB);
240typedef dfp_val_t (*test_func_bcdp_t)(unsigned int imm, dfp_val_t *valB);
241typedef void (*test_driver_func_t)(void);
242typedef struct test_table
243{
244   test_driver_func_t test_category;
245   char * name;
246} test_table_t;
247
248/*
249 *  345.0DD (0x2207c00000000000 0xe50)
250 *  1.2300e+5DD (0x2207c00000000000 0x14c000)
251 *  -16.0DD (0xa207c00000000000 0xe0)
252 *  0.00189DD (0x2206c00000000000 0xcf)
253 *  -4.1235DD (0xa205c00000000000 0x10a395bcf)
254 *  9.8399e+20DD (0x2209400000000000 0x253f1f534acdd4)
255 *  0DD (0x2208000000000000 0x0)
256 *  0DD (0x2208000000000000 0x0)
257 *  infDD (0x7800000000000000 0x0)
258 *  nanDD (0x7c00000000000000 0x0
259 */
260static unsigned long long dfp128_vals[] = {
261                                    // Some finite numbers
262                                    0x2207c00000000000ULL, 0x0000000000000e50ULL,
263                                    0x2207c00000000000ULL, 0x000000000014c000ULL,
264                                    0xa207c00000000000ULL, 0x00000000000000e0ULL,
265                                    0x2206c00000000000ULL, 0x00000000000000cfULL,
266                                    0xa205c00000000000ULL, 0x000000010a395bcfULL,
267                                    0x6209400000fd0000ULL, 0x00253f1f534acdd4ULL, // huge number
268                                    0x000400000089b000ULL, 0x0a6000d000000049ULL, // very small number
269                                    // flavors of zero
270                                    0x2208000000000000ULL, 0x0000000000000000ULL,
271                                    0xa208000000000000ULL, 0x0000000000000000ULL, // negative
272                                    0xa248000000000000ULL, 0x0000000000000000ULL,
273                                    // flavors of NAN
274                                    0x7c00000000000000ULL, 0x0000000000000000ULL, // quiet
275                                    0xfc00000000000000ULL, 0xc00100035b007700ULL,
276                                    0x7e00000000000000ULL, 0xfe000000d0e0a0d0ULL, // signaling
277                                    // flavors of Infinity
278                                    0x7800000000000000ULL, 0x0000000000000000ULL,
279                                    0xf800000000000000ULL, 0x0000000000000000ULL, // negative
280                                    0xf900000000000000ULL, 0x0000000000000000ULL
281};
282
283static unsigned long long dfp64_vals[] = {
284                                 // various finite numbers
285                                 0x2234000000000e50ULL,
286                                 0x223400000014c000ULL,
287                                 0xa2340000000000e0ULL,// negative
288                                 0x22240000000000cfULL,
289                                 0xa21400010a395bcfULL,// negative
290                                 0x6e4d3f1f534acdd4ULL,// huge number
291                                 0x000400000089b000ULL,// very small number
292                                 // flavors of zero
293                                 0x2238000000000000ULL,
294                                 0xa238000000000000ULL,
295                                 0x4248000000000000ULL,
296                                 // flavors of NAN
297                                 0x7e34000000000111ULL,
298                                 0xfe000000d0e0a0d0ULL,//signaling
299                                 0xfc00000000000000ULL,//quiet
300                                 // flavors of Infinity
301                                 0x7800000000000000ULL,
302                                 0xf800000000000000ULL,//negative
303                                 0x7a34000000000000ULL,
304};
305
306/* The bcd64_vals and bdc128_vals hold the unique results of executing
307 * the ddedpd instruction on the basic dfp64 and dfp128 array values.
308 * Executing the inverse operation (denbcd) on these values with the
309 * appropriate S (signed) value should yield values approximating the
310 * original dfp values (except being 2^4 in magnitude since the decoding
311 * operation shifted the value one hex digit to the left to make room
312 * for signedness info).
313 */
314static unsigned long long bcd64_vals[] = {
315                                          0x0000000000003450ULL,
316                                          0x000000000003450cULL,
317                                          0x000000000003450fULL,
318                                          0x0000000001230000ULL,
319                                          0x000000001230000cULL,
320                                          0x000000001230000fULL,
321                                          0x0000000000000160ULL,
322                                          0x000000000000160dULL,
323                                          0x0000000000000189ULL,
324                                          0x000000000000189cULL,
325                                          0x000000000000189fULL,
326                                          0x0000004123456789ULL,
327                                          0x000004123456789dULL,
328                                          0x9839871234533354ULL,
329                                          0x839871234533354cULL,
330                                          0x839871234533354fULL,
331                                          0x0000000008864000ULL,
332                                          0x000000008864000cULL,
333                                          0x000000008864000fULL,
334                                          0x0000000000000000ULL,
335                                          0x000000000000000cULL,
336                                          0x000000000000000fULL,
337                                          0x000000000000000dULL,
338                                          0x0000000000000211ULL,
339                                          0x000000000000211cULL,
340                                          0x000000000000211fULL,
341                                          0x0000003882028150ULL,
342                                          0x000003882028150dULL
343 };
344
345static unsigned long long bcd128_vals[] = {
346                                           0x0000000000000000ULL, 0x0000000000003450ULL,
347                                           0x0000000000000000ULL, 0x000000000003450cULL,
348                                           0x0000000000000000ULL, 0x000000000003450fULL,
349                                           0x0000000000000000ULL, 0x0000000001230000ULL,
350                                           0x0000000000000000ULL, 0x000000001230000cULL,
351                                           0x0000000000000000ULL, 0x000000001230000fULL,
352                                           0x0000000000000000ULL, 0x0000000000000160ULL,
353                                           0x0000000000000000ULL, 0x000000000000160dULL,
354                                           0x0000000000000000ULL, 0x0000000000000189ULL,
355                                           0x0000000000000000ULL, 0x000000000000189cULL,
356                                           0x0000000000000000ULL, 0x000000000000189fULL,
357                                           0x0000000000000000ULL, 0x0000004123456789ULL,
358                                           0x0000000000000000ULL, 0x000004123456789dULL,
359                                           0x0000097100000000ULL, 0x9839871234533354ULL,
360                                           0x0000971000000009ULL, 0x839871234533354cULL,
361                                           0x0000971000000009ULL, 0x839871234533354fULL,
362                                           0x0000010954000051ULL, 0x8000640000000049ULL,
363                                           0x0000109540000518ULL, 0x000640000000049cULL,
364                                           0x0000109540000518ULL, 0x000640000000049fULL,
365                                           0x0000000000000000ULL, 0x0000000000000000ULL,
366                                           0x0000000000000000ULL, 0x000000000000000cULL,
367                                           0x0000000000000000ULL, 0x000000000000000fULL,
368                                           0x0000000000000000ULL, 0x000000000000000dULL,
369                                           0x0000000000080000ULL, 0x0200801330811600ULL,
370                                           0x0000000000800000ULL, 0x200801330811600dULL,
371                                           0x0000000000088170ULL, 0x0000003882028150ULL,
372                                           0x0000000000881700ULL, 0x000003882028150cULL,
373                                           0x0000000000881700ULL, 0x000003882028150fULL
374};
375
376// Both Long and Quad arrays of DFP values should have the same length, so it
377// doesn't matter which array I use for calculating the following #define.
378#define NUM_DFP_VALS (sizeof(dfp64_vals)/8)
379
380typedef enum {
381   LONG_TEST,
382   QUAD_TEST
383} precision_type_t;
384
385typedef struct dfp_one_arg_test
386{
387   test_funcp_t test_func;
388   const char * name;
389   precision_type_t precision;
390   const char * op;
391} dfp_one_arg_test_t;
392
393typedef struct dfp_one_arg_bcd_test
394{
395   test_func_bcdp_t test_func;
396   const char * name;
397   precision_type_t precision;
398   const char * op;
399} dfp_one_arg_bcd_test_t;
400
401static dfp_one_arg_bcd_test_t
402dfp_test_dfp_ddedpd_tests[] = {
403                            { &_test_ddedpd, "ddedpd", LONG_TEST, "[D->B]"},
404                            { &_test_ddedpdq, "ddedpdq", QUAD_TEST, "[D->B]"},
405                            { NULL, NULL, 0, NULL}
406};
407
408static void test_dfp_ddedpd_ops(void)
409{
410   test_func_bcdp_t func;
411   dfp_val_t test_val;
412
413   int k = 0;
414
415   while ((func = dfp_test_dfp_ddedpd_tests[k].test_func)) {
416      int i;
417      dfp_one_arg_bcd_test_t test_def = dfp_test_dfp_ddedpd_tests[k];
418
419      for (i = 0; i < NUM_DFP_VALS; i++) {
420         unsigned int SP;
421
422         if (test_def.precision == LONG_TEST) {
423            test_val.u64_val = dfp64_vals[i];
424         } else {
425            test_val.u128.valu = dfp128_vals[i * 2];
426            test_val.u128.vall = dfp128_vals[(i * 2) + 1];
427         }
428
429         for (SP = 0; SP < 4; SP++) {
430            dfp_val_t result;
431
432	    /* There is an ABI change in how 128 bit arguments are aligned
433             * with GCC 5.0.  The compiler generates a "note" about this
434             * starting with GCC 4.8.  To avoid generating the "note", pass
435             * the address of the 128-bit arguments rather then the value.
436	     */
437            result = (*func)(SP, &test_val);
438            printf("%s (SP=%d) %s", test_def.name, SP, test_def.op);
439            if (test_def.precision == LONG_TEST) {
440               printf("%016llx ==> %016llx\n", test_val.u64_val, result.u64_val);
441            } else {
442               printf("%016llx %016llx ==> %016llx %016llx\n",
443                      test_val.u128.valu, test_val.u128.vall,
444                      result.u128.valu, result.u128.vall);
445            }
446         }
447      }
448      k++;
449      printf( "\n" );
450   }
451}
452
453static dfp_one_arg_bcd_test_t
454dfp_test_dfp_denbcd_tests[] = {
455                            { &_test_denbcd, "denbcd", LONG_TEST, "[B->D]"},
456                            { &_test_denbcdq, "denbcdq", QUAD_TEST, "[B->D]"},
457                            { NULL, NULL, 0, NULL}
458};
459
460static void test_dfp_denbcd_ops(void)
461{
462   test_func_bcdp_t func;
463   dfp_val_t test_val;
464   int num_test_vals;
465
466   int k = 0;
467
468   while ((func = dfp_test_dfp_denbcd_tests[k].test_func)) {
469      int i;
470      dfp_one_arg_bcd_test_t test_def = dfp_test_dfp_denbcd_tests[k];
471      if (test_def.precision == LONG_TEST)
472         num_test_vals = sizeof(bcd64_vals)/sizeof(unsigned long long);
473      else
474         num_test_vals = sizeof(bcd128_vals)/(2 * sizeof(unsigned long long));
475
476      for (i = 0; i < num_test_vals; i++) {
477         unsigned int S;
478         dfp_val_t result;
479         /* The DPD-to-BCD decodings may contain up to 3 decodings for each normal DFP
480          * value: the first is an unsigned decoding, and the other two are
481          * signed decodings, with SP[1] set to '0' and '1' respectively at decode
482          * time. But some of the results of decodings were duplicates, so they were
483          * not included in the bcd64_vals and bcd128_vals arrays.
484          *
485          * When doing the encoding operation (denbcd), we'll attempt both S=0 and
486          * S=1; one or the other should encode the BCD value to something close to
487          * its original DFP value (except being 2^4 in magnitude since the decoding
488          * operation shifted the value one hex digit to the left to make room
489          * for signedness info).
490          */
491         for (S = 0; S < 2; S++) {
492            if (test_def.precision == LONG_TEST) {
493               test_val.u64_val = bcd64_vals[i];
494            } else {
495               test_val.u128.valu = bcd128_vals[i * 2];
496               test_val.u128.vall = bcd128_vals[(i * 2) + 1];
497            }
498
499	    /* There is an API change in how 128 bit arguments are aligned
500             * with GCC 5.0.  The compiler generates a "note" about this
501             * starting with GCC 4.8.  To avoid generating the "note", pass
502             * the address of the 128-bit arguments rather then the value.
503	     */
504            result = (*func)(S, &test_val);
505            printf("%s (S=%d) %s", test_def.name, S, test_def.op);
506            if (test_def.precision == LONG_TEST) {
507               printf("%016llx ==> %016llx\n", test_val.u64_val, result.u64_val);
508            } else {
509               printf("%016llx %016llx ==> %016llx %016llx\n",
510                      test_val.u128.valu, test_val.u128.vall,
511                      result.u128.valu, result.u128.vall);
512            }
513         }
514      }
515      k++;
516      printf( "\n" );
517   }
518}
519
520
521static dfp_one_arg_test_t
522dfp_test_significance_tests[] = {
523                                          { &_test_dtstsf,  "dtstsf", LONG_TEST, "[tSig]"},
524                                          { &_test_dtstsfq, "dtstsfq", QUAD_TEST, "[tSig]"},
525                                          { NULL, NULL, 0, NULL}
526};
527
528static void test_dfp_test_significance_ops(void)
529{
530   test_funcp_t func;
531   dfp_val_t test_valB;
532   int k = 0;
533   unsigned int BF_vals[] = {BF_val1, BF_val2, BF_val3};
534   unsigned int reference_sig, reference_sig_vals[] = {0U, 1U, 2U, 4U, 6U, 63U};
535   int num_reference_sig_vals = sizeof(reference_sig_vals)/sizeof(unsigned int);
536
537   while ((func = dfp_test_significance_tests[k].test_func)) {
538      int i;
539      dfp_one_arg_test_t test_def = dfp_test_significance_tests[k];
540
541      for (i = 0; i < NUM_DFP_VALS; i++) {
542         int j;
543         if (test_def.precision == LONG_TEST) {
544            test_valB.u64_val = dfp64_vals[i];
545         } else {
546            test_valB.u128.valu = dfp128_vals[i * 2];
547            test_valB.u128.vall = dfp128_vals[(i * 2) + 1];
548         }
549
550         for (j = 0; j < num_reference_sig_vals; j++) {
551            int bf_idx, BF;
552            reference_sig = reference_sig_vals[j];
553            for (bf_idx = 0; bf_idx < sizeof(BF_vals)/sizeof(unsigned int); bf_idx++) {
554               unsigned int condreg;
555               unsigned int flags;
556               BF = BF_vals[bf_idx];
557               SET_FPSCR_ZERO;
558               SET_CR_XER_ZERO;
559               /* There is an ABI change in how 128 bit arguments are aligned
560                * with GCC 5.0.  The compiler generates a "note" about this
561                * starting with GCC 4.9.  To avoid generating the "note", pass
562                * the address of the 128-bit arguments rather then the value.
563                */
564               (*func)(BF, reference_sig, &test_valB);
565               GET_CR(flags);
566
567               condreg = ((flags >> (4 * (7-BF)))) & 0xf;
568               printf("%s (ref_sig=%d) %s", test_def.name, reference_sig, test_def.op);
569               if (test_def.precision == LONG_TEST) {
570                  printf("%016llx", test_valB.u64_val);
571               } else {
572                  printf("%016llx %016llx", test_valB.u128.valu, test_valB.u128.vall);
573               }
574               printf(" => %x (BF=%d)\n", condreg, BF);
575            }
576         }
577         printf( "\n" );
578      }
579      k++;
580   }
581}
582
583static test_table_t
584         all_tests[] =
585{
586                    { &test_dfp_test_significance_ops,
587                      "Test DFP test significance instructions"},
588                    { &test_dfp_ddedpd_ops,
589                      "Test DFP DPD-to-BCD instructions"},
590                    { &test_dfp_denbcd_ops,
591                      "Test DFP BCD-to-DPD instructions"},
592                    { NULL, NULL }
593};
594#endif // HAS_DFP
595
596int main() {
597#if defined(HAS_DFP)
598
599   test_table_t aTest;
600   test_driver_func_t func;
601   int i = 0;
602
603   while ((func = all_tests[i].test_category)) {
604      aTest = all_tests[i];
605      printf( "%s\n", aTest.name );
606      (*func)();
607      i++;
608   }
609
610#endif // HAS_DFP
611   return 0;
612}
613