summaryrefslogtreecommitdiffstats
path: root/tests/tcg/ppc64le/bcdsub.c
blob: 8c188cae6d28075557dd83da5baf2d62a1c2b1ba (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <assert.h>
#include <unistd.h>
#include <signal.h>

#define CRF_LT  (1 << 3)
#define CRF_GT  (1 << 2)
#define CRF_EQ  (1 << 1)
#define CRF_SO  (1 << 0)
#define UNDEF   0

#define BCDSUB(vra, vrb, ps)                    \
    asm ("bcdsub. %1,%2,%3,%4;"                 \
         "mfocrf %0,0b10;"                      \
         : "=r" (cr), "=v" (vrt)                \
         : "v" (vra), "v" (vrb), "i" (ps)       \
         : );

#define TEST(vra, vrb, ps, exp_res, exp_cr6)    \
    do {                                        \
        __int128 vrt = 0;                       \
        int cr = 0;                             \
        BCDSUB(vra, vrb, ps);                   \
        if (exp_res)                            \
            assert(vrt == exp_res);             \
        assert((cr >> 4) == exp_cr6);           \
    } while (0)


/*
 * Unbounded result is equal to zero:
 *   sign = (PS) ? 0b1111 : 0b1100
 *   CR6 = 0b0010
 */
void test_bcdsub_eq(void)
{
    __int128 a, b;

    /* maximum positive BCD value */
    a = b = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c);

    TEST(a, b, 0, 0xc, CRF_EQ);
    TEST(a, b, 1, 0xf, CRF_EQ);
}

/*
 * Unbounded result is greater than zero:
 *   sign = (PS) ? 0b1111 : 0b1100
 *   CR6 = (overflow) ? 0b0101 : 0b0100
 */
void test_bcdsub_gt(void)
{
    __int128 a, b, c;

    /* maximum positive BCD value */
    a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999c);

    /* negative one BCD value */
    b = (__int128) 0x1d;

    TEST(a, b, 0, 0xc, (CRF_GT | CRF_SO));
    TEST(a, b, 1, 0xf, (CRF_GT | CRF_SO));

    c = (((__int128) 0x9999999999999999) << 64 | 0x999999999999998c);

    TEST(c, b, 0, a, CRF_GT);
    TEST(c, b, 1, (a | 0x3), CRF_GT);
}

/*
 * Unbounded result is less than zero:
 *   sign = 0b1101
 *   CR6 = (overflow) ? 0b1001 : 0b1000
 */
void test_bcdsub_lt(void)
{
    __int128 a, b;

    /* positive zero BCD value */
    a = (__int128) 0xc;

    /* positive one BCD value */
    b = (__int128) 0x1c;

    TEST(a, b, 0, 0x1d, CRF_LT);
    TEST(a, b, 1, 0x1d, CRF_LT);

    /* maximum negative BCD value */
    a = (((__int128) 0x9999999999999999) << 64 | 0x999999999999999d);

    /* positive one BCD value */
    b = (__int128) 0x1c;

    TEST(a, b, 0, 0xd, (CRF_LT | CRF_SO));
    TEST(a, b, 1, 0xd, (CRF_LT | CRF_SO));
}

void test_bcdsub_invalid(void)
{
    __int128 a, b;

    /* positive one BCD value */
    a = (__int128) 0x1c;
    b = 0xf00;

    TEST(a, b, 0, UNDEF, CRF_SO);
    TEST(a, b, 1, UNDEF, CRF_SO);

    TEST(b, a, 0, UNDEF, CRF_SO);
    TEST(b, a, 1, UNDEF, CRF_SO);

    a = 0xbad;

    TEST(a, b, 0, UNDEF, CRF_SO);
    TEST(a, b, 1, UNDEF, CRF_SO);
}

int main(void)
{
    struct sigaction action;

    action.sa_handler = _exit;
    sigaction(SIGABRT, &action, NULL);

    test_bcdsub_eq();
    test_bcdsub_gt();
    test_bcdsub_lt();
    test_bcdsub_invalid();

    return 0;
}