target-s390: Convert TEST UNDER MASK
[sdk/emulator/qemu.git] / target-s390x / cc_helper.c
1 /*
2  *  S/390 condition code helper routines
3  *
4  *  Copyright (c) 2009 Ulrich Hecht
5  *  Copyright (c) 2009 Alexander Graf
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2 of the License, or (at your option) any later version.
11  *
12  * This library is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include "cpu.h"
22 #include "helper.h"
23 #include "qemu/host-utils.h"
24
25 /* #define DEBUG_HELPER */
26 #ifdef DEBUG_HELPER
27 #define HELPER_LOG(x...) qemu_log(x)
28 #else
29 #define HELPER_LOG(x...)
30 #endif
31
32 static inline uint32_t cc_calc_ltgt_32(CPUS390XState *env, int32_t src,
33                                        int32_t dst)
34 {
35     if (src == dst) {
36         return 0;
37     } else if (src < dst) {
38         return 1;
39     } else {
40         return 2;
41     }
42 }
43
44 static inline uint32_t cc_calc_ltgt0_32(CPUS390XState *env, int32_t dst)
45 {
46     return cc_calc_ltgt_32(env, dst, 0);
47 }
48
49 static inline uint32_t cc_calc_ltgt_64(CPUS390XState *env, int64_t src,
50                                        int64_t dst)
51 {
52     if (src == dst) {
53         return 0;
54     } else if (src < dst) {
55         return 1;
56     } else {
57         return 2;
58     }
59 }
60
61 static inline uint32_t cc_calc_ltgt0_64(CPUS390XState *env, int64_t dst)
62 {
63     return cc_calc_ltgt_64(env, dst, 0);
64 }
65
66 static inline uint32_t cc_calc_ltugtu_32(CPUS390XState *env, uint32_t src,
67                                          uint32_t dst)
68 {
69     if (src == dst) {
70         return 0;
71     } else if (src < dst) {
72         return 1;
73     } else {
74         return 2;
75     }
76 }
77
78 static inline uint32_t cc_calc_ltugtu_64(CPUS390XState *env, uint64_t src,
79                                          uint64_t dst)
80 {
81     if (src == dst) {
82         return 0;
83     } else if (src < dst) {
84         return 1;
85     } else {
86         return 2;
87     }
88 }
89
90 static uint32_t cc_calc_tm_32(CPUS390XState *env, uint32_t val, uint32_t mask)
91 {
92     uint32_t r = val & mask;
93
94     if (r == 0) {
95         return 0;
96     } else if (r == mask) {
97         return 3;
98     } else {
99         return 1;
100     }
101 }
102
103 static uint32_t cc_calc_tm_64(CPUS390XState *env, uint64_t val, uint64_t mask)
104 {
105     uint64_t r = val & mask;
106
107     if (r == 0) {
108         return 0;
109     } else if (r == mask) {
110         return 3;
111     } else {
112         int top = clz64(mask);
113         if ((int64_t)(val << top) < 0) {
114             return 2;
115         } else {
116             return 1;
117         }
118     }
119 }
120
121 static inline uint32_t cc_calc_nz(CPUS390XState *env, uint64_t dst)
122 {
123     return !!dst;
124 }
125
126 static inline uint32_t cc_calc_add_64(CPUS390XState *env, int64_t a1,
127                                       int64_t a2, int64_t ar)
128 {
129     if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) {
130         return 3; /* overflow */
131     } else {
132         if (ar < 0) {
133             return 1;
134         } else if (ar > 0) {
135             return 2;
136         } else {
137             return 0;
138         }
139     }
140 }
141
142 static uint32_t cc_calc_addu_64(CPUS390XState *env, uint64_t a1,
143                                 uint64_t a2, uint64_t ar)
144 {
145     return (ar != 0) + 2 * (ar < a1);
146 }
147
148 static uint32_t cc_calc_addc_64(CPUS390XState *env, uint64_t a1,
149                                 uint64_t a2, uint64_t ar)
150 {
151     /* Recover a2 + carry_in.  */
152     uint64_t a2c = ar - a1;
153     /* Check for a2+carry_in overflow, then a1+a2c overflow.  */
154     int carry_out = (a2c < a2) || (ar < a1);
155
156     return (ar != 0) + 2 * carry_out;
157 }
158
159 static inline uint32_t cc_calc_sub_64(CPUS390XState *env, int64_t a1,
160                                       int64_t a2, int64_t ar)
161 {
162     if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) {
163         return 3; /* overflow */
164     } else {
165         if (ar < 0) {
166             return 1;
167         } else if (ar > 0) {
168             return 2;
169         } else {
170             return 0;
171         }
172     }
173 }
174
175 static inline uint32_t cc_calc_subu_64(CPUS390XState *env, uint64_t a1,
176                                        uint64_t a2, uint64_t ar)
177 {
178     if (ar == 0) {
179         return 2;
180     } else {
181         if (a2 > a1) {
182             return 1;
183         } else {
184             return 3;
185         }
186     }
187 }
188
189 static uint32_t cc_calc_subb_64(CPUS390XState *env, uint64_t a1,
190                                 uint64_t a2, uint64_t ar)
191 {
192     /* We had borrow-in if normal subtraction isn't equal.  */
193     int borrow_in = ar - (a1 - a2);
194     int borrow_out;
195
196     /* If a2 was ULONG_MAX, and borrow_in, then a2 is logically 65 bits,
197        and we must have had borrow out.  */
198     if (borrow_in && a2 == (uint64_t)-1) {
199         borrow_out = 1;
200     } else {
201         a2 += borrow_in;
202         borrow_out = (a2 > a1);
203     }
204
205     return (ar != 0) + 2 * !borrow_out;
206 }
207
208 static inline uint32_t cc_calc_abs_64(CPUS390XState *env, int64_t dst)
209 {
210     if ((uint64_t)dst == 0x8000000000000000ULL) {
211         return 3;
212     } else if (dst) {
213         return 1;
214     } else {
215         return 0;
216     }
217 }
218
219 static inline uint32_t cc_calc_nabs_64(CPUS390XState *env, int64_t dst)
220 {
221     return !!dst;
222 }
223
224 static inline uint32_t cc_calc_comp_64(CPUS390XState *env, int64_t dst)
225 {
226     if ((uint64_t)dst == 0x8000000000000000ULL) {
227         return 3;
228     } else if (dst < 0) {
229         return 1;
230     } else if (dst > 0) {
231         return 2;
232     } else {
233         return 0;
234     }
235 }
236
237
238 static inline uint32_t cc_calc_add_32(CPUS390XState *env, int32_t a1,
239                                       int32_t a2, int32_t ar)
240 {
241     if ((a1 > 0 && a2 > 0 && ar < 0) || (a1 < 0 && a2 < 0 && ar > 0)) {
242         return 3; /* overflow */
243     } else {
244         if (ar < 0) {
245             return 1;
246         } else if (ar > 0) {
247             return 2;
248         } else {
249             return 0;
250         }
251     }
252 }
253
254 static uint32_t cc_calc_addu_32(CPUS390XState *env, uint32_t a1,
255                                 uint32_t a2, uint32_t ar)
256 {
257     return (ar != 0) + 2 * (ar < a1);
258 }
259
260 static uint32_t cc_calc_addc_32(CPUS390XState *env, uint32_t a1,
261                                 uint32_t a2, uint32_t ar)
262 {
263     /* Recover a2 + carry_in.  */
264     uint32_t a2c = ar - a1;
265     /* Check for a2+carry_in overflow, then a1+a2c overflow.  */
266     int carry_out = (a2c < a2) || (ar < a1);
267
268     return (ar != 0) + 2 * carry_out;
269 }
270
271 static inline uint32_t cc_calc_sub_32(CPUS390XState *env, int32_t a1,
272                                       int32_t a2, int32_t ar)
273 {
274     if ((a1 > 0 && a2 < 0 && ar < 0) || (a1 < 0 && a2 > 0 && ar > 0)) {
275         return 3; /* overflow */
276     } else {
277         if (ar < 0) {
278             return 1;
279         } else if (ar > 0) {
280             return 2;
281         } else {
282             return 0;
283         }
284     }
285 }
286
287 static inline uint32_t cc_calc_subu_32(CPUS390XState *env, uint32_t a1,
288                                        uint32_t a2, uint32_t ar)
289 {
290     if (ar == 0) {
291         return 2;
292     } else {
293         if (a2 > a1) {
294             return 1;
295         } else {
296             return 3;
297         }
298     }
299 }
300
301 static uint32_t cc_calc_subb_32(CPUS390XState *env, uint32_t a1,
302                                 uint32_t a2, uint32_t ar)
303 {
304     /* We had borrow-in if normal subtraction isn't equal.  */
305     int borrow_in = ar - (a1 - a2);
306     int borrow_out;
307
308     /* If a2 was UINT_MAX, and borrow_in, then a2 is logically 65 bits,
309        and we must have had borrow out.  */
310     if (borrow_in && a2 == (uint32_t)-1) {
311         borrow_out = 1;
312     } else {
313         a2 += borrow_in;
314         borrow_out = (a2 > a1);
315     }
316
317     return (ar != 0) + 2 * !borrow_out;
318 }
319
320 static inline uint32_t cc_calc_abs_32(CPUS390XState *env, int32_t dst)
321 {
322     if ((uint32_t)dst == 0x80000000UL) {
323         return 3;
324     } else if (dst) {
325         return 1;
326     } else {
327         return 0;
328     }
329 }
330
331 static inline uint32_t cc_calc_nabs_32(CPUS390XState *env, int32_t dst)
332 {
333     return !!dst;
334 }
335
336 static inline uint32_t cc_calc_comp_32(CPUS390XState *env, int32_t dst)
337 {
338     if ((uint32_t)dst == 0x80000000UL) {
339         return 3;
340     } else if (dst < 0) {
341         return 1;
342     } else if (dst > 0) {
343         return 2;
344     } else {
345         return 0;
346     }
347 }
348
349 /* calculate condition code for insert character under mask insn */
350 static inline uint32_t cc_calc_icm_32(CPUS390XState *env, uint32_t mask,
351                                       uint32_t val)
352 {
353     uint32_t cc;
354
355     HELPER_LOG("%s: mask 0x%x val %d\n", __func__, mask, val);
356     if (mask == 0xf) {
357         if (!val) {
358             return 0;
359         } else if (val & 0x80000000) {
360             return 1;
361         } else {
362             return 2;
363         }
364     }
365
366     if (!val || !mask) {
367         cc = 0;
368     } else {
369         while (mask != 1) {
370             mask >>= 1;
371             val >>= 8;
372         }
373         if (val & 0x80) {
374             cc = 1;
375         } else {
376             cc = 2;
377         }
378     }
379     return cc;
380 }
381
382 static inline uint32_t cc_calc_slag(CPUS390XState *env, uint64_t src,
383                                     uint64_t shift)
384 {
385     uint64_t mask = ((1ULL << shift) - 1ULL) << (64 - shift);
386     uint64_t match, r;
387
388     /* check if the sign bit stays the same */
389     if (src & (1ULL << 63)) {
390         match = mask;
391     } else {
392         match = 0;
393     }
394
395     if ((src & mask) != match) {
396         /* overflow */
397         return 3;
398     }
399
400     r = ((src << shift) & ((1ULL << 63) - 1)) | (src & (1ULL << 63));
401
402     if ((int64_t)r == 0) {
403         return 0;
404     } else if ((int64_t)r < 0) {
405         return 1;
406     }
407
408     return 2;
409 }
410
411
412 static inline uint32_t do_calc_cc(CPUS390XState *env, uint32_t cc_op,
413                                   uint64_t src, uint64_t dst, uint64_t vr)
414 {
415     uint32_t r = 0;
416
417     switch (cc_op) {
418     case CC_OP_CONST0:
419     case CC_OP_CONST1:
420     case CC_OP_CONST2:
421     case CC_OP_CONST3:
422         /* cc_op value _is_ cc */
423         r = cc_op;
424         break;
425     case CC_OP_LTGT0_32:
426         r = cc_calc_ltgt0_32(env, dst);
427         break;
428     case CC_OP_LTGT0_64:
429         r =  cc_calc_ltgt0_64(env, dst);
430         break;
431     case CC_OP_LTGT_32:
432         r =  cc_calc_ltgt_32(env, src, dst);
433         break;
434     case CC_OP_LTGT_64:
435         r =  cc_calc_ltgt_64(env, src, dst);
436         break;
437     case CC_OP_LTUGTU_32:
438         r =  cc_calc_ltugtu_32(env, src, dst);
439         break;
440     case CC_OP_LTUGTU_64:
441         r =  cc_calc_ltugtu_64(env, src, dst);
442         break;
443     case CC_OP_TM_32:
444         r =  cc_calc_tm_32(env, src, dst);
445         break;
446     case CC_OP_TM_64:
447         r =  cc_calc_tm_64(env, src, dst);
448         break;
449     case CC_OP_NZ:
450         r =  cc_calc_nz(env, dst);
451         break;
452     case CC_OP_ADD_64:
453         r =  cc_calc_add_64(env, src, dst, vr);
454         break;
455     case CC_OP_ADDU_64:
456         r =  cc_calc_addu_64(env, src, dst, vr);
457         break;
458     case CC_OP_ADDC_64:
459         r =  cc_calc_addc_64(env, src, dst, vr);
460         break;
461     case CC_OP_SUB_64:
462         r =  cc_calc_sub_64(env, src, dst, vr);
463         break;
464     case CC_OP_SUBU_64:
465         r =  cc_calc_subu_64(env, src, dst, vr);
466         break;
467     case CC_OP_SUBB_64:
468         r =  cc_calc_subb_64(env, src, dst, vr);
469         break;
470     case CC_OP_ABS_64:
471         r =  cc_calc_abs_64(env, dst);
472         break;
473     case CC_OP_NABS_64:
474         r =  cc_calc_nabs_64(env, dst);
475         break;
476     case CC_OP_COMP_64:
477         r =  cc_calc_comp_64(env, dst);
478         break;
479
480     case CC_OP_ADD_32:
481         r =  cc_calc_add_32(env, src, dst, vr);
482         break;
483     case CC_OP_ADDU_32:
484         r =  cc_calc_addu_32(env, src, dst, vr);
485         break;
486     case CC_OP_ADDC_32:
487         r =  cc_calc_addc_32(env, src, dst, vr);
488         break;
489     case CC_OP_SUB_32:
490         r =  cc_calc_sub_32(env, src, dst, vr);
491         break;
492     case CC_OP_SUBU_32:
493         r =  cc_calc_subu_32(env, src, dst, vr);
494         break;
495     case CC_OP_SUBB_32:
496         r =  cc_calc_subb_32(env, src, dst, vr);
497         break;
498     case CC_OP_ABS_32:
499         r =  cc_calc_abs_64(env, dst);
500         break;
501     case CC_OP_NABS_32:
502         r =  cc_calc_nabs_64(env, dst);
503         break;
504     case CC_OP_COMP_32:
505         r =  cc_calc_comp_32(env, dst);
506         break;
507
508     case CC_OP_ICM:
509         r =  cc_calc_icm_32(env, src, dst);
510         break;
511     case CC_OP_SLAG:
512         r =  cc_calc_slag(env, src, dst);
513         break;
514
515     case CC_OP_LTGT_F32:
516         r = set_cc_f32(env, src, dst);
517         break;
518     case CC_OP_LTGT_F64:
519         r = set_cc_f64(env, src, dst);
520         break;
521     case CC_OP_NZ_F32:
522         r = set_cc_nz_f32(dst);
523         break;
524     case CC_OP_NZ_F64:
525         r = set_cc_nz_f64(dst);
526         break;
527
528     default:
529         cpu_abort(env, "Unknown CC operation: %s\n", cc_name(cc_op));
530     }
531
532     HELPER_LOG("%s: %15s 0x%016lx 0x%016lx 0x%016lx = %d\n", __func__,
533                cc_name(cc_op), src, dst, vr, r);
534     return r;
535 }
536
537 uint32_t calc_cc(CPUS390XState *env, uint32_t cc_op, uint64_t src, uint64_t dst,
538                  uint64_t vr)
539 {
540     return do_calc_cc(env, cc_op, src, dst, vr);
541 }
542
543 uint32_t HELPER(calc_cc)(CPUS390XState *env, uint32_t cc_op, uint64_t src,
544                          uint64_t dst, uint64_t vr)
545 {
546     return do_calc_cc(env, cc_op, src, dst, vr);
547 }
548
549 /* insert psw mask and condition code into r1 */
550 void HELPER(ipm)(CPUS390XState *env, uint32_t cc, uint32_t r1)
551 {
552     uint64_t r = env->regs[r1];
553
554     r &= 0xffffffff00ffffffULL;
555     r |= (cc << 28) | ((env->psw.mask >> 40) & 0xf);
556     env->regs[r1] = r;
557     HELPER_LOG("%s: cc %d psw.mask 0x%lx r1 0x%lx\n", __func__,
558                cc, env->psw.mask, r);
559 }
560
561 #ifndef CONFIG_USER_ONLY
562 void HELPER(load_psw)(CPUS390XState *env, uint64_t mask, uint64_t addr)
563 {
564     load_psw(env, mask, addr);
565     cpu_loop_exit(env);
566 }
567
568 void HELPER(sacf)(CPUS390XState *env, uint64_t a1)
569 {
570     HELPER_LOG("%s: %16" PRIx64 "\n", __func__, a1);
571
572     switch (a1 & 0xf00) {
573     case 0x000:
574         env->psw.mask &= ~PSW_MASK_ASC;
575         env->psw.mask |= PSW_ASC_PRIMARY;
576         break;
577     case 0x100:
578         env->psw.mask &= ~PSW_MASK_ASC;
579         env->psw.mask |= PSW_ASC_SECONDARY;
580         break;
581     case 0x300:
582         env->psw.mask &= ~PSW_MASK_ASC;
583         env->psw.mask |= PSW_ASC_HOME;
584         break;
585     default:
586         qemu_log("unknown sacf mode: %" PRIx64 "\n", a1);
587         program_interrupt(env, PGM_SPECIFICATION, 2);
588         break;
589     }
590 }
591 #endif